API Reference

This page documents the public API of the KPI Calculator. For usage examples, see Getting Started. For architecture details, see Architecture.

Public API

KPI Calculator package for energy systems.

The main entry points are calculate_kpis(), calculate_kpis_from_simulator(), and KpiManager. To embed results back into an ESDL string, use build_esdl_string_with_kpis(). All calculate_kpis_from_* functions return a KpiResults dict. The following types are exported for use in type annotations:

Default parameter values for callers that need to resolve absent config:

  • DEFAULT_SYSTEM_LIFETIME_YEARS — 30.0

  • DEFAULT_DISCOUNT_RATE_PERCENT — 5.0

class kpicalculator.Asset(id, name, asset_type, power=0.0, length=0.0, volume=0.0, cop=0.0, investment_cost=0.0, investment_cost_unit='EUR', installation_cost=0.0, installation_cost_unit='EUR', fixed_operational_cost=0.0, fixed_operational_cost_unit='EUR/yr', variable_operational_cost=0.0, variable_operational_cost_unit='EUR/MWh', fixed_maintenance_cost=0.0, fixed_maintenance_cost_unit='EUR/yr', variable_maintenance_cost=0.0, variable_maintenance_cost_unit='EUR/MWh', technical_lifetime=40.0, discount_rate=None, emission_factor=0.0, aggregation_count=1, time_series=<factory>)[source]

Bases: object

aggregation_count: int = 1
asset_type: AssetType
cop: float = 0.0
discount_rate: float | None = None
emission_factor: float = 0.0
fixed_maintenance_cost: float = 0.0
fixed_maintenance_cost_unit: str = 'EUR/yr'
fixed_operational_cost: float = 0.0
fixed_operational_cost_unit: str = 'EUR/yr'
id: str
installation_cost: float = 0.0
installation_cost_unit: str = 'EUR'
investment_cost: float = 0.0
investment_cost_unit: str = 'EUR'
length: float = 0.0
name: str
power: float = 0.0
technical_lifetime: float = 40.0
time_series: dict[str, TimeSeries]
variable_maintenance_cost: float = 0.0
variable_maintenance_cost_unit: str = 'EUR/MWh'
variable_operational_cost: float = 0.0
variable_operational_cost_unit: str = 'EUR/MWh'
volume: float = 0.0
class kpicalculator.AssetFinancialResult[source]

Bases: TypedDict

Per-asset financial KPIs returned in KpiResults["asset_financials"].

System totals in KpiResults["financials"] are derived by summing the corresponding field across all assets (except lcoe — see below).

Per-asset discount rate: all discounted KPIs (annualized_capex, eac, npv, and lcoe) use the discount rate from costInformation.discountRate in the ESDL when present; otherwise the system-level discount_rate parameter is used as a fallback.

Geothermal COP adjustment: variable operational and maintenance costs in EUR/kWh or EUR/MWh are applied to energy / COP for geothermal assets with COP > 0, reflecting that they deliver more heat than they consume as input.

annualized_capex: float

CAPEX spread over the asset’s technical lifetime via the annuity formula, in EUR/year. At discount_rate = 0% this reduces to (investment + installation) / technical_lifetime.

eac: float

annualized_capex + total annual OPEX, in EUR/year.

Type:

Equivalent Annual Cost

fixed_maintenance_cost: float

Annual fixed maintenance cost in EUR/year.

fixed_operational_cost: float

Annual fixed operational cost in EUR/year.

installation_cost: float

Installation cost in EUR.

investment_cost: float

Upfront capital cost in EUR.

lcoe: float | None

Levelized Cost of Energy in EUR/MWh for this asset, or None.

None when:

  • the asset is not a generating type (consumers, transport, storage, conversion), or

  • the asset is a generating type but its annual energy production is zero or unknown (no time series data supplied).

None means not applicable or not computable — exporters should omit the field or write a format-appropriate null. The system LCOE is computed separately as total_npv / total_discounted_energy and is not the average of per-asset LCOEs.

npv: float

Discounted lifecycle cost of this asset in EUR.

tco: float

Undiscounted total spend on this asset over the system lifetime, in EUR.

variable_maintenance_cost: float

Annual variable maintenance cost in EUR/year (scales with energy use).

variable_operational_cost: float

Annual variable operational cost in EUR/year (scales with energy use).

class kpicalculator.AssetType(value)[source]

Bases: Enum

CONSUMER = 'Consumer'
CONVERSION = 'Conversion'
GEOTHERMAL = 'GeothermalSource'
PIPE = 'Pipe'
PRODUCER = 'Producer'
PUMP = 'Pump'
STORAGE = 'Storage'
TRANSPORT = 'Transport'
exception kpicalculator.CalculationError[source]

Bases: KpiCalculatorError

Raised when KPI calculation fails.

class kpicalculator.ConfigFileCredentialManager(config_path=None)[source]

Bases: CredentialManager

Load credentials from secure configuration files.

get_database_credentials(host, port)[source]

Get credentials from config file.

Parameters:
  • host (str) – Database host

  • port (int) – Database port

Return type:

DatabaseCredentials | None

Returns:

DatabaseCredentials if found in config, None otherwise

supports_environment_credentials()[source]

Return False since this manager reads from config files, not environment.

Return type:

bool

exception kpicalculator.CredentialError[source]

Bases: SecurityError

Raised when credential loading or validation fails.

exception kpicalculator.DataSourceError[source]

Bases: KpiCalculatorError

Raised when data source loading fails.

exception kpicalculator.DatabaseError[source]

Bases: DataSourceError

Raised when database operations fail.

class kpicalculator.EmissionResults[source]

Bases: TypedDict

System-level emission KPI results.

per_mwh: float

Emission intensity in kg CO2e/MWh of energy consumed.

total: float

Total greenhouse gas emissions in tonnes CO2e/year.

class kpicalculator.EnergyResults[source]

Bases: TypedDict

System-level energy KPI results. All values in Joules.

consumption: float

Total thermal energy consumed by all consumer assets in J.

demand: float

Total thermal energy demand from all consumer assets in J.

efficiency: float

consumption / production (0-1). Zero when production is zero.

Type:

Distribution efficiency

production: float

Total thermal energy produced by all producer assets in J.

class kpicalculator.EnergySystem(name, assets, unit_conversion=<factory>, source_metadata=<factory>, esdl_energy_system=None)[source]

Bases: object

assets: list[Asset]
esdl_energy_system: EnergySystem | None = None
name: str
source_metadata: dict[str, str]
unit_conversion: dict[str, float]
class kpicalculator.FinancialResults[source]

Bases: TypedDict

System-level financial KPI results.

