JSON format¶
ExerPy keeps a model in a JSON file. The parsers for TESPy, Ebsilon and Aspen write it with
export_to_json(), and
from_json() reads it back. You can also write such a file
yourself, for example to analyse a model from a simulator that ExerPy has no parser for. This
page describes the format and how ExerPy checks it.
Structure¶
A file has four sections:
Section |
Content |
|---|---|
|
The components, grouped by their type, e.g. |
|
The streams between the components, stored under their names. |
|
The ambient temperature |
|
Optional: |
A compressor that takes in air and is driven by a motor outside the system looks like this:
{
"components": {
"Compressor": {
"COMP": {"name": "COMP"}
}
},
"connections": {
"1": {
"kind": "material",
"source_component": null,
"target_component": "COMP",
"target_connector": 0,
"m": 10.0, "T": 288.15, "p": 101300.0, "h": 15156.0, "e_PH": 0.0
},
"2": {
"kind": "material",
"source_component": "COMP",
"source_connector": 0,
"target_component": null,
"m": 10.0, "T": 655.0, "p": 1551000.0, "h": 400000.0, "e_PH": 362000.0
},
"W1": {
"kind": "power",
"source_component": null,
"target_component": "COMP",
"target_connector": 1,
"energy_flow": 3848440.0
}
},
"ambient_conditions": {"Tamb": 288.15, "pamb": 101300.0},
"settings": {"split_physical_exergy": false}
}
Components¶
Every component needs its name, which has to be the same as the key it is stored under.
Some types read further values:
Type |
Further values |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
All other values of a component, such as the efficiencies a simulator reports, are kept but not checked.
Connections¶
kind says what a connection carries: material, power, heat or other.
Every connection names its source_component and target_component. Use null where it
crosses the system boundary. source_connector and target_connector give the port of the
component, counted from 0.
Kind |
Required |
Optional |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
ExerPy computes the exergy of each connection itself, so values such as E in an exported
file are not needed.
The species of a mass_composition carry the names CoolProp knows, such as CH4, N2 or
Ethane. The chemical exergy of a mixture needs the molar mass of every species, so a species
CoolProp does not know stops the analysis with an error that names it.
Units¶
A value can be given in any unit. Write the unit next to it, under the same key with _unit
added:
{"T": 15.0, "T_unit": "degC", "p": 1.013, "p_unit": "bar", "m": 36.0, "m_unit": "t / h"}
ExerPy converts every such value to SI before the analysis. A value without a unit is taken as SI.
The units are read with pint, so the usual names and symbols
work, for example degC, bar, kJ / kg, kJ / kgK, t / h, kW, MW or
%. The spellings of older ExerPy files, such as J / kgK, m3 / s or -, are read as
well. A unit that ExerPy does not know, or a unit of the wrong kind such as a pressure in K,
stops the analysis with an error that names its place in the file.
These values are converted, wherever they appear in a component, its parameters or a
connection:
Value |
SI unit |
|---|---|
|
K |
|
Pa |
|
kg/s |
|
mol/s |
|
J/kg |
|
J/(kg K) |
|
W |
|
W/K |
|
m² |
|
m³/s |
|
dimensionless |
All other values keep the value and unit they have. Mass and molar fractions have no unit.
Exporting¶
export_to_json() writes every value with its unit, in SI
unless asked otherwise. It adds the exergy_results of every component and the
system_results of the plant. Pass units to write some values in other units:
ean.export_to_json("results.json", units={"T": "degC", "p": "bar", "E_D": "kW"})
from_json reads such a file back in SI.
After an exergoeconomic analysis, the export of
ExergoeconomicAnalysis writes the same file with the costs added:
eco = ExergoeconomicAnalysis(ean)
eco.run(costs)
eco.export_to_json("costs.json")
It holds the cost rates C_T, C_M, C_PH, C_CH and C_TOT of every stream in
currency/h, the specific costs c_T to c_TOT in currency/GJ, the exergoeconomic_results
of every component and the system_costs of the plant, again each with its unit.
Validation¶
from_json checks the file before the analysis starts. The models parsed with from_tespy,
from_ebsilon and from_aspen are checked in the same way. The check has two steps:
Structure: missing values, values of the wrong type, unknown component types, two streams entering the same port, missing
e_Tande_Mwhen the physical exergy is split, and units that are unknown or of the wrong kind.Values, once the structure is valid and the values are converted to SI: temperatures and pressures above zero, mass flows that are not negative, and mass and molar fractions between 0 and 1.
ExerPy reports all problems of a step together, each with its place in the file:
Invalid ExerPy input (2 error(s)):
- error: Expected a value >= 0, got -1.0 - at `$.connections['1'].m`
- error: Expected a value > 0, got -10.0 - at `$.connections['2'].T`
The error is an InputValidationError, a ValueError whose issues
attribute lists every problem. Some findings are only warnings and are logged, for example mass
fractions that do not add up to one or a connection that is not attached to any component. Pass
validate=False to skip the check.
A file can also be checked without running an analysis, in the same order as from_json:
import json
from exerpy.schema import validate_structure
from exerpy.schema import validate_values
from exerpy.units import to_SI
with open("my_model.json") as file:
data = json.load(file)
validate_structure(data)
data, _ = to_SI(data)
validate_values(data)
validate() runs both steps at once, for a file that is already in SI.
A custom component declares the values it reads in a nested Input class, see
Component. The check then covers it as well.
JSON Schema¶
json_schema() returns the format as a JSON Schema. Saved to a file, it lets an editor such as VS Code complete and
check a model while you write it. The schema describes the structure. The ranges of the values
are checked by ExerPy only.
import json
from exerpy.schema import json_schema
with open("exerpy_schema.json", "w") as file:
json.dump(json_schema(), file, indent=4)