Skip to content

controller_em

ControllerEM

ControllerEM(price_percentile, klas_init, kla_reduction, s_nh_threshold, biogas, o2, ch4)

Creates a Controller object.

Parameters:

Name Type Description Default
price_percentile float

Percentile of electricity prices used to adjust KLa values (aeration reduction at prices above the percentile, e.g. 0.9 -> aerate less when electricity prices are in the top 10%).

required
klas_init ndarray

Initial KLa values for the reactor compartments [d⁻¹].

required
kla_reduction float

Reduction factor for KLa values.

required
s_nh_threshold float

Maximum value of ammonia concentration in the effluent [g ⋅ m⁻³].

required
biogas GasMix

Biogas object.

required
o2 Gas

Oxygen object.

required
ch4 Gas

Methane object.

required
Source code in src/bsm2_python/controller_em.py
def __init__(
    self,
    price_percentile: float,
    klas_init: np.ndarray,
    kla_reduction: float,
    s_nh_threshold: float,
    biogas: GasMix,
    o2: Gas,
    ch4: Gas,
):
    super().__init__(
        price_percentile=price_percentile,
        klas_init=klas_init,
        kla_reduction=kla_reduction,
        s_nh_threshold=s_nh_threshold,
    )
    self.biogas = biogas
    self.o2 = o2
    self.ch4 = ch4

control_gas_management

control_gas_management(time_diff, chps, boilers, biogas_storage, cooler, flare, heat_net, fermenter)

Set loads for all gas management modules for current timestep.

Parameters:

Name Type Description Default
time_diff float

Length of current timestep [h].

required
chps list[CHP]

List of CHP objects.

required
boilers list[Boiler]

List of boiler objects.

required
biogas_storage BiogasStorage

BiogasStorage object.

required
cooler Cooler

Cooler object.

required
flare Flare

Flare object.

required
heat_net HeatNet

HeatNet object.

required
fermenter Fermenter

Fermenter object.

required
Source code in src/bsm2_python/controller_em.py
def control_gas_management(
    self,
    time_diff: float,
    chps: list[CHP],
    boilers: list[Boiler],
    biogas_storage: BiogasStorage,
    cooler: Cooler,
    flare: Flare,
    heat_net: HeatNet,
    fermenter: Fermenter,
):
    """Set loads for all gas management modules for current timestep.

    Parameters
    ----------
    time_diff : float
        Length of current timestep [h].
    chps : list[CHP]
        List of CHP objects.
    boilers : list[Boiler]
        List of boiler objects.
    biogas_storage : BiogasStorage
        BiogasStorage object.
    cooler : Cooler
        Cooler object.
    flare : Flare
        Flare object.
    heat_net : HeatNet
        HeatNet object.
    fermenter : Fermenter
        Fermenter object.
    """

    # calculate loads for chps, electrolyzer, methanation
    self.calculate_load_chps(chps, biogas_storage)

    # calculate heat demand and load of boilers
    temperature_after_chps = self.calculate_temperature_after_chps(heat_net, chps, fermenter.heat_demand)

    biogas_consumption_chps = 0.0  # np.sum([chp.max_gas_power_uptake * chp.load for chp in chps]) / self.biogas.h_u
    for chp in chps:
        biogas_consumption_chps += chp.max_gas_power_uptake * chp.load / self.biogas.h_u
    biogas_storage_after_chps = self.predict_biogas_storage_vol(
        biogas_storage.vol, fermenter.gas_production, biogas_consumption_chps, time_diff
    )

    temperature_deficit = max(heat_net.lower_threshold - temperature_after_chps, 0)
    self.calculate_load_boilers(temperature_deficit, heat_net, boilers, biogas_storage_after_chps)

    temperature_surplus = max(temperature_after_chps - heat_net.upper_threshold, 0)
    self.calculate_load_cooler(temperature_surplus, heat_net, cooler)

    biogas_consumption_boiler = 0.0
    for boiler in boilers:
        biogas_consumption_boiler += boiler.max_gas_power_uptake * boiler.load / self.biogas.h_u
    biogas_storage_after_boilers = self.predict_biogas_storage_vol(
        biogas_storage_after_chps, 0, biogas_consumption_boiler, time_diff
    )
    # calculate load of flare depending on biogas storage fill level and useage
    self.calculate_load_flare(biogas_storage.max_vol, biogas_storage_after_boilers, flare)

