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**: 1. **Data Ingestion**: Raw configuration and data files are processed into a structured ``xarray.Dataset``. 2. **Orchestration**: A central builder initializes the optimization model and coordinates the addition of various system components. 3. **Component Injection**: Specialized modules inject their specific variables, constraints, and objective terms into the shared model state. 4. **State Management**: The ``linopy.Model`` object 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. .. image:: _static/architecture.png :width: 100% :align: center :alt: POMMES Architecture Diagram 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 as ``model_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: 1. Create a new module in ``pommes/model/``. 2. Define a function ``add_new_feature(model, parameters, ...)``. 3. Inside the function, create necessary variables and constraints. 4. Update the shared **Adequacy** and **Objective** expressions. 5. Register the new module in ``build_model.py``.