8
8
import numpy as np
9
9
import sys
10
10
import itertools
11
+ import warnings
11
12
12
13
13
14
class BaseSolver (object ):
@@ -30,6 +31,8 @@ class BaseSolver(object):
30
31
specified by 'root_method' (e.g. "lm", "hybr", ...)
31
32
root_tol : float, optional
32
33
The tolerance for the initial-condition solver (default is 1e-6).
34
+ extrap_tol : float, optional
35
+ The tolerance to assert whether extrapolation occurs or not. Default is 0.
33
36
"""
34
37
35
38
def __init__ (
@@ -39,13 +42,15 @@ def __init__(
39
42
atol = 1e-6 ,
40
43
root_method = None ,
41
44
root_tol = 1e-6 ,
45
+ extrap_tol = 0 ,
42
46
max_steps = "deprecated" ,
43
47
):
44
48
self ._method = method
45
49
self ._rtol = rtol
46
50
self ._atol = atol
47
51
self .root_tol = root_tol
48
52
self .root_method = root_method
53
+ self .extrap_tol = extrap_tol
49
54
if max_steps != "deprecated" :
50
55
raise ValueError (
51
56
"max_steps has been deprecated, and should be set using the "
@@ -361,6 +366,12 @@ def report(string):
361
366
if event .event_type == pybamm .EventType .TERMINATION
362
367
]
363
368
369
+ interpolant_extrapolation_events_eval = [
370
+ process (event .expression , "event" , use_jacobian = False )[1 ]
371
+ for event in model .events
372
+ if event .event_type == pybamm .EventType .INTERPOLANT_EXTRAPOLATION
373
+ ]
374
+
364
375
# discontinuity events are evaluated before the solver is called, so don't need
365
376
# to process them
366
377
discontinuity_events_eval = [
@@ -376,6 +387,9 @@ def report(string):
376
387
model .jac_algebraic_eval = jac_algebraic
377
388
model .terminate_events_eval = terminate_events_eval
378
389
model .discontinuity_events_eval = discontinuity_events_eval
390
+ model .interpolant_extrapolation_events_eval = (
391
+ interpolant_extrapolation_events_eval
392
+ )
379
393
380
394
# Calculate initial conditions
381
395
model .y0 = init_eval (inputs )
@@ -697,6 +711,16 @@ def solve(
697
711
solution .timescale_eval = model .timescale_eval
698
712
solution .length_scales_eval = model .length_scales_eval
699
713
714
+ # Check if extrapolation occurred
715
+ extrapolation = self .check_extrapolation (solution , model .events )
716
+ if extrapolation :
717
+ warnings .warn (
718
+ "While solving {} extrapolation occurred for {}" .format (
719
+ model .name , extrapolation
720
+ ),
721
+ pybamm .SolverWarning ,
722
+ )
723
+
700
724
# Identify the event that caused termination
701
725
termination = self .get_termination_reason (solution , model .events )
702
726
@@ -852,6 +876,16 @@ def step(
852
876
solution .timescale_eval = temp_timescale_eval
853
877
solution .length_scales_eval = temp_length_scales_eval
854
878
879
+ # Check if extrapolation occurred
880
+ extrapolation = self .check_extrapolation (solution , model .events )
881
+ if extrapolation :
882
+ warnings .warn (
883
+ "While solving {} extrapolation occurred for {}" .format (
884
+ model .name , extrapolation
885
+ ),
886
+ pybamm .SolverWarning ,
887
+ )
888
+
855
889
# Identify the event that caused termination
856
890
termination = self .get_termination_reason (solution , model .events )
857
891
@@ -921,6 +955,48 @@ def get_termination_reason(self, solution, events):
921
955
922
956
return "the termination event '{}' occurred" .format (termination_event )
923
957
958
+ def check_extrapolation (self , solution , events ):
959
+ """
960
+ Check if extrapolation occurred for any of the interpolants. Note that with the
961
+ current approach (evaluating all the events at the solution times) some
962
+ extrapolations might not be found if they only occurred for a small period of
963
+ time.
964
+
965
+ Parameters
966
+ ----------
967
+ solution : :class:`pybamm.Solution`
968
+ The solution object
969
+ events : dict
970
+ Dictionary of events
971
+ """
972
+ extrap_events = {}
973
+
974
+ for event in events :
975
+ if event .event_type == pybamm .EventType .INTERPOLANT_EXTRAPOLATION :
976
+ extrap_events [event .name ] = False
977
+
978
+ try :
979
+ y_full = solution .y .full ()
980
+ except AttributeError :
981
+ y_full = solution .y
982
+
983
+ for event in events :
984
+ if event .event_type == pybamm .EventType .INTERPOLANT_EXTRAPOLATION :
985
+ if (
986
+ event .expression .evaluate (
987
+ solution .t ,
988
+ y_full ,
989
+ inputs = {k : v for k , v in solution .inputs .items ()},
990
+ )
991
+ < self .extrap_tol
992
+ ).any ():
993
+ extrap_events [event .name ] = True
994
+
995
+ # Add the event dictionaryto the solution object
996
+ solution .extrap_events = extrap_events
997
+
998
+ return [k for k , v in extrap_events .items () if v ]
999
+
924
1000
def _set_up_ext_and_inputs (self , model , external_variables , inputs ):
925
1001
"Set up external variables and input parameters"
926
1002
inputs = inputs or {}
0 commit comments