All monetary values are in EUR or EUR/year. Each field is the sum of the corresponding per-asset value across all assets in the system (except lcoe, which is computed as sum(per-asset NPVs) / total_discounted_energy and is therefore not the average of per-asset LCOEs).

capex and opex are broken down by asset category: "Production", "Consumption", "Storage", "Transport", "Conversion", and "All" (the system-wide sum).

capex: dict[str, float]

CAPEX by asset category in EUR (investment + installation costs).

eac: float

Equivalent Annual Cost — sum of per-asset annualized costs in EUR/year.

lcoe: float

Levelized Cost of Energy in EUR/MWh (system_npv / discounted_energy).

system_npv is the sum of per-asset NPVs, each discounted at the asset’s own rate (from ESDL costInformation.discountRate, falling back to the system default). It is not the average of per-asset LCOEs.

npv: float

Net Present Value — discounted lifecycle cost in EUR.

opex: dict[str, float]

Annual OPEX by asset category in EUR/year (fixed + variable costs).

tco: float

Total Cost of Ownership — undiscounted lifecycle cost in EUR.

exception kpicalculator.KpiCalculatorError[source]

Bases: Exception

Base exception for KPI Calculator.

class kpicalculator.KpiManager[source]

Bases: object

Main class for managing KPI calculations across different data sources.

Cost unit conversion factors (EUR/kW, EUR/MW, EUR/km, EUR/kWh, EUR/MWh, % OF CAPEX, etc.) are built-in and used by the cost calculator when computing KPI values from ESDL costInformation elements.

build_esdl_string_with_kpis(esdl_string, results, level='system')[source]

Embed KPI results into an ESDL XML string and return the updated string.

This is the preferred integration method for systems that work with ESDL strings (e.g. simulator-worker). It operates entirely on a local EnergySystemHandler parsed from esdl_string and does not modify any manager state, making it safe to call repeatedly on the same instance.

Parameters:
  • esdl_string (str) – Input ESDL XML string to embed KPIs into.

  • results (KpiResults) – KPI calculation results from calculate_all_kpis()

  • level (str) – KPI granularity — '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.

Return type:

str

Returns:

ESDL XML string with KPIs embedded.

Raises:

ValueError – If esdl_string is empty or invalid parameters are provided.

build_esdl_with_kpis(results, level='system')[source]

Build an ESDL energy system data structure with KPI results embedded.

Parameters:
  • results (KpiResults) – KPI calculation results from calculate_all_kpis()

  • level (str) – KPI granularity — '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.

Returns:

ESDL data structure with KPIs

Return type:

esdl.EnergySystem

Raises:

ValueError – If no energy system is loaded or invalid parameters

calculate_all_kpis(system_lifetime=30.0, discount_rate=5.0, round_up_replacement=True)[source]

Calculate all KPIs for the energy system.

Raises:

ValueError – If no energy system is loaded, system_lifetime <= 0, or discount_rate is outside [0, 100].

Parameters:
  • system_lifetime (float) – System lifetime in years. Must be positive. Default: DEFAULT_SYSTEM_LIFETIME_YEARS.

  • discount_rate (float) – System-wide fallback discount rate in percentage (e.g. 5 for 5%). Must be in [0, 100]. Individual assets may override this via costInformation.discountRate in the ESDL — this method respects those overrides because it uses get_asset_financial_breakdown() internally. Note that calling FinancialCalculator.calculate_npv() directly does not respect per-asset overrides. Default: DEFAULT_DISCOUNT_RATE_PERCENT.

  • round_up_replacement (bool) – If True (default), NPV, LCOE, and TCO use ceil for the asset replacement count — the financially exact calculation. If False, uses the continuous factor max(1, system_lifetime / technical_lifetime) for compatibility with MESIDO optimizer output. Only set this to False when comparing results against MESIDO.

Return type:

KpiResults

Returns:

KpiResults dict with financials, energy, emissions, and asset_financials keys.

energy_system: EnergySystem | None
export_to_esdl(results, output_file=None, level='system')[source]

Export KPI results to ESDL format.

Parameters:
  • results (KpiResults) – KPI calculation results from calculate_all_kpis()

  • output_file (str | None) – Output ESDL file path. If None, returns data structure.

  • level (str) – KPI granularity — '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.

Returns:

True if file export succeeded (when output_file provided) esdl.EnergySystem: ESDL data structure (when output_file is None)

Return type:

bool

Raises:

ValueError – If no energy system is loaded or invalid parameters

load_from_esdl(esdl_file, time_series_file=None, timeseries_dataframes=None)[source]

Load energy system data from ESDL file.

Cost data is extracted from ESDL costInformation elements.

Note

InfluxDB profile loading is disabled here. To enable it, call EsdlAdapter().load_data(..., use_database_profiles=True) directly.

Parameters:
  • esdl_file (str) – Path to ESDL file

  • time_series_file (str | None) – Optional path to time series file (when timeseries_dataframes not provided)

  • timeseries_dataframes (dict[str, DataFrame] | None) – Optional dict mapping asset IDs to pandas DataFrames with time-indexed energy/power data. When provided, takes precedence over database loading and time_series_file.

Return type:

None

load_from_esdl_string(esdl_string, timeseries_dataframes=None)[source]

Load energy system data from ESDL XML string content.

This method allows loading ESDL data directly from a string without needing a temporary file. Useful for integration with systems that provide ESDL content in memory (e.g., simulator_worker).

Cost data is extracted from ESDL costInformation elements.

Parameters:
  • esdl_string (str) – ESDL XML content as a string

  • timeseries_dataframes (dict[str, DataFrame] | None) – Optional dict mapping asset IDs to pandas DataFrames with time-indexed energy/power data.

Return type:

None

load_from_mesido(_mesido_data)[source]

Load energy system data from mesido data structure.

Parameters:

_mesido_data (Any) – Mesido data structure (unused, placeholder for future implementation)

Return type:

None

load_from_simulator(simulator_result, esdl_string)[source]

Load energy system data from OMOTES Simulator results.

Converts the simulator’s port-indexed DataFrame to the asset-indexed common model and extracts cost data from the supplied ESDL string.

Parameters:
  • simulator_result (DataFrame) – DataFrame produced by the simulator, with a DatetimeIndex and (port_id, property_name) tuple columns.

  • esdl_string (str) – The input ESDL as an XML string, used to resolve port IDs to their owning assets and to extract cost data.

Return type:

None

source_esdl_file: str | None
class kpicalculator.KpiResults[source]

Bases: TypedDict

Complete KPI results returned by KpiManager.calculate_all_kpis() and the top-level kpicalculator.calculate_kpis() function.

Four top-level keys:

  • financials: system-level monetary KPIs (CAPEX, OPEX, NPV, LCOE, EAC, TCO)

  • energy: system-level energy totals in Joules

  • emissions: system-level CO2e emissions

  • asset_financials: per-asset financial breakdown keyed by asset ID; system totals in financials are derived by summing these values

