Repository: pybamm-team/PyBaMM

Issue:
Add "temperature" argument to experiment
Simulation should then update the parameter values ("Ambient temperature [K]" and "Initial temperature [K]", not "Reference temperature [K]"), with the given temperature

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7dff7efed..de37f21b3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
@@ -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
 
diff --git a/examples/scripts/run_ecm.py b/examples/scripts/run_ecm.py
index c86d5bce0..0ca4e1048 100644
--- a/examples/scripts/run_ecm.py
+++ b/examples/scripts/run_ecm.py
@@ -2,43 +2,22 @@ import pybamm
 
 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()
diff --git a/pybamm/experiments/experiment.py b/pybamm/experiments/experiment.py
index c907e767d..865af8f32 100644
--- a/pybamm/experiments/experiment.py
+++ b/pybamm/experiments/experiment.py
@@ -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",
+
     """
 
 
@@ -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
     ----------
@@ -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
@@ -60,6 +73,7 @@ class Experiment:
         self,
         operating_conditions,
         period="1 minute",
+        temperature=None,
         termination=None,
         drive_cycles={},
         cccv_handling="two-step",
@@ -71,12 +85,15 @@ class Experiment:
         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
@@ -163,16 +180,26 @@ class Experiment:
             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"],
@@ -198,6 +225,11 @@ class Experiment:
             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()
@@ -234,13 +266,17 @@ class Experiment:
                 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
@@ -264,8 +300,9 @@ class Experiment:
             **electric,
             "time": time,
             "period": period,
+            "temperature": temperature,
             "dc_data": dc_data,
-            "string": cond,
+            "string": unprocessed_cond,
             "events": events,
             "tags": tags,
         }
@@ -329,6 +366,9 @@ class Experiment:
                     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
@@ -377,6 +417,44 @@ class Experiment:
                     )
                 )
 
+    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
@@ -442,7 +520,7 @@ class Experiment:
         # 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
diff --git a/pybamm/input/parameters/ecm/example_set.py b/pybamm/input/parameters/ecm/example_set.py
index 06a990681..52ca0609d 100644
--- a/pybamm/input/parameters/ecm/example_set.py
+++ b/pybamm/input/parameters/ecm/example_set.py
@@ -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,
diff --git a/pybamm/parameters/ecm_parameters.py b/pybamm/parameters/ecm_parameters.py
index cad755049..919b40d0c 100644
--- a/pybamm/parameters/ecm_parameters.py
+++ b/pybamm/parameters/ecm_parameters.py
@@ -42,11 +42,14 @@ class EcmParameters:
 
     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})
diff --git a/pybamm/simulation.py b/pybamm/simulation.py
index b41b48581..e2647fa1c 100644
--- a/pybamm/simulation.py
+++ b/pybamm/simulation.py
@@ -143,6 +143,7 @@ class Simulation:
         # Update experiment using parameters such as timescale and capacity
         timescale = self._parameter_values.evaluate(model.timescale)
         capacity = self._parameter_values["Nominal cell capacity [A.h]"]
+
         for op_conds in experiment.operating_conditions:
             op_type = op_conds["type"]
             if op_conds["dc_data"] is not None:
@@ -211,7 +212,7 @@ class Simulation:
         """
         self.op_type_to_model = {}
         self.op_string_to_model = {}
-        for op in self.experiment.operating_conditions:
+        for op_number, op in enumerate(self.experiment.operating_conditions):
             # Create model for this operating condition type (current/voltage/power)
             # if it has not already been seen before
             if op["type"] not in self.op_type_to_model:
@@ -258,7 +259,12 @@ class Simulation:
                 self.update_new_model_events(new_model, op)
                 # Update parameter values
                 new_parameter_values = self.parameter_values.copy()
-                experiment_parameter_values = self.get_experiment_parameter_values(op)
+                self._original_temperature = new_parameter_values[
+                    "Ambient temperature [K]"
+                ]
+                experiment_parameter_values = self.get_experiment_parameter_values(
+                    op, op_number
+                )
                 new_parameter_values.update(
                     experiment_parameter_values, check_already_exists=False
                 )
