Skip to content

Commit 37ceb88

Browse files
authored
Version 2026.03.0.rc1 (#561)
* Made check 'optimal lower level" lower as "lower limit" (feature request of @Mistral2) * Reduce power during charing at high soc and during discharing at low soc (feature requests form @Bartzzz and @arjenhiemstra) * Made optional battery settings "at_to_dc max power" and "dc_to_bat max power" flex-setting (feature request by @dabit) * Version 2026.03.0.rc1 * Version 2026.03.0.rc1
1 parent 72db02a commit 37ceb88

File tree

5 files changed

+130
-21
lines changed

5 files changed

+130
-21
lines changed

dao/prog/da_base.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,10 @@ def calc_solar_predictions(
649649
solar_prog["tijd"] = pd.to_datetime(solar_prog["date_time"])
650650
if interval == "15min":
651651
solar_prog = interpolate(solar_prog, "prediction", quantity=True)
652-
while len(solar_prog)>0 and solar_prog["tijd"].iloc[0].tz_localize(None) < vanaf:
652+
while (
653+
len(solar_prog) > 0
654+
and solar_prog["tijd"].iloc[0].tz_localize(None) < vanaf
655+
):
653656
solar_prog = solar_prog.iloc[1:]
654657
else:
655658
solar_prog = pd.DataFrame(columns=["tijd", "prediction"])

dao/prog/da_graph.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,8 @@ def build(self, df, options, show=True):
185185
labels.append(label)
186186
plot = None
187187
if vax == "left":
188-
ymax_left = math.ceil(max(ymax_left, max(data_array)))
189-
ymin_left = math.floor(min(ymin_left, min(data_array)))
188+
ymax_left = math.ceil(max(ymax_left, max(data_array)) * 10) / 10
189+
ymin_left = math.floor(min(ymin_left, min(data_array)) *10) / 10
190190
if vax == "right":
191191
ymax_right = (
192192
math.ceil(max(ymax_right, max(data_array)) * 10) / 10
@@ -299,7 +299,7 @@ def build(self, df, options, show=True):
299299
if "title" in haxis and g_nr == (num_graphs - 1):
300300
ax.set_xlabel(haxis["title"])
301301
num_xas = len(df.index)
302-
if num_xas > 12:
302+
if num_xas > 24:
303303
ax.xaxis.set_major_locator(ticker.MultipleLocator(12))
304304
ax.xaxis.set_minor_locator(ticker.MultipleLocator(2))
305305
if len(str(xlabels[0])) > 2:
@@ -340,11 +340,14 @@ def build(self, df, options, show=True):
340340
)
341341

342342
if stacked_plus is not None:
343-
ylim = math.ceil(
344-
max(
345-
max(np.max(stacked_plus), ymax_left),
346-
-min(np.min(stacked_neg), ymin_left),
343+
ylim = (
344+
math.ceil(
345+
max(
346+
max(np.max(stacked_plus), ymax_left) * 10,
347+
-min(np.min(stacked_neg), ymin_left) * 10,
348+
)
347349
)
350+
/ 10
348351
)
349352
if ylim > 0:
350353
if np.min(stacked_neg) < 0:

dao/prog/day_ahead.py

Lines changed: 110 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,9 @@ def calc_optimum(
436436
# in kW
437437
max_charge_power = []
438438
max_discharge_power = []
439-
reduced_power = []
439+
reduce_power_high_soc = []
440+
reduce_power_low_soc = []
441+
reduce_power_hours = []
440442
max_dc_from_bat_power = []
441443
max_dc_to_bat_power = []
442444
avg_eff_dc_to_ac = []
@@ -477,7 +479,7 @@ def calc_optimum(
477479
int_uur = uur[u] if self.interval == "1hour" else int(uur[u][0:2])
478480
if int_uur == hour:
479481
red_power[u] = power
480-
reduced_power.append(red_power)
482+
reduce_power_hours.append(red_power)
481483
if reduced:
482484
if self.log_level == logging.DEBUG:
483485
logging.debug(
@@ -496,21 +498,82 @@ def calc_optimum(
496498
)
497499

498500
max_dc_from_bat_power.append(
499-
self.config.get(
500-
["bat_to_dc max power"],
501+
self.get_setting_state(
502+
"bat_to_dc max power",
501503
self.battery_options[b],
504+
"number",
502505
2000 * max_discharge_power[b],
503506
)
504507
/ 1000
505508
)
506509
max_dc_to_bat_power.append(
507-
self.config.get(
508-
["dc_to_bat max power"],
510+
self.get_setting_state(
511+
"dc_to_bat max power",
509512
self.battery_options[b],
510-
2000 * max_discharge_power[b],
513+
"number",
514+
2000 * max_charge_power[b],
511515
)
512516
/ 1000
513517
)
518+
519+
# reduce power low soc
520+
red_power_low_soc = self.config.get(
521+
["reduce_power_low_soc"], self.battery_options[b], []
522+
)
523+
if len(red_power_low_soc) == 1:
524+
logging.warning(
525+
f"For reduced power at low soc there must be two entries, "
526+
f"one found."
527+
)
528+
red_power_low_soc = []
529+
red_power_low_soc = sorted(red_power_low_soc, key=lambda x: x["soc"])
530+
for rpl in range(len(red_power_low_soc) - 1):
531+
helling = (
532+
red_power_low_soc[rpl + 1]["power"]
533+
- red_power_low_soc[rpl]["power"]
534+
) / (red_power_low_soc[rpl + 1]["soc"] - red_power_low_soc[rpl]["soc"])
535+
red_power_low_soc[rpl]["helling"] = helling
536+
logging.info(
537+
f"Reduced power applied during discharging at low soc, between "
538+
f"{red_power_low_soc[rpl]['soc']}% and "
539+
f"{red_power_low_soc[rpl + 1]['soc']}% power is reduced from "
540+
f"{red_power_low_soc[rpl]['power']}W until "
541+
f"{red_power_low_soc[rpl + 1]['power']}W"
542+
)
543+
if not red_power_low_soc:
544+
logging.info(f"No reduced power applied during discharging at low soc")
545+
reduce_power_low_soc.append(red_power_low_soc)
546+
547+
# reduce power high soc
548+
red_power_high_soc = self.config.get(
549+
["reduce_power_high_soc"], self.battery_options[b], []
550+
)
551+
if len(red_power_high_soc) == 1:
552+
logging.warning(
553+
f"For reduced power at high soc there must be two entries, "
554+
f"one found"
555+
)
556+
red_power_high_soc = []
557+
red_power_high_soc = sorted(red_power_high_soc, key=lambda x: x["soc"])
558+
for rph in range(len(red_power_high_soc) - 1):
559+
helling = (
560+
red_power_high_soc[rph + 1]["power"]
561+
- red_power_high_soc[rph]["power"]
562+
) / (
563+
red_power_high_soc[rph + 1]["soc"] - red_power_high_soc[rph]["soc"]
564+
)
565+
red_power_high_soc[rph]["helling"] = helling
566+
logging.info(
567+
f"Reduced power applied during charging at high soc, between "
568+
f"{red_power_high_soc[rph]['soc']}% and "
569+
f"{red_power_high_soc[rph + 1]['soc']}% power is reduced from "
570+
f"{red_power_high_soc[rph]['power']}W until "
571+
f"{red_power_high_soc[rph + 1]['power']}W"
572+
)
573+
if not red_power_low_soc:
574+
logging.info(f"No reduced power applied during charging at high soc")
575+
reduce_power_high_soc.append(red_power_high_soc)
576+
514577
# DS is aantal discharge stages
515578
DS.append(len(discharge_stages[b]))
516579
sum_eff = 0
@@ -541,6 +604,14 @@ def calc_optimum(
541604
default=lower_limit[b],
542605
)
543606
)
607+
if opt_low_lvl < lower_limit[b]:
608+
logging.warning(
609+
f"'optimal lower level' is lower defined as 'lower limit'."
610+
f" 'Optimal lower level' is adjusted to "
611+
f"'lower limit' ({lower_limit[b]})"
612+
)
613+
opt_low_lvl = lower_limit[b]
614+
544615
opt_low_level.append(opt_low_lvl)
545616

546617
# penalty in euro/%.hour
@@ -647,7 +718,7 @@ def calc_optimum(
647718
model.add_var(
648719
var_type=CONTINUOUS,
649720
lb=0,
650-
ub=min(reduced_power[b][u], max_charge_power[b]),
721+
ub=min(reduce_power_hours[b][u], max_charge_power[b]),
651722
)
652723
for u in range(U)
653724
]
@@ -672,7 +743,7 @@ def calc_optimum(
672743
model.add_var(
673744
var_type=CONTINUOUS,
674745
lb=0,
675-
ub=min(reduced_power[b][u], max_discharge_power[b]),
746+
ub=min(reduce_power[b][u], max_discharge_power[b]),
676747
)
677748
for u in range(U)
678749
]
@@ -724,7 +795,7 @@ def calc_optimum(
724795
model.add_var(
725796
var_type=CONTINUOUS,
726797
lb=0,
727-
ub=min(reduced_power[b][u], max_discharge_power[b]),
798+
ub=min(reduce_power_hours[b][u], max_discharge_power[b]),
728799
)
729800
for u in range(U)
730801
]
@@ -908,6 +979,33 @@ def calc_optimum(
908979
)
909980
# tot hier constraints ontladen met sos
910981

982+
"""
983+
constraints reduced charging power at low or high soc
984+
max_power[u] <= max_power_0 + helling x (soc[u] – soc_0)
985+
max_power[u] <= max_power_0 + helling x soc[u] – helling x soc_0
986+
max_power[u] - helling x soc[u] <= max_power_0 - helling x soc-0
987+
"""
988+
# low soc
989+
for b in range(B):
990+
red_power = reduce_power_low_soc[b]
991+
for rpl in range(len(red_power) - 1):
992+
helling = int(red_power[rpl]["helling"])
993+
for u in range(U):
994+
model += (
995+
dc_from_bat[b][u] - helling * soc[b][u]
996+
<= red_power[rpl]["power"] - helling * red_power[rpl]["soc"]
997+
)
998+
# high soc
999+
for b in range(B):
1000+
red_power = reduce_power_high_soc[b]
1001+
for rph in range(len(red_power) - 1):
1002+
helling = int(red_power[rph]["helling"])
1003+
for u in range(U):
1004+
model += (
1005+
dc_to_bat[b][u] - helling * soc[b][u]
1006+
<= red_power[rph]["power"] - helling * red_power[rph]["soc"]
1007+
)
1008+
9111009
for b in range(B):
9121010
for u in range(U + 1):
9131011
model += soc[b][u] == soc_low[b][u] + soc_mid[b][u]
@@ -1141,8 +1239,8 @@ def calc_optimum(
11411239
for _ in range(U + 1)
11421240
] # end temp boiler
11431241

1144-
if (
1145-
(boiler_start_index > boiler_end_index) or (boiler_end_temp>= boiler_bovengrens)
1242+
if (boiler_start_index > boiler_end_index) or (
1243+
boiler_end_temp >= boiler_bovengrens
11461244
): # geen boiler opwarming in deze periode
11471245
logging.info(
11481246
f"Boiler wordt niet ingepland, omdat de verwachte "

release-testing/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog 刀 DAO
22
# Day Ahead Optimizer
3+
# 2026.03.0.rc1
4+
- Made optional battery settings "bat_to_dc max power" and "dc_to_bat max power" flex-setting (feature request by @DaBit)
5+
- Reduce power during charging at high soc and during discharging at low soc (feature requests form @bartzzz and @arjenhiemstra)
6+
- Made check 'optimal lower level" lower as "lower limit" (feature request of @mistral2)
7+
38
# 2026.02.2.rc4
49
- Fix error in calculating heating window boiler
510
- Update python module pandas 3.0.0 -> 3.0.1

release-testing/config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: 刀 Day Ahead Optimizer (TESTING)
3-
version: 2026.02.2.rc4
3+
version: 2026.03.0.rc1
44
stage: experimental
55
slug: day_ahead_opt-testing
66
description: Beta version of DAO. Use only for testing!

0 commit comments

Comments
 (0)