A Python framework for modeling and optimizing flexibility assets in energy systems from a utility company perspective.
This framework provides a clean, extensible architecture for evaluating the technical and economic potential of flexibility resources such as battery storage, photovoltaic systems, domestic hot water heaters, and other distributed energy resources (DERs).
The framework is designed to:
- Separate concerns: Physical behavior, economic evaluation, and operational decisions are cleanly separated into distinct layers
- Enable optimization: Provide interfaces that optimization algorithms can use to evaluate and execute operations
- Support multiple assets: Generic base classes allow easy implementation of new flexibility assets
- Facilitate learning: Clear architecture and comprehensive documentation make it suitable for teaching energy systems modeling
- Evaluating flexibility potential for utility companies
- Optimizing distributed energy resources (DERs) operation
- Assessing techno-economic feasibility of flexibility services
- Teaching energy systems modeling and optimization
- Research on flexibility aggregation and market participation
The framework uses a three-layer architecture that separates physical modeling from economic evaluation, joined by operational decision-making:
┌─────────────────────────────────────────────────────┐
│ FlexAsset │
│ (Operational Composition) │
│ • evaluate_operation() - Check feasibility & cost │
│ • execute_operation() - Update state & track │
└───────────────┬─────────────────┬───────────────────┘
│ │
┌────────▼────────┐ ┌────▼──────────┐
│ FlexUnit │ │ CostModel │
│ (Physics) │ │ (Economics) │
│ │ │ │
│ • Power limits │ │ • Investment │
│ • Efficiency │ │ • Degradation │
│ • State (SOC) │ │ • Energy cost │
│ • Constraints │ │ • Revenues │
└─────────────────┘ └───────────────┘
Models the physical behavior of a flexibility asset:
- Power limits and ramping constraints
- Energy capacity and state of charge (SOC)
- Efficiency characteristics (charging, discharging, conversion)
- Self-discharge, standby losses, degradation
- Availability and maintenance schedules
Key concept: Energy headroom representation
E_plus: Energy available to inject into the grid [kWh]E_minus: Energy capacity available to draw from the grid [kWh]
Evaluates the economic implications of operations:
- Investment costs (CAPEX)
- Fixed and variable O&M costs
- Degradation and cycling costs
- Energy purchase and sales prices (time-varying)
- Capacity reservation payments
- Emissions costs (CO₂)
Composes physics and economics to provide an operational interface:
evaluate_operation(): Check if an operation is feasible and calculate its costexecute_operation(): Execute the operation and update system state- Tracks operational metrics (throughput, costs, activations)
This interface allows optimization algorithms to explore the solution space without needing to understand the internal physics or economics.
A complete implementation demonstrating all three layers:
- BatteryUnit: Physical model with efficiency, SOC limits, power constraints, self-discharge
- BatteryCostModel: Economic model with investment and degradation costs
- BatteryFlex: Operational composition for feasibility checking and execution
Example:
from flex_model.assets import BatteryUnit, BatteryCostModel, BatteryFlex
# 1. Create physical model
battery_unit = BatteryUnit(
name="BESS_100kWh",
capacity_kwh=100.0, # 100 kWh storage
power_kw=50.0, # 50 kW charge/discharge
efficiency=0.95, # 95% one-way efficiency
soc_min=0.1, # Don't discharge below 10%
soc_max=0.9, # Don't charge above 90%
)
# 2. Create economic model
battery_cost = BatteryCostModel(
name="battery_economics",
c_inv=500.0, # 500 CHF/kWh investment
n_lifetime=10.0, # 10 year lifetime
p_int=0.05, # 0.05 CHF/kWh degradation cost
)
# 3. Compose operational interface
battery = BatteryFlex(unit=battery_unit, cost_model=battery_cost)
# 4. Initialize state (50% SOC)
battery.reset(E_plus_init=50.0, E_minus_init=50.0)
# 5. Evaluate operation (discharge 30 kW for 15 min)
result = battery.evaluate_operation(
t=10,
P_grid_import=0.0,
P_grid_export=30.0
)
if result['feasible']:
print(f"Operation cost: {result['cost']:.2f} CHF")
print(f"SOC after: {result['soc']:.1%}")
# 6. Execute if optimal
battery.execute_operation(t=10, P_grid_import=0.0, P_grid_export=30.0)- Photovoltaic (PV): Solar generation with curtailment options
- Domestic Hot Water (DHW): Thermal storage with temperature layers
- Heat Pumps: Heating/cooling with COP modeling
- Electric Vehicles (EV): Mobile storage with availability patterns
# Clone the repository
git clone https://github.com/yourusername/GenericFlexiblityModel.git
cd GenericFlexiblityModel
# Install in development mode
pip install -e .
# Run tests
pytest tests/ -v- Python 3.10+
- NumPy (for numerical operations)
- pytest (for testing)
GenericFlexiblityModel/
├── flex_model/
│ ├── core/
│ │ ├── flex_unit.py # Base class for physical models
│ │ ├── cost_model.py # Base class for economic models
│ │ ├── flex_asset.py # Base class for operational interface
│ │ └── access_state.py # State management utilities
│ └── assets/
│ ├── battery.py # Battery implementation (BatteryUnit, BatteryCostModel, BatteryFlex)
│ └── ... # Future: PV, DHW, heat pumps, etc.
├── tests/
│ ├── test_battery.py # Battery unit tests
│ └── ...
├── README.md
└── setup.py
The framework is designed to integrate with multiple optimization algorithms for finding optimal operation schedules. The FlexAsset interface provides two key methods:
- evaluate_operation(): For optimization algorithms to query feasibility and costs without modifying state
- execute_operation(): To apply the optimal solution and update system state
- Optimizer proposes an operation (e.g., "charge battery at 30 kW")
- FlexAsset evaluates feasibility and calculates cost
- Optimizer explores solution space using its algorithm-specific approach
- Optimal operations are executed to update system state
Location: flex_model/optimization/lp_optimizer.py
Approach: Converts FlexAssets to LinearModel matrix representation and solves globally optimal solution using scipy's HiGHS solver.
Use cases:
- Assets with linear constraints and costs (battery, market settlement)
- Multi-asset coordination with global energy balance
- Benchmark for optimal solutions
Limitations: Only handles convex, linear problems. Cannot model discrete decisions or non-linear relationships.
Location: examples/battery_vs_market/greedy_optimizer.py
Approach: Time-sequential, rule-based decision making with threshold logic.
Use cases:
- Fast approximate solutions
- Real-time control where perfect foresight isn't available
- Baseline comparison for more sophisticated methods
Limitations: Myopic (no future foresight), sub-optimal solutions.
The framework is optimization-algorithm agnostic and designed to support:
Planned implementation: Extend LinearModel to support integer decision variables.
Use cases:
- Discrete operating states (on/off decisions)
- PV curtailment levels
- Scheduling with binary availability constraints
Approach: Extends LP with integer variables, uses commercial solvers (Gurobi, CPLEX) or open-source (HiGHS with MILP support).
Planned implementation: State-space representation with backward induction.
Use cases:
- Sequential decision problems with state dependencies
- Stochastic optimization (if extended to SDP)
- Problems where Bellman optimality principle applies
Approach: Discretize state space (e.g., SOC levels), compute value function backward in time.
Planned implementation: Population-based evolutionary optimization.
Use cases:
- Non-convex, non-linear objective functions
- Complex constraints that don't fit LP/MILP formulations
- Heat pumps with COP curves, thermal systems with stratification
Approach: Encode operation schedules as chromosomes, evolve through selection, crossover, and mutation.
Planned implementation: Swarm intelligence approach.
Use cases:
- Similar to GA, but often faster convergence
- Continuous decision spaces
- Multi-modal optimization landscapes
Approach: Particles explore solution space guided by personal and global best solutions.
Planned implementation: Probabilistic single-solution metaheuristic.
Use cases:
- Escaping local optima in non-convex problems
- Faster than population methods for certain problem structures
- When solution quality vs. computation time tradeoff is important
Approach: Random walk with probabilistic acceptance of worse solutions, cooling schedule.
Planned implementation: Learn optimal policies through interaction.
Use cases:
- Unknown or uncertain system dynamics
- Adaptive control that learns from operational data
- Real-time optimization with prediction updates
Approach: Use evaluate_operation() as environment, learn Q-values or policy networks.
| Algorithm | Speed | Optimality | Problem Types | Maturity |
|---|---|---|---|---|
| LP | Fast | Globally optimal | Linear, convex | ✅ Implemented |
| Greedy | Very fast | Sub-optimal | Any (heuristic) | ✅ Implemented |
| MILP | Medium | Globally optimal | Linear + discrete | 🚧 Planned |
| DP | Medium | Globally optimal | Sequential, discrete state | 🚧 Planned |
| GA | Slow | Near-optimal | Non-linear, non-convex | 🚧 Planned |
| PSO | Medium | Near-optimal | Non-linear, continuous | 🚧 Planned |
| SA | Medium | Near-optimal | Non-linear, non-convex | 🚧 Planned |
| RL | Slow (training) | Near-optimal | Unknown dynamics | 🚧 Planned |
Different optimization algorithms require different model representations. FlexAssets support multiple representations through dedicated methods:
- Operational interface (
evaluate_operation,execute_operation): Used by greedy, GA, PSO, SA, RL - Linear model (
get_linear_model): Used by LP, MILP - State-space model (future:
get_dp_model): Used by DP - Differentiable model (future:
get_torch_model): Used by gradient-based RL
As new optimizers are implemented, corresponding representation methods will be added to FlexAsset classes. A model consistency test framework ensures all representations produce equivalent results.
The framework includes an interactive visualization toolkit for analyzing optimization results and supporting business decision-making.
Install visualization dependencies:
pip install -e .[visualization]
# or manually:
pip install plotly pandasOptimizationResult: Wrapper for optimizer outputs with convenient data extraction methods
get_power_profile()- Extract power dispatch time-seriesget_soc_profile()- Extract state of charge evolutionget_cost_breakdown()- Parse cost components by assetget_utilization_metrics()- Calculate capacity factors, cycle counts
EconomicMetrics: Calculator for financial KPIs
compute_roi()- Return on investment [%]compute_payback_period()- Years to break evencompute_npv()- Net present value with discountingcompute_lcoe()- Levelized cost of energycompute_financial_summary()- Complete KPI dashboard
Visualization Plots: Interactive Plotly visualizations
- Operational: Power dispatch profiles, SOC evolution, price overlays
- Economic: Cost breakdown, savings comparison, ROI gauges, payback timelines
- Executive: Multi-panel financial dashboards
from flex_model.visualization import OptimizationResult, EconomicMetrics
from flex_model.visualization.plots import OperationalPlots, EconomicPlots
# Run optimization
result_dict = optimizer.solve()
# Wrap result
result = OptimizationResult(
lp_result=result_dict,
assets={'battery': battery, 'market': market},
imbalance=imbalance_profile
)
# Generate visualizations
fig1 = OperationalPlots.create_dispatch_profile(result)
fig2 = OperationalPlots.create_soc_evolution(result, 'battery')
fig3 = EconomicPlots.create_savings_comparison(baseline_cost, optimized_cost)
# Display (in Jupyter) or save
fig1.show()
fig2.write_html('soc_evolution.html')
# Calculate economic metrics
financial_summary = EconomicMetrics.compute_financial_summary(
result=result,
baseline_cost=baseline_cost_annual,
lifetime_years=10,
discount_rate=0.05
)
print(f"ROI: {financial_summary['roi']:.1f}%")
print(f"Payback: {financial_summary['payback_period']:.1f} years")See a complete demonstration in examples/battery_vs_market/visualize_results.py:
cd examples/battery_vs_market
python visualize_results.pyThis generates 8 interactive HTML visualizations:
- Power dispatch profile (stacked area chart)
- SOC evolution with limits
- Price signals and market operations
- Cost breakdown by asset
- Savings vs baseline comparison
- ROI gauge with target benchmark
- Payback period timeline
- Comprehensive financial dashboard
Power Dispatch Profile: Shows how imbalances are balanced across battery and market
- Stacked area chart showing charge/discharge, import/export
- Imbalance profile overlay for context
- Toggle between system view and individual asset contributions
SOC Evolution: Battery state of charge with operating limits
- Line chart with SOC bounds (min/max) as reference
- Highlights constraint violations if any occur
- Dual y-axis showing both % and absolute energy [kWh]
Economic Dashboard: Multi-panel KPI summary for executives
- ROI gauge with color-coded targets
- Payback period bar chart vs lifetime
- Cost comparison (baseline vs optimized)
- Key metrics table (NPV, LCOE, savings)
All visualizations are:
- Interactive: Hover for details, zoom, pan, export images
- Customizable: Plotly figures can be modified before saving
- Business-focused: Designed for stakeholder communication
- Web-ready: Export to standalone HTML files
- Separation of Concerns: Physics, economics, and operations are cleanly separated
- Composition over Inheritance: FlexAsset composes FlexUnit + CostModel
- Interface Segregation: Optimization algorithms only see evaluate/execute interface
- Time-Dependent Flexibility: All parameters can vary with time
- Educational Value: Clear documentation and examples for learning
This is a research/educational project. Contributions are welcome:
- New flexibility asset implementations
- Optimization algorithm examples
- Documentation improvements
- Bug fixes and test coverage
This project is licensed under the MIT License - see the LICENSE file for details.
If you use this framework in your research, please cite:
[Add citation information when published]
Mathias Niffeler Urban Energy Systems Laboratory Empa, Dübendorf mathias.niffeler@empa.ch