Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion dao/prog/da_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,10 @@ def calc_solar_predictions(
solar_prog["tijd"] = pd.to_datetime(solar_prog["date_time"])
if interval == "15min":
solar_prog = interpolate(solar_prog, "prediction", quantity=True)
while len(solar_prog)>0 and solar_prog["tijd"].iloc[0].tz_localize(None) < vanaf:
while (
len(solar_prog) > 0
and solar_prog["tijd"].iloc[0].tz_localize(None) < vanaf
):
solar_prog = solar_prog.iloc[1:]
else:
solar_prog = pd.DataFrame(columns=["tijd", "prediction"])
Expand Down
17 changes: 10 additions & 7 deletions dao/prog/da_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ def build(self, df, options, show=True):
labels.append(label)
plot = None
if vax == "left":
ymax_left = math.ceil(max(ymax_left, max(data_array)))
ymin_left = math.floor(min(ymin_left, min(data_array)))
ymax_left = math.ceil(max(ymax_left, max(data_array)) * 10) / 10
ymin_left = math.floor(min(ymin_left, min(data_array)) *10) / 10
if vax == "right":
ymax_right = (
math.ceil(max(ymax_right, max(data_array)) * 10) / 10
Expand Down Expand Up @@ -299,7 +299,7 @@ def build(self, df, options, show=True):
if "title" in haxis and g_nr == (num_graphs - 1):
ax.set_xlabel(haxis["title"])
num_xas = len(df.index)
if num_xas > 12:
if num_xas > 24:
ax.xaxis.set_major_locator(ticker.MultipleLocator(12))
ax.xaxis.set_minor_locator(ticker.MultipleLocator(2))
if len(str(xlabels[0])) > 2:
Expand Down Expand Up @@ -340,11 +340,14 @@ def build(self, df, options, show=True):
)

if stacked_plus is not None:
ylim = math.ceil(
max(
max(np.max(stacked_plus), ymax_left),
-min(np.min(stacked_neg), ymin_left),
ylim = (
math.ceil(
max(
max(np.max(stacked_plus), ymax_left) * 10,
-min(np.min(stacked_neg), ymin_left) * 10,
)
)
/ 10
)
if ylim > 0:
if np.min(stacked_neg) < 0:
Expand Down
122 changes: 110 additions & 12 deletions dao/prog/day_ahead.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,9 @@ def calc_optimum(
# in kW
max_charge_power = []
max_discharge_power = []
reduced_power = []
reduce_power_high_soc = []
reduce_power_low_soc = []
reduce_power_hours = []
max_dc_from_bat_power = []
max_dc_to_bat_power = []
avg_eff_dc_to_ac = []
Expand Down Expand Up @@ -477,7 +479,7 @@ def calc_optimum(
int_uur = uur[u] if self.interval == "1hour" else int(uur[u][0:2])
if int_uur == hour:
red_power[u] = power
reduced_power.append(red_power)
reduce_power_hours.append(red_power)
if reduced:
if self.log_level == logging.DEBUG:
logging.debug(
Expand All @@ -496,21 +498,82 @@ def calc_optimum(
)

max_dc_from_bat_power.append(
self.config.get(
["bat_to_dc max power"],
self.get_setting_state(
"bat_to_dc max power",
self.battery_options[b],
"number",
2000 * max_discharge_power[b],
)
/ 1000
)
max_dc_to_bat_power.append(
self.config.get(
["dc_to_bat max power"],
self.get_setting_state(
"dc_to_bat max power",
self.battery_options[b],
2000 * max_discharge_power[b],
"number",
2000 * max_charge_power[b],
)
/ 1000
)

# reduce power low soc
red_power_low_soc = self.config.get(
["reduce_power_low_soc"], self.battery_options[b], []
)
if len(red_power_low_soc) == 1:
logging.warning(
f"For reduced power at low soc there must be two entries, "
f"one found."
)
red_power_low_soc = []
red_power_low_soc = sorted(red_power_low_soc, key=lambda x: x["soc"])
for rpl in range(len(red_power_low_soc) - 1):
helling = (
red_power_low_soc[rpl + 1]["power"]
- red_power_low_soc[rpl]["power"]
) / (red_power_low_soc[rpl + 1]["soc"] - red_power_low_soc[rpl]["soc"])
red_power_low_soc[rpl]["helling"] = helling
logging.info(
f"Reduced power applied during discharging at low soc, between "
f"{red_power_low_soc[rpl]['soc']}% and "
f"{red_power_low_soc[rpl + 1]['soc']}% power is reduced from "
f"{red_power_low_soc[rpl]['power']}W until "
f"{red_power_low_soc[rpl + 1]['power']}W"
)
if not red_power_low_soc:
logging.info(f"No reduced power applied during discharging at low soc")
reduce_power_low_soc.append(red_power_low_soc)

# reduce power high soc
red_power_high_soc = self.config.get(
["reduce_power_high_soc"], self.battery_options[b], []
)
if len(red_power_high_soc) == 1:
logging.warning(
f"For reduced power at high soc there must be two entries, "
f"one found"
)
red_power_high_soc = []
red_power_high_soc = sorted(red_power_high_soc, key=lambda x: x["soc"])
for rph in range(len(red_power_high_soc) - 1):
helling = (
red_power_high_soc[rph + 1]["power"]
- red_power_high_soc[rph]["power"]
) / (
red_power_high_soc[rph + 1]["soc"] - red_power_high_soc[rph]["soc"]
)
red_power_high_soc[rph]["helling"] = helling
logging.info(
f"Reduced power applied during charging at high soc, between "
f"{red_power_high_soc[rph]['soc']}% and "
f"{red_power_high_soc[rph + 1]['soc']}% power is reduced from "
f"{red_power_high_soc[rph]['power']}W until "
f"{red_power_high_soc[rph + 1]['power']}W"
)
if not red_power_low_soc:
logging.info(f"No reduced power applied during charging at high soc")
reduce_power_high_soc.append(red_power_high_soc)

# DS is aantal discharge stages
DS.append(len(discharge_stages[b]))
sum_eff = 0
Expand Down Expand Up @@ -541,6 +604,14 @@ def calc_optimum(
default=lower_limit[b],
)
)
if opt_low_lvl < lower_limit[b]:
logging.warning(
f"'optimal lower level' is lower defined as 'lower limit'."
f" 'Optimal lower level' is adjusted to "
f"'lower limit' ({lower_limit[b]})"
)
opt_low_lvl = lower_limit[b]

opt_low_level.append(opt_low_lvl)

# penalty in euro/%.hour
Expand Down Expand Up @@ -647,7 +718,7 @@ def calc_optimum(
model.add_var(
var_type=CONTINUOUS,
lb=0,
ub=min(reduced_power[b][u], max_charge_power[b]),
ub=min(reduce_power_hours[b][u], max_charge_power[b]),
)
for u in range(U)
]
Expand All @@ -672,7 +743,7 @@ def calc_optimum(
model.add_var(
var_type=CONTINUOUS,
lb=0,
ub=min(reduced_power[b][u], max_discharge_power[b]),
ub=min(reduce_power[b][u], max_discharge_power[b]),
)
for u in range(U)
]
Expand Down Expand Up @@ -724,7 +795,7 @@ def calc_optimum(
model.add_var(
var_type=CONTINUOUS,
lb=0,
ub=min(reduced_power[b][u], max_discharge_power[b]),
ub=min(reduce_power_hours[b][u], max_discharge_power[b]),
)
for u in range(U)
]
Expand Down Expand Up @@ -908,6 +979,33 @@ def calc_optimum(
)
# tot hier constraints ontladen met sos

"""
constraints reduced charging power at low or high soc
max_power[u] <= max_power_0 + helling x (soc[u] – soc_0)
max_power[u] <= max_power_0 + helling x soc[u] – helling x soc_0
max_power[u] - helling x soc[u] <= max_power_0 - helling x soc-0
"""
# low soc
for b in range(B):
red_power = reduce_power_low_soc[b]
for rpl in range(len(red_power) - 1):
helling = int(red_power[rpl]["helling"])
for u in range(U):
model += (
dc_from_bat[b][u] - helling * soc[b][u]
<= red_power[rpl]["power"] - helling * red_power[rpl]["soc"]
)
# high soc
for b in range(B):
red_power = reduce_power_high_soc[b]
for rph in range(len(red_power) - 1):
helling = int(red_power[rph]["helling"])
for u in range(U):
model += (
dc_to_bat[b][u] - helling * soc[b][u]
<= red_power[rph]["power"] - helling * red_power[rph]["soc"]
)

for b in range(B):
for u in range(U + 1):
model += soc[b][u] == soc_low[b][u] + soc_mid[b][u]
Expand Down Expand Up @@ -1141,8 +1239,8 @@ def calc_optimum(
for _ in range(U + 1)
] # end temp boiler

if (
(boiler_start_index > boiler_end_index) or (boiler_end_temp>= boiler_bovengrens)
if (boiler_start_index > boiler_end_index) or (
boiler_end_temp >= boiler_bovengrens
): # geen boiler opwarming in deze periode
logging.info(
f"Boiler wordt niet ingepland, omdat de verwachte "
Expand Down
5 changes: 5 additions & 0 deletions release-testing/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog 刀 DAO
# Day Ahead Optimizer
# 2026.03.0.rc1
- Made optional battery settings "bat_to_dc max power" and "dc_to_bat max power" flex-setting (feature request by @DaBit)
- Reduce power during charging at high soc and during discharging at low soc (feature requests form @bartzzz and @arjenhiemstra)
- Made check 'optimal lower level" lower as "lower limit" (feature request of @mistral2)

# 2026.02.2.rc4
- Fix error in calculating heating window boiler
- Update python module pandas 3.0.0 -> 3.0.1
Expand Down
2 changes: 1 addition & 1 deletion release-testing/config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: 刀 Day Ahead Optimizer (TESTING)
version: 2026.02.2.rc4
version: 2026.03.0.rc1
stage: experimental
slug: day_ahead_opt-testing
description: Beta version of DAO. Use only for testing!
Expand Down