NREL / hive

HIVE™ is a mobility services research platform
https://nrelhive.readthedocs.io/en/latest/
BSD 3-Clause "New" or "Revised" License
21 stars 9 forks source link

stack overflow due to interaction of human_driver_off_shift_charge_target and fundamental powercurve diagram #146

Closed robfitzgerald closed 1 year ago

robfitzgerald commented 1 year ago

problem

powercurve_ops.time_to_fill estimates the time to charge a vehicle by micro-simulating the charge session until the target SoC is reached. but it is possible for the charger/mechatronics combination to prevent reaching this target SoC value, and because the function does not check for incremental improvement, it leads to an infinite loop which terminates with a stack overflow from reaching the max function recursion depth.

where this happens can be illustrated with this example. the vehicle is a leaf_50. the agent is headed home and finds a DCFC charger. the charging session begins with the vehicle at 44.08 kWh. the human_driver_off_shift_charge_target (by default) is 100%. the charging session is extremely slow since the powercurve tapers off near the 100% mark:

Screen Shot 2023-03-22 at 2 33 19 PM

at around 4 hours, we stop seeing any effect from charging and this leads to the stack overflow (see log section, below). i'm guessing this has somewhat to do with floating point arithmetic precision and the charge tapering table.

fix

two ideas.

first, this function could check if the previous kWh == the next kWh and if so, cancel. this is probably ok for a function that has the purpose of estimation, but i could see where this approach would fail more generally, such as when we implement grid throttling. in the case the grid momentarily ceases charge sessions, this would be mis-interpreted as "finishing a session" by this function.

second, we could modify the powercurve to have a lower limit to output values. if you look at the chart above, it gets really close to zero, or maybe even reaches zero. that's a bit nonsensical for our purposes, even if it is correctly modeling the physics. if we enforced a lookup to always produce a value that is at least 1% for instance, then we shouldn't run into this problem. this may be a better solution.

log

i stubbed the recursive function to show it at work and include the error message displayed.