@@ -342,7 +348,7 @@ class Simulation:
                     event.name, event.expression + 1, event.event_type
                 )
 
-    def get_experiment_parameter_values(self, op):
+    def get_experiment_parameter_values(self, op, op_number):
         experiment_parameter_values = {}
         if op["type"] == "current":
             experiment_parameter_values.update(
@@ -360,6 +366,26 @@ class Simulation:
             experiment_parameter_values.update(
                 {"Power function [W]": op["Power input [W]"]}
             )
+
+        if op["temperature"] is not None:
+            ambient_temperature = op["temperature"] + 273.15
+            experiment_parameter_values.update(
+                {"Ambient temperature [K]": ambient_temperature}
+            )
+
+            # If at the first operation, then the intial temperature
+            # should be the ambient temperature.
+            if op_number == 0:
+                experiment_parameter_values.update(
+                    {
+                        "Initial temperature [K]": ambient_temperature,
+                    }
+                )
+        else:
+            experiment_parameter_values.update(
+                {"Ambient temperature [K]": self._original_temperature}
+            )
+
         return experiment_parameter_values
 
     def set_parameters(self):


Test patch:
diff --git a/tests/unit/test_experiments/test_experiment.py b/tests/unit/test_experiments/test_experiment.py
index af0d5ea23..eca562126 100644
--- a/tests/unit/test_experiments/test_experiment.py
+++ b/tests/unit/test_experiments/test_experiment.py
@@ -21,162 +21,178 @@ class TestExperiment(unittest.TestCase):
 
         experiment = pybamm.Experiment(
             [
-                "Discharge at 1C for 0.5 hours [tag1]",
-                "Discharge at C/20 for 0.5 hours [tag2,tag3]",
-                "Charge at 0.5 C for 45 minutes",
-                "Discharge at 1 A for 0.5 hours",
-                "Charge at 200 mA for 45 minutes (1 minute period)",
-                "Discharge at 1W for 0.5 hours",
+                "Discharge at 1C for 0.5 hours at 27oC [tag1]",
+                "Discharge at C/20 for 0.5 hours at 29oC [tag2,tag3]",
+                "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) [tag1,tag3]",
                 "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",
-                "Run US06 (A)",
+                "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",
             ],
+            temperature=43,
             drive_cycles={"US06": drive_cycle},
             period="20 seconds",
         )
+        expected_result = [
+            {
+                "C-rate input [-]": 1.0,
+                "type": "C-rate",
+                "time": 1800.0,
+                "period": 20.0,
+                "temperature": 27.0,
+                "dc_data": None,
+                "string": "Discharge at 1C for 0.5 hours at 27oC",
+                "events": None,
+                "tags": ["tag1"],
+            },
+            {
+                "C-rate input [-]": 0.05,
+                "type": "C-rate",
+                "time": 1800.0,
+                "period": 20.0,
+                "temperature": 29.0,
+                "dc_data": None,
+                "string": "Discharge at C/20 for 0.5 hours at 29oC",
+                "events": None,
+                "tags": ["tag2", "tag3"],
+            },
+            {
+                "C-rate input [-]": -0.5,
+                "type": "C-rate",
+                "time": 2700.0,
+                "period": 20.0,
+                "temperature": -5.0,
+                "dc_data": None,
+                "string": "Charge at 0.5 C for 45 minutes at -5oC",
+                "events": None,
+                "tags": None,
+            },
+            {
+                "Current input [A]": 1.0,
+                "type": "current",
+                "time": 1800.0,
+                "period": 20.0,
+                "temperature": -5.1,
+                "dc_data": None,
+                "string": "Discharge at 1 A for 0.5 hours at -5.1oC",
+                "events": None,
+                "tags": None,
+            },
+            {
+                "Current input [A]": -0.2,
+                "type": "current",
+                "time": 2700.0,
+                "period": 60.0,
+                "temperature": 10.2,
+                "dc_data": None,
+                "string": "Charge at 200 mA for 45 minutes at 10.2oC",
+                "events": None,
+                "tags": None,
+            },
+            {
+                "Power input [W]": 1.0,
+                "type": "power",
+                "time": 1800.0,
+                "period": 20.0,
+                "temperature": -10.4,
+                "dc_data": None,
+                "string": "Discharge at 1W for 0.5 hours at -10.4oC",
+                "events": None,
+                "tags": None,
+            },
+            {
+                "Power input [W]": -0.2,
+                "type": "power",
+                "time": 2700.0,
+                "period": 20.0,
+                "temperature": 43,
+                "dc_data": None,
+                "string": "Charge at 200mW for 45 minutes",
+                "events": None,
+                "tags": None,
+            },
+            {
+                "Current input [A]": 0,
+                "type": "current",
+                "time": 600.0,
+                "period": 300.0,
+                "temperature": 43,
+                "dc_data": None,
+                "string": "Rest for 10 minutes",
+                "events": None,
+                "tags": ["tag1", "tag3"],
+            },
+            {
+                "Voltage input [V]": 1,
+                "type": "voltage",
+                "time": 20.0,
+                "period": 20.0,
+                "temperature": 43,
+                "dc_data": None,
+                "string": "Hold at 1V for 20 seconds",
+                "events": None,
+                "tags": None,
+            },
+            {
+                "C-rate input [-]": -1,
+                "type": "C-rate",
+                "time": None,
+                "period": 20.0,
+                "temperature": 43,
+                "dc_data": None,
+                "string": "Charge at 1 C until 4.1V",
+                "events": {"Voltage input [V]": 4.1, "type": "voltage"},
+                "tags": None,
+            },
+            {
+                "Voltage input [V]": 4.1,
+                "type": "voltage",
+                "time": None,
+                "period": 20.0,
+                "temperature": 43,
+                "dc_data": None,
+                "string": "Hold at 4.1 V until 50mA",
+                "events": {"Current input [A]": 0.05, "type": "current"},
+                "tags": None,
+            },
+            {
+                "Voltage input [V]": 3,
+                "type": "voltage",
+                "time": None,
+                "period": 20.0,
+                "temperature": 43,
+                "dc_data": None,
+                "string": "Hold at 3V until C/50",
+                "events": {"C-rate input [-]": 0.02, "type": "C-rate"},
+                "tags": None,
+            },
+            {
+                "C-rate input [-]": 1 / 3,
+                "type": "C-rate",
+                "time": 7200.0,
+                "period": 20.0,
+                "temperature": 26,
+                "dc_data": None,
+                "string": "Discharge at C/3 for 2 hours or until 2.5 V at 26oC",
+                "events": {"Voltage input [V]": 2.5, "type": "voltage"},
+                "tags": None,
+            },
+        ]
+
+        for expected, actual in zip(expected_result, experiment.operating_conditions):
+            for k in expected.keys():
+                # useful form for debugging
+                self.assertEqual([k, expected[k]], [k, actual[k]])
 
-        self.assertEqual(
-            experiment.operating_conditions[:-3],
-            [
-                {
-                    "C-rate input [-]": 1,
-                    "type": "C-rate",
-                    "time": 1800.0,
-                    "period": 20.0,
-                    "dc_data": None,
-                    "string": "Discharge at 1C for 0.5 hours",
-                    "events": None,
-                    "tags": ["tag1"],
-                },
-                {
-                    "C-rate input [-]": 0.05,
-                    "type": "C-rate",
-                    "time": 1800.0,
-                    "period": 20.0,
-                    "dc_data": None,
-                    "string": "Discharge at C/20 for 0.5 hours",
-                    "events": None,
-                    "tags": ["tag2", "tag3"],
-                },
-                {
-                    "C-rate input [-]": -0.5,
-                    "type": "C-rate",
-                    "time": 2700.0,
-                    "period": 20.0,
-                    "dc_data": None,
-                    "string": "Charge at 0.5 C for 45 minutes",
-                    "events": None,
-                    "tags": None,
-                },
-                {
-                    "Current input [A]": 1,
-                    "type": "current",
-                    "time": 1800.0,
-                    "period": 20.0,
-                    "dc_data": None,
-                    "string": "Discharge at 1 A for 0.5 hours",
-                    "events": None,
-                    "tags": None,
-                },
-                {
-                    "Current input [A]": -0.2,
-                    "type": "current",
-                    "time": 2700.0,
-                    "period": 60.0,
-                    "dc_data": None,
-                    "string": "Charge at 200 mA for 45 minutes",
-                    "events": None,
-                    "tags": None,
-                },
-                {
-                    "Power input [W]": 1,
-                    "type": "power",
-                    "time": 1800.0,
-                    "period": 20.0,
-                    "dc_data": None,
-                    "string": "Discharge at 1W for 0.5 hours",
-                    "events": None,
-                    "tags": None,
-                },
-                {
-                    "Power input [W]": -0.2,
-                    "type": "power",
-                    "time": 2700.0,
-                    "period": 20.0,
-                    "dc_data": None,
-                    "string": "Charge at 200mW for 45 minutes",
-                    "events": None,
-                    "tags": None,
-                },
-                {
-                    "Current input [A]": 0,
-                    "type": "current",
-                    "time": 600.0,
-                    "period": 300.0,
-                    "dc_data": None,
-                    "string": "Rest for 10 minutes",
-                    "events": None,
-                    "tags": ["tag1", "tag3"],
-                },
-                {
-                    "Voltage input [V]": 1,
-                    "type": "voltage",
-                    "time": 20.0,
-                    "period": 20.0,
-                    "dc_data": None,
-                    "string": "Hold at 1V for 20 seconds",
-                    "events": None,
-                    "tags": None,
-                },
-                {
-                    "C-rate input [-]": -1,
-                    "type": "C-rate",
-                    "time": None,
-                    "period": 20.0,
-                    "dc_data": None,
-                    "string": "Charge at 1 C until 4.1V",
-                    "events": {"Voltage input [V]": 4.1, "type": "voltage"},
-                    "tags": None,
-                },
-                {
-                    "Voltage input [V]": 4.1,
-                    "type": "voltage",
-                    "time": None,
-                    "period": 20.0,
-                    "dc_data": None,
-                    "string": "Hold at 4.1 V until 50mA",
-                    "events": {"Current input [A]": 0.05, "type": "current"},
-                    "tags": None,
-                },
-                {
-                    "Voltage input [V]": 3,
-                    "type": "voltage",
-                    "time": None,
-                    "period": 20.0,
-                    "dc_data": None,
-                    "string": "Hold at 3V until C/50",
-                    "events": {"C-rate input [-]": 0.02, "type": "C-rate"},
-                    "tags": None,
-                },
-                {
-                    "C-rate input [-]": 1 / 3,
-                    "type": "C-rate",
-                    "time": 7200.0,
-                    "period": 20.0,
-                    "dc_data": None,
-                    "string": "Discharge at C/3 for 2 hours or until 2.5 V",
-                    "events": {"Voltage input [V]": 2.5, "type": "voltage"},
-                    "tags": None,
-                },
-            ],
-        )
         # Calculation for operating conditions of drive cycle
         time_0 = drive_cycle[:, 0][-1]
         period_0 = np.min(np.diff(drive_cycle[:, 0]))
@@ -193,6 +209,7 @@ class TestExperiment(unittest.TestCase):
         self.assertEqual(experiment.operating_conditions[-3]["type"], "current")
         self.assertEqual(experiment.operating_conditions[-3]["time"], time_0)
         self.assertEqual(experiment.operating_conditions[-3]["period"], period_0)
+        self.assertEqual(experiment.operating_conditions[-3]["temperature"], -5)
         self.assertEqual(experiment.operating_conditions[-3]["tags"], None)
         np.testing.assert_array_equal(
             experiment.operating_conditions[-2]["dc_data"], drive_cycle_1
@@ -214,50 +231,56 @@ class TestExperiment(unittest.TestCase):
         experiment = pybamm.Experiment(
             [
                 (
-                    "Discharge at C/20 for 0.5 hours",
-                    "Charge at 0.5 C until 1V",
-                    "Hold at 1V until C/50",
+                    "Discharge at C/20 for 0.5 hours at 34 oC",
+                    "Charge at 0.5 C until 1V at 32 oC",
+                    "Hold at 1V until C/50 at 32 oC",
                     "Discharge at C/20 for 0.5 hours",
                 ),
             ],
             cccv_handling="ode",
         )
-        self.assertEqual(
-            experiment.operating_conditions,
-            [
-                {
-                    "C-rate input [-]": 0.05,
-                    "type": "C-rate",
-                    "time": 1800.0,
-                    "period": 60.0,
-                    "dc_data": None,
-                    "string": "Discharge at C/20 for 0.5 hours",
-                    "events": None,
-                    "tags": None,
-                },
-                {
-                    "type": "CCCV",
-                    "C-rate input [-]": -0.5,
-                    "Voltage input [V]": 1,
-                    "time": None,
-                    "period": 60.0,
-                    "dc_data": None,
-                    "string": "Charge at 0.5 C until 1V then hold at 1V until C/50",
-                    "events": {"C-rate input [-]": 0.02, "type": "C-rate"},
-                    "tags": None,
-                },
-                {
-                    "C-rate input [-]": 0.05,
-                    "type": "C-rate",
-                    "time": 1800.0,
-                    "period": 60.0,
-                    "dc_data": None,
-                    "string": "Discharge at C/20 for 0.5 hours",
-                    "events": None,
-                    "tags": None,
-                },
-            ],
-        )
+
+        expected_result = [
+            {
+                "C-rate input [-]": 0.05,
+                "type": "C-rate",
+                "time": 1800.0,
+                "period": 60.0,
+                "temperature": 34.0,
+                "dc_data": None,
+                "string": "Discharge at C/20 for 0.5 hours at 34 oC",
+                "events": None,
+                "tags": None,
+            },
+            {
+                "type": "CCCV",
+                "C-rate input [-]": -0.5,
+                "Voltage input [V]": 1,
+                "time": None,
+                "period": 60.0,
+                "temperature": 32.0,
+                "dc_data": None,
+                "string": "Charge at 0.5 C until 1V at 32 oC "
+                "then hold at 1V until C/50 at 32 oC",
+                "events": {"C-rate input [-]": 0.02, "type": "C-rate"},
+                "tags": None,
+            },
+            {
+                "C-rate input [-]": 0.05,
+                "type": "C-rate",
+                "time": 1800.0,
+                "period": 60.0,
+                "temperature": None,
+                "dc_data": None,
+                "string": "Discharge at C/20 for 0.5 hours",
+                "events": None,
+                "tags": None,
+            },
+        ]
+
+        for expected, actual in zip(expected_result, experiment.operating_conditions):
+            for k in expected.keys():
+                self.assertEqual([k, expected[k]], [k, actual[k]])
 
         # Cases that don't quite match shouldn't do CCCV setup
         experiment = pybamm.Experiment(
@@ -275,6 +298,7 @@ class TestExperiment(unittest.TestCase):
                     "type": "C-rate",
                     "time": None,
                     "period": 60.0,
+                    "temperature": None,
                     "dc_data": None,
                     "string": "Charge at 0.5 C until 2V",
                     "events": {"Voltage input [V]": 2, "type": "voltage"},
@@ -285,6 +309,7 @@ class TestExperiment(unittest.TestCase):
                     "type": "voltage",
                     "time": None,
                     "period": 60.0,
+                    "temperature": None,
                     "dc_data": None,
                     "string": "Hold at 1V until C/50",
                     "events": {"C-rate input [-]": 0.02, "type": "C-rate"},
@@ -307,6 +332,7 @@ class TestExperiment(unittest.TestCase):
                     "type": "C-rate",
                     "time": 120.0,
                     "period": 60.0,
+                    "temperature": None,
                     "dc_data": None,
                     "string": "Charge at 0.5 C for 2 minutes",
                     "events": None,
@@ -317,6 +343,7 @@ class TestExperiment(unittest.TestCase):
                     "type": "voltage",
                     "time": None,
                     "period": 60.0,
+                    "temperature": None,
                     "dc_data": None,
                     "string": "Hold at 1V until C/50",
                     "events": {"C-rate input [-]": 0.02, "type": "C-rate"},
@@ -341,6 +368,7 @@ class TestExperiment(unittest.TestCase):
                     "type": "C-rate",
                     "time": 1800.0,
                     "period": 60.0,
+                    "temperature": None,
                     "dc_data": None,
                     "string": "Discharge at C/20 for 0.5 hours",
                     "events": None,
@@ -351,6 +379,7 @@ class TestExperiment(unittest.TestCase):
                     "type": "C-rate",
                     "time": 2700.0,
                     "period": 60.0,
+                    "temperature": None,
                     "dc_data": None,
                     "string": "Charge at C/5 for 45 minutes",
                     "events": None,
@@ -361,6 +390,7 @@ class TestExperiment(unittest.TestCase):
                     "type": "C-rate",
                     "time": 1800.0,
                     "period": 60.0,
+                    "temperature": None,
                     "dc_data": None,
                     "string": "Discharge at C/20 for 0.5 hours",
                     "events": None,
@@ -371,6 +401,7 @@ class TestExperiment(unittest.TestCase):
                     "type": "C-rate",
                     "time": 2700.0,
                     "period": 60.0,
+                    "temperature": None,
                     "dc_data": None,
                     "string": "Charge at C/5 for 45 minutes",
                     "events": None,
@@ -408,7 +439,7 @@ class TestExperiment(unittest.TestCase):
             TypeError, "Operating conditions should be strings or tuples of strings"
         ):
             pybamm.Experiment([(1, 2, 3)])
-        with self.assertRaisesRegex(ValueError, "Operating conditions must contain"):
+        with self.assertRaisesRegex(ValueError, "Operating conditions must"):
             pybamm.Experiment(["Discharge at 1 A at 2 hours"])
         with self.assertRaisesRegex(ValueError, "Instruction must be"):
             pybamm.Experiment(["Run at 1 A for 2 hours"])
@@ -418,12 +449,35 @@ class TestExperiment(unittest.TestCase):
             pybamm.Experiment(["Run at at 1 A for 2 hours"])
         with self.assertRaisesRegex(ValueError, "Instruction must be"):
             pybamm.Experiment(["Play at 1 A for 2 hours"])
+        with self.assertRaisesRegex(ValueError, "Operating conditions must"):
+            pybamm.Experiment(["Do at 1 A"])
+        with self.assertRaisesRegex(ValueError, "Instruction must be"):
+            pybamm.Experiment(["Run US06 at 1 A"])
         with self.assertRaisesRegex(ValueError, "Instruction"):
             pybamm.Experiment(["Cell Charge at 1 A for 2 hours"])
         with self.assertRaisesRegex(ValueError, "units must be"):
             pybamm.Experiment(["Discharge at 1 B for 2 hours"])
         with self.assertRaisesRegex(ValueError, "time units must be"):
             pybamm.Experiment(["Discharge at 1 A for 2 years"])
+        with self.assertRaisesRegex(ValueError, "More than one temperature found"):
+            pybamm.Experiment(["Discharge at 1 A for 2 hours at 25oC at 30oC"])
+        with self.assertRaisesRegex(
+            ValueError, "The temperature for the CC and CV steps"
+        ):
+            pybamm.Experiment(
+                [
+                    (
+                        "Discharge at 1A until 3.2V at 24oC",
+                        "Hold at 3.2V until C/50 at 27oC",
+                    )
+                ],
+                cccv_handling="ode",
+            )
+
+        with self.assertRaisesRegex(
+            ValueError, "Temperature not written correctly on step"
+        ):
+            pybamm.Experiment(["Discharge at 1 A for 2 hours 25oC"])
 
     def test_termination(self):
         experiment = pybamm.Experiment(["Discharge at 1 C for 20 seconds"])
diff --git a/tests/unit/test_experiments/test_simulation_with_experiment.py b/tests/unit/test_experiments/test_simulation_with_experiment.py
index 4f38100fb..39ac926a6 100644
--- a/tests/unit/test_experiments/test_simulation_with_experiment.py
+++ b/tests/unit/test_experiments/test_simulation_with_experiment.py
@@ -55,12 +55,13 @@ class TestSimulationExperiment(unittest.TestCase):
         experiment = pybamm.Experiment(
             [
                 (
-                    "Discharge at C/20 for 1 hour",
-                    "Charge at 1 A until 4.1 V",
-                    "Hold at 4.1 V until C/2",
+                    "Discharge at C/20 for 1 hour at 30.5oC",
+                    "Charge at 1 A until 4.1 V at 24oC",
+                    "Hold at 4.1 V until C/2 at 24oC",
                     "Discharge at 2 W for 1 hour",
                 )
-            ]
+            ],
+            temperature=-14,
         )
         model = pybamm.lithium_ion.SPM()
         sim = pybamm.Simulation(model, experiment=experiment)
@@ -79,6 +80,22 @@ class TestSimulationExperiment(unittest.TestCase):
             sol.cycles[0].steps[3]["Power [W]"].data, 2, decimal=5
         )
 
