Skip to content

Commit 073eb58

Browse files
committed
#1477 integration tests work ok, pretty poor accuracy (perhaps on fd?)
1 parent 241af9a commit 073eb58

File tree

6 files changed

+61
-61
lines changed

6 files changed

+61
-61
lines changed

pybamm/solvers/idaklu_solver.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -321,10 +321,11 @@ def sensfn(resvalS, t, y, yp, yS, ypS):
321321
number_of_states = y0.size
322322
y_out = sol.y.reshape((number_of_timesteps, number_of_states))
323323

324-
# return solution, we need to tranpose y to match scipy's interface
324+
# return sensitivity solution, we need to flatten yS to
325+
# (#timesteps * #states,) to match format used by Solution
325326
if number_of_sensitivity_parameters != 0:
326327
yS_out = {
327-
name: sol.yS[i].transpose() for i, name in enumerate(sens0.keys())
328+
name: sol.yS[i].reshape(-1, 1) for i, name in enumerate(sens0.keys())
328329
}
329330
else:
330331
yS_out = False

pybamm/solvers/solution.py

+14-33
Original file line numberDiff line numberDiff line change
@@ -81,23 +81,10 @@ def __init__(
8181
else:
8282
self.all_inputs = all_inputs
8383

84-
# sensitivities
85-
if isinstance(sensitivities, bool):
86-
self._sensitivities = {}
87-
# if solution consists of explicit sensitivity equations, extract them
88-
if (
89-
sensitivities is True
90-
and all_models[0] is not None
91-
and not isinstance(all_ys[0], casadi.Function)
92-
and all_models[0].len_rhs_and_alg != all_ys[0].shape[0]
93-
and all_models[0].len_rhs_and_alg != 0 # for the dummy solver
94-
):
95-
self.extract_explicit_sensitivities()
96-
97-
elif isinstance(sensitivities, dict):
98-
self._sensitivities = sensitivities
99-
else:
84+
# sensitivities must be a dict or bool
85+
if not isinstance(sensitivities, (bool, dict)):
10086
raise TypeError('sensitivities arg needs to be a bool or dict')
87+
self._sensitivities = sensitivities
10188

10289
self._t_event = t_event
10390
self._y_event = y_event
@@ -148,23 +135,10 @@ def __init__(
148135
pybamm.citations.register("Andersson2019")
149136

150137
def extract_explicit_sensitivities(self):
151-
for index, (model, ys, ts, inputs) in enumerate(
152-
zip(self.all_models, self.all_ys, self.all_ts,
153-
self.all_inputs)
154-
):
155-
# TODO: only support sensitivities for one solution atm
156-
# but make sure that sensitivities are removed for all
157-
# solutions
158-
if index == 0:
159-
self._all_ys[index], self._sensitivities = \
160-
self._extract_explicit_sensitivities(
161-
model, ys, ts, inputs
162-
)
163-
else:
164-
self._all_ys[index], _ = \
165-
self._extract_explicit_sensitivities(
166-
model, ys, ts, inputs
167-
)
138+
self._y, self._sensitivities = \
139+
self._extract_explicit_sensitivities(
140+
self.all_models[0], self.y, self.t, self.all_inputs[0]
141+
)
168142

169143
def _extract_explicit_sensitivities(self, model, y, t_eval, inputs):
170144
"""
@@ -277,11 +251,18 @@ def y(self):
277251
return self._y
278252
except AttributeError:
279253
self.set_y()
254+
255+
# if y is evaluated before sensitivities then need to extract them
256+
if isinstance(self._sensitivities, bool) and self._sensitivities:
257+
self.extract_explicit_sensitivities()
258+
280259
return self._y
281260

282261
@property
283262
def sensitivities(self):
284263
"""Values of the sensitivities. Returns a dict of param_name: np_array"""
264+
if isinstance(self._sensitivities, bool) and self._sensitivities:
265+
self.extract_explicit_sensitivities()
285266
return self._sensitivities
286267

287268
def set_y(self):

tests/integration/test_models/standard_model_tests.py

+26-22
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ def test_solving(self, solver=None, t_eval=None, inputs=None,
8383

8484
self.solution = self.solver.solve(
8585
self.model, t_eval, inputs=inputs,
86-
calculate_sensitivities=calculate_sensitivities
8786
)
8887

8988
def test_outputs(self):
@@ -93,40 +92,45 @@ def test_outputs(self):
9392
)
9493
std_out_test.test_all()
9594

96-
def test_sensitivities(self):
97-
param_name = "Negative electrode conductivity [S.m-1]"
98-
neg_electrode_cond = 100.0
95+
def test_sensitivities(self, param_name, param_value):
9996
self.parameter_values.update({param_name: "[input]"})
100-
inputs = {param_name: neg_electrode_cond}
97+
inputs = {param_name: param_value}
10198

10299
self.test_processing_parameters()
103100
self.test_processing_disc()
104101

105-
self.test_solving(inputs=inputs, calculate_sensitivities=True)
102+
# Use tighter default tolerances for testing
103+
self.solver.rtol = 1e-8
104+
self.solver.atol = 1e-8
105+
106+
Crate = abs(
107+
self.parameter_values["Current function [A]"]
108+
/ self.parameter_values["Nominal cell capacity [A.h]"]
109+
)
110+
t_eval = np.linspace(0, 3600 / Crate, 100)
111+
112+
self.solution = self.solver.solve(
113+
self.model, t_eval, inputs=inputs,
114+
calculate_sensitivities=True
115+
)
106116

107117
# check via finite differencing
108-
h = 1e-6
109-
inputs_plus = {param_name: neg_electrode_cond + 0.5 * h}
110-
inputs_neg = {param_name: neg_electrode_cond - 0.5 * h}
118+
h = 1e-6 * param_value
119+
inputs_plus = {param_name: (param_value + 0.5 * h)}
120+
inputs_neg = {param_name: (param_value - 0.5 * h)}
111121
sol_plus = self.solver.solve(
112-
self.model, self.solution.all_ts[0], inputs=inputs_plus
122+
self.model, t_eval, inputs=inputs_plus,
113123
)
114124
sol_neg = self.solver.solve(
115-
self.model, self.solution.all_ts[0], inputs=inputs_neg
125+
self.model, t_eval, inputs=inputs_neg
116126
)
117-
n = self.solution.sensitivities[param_name].shape[0]
118-
np.testing.assert_array_almost_equal(
119-
self.solution.sensitivities[param_name],
120-
((sol_plus.y - sol_neg.y) / h).reshape((n, 1))
127+
fd = ((np.array(sol_plus.y) - np.array(sol_neg.y)) / h)
128+
fd = fd.transpose().reshape(-1, 1)
129+
np.testing.assert_allclose(
130+
self.solution.sensitivities[param_name], fd,
131+
rtol=1e-1, atol=1e-5,
121132
)
122133

123-
if (
124-
isinstance(
125-
self.model, (pybamm.lithium_ion.BaseModel, pybamm.lead_acid.BaseModel)
126-
)
127-
):
128-
self.test_outputs()
129-
130134
def test_all(
131135
self, param=None, disc=None, solver=None, t_eval=None, skip_output_tests=False
132136
):

tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_dfn.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ def test_sensitivities(self):
3131
modeltest = tests.StandardModelTest(
3232
model, parameter_values=param, var_pts=var_pts
3333
)
34-
modeltest.test_sensitivities()
34+
modeltest.test_sensitivities(
35+
#'Separator thickness [m]', 2e-05,
36+
'Typical current [A]', 0.15652,
37+
)
3538

3639
def test_basic_processing_1plus1D(self):
3740
options = {"current collector": "potential pair", "dimensionality": 1}

tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_spm.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,18 @@ def test_basic_processing(self):
2020
def test_sensitivities(self):
2121
options = {"thermal": "isothermal"}
2222
model = pybamm.lithium_ion.SPM(options)
23-
# use Ecker parameters for nonlinear diffusion
2423
param = pybamm.ParameterValues(chemistry=pybamm.parameter_sets.Ecker2015)
2524
modeltest = tests.StandardModelTest(model, parameter_values=param)
26-
modeltest.test_sensitivities()
25+
modeltest.test_sensitivities(
26+
#"Negative electrode conductivity [S.m-1]", 14.0
27+
'Typical current [A]', 0.15652,
28+
#"Typical electrolyte concentration [mol.m-3]", 1000.0
29+
#''Negative electrode diffusivity [m2.s-1]', 1e-3,
30+
#'Negative electrode active material volume fraction', 0.372403,
31+
#'Separator thickness [m]', 2e-05,
32+
#'Negative electrode electrons in reaction', 1.0,
33+
#'Outer SEI open-circuit potential [V]', 0.8,
34+
)
2735

2836
def test_basic_processing_1plus1D(self):
2937
options = {"current collector": "potential pair", "dimensionality": 1}

tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_spme.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ def test_sensitivities(self):
2424
# use Ecker parameters for nonlinear diffusion
2525
param = pybamm.ParameterValues(chemistry=pybamm.parameter_sets.Ecker2015)
2626
modeltest = tests.StandardModelTest(model, parameter_values=param)
27-
modeltest.test_sensitivities()
27+
modeltest.test_sensitivities(
28+
#'Separator thickness [m]', 2e-05,
29+
'Typical current [A]', 0.15652,
30+
)
2831

2932
def test_basic_processing_python(self):
3033
options = {"thermal": "isothermal"}

0 commit comments

Comments
 (0)