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)