Skip to content

Commit 8863c25

Browse files
Merge pull request #1026 from pybamm-team/issue-1024-plot-external-variables
#1024 fix external variables
2 parents 665f6c0 + 2eb29a0 commit 8863c25

File tree

9 files changed

+106
-18
lines changed

9 files changed

+106
-18
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727

2828
## Bug fixes
2929

30+
- Fix storing and plotting external variables in the solution ([#1026](https://github.com/pybamm-team/PyBaMM/pull/1026))
31+
- Fix running a simulation with a model that is already discretized ([#1025](https://github.com/pybamm-team/PyBaMM/pull/1025))
3032
- Fix CI not triggering for PR. ([#1013](https://github.com/pybamm-team/PyBaMM/pull/1013))
3133
- Fix schedule testing running too often. ([#1010](https://github.com/pybamm-team/PyBaMM/pull/1010))
3234
- Fix doctests failing due to mismatch in unsorted output.([#990](https://github.com/pybamm-team/PyBaMM/pull/990))

pybamm/expression_tree/variable.py

+2
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ def _base_evaluate(self, t=None, y=None, y_dot=None, inputs=None):
197197
)
198198
)
199199
else:
200+
if isinstance(out, np.ndarray) and out.ndim == 1:
201+
out = out[:, np.newaxis]
200202
return out
201203
# raise more informative error if can't find name in dict
202204
except KeyError:

pybamm/simulation.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,12 @@ def solve(
418418
t_eval = np.linspace(0, t_end, 100)
419419

420420
self.t_eval = t_eval
421-
self._solution = solver.solve(self.built_model, t_eval, inputs=inputs)
421+
self._solution = solver.solve(
422+
self.built_model,
423+
t_eval,
424+
external_variables=external_variables,
425+
inputs=inputs,
426+
)
422427

423428
elif self.operating_mode == "with experiment":
424429
if t_eval is not None:

pybamm/solvers/base_solver.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -775,7 +775,7 @@ def get_termination_reason(self, solution, events):
775775
event.expression.evaluate(
776776
solution.t_event,
777777
solution.y_event,
778-
inputs={k: v[-1] for k, v in solution.inputs.items()},
778+
inputs={k: v[:, -1] for k, v in solution.inputs.items()},
779779
)
780780
)
781781
termination_event = min(final_event_values, key=final_event_values.get)

pybamm/solvers/processed_variable.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,14 @@ def __init__(self, base_variable, solution, known_evals=None):
5959
self.base_eval, self.known_evals[solution.t[0]] = base_variable.evaluate(
6060
solution.t[0],
6161
solution.y[:, 0],
62-
inputs={name: inp[0] for name, inp in solution.inputs.items()},
62+
inputs={name: inp[:, 0] for name, inp in solution.inputs.items()},
6363
known_evals=self.known_evals[solution.t[0]],
6464
)
6565
else:
6666
self.base_eval = base_variable.evaluate(
6767
solution.t[0],
6868
solution.y[:, 0],
69-
inputs={name: inp[0] for name, inp in solution.inputs.items()},
69+
inputs={name: inp[:, 0] for name, inp in solution.inputs.items()},
7070
)
7171

7272
# handle 2D (in space) finite element variables differently
@@ -115,7 +115,7 @@ def initialise_0D(self):
115115
for idx in range(len(self.t_sol)):
116116
t = self.t_sol[idx]
117117
u = self.u_sol[:, idx]
118-
inputs = {name: inp[idx] for name, inp in self.inputs.items()}
118+
inputs = {name: inp[:, idx] for name, inp in self.inputs.items()}
119119
if self.known_evals:
120120
entries[idx], self.known_evals[t] = self.base_variable.evaluate(
121121
t, u, inputs=inputs, known_evals=self.known_evals[t]
@@ -151,7 +151,7 @@ def initialise_1D(self, fixed_t=False):
151151
for idx in range(len(self.t_sol)):
152152
t = self.t_sol[idx]
153153
u = self.u_sol[:, idx]
154-
inputs = {name: inp[idx] for name, inp in self.inputs.items()}
154+
inputs = {name: inp[:, idx] for name, inp in self.inputs.items()}
155155
if self.known_evals:
156156
eval_and_known_evals = self.base_variable.evaluate(
157157
t, u, inputs=inputs, known_evals=self.known_evals[t]
@@ -270,7 +270,7 @@ def initialise_2D(self):
270270
for idx in range(len(self.t_sol)):
271271
t = self.t_sol[idx]
272272
u = self.u_sol[:, idx]
273-
inputs = {name: inp[idx] for name, inp in self.inputs.items()}
273+
inputs = {name: inp[:, idx] for name, inp in self.inputs.items()}
274274
if self.known_evals:
275275
eval_and_known_evals = self.base_variable.evaluate(
276276
t, u, inputs=inputs, known_evals=self.known_evals[t]
@@ -330,7 +330,7 @@ def initialise_2D_scikit_fem(self):
330330
for idx in range(len(self.t_sol)):
331331
t = self.t_sol[idx]
332332
u = self.u_sol[:, idx]
333-
inputs = {name: inp[idx] for name, inp in self.inputs.items()}
333+
inputs = {name: inp[:, idx] for name, inp in self.inputs.items()}
334334

335335
if self.known_evals:
336336
eval_and_known_evals = self.base_variable.evaluate(

pybamm/solvers/solution.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,12 @@ def inputs(self, inputs):
108108
self.has_symbolic_inputs = False
109109
self._inputs = {}
110110
for name, inp in inputs.items():
111+
# Convert number to vector of the right shape
111112
if isinstance(inp, numbers.Number):
112-
inp = inp * np.ones_like(self.t)
113+
inp = inp * np.ones((1, len(self.t)))
114+
# Tile a vector
115+
else:
116+
inp = np.tile(inp, len(self.t))
113117
self._inputs[name] = inp
114118

115119
@property
@@ -316,7 +320,7 @@ def append(self, solution, start_index=1, create_sub_solutions=False):
316320
self._y = np.concatenate((self._y, solution.y[:, start_index:]), axis=1)
317321
for name, inp in self.inputs.items():
318322
solution_inp = solution.inputs[name]
319-
self.inputs[name] = np.concatenate((inp, solution_inp[start_index:]))
323+
self.inputs[name] = np.c_[inp, solution_inp[:, start_index:]]
320324
# Update solution time
321325
self.solve_time += solution.solve_time
322326
# Update termination

tests/integration/test_solvers/test_solution.py

+59
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,65 @@ def test_append(self):
5959
decimal=4,
6060
)
6161

62+
def test_append_external_variables(self):
63+
model = pybamm.lithium_ion.SPM(
64+
{
65+
"thermal": "lumped",
66+
"external submodels": ["thermal", "negative particle"],
67+
}
68+
)
69+
# create geometry
70+
geometry = model.default_geometry
71+
72+
# load parameter values and process model and geometry
73+
param = model.default_parameter_values
74+
param.process_model(model)
75+
param.process_geometry(geometry)
76+
77+
# set mesh
78+
mesh = pybamm.Mesh(geometry, model.default_submesh_types, model.default_var_pts)
79+
80+
# discretise model
81+
disc = pybamm.Discretisation(mesh, model.default_spatial_methods)
82+
disc.process_model(model)
83+
84+
# solve model
85+
solver = model.default_solver
86+
87+
T_av = 0
88+
c_s_n_av = np.ones((10, 1)) * 0.6
89+
external_variables = {
90+
"Volume-averaged cell temperature": T_av,
91+
"X-averaged negative particle concentration": c_s_n_av,
92+
}
93+
94+
# Step
95+
dt = 0.1
96+
sol_step = None
97+
for _ in range(5):
98+
sol_step = solver.step(
99+
sol_step, model, dt, external_variables=external_variables
100+
)
101+
np.testing.assert_array_equal(
102+
sol_step.inputs["Volume-averaged cell temperature"],
103+
np.zeros((1, len(sol_step.t))),
104+
)
105+
np.testing.assert_array_equal(
106+
sol_step.inputs["X-averaged negative particle concentration"],
107+
np.ones((mesh["negative particle"][0].npts, len(sol_step.t))) * 0.6,
108+
)
109+
110+
# Solve
111+
t_eval = np.linspace(0, 3600)
112+
sol = solver.solve(model, t_eval, external_variables=external_variables)
113+
np.testing.assert_array_equal(
114+
sol.inputs["Volume-averaged cell temperature"], np.zeros((1, len(sol.t))),
115+
)
116+
np.testing.assert_array_equal(
117+
sol.inputs["X-averaged negative particle concentration"],
118+
np.ones((mesh["negative particle"][0].npts, len(sol.t))) * 0.6,
119+
)
120+
62121

63122
if __name__ == "__main__":
64123
print("Add -v for more debug output")

tests/unit/test_simulation.py

+19-5
Original file line numberDiff line numberDiff line change
@@ -235,16 +235,30 @@ def test_get_variable_array(self):
235235
self.assertIsInstance(c_e, np.ndarray)
236236

237237
def test_set_external_variable(self):
238-
model_options = {"thermal": "lumped", "external submodels": ["thermal"]}
238+
model_options = {
239+
"thermal": "lumped",
240+
"external submodels": ["thermal", "negative particle"],
241+
}
239242
model = pybamm.lithium_ion.SPMe(model_options)
240243
sim = pybamm.Simulation(model)
241244

242245
T_av = 0
246+
c_s_n_av = np.ones((10, 1)) * 0.5
247+
external_variables = {
248+
"Volume-averaged cell temperature": T_av,
249+
"X-averaged negative particle concentration": c_s_n_av,
250+
}
243251

244-
dt = 0.001
252+
# Step
253+
dt = 0.1
254+
for _ in range(5):
255+
sim.step(dt, external_variables=external_variables)
256+
sim.plot(testing=True)
245257

246-
external_variables = {"Volume-averaged cell temperature": T_av}
247-
sim.step(dt, external_variables=external_variables)
258+
# Solve
259+
t_eval = np.linspace(0, 3600)
260+
sim.solve(t_eval, external_variables=external_variables)
261+
sim.plot(testing=True)
248262

249263
def test_step(self):
250264

@@ -301,7 +315,7 @@ def test_step_with_inputs(self):
301315
self.assertEqual(sim.solution.t[1], dt / tau)
302316
self.assertEqual(sim.solution.t[2], 2 * dt / tau)
303317
np.testing.assert_array_equal(
304-
sim.solution.inputs["Current function [A]"], np.array([1, 1, 2])
318+
sim.solution.inputs["Current function [A]"], np.array([[1, 1, 2]])
305319
)
306320

307321
def test_save_load(self):

tests/unit/test_solvers/test_solution.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ def test_append(self):
4848
np.testing.assert_array_equal(sol1.y, np.concatenate([y1, y2[:, 1:]], axis=1))
4949
np.testing.assert_array_equal(
5050
sol1.inputs["a"],
51-
np.concatenate([1 * np.ones_like(t1), 2 * np.ones_like(t2[1:])]),
51+
np.concatenate([1 * np.ones_like(t1), 2 * np.ones_like(t2[1:])])[
52+
np.newaxis, :
53+
],
5254
)
5355

5456
# Test sub-solutions
@@ -57,11 +59,11 @@ def test_append(self):
5759
np.testing.assert_array_equal(sol1.sub_solutions[1].t, t2)
5860
self.assertEqual(sol1.sub_solutions[0].model, sol1.model)
5961
np.testing.assert_array_equal(
60-
sol1.sub_solutions[0].inputs["a"], 1 * np.ones_like(t1)
62+
sol1.sub_solutions[0].inputs["a"], 1 * np.ones_like(t1)[np.newaxis, :]
6163
)
6264
self.assertEqual(sol1.sub_solutions[1].model, sol2.model)
6365
np.testing.assert_array_equal(
64-
sol1.sub_solutions[1].inputs["a"], 2 * np.ones_like(t2)
66+
sol1.sub_solutions[1].inputs["a"], 2 * np.ones_like(t2)[np.newaxis, :]
6567
)
6668

6769
def test_total_time(self):

0 commit comments

Comments
 (0)