implmemtace cuttoff genportu
This commit is contained in:
@@ -187,6 +187,9 @@ class DispatchResult:
|
||||
#: Explicitní fyzický režim Deye pro control exporter (PASSIVE / SELL / CHARGE).
|
||||
#: Cíl: odstranit heuristiky z exporteru a nést záměr přímo v plánu.
|
||||
deye_physical_mode: str
|
||||
#: True = v daném slotu odpojit GEN port (MI export cutoff) přes reg 179 bits0–1.
|
||||
#: None = lokalita tuto funkci nemá / nepoužívá.
|
||||
deye_gen_cutoff_enabled: bool | None
|
||||
ev1_setpoint_w: Optional[int]
|
||||
ev2_setpoint_w: Optional[int]
|
||||
ev1_via_bat_w: int
|
||||
@@ -346,6 +349,14 @@ def solve_dispatch(
|
||||
hp = [pulp.LpVariable(f"hp_{t}", 0, heat_pump.rated_heating_power_w) for t in range(T)]
|
||||
soc_deficit_24h = pulp.LpVariable("soc_deficit_24h", 0, battery.usable_capacity_wh)
|
||||
|
||||
# GEN port cut-off (BA81): binární proměnná pouze pokud je feature povolená v konfiguraci site/invertoru.
|
||||
gen_cutoff_enabled = bool(getattr(grid, "deye_gen_microinverter_cutoff_enabled", False))
|
||||
z_gen_cutoff = (
|
||||
[pulp.LpVariable(f"z_gen_cutoff_{t}", cat=pulp.LpBinary) for t in range(T)]
|
||||
if gen_cutoff_enabled
|
||||
else None
|
||||
)
|
||||
|
||||
# EV proměnné per vozidlo
|
||||
ev_direct = [[pulp.LpVariable(f"evd_{e}_{t}", 0,
|
||||
min(vehicles[e].max_charge_power_w, grid.max_import_power_w))
|
||||
@@ -391,8 +402,13 @@ def solve_dispatch(
|
||||
ev_total_t = pulp.lpSum(ev_direct[e][t] + ev_via_bat[e][t] for e in range(EV))
|
||||
|
||||
# Energetická bilance
|
||||
pv_b_effective = (
|
||||
float(s.pv_b_forecast_w) * (1 - z_gen_cutoff[t])
|
||||
if z_gen_cutoff is not None
|
||||
else float(s.pv_b_forecast_w)
|
||||
)
|
||||
prob += (
|
||||
pv_a_net + s.pv_b_forecast_w + gi[t] + bd[t]
|
||||
pv_a_net + pv_b_effective + gi[t] + bd[t]
|
||||
== s.load_baseline_w + ev_total_t + hp[t] + bc[t] + ge[t]
|
||||
)
|
||||
|
||||
@@ -410,6 +426,9 @@ def solve_dispatch(
|
||||
# Záporná prodejní cena → zakázat export
|
||||
if s.sell_price < 0:
|
||||
prob += ge[t] == 0
|
||||
# GEN cut-off používáme jen jako nástroj pro BLOCK_EXPORT (sell < 0).
|
||||
if z_gen_cutoff is not None and s.sell_price >= 0:
|
||||
prob += z_gen_cutoff[t] == 0
|
||||
|
||||
# Záporná nákupní cena → cap import (baseline domu + akumulace + řízené zátěže)
|
||||
if s.buy_price < 0:
|
||||
@@ -552,6 +571,10 @@ def solve_dispatch(
|
||||
elif batt_w > 0 and grid_w > 0:
|
||||
deye_mode = "CHARGE"
|
||||
|
||||
deye_gen_cutoff = None
|
||||
if z_gen_cutoff is not None:
|
||||
deye_gen_cutoff = bool(round(float(pulp.value(z_gen_cutoff[t]) or 0)))
|
||||
|
||||
cost = (
|
||||
pulp.value(gi[t]) * slots[t].buy_price * INTERVAL_H / 1000
|
||||
- pulp.value(ge[t]) * slots[t].sell_price * INTERVAL_H / 1000
|
||||
@@ -563,6 +586,7 @@ def solve_dispatch(
|
||||
battery_soc_target = soc_pct,
|
||||
grid_setpoint_w = grid_w,
|
||||
deye_physical_mode = deye_mode,
|
||||
deye_gen_cutoff_enabled = deye_gen_cutoff,
|
||||
ev1_setpoint_w = round(pulp.value(ev_direct[0][t]) + pulp.value(ev_via_bat[0][t]))
|
||||
if slots[t].ev1_connected else None,
|
||||
ev2_setpoint_w = round(pulp.value(ev_direct[1][t]) + pulp.value(ev_via_bat[1][t]))
|
||||
@@ -847,6 +871,7 @@ async def _load_site_context(site_id: int, db):
|
||||
grid = SimpleNamespace(
|
||||
max_import_power_w=int(g["max_import_power_w"]),
|
||||
max_export_power_w=int(g["max_export_power_w"]),
|
||||
deye_gen_microinverter_cutoff_enabled=bool(g.get("deye_gen_microinverter_cutoff_enabled") or False),
|
||||
)
|
||||
|
||||
vehicles: list[SimpleNamespace] = []
|
||||
@@ -995,6 +1020,7 @@ async def _save_planning_run(
|
||||
"battery_soc_target_pct": r.battery_soc_target,
|
||||
"grid_setpoint_w": r.grid_setpoint_w,
|
||||
"deye_physical_mode": r.deye_physical_mode,
|
||||
"deye_gen_cutoff_enabled": r.deye_gen_cutoff_enabled,
|
||||
"ev1_setpoint_w": r.ev1_setpoint_w,
|
||||
"ev2_setpoint_w": r.ev2_setpoint_w,
|
||||
"ev1_via_bat_w": r.ev1_via_bat_w,
|
||||
|
||||
Reference in New Issue
Block a user