Getting Started¶
This guide walks you through installing the KPI Calculator, running your first calculation, and understanding the results.
Installation¶
Install from PyPI:
pip install kpi-calculator
Basic Usage¶
The simplest way to calculate KPIs is from an ESDL file:
from kpicalculator import calculate_kpis
results = calculate_kpis(esdl_file="path/to/model.esdl")
print(f"Total CAPEX: {results['financials']['capex']['All']} EUR")
print(f"LCOE: {results['financials']['lcoe']} EUR/MWh")
Note
Validated by test_quick_start in unit_test/test_examples.py.
By default, the analysis uses a 30-year system lifetime with a 5% discount rate. Assets default to a 40-year technical lifetime when not specified in the ESDL. Both lifetime and discount rate can be overridden via KpiManager.calculate_all_kpis():
from kpicalculator import KpiManager
manager = KpiManager()
manager.load_from_esdl("model.esdl")
results = manager.calculate_all_kpis(
system_lifetime=25, # Overrides the default (30 years)
discount_rate=3, # Overrides the default (5 percent)
)
calculate_kpis() exposes system_lifetime but not discount_rate; use KpiManager directly when you need to override the discount rate.
Note
Validated by test_basic_usage_with_parameters in unit_test/test_examples.py.
Note
When comparing output against the MESIDO optimizer, pass round_up_replacement=False to calculate_all_kpis(). Use the default (True) for all other purposes. See the calculate_all_kpis docstring for details.
Providing Time Series¶
Without time series data, energy values (consumption, production, demand) are returned as zero. There is no rated-capacity fallback — you need to supply actual time series for meaningful energy and emission KPIs.
The package supports three time series sources (DataFrames, InfluxDB profiles, and XML files), but the default API exposes two. InfluxDB is available only when using the adapter layer directly; see the Architecture documentation for details.
XML files — pass a path to an XML time series file:
results = calculate_kpis(
esdl_file="model.esdl",
time_series="timeseries.xml"
)
Note
Validated by test_basic_usage_testing_override in unit_test/test_examples.py.
pandas DataFrames — pass a dictionary of DataFrames keyed by asset ID. Each DataFrame column
represents a time series field; the column name must match a field name recognised by the KPI
calculators (ThermalConsumption, ThermalProduction, ThermalDemand, Consumption,
Production, Demand, ElectricalConsumption, Energy, or heat_demand).
The simulator-specific column heat_supplied is also recognised on producer assets.
Unrecognised column names are stored but will not contribute to any KPI — a warning is logged
in that case:
import pandas as pd
timeseries_data = {
"asset_id_1": pd.DataFrame({
"ThermalConsumption": [100000, 102000, ...],
}, index=pd.date_range("2024-01-01", periods=24, freq="h")),
}
results = calculate_kpis(
esdl_file="model.esdl",
timeseries_dataframes=timeseries_data,
)
Note
Validated by test_timeseries_dataframes_produce_valid_kpi_results in unit_test/test_examples.py.
For implementation details on the composite key mapping, see the Architecture documentation.
From ESDL String Content¶
When the ESDL is already in memory (for example, received from an API or generated by another tool), use KpiManager directly instead of writing to a file:
from kpicalculator import KpiManager
manager = KpiManager()
manager.load_from_esdl_string(esdl_string)
results = manager.calculate_all_kpis()
Note
Validated by test_string_loaded_esdl_export in unit_test/test_examples.py.
Command Line¶
The calculator also works from the command line:
kpicalculator model.esdl
kpicalculator model.esdl --time-series data.xml --system-lifetime 25
Exporting Results to ESDL¶
Export calculated KPIs back into ESDL as DistributionKPI elements for visualization in the ESDL MapEditor.
Export to file:
from kpicalculator import KpiManager
manager = KpiManager()
manager.load_from_esdl("model.esdl", time_series_file="data.xml")
results = manager.calculate_all_kpis()
manager.export_to_esdl(results, output_file="model_with_kpis.esdl")
Get ESDL object (no file):
esdl_with_kpis = manager.build_esdl_with_kpis(results)
Get ESDL string (preferred for in-memory workflows):
When working with ESDL strings end-to-end (for example, in simulator-worker), use
build_esdl_string_with_kpis to embed KPIs into an existing ESDL string and get an
updated string back. This avoids temporary files and does not require manipulating
internal adapter state:
# manager and results from a previous load_from_esdl_string() call (see above)
esdl_string_with_kpis = manager.build_esdl_string_with_kpis(esdl_string, results)
Note
Both export patterns are validated by test_string_loaded_esdl_export in unit_test/test_examples.py.
All three export methods accept an optional level parameter ('system', 'area', or 'asset'). Currently all levels write system-wide KPIs to the main area — area-level and asset-level placement are not yet implemented.
Export works with both file-loaded and string-loaded ESDL — no temporary files needed for in-memory workflows.
From OMOTES Simulator Results¶
When integrating with the OMOTES simulator-worker, use load_from_simulator to load the
simulator’s port-indexed DataFrame together with the input ESDL string. The adapter resolves
port IDs to assets and extracts cost data in one step. Use build_esdl_string_with_kpis to
embed the results into the output ESDL string:
from kpicalculator import KpiManager
manager = KpiManager()
# input_esdl: the ESDL string submitted to the simulator (used to resolve port IDs and cost data)
# output_esdl: the ESDL string returned by the simulator (KPIs will be embedded into this)
manager.load_from_simulator(simulator_result_df, esdl_string=input_esdl)
results = manager.calculate_all_kpis(system_lifetime=30)
output_esdl_with_kpis = manager.build_esdl_string_with_kpis(output_esdl, results)
The simulator produces columns named heat_demand and heat_supplied. These are
recognised directly by the energy and emission calculators — no column renaming is needed.
Results Structure¶
calculate_kpis() returns a dictionary with four top-level keys:
{
"financials": {
"capex": {"All": 1000000, "Production": 500000, "Transport": 300000, ...},
"opex": {"All": 50000, "Production": 30000, ...},
"npv": 850000,
"lcoe": 45.5,
"eac": 62000,
"tco": 1400000,
},
"energy": {
"consumption": 1e10, # Joules
"production": 1.1e10,
"demand": 9.5e9,
"efficiency": 0.91,
},
"emissions": {
"total": 1200, # tonnes CO2e/year
"per_mwh": 178, # kg CO2e/MWh
},
"asset_financials": {
"<asset_id>": { ... }, # per-asset breakdown — see KPI Guide
...
},
}
Financials: Top-level category for all monetary KPIs. This includes cost breakdowns such as CAPEX and OPEX by asset category (Production, Transport, Storage, Conversion, Consumption, and All), as well as derived financial indicators (NPV, LCOE, EAC, TCO). All values are sums over assets.
Energy: System-wide totals in Joules. Efficiency is the ratio of consumption to production (0 to 1).
Emissions: Total CO2-equivalent emissions in tonnes and emissions intensity in kg CO2e per MWh of energy consumed.
Asset financials: Per-asset breakdown of all financial KPIs, keyed by asset ID. For field definitions and interpretation, see Per-asset Financial KPIs in the KPI Guide.
For a detailed explanation of what each KPI means and how to interpret it, see KPI Guide.
Time Series Behavior¶
The calculator supports three time series sources. The full priority order (when all sources are enabled) is:
pandas DataFrames — passed via
timeseries_dataframesInfluxDB profiles — referenced in the ESDL as
InfluxDBProfileelementsXML files — passed via the
time_seriesparameter
The first source that returns data wins; the rest are not tried. If a source fails with an error, the calculator logs a warning and moves to the next.
However, the default API (calculate_kpis()) sets use_database_profiles=False internally, which removes InfluxDB from the priority list entirely — it is not merely skipped, it is excluded. This reduces the effective order to:
pandas DataFrames
XML files
If both a DataFrame and an XML file are provided, the DataFrame source is tried first. If it returns data, XML is not tried.
Without time series from a working source, energy calculators return zero for consumption, production, and demand. There is no rated-capacity fallback (the power × 8760 estimation found in some energy system tools is not implemented here). Providing time series is required for meaningful energy and emission KPIs.
For the full implementation details — including the dynamic priority-building code and how to re-enable InfluxDB — see the Architecture documentation.