_fill with time 0, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 44.081174676107885})
_fill with time 60, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 44.21282809079099})
_fill with time 120, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 44.3385132173418})
_fill with time 180, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 44.45850061815564})
_fill with time 240, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 44.573048590132586})
_fill with time 300, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 44.68240372071324})
_fill with time 360, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 44.786801418707576})
_fill with time 420, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 44.8864664210595})
_fill with time 480, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 44.981613276638136})
_fill with time 540, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 45.072446808097204})
_fill with time 600, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 45.160128510238096})
_fill with time 660, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 45.24500439791048})
_fill with time 720, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 45.327164257177344})
_fill with time 780, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 45.40669500094767})
_fill with time 840, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 45.483680760917345})
_fill with time 900, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 45.55820297656799})
_fill with time 960, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 45.630340481317816})
_fill with time 1020, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 45.700169585915646})
_fill with time 1080, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 45.76776415916635})
_fill with time 1140, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 45.83319570607303})
_fill with time 1200, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 45.89653344347869})
_fill with time 1260, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 45.95784437328737})
_fill with time 1320, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.01719335334218})
_fill with time 1380, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.07464316603523})
_fill with time 1440, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.1302545847221})
_fill with time 1500, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.18408643801099})
_fill with time 1560, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.23619567199464})
_fill with time 1620, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.28663741049081})
_fill with time 1680, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.3358753523526})
_fill with time 1740, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.384089145023665})
_fill with time 1800, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.43130009080717})
_fill with time 1860, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.47752904891838})
_fill with time 1920, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.52279644470087})
_fill with time 1980, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.5671222786511})
_fill with time 2040, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.61052613525516})
_fill with time 2100, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.65302719164185})
_fill with time 2160, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.69464422605569})
_fill with time 2220, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.73539562615373})
_fill with time 2280, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.775299397129736})
_fill with time 2340, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.81437316966944})
_fill with time 2400, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.852634207740316})
_fill with time 2460, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.890099416219314})
_fill with time 2520, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.92678534836195})
_fill with time 2580, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.962708213116024})
_fill with time 2640, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 46.99788388228321})
_fill with time 2700, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.03232789753172})
_fill with time 2760, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.06605547726306})
_fill with time 2820, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.09908152333599})
_fill with time 2880, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.1314206276506})
_fill with time 2940, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.16308707859547})
_fill with time 3000, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.19409486736068})
_fill with time 3060, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.22445769411958})
_fill with time 3120, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.25418897408189})
_fill with time 3180, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.283301843420986})
_fill with time 3240, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.31180916507783})
_fill with time 3300, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.33972353444421})
_fill with time 3360, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.367057284927775})
_fill with time 3420, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.393822493401274})
_fill with time 3480, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.420030985538524})
_fill with time 3540, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.44569434103932})
_fill with time 3600, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.4708238987457})
_fill with time 3660, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.49543076165179})
_fill with time 3720, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.51952580180943})
_fill with time 3780, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.54352580180943})
_fill with time 3840, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.56752580180943})
_fill with time 3900, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.59152580180943})
_fill with time 3960, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.61552580180943})
_fill with time 4020, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.639525801809434})
_fill with time 4080, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.663525801809435})
_fill with time 4140, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.687525801809436})
_fill with time 4200, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.71152580180944})
_fill with time 4260, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.73552580180944})
_fill with time 4320, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.75952580180944})
_fill with time 4380, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.78352580180944})
_fill with time 4440, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.80752580180944})
_fill with time 4500, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.83152580180944})
_fill with time 4560, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.85552580180944})
_fill with time 4620, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.87952580180944})
_fill with time 4680, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.903525801809444})
_fill with time 4740, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.927525801809445})
_fill with time 4800, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.951525801809446})
_fill with time 4860, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.97552580180945})
_fill with time 4920, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 47.99952580180945})
_fill with time 4980, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.02352580180945})
_fill with time 5040, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.04752580180945})
_fill with time 5100, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.07152580180945})
_fill with time 5160, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.09552580180945})
_fill with time 5220, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.11952580180945})
_fill with time 5280, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.14352580180945})
_fill with time 5340, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.167525801809454})
_fill with time 5400, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.191525801809455})
_fill with time 5460, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.215525801809456})
_fill with time 5520, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.23952580180946})
_fill with time 5580, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.26352580180946})
_fill with time 5640, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.28752580180946})
_fill with time 5700, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.31152580180946})
_fill with time 5760, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.33552580180946})
_fill with time 5820, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.35952580180946})
_fill with time 5880, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.38352580180946})
_fill with time 5940, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.40752580180946})
_fill with time 6000, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.431525801809464})
_fill with time 6060, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.455525801809465})
_fill with time 6120, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.479525801809466})
_fill with time 6180, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.50352580180947})
_fill with time 6240, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.52752580180947})
_fill with time 6300, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.55152580180947})
_fill with time 6360, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.57552580180947})
_fill with time 6420, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.59952580180947})
_fill with time 6480, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.62352580180947})
_fill with time 6540, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.64752580180947})
_fill with time 6600, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.67152580180947})
_fill with time 6660, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.695525801809474})
_fill with time 6720, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.719525801809475})
_fill with time 6780, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.743525801809476})
_fill with time 6840, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.76752580180948})
_fill with time 6900, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.791189306414736})
_fill with time 6960, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.81439847173157})
_fill with time 7020, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.83716202107433})
_fill with time 7080, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.8594885102697})
_fill with time 7140, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.88138633087252})
_fill with time 7200, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.90286371331977})
_fill with time 7260, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.92392873002403})
_fill with time 7320, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.94458929840757})
_fill with time 7380, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.96485318387815})
_fill with time 7440, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 48.98472800274769})
_fill with time 7500, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.00422122509493})
_fill with time 7560, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.02334017757311})
_fill with time 7620, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.04209204616371})
_fill with time 7680, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.06048387887736})
_fill with time 7740, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.07852258840292})
_fill with time 7800, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.09621495470558})
_fill with time 7860, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.113567627575236})
_fill with time 7920, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.13058712912579})
_fill with time 7980, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.14727985624658})
_fill with time 8040, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.163652083006646})
_fill with time 8100, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.17970996301292})
_fill with time 8160, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.19545953172307})
_fill with time 8220, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.210906708713985})
_fill with time 8280, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.22605729990668})
_fill with time 8340, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.24091699974847})
_fill with time 8400, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.2554913933533})
_fill with time 8460, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.26978595860092})
_fill with time 8520, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.28380606819578})
_fill with time 8580, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.29755699168642})
_fill with time 8640, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.31104389744604})
_fill with time 8700, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.32427185461508})
_fill with time 8760, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.33724583500647})
_fill with time 8820, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.34997071497435})
_fill with time 8880, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.36245127724684})
_fill with time 8940, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.3746922127237})
_fill with time 9000, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.38669812223941})
_fill with time 9060, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.39847351829241})
_fill with time 9120, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.4100228267412})
_fill with time 9180, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.421350388467765})
_fill with time 9240, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.432460461009185})
_fill with time 9300, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.44335722015781})
_fill with time 9360, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.454044761530774})
_fill with time 9420, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.464527102109386})
_fill with time 9480, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.474808181748884})
_fill with time 9540, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.4848918646593})
_fill with time 9600, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.49478194085785})
_fill with time 9660, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.50448212759338})
_fill with time 9720, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.51399607074359})
_fill with time 9780, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.52332734618531})
_fill with time 9840, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.53247946113856})
_fill with time 9900, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.5414558554847})
_fill with time 9960, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.55025990305939})
_fill with time 10020, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.55889491292065})
_fill with time 10080, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.56736413059257})
_fill with time 10140, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.575670739285194})
_fill with time 10200, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.58381786109092})
_fill with time 10260, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.59180855815798})
_fill with time 10320, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.59964583384134})
_fill with time 10380, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.60733263383159})
_fill with time 10440, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.61487184726202})
_fill with time 10500, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.62226630779459})
_fill with time 10560, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.629518794684934})
_fill with time 10620, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.636632033826984})
_fill with time 10680, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.643608698777506})
_fill with time 10740, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.650451411760976})
_fill with time 10800, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.65716274465517})
_fill with time 10860, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.66374521995779})
_fill with time 10920, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.6702013117346})
_fill with time 10980, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.6765334465493})
_fill with time 11040, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.68274400437555})
_fill with time 11100, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.68883531949154})
_fill with time 11160, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.6948096813573})
_fill with time 11220, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.70066933547524})
_fill with time 11280, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.70641648423412})
_fill with time 11340, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.71205328773682})
_fill with time 11400, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.71758186461228})
_fill with time 11460, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.72300429281172})
_fill with time 11520, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.72832261038974})
_fill with time 11580, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.733538816270254})
_fill with time 11640, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.73865487099786})
_fill with time 11700, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.7436726974747})
_fill with time 11760, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.74859418168319})
_fill with time 11820, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.75342117339487})
_fill with time 11880, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.75815548686569})
_fill with time 11940, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.76279890151787})
_fill with time 12000, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.76735316260873})
_fill with time 12060, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.771819981886644})
_fill with time 12120, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.77620103823442})
_fill with time 12180, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.78049797830032})
_fill with time 12240, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.78471241711695})
_fill with time 12300, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.788845938708306})
_fill with time 12360, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.79290009668511})
_fill with time 12420, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.79687641482875})
_fill with time 12480, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.80077638766404})
_fill with time 12540, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.80460148102089})
_fill with time 12600, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.80835313258529})
_fill with time 12660, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.81203275243965})
_fill with time 12720, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.815641723592805})
_fill with time 12780, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.81918140249982})
_fill with time 12840, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.822653119571825})
_fill with time 12900, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.826058179676046})
_fill with time 12960, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.829397862626266})
_fill with time 13020, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.83267342366384})
_fill with time 13080, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.835886093929496})
_fill with time 13140, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.83903708092605})
_fill with time 13200, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.842127568972266})
_fill with time 13260, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.845158719648})
_fill with time 13320, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.84813167223076})
_fill with time 13380, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.85104754412393})
_fill with time 13440, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.85390743127675})
_fill with time 13500, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.85671240859624})
_fill with time 13560, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.85946353035119})
_fill with time 13620, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.862161830568446})
_fill with time 13680, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.864808323421535})
_fill with time 13740, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.86740400361184})
_fill with time 13800, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.86994984674249})
_fill with time 13860, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.87244680968504})
_fill with time 13920, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.87489583093908})
_fill with time 13980, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.87729783098505})
_fill with time 14040, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.87965371263014})
_fill with time 14100, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.88196436134764})
_fill with time 14160, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.88423064560977})
_fill with time 14220, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.886453417214064})
_fill with time 14280, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.88863351160355})
_fill with time 14340, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.89077174818076})
_fill with time 14400, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.89286893061569})
_fill with time 14460, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.89492584714787})
_fill with time 14520, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.89694327088263})
_fill with time 14580, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.89892196008169})
_fill with time 14640, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.90086265844812})
_fill with time 14640, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.90086265844812})
_fill with time 14640, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.90086265844812})
_fill with time 14640, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.90086265844812})
_fill with time 14640, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.90086265844812})
_fill with time 14640, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.90086265844812})
_fill with time 14640, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.90086265844812})
_fill with time 14640, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.90086265844812})
_fill with time 14640, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.90086265844812})
_fill with time 14640, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.90086265844812})
_fill with time 14640, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.90086265844812})
_fill with time 14640, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.90086265844812})
_fill with time 14640, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.90086265844812})
_fill with time 14640, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.90086265844812})

