Skip to content

Commit e7e99b7

Browse files
Merge branch 'develop' into remove-unneeded-params
2 parents 4241404 + b483d9c commit e7e99b7

File tree

18 files changed

+182
-96
lines changed

18 files changed

+182
-96
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
## Bug fixes
1111

12+
- Fixed electrolyte conservation in the case of concentration-dependent transference number ([#2758](https://github.com/pybamm-team/PyBaMM/pull/2758))
1213
- Fixed `plot_voltage_components` so that the sum of overpotentials is now equal to the voltage ([#2740](https://github.com/pybamm-team/PyBaMM/pull/2740))
1314

1415
## Optimizations
@@ -18,6 +19,7 @@
1819
## Breaking changes
1920

2021
- Removed "... cation signed stoichiometry" and "... electrons in reaction" parameters, they are now hardcoded. ([#2778](https://github.com/pybamm-team/PyBaMM/pull/2778))
22+
- When using `solver.step()`, the first time point in the step is shifted by `pybamm.settings.step_start_offset` (default 1 ns) to avoid having duplicate times in the solution steps from the end of one step and the start of the next. ([#2773](https://github.com/pybamm-team/PyBaMM/pull/2773))
2123
- Renamed "Measured open circuit voltage [V]" to "Surface open-circuit voltage [V]". This variable was calculated from surface particle concentrations, and hence "hid" the overpotential from particle gradients. The new variable "Open-circuit voltage [V]" is calculated from bulk particle concentrations instead. ([#2740](https://github.com/pybamm-team/PyBaMM/pull/2740))
2224
- Renamed all references to "open circuit" to be "open-circuit" instead. ([#2740](https://github.com/pybamm-team/PyBaMM/pull/2740))
2325
- Renamed parameter "1 + dlnf/dlnc" to "Thermodynamic factor". ([#2727](https://github.com/pybamm-team/PyBaMM/pull/2727))
@@ -30,6 +32,7 @@
3032
- Added an option for using a banded jacobian and sundials banded solvers for the IDAKLU solve ([#2677](https://github.com/pybamm-team/PyBaMM/pull/2677))
3133
- The "particle size" option can now be a tuple to allow different behaviour in each electrode ([#2672](https://github.com/pybamm-team/PyBaMM/pull/2672)).
3234
- Added temperature control to experiment class. ([#2518](https://github.com/pybamm-team/PyBaMM/pull/2518))
35+
- Added method to calculate maximum theoretical energy. ([#2777](https://github.com/pybamm-team/PyBaMM/pull/2777))
3336

3437
## Bug fixes
3538

examples/notebooks/Getting Started/Tutorial 7 - Model options.ipynb

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
{
6060
"data": {
6161
"text/plain": [
62-
"<pybamm.solvers.solution.Solution at 0x7f29f1333e50>"
62+
"<pybamm.solvers.solution.Solution at 0x139461520>"
6363
]
6464
},
6565
"execution_count": 3,
@@ -70,10 +70,7 @@
7070
"source": [
7171
"model = pybamm.lithium_ion.SPMe(options=options) # loading in options\n",
7272
"\n",
73-
"parameter_values = model.default_parameter_values\n",
74-
"parameter_values[\"Current function [A]\"] = 3\n",
75-
"\n",
76-
"sim = pybamm.Simulation(model, parameter_values=parameter_values)\n",
73+
"sim = pybamm.Simulation(model)\n",
7774
"sim.solve([0, 3600])"
7875
]
7976
},
@@ -92,16 +89,26 @@
9289
{
9390
"data": {
9491
"application/vnd.jupyter.widget-view+json": {
95-
"model_id": "c00d126a3fc543829596dd24f8b9d626",
92+
"model_id": "0fc5db759e804d3fb2793222ddbec5d4",
9693
"version_major": 2,
9794
"version_minor": 0
9895
},
9996
"text/plain": [
100-
"interactive(children=(FloatSlider(value=0.0, description='t', max=800.0000000000001, step=8.000000000000002), …"
97+
"interactive(children=(FloatSlider(value=0.0, description='t', max=1.0, step=0.01), Output()), _dom_classes=('w"
10198
]
10299
},
103100
"metadata": {},
104101
"output_type": "display_data"
102+
},
103+
{
104+
"data": {
105+
"text/plain": [
106+
"<pybamm.plotting.quick_plot.QuickPlot at 0x139461fd0>"
107+
]
108+
},
109+
"execution_count": 4,
110+
"metadata": {},
111+
"output_type": "execute_result"
105112
}
106113
],
107114
"source": [
@@ -139,7 +146,8 @@
139146
"[1] Joel A. E. Andersson, Joris Gillis, Greg Horn, James B. Rawlings, and Moritz Diehl. CasADi – A software framework for nonlinear optimization and optimal control. Mathematical Programming Computation, 11(1):1–36, 2019. doi:10.1007/s12532-018-0139-4.\n",
140147
"[2] Charles R. Harris, K. Jarrod Millman, Stéfan J. van der Walt, Ralf Gommers, Pauli Virtanen, David Cournapeau, Eric Wieser, Julian Taylor, Sebastian Berg, Nathaniel J. Smith, and others. Array programming with NumPy. Nature, 585(7825):357–362, 2020. doi:10.1038/s41586-020-2649-2.\n",
141148
"[3] Scott G. Marquis, Valentin Sulzer, Robert Timms, Colin P. Please, and S. Jon Chapman. An asymptotic derivation of a single particle model with electrolyte. Journal of The Electrochemical Society, 166(15):A3693–A3706, 2019. doi:10.1149/2.0341915jes.\n",
142-
"[4] Valentin Sulzer, Scott G. Marquis, Robert Timms, Martin Robinson, and S. Jon Chapman. Python Battery Mathematical Modelling (PyBaMM). ECSarXiv. February, 2020. doi:10.1149/osf.io/67ckj.\n",
149+
"[4] Valentin Sulzer, Scott G. Marquis, Robert Timms, Martin Robinson, and S. Jon Chapman. Python Battery Mathematical Modelling (PyBaMM). Journal of Open Research Software, 9(1):14, 2021. doi:10.5334/jors.309.\n",
150+
"[5] Robert Timms, Scott G Marquis, Valentin Sulzer, Colin P. Please, and S Jonathan Chapman. Asymptotic Reduction of a Lithium-ion Pouch Cell Model. SIAM Journal on Applied Mathematics, 81(3):765–788, 2021. doi:10.1137/20M1336898.\n",
143151
"\n"
144152
]
145153
}
@@ -165,7 +173,7 @@
165173
"name": "python",
166174
"nbconvert_exporter": "python",
167175
"pygments_lexer": "ipython3",
168-
"version": "3.11.1"
176+
"version": "3.9.16"
169177
},
170178
"vscode": {
171179
"interpreter": {

pybamm/models/full_battery_models/lithium_ion/electrode_soh.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,3 +557,47 @@ def get_min_max_stoichiometries(
557557
"""
558558
esoh_solver = ElectrodeSOHSolver(parameter_values, param, known_value)
559559
return esoh_solver.get_min_max_stoichiometries()
560+
561+
562+
def calculate_theoretical_energy(
563+
parameter_values, initial_soc=1.0, final_soc=0.0, points=100
564+
):
565+
"""
566+
Calculate maximum energy possible from a cell given OCV, initial soc, and final soc
567+
given voltage limits, open-circuit potentials, etc defined by parameter_values
568+
569+
Parameters
570+
----------
571+
parameter_values : :class:`pybamm.ParameterValues`
572+
The parameter values class that will be used for the simulation.
573+
initial_soc : float
574+
The soc at begining of discharge, default 1.0
575+
final_soc : float
576+
The soc at end of discharge, default 1.0
577+
points : int
578+
The number of points at which to calculate voltage.
579+
580+
Returns
581+
-------
582+
E
583+
The total energy of the cell in Wh
584+
"""
585+
# Get initial and final stoichiometric values.
586+
n_i, p_i = get_initial_stoichiometries(initial_soc, parameter_values)
587+
n_f, p_f = get_initial_stoichiometries(final_soc, parameter_values)
588+
n_vals = np.linspace(n_i, n_f, num=points)
589+
p_vals = np.linspace(p_i, p_f, num=points)
590+
# Calculate OCV at each stoichiometry
591+
param = pybamm.LithiumIonParameters()
592+
T = param.T_amb(0)
593+
Vs = np.empty(n_vals.shape)
594+
for i in range(n_vals.size):
595+
Vs[i] = parameter_values.evaluate(
596+
param.p.prim.U(p_vals[i], T)
597+
) - parameter_values.evaluate(param.n.prim.U(n_vals[i], T))
598+
# Calculate dQ
599+
Q_p = parameter_values.evaluate(param.p.prim.Q_init) * (p_f - p_i)
600+
dQ = Q_p / (points - 1)
601+
# Integrate and convert to W-h
602+
E = np.trapz(Vs, dx=dQ)
603+
return E

pybamm/models/submodels/electrolyte_diffusion/full_diffusion.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,17 +89,12 @@ def get_coupled_variables(self, variables):
8989
def set_rhs(self, variables):
9090
eps_c_e = variables["Porosity times concentration [mol.m-3]"]
9191
c_e = variables["Electrolyte concentration [mol.m-3]"]
92-
T = variables["Cell temperature [K]"]
93-
N_e_diffusion = variables["Electrolyte diffusion flux [mol.m-2.s-1]"]
94-
N_e_convection = variables["Electrolyte convection flux [mol.m-2.s-1]"]
92+
N_e = variables["Electrolyte flux [mol.m-2.s-1]"]
9593
div_Vbox = variables["Transverse volume-averaged acceleration [m.s-2]"]
9694

9795
sum_s_a_j = variables["Sum of electrolyte reaction source terms [A.m-3]"]
98-
sum_a_j = variables["Sum of volumetric interfacial current densities [A.m-3]"]
9996
sum_s_a_j.print_name = "aj"
100-
source_terms = (sum_s_a_j - self.param.t_plus(c_e, T) * sum_a_j) / self.param.F
101-
102-
N_e = N_e_diffusion + N_e_convection
97+
source_terms = sum_s_a_j / self.param.F
10398

10499
self.rhs = {eps_c_e: -pybamm.div(N_e) + source_terms - c_e * div_Vbox}
105100

pybamm/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class Settings(object):
1212
_abs_smoothing = "exact"
1313
max_words_in_line = 4
1414
max_y_value = 1e5
15+
step_start_offset = 1e-9
1516
tolerances = {
1617
"D_e__c_e": 10, # dimensional
1718
"kappa_e__c_e": 10, # dimensional

pybamm/solvers/base_solver.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,18 +1085,34 @@ def step(
10851085
"Cannot step empty model, use `pybamm.DummySolver` instead"
10861086
)
10871087

1088-
# Make sure dt is positive
1089-
if dt <= 0:
1090-
raise pybamm.SolverError("Step time must be positive")
1088+
# Make sure dt is greater than the offset
1089+
step_start_offset = pybamm.settings.step_start_offset
1090+
if dt <= step_start_offset:
1091+
raise pybamm.SolverError(
1092+
f"Step time must be at least {pybamm.TimerTime(step_start_offset)}"
1093+
)
1094+
1095+
t_start = old_solution.t[-1]
1096+
t_end = t_start + dt
1097+
# Calculate t_eval
1098+
t_eval = np.linspace(t_start, t_end, npts)
1099+
1100+
if t_start == 0:
1101+
t_start_shifted = t_start
1102+
else:
1103+
# offset t_start by t_start_offset (default 1 ns)
1104+
# to avoid repeated times in the solution
1105+
# from having the same time at the end of the previous step and
1106+
# the start of the next step
1107+
t_start_shifted = t_start + step_start_offset
1108+
t_eval[0] = t_start_shifted
10911109

10921110
# Set timer
10931111
timer = pybamm.Timer()
10941112

10951113
# Set up inputs
10961114
model_inputs = self._set_up_model_inputs(model, inputs)
10971115

1098-
t = old_solution.t[-1]
1099-
11001116
first_step_this_model = False
11011117
if model not in self._model_set_up:
11021118
first_step_this_model = True
@@ -1139,16 +1155,17 @@ def step(
11391155
set_up_time = timer.time()
11401156

11411157
# (Re-)calculate consistent initial conditions
1142-
self._set_initial_conditions(model, t, model_inputs, update_rhs=False)
1143-
1144-
# Calculate t_eval
1145-
t_eval = np.linspace(t, t + dt, npts)
1158+
self._set_initial_conditions(
1159+
model, t_start_shifted, model_inputs, update_rhs=False
1160+
)
11461161

11471162
# Check initial conditions don't violate events
11481163
self._check_events_with_initial_conditions(t_eval, model, model_inputs)
11491164

11501165
# Step
1151-
pybamm.logger.verbose("Stepping for {:.0f} < t < {:.0f}".format(t, (t + dt)))
1166+
pybamm.logger.verbose(
1167+
"Stepping for {:.0f} < t < {:.0f}".format(t_start_shifted, t_end)
1168+
)
11521169
timer.reset()
11531170
solution = self._integrate(model, t_eval, model_inputs)
11541171
solution.solve_time = timer.time()

tests/integration/test_experiments.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,21 @@ def test_discharge_rest_charge(self):
1414
"Rest for 1 hour",
1515
"Charge at C/2 for 1 hour",
1616
],
17-
period="0.25 hours",
17+
period="0.5 hours",
1818
)
1919
model = pybamm.lithium_ion.SPM()
2020
sim = pybamm.Simulation(
2121
model, experiment=experiment, solver=pybamm.CasadiSolver()
2222
)
2323
sim.solve()
2424
np.testing.assert_array_almost_equal(
25-
sim._solution["Time [h]"].entries, np.linspace(0, 3, 13)
25+
sim._solution["Time [h]"].entries,
26+
np.array([0, 0.5, 1, 1 + 1e-9, 1.5, 2, 2 + 1e-9, 2.5, 3]),
2627
)
2728
cap = model.default_parameter_values["Nominal cell capacity [A.h]"]
2829
np.testing.assert_array_almost_equal(
2930
sim._solution["Current [A]"].entries,
30-
[cap / 2] * 5 + [0] * 4 + [-cap / 2] * 4,
31+
[cap / 2] * 3 + [0] * 3 + [-cap / 2] * 3,
3132
)
3233

3334
def test_rest_discharge_rest(self):
@@ -57,13 +58,10 @@ def test_gitt(self):
5758
model, experiment=experiment, solver=pybamm.CasadiSolver()
5859
)
5960
sim.solve()
60-
np.testing.assert_array_almost_equal(
61-
sim._solution["Time [h]"].entries, np.arange(0, 20.01, 0.1)
62-
)
6361
cap = model.default_parameter_values["Nominal cell capacity [A.h]"]
6462
np.testing.assert_array_almost_equal(
6563
sim._solution["Current [A]"].entries,
66-
[cap / 20] * 11 + [0] * 10 + ([cap / 20] * 10 + [0] * 10) * 9,
64+
[cap / 20] * 11 + [0] * 11 + ([cap / 20] * 11 + [0] * 11) * 9,
6765
)
6866

6967
def test_infeasible(self):

tests/integration/test_models/standard_output_comparison.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,13 @@ def test_all(self):
128128
self.compare("X-averaged positive electrode open-circuit potential [V]")
129129
self.compare("Voltage [V]")
130130
self.compare("X-averaged solid phase ohmic losses [V]")
131-
self.compare("Negative electrode reaction overpotential [V]")
131+
self.compare("Negative electrode reaction overpotential [V]", atol=1e-4)
132132
self.compare("Positive electrode reaction overpotential [V]")
133133
self.compare("Negative electrode potential [V]", atol=1e-5)
134134
self.compare("Positive electrode potential [V]")
135135
self.compare("Electrolyte potential [V]")
136136
# Currents
137-
self.compare("Exchange current density [A.m-2]")
137+
self.compare("Exchange current density [A.m-2]", atol=2e-3)
138138
self.compare("Negative electrode current density [A.m-2]", atol=1e-10)
139139
self.compare("Positive electrode current density [A.m-2]", atol=1e-10)
140140

tests/integration/test_models/standard_output_tests.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,10 @@ def test_conservation(self):
545545
diff = (
546546
self.c_e_tot(self.solution.t[1:]) - self.c_e_tot(self.solution.t[:-1])
547547
) / self.c_e_tot(self.solution.t[:-1])
548-
if self.model.options["surface form"] == "differential":
548+
if self.model.options["surface form"] == "differential" or (
549+
isinstance(self.model, pybamm.lithium_ion.DFN)
550+
and self.model.options["surface form"] == "algebraic"
551+
):
549552
np.testing.assert_allclose(0, diff, atol=1e-4, rtol=1e-6)
550553
else:
551554
np.testing.assert_allclose(0, diff, atol=1e-14, rtol=1e-14)

tests/unit/test_experiments/test_simulation_with_experiment.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -176,22 +176,18 @@ def test_run_experiment_cccv_ode(self):
176176
solution = sim.solve(solver=pybamm.CasadiSolver("fast with events"))
177177
solutions.append(solution)
178178

179+
t = solutions[1]["Time [s]"].data
179180
np.testing.assert_array_almost_equal(
180-
solutions[0]["Voltage [V]"].data,
181-
solutions[1]["Voltage [V]"].data,
181+
solutions[0]["Voltage [V]"](t=t),
182+
solutions[1]["Voltage [V]"](t=t),
182183
decimal=1,
183184
)
184185
np.testing.assert_array_almost_equal(
185-
solutions[0]["Current [A]"].data,
186-
solutions[1]["Current [A]"].data,
186+
solutions[0]["Current [A]"](t=t),
187+
solutions[1]["Current [A]"](t=t),
187188
decimal=0,
188189
)
189190

190-
np.testing.assert_array_equal(
191-
solutions[0]["Ambient temperature [C]"].data,
192-
solutions[1]["Ambient temperature [C]"].data,
193-
)
194-
195191
self.assertEqual(solutions[1].termination, "final time")
196192

197193
@unittest.skipIf(not pybamm.have_idaklu(), "idaklu solver is not installed")
@@ -516,12 +512,13 @@ def test_run_experiment_skip_steps(self):
516512
)
517513
sol2 = sim2.solve()
518514
np.testing.assert_array_almost_equal(
519-
sol["Voltage [V]"].data, sol2["Voltage [V]"].data
515+
sol["Voltage [V]"].data, sol2["Voltage [V]"].data, decimal=5
520516
)
521517
for idx1, idx2 in [(1, 0), (2, 1), (4, 2)]:
522518
np.testing.assert_array_almost_equal(
523519
sol.cycles[0].steps[idx1]["Voltage [V]"].data,
524520
sol2.cycles[0].steps[idx2]["Voltage [V]"].data,
521+
decimal=5,
525522
)
526523

527524
def test_all_empty_solution_errors(self):

0 commit comments

Comments
 (0)