feat(application): integrate measured-load ingestion training and planner source
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
from dataclasses import replace
|
||||
import unittest
|
||||
from netplan_v4.optimizer import optimize
|
||||
from netplan_v4.domain import Limits
|
||||
from test_v4 import AT,seq,batt
|
||||
|
||||
class BatteryRecoveryTest(unittest.TestCase):
|
||||
def plan(self,b,steps=None):
|
||||
steps=steps or seq([0.]*3+[1000.]*3,pv=[1000.]*3+[0.]*3,sell=[0.]*6)
|
||||
return optimize(steps,[b],at=AT,observed_peaks={'2026-10':1.},peak_prices={'2026-10':0.})
|
||||
def test_reserve_recovery_preserves_real_initial_energy(self):
|
||||
b=batt(capacity_kwh=1.,soc_percent=5.,min_soc_percent=15.,physical_min_soc_percent=3.,recovery_allowed=True,rearm_soc_percent=17.,discharge_blocked=True,grid_charging=False,max_charge_w=1000.,max_discharge_w=1000.)
|
||||
p=self.plan(b);self.assertTrue(p['executable'],p)
|
||||
energy=.05
|
||||
for x in p['points']:
|
||||
target=x['batteryTargetW'];energy+=target/1000/12*(.9**.5 if target>=0 else 1/(.9**.5))
|
||||
self.assertAlmostEqual(100*energy,x['socEndPercent']['b'],places=5)
|
||||
self.assertGreaterEqual(energy,.05-1e-6)
|
||||
self.assertGreaterEqual(p['points'][-1]['socEndPercent']['b'],15.-1e-5)
|
||||
def test_below_hardware_min_not_fabricated(self):
|
||||
b=batt(soc_percent=1.,min_soc_percent=15.,physical_min_soc_percent=3.,recovery_allowed=True)
|
||||
self.assertFalse(self.plan(b)['executable'])
|
||||
def test_disabled_can_rearm_after_charging(self):
|
||||
b=batt(capacity_kwh=10.,soc_percent=16.,min_soc_percent=15.,rearm_soc_percent=17.,discharge_blocked=True,grid_charging=False,max_charge_w=1000.,max_discharge_w=1000.)
|
||||
p=self.plan(b);self.assertTrue(p['executable'],p)
|
||||
self.assertAlmostEqual(min(0.,p['points'][0]['batteryTargetW']),0.)
|
||||
self.assertTrue(any(x['batteryTargetW']<-1. for x in p['points'][3:]))
|
||||
first=next(i for i,x in enumerate(p['points']) if x['batteryTargetW']<-1.)
|
||||
self.assertGreaterEqual(p['points'][first-1]['socEndPercent']['b'],17.-1e-5)
|
||||
def test_cannot_discharge_below_rearm_while_blocked(self):
|
||||
b=batt(capacity_kwh=10.,soc_percent=16.,min_soc_percent=15.,rearm_soc_percent=17.,discharge_blocked=True,grid_charging=False)
|
||||
p=self.plan(b,seq([1000.]*6));self.assertTrue(p['executable'])
|
||||
self.assertTrue(all(x['batteryTargetW']>=-1e-5 for x in p['points']))
|
||||
def test_unavailable_power_does_not_get_invented(self):
|
||||
b=batt(capacity_kwh=1.,soc_percent=5.,min_soc_percent=15.,physical_min_soc_percent=3.,recovery_allowed=True,max_charge_w=0.,max_discharge_w=0.)
|
||||
p=self.plan(b);self.assertFalse(p['executable']);self.assertEqual(p['status'],'no_feasible_plan')
|
||||
def test_invalid_hysteresis_range_rejected(self):
|
||||
b=batt(rearm_soc_percent=95.)
|
||||
self.assertFalse(self.plan(b)['executable'])
|
||||
def test_no_recovery_permission_keeps_strict_behavior(self):
|
||||
self.assertFalse(self.plan(batt(soc_percent=5.))['executable'])
|
||||
|
||||
if __name__=='__main__':unittest.main()
|
||||
Reference in New Issue
Block a user