diff --git a/CHANGELOG.md b/CHANGELOG.md index 17fcfec37..604769b4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,90 +46,113 @@ This release introduces new model dimensions (periods and scenarios) for multi-p ### ✨ Added **New model dimensions:** -* **Period dimension**: Enables multi-period investment modeling with distinct decisions in each period for transformation pathway optimization -* **Scenario dimension**: Supports stochastic modeling with weighted scenarios for robust decision-making under uncertainty (demand, prices, weather) + +- **Period dimension**: Enables multi-period investment modeling with distinct decisions in each period for transformation pathway optimization +- **Scenario dimension**: Supports stochastic modeling with weighted scenarios for robust decision-making under uncertainty (demand, prices, weather) **Redesigned effect sharing system:** + Effects now use intuitive `share_from_*` syntax that clearly shows contribution sources: + ```python costs = fx.Effect('costs', '€', 'Total costs', share_from_temporal={'CO2': 0.2}, # From temporal effects share_from_periodic={'land': 100}) # From periodic effects ``` + This replaces `specific_share_to_other_effects_*` parameters and inverts the direction for clearer relationships. **Enhanced I/O and data handling:** -* NetCDF/JSON serialization for all Interface objects and FlowSystem with round-trip support -* FlowSystem manipulation: `sel()`, `isel()`, `resample()`, `copy()`, `__eq__()` methods -* Direct access to FlowSystem from results without manual restoring (lazily loaded) -* New `FlowResults` class and precomputed DataArrays for sizes/flow_rates/flow_hours -* `effects_per_component()` dataset for component impact evaluation, including all indirect effects through effect shares + +- NetCDF/JSON serialization for all Interface objects and FlowSystem with round-trip support +- FlowSystem manipulation: `sel()`, `isel()`, `resample()`, `copy()`, `__eq__()` methods +- Direct access to FlowSystem from results without manual restoring (lazily loaded) +- New `FlowResults` class and precomputed DataArrays for sizes/flow_rates/flow_hours +- `effects_per_component()` dataset for component impact evaluation, including all indirect effects through effect shares **Other additions:** -* Balanced storage - charging and discharging sizes can be forced equal via `balanced` parameter -* New Storage parameters: `relative_minimum_final_charge_state` and `relative_maximum_final_charge_state` for final state control -* Improved filter methods in results -* Example for 2-stage investment decisions leveraging FlowSystem resampling + +- Balanced storage - charging and discharging sizes can be forced equal via `balanced` parameter +- New Storage parameters: `relative_minimum_final_charge_state` and `relative_maximum_final_charge_state` for final state control +- Improved filter methods in results +- Example for 2-stage investment decisions leveraging FlowSystem resampling ### πŸ’₯ Breaking Changes -* `relative_minimum_charge_state` and `relative_maximum_charge_state` don't have an extra timestep anymore. -* Renamed class `SystemModel` to `FlowSystemModel` -* Renamed class `Model` to `Submodel` -* Renamed `mode` parameter in plotting methods to `style` -* `Calculation.do_modeling()` now returns the `Calculation` object instead of its `linopy.Model`. Callers that previously accessed the linopy model directly should now use `calculation.do_modeling().model` instead of `calculation.do_modeling()`. + +- `relative_minimum_charge_state` and `relative_maximum_charge_state` don't have an extra timestep anymore. +- Renamed class `SystemModel` to `FlowSystemModel` +- Renamed class `Model` to `Submodel` +- Renamed `mode` parameter in plotting methods to `style` +- `Calculation.do_modeling()` now returns the `Calculation` object instead of its `linopy.Model`. Callers that previously accessed the linopy model directly should now use `calculation.do_modeling().model` instead of `calculation.do_modeling()`. ### ♻️ Changed -* FlowSystems cannot be shared across multiple Calculations anymore. A copy of the FlowSystem is created instead, making every Calculation independent -* Each Subcalculation in `SegmentedCalculation` now has its own distinct `FlowSystem` object -* Type system overhaul - added clear separation between temporal and non-temporal data throughout codebase for better clarity -* Enhanced FlowSystem interface with improved `__repr__()` and `__str__()` methods -* Improved Model Structure - Views and organisation is now divided into: - * Model: The main Model (linopy.Model) that is used to create and store the variables and constraints for the flow_system. - * Submodel: The base class for all submodels. Each is a subset of the Model, for simpler access and clearer code. + +- FlowSystems cannot be shared across multiple Calculations anymore. A copy of the FlowSystem is created instead, making every Calculation independent +- Each Subcalculation in `SegmentedCalculation` now has its own distinct `FlowSystem` object +- Type system overhaul - added clear separation between temporal and non-temporal data throughout codebase for better clarity +- Enhanced FlowSystem interface with improved `__repr__()` and `__str__()` methods +- Improved Model Structure - Views and organisation is now divided into: + - Model: The main Model (linopy.Model) that is used to create and store the variables and constraints for the flow_system. + - Submodel: The base class for all submodels. Each is a subset of the Model, for simpler access and clearer code. ### πŸ—‘οΈ Deprecated -* The `agg_group` and `agg_weight` parameters of `TimeSeriesData` are deprecated and will be removed in a future version. Use `aggregation_group` and `aggregation_weight` instead. -* The `active_timesteps` parameter of `Calculation` is deprecated and will be removed in a future version. Use the new `sel(time=...)` method on the FlowSystem instead. -* The assignment of Bus Objects to Flow.bus is deprecated and will be removed in a future version. Use the label of the Bus instead. -* The usage of Effects objects in Dicts to assign shares to Effects is deprecated and will be removed in a future version. Use the label of the Effect instead. -* Effect parameters renamed: - - `minimum_investment` β†’ `minimum_periodic` - - `maximum_investment` β†’ `maximum_periodic` - - `minimum_operation` β†’ `minimum_temporal` - - `maximum_operation` β†’ `maximum_temporal` - - `minimum_operation_per_hour` β†’ `minimum_per_hour` - - `maximum_operation_per_hour` β†’ `maximum_per_hour` + +- The `agg_group` and `agg_weight` parameters of `TimeSeriesData` are deprecated and will be removed in a future version. Use `aggregation_group` and `aggregation_weight` instead. +- The `active_timesteps` parameter of `Calculation` is deprecated and will be removed in a future version. Use the new `sel(time=...)` method on the FlowSystem instead. +- The assignment of Bus Objects to Flow.bus is deprecated and will be removed in a future version. Use the label of the Bus instead. +- The usage of Effects objects in Dicts to assign shares to Effects is deprecated and will be removed in a future version. Use the label of the Effect instead. +- Effect parameters renamed: + - `minimum_investment` β†’ `minimum_periodic` + - `maximum_investment` β†’ `maximum_periodic` + - `minimum_operation` β†’ `minimum_temporal` + - `maximum_operation` β†’ `maximum_temporal` + - `minimum_operation_per_hour` β†’ `minimum_per_hour` + - `maximum_operation_per_hour` β†’ `maximum_per_hour` ### πŸ”₯ Removed -* **Effect share parameters**: The old `specific_share_to_other_effects_*` parameters were replaced WITHOUT DEPRECATION - - `specific_share_to_other_effects_operation` β†’ `share_from_temporal` (with inverted direction) - - `specific_share_to_other_effects_invest` β†’ `share_from_periodic` (with inverted direction) + +- **Effect share parameters**: The old `specific_share_to_other_effects_*` parameters were replaced WITHOUT DEPRECATION + - `specific_share_to_other_effects_operation` β†’ `share_from_temporal` (with inverted direction) + - `specific_share_to_other_effects_invest` β†’ `share_from_periodic` (with inverted direction) ### πŸ› Fixed -* Enhanced NetCDF I/O with proper attribute preservation for DataArrays -* Improved error handling and validation in serialization processes -* Better type consistency across all framework components + +- Enhanced NetCDF I/O with proper attribute preservation for DataArrays +- Improved error handling and validation in serialization processes +- Better type consistency across all framework components + +### πŸ“ Docs + +- Reorganized mathematical notation docs: moved to lowercase `mathematical-notation/` with subdirectories (`elements/`, `features/`, `modeling-patterns/`) +- Added comprehensive documentation pages: `dimensions.md` (time/period/scenario), `effects-penalty-objective.md`, modeling patterns +- Enhanced all element pages with implementation details, cross-references, and "See Also" sections +- Rewrote README and landing page with clearer vision, roadmap, and universal applicability emphasis +- Removed deprecated `docs/SUMMARY.md`, updated `mkdocs.yml` for new structure +- Tightened docstrings in core modules with better cross-referencing +- Added recipies section to docs ### 🚧 Known Issues -* IO for single Interfaces/Elements to Datasets might not work properly if the Interface/Element is not part of a fully transformed and connected FlowSystem. This arises from Numeric Data not being stored as xr.DataArray by the user. To avoid this, always use the `to_dataset()` on Elements inside a FlowSystem that's connected and transformed. + +- IO for single Interfaces/Elements to Datasets might not work properly if the Interface/Element is not part of a fully transformed and connected FlowSystem. This arises from Numeric Data not being stored as xr.DataArray by the user. To avoid this, always use the `to_dataset()` on Elements inside a FlowSystem that's connected and transformed. ### πŸ‘· Development -* FlowSystem data management simplified - removed `time_series_collection` pattern in favor of direct timestep properties -* Change modeling hierarchy to allow for more flexibility in future development. This leads to minimal changes in the access and creation of Submodels and their variables. -* Added new module `.modeling` that contains Modelling primitives and utilities -* Clearer separation between the main Model and "Submodels" -* Improved access to the Submodels and their variables, constraints and submodels -* Added `__repr__()` for Submodels to easily inspect its content -* Enhanced data handling methods - * `fit_to_model_coords()` method for data alignment - * `fit_effects_to_model_coords()` method for effect data processing - * `connect_and_transform()` method replacing several operations -* **Testing improvements**: Eliminated warnings during test execution - * Updated deprecated code patterns in tests and examples (e.g., `sink`/`source` β†’ `inputs`/`outputs`, `'H'` β†’ `'h'` frequency) - * Refactored plotting logic to handle test environments explicitly with non-interactive backends - * Added comprehensive warning filters in `__init__.py` and `pyproject.toml` to suppress third-party library warnings - * Improved test fixtures with proper figure cleanup to prevent memory leaks - * Enhanced backend detection and handling in `plotting.py` for both Matplotlib and Plotly + +- FlowSystem data management simplified - removed `time_series_collection` pattern in favor of direct timestep properties +- Change modeling hierarchy to allow for more flexibility in future development. This leads to minimal changes in the access and creation of Submodels and their variables. +- Added new module `.modeling` that contains Modelling primitives and utilities +- Clearer separation between the main Model and "Submodels" +- Improved access to the Submodels and their variables, constraints and submodels +- Added `__repr__()` for Submodels to easily inspect its content +- Enhanced data handling methods + - `fit_to_model_coords()` method for data alignment + - `fit_effects_to_model_coords()` method for effect data processing + - `connect_and_transform()` method replacing several operations +- **Testing improvements**: Eliminated warnings during test execution + - Updated deprecated code patterns in tests and examples (e.g., `sink`/`source` β†’ `inputs`/`outputs`, `'H'` β†’ `'h'` frequency) + - Refactored plotting logic to handle test environments explicitly with non-interactive backends + - Added comprehensive warning filters in `__init__.py` and `pyproject.toml` to suppress third-party library warnings + - Improved test fixtures with proper figure cleanup to prevent memory leaks + - Enhanced backend detection and handling in `plotting.py` for both Matplotlib and Plotly Until here --> diff --git a/README.md b/README.md index edb77e74d..957274f1c 100644 --- a/README.md +++ b/README.md @@ -8,84 +8,122 @@ --- -## πŸš€ Purpose +## 🎯 Vision -**flixopt** is a Python-based optimization framework designed to tackle energy and material flow problems using mixed-integer linear programming (MILP). +**FlixOpt aims to be the most accessible and flexible Python framework for energy and material flow optimization.** -**flixopt** bridges the gap between high-level energy systems models like [FINE](https://github.com/FZJ-IEK3-VSA/FINE) used for design and (multi-period) investment decisions and low-level dispatch optimization tools used for operation decisions. +We believe that optimization modeling should be **approachable for beginners** yet **powerful for experts**. Too often, frameworks force you to choose between ease of use and flexibility. FlixOpt refuses this compromise. -**flixopt** leverages the fast and efficient [linopy](https://github.com/PyPSA/linopy/) for the mathematical modeling and [xarray](https://github.com/pydata/xarray) for data handling. +### Where We're Going -**flixopt** provides a user-friendly interface with options for advanced users. +**Short-term goals:** +- **Multi-dimensional modeling**: Full support for multi-period investments and scenario-based stochastic optimization (periods and scenarios are in active development) +- **Enhanced component library**: More pre-built, domain-specific components (sector coupling, hydrogen systems, thermal networks, demand-side management) -It was originally developed by [TU Dresden](https://github.com/gewv-tu-dresden) as part of the SMARTBIOGRID project, funded by the German Federal Ministry for Economic Affairs and Energy (FKZ: 03KB159B). Building on the Matlab-based flixOptMat framework (developed in the FAKS project), FlixOpt also incorporates concepts from [oemof/solph](https://github.com/oemof/oemof-solph). +**Medium-term vision:** +- **Modeling to generate alternatives (MGA)**: Built-in support for exploring near-optimal solution spaces to produce more robust, diverse solutions under uncertainty +- **Interactive tutorials**: Browser-based, reactive tutorials for learning FlixOpt without local installation +- **Standardized cost calculations**: Align with industry standards (VDI 2067) for CAPEX/OPEX calculations +- **Advanced result analysis**: Time-series aggregation, automated reporting, and rich visualization options ---- +**Long-term vision:** +- **Showcase universal applicability**: FlixOpt already handles any flow-based system (supply chains, water networks, production planning, chemical processes) - we need more examples and domain-specific component libraries to demonstrate this +- **Seamless integration**: First-class support for coupling with simulation tools, databases, existing energy system models, and GIS data +- **Robust optimization**: Built-in uncertainty quantification and stochastic programming capabilities +- **Community ecosystem**: Rich library of user-contributed components, examples, and domain-specific extensions +- **Model validation tools**: Automated checks for physical plausibility, data consistency, and common modeling errors + +### Why FlixOpt Exists -## 🌟 Key Features +FlixOpt is a **general-purpose framework for modeling any system involving flows and conversions** - energy, materials, fluids, goods, or data. While energy systems are our primary focus, the same mathematical foundation applies to supply chains, water networks, production lines, and more. -- **High-level Interface** with low-level control - - User-friendly interface for defining flow systems - - Pre-defined components like CHP, Heat Pump, Cooling Tower, etc. - - Fine-grained control for advanced configurations +We bridge the gap between high-level strategic models (like [FINE](https://github.com/FZJ-IEK3-VSA/FINE)) for long-term planning and low-level dispatch tools for operations. FlixOpt is the **sweet spot** for: -- **Investment Optimization** - - Combined dispatch and investment optimization - - Size optimization and discrete investment decisions - - Combined with On/Off variables and constraints +- **Researchers** who need to prototype quickly but may require deep customization later +- **Engineers** who want reliable, tested components without black-box abstractions +- **Students** learning optimization who benefit from clear, Pythonic interfaces +- **Practitioners** who need to move from model to production-ready results +- **Domain experts** from any field where things flow, transform, and need optimizing -- **Effects, not only Costs --> Multi-criteria Optimization** - - flixopt abstracts costs as so called 'Effects'. This allows to model costs, CO2-emissions, primary-energy-demand or area-demand at the same time. - - Effects can interact with each other(e.g., specific CO2 costs) - - Any of these `Effects` can be used as the optimization objective. - - A **Weigted Sum** of Effects can be used as the optimization objective. - - Every Effect can be constrained ($\epsilon$-constraint method). +Built on modern foundations ([linopy](https://github.com/PyPSA/linopy/) and [xarray](https://github.com/pydata/xarray)), FlixOpt delivers both **performance** and **transparency**. You can inspect everything, extend anything, and trust that your model does exactly what you designed. -- **Calculation Modes** - - **Full** - Solve the model with highest accuracy and computational requirements. - - **Segmented** - Speed up solving by using a rolling horizon. - - **Aggregated** - Speed up solving by identifying typical periods using [TSAM](https://github.com/FZJ-IEK3-VSA/tsam). Suitable for large models. +Originally developed at [TU Dresden](https://github.com/gewv-tu-dresden) for the SMARTBIOGRID project (funded by the German Federal Ministry for Economic Affairs and Energy, FKZ: 03KB159B), FlixOpt has evolved from the Matlab-based flixOptMat framework while incorporating the best ideas from [oemof/solph](https://github.com/oemof/oemof-solph). --- -## πŸ“¦ Installation +## 🌟 What Makes FlixOpt Different -Install FlixOpt via pip. -`pip install flixopt` -With [HiGHS](https://github.com/ERGO-Code/HiGHS?tab=readme-ov-file) included out of the box, flixopt is ready to use.. +### Start Simple, Scale Complex +Define a working model in minutes with high-level components, then drill down to fine-grained control when needed. No rewriting, no framework switching. -We recommend installing FlixOpt with all dependencies, which enables additional features like interactive network visualizations ([pyvis](https://github.com/WestHealth/pyvis)) and time series aggregation ([tsam](https://github.com/FZJ-IEK3-VSA/tsam)). -`pip install "flixopt[full]"` +```python +import flixopt as fx ---- +# Simple start +boiler = fx.Boiler("Boiler", eta=0.9, ...) + +# Advanced control when needed - extend with native linopy +boiler.model.add_constraints(custom_constraint, name="my_constraint") +``` -## πŸ“š Documentation +### Multi-Criteria Optimization Done Right +Model costs, emissions, resource use, and any custom metric simultaneously as **Effects**. Optimize any single Effect, use weighted combinations, or apply Ξ΅-constraints: -The documentation is available at [https://flixopt.github.io/flixopt/latest/](https://flixopt.github.io/flixopt/latest/) +```python +costs = fx.Effect('costs', '€', 'Total costs', + share_from_temporal={'CO2': 180}) # 180 €/tCO2 +co2 = fx.Effect('CO2', 'kg', 'Emissions', maximum_periodic=50000) +``` + +### Performance at Any Scale +Choose the right calculation mode for your problem: +- **Full** - Maximum accuracy for smaller problems +- **Segmented** - Rolling horizon for large time series +- **Aggregated** - Typical periods using [TSAM](https://github.com/FZJ-IEK3-VSA/tsam) for massive models + +### Built for Reproducibility +Every result file is self-contained with complete model information. Load it months later and know exactly what you optimized. Export to NetCDF, share with colleagues, archive for compliance. --- -## 🎯️ Solver Integration +## πŸš€ Quick Start + +```bash +pip install flixopt +``` -By default, FlixOpt uses the open-source solver [HiGHS](https://highs.dev/) which is installed by default. However, it is compatible with additional solvers such as: +That's it. FlixOpt comes with the [HiGHS](https://highs.dev/) solver included - you're ready to optimize. +Many more solvers are supported (gurobi, cplex, cbc, glpk, ...) -- [Gurobi](https://www.gurobi.com/) -- [CBC](https://github.com/coin-or/Cbc) -- [GLPK](https://www.gnu.org/software/glpk/) -- [CPLEX](https://www.ibm.com/analytics/cplex-optimizer) +For additional features (interactive network visualization, time series aggregation): +```bash +pip install "flixopt[full]" +``` -For detailed licensing and installation instructions, refer to the respective solver documentation. +**Next steps:** +- πŸ“š [Full Documentation](https://flixopt.github.io/flixopt/latest/) +- πŸ’‘ [Examples](https://flixopt.github.io/flixopt/latest/examples/) +- πŸ”§ [API Reference](https://flixopt.github.io/flixopt/latest/api-reference/) --- -## πŸ›  Development Setup -Look into our docs for [development setup](https://flixopt.github.io/flixopt/latest/contribute/) +## 🀝 Contributing + +FlixOpt thrives on community input. Whether you're fixing bugs, adding components, improving docs, or sharing use cases - we welcome your contributions. + +See our [contribution guide](https://flixopt.github.io/flixopt/latest/contribute/) to get started. --- ## πŸ“– Citation -If you use FlixOpt in your research or project, please cite the following: +If FlixOpt supports your research or project, please cite: - **Main Citation:** [DOI:10.18086/eurosun.2022.04.07](https://doi.org/10.18086/eurosun.2022.04.07) - **Short Overview:** [DOI:10.13140/RG.2.2.14948.24969](https://doi.org/10.13140/RG.2.2.14948.24969) + +--- + +## πŸ“„ License + +MIT License - See [LICENSE](https://github.com/flixopt/flixopt/blob/main/LICENSE) for details. diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md deleted file mode 100644 index f66c4e5e5..000000000 --- a/docs/SUMMARY.md +++ /dev/null @@ -1,7 +0,0 @@ -- [Home](index.md) -- [Getting Started](getting-started.md) -- [User Guide](user-guide/) -- [Examples](examples/) -- [Contribute](contribute.md) -- [API Reference](api-reference/) -- [Release Notes](changelog/) diff --git a/docs/index.md b/docs/index.md index 04020639e..7d2462c67 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,24 +1,85 @@ # FlixOpt -**FlixOpt** is a Python-based optimization framework designed to tackle energy and material flow problems using mixed-integer linear programming (MILP). +## 🎯 Vision -It borrows concepts from both [FINE](https://github.com/FZJ-IEK3-VSA/FINE) and [oemof.solph](https://github.com/oemof/oemof-solph). +**FlixOpt aims to be the most accessible and flexible Python framework for energy and material flow optimization.** -## Why FlixOpt? +We believe that optimization modeling should be **approachable for beginners** yet **powerful for experts**. Too often, frameworks force you to choose between ease of use and flexibility. FlixOpt refuses this compromise. -FlixOpt is designed as a general-purpose optimization framework to get your model running quickly, without sacrificing flexibility down the road: +### Where We're Going -- **Easy to Use API**: FlixOpt provides a Pythonic, object-oriented interface that makes mathematical optimization more accessible to Python developers. +**Short-term goals:** -- **Approachable Learning Curve**: Designed to be accessible from the start, with options for more detailed models down the road. +- **Multi-dimensional modeling**: Full support for multi-period investments and scenario-based stochastic optimization (periods and scenarios are in active development) +- **Enhanced component library**: More pre-built, domain-specific components (sector coupling, hydrogen systems, thermal networks, demand-side management) -- **Domain Independence**: While frameworks like oemof and FINE excel at energy system modeling with domain-specific components, FlixOpt offers a more general mathematical approach that can be applied across different fields. +**Medium-term vision:** -- **Extensibility**: Easily add custom constraints or variables to any FlixOpt Model using [linopy](https://github.com/PyPSA/linopy). Tailor any FlixOpt model to your specific needs without loosing the convenience of the framework. +- **Modeling to generate alternatives (MGA)**: Built-in support for exploring near-optimal solution spaces to produce more robust, diverse solutions under uncertainty +- **Interactive tutorials**: Browser-based, reactive tutorials for learning FlixOpt without local installation (marimo) +- **Standardized cost calculations**: Align with industry standards (VDI 2067) for CAPEX/OPEX calculations +- **Advanced result analysis**: Time-series aggregation, automated reporting, and rich visualization options +- **Recipe collection**: Community-driven library of common modeling patterns, data manipulation techniques, and optimization strategies (see [Recipes](user-guide/recipies/index.md) - help wanted!) -- **Solver Agnostic**: Work with different solvers through a consistent interface. +**Long-term vision:** -- **Results File I/O**: Built to analyze results independent of running the optimization. +- **Showcase universal applicability**: FlixOpt already handles any flow-based system (supply chains, water networks, production planning, chemical processes) - we need more examples and domain-specific component libraries to demonstrate this +- **Seamless integration**: First-class support for coupling with simulation tools, databases, existing energy system models, and GIS data +- **Robust optimization**: Built-in uncertainty quantification and stochastic programming capabilities +- **Community ecosystem**: Rich library of user-contributed components, examples, and domain-specific extensions +- **Model validation tools**: Automated checks for physical plausibility, data consistency, and common modeling errors + +### Why FlixOpt Exists + +FlixOpt is a **general-purpose framework for modeling any system involving flows and conversions** - energy, materials, fluids, goods, or data. While energy systems are our primary focus, the same mathematical foundation applies to supply chains, water networks, production lines, and more. + +We bridge the gap between high-level strategic models (like [FINE](https://github.com/FZJ-IEK3-VSA/FINE)) for long-term planning and low-level dispatch tools for operations. FlixOpt is the **sweet spot** for: + +- **Researchers** who need to prototype quickly but may require deep customization later +- **Engineers** who want reliable, tested components without black-box abstractions +- **Students** learning optimization who benefit from clear, Pythonic interfaces +- **Practitioners** who need to move from model to production-ready results +- **Domain experts** from any field where things flow, transform, and need optimizing + +Built on modern foundations ([linopy](https://github.com/PyPSA/linopy/) and [xarray](https://github.com/pydata/xarray)), FlixOpt delivers both **performance** and **transparency**. You can inspect everything, extend anything, and trust that your model does exactly what you designed. + +Originally developed at [TU Dresden](https://github.com/gewv-tu-dresden) for the SMARTBIOGRID project (funded by the German Federal Ministry for Economic Affairs and Energy, FKZ: 03KB159B), FlixOpt has evolved from the Matlab-based flixOptMat framework while incorporating the best ideas from [oemof/solph](https://github.com/oemof/oemof-solph). + +--- + +## What Makes FlixOpt Different + +### Start Simple, Scale Complex +Define a working model in minutes with high-level components, then drill down to fine-grained control when needed. No rewriting, no framework switching. + +```python +import flixopt as fx + +# Simple start +boiler = fx.Boiler("Boiler", eta=0.9, ...) + +# Advanced control when needed - extend with native linopy +boiler.model.add_constraints(custom_constraint, name="my_constraint") +``` + +### Multi-Criteria Optimization Done Right +Model costs, emissions, resource use, and any custom metric simultaneously as **Effects**. Optimize any single Effect, use weighted combinations, or apply Ξ΅-constraints: + +```python +costs = fx.Effect('costs', '€', 'Total costs', + share_from_temporal={'CO2': 180}) # 180 €/tCO2 +co2 = fx.Effect('CO2', 'kg', 'Emissions', maximum_periodic=50000) +``` + +### Performance at Any Scale +Choose the right calculation mode for your problem: + +- **Full** - Maximum accuracy for smaller problems +- **Segmented** - Rolling horizon for large time series +- **Aggregated** - Typical periods using [TSAM](https://github.com/FZJ-IEK3-VSA/tsam) for massive models + +### Built for Reproducibility +Every result file is self-contained with complete model information. Load it months later and know exactly what you optimized. Export to NetCDF, share with colleagues, archive for compliance.
![FlixOpt Conceptual Usage](./images/architecture_flixOpt.png) diff --git a/docs/user-guide/Mathematical Notation/Effects, Penalty & Objective.md b/docs/user-guide/Mathematical Notation/Effects, Penalty & Objective.md deleted file mode 100644 index 7da311c37..000000000 --- a/docs/user-guide/Mathematical Notation/Effects, Penalty & Objective.md +++ /dev/null @@ -1,132 +0,0 @@ -## Effects -[`Effects`][flixopt.effects.Effect] are used to allocate things like costs, emissions, or other "effects" occurring in the system. -These arise from so called **Shares**, which originate from **Elements** like [Flows](Flow.md). - -**Example:** - -[`Flows`][flixopt.elements.Flow] have an attribute called `effects_per_flow_hour`, defining the effect amount of per flow hour. -Associated effects could be: -- costs - given in [€/kWh]... -- ...or emissions - given in [kg/kWh]. -- -Effects are allocated separately for investments and operation. - -### Shares to Effects - -$$ \label{eq:Share_invest} -s_{l \rightarrow e, \text{inv}} = \sum_{v \in \mathcal{V}_{l, \text{inv}}} v \cdot \text a_{v \rightarrow e} -$$ - -$$ \label{eq:Share_operation} -s_{l \rightarrow e, \text{op}}(\text{t}_i) = \sum_{v \in \mathcal{V}_{l,\text{op}}} v(\text{t}_i) \cdot \text a_{v \rightarrow e}(\text{t}_i) -$$ - -With: - -- $\text{t}_i$ being the time step -- $\mathcal{V_l}$ being the set of all optimization variables of element $e$ -- $\mathcal{V}_{l, \text{inv}}$ being the set of all optimization variables of element $e$ related to investment -- $\mathcal{V}_{l, \text{op}}$ being the set of all optimization variables of element $e$ related to operation -- $v$ being an optimization variable of the element $l$ -- $v(\text{t}_i)$ being an optimization variable of the element $l$ at timestep $\text{t}_i$ -- $\text a_{v \rightarrow e}$ being the factor between the optimization variable $v$ to effect $e$ -- $\text a_{v \rightarrow e}(\text{t}_i)$ being the factor between the optimization variable $v$ to effect $e$ for timestep $\text{t}_i$ -- $s_{l \rightarrow e, \text{inv}}$ being the share of element $l$ to the investment part of effect $e$ -- $s_{l \rightarrow e, \text{op}}(\text{t}_i)$ being the share of element $l$ to the operation part of effect $e$ - -### Shares between different Effects - -Furthermore, the Effect $x$ can contribute a share to another Effect ${e} \in \mathcal{E}\backslash x$. -This share is defined by the factor $\text r_{x \rightarrow e}$. - -For example, the Effect "CO$_2$ emissions" (unit: kg) -can cause an additional share to Effect "monetary costs" (unit: €). -In this case, the factor $\text a_{x \rightarrow e}$ is the specific CO$_2$ price in €/kg. However, circular references have to be avoided. - -The overall sum of investment shares of an Effect $e$ is given by $\eqref{eq:Effect_invest}$ - -$$ \label{eq:Effect_invest} -E_{e, \text{inv}} = -\sum_{l \in \mathcal{L}} s_{l \rightarrow e,\text{inv}} + -\sum_{x \in \mathcal{E}\backslash e} E_{x, \text{inv}} \cdot \text{r}_{x \rightarrow e,\text{inv}} -$$ - -The overall sum of operation shares is given by $\eqref{eq:Effect_Operation}$ - -$$ \label{eq:Effect_Operation} -E_{e, \text{op}}(\text{t}_{i}) = -\sum_{l \in \mathcal{L}} s_{l \rightarrow e, \text{op}}(\text{t}_i) + -\sum_{x \in \mathcal{E}\backslash e} E_{x, \text{op}}(\text{t}_i) \cdot \text{r}_{x \rightarrow {e},\text{op}}(\text{t}_i) -$$ - -and totals to $\eqref{eq:Effect_Operation_total}$ -$$\label{eq:Effect_Operation_total} -E_{e,\text{op},\text{tot}} = \sum_{i=1}^n E_{e,\text{op}}(\text{t}_{i}) -$$ - -With: - -- $\mathcal{L}$ being the set of all elements in the FlowSystem -- $\mathcal{E}$ being the set of all effects in the FlowSystem -- $\text r_{x \rightarrow e, \text{inv}}$ being the factor between the invest part of Effect $x$ and Effect $e$ -- $\text r_{x \rightarrow e, \text{op}}(\text{t}_i)$ being the factor between the operation part of Effect $x$ and Effect $e$ - -- $\text{t}_i$ being the time step -- $s_{l \rightarrow e, \text{inv}}$ being the share of element $l$ to the investment part of effect $e$ -- $s_{l \rightarrow e, \text{op}}(\text{t}_i)$ being the share of element $l$ to the operation part of effect $e$ - - -The total of an effect $E_{e}$ is given as $\eqref{eq:Effect_Total}$ - -$$ \label{eq:Effect_Total} -E_{e} = E_{\text{inv},e} +E_{\text{op},\text{tot},e} -$$ - -### Constraining Effects - -For each variable $v \in \{ E_{e,\text{inv}}, E_{e,\text{op},\text{tot}}, E_e\}$, a lower bound $v^\text{L}$ and upper bound $v^\text{U}$ can be defined as - -$$ \label{eq:Bounds_Single} -\text v^\text{L} \leq v \leq \text v^\text{U} -$$ - -Furthermore, bounds for the operational shares can be set for each time step - -$$ \label{eq:Bounds_Time_Steps} -\text E_{e,\text{op}}^\text{L}(\text{t}_i) \leq E_{e,\text{op}}(\text{t}_i) \leq \text E_{e,\text{op}}^\text{U}(\text{t}_i) -$$ - -## Penalty - -Additionally to the user defined [Effects](#effects), a Penalty $\Phi$ is part of every FlixOpt Model. -Its used to prevent unsolvable problems and simplify troubleshooting. -Shares to the penalty can originate from every Element and are constructed similarly to -$\eqref{Share_invest}$ and $\eqref{Share_operation}$. - -$$ \label{eq:Penalty} -\Phi = \sum_{l \in \mathcal{L}} \left( s_{l \rightarrow \Phi} +\sum_{\text{t}_i \in \mathcal{T}} s_{l \rightarrow \Phi}(\text{t}_{i}) \right) -$$ - -With: - -- $\mathcal{L}$ being the set of all elements in the FlowSystem -- $\mathcal{T}$ being the set of all timesteps -- $s_{l \rightarrow \Phi}$ being the share of element $l$ to the penalty - -At the moment, penalties only occur in [Buses](Bus.md) - -## Objective - -The optimization objective of a FlixOpt Model is defined as $\eqref{eq:Objective}$ -$$ \label{eq:Objective} -\min(E_{\Omega} + \Phi) -$$ - -With: - -- $\Omega$ being the chosen **Objective [Effect](#effects)** (see $\eqref{eq:Effect_Total}$) -- $\Phi$ being the [Penalty](#penalty) - -This approach allows for a multi-criteria optimization using both... - - ... the **Weighted Sum** method, as the chosen **Objective Effect** can incorporate other Effects. - - ... the ($\epsilon$-constraint method) by constraining effects. diff --git a/docs/user-guide/Mathematical Notation/Flow.md b/docs/user-guide/Mathematical Notation/Flow.md deleted file mode 100644 index 78135e822..000000000 --- a/docs/user-guide/Mathematical Notation/Flow.md +++ /dev/null @@ -1,26 +0,0 @@ -The flow_rate is the main optimization variable of the Flow. It's limited by the size of the Flow and relative bounds \eqref{eq:flow_rate}. - -$$ \label{eq:flow_rate} - \text P \cdot \text p^{\text{L}}_{\text{rel}}(\text{t}_{i}) - \leq p(\text{t}_{i}) \leq - \text P \cdot \text p^{\text{U}}_{\text{rel}}(\text{t}_{i}) -$$ - -With: - -- $\text P$ being the size of the Flow -- $p(\text{t}_{i})$ being the flow-rate at time $\text{t}_{i}$ -- $\text p^{\text{L}}_{\text{rel}}(\text{t}_{i})$ being the relative lower bound (typically 0) -- $\text p^{\text{U}}_{\text{rel}}(\text{t}_{i})$ being the relative upper bound (typically 1) - -With $\text p^{\text{L}}_{\text{rel}}(\text{t}_{i}) = 0$ and $\text p^{\text{U}}_{\text{rel}}(\text{t}_{i}) = 1$, -equation \eqref{eq:flow_rate} simplifies to - -$$ - 0 \leq p(\text{t}_{i}) \leq \text P -$$ - - -This mathematical formulation can be extended by using [OnOffParameters](./OnOffParameters.md) -to define the on/off state of the Flow, or by using [InvestParameters](./InvestParameters.md) -to change the size of the Flow from a constant to an optimization variable. diff --git a/docs/user-guide/Mathematical Notation/InvestParameters.md b/docs/user-guide/Mathematical Notation/InvestParameters.md deleted file mode 100644 index d3cd4f81e..000000000 --- a/docs/user-guide/Mathematical Notation/InvestParameters.md +++ /dev/null @@ -1,3 +0,0 @@ -# InvestParameters - -This is a work in progress. diff --git a/docs/user-guide/Mathematical Notation/LinearConverter.md b/docs/user-guide/Mathematical Notation/LinearConverter.md deleted file mode 100644 index bf1279c32..000000000 --- a/docs/user-guide/Mathematical Notation/LinearConverter.md +++ /dev/null @@ -1,21 +0,0 @@ -[`LinearConverters`][flixopt.components.LinearConverter] define a ratio between incoming and outgoing [Flows](Flow.md). - -$$ \label{eq:Linear-Transformer-Ratio} - \sum_{f_{\text{in}} \in \mathcal F_{in}} \text a_{f_{\text{in}}}(\text{t}_i) \cdot p_{f_\text{in}}(\text{t}_i) = \sum_{f_{\text{out}} \in \mathcal F_{out}} \text b_{f_\text{out}}(\text{t}_i) \cdot p_{f_\text{out}}(\text{t}_i) -$$ - -With: - -- $\mathcal F_{in}$ and $\mathcal F_{out}$ being the set of all incoming and outgoing flows -- $p_{f_\text{in}}(\text{t}_i)$ and $p_{f_\text{out}}(\text{t}_i)$ being the flow-rate at time $\text{t}_i$ for flow $f_\text{in}$ and $f_\text{out}$, respectively -- $\text a_{f_\text{in}}(\text{t}_i)$ and $\text b_{f_\text{out}}(\text{t}_i)$ being the ratio of the flow-rate at time $\text{t}_i$ for flow $f_\text{in}$ and $f_\text{out}$, respectively - -With one incoming **Flow** and one outgoing **Flow**, this can be simplified to: - -$$ \label{eq:Linear-Transformer-Ratio-simple} - \text a(\text{t}_i) \cdot p_{f_\text{in}}(\text{t}_i) = p_{f_\text{out}}(\text{t}_i) -$$ - -where $\text a$ can be interpreted as the conversion efficiency of the **LinearConverter**. -#### Piecewise Conversion factors -The conversion efficiency can be defined as a piecewise linear approximation. See [Piecewise](Piecewise.md) for more details. diff --git a/docs/user-guide/Mathematical Notation/OnOffParameters.md b/docs/user-guide/Mathematical Notation/OnOffParameters.md deleted file mode 100644 index ca22d7d33..000000000 --- a/docs/user-guide/Mathematical Notation/OnOffParameters.md +++ /dev/null @@ -1,3 +0,0 @@ -# OnOffParameters - -This is a work in progress. diff --git a/docs/user-guide/Mathematical Notation/index.md b/docs/user-guide/Mathematical Notation/index.md deleted file mode 100644 index b76a1ba1f..000000000 --- a/docs/user-guide/Mathematical Notation/index.md +++ /dev/null @@ -1,22 +0,0 @@ - -# Mathematical Notation - -## Naming Conventions - -FlixOpt uses the following naming conventions: - -- All optimization variables are denoted by italic letters (e.g., $x$, $y$, $z$) -- All parameters and constants are denoted by non italic small letters (e.g., $\text{a}$, $\text{b}$, $\text{c}$) -- All Sets are denoted by greek capital letters (e.g., $\mathcal{F}$, $\mathcal{E}$) -- All units of a set are denoted by greek small letters (e.g., $\mathcal{f}$, $\mathcal{e}$) -- The letter $i$ is used to denote an index (e.g., $i=1,\dots,\text n$) -- All time steps are denoted by the letter $\text{t}$ (e.g., $\text{t}_0$, $\text{t}_1$, $\text{t}_i$) - -## Timesteps -Time steps are defined as a sequence of discrete time steps $\text{t}_i \in \mathcal{T} \quad \text{for} \quad i \in \{1, 2, \dots, \text{n}\}$ (left-aligned in its timespan). -From this sequence, the corresponding time intervals $\Delta \text{t}_i \in \Delta \mathcal{T}$ are derived as - -$$\Delta \text{t}_i = \text{t}_{i+1} - \text{t}_i \quad \text{for} \quad i \in \{1, 2, \dots, \text{n}-1\}$$ - -The final time interval $\Delta \text{t}_\text n$ defaults to $\Delta \text{t}_\text n = \Delta \text{t}_{\text n-1}$, but is of course customizable. -Non-equidistant time steps are also supported. diff --git a/docs/user-guide/index.md b/docs/user-guide/index.md index bc1738997..df97bf768 100644 --- a/docs/user-guide/index.md +++ b/docs/user-guide/index.md @@ -1,6 +1,8 @@ # FlixOpt Concepts -FlixOpt is built around a set of core concepts that work together to represent and optimize energy and material flow systems. This page provides a high-level overview of these concepts and how they interact. +FlixOpt is built around a set of core concepts that work together to represent and optimize **any system involving flows and conversions** - whether that's energy systems, material flows, supply chains, water networks, or production processes. + +This page provides a high-level overview of these concepts and how they interact. ## Core Concepts @@ -45,28 +47,49 @@ Examples: ### Components -[`Component`][flixopt.elements.Component] objects usually represent physical entities in your system that interact with [`Flows`][flixopt.elements.Flow]. They include: +[`Component`][flixopt.elements.Component] objects usually represent physical entities in your system that interact with [`Flows`][flixopt.elements.Flow]. The generic component types work across all domains: - [`LinearConverters`][flixopt.components.LinearConverter] - Converts input flows to output flows with (piecewise) linear relationships + - *Energy: boilers, heat pumps, turbines* + - *Manufacturing: assembly lines, processing equipment* + - *Chemistry: reactors, separators* - [`Storages`][flixopt.components.Storage] - Stores energy or material over time -- [`Sources`][flixopt.components.Source] / [`Sinks`][flixopt.components.Sink] / [`SourceAndSinks`][flixopt.components.SourceAndSink] - Produce or consume flows. They are usually used to model external demands or supplies. + - *Energy: batteries, thermal storage, gas storage* + - *Logistics: warehouses, buffer inventory* + - *Water: reservoirs, tanks* +- [`Sources`][flixopt.components.Source] / [`Sinks`][flixopt.components.Sink] / [`SourceAndSinks`][flixopt.components.SourceAndSink] - Produce or consume flows + - *Energy: demands, renewable generation* + - *Manufacturing: raw material supply, product demand* + - *Supply chain: suppliers, customers* - [`Transmissions`][flixopt.components.Transmission] - Moves flows between locations with possible losses -- Specialized [`LinearConverters`][flixopt.components.LinearConverter] like [`Boilers`][flixopt.linear_converters.Boiler], [`HeatPumps`][flixopt.linear_converters.HeatPump], [`CHPs`][flixopt.linear_converters.CHP], etc. These simplify the usage of the `LinearConverter` class and can also be used as blueprint on how to define custom classes or parameterize existing ones. + - *Energy: pipelines, power lines* + - *Logistics: transport routes* + - *Water: distribution networks* + +**Pre-built specialized components** for energy systems include [`Boilers`][flixopt.linear_converters.Boiler], [`HeatPumps`][flixopt.linear_converters.HeatPump], [`CHPs`][flixopt.linear_converters.CHP], etc. These can serve as blueprints for custom domain-specific components. ### Effects -[`Effect`][flixopt.effects.Effect] objects represent impacts or metrics related to your system, such as: +[`Effect`][flixopt.effects.Effect] objects represent impacts or metrics related to your system. While commonly used to allocate costs, they're completely flexible: +**Energy systems:** - Costs (investment, operation) - Emissions (COβ‚‚, NOx, etc.) -- Resource consumption -- Area demand +- Primary energy consumption + +**Other domains:** +- Production time, labor hours (manufacturing) +- Water consumption, wastewater (process industries) +- Transport distance, vehicle utilization (logistics) +- Space consumption +- Any custom metric relevant to your domain These can be freely defined and crosslink to each other (`COβ‚‚` ──[specific COβ‚‚-costs]─→ `Costs`). One effect is designated as the **optimization objective** (typically Costs), while others can be constrained. -This approach allows for a multi-criteria optimization using both... - - ... the **Weigted Sum**Method, by Optimizing a theoretical Effect which other Effects crosslink to. - - ... the ($\epsilon$-constraint method) by constraining effects. +This approach allows for multi-criteria optimization using both: + + - **Weighted Sum Method**: Optimize a theoretical Effect which other Effects crosslink to + - **Ξ΅-constraint method**: Constrain effects to specific limits ### Calculation diff --git a/docs/user-guide/mathematical-notation/dimensions.md b/docs/user-guide/mathematical-notation/dimensions.md new file mode 100644 index 000000000..e7104fa6a --- /dev/null +++ b/docs/user-guide/mathematical-notation/dimensions.md @@ -0,0 +1,242 @@ +# Dimensions + +FlixOpt's `FlowSystem` supports multiple dimensions for modeling optimization problems. Understanding these dimensions is crucial for interpreting the mathematical formulations presented in this documentation. + +## The Three Dimensions + +FlixOpt models can have up to three dimensions: + +1. **Time (`time`)** - **MANDATORY** + - Represents the temporal evolution of the system + - Defined via `pd.DatetimeIndex` + - Must contain at least 2 timesteps + - All optimization variables and constraints evolve over time +2. **Period (`period`)** - **OPTIONAL** + - Represents independent planning periods (e.g., years 2020, 2021, 2022) + - Defined via `pd.Index` with integer values + - Used for multi-period optimization such as investment planning across years + - Each period is independent with its own time series +3. **Scenario (`scenario`)** - **OPTIONAL** + - Represents alternative futures or uncertainty realizations (e.g., "Base Case", "High Demand") + - Defined via `pd.Index` with any labels + - Scenarios within the same period share the same time dimension + - Used for stochastic optimization or scenario comparison + +--- + +## Dimensional Structure + +**Coordinate System:** + +```python +FlowSystemDimensions = Literal['time', 'period', 'scenario'] + +coords = { + 'time': pd.DatetimeIndex, # Always present + 'period': pd.Index | None, # Optional + 'scenario': pd.Index | None # Optional +} +``` + +**Example:** +```python +import pandas as pd +import numpy as np +import flixopt as fx + +timesteps = pd.date_range('2020-01-01', periods=24, freq='h') +scenarios = pd.Index(['Base Case', 'High Demand']) +periods = pd.Index([2020, 2021, 2022]) + +flow_system = fx.FlowSystem( + timesteps=timesteps, + periods=periods, + scenarios=scenarios, + weights=np.array([0.5, 0.5]) # Scenario weights +) +``` + +This creates a system with: +- 24 time steps per scenario per period +- 2 scenarios with equal weights (0.5 each) +- 3 periods (years) +- **Total decision space:** 24 Γ— 2 Γ— 3 = 144 time-scenario-period combinations + +--- + +## Independence of Formulations + +**All mathematical formulations in this documentation are independent of whether periods or scenarios are present.** + +The equations shown throughout this documentation (for [Flow](elements/Flow.md), [Storage](elements/Storage.md), [Bus](elements/Bus.md), etc.) are written with only the time index $\text{t}_i$. When periods and/or scenarios are added, **the same equations apply** - they are simply expanded to additional dimensions. + +### How Dimensions Expand Formulations + +**Flow rate bounds** (from [Flow](elements/Flow.md)): + +$$ +\text{P} \cdot \text{p}^{\text{L}}_{\text{rel}}(\text{t}_{i}) \leq p(\text{t}_{i}) \leq \text{P} \cdot \text{p}^{\text{U}}_{\text{rel}}(\text{t}_{i}) +$$ + +This equation remains valid regardless of dimensions: + +| Dimensions Present | Variable Indexing | Interpretation | +|-------------------|-------------------|----------------| +| Time only | $p(\text{t}_i)$ | Flow rate at time $\text{t}_i$ | +| Time + Scenario | $p(\text{t}_i, s)$ | Flow rate at time $\text{t}_i$ in scenario $s$ | +| Time + Period | $p(\text{t}_i, y)$ | Flow rate at time $\text{t}_i$ in period $y$ | +| Time + Period + Scenario | $p(\text{t}_i, y, s)$ | Flow rate at time $\text{t}_i$ in period $y$, scenario $s$ | + +**The mathematical relationship remains identical** - only the indexing expands. + +--- + +## Independence Between Scenarios and Periods + +**There is no interconnection between scenarios and periods, except for shared investment decisions within a period.** + +### Scenario Independence + +Scenarios within a period are **operationally independent**: + +- Each scenario has its own operational variables: $p(\text{t}_i, s_1)$ and $p(\text{t}_i, s_2)$ are independent +- Scenarios cannot exchange energy, information, or resources +- Storage states are separate: $c(\text{t}_i, s_1) \neq c(\text{t}_i, s_2)$ +- Binary states (on/off) are independent: $s(\text{t}_i, s_1)$ vs $s(\text{t}_i, s_2)$ + +Scenarios are connected **only through the objective function** via weights: + +$$ +\min \quad \sum_{s \in \mathcal{S}} w_s \cdot \text{Objective}_s +$$ + +Where: +- $\mathcal{S}$ is the set of scenarios +- $w_s$ is the weight for scenario $s$ +- The optimizer balances performance across scenarios according to their weights + +### Period Independence + +Periods are **completely independent** optimization problems: + +- Each period has separate operational variables +- Each period has separate investment decisions +- No temporal coupling between periods (e.g., storage state at end of period $y$ does not affect period $y+1$) +- Periods cannot exchange resources or information + +Periods are connected **only through weighted aggregation** in the objective: + +$$ +\min \quad \sum_{y \in \mathcal{Y}} w_y \cdot \text{Objective}_y +$$ + +### Shared Periodic Decisions: The Exception + +**Within a period, periodic (investment) decisions are shared across all scenarios:** + +If a period has multiple scenarios, periodic variables (e.g., component size) are **scenario-independent** but **temporal variables are scenario-specific**. + +**Example - Flow with investment:** + +$$ +v_\text{invest}(y) = s_\text{invest}(y) \cdot \text{size}_\text{fixed} \quad \text{(one decision per period)} +$$ + +$$ +p(\text{t}_i, y, s) \leq v_\text{invest}(y) \cdot \text{rel}_\text{upper} \quad \forall s \in \mathcal{S} \quad \text{(same capacity for all scenarios)} +$$ + +**Interpretation:** +- "We decide once in period $y$ how much capacity to build" (periodic decision) +- "This capacity is then operated differently in each scenario $s$ within period $y$" (temporal decisions) +- "Periodic effects (investment) are incurred once per period, temporal effects (operational) are weighted across scenarios" + +This reflects real-world investment under uncertainty: you build capacity once (periodic/investment decision), but it operates under different conditions (temporal/operational decisions per scenario). + +--- + +## Dimensional Impact on Objective Function + +The objective function aggregates effects across all dimensions with weights: + +### Time Only +$$ +\min \quad \sum_{\text{t}_i \in \mathcal{T}} \sum_{e \in \mathcal{E}} s_{e}(\text{t}_i) +$$ + +### Time + Scenario +$$ +\min \quad \sum_{s \in \mathcal{S}} w_s \cdot \left( \sum_{\text{t}_i \in \mathcal{T}} \sum_{e \in \mathcal{E}} s_{e}(\text{t}_i, s) \right) +$$ + +### Time + Period +$$ +\min \quad \sum_{y \in \mathcal{Y}} w_y \cdot \left( \sum_{\text{t}_i \in \mathcal{T}} \sum_{e \in \mathcal{E}} s_{e}(\text{t}_i, y) \right) +$$ + +### Time + Period + Scenario (Full Multi-Dimensional) +$$ +\min \quad \sum_{y \in \mathcal{Y}} \sum_{s \in \mathcal{S}} w_{y,s} \cdot \left( \sum_{\text{t}_i \in \mathcal{T}} \sum_{e \in \mathcal{E}} s_{e}(\text{t}_i, y, s) \right) +$$ + +Where: +- $\mathcal{T}$ is the set of time steps +- $\mathcal{E}$ is the set of effects +- $\mathcal{S}$ is the set of scenarios +- $\mathcal{Y}$ is the set of periods +- $s_{e}(\cdots)$ are the effect contributions (costs, emissions, etc.) +- $w_s, w_y, w_{y,s}$ are the dimension weights + +**See [Effects, Penalty & Objective](effects-penalty-objective.md) for complete formulations including:** +- How temporal and periodic effects expand with dimensions +- Detailed objective function for each dimensional case +- Periodic (investment) vs temporal (operational) effect handling + +--- + +## Weights + +Weights determine the relative importance of scenarios and periods in the objective function. + +**Specification:** + +```python +flow_system = fx.FlowSystem( + timesteps=timesteps, + periods=periods, + scenarios=scenarios, + weights=weights # Shape depends on dimensions +) +``` + +**Weight Dimensions:** + +| Dimensions Present | Weight Shape | Example | Meaning | +|-------------------|--------------|---------|---------| +| Time + Scenario | 1D array of length `n_scenarios` | `[0.3, 0.7]` | Scenario probabilities | +| Time + Period | 1D array of length `n_periods` | `[0.5, 0.3, 0.2]` | Period importance | +| Time + Period + Scenario | 2D array `(n_periods, n_scenarios)` | `[[0.25, 0.25], [0.25, 0.25]]` | Combined weights | + +**Default:** If not specified, all scenarios/periods have equal weight (normalized to sum to 1). + +**Normalization:** Set `normalize_weights=True` in `Calculation` to automatically normalize weights to sum to 1. + +--- + +## Summary Table + +| Dimension | Required? | Independence | Typical Use Case | +|-----------|-----------|--------------|------------------| +| **time** | βœ… Yes | Variables evolve over time via constraints (e.g., storage balance) | All optimization problems | +| **scenario** | ❌ No | Fully independent operations; shared investments within period | Uncertainty modeling, risk assessment | +| **period** | ❌ No | Fully independent; no coupling between periods | Multi-year planning, long-term investment | + +**Key Principle:** All constraints and formulations operate **within** each (period, scenario) combination independently. Only the objective function couples them via weighted aggregation. + +--- + +## See Also + +- [Effects, Penalty & Objective](effects-penalty-objective.md) - How dimensions affect the objective function +- [InvestParameters](features/InvestParameters.md) - Investment decisions across scenarios +- [FlowSystem API][flixopt.flow_system.FlowSystem] - Creating multi-dimensional systems diff --git a/docs/user-guide/mathematical-notation/effects-penalty-objective.md b/docs/user-guide/mathematical-notation/effects-penalty-objective.md new file mode 100644 index 000000000..0759ef5ee --- /dev/null +++ b/docs/user-guide/mathematical-notation/effects-penalty-objective.md @@ -0,0 +1,286 @@ +# Effects, Penalty & Objective + +## Effects + +[`Effects`][flixopt.effects.Effect] are used to quantify system-wide impacts like costs, emissions, or resource consumption. These arise from **shares** contributed by **Elements** such as [Flows](elements/Flow.md), [Storage](elements/Storage.md), and other components. + +**Example:** + +[`Flows`][flixopt.elements.Flow] have an attribute `effects_per_flow_hour` that defines the effect contribution per flow-hour: +- Costs (€/kWh) +- Emissions (kg COβ‚‚/kWh) +- Primary energy consumption (kWh_primary/kWh) + +Effects are categorized into two domains: + +1. **Temporal effects** - Time-dependent contributions (e.g., operational costs, hourly emissions) +2. **Periodic effects** - Time-independent contributions (e.g., investment costs, fixed annual fees) + +### Multi-Dimensional Effects + +**The formulations below are written with time index $\text{t}_i$ only, but automatically expand when periods and/or scenarios are present.** + +When the FlowSystem has additional dimensions (see [Dimensions](dimensions.md)): + +- **Temporal effects** are indexed by all present dimensions: $E_{e,\text{temp}}(\text{t}_i, y, s)$ +- **Periodic effects** are indexed by period only (scenario-independent within a period): $E_{e,\text{per}}(y)$ +- Effects are aggregated with dimension weights in the objective function + +For complete details on how dimensions affect effects and the objective, see [Dimensions](dimensions.md). + +--- + +## Effect Formulation + +### Shares from Elements + +Each element $l$ contributes shares to effect $e$ in both temporal and periodic domains: + +**Periodic shares** (time-independent): +$$ \label{eq:Share_periodic} +s_{l \rightarrow e, \text{per}} = \sum_{v \in \mathcal{V}_{l, \text{per}}} v \cdot \text{a}_{v \rightarrow e} +$$ + +**Temporal shares** (time-dependent): +$$ \label{eq:Share_temporal} +s_{l \rightarrow e, \text{temp}}(\text{t}_i) = \sum_{v \in \mathcal{V}_{l,\text{temp}}} v(\text{t}_i) \cdot \text{a}_{v \rightarrow e}(\text{t}_i) +$$ + +Where: + +- $\text{t}_i$ is the time step +- $\mathcal{V}_l$ is the set of all optimization variables of element $l$ +- $\mathcal{V}_{l, \text{per}}$ is the subset of periodic (investment-related) variables +- $\mathcal{V}_{l, \text{temp}}$ is the subset of temporal (operational) variables +- $v$ is an optimization variable +- $v(\text{t}_i)$ is the variable value at timestep $\text{t}_i$ +- $\text{a}_{v \rightarrow e}$ is the effect factor (e.g., €/kW for investment, €/kWh for operation) +- $s_{l \rightarrow e, \text{per}}$ is the periodic share of element $l$ to effect $e$ +- $s_{l \rightarrow e, \text{temp}}(\text{t}_i)$ is the temporal share of element $l$ to effect $e$ + +**Examples:** +- **Periodic share**: Investment cost = $\text{size} \cdot \text{specific\_cost}$ (€/kW) +- **Temporal share**: Operational cost = $\text{flow\_rate}(\text{t}_i) \cdot \text{price}(\text{t}_i)$ (€/kWh) + +--- + +### Cross-Effect Contributions + +Effects can contribute shares to other effects, enabling relationships like carbon pricing or resource accounting. + +An effect $x$ can contribute to another effect $e \in \mathcal{E}\backslash x$ via conversion factors: + +**Example:** COβ‚‚ emissions (kg) β†’ Monetary costs (€) +- Effect $x$: "COβ‚‚ emissions" (unit: kg) +- Effect $e$: "costs" (unit: €) +- Factor $\text{r}_{x \rightarrow e}$: COβ‚‚ price (€/kg) + +**Note:** Circular references must be avoided. + +### Total Effect Calculation + +**Periodic effects** aggregate element shares and cross-effect contributions: + +$$ \label{eq:Effect_periodic} +E_{e, \text{per}} = +\sum_{l \in \mathcal{L}} s_{l \rightarrow e,\text{per}} + +\sum_{x \in \mathcal{E}\backslash e} E_{x, \text{per}} \cdot \text{r}_{x \rightarrow e,\text{per}} +$$ + +**Temporal effects** at each timestep: + +$$ \label{eq:Effect_temporal} +E_{e, \text{temp}}(\text{t}_{i}) = +\sum_{l \in \mathcal{L}} s_{l \rightarrow e, \text{temp}}(\text{t}_i) + +\sum_{x \in \mathcal{E}\backslash e} E_{x, \text{temp}}(\text{t}_i) \cdot \text{r}_{x \rightarrow {e},\text{temp}}(\text{t}_i) +$$ + +**Total temporal effects** (sum over all timesteps): + +$$\label{eq:Effect_temporal_total} +E_{e,\text{temp},\text{tot}} = \sum_{i=1}^n E_{e,\text{temp}}(\text{t}_{i}) +$$ + +**Total effect** (combining both domains): + +$$ \label{eq:Effect_Total} +E_{e} = E_{e,\text{per}} + E_{e,\text{temp},\text{tot}} +$$ + +Where: + +- $\mathcal{L}$ is the set of all elements in the FlowSystem +- $\mathcal{E}$ is the set of all effects +- $\text{r}_{x \rightarrow e, \text{per}}$ is the periodic conversion factor from effect $x$ to effect $e$ +- $\text{r}_{x \rightarrow e, \text{temp}}(\text{t}_i)$ is the temporal conversion factor + +--- + +### Constraining Effects + +Effects can be bounded to enforce limits on costs, emissions, or other impacts: + +**Total bounds** (apply to $E_{e,\text{per}}$, $E_{e,\text{temp},\text{tot}}$, or $E_e$): + +$$ \label{eq:Bounds_Total} +E^\text{L} \leq E \leq E^\text{U} +$$ + +**Temporal bounds per timestep:** + +$$ \label{eq:Bounds_Timestep} +E_{e,\text{temp}}^\text{L}(\text{t}_i) \leq E_{e,\text{temp}}(\text{t}_i) \leq E_{e,\text{temp}}^\text{U}(\text{t}_i) +$$ + +**Implementation:** See [`Effect`][flixopt.effects.Effect] parameters: +- `minimum_temporal`, `maximum_temporal` - Total temporal bounds +- `minimum_per_hour`, `maximum_per_hour` - Hourly temporal bounds +- `minimum_periodic`, `maximum_periodic` - Periodic bounds +- `minimum_total`, `maximum_total` - Combined total bounds + +--- + +## Penalty + +In addition to user-defined [Effects](#effects), every FlixOpt model includes a **Penalty** term $\Phi$ to: +- Prevent infeasible problems +- Simplify troubleshooting by allowing constraint violations with high cost + +Penalty shares originate from elements, similar to effect shares: + +$$ \label{eq:Penalty} +\Phi = \sum_{l \in \mathcal{L}} \left( s_{l \rightarrow \Phi} +\sum_{\text{t}_i \in \mathcal{T}} s_{l \rightarrow \Phi}(\text{t}_{i}) \right) +$$ + +Where: + +- $\mathcal{L}$ is the set of all elements +- $\mathcal{T}$ is the set of all timesteps +- $s_{l \rightarrow \Phi}$ is the penalty share from element $l$ + +**Current usage:** Penalties primarily occur in [Buses](elements/Bus.md) via the `excess_penalty_per_flow_hour` parameter, which allows nodal imbalances at a high cost. + +--- + +## Objective Function + +The optimization objective minimizes the chosen effect plus any penalties: + +$$ \label{eq:Objective} +\min \left( E_{\Omega} + \Phi \right) +$$ + +Where: + +- $E_{\Omega}$ is the chosen **objective effect** (see $\eqref{eq:Effect_Total}$) +- $\Phi$ is the [penalty](#penalty) term + +One effect must be designated as the objective via `is_objective=True`. + +### Multi-Criteria Optimization + +This formulation supports multiple optimization approaches: + +**1. Weighted Sum Method** +- The objective effect can incorporate other effects via cross-effect factors +- Example: Minimize costs while including carbon pricing: $\text{CO}_2 \rightarrow \text{costs}$ + +**2. Ξ΅-Constraint Method** +- Optimize one effect while constraining others +- Example: Minimize costs subject to $\text{CO}_2 \leq 1000$ kg + +--- + +## Objective with Multiple Dimensions + +When the FlowSystem includes **periods** and/or **scenarios** (see [Dimensions](dimensions.md)), the objective aggregates effects across all dimensions using weights. + +### Time Only (Base Case) + +$$ +\min \quad E_{\Omega} + \Phi = \sum_{\text{t}_i \in \mathcal{T}} E_{\Omega,\text{temp}}(\text{t}_i) + E_{\Omega,\text{per}} + \Phi +$$ + +Where: +- Temporal effects sum over time: $\sum_{\text{t}_i} E_{\Omega,\text{temp}}(\text{t}_i)$ +- Periodic effects are constant: $E_{\Omega,\text{per}}$ +- Penalty sums over time: $\Phi = \sum_{\text{t}_i} \Phi(\text{t}_i)$ + +--- + +### Time + Scenario + +$$ +\min \quad \sum_{s \in \mathcal{S}} w_s \cdot \left( E_{\Omega}(s) + \Phi(s) \right) +$$ + +Where: +- $\mathcal{S}$ is the set of scenarios +- $w_s$ is the weight for scenario $s$ (typically scenario probability) +- Periodic effects are **shared across scenarios**: $E_{\Omega,\text{per}}$ (same for all $s$) +- Temporal effects are **scenario-specific**: $E_{\Omega,\text{temp}}(s) = \sum_{\text{t}_i} E_{\Omega,\text{temp}}(\text{t}_i, s)$ +- Penalties are **scenario-specific**: $\Phi(s) = \sum_{\text{t}_i} \Phi(\text{t}_i, s)$ + +**Interpretation:** +- Investment decisions (periodic) made once, used across all scenarios +- Operations (temporal) differ by scenario +- Objective balances expected value across scenarios + +--- + +### Time + Period + +$$ +\min \quad \sum_{y \in \mathcal{Y}} w_y \cdot \left( E_{\Omega}(y) + \Phi(y) \right) +$$ + +Where: +- $\mathcal{Y}$ is the set of periods (e.g., years) +- $w_y$ is the weight for period $y$ (typically annual discount factor) +- Each period $y$ has **independent** periodic and temporal effects +- Each period $y$ has **independent** investment and operational decisions + +--- + +### Time + Period + Scenario (Full Multi-Dimensional) + +$$ +\min \quad \sum_{y \in \mathcal{Y}} \left[ w_y \cdot E_{\Omega,\text{per}}(y) + \sum_{s \in \mathcal{S}} w_{y,s} \cdot \left( E_{\Omega,\text{temp}}(y,s) + \Phi(y,s) \right) \right] +$$ + +Where: +- $\mathcal{S}$ is the set of scenarios +- $\mathcal{Y}$ is the set of periods +- $w_y$ is the period weight (for periodic effects) +- $w_{y,s}$ is the combined period-scenario weight (for temporal effects) +- **Periodic effects** $E_{\Omega,\text{per}}(y)$ are period-specific but **scenario-independent** +- **Temporal effects** $E_{\Omega,\text{temp}}(y,s) = \sum_{\text{t}_i} E_{\Omega,\text{temp}}(\text{t}_i, y, s)$ are **fully indexed** +- **Penalties** $\Phi(y,s)$ are **fully indexed** + +**Key Principle:** +- Scenarios and periods are **operationally independent** (no energy/resource exchange) +- Coupled **only through the weighted objective function** +- **Periodic effects within a period are shared across all scenarios** (investment made once per period) +- **Temporal effects are independent per scenario** (different operations under different conditions) + +--- + +## Summary + +| Concept | Formulation | Time Dependency | Dimension Indexing | +|---------|-------------|-----------------|-------------------| +| **Temporal share** | $s_{l \rightarrow e, \text{temp}}(\text{t}_i)$ | Time-dependent | $(t, y, s)$ when present | +| **Periodic share** | $s_{l \rightarrow e, \text{per}}$ | Time-independent | $(y)$ when periods present | +| **Total temporal effect** | $E_{e,\text{temp},\text{tot}} = \sum_{\text{t}_i} E_{e,\text{temp}}(\text{t}_i)$ | Sum over time | Depends on dimensions | +| **Total periodic effect** | $E_{e,\text{per}}$ | Constant | $(y)$ when periods present | +| **Total effect** | $E_e = E_{e,\text{per}} + E_{e,\text{temp},\text{tot}}$ | Combined | Depends on dimensions | +| **Objective** | $\min(E_{\Omega} + \Phi)$ | With weights when multi-dimensional | See formulations above | + +--- + +## See Also + +- [Dimensions](dimensions.md) - Complete explanation of multi-dimensional modeling +- [Flow](elements/Flow.md) - Temporal effect contributions via `effects_per_flow_hour` +- [InvestParameters](features/InvestParameters.md) - Periodic effect contributions via investment +- [Effect API][flixopt.effects.Effect] - Implementation details and parameters diff --git a/docs/user-guide/Mathematical Notation/Bus.md b/docs/user-guide/mathematical-notation/elements/Bus.md similarity index 78% rename from docs/user-guide/Mathematical Notation/Bus.md rename to docs/user-guide/mathematical-notation/elements/Bus.md index 6ba17eede..bfe57d234 100644 --- a/docs/user-guide/Mathematical Notation/Bus.md +++ b/docs/user-guide/mathematical-notation/elements/Bus.md @@ -31,3 +31,19 @@ With: - $\text{t}_i$ being the time step - $s_{b \rightarrow \Phi}(\text{t}_i)$ being the penalty term - $\text a_{b \rightarrow \Phi}(\text{t}_i)$ being the penalty coefficient (`excess_penalty_per_flow_hour`) + +--- + +## Implementation + +**Python Class:** [`Bus`][flixopt.elements.Bus] + +See the API documentation for implementation details and usage examples. + +--- + +## See Also + +- [Flow](../elements/Flow.md) - Definition of flow rates in the balance +- [Effects, Penalty & Objective](../effects-penalty-objective.md) - How penalties are included in the objective function +- [Modeling Patterns](../modeling-patterns/index.md) - Mathematical building blocks diff --git a/docs/user-guide/mathematical-notation/elements/Flow.md b/docs/user-guide/mathematical-notation/elements/Flow.md new file mode 100644 index 000000000..5914ba911 --- /dev/null +++ b/docs/user-guide/mathematical-notation/elements/Flow.md @@ -0,0 +1,64 @@ +# Flow + +The flow_rate is the main optimization variable of the Flow. It's limited by the size of the Flow and relative bounds \eqref{eq:flow_rate}. + +$$ \label{eq:flow_rate} + \text P \cdot \text p^{\text{L}}_{\text{rel}}(\text{t}_{i}) + \leq p(\text{t}_{i}) \leq + \text P \cdot \text p^{\text{U}}_{\text{rel}}(\text{t}_{i}) +$$ + +With: + +- $\text P$ being the size of the Flow +- $p(\text{t}_{i})$ being the flow-rate at time $\text{t}_{i}$ +- $\text p^{\text{L}}_{\text{rel}}(\text{t}_{i})$ being the relative lower bound (typically 0) +- $\text p^{\text{U}}_{\text{rel}}(\text{t}_{i})$ being the relative upper bound (typically 1) + +With $\text p^{\text{L}}_{\text{rel}}(\text{t}_{i}) = 0$ and $\text p^{\text{U}}_{\text{rel}}(\text{t}_{i}) = 1$, +equation \eqref{eq:flow_rate} simplifies to + +$$ + 0 \leq p(\text{t}_{i}) \leq \text P +$$ + + +This mathematical formulation can be extended by using [OnOffParameters](../features/OnOffParameters.md) +to define the on/off state of the Flow, or by using [InvestParameters](../features/InvestParameters.md) +to change the size of the Flow from a constant to an optimization variable. + +--- + +## Mathematical Patterns Used + +Flow formulation uses the following modeling patterns: + +- **[Scaled Bounds](../modeling-patterns/bounds-and-states.md#scaled-bounds)** - Basic flow rate bounds (equation $\eqref{eq:flow_rate}$) +- **[Scaled Bounds with State](../modeling-patterns/bounds-and-states.md#scaled-bounds-with-state)** - When combined with [OnOffParameters](../features/OnOffParameters.md) +- **[Bounds with State](../modeling-patterns/bounds-and-states.md#bounds-with-state)** - Investment decisions with [InvestParameters](../features/InvestParameters.md) + +--- + +## Implementation + +**Python Class:** [`Flow`][flixopt.elements.Flow] + +**Key Parameters:** +- `size`: Flow size $\text{P}$ (can be fixed or variable with InvestParameters) +- `relative_minimum`, `relative_maximum`: Relative bounds $\text{p}^{\text{L}}_{\text{rel}}, \text{p}^{\text{U}}_{\text{rel}}$ +- `effects_per_flow_hour`: Operational effects (costs, emissions, etc.) +- `invest_parameters`: Optional investment modeling (see [InvestParameters](../features/InvestParameters.md)) +- `on_off_parameters`: Optional on/off operation (see [OnOffParameters](../features/OnOffParameters.md)) + +See the [`Flow`][flixopt.elements.Flow] API documentation for complete parameter list and usage examples. + +--- + +## See Also + +- [OnOffParameters](../features/OnOffParameters.md) - Binary on/off operation +- [InvestParameters](../features/InvestParameters.md) - Variable flow sizing +- [Bus](../elements/Bus.md) - Flow balance constraints +- [LinearConverter](../elements/LinearConverter.md) - Flow ratio constraints +- [Storage](../elements/Storage.md) - Flow integration over time +- [Modeling Patterns](../modeling-patterns/index.md) - Mathematical building blocks diff --git a/docs/user-guide/mathematical-notation/elements/LinearConverter.md b/docs/user-guide/mathematical-notation/elements/LinearConverter.md new file mode 100644 index 000000000..b007aa7f5 --- /dev/null +++ b/docs/user-guide/mathematical-notation/elements/LinearConverter.md @@ -0,0 +1,50 @@ +[`LinearConverters`][flixopt.components.LinearConverter] define a ratio between incoming and outgoing [Flows](../elements/Flow.md). + +$$ \label{eq:Linear-Transformer-Ratio} + \sum_{f_{\text{in}} \in \mathcal F_{in}} \text a_{f_{\text{in}}}(\text{t}_i) \cdot p_{f_\text{in}}(\text{t}_i) = \sum_{f_{\text{out}} \in \mathcal F_{out}} \text b_{f_\text{out}}(\text{t}_i) \cdot p_{f_\text{out}}(\text{t}_i) +$$ + +With: + +- $\mathcal F_{in}$ and $\mathcal F_{out}$ being the set of all incoming and outgoing flows +- $p_{f_\text{in}}(\text{t}_i)$ and $p_{f_\text{out}}(\text{t}_i)$ being the flow-rate at time $\text{t}_i$ for flow $f_\text{in}$ and $f_\text{out}$, respectively +- $\text a_{f_\text{in}}(\text{t}_i)$ and $\text b_{f_\text{out}}(\text{t}_i)$ being the ratio of the flow-rate at time $\text{t}_i$ for flow $f_\text{in}$ and $f_\text{out}$, respectively + +With one incoming **Flow** and one outgoing **Flow**, this can be simplified to: + +$$ \label{eq:Linear-Transformer-Ratio-simple} + \text a(\text{t}_i) \cdot p_{f_\text{in}}(\text{t}_i) = p_{f_\text{out}}(\text{t}_i) +$$ + +where $\text a$ can be interpreted as the conversion efficiency of the **LinearConverter**. + +#### Piecewise Conversion factors +The conversion efficiency can be defined as a piecewise linear approximation. See [Piecewise](../features/Piecewise.md) for more details. + +--- + +## Implementation + +**Python Class:** [`LinearConverter`][flixopt.components.LinearConverter] + +**Specialized Linear Converters:** + +FlixOpt provides specialized linear converter classes for common applications: + +- **[`HeatPump`][flixopt.linear_converters.HeatPump]** - Coefficient of Performance (COP) based conversion +- **[`Power2Heat`][flixopt.linear_converters.Power2Heat]** - Electric heating with efficiency ≀ 1 +- **[`CHP`][flixopt.linear_converters.CHP]** - Combined heat and power generation +- **[`Boiler`][flixopt.linear_converters.Boiler]** - Fuel to heat conversion + +These classes handle the mathematical formulation automatically based on physical relationships. + +See the API documentation for implementation details and usage examples. + +--- + +## See Also + +- [Flow](../elements/Flow.md) - Definition of flow rates +- [Piecewise](../features/Piecewise.md) - Non-linear conversion efficiency modeling +- [InvestParameters](../features/InvestParameters.md) - Variable converter sizing +- [Modeling Patterns](../modeling-patterns/index.md) - Mathematical building blocks diff --git a/docs/user-guide/Mathematical Notation/Storage.md b/docs/user-guide/mathematical-notation/elements/Storage.md similarity index 52% rename from docs/user-guide/Mathematical Notation/Storage.md rename to docs/user-guide/mathematical-notation/elements/Storage.md index 63f01d198..cd7046592 100644 --- a/docs/user-guide/Mathematical Notation/Storage.md +++ b/docs/user-guide/mathematical-notation/elements/Storage.md @@ -1,5 +1,5 @@ # Storages -**Storages** have one incoming and one outgoing **[Flow](Flow.md)** with a charging and discharging efficiency. +**Storages** have one incoming and one outgoing **[Flow](../elements/Flow.md)** with a charging and discharging efficiency. A storage has a state of charge $c(\text{t}_i)$ which is limited by its `size` $\text C$ and relative bounds $\eqref{eq:Storage_Bounds}$. $$ \label{eq:Storage_Bounds} @@ -25,9 +25,9 @@ $ \dot{ \text c}_\text{rel, loss}(\text{t}_i)$ expresses the "loss fraction per $$ \begin{align*} - c(\text{t}_{i+1}) &= c(\text{t}_{i}) \cdot (1-\dot{\text{c}}_\text{rel,loss}(\text{t}_i) \cdot \Delta \text{t}_{i}) \\ + c(\text{t}_{i+1}) &= c(\text{t}_{i}) \cdot (1-\dot{\text{c}}_\text{rel,loss}(\text{t}_i))^{\Delta \text{t}_{i}} \\ &\quad + p_{f_\text{in}}(\text{t}_i) \cdot \Delta \text{t}_i \cdot \eta_\text{in}(\text{t}_i) \\ - &\quad - \frac{p_{f_\text{out}}(\text{t}_i) \cdot \Delta \text{t}_i}{\eta_\text{out}(\text{t}_i)} + &\quad - p_{f_\text{out}}(\text{t}_i) \cdot \Delta \text{t}_i \cdot \eta_\text{out}(\text{t}_i) \tag{3} \end{align*} $$ @@ -42,3 +42,38 @@ Where: - $\eta_\text{in}(\text{t}_i)$ is the charging efficiency at time $\text{t}_i$ - $p_{f_\text{out}}(\text{t}_i)$ is the output flow rate at time $\text{t}_i$ - $\eta_\text{out}(\text{t}_i)$ is the discharging efficiency at time $\text{t}_i$ + +--- + +## Mathematical Patterns Used + +Storage formulation uses the following modeling patterns: + +- **[Basic Bounds](../modeling-patterns/bounds-and-states.md#basic-bounds)** - For charge state bounds (equation $\eqref{eq:Storage_Bounds}$) +- **[Scaled Bounds](../modeling-patterns/bounds-and-states.md#scaled-bounds)** - For flow rate bounds relative to storage size + +When combined with investment parameters, storage can use: +- **[Bounds with State](../modeling-patterns/bounds-and-states.md#bounds-with-state)** - Investment decisions (see [InvestParameters](../features/InvestParameters.md)) + +--- + +## Implementation + +**Python Class:** [`Storage`][flixopt.components.Storage] + +**Key Parameters:** +- `capacity_in_flow_hours`: Storage capacity $\text{C}$ +- `relative_loss_per_hour`: Self-discharge rate $\dot{\text{c}}_\text{rel,loss}$ +- `initial_charge_state`: Initial charge $c(\text{t}_0)$ +- `minimal_final_charge_state`, `maximal_final_charge_state`: Final charge bounds $c(\text{t}_\text{end})$ (optional) +- `eta_charge`, `eta_discharge`: Charging/discharging efficiencies $\eta_\text{in}, \eta_\text{out}$ + +See the [`Storage`][flixopt.components.Storage] API documentation for complete parameter list and usage examples. + +--- + +## See Also + +- [Flow](../elements/Flow.md) - Input and output flow definitions +- [InvestParameters](../features/InvestParameters.md) - Variable storage sizing +- [Modeling Patterns](../modeling-patterns/index.md) - Mathematical building blocks diff --git a/docs/user-guide/mathematical-notation/features/InvestParameters.md b/docs/user-guide/mathematical-notation/features/InvestParameters.md new file mode 100644 index 000000000..45c338630 --- /dev/null +++ b/docs/user-guide/mathematical-notation/features/InvestParameters.md @@ -0,0 +1,301 @@ +# InvestParameters + +[`InvestParameters`][flixopt.interface.InvestParameters] model investment decisions in optimization problems, enabling both binary (invest/don't invest) and continuous sizing choices with comprehensive cost modeling. + +## Investment Decision Types + +FlixOpt supports two main types of investment decisions: + +### Binary Investment + +Fixed-size investment creating a yes/no decision (e.g., install a 100 kW generator): + +$$\label{eq:invest_binary} +v_\text{invest} = s_\text{invest} \cdot \text{size}_\text{fixed} +$$ + +With: +- $v_\text{invest}$ being the resulting investment size +- $s_\text{invest} \in \{0, 1\}$ being the binary investment decision +- $\text{size}_\text{fixed}$ being the predefined component size + +**Behavior:** +- $s_\text{invest} = 0$: no investment ($v_\text{invest} = 0$) +- $s_\text{invest} = 1$: invest at fixed size ($v_\text{invest} = \text{size}_\text{fixed}$) + +--- + +### Continuous Sizing + +Variable-size investment with bounds (e.g., battery capacity from 10-1000 kWh): + +$$\label{eq:invest_continuous} +s_\text{invest} \cdot \text{size}_\text{min} \leq v_\text{invest} \leq s_\text{invest} \cdot \text{size}_\text{max} +$$ + +With: +- $v_\text{invest}$ being the investment size variable (continuous) +- $s_\text{invest} \in \{0, 1\}$ being the binary investment decision +- $\text{size}_\text{min}$ being the minimum investment size (if investing) +- $\text{size}_\text{max}$ being the maximum investment size + +**Behavior:** +- $s_\text{invest} = 0$: no investment ($v_\text{invest} = 0$) +- $s_\text{invest} = 1$: invest with size in $[\text{size}_\text{min}, \text{size}_\text{max}]$ + +This uses the **bounds with state** pattern described in [Bounds and States](../modeling-patterns/bounds-and-states.md#bounds-with-state). + +--- + +### Optional vs. Mandatory Investment + +The `optional` parameter controls whether investment is required: + +**Optional Investment** (`optional=True`): +$$\label{eq:invest_optional} +s_\text{invest} \in \{0, 1\} +$$ + +The optimization can freely choose to invest or not. + +**Mandatory Investment** (`optional=False`): +$$\label{eq:invest_mandatory} +s_\text{invest} = 1 +$$ + +The investment must occur (useful for mandatory upgrades or replacements). + +--- + +## Effect Modeling + +Investment effects (costs, emissions, etc.) are modeled using three components: + +### Fixed Effects + +One-time effects incurred if investment is made, independent of size: + +$$\label{eq:invest_fixed_effects} +E_{e,\text{fix}} = s_\text{invest} \cdot \text{fix}_e +$$ + +With: +- $E_{e,\text{fix}}$ being the fixed contribution to effect $e$ +- $\text{fix}_e$ being the fixed effect value (e.g., fixed installation cost) + +**Examples:** +- Fixed installation costs (permits, grid connection) +- One-time environmental impacts (land preparation) +- Fixed labor or administrative costs + +--- + +### Specific Effects + +Effects proportional to investment size (per-unit costs): + +$$\label{eq:invest_specific_effects} +E_{e,\text{spec}} = v_\text{invest} \cdot \text{spec}_e +$$ + +With: +- $E_{e,\text{spec}}$ being the size-dependent contribution to effect $e$ +- $\text{spec}_e$ being the specific effect value per unit size (e.g., €/kW) + +**Examples:** +- Equipment costs (€/kW) +- Material requirements (kg steel/kW) +- Recurring costs (€/kW/year maintenance) + +--- + +### Piecewise Effects + +Non-linear effect relationships using piecewise linear approximations: + +$$\label{eq:invest_piecewise_effects} +E_{e,\text{pw}} = \sum_{k=1}^{K} \lambda_k \cdot r_{e,k} +$$ + +Subject to: +$$ +v_\text{invest} = \sum_{k=1}^{K} \lambda_k \cdot v_k +$$ + +With: +- $E_{e,\text{pw}}$ being the piecewise contribution to effect $e$ +- $\lambda_k$ being the piecewise lambda variables (see [Piecewise](../features/Piecewise.md)) +- $r_{e,k}$ being the effect rate at piece $k$ +- $v_k$ being the size points defining the pieces + +**Use cases:** +- Economies of scale (bulk discounts) +- Technology learning curves +- Threshold effects (capacity tiers with different costs) + +See [Piecewise](../features/Piecewise.md) for detailed mathematical formulation. + +--- + +### Divestment Effects + +Costs incurred if investment is NOT made: + +$$\label{eq:invest_divest_effects} +E_{e,\text{divest}} = (1 - s_\text{invest}) \cdot \text{divest}_e +$$ + +With: +- $E_{e,\text{divest}}$ being the divestment contribution to effect $e$ +- $\text{divest}_e$ being the divestment effect value + +**Behavior:** +- $s_\text{invest} = 0$: divestment effects are incurred +- $s_\text{invest} = 1$: no divestment effects + +**Examples:** +- Demolition or disposal costs +- Contractual penalties for not investing +- Opportunity costs or lost revenues + +--- + +### Total Investment Effects + +The total contribution to effect $e$ from an investment is: + +$$\label{eq:invest_total_effects} +E_{e,\text{invest}} = E_{e,\text{fix}} + E_{e,\text{spec}} + E_{e,\text{pw}} + E_{e,\text{divest}} +$$ + +Effects integrate into the overall system effects as described in [Effects, Penalty & Objective](../effects-penalty-objective.md). + +--- + +## Integration with Components + +Investment parameters modify component sizing: + +### Without Investment +Component size is a fixed parameter: +$$ +\text{size} = \text{size}_\text{nominal} +$$ + +### With Investment +Component size becomes a variable: +$$ +\text{size} = v_\text{invest} +$$ + +This size variable then appears in component constraints. For example, flow rate bounds become: + +$$ +v_\text{invest} \cdot \text{rel}_\text{lower} \leq p(t) \leq v_\text{invest} \cdot \text{rel}_\text{upper} +$$ + +Using the **scaled bounds** pattern from [Bounds and States](../modeling-patterns/bounds-and-states.md#scaled-bounds). + +--- + +## Cost Annualization + +**Important:** All investment cost values must be properly weighted to match the optimization model's time horizon. + +For long-term investments, costs should be annualized: + +$$\label{eq:annualization} +\text{cost}_\text{annual} = \frac{\text{cost}_\text{capital} \cdot r}{1 - (1 + r)^{-n}} +$$ + +With: +- $\text{cost}_\text{capital}$ being the upfront investment cost +- $r$ being the discount rate +- $n$ being the equipment lifetime in years + +**Example:** €1,000,000 equipment with 20-year life and 5% discount rate +$$ +\text{cost}_\text{annual} = \frac{1{,}000{,}000 \cdot 0.05}{1 - (1.05)^{-20}} \approx €80{,}243/\text{year} +$$ + +--- + +## Implementation + +**Python Class:** [`InvestParameters`][flixopt.interface.InvestParameters] + +**Key Parameters:** +- `fixed_size`: For binary investments (mutually exclusive with continuous sizing) +- `minimum_size`, `maximum_size`: For continuous sizing +- `optional`: Whether investment can be skipped +- `fix_effects`: Fixed costs dictionary +- `specific_effects`: Per-unit costs dictionary +- `piecewise_effects`: Non-linear cost modeling +- `divest_effects`: Costs for not investing + +See the [`InvestParameters`][flixopt.interface.InvestParameters] API documentation for complete parameter list and usage examples. + +**Used in:** +- [`Flow`][flixopt.elements.Flow] - Flexible capacity decisions +- [`Storage`][flixopt.components.Storage] - Storage sizing optimization +- [`LinearConverter`][flixopt.components.LinearConverter] - Converter capacity planning +- All components supporting investment decisions + +--- + +## Examples + +### Binary Investment (Solar Panels) +```python +solar_investment = InvestParameters( + fixed_size=100, # 100 kW system + optional=True, + fix_effects={'cost': 25000}, # Installation costs + specific_effects={'cost': 1200}, # €1200/kW +) +``` + +### Continuous Sizing (Battery) +```python +battery_investment = InvestParameters( + minimum_size=10, # kWh + maximum_size=1000, + optional=True, + fix_effects={'cost': 5000}, # Grid connection + specific_effects={'cost': 600}, # €600/kWh +) +``` + +### With Divestment Costs (Replacement) +```python +boiler_replacement = InvestParameters( + minimum_size=50, # kW + maximum_size=200, + optional=True, + fix_effects={'cost': 15000}, + specific_effects={'cost': 400}, + divest_effects={'cost': 8000}, # Demolition if not replaced +) +``` + +### Economies of Scale (Piecewise) +```python +battery_investment = InvestParameters( + minimum_size=10, + maximum_size=1000, + piecewise_effects=PiecewiseEffects( + piecewise_origin=Piecewise([ + Piece(0, 100), # Small + Piece(100, 500), # Medium + Piece(500, 1000), # Large + ]), + piecewise_shares={ + 'cost': Piecewise([ + Piece(800, 750), # €800-750/kWh + Piece(750, 600), # €750-600/kWh + Piece(600, 500), # €600-500/kWh (bulk discount) + ]) + }, + ), +) +``` diff --git a/docs/user-guide/mathematical-notation/features/OnOffParameters.md b/docs/user-guide/mathematical-notation/features/OnOffParameters.md new file mode 100644 index 000000000..4ec6a9726 --- /dev/null +++ b/docs/user-guide/mathematical-notation/features/OnOffParameters.md @@ -0,0 +1,307 @@ +# OnOffParameters + +[`OnOffParameters`][flixopt.interface.OnOffParameters] model equipment that operates in discrete on/off states rather than continuous operation. This captures realistic operational constraints including startup costs, minimum run times, cycling limitations, and maintenance scheduling. + +## Binary State Variable + +Equipment operation is modeled using a binary state variable: + +$$\label{eq:onoff_state} +s(t) \in \{0, 1\} \quad \forall t +$$ + +With: +- $s(t) = 1$: equipment is operating (on state) +- $s(t) = 0$: equipment is shutdown (off state) + +This state variable controls the equipment's operational constraints and modifies flow bounds using the **bounds with state** pattern from [Bounds and States](../modeling-patterns/bounds-and-states.md#bounds-with-state). + +--- + +## State Transitions and Switching + +State transitions are tracked using switch variables (see [State Transitions](../modeling-patterns/state-transitions.md#binary-state-transitions)): + +$$\label{eq:onoff_transitions} +s^\text{on}(t) - s^\text{off}(t) = s(t) - s(t-1) \quad \forall t > 0 +$$ + +$$\label{eq:onoff_switch_exclusivity} +s^\text{on}(t) + s^\text{off}(t) \leq 1 \quad \forall t +$$ + +With: +- $s^\text{on}(t) \in \{0, 1\}$: equals 1 when switching from off to on (startup) +- $s^\text{off}(t) \in \{0, 1\}$: equals 1 when switching from on to off (shutdown) + +**Behavior:** +- Off β†’ On: $s^\text{on}(t) = 1, s^\text{off}(t) = 0$ +- On β†’ Off: $s^\text{on}(t) = 0, s^\text{off}(t) = 1$ +- No change: $s^\text{on}(t) = 0, s^\text{off}(t) = 0$ + +--- + +## Effects and Costs + +### Switching Effects + +Effects incurred when equipment starts up: + +$$\label{eq:onoff_switch_effects} +E_{e,\text{switch}} = \sum_{t} s^\text{on}(t) \cdot \text{effect}_{e,\text{switch}} +$$ + +With: +- $\text{effect}_{e,\text{switch}}$ being the effect value per startup event + +**Examples:** +- Startup fuel consumption +- Wear and tear costs +- Labor costs for startup procedures +- Inrush power demands + +--- + +### Running Effects + +Effects incurred while equipment is operating: + +$$\label{eq:onoff_running_effects} +E_{e,\text{run}} = \sum_{t} s(t) \cdot \Delta t \cdot \text{effect}_{e,\text{run}} +$$ + +With: +- $\text{effect}_{e,\text{run}}$ being the effect rate per operating hour +- $\Delta t$ being the time step duration + +**Examples:** +- Fixed operating and maintenance costs +- Auxiliary power consumption +- Consumable materials +- Emissions while running + +--- + +## Operating Hour Constraints + +### Total Operating Hours + +Bounds on total operating time across the planning horizon: + +$$\label{eq:onoff_total_hours} +h_\text{min} \leq \sum_{t} s(t) \cdot \Delta t \leq h_\text{max} +$$ + +With: +- $h_\text{min}$ being the minimum total operating hours +- $h_\text{max}$ being the maximum total operating hours + +**Use cases:** +- Minimum runtime requirements (contracts, maintenance) +- Maximum runtime limits (fuel availability, permits, equipment life) + +--- + +### Consecutive Operating Hours + +**Minimum Consecutive On-Time:** + +Enforces minimum runtime once started using duration tracking (see [Duration Tracking](../modeling-patterns/duration-tracking.md#minimum-duration-constraints)): + +$$\label{eq:onoff_min_on_duration} +d^\text{on}(t) \geq (s(t-1) - s(t)) \cdot h^\text{on}_\text{min} \quad \forall t > 0 +$$ + +With: +- $d^\text{on}(t)$ being the consecutive on-time duration at time $t$ +- $h^\text{on}_\text{min}$ being the minimum required on-time + +**Behavior:** +- When shutting down at time $t$: enforces equipment was on for at least $h^\text{on}_\text{min}$ prior to the switch +- Prevents short cycling and frequent startups + +**Maximum Consecutive On-Time:** + +Limits continuous operation before requiring shutdown: + +$$\label{eq:onoff_max_on_duration} +d^\text{on}(t) \leq h^\text{on}_\text{max} \quad \forall t +$$ + +**Use cases:** +- Mandatory maintenance intervals +- Process batch time limits +- Thermal cycling requirements + +--- + +### Consecutive Shutdown Hours + +**Minimum Consecutive Off-Time:** + +Enforces minimum shutdown duration before restarting: + +$$\label{eq:onoff_min_off_duration} +d^\text{off}(t) \geq (s(t) - s(t-1)) \cdot h^\text{off}_\text{min} \quad \forall t > 0 +$$ + +With: +- $d^\text{off}(t)$ being the consecutive off-time duration at time $t$ +- $h^\text{off}_\text{min}$ being the minimum required off-time + +**Use cases:** +- Cooling periods +- Maintenance requirements +- Process stabilization + +**Maximum Consecutive Off-Time:** + +Limits shutdown duration before mandatory restart: + +$$\label{eq:onoff_max_off_duration} +d^\text{off}(t) \leq h^\text{off}_\text{max} \quad \forall t +$$ + +**Use cases:** +- Equipment preservation requirements +- Process stability needs +- Contractual minimum activity levels + +--- + +## Cycling Limits + +Maximum number of startups across the planning horizon: + +$$\label{eq:onoff_max_switches} +\sum_{t} s^\text{on}(t) \leq n_\text{max} +$$ + +With: +- $n_\text{max}$ being the maximum allowed number of startups + +**Use cases:** +- Preventing excessive equipment wear +- Grid stability requirements +- Operational complexity limits +- Maintenance budget constraints + +--- + +## Integration with Flow Bounds + +OnOffParameters modify flow rate bounds by coupling them to the on/off state. + +**Without OnOffParameters** (continuous operation): +$$ +P \cdot \text{rel}_\text{lower} \leq p(t) \leq P \cdot \text{rel}_\text{upper} +$$ + +**With OnOffParameters** (binary operation): +$$ +s(t) \cdot P \cdot \max(\varepsilon, \text{rel}_\text{lower}) \leq p(t) \leq s(t) \cdot P \cdot \text{rel}_\text{upper} +$$ + +Using the **bounds with state** pattern from [Bounds and States](../modeling-patterns/bounds-and-states.md#bounds-with-state). + +**Behavior:** +- When $s(t) = 0$: flow is forced to zero +- When $s(t) = 1$: flow follows normal bounds + +--- + +## Complete Formulation Summary + +For equipment with OnOffParameters, the complete constraint system includes: + +1. **State variable:** $s(t) \in \{0, 1\}$ +2. **Switch tracking:** $s^\text{on}(t) - s^\text{off}(t) = s(t) - s(t-1)$ +3. **Switch exclusivity:** $s^\text{on}(t) + s^\text{off}(t) \leq 1$ +4. **Duration tracking:** + - On-duration: $d^\text{on}(t)$ following duration tracking pattern + - Off-duration: $d^\text{off}(t)$ following duration tracking pattern +5. **Minimum on-time:** $d^\text{on}(t) \geq (s(t-1) - s(t)) \cdot h^\text{on}_\text{min}$ +6. **Maximum on-time:** $d^\text{on}(t) \leq h^\text{on}_\text{max}$ +7. **Minimum off-time:** $d^\text{off}(t) \geq (s(t) - s(t-1)) \cdot h^\text{off}_\text{min}$ +8. **Maximum off-time:** $d^\text{off}(t) \leq h^\text{off}_\text{max}$ +9. **Total hours:** $h_\text{min} \leq \sum_t s(t) \cdot \Delta t \leq h_\text{max}$ +10. **Cycling limit:** $\sum_t s^\text{on}(t) \leq n_\text{max}$ +11. **Flow bounds:** $s(t) \cdot P \cdot \text{rel}_\text{lower} \leq p(t) \leq s(t) \cdot P \cdot \text{rel}_\text{upper}$ + +--- + +## Implementation + +**Python Class:** [`OnOffParameters`][flixopt.interface.OnOffParameters] + +**Key Parameters:** +- `effects_per_switch_on`: Costs per startup event +- `effects_per_running_hour`: Costs per hour of operation +- `on_hours_total_min`, `on_hours_total_max`: Total runtime bounds +- `consecutive_on_hours_min`, `consecutive_on_hours_max`: Consecutive runtime bounds +- `consecutive_off_hours_min`, `consecutive_off_hours_max`: Consecutive shutdown bounds +- `switch_on_total_max`: Maximum number of startups +- `force_switch_on`: Create switch variables even without limits (for tracking) + +See the [`OnOffParameters`][flixopt.interface.OnOffParameters] API documentation for complete parameter list and usage examples. + +**Mathematical Patterns Used:** +- [State Transitions](../modeling-patterns/state-transitions.md#binary-state-transitions) - Switch tracking +- [Duration Tracking](../modeling-patterns/duration-tracking.md) - Consecutive time constraints +- [Bounds with State](../modeling-patterns/bounds-and-states.md#bounds-with-state) - Flow control + +**Used in:** +- [`Flow`][flixopt.elements.Flow] - On/off operation for flows +- All components supporting discrete operational states + +--- + +## Examples + +### Power Plant with Startup Costs +```python +power_plant = OnOffParameters( + effects_per_switch_on={'startup_cost': 25000}, # €25k per startup + effects_per_running_hour={'fixed_om': 125}, # €125/hour while running + consecutive_on_hours_min=8, # Minimum 8-hour run + consecutive_off_hours_min=4, # 4-hour cooling period + on_hours_total_max=6000, # Annual limit +) +``` + +### Batch Process with Cycling Limits +```python +batch_reactor = OnOffParameters( + effects_per_switch_on={'setup_cost': 1500}, + consecutive_on_hours_min=12, # 12-hour minimum batch + consecutive_on_hours_max=24, # 24-hour maximum batch + consecutive_off_hours_min=6, # Cleaning time + switch_on_total_max=200, # Max 200 batches +) +``` + +### HVAC with Cycle Prevention +```python +hvac = OnOffParameters( + effects_per_switch_on={'compressor_wear': 0.5}, + consecutive_on_hours_min=1, # Prevent short cycling + consecutive_off_hours_min=0.5, # 30-min minimum off + switch_on_total_max=2000, # Limit compressor starts +) +``` + +### Backup Generator with Testing Requirements +```python +backup_gen = OnOffParameters( + effects_per_switch_on={'fuel_priming': 50}, # L diesel + consecutive_on_hours_min=0.5, # 30-min test duration + consecutive_off_hours_max=720, # Test every 30 days + on_hours_total_min=26, # Weekly testing requirement +) +``` + +--- + +## Notes + +**Time Series Boundary:** The final time period constraints for consecutive_on_hours_min/max and consecutive_off_hours_min/max are not enforced at the end of the planning horizon. This allows optimization to end with ongoing campaigns that may be shorter/longer than specified, as they extend beyond the modeled period. diff --git a/docs/user-guide/Mathematical Notation/Piecewise.md b/docs/user-guide/mathematical-notation/features/Piecewise.md similarity index 100% rename from docs/user-guide/Mathematical Notation/Piecewise.md rename to docs/user-guide/mathematical-notation/features/Piecewise.md diff --git a/docs/user-guide/mathematical-notation/index.md b/docs/user-guide/mathematical-notation/index.md new file mode 100644 index 000000000..ae89f3b67 --- /dev/null +++ b/docs/user-guide/mathematical-notation/index.md @@ -0,0 +1,123 @@ + +# Mathematical Notation + +This section provides the **mathematical formulations** underlying FlixOpt's optimization models. It is intended as **reference documentation** for users who want to understand the mathematical details behind the high-level FlixOpt API described in the [FlixOpt Concepts](../index.md) guide. + +**For typical usage**, refer to the [FlixOpt Concepts](../index.md) guide, [Examples](../../examples/), and [API Reference](../../api-reference/) - you don't need to understand these mathematical formulations to use FlixOpt effectively. + +--- + +## Naming Conventions + +FlixOpt uses the following naming conventions: + +- All optimization variables are denoted by italic letters (e.g., $x$, $y$, $z$) +- All parameters and constants are denoted by non italic small letters (e.g., $\text{a}$, $\text{b}$, $\text{c}$) +- All Sets are denoted by greek capital letters (e.g., $\mathcal{F}$, $\mathcal{E}$) +- All units of a set are denoted by greek small letters (e.g., $\mathcal{f}$, $\mathcal{e}$) +- The letter $i$ is used to denote an index (e.g., $i=1,\dots,\text n$) +- All time steps are denoted by the letter $\text{t}$ (e.g., $\text{t}_0$, $\text{t}_1$, $\text{t}_i$) + +## Dimensions and Time Steps + +FlixOpt supports multi-dimensional optimization with up to three dimensions: **time** (mandatory), **period** (optional), and **scenario** (optional). + +**All mathematical formulations in this documentation are independent of whether periods or scenarios are present.** The equations shown are written with time index $\text{t}_i$ only, but automatically expand to additional dimensions when periods/scenarios are added. + +For complete details on dimensions, their relationships, and influence on formulations, see **[Dimensions](dimensions.md)**. + +### Time Steps + +Time steps are defined as a sequence of discrete time steps $\text{t}_i \in \mathcal{T} \quad \text{for} \quad i \in \{1, 2, \dots, \text{n}\}$ (left-aligned in its timespan). +From this sequence, the corresponding time intervals $\Delta \text{t}_i \in \Delta \mathcal{T}$ are derived as + +$$\Delta \text{t}_i = \text{t}_{i+1} - \text{t}_i \quad \text{for} \quad i \in \{1, 2, \dots, \text{n}-1\}$$ + +The final time interval $\Delta \text{t}_\text n$ defaults to $\Delta \text{t}_\text n = \Delta \text{t}_{\text n-1}$, but is of course customizable. +Non-equidistant time steps are also supported. + +--- + +## Documentation Structure + +This reference is organized to match the FlixOpt API structure: + +### Elements +Mathematical formulations for core FlixOpt elements (corresponding to [`flixopt.elements`][flixopt.elements]): + +- [Flow](elements/Flow.md) - Flow rate constraints and bounds +- [Bus](elements/Bus.md) - Nodal balance equations +- [Storage](elements/Storage.md) - Storage balance and charge state evolution +- [LinearConverter](elements/LinearConverter.md) - Linear conversion relationships + +**User API:** When you create a `Flow`, `Bus`, `Storage`, or `LinearConverter` in your FlixOpt model, these mathematical formulations are automatically applied. + +### Features +Mathematical formulations for optional features (corresponding to parameters in FlixOpt classes): + +- [InvestParameters](features/InvestParameters.md) - Investment decision modeling +- [OnOffParameters](features/OnOffParameters.md) - Binary on/off operation +- [Piecewise](features/Piecewise.md) - Piecewise linear approximations + +**User API:** When you pass `invest_parameters` or `on_off_parameters` to a `Flow` or component, these formulations are applied. + +### System-Level +- [Effects, Penalty & Objective](effects-penalty-objective.md) - Cost allocation and objective function + +**User API:** When you create [`Effect`][flixopt.effects.Effect] objects and set `effects_per_flow_hour`, these formulations govern how costs are calculated. + +### Modeling Patterns (Advanced) +**Internal implementation details** - These low-level patterns are used internally by Elements and Features. They are documented here for: + +- Developers extending FlixOpt +- Advanced users debugging models or understanding solver behavior +- Researchers comparing mathematical formulations + +**Normal users do not need to read this section** - the patterns are automatically applied when you use Elements and Features: + +- [Bounds and States](modeling-patterns/bounds-and-states.md) - Variable bounding patterns +- [Duration Tracking](modeling-patterns/duration-tracking.md) - Consecutive time period tracking +- [State Transitions](modeling-patterns/state-transitions.md) - State change modeling + +--- + +## Quick Reference + +### Components Cross-Reference + +| Concept | Documentation | Python Class | +|---------|---------------|--------------| +| **Flow rate bounds** | [Flow](elements/Flow.md) | [`Flow`][flixopt.elements.Flow] | +| **Bus balance** | [Bus](elements/Bus.md) | [`Bus`][flixopt.elements.Bus] | +| **Storage balance** | [Storage](elements/Storage.md) | [`Storage`][flixopt.components.Storage] | +| **Linear conversion** | [LinearConverter](elements/LinearConverter.md) | [`LinearConverter`][flixopt.components.LinearConverter] | + +### Features Cross-Reference + +| Concept | Documentation | Python Class | +|---------|---------------|--------------| +| **Binary investment** | [InvestParameters](features/InvestParameters.md) | [`InvestParameters`][flixopt.interface.InvestParameters] | +| **On/off operation** | [OnOffParameters](features/OnOffParameters.md) | [`OnOffParameters`][flixopt.interface.OnOffParameters] | +| **Piecewise segments** | [Piecewise](features/Piecewise.md) | [`Piecewise`][flixopt.interface.Piecewise] | + +### Modeling Patterns Cross-Reference + +| Pattern | Documentation | Implementation | +|---------|---------------|----------------| +| **Basic bounds** | [bounds-and-states](modeling-patterns/bounds-and-states.md#basic-bounds) | [`BoundingPatterns.basic_bounds()`][flixopt.modeling.BoundingPatterns.basic_bounds] | +| **Bounds with state** | [bounds-and-states](modeling-patterns/bounds-and-states.md#bounds-with-state) | [`BoundingPatterns.bounds_with_state()`][flixopt.modeling.BoundingPatterns.bounds_with_state] | +| **Scaled bounds** | [bounds-and-states](modeling-patterns/bounds-and-states.md#scaled-bounds) | [`BoundingPatterns.scaled_bounds()`][flixopt.modeling.BoundingPatterns.scaled_bounds] | +| **Duration tracking** | [duration-tracking](modeling-patterns/duration-tracking.md) | [`ModelingPrimitives.consecutive_duration_tracking()`][flixopt.modeling.ModelingPrimitives.consecutive_duration_tracking] | +| **State transitions** | [state-transitions](modeling-patterns/state-transitions.md) | [`BoundingPatterns.state_transition_bounds()`][flixopt.modeling.BoundingPatterns.state_transition_bounds] | + +### Python Class Lookup + +| Class | Documentation | API Reference | +|-------|---------------|---------------| +| `Flow` | [Flow](elements/Flow.md) | [`Flow`][flixopt.elements.Flow] | +| `Bus` | [Bus](elements/Bus.md) | [`Bus`][flixopt.elements.Bus] | +| `Storage` | [Storage](elements/Storage.md) | [`Storage`][flixopt.components.Storage] | +| `LinearConverter` | [LinearConverter](elements/LinearConverter.md) | [`LinearConverter`][flixopt.components.LinearConverter] | +| `InvestParameters` | [InvestParameters](features/InvestParameters.md) | [`InvestParameters`][flixopt.interface.InvestParameters] | +| `OnOffParameters` | [OnOffParameters](features/OnOffParameters.md) | [`OnOffParameters`][flixopt.interface.OnOffParameters] | +| `Piecewise` | [Piecewise](features/Piecewise.md) | [`Piecewise`][flixopt.interface.Piecewise] | diff --git a/docs/user-guide/mathematical-notation/modeling-patterns/bounds-and-states.md b/docs/user-guide/mathematical-notation/modeling-patterns/bounds-and-states.md new file mode 100644 index 000000000..d5821948f --- /dev/null +++ b/docs/user-guide/mathematical-notation/modeling-patterns/bounds-and-states.md @@ -0,0 +1,165 @@ +# Bounds and States + +This document describes the mathematical formulations for variable bounding patterns used throughout FlixOpt. These patterns define how optimization variables are constrained, both with and without state control. + +## Basic Bounds + +The simplest bounding pattern constrains a variable between lower and upper bounds. + +$$\label{eq:basic_bounds} +\text{lower} \leq v \leq \text{upper} +$$ + +With: +- $v$ being the optimization variable +- $\text{lower}$ being the lower bound (constant or time-dependent) +- $\text{upper}$ being the upper bound (constant or time-dependent) + +**Implementation:** [`BoundingPatterns.basic_bounds()`][flixopt.modeling.BoundingPatterns.basic_bounds] + +**Used in:** +- Storage charge state bounds (see [Storage](../elements/Storage.md)) +- Flow rate absolute bounds + +--- + +## Bounds with State + +When a variable should only be non-zero if a binary state variable is active (e.g., on/off operation, investment decisions), the bounds are controlled by the state: + +$$\label{eq:bounds_with_state} +s \cdot \max(\varepsilon, \text{lower}) \leq v \leq s \cdot \text{upper} +$$ + +With: +- $v$ being the optimization variable +- $s \in \{0, 1\}$ being the binary state variable +- $\text{lower}$ being the lower bound when active +- $\text{upper}$ being the upper bound when active +- $\varepsilon$ being a small positive number to ensure numerical stability + +**Behavior:** +- When $s = 0$: variable is forced to zero ($0 \leq v \leq 0$) +- When $s = 1$: variable can take values in $[\text{lower}, \text{upper}]$ + +**Implementation:** [`BoundingPatterns.bounds_with_state()`][flixopt.modeling.BoundingPatterns.bounds_with_state] + +**Used in:** +- Flow rates with on/off operation (see [OnOffParameters](../features/OnOffParameters.md)) +- Investment size decisions (see [InvestParameters](../features/InvestParameters.md)) + +--- + +## Scaled Bounds + +When a variable's bounds depend on another variable (e.g., flow rate scaled by component size), scaled bounds are used: + +$$\label{eq:scaled_bounds} +v_\text{scale} \cdot \text{rel}_\text{lower} \leq v \leq v_\text{scale} \cdot \text{rel}_\text{upper} +$$ + +With: +- $v$ being the optimization variable (e.g., flow rate) +- $v_\text{scale}$ being the scaling variable (e.g., component size) +- $\text{rel}_\text{lower}$ being the relative lower bound factor (typically 0) +- $\text{rel}_\text{upper}$ being the relative upper bound factor (typically 1) + +**Example:** Flow rate bounds +- If $v_\text{scale} = P$ (flow size) and $\text{rel}_\text{upper} = 1$ +- Then: $0 \leq p(t_i) \leq P$ (see [Flow](../elements/Flow.md)) + +**Implementation:** [`BoundingPatterns.scaled_bounds()`][flixopt.modeling.BoundingPatterns.scaled_bounds] + +**Used in:** +- Flow rate constraints (see [Flow](../elements/Flow.md) equation 1) +- Storage charge state constraints (see [Storage](../elements/Storage.md) equation 1) + +--- + +## Scaled Bounds with State + +Combining scaled bounds with binary state control requires a Big-M formulation to handle both the scaling and the on/off behavior: + +$$\label{eq:scaled_bounds_with_state_1} +(s - 1) \cdot M_\text{misc} + v_\text{scale} \cdot \text{rel}_\text{lower} \leq v \leq v_\text{scale} \cdot \text{rel}_\text{upper} +$$ + +$$\label{eq:scaled_bounds_with_state_2} +s \cdot M_\text{lower} \leq v \leq s \cdot M_\text{upper} +$$ + +With: +- $v$ being the optimization variable +- $v_\text{scale}$ being the scaling variable +- $s \in \{0, 1\}$ being the binary state variable +- $\text{rel}_\text{lower}$ being the relative lower bound factor +- $\text{rel}_\text{upper}$ being the relative upper bound factor +- $M_\text{misc} = v_\text{scale,max} \cdot \text{rel}_\text{lower}$ +- $M_\text{upper} = v_\text{scale,max} \cdot \text{rel}_\text{upper}$ +- $M_\text{lower} = \max(\varepsilon, v_\text{scale,min} \cdot \text{rel}_\text{lower})$ + +Where $v_\text{scale,max}$ and $v_\text{scale,min}$ are the maximum and minimum possible values of the scaling variable. + +**Behavior:** +- When $s = 0$: variable is forced to zero +- When $s = 1$: variable follows scaled bounds $v_\text{scale} \cdot \text{rel}_\text{lower} \leq v \leq v_\text{scale} \cdot \text{rel}_\text{upper}$ + +**Implementation:** [`BoundingPatterns.scaled_bounds_with_state()`][flixopt.modeling.BoundingPatterns.scaled_bounds_with_state] + +**Used in:** +- Flow rates with on/off operation and investment sizing +- Components combining [OnOffParameters](../features/OnOffParameters.md) and [InvestParameters](../features/InvestParameters.md) + +--- + +## Expression Tracking + +Sometimes it's necessary to create an auxiliary variable that equals an expression: + +$$\label{eq:expression_tracking} +v_\text{tracker} = \text{expression} +$$ + +With optional bounds: + +$$\label{eq:expression_tracking_bounds} +\text{lower} \leq v_\text{tracker} \leq \text{upper} +$$ + +With: +- $v_\text{tracker}$ being the auxiliary tracking variable +- $\text{expression}$ being a linear expression of other variables +- $\text{lower}, \text{upper}$ being optional bounds on the tracker + +**Use cases:** +- Creating named variables for complex expressions +- Bounding intermediate results +- Simplifying constraint formulations + +**Implementation:** [`ModelingPrimitives.expression_tracking_variable()`][flixopt.modeling.ModelingPrimitives.expression_tracking_variable] + +--- + +## Mutual Exclusivity + +When multiple binary variables should not be active simultaneously (at most one can be 1): + +$$\label{eq:mutual_exclusivity} +\sum_{i} s_i(t) \leq \text{tolerance} \quad \forall t +$$ + +With: +- $s_i(t) \in \{0, 1\}$ being binary state variables +- $\text{tolerance}$ being the maximum number of simultaneously active states (typically 1) +- $t$ being the time index + +**Use cases:** +- Ensuring only one operating mode is active +- Mutual exclusion of operation and maintenance states +- Enforcing single-choice decisions + +**Implementation:** [`ModelingPrimitives.mutual_exclusivity_constraint()`][flixopt.modeling.ModelingPrimitives.mutual_exclusivity_constraint] + +**Used in:** +- Operating mode selection +- Piecewise linear function segments (see [Piecewise](../features/Piecewise.md)) diff --git a/docs/user-guide/mathematical-notation/modeling-patterns/duration-tracking.md b/docs/user-guide/mathematical-notation/modeling-patterns/duration-tracking.md new file mode 100644 index 000000000..5d430d28c --- /dev/null +++ b/docs/user-guide/mathematical-notation/modeling-patterns/duration-tracking.md @@ -0,0 +1,159 @@ +# Duration Tracking + +Duration tracking allows monitoring how long a binary state has been consecutively active. This is essential for modeling minimum run times, ramp-up periods, and similar time-dependent constraints. + +## Consecutive Duration Tracking + +For a binary state variable $s(t) \in \{0, 1\}$, the consecutive duration $d(t)$ tracks how long the state has been continuously active. + +### Duration Upper Bound + +The duration cannot exceed zero when the state is inactive: + +$$\label{eq:duration_upper} +d(t) \leq s(t) \cdot M \quad \forall t +$$ + +With: +- $d(t)$ being the duration variable (continuous, non-negative) +- $s(t) \in \{0, 1\}$ being the binary state variable +- $M$ being a sufficiently large constant (big-M) + +**Behavior:** +- When $s(t) = 0$: forces $d(t) \leq 0$, thus $d(t) = 0$ +- When $s(t) = 1$: allows $d(t)$ to be positive + +--- + +### Duration Accumulation + +While the state is active, the duration increases by the time step size: + +$$\label{eq:duration_accumulation_upper} +d(t+1) \leq d(t) + \Delta d(t) \quad \forall t +$$ + +$$\label{eq:duration_accumulation_lower} +d(t+1) \geq d(t) + \Delta d(t) + (s(t+1) - 1) \cdot M \quad \forall t +$$ + +With: +- $\Delta d(t)$ being the duration increment for time step $t$ (typically $\Delta t_i$ from the time series) +- $M$ being a sufficiently large constant + +**Behavior:** +- When $s(t+1) = 1$: both inequalities enforce $d(t+1) = d(t) + \Delta d(t)$ +- When $s(t+1) = 0$: only the upper bound applies, and $d(t+1) = 0$ (from equation $\eqref{eq:duration_upper}$) + +--- + +### Initial Duration + +The duration at the first time step depends on both the state and any previous duration: + +$$\label{eq:duration_initial} +d(0) = (\Delta d(0) + d_\text{prev}) \cdot s(0) +$$ + +With: +- $d_\text{prev}$ being the duration from before the optimization period +- $\Delta d(0)$ being the duration increment for the first time step + +**Behavior:** +- When $s(0) = 1$: duration continues from previous period +- When $s(0) = 0$: duration resets to zero + +--- + +### Complete Formulation + +Combining all constraints: + +$$ +\begin{align} +d(t) &\leq s(t) \cdot M && \forall t \label{eq:duration_complete_1} \\ +d(t+1) &\leq d(t) + \Delta d(t) && \forall t \label{eq:duration_complete_2} \\ +d(t+1) &\geq d(t) + \Delta d(t) + (s(t+1) - 1) \cdot M && \forall t \label{eq:duration_complete_3} \\ +d(0) &= (\Delta d(0) + d_\text{prev}) \cdot s(0) && \label{eq:duration_complete_4} +\end{align} +$$ + +--- + +## Minimum Duration Constraints + +To enforce a minimum consecutive duration (e.g., minimum run time), an additional constraint links the duration to state changes: + +$$\label{eq:minimum_duration} +d(t) \geq (s(t-1) - s(t)) \cdot d_\text{min}(t-1) \quad \forall t > 0 +$$ + +With: +- $d_\text{min}(t)$ being the required minimum duration at time $t$ + +**Behavior:** +- When shutting down ($s(t-1) = 1, s(t) = 0$): enforces $d(t-1) \geq d_\text{min}(t-1)$ +- This ensures the state was active for at least $d_\text{min}$ before turning off +- When state is constant or turning on: constraint is non-binding + +--- + +## Implementation + +**Function:** [`ModelingPrimitives.consecutive_duration_tracking()`][flixopt.modeling.ModelingPrimitives.consecutive_duration_tracking] + +See the API documentation for complete parameter list and usage details. + +--- + +## Use Cases + +### Minimum Run Time + +Ensuring equipment runs for a minimum duration once started: + +```python +# State: 1 when running, 0 when off +# Require at least 2 hours of operation +duration = modeling.consecutive_duration_tracking( + state_variable=on_state, + duration_per_step=time_step_hours, + minimum_duration=2.0 +) +``` + +### Ramp-Up Tracking + +Tracking time since startup for gradual ramp-up constraints: + +```python +# Track startup duration +startup_duration = modeling.consecutive_duration_tracking( + state_variable=on_state, + duration_per_step=time_step_hours +) +# Constrain output based on startup duration +# (additional constraints would link output to startup_duration) +``` + +### Cooldown Requirements + +Tracking time in a state before allowing transitions: + +```python +# Track maintenance duration +maintenance_duration = modeling.consecutive_duration_tracking( + state_variable=maintenance_state, + duration_per_step=time_step_hours, + minimum_duration=scheduled_maintenance_hours +) +``` + +--- + +## Used In + +This pattern is used in: +- [`OnOffParameters`](../features/OnOffParameters.md) - Minimum on/off times +- Operating mode constraints with minimum durations +- Startup/shutdown sequence modeling diff --git a/docs/user-guide/mathematical-notation/modeling-patterns/index.md b/docs/user-guide/mathematical-notation/modeling-patterns/index.md new file mode 100644 index 000000000..15ff8dbd2 --- /dev/null +++ b/docs/user-guide/mathematical-notation/modeling-patterns/index.md @@ -0,0 +1,54 @@ +# Modeling Patterns + +This section documents the fundamental mathematical patterns used throughout FlixOpt for constructing optimization models. These patterns are implemented in `flixopt.modeling` and provide reusable building blocks for creating constraints. + +## Overview + +The modeling patterns are organized into three categories: + +1. **[Bounds and States](bounds-and-states.md)** - Variable bounding with optional state control +2. **[Duration Tracking](duration-tracking.md)** - Tracking consecutive durations of states +3. **[State Transitions](state-transitions.md)** - Modeling state changes and transitions + +## Pattern Categories + +### Bounding Patterns + +These patterns define how optimization variables are constrained within bounds: + +- **Basic Bounds** - Simple upper and lower bounds on variables +- **Bounds with State** - Binary-controlled bounds (on/off states) +- **Scaled Bounds** - Bounds dependent on another variable (e.g., size) +- **Scaled Bounds with State** - Combination of scaling and binary control + +### Tracking Patterns + +These patterns track properties over time: + +- **Expression Tracking** - Creating auxiliary variables that track expressions +- **Consecutive Duration Tracking** - Tracking how long a state has been active +- **Mutual Exclusivity** - Ensuring only one of multiple options is active + +### Transition Patterns + +These patterns model changes between states: + +- **State Transitions** - Tracking switches between binary states (onβ†’off, offβ†’on) +- **Continuous Transitions** - Linking continuous variable changes to switches +- **Level Changes with Binaries** - Controlled increases/decreases in levels + +## Usage in Components + +These patterns are used throughout FlixOpt components: + +- [`Flow`][flixopt.elements.Flow] uses **scaled bounds with state** for flow rate constraints +- [`Storage`][flixopt.components.Storage] uses **basic bounds** for charge state +- [`OnOffParameters`](../features/OnOffParameters.md) uses **state transitions** for startup/shutdown +- [`InvestParameters`](../features/InvestParameters.md) uses **bounds with state** for investment decisions + +## Implementation + +All patterns are implemented in [`flixopt.modeling`][flixopt.modeling] module: + +- [`ModelingPrimitives`][flixopt.modeling.ModelingPrimitives] - Core constraint patterns +- [`BoundingPatterns`][flixopt.modeling.BoundingPatterns] - Specialized bounding patterns diff --git a/docs/user-guide/mathematical-notation/modeling-patterns/state-transitions.md b/docs/user-guide/mathematical-notation/modeling-patterns/state-transitions.md new file mode 100644 index 000000000..dc75a8008 --- /dev/null +++ b/docs/user-guide/mathematical-notation/modeling-patterns/state-transitions.md @@ -0,0 +1,227 @@ +# State Transitions + +State transition patterns model changes between discrete states and link them to continuous variables. These patterns are essential for modeling startup/shutdown events, switching behavior, and controlled changes in system operation. + +## Binary State Transitions + +For a binary state variable $s(t) \in \{0, 1\}$, state transitions track when the state switches on or off. + +### Switch Variables + +Two binary variables track the transitions: +- $s^\text{on}(t) \in \{0, 1\}$: equals 1 when switching from off to on +- $s^\text{off}(t) \in \{0, 1\}$: equals 1 when switching from on to off + +### Transition Tracking + +The state change equals the difference between switch-on and switch-off: + +$$\label{eq:state_transition} +s^\text{on}(t) - s^\text{off}(t) = s(t) - s(t-1) \quad \forall t > 0 +$$ + +$$\label{eq:state_transition_initial} +s^\text{on}(0) - s^\text{off}(0) = s(0) - s_\text{prev} +$$ + +With: +- $s(t)$ being the binary state variable +- $s_\text{prev}$ being the state before the optimization period +- $s^\text{on}(t), s^\text{off}(t)$ being the switch variables + +**Behavior:** +- Off β†’ On ($s(t-1)=0, s(t)=1$): $s^\text{on}(t)=1, s^\text{off}(t)=0$ +- On β†’ Off ($s(t-1)=1, s(t)=0$): $s^\text{on}(t)=0, s^\text{off}(t)=1$ +- No change: $s^\text{on}(t)=0, s^\text{off}(t)=0$ + +--- + +### Mutual Exclusivity of Switches + +A state cannot switch on and off simultaneously: + +$$\label{eq:switch_exclusivity} +s^\text{on}(t) + s^\text{off}(t) \leq 1 \quad \forall t +$$ + +This ensures: +- At most one switch event per time step +- No simultaneous on/off switching + +--- + +### Complete State Transition Formulation + +$$ +\begin{align} +s^\text{on}(t) - s^\text{off}(t) &= s(t) - s(t-1) && \forall t > 0 \label{eq:transition_complete_1} \\ +s^\text{on}(0) - s^\text{off}(0) &= s(0) - s_\text{prev} && \label{eq:transition_complete_2} \\ +s^\text{on}(t) + s^\text{off}(t) &\leq 1 && \forall t \label{eq:transition_complete_3} \\ +s^\text{on}(t), s^\text{off}(t) &\in \{0, 1\} && \forall t \label{eq:transition_complete_4} +\end{align} +$$ + +**Implementation:** [`BoundingPatterns.state_transition_bounds()`][flixopt.modeling.BoundingPatterns.state_transition_bounds] + +--- + +## Continuous Transitions + +When a continuous variable should only change when certain switch events occur, continuous transition bounds link the variable changes to binary switches. + +### Change Bounds with Switches + +$$\label{eq:continuous_transition} +-\Delta v^\text{max} \cdot (s^\text{on}(t) + s^\text{off}(t)) \leq v(t) - v(t-1) \leq \Delta v^\text{max} \cdot (s^\text{on}(t) + s^\text{off}(t)) \quad \forall t > 0 +$$ + +$$\label{eq:continuous_transition_initial} +-\Delta v^\text{max} \cdot (s^\text{on}(0) + s^\text{off}(0)) \leq v(0) - v_\text{prev} \leq \Delta v^\text{max} \cdot (s^\text{on}(0) + s^\text{off}(0)) +$$ + +With: +- $v(t)$ being the continuous variable +- $v_\text{prev}$ being the value before the optimization period +- $\Delta v^\text{max}$ being the maximum allowed change +- $s^\text{on}(t), s^\text{off}(t) \in \{0, 1\}$ being switch binary variables + +**Behavior:** +- When $s^\text{on}(t) = 0$ and $s^\text{off}(t) = 0$: forces $v(t) = v(t-1)$ (no change) +- When $s^\text{on}(t) = 1$ or $s^\text{off}(t) = 1$: allows change up to $\pm \Delta v^\text{max}$ + +**Implementation:** [`BoundingPatterns.continuous_transition_bounds()`][flixopt.modeling.BoundingPatterns.continuous_transition_bounds] + +--- + +## Level Changes with Binaries + +This pattern models a level variable that can increase or decrease, with changes controlled by binary variables. This is useful for inventory management, capacity adjustments, or gradual state changes. + +### Level Evolution + +The level evolves based on increases and decreases: + +$$\label{eq:level_initial} +\ell(0) = \ell_\text{init} + \ell^\text{inc}(0) - \ell^\text{dec}(0) +$$ + +$$\label{eq:level_evolution} +\ell(t) = \ell(t-1) + \ell^\text{inc}(t) - \ell^\text{dec}(t) \quad \forall t > 0 +$$ + +With: +- $\ell(t)$ being the level variable +- $\ell_\text{init}$ being the initial level +- $\ell^\text{inc}(t)$ being the increase in level at time $t$ (non-negative) +- $\ell^\text{dec}(t)$ being the decrease in level at time $t$ (non-negative) + +--- + +### Change Bounds with Binary Control + +Changes are bounded and controlled by binary variables: + +$$\label{eq:increase_bound} +\ell^\text{inc}(t) \leq \Delta \ell^\text{max} \cdot b^\text{inc}(t) \quad \forall t +$$ + +$$\label{eq:decrease_bound} +\ell^\text{dec}(t) \leq \Delta \ell^\text{max} \cdot b^\text{dec}(t) \quad \forall t +$$ + +With: +- $\Delta \ell^\text{max}$ being the maximum change per time step +- $b^\text{inc}(t), b^\text{dec}(t) \in \{0, 1\}$ being binary control variables + +--- + +### Mutual Exclusivity of Changes + +Simultaneous increase and decrease are prevented: + +$$\label{eq:change_exclusivity} +b^\text{inc}(t) + b^\text{dec}(t) \leq 1 \quad \forall t +$$ + +This ensures: +- Level can only increase OR decrease (or stay constant) in each time step +- No simultaneous contradictory changes + +--- + +### Complete Level Change Formulation + +$$ +\begin{align} +\ell(0) &= \ell_\text{init} + \ell^\text{inc}(0) - \ell^\text{dec}(0) && \label{eq:level_complete_1} \\ +\ell(t) &= \ell(t-1) + \ell^\text{inc}(t) - \ell^\text{dec}(t) && \forall t > 0 \label{eq:level_complete_2} \\ +\ell^\text{inc}(t) &\leq \Delta \ell^\text{max} \cdot b^\text{inc}(t) && \forall t \label{eq:level_complete_3} \\ +\ell^\text{dec}(t) &\leq \Delta \ell^\text{max} \cdot b^\text{dec}(t) && \forall t \label{eq:level_complete_4} \\ +b^\text{inc}(t) + b^\text{dec}(t) &\leq 1 && \forall t \label{eq:level_complete_5} \\ +b^\text{inc}(t), b^\text{dec}(t) &\in \{0, 1\} && \forall t \label{eq:level_complete_6} +\end{align} +$$ + +**Implementation:** [`BoundingPatterns.link_changes_to_level_with_binaries()`][flixopt.modeling.BoundingPatterns.link_changes_to_level_with_binaries] + +--- + +## Use Cases + +### Startup/Shutdown Costs + +Track startup and shutdown events to apply costs: + +```python +# Create switch variables +switch_on, switch_off = modeling.state_transition_bounds( + state_variable=on_state, + previous_state=previous_on_state +) + +# Apply costs to switches +startup_cost = switch_on * startup_cost_per_event +shutdown_cost = switch_off * shutdown_cost_per_event +``` + +### Limited Switching + +Restrict the number of state changes: + +```python +# Track all switches +switch_on, switch_off = modeling.state_transition_bounds( + state_variable=on_state +) + +# Limit total switches +model.add_constraint( + (switch_on + switch_off).sum() <= max_switches +) +``` + +### Gradual Capacity Changes + +Model systems where capacity can be incrementally adjusted: + +```python +# Level represents installed capacity +level_var, increase, decrease, inc_binary, dec_binary = \ + modeling.link_changes_to_level_with_binaries( + initial_level=current_capacity, + max_change=max_capacity_change_per_period + ) + +# Constrain total increases +model.add_constraint(increase.sum() <= max_total_expansion) +``` + +--- + +## Used In + +These patterns are used in: +- [`OnOffParameters`](../features/OnOffParameters.md) - Startup/shutdown tracking and costs +- Operating mode switching with transition costs +- Investment planning with staged capacity additions +- Inventory management with controlled stock changes diff --git a/docs/user-guide/Mathematical Notation/others.md b/docs/user-guide/mathematical-notation/others.md similarity index 100% rename from docs/user-guide/Mathematical Notation/others.md rename to docs/user-guide/mathematical-notation/others.md diff --git a/docs/user-guide/recipies/index.md b/docs/user-guide/recipies/index.md new file mode 100644 index 000000000..8ac7d1812 --- /dev/null +++ b/docs/user-guide/recipies/index.md @@ -0,0 +1,47 @@ +# Recipes + +**Coming Soon!** 🚧 + +This section will contain quick, copy-paste ready code snippets for common FlixOpt patterns. + +--- + +## What Will Be Here? + +Short, focused code snippets showing **how to do specific things** in FlixOpt: + +- Common modeling patterns +- Integration with other tools +- Performance optimizations +- Domain-specific solutions +- Data analysis shortcuts + +Unlike full examples, recipes will be focused snippets showing a single concept. + +--- + +## Planned Topics + +- **Storage Patterns** - Batteries, thermal storage, seasonal storage +- **Multi-Criteria Optimization** - Balance multiple objectives +- **Data I/O** - Loading time series from CSV, databases, APIs +- **Data Manipulation** - Common xarray operations for parameterization and analysis +- **Investment Optimization** - Size optimization strategies +- **Renewable Integration** - Solar, wind capacity optimization +- **On/Off Constraints** - Minimum runtime, startup costs +- **Large-Scale Problems** - Segmented and aggregated calculations +- **Custom Constraints** - Extend models with linopy +- **Domain-Specific Patterns** - District heating, microgrids, industrial processes + +--- + +## Want to Contribute? + +**We need your help!** If you have recurring modeling patterns or clever solutions to share, please contribute via [GitHub issues](https://github.com/flixopt/flixopt/issues) or pull requests. + +Guidelines: +1. Keep it short (< 100 lines of code) +2. Focus on one specific technique +3. Add brief explanation and when to use it + +Check the [contribution guide](../../contribute.md) for details. diff --git a/flixopt/components.py b/flixopt/components.py index 01b6864e3..9af6be153 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -40,6 +40,10 @@ class LinearConverter(Component): straightforward linear relationships, or piecewise conversion for complex non-linear behavior approximated through piecewise linear segments. + Mathematical Formulation: + See the complete mathematical model in the documentation: + [LinearConverter](../user-guide/mathematical-notation/elements/LinearConverter.md) + Args: label: The label of the Element. Used to identify it in the FlowSystem. inputs: list of input Flows that feed into the converter. @@ -247,39 +251,41 @@ class Storage(Component): final state constraints, and time-varying parameters. It supports both fixed-size and investment-optimized storage systems with comprehensive techno-economic modeling. + Mathematical Formulation: + See the complete mathematical model in the documentation: + [Storage](../user-guide/mathematical-notation/elements/Storage.md) + + - Equation (1): Charge state bounds + - Equation (3): Storage balance (charge state evolution) + + Variable Mapping: + - ``capacity_in_flow_hours`` β†’ C (storage capacity) + - ``charge_state`` β†’ c(t_i) (state of charge at time t_i) + - ``relative_loss_per_hour`` β†’ Δ‹_rel,loss (self-discharge rate) + - ``eta_charge`` β†’ Ξ·_in (charging efficiency) + - ``eta_discharge`` β†’ Ξ·_out (discharging efficiency) + Args: - label: The label of the Element. Used to identify it in the FlowSystem. - charging: Incoming flow for loading the storage. Represents energy or material - flowing into the storage system. - discharging: Outgoing flow for unloading the storage. Represents energy or - material flowing out of the storage system. - capacity_in_flow_hours: Nominal capacity/size of the storage in flow-hours - (e.g., kWh for electrical storage, mΒ³ or kg for material storage). Can be a scalar - for fixed capacity or InvestParameters for optimization. - relative_minimum_charge_state: Minimum relative charge state (0-1 range). - Prevents deep discharge that could damage equipment. Default is 0. - relative_maximum_charge_state: Maximum relative charge state (0-1 range). - Accounts for practical capacity limits, safety margins or temperature impacts. Default is 1. - initial_charge_state: Storage charge state at the beginning of the time horizon. - Can be numeric value or 'lastValueOfSim', which is recommended for if the initial start state is not known. - Default is 0. - minimal_final_charge_state: Minimum absolute charge state required at the end of the time horizon. - maximal_final_charge_state: Maximum absolute charge state allowed at the end of the time horizon. - relative_minimum_final_charge_state: Minimum relative charge state required at the end of the time horizon. - Defaults to the last value of the relative_minimum_charge_state. - relative_maximum_final_charge_state: Maximum relative charge state allowed at the end of the time horizon. - Defaults to the last value of the relative_maximum_charge_state. - eta_charge: Charging efficiency factor (0-1 range). Accounts for conversion - losses during charging. Default is 1 (perfect efficiency). - eta_discharge: Discharging efficiency factor (0-1 range). Accounts for - conversion losses during discharging. Default is 1 (perfect efficiency). - relative_loss_per_hour: Self-discharge rate per hour (typically 0-0.1 range). - Represents standby losses, leakage, or degradation. Default is 0. - prevent_simultaneous_charge_and_discharge: If True, prevents charging and - discharging simultaneously. Increases binary variables but improves model - realism and solution interpretation. Default is True. - meta_data: Used to store additional information about the Element. Not used - internally, but saved in results. Only use Python native types. + label: Element identifier used in the FlowSystem. + charging: Incoming flow for loading the storage. + discharging: Outgoing flow for unloading the storage. + capacity_in_flow_hours: Storage capacity in flow-hours (kWh, mΒ³, kg). + Scalar for fixed size or InvestParameters for optimization. + relative_minimum_charge_state: Minimum charge state (0-1). Default: 0. + relative_maximum_charge_state: Maximum charge state (0-1). Default: 1. + initial_charge_state: Charge at start. Numeric or 'lastValueOfSim'. Default: 0. + minimal_final_charge_state: Minimum absolute charge required at end (optional). + maximal_final_charge_state: Maximum absolute charge allowed at end (optional). + relative_minimum_final_charge_state: Minimum relative charge at end. + Defaults to last value of relative_minimum_charge_state. + relative_maximum_final_charge_state: Maximum relative charge at end. + Defaults to last value of relative_maximum_charge_state. + eta_charge: Charging efficiency (0-1). Default: 1. + eta_discharge: Discharging efficiency (0-1). Default: 1. + relative_loss_per_hour: Self-discharge per hour (0-0.1). Default: 0. + prevent_simultaneous_charge_and_discharge: Prevent charging and discharging + simultaneously. Adds binary variables. Default: True. + meta_data: Additional information stored in results. Python native types only. Examples: Battery energy storage system: @@ -355,20 +361,19 @@ class Storage(Component): ``` Note: - Charge state evolution follows the equation: - charge[t+1] = charge[t] Γ— (1-loss_rate)^hours_per_step + - charge_flow[t] Γ— eta_charge Γ— hours_per_step - - discharge_flow[t] Γ— hours_per_step / eta_discharge + **Mathematical formulation**: See [Storage](../user-guide/mathematical-notation/elements/Storage.md) + for charge state evolution equations and balance constraints. - All efficiency parameters (eta_charge, eta_discharge) are dimensionless (0-1 range). - The relative_loss_per_hour parameter represents exponential decay per hour. + **Efficiency parameters** (eta_charge, eta_discharge) are dimensionless (0-1 range). + The relative_loss_per_hour represents exponential decay per hour. - When prevent_simultaneous_charge_and_discharge is True, binary variables are - created to enforce mutual exclusivity, which increases solution time but - prevents unrealistic simultaneous charging and discharging. + **Binary variables**: When prevent_simultaneous_charge_and_discharge is True, binary + variables enforce mutual exclusivity, increasing solution time but preventing unrealistic + simultaneous charging and discharging. - Initial and final charge state constraints use absolute values (not relative), - matching the capacity_in_flow_hours units. + **Units**: Flow rates and charge states are related by the concept of 'flow hours' (=flow_rate * time). + With flow rates in kW, the charge state is therefore (usually) kWh. + With flow rates in m3/h, the charge state is therefore in m3. """ def __init__( diff --git a/flixopt/elements.py b/flixopt/elements.py index d094ed9e0..f911b6646 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -126,6 +126,10 @@ class Bus(Element): physical or logical connection points for energy carriers (electricity, heat, gas) or material flows between different Components. + Mathematical Formulation: + See the complete mathematical model in the documentation: + [Bus](../user-guide/mathematical-notation/elements/Bus.md) + Args: label: The label of the Element. Used to identify it in the FlowSystem. excess_penalty_per_flow_hour: Penalty costs for bus balance violations. @@ -242,39 +246,27 @@ class Flow(Element): - **InvestParameters**: Used for `size` when flow Size is an investment decision - **OnOffParameters**: Used for `on_off_parameters` when flow has discrete states + Mathematical Formulation: + See the complete mathematical model in the documentation: + [Flow](../user-guide/mathematical-notation/elements/Flow.md) + Args: - label: Unique identifier for the flow within its component. - The full label combines component and flow labels. - bus: Label of the bus this flow connects to. Must match a bus in the FlowSystem. - size: Flow capacity or nominal rating. Can be: - - Scalar value for fixed capacity - - InvestParameters for investment-based sizing decisions - - None to use large default value (CONFIG.modeling.BIG) - relative_minimum: Minimum flow rate as fraction of size. - Example: 0.2 means flow cannot go below 20% of rated capacity. - relative_maximum: Maximum flow rate as fraction of size (typically 1.0). - Values >1.0 allow temporary overload operation. - load_factor_min: Minimum average utilization over the time horizon (0-1). - Calculated as total flow hours divided by (size Γ— total time). - load_factor_max: Maximum average utilization over the time horizon (0-1). - Useful for equipment duty cycle limits or maintenance scheduling. - effects_per_flow_hour: Operational costs and impacts per unit of flow-time. - Dictionary mapping effect names to unit costs (e.g., fuel costs, emissions). - on_off_parameters: Binary operation constraints using OnOffParameters. - Enables modeling of startup costs, minimum run times, cycling limits. - Only relevant when relative_minimum > 0 or discrete operation is required. - flow_hours_total_max: Maximum cumulative flow-hours over time horizon. - Alternative to load_factor_max for absolute energy/material limits. - flow_hours_total_min: Minimum cumulative flow-hours over time horizon. - Alternative to load_factor_min for contractual or operational requirements. - fixed_relative_profile: Predetermined flow pattern as fraction of size. - When specified, flow rate becomes: size Γ— fixed_relative_profile(t). - Used for: demand profiles, renewable generation, fixed schedules. - previous_flow_rate: Initial flow state for startup/shutdown dynamics. - Used with on_off_parameters to determine initial on/off status. - If None, assumes flow was off in previous time period. - meta_data: Additional information stored with results but not used in optimization. - Must contain only Python native types (dict, list, str, int, float, bool). + label: Unique flow identifier within its component. + bus: Bus label this flow connects to. + size: Flow capacity. Scalar, InvestParameters, or None (uses CONFIG.modeling.BIG). + relative_minimum: Minimum flow rate as fraction of size (0-1). Default: 0. + relative_maximum: Maximum flow rate as fraction of size. Default: 1. + load_factor_min: Minimum average utilization (0-1). Default: 0. + load_factor_max: Maximum average utilization (0-1). Default: 1. + effects_per_flow_hour: Operational costs/impacts per flow-hour. + Dict mapping effect names to values (e.g., {'cost': 45, 'CO2': 0.8}). + on_off_parameters: Binary operation constraints (OnOffParameters). Default: None. + flow_hours_total_max: Maximum cumulative flow-hours. Alternative to load_factor_max. + flow_hours_total_min: Minimum cumulative flow-hours. Alternative to load_factor_min. + fixed_relative_profile: Predetermined pattern as fraction of size. + Flow rate = size Γ— fixed_relative_profile(t). + previous_flow_rate: Initial flow state for on/off dynamics. Default: None (off). + meta_data: Additional info stored in results. Python native types only. Examples: Basic power flow with fixed capacity: diff --git a/flixopt/interface.py b/flixopt/interface.py index b3edffffc..c3247ee50 100644 --- a/flixopt/interface.py +++ b/flixopt/interface.py @@ -239,6 +239,10 @@ class PiecewiseConversion(Interface): When the equipment operates at a given point, ALL flows scale proportionally within their respective pieces. + Mathematical Formulation: + See the complete mathematical model in the documentation: + [Piecewise](../user-guide/mathematical-notation/features/Piecewise.md) + Args: piecewises: Dictionary mapping flow labels to their Piecewise functions. Keys are flow identifiers (e.g., 'electricity_in', 'heat_out', 'fuel_consumed'). @@ -681,30 +685,26 @@ class InvestParameters(Interface): - **Piecewise Effects**: Non-linear relationships (bulk discounts, learning curves) - **Divestment Effects**: Penalties for not investing (demolition, opportunity costs) + Mathematical Formulation: + See the complete mathematical model in the documentation: + [InvestParameters](../user-guide/mathematical-notation/features/InvestParameters.md) + Args: - fixed_size: When specified, creates a binary investment decision at exactly - this size. When None, allows continuous sizing between minimum and maximum bounds. - minimum_size: Lower bound for continuous sizing decisions. Defaults to a small - positive value (CONFIG.modeling.EPSILON) to avoid numerical issues. - Ignored when fixed_size is specified. - maximum_size: Upper bound for continuous sizing decisions. Defaults to a large - value (CONFIG.modeling.BIG) representing unlimited capacity. - Ignored when fixed_size is specified. - optional: Controls whether investment is required. When True (default), - optimization can choose not to invest. When False, forces investment - to occur (useful for mandatory upgrades or replacement decisions). - fix_effects: Fixed costs incurred once if investment is made, regardless - of size. Dictionary mapping effect names to values - (e.g., {'cost': 10000, 'CO2_construction': 500}). - specific_effects: Variable costs proportional to investment size, representing - per-unit costs (€/kW, €/mΒ²). Dictionary mapping effect names to unit values - (e.g., {'cost': 1200, 'steel_required': 0.5}). - piecewise_effects: Non-linear cost relationships using PiecewiseEffects for - economies of scale, learning curves, or threshold effects. Can be combined - with fix_effects and specific_effects. - divest_effects: Costs incurred if the investment is NOT made, such as - demolition of existing equipment, contractual penalties, or lost opportunities. - Dictionary mapping effect names to values. + fixed_size: Creates binary decision at this exact size. None allows continuous sizing. + minimum_size: Lower bound for continuous sizing. Default: CONFIG.modeling.EPSILON. + Ignored if fixed_size is specified. + maximum_size: Upper bound for continuous sizing. Default: CONFIG.modeling.BIG. + Ignored if fixed_size is specified. + optional: If True, can choose not to invest. If False, investment is mandatory. + Default: True. + fix_effects: Fixed costs if investment is made, regardless of size. + Dict: {'effect_name': value} (e.g., {'cost': 10000}). + specific_effects: Variable costs proportional to size (per-unit costs). + Dict: {'effect_name': value/unit} (e.g., {'cost': 1200}). + piecewise_effects: Non-linear costs using PiecewiseEffects. + Combinable with fix_effects and specific_effects. + divest_effects: Costs incurred if NOT investing (demolition, penalties). + Dict: {'effect_name': value}. Cost Annualization Requirements: All cost values must be properly weighted to match the optimization model's time horizon. @@ -936,6 +936,10 @@ class OnOffParameters(Interface): - **Backup Equipment**: Emergency generators, standby systems - **Process Equipment**: Compressors, pumps with operational constraints + Mathematical Formulation: + See the complete mathematical model in the documentation: + [OnOffParameters](../user-guide/mathematical-notation/features/OnOffParameters.md) + Args: effects_per_switch_on: Costs or impacts incurred for each transition from off state (var_on=0) to on state (var_on=1). Represents startup costs, diff --git a/mkdocs.yml b/mkdocs.yml index 98747d987..8464398c2 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -8,6 +8,56 @@ site_url: https://flixopt.github.io/flixopt/ repo_url: https://github.com/flixOpt/flixopt repo_name: flixOpt/flixopt +nav: + - Home: index.md + - Getting Started: getting-started.md + - User Guide: + - user-guide/index.md + - Recipes: user-guide/recipies/index.md + - Mathematical Notation: + - Overview: user-guide/mathematical-notation/index.md + - Dimensions: user-guide/mathematical-notation/dimensions.md + - Elements: + - Flow: user-guide/mathematical-notation/elements/Flow.md + - Bus: user-guide/mathematical-notation/elements/Bus.md + - Storage: user-guide/mathematical-notation/elements/Storage.md + - LinearConverter: user-guide/mathematical-notation/elements/LinearConverter.md + - Features: + - InvestParameters: user-guide/mathematical-notation/features/InvestParameters.md + - OnOffParameters: user-guide/mathematical-notation/features/OnOffParameters.md + - Piecewise: user-guide/mathematical-notation/features/Piecewise.md + - Effects, Penalty & Objective: user-guide/mathematical-notation/effects-penalty-objective.md + - Modeling Patterns: + - Overview: user-guide/mathematical-notation/modeling-patterns/index.md + - Bounds and States: user-guide/mathematical-notation/modeling-patterns/bounds-and-states.md + - Duration Tracking: user-guide/mathematical-notation/modeling-patterns/duration-tracking.md + - State Transitions: user-guide/mathematical-notation/modeling-patterns/state-transitions.md + - Examples: examples/ + - Contribute: contribute.md + - API Reference: + - api-reference/index.md + - Aggregation: api-reference/aggregation.md + - Calculation: api-reference/calculation.md + - Commons: api-reference/commons.md + - Components: api-reference/components.md + - Config: api-reference/config.md + - Core: api-reference/core.md + - Effects: api-reference/effects.md + - Elements: api-reference/elements.md + - Features: api-reference/features.md + - Flow System: api-reference/flow_system.md + - Interface: api-reference/interface.md + - IO: api-reference/io.md + - Linear Converters: api-reference/linear_converters.md + - Modeling: api-reference/modeling.md + - Network App: api-reference/network_app.md + - Plotting: api-reference/plotting.md + - Results: api-reference/results.md + - Solvers: api-reference/solvers.md + - Structure: api-reference/structure.md + - Utils: api-reference/utils.md + - Release Notes: changelog/ + theme: name: material @@ -88,9 +138,6 @@ plugins: - gen-files: scripts: - scripts/gen_ref_pages.py - - literate-nav: - nav_file: SUMMARY.md - implicit_index: true # This makes index.md the default landing page - mkdocstrings: # Handles automatic API documentation generation default_handler: python # Sets Python as the default language handlers: diff --git a/scripts/gen_ref_pages.py b/scripts/gen_ref_pages.py index f2de8a701..3c8eb600a 100644 --- a/scripts/gen_ref_pages.py +++ b/scripts/gen_ref_pages.py @@ -1,4 +1,4 @@ -"""Generate the code reference pages and navigation.""" +"""Generate the code reference pages.""" import sys from pathlib import Path @@ -9,11 +9,11 @@ root = Path(__file__).parent.parent sys.path.insert(0, str(root)) -nav = mkdocs_gen_files.Nav() - src = root / 'flixopt' api_dir = 'api-reference' +generated_files = [] + for path in sorted(src.rglob('*.py')): module_path = path.relative_to(src).with_suffix('') doc_path = path.relative_to(src).with_suffix('.md') @@ -30,10 +30,8 @@ elif parts[-1] == '__main__' or parts[-1].startswith('_'): continue - # Only add to navigation if there are actual parts + # Only generate documentation if there are actual parts if parts: - nav[parts] = doc_path.as_posix() - # Generate documentation file - always using the flixopt prefix with mkdocs_gen_files.open(full_doc_path, 'w') as fd: # Use 'flixopt.' prefix for all module references @@ -41,6 +39,7 @@ fd.write(f'::: {module_id}\n options:\n inherited_members: true\n') mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root)) + generated_files.append(str(full_doc_path)) # Create an index file for the API reference with mkdocs_gen_files.open(f'{api_dir}/index.md', 'w') as index_file: @@ -50,5 +49,7 @@ 'For more information on how to use the classes and functions, see the [User Guide](../user-guide/index.md) section.\n' ) -with mkdocs_gen_files.open(f'{api_dir}/SUMMARY.md', 'w') as nav_file: - nav_file.writelines(nav.build_literate_nav()) +# Print generated files for validation +print(f'Generated {len(generated_files)} API reference files:') +for file in sorted(generated_files): + print(f' - {file}')