Code Base
Following section display the code snippets that were used to create the TransC/E results and diagrams.
System-Model-Scenario-Combination Creation Code
Following code snippet shows how to create the TransC/E combinations
import os
import numpy as np
import pandas as pd
import tessif.frused.namedtuples as nts
from tessif.frused.paths import example_dir
from tessif.model import components, energy_system
def create_transcne_es(
periods=24,
transformer_efficiency=0.93,
gridcapacity=60000,
expansion=False
):
"""Create the TransCnE system model scenarios combinations.
Parameters
----------
periods : int, default=24
Number of time steps of the evaluated timeframe (one time step is one
hour)
transformer_efficiency : int, default=0.99
Efficiency of the grid transformers (must be a value between 0 and 1)
gridcapacity : int, default=60000
Transmission capacity of the transformers of the gridstructure
(at 0 the parts of the grid are not connected)
expansion: bool, default=False
If ``True`` :ref:`gridcapacity` is subject to expansion.
Note
----
Changes compared to `Hanke Project Thesis
<https://tore.tuhh.de/handle/11420/11759>`_:
1. Rename ``"Power Source [Voltage]"`` to
``"Deficit Source [Voltage]"``
2. Rename ``"Power Sinks [Voltage]"`` to ``"Excess Sinks [Voltage]"``
3. Rename ``"[High/Mid/Low] Voltage Powerline"`` to ``"[High/Mid/Low]
Voltage Grid"``
4. Rename ``"[Voltage to Voltage] Transformator"`` to
``"[Voltage to Voltage] Transfer"``
5. Change :ref:`gridcapacity` to reference the maximum outflow.
6. :ref:`expansion` capabilities are added to allow :ref:`gridcapacity`
to be expanded
7. Default :ref:`transformer_efficiency` is decreased to ``0.93`` to
discourage energy circulation between to grid busses to dissipate
surplus amounts.
8. Outflow costs of 10 cost units per power unit are added to all
``Transfer`` components, to further discourage energy circulation
between to grid busses to dissipate surplus amounts and to encourage
local production and consumption.
Return
------
es: :class:`tessif.model.energy_system.AbstractEnergySystem`
Tessif energy system.
"""
# 2. Create a simulation time frame as a :class:`pandas.DatetimeIndex`:
timeframe = pd.date_range('10/13/2030', periods=periods, freq='H')
# 3. Parse csv files with the demand and renewables load data:
d = os.path.join(example_dir, 'data', 'tsf', 'load_profiles')
# solar:
pv = pd.read_csv(os.path.join(d, 'Renewable_Energy.csv'),
index_col=0, sep=';')
pv = pv['pv_load'].values.flatten()[0:periods]
max_pv = np.max(pv)
# wind onshore:
w_on = pd.read_csv(os.path.join(
d, 'Renewable_Energy.csv'), index_col=0, sep=';')
w_on = w_on['won_load'].values.flatten()[0:periods]
max_w_on = np.max(w_on)
# wind offshore:
w_off = pd.read_csv(os.path.join(
d, 'Renewable_Energy.csv'), index_col=0, sep=';')
w_off = w_off['woff_load'].values.flatten()[0:periods]
max_w_off = np.max(w_off)
# solar thermal:
s_t = pd.read_csv(os.path.join(
d, 'Renewable_Energy.csv'), index_col=0, sep=';')
s_t = s_t['st_load'].values.flatten()[0:periods]
max_s_t = np.max(s_t)
# household demand
h_d = pd.read_csv(os.path.join(d, 'Loads.csv'), index_col=0, sep=';')
h_d = h_d['household_demand'].values.flatten()[0:periods]
max_h_d = np.max(h_d)
# industrial demand
i_d = pd.read_csv(os.path.join(d, 'Loads.csv'), index_col=0, sep=';')
i_d = i_d['industrial_demand'].values.flatten()[0:periods]
max_i_d = np.max(i_d)
# commercial demand
c_d = pd.read_csv(os.path.join(d, 'Loads.csv'), index_col=0, sep=';')
c_d = c_d['commercial_demand'].values.flatten()[0:periods]
max_c_d = np.max(c_d)
# district heating demand
dh_d = pd.read_csv(os.path.join(d, 'Loads.csv'), index_col=0, sep=';')
dh_d = dh_d['heat_demand'].values.flatten()[0:periods]
max_dh_d = np.max(dh_d)
# car charging demand
cc_d = pd.read_csv(os.path.join(d, 'Car_Charging.csv'),
index_col=0, sep=';')
cc_d = cc_d['cc_demand'].values.flatten()[0:periods]
max_cc_d = np.max(cc_d)
# 4. Create the individual energy system components:
global_constraints = {
'name': 'default',
'emissions': float('+inf'),
}
# -------------Low Voltage and heat ------------------
solar_panel = components.Source(
name='Solar Panel',
outputs=('low-voltage-electricity',),
# Minimum number of arguments required
latitude=42,
longitude=42,
region='Here',
sector='Power',
carrier='low-voltage-electricity',
node_type='Renewable',
flow_rates={'low-voltage-electricity': nts.MinMax(min=0, max=max_pv)},
flow_costs={'low-voltage-electricity': 60.85},
flow_emissions={'low-voltage-electricity': 0},
timeseries={'low-voltage-electricity': nts.MinMax(min=pv, max=pv)},
)
gas_supply = components.Source(
name='Gas Station',
outputs=('fuel',),
# Minimum number of arguments required
latitude=42,
longitude=42,
region='Here',
sector='Power',
carrier='Gas',
node_type='source',
flow_rates={'fuel': nts.MinMax(min=0, max=float('+inf'))},
flow_costs={'fuel': 0},
flow_emissions={'fuel': 0},
timeseries=None,
)
biogas_supply = components.Source(
name='Biogas plant',
outputs=('fuel',),
# Minimum number of arguments required
latitude=42,
longitude=42,
region='Here',
sector='Coupled',
carrier='Gas',
node_type='source',
flow_rates={'fuel': nts.MinMax(min=0, max=25987.87879)},
flow_costs={'fuel': 0},
flow_emissions={'fuel': 0},
timeseries=None,
)
bhkw_generator = components.Transformer(
name='BHKW',
inputs=('fuel',),
outputs=('low-voltage-electricity', 'heat'),
conversions={
('fuel', 'low-voltage-electricity'): 0.33,
('fuel', 'heat'): 0.52,
},
# Minimum number of arguments required',
sector='Coupled',
carrier='low-voltage-electricity',
node_type='transformer',
flow_rates={
'fuel': nts.MinMax(min=0, max=25987.87879),
'low-voltage-electricity': nts.MinMax(min=0, max=8576),
'heat': nts.MinMax(min=0, max=13513.69697),
},
flow_costs={'fuel': 0, 'low-voltage-electricity': 124.4, 'heat': 31.1},
flow_emissions={
'fuel': 0,
'low-voltage-electricity': 0.1573,
'heat': 0.0732,
},
)
household_demand = components.Sink(
name='Household Demand',
inputs=('low-voltage-electricity',),
# Minimum number of arguments required
sector='Power',
carrier='low-voltage-electricity',
node_type='demand',
flow_rates={'low-voltage-electricity': nts.MinMax(min=0, max=max_h_d)},
flow_costs={'low-voltage-electricity': 0},
flow_emissions={'low-voltage-electricity': 0},
timeseries={'low-voltage-electricity': nts.MinMax(min=h_d, max=h_d)},
)
commercial_demand = components.Sink(
name='Commercial Demand',
inputs=('low-voltage-electricity',),
# Minimum number of arguments required
sector='Power',
carrier='low-voltage-electricity',
node_type='demand',
flow_rates={'low-voltage-electricity': nts.MinMax(min=0, max=max_c_d)},
flow_costs={'low-voltage-electricity': 0},
flow_emissions={'low-voltage-electricity': 0},
timeseries={'low-voltage-electricity': nts.MinMax(min=c_d, max=c_d)},
)
heat_demand = components.Sink(
name='District Heating Demand',
inputs=('heat',),
# Minimum number of arguments required
latitude=42,
longitude=42,
region='Here',
sector='Heat',
carrier='hot Water',
node_type='demand',
flow_rates={'heat': nts.MinMax(min=0, max=max_dh_d)},
flow_costs={'heat': 0},
flow_emissions={'heat': 0},
timeseries={'heat': nts.MinMax(min=dh_d, max=dh_d)},
)
gas_supply_line = components.Bus(
name='Gaspipeline',
inputs=('Gas Station.fuel',),
outputs=('GuD.fuel',),
# Minimum number of arguments required
sector='Power',
carrier='gas',
node_type='bus',
)
biogas_supply_line = components.Bus(
name='Biogas',
inputs=('Biogas plant.fuel',),
outputs=('BHKW.fuel',),
# Minimum number of arguments required
sector='Coupled',
carrier='gas',
node_type='bus',
)
low_electricity_line = components.Bus(
name='Low Voltage Grid',
inputs=(
'BHKW.low-voltage-electricity',
'Deficit Source LV.low-voltage-electricity',
'Solar Panel.low-voltage-electricity',
'Medium Low Transfer.low-voltage-electricity',
),
outputs=(
'Household Demand.low-voltage-electricity',
'Commercial Demand.low-voltage-electricity',
'Low Medium Transfer.low-voltage-electricity',
'Excess Sink LV.low-voltage-electricity',
),
# Minimum number of arguments required
sector='Power',
carrier='electricity',
node_type='bus',
)
heat_line = components.Bus(
name='District Heating',
inputs=(
'BHKW.heat',
'Solar Thermal.heat',
'Power to Heat.heat',
'HKW.heat',
),
outputs=('District Heating Demand.heat',),
# Minimum number of arguments required
sector='Heat',
carrier='hot Water',
node_type='bus',
)
# ----- -------Medium Voltage and Heat ------------------
onshore_wind_power = components.Source(
name='Onshore Wind Power',
outputs=('medium-voltage-electricity',),
# Minimum number of arguments required
sector='power',
carrier='medium-voltage-electricity',
node_type='Renewable',
flow_rates={
'medium-voltage-electricity': nts.MinMax(min=0, max=max_w_on)},
flow_costs={'medium-voltage-electricity': 61.1},
flow_emissions={'medium-voltage-electricity': 0},
timeseries={
'medium-voltage-electricity': nts.MinMax(min=w_on, max=w_on)},
)
solar_thermal = components.Source(
name='Solar Thermal',
outputs=('heat',),
# Minimum number of arguments required
sector='Heat',
carrier='Hot Water',
node_type='Renewable',
flow_rates={'heat': nts.MinMax(min=0, max=max_s_t)},
flow_costs={'heat': 73},
flow_emissions={'heat': 0},
timeseries={'heat': nts.MinMax(min=s_t, max=s_t)},
)
industrial_demand = components.Sink(
name='Industrial Demand',
inputs=('medium-voltage-electricity',),
# Minimum number of arguments required
sector='Power',
carrier='medium-voltage-electricity',
node_type='demand',
flow_rates={
'medium-voltage-electricity': nts.MinMax(min=0, max=max_i_d)},
flow_costs={'medium-voltage-electricity': 0},
flow_emissions={'medium-voltage-electricity': 0},
timeseries={
'medium-voltage-electricity': nts.MinMax(min=i_d, max=i_d)},
)
car_charging_station_demand = components.Sink(
name='Car charging Station',
inputs=('medium-voltage-electricity',),
# Minimum number of arguments required
sector='Power',
carrier='medium-voltage-electricity',
node_type='demand',
flow_rates={
'medium-voltage-electricity': nts.MinMax(min=0, max=max_cc_d)},
flow_costs={'medium-voltage-electricity': 0},
flow_emissions={'medium-voltage-electricity': 0},
timeseries={
'medium-voltage-electricity': nts.MinMax(min=cc_d, max=cc_d)},
)
power_to_heat = components.Transformer(
name='Power to Heat',
inputs=('medium-voltage-electricity',),
outputs=('heat',),
conversions={('medium-voltage-electricity', 'heat'): 1.00},
# Minimum number of arguments required
carrier='Hot Water',
node_type='transformer',
flow_rates={
'medium-voltage-electricity': nts.MinMax(min=0, max=float('+inf')),
'heat': nts.MinMax(min=0, max=float('+inf'))},
flow_costs={'medium-voltage-electricity': 0, 'heat': 0},
flow_emissions={'medium-voltage-electricity': 0, 'heat': 0},
)
medium_electricity_line = components.Bus(
name='Medium Voltage Grid',
inputs=(
'Onshore Wind Power.medium-voltage-electricity',
'High Medium Transfer.medium-voltage-electricity',
'Low Medium Transfer.medium-voltage-electricity',
'Deficit Source MV.medium-voltage-electricity',
),
outputs=(
'Car charging Station.medium-voltage-electricity',
'Industrial Demand.medium-voltage-electricity',
'Power to Heat.medium-voltage-electricity',
'Medium High Transfer.medium-voltage-electricity',
'Medium Low Transfer.medium-voltage-electricity',
'Excess Sink MV.medium-voltage-electricity',
),
# Minimum number of arguments required
sector='Power',
carrier='electricity',
node_type='bus',
)
# ----------------- High Voltage -------------------------
offshore_wind_power = components.Source(
name='Offshore Wind Power',
outputs=('high-voltage-electricity',),
# Minimum number of arguments required
sector='Power',
carrier='high-voltage-electricity',
node_type='Renewable',
flow_rates={
'high-voltage-electricity': nts.MinMax(min=0, max=max_w_off)},
flow_costs={'high-voltage-electricity': 106.4},
flow_emissions={'high-voltage-electricity': 0},
timeseries={
'high-voltage-electricity': nts.MinMax(min=w_off, max=w_off)},
)
coal_supply = components.Source(
name='Coal Supply',
outputs=('fuel',),
# Minimum number of arguments required
sector='Coupled',
carrier='Coal',
node_type='source',
flow_rates={'fuel': nts.MinMax(min=0, max=102123.3)},
flow_costs={'fuel': 0},
flow_emissions={'fuel': 0},
)
hkw_generator = components.Transformer(
name='HKW',
inputs=('fuel',),
outputs=('high-voltage-electricity', 'heat'),
conversions={
('fuel', 'high-voltage-electricity'): 0.24,
('fuel', 'heat'): 0.6,
},
# Minimum number of arguments required
sector='Coupled',
carrier='high-voltage-electricity',
node_type='transformer',
flow_rates={
'fuel': nts.MinMax(min=0, max=102123.3),
'high-voltage-electricity': nts.MinMax(min=0, max=24509.6),
'heat': nts.MinMax(min=0, max=61273.96),
},
flow_costs={
'fuel': 0,
'high-voltage-electricity': 80.65,
'heat': 20.1625,
},
flow_emissions={
'fuel': 0,
'high-voltage-electricity': 0.5136,
'heat': 0.293,
},
)
hkw_generator_2 = components.Transformer(
name='HKW2',
inputs=('fuel',),
outputs=('high-voltage-electricity',),
conversions={('fuel', 'high-voltage-electricity'): 0.43},
# Minimum number of arguments required
sector='Coupled',
carrier='electricity',
node_type='connector',
flow_rates={
'fuel': nts.MinMax(min=0, max=102123.3),
'high-voltage-electricity': nts.MinMax(min=0, max=43913),
},
flow_costs={'fuel': 0, 'high-voltage-electricity': 80.65},
flow_emissions={'fuel': 0, 'high-voltage-electricity': 0.5136},
)
gud_generator = components.Transformer(
name='GuD',
inputs=('fuel',),
outputs=('high-voltage-electricity',),
conversions={('fuel', 'high-voltage-electricity'): 0.59},
# Minimum number of arguments required
sector='Power',
carrier='electricity',
node_type='generator',
flow_rates={
'fuel': nts.MinMax(min=0, max=45325.42373),
'high-voltage-electricity': nts.MinMax(min=0, max=26742),
},
flow_costs={'fuel': 0, 'high-voltage-electricity': 88.7},
flow_emissions={'fuel': 0, 'high-voltage-electricity': 0.3366},
)
coal_supply_line = components.Bus(
name='Coal Supply Line',
inputs=('Coal Supply.fuel',),
outputs=('HKW.fuel', 'HKW2.fuel'),
# Minimum number of arguments required
sector='Coupled',
carrier='Coal',
node_type='bus',
)
high_electricity_line = components.Bus(
name='High Voltage Grid',
inputs=(
'Offshore Wind Power.high-voltage-electricity',
'HKW2.high-voltage-electricity',
'GuD.high-voltage-electricity', 'HKW.high-voltage-electricity',
'Medium High Transfer.high-voltage-electricity',
'Deficit Source HV.high-voltage-electricity',
),
outputs=(
'Pumped Storage.high-voltage-electricity',
'High Medium Transfer.high-voltage-electricity',
'Excess Sink HV.high-voltage-electricity',
),
# Minimum number of arguments required
sector='Power',
carrier='electricity',
node_type='bus',
)
# Gridstructure and Transformer
low_medium_transformator = components.Transformer(
name='Low Medium Transfer',
inputs=('low-voltage-electricity',),
outputs=('medium-voltage-electricity',),
conversions={('low-voltage-electricity',
'medium-voltage-electricity'): transformer_efficiency},
# Minimum number of arguments required
latitude=42,
longitude=42,
region='Here',
sector='Power',
carrier='electricity',
node_type='connector',
flow_rates={
'low-voltage-electricity': nts.MinMax(
min=0,
max=float("+inf"),
),
'medium-voltage-electricity': nts.MinMax(
min=0,
max=gridcapacity,
),
},
flow_costs={
'low-voltage-electricity': 0,
'medium-voltage-electricity': 10,
},
flow_emissions={
'low-voltage-electricity': 0,
'medium-voltage-electricity': 0},
expandable={
'low-voltage-electricity': False,
'medium-voltage-electricity': expansion,
},
expansion_costs={
'low-voltage-electricity': 0,
'medium-voltage-electricity': 10,
},
expansion_limits={
'low-voltage-electricity': nts.MinMax(
min=gridcapacity,
max=float("+inf"),
),
'medium-voltage-electricity': nts.MinMax(
min=gridcapacity,
max=float("+inf"),
),
},
)
medium_low_transformator = components.Transformer(
name='Medium Low Transfer',
inputs=('medium-voltage-electricity',),
outputs=('low-voltage-electricity',),
conversions={('medium-voltage-electricity',
'low-voltage-electricity'): transformer_efficiency},
# Minimum number of arguments required
latitude=42,
longitude=42,
region='Here',
sector='Power',
carrier='electricity',
node_type='connector',
flow_rates={
'medium-voltage-electricity': nts.MinMax(
min=0,
max=float("+inf"),
),
'low-voltage-electricity': nts.MinMax(
min=0,
max=gridcapacity,
),
},
flow_costs={
'medium-voltage-electricity': 0,
'low-voltage-electricity': 10,
},
flow_emissions={
'low-voltage-electricity': 0,
'medium-voltage-electricity': 0,
},
expandable={
'medium-voltage-electricity': False,
'low-voltage-electricity': expansion,
},
expansion_costs={
'medium-voltage-electricity': 0,
'low-voltage-electricity': 10,
},
expansion_limits={
'medium-voltage-electricity': nts.MinMax(
min=gridcapacity,
max=float("+inf"),
),
'low-voltage-electricity': nts.MinMax(
min=gridcapacity,
max=float("+inf"),
),
},
)
medium_high_transformator = components.Transformer(
name='Medium High Transfer',
inputs=('medium-voltage-electricity',),
outputs=('high-voltage-electricity',),
conversions={
('medium-voltage-electricity',
'high-voltage-electricity'): transformer_efficiency},
# Minimum number of arguments required
sector='Power',
carrier='electricity',
node_type='connector',
flow_rates={
'medium-voltage-electricity': nts.MinMax(
min=0,
max=float("+inf"),
),
'high-voltage-electricity': nts.MinMax(
min=0,
max=gridcapacity,
),
},
flow_costs={
'medium-voltage-electricity': 0,
'high-voltage-electricity': 10,
},
flow_emissions={
'medium-voltage-electricity': 0,
'high-voltage-electricity': 0,
},
expandable={
'medium-voltage-electricity': False,
'high-voltage-electricity': expansion,
},
expansion_costs={
'medium-voltage-electricity': 0,
'high-voltage-electricity': 10,
},
expansion_limits={
'medium-voltage-electricity': nts.MinMax(
min=gridcapacity,
max=float("+inf"),
),
'high-voltage-electricity': nts.MinMax(
min=gridcapacity,
max=float("+inf"),
),
},
)
high_medium_transformator = components.Transformer(
name='High Medium Transfer',
inputs=('high-voltage-electricity',),
outputs=('medium-voltage-electricity',),
conversions={
('high-voltage-electricity',
'medium-voltage-electricity'): transformer_efficiency},
# Minimum number of arguments required
sector='Power',
carrier='electricity',
node_type='connector',
flow_rates={
'high-voltage-electricity': nts.MinMax(
min=0,
max=float("+inf"),
),
'medium-voltage-electricity': nts.MinMax(
min=0,
max=gridcapacity,
),
},
flow_costs={
'high-voltage-electricity': 0,
'medium-voltage-electricity': 10,
},
flow_emissions={
'high-voltage-electricity': 0,
'medium-voltage-electricity': 0,
},
expandable={
'high-voltage-electricity': False,
'medium-voltage-electricity': expansion,
},
expansion_costs={
'high-voltage-electricity': 0,
'medium-voltage-electricity': 10,
},
expansion_limits={
'high-voltage-electricity': nts.MinMax(
min=gridcapacity,
max=float("+inf"),
),
'medium-voltage-electricity': nts.MinMax(
min=gridcapacity,
max=float("+inf"),
),
},
)
# ---------- Deficit Sources ---------------
power_source_lv = components.Source(
name='Deficit Source LV',
outputs=('low-voltage-electricity',),
# Minimum number of arguments required
sector='Power',
carrier='electricity',
node_type='source',
flow_rates={
'low-voltage-electricity': nts.MinMax(min=0, max=float('+inf'))},
flow_costs={'low-voltage-electricity': 300},
flow_emissions={'low-voltage-electricity': 0.6},
)
power_source_mv = components.Source(
name='Deficit Source MV',
outputs=('medium-voltage-electricity',),
# Minimum number of arguments required
sector='Power',
carrier='electricity',
node_type='source',
flow_rates={
'medium-voltage-electricity': nts.MinMax(min=0, max=float('+inf')),
},
flow_costs={'medium-voltage-electricity': 300},
flow_emissions={'medium-voltage-electricity': 0.6},
)
power_source_hv = components.Source(
name='Deficit Source HV',
outputs=('high-voltage-electricity',),
# Minimum number of arguments required
sector='Power',
carrier='electricity',
node_type='source',
flow_rates={
'high-voltage-electricity': nts.MinMax(min=0, max=float('+inf'))},
flow_costs={'high-voltage-electricity': 300},
flow_emissions={'high-voltage-electricity': 0.6},
)
power_sink_lv = components.Sink(
name='Excess Sink LV',
inputs=('low-voltage-electricity',),
# Minimum number of arguments required
sector='Power',
carrier='electricity',
node_type='sink',
flow_rates={
'low-voltage-electricity': nts.MinMax(min=0, max=float('+inf'))},
flow_costs={'low-voltage-electricity': 300},
flow_emissions={'low-voltage-electricity': 0.6},
)
# ------------------- Excess Sinks --------------------------
power_sink_mv = components.Sink(
name='Excess Sink MV',
inputs=('medium-voltage-electricity',),
# Minimum number of arguments required
sector='Power',
carrier='electricity',
node_type='sink',
flow_rates={
'medium-voltage-electricity': nts.MinMax(min=0, max=float('+inf')),
},
flow_costs={'medium-voltage-electricity': 300},
flow_emissions={'medium-voltage-electricity': 0.6},
)
power_sink_hv = components.Sink(
name='Excess Sink HV',
inputs=('high-voltage-electricity',),
# Minimum number of arguments required
sector='Power',
carrier='electricity',
node_type='sink',
flow_rates={
'high-voltage-electricity': nts.MinMax(min=0, max=float('+inf'))},
flow_costs={'high-voltage-electricity': 300},
flow_emissions={'high-voltage-electricity': 0.6},
)
# 4. Create the actual energy system:
if expansion:
uid = "TransE"
else:
uid = "TransC"
es = energy_system.AbstractEnergySystem(
uid=uid,
busses=(
gas_supply_line,
low_electricity_line,
heat_line,
medium_electricity_line,
high_electricity_line,
coal_supply_line,
biogas_supply_line,
),
sinks=(
household_demand,
commercial_demand,
heat_demand,
industrial_demand,
car_charging_station_demand,
power_sink_lv,
power_sink_mv,
power_sink_hv,
),
sources=(
solar_panel,
offshore_wind_power,
onshore_wind_power,
gas_supply,
coal_supply,
solar_thermal,
biogas_supply,
power_source_lv,
power_source_mv,
power_source_hv,
),
transformers=(
bhkw_generator,
power_to_heat,
gud_generator,
hkw_generator,
high_medium_transformator,
low_medium_transformator,
medium_low_transformator,
medium_high_transformator,
hkw_generator_2,
),
timeframe=timeframe,
global_constraints=global_constraints,
)
return es
Generic Graph
import os
from tessif import parse
import tessif.visualize.dcgrph as dcv
from tessif.frused.paths import doc_dir
creation_module_path = os.path.join(
doc_dir,
"source",
"getting_started",
"examples",
"application",
"phd",
"model_scenario_combinations",
"TransCnE",
"creation.py",
)
creation_module = parse.python_file(creation_module_path)
transcne_es = creation_module.create_transcne_es()
app = dcv.draw_generic_graph(
energy_system=transcne_es,
color_group={
'Coal Supply': '#666666',
'Coal Supply Line': '#666666',
'HKW': '#666666',
'HKW2': '#666666',
'Solar Thermal': '#b30000',
'Heat Storage': '#cc0033',
'District Heating': 'Red',
'District Heating Demand': 'Red',
'Power to Heat': '#b30000',
'Biogas plant': '#006600',
'Biogas': '#006600',
'BHKW': '#006600',
'Onshore Wind Power': '#99ccff',
'Offshore Wind Power': '#00ccff',
'Gas Station': '#336666',
'Gaspipeline': '#336666',
'GuD': '#336666',
'Solar Panel': '#ffe34d',
'Commercial Demand': '#ffe34d',
'Household Demand': '#ffe34d',
'Industrial Demand': '#ffe34d',
'Car charging Station': '#669999',
'Low Voltage Grid': '#ffcc00',
'Medium Voltage Grid': '#ffcc00',
'High Voltage Grid': '#ffcc00',
'Low Medium Transfer': '#ff9900',
'Medium Low Transfer': '#ff9900',
'High Medium Transfer': '#ff9900',
'Medium High Transfer': '#ff9900',
"Excess Sink HV": "yellow",
"Excess Sink MV": "yellow",
"Excess Sink LV": "yellow",
"Deficit Source HV": "yellow",
"Deficit Source MV": "yellow",
"Deficit Source LV": "yellow",
},
node_size={
'High Voltage Grid': 150,
'Medium Voltage Grid': 150,
'Low Voltage Grid': 150,
'District Heating': 150
},
title='Lossless Power Grid Example Energy System Graph',
)
app.run_server(debug=False)
Optimization Results
Shown below, is the code to generate the TransC and TransE results. Use the
PERIODS constant to adjust the simulated time span. And the Expansion
constant to switch between TransC and TransE.
Results displayed were created using PERIODS = 24.
Optimize and Post Process via Tessif
Following script uses the TransC/E creation skript above to generate and tessif internally post-process the optimization results, ready for further analysis.
import pandas as pd
import json
import importlib
import os
from pathlib import Path
from tessif.analyze import ComparativeResultier
from tessif import parse
from tessif.frused.paths import doc_dir
import tessif.examples.data.tsf.py_hard as tsf_examples
import tessif.simulate as optimize
from tessif.transform.es2mapping import compile_result_data_representation
import tessif.visualize.dcgrph as dcv
PERIODS = 24
# GRID_CAPACITY = 60000 # no congestion
# GRID_CAPACITY = 20000 # congestion
GRID_CAPACITY = 1 # Expansion
# EXPANSION = False
EXPANSION = True
FOLDER = "commitment_nocongestion_results"
# FOLDER = "commitment_congestion_results"
FOLDER = "expansion_results"
# define the softwares to be used
SOFTWARES = ['cllp', 'fine', 'omf', 'ppsa', ]
# use this in case you are just testing out the water
# SOFTWARES = ['omf', ]
CYTOSCAPE_ADVANCED_GRAPH = False
MATPLOTLIB_ADVANCED_GRAPH = False
ADVANCED_GRAPH_ON = "omf"
TRANS_OPS = {
"ppsa": {
"forced_links": (
'Low Medium Transfer',
'Medium Low Transfer',
'High Medium Transfer',
'Medium High Transfer',
),
"excess_sinks": (
"Excess Sink HV",
"Excess Sink MV",
"Excess Sink LV",
),
}
}
PARENT = os.path.join(
doc_dir,
"source",
"getting_started",
"examples",
"application",
"phd",
"field_study",
"TransCnE",
)
# create dispatch problem aka TransC or TransE combination
creation_module_path = os.path.join(PARENT, "creation.py")
creation_module = parse.python_file(creation_module_path)
tessif_TransCnE = creation_module.create_transcne_es(
periods=PERIODS, expansion=EXPANSION, gridcapacity=GRID_CAPACITY)
# dynamically access the tessif transform utilities based on requested
# softwares above. Store them in a dictionairy for
# ease of access.
transformers = {}
for software in SOFTWARES:
transformers[software] = importlib.import_module(
'.'.join(['tessif.transform.es2es', software]))
# Do the tessif -> software transformations and store them in a dictionairy for
# ease of access
transformed_TransCnE_combinations = {}
for software in SOFTWARES:
if software in TRANS_OPS:
transops = TRANS_OPS[software]
else:
transops = {}
transformed_TransCnE_combinations[software] = transformers[software].transform(
tessif_TransCnE, **transops)
# Perform the software specific optimizations
optimized_TransCnE_combinations = {}
for software in SOFTWARES:
optimizer = getattr(optimize, "_".join([software, "from_es"]))
optimized_TransCnE_combinations[software] = optimizer(
transformed_TransCnE_combinations[software])
# post process the allresultiers:
all_resultiers = {}
for software in SOFTWARES:
post_processor = importlib.import_module(
'.'.join(['tessif.transform.es2mapping', software]))
all_resultiers[software] = post_processor.AllResultier(
optimized_TransCnE_combinations[software])
# post process the comparative results using the constructed all-resultiers:
comparatier = ComparativeResultier(all_resultiers)
data_storage_path = os.path.join(PARENT, FOLDER)
# store the all_loads results
for software in SOFTWARES:
result_id = f"{software}_all_loads"
storage_location = os.path.join(data_storage_path, result_id)
result_df = comparatier.all_loads[software]
result_df.to_csv(".".join([storage_location, "csv"]))
# store the all_socs results
result_id = f"all_socs"
storage_location = os.path.join(data_storage_path, result_id)
result_df = comparatier.all_socs
if not result_df.empty:
result_df.to_csv(".".join([storage_location, "csv"]))
# store the rest of the all_* results:
for rtype in [
"all_capacities",
"all_original_capacities",
"all_net_energy_flows",
"all_costs_incurred",
"all_emissions_caused",
]:
result_id = rtype
storage_location = os.path.join(data_storage_path, result_id)
result_df = getattr(comparatier, rtype)
result_df.to_csv(".".join([storage_location, "csv"]))
result_types = ['Load', 'Capacity', 'IntegratedGlobal', ]
post_processed_data = {}
for software in SOFTWARES:
post_processor = importlib.import_module(
'.'.join(['tessif.transform.es2mapping', software]))
post_processed_data[software] = {}
for result_type in result_types:
post_processed_data[software][result_type] = getattr(
post_processor, "".join([result_type, "Resultier"]))(
optimized_TransCnE_combinations[software])
wanted_results = {
'Load': 'node_load',
'Capacity': 'node_installed_capacity',
'IntegratedGlobal': 'global_results',
}
nodes_of_interest = {
'Load': [
'High Voltage Grid',
"Medium Voltage Grid",
"Low Voltage Grid",
'District Heating',
],
'Capacity': [],
'IntegratedGlobal': [],
}
for software in SOFTWARES:
for rtype in result_types:
if nodes_of_interest[rtype]:
for node in nodes_of_interest[rtype]:
result_id = f"{software}_{rtype}_{node}"
storage_location = os.path.join(data_storage_path, result_id)
res = getattr(post_processed_data[software][rtype], wanted_results[rtype])[
node]
if rtype == 'Load':
# store timeseries results
res.name = f"{software}_timeseries_{rtype}_{node}"
timeseries_storage_location = storage_location.replace(
rtype, "timeseries_"+rtype)
res.to_json(
".".join([timeseries_storage_location, "json"]),
orient="split",
)
# store regular loads as summed loads
res = res.sum()
res.name = result_id
res.to_json(
".".join([storage_location, "json"]), orient="split")
else:
result_id = f"{software}_{rtype}"
storage_location = os.path.join(data_storage_path, result_id)
res = getattr(
post_processed_data[software][rtype], wanted_results[rtype])
res = pd.Series(res.values(), index=res.keys())
res.name = result_id
res.to_json(
".".join([storage_location, "json"]), orient="split")
# draw advanced graphs
advanced_graphs = {}
if CYTOSCAPE_ADVANCED_GRAPH:
if ADVANCED_GRAPH_ON in SOFTWARES:
app = dcv.draw_advanced_graph(
optimized_es=optimized_TransCnE_combinations[ADVANCED_GRAPH_ON],
# layout='style',
# layout_nodeDimensionsIncludeLabels=True,
node_shape="circle",
node_color={
'Coal Supply': '#666666',
'Coal Supply Line': '#666666',
'HKW': '#666666',
'HKW2': '#666666',
'Solar Thermal': '#b30000',
'Heat Storage': '#cc0033',
'District Heating': 'Red',
'District Heating Demand': 'Red',
'Power to Heat': '#b30000',
'Biogas plant': '#006600',
'Biogas': '#006600',
'BHKW': '#006600',
'Onshore Wind Power': '#99ccff',
'Offshore Wind Power': '#00ccff',
'Gas Station': '#336666',
'Gaspipeline': '#336666',
'GuD': '#336666',
'Solar Panel': '#ffe34d',
'Commercial Demand': '#ffe34d',
'Household Demand': '#ffe34d',
'Industrial Demand': '#ffe34d',
'Car charging Station': '#669999',
'Low Voltage Grid': '#ffcc00',
'Medium Voltage Grid': '#ffcc00',
'High Voltage Grid': '#ffcc00',
'Low Medium Transfer': '#ff9900',
'Medium Low Transfer': '#ff9900',
'High Medium Transfer': '#ff9900',
'Medium High Transfer': '#ff9900',
"Excess Sink HV": "yellow",
"Excess Sink MV": "yellow",
"Excess Sink LV": "yellow",
"Deficit Source HV": "yellow",
"Deficit Source MV": "yellow",
"Deficit Source LV": "yellow",
}
)
app.run_server()
if MATPLOTLIB_ADVANCED_GRAPH:
if ADVANCED_GRAPH_ON in SOFTWARES:
from tessif.transform.es2mapping import omf as tomf
from tessif.transform import nxgrph as nxt
import matplotlib.pyplot as plt
import tessif.visualize.nxgrph as nxv
es = optimized_TransCnE_combinations[software]
formatier = tomf.AllFormatier(es, cgrp='all')
grph = nxt.Graph(tomf.FlowResultier(es))
for key, value in formatier.edge_data()['edge_width'].items():
formatier.edge_data()['edge_width'][key] = 4 * value
nxv.draw_graphical_representation(
formatier=formatier, colored_by='sector')
figure = plt.gcf()
figure.show()
# Use this in case you want to see the result representation of certain nodes
# rdrs = {}
# for software in SOFTWARES:
# rdrs[software] = compile_result_data_representation(
# optimized_es=optimized_TransCnE_combinations[software],
# software=software,
# node="Heatline",
# )
# # print(rdrs['fine'])
Aggregate, Compare, and Store Representative Results
Following script takes the stored results from the optimization process above and turns them into representative csv files shown in the results section.
import pandas as pd
from numpy import int_
import os
from collections import defaultdict
import numbers
from tessif.frused.paths import doc_dir
FOLDER = "commitment_nocongestion_results"
# FOLDER = "commitment_congestion_results"
# FOLDER = "expansion_results"
# locate the storage directory
cp = os.path.join(
doc_dir, "source", "getting_started", "examples", "application",
"phd", "model_scenario_combinations", "TransCnE", FOLDER
)
softwares = ['cllp', 'fine', 'omf', 'ppsa', ]
# use this in case you are just testing out the water
# softwares = ['omf', ]
load_nodes = [
'High Voltage Grid',
"Medium Voltage Grid",
"Low Voltage Grid",
'District Heating',
# "Low Medium Transfer",
# "High Medium Transfer",
# "Medium High Transfer",
# "Medium Low Transfer",
# "Excess Sink HV",
# "Excess Sink MV",
# "Excess Sink LV",
# "Deficit Source HV",
# "Deficit Source MV",
# "Deficit Source LV",
]
# Result dicts for convenience access
globalesque_results = {}
load_results = {}
timeseries_results = {}
for software in softwares:
load_results[software] = {}
timeseries_results[software] = {}
for node in load_nodes:
load_result_file = f"{software}_Load_{node}"
timeseries_load_result_file = f"{software}_timeseries_Load_{node}"
storage_path = os.path.join(cp, ".".join([load_result_file, "json"]))
ser = pd.read_json(
storage_path, orient="split", typ="series")
ser.name = load_result_file
load_results[software][node] = ser
timeseries_storage_path = os.path.join(cp, ".".join(
[timeseries_load_result_file, "json"]))
df = pd.read_json(
timeseries_storage_path, orient="split", typ="frame")
df.name = timeseries_load_result_file
timeseries_results[software][node] = df
globalesque_results[software] = {}
for rtype in ["Capacity", "IntegratedGlobal"]:
result_file = f"{software}_{rtype}"
storage_path = os.path.join(cp, ".".join([result_file, "json"]))
ser = pd.read_json(
storage_path, orient="split", typ="series")
ser.name = result_file
globalesque_results[software][rtype] = ser
# flatten capacity and igr results for better readability:
capacity_results = {}
integrated_global_results = {}
for software in softwares:
capacity_results[software] = globalesque_results[software]["Capacity"]
integrated_global_results[software] = globalesque_results[software]["IntegratedGlobal"]
# aggregate software results into one dataframe for ease of comparison
load_by_node = defaultdict(dict)
timeseries_by_node = defaultdict(dict)
for software in softwares:
for node in load_nodes:
load_by_node[node][software] = load_results[software][node]
timeseries_by_node[node][software] = timeseries_results[software][node]
labels = [
"Capacity",
"IGR",
"Load-High Voltage Grid",
"Load-Medium Voltage Grid",
"Load-Low Voltage Grid",
"Load-District Heating",
"Timeseries-High Voltage Grid",
"Timeseries-Medium Voltage Grid",
"Timeseries-Low Voltage Grid",
"Timeseries-District Heating",
"Load-Low Medium Transfer",
"Load-High Medium Transfer",
"Load-Medium High Transfer",
"Load-Medium Low Transfer",
"Timeseries-Low Medium Transfer",
"Timeseries-High Medium Transfer",
"Timeseries-Medium High Transfer",
"Timeseries-Medium Low Transfer",
"Timeseries-Excess Sink HV",
"Timeseries-Excess Sink MV",
"Timeseries-Excess Sink LV",
"Timeseries-Deficit Source HV",
"Timeseries-Deficit Source MV",
"Timeseries-Deficit Source LV",
]
dimensions = [
"MW or MWh",
"€ or t_CO2",
"MW",
"MW",
"MW",
"MW",
"MW",
"MW",
"MW",
"MW",
"MW",
"MW",
"MW",
"MW",
"MW",
"MW",
"MW",
"MW",
"MW",
"MW",
"MW",
"MW",
"MW",
"MW",
]
for pos, dct in enumerate([
capacity_results,
integrated_global_results,
load_by_node["High Voltage Grid"],
load_by_node["Medium Voltage Grid"],
load_by_node["Low Voltage Grid"],
load_by_node["District Heating"],
timeseries_by_node["High Voltage Grid"],
timeseries_by_node["Medium Voltage Grid"],
timeseries_by_node["Low Voltage Grid"],
timeseries_by_node["District Heating"],
# load_by_node["Low Medium Transfer"],
# load_by_node["High Medium Transfer"],
# load_by_node["Medium High Transfer"],
# load_by_node["Medium Low Transfer"],
# timeseries_by_node["High Voltage Grid"],
# timeseries_by_node["Medium Voltage Grid"],
# timeseries_by_node["Low Voltage Grid"],
# timeseries_by_node["District Heating"],
# timeseries_by_node["Low Medium Transfer"],
# timeseries_by_node["High Medium Transfer"],
# timeseries_by_node["Medium High Transfer"],
# timeseries_by_node["Medium Low Transfer"],
# timeseries_by_node["Excess Sink HV"],
# timeseries_by_node["Excess Sink MV"],
# timeseries_by_node["Excess Sink LV"],
# timeseries_by_node["Deficit Source HV"],
# timeseries_by_node["Deficit Source MV"],
# timeseries_by_node["Deficit Source LV"],
]):
df = pd.concat(
dct.values(),
keys=dct.keys(),
axis='columns')
# drop all zero columns
# df = df.loc[:, (df != 0).any(axis=0)]
# give dataframe a name for higher recognition value
df.index.name = labels[pos] + f" [{dimensions[pos]}]"
# sort dataframe for index
df = df.sort_index()
# change None to "variable" since that is what it means
df = df.fillna("variable")
# cast values into integers, since they are deemed to provide enough
# information
df = df.applymap(
lambda x: int_(x) if isinstance(x, numbers.Number) else x,
# na_action="ignore",
)
# dynamically create the filename using the df.index name
storage_path = os.path.join(cp, ".".join([labels[pos], "csv"]))
# store the result dfs as csv for convenient access
df.to_csv(storage_path)
IGR Bar Plot Code
The TransC/E “Integrated Global Result” bar plots are created using following code:
import pandas as pd
from pathlib import Path
from tessif.frused.paths import doc_dir
from tessif.visualize import igr
mpl_config = {
"title": "Integrated Global Results Relative to 'Oemof'",
"ylabel": "",
"xlabel": "Result Values",
"rot": 0,
}
for result in [
"commitment_nocongestion_results",
"commitment_congestion_results",
"expansion_results",
]:
igr_results_csv_path = (
Path(doc_dir)
/ "source"
/ "getting_started"
/ "examples"
/ "application"
/ "phd"
/ "model_scenario_combinations"
/ "TransCnE"
/ result
/ "IGR.csv"
)
result_df = pd.read_csv(igr_results_csv_path, index_col=0)
# make results relative to oemof
result_df = result_df.div(result_df['omf'], axis='index')
# mpl_config["ylim"] = [
# math.floor(10*result_df.min().min())/10,
# math.ceil(10*result_df.max().max())/10,
# ]
handle = igr.plot(
igr_df=result_df,
plt_config=mpl_config,
ptype="bar",
)
for category in ["costs", "non_costs"]:
figure_storage_path = igr_results_csv_path.parents[0] / "_".join(
[category, "IGR.png"])
handle[category].savefig(figure_storage_path)
# handle["costs"].show()
# handle["non_costs"].show()
plexp_config = {
"title": "Integrated Global Results Relative to 'Oemof (omf)'",
"barmode": "group",
"text_auto": True,
}
handle = igr.plot(
igr_df=result_df,
plt_config=plexp_config,
ptype="bar",
draw_util="plexp",
)
# handle["costs"].show()
# handle["non_costs"].show()
Installed Transfer Grid Capacity
The TransC/E “Installed Transfer Grid Capacities” bar plots are created using following code:
import os
from tessif.frused.paths import doc_dir
import pandas as pd
import plotly.express as px
PARENT = "TransCnE"
FOLDERS = [
"commitment_nocongestion_results",
"commitment_congestion_results",
"expansion_results",
]
FILE = "Capacity.csv"
NODES = [
"High Medium Transfer",
"Medium High Transfer",
"Medium Low Transfer",
"Low Medium Transfer",
]
PLOTLY = False
MATPLOTLIB = True
# locate the storage directory
parent_folder = os.path.join(
doc_dir,
"source",
"getting_started",
"examples",
"application",
"phd",
"model_scenario_combinations",
PARENT,
)
# cp = os.path.join(parent_folder, FOLDER)
for folder in FOLDERS:
capacities_path = os.path.join(parent_folder, folder, FILE)
df = pd.read_csv(capacities_path, index_col=0)
# only keep NODES data:
df = df.loc[NODES]
# rename df cols and rows for increased verbosity
df.columns.name = "Softwares"
df.index.name = "Nodes"
# original df holds "variable" strings so data needs to be parsed to numeric
df = df.apply(pd.to_numeric, errors='coerce')
# store the df as csv for reference
write_path = os.path.join(parent_folder, folder, "Transfer_Capacities.csv")
df.to_csv(write_path)
if PLOTLY:
figure = px.bar(
df,
barmode="group",
text_auto=True,
title="Installed Capacities in MWh",
template="simple_white",
)
figure.show()
if MATPLOTLIB:
ax = df.plot(
kind="bar",
title="Installed Capacities",
xlabel="Transfer Grid Nodes",
ylabel="Installed Capacities in MWh",
rot=45,
)
ax.grid(axis='y')
figure = ax.figure
figure.show()
Summed Load Bar Plots
The TransC/E summed load bar plots are created using following code:
import plotly.express as px
import os
from collections import defaultdict
import matplotlib.pyplot as plt
import pandas as pd
from tessif.frused.paths import doc_dir
PARENT = "TransCnE"
FOLDER = "commitment_nocongestion_results"
FOLDER = "commitment_congestion_results"
FOLDER = "expansion_results"
SOFTWARES = ['cllp', 'fine', 'omf', 'ppsa', ]
NODES = [
"Medium Voltage Grid",
# "Low Medium Transfer",
# "High Medium Transfer",
# "Medium High Transfer",
# "Medium Low Transfer",
]
# locate the storage directory
parent_folder = os.path.join(
doc_dir,
"source",
"getting_started",
"examples",
"application",
"phd",
"model_scenario_combinations",
PARENT,
)
cp = os.path.join(parent_folder, FOLDER)
for node in NODES:
summed_loads_file = f"Load-{node}.csv"
summed_loads_path = os.path.join(parent_folder, FOLDER, summed_loads_file)
df = pd.read_csv(summed_loads_path, index_col=0)
# drop all zero rows
df = df.loc[(df != 0).any(axis=1)]
df.columns.name = "softwares"
# print(node)
print(df)
if not df.empty:
figure = px.bar(
df,
barmode="group",
text_auto=True,
title=f"{node} Summed Loads",
template="simple_white",
)
ax = df.plot(
kind="bar",
title=f"{node} Summed Loads",
xlabel="Connecting Nodes",
ylabel="Summed Loads in MW",
rot=45,
)
ax.grid(axis='y')
figure = ax.figure
figure.show()
Load Probile Plots
The TransC/E load profile plots are created using following code:
import os
from collections import defaultdict
import matplotlib.pyplot as plt
import pandas as pd
from tessif.frused.paths import doc_dir
from tessif.visualize import component_loads
PARENT = "TransCnE"
FOLDER = "commitment_nocongestion_results"
# FOLDER = "commitment_congestion_results"
# FOLDER = "expansion_results"
SOFTWARES = [
'cllp',
# 'fine',
'omf',
# 'ppsa',
]
NODES = [
"Medium Voltage Grid",
# "Low Medium Transfer",
# "High Medium Transfer",
# "Medium High Transfer",
# "Medium Low Transfer",
]
# locate the storage directory
parent_folder = os.path.join(
doc_dir,
"source",
"getting_started",
"examples",
"application",
"phd",
"model_scenario_combinations",
PARENT,
)
cp = os.path.join(parent_folder, FOLDER)
load_results = {}
for software in SOFTWARES:
load_results[software] = {}
for node in NODES:
load_result_file = f"{software}_timeseries_Load_{node}"
storage_path = os.path.join(cp, ".".join([load_result_file, "json"]))
df = pd.read_json(
storage_path, orient="split", typ="frame")
df.name = load_result_file
load_results[software][node] = df
# store the df as csv for reference
df.columns.name = "Softwares"
write_path = os.path.join(cp, ".".join([load_result_file, "csv"]))
df.to_csv(write_path)
# aggregate software results into one dataframe for ease of comparison
load_by_node = defaultdict(dict)
for software in SOFTWARES:
for node in NODES:
load_by_node[node][software] = load_results[software][node]
for software in SOFTWARES:
for node in NODES:
df = load_by_node[node][software]
# drop all zero columns
df = df.loc[:, (df != 0).any(axis=0)]
figure = component_loads.bar_lines(
df,
component_type='bus',
)
title = f"{node} Load Profile Results of Software: '{software}'"
figure.axes[0].set_title(title)
figure.show()
Redispatch
The redispatch calculations are done using following code:
import os
from collections import defaultdict
from tessif.frused.paths import doc_dir
import pandas as pd
PARENT = "TransCnE"
FOLDER = "commitment_nocongestion_results"
# FOLDER = "commitment_congestion_results"
# FOLDER = "expansion_results"
SOFTWARES = [
# 'cllp',
# 'fine',
'omf',
# 'ppsa',
]
NODES = [
"High Voltage Grid",
"Medium Voltage Grid",
"Low Voltage Grid",
]
# locate the storage directory
parent_folder = os.path.join(
doc_dir,
"source",
"getting_started",
"examples",
"application",
"phd",
"model_scenario_combinations",
PARENT,
)
cp = os.path.join(parent_folder, FOLDER)
load_results = {}
for node in NODES:
summed_loads_file = f"Timeseries-{node}.csv"
summed_loads_path = os.path.join(parent_folder, FOLDER, summed_loads_file)
df = pd.read_csv(summed_loads_path, index_col=0, header=[0, 1])
df.columns.name = "softwares"
load_results[node] = df
# prepare the redispatch calculations
redispatches = {}
def calc_redispatch(loads_from, loads_to, uid_XSsink, uid_DEFsource):
"""Calc redispatch between from bus and to bus."""
# select all indices where surplus sink gets used
congestion_occasions = loads_from[loads_from[uid_XSsink] > 0].index
# access all lack bus loads, of prior selected indices
pot_redispatch = loads_to.loc[congestion_occasions][uid_DEFsource]
# redispatch = the amount of suprlus energy provided on timesteps where
# excess energy was dumped
redispatch = pot_redispatch[pot_redispatch.abs() > 0].abs()
return redispatch
for software in SOFTWARES:
redispatches["High2Medium"] = calc_redispatch(
loads_from=load_results["High Voltage Grid"][software],
loads_to=load_results["Medium Voltage Grid"][software],
uid_XSsink="Excess Sink HV",
uid_DEFsource="Deficit Source MV",
)
redispatches["Medium2High"] = calc_redispatch(
loads_from=load_results["Medium Voltage Grid"][software],
loads_to=load_results["High Voltage Grid"][software],
uid_XSsink="Excess Sink MV",
uid_DEFsource="Deficit Source HV",
)
redispatches["Low2Medium"] = calc_redispatch(
loads_from=load_results["Low Voltage Grid"][software],
loads_to=load_results["Medium Voltage Grid"][software],
uid_XSsink="Excess Sink LV",
uid_DEFsource="Deficit Source MV",
)
redispatches["Medium2Low"] = calc_redispatch(
loads_from=load_results["Medium Voltage Grid"][software],
loads_to=load_results["Low Voltage Grid"][software],
uid_XSsink="Excess Sink MV",
uid_DEFsource="Deficit Source LV",
)
for direction, redispatch in redispatches.items():
filename = f"Redispatch_{direction}.csv"
location = os.path.join(parent_folder, FOLDER, filename)
redispatch.name = f"Redispatch {direction}"
redispatch.to_csv(location)
print(direction)
print(redispatch)
Circulation
The energy circulation calculations are done using following code:
import os
from collections import defaultdict
from tessif.frused.paths import doc_dir
import pandas as pd
PARENT = "TransCnE"
FOLDER = "commitment_nocongestion_results"
# FOLDER = "commitment_congestion_results"
# FOLDER = "expansion_results"
SOFTWARES = [
# 'cllp',
# 'fine',
'omf',
# 'ppsa',
]
NODES = [
"High Voltage Grid",
"Medium Voltage Grid",
"Low Voltage Grid",
]
# locate the storage directory
parent_folder = os.path.join(
doc_dir,
"source",
"getting_started",
"examples",
"application",
"phd",
"model_scenario_combinations",
PARENT,
)
cp = os.path.join(parent_folder, FOLDER)
load_results = {}
for node in NODES:
summed_loads_file = f"Timeseries-{node}.csv"
summed_loads_path = os.path.join(parent_folder, FOLDER, summed_loads_file)
df = pd.read_csv(summed_loads_path, index_col=0, header=[0, 1])
df.columns.name = "softwares"
load_results[node] = df
def calc_circulated_energy(
tansfer_grid_connected_bus_loads,
transfer_grid_transformer_uids
):
"""Calc redispatch between from bus and to bus."""
loads1 = tansfer_grid_connected_bus_loads
grid1, grid2 = transfer_grid_transformer_uids
# select all indices where grid 1 transfers energy from or to bus
pot_circulate_occasions = loads1[
loads1[grid1] != 0.0].index
# abbrevate for brevity
cos = pot_circulate_occasions
# energy gets circulated when grid 1 transfers energy from or to the bus,
# while grid 2 also does:
circulate_occasions = loads1.loc[cos][loads1.loc[cos][grid2] != 0].index
# abbrevate again for brevity
cos = circulate_occasions
# calculate the absolute difference of the identified energy flows
amount_circulated = loads1.loc[
cos][grid1].abs() - loads1.loc[cos][grid2].abs()
# since we do not know which direction the energy circulates, we are
# content for now with just knowing the amount
amount_circulated = amount_circulated.abs()
return amount_circulated
# prepare the circulation calculations
circulations = {}
for software in SOFTWARES:
circulations["Medium and High"] = calc_circulated_energy(
tansfer_grid_connected_bus_loads=load_results["Medium Voltage Grid"][software],
transfer_grid_transformer_uids=(
"Medium High Transfer", "High Medium Transfer")
)
circulations["Medium and Low"] = calc_circulated_energy(
tansfer_grid_connected_bus_loads=load_results["Medium Voltage Grid"][software],
transfer_grid_transformer_uids=(
"Medium Low Transfer", "Low Medium Transfer")
)
for direction, circulation in circulations.items():
filename = f"Circulation {direction}.csv"
location = os.path.join(parent_folder, FOLDER, filename)
circulation.name = f"Circulation {direction}"
circulation.to_csv(location)
print(direction)
print(circulation)
Computational Results
Computational Ressources Estimation Code
The computational ressources bar plots are created using following code:
# change spellings_logging_level to debug to declutter output
import pandas as pd
import tessif.examples.data.tsf.py_hard as tsf_examples
import os
from tessif.frused.paths import doc_dir, write_dir
import tessif.analyze
from tessif import parse
import tessif.frused.configurations as configurations
configurations.spellings_logging_level = 'debug'
SOFTWARES = ('cllp', 'fine', 'omf', 'ppsa', )
# SOFTWARES = ('ppsa',)
PERIODS = 24
GRID_CAPACITY = 60000 # no congestion
GRID_CAPACITY = 20000 # congestion
# GRID_CAPACITY = 1 # Expansion
EXPANSION = False
PARENT = "TransCnE"
FOLDER = "commitment_nocongestion_results"
# FOLDER = "commitment_congestion_results"
# FOLDER = "expansion_results"
TRANS_OPS = {
"ppsa": {
"forced_links": (
'Low Medium Transfer',
'Medium Low Transfer',
'High Medium Transfer',
'Medium High Transfer',
),
"excess_sinks": (
"Excess Sink HV",
"Excess Sink MV",
"Excess Sink LV",
),
}
}
# locate the storage directory
parent_location = os.path.join(
doc_dir, "source", "getting_started", "examples", "application",
"phd", "model_scenario_combinations", PARENT
)
result_path = os.path.join(parent_location, FOLDER)
creation_path = os.path.join(parent_location, "creation.py")
creation_module = parse.python_file(creation_path)
tessif_TransCnE = creation_module.create_transcne_es(
periods=PERIODS, expansion=EXPANSION, gridcapacity=GRID_CAPACITY)
output_msg = tessif_TransCnE.to_cfg(
directory=os.path.join(write_dir, 'tsf', "transCnE"),
)
# let the comparatier do the auto comparison:
memory_results = {}
for software in SOFTWARES:
if software == "ppsa":
transops = TRANS_OPS
else:
transops = None
dct = tessif.analyze.trace_memory(
path=os.path.join(write_dir, 'tsf', 'transCnE'),
# parser=tessif.parse.hdf5,
parser=tessif.parse.flat_config_folder,
model=software,
trans_ops=transops,
)
memory_df = pd.DataFrame(
data=dct.values(), index=dct.keys(), columns=(software,))
memory_df = memory_df.divide(1e6).round(0)
memory_df.index.name = "Memory [MB]"
memory_df.rename(index={'simulation': 'optimization'}, inplace=True)
csv_path = os.path.join(result_path, "_".join(
[software, "memory_results.csv"]))
memory_df.to_csv(csv_path)
print("memory results obtianed")
timing_results = {}
for software in SOFTWARES:
if software == "ppsa":
transops = TRANS_OPS
else:
transops = None
dct = tessif.analyze.stop_time(
path=os.path.join(write_dir, 'tsf', 'es_to_compare.hdf5'),
parser=tessif.parse.hdf5,
model=software,
measurement='wall',
trans_ops=TRANS_OPS
)
timings_df = pd.DataFrame(
data=dct.values(), index=dct.keys(), columns=(software,))
timings_df = timings_df.round(1)
timings_df.index.name = "Timings [s]"
timings_df.rename(index={'simulation': 'optimization'}, inplace=True)
csv_path = os.path.join(result_path, "_".join(
[software, "timings_results.csv"]))
timings_df.to_csv(csv_path)
print("timing results obtianed")
Computational Ressources Plots
The computational ressources bar plots are created using following code:
from tessif.frused.paths import doc_dir # nopep8
import os # nopep8
import pandas as pd # nopep8
SOFTWARES = ('cllp', 'fine', 'omf', 'ppsa', )
PARENT = "TransCnE"
FOLDER = "commitment_nocongestion_results"
FOLDER = "commitment_congestion_results"
# FOLDER = "expansion_results"
# locate the storage directory
parent_location = os.path.join(
doc_dir, "source", "getting_started", "examples", "application",
"phd", "model_scenario_combinations", PARENT
)
# locate the storage directory
result_path = os.path.join(parent_location, FOLDER)
memory_results = {}
for software in SOFTWARES:
csv_path = os.path.join(result_path, "_".join(
[software, "memory_results.csv"]))
memory_results[software] = pd.read_csv(
csv_path, index_col=0).to_dict()[software]
memory_df = pd.DataFrame(memory_results)
memory_df.index.name = "Memory [MB]"
csv_path = os.path.join(result_path, "memory_results.csv")
plot_path = os.path.join(result_path, "memory_results.png")
memory_df.to_csv(csv_path)
memory_df.plot(kind="bar", rot=0, figsize=(10, 5)).figure.savefig(plot_path)
timings_results = {}
for software in SOFTWARES:
csv_path = os.path.join(result_path, "_".join(
[software, "timings_results.csv"]))
timings_results[software] = pd.read_csv(
csv_path, index_col=0).to_dict()[software]
timings_df = pd.DataFrame(timings_results)
timings_df.index.name = "Time [s]"
csv_path = os.path.join(result_path, "timings_results.csv")
plot_path = os.path.join(result_path, "timings_results.png")
timings_df.to_csv(csv_path)
timings_df.plot(kind="bar", rot=0, figsize=(10, 5)).figure.savefig(plot_path)