Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
394e7fa
first implementation of virtual storage DSM Sink class
May 19, 2025
91c0fe5
implemented initial and final charge state constraints
May 19, 2025
7933a9c
added penalty costs for positive and negative charge states
May 20, 2025
693e354
added charge state exclusivity constraints
May 24, 2025
3536c43
added epsilon values to the charge state exclusivity to avoid numeric…
May 24, 2025
797c751
fixed minor flaw where penalty costs would not scale with hours per t…
May 24, 2025
c1ab820
implemented DSMSinkTS class for demand side management using a timesh…
May 26, 2025
6ceb3ec
renamed DSMSink class to SDMSinkVS; removed hours_per_timestep in pen…
May 26, 2025
03fb387
changes to how hours_per_timestep is handeled in the DSMSinkVS class
Jun 4, 2025
b1de3a1
added diagrams for the DSM classes in examples files
Jun 4, 2025
bba2060
added constraints that set unused deficit variables to zero
Jun 4, 2025
e8c07f8
penalty costs now correctly scale with hours_per_step
Jun 6, 2025
c65e2bb
amount of timesteps now adapt automatically to timestep length
Jun 7, 2025
c9fd235
implemented possibility to set boundaries for cumulated flow deviatio…
Jun 9, 2025
5962487
some changes made to the diagrams in minimal example timeshift DSM
Jun 10, 2025
c65921d
small changes to diagram scaling in timeshift DSM example
Jun 10, 2025
d83c65f
initial commit
Jun 10, 2025
6c92abe
added seperate positive and negative charge rates instead of one nett…
Jun 10, 2025
cad7b5b
added parameter for allowing simultaneous charge and discharge plus s…
Jun 10, 2025
467a716
implemented full functionality of both DSM classes in a common DSMSin…
Jun 10, 2025
2657081
fixed minor error with timeshift limit constraints
Jun 10, 2025
da7b82c
removed possibility to manualley set initial and final charge states.…
Jun 10, 2025
3998e59
added native diagrams for dsm sink class
Jun 11, 2025
a714a78
changed DSM Sink class to use parameters for absolute bounds instead …
Jun 11, 2025
75757d0
removed DSMSinkTS class because all the functionalities are now model…
Jun 11, 2025
c72022a
changed some parameter names to make them more intuitive and some cha…
Jun 11, 2025
1a335dd
used built-in epsilon value for default penalty costs
Jun 11, 2025
091282a
minor changes to comments
Jun 11, 2025
4c63668
Added many plausibility checks
Jun 11, 2025
62d52bf
small style change to DSM diagramm
Jun 11, 2025
096d001
implemented possibility to add penalty costs to charge rates + some c…
Jun 28, 2025
96bad7c
renamed a label in DSM sink plot
Jul 4, 2025
8c4c118
Update examples/00_Minmal/minimal_example_DSM.py
FBumann Oct 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions examples/00_Minmal/minimal_example_DSM.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"""
This script shows how to use the flixopt framework to model a super minimalistic energy system.
"""

import numpy as np
import pandas as pd
from rich.pretty import pprint

import flixopt as fx
if __name__ == '__main__':
# --- Define the Flow System, that will hold all elements, and the time steps you want to model ---
timesteps = pd.date_range('2020-01-01', periods=24, freq='h')
flow_system = fx.FlowSystem(timesteps)

# --- Define Thermal Load Profile ---
# Load profile (e.g., kW) for heating demand over time
thermal_load_profile = np.array([80, 80, 80, 80, 80, 80, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 80, 80, 80, 80, 80, 80])
#thermal_load_profile = np.array([100, 100, 100, 100, 100, 100, 120, 120, 120, 100, 100, 100, 100, 100, 100, 80, 80, 80, 100, 100, 100, 100, 100, 100])
#thermal_load_profile = np.array([80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80])

# --- Define Energy Buses ---
# These are balancing nodes (inputs=outputs) and balance the different energy carriers your system
flow_system.add_elements(fx.Bus('District Heating'), fx.Bus('Natural Gas'))

# --- Define Objective Effect (Cost) ---
# Cost effect representing the optimization objective (minimizing costs)
cost_effect = fx.Effect('costs', '€', 'Cost', is_standard=True, is_objective=True)

# --- Define Flow System Components ---
# Boiler component with thermal output (heat) and fuel input (gas)
boiler1 = fx.linear_converters.Boiler(
'Boiler1',
eta=0.5,
Q_th=fx.Flow(label='Thermal Output', bus='District Heating', size=100),
Q_fu=fx.Flow(label='Fuel Input', bus='Natural Gas'),
)
boiler2 = fx.linear_converters.Boiler(
'Boiler2',
eta=1/3,
Q_th=fx.Flow(label='Thermal Output', bus='District Heating', size=50),
Q_fu=fx.Flow(label='Fuel Input', bus='Natural Gas'),
)

# Heat load component with a fixed thermal demand profile
heat_load = fx.DSMSink(
'DSM Sink Heat Demand',
sink=fx.Flow(label='Heat Load', bus='District Heating', size=150),
initial_demand=thermal_load_profile,
maximum_cumulated_deficit = -50,
maximum_cumulated_surplus = 50,
maximum_flow_deficit = -20,
maximum_flow_surplus = 20,
relative_loss_per_hour_positive_charge_state = 0.05,
relative_loss_per_hour_negative_charge_state = 0.05,
penalty_costs_positive_charge_states=0,
penalty_costs_negative_charge_states=0.01,
forward_timeshift = 3,
backward_timeshift = 3
)

# Gas source component with cost-effect per flow hour
gas_source = fx.Source(
'Natural Gas Tariff',
source=fx.Flow(label='Gas Flow', bus='Natural Gas', size=1000, effects_per_flow_hour=0.04), # 0.04 €/kWh
)

# --- Build the Flow System ---
# Add all components and effects to the system
flow_system.add_elements(cost_effect, boiler1, boiler2, heat_load, gas_source)

# --- Define, model and solve a Calculation ---
calculation = fx.FullCalculation('Simulation1', flow_system)
calculation.do_modeling()
#calculation.solve(fx.solvers.HighsSolver(0.01, 60))
calculation.solve(fx.solvers.GurobiSolver(0.001, 60))

# --- Analyze Results ---
# Access the results of an element
df1 = calculation.results['costs'].filter_solution('time').to_dataframe()

# Original plots
#calculation.results['District Heating'].plot_node_balance_pie()
calculation.results['District Heating'].plot_node_balance()
calculation.results['DSM Sink Heat Demand'].plot_DSM_sink()

# Save the DSM Sink Heat Demand solution dataset to a CSV file
calculation.results['DSM Sink Heat Demand'].solution.to_dataframe().to_csv('results/DSM_Sink_Heat_Demand_results.csv')

# Save results to a file
df2 = calculation.results['District Heating'].node_balance().to_dataframe()
#df2.to_csv('results/District Heating.csv') # Save results to csv

# Print infos to the console.
pprint(calculation.summary)
1 change: 1 addition & 0 deletions flixopt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
PiecewiseEffects,
SegmentedCalculation,
Sink,
DSMSink,
Source,
SourceAndSink,
Storage,
Expand Down
2 changes: 2 additions & 0 deletions flixopt/commons.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from .components import (
LinearConverter,
Sink,
DSMSink,
Source,
SourceAndSink,
Storage,
Expand All @@ -29,6 +30,7 @@
'Effect',
'Source',
'Sink',
'DSMSink',
'SourceAndSink',
'Storage',
'LinearConverter',
Expand Down
Loading