calculate_load_chps

calculate_load_chps(chps, biogas_storage)

Set loads for all chps by comparing fill level of biogas storage with chp thresholds set in chp_gas_storage_rules.

Parameters:

Name Type Description Default
chps list[CHP]

List of CHP objects.

required
biogas_storage BiogasStorage

BiogasStorage object.

required
Source code in src/bsm2_python/controller_em.py
@staticmethod
def calculate_load_chps(chps: list[CHP], biogas_storage: BiogasStorage):
    """Set loads for all chps by comparing fill level of biogas storage
    with chp thresholds set in chp_gas_storage_rules.

    Parameters
    ----------
    chps : list[CHP]
        List of CHP objects.
    biogas_storage : BiogasStorage
        BiogasStorage object.
    """

    for chp in chps:
        if not chp.ready_to_change_load:
            continue
        # get rules for current chp, iterate over upper threshold, lower threshold
        for rule in chp.storage_rules:
            # if rules are met set load and break loop
            if (biogas_storage.vol / biogas_storage.max_vol > rule[0]) & (biogas_storage.tendency * rule[1] >= 0):
                chp.load = rule[2]
                break
            # if rules not met set load to 0 and potentially go to lower threshold
            else:
                chp.load = 0
        if chp.under_maintenance:
            chp.load = 0

calculate_temperature_after_chps

calculate_temperature_after_chps(heat_net, chps, heat_demand)

Calculate the new temperature of the heat net after supplying heat for the fermenter and using heat from chps.

Parameters:

Name Type Description Default
heat_net HeatNet

Heat net object.

required
chps list[CHP]

List of CHP objects.

required
heat_demand float

Heat demand of the fermenter [kW].

required

Returns:

Name Type Description
heatnet_temp float

Temperature of heat net after using heat from chps [°C].

Source code in src/bsm2_python/controller_em.py
@staticmethod
def calculate_temperature_after_chps(heat_net: HeatNet, chps: list[CHP], heat_demand: float):
    """Calculate the new temperature of the heat net after supplying heat for the fermenter
    and using heat from chps.

    Parameters
    ----------
    heat_net : HeatNet
        Heat net object.
    chps : list[CHP]
        List of CHP objects.
    heat_demand : float
        Heat demand of the fermenter [kW].

    Returns
    -------
    heatnet_temp : float
        Temperature of heat net after using heat from chps [°C].
    """

    chps_heat = 0
    for chp in chps:
        chps_heat += chp.get_products(chp.load)[1]

    total_heat = chps_heat - heat_demand
    return heat_net.calculate_temperature(total_heat, heat_net.temperature)

predict_biogas_storage_vol

predict_biogas_storage_vol(fill_level, inflow, outflow, time_diff)

Predict the biogas storage volume at the end of the current timestep.

Parameters:

Name Type Description Default
fill_level float

Fill level of biogas storage at start of current timestep [Nm³].

required
inflow float

Inflow of biogas in current timestep [Nm³ ⋅ h⁻¹].

required
outflow float

Outflow of biogas in current timestep [Nm³ ⋅ h⁻¹].

required
time_diff float

Length of current timestep [h].

required

Returns:

Name Type Description
biogas_storage_vol_prog float

Fill level of biogas storage at end of current timestep [Nm³].

Source code in src/bsm2_python/controller_em.py
@staticmethod
def predict_biogas_storage_vol(fill_level, inflow, outflow, time_diff: float):
    """Predict the biogas storage volume at the end of the current timestep.

    Parameters
    ----------
    fill_level : float
        Fill level of biogas storage at start of current timestep [Nm³].
    inflow : float
        Inflow of biogas in current timestep [Nm³ ⋅ h⁻¹].
    outflow : float
        Outflow of biogas in current timestep [Nm³ ⋅ h⁻¹].
    time_diff : float
        Length of current timestep [h].

    Returns
    -------
    biogas_storage_vol_prog : float
        Fill level of biogas storage at end of current timestep [Nm³].
    """

    biogas_storage_vol_prog = fill_level + (inflow - outflow) * time_diff

    return biogas_storage_vol_prog

calculate_load_boilers

calculate_load_boilers(temperature_deficit, heat_net, boilers, biogas_storage_fill_level)

Calculate the load of the boilers.

Parameters:

Name Type Description Default
temperature_deficit float

