Skip to content

Commit

Permalink
Updated comments in complex_example.py and complex_example_results.py
Browse files Browse the repository at this point in the history
  • Loading branch information
FBumann committed Nov 12, 2024
1 parent 006c7ae commit 03aabfa
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 81 deletions.
147 changes: 70 additions & 77 deletions examples/Ex02_complex/complex_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,143 +11,136 @@

if __name__ == '__main__':

# Some options to experiment with
# --- Experiment Options ---
# Configure options for testing various parameters and behaviors
check_penalty = False
excess_penalty = 1e5
use_chp_with_segments = False
time_indices = None # You can solve the whole calculation or just a custom fraction of time steps [0, 1, 2, 3]
time_indices = None # Define specific time steps for custom calculations, or use the entire series

# --- Define Demand and Price Profiles ---
# Input data for electricity and heat demands, as well as electricity price
electricity_demand = np.array([70., 80., 90., 90, 90, 90, 90, 90, 90])
heat_demand = np.array([30., 0., 90., 110, 2000, 20, 20, 20, 20]) if check_penalty else np.array([30., 0., 90., 110, 110, 20, 20, 20, 20])
electricity_price = np.array([40., 40., 40., 40, 40, 40, 40, 40, 40])

time_series = fx.create_datetime_array('2020-01-01', len(heat_demand), freq='h')

# ## Bus-Definition: ##
# --- Define Energy Buses ---
# Represent different energy carriers (electricity, heat, gas) in the system
Strom = fx.Bus('Strom', excess_penalty_per_flow_hour=excess_penalty)
Fernwaerme = fx.Bus('Fernwärme', excess_penalty_per_flow_hour=excess_penalty)
Gas = fx.Bus('Gas', excess_penalty_per_flow_hour=excess_penalty)

# Effect-Definition:
# --- Define Effects ---
# Specify effects related to costs, CO2 emissions, and primary energy consumption
Costs = fx.Effect('costs', '€', 'Kosten', is_standard=True, is_objective=True)
CO2 = fx.Effect('CO2', 'kg', 'CO2_e-Emissionen', specific_share_to_other_effects_operation={Costs: 0.2})
PE = fx.Effect('PE', 'kWh_PE', 'Primärenergie', maximum_total=3.5e3)

################################
# ## definition of components ##
################################

# 1. definition of boiler #
# --- Define Components ---
# 1. Define Boiler Component
# A gas boiler that converts fuel into thermal output, with investment and on-off parameters
Gaskessel = fx.linear_converters.Boiler(
'Kessel', eta=0.5, # efficiency ratio
on_off_parameters=fx.OnOffParameters(effects_per_running_hour={Costs: 0, CO2: 1000}), # 1000 kg_CO2/h (just for testing)
# defining flows:
Q_th=fx.Flow(label='Q_th', # name
bus=Fernwaerme, # linked bus
size=fx.InvestParameters(fix_effects=1000, # 1000 € investment costs
fixed_size=50, # fixed size
optional=False, # forced investment
specific_effects={Costs: 10, PE: 2}), # specific costs: 10 €/kW; 2 kWh_PE/kW
load_factor_max=1.0, # maximal mean power 50 kW
load_factor_min=0.1, # minimal mean power 5 kW
relative_minimum=5 / 50, # 10 % part load
relative_maximum=1, # 50 kW
previous_flow_rate=50, # 50 kW is value before start
flow_hours_total_max=1e6, # kWh, overall maximum "flow-work"
'Kessel', eta=0.5, # Efficiency ratio
on_off_parameters=fx.OnOffParameters(effects_per_running_hour={Costs: 0, CO2: 1000}), # CO2 emissions per hour
Q_th=fx.Flow(label='Q_th', # Thermal output
bus=Fernwaerme, # Linked bus
size=fx.InvestParameters(fix_effects=1000, # Fixed investment costs
fixed_size=50, # Fixed size
optional=False, # Forced investment
specific_effects={Costs: 10, PE: 2}), # Specific costs
load_factor_max=1.0, # Maximum load factor (50 kW)
load_factor_min=0.1, # Minimum load factor (5 kW)
relative_minimum=5 / 50, # Minimum part load
relative_maximum=1, # Maximum part load
previous_flow_rate=50, # Previous flow rate
flow_hours_total_max=1e6, # Total energy flow limit
can_be_off=fx.OnOffParameters(
on_hours_total_min=0, # minimum of working hours
on_hours_total_max=1000, # maximum of working hours
consecutive_on_hours_max=10, # maximum of working hours in one step
consecutive_off_hours_max=10, # maximum of off hours in one step
# consecutive_on_hours_min = 2, # minimum on hours in one step
# consecutive_off_hours_min = 4, # minimum off hours in one step
effects_per_switch_on=0.01, # € per start
switch_on_total_max=1000), # max nr of starts
on_hours_total_min=0, # Minimum operating hours
on_hours_total_max=1000, # Maximum operating hours
consecutive_on_hours_max=10, # Max consecutive operating hours
consecutive_off_hours_max=10, # Max consecutive off hours
effects_per_switch_on=0.01, # Cost per switch-on
switch_on_total_max=1000), # Max number of starts
),
Q_fu=fx.Flow(label='Q_fu', # name
bus=Gas, # linked bus
size=200, # kW
relative_minimum=0,
relative_maximum=1))
Q_fu=fx.Flow(label='Q_fu', bus=Gas, size=200))

