openpytea.analysis

openpytea.analysis.direct_costs_data(plants, pct=False)[source]

Extract and organize direct cost data from one or more plants. This function aggregates direct cost information from equipment lists across one or more plants and prepares the data for visualization as a bar chart. :param plants: A single plant object or a list of plant objects from which to extract

direct cost data.

Parameters:

pct (bool, optional) – If True, return direct costs as percentages of the total. If False (default), return absolute cost values.

Returns:

A dictionary containing structured data for bar chart visualization, including: - Component costs keyed by equipment name - Plant names as x-axis labels - Currency symbol - Chart title and formatting information

Return type:

dict

Notes

  • If plants list is empty, USD currency symbol is used as default

  • Currency is automatically extracted from the first plant in the list

  • Each equipment’s direct cost is converted to float for numerical

operations

Examples

>>> plant1 = Plant(name="Plant A", currency="$")
>>> data = direct_costs_data(plant1)
>>> data = direct_costs_data([plant1, plant2], pct=True)
openpytea.analysis.fixed_capital_data(plants, additional_capex=False, pct=False)[source]

Generate fixed capital expenditure data for one or more plants. This function calculates and aggregates the fixed capital costs for given plants, breaking down costs into components (ISBL, OSBL, Design & Engineering, and Contingency). Optionally includes additional CAPEX costs if available. Args:

plants (Plant or list[Plant]): A single plant object or list of plant objects to generate fixed capital data for. additional_capex (bool, optional): If True, includes additional CAPEX costs

from the plant’s additional_capex_cost attribute. Defaults to False.

pct (bool, optional): If True, returns data as percentages of total CAPEX.

If False, returns absolute values. Defaults to False.

Returns:
dict: A dictionary containing structured bar chart data with keys:
  • “components”: List of dictionaries with CAPEX component

breakdowns - “labels”: List of plant names (x-axis labels) - “title”: Chart title (“Fixed CAPEX”) - “currency”: Currency symbol or code - “percentage”: Boolean indicating if values are percentages

Raises:

AttributeError: If plant objects lack required attributes (isbl, osbl, dne, etc.).

Example:
>>> plants = [plant1, plant2]
>>> data = fixed_capital_data(plants, additional_capex=True, pct=False)
>>> # Returns fixed CAPEX breakdown for both plants with additional
>>> # costs in absolute values
openpytea.analysis.variable_opex_data(plants, pct=False)[source]

Extract variable operational expenditure (OPEX) data from ‘ one or more plants. This function processes plant objects to compile their variable OPEX components and returns formatted data suitable for visualization. It handles multiple cost definition formats and supports currency representation. Args:

plants (Plant or list[Plant]): A single plant object or list of plant objects from which to extract variable OPEX data. pct (bool, optional): If True, display values as percentages. Default is False.

Returns:

dict: A dictionary containing structured data for visualization, including:

  • Components breakdown for each plant

  • X-axis labels (plant names)

  • Title: “Annual variable OPEX”

  • Currency symbol or format

  • Data formatted as percentages if pct=True

Notes:
  • Cost values are determined from (in priority order):
    1. “annual_cost” field

    2. “cost” field

    3. “consumption” * “price” calculation

  • If none of these fields exist, the component is skipped.

  • Component names are formatted via _make_label() function.

  • Currency is extracted from the first plant,

defaulting to “$” if no plants provided.

openpytea.analysis.fixed_opex_data(plants, pct=False)[source]

Generate fixed operating expenditure (OPEX) data for one or more plants. This function calculates and aggregates the fixed OPEX components for the given plants, including operating labor, supervision, maintenance, taxes, insurance, and other operational costs. :param plants: A single Plant object or a list of Plant objects for which to

calculate fixed OPEX data.

Parameters:

pct (bool, optional) – If True, return OPEX data as percentages. If False (default), return absolute values.

Returns:

A dictionary containing structured bar chart data with OPEX components and plant names. The structure includes: - Component costs (Operating labor, Supervision, Maintenance, etc.) - Plant names as x-axis labels - Currency information - Annual fixed OPEX totals

Return type:

dict

Notes

The function calculates the following fixed OPEX components: - Operating labor - Supervision - Direct salary overhead - Laboratory charges - Maintenance - Taxes & insurance - Rent of land - Environmental charges - Operating supplies - General plant overhead - Interest on working capital - Patents & royalties - Distribution & selling - Research & Development (R&D)

Examples

>>> result = fixed_opex_data(plant1)
>>> result = fixed_opex_data([plant1, plant2], pct=True)
openpytea.analysis.levelized_cost_data(plants, pct=False)[source]

