uprava solver gneport cutoff u ba81
This commit is contained in:
1
.idea/data_source_mapping.xml
generated
1
.idea/data_source_mapping.xml
generated
@@ -2,6 +2,7 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="DataSourcePerFileMappings">
|
<component name="DataSourcePerFileMappings">
|
||||||
<file url="file://$PROJECT_DIR$/../ems-cursor-db-pracovni/debug-forecast.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
<file url="file://$PROJECT_DIR$/../ems-cursor-db-pracovni/debug-forecast.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
||||||
|
<file url="file://$PROJECT_DIR$/../ems-cursor-db-pracovni/naplneni-base-line-ba81.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
||||||
<file url="file://$PROJECT_DIR$/../ems-cursor-db-pracovni/porovnani-view-status.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
<file url="file://$PROJECT_DIR$/../ems-cursor-db-pracovni/porovnani-view-status.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
||||||
<file url="file://$PROJECT_DIR$/db/migration/V009__postgrest_roles.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
<file url="file://$PROJECT_DIR$/db/migration/V009__postgrest_roles.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
||||||
<file url="file://$PROJECT_DIR$/db/routines/R__079_fn_forecast_pv_slots_range_corrected.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
<file url="file://$PROJECT_DIR$/db/routines/R__079_fn_forecast_pv_slots_range_corrected.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
||||||
|
|||||||
@@ -551,6 +551,18 @@ def solve_dispatch(
|
|||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
om = (operating_mode or "AUTO").strip().upper()
|
||||||
|
# SELF_SUSTAIN dřív vynucoval ge[t] == 0, což umí udělat MILP infeasible v okamžiku, kdy:
|
||||||
|
# - baterie je na max SoC (nelze nabíjet),
|
||||||
|
# - PV pole B není curtailable,
|
||||||
|
# - a pv_b_forecast_w > load_baseline_w (typicky po ručním snížení baseline).
|
||||||
|
# Export v SELF_SUSTAIN proto povolíme jako nouzový ventil, ale silně penalizujeme,
|
||||||
|
# aby k němu docházelo jen když už neexistuje jiné fyzikálně možné řešení.
|
||||||
|
SELF_SUSTAIN_EXPORT_PENALTY_CZK_KWH = 100.0
|
||||||
|
# Penalizace vypnutí GEN portu (mikroinvertory): preferujeme nechat zapnuto a vypnout jen když
|
||||||
|
# by to jinak vedlo k nežádoucímu exportu / infeasible řešení.
|
||||||
|
GEN_CUTOFF_PENALTY_CZK_KWH = 5.0
|
||||||
|
|
||||||
# EV proměnné per vozidlo
|
# EV proměnné per vozidlo
|
||||||
ev_direct = [[pulp.LpVariable(f"evd_{e}_{t}", 0,
|
ev_direct = [[pulp.LpVariable(f"evd_{e}_{t}", 0,
|
||||||
min(vehicles[e].max_charge_power_w, grid.max_import_power_w))
|
min(vehicles[e].max_charge_power_w, grid.max_import_power_w))
|
||||||
@@ -582,6 +594,16 @@ def solve_dispatch(
|
|||||||
pulp.lpSum(
|
pulp.lpSum(
|
||||||
gi[t] * slots[t].buy_price * INTERVAL_H / 1000
|
gi[t] * slots[t].buy_price * INTERVAL_H / 1000
|
||||||
- ge[t] * slots[t].sell_price * INTERVAL_H / 1000
|
- ge[t] * slots[t].sell_price * INTERVAL_H / 1000
|
||||||
|
+ (
|
||||||
|
ge[t] * SELF_SUSTAIN_EXPORT_PENALTY_CZK_KWH * INTERVAL_H / 1000
|
||||||
|
if om == "SELF_SUSTAIN"
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
+ (
|
||||||
|
(slots[t].pv_b_forecast_w * z_gen_cutoff[t]) * GEN_CUTOFF_PENALTY_CZK_KWH * INTERVAL_H / 1000
|
||||||
|
if z_gen_cutoff is not None
|
||||||
|
else 0
|
||||||
|
)
|
||||||
+ gi_over[t] * IMPORT_OVER_BREAKER_PENALTY_CZK_KWH * INTERVAL_H / 1000
|
+ gi_over[t] * IMPORT_OVER_BREAKER_PENALTY_CZK_KWH * INTERVAL_H / 1000
|
||||||
+ 0.5 * (bc[t] + bd[t]) * degradation_cost_effective * INTERVAL_H / 1000
|
+ 0.5 * (bc[t] + bd[t]) * degradation_cost_effective * INTERVAL_H / 1000
|
||||||
+ pulp.lpSum(
|
+ pulp.lpSum(
|
||||||
@@ -638,10 +660,16 @@ def solve_dispatch(
|
|||||||
# ev_via_bat kryto z discharge
|
# ev_via_bat kryto z discharge
|
||||||
prob += pulp.lpSum(ev_via_bat[e][t] for e in range(EV)) <= bd[t]
|
prob += pulp.lpSum(ev_via_bat[e][t] for e in range(EV)) <= bd[t]
|
||||||
|
|
||||||
# Záporná prodejní cena: export nepovinně zakazovat — účelovka už obsahuje -ge*sell
|
# GEN port cut-off chceme vůbec připustit jen v režimech/politikách, kde má smysl:
|
||||||
# (záporné sell zvyšuje náklad exportu). GEN cut-off držíme vypnutý (jinak by z_gen_cutoff
|
# - SELF_SUSTAIN (no-export intent; typicky ge=0, takže cut-off je bezpečnostní ventil),
|
||||||
# uměle nulovalo forecast pole B).
|
# - BLOCK_EXPORT okna (v projektu reprezentované sloty se sell_price < 0),
|
||||||
if z_gen_cutoff is not None:
|
# - případně explicitní no_export politika (pokud bude v kontextu dostupná).
|
||||||
|
allow_gen_cutoff = (
|
||||||
|
om == "SELF_SUSTAIN"
|
||||||
|
or float(s.sell_price) < 0
|
||||||
|
or bool(getattr(grid, "no_export", False))
|
||||||
|
)
|
||||||
|
if z_gen_cutoff is not None and not allow_gen_cutoff:
|
||||||
prob += z_gen_cutoff[t] == 0
|
prob += z_gen_cutoff[t] == 0
|
||||||
|
|
||||||
# Záporná nákupní cena → cap import (baseline domu + akumulace + řízené zátěže)
|
# Záporná nákupní cena → cap import (baseline domu + akumulace + řízené zátěže)
|
||||||
@@ -703,10 +731,8 @@ def solve_dispatch(
|
|||||||
else:
|
else:
|
||||||
prob += ev_direct[e][t] + ev_via_bat[e][t] <= vehicles[e].max_charge_power_w
|
prob += ev_direct[e][t] + ev_via_bat[e][t] <= vehicles[e].max_charge_power_w
|
||||||
|
|
||||||
om = (operating_mode or "AUTO").strip().upper()
|
|
||||||
if om == "SELF_SUSTAIN":
|
if om == "SELF_SUSTAIN":
|
||||||
for t in range(T):
|
for t in range(T):
|
||||||
prob += ge[t] == 0
|
|
||||||
prob += gi[t] <= slots[t].load_baseline_w
|
prob += gi[t] <= slots[t].load_baseline_w
|
||||||
elif om == "PRESERVE":
|
elif om == "PRESERVE":
|
||||||
for t in range(T):
|
for t in range(T):
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
| Mode | Solver constraints | Deye fyzický režim | Baterie |
|
| Mode | Solver constraints | Deye fyzický režim | Baterie |
|
||||||
|------|-------------------|-------------------|---------|
|
|------|-------------------|-------------------|---------|
|
||||||
| AUTO | žádné | PASSIVE/SELL/CHARGE dle plánu | dle plánu |
|
| AUTO | žádné | PASSIVE/SELL/CHARGE dle plánu | dle plánu |
|
||||||
| SELF_SUSTAIN | no_export, min_import | vždy PASSIVE | plné limity |
|
| SELF_SUSTAIN | min_import; export jen jako nouzový ventil (silně penalizovaný) | vždy PASSIVE | plné limity |
|
||||||
| CHARGE_CHEAP | no_export, no_discharge | CHARGE | nabíjení max |
|
| CHARGE_CHEAP | no_export, no_discharge | CHARGE | nabíjení max |
|
||||||
| PRESERVE | no_charge, no_discharge | PASSIVE | lock (0/0) |
|
| PRESERVE | no_charge, no_discharge | PASSIVE | lock (0/0) |
|
||||||
| MANUAL | solver neběží | EMS nezapisuje | — |
|
| MANUAL | solver neběží | EMS nezapisuje | — |
|
||||||
|
|||||||
@@ -267,12 +267,17 @@ kde:
|
|||||||
- `batt_charge_cap_w` = min(`battery.max_charge_power_w`, \((soc_{max}-soc)_{wh} / 0.25h\)) – tj. výkonově omezené a SoC-headroom omezené
|
- `batt_charge_cap_w` = min(`battery.max_charge_power_w`, \((soc_{max}-soc)_{wh} / 0.25h\)) – tj. výkonově omezené a SoC-headroom omezené
|
||||||
- `flexible_load_w` = plánované EV/TČ setpointy v daném slotu (pokud jsou připojené / povolené)
|
- `flexible_load_w` = plánované EV/TČ setpointy v daném slotu (pokud jsou připojené / povolené)
|
||||||
|
|
||||||
#### Doporučená implementace v EMS (aby se to chovalo správně)
|
#### Implementace v EMS (aktuální chování)
|
||||||
|
|
||||||
- **Správně (v solveru / plánu)**: řešit cut-off přímo v LP binární proměnnou `z_gen_cutoff[t]` (0/1), která modeluje, zda je GEN port odpojen.
|
- Cut-off se řeší přímo v LP binární proměnnou `z_gen_cutoff[t]` (0/1), která modeluje, zda je GEN port odpojen.
|
||||||
- Efektivní výkon z GEN do bilance: `pv_b_effective[t] = pv_b_forecast_w * (1 - z_gen_cutoff[t])`
|
- Efektivní výkon z GEN do bilance: `pv_b_effective[t] = pv_b_forecast_w * (1 - z_gen_cutoff[t])`
|
||||||
- Solver nechá GEN připojený vždy, když je výkon užitečný (sníží import / nabije baterii / pokryje zátěž).
|
- Solver nechá GEN připojený vždy, když je výkon užitečný (sníží import / nabije baterii / pokryje zátěž).
|
||||||
- Při `sell_price < 0` a zároveň hrozícím přebytku (ge je zakázané) solver může zvolit `z_gen_cutoff[t]=1` (cut-off) jako poslední možnost.
|
- `z_gen_cutoff[t]` je **vůbec povolené jen** v režimech/politikách, kde to dává smysl:
|
||||||
|
- `SELF_SUSTAIN`
|
||||||
|
- sloty s `sell_price < 0` („BLOCK_EXPORT“ okna)
|
||||||
|
- (případně) explicitní `no_export` politika, pokud je v kontextu dostupná
|
||||||
|
Mimo tyto případy je `z_gen_cutoff[t]` vynucené na `0`.
|
||||||
|
- Cut-off je v účelové funkci **penalizované** (za „zahozenou“ GEN výrobu), aby se zapínalo jen jako poslední možnost.
|
||||||
- Výstup se ukládá do `planning_interval.deye_gen_cutoff_enabled` (nullable) a exporter pak jen provede reg 179.
|
- Výstup se ukládá do `planning_interval.deye_gen_cutoff_enabled` (nullable) a exporter pak jen provede reg 179.
|
||||||
|
|
||||||
**Scope / bezpečnost:** proměnná i flag existují jen na lokalitách, kde je zapnutý `asset_inverter.deye_gen_microinverter_cutoff_enabled` (tj. kde je GEN port s mikroinvertory reálně zapojen). Jinde se nic neřeší ani nezobrazuje.
|
**Scope / bezpečnost:** proměnná i flag existují jen na lokalitách, kde je zapnutý `asset_inverter.deye_gen_microinverter_cutoff_enabled` (tj. kde je GEN port s mikroinvertory reálně zapojen). Jinde se nic neřeší ani nezobrazuje.
|
||||||
|
|||||||
Reference in New Issue
Block a user