@@ -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 "
0 commit comments