Generate levelized cost of production (LCOP) breakdown data for one or more plants. This function discounts capital costs, cash costs, side-product revenue, and production over each plant’s project lifetime at its interest rate (mirroring Plant.calculate_levelized_cost), then divides the discounted CAPEX, OPEX, and side revenue by the discounted production so each component is expressed per unit of main product. Side revenue is negated (since it is subtracted from the LCOP numerator), so the components sum directly to the plant’s LCOP: CAPEX + OPEX + Side revenue = LCOP. :param plants: A single plant object or a list of plant objects for which to build

the levelized cost breakdown.

Parameters:

pct (bool, optional) – If True, return the breakdown as percentages of the total. If False (default), return absolute values.

Returns:

A dictionary containing structured bar chart data with keys: - CAPEX - OPEX - Side revenue (each expressed per unit of main product), along with plant names, currency, and formatting information.

Return type:

dict

Notes

  • Only the scalar (non-Monte Carlo) case is supported; each plant’s project_lifetime and interest_rate must be scalar values.

Examples

>>> data = levelized_cost_data(plant1)
>>> data = levelized_cost_data([plant1, plant2], pct=True)
openpytea.analysis.cash_flow_data(plants)[source]

Prepare cumulative cash flow data for one or more plants, for plotting the classic project cash flow diagram (cumulative cash position vs. time): a dip into debt during construction/start-up, a minimum (“maximum investment”), a break-even point where the curve crosses back above zero, and a rise into profit for the remainder of the project life.

Parameters:

plants (Plant or list of Plant) – A single plant object or a list of plant objects to build the cash flow diagram data for. Each plant’s calculate_cash_flow is (re)run to ensure the underlying annual cash flow array is up to date.

Returns:

A dictionary containing: - “curves” : list of dict

One entry per plant, each containing: - “plant” : str

Plant name.

  • ”years”ndarray

    Time axis from 0 (project start) to the project lifetime, one point per year.

  • ”cumulative”ndarray

    Cumulative cash position at each year in years.

  • ”max_investment”float

    Depth of the deepest point of the cumulative cash flow curve (0 if the curve never goes negative).

  • ”max_investment_year”float

    Year at which max_investment occurs.

  • ”breakeven_year”float or None

    Year at which the cumulative cash flow first crosses back above zero after having been negative (linearly interpolated between the two surrounding years). None if the project never goes into debt or never recovers.

  • ”payback_time”float or None

    Alias of breakeven_year.

  • ”project_life”float

    Final year in years (the plant’s project lifetime).

  • ”xlabel”str

    Label for the x-axis.

  • ”ylabel”str

    Label for the y-axis (excluding currency units).

  • ”currency”str

    Currency symbol, taken from the first plant.

Return type:

dict

Notes

  • Only the scalar (non-Monte Carlo) case is supported; if a plant’s cash_flow has multiple rows (vectorised inputs), the first row is used.

  • The cumulative cash flow already reflects the plant’s CAPEX ramp, working capital draw/release, production ramp, depreciation, and tax lag, as computed by Plant.calculate_cash_flow.

Examples

>>> data = cash_flow_data(plant)
>>> data = cash_flow_data([plant_a, plant_b])
openpytea.analysis.sensitivity_data(plants, parameter, plus_minus_value, n_points=21, metric='LCOP', label=None, additional_capex=False)[source]

Perform sensitivity analysis on one or more plants by varying a parameter. This function computes how a specified metric (e.g., LCOP) changes as a parameter is varied by a given percentage range. It supports both top-level parameters (capital, opex, etc.) and nested parameters (variable costs, product prices, etc.). :param plants: One or more Plant objects to analyze. If a single plant is provided,

it is converted to a list.

Parameters:
  • parameter (str) –

    The parameter to vary. Can be specified as: - A top-level key: “fixed_capital”, “fixed_opex”, “project_lifetime”,

    ”interest_rate”, or “operator_hourly_rate”

    • A nested key: “variable_opex_inputs.{key}” or “plant_products.{key}”

    • A shorthand: “{key}” (resolved to full path if unambiguous)

  • plus_minus_value (float) – The fraction (0-1) to vary the parameter by in both directions. For example, 0.2 varies from -20% to +20%.

  • n_points (int, optional) – Number of points along the variation range. Default is 21.

  • metric (str, optional) – The metric to compute. Default is “LCOP”. Will be converted to uppercase.

  • label (str, optional) – Custom label for the y-axis. If None, a default label is generated based on the metric and plant currency.

  • additional_capex (bool, optional) – Whether to include additional capital expenditure in calculations. Default is False.