+        np.testing.assert_array_equal(
+            sol.cycles[0].steps[0]["Ambient temperature [C]"].data[0], 30.5
+        )
+
+        np.testing.assert_array_equal(
+            sol.cycles[0].steps[1]["Ambient temperature [C]"].data[0], 24
+        )
+
+        np.testing.assert_array_equal(
+            sol.cycles[0].steps[2]["Ambient temperature [C]"].data[0], 24
+        )
+
+        np.testing.assert_array_equal(
+            sol.cycles[0].steps[3]["Ambient temperature [C]"].data[0], -14
+        )
+
         for i, step in enumerate(sol.cycles[0].steps[:-1]):
             len_rhs = sol.all_models[0].concatenated_rhs.size
             y_left = step.all_ys[-1][:len_rhs, -1]
@@ -114,8 +131,8 @@ class TestSimulationExperiment(unittest.TestCase):
         experiment = pybamm.Experiment(
             [
                 (
-                    "Discharge at C/20 for 1 hour",
-                    "Charge at C/20 until 4.1 V",
+                    "Discharge at C/20 for 1 hour at 24oC",
+                    "Charge at C/20 until 4.1 V at 26oC",
                 )
             ]
             * 3
@@ -134,9 +151,9 @@ class TestSimulationExperiment(unittest.TestCase):
         experiment_2step = pybamm.Experiment(
             [
                 (
-                    "Discharge at C/20 for 1 hour",
-                    "Charge at 1 A until 4.1 V",
-                    "Hold at 4.1 V until C/2",
+                    "Discharge at C/20 for 1 hour at 20oC",
+                    "Charge at 1 A until 4.1 V at 24oC",
+                    "Hold at 4.1 V until C/2 at 24oC",
                     "Discharge at 2 W for 1 hour",
                 ),
             ],
@@ -144,9 +161,9 @@ class TestSimulationExperiment(unittest.TestCase):
         experiment_ode = pybamm.Experiment(
             [
                 (
-                    "Discharge at C/20 for 1 hour",
-                    "Charge at 1 A until 4.1 V",
-                    "Hold at 4.1 V until C/2",
+                    "Discharge at C/20 for 1 hour at 20oC",
+                    "Charge at 1 A until 4.1 V at 24oC",
+                    "Hold at 4.1 V until C/2 at 24oC",
                     "Discharge at 2 W for 1 hour",
                 ),
             ],
@@ -169,6 +186,12 @@ class TestSimulationExperiment(unittest.TestCase):
             solutions[1]["Current [A]"].data,
             decimal=0,
         )
+
+        np.testing.assert_array_equal(
+            solutions[0]["Ambient temperature [C]"].data,
+            solutions[1]["Ambient temperature [C]"].data,
+        )
+
         self.assertEqual(solutions[1].termination, "final time")
 
     @unittest.skipIf(not pybamm.have_idaklu(), "idaklu solver is not installed")
@@ -209,7 +232,7 @@ class TestSimulationExperiment(unittest.TestCase):
         experiment = pybamm.Experiment(
             [
                 (
-                    "Run drive_cycle (A)",
+                    "Run drive_cycle (A) at 35oC",
                     "Run drive_cycle (V)",
                     "Run drive_cycle (W)",
                 )
@@ -219,7 +242,7 @@ class TestSimulationExperiment(unittest.TestCase):
         model = pybamm.lithium_ion.SPM()
         sim = pybamm.Simulation(model, experiment=experiment)
         sim.build_for_experiment()
-        self.assertIn(("Run drive_cycle (A)"), sim.op_string_to_model)
+        self.assertIn(("Run drive_cycle (A) at 35oC"), sim.op_string_to_model)
         self.assertIn(("Run drive_cycle (V)"), sim.op_string_to_model)
         self.assertIn(("Run drive_cycle (W)"), sim.op_string_to_model)
 
diff --git a/tests/unit/test_parameters/test_ecm_parameters.py b/tests/unit/test_parameters/test_ecm_parameters.py
index 01ebf3903..53e79cd16 100644
--- a/tests/unit/test_parameters/test_ecm_parameters.py
+++ b/tests/unit/test_parameters/test_ecm_parameters.py
@@ -7,11 +7,10 @@ import unittest
 
 values = {
     "Initial SoC": 0.5,
-    "Initial cell temperature [degC]": 25,
-    "Initial jig temperature [degC]": 25,
+    "Initial temperature [K]": 25 + 273.15,
     "Cell capacity [A.h]": 100,
     "Nominal cell capacity [A.h]": 100,
-    "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,
@@ -48,8 +47,6 @@ class TestEcmParameters(unittest.TestCase):
             (param.current_with_time, "Current function [A]"),
             (param.dimensional_current_density_with_time, "Current function [A]"),
             (param.initial_soc, "Initial SoC"),
-            (param.initial_T_cell, "Initial cell temperature [degC]"),
-            (param.initial_T_jig, "Initial jig temperature [degC]"),
         ]
 
         for symbol, key in simpled_mapped_parameters:
@@ -57,6 +54,12 @@ class TestEcmParameters(unittest.TestCase):
             expected_value = values[key]
             self.assertEqual(value, expected_value)
 
+        value = parameter_values.evaluate(param.initial_T_cell)
+        self.assertEqual(value, values["Initial temperature [K]"] - 273.15)
+
+        value = parameter_values.evaluate(param.initial_T_jig)
+        self.assertEqual(value, values["Initial temperature [K]"] - 273.15)
+
         compatibility_parameters = [
             (param.I_typ, 1),
             (param.n_electrodes_parallel, 1),
@@ -74,7 +77,6 @@ class TestEcmParameters(unittest.TestCase):
         sym = pybamm.Scalar(1)
 
         mapped_functions = [
-            (param.T_amb(sym), "Ambient temperature [degC]"),
             (param.ocv(sym), "Open circuit voltage [V]"),
             (param.rcr_element("R0 [Ohm]", sym, sym, sym), "R0 [Ohm]"),
             (param.rcr_element("R1 [Ohm]", sym, sym, sym), "R1 [Ohm]"),
@@ -88,6 +90,9 @@ class TestEcmParameters(unittest.TestCase):
             expected_value = values[key]
             self.assertEqual(value, expected_value)
 
+        value = parameter_values.evaluate(param.T_amb(sym))
+        self.assertEqual(value, values["Ambient temperature [K]"] - 273.15)
+
 
 if __name__ == "__main__":
     print("Add -v for more debug output")