Temperature deficit of the heat net after supplying heat for the fermenter and using heat from chps [K].

required
heat_net HeatNet

Heat network object.

required
boilers list[Boiler]

List of Boiler objects.

required
biogas_storage_fill_level float

Fill level of biogas storage after supplying biogas for the chps and using receiving biogas from the fermenter [Nm³].

required
Source code in src/bsm2_python/controller_em.py
def calculate_load_boilers(
    self, temperature_deficit: float, heat_net: HeatNet, boilers: list[Boiler], biogas_storage_fill_level: float
):
    """Calculate the load of the boilers.

    Parameters
    ----------
    temperature_deficit : float
        Temperature deficit of the heat net after supplying heat for the fermenter and using heat from chps [K].
    heat_net : HeatNet
        Heat network object.
    boilers : list[Boiler]
        List of Boiler objects.
    biogas_storage_fill_level : float
        Fill level of biogas storage after supplying biogas for the
        chps and using receiving biogas from the fermenter [Nm³].
    """

    heat_demand = temperature_deficit * heat_net.mass_flow * (heat_net.cp / 3600)
    for boiler in boilers:
        max_load_possible = min(biogas_storage_fill_level * self.biogas.h_u / boiler.max_gas_power_uptake, 1.0)
        load = boiler.calculate_load(heat_demand)
        current_load = boiler.load
        # only check changes from on to off/off to on
        supposed_to_change = (load == 0 and current_load > 0) or (load > 0 and current_load == 0)
        # boiler can't change on/off status if not ready to change load, however partial load level can change
        if supposed_to_change and not boiler.ready_to_change_load:
            continue
        if load <= max_load_possible:
            boiler.load = load
        elif max_load_possible >= boiler.minimum_load:
            boiler.load = max_load_possible
        else:
            boiler.load = 0
        heat_production = boiler.get_products(boiler.load)[0]
        heat_demand -= heat_production
        threshold = 1e-5
        if heat_demand <= threshold:
            heat_demand = 0

calculate_load_cooler

calculate_load_cooler(temperature_surplus, heat_net, cooler)

Calculate the load of the cooler.

Parameters:

Name Type Description Default
temperature_surplus float

Temperature surplus of the heat net after supplying heat for the fermenter and using heat from chps [K].

required
heat_net HeatNet

Heat network object.

required
cooler Cooler

Cooler object.

required
Source code in src/bsm2_python/controller_em.py
@staticmethod
def calculate_load_cooler(temperature_surplus: float, heat_net: HeatNet, cooler: Cooler):
    """Calculate the load of the cooler.

    Parameters
    ----------
    temperature_surplus : float
        Temperature surplus of the heat net after supplying heat for the fermenter and using heat from chps [K].
    heat_net : HeatNet
        Heat network object.
    cooler : Cooler
        Cooler object.
    """

    excess_heat = temperature_surplus * heat_net.mass_flow * (heat_net.cp / 3600)
    cooler.load = cooler.calculate_load(excess_heat)

calculate_load_flare

calculate_load_flare(biogas_storeage_max_vol, biogas_storage_vol_prog, flare)

Calculate the load of the flare.

Calculate fill level of biogas storage at end of current timestep, adjust flare load if fill level above threshold.

Parameters:

Name Type Description Default
biogas_storeage_max_vol float

Maximum volume of biogas storage [Nm³].

required
biogas_storage_vol_prog float

Prognosis of fill level of biogas storage at end of current timestep [Nm³].

required
flare Flare

Flare object.

required
Source code in src/bsm2_python/controller_em.py
@staticmethod
def calculate_load_flare(biogas_storeage_max_vol: float, biogas_storage_vol_prog: float, flare: Flare):
    """Calculate the load of the flare.

    Calculate fill level of biogas storage at end of current timestep,
    adjust flare load if fill level above threshold.

    Parameters
    ----------
    biogas_storeage_max_vol : float
        Maximum volume of biogas storage [Nm³].
    biogas_storage_vol_prog : float
        Prognosis of fill level of biogas storage at end of current timestep [Nm³].
    flare : Flare
        Flare object.
    """

    flare_threshold = flare.threshold * biogas_storeage_max_vol
    if biogas_storage_vol_prog >= flare_threshold:
        flare.load = flare.calculate_load(biogas_storage_vol_prog - flare_threshold)
    else:
        flare.load = 0.0