Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 2358 add temperature to experiment #2518

Merged
merged 15 commits into from
Feb 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@

## Features

- Added temperature control to experiment class. ([#2518])(https://github.com/pybamm-team/PyBaMM/pull/2518)
- 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)).

## Bug fixes

- Fixed the length scaling for the first dimension of r-R plots ([#2663](https://github.com/pybamm-team/PyBaMM/pull/2663)).

# [v23.1](https://github.com/pybamm-team/PyBaMM/tree/v23.1) - 2023-01-31

## Features
Expand All @@ -17,7 +22,6 @@

- Fixed a bug where the solid phase conductivity was double-corrected for tortuosity when loading parameters from a BPX file ([#2638](https://github.com/pybamm-team/PyBaMM/pull/2638)).
- Changed termination from "success" to "final time" for algebraic solvers to match ODE/DAE solvers ([#2613](https://github.com/pybamm-team/PyBaMM/pull/2613)).
- Fixed the length scaling for the first dimension of r-R plots ([#2663](https://github.com/pybamm-team/PyBaMM/pull/2663)).

# [v22.12](https://github.com/pybamm-team/PyBaMM/tree/v22.12) - 2022-12-31

Expand Down
41 changes: 10 additions & 31 deletions examples/scripts/run_ecm.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,22 @@

pybamm.set_logging_level("INFO")

# options = {"number of rc elements": 2}
options = {}
model = pybamm.equivalent_circuit.Thevenin(options=options)

parameter_values = model.default_parameter_values
# parameter_values.update(
# {
# "R2 [Ohm]": 0.3e-3,
# "C2 [F]": 1000 / 0.3e-3,
# "Element-2 initial overpotential [V]": 0,
# },
# check_already_exists=False,
# )
model = pybamm.equivalent_circuit.Thevenin()

experiment = pybamm.Experiment(
[
(
"Discharge at C/10 for 10 hours or until 3.3 V",
"Rest for 1 hour",
"Charge at 100 A until 4.1 V (1 second period)",
"Hold at 4.1 V until 5 A (1 seconds period)",
"Rest for 1 hour",
"Discharge at C/10 for 10 hours or until 3.3 V at 15oC",
"Rest for 30 minutes at 15oC",
"Rest for 2 hours at 35oC",
"Charge at 100 A until 4.1 V at 35oC (1 second period)",
"Hold at 4.1 V until 5 A at 35oC (1 seconds period)",
"Rest for 30 minutes at 35oC",
"Rest for 1 hour at 25oC",
),
]
)

sim = pybamm.Simulation(model, experiment=experiment, parameter_values=parameter_values)
sim = pybamm.Simulation(model, experiment=experiment)
sim.solve()
sim.plot(
output_variables=[
"SoC",
"Open circuit voltage [V]",
"Current [A]",
"Cell temperature [degC]",
"Entropic change [V/K]",
"R0 [Ohm]",
"R1 [Ohm]",
"C1 [F]",
]
)
sim.plot()
120 changes: 99 additions & 21 deletions pybamm/experiments/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,28 @@
#

import numpy as np
import re
import pybamm

examples = """

Discharge at 1C for 0.5 hours,
Discharge at C/20 for 0.5 hours,
Charge at 0.5 C for 45 minutes,
Discharge at 1 A for 90 seconds,
Charge at 200mA for 45 minutes (1 minute period),
Discharge at 1 W for 0.5 hours,
Charge at 200 mW for 45 minutes,
Rest for 10 minutes (5 minute period),
Hold at 1 V for 20 seconds,
Charge at 1 C until 4.1V,
Hold at 4.1 V until 50 mA,
Hold at 3V until C/50,
Run US06 (A),
Run US06 (A) for 20 seconds,
Run US06 (V) for 45 minutes,
Run US06 (W) for 2 hours,
"Discharge at 1C for 0.5 hours at 27oC",
"Discharge at C/20 for 0.5 hours at 29oC",
"Charge at 0.5 C for 45 minutes at -5oC",
"Discharge at 1 A for 0.5 hours at -5.1oC",
"Charge at 200 mA for 45 minutes at 10.2oC (1 minute period)",
"Discharge at 1W for 0.5 hours at -10.4oC",
"Charge at 200mW for 45 minutes",
"Rest for 10 minutes (5 minute period)",
"Hold at 1V for 20 seconds",
"Charge at 1 C until 4.1V",
"Hold at 4.1 V until 50mA",
"Hold at 3V until C/50",
"Discharge at C/3 for 2 hours or until 2.5 V at 26oC",
"Run US06 (A) at -5oC",
"Run US06 (V) for 5 minutes",
"Run US06 (W) for 0.5 hours",

"""


Expand All @@ -31,12 +34,18 @@ class Experiment:
list of operating conditions should be passed in. Each operating condition should
be of the form "Do this for this long" or "Do this until this happens". For example,
"Charge at 1 C for 1 hour", or "Charge at 1 C until 4.2 V", or "Charge at 1 C for 1
hour or until 4.2 V". The instructions can be of the form "(Dis)charge at x A/C/W",
"Rest", or "Hold at x V". The running time should be a time in seconds, minutes or
hour or until 4.2 V at 25oC". The instructions can be of the form
"(Dis)charge at x A/C/W", "Rest", or "Hold at x V until y A at z oC". The running
time should be a time in seconds, minutes or
hours, e.g. "10 seconds", "3 minutes" or "1 hour". The stopping conditions should be
a circuit state, e.g. "1 A", "C/50" or "3 V". The parameter drive_cycles is
mandatory to run drive cycle. For example, "Run x", then x must be the key
of drive_cycles dictionary.
of drive_cycles dictionary. The temperature should be provided after the stopping
condition but before the period, e.g. "1 A at 25 oC (1 second period)". It is
not essential to provide a temperature and a global temperature can be set either
from within the paramter values of passing a temperature to this experiment class.
If the temperature is not specified in a line, then the global temperature is used,
even if another temperature has been set in an earlier line.

Parameters
----------
Expand All @@ -45,6 +54,10 @@ class Experiment:
period : string, optional
Period (1/frequency) at which to record outputs. Default is 1 minute. Can be
overwritten by individual operating conditions.
temperature: float, optional
The ambient air temperature in degrees Celsius at which to run the experiment.
Default is None whereby the ambient temperature is taken from the parameter set.
This value is overwritten if the temperature is specified in a step.
termination : list, optional
List of conditions under which to terminate the experiment. Default is None.
drive_cycles : dict
Expand All @@ -60,6 +73,7 @@ def __init__(
self,
operating_conditions,
period="1 minute",
temperature=None,
termination=None,
drive_cycles={},
cccv_handling="two-step",
Expand All @@ -71,12 +85,15 @@ def __init__(
self.args = (
operating_conditions,
period,
temperature,
termination,
drive_cycles,
cccv_handling,
)

self.period = self.convert_time_to_seconds(period.split())
self.temperature = temperature

operating_conditions_cycles = []
for cycle in operating_conditions:
# Check types and convert strings to 1-tuples
Expand Down Expand Up @@ -163,16 +180,26 @@ def read_string(self, cond, drive_cycles):
cond_CC, cond_CV = cond.split(" then ")
op_CC = self.read_string(cond_CC, drive_cycles)
op_CV = self.read_string(cond_CV, drive_cycles)

if op_CC["temperature"] != op_CV["temperature"]:
raise ValueError(
"The temperature for the CC and CV steps must be the same."
f"Got {op_CC['temperature']} and {op_CV['temperature']}"
f"from {op_CC['string']} and {op_CV['string']}"
)

tag_CC = op_CC["tags"] or []
tag_CV = op_CV["tags"] or []
tags = list(np.unique(tag_CC + tag_CV))
if len(tags) == 0:
tags = None

outputs = {
"type": "CCCV",
"Voltage input [V]": op_CV["Voltage input [V]"],
"time": op_CV["time"],
"period": op_CV["period"],
"temperature": op_CC["temperature"],
"dc_data": None,
"string": cond,
"events": op_CV["events"],
Expand All @@ -198,6 +225,11 @@ def read_string(self, cond, drive_cycles):
period = self.convert_time_to_seconds(time.split())
else:
period = self.period

# Temperature part of the condition is removed here
unprocessed_cond = cond
temperature, cond = self._read_and_drop_temperature(cond)

# Read instructions
if "Run" in cond:
cond_list = cond.split()
Expand Down Expand Up @@ -234,13 +266,17 @@ def read_string(self, cond, drive_cycles):
cond_list = cond.split()
idx_for = cond_list.index("for")
idx_until = cond_list.index("or")

electric = self.convert_electric(cond_list[:idx_for])

time = self.convert_time_to_seconds(cond_list[idx_for + 1 : idx_until])
events = self.convert_electric(cond_list[idx_until + 2 :])

elif "for" in cond:
# e.g. for 3 hours
cond_list = cond.split()
idx = cond_list.index("for")

electric = self.convert_electric(cond_list[:idx])
time = self.convert_time_to_seconds(cond_list[idx + 1 :])
events = None
Expand All @@ -264,8 +300,9 @@ def read_string(self, cond, drive_cycles):
**electric,
"time": time,
"period": period,
"temperature": temperature,
"dc_data": dc_data,
"string": cond,
"string": unprocessed_cond,
"events": events,
"tags": tags,
}
Expand Down Expand Up @@ -329,6 +366,9 @@ def convert_electric(self, electric):
raise ValueError(
"Instruction must be 'discharge', 'charge', 'rest', 'hold' or "
f"'Run'. For example: {examples}"
""
"The following instruction does not comply: "
f"{instruction}"
)
elif len(electric) == 2:
# e.g. 3 A, 4.1 V
Expand Down Expand Up @@ -377,6 +417,44 @@ def convert_electric(self, electric):
)
)

def _detect_mistyped_temperatures(self, cond):
if "oC" in cond:
raise ValueError(f"Temperature not written correctly on step: '{cond}'")

def _read_and_drop_temperature(self, cond):
matches = re.findall(r"at\s-*\d+\.*\d*\s*oC", cond)

if len(matches) == 0:
self._detect_mistyped_temperatures(cond)

if self.temperature is None:
pybamm.logger.warning(
"Temperature not found on step: "
f"'{cond}', using temperature "
"from parameter values."
)

else:
pybamm.logger.warning(
f"Temperature not found on step: '{cond}', "
f"using global temperature "
f"({self.temperature}oC) instead"
)

temperature = self.temperature
reduced_cond = cond

elif len(matches) == 1:
match = matches[0]
numerical_part = re.findall(r"-*\d+\.*\d*", match)[0]
temperature = float(numerical_part)
reduced_cond = cond.replace(match, "")

else:
raise ValueError(f"More than one temperature found on step: '{cond}'")

return temperature, reduced_cond

def convert_time_to_seconds(self, time_and_units):
"""Convert a time in seconds, minutes or hours to a time in seconds"""
time, units = time_and_units
Expand Down Expand Up @@ -442,7 +520,7 @@ def is_cccv(self, step, next_step):
# e.g. step="Charge at 2.0 A until 4.2V"
# next_step="Hold at 4.2V until C/50"
if (
step.startswith("Charge")
(step.startswith("Charge") or step.startswith("Discharge"))
and "until" in step
and "V" in step
and "Hold at " in next_step
Expand Down
5 changes: 2 additions & 3 deletions pybamm/input/parameters/ecm/example_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,10 @@ def get_parameter_values():
values = {
"chemistry": "ecm",
"Initial SoC": 0.5,
"Initial cell temperature [degC]": 25,
"Initial jig temperature [degC]": 25,
"Initial temperature [K]": 25 + 273.15,
"Cell capacity [A.h]": cell_capacity,
"Nominal cell capacity [A.h]": cell_capacity,
"Ambient temperature [degC]": 25,
"Ambient temperature [K]": 25 + 273.15,
"Current function [A]": 100,
"Upper voltage cut-off [V]": 4.2,
"Lower voltage cut-off [V]": 3.2,
Expand Down
9 changes: 6 additions & 3 deletions pybamm/parameters/ecm_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,14 @@ def _set_compatibility_parameters(self):

def _set_initial_condition_parameters(self):
self.initial_soc = pybamm.Parameter("Initial SoC")
self.initial_T_cell = pybamm.Parameter("Initial cell temperature [degC]")
self.initial_T_jig = pybamm.Parameter("Initial jig temperature [degC]")
self.initial_T_cell = pybamm.Parameter("Initial temperature [K]") - 273.15
self.initial_T_jig = pybamm.Parameter("Initial temperature [K]") - 273.15

def T_amb(self, t):
return pybamm.FunctionParameter("Ambient temperature [degC]", {"Time [s]": t})
ambient_temperature_K = pybamm.FunctionParameter(
"Ambient temperature [K]", {"Time [s]": t}
)
return ambient_temperature_K - 273.15

def ocv(self, soc):
return pybamm.FunctionParameter("Open circuit voltage [V]", {"SoC": soc})
Expand Down
Loading