asset_financials: dict[str, AssetFinancialResult]
emissions: EmissionResults
energy: EnergyResults
financials: FinancialResults
class kpicalculator.SecureCredentialManager[source]

Bases: CredentialManager

Secure credential management using environment variables.

get_database_credentials(host, port)[source]

Load credentials from environment variables.

This method first attempts to load credentials using environment variables with the format KPI_DB_{HOST}_{PORT}_{FIELD}, where HOST is uppercased and dots/hyphens are replaced with underscores. If either the username or password is missing, it falls back to using INFLUXDB_* environment variables for compatibility with simulator-worker setups. The priority order is: 1. KPI_DB_{HOST}_{PORT}_{FIELD} (primary) 2. INFLUXDB_* (fallback if primary is incomplete)

Parameters:
  • host (str) – Database host (e.g., “wu-profiles.esdl-beta.hesi.energy”)

  • port (int) – Database port (e.g., 443)

Return type:

DatabaseCredentials | None

Returns:

DatabaseCredentials if environment variables are set, None otherwise

supports_environment_credentials()[source]

Return True since this manager reads from environment variables.

Return type:

bool

exception kpicalculator.SecurityError[source]

Bases: KpiCalculatorError

Raised when security validation fails.

class kpicalculator.TimeSeries(time_step, values)[source]

Bases: object

time_step: float
values: list[float]
exception kpicalculator.ValidationError[source]

Bases: KpiCalculatorError

Raised when input validation fails.

kpicalculator.build_esdl_string_with_kpis(esdl_string, kpi_results)[source]

Embed KPI results into an ESDL XML string and return the updated string.

Data-source-agnostic: works with results from any calculate_kpis_from_* function. The KPI values are written as DistributionKPI elements on the main area of the energy system, ready for MapEditor visualisation.

This is the standalone equivalent of KpiManager.build_esdl_string_with_kpis() — use this when you do not hold a KpiManager instance.

Parameters:
  • esdl_string (str) – ESDL XML string to embed KPIs into.

  • kpi_results (KpiResults) – KPI results from any calculate_kpis_from_* call.

Return type:

str

Returns:

Updated ESDL XML string with KPIs embedded.

Raises:

KpiCalculatorError – If embedding fails.

kpicalculator.calculate_kpis(esdl_file, time_series=None, timeseries_dataframes=None, system_lifetime=30.0, discount_rate=5.0, round_up_replacement=True)[source]

Calculate KPIs from an ESDL file with optional time series data.

Cost data is extracted from ESDL costInformation elements. Cost unit conversion factors (EUR/kW, EUR/MW, etc.) are built-in; see kpicalculator.common.constants.COST_UNIT_FACTORS for the full list.

Parameters:
  • esdl_file (str | Path) – Path to ESDL file.

  • time_series (str | Path | None) – Optional path to time series XML (when timeseries_dataframes not provided).

  • timeseries_dataframes (dict[str, DataFrame] | None) – Optional dict mapping asset IDs to pandas DataFrames with time-indexed energy/power data. Takes precedence over time_series when provided.

  • system_lifetime (float) – System lifetime in years. Default: DEFAULT_SYSTEM_LIFETIME_YEARS.

  • discount_rate (float) – System-wide fallback discount rate in percent. Default: DEFAULT_DISCOUNT_RATE_PERCENT.

  • round_up_replacement (bool) – If True (default), NPV, LCOE, and TCO use ceil for the asset replacement count (financially exact). If False, uses the continuous factor max(1, n / technical_lifetime) for optimizer compatibility.

Return type:

KpiResults

Returns:

KpiResults containing calculated KPIs.

Raises:

KpiCalculatorError – For any calculation or validation errors.

kpicalculator.calculate_kpis_from_simulator(simulator_result, esdl_string, system_lifetime=30.0, discount_rate=5.0, round_up_replacement=True)[source]

Calculate KPIs from OMOTES simulator results.

Parameters:
  • simulator_result (DataFrame) – DataFrame produced by the simulator, with a DatetimeIndex and (port_id, property_name) tuple columns.

  • esdl_string (str) – The input ESDL as an XML string, used to resolve port IDs to their owning assets and to extract cost data.

  • system_lifetime (float) – System lifetime in years. Default: DEFAULT_SYSTEM_LIFETIME_YEARS.

  • discount_rate (float) – System-wide fallback discount rate in percent. Default: DEFAULT_DISCOUNT_RATE_PERCENT.

  • round_up_replacement (bool) – If True (default), NPV, LCOE, and TCO use ceil for the asset replacement count (financially exact). If False, uses the continuous factor max(1, n / technical_lifetime) for optimizer compatibility.

Return type:

KpiResults

Returns:

KpiResults containing calculated KPIs.

Raises:

KpiCalculatorError – For any calculation or validation errors.

kpicalculator.calculate_kpis(esdl_file, time_series=None, timeseries_dataframes=None, system_lifetime=30.0, discount_rate=5.0, round_up_replacement=True)[source]

Calculate KPIs from an ESDL file with optional time series data.

Cost data is extracted from ESDL costInformation elements. Cost unit conversion factors (EUR/kW, EUR/MW, etc.) are built-in; see kpicalculator.common.constants.COST_UNIT_FACTORS for the full list.

Parameters:
  • esdl_file (str | Path) – Path to ESDL file.

  • time_series (str | Path | None) – Optional path to time series XML (when timeseries_dataframes not provided).

  • timeseries_dataframes (dict[str, DataFrame] | None) – Optional dict mapping asset IDs to pandas DataFrames with time-indexed energy/power data. Takes precedence over time_series when provided.

  • system_lifetime (float) – System lifetime in years. Default: DEFAULT_SYSTEM_LIFETIME_YEARS.

  • discount_rate (float) – System-wide fallback discount rate in percent. Default: DEFAULT_DISCOUNT_RATE_PERCENT.

  • round_up_replacement (bool) – If True (default), NPV, LCOE, and TCO use ceil for the asset replacement count (financially exact). If False, uses the continuous factor max(1, n / technical_lifetime) for optimizer compatibility.

Return type:

KpiResults

Returns:

KpiResults containing calculated KPIs.

Raises:

KpiCalculatorError – For any calculation or validation errors.

class kpicalculator.KpiManager[source]

Bases: object

Main class for managing KPI calculations across different data sources.

Cost unit conversion factors (EUR/kW, EUR/MW, EUR/km, EUR/kWh, EUR/MWh, % OF CAPEX, etc.) are built-in and used by the cost calculator when computing KPI values from ESDL costInformation elements.

build_esdl_string_with_kpis(esdl_string, results, level='system')[source]