...

_fill with time 14640, charge immutables.Map({<EnergyType.ELECTRIC: 'kilowatt_hours'>: 49.90086265844812})
  4%|█████▉                                                                                                                                        | 60/1439 [00:01<00:30, 45.40it/s]
Traceback (most recent call last):
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/bin/hive-aumc", line 8, in <module>
    sys.exit(run())
  File "/Users/rfitzger/dev/nrel/hive/hive-extensions/hive-aumc/hive_aumc/run_aumc.py", line 201, in run
    rp, charge_events = advance_hive(rp, t, priorities)
  File "/Users/rfitzger/dev/nrel/hive/hive-extensions/hive-aumc/hive_aumc/run_aumc.py", line 173, in advance_hive
    result = hc.crank(rp, steps, progress_bar, flush_events)
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/app/hive_cosim.py", line 73, in crank
    next_state = ft.reduce(run_step, steps, initial)
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/app/hive_cosim.py", line 66, in run_step
    rp1 = rp0.u.apply_update(rp0)
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/state/simulation_state/update/update.py", line 83, in apply_update
    updated_sim, updated_step_fn = self.step_update.update(
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/state/simulation_state/update/step_simulation.py", line 95, in update
    i_stack, updated_i_gens = generate_instructions(
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/dispatcher/instruction_generator/instruction_generator_ops.py", line 128, in generate_instructions
    driver_result = result.add_driver_instructions(simulation_state, environment)
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/dispatcher/instruction_generator/instruction_generator_ops.py", line 79, in add_driver_instructions
    new_instructions = ft.reduce(
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/dispatcher/instruction_generator/instruction_generator_ops.py", line 81, in <lambda>
    v.driver_state.generate_instruction(
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/state/driver_state/human_driver_state/human_driver_state.py", line 240, in generate_instruction
    return human_go_home(my_vehicle, my_base, sim, env)
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/state/driver_state/driver_instruction_ops.py", line 120, in human_go_home
    charge_instructions = instruct_vehicles_to_dispatch_to_station(
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/dispatcher/instruction_generator/instruction_generator_ops.py", line 210, in instruct_vehicles_to_dispatch_to_station
    nearest_station = H3Ops.nearest_entity(
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/util/h3_ops.py", line 121, in nearest_entity
    return _search()
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/util/h3_ops.py", line 109, in _search
    dist_km = distance_function(entity)
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/dispatcher/instruction_generator/assignment_ops.py", line 231, in fn
    result = shortest_time_to_charge_ranking(sim, env, vehicle, station, target_soc)
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/dispatcher/instruction_generator/assignment_ops.py", line 385, in shortest_time_to_charge_ranking
    this_vehicle_charge_time = powercurve_ops.time_to_full(
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/model/vehicle/mechatronics/powercurve/powercurve_ops.py", line 36, in time_to_full
    time_charged = _fill(vehicle)
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/model/vehicle/mechatronics/powercurve/powercurve_ops.py", line 34, in _fill
    return _fill(updated_veh, updated_time_charged_acc)
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/model/vehicle/mechatronics/powercurve/powercurve_ops.py", line 34, in _fill
    return _fill(updated_veh, updated_time_charged_acc)
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/model/vehicle/mechatronics/powercurve/powercurve_ops.py", line 34, in _fill
    return _fill(updated_veh, updated_time_charged_acc)
  [Previous line repeated 971 more times]
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/model/vehicle/mechatronics/powercurve/powercurve_ops.py", line 29, in _fill
    updated_veh, time_delta = mechatronics.add_energy(
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/model/vehicle/mechatronics/bev.py", line 235, in add_energy
    updated_vehicle = vehicle.modify_energy(
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/site-packages/nrel/hive/model/vehicle/vehicle.py", line 154, in modify_energy
    return replace(self, energy=energy)
  File "/Users/rfitzger/anaconda3/envs/hive-extensions/lib/python3.9/dataclasses.py", line 1284, in replace
    return obj.__class__(**changes)
  File "<string>", line 3, in __init__
RecursionError: maximum recursion depth exceeded while calling a Python object
clintonsteiner commented 1 year ago

Hey @robfitzgerald would you mind helping me write a test to illustrate this behavior? Then I think we could write something to solve this

Wondering if we could skip the iteration while the delta it creates is below a certain percentage, then if its due to grid throttle, when the grid is unthrottled it would resume charging? Or in the case of the battery being full, it would just continue to skip

robfitzgerald commented 1 year ago

Wondering if we could skip the iteration while the delta it creates is below a certain percentage

i'm not sure if a skip makes sense here, because then at the i+1th iteration, it would just "try again" with nothing else changed. i like the idea of a practical threshold of some very low increment, but, as stated above, we also can capture this effect by comparing the mechatronics.fuel_source_soc(charging_vehicle) to the mechatronics.fuel_source_soc(updated_veh). if it's equal, we break.

as for reproducing it, the problem here is a DC fast charger, BEV mechatronics, default powercurve, with a 100% battery charge target. that should produce this result:

from nrel.hive.resources.mock_lobster import *
from nrel.hive.model.vehicle.mechatronics.powercurve.powercurve_ops import time_to_full
bev = mock_bev(battery_capacity_kwh=50)
vehicle = mock_vehicle(soc=0.99)  # ...almost full
charger = mock_dcfc_charger()
# should throw stack overflow:
time_to_full(vehicle, bev, charger, target_soc=1.0, sim_timestep_duration_seconds=60)

edit: yuppers:

>>> time_to_full(vehicle, bev, charger, target_soc=1.0, sim_timestep_duration_seconds=60)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/rfitzger/dev/nrel/hive/hive/nrel/hive/model/vehicle/mechatronics/powercurve/powercurve_ops.py", line 35, in time_to_full
    time_charged = _fill(vehicle)
  File "/Users/rfitzger/dev/nrel/hive/hive/nrel/hive/model/vehicle/mechatronics/powercurve/powercurve_ops.py", line 33, in _fill
    return _fill(updated_veh, updated_time_charged_acc)
  File "/Users/rfitzger/dev/nrel/hive/hive/nrel/hive/model/vehicle/mechatronics/powercurve/powercurve_ops.py", line 33, in _fill
    return _fill(updated_veh, updated_time_charged_acc)
  File "/Users/rfitzger/dev/nrel/hive/hive/nrel/hive/model/vehicle/mechatronics/powercurve/powercurve_ops.py", line 33, in _fill
    return _fill(updated_veh, updated_time_charged_acc)
  [Previous line repeated 989 more times]
  File "/Users/rfitzger/dev/nrel/hive/hive/nrel/hive/model/vehicle/mechatronics/powercurve/powercurve_ops.py", line 29, in _fill
    updated_veh, time_delta = mechatronics.add_energy(
  File "/Users/rfitzger/dev/nrel/hive/hive/nrel/hive/model/vehicle/mechatronics/bev.py", line 235, in add_energy
    updated_vehicle = vehicle.modify_energy(
  File "/Users/rfitzger/dev/nrel/hive/hive/nrel/hive/model/vehicle/vehicle.py", line 154, in modify_energy
    return replace(self, energy=energy)
  File "/Users/rfitzger/anaconda3/envs/hive/lib/python3.9/dataclasses.py", line 1284, in replace
    return obj.__class__(**changes)
  File "<string>", line 3, in __init__
RecursionError: maximum recursion depth exceeded while calling a Python object
clintonsteiner commented 1 year ago

Thanks for the response, in my head I would assume we would need to continue evaluating if its charging or not indefinitely to get around the grid throttle scenario.

How would you feel about breaking if we are within some percent of full and the delta change is low enough?

robfitzgerald commented 1 year ago

in my head I would assume we would need to continue evaluating if its charging or not indefinitely to get around the grid throttle scenario

that's an interesting point. at first my answer was going to be "this function is used in estimating the expected duration of a charging session during charging session dispatch where we have more relaxed correctness requirements". but, you're right, assuming the grid was throttled 100% for 10 minutes, we want the logic to be correct for both dispatch and the underlying simulation physics. thanks for the suggestion.

do you think we have gotten around the stack overflow issue? we could replace the iteration from recursion to using a fold (ft.reduce) to protect against very long grid blackouts.

sorry for the delay, i was in a meeting 🕴️

robfitzgerald commented 1 year ago

as for what to use for the threshold; that may require some empirical playing-around-with the arguments to Mechatronics.add_energy (or, a PhD in Python floating point number behaviors).

ideally we also add this to the config, perhaps in the DispatcherConfig class, with your sensible default value. though i can imagine we may need to add similar logic to the vehicle_state_ops.charge method. @nreinicke should this go in Sim instead for that reason?

clintonsteiner commented 1 year ago

I don't have a ton of experience with ft.reduce but I think switching out of the recursion model for this is going to be required or else we will keep hitting this recursion depth limit

nreinicke commented 1 year ago

should this go in Sim instead for that reason?

yeah I think that makes sense.

The grid throttle in an interesting edge case I hadn't considered. This is tricky since that would assume we have perfect knowledge of when the grid might throttle but this is really meant to be an estimate since we probably won't have perfect knowledge of the grid.

robfitzgerald commented 1 year ago

switching out of the recursion model for this is going to be required or else we will keep hitting this recursion depth limit

yea. and actually, to make this a fold/reduce, we would need to make an overly-complex iterator that encapsulates the stopping condition. i think it's just not pythonic to do this without side effects, such as a while loop, which is a bummer, but stack overflows are an even bigger bummer, so maybe it should be a while loop.

clintonsteiner commented 1 year ago

Opened a pull request here: https://github.com/NREL/hive/pull/173 Seems like it is working ok and iterates a couple hundred times before giving up.

The grid throttle scenario will certainly affect this logic though, might want to put that in assumptions Happy to do it if you point me in the right direction

robfitzgerald commented 1 year ago

added the comment on your PR that we know the maximum time to test, it's always (sim.end_time - sim.sim_time) / sim.timestep_duration_seconds. that could be piped in from the call site of this function, which at least prevents an infinite loop (since end_time is finite in HIVE)

nreinicke commented 1 year ago

Closed by #173