Returns:

A dictionary containing: - “curves” : list of dict

List of results for each plant, each containing: - “plant” : str

Plant name or identifier

  • ”x”ndarray

    Percentage changes along the variation range

  • ”y”ndarray or list

    Metric values corresponding to each point

  • ”baseline”float

    Metric value at the baseline (0% variation)

  • ”xlabel”str

    Label for the x-axis (parameter name with % unit)

  • ”ylabel”str

    Label for the y-axis (metric name and unit)

  • ”parameter”str

    Full parameter name that was varied

  • ”metric”str

    Metric that was computed (uppercase)

Return type:

dict

Raises:

ValueError – If parameter is ambiguous across plants or unrecognized.

Notes

  • For “fixed_capital” and “fixed_opex”,

the original value is assumed to be 1.0 - If a parameter does not exist for a particular plant, a flat baseline curve is returned - Shorthand parameters are resolved from full nested keys (e.g., “CO2” -> “variable_opex_inputs.CO2”)

openpytea.analysis.tornado_data(plant, plus_minus_value, metric='LCOP', label=None, additional_capex=False)[source]

Generate tornado plot data for sensitivity analysis (no plotting). This function performs a sensitivity analysis on a plant model by varying key parameters and calculating their impact on a specified metric. The results are sorted by total effect magnitude to facilitate tornado plot visualization. :param plant: The plant object containing model parameters and configuration. :type plant: Plant :param plus_minus_value: The percentage or absolute value to vary each parameter by

(e.g., 0.1 for ±10%).

Parameters:
  • metric (str, optional) – The metric to analyze. Default is “LCOP” (Levelized Cost of Power). Common metrics: “LCOP”, “LCOH”, “IRR”, “NPV”.

  • label (str, optional) – Custom label for the metric on the x-axis. If None, uses default label based on currency and metric type.

  • additional_capex (bool, optional) – Whether to include additional capital expenditure in calculations. Default is False.

  • dict

    Dictionary containing tornado plot data with keys: - factors : list[str]

    Sorted list of parameter names by sensitivity magnitude (ascending).

    • lowsnp.ndarray

      Metric values when each factor is reduced (sorted by effect size).

    • highsnp.ndarray

      Metric values when each factor is increased (sorted by effect size).

    • base_valuefloat

      Metric value with baseline parameters.

    • labelslist[str]

      Display labels for each factor (sorted by effect size).

    • plus_minus_valuefloat

      The sensitivity variation used.

    • metricstr

      The analyzed metric in uppercase.

    • xlabelstr

      Label for the x-axis.

Examples

>>> tornado_data = tornado_data(plant, plus_minus_value=0.1, metric="LCOP")
>>> factors = tornado_data["factors"]
>>> lows = tornado_data["lows"]
>>> highs = tornado_data["highs"]
openpytea.analysis.make_distribution(dist_id, loc=None, scale=None, shape=None, minimum=None, maximum=None)[source]

Build a frozen scipy.stats distribution from an OpenPyTEA dist_id.

Translates the compact (dist_id, loc, scale, shape, minimum, maximum) parameterization used throughout the Monte Carlo module into the corresponding frozen SciPy distribution object.

Parameters:
  • dist_id (int) –

    Distribution family identifier:

    • 2 : Lognormal (``loc``=mu, ``scale``=sigma)

    • 3 : Normal (``loc``=mean, ``scale``=std)

    • 4 : Uniform (minimum, maximum)

    • 5 : Triangular (loc``=mode, ``minimum, maximum)

    • 6 : Bernoulli (``loc``=p, ``scale``=success value, default 1)

    • 7 : Discrete uniform (minimum, maximum, inclusive)

    • 8 : Weibull (``loc``=offset, ``scale``=lambda, ``shape``=k)

    • 9 : Gamma (``loc``=offset, ``scale``=theta, ``shape``=k)

    • 10 : Beta (``loc``=alpha, ``shape``=beta, ``maximum``=upper bound)

    • 11 : GEV (``loc``=mu, ``scale``=sigma, ``shape``=xi)

    • 12 : Student’s t (``loc``=median, ``scale``=scale, ``shape``=nu)

  • loc (float, optional) – Location parameter; meaning depends on dist_id (see above).

  • scale (float, optional) – Scale parameter; meaning depends on dist_id (see above).

  • shape (float, optional) – Shape parameter, required for Weibull, Gamma, Beta, GEV, and Student’s t.

  • minimum (float, optional) – Lower bound, required for Uniform, Triangular, and Discrete uniform.

  • maximum (float, optional) – Upper bound, required for Uniform, Triangular, Discrete uniform, and (optionally) Beta.