Embed KPI results into an ESDL XML string and return the updated string.

This is the preferred integration method for systems that work with ESDL strings (e.g. simulator-worker). It operates entirely on a local EnergySystemHandler parsed from esdl_string and does not modify any manager state, making it safe to call repeatedly on the same instance.

Parameters:
  • esdl_string (str) – Input ESDL XML string to embed KPIs into.

  • results (KpiResults) – KPI calculation results from calculate_all_kpis()

  • level (str) – KPI granularity — '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.

Return type:

str

Returns:

ESDL XML string with KPIs embedded.

Raises:

ValueError – If esdl_string is empty or invalid parameters are provided.

build_esdl_with_kpis(results, level='system')[source]

Build an ESDL energy system data structure with KPI results embedded.

Parameters:
  • results (KpiResults) – KPI calculation results from calculate_all_kpis()

  • level (str) – KPI granularity — '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.

Returns:

ESDL data structure with KPIs

Return type:

esdl.EnergySystem

Raises:

ValueError – If no energy system is loaded or invalid parameters

calculate_all_kpis(system_lifetime=30.0, discount_rate=5.0, round_up_replacement=True)[source]

Calculate all KPIs for the energy system.

Raises:

ValueError – If no energy system is loaded, system_lifetime <= 0, or discount_rate is outside [0, 100].

Parameters:
  • system_lifetime (float) – System lifetime in years. Must be positive. Default: DEFAULT_SYSTEM_LIFETIME_YEARS.

  • discount_rate (float) – System-wide fallback discount rate in percentage (e.g. 5 for 5%). Must be in [0, 100]. Individual assets may override this via costInformation.discountRate in the ESDL — this method respects those overrides because it uses get_asset_financial_breakdown() internally. Note that calling FinancialCalculator.calculate_npv() directly does not respect per-asset overrides. Default: DEFAULT_DISCOUNT_RATE_PERCENT.

  • round_up_replacement (bool) – If True (default), NPV, LCOE, and TCO use ceil for the asset replacement count — the financially exact calculation. If False, uses the continuous factor max(1, system_lifetime / technical_lifetime) for compatibility with MESIDO optimizer output. Only set this to False when comparing results against MESIDO.

Return type:

KpiResults

Returns:

KpiResults dict with financials, energy, emissions, and asset_financials keys.

energy_system: EnergySystem | None
export_to_esdl(results, output_file=None, level='system')[source]

Export KPI results to ESDL format.

Parameters:
  • results (KpiResults) – KPI calculation results from calculate_all_kpis()

  • output_file (str | None) – Output ESDL file path. If None, returns data structure.

  • level (str) – KPI granularity — '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.

Returns:

True if file export succeeded (when output_file provided) esdl.EnergySystem: ESDL data structure (when output_file is None)

Return type:

bool

Raises:

ValueError – If no energy system is loaded or invalid parameters

load_from_esdl(esdl_file, time_series_file=None, timeseries_dataframes=None)[source]

Load energy system data from ESDL file.

Cost data is extracted from ESDL costInformation elements.

Note

InfluxDB profile loading is disabled here. To enable it, call EsdlAdapter().load_data(..., use_database_profiles=True) directly.

Parameters:
  • esdl_file (str) – Path to ESDL file

  • time_series_file (str | None) – Optional path to time series file (when timeseries_dataframes not provided)

  • timeseries_dataframes (dict[str, DataFrame] | None) – Optional dict mapping asset IDs to pandas DataFrames with time-indexed energy/power data. When provided, takes precedence over database loading and time_series_file.

Return type:

None

load_from_esdl_string(esdl_string, timeseries_dataframes=None)[source]

Load energy system data from ESDL XML string content.

This method allows loading ESDL data directly from a string without needing a temporary file. Useful for integration with systems that provide ESDL content in memory (e.g., simulator_worker).

Cost data is extracted from ESDL costInformation elements.

Parameters:
  • esdl_string (str) – ESDL XML content as a string

  • timeseries_dataframes (dict[str, DataFrame] | None) – Optional dict mapping asset IDs to pandas DataFrames with time-indexed energy/power data.

Return type:

None

load_from_mesido(_mesido_data)[source]

Load energy system data from mesido data structure.

Parameters:

_mesido_data (Any) – Mesido data structure (unused, placeholder for future implementation)

Return type:

None

load_from_simulator(simulator_result, esdl_string)[source]

Load energy system data from OMOTES Simulator results.

Converts the simulator’s port-indexed DataFrame to the asset-indexed common model and extracts cost data from the supplied ESDL string.

Parameters:
  • simulator_result (DataFrame) – DataFrame produced by the simulator, with a DatetimeIndex and (port_id, property_name) tuple columns.

  • esdl_string (str) – The input ESDL as an XML string, used to resolve port IDs to their owning assets and to extract cost data.

Return type:

None

source_esdl_file: str | None

Result Types

class kpicalculator.KpiResults[source]

Bases: TypedDict

Complete KPI results returned by KpiManager.calculate_all_kpis() and the top-level kpicalculator.calculate_kpis() function.

Four top-level keys:

  • financials: system-level monetary KPIs (CAPEX, OPEX, NPV, LCOE, EAC, TCO)

  • energy: system-level energy totals in Joules

  • emissions: system-level CO2e emissions

  • asset_financials: per-asset financial breakdown keyed by asset ID; system totals in financials are derived by summing these values

asset_financials: dict[str, AssetFinancialResult]
emissions: EmissionResults
energy: EnergyResults
financials: FinancialResults
class kpicalculator.FinancialResults[source]

Bases: TypedDict

System-level financial KPI results.

All monetary values are in EUR or EUR/year. Each field is the sum of the corresponding per-asset value across all assets in the system (except lcoe, which is computed as sum(per-asset NPVs) / total_discounted_energy and is therefore not the average of per-asset LCOEs).

capex and opex are broken down by asset category: "Production", "Consumption", "Storage", "Transport", "Conversion", and "All" (the system-wide sum).

capex: dict[str, float]

CAPEX by asset category in EUR (investment + installation costs).

eac: float

Equivalent Annual Cost — sum of per-asset annualized costs in EUR/year.

lcoe: float

Levelized Cost of Energy in EUR/MWh (system_npv / discounted_energy).

system_npv is the sum of per-asset NPVs, each discounted at the asset’s own rate (from ESDL costInformation.discountRate, falling back to the system default). It is not the average of per-asset LCOEs.

npv: float

Net Present Value — discounted lifecycle cost in EUR.

opex: dict[str, float]

Annual OPEX by asset category in EUR/year (fixed + variable costs).

tco: float

Total Cost of Ownership — undiscounted lifecycle cost in EUR.

class kpicalculator.EnergyResults[source]

