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