Skip to content

MESQUAL KPI System

MESQUAL KPI System V2 (kpis)

A high-performance, attribute-rich KPI system for energy systems analysis.

When to Use the KPI System vs Direct DataFrame Processing:

The KPI framework is NOT required for basic metric extraction. For quick analysis, you can simply fetch time-series data and process it directly with pandas:

>>> # Quick approach: Direct dataframe processing
>>> df = study.scen.fetch('BiddingZone.Results.market_price')
>>> mean_price = df.mean().unstack('dataset')  # Fast and straightforward

However, the KPI system provides unique advantages for complex workflows:

1. **Model Object Integration**: KPIs retain links to model object metadata,
   enabling property-based filtering and queries (e.g., filter KPIs by bus
   voltage level, generator carrier type, or custom model properties)

2. **Folium Visualization Integration**: KPIs work seamlessly with MESQUAL's
   map visualization system, automatically providing geographic context and
   tooltips with related metrics

3. **Related KPI Discovery**: Within a KPICollection, easily find related metrics
   (e.g., same object/scenario but different aggregation, time period, or unit)

4. **Advanced Unit Handling**: Automatic unit conversion and validation when
   working with collections of heterogeneous KPIs (critical for multi-physics
   analysis with mixed energy/power/price metrics)

5. **Multi-Scenario Workflows**: Built-in support for comparison KPIs and
   bulk computation across scenario collections with progress tracking

Use the KPI system when you need these features; use direct dataframe processing for quick, one-off calculations that stay within the pandas framework.

Core Components:

- KPI: Single computed metric with rich metadata
- KPIAttributes: Metadata container for filtering and grouping
- KPICollection: Container with advanced filtering and export
- KPIDefinition: Abstract base API for KPI specifications
- KPIBuilder: Abstract base API for creating KPIDefinitions in bulk

Architecture and High Level Workflow:

KPIBuilder (instructions on how to create a set of definitions)
    ↓
KPIDefinition (what to compute)
    ↓
KPI (computed result + metadata)
    ↓
KPICollection (filtering, querying, visualization)

Definitions:

- FlagAggKPIDefinition: Standard flag + aggregation KPIs (most common)
- CustomKPIDefinition: Abstract base for study-specific computation logic
- ComparisonKPIDefinition: Delta calculations between scenarios

Aggregations:

- Aggregations: Standard aggregation functions (Mean, Sum, Max, etc.)
- ValueComparisons: Comparison operations (Increase, PercentageIncrease, etc.)
- ArithmeticValueOperations: Arithmetic operations (Product, Division, etc.)

Example Usage - Standard KPIs:

>>> from mesqual.kpis import (
...     KPI, KPICollection, FlagAggKPIDefinition, Aggregations
... )
>>>
>>> # Create definition for standard aggregation
>>> definition = FlagAggKPIDefinition(
...     flag='BZ.Results.market_price',
...     aggregation=Aggregations.Mean
... )
>>>
>>> # Generate and add KPIs to a dataset
>>> dataset: Dataset
>>> dataset.add_kpis_from_definitions(definition)
>>>
>>> # Access collection with filtering and unit handling
>>> collection = dataset.kpi_collection
>>> german_kpis = collection.filter_by_model_properties(properties={'country': 'DE'})
>>> df = german_kpis.to_dataframe(unit_handling='auto_convert')

Example Usage - Custom KPIs:

For study-specific calculations, subclass CustomKPIDefinition and implement either compute_for_object() or compute_batch() plus get_unit():

>>> from mesqual.kpis import CustomKPIDefinition
>>> from mesqual.units import Units
>>>
>>> class GeneratorCapacityFactor(CustomKPIDefinition):
...     '''Custom KPI calculating system efficiency per generator.'''
...
...     def compute_for_object(self, dataset, object_name):
...         # Fetch data for specific generator
...         generation = dataset.fetch('generators_t.p')[object_name].sum()
...         capacity = dataset.fetch('generators').loc[object_name, 'p_nom']
...         return (generation / (capacity * 8760)) * 100
...
...     def get_unit(self):
...         return Units.percent
>>>
>>> # Use like standard definitions
>>> custom_def = SystemEfficiencyKPI(
...     flag='generators_t.p',
...     aggregation=None  # Optional metadata
... )
>>> study.scen.add_kpis_from_definitions_to_all_child_datasets(custom_def)

Note: For many custom metrics, it's often cleaner to first create a study-specific flag (variable) via a custom flag interpreter, then apply standard FlagAggKPIDefinition aggregations to that flag. This keeps computation logic in the interpreter layer and enables you to fetch the flag also as a time-series or outside of the KPI framework.