Architecture
This document describes the software architecture of the POMMES package. The architecture is designed to be modular, extensible, and centered around the linopy optimization framework.
Overview
The model follows a functional orchestration pattern:
Data Ingestion: Raw configuration and data files are processed into a structured
xarray.Dataset.Orchestration: A central builder initializes the optimization model and coordinates the addition of various system components.
Component Injection: Specialized modules inject their specific variables, constraints, and objective terms into the shared model state.
State Management: The
linopy.Modelobject serves as the central state, accumulating all mathematical definitions before solving.
Architecture Diagram
The following diagram illustrates the data flow and the interaction between the different modules.
Core Components
Data Ingestion Layer
The entry point for data is the pommes.io.build_input_dataset module.
Inputs:
config.yaml: Defines the simulation scope, dimensions (time, area), and active modules.data/*.csv: Contains raw time-series and parameter data.
Process:
Validates input consistency.
Computes derived parameters (e.g., annuities, discount factors).
Broadcasts parameters to the required dimensions.
Output: A comprehensive
xarray.Dataset(referred to asmodel_parameters) containing all necessary data for the optimization.
Orchestration Layer
The pommes.model.build_model module acts as the conductor.
- Responsibilities:
Initializes the empty
linopy.Model.Creates global “safety” variables (load shedding, spillage).
Initializes the two fundamental equations of the model:
Adequacy Constraint: Ensures supply equals demand at every time step.
Objective Function: Defines the minimization of total annualized costs (TOTEX).
Conditionally calls specific component modules based on the configuration.
Functional Modules
The model logic is split into specialized modules located in pommes.model.*. These modules are stateless functions that modify the model object in place. They are categorized into two groups:
1. Energy Balance Components These modules directly influence the physical energy balance of the system. They add terms to the Adequacy Constraint (production, consumption, storage) and the Objective Function (costs).
conversion.py: Power generation technologies (renewable, thermal). Handles capacity, dispatch, and ramping.storage.py: Energy storage systems (batteries, hydro). Handles state of charge and cycling.transport.py: Inter-area transmission. Handles power flow between regions.combined.py: Multi-mode technologies (e.g., CHP).net_import.py: Exchanges with external markets/countries.flexible_demand.py: Demand-side response and load shifting.
2. Regulatory & Accounting Layers These modules do not produce or consume energy but impose constraints or add costs to the system. They primarily modify the Objective Function.
carbon.py: CO2 emission tracking, quotas, and carbon taxes.turpe.py: Grid usage tariffs (TURPE) and subscription optimization.retrofit.py: Logic for converting existing assets to new technologies (e.g., gas to H2).
Linopy State
The linopy.Model object acts as the shared repository for the mathematical formulation.
Decision Variables: Created by each module (e.g.,
operation_conversion_power,planning_storage_capacity).Technical Constraints: Specific limitations added by modules (e.g., ramp rates, max capacity, must-run).
Shared Expressions: *
operation_adequacy_constraint: All energy modules append their net generation to this constraint. *annualised_totex_def: All modules append their investment and operation costs to this expression.
Extensibility
This architecture allows for easy extension. To add a new technology or mechanism:
Create a new module in
pommes/model/.Define a function
add_new_feature(model, parameters, ...).Inside the function, create necessary variables and constraints.
Update the shared Adequacy and Objective expressions.
Register the new module in
build_model.py.