Skip to content

Commit 8523e75

Browse files
authored
2026.01.0.rc5 (#507)
* Fixed errors in solar-reports (reported by @Balk and @dabit) * Fixed problem with managing battery with short periods (reported by @Mirabis) * 2026.01.0.rc5
1 parent beda72a commit 8523e75

File tree

5 files changed

+187
-32
lines changed

5 files changed

+187
-32
lines changed

dao/prog/da_report.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,9 +1139,13 @@ def add_col_df(
11391139
# add_from.at[row.tijd, col_name_from])
11401140
if row.tijd in add_to.index:
11411141
org_value = add_to.at[row.tijd, col_name_to]
1142-
if pd.isna(org_value):
1142+
try:
1143+
if pd.isna(org_value):
1144+
org_value = 0
1145+
except ValueError:
11431146
org_value = 0
1144-
add_to.at[row.tijd, col_name_to] = org_value + factor * row[col_index]
1147+
if pd.notna(row[col_index]):
1148+
add_to.at[row.tijd, col_name_to] = org_value + factor * row[col_index]
11451149
else:
11461150
for row in add_from.itertuples():
11471151
# add_from.at[row.tijd, col_name_from])
@@ -3019,9 +3023,15 @@ def calc_solar_data(self, device: dict, day: datetime.date, active_view: str):
30193023
pred_dao = []
30203024
for row in result.itertuples():
30213025
if pd.notna(row.tijd):
3022-
prod = self.calc_prod_solar(
3023-
device, row.tijd.timestamp(), row.prognose_straling, 1
3024-
)
3026+
straling = row.gemeten_straling
3027+
if pd.isna(straling):
3028+
straling = row.prognose_straling
3029+
if pd.notna(straling):
3030+
prod = self.calc_prod_solar(
3031+
device, row.tijd.timestamp(), straling, 1
3032+
)
3033+
else:
3034+
prod = pd.NA
30253035
else:
30263036
prod = pd.NA
30273037
pred_dao.append(prod)

dao/prog/day_ahead.py

Lines changed: 161 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ def __init__(self, file_name=None):
5454
def calc_optimum(
5555
self, _start_dt: dt.datetime | None = None, _start_soc: float | None = None
5656
):
57-
# _start_dt = datetime.datetime(year=2026, month=1, day=8, hour=16, minute=0)
58-
# _start_soc = 37
57+
# _start_dt = datetime.datetime(year=2026, month=1, day=8, hour=18, minute=0)
58+
# _start_soc = 18.8
5959
if _start_dt is not None or _start_soc is not None:
6060
self.debug = True
6161
logging.info(f"Debug = {self.debug}")
@@ -324,6 +324,7 @@ def calc_optimum(
324324
pv_dc_num = 0
325325
for b in range(B):
326326
for s in range(len(self.battery_options[b]["solar"])):
327+
solar_option = self.battery_options[b]["solar"][s]
327328
if pv_dc_num <= 9:
328329
pv_dc_varcode.append("pv_dc_" + str(pv_dc_num))
329330
pv_dc_num += 1
@@ -334,12 +335,12 @@ def calc_optimum(
334335
== "true"
335336
):
336337
prod = (
337-
max(0, getattr(row, self.solar[s]["name"]))
338+
max(0, getattr(row, solar_option["name"]))
338339
* interval_fraction[-1]
339340
)
340341
else:
341342
prod = self.calc_prod_solar(
342-
self.battery_options[b]["solar"][s],
343+
solar_option,
343344
row.time,
344345
gr,
345346
hour_fraction[-1],
@@ -612,8 +613,8 @@ def calc_optimum(
612613
for b in range(B)
613614
]
614615

615-
# ac_to_dc met aan uit #############################################################
616616
"""
617+
# declaraties laden ac_to_dc met aan uit zonder sos #######################################
617618
#ac_to_dc: wat er gaat er vanuit ac naar de omvormer
618619
ac_to_dc = [[model.add_var(var_type=CONTINUOUS, lb=0, ub=max_charge_power[b])
619620
for u in range(U)] for b in range(B)]
@@ -626,8 +627,9 @@ def calc_optimum(
626627
# vermogens klasse aan/uit
627628
ac_to_dc_st_on = [[[model.add_var(var_type=BINARY)
628629
for u in range(U)] for cs in range(CS[b])] for b in range(B)]
630+
# tot hier declaraties laden zonder sos ##################################################
629631
"""
630-
# met sos ###################################################################
632+
# declaraties laden met sos ###############################################################
631633
ac_to_dc_samples = [
632634
[charge_stages[b][cs]["power"] / 1000 for cs in range(CS[b])]
633635
for b in range(B)
@@ -664,8 +666,10 @@ def calc_optimum(
664666
]
665667
for b in range(B)
666668
]
667-
# tot hier met sos
668-
# '''
669+
# declaraties laden tot hier met sos ####################################################
670+
671+
"""
672+
# vanaf hier decalaties ontladen zonder sos #############################################
669673
ac_from_dc = [
670674
[
671675
model.add_var(
@@ -700,6 +704,51 @@ def calc_optimum(
700704
[[model.add_var(var_type=BINARY) for _ in range(U)] for _ in range(DS[b])]
701705
for b in range(B)
702706
]
707+
# tot hier declaraties ontladen zonder sos ###############################################
708+
"""
709+
710+
# vanaf hier declaraties ontladen met sos ###############################################
711+
ac_from_dc_samples = [
712+
[
713+
(
714+
discharge_stages[b][ds]["power"]
715+
/ 1000
716+
)
717+
for ds in range(DS[b])]
718+
for b in range(B)
719+
]
720+
dc_to_ac_samples = [
721+
[
722+
(
723+
discharge_stages[b][ds]["power"]
724+
/ (discharge_stages[b][ds]["efficiency"] * 1000)
725+
)
726+
for ds in range(DS[b])
727+
]
728+
for b in range(B)
729+
]
730+
ac_from_dc = [
731+
[
732+
model.add_var(
733+
var_type=CONTINUOUS,
734+
lb=0,
735+
ub=min(reduced_power[b][u], max_discharge_power[b]),
736+
)
737+
for u in range(U)
738+
]
739+
for b in range(B)
740+
]
741+
ac_from_dc_on = [
742+
[model.add_var(var_type=BINARY) for _ in range(U)] for _ in range(B)
743+
]
744+
ac_from_dc_w = [
745+
[
746+
[model.add_var(var_type=CONTINUOUS, lb=0, ub=1) for _ in range(DS[b])]
747+
for _ in range(U)
748+
]
749+
for b in range(B)
750+
]
751+
# tot hier declaraties ontladen met sos ###############################################
703752

704753
# energiebalans dc
705754
dc_from_ac = [
@@ -771,7 +820,7 @@ def calc_optimum(
771820
for b in range(B):
772821
for u in range(U):
773822
# laden, alles uitgedrukt in vermogen kW
774-
# met aan/uit
823+
# vanaf hier laden met aan/uit zonder sos
775824
"""
776825
for cs in range(CS[b]):
777826
model += (ac_to_dc_st[b][cs][u] <=
@@ -787,8 +836,9 @@ def calc_optimum(
787836
model += dc_from_ac[b][u] == xsum(ac_to_dc_st[b][cs][u] * \
788837
charge_stages[b][cs]["efficiency"]
789838
for cs in range(CS[b]))
839+
# tot hier laden met aan/uit zonder sos #######################################
790840
"""
791-
# met sos
841+
# vanaf hier laden met sos ######################################################
792842
model += xsum(ac_to_dc_w[b][u][cs] for cs in range(CS[b])) == 1
793843
model += (
794844
xsum(
@@ -811,9 +861,10 @@ def calc_optimum(
811861
],
812862
2,
813863
)
814-
# tot hier met sos
864+
# tot hier constraints laden met sos
815865

816-
# ontladen
866+
"""
867+
# vanaf hier ontladen met aan/uit
817868
for ds in range(DS[b]):
818869
model += (
819870
ac_from_dc_st[b][ds][u]
@@ -837,6 +888,33 @@ def calc_optimum(
837888
ac_from_dc_st[b][ds][u] / discharge_stages[b][ds]["efficiency"]
838889
for ds in range(DS[b])
839890
)
891+
#tot hier ontladen met aan/uit
892+
"""
893+
894+
# vanaf hier ontladen met sos ######################################################
895+
model += xsum(ac_from_dc_w[b][u][ds] for ds in range(DS[b])) <= 1
896+
model += (
897+
xsum(
898+
ac_from_dc_w[b][u][ds] * ac_from_dc_samples[b][ds]
899+
for ds in range(DS[b])
900+
)
901+
== ac_from_dc[b][u]
902+
)
903+
model += (
904+
xsum(
905+
ac_from_dc_w[b][u][ds] * dc_to_ac_samples[b][ds]
906+
for ds in range(DS[b])
907+
)
908+
== dc_to_ac[b][u]
909+
)
910+
model.add_sos(
911+
[
912+
(ac_from_dc_w[b][u][ds], ac_from_dc_samples[b][ds])
913+
for ds in range(DS[b])
914+
],
915+
2,
916+
)
917+
# tot hier constraints ontladen met sos
840918

841919
for b in range(B):
842920
for u in range(U + 1):
@@ -3111,6 +3189,24 @@ def calc_optimum(
31113189
dc_to_ac_eff =
31123190
discharge_stages[ds]["efficiency"] * 100.0
31133191
"""
3192+
if self.log_level == logging.INFO:
3193+
# debug laden
3194+
if ac_to_dc[b][u].x > 0.0:
3195+
logging.info(f"Laad volume in uur {u} {uur[u]} "
3196+
f"{ac_from_dc[b][u].x*hour_fraction[u]} kWh")
3197+
for cs in range(CS[b]):
3198+
if ac_to_dc_w[b][u][cs].x > 0:
3199+
logging.info(f"{cs} {ac_to_dc_w[b][u][cs].x} "
3200+
f"{ac_to_dc_samples[b][cs]}")
3201+
3202+
# debug ontladen
3203+
if ac_from_dc[b][u].x > 0.0:
3204+
logging.info(f"Ontlaad volume in uur {u} {uur[u]} "
3205+
f"{ac_from_dc[b][u].x*hour_fraction[u]} kWh")
3206+
for ds in range(DS[b]):
3207+
if ac_from_dc_w[b][u][ds].x > 0:
3208+
logging.info(f"{ds} {ac_from_dc_w[b][u][ds].x} "
3209+
f"{ac_from_dc_samples[b][ds]}")
31143210

31153211
row = [
31163212
str(uur[u]),
@@ -3599,7 +3695,7 @@ def calc_optimum(
35993695
############################################
36003696
for b in range(B):
36013697
# vermogen aan ac kant
3602-
netto_vermogen = int(1000 * (ac_to_dc[b][0].x - ac_from_dc[b][0].x))
3698+
netto_vermogen_bat = int(1000 * (ac_to_dc[b][0].x - ac_from_dc[b][0].x))
36033699
minimum_power = int(self.battery_options[b]["minimum power"])
36043700
battery_state_on_value = self.config.get(
36053701
["entity set operating mode on"], self.battery_options[b], "Aan"
@@ -3608,27 +3704,68 @@ def calc_optimum(
36083704
["entity set operating mode off"], self.battery_options[b], "Uit"
36093705
)
36103706
bat_name = self.battery_options[b]["name"]
3611-
if abs(netto_vermogen) <= 20:
3612-
netto_vermogen = 0
3707+
if abs(netto_vermogen_bat) <= 20:
3708+
netto_vermogen_bat = 0
36133709
new_state = battery_state_off_value
36143710
stop_omvormer = None
36153711
balance = False
36163712
elif abs(c_l[0].x - c_t[0].x) <= 0.01:
36173713
new_state = battery_state_on_value
36183714
balance = True
36193715
stop_omvormer = None
3620-
elif abs(netto_vermogen) < minimum_power:
3716+
elif abs(netto_vermogen_bat) < minimum_power:
36213717
new_state = battery_state_on_value
36223718
balance = False
36233719
new_ts = (
36243720
start_dt.timestamp()
3625-
+ (abs(netto_vermogen) / minimum_power) * self.interval_s
3721+
+ (abs(netto_vermogen_bat) / minimum_power) * self.interval_s
36263722
)
36273723
stop_omvormer = dt.datetime.fromtimestamp(int(new_ts))
3628-
if netto_vermogen > 0:
3629-
netto_vermogen = minimum_power
3724+
if netto_vermogen_bat > 0:
3725+
netto_vermogen_bat = minimum_power
3726+
else:
3727+
netto_vermogen_bat = -minimum_power
3728+
elif ac_to_dc[b][0].x > 0.0: # laden met optimaal vermogen
3729+
sum_weight_factor = 0
3730+
sum_power = 0 # in W
3731+
for cs in range(CS[b]):
3732+
wf = ac_to_dc_w[b][0][ds].x
3733+
if wf > 0:
3734+
sum_weight_factor += wf
3735+
sum_power += wf * charge_stages[b][cs]["power"]
3736+
if sum_weight_factor < 0.95:
3737+
new_state = battery_state_on_value
3738+
balance = False
3739+
netto_vermogen_bat = round(sum_power/sum_weight_factor)
3740+
new_ts = (
3741+
start_dt.timestamp() + self.interval_s * sum_weight_factor * interval_fraction[0]
3742+
)
3743+
stop_omvormer = dt.datetime.fromtimestamp(int(new_ts))
36303744
else:
3631-
netto_vermogen = -minimum_power
3745+
new_state = battery_state_on_value
3746+
balance = False
3747+
stop_omvormer = None
3748+
elif ac_from_dc[b][0].x > 0.0: # ontladen met optimaal vermogen
3749+
sum_weight_factor = 0
3750+
sum_power = 0 # in W
3751+
for ds in range(DS[b]):
3752+
wf = ac_from_dc_w[b][0][ds].x
3753+
if wf > 0:
3754+
sum_weight_factor += wf
3755+
sum_power += wf * discharge_stages[b][ds]["power"]
3756+
if sum_weight_factor < 0.95:
3757+
new_state = battery_state_on_value
3758+
balance = False
3759+
netto_vermogen_bat = - round(sum_power / sum_weight_factor)
3760+
new_ts = (
3761+
start_dt.timestamp() + self.interval_s * sum_weight_factor *
3762+
interval_fraction[0]
3763+
)
3764+
stop_omvormer = dt.datetime.fromtimestamp(int(new_ts))
3765+
else:
3766+
new_state = battery_state_on_value
3767+
balance = False
3768+
stop_omvormer = None
36323769
else:
36333770
new_state = battery_state_on_value
36343771
balance = False
@@ -3652,7 +3789,7 @@ def calc_optimum(
36523789
if self.debug:
36533790
logging.info(
36543791
f"Netto vermogen naar(+)/uit(-) batterij {bat_name} "
3655-
f"zou zijn: {netto_vermogen} W"
3792+
f"zou zijn: {netto_vermogen_bat} W"
36563793
)
36573794
if stop_omvormer:
36583795
logging.info(f"tot: {stop_str}")
@@ -3667,7 +3804,7 @@ def calc_optimum(
36673804
self.set_entity_value(
36683805
"entity set power feedin",
36693806
self.battery_options[b],
3670-
netto_vermogen,
3807+
netto_vermogen_bat,
36713808
)
36723809
self.set_entity_option(
36733810
"entity set operating mode", self.battery_options[b], new_state
@@ -3678,7 +3815,7 @@ def calc_optimum(
36783815
)
36793816
logging.info(
36803817
f"Netto vermogen naar(+)/uit(-) omvormer {bat_name}: "
3681-
f"{netto_vermogen} W"
3818+
f"{netto_vermogen_bat} W"
36823819
f"{' tot: ' + stop_str if stop_omvormer else ''}"
36833820
)
36843821
logging.info(
@@ -4555,7 +4692,7 @@ def main():
45554692
if arg.lower() == "calc_baseloads":
45564693
da_calc.run_task_function("calc_baseloads")
45574694
continue
4558-
if arg.lower() == "train":
4695+
if arg.lower() == "train_ml_predictions":
45594696
da_calc.run_task_function("train_ml_predictions")
45604697
continue
45614698
da_calc.db_da.log_pool_status()

dao/prog/solar_predictor.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,10 @@ def predict(
697697

698698
# Select required features
699699
featured_df = weather_data[self.feature_columns]
700-
prediction = self.model.predict(featured_df)
700+
if len(featured_df) == 0:
701+
prediction = []
702+
else:
703+
prediction = self.model.predict(featured_df)
701704
prediction = np.maximum(0, prediction) # Ensure non-negative
702705
result = pd.DataFrame(
703706
{"date_time": featured_df.index, "prediction": prediction}
@@ -1033,7 +1036,9 @@ def predict_solar_device(
10331036
raise FileNotFoundError(
10341037
f"Er is geen model aanwezig voor {self.solar_name},svp eerst trainen."
10351038
)
1036-
weather_data = self.get_weatherdata(start, end, prognose=True)
1039+
latest_dt = self.db_da.get_time_border_record("gr", latest=True)
1040+
prognose = (latest_dt < end)
1041+
weather_data = self.get_weatherdata(start, end, prognose=prognose)
10371042
prediction = self.predict(weather_data)
10381043
logging.info(f"ML prediction {self.solar_name}\n{prediction}")
10391044
return prediction

release-testing/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog 刀 DAO
22
# Day Ahead Optimizer
3+
# 2026.01.0.rc5
4+
- Fixed errors in solar-reports (reported by @balk and @Dabit)
5+
- Fixed problem with managing battery with short periods (reported by @mirabis)
36

47
# 2026.01.0.rc4
58
Fixed solar report errors with missing radiation or measured solar data (reported by @Dabit and @balk)

0 commit comments

Comments
 (0)