controller ¶
Adjusts KLA values based on electricity prices and ammonia concentration in the effluent.
Controller ¶
Controller(price_percentile, klas_init, kla_reduction, s_nh_threshold, elec_price_path=None)
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 |
Source code in src/bsm2_python/controller.py
def __init__(
self,
price_percentile: float,
klas_init: np.ndarray,
kla_reduction: float,
s_nh_threshold: float,
elec_price_path: str | None = None,
):
self.price_percentile = price_percentile
self.klas_init = klas_init
self.kla_reduction = kla_reduction
self.s_nh_threshold = s_nh_threshold
self.is_price_in_percentile: list[float] = []
path_name = os.path.dirname(__file__)
if elec_price_path is None:
self.elec_price_path = path_name + '/data/electricity_prices_2023.csv'
else:
self.elec_price_path = elec_price_path
with open(self.elec_price_path, encoding='utf-8-sig') as f:
prices = []
price_times = []
data = np.array(list(csv.reader(f, delimiter=','))).astype(np.float64)
for price in data:
prices.append(price[1])
price_times.append(price[0])
self.electricity_prices = np.array(prices).astype(np.float64)
self.price_times = np.array(price_times).astype(np.float64)
get_klas ¶
get_klas(step_simtime, s_nh_eff)
Returns the KLA values for the reactor compartments based on electricity prices and ammonia concentration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
step_simtime | int | Current timestep in the simtime of the simulation [d]. | required |
s_nh_eff | float | Ammonia concentration in the plant effluent [g ⋅ m⁻³]. | required |
Returns:
| Name | Type | Description |
|---|---|---|
klas | ndarray | KLa values for the reactor compartments [d⁻¹]. [kla_reactor1, kla_reactor2, ...] |
Source code in src/bsm2_python/controller.py
def get_klas(self, step_simtime: float, s_nh_eff: np.ndarray):
"""Returns the KLA values for the reactor compartments based on electricity prices and ammonia concentration.
Parameters
----------
step_simtime : int
Current timestep in the simtime of the simulation [d].
s_nh_eff : float
Ammonia concentration in the plant effluent [g ⋅ m⁻³].
Returns
-------
klas : np.ndarray
KLa values for the reactor compartments [d⁻¹]. \n
[kla_reactor1, kla_reactor2, ...]
"""
# necessary to deal with floating point errors
eps = 1e-8
step_day_start = np.where(self.price_times - math.floor(step_simtime + eps) <= 0)[0][-1]
step_day_end = np.where(self.price_times - math.floor(step_simtime + eps + 1) <= 0)[0][-1]
steps_day = step_day_end - step_day_start
step_in_day = np.where(self.price_times - (step_simtime + eps) <= 0)[0][-1] - step_day_start
# get hours with the highest electricity prices at start of day
if step_in_day == 0:
self.is_price_in_percentile.clear()
electricity_prices_day = self.electricity_prices[step_day_start:step_day_end]
steps_in_percentile = round(steps_day * (1 - self.price_percentile))
indices_percentile = np.argpartition(electricity_prices_day, -steps_in_percentile)[-steps_in_percentile:]
for i, _ in enumerate(electricity_prices_day):
self.is_price_in_percentile.append(i in indices_percentile)
# add one more step to the end of the day to avoid index out of bounds
self.is_price_in_percentile.append(False)
if self.s_nh_threshold > s_nh_eff and self.is_price_in_percentile[step_in_day]:
klas = self.klas_init * self.kla_reduction
else:
klas = self.klas_init
return klas