Bases: TypedDict

System-level energy KPI results. All values in Joules.

consumption: float

Total thermal energy consumed by all consumer assets in J.

demand: float

Total thermal energy demand from all consumer assets in J.

efficiency: float

consumption / production (0-1). Zero when production is zero.

Type:

Distribution efficiency

production: float

Total thermal energy produced by all producer assets in J.

class kpicalculator.EmissionResults[source]

Bases: TypedDict

System-level emission KPI results.

per_mwh: float

Emission intensity in kg CO2e/MWh of energy consumed.

total: float

Total greenhouse gas emissions in tonnes CO2e/year.

class kpicalculator.AssetFinancialResult[source]

Bases: TypedDict

Per-asset financial KPIs returned in KpiResults["asset_financials"].

System totals in KpiResults["financials"] are derived by summing the corresponding field across all assets (except lcoe — see below).

Per-asset discount rate: all discounted KPIs (annualized_capex, eac, npv, and lcoe) use the discount rate from costInformation.discountRate in the ESDL when present; otherwise the system-level discount_rate parameter is used as a fallback.

Geothermal COP adjustment: variable operational and maintenance costs in EUR/kWh or EUR/MWh are applied to energy / COP for geothermal assets with COP > 0, reflecting that they deliver more heat than they consume as input.

annualized_capex: float

CAPEX spread over the asset’s technical lifetime via the annuity formula, in EUR/year. At discount_rate = 0% this reduces to (investment + installation) / technical_lifetime.

eac: float

annualized_capex + total annual OPEX, in EUR/year.

Type:

Equivalent Annual Cost

fixed_maintenance_cost: float

Annual fixed maintenance cost in EUR/year.

fixed_operational_cost: float

Annual fixed operational cost in EUR/year.

installation_cost: float

Installation cost in EUR.

investment_cost: float

Upfront capital cost in EUR.

lcoe: float | None

Levelized Cost of Energy in EUR/MWh for this asset, or None.

None when:

  • the asset is not a generating type (consumers, transport, storage, conversion), or

  • the asset is a generating type but its annual energy production is zero or unknown (no time series data supplied).

None means not applicable or not computable — exporters should omit the field or write a format-appropriate null. The system LCOE is computed separately as total_npv / total_discounted_energy and is not the average of per-asset LCOEs.

npv: float

Discounted lifecycle cost of this asset in EUR.

tco: float

Undiscounted total spend on this asset over the system lifetime, in EUR.

variable_maintenance_cost: float

Annual variable maintenance cost in EUR/year (scales with energy use).

variable_operational_cost: float

Annual variable operational cost in EUR/year (scales with energy use).

Calculators

class kpicalculator.calculators.financial_calculator.AssetFinancialResult[source]

Bases: TypedDict

Per-asset financial KPIs returned in KpiResults["asset_financials"].

System totals in KpiResults["financials"] are derived by summing the corresponding field across all assets (except lcoe — see below).

Per-asset discount rate: all discounted KPIs (annualized_capex, eac, npv, and lcoe) use the discount rate from costInformation.discountRate in the ESDL when present; otherwise the system-level discount_rate parameter is used as a fallback.

Geothermal COP adjustment: variable operational and maintenance costs in EUR/kWh or EUR/MWh are applied to energy / COP for geothermal assets with COP > 0, reflecting that they deliver more heat than they consume as input.

annualized_capex: float

CAPEX spread over the asset’s technical lifetime via the annuity formula, in EUR/year. At discount_rate = 0% this reduces to (investment + installation) / technical_lifetime.

eac: float

annualized_capex + total annual OPEX, in EUR/year.

Type:

Equivalent Annual Cost

fixed_maintenance_cost: float

Annual fixed maintenance cost in EUR/year.

fixed_operational_cost: float

Annual fixed operational cost in EUR/year.

installation_cost: float

Installation cost in EUR.

investment_cost: float

Upfront capital cost in EUR.

lcoe: float | None

Levelized Cost of Energy in EUR/MWh for this asset, or None.

None when:

  • the asset is not a generating type (consumers, transport, storage, conversion), or

  • the asset is a generating type but its annual energy production is zero or unknown (no time series data supplied).

None means not applicable or not computable — exporters should omit the field or write a format-appropriate null. The system LCOE is computed separately as total_npv / total_discounted_energy and is not the average of per-asset LCOEs.

npv: float

Discounted lifecycle cost of this asset in EUR.

tco: float

Undiscounted total spend on this asset over the system lifetime, in EUR.

variable_maintenance_cost: float

Annual variable maintenance cost in EUR/year (scales with energy use).

variable_operational_cost: float

Annual variable operational cost in EUR/year (scales with energy use).

class kpicalculator.calculators.financial_calculator.FinancialCalculator(energy_system)[source]

Bases: object

Calculator for financial KPIs (CAPEX, OPEX, NPV, LCOE, EAC, TCO).

aggregate_by_category(asset_financials)[source]

Derive CAPEX and OPEX category breakdowns from a pre-computed asset breakdown.

Parameters:

asset_financials (dict[str, AssetFinancialResult]) – Dict mapping asset ID to AssetFinancialResult, as returned by get_asset_financial_breakdown().

Return type:

tuple[dict[str, float], dict[str, float]]

Returns:

Tuple of (capex_by_category, opex_by_category), each a dict with keys "Production", "Consumption", "Storage", "Transport", "Conversion", and "All".

calculate_eac(discount_rate=5.0)[source]

Calculate Equivalent Annual Cost for the energy system.

Sums per-asset annualized costs using each asset’s own technical_lifetime and discount_rate (from ESDL costInformation.discountRate, falling back to the discount_rate parameter). The annuity formula spreads one asset purchase over its technical lifetime, implicitly assuming perpetual replacement — the annual charge is the same regardless of how many replacements occur within the system lifetime. OPEX is already annual and is passed through directly.

This matches the approach used in the MESIDO optimizer (calculate_annuity_factor in financial_mixin.py). See kpi_guide.rst for the formula and a discussion of the replacement assumption.

Raises:

CalculationError – If discount_rate is outside [0, 100] or any asset has a non-positive technical_lifetime.

Parameters:

discount_rate (float) – Fallback discount rate in percentage (e.g. 5 for 5%), used when asset.discount_rate is None (i.e. no costInformation.discountRate was present in the ESDL for that asset).

Return type:

float

Returns:

Equivalent Annual Cost in EUR/year.

calculate_lcoe(system_lifetime, discount_rate=5.0, round_up_replacement=True, system_npv=None)[source]

Calculate Levelized Cost of Energy.

Divides NPV by discounted energy to put costs and energy on the same present-value basis. Energy discounting uses the same end-of-period convention and fractional-year proration as NPV OPEX. See kpi_guide.rst for the formula.

