Config Files & CLI¶
OpenPyTEA supports a fully declarative, JSON-based workflow that makes studies
reproducible, shareable, and easy to version-control.
The openpytea.io module handles loading configuration files and
exporting results.
Four high-level functions drive the workflow:
run_equipment()— reads an equipment configuration file and returns a list ofEquipmentobjects with all costs estimated, writing the results to an output JSON file.run_plant()— reads a plant configuration file, combines it with the equipment list, runs all calculations, and writes the plant results to an output JSON file.run_tea()— executes the full TEA pipeline (cost breakdowns, sensitivity analysis, Monte Carlo simulation) from three input files, writing results and optional plots to an output directory.run_openpytea()— the single-file counterpart torun_tea(): same pipeline, but driven by one combined JSON file rather than three. This is the entry point used by the CLI (see Single-file / CLI workflow).
To see all examples below in action, refer to the walkthrough notebook and the case study JSON notebook.
from openpytea import (
run_equipment, run_plant, run_tea, run_openpytea, load_results
)
File structure¶
A complete TEA study uses three JSON input files:
project/
├── equipment.json # List of equipment items
├── plant.json # Plant configuration and financial assumptions
└── analysis.json # Analysis settings and output options
For single-file / CLI use, the same three blocks (equipment, plant,
analysis) can instead live under one combined file — see
Single-file / CLI workflow below.
equipment.json¶
Each entry requires name, process_type, category, and either
param (size/capacity parameter) or purchased_cost (manual override).
All other fields are optional — omitting material, as shown below,
resolves it from the matched correlation’s own default material instead
of assuming carbon steel (see Available materials).
{
"equipment": [
{
"name": "COMP-1",
"param": 945,
"process_type": "Fluids",
"category": "Compressors, fans, & blowers",
"type": "Compressor, centrifugal",
"material": "Carbon steel"
},
{
"name": "HX-1",
"param": 31.87,
"process_type": "Fluids",
"category": "Heat Exchangers",
"type": "U-tube shell & tube"
}
]
}
HX-1 omits material entirely, so it resolves to the matched
correlation’s own default material with a material factor of 1.0.
plant.json¶
The plant object mirrors the configuration dict accepted by
Plant. Monte Carlo uncertainty fields (std,
min, max) can be embedded directly in variable_opex_inputs,
plant_products, and operator_hourly_rate — they are ignored when
running plant-only calculations and activated automatically when
run_tea() executes a Monte Carlo block. Uncertainty on project-level
factors (fixed_capital_factor, fixed_opex_factor,
project_lifetime, interest_rate, plant_utilization,
tax_rate) is instead configured via the top-level
project_uncertainties key; omitting a parameter there falls back to its
built-in default distribution, and setting "std": 0 disables sampling
for it.
{
"plant": {
"plant_name": "Steam Reforming",
"process_type": "Fluids",
"country": "United States",
"region": "Gulf Coast",
"interest_rate": 0.09,
"project_lifetime": 30,
"plant_utilization": 0.95,
"project_uncertainties": {
"fixed_capital_factor": { "std": 0.3, "min": 0.25, "max": 1.75 },
"interest_rate": { "std": 0.02 },
"plant_utilization": { "std": 0.05 }
},
"operator_hourly_rate": {
"rate": 38.11,
"std": 10,
"min": 25,
"max": 50
},
"plant_products": {
"hydrogen": {
"production": 50000,
"price": 5.5
}
},
"variable_opex_inputs": {
"electricity": {
"consumption": 38293.44,
"price": 0.05,
"std": 0.03,
"min": 0.01,
"max": 3
},
"methane_feed": {
"consumption": 219936,
"price": 0.4,
"std": 0.25,
"min": 0,
"max": 5
}
}
}
}
analysis.json¶
The analysis block contains one sub-object per analysis type. Each must
have "run": true to be executed, plus an "args" dict that is passed
directly to the corresponding Python function. The output block controls
whether results and plots are saved, and in what format.
{
"analysis": {
"direct_costs": { "run": true, "args": { "pct": false } },
"fixed_capital": { "run": true, "args": { "additional_capex": false, "pct": false } },
"fixed_opex": { "run": true, "args": { "pct": true } },
"variable_opex": { "run": true, "args": { "pct": false } },
"levelized_cost": { "run": true, "args": { "pct": false } },
"cash_flow": { "run": true },
"sensitivity": {
"run": true,
"cases": [
{
"name": "methane_feed_lcop",
"args": { "parameter": "methane_feed", "plus_minus_value": 1.0, "metric": "LCOP" }
},
{
"name": "electricity_npv",
"args": { "parameter": "electricity", "plus_minus_value": 0.5, "metric": "NPV" }
}
]
},
"tornado": {
"run": true,
"args": { "plus_minus_value": 0.5, "metric": "NPV" }
},
"monte_carlo": {
"run": true,
"args": { "num_samples": 1000000, "batch_size": 10000 },
"metric": ["LCOP", "NPV"],
"plot_inputs": true
}
},
"output": {
"save_json": true,
"save_plots": true,
"plot_format": "pdf",
"dpi": 600
}
}
The "metric" list under monte_carlo controls which metrics are
rendered as histogram plots when save_plots is true. Setting
"plot_inputs": true additionally renders a grid of histograms (via
plot_monte_carlo_inputs()) showing the sampled
distribution of every uncertain input (project-level factors from
plant.project_uncertainties, plus any priced variable_opex_inputs,
plant_products, and operator_hourly_rate), saved as
{plant_name}_monte_carlo_inputs.{plot_format}. The args dict maps
directly to monte_carlo() keyword arguments.
Running a study¶
Equipment I/O¶
run_equipment() reads the equipment file, builds the
Equipment objects, and writes a results JSON:
equipment_list = run_equipment(
input_path="project/equipment.json",
output_path="outputs/equipment_results.json",
)
print(equipment_list[0]) # inspect a single Equipment object
Plant I/O¶
run_plant() loads the plant configuration, attaches the
equipment, runs all calculations, and writes a plant results JSON:
plant = run_plant(
plant_input_path="project/plant.json",
plant_output_path="outputs/plant_results.json",
equipment_input_path="project/equipment.json",
)
If you already have an equipment_list from a previous run_equipment
call, pass it directly instead:
plant = run_plant(
plant_input_path="project/plant.json",
plant_output_path="outputs/plant_results.json",
equipment_list=equipment_list,
)
Full TEA pipeline¶
run_tea() orchestrates the complete workflow from the
three input files and writes all results and plots to output_dir:
results = run_tea(
equipment_input_path="project/equipment.json",
plant_input_path="project/plant.json",
analysis_input_path="project/analysis.json",
output_dir="outputs/tea_results",
)
The function returns a dict with keys for each analysis that was run:
results["direct_costs"] # equipment-level cost breakdown
results["fixed_capital"] # CAPEX breakdown
results["fixed_opex"] # fixed OPEX breakdown
results["variable_opex"] # variable OPEX breakdown
results["levelized_cost"] # LCOP breakdown (CAPEX, OPEX, side revenue)
results["cash_flow"] # cumulative cash flow diagram data
results["sensitivity"] # dict of sensitivity cases
results["tornado"] # tornado data
results["monte_carlo"] # Monte Carlo results (metrics + sampled inputs)
Single-file / CLI workflow¶
run_openpytea() runs the exact same pipeline as
run_tea(), but from a single combined configuration file instead of
three. This is convenient for CLI use and for sharing one self-contained
study file. The file simply merges the top-level keys that would otherwise
live in equipment.json, plant.json, and analysis.json:
{
"equipment": [ { "name": "COMP-1", "param": 945, "...": "..." } ],
"plant": { "plant_name": "Steam Reforming", "...": "..." },
"analysis": { "direct_costs": { "run": true }, "...": "..." },
"output": { "save_json": true, "save_plots": true }
}
results = run_openpytea(
config_path="project/config.json",
output_dir="outputs/tea_results",
)
It returns the same results dict, and writes the same
{plant_name}_equipment_results.json,
{plant_name}_plant_results.json, and
{plant_name}_analysis_results.json files (plus optional plots) as
run_tea() — so load_results() and everything under
Comparing multiple scenarios work
unchanged. Use load_openpytea_config() directly if you
only need the parsed config dict (e.g. to validate a file before running
it). See examples/input_configs/smr_combined.json for a full example.
The openpytea command¶
Installing the package also installs an openpytea console command
(openpytea.cli), a thin wrapper around the four functions above.
Running the combined config from the previous example needs no Python at
all:
$ openpytea run project/config.json --output-dir outputs/tea_results
Ran: direct_costs, fixed_capital, fixed_opex, variable_opex, ...
Results written to: outputs/tea_results
--output-dir/-o is optional — omit it to fall back to the config’s
own output.directory (or "results" if that isn’t set either).
The other three functions are available as their own subcommands, for running a single stage of the pipeline:
$ openpytea equipment project/equipment.json outputs/equipment_results.json
Wrote 14 equipment item(s) to outputs/equipment_results.json
$ openpytea plant project/plant.json outputs/plant_results.json \
--equipment project/equipment.json
Wrote plant 'Steam Reforming' results to outputs/plant_results.json
$ openpytea tea --equipment project/equipment.json \
--plant project/plant.json \
--analysis project/analysis.json \
--output-dir outputs/tea_results
Ran: direct_costs, fixed_capital, ...
Results written to: outputs/tea_results
Run openpytea --help or openpytea <command> --help for the full
list of options, and openpytea --version to print the installed
version.
Loading saved results¶
Use load_results() to reload a previously saved analysis
results file for further analysis or visualization. The output file is named
{plant_name}_analysis_results.json inside output_dir:
results = load_results(
filepath="outputs/tea_results/Steam Reforming_analysis_results.json"
)
mc = results["monte_carlo"]
from openpytea.plotting import plot_monte_carlo
fig, ax = plot_monte_carlo(mc, metric="LCOP", bins=30)
Comparing multiple scenarios¶
Run run_tea() for each scenario and reload the Monte Carlo results to
compare them on the same figure:
run_tea(
equipment_input_path="project/smr_equipment.json",
plant_input_path="project/smr_plant.json",
analysis_input_path="project/analysis.json",
output_dir="outputs/smr_results",
)
run_tea(
equipment_input_path="project/aec_equipment.json",
plant_input_path="project/aec_plant.json",
analysis_input_path="project/analysis.json",
output_dir="outputs/aec_results",
)
smr_mc = load_results("outputs/smr_results/Steam Reforming_analysis_results.json")["monte_carlo"]
aec_mc = load_results("outputs/aec_results/Electrolysis_analysis_results.json")["monte_carlo"]
from openpytea.plotting import plot_multiple_monte_carlo
plot_multiple_monte_carlo(data_list=[smr_mc, aec_mc], metric="LCOP", bins=30)
See also¶
openpytea.io— full API referenceAnalysis — analysis functions used inside
run_tea()Plotting — visualization functions for loaded results
Walkthrough notebook — end-to-end worked example