# 2. defining of CHP-unit:
# 2. Define CHP Unit
# Combined Heat and Power unit that generates both electricity and heat from fuel
aKWK = fx.linear_converters.CHP(
'BHKW2', eta_th=0.5, eta_el=0.4, on_off_parameters=fx.OnOffParameters(effects_per_switch_on=0.01),
P_el=fx.Flow('P_el', bus=Strom, size=60, relative_minimum=5 / 60),
Q_th=fx.Flow('Q_th', bus=Fernwaerme, size=1e3),
Q_fu=fx.Flow('Q_fu', bus=Gas, size=1e3, previous_flow_rate=20)) # The CHP was ON previously

# 3. defining a alternative CHP-unit with linear segments :
# (Flows mst be defined before the segmented_conversion_factors can be defined)
# 3. Define CHP with Linear Segments
# This CHP unit uses linear segments for more dynamic behavior over time
P_el = fx.Flow('P_el', bus=Strom, size=60, previous_flow_rate=20)
Q_th = fx.Flow('Q_th', bus=Fernwaerme)
Q_fu = fx.Flow('Q_fu', bus=Gas)
segmented_conversion_factors = ({P_el: [(5, 30), (40, 60)], # elements can also be a time series (can change over time)
segmented_conversion_factors = ({P_el: [(5, 30), (40, 60)], # Similar to eta_th, each factor here can be an array
Q_th: [(6, 35), (45, 100)],
Q_fu: [(12, 70), (90, 200)]})

aKWK2 = fx.LinearConverter('BHKW2', inputs=[Q_fu], outputs=[P_el, Q_th], segmented_conversion_factors=segmented_conversion_factors,
on_off_parameters=fx.OnOffParameters(effects_per_switch_on=0.01))

# 4. definition of storage:
# segmented_investment_effects: [start1, end1, start2, end2, ...] If start and end are equal, then its a point
segmented_investment_effects = ([(5, 25), (25, 100)], # size
{Costs: [(50, 250), (250, 800)], # €
PE: [(5, 25), (25, 100)] # kWh_PE
})
# 4. Define Storage Component
# Storage with variable size and segmented investment effects
segmented_investment_effects = ([(5, 25), (25, 100)], # Investment size
{Costs: [(50, 250), (250, 800)], # Investment costs
PE: [(5, 25), (25, 100)]}) # Primary energy costs

aSpeicher = fx.Storage('Speicher', # defining flows:
aSpeicher = fx.Storage('Speicher',
charging=fx.Flow('Q_th_load', bus=Fernwaerme, size=1e4),
discharging=fx.Flow('Q_th_unload', bus=Fernwaerme, size=1e4),
capacity_in_flow_hours=fx.InvestParameters(
fix_effects=0, # no fix costs
fixed_size=None, # variable size
effects_in_segments=segmented_investment_effects, # see above
optional=False, # forced invest
specific_effects={Costs: 0.01, CO2: 0.01}, # €/kWh; kg_CO2/kWh
minimum_size=0, maximum_size=1000), # optimizing between 0...1000 kWh
initial_charge_state=0, # empty storage at beginning
# minimal_final_charge_state = 3, # min charge state and end
maximal_final_charge_state=10, # max charge state and end
eta_charge=0.9, eta_discharge=1, # efficiency of (un)-loading
relative_loss_per_hour=0.08, # loss of storage per time
prevent_simultaneous_charge_and_discharge=True) # no parallel loading and unloading

# 5. definition of sinks and sources:
# 5.a) heat load profile:
Waermelast = fx.Sink('Wärmelast', sink=fx.Flow('Q_th_Last', # name
bus=Fernwaerme, # linked bus
size=1, relative_maximum=max(heat_demand), fixed_relative_value=heat_demand)) # fixed values fixed_relative_value * size
# 5.b) gas tarif:
Gasbezug = fx.Source('Gastarif', source=fx.Flow('Q_Gas', bus=Gas, # linked bus
size=1000, # defining nominal size
effects_in_segments=segmented_investment_effects, # Investment effects
optional=False, # Forced investment
minimum_size=0, maximum_size=1000), # Optimizing between 0 and 1000 kWh
initial_charge_state=0, # Initial charge state
maximal_final_charge_state=10, # Maximum final charge state
eta_charge=0.9, eta_discharge=1, # Charge/discharge efficiency
relative_loss_per_hour=0.08, # Energy loss per hour, relative to current charge state
prevent_simultaneous_charge_and_discharge=True) # Prevent simultaneous charge/discharge

# 5. Define Sinks and Sources
# 5.a) Heat demand profile
Waermelast = fx.Sink('Wärmelast', sink=fx.Flow('Q_th_Last', # Heat sink
bus=Fernwaerme, # Linked bus
size=1, relative_maximum=max(heat_demand), fixed_relative_value=heat_demand)) # Fixed demand profile

# 5.b) Gas tariff
Gasbezug = fx.Source('Gastarif', source=fx.Flow('Q_Gas', bus=Gas, # Gas source
size=1000, # Nominal size
effects_per_flow_hour={Costs: 0.04, CO2: 0.3}))
# 5.c) feed-in of electricity:
Stromverkauf = fx.Sink('Einspeisung', sink=fx.Flow('P_el', bus=Strom, # linked bus
effects_per_flow_hour=-1 * electricity_price)) # feed-in tariff

# ## Build FlowSystem ##
# Choose which of the above created Elements you want to add to The final FlowSystem. Not added Elements are not part of the Model!
# 5.c) Feed-in of electricity
Stromverkauf = fx.Sink('Einspeisung', sink=fx.Flow('P_el', bus=Strom, # Feed-in tariff for electricity
effects_per_flow_hour=-1 * electricity_price)) # Negative price for feed-in

flow_system = fx.FlowSystem(time_series, last_time_step_hours=None) # creating FlowSystem
# --- Build FlowSystem ---
# Select components to be included in the final system model
flow_system = fx.FlowSystem(time_series, last_time_step_hours=None) # Create FlowSystem

flow_system.add_elements(Costs, CO2, PE, Gaskessel, Waermelast, Gasbezug, Stromverkauf, aSpeicher)
flow_system.add_elements(aKWK2) if use_chp_with_segments else flow_system.add_components(aKWK)

print(flow_system) # Get a string representation of the FlowSystem

# --- Solve FlowSystem ---
calculation = fx.FullCalculation('Sim1', flow_system, 'pyomo', time_indices)
calculation.do_modeling()

# Show variables as str (else, you can find them in the results.yaml file
print(calculation.system_model.description_of_equations())
print(calculation.system_model.description_of_variables())


calculation.solve(fx.solvers.HighsSolver(mip_gap=0.005, time_limit_seconds=30), # Specify which solver you want to use and specify parameters
save_results='results') # If and where to save results


# --- Results ---
# You can analyze results directly. But it's better to save them to a file and start from there,
# letting you continue at any time
# See complex_example_evaluation.py
Expand Down
12 changes: 8 additions & 4 deletions examples/Ex02_complex/complex_example_results.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
# -*- coding: utf-8 -*-
"""
This shows how to analyze results from file
Created on Thu Jun 16 11:19:17 2022
developed by Felix Panitz* and Peter Stange*
* at Chair of Building Energy Systems and Heat Supply, Technische Universität Dresden
"""

import pandas as pd
import plotly.offline
import flixOpt as fx

if __name__ == '__main__':
# load results
# --- Load Results ---
try:
results = fx.results.CalculationResults('Sim1', folder='results')
except FileNotFoundError:
raise FileNotFoundError('Results file was not found. DId you run complex_example.py already?')

# Basic overview
# --- Basic overview ---
results.visualize_network()
results.plot_operation('Fernwärme')
results.plot_operation('Fernwärme', engine='matplotlib')
results.plot_operation('Fernwärme', 'area')

# --- Detailed Plots ---
# In depth plot for individual flow rates ('__' is used as the delimiter between Component and Flow
results.plot_flow_rate('Wärmelast__Q_th_Last', 'heatmap')
figs = []
Expand All @@ -27,7 +31,7 @@
fig = results.plot_flow_rate(flow_label, 'heatmap', heatmap_steps_per_period='h', heatmap_periods='D')


# Visualizing an internal variables
# --- Plotting internal variables manually ---
on_data = pd.DataFrame({'BHKW2 On': results.component_results['BHKW2'].variables['Q_th']['OnOff']['on'],
'Kessel On': results.component_results['Kessel'].variables['Q_th']['OnOff']['on']},
index = results.time)
Expand Down

0 comments on commit 03aabfa

Please sign in to comment.