Returns:

A frozen distribution instance (continuous rv_continuous / rv_discrete) exposing the usual rvs, pdf/pmf, etc.

Return type:

scipy.stats distribution

Raises:

ValueError – If dist_id is not one of the supported values above (0/1 are handled separately by sample_distribution() as constants).

See also

sample_distribution

Draws random samples, with optional truncation.

openpytea.analysis.sample_distribution(dist_id, size, loc=None, scale=None, shape=None, minimum=None, maximum=None, random_state=None)[source]

Draw random samples for a Monte Carlo input, with optional truncation.

Wraps make_distribution() to generate an array of samples. For dist_id 0 or 1 (fixed/constant values) it returns a constant array without touching random_state. When minimum/maximum bounds are given for Lognormal, Normal, or Bernoulli (dist_id 2, 3, 6), samples are drawn and re-drawn (rejection sampling) until size values fall within [minimum, maximum].

Parameters:
  • dist_id (int) – Distribution family identifier, see make_distribution(). 0 or 1 means “constant value equal to loc”.

  • size (int) – Number of samples to draw.

  • loc (float, optional) – Location parameter, forwarded to make_distribution().

  • scale (float, optional) – Scale parameter, forwarded to make_distribution().

  • shape (float, optional) – Shape parameter, forwarded to make_distribution().

  • minimum (float, optional) – Lower truncation bound (also used as a distribution parameter for some families, see make_distribution()).

  • maximum (float, optional) – Upper truncation bound (also used as a distribution parameter for some families, see make_distribution()).

  • random_state (numpy.random.Generator or int, optional) – Random state passed to scipy.statsrvs. Pass a single shared Generator across calls to keep an entire Monte Carlo run reproducible from one seed.

Returns:

Array of size samples.

Return type:

numpy.ndarray

Notes

Rejection sampling redraws in batches of 2 * remaining until enough in-bounds values are collected, so very narrow [minimum, maximum] windows relative to the distribution’s spread can be slow.

See also

make_distribution

Builds the underlying frozen SciPy distribution.

openpytea.analysis.monte_carlo(plant, num_samples=1000000, batch_size=1000, additional_capex=False, random_seed=None)[source]

Run a Monte Carlo uncertainty simulation over a plant’s financial metrics.

Samples every configured uncertain input (project-level factors such as fixed capital/OPEX, project lifetime, and interest rate; optionally plant utilization and tax rate; variable OPEX item prices; and product prices) and re-evaluates the plant’s economics num_samples times, producing a distribution of outcomes for LCOP and, when product prices are configured, NPV, ROI, and payback time.

Parameters:
  • plant (Plant) – A configured Plant. Uncertainty ranges are read from plant.project_uncertainties, plant.operator_hourly_rate, plant.variable_opex_inputs, and plant.plant_products — see the Monte Carlo section of the user guide for the configuration format. The plant is first baseline-initialized (fixed capital, variable/fixed OPEX, cash flow, levelized cost) but is not mutated by the simulation itself; each batch operates on a deep copy.

  • num_samples (int, optional) – Total number of Monte Carlo draws. Default is 1,000,000.

  • batch_size (int, optional) – Number of samples evaluated per batch (each batch deep-copies the plant and vectorizes the economic calculations over the batch). Larger values are faster but use more memory. Default is 1000.

  • additional_capex (bool, optional) – Whether to include additional CAPEX events when computing ROI and payback time. Default is False.

  • random_seed (int, optional) – Seed for the single numpy.random.Generator shared across all parameter draws, for reproducible runs. Default is None (nondeterministic).

Returns:

Dictionary with keys:

  • "name" : the plant’s name.

  • "metrics" : dict mapping "LCOP", "ROI", "NPV", "PBT" to numpy.ndarray of length num_samples (ROI, NPV, PBT stay zero-filled if product prices aren’t configured).

  • "inputs" : dict mapping each sampled input’s display name to its numpy.ndarray of drawn values.

  • "num_samples" : the requested sample count.

  • "additional_capex" : the flag used for ROI/PBT.

  • "currency" : the plant’s currency symbol.

Return type:

dict

Notes

  • The same results are also stored on the plant as plant.monte_carlo_metrics and plant.monte_carlo_inputs for use by plot_monte_carlo() and plot_monte_carlo_inputs().

  • All inputs are sampled once up front (in a fixed order, from one shared RNG) and then consumed batch-by-batch, so results are reproducible for a given random_seed regardless of batch_size.

See also

sample_distribution

Underlying per-input sampling routine.