Returns 0.0 if annual energy consumption is zero or negative.

When system_npv is not provided, it is derived by summing per-asset NPVs from get_asset_financial_breakdown(), which respects per-asset discount rates from ESDL costInformation.discountRate. This differs from calculate_npv(), which applies a uniform rate to all assets.

Raises:

CalculationError – If system_lifetime <= 0, discount_rate is outside [0, 100], or any asset has a non-positive technical_lifetime.

Parameters:
  • system_lifetime (float) – System lifetime in years. May be fractional.

  • discount_rate (float) – System-wide fallback discount rate in percentage (e.g. 5 for 5%). Per-asset overrides from ESDL are applied automatically.

  • round_up_replacement (bool) – If True (default), use ceil for the replacement count. If False, use the continuous factor for optimizer compatibility.

  • system_npv (float | None) – Pre-computed system NPV in EUR. When provided, skips the internal asset iteration. Pass this from KpiManager.calculate_all_kpis() to avoid a redundant computation.

Return type:

float

Returns:

Levelized Cost of Energy in EUR/MWh.

calculate_npv(system_lifetime, discount_rate=5.0, round_up_replacement=True)[source]

Calculate Net Present Value for the energy system.

By default (round_up_replacement=True) CAPEX uses a start-of-period convention: one discounted payment per replacement cycle, with the number of replacements equal to ceil(system_lifetime / technical_lifetime). OPEX uses the standard end-of-period convention (t = 1 n). A fractional final year is prorated linearly. See kpi_guide.rst for the full formula.

Set round_up_replacement=False to use the continuous replacement factor max(1, system_lifetime / technical_lifetime) as a scalar multiplier on a single undiscounted CAPEX — the approximation used by optimizers such as MESIDO.

Raises:

CalculationError – If system_lifetime <= 0, discount_rate is outside [0, 100], or any asset has a non-positive technical_lifetime.

Note

This method applies a single uniform discount_rate to all assets and does not respect per-asset discount rates from ESDL costInformation.discountRate. It is not used in the main calculation path — KpiManager.calculate_all_kpis() derives system NPV by summing per-asset NPVs from get_asset_financial_breakdown(), and calculate_lcoe() does the same when no system_npv is supplied. Use this method only when a quick uniform-rate NPV estimate is needed without the full per-asset breakdown.

Parameters:
  • system_lifetime (float) – System lifetime in years. May be fractional.

  • discount_rate (float) – Discount rate in percentage (e.g. 5 for 5%). Applied uniformly to all assets — per-asset overrides are not supported here.

  • round_up_replacement (bool) – If True (default), use ceil for the replacement count with per-replacement discounting. If False, use the continuous factor max(1, n / technical_lifetime) for optimizer compatibility.

Return type:

float

Returns:

Net Present Value in EUR.

calculate_tco(system_lifetime, round_up_replacement=True)[source]

Calculate Total Cost of Ownership for the energy system.

Undiscounted sum of all costs over the system lifetime:

TCO = Sum over assets of:
    (investment + installation) * replacement_factor
    + annual_opex * system_lifetime

By default (round_up_replacement=True) the replacement factor is ceil(system_lifetime / technical_lifetime) — the financially exact count of full asset purchases needed to keep the system operational. This is consistent with calculate_npv(), which uses the same ceil logic for CAPEX discounting, so TCO == NPV at discount_rate=0.

Set round_up_replacement=False to use the continuous factor max(1, system_lifetime / technical_lifetime) instead. Optimizers such as MESIDO use this approximation to keep the objective smooth and differentiable. Use this option only when comparing KPI output against optimizer results.

Note: MESIDO’s MinimizeTCO covers variable and fixed operational costs only. This calculator also includes fixed and variable maintenance costs, so TCO values will differ from MESIDO when maintenance costs are non-zero.

Raises:

CalculationError – If system_lifetime <= 0 or any asset has a non-positive technical_lifetime.

Parameters:
  • system_lifetime (float) – System lifetime in years.

  • round_up_replacement (bool) – If True (default), use ceil for the replacement count (financially exact). If False, use the continuous factor max(1, n / technical_lifetime) for optimizer compatibility.

Return type:

float

Returns:

Total Cost of Ownership in EUR.

get_asset_financial_breakdown(system_lifetime, discount_rate=5.0, round_up_replacement=True, annual_energy_mwh_by_asset=None)[source]

Compute per-asset financial KPIs.

Returns a dict keyed by asset.id. System totals for NPV, TCO, EAC are the sum of these values across all assets.

lcoe semantics:

  • None — asset is not a generating type (consumer, storage, transport, conversion), or is a generating asset whose annual energy production is zero or unknown. None means not applicable or not computable, regardless of the output format (ESDL, JSON, or any future schema). Exporters omit the field or write a format-appropriate null for None values.

  • float — EUR/MWh; only set for generating assets with non-zero energy output.

System LCOE must be computed separately as total NPV / total discounted energy output — it is not the sum of per-asset LCOEs (summing ratios with different denominators is mathematically incorrect).

Raises:

CalculationError – If system_lifetime <= 0, discount_rate is outside [0, 100], or any asset has a non-positive technical_lifetime.

Parameters:
  • system_lifetime (float) – System lifetime in years.

  • discount_rate (float) – System-wide fallback discount rate in percentage (e.g. 5 for 5%). Individual assets override this when asset.discount_rate is set (from ESDL costInformation.discountRate).

  • round_up_replacement (bool) – If True (default), use ceil for the replacement count — the financially exact calculation. If False, uses the continuous factor max(1, system_lifetime / technical_lifetime) for MESIDO optimizer compatibility.

  • annual_energy_mwh_by_asset (dict[str, float] | None) – Optional mapping of asset ID to annual energy production in MWh. When provided, lcoe is computed for generating assets (those in PRODUCER_ASSET_TYPES) with non-zero energy. When None (default), lcoe is None for all assets. Callers that hold an energy calculator should pre-compute this dict and pass it here.

Return type:

dict[str, AssetFinancialResult]

Returns:

Dict mapping asset ID to its AssetFinancialResult.

class kpicalculator.calculators.energy_calculator.EnergyCalculator(energy_system)[source]

Bases: object

Calculator for energy-related KPIs.

calculate_system_efficiency()[source]

Calculate overall system efficiency.

Return type:

float

Returns:

System efficiency as a ratio (0-1)

get_asset_energy_production_per_year(asset)[source]

Calculate annual energy production for a single producing asset.

Parameters:

asset (Asset) – Producing asset (PRODUCER or GEOTHERMAL type).

Return type:

float

Returns:

Energy production in joules per year, or 0.0 if no time series data.

get_total_energy_consumption_per_year()[source]

Calculate total energy consumption per year.

