@@ -37,15 +37,15 @@ class BaseSolver(object):
37
37
The tolerance to assert whether extrapolation occurs or not. Default is 0.
38
38
sensitivity : str, optional
39
39
Whether (and how) to calculate sensitivities when solving. Options are:
40
- - None (default): user must give the names of input parameters to calculate
41
- sensitivity via the "solve" method, the individual solver is responsible for
40
+ - None (default): the individual solver is responsible for
42
41
calculating the sensitivity wrt these parameters, and providing the result in
43
42
the solution instance returned. At the moment this is only implemented for the
44
43
IDAKLU solver.\
45
- - "explicit forward": explicitly formulate the sensitivity equations for *all*
46
- the input parameters. The formulation is as per "Park, S., Kato, D., Gima, Z., \
47
- Klein, R., & Moura, S. (2018). Optimal experimental design for parameterization\
48
- of an electrochemical lithium-ion battery model. Journal of The Electrochemical\
44
+ - "explicit forward": explicitly formulate the sensitivity equations for
45
+ the chosen input parameters. The formulation is as per
46
+ "Park, S., Kato, D., Gima, Z., Klein, R., & Moura, S. (2018).\
47
+ Optimal experimental design for parameterization of an electrochemical
48
+ lithium-ion battery model. Journal of The Electrochemical\
49
49
Society, 165(7), A1309.". See #1100 for details. At the moment this is only
50
50
implemented using convert_to_format = 'casadi'. \
51
51
- see individual solvers for other options
@@ -140,6 +140,8 @@ def copy(self):
140
140
new_solver .models_set_up = {}
141
141
return new_solver
142
142
143
+
144
+
143
145
def set_up (self , model , inputs = None , t_eval = None ,
144
146
calculate_sensitivites = False ):
145
147
"""Unpack model, perform checks, and calculate jacobian.
@@ -236,6 +238,9 @@ def set_up(self, model, inputs=None, t_eval=None,
236
238
calculate_sensitivites = [p for p in inputs .keys ()]
237
239
else :
238
240
calculate_sensitivites = []
241
+ # save sensitivity parameters so we can identify them later on
242
+ # (FYI: this is used in the Solution class)
243
+ model .calculate_sensitivities = calculate_sensitivites
239
244
240
245
# Only allow solving explicit sensitivity equations with the casadi format for now
241
246
if (
@@ -269,8 +274,13 @@ def set_up(self, model, inputs=None, t_eval=None,
269
274
p_casadi_stacked = casadi .vertcat (* [p for p in p_casadi .values ()])
270
275
# sensitivity vectors
271
276
if self .sensitivity == "explicit forward" :
272
- S_x = casadi .MX .sym ("S_x" , model .len_rhs * p_casadi_stacked .shape [0 ])
273
- S_z = casadi .MX .sym ("S_z" , model .len_alg * p_casadi_stacked .shape [0 ])
277
+ pS_casadi_stacked = casadi .vertcat (
278
+ * [p_casadi [name ] for name in calculate_sensitivites ]
279
+ )
280
+ model .len_rhs_sens = model .len_rhs * pS_casadi_stacked .shape [0 ]
281
+ model .len_alg_sens = model .len_alg * pS_casadi_stacked .shape [0 ]
282
+ S_x = casadi .MX .sym ("S_x" , model .len_rhs_sens )
283
+ S_z = casadi .MX .sym ("S_z" , model .len_alg_sens )
274
284
y_and_S = casadi .vertcat (y_diff , S_x , y_alg , S_z )
275
285
else :
276
286
y_and_S = y_casadi
@@ -356,16 +366,16 @@ def jacp(*args, **kwargs):
356
366
if name == "rhs" and model .len_rhs > 0 :
357
367
report ("Creating sensitivity equations for rhs using CasADi" )
358
368
df_dx = casadi .jacobian (func , y_diff )
359
- df_dp = casadi .jacobian (func , p_casadi_stacked )
369
+ df_dp = casadi .jacobian (func , pS_casadi_stacked )
360
370
S_x_mat = S_x .reshape (
361
- (model .len_rhs , p_casadi_stacked .shape [0 ])
371
+ (model .len_rhs , pS_casadi_stacked .shape [0 ])
362
372
)
363
373
if model .len_alg == 0 :
364
374
S_rhs = (df_dx @ S_x_mat + df_dp ).reshape ((- 1 , 1 ))
365
375
else :
366
376
df_dz = casadi .jacobian (func , y_alg )
367
377
S_z_mat = S_z .reshape (
368
- (model .len_alg , p_casadi_stacked .shape [0 ])
378
+ (model .len_alg , pS_casadi_stacked .shape [0 ])
369
379
)
370
380
S_rhs = (df_dx @ S_x_mat + df_dz @ S_z_mat + df_dp ).reshape (
371
381
(- 1 , 1 )
@@ -376,34 +386,34 @@ def jacp(*args, **kwargs):
376
386
"Creating sensitivity equations for algebraic using CasADi"
377
387
)
378
388
dg_dz = casadi .jacobian (func , y_alg )
379
- dg_dp = casadi .jacobian (func , p_casadi_stacked )
389
+ dg_dp = casadi .jacobian (func , pS_casadi_stacked )
380
390
S_z_mat = S_z .reshape (
381
- (model .len_alg , p_casadi_stacked .shape [0 ])
391
+ (model .len_alg , pS_casadi_stacked .shape [0 ])
382
392
)
383
393
if model .len_rhs == 0 :
384
394
S_alg = (dg_dz @ S_z_mat + dg_dp ).reshape ((- 1 , 1 ))
385
395
else :
386
396
dg_dx = casadi .jacobian (func , y_diff )
387
397
S_x_mat = S_x .reshape (
388
- (model .len_rhs , p_casadi_stacked .shape [0 ])
398
+ (model .len_rhs , pS_casadi_stacked .shape [0 ])
389
399
)
390
400
S_alg = (dg_dx @ S_x_mat + dg_dz @ S_z_mat + dg_dp ).reshape (
391
401
(- 1 , 1 )
392
402
)
393
403
func = casadi .vertcat (func , S_alg )
394
404
elif name == "initial_conditions" :
395
405
if model .len_rhs == 0 or model .len_alg == 0 :
396
- S_0 = casadi .jacobian (func , p_casadi_stacked ).reshape (
406
+ S_0 = casadi .jacobian (func , pS_casadi_stacked ).reshape (
397
407
(- 1 , 1 )
398
408
)
399
409
func = casadi .vertcat (func , S_0 )
400
410
else :
401
411
x0 = func [: model .len_rhs ]
402
412
z0 = func [model .len_rhs :]
403
- Sx_0 = casadi .jacobian (x0 , p_casadi_stacked ).reshape (
413
+ Sx_0 = casadi .jacobian (x0 , pS_casadi_stacked ).reshape (
404
414
(- 1 , 1 )
405
415
)
406
- Sz_0 = casadi .jacobian (z0 , p_casadi_stacked ).reshape (
416
+ Sz_0 = casadi .jacobian (z0 , pS_casadi_stacked ).reshape (
407
417
(- 1 , 1 )
408
418
)
409
419
func = casadi .vertcat (x0 , Sx_0 , z0 , Sz_0 )
@@ -416,7 +426,7 @@ def jacp(*args, **kwargs):
416
426
else :
417
427
jac = None
418
428
419
- if calculate_sensitivites :
429
+ if calculate_sensitivites and self . sensitivity != "explicit forward" :
420
430
report ((
421
431
f"Calculating sensitivities for { name } with respect "
422
432
f"to parameters { calculate_sensitivites } using CasADi"
@@ -529,12 +539,11 @@ def jacp(*args, **kwargs):
529
539
init_eval = InitialConditions (initial_conditions , model )
530
540
531
541
if self .sensitivity == "explicit forward" :
532
- init_eval .y_dummy = np .zeros (
533
- (
534
- model .len_rhs_and_alg * (np .vstack (list (inputs .values ())).size + 1 ),
535
- 1 ,
536
- )
542
+ y0_total_size = (
543
+ model .len_rhs + model .len_rhs_sens
544
+ + model .len_alg + model .len_alg_sens
537
545
)
546
+ init_eval .y_dummy = np .zeros ((y0_total_size , 1 ))
538
547
else :
539
548
init_eval .y_dummy = np .zeros ((model .len_rhs_and_alg , 1 ))
540
549
@@ -546,6 +555,7 @@ def jacp(*args, **kwargs):
546
555
547
556
# Calculate initial conditions
548
557
model .y0 = init_eval (inputs )
558
+ print ('YYYYY' , model .y0 )
549
559
550
560
casadi_terminate_events = []
551
561
terminate_events_eval = []
@@ -710,6 +720,7 @@ def _set_initial_conditions(self, model, inputs, update_rhs):
710
720
model .y0 = casadi .Function ("y0" , [symbolic_inputs ], [y0 ])
711
721
else :
712
722
model .y0 = y0
723
+ print ('ASDF' , model .y0 )
713
724
714
725
def calculate_consistent_state (self , model , time = 0 , inputs = None ):
715
726
"""
@@ -736,13 +747,15 @@ def calculate_consistent_state(self, model, time=0, inputs=None):
736
747
if self .root_method is None :
737
748
return model .y0
738
749
try :
739
- root_sol = self .root_method ._integrate (model , [time ], inputs )
750
+ root_sol = self .root_method ._integrate (model , np . array ( [time ]) , inputs )
740
751
except pybamm .SolverError as e :
741
752
raise pybamm .SolverError (
742
753
"Could not find consistent states: {}" .format (e .args [0 ])
743
754
)
744
755
pybamm .logger .debug ("Found consistent states" )
745
- y0 = root_sol .all_ys [0 ]
756
+
757
+ # use all_ys_and_sens in case we are solving the full sensitivity equations
758
+ y0 = root_sol .all_ys_and_sens [0 ]
746
759
if isinstance (y0 , np .ndarray ):
747
760
y0 = y0 .flatten ()
748
761
return y0
@@ -1428,7 +1441,7 @@ def __call__(self, t, y, inputs):
1428
1441
self .name , self .model .name , t * self .timescale
1429
1442
)
1430
1443
)
1431
- if self .name in ["RHS" , "algebraic" , "residuals" ]:
1444
+ if self .name in ["RHS" , "algebraic" , "residuals" , "event" ]:
1432
1445
1433
1446
return self .function (t , y , inputs ).flatten ()
1434
1447
else :
@@ -1437,7 +1450,7 @@ def __call__(self, t, y, inputs):
1437
1450
def function (self , t , y , inputs ):
1438
1451
if self .form == "casadi" :
1439
1452
states_eval = self ._function (t , y , inputs )
1440
- if self .name in ["rhs " , "algebraic" , "residuals" , "event" ]:
1453
+ if self .name in ["RHS " , "algebraic" , "residuals" , "event" ]:
1441
1454
return states_eval .full ()
1442
1455
else :
1443
1456
# keep jacobians sparse
0 commit comments