Return type:

float

Returns:

Total energy consumption in joules per year

get_total_energy_demand_per_year()[source]

Calculate total energy demand per year.

Return type:

float

Returns:

Total energy demand in joules per year

get_total_energy_production_per_year()[source]

Calculate total energy production per year.

Return type:

float

Returns:

Total energy production in joules per year

class kpicalculator.calculators.emission_calculator.EmissionCalculator(energy_system)[source]

Bases: object

Calculator for emission-related KPIs.

get_emissions_per_energy_unit()[source]

Calculate CO2 emissions per GJ of energy consumed.

Return type:

float

Returns:

CO2 emissions in kg/GJ

Warning

This method is not yet wired into KpiManager.calculate_all_kpis(). Results are not included in the standard KPI output. Call get_emissions_per_mwh() for the equivalent metric that is part of the public API.

get_emissions_per_mwh()[source]

Calculate CO2 emissions per MWh of energy consumed.

Return type:

float

Returns:

CO2 emissions in kg/MWh

get_total_emissions()[source]

Calculate total CO2 emissions.

Return type:

float

Returns:

Total CO2 emissions in tons per year

Common Model

class kpicalculator.adapters.common_model.Asset(id, name, asset_type, power=0.0, length=0.0, volume=0.0, cop=0.0, investment_cost=0.0, investment_cost_unit='EUR', installation_cost=0.0, installation_cost_unit='EUR', fixed_operational_cost=0.0, fixed_operational_cost_unit='EUR/yr', variable_operational_cost=0.0, variable_operational_cost_unit='EUR/MWh', fixed_maintenance_cost=0.0, fixed_maintenance_cost_unit='EUR/yr', variable_maintenance_cost=0.0, variable_maintenance_cost_unit='EUR/MWh', technical_lifetime=40.0, discount_rate=None, emission_factor=0.0, aggregation_count=1, time_series=<factory>)[source]

Bases: object

aggregation_count: int = 1
asset_type: AssetType
cop: float = 0.0
discount_rate: float | None = None
emission_factor: float = 0.0
fixed_maintenance_cost: float = 0.0
fixed_maintenance_cost_unit: str = 'EUR/yr'
fixed_operational_cost: float = 0.0
fixed_operational_cost_unit: str = 'EUR/yr'
id: str
installation_cost: float = 0.0
installation_cost_unit: str = 'EUR'
investment_cost: float = 0.0
investment_cost_unit: str = 'EUR'
length: float = 0.0
name: str
power: float = 0.0
technical_lifetime: float = 40.0
time_series: dict[str, TimeSeries]
variable_maintenance_cost: float = 0.0
variable_maintenance_cost_unit: str = 'EUR/MWh'
variable_operational_cost: float = 0.0
variable_operational_cost_unit: str = 'EUR/MWh'
volume: float = 0.0
class kpicalculator.adapters.common_model.AssetType(value)[source]

Bases: Enum

CONSUMER = 'Consumer'
CONVERSION = 'Conversion'
GEOTHERMAL = 'GeothermalSource'
PIPE = 'Pipe'
PRODUCER = 'Producer'
PUMP = 'Pump'
STORAGE = 'Storage'
TRANSPORT = 'Transport'
class kpicalculator.adapters.common_model.EnergySystem(name, assets, unit_conversion=<factory>, source_metadata=<factory>, esdl_energy_system=None)[source]

Bases: object

assets: list[Asset]
esdl_energy_system: EnergySystem | None = None
name: str
source_metadata: dict[str, str]
unit_conversion: dict[str, float]
class kpicalculator.adapters.common_model.TimeSeries(time_step, values)[source]

Bases: object

time_step: float
values: list[float]
class kpicalculator.adapters.common_model.Asset(id, name, asset_type, power=0.0, length=0.0, volume=0.0, cop=0.0, investment_cost=0.0, investment_cost_unit='EUR', installation_cost=0.0, installation_cost_unit='EUR', fixed_operational_cost=0.0, fixed_operational_cost_unit='EUR/yr', variable_operational_cost=0.0, variable_operational_cost_unit='EUR/MWh', fixed_maintenance_cost=0.0, fixed_maintenance_cost_unit='EUR/yr', variable_maintenance_cost=0.0, variable_maintenance_cost_unit='EUR/MWh', technical_lifetime=40.0, discount_rate=None, emission_factor=0.0, aggregation_count=1, time_series=<factory>)[source]

Bases: object

aggregation_count: int = 1
asset_type: AssetType
cop: float = 0.0
discount_rate: float | None = None
emission_factor: float = 0.0
fixed_maintenance_cost: float = 0.0
fixed_maintenance_cost_unit: str = 'EUR/yr'
fixed_operational_cost: float = 0.0
fixed_operational_cost_unit: str = 'EUR/yr'
id: str
installation_cost: float = 0.0
installation_cost_unit: str = 'EUR'
investment_cost: float = 0.0
investment_cost_unit: str = 'EUR'
length: float = 0.0
name: str
power: float = 0.0
technical_lifetime: float = 40.0
time_series: dict[str, TimeSeries]
variable_maintenance_cost: float = 0.0
variable_maintenance_cost_unit: str = 'EUR/MWh'
variable_operational_cost: float = 0.0
variable_operational_cost_unit: str = 'EUR/MWh'
volume: float = 0.0
class kpicalculator.adapters.common_model.EnergySystem(name, assets, unit_conversion=<factory>, source_metadata=<factory>, esdl_energy_system=None)[source]

Bases: object

assets: list[Asset]
esdl_energy_system: EnergySystem | None = None
name: str
source_metadata: dict[str, str]
unit_conversion: dict[str, float]
class kpicalculator.adapters.common_model.TimeSeries(time_step, values)[source]

Bases: object

time_step: float
values: list[float]

Adapters

class kpicalculator.adapters.esdl_adapter.EsdlAdapter(credential_manager=None)[source]

Bases: BaseAdapter

Adapter for loading energy system data from ESDL files with database support.

Supports both XML time series files (for testing) and InfluxDB profiles (for production) following the MESIDO pattern.

get_supported_parameters()[source]

Return list of supported optional parameters.

Return type:

list[str]

get_supported_source_type()[source]

Return identifier for ESDL adapter.

Return type:

str

load_data(source, time_series_file=None, timeseries_dataframes=None, use_database_profiles=True)[source]

Load energy system data from an ESDL file.

Costs are extracted from ESDL costInformation elements.

Parameters:
  • source (str | Path) – Path to the ESDL file (str or Path).

  • time_series_file (str | None) – Optional XML time series file path (testing only).

  • timeseries_dataframes (dict[str, DataFrame] | None) – Optional dict mapping asset IDs to pandas DataFrames with time-indexed energy/power data. When provided, takes precedence over database loading and time_series_file.

  • use_database_profiles (bool) – Whether to load InfluxDB profiles.

Return type:

EnergySystem

Returns:

EnergySystem object with costs from ESDL costInformation.

Raises:

TypeError – If source is not a str or Path.

load_from_esdl_object(es, timeseries_dataframes=None, use_database_profiles=False)[source]

Load from an already-parsed PyESDL EnergySystem object.

Prefer this over load_from_string() when the caller has already parsed the ESDL XML — for example, when the object must be inspected before loading (e.g. port→asset resolution in SimulatorAdapter). Use load_from_string() when only the raw XML string is available.

Avoids a second EnergySystemHandler.load_from_string() call when the caller has already parsed the ESDL (e.g. SimulatorAdapter).

Parameters:
  • es (EnergySystem) – Parsed PyESDL EnergySystem.

  • timeseries_dataframes (dict[str, DataFrame] | None) – Optional asset-id-keyed DataFrames.

  • use_database_profiles (bool) – Whether to load InfluxDB profiles.

Return type:

EnergySystem

Returns:

EnergySystem with costs extracted from the ESDL object.

load_from_string(esdl_string, timeseries_dataframes=None, use_database_profiles=False)[source]

Load energy system data from ESDL XML string content.

This method allows loading ESDL data directly from a string without needing a file. Useful for integration with systems that provide ESDL content in memory (e.g., simulator_worker).

Costs are extracted from ESDL costInformation elements.

Note

The default use_database_profiles=False reflects the typical use case for string loading: in-memory workflows where time series data is provided via timeseries_dataframes rather than fetched from a database.

Parameters:
  • esdl_string (str) – ESDL XML content as a string

  • timeseries_dataframes (dict[str, DataFrame] | None) – Optional dict mapping asset IDs to pandas DataFrames with time-indexed energy/power data.

  • use_database_profiles (bool) – Whether to load InfluxDB profiles (default False for in-memory workflows)

Return type:

EnergySystem

Returns:

EnergySystem object with costs from ESDL costInformation

Raises:

ValidationError – If esdl_string is empty or cannot be parsed

validate_source(source)[source]

Validate ESDL file path and basic structure.

Parameters:

source (str) – Path to ESDL file as a string (Path objects are normalised to str by load_data before this method is called).

Return type:

ValidationResult

Returns:

ValidationResult indicating if source is valid

Centralized time series loading with multiple source support.

class kpicalculator.adapters.time_series_manager.TimeSeriesManager(credential_manager=None)[source]

Bases: object

Centralized time series loading with multiple source support.

This class implements the Factory pattern to provide a single entry point for loading time series data from various sources (DataFrame, database, XML) with configurable priority and graceful degradation.

load_time_series(energy_system, timeseries_dataframes=None, xml_file=None, source_priority=None)[source]

Single entry point for all time series loading with source priority.

Parameters:
  • energy_system (EnergySystem) – ESDL energy system containing asset definitions

  • timeseries_dataframes (dict[str, DataFrame] | None) – Optional dict mapping asset IDs to pandas DataFrames with time-indexed energy/power data. When provided, takes precedence over database loading.

  • xml_file (str | None) – Optional XML time series file path (testing only)

  • source_priority (list[str] | None) – Optional list defining loading priority Default: [“dataframes”, “database”, “xml”, “empty”]

Return type:

tuple[dict[str, TimeSeries], ValidationResult]

Returns:

Tuple of (time_series_dict, validation_result)

Raises:

ValidationError – If critical validation fails across all sources

Reporting

ESDL KPI exporter for converting KPI results to ESDL format.

class kpicalculator.reporting.esdl_kpi_exporter.EsdlKpiExporter[source]

Bases: BaseExporter

Export KPI calculation results to ESDL (Energy System Description Language) format.

This exporter takes pre-calculated KPI results and integrates them into ESDL files by adding DistributionKPI elements with StringLabelDistribution structures.

The exporter operates in two modes: - File mode: Saves enhanced ESDL file to disk and returns success boolean - Data structure mode: Returns ESDL EnergySystem object with integrated KPIs

Note: This exporter does NOT perform any calculations. All KPI values must be pre-calculated by the appropriate calculator classes.

Note

This is an internal class, not part of the public API. Use KpiManager.export_to_esdl(), KpiManager.build_esdl_string_with_kpis(), or the top-level kpicalculator.build_esdl_string_with_kpis() instead.

export(results, esdl_energy_system, destination=None, level='system')[source]

Export pre-calculated KPI results to ESDL format.

Takes KPI calculation results and integrates them into an ESDL energy system by adding DistributionKPI elements with StringLabelDistribution structures. Operates in dual mode: file export or data structure return.

Parameters:
  • results (KpiResults) – Pre-calculated KPI results from cost/energy/emission calculators.

  • esdl_energy_system (EnergySystem) – Parsed PyESDL energy system object to write KPIs into.

  • destination (str | Path | None) – Output ESDL file path. If None, returns data structure instead.

  • level (str) – KPI integration level - ‘system’ (main area), ‘area’ (per area), or ‘asset’ (per asset). Currently ‘area’ and ‘asset’ delegate to ‘system’.

Returns:

Always True on success; raises on failure. When destination is None: esdl.EnergySystem object with integrated KPIs.

Return type:

When destination provided

Raises:
  • ValueError – If esdl_energy_system is None, level is invalid, or results structure is malformed.

  • OSError – If file operations fail during save.

Exceptions

Custom exception hierarchy for KPI Calculator.

exception kpicalculator.exceptions.CalculationError[source]

Bases: KpiCalculatorError

Raised when KPI calculation fails.

exception kpicalculator.exceptions.ConfigurationError[source]

Bases: KpiCalculatorError

Raised when configuration is invalid.

exception kpicalculator.exceptions.CredentialError[source]

Bases: SecurityError

Raised when credential loading or validation fails.

exception kpicalculator.exceptions.DataSourceError[source]

Bases: KpiCalculatorError

Raised when data source loading fails.

exception kpicalculator.exceptions.DatabaseError[source]

Bases: DataSourceError

Raised when database operations fail.

exception kpicalculator.exceptions.ExportError[source]

Bases: KpiCalculatorError

Raised when result export fails.

exception kpicalculator.exceptions.KpiCalculatorError[source]

Bases: Exception

Base exception for KPI Calculator.

exception kpicalculator.exceptions.MathematicalError[source]

Bases: CalculationError

Raised when mathematical constraints are violated.

exception kpicalculator.exceptions.SecurityError[source]

Bases: KpiCalculatorError

Raised when security validation fails.

exception kpicalculator.exceptions.ValidationError[source]

Bases: KpiCalculatorError

Raised when input validation fails.