From b1a9184232bce26cecf97a6ceaa094c982d580d0 Mon Sep 17 00:00:00 2001 From: Ali Hussain Umar Bhatti <65511923+alibh95@users.noreply.github.com> Date: Thu, 29 Apr 2021 01:45:54 +0500 Subject: [PATCH 01/12] Simulate Drive Cycle In Experiment This commit simulates drive cycle ('A' or 'V' or 'W') in experiment only if drive cycle of particular type is initial instruction in experiment and there are no further drive cycle related instructions. --- examples/scripts/experiment_drive_cycle.py | 87 +++++++++++++++ pybamm/experiments/experiment.py | 2 +- pybamm/simulation.py | 124 ++++++++++++++------- pybamm/solvers/base_solver.py | 9 ++ 4 files changed, 180 insertions(+), 42 deletions(-) create mode 100644 examples/scripts/experiment_drive_cycle.py diff --git a/examples/scripts/experiment_drive_cycle.py b/examples/scripts/experiment_drive_cycle.py new file mode 100644 index 0000000000..5ee7fbab2c --- /dev/null +++ b/examples/scripts/experiment_drive_cycle.py @@ -0,0 +1,87 @@ +# +# Constant-current constant-voltage charge with US06 Drive Cycle using Experiment Class. +# +import pybamm +import pandas as pd +import os + +os.chdir(pybamm.__path__[0] + "/..") + +pybamm.set_logging_level("INFO") + +# import drive cycle from file +drive_cycle_current = pd.read_csv("pybamm/input/drive_cycles/US06.csv", + comment="#", + header=None).to_numpy() + + +# Map Drive Cycle +def Map_Drive_Cycle(x, min_ip_value, max_ip_value, min_op_value, max_op_value): + return (x - min_ip_value) * (max_op_value - min_op_value) / ( + max_ip_value - min_ip_value) + min_op_value + + +# Map Current to Voltage +temp_volts = np.array([]) +min_ip_value = drive_cycle_current[:, 1].min() +max_ip_value = drive_cycle_current[:, 1].max() +min_Voltage = 3.5 +max_Voltage = 4.1 +for I in drive_cycle_current[:, 1]: + temp_volts = np.append( + temp_volts, + Map_Drive_Cycle(I, min_ip_value, max_ip_value, min_Voltage, + max_Voltage)) + +drive_cycle_voltage = drive_cycle_current +drive_cycle_voltage = np.column_stack((np.delete(drive_cycle_voltage, 1, + 1), np.array(temp_volts))) + +# Map Current to Power +temp_volts = np.array([]) +min_ip_value = drive_cycle_current[:, 1].min() +max_ip_value = drive_cycle_current[:, 1].max() +min_Power = 2.5 +max_Power = 5.5 +for I in drive_cycle_current[:, 1]: + temp_volts = np.append( + temp_volts, + Map_Drive_Cycle(I, min_ip_value, max_ip_value, min_Power, max_Power)) + +drive_cycle_power = drive_cycle_current +drive_cycle_power = np.column_stack((np.delete(drive_cycle_power, 1, + 1), np.array(temp_volts))) +experiment = pybamm.Experiment([ +# "Charge at 1 A until 4.1 V", +# "Hold at 4.1 V until 50 mA", +# "Rest for 1 hour", + "Run US06_A (A), +# "Rest for 1 hour", +] +# + [ +# "Charge at 1 A until 4.1 V", +# "Hold at 4.1 V until 50 mA", +# "Rest for 1 hour", +# "Run US06_V (V)", +# "Rest for 1 hour", +# ] + [ +# "Charge at 1 A until 4.1 V", +# "Hold at 4.1 V until 50 mA", +# "Rest for 1 hour", +# "Run US06_W (W)", +# "Rest for 1 hour", +# ] + ,drive_cycles={ + "US06_A": drive_cycle_current, + "US06_V": drive_cycle_voltage, + "US06_W": drive_cycle_power, + }) + +model = pybamm.lithium_ion.DFN() +sim = pybamm.Simulation(model, + experiment=experiment, + solver=pybamm.CasadiSolver()) +sim.solve() + +# Show all plots +sim.plot() diff --git a/pybamm/experiments/experiment.py b/pybamm/experiments/experiment.py index 724d913565..2ef16d8878 100644 --- a/pybamm/experiments/experiment.py +++ b/pybamm/experiments/experiment.py @@ -233,7 +233,7 @@ def read_string(self, cond, drive_cycles): examples ) ) - return electric + (time,) + (period,), events + return electric + (time,) + (period,) + (cond,), events def extend_drive_cycle(self, drive_cycle, end_time): "Extends the drive cycle to enable for event" diff --git a/pybamm/simulation.py b/pybamm/simulation.py index e5c4f0e3a3..ea637dc77f 100644 --- a/pybamm/simulation.py +++ b/pybamm/simulation.py @@ -170,44 +170,82 @@ def set_up_experiment(self, model, experiment): self._experiment_inputs = [] self._experiment_times = [] for op, events in zip(experiment.operating_conditions, experiment.events): - if op[1] in ["A", "C"]: - # Update inputs for constant current + if isinstance(op[0], np.ndarray): + # self.operating_mode = "drive cycle" ########!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # If ndarray is recived from, create interpolant + # create interpolant + timescale = self._parameter_values.evaluate(model.timescale) + drive_cycle_interpolant = pybamm.Interpolant( + op[0][:, 0], op[0][:, 1], timescale * pybamm.t + ) + # print(type(drive_cycle_interpolant)) #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if op[1] == "A": - I = op[0] - else: - # Scale C-rate with capacity to obtain current - capacity = self._parameter_values["Nominal cell capacity [A.h]"] - I = op[0] * capacity - operating_inputs = { - "Current switch": 1, - "Voltage switch": 0, - "Power switch": 0, - "Current input [A]": I, - "Voltage input [V]": 0, # doesn't matter - "Power input [W]": 0, # doesn't matter - } - elif op[1] == "V": - # Update inputs for constant voltage - V = op[0] - operating_inputs = { - "Current switch": 0, - "Voltage switch": 1, - "Power switch": 0, - "Current input [A]": 0, # doesn't matter - "Voltage input [V]": V, - "Power input [W]": 0, # doesn't matter - } - elif op[1] == "W": - # Update inputs for constant power - P = op[0] - operating_inputs = { - "Current switch": 0, - "Voltage switch": 0, - "Power switch": 1, - "Current input [A]": 0, # doesn't matter - "Voltage input [V]": 0, # doesn't matter - "Power input [W]": P, - } + operating_inputs = { + "Current switch": 1, + "Voltage switch": 0, + "Power switch": 0, + "Current input [A]": drive_cycle_interpolant, + "Voltage input [V]": 0, # doesn't matter + "Power input [W]": 0, # doesn't matter + } + if op[1] == "V": + operating_inputs = { + "Current switch": 0, + "Voltage switch": 1, + "Power switch": 0, + "Current input [A]": 0, + "Voltage input [V]": drive_cycle_interpolant, # doesn't matter + "Power input [W]": 0, # doesn't matter + } + if op[1] == "W": + operating_inputs = { + "Current switch": 0, + "Voltage switch": 0, + "Power switch": 1, + "Current input [A]": 0, + "Voltage input [V]": 0, # doesn't matter + "Power input [W]": drive_cycle_interpolant, # doesn't matter + } + else: + # self.operating_mode = "experiment" ########!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + if op[1] in ["A", "C"]: + # Update inputs for constant current + if op[1] == "A": + I = op[0] + else: + # Scale C-rate with capacity to obtain current + capacity = self._parameter_values["Nominal cell capacity [A.h]"] + I = op[0] * capacity + operating_inputs = { + "Current switch": 1, + "Voltage switch": 0, + "Power switch": 0, + "Current input [A]": I, + "Voltage input [V]": 0, # doesn't matter + "Power input [W]": 0, # doesn't matter + } + elif op[1] == "V": + # Update inputs for constant voltage + V = op[0] + operating_inputs = { + "Current switch": 0, + "Voltage switch": 1, + "Power switch": 0, + "Current input [A]": 0, # doesn't matter + "Voltage input [V]": V, + "Power input [W]": 0, # doesn't matter + } + elif op[1] == "W": + # Update inputs for constant power + P = op[0] + operating_inputs = { + "Current switch": 0, + "Voltage switch": 0, + "Power switch": 1, + "Current input [A]": 0, # doesn't matter + "Voltage input [V]": 0, # doesn't matter + "Power input [W]": P, + } # Update period operating_inputs["period"] = op[3] # Update events @@ -319,7 +357,7 @@ def set_up_model_for_experiment_old(self, model): self.model = new_model self.op_conds_to_model_and_param = { - op_cond[:2]: (new_model, self.parameter_values) + op_cond[-1]: (new_model, self.parameter_values) for op_cond in set(self.experiment.operating_conditions) } self.op_conds_to_built_models = None @@ -339,7 +377,7 @@ def set_up_model_for_experiment_new(self, model): ): # Create model for this operating condition if it has not already been seen # before - if op_cond[:2] not in self.op_conds_to_model_and_param: + if op_cond[-1] not in self.op_conds_to_model_and_param: if op_inputs["Current switch"] == 1: # Current control # Make a new copy of the model (we will update events later)) @@ -439,8 +477,10 @@ def set_up_model_for_experiment_new(self, model): {"Power function [W]": op_inputs["Power input [W]"]}, check_already_exists=False, ) + + # print(new_parameter_values) #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!) - self.op_conds_to_model_and_param[op_cond[:2]] = ( + self.op_conds_to_model_and_param[op_cond[-1]] = ( new_model, new_parameter_values, ) @@ -691,7 +731,9 @@ def solve( dt = self._experiment_times[idx] op_conds_str = self.experiment.operating_conditions_strings[idx] op_conds_elec = self.experiment.operating_conditions[idx][:2] - model = self.op_conds_to_built_models[op_conds_elec] + # print('Operating Conditions', op_conds_elec) + # model = self.op_conds_to_built_models[op_conds_elec] + model = self.op_conds_to_built_models[op_conds_str] # Use 1-indexing for printing cycle number as it is more # human-intuitive pybamm.logger.notice( diff --git a/pybamm/solvers/base_solver.py b/pybamm/solvers/base_solver.py index 77069c78ce..736d63a53c 100644 --- a/pybamm/solvers/base_solver.py +++ b/pybamm/solvers/base_solver.py @@ -907,7 +907,15 @@ def step( # Set up external variables and inputs external_variables = external_variables or {} inputs = inputs or {} + if isinstance(inputs['Current input [A]'], pybamm.Interpolant): + del inputs['Current input [A]'] + elif isinstance(inputs['Voltage input [V]'], pybamm.Interpolant): + del inputs['Voltage input [V]'] + elif isinstance(inputs['Power input [W]'], pybamm.Interpolant): + del inputs['Power input [W]'] + print("First Inputs",inputs) ext_and_inputs = {**external_variables, **inputs} + print("First EXT and Inputs",ext_and_inputs) # Check that any inputs that may affect the scaling have not changed # Set model timescale @@ -1199,6 +1207,7 @@ def __init__(self, function, model): def __call__(self, inputs): if self.form == "casadi": if isinstance(inputs, dict): + print(inputs) #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! inputs = casadi.vertcat(*[x for x in inputs.values()]) return self._function(0, self.y_dummy, inputs) else: From 351183b2e8bd6fd11da998625d0fc029d8f99c1b Mon Sep 17 00:00:00 2001 From: Ali Hussain Umar Bhatti <65511923+alibh95@users.noreply.github.com> Date: Fri, 25 Jun 2021 15:35:02 +0500 Subject: [PATCH 02/12] Format Code --- pybamm/simulation.py | 18 ++++++------------ pybamm/solvers/base_solver.py | 3 --- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/pybamm/simulation.py b/pybamm/simulation.py index ea637dc77f..08a9773049 100644 --- a/pybamm/simulation.py +++ b/pybamm/simulation.py @@ -171,14 +171,12 @@ def set_up_experiment(self, model, experiment): self._experiment_times = [] for op, events in zip(experiment.operating_conditions, experiment.events): if isinstance(op[0], np.ndarray): - # self.operating_mode = "drive cycle" ########!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # If ndarray is recived from, create interpolant # create interpolant timescale = self._parameter_values.evaluate(model.timescale) drive_cycle_interpolant = pybamm.Interpolant( op[0][:, 0], op[0][:, 1], timescale * pybamm.t ) - # print(type(drive_cycle_interpolant)) #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if op[1] == "A": operating_inputs = { "Current switch": 1, @@ -193,8 +191,8 @@ def set_up_experiment(self, model, experiment): "Current switch": 0, "Voltage switch": 1, "Power switch": 0, - "Current input [A]": 0, - "Voltage input [V]": drive_cycle_interpolant, # doesn't matter + "Current input [A]": 0, # doesn't matter + "Voltage input [V]": drive_cycle_interpolant, "Power input [W]": 0, # doesn't matter } if op[1] == "W": @@ -202,12 +200,11 @@ def set_up_experiment(self, model, experiment): "Current switch": 0, "Voltage switch": 0, "Power switch": 1, - "Current input [A]": 0, + "Current input [A]": 0, # doesn't matter "Voltage input [V]": 0, # doesn't matter - "Power input [W]": drive_cycle_interpolant, # doesn't matter + "Power input [W]": drive_cycle_interpolant, } else: - # self.operating_mode = "experiment" ########!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if op[1] in ["A", "C"]: # Update inputs for constant current if op[1] == "A": @@ -460,7 +457,8 @@ def set_up_model_for_experiment_new(self, model): for event in new_model.events if event.name not in ["Minimum voltage", "Maximum voltage"] ] - +# Make Interpolant Here + print("OK GEE") # Update parameter values new_parameter_values = self.parameter_values.copy() if op_inputs["Current switch"] == 1: @@ -731,8 +729,6 @@ def solve( dt = self._experiment_times[idx] op_conds_str = self.experiment.operating_conditions_strings[idx] op_conds_elec = self.experiment.operating_conditions[idx][:2] - # print('Operating Conditions', op_conds_elec) - # model = self.op_conds_to_built_models[op_conds_elec] model = self.op_conds_to_built_models[op_conds_str] # Use 1-indexing for printing cycle number as it is more # human-intuitive @@ -754,9 +750,7 @@ def solve( ) steps.append(step_solution) current_solution = step_solution - cycle_solution = cycle_solution + step_solution - # Only allow events specified by experiment if not ( cycle_solution is None diff --git a/pybamm/solvers/base_solver.py b/pybamm/solvers/base_solver.py index 736d63a53c..34f9f9a2ec 100644 --- a/pybamm/solvers/base_solver.py +++ b/pybamm/solvers/base_solver.py @@ -913,9 +913,7 @@ def step( del inputs['Voltage input [V]'] elif isinstance(inputs['Power input [W]'], pybamm.Interpolant): del inputs['Power input [W]'] - print("First Inputs",inputs) ext_and_inputs = {**external_variables, **inputs} - print("First EXT and Inputs",ext_and_inputs) # Check that any inputs that may affect the scaling have not changed # Set model timescale @@ -1207,7 +1205,6 @@ def __init__(self, function, model): def __call__(self, inputs): if self.form == "casadi": if isinstance(inputs, dict): - print(inputs) #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! inputs = casadi.vertcat(*[x for x in inputs.values()]) return self._function(0, self.y_dummy, inputs) else: From c8630d806d49feb7ac1cbf63e55ab2055fa06852 Mon Sep 17 00:00:00 2001 From: Ferran Brosa Planella Date: Tue, 9 Nov 2021 14:01:49 +0000 Subject: [PATCH 03/12] #1788 implement solve experiment drive cycles --- pybamm/experiments/experiment.py | 8 ++++++-- pybamm/simulation.py | 35 +++++++++++++++++++++++--------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/pybamm/experiments/experiment.py b/pybamm/experiments/experiment.py index 125bc5bc81..222d31703b 100644 --- a/pybamm/experiments/experiment.py +++ b/pybamm/experiments/experiment.py @@ -223,26 +223,29 @@ def read_string(self, cond, drive_cycles): drive_cycles[cond_list[1]], end_time ) # Drive cycle as numpy array + dc_name = cond_list[1] + "_ext_{}".format(end_time) dc_data = ext_drive_cycle # Find the type of drive cycle ("A", "V", or "W") typ = cond_list[2][1] - electric = (dc_data, typ) + electric = (dc_name, typ) time = ext_drive_cycle[:, 0][-1] period = np.min(np.diff(ext_drive_cycle[:, 0])) events = None else: # e.g. Run US06 # Drive cycle as numpy array + dc_name = cond_list[1] dc_data = drive_cycles[cond_list[1]] # Find the type of drive cycle ("A", "V", or "W") typ = cond_list[2][1] - electric = (dc_data, typ) + electric = (dc_name, typ) # Set time and period to 1 second for first step and # then calculate the difference in consecutive time steps time = drive_cycles[cond_list[1]][:, 0][-1] period = np.min(np.diff(drive_cycles[cond_list[1]][:, 0])) events = None else: + dc_data = None if "for" in cond and "or until" in cond: # e.g. for 3 hours or until 4.2 V cond_list = cond.split() @@ -278,6 +281,7 @@ def read_string(self, cond, drive_cycles): "time": time, "period": period, "cond": cond, + "dc_data": dc_data, }, events def extend_drive_cycle(self, drive_cycle, end_time): diff --git a/pybamm/simulation.py b/pybamm/simulation.py index 075678f8e7..1ee2488f1a 100644 --- a/pybamm/simulation.py +++ b/pybamm/simulation.py @@ -170,25 +170,33 @@ def set_up_experiment(self, model, experiment): "Power switch": 0, "CCCV switch": 0, "Current input [A]": 0, - "Voltage input [V]": 0, # doesn't matter - "Power input [W]": 0, # doesn't matter + "Voltage input [V]": 0, + "Power input [W]": 0, } op_control = op["electric"][1] - if isinstance(op[0], np.ndarray): + if op["dc_data"] is not None: # If ndarray is recived from, create interpolant timescale = self._parameter_values.evaluate(model.timescale) drive_cycle_interpolant = pybamm.Interpolant( - op[0][:, 0], op[0][:, 1], timescale * pybamm.t + op["dc_data"][:, 0], + op["dc_data"][:, 1], + timescale * (pybamm.t - pybamm.InputParameter("start time")), ) if op_control == "A": operating_inputs.update( - {"Current switch": 1, "Current input [A]": drive_cycle_interpolant} + { + "Current switch": 1, + "Current input [A]": drive_cycle_interpolant, + } ) - if op[1] == "V": + if op_control == "V": operating_inputs.update( - {"Voltage switch": 1, "Voltage input [V]": drive_cycle_interpolant} + { + "Voltage switch": 1, + "Voltage input [V]": drive_cycle_interpolant, + } ) - if op[1] == "W": + if op_control == "W": operating_inputs.update( {"Power switch": 1, "Power input [W]": drive_cycle_interpolant} ) @@ -221,7 +229,9 @@ def set_up_experiment(self, model, experiment): elif op_control == "V": # Update inputs for constant voltage V = op["electric"][0] - operating_inputs.update({"Voltage switch": 1, "Voltage input [V]": V}) + operating_inputs.update( + {"Voltage switch": 1, "Voltage input [V]": V} + ) elif op_control == "W": # Update inputs for constant power P = op["electric"][0] @@ -872,6 +882,11 @@ def solve( f"step {step_num}/{cycle_length}: {op_conds_str}" ) inputs.update(exp_inputs) + if current_solution is None: + start_time = 0 + else: + start_time = current_solution.t[-1] + inputs.update({"start time": start_time}) kwargs["inputs"] = inputs # Make sure we take at least 2 timesteps npts = max(int(round(dt / exp_inputs["period"])) + 1, 2) @@ -1204,4 +1219,4 @@ def save(self, filename): def load_sim(filename): """Load a saved simulation""" - return pybamm.load(filename) \ No newline at end of file + return pybamm.load(filename) From 855b972a83f6d1a055930a7c93c4a21c3f9b81d5 Mon Sep 17 00:00:00 2001 From: Ferran Brosa Planella Date: Tue, 9 Nov 2021 14:02:05 +0000 Subject: [PATCH 04/12] #1788 add experiment drive cycles example --- examples/scripts/experiment_drive_cycle.py | 85 ++++++++++++---------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/examples/scripts/experiment_drive_cycle.py b/examples/scripts/experiment_drive_cycle.py index 5ee7fbab2c..e407bebf5b 100644 --- a/examples/scripts/experiment_drive_cycle.py +++ b/examples/scripts/experiment_drive_cycle.py @@ -2,6 +2,7 @@ # Constant-current constant-voltage charge with US06 Drive Cycle using Experiment Class. # import pybamm +import numpy as np import pandas as pd import os @@ -10,15 +11,16 @@ pybamm.set_logging_level("INFO") # import drive cycle from file -drive_cycle_current = pd.read_csv("pybamm/input/drive_cycles/US06.csv", - comment="#", - header=None).to_numpy() +drive_cycle_current = pd.read_csv( + "pybamm/input/drive_cycles/US06.csv", comment="#", header=None +).to_numpy() # Map Drive Cycle def Map_Drive_Cycle(x, min_ip_value, max_ip_value, min_op_value, max_op_value): return (x - min_ip_value) * (max_op_value - min_op_value) / ( - max_ip_value - min_ip_value) + min_op_value + max_ip_value - min_ip_value + ) + min_op_value # Map Current to Voltage @@ -30,12 +32,13 @@ def Map_Drive_Cycle(x, min_ip_value, max_ip_value, min_op_value, max_op_value): for I in drive_cycle_current[:, 1]: temp_volts = np.append( temp_volts, - Map_Drive_Cycle(I, min_ip_value, max_ip_value, min_Voltage, - max_Voltage)) + Map_Drive_Cycle(I, min_ip_value, max_ip_value, min_Voltage, max_Voltage), + ) drive_cycle_voltage = drive_cycle_current -drive_cycle_voltage = np.column_stack((np.delete(drive_cycle_voltage, 1, - 1), np.array(temp_volts))) +drive_cycle_voltage = np.column_stack( + (np.delete(drive_cycle_voltage, 1, 1), np.array(temp_volts)) +) # Map Current to Power temp_volts = np.array([]) @@ -45,42 +48,44 @@ def Map_Drive_Cycle(x, min_ip_value, max_ip_value, min_op_value, max_op_value): max_Power = 5.5 for I in drive_cycle_current[:, 1]: temp_volts = np.append( - temp_volts, - Map_Drive_Cycle(I, min_ip_value, max_ip_value, min_Power, max_Power)) + temp_volts, Map_Drive_Cycle(I, min_ip_value, max_ip_value, min_Power, max_Power) + ) drive_cycle_power = drive_cycle_current -drive_cycle_power = np.column_stack((np.delete(drive_cycle_power, 1, - 1), np.array(temp_volts))) -experiment = pybamm.Experiment([ -# "Charge at 1 A until 4.1 V", -# "Hold at 4.1 V until 50 mA", -# "Rest for 1 hour", - "Run US06_A (A), -# "Rest for 1 hour", -] -# + [ -# "Charge at 1 A until 4.1 V", -# "Hold at 4.1 V until 50 mA", -# "Rest for 1 hour", -# "Run US06_V (V)", -# "Rest for 1 hour", -# ] + [ -# "Charge at 1 A until 4.1 V", -# "Hold at 4.1 V until 50 mA", -# "Rest for 1 hour", -# "Run US06_W (W)", -# "Rest for 1 hour", -# ] - ,drive_cycles={ - "US06_A": drive_cycle_current, - "US06_V": drive_cycle_voltage, - "US06_W": drive_cycle_power, - }) +drive_cycle_power = np.column_stack( + (np.delete(drive_cycle_power, 1, 1), np.array(temp_volts)) +) +experiment = pybamm.Experiment( + [ + "Charge at 1 A until 4.1 V", + "Hold at 4.1 V until 50 mA", + "Rest for 1 hour", + "Run US06_A (A)", + "Rest for 1 hour", + ] + # + [ + # "Charge at 1 A until 4.1 V", + # "Hold at 4.1 V until 50 mA", + # "Rest for 1 hour", + # "Run US06_V (V)", + # "Rest for 1 hour", + # ] + + [ + # "Charge at 1 A until 4.1 V", + # "Hold at 4.1 V until 50 mA", + # "Rest for 1 hour", + "Run US06_W (W)", + "Rest for 1 hour", + ], + drive_cycles={ + "US06_A": drive_cycle_current, + "US06_V": drive_cycle_voltage, + "US06_W": drive_cycle_power, + }, +) model = pybamm.lithium_ion.DFN() -sim = pybamm.Simulation(model, - experiment=experiment, - solver=pybamm.CasadiSolver()) +sim = pybamm.Simulation(model, experiment=experiment, solver=pybamm.CasadiSolver()) sim.solve() # Show all plots From 22488344f10817819401e2a882743549a9e5f9e9 Mon Sep 17 00:00:00 2001 From: Ferran Brosa Planella Date: Tue, 9 Nov 2021 14:34:00 +0000 Subject: [PATCH 05/12] #1788 fix tests --- pybamm/experiments/experiment.py | 3 +- .../unit/test_experiments/test_experiment.py | 149 ++++++++++++++---- 2 files changed, 119 insertions(+), 33 deletions(-) diff --git a/pybamm/experiments/experiment.py b/pybamm/experiments/experiment.py index 222d31703b..b78e93ff8d 100644 --- a/pybamm/experiments/experiment.py +++ b/pybamm/experiments/experiment.py @@ -195,6 +195,7 @@ def read_string(self, cond, drive_cycles): "electric": op_CC["electric"] + op_CV["electric"], "time": op_CV["time"], "period": op_CV["period"], + "dc_data": None, }, event_CV # Read period if " period)" in cond: @@ -280,7 +281,7 @@ def read_string(self, cond, drive_cycles): "electric": electric, "time": time, "period": period, - "cond": cond, + # "cond": cond, "dc_data": dc_data, }, events diff --git a/tests/unit/test_experiments/test_experiment.py b/tests/unit/test_experiments/test_experiment.py index db6f6196e2..b91645460e 100644 --- a/tests/unit/test_experiments/test_experiment.py +++ b/tests/unit/test_experiments/test_experiment.py @@ -46,19 +46,44 @@ def test_read_strings(self): self.assertEqual( experiment.operating_conditions[:-3], [ - {"electric": (1, "C"), "time": 1800.0, "period": 20.0}, - {"electric": (0.05, "C"), "time": 1800.0, "period": 20.0}, - {"electric": (-0.5, "C"), "time": 2700.0, "period": 20.0}, - {"electric": (1, "A"), "time": 1800.0, "period": 20.0}, - {"electric": (-0.2, "A"), "time": 2700.0, "period": 60.0}, - {"electric": (1, "W"), "time": 1800.0, "period": 20.0}, - {"electric": (-0.2, "W"), "time": 2700.0, "period": 20.0}, - {"electric": (0, "A"), "time": 600.0, "period": 300.0}, - {"electric": (1, "V"), "time": 20.0, "period": 20.0}, - {"electric": (-1, "C"), "time": None, "period": 20.0}, - {"electric": (4.1, "V"), "time": None, "period": 20.0}, - {"electric": (3, "V"), "time": None, "period": 20.0}, - {"electric": (1 / 3, "C"), "time": 7200.0, "period": 20.0}, + {"electric": (1, "C"), "time": 1800.0, "period": 20.0, "dc_data": None}, + { + "electric": (0.05, "C"), + "time": 1800.0, + "period": 20.0, + "dc_data": None, + }, + { + "electric": (-0.5, "C"), + "time": 2700.0, + "period": 20.0, + "dc_data": None, + }, + {"electric": (1, "A"), "time": 1800.0, "period": 20.0, "dc_data": None}, + { + "electric": (-0.2, "A"), + "time": 2700.0, + "period": 60.0, + "dc_data": None, + }, + {"electric": (1, "W"), "time": 1800.0, "period": 20.0, "dc_data": None}, + { + "electric": (-0.2, "W"), + "time": 2700.0, + "period": 20.0, + "dc_data": None, + }, + {"electric": (0, "A"), "time": 600.0, "period": 300.0, "dc_data": None}, + {"electric": (1, "V"), "time": 20.0, "period": 20.0, "dc_data": None}, + {"electric": (-1, "C"), "time": None, "period": 20.0, "dc_data": None}, + {"electric": (4.1, "V"), "time": None, "period": 20.0, "dc_data": None}, + {"electric": (3, "V"), "time": None, "period": 20.0, "dc_data": None}, + { + "electric": (1 / 3, "C"), + "time": 7200.0, + "period": 20.0, + "dc_data": None, + }, ], ) # Calculation for operating conditions of drive cycle @@ -72,19 +97,19 @@ def test_read_strings(self): period_2 = np.min(np.diff(drive_cycle_2[:, 0])) # Check drive cycle operating conditions np.testing.assert_array_equal( - experiment.operating_conditions[-3]["electric"][0], drive_cycle + experiment.operating_conditions[-3]["dc_data"], drive_cycle ) self.assertEqual(experiment.operating_conditions[-3]["electric"][1], "A") self.assertEqual(experiment.operating_conditions[-3]["time"], time_0) self.assertEqual(experiment.operating_conditions[-3]["period"], period_0) np.testing.assert_array_equal( - experiment.operating_conditions[-2]["electric"][0], drive_cycle_1 + experiment.operating_conditions[-2]["dc_data"], drive_cycle_1 ) self.assertEqual(experiment.operating_conditions[-2]["electric"][1], "V") self.assertEqual(experiment.operating_conditions[-2]["time"], time_1) self.assertEqual(experiment.operating_conditions[-2]["period"], period_1) np.testing.assert_array_equal( - experiment.operating_conditions[-1]["electric"][0], drive_cycle_2 + experiment.operating_conditions[-1]["dc_data"], drive_cycle_2 ) self.assertEqual(experiment.operating_conditions[-1]["electric"][1], "W") self.assertEqual(experiment.operating_conditions[-1]["time"], time_2) @@ -128,9 +153,24 @@ def test_read_strings_cccv_combined(self): self.assertEqual( experiment.operating_conditions, [ - {"electric": (0.05, "C"), "time": 1800.0, "period": 60.0}, - {"electric": (-0.5, "C", 1, "V"), "time": None, "period": 60.0}, - {"electric": (0.05, "C"), "time": 1800.0, "period": 60.0}, + { + "electric": (0.05, "C"), + "time": 1800.0, + "period": 60.0, + "dc_data": None, + }, + { + "electric": (-0.5, "C", 1, "V"), + "time": None, + "period": 60.0, + "dc_data": None, + }, + { + "electric": (0.05, "C"), + "time": 1800.0, + "period": 60.0, + "dc_data": None, + }, ], ) self.assertEqual(experiment.events, [None, (0.02, "C"), None]) @@ -146,8 +186,13 @@ def test_read_strings_cccv_combined(self): self.assertEqual( experiment.operating_conditions, [ - {"electric": (-0.5, "C"), "time": None, "period": 60.0}, - {"electric": (1, "V"), "time": None, "period": 60.0}, + { + "electric": (-0.5, "C"), + "time": None, + "period": 60.0, + "dc_data": None, + }, + {"electric": (1, "V"), "time": None, "period": 60.0, "dc_data": None}, ], ) experiment = pybamm.Experiment( @@ -160,8 +205,13 @@ def test_read_strings_cccv_combined(self): self.assertEqual( experiment.operating_conditions, [ - {"electric": (-0.5, "C"), "time": 120.0, "period": 60.0}, - {"electric": (1, "V"), "time": None, "period": 60.0}, + { + "electric": (-0.5, "C"), + "time": 120.0, + "period": 60.0, + "dc_data": None, + }, + {"electric": (1, "V"), "time": None, "period": 60.0, "dc_data": None}, ], ) @@ -173,11 +223,26 @@ def test_read_strings_repeat(self): self.assertEqual( experiment.operating_conditions, [ - {"electric": (0.01, "A"), "time": 1800.0, "period": 60}, - {"electric": (-0.5, "C"), "time": 2700.0, "period": 60}, - {"electric": (1, "V"), "time": 20.0, "period": 60}, - {"electric": (-0.5, "C"), "time": 2700.0, "period": 60}, - {"electric": (1, "V"), "time": 20.0, "period": 60}, + { + "electric": (0.01, "A"), + "time": 1800.0, + "period": 60, + "dc_data": None, + }, + { + "electric": (-0.5, "C"), + "time": 2700.0, + "period": 60, + "dc_data": None, + }, + {"electric": (1, "V"), "time": 20.0, "period": 60, "dc_data": None}, + { + "electric": (-0.5, "C"), + "time": 2700.0, + "period": 60, + "dc_data": None, + }, + {"electric": (1, "V"), "time": 20.0, "period": 60, "dc_data": None}, ], ) self.assertEqual(experiment.period, 60) @@ -193,10 +258,30 @@ def test_cycle_unpacking(self): self.assertEqual( experiment.operating_conditions, [ - {"electric": (0.05, "C"), "time": 1800.0, "period": 60.0}, - {"electric": (-0.2, "C"), "time": 2700.0, "period": 60.0}, - {"electric": (0.05, "C"), "time": 1800.0, "period": 60.0}, - {"electric": (-0.2, "C"), "time": 2700.0, "period": 60.0}, + { + "electric": (0.05, "C"), + "time": 1800.0, + "period": 60.0, + "dc_data": None, + }, + { + "electric": (-0.2, "C"), + "time": 2700.0, + "period": 60.0, + "dc_data": None, + }, + { + "electric": (0.05, "C"), + "time": 1800.0, + "period": 60.0, + "dc_data": None, + }, + { + "electric": (-0.2, "C"), + "time": 2700.0, + "period": 60.0, + "dc_data": None, + }, ], ) self.assertEqual(experiment.cycle_lengths, [2, 1, 1]) From 412f294e7907a517ad8c29e6a098bdddd4fe3be2 Mon Sep 17 00:00:00 2001 From: Ferran Brosa Planella Date: Tue, 9 Nov 2021 14:56:37 +0000 Subject: [PATCH 06/12] #1788 fixed base_solver --- pybamm/solvers/base_solver.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pybamm/solvers/base_solver.py b/pybamm/solvers/base_solver.py index f215b880c5..2c2cf15b14 100644 --- a/pybamm/solvers/base_solver.py +++ b/pybamm/solvers/base_solver.py @@ -1201,12 +1201,12 @@ def step( # Set up external variables and inputs external_variables = external_variables or {} inputs = inputs or {} - if isinstance(inputs['Current input [A]'], pybamm.Interpolant): - del inputs['Current input [A]'] - elif isinstance(inputs['Voltage input [V]'], pybamm.Interpolant): - del inputs['Voltage input [V]'] - elif isinstance(inputs['Power input [W]'], pybamm.Interpolant): - del inputs['Power input [W]'] + # if isinstance(inputs['Current input [A]'], pybamm.Interpolant): + # del inputs['Current input [A]'] + # elif isinstance(inputs['Voltage input [V]'], pybamm.Interpolant): + # del inputs['Voltage input [V]'] + # elif isinstance(inputs['Power input [W]'], pybamm.Interpolant): + # del inputs['Power input [W]'] ext_and_inputs = {**external_variables, **inputs} # Check that any inputs that may affect the scaling have not changed From b94158fc4be50f22cf56230099847449dc73a88f Mon Sep 17 00:00:00 2001 From: Ferran Brosa Planella Date: Tue, 9 Nov 2021 17:55:44 +0000 Subject: [PATCH 07/12] #1788 Tino's comments --- examples/scripts/experiment_drive_cycle.py | 65 ++++------------------ 1 file changed, 12 insertions(+), 53 deletions(-) diff --git a/examples/scripts/experiment_drive_cycle.py b/examples/scripts/experiment_drive_cycle.py index e407bebf5b..14a9dd0191 100644 --- a/examples/scripts/experiment_drive_cycle.py +++ b/examples/scripts/experiment_drive_cycle.py @@ -17,69 +17,28 @@ # Map Drive Cycle -def Map_Drive_Cycle(x, min_ip_value, max_ip_value, min_op_value, max_op_value): - return (x - min_ip_value) * (max_op_value - min_op_value) / ( - max_ip_value - min_ip_value - ) + min_op_value +def map_drive_cycle(x, min_op_value, max_op_value): + min_ip_value = x[:, 1].min() + max_ip_value = x[:, 1].max() + x[:, 1] = (x[:, 1] - min_ip_value) / (max_ip_value - min_ip_value) * (max_op_value - min_op_value) + min_op_value + return x -# Map Current to Voltage -temp_volts = np.array([]) -min_ip_value = drive_cycle_current[:, 1].min() -max_ip_value = drive_cycle_current[:, 1].max() -min_Voltage = 3.5 -max_Voltage = 4.1 -for I in drive_cycle_current[:, 1]: - temp_volts = np.append( - temp_volts, - Map_Drive_Cycle(I, min_ip_value, max_ip_value, min_Voltage, max_Voltage), - ) +# Map current drive cycle to voltage and power +drive_cycle_power = map_drive_cycle(drive_cycle_current, 1.5, 3.5) -drive_cycle_voltage = drive_cycle_current -drive_cycle_voltage = np.column_stack( - (np.delete(drive_cycle_voltage, 1, 1), np.array(temp_volts)) -) - -# Map Current to Power -temp_volts = np.array([]) -min_ip_value = drive_cycle_current[:, 1].min() -max_ip_value = drive_cycle_current[:, 1].max() -min_Power = 2.5 -max_Power = 5.5 -for I in drive_cycle_current[:, 1]: - temp_volts = np.append( - temp_volts, Map_Drive_Cycle(I, min_ip_value, max_ip_value, min_Power, max_Power) - ) - -drive_cycle_power = drive_cycle_current -drive_cycle_power = np.column_stack( - (np.delete(drive_cycle_power, 1, 1), np.array(temp_volts)) -) experiment = pybamm.Experiment( [ - "Charge at 1 A until 4.1 V", - "Hold at 4.1 V until 50 mA", - "Rest for 1 hour", + "Charge at 1 A until 4.0 V", + "Hold at 4.0 V until 50 mA", + "Rest for 30 minutes", "Run US06_A (A)", - "Rest for 1 hour", - ] - # + [ - # "Charge at 1 A until 4.1 V", - # "Hold at 4.1 V until 50 mA", - # "Rest for 1 hour", - # "Run US06_V (V)", - # "Rest for 1 hour", - # ] - + [ - # "Charge at 1 A until 4.1 V", - # "Hold at 4.1 V until 50 mA", - # "Rest for 1 hour", + "Rest for 30 minutes", "Run US06_W (W)", - "Rest for 1 hour", + "Rest for 30 minutes", ], drive_cycles={ "US06_A": drive_cycle_current, - "US06_V": drive_cycle_voltage, "US06_W": drive_cycle_power, }, ) From 5cdf51dd50ec80b01582fbc61d82ebf136c1f4ae Mon Sep 17 00:00:00 2001 From: Ferran Brosa Planella Date: Tue, 9 Nov 2021 17:56:25 +0000 Subject: [PATCH 08/12] #1788 fix tests --- pybamm/experiments/experiment.py | 1 - pybamm/simulation.py | 2 +- pybamm/solvers/base_solver.py | 14 ++++++++------ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/pybamm/experiments/experiment.py b/pybamm/experiments/experiment.py index b78e93ff8d..c175a94815 100644 --- a/pybamm/experiments/experiment.py +++ b/pybamm/experiments/experiment.py @@ -281,7 +281,6 @@ def read_string(self, cond, drive_cycles): "electric": electric, "time": time, "period": period, - # "cond": cond, "dc_data": dc_data, }, events diff --git a/pybamm/simulation.py b/pybamm/simulation.py index 1ee2488f1a..48d3ec5837 100644 --- a/pybamm/simulation.py +++ b/pybamm/simulation.py @@ -175,7 +175,7 @@ def set_up_experiment(self, model, experiment): } op_control = op["electric"][1] if op["dc_data"] is not None: - # If ndarray is recived from, create interpolant + # If operating condition includes a drive cycle, define the interpolant timescale = self._parameter_values.evaluate(model.timescale) drive_cycle_interpolant = pybamm.Interpolant( op["dc_data"][:, 0], diff --git a/pybamm/solvers/base_solver.py b/pybamm/solvers/base_solver.py index 2c2cf15b14..f80b4bfbf7 100644 --- a/pybamm/solvers/base_solver.py +++ b/pybamm/solvers/base_solver.py @@ -1201,12 +1201,14 @@ def step( # Set up external variables and inputs external_variables = external_variables or {} inputs = inputs or {} - # if isinstance(inputs['Current input [A]'], pybamm.Interpolant): - # del inputs['Current input [A]'] - # elif isinstance(inputs['Voltage input [V]'], pybamm.Interpolant): - # del inputs['Voltage input [V]'] - # elif isinstance(inputs['Power input [W]'], pybamm.Interpolant): - # del inputs['Power input [W]'] + + # Remove interpolant inputs as Casadi can't handle them + if isinstance(inputs.get("Current input [A]"), pybamm.Interpolant): + del inputs["Current input [A]"] + elif isinstance(inputs.get("Voltage input [V]"), pybamm.Interpolant): + del inputs["Voltage input [V]"] + elif isinstance(inputs.get("Power input [W]"), pybamm.Interpolant): + del inputs["Power input [W]"] ext_and_inputs = {**external_variables, **inputs} # Check that any inputs that may affect the scaling have not changed From 134d11c7034e4e03eebf1a99287ee2c33d50457f Mon Sep 17 00:00:00 2001 From: Ferran Brosa Planella Date: Tue, 9 Nov 2021 17:58:24 +0000 Subject: [PATCH 09/12] #1788 update CHANGELOG --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7403a17edb..020a47a35d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,9 @@ ## Features -- Half-cell SPM and SPMe have been implemented ( [#1731](https://github.com/pybamm-team/PyBaMM/pull/1731)) +- `Experiment`s with drive cycles can be solved ([#1793](https://github.com/pybamm-team/PyBaMM/pull/1793)) +- Half-cell SPM and SPMe have been implemented ([#1731](https://github.com/pybamm-team/PyBaMM/pull/1731)) + ## Bug fixes - Fixed `sympy` operators for `Arctan` and `Exponential` ([#1786](https://github.com/pybamm-team/PyBaMM/pull/1786)) From b4e16b151bb461e064beb4650278f5cc2539aa21 Mon Sep 17 00:00:00 2001 From: Ferran Brosa Planella Date: Tue, 9 Nov 2021 17:58:38 +0000 Subject: [PATCH 10/12] flake8 --- examples/scripts/experiment_drive_cycle.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/scripts/experiment_drive_cycle.py b/examples/scripts/experiment_drive_cycle.py index 14a9dd0191..2ead334dad 100644 --- a/examples/scripts/experiment_drive_cycle.py +++ b/examples/scripts/experiment_drive_cycle.py @@ -2,7 +2,6 @@ # Constant-current constant-voltage charge with US06 Drive Cycle using Experiment Class. # import pybamm -import numpy as np import pandas as pd import os @@ -20,7 +19,9 @@ def map_drive_cycle(x, min_op_value, max_op_value): min_ip_value = x[:, 1].min() max_ip_value = x[:, 1].max() - x[:, 1] = (x[:, 1] - min_ip_value) / (max_ip_value - min_ip_value) * (max_op_value - min_op_value) + min_op_value + x[:, 1] = (x[:, 1] - min_ip_value) / (max_ip_value - min_ip_value) * ( + max_op_value - min_op_value + ) + min_op_value return x From 6565cd59db7e2859fbab4d9c61315f234e6e85f1 Mon Sep 17 00:00:00 2001 From: Ferran Brosa Planella Date: Tue, 9 Nov 2021 22:14:17 +0000 Subject: [PATCH 11/12] #1788 improve coverage --- pybamm/simulation.py | 2 +- pybamm/solvers/base_solver.py | 2 +- .../test_simulation_with_experiment.py | 18 ++++++++ tests/unit/test_solvers/test_base_solver.py | 46 +++++++++++-------- 4 files changed, 47 insertions(+), 21 deletions(-) diff --git a/pybamm/simulation.py b/pybamm/simulation.py index 48d3ec5837..589b762cd9 100644 --- a/pybamm/simulation.py +++ b/pybamm/simulation.py @@ -21,7 +21,7 @@ def is_notebook(): return False # Terminal running IPython elif shell == "Shell": # pragma: no cover return True # Google Colab notebook - else: + else: # pragma: no cover return False # Other type (?) except NameError: return False # Probably standard Python interpreter diff --git a/pybamm/solvers/base_solver.py b/pybamm/solvers/base_solver.py index f80b4bfbf7..98fa02dd59 100644 --- a/pybamm/solvers/base_solver.py +++ b/pybamm/solvers/base_solver.py @@ -1160,7 +1160,7 @@ def step( external_variables : dict A dictionary of external variables and their corresponding values at the current time - inputs_dict : dict, optional + inputs : dict, optional Any input parameters to pass to the model when solving save : bool Turn on to store the solution of all previous timesteps diff --git a/tests/unit/test_experiments/test_simulation_with_experiment.py b/tests/unit/test_experiments/test_simulation_with_experiment.py index 31eb795df9..5b47b672b8 100644 --- a/tests/unit/test_experiments/test_simulation_with_experiment.py +++ b/tests/unit/test_experiments/test_simulation_with_experiment.py @@ -158,6 +158,24 @@ def test_run_experiment_cccv_ode(self): ) self.assertEqual(solutions[1].termination, "final time") + def test_run_experiment_drive_cycle(self): + drive_cycle = np.array([np.arange(10), np.arange(10)]).T + experiment = pybamm.Experiment( + [ + ( + "Run drive_cycle (A)", + "Run drive_cycle (V)", + "Run drive_cycle (W)", + ) + ], + drive_cycles={"drive_cycle": drive_cycle} + ) + model = pybamm.lithium_ion.DFN() + sim = pybamm.Simulation(model, experiment=experiment) + self.assertIn(('drive_cycle', 'A'), sim.op_conds_to_model_and_param) + self.assertIn(('drive_cycle', 'V'), sim.op_conds_to_model_and_param) + self.assertIn(('drive_cycle', 'W'), sim.op_conds_to_model_and_param) + def test_run_experiment_old_setup_type(self): experiment = pybamm.Experiment( [ diff --git a/tests/unit/test_solvers/test_base_solver.py b/tests/unit/test_solvers/test_base_solver.py index 7967c96949..710bc79815 100644 --- a/tests/unit/test_solvers/test_base_solver.py +++ b/tests/unit/test_solvers/test_base_solver.py @@ -2,6 +2,7 @@ # Tests for the Base Solver class # import casadi +from casadi.casadi import interpolant import pybamm import numpy as np from scipy.sparse import csr_matrix @@ -296,6 +297,21 @@ def test_timescale_input_fail(self): with self.assertRaisesRegex(pybamm.SolverError, "The model timescale"): sol = solver.step(old_solution=sol, model=model, dt=1.0, inputs={"a": 20}) + def test_inputs_step(self): + # Make sure interpolant inputs are dropped + model = pybamm.BaseModel() + v = pybamm.Variable("v") + model.rhs = {v: -1} + model.initial_conditions = {v: 1} + x = np.array([0, 1]) + interpolant = pybamm.Interpolant(x, x, pybamm.t) + solver = pybamm.CasadiSolver() + for input_key in ["Current input [A]", "Voltage input [V]", "Power input [W]"]: + sol = solver.step( + old_solution=None, model=model, dt=1.0, inputs={input_key: interpolant} + ) + self.assertFalse(input_key in sol.all_inputs[0]) + def test_extrapolation_warnings(self): # Make sure the extrapolation warnings work model = pybamm.BaseModel() @@ -327,29 +343,25 @@ def test_extrapolation_warnings(self): @unittest.skipIf(not pybamm.have_idaklu(), "idaklu solver is not installed") def test_sensitivities(self): - def exact_diff_a(y, a, b): - return np.array([ - [y[0]**2 + 2 * a], - [y[0]] - ]) + return np.array([[y[0] ** 2 + 2 * a], [y[0]]]) def exact_diff_b(y, a, b): return np.array([[y[0]], [0]]) - for convert_to_format in ['', 'python', 'casadi', 'jax']: + for convert_to_format in ["", "python", "casadi", "jax"]: model = pybamm.BaseModel() v = pybamm.Variable("v") u = pybamm.Variable("u") a = pybamm.InputParameter("a") b = pybamm.InputParameter("b") - model.rhs = {v: a * v**2 + b * v + a**2} + model.rhs = {v: a * v ** 2 + b * v + a ** 2} model.algebraic = {u: a * v - u} model.initial_conditions = {v: 1, u: a * 1} model.convert_to_format = convert_to_format - solver = pybamm.IDAKLUSolver(root_method='lm') - model.calculate_sensitivities = ['a', 'b'] - solver.set_up(model, inputs={'a': 0, 'b': 0}) + solver = pybamm.IDAKLUSolver(root_method="lm") + model.calculate_sensitivities = ["a", "b"] + solver.set_up(model, inputs={"a": 0, "b": 0}) all_inputs = [] for v_value in [0.1, -0.2, 1.5, 8.4]: for u_value in [0.13, -0.23, 1.3, 13.4]: @@ -357,24 +369,20 @@ def exact_diff_b(y, a, b): for b_value in [0.82, 1.9]: y = np.array([v_value, u_value]) t = 0 - inputs = {'a': a_value, 'b': b_value} + inputs = {"a": a_value, "b": b_value} all_inputs.append((t, y, inputs)) for t, y, inputs in all_inputs: - if model.convert_to_format == 'casadi': + if model.convert_to_format == "casadi": use_inputs = casadi.vertcat(*[x for x in inputs.values()]) else: use_inputs = inputs - sens = model.sensitivities_eval( - t, y, use_inputs - ) + sens = model.sensitivities_eval(t, y, use_inputs) np.testing.assert_allclose( - sens['a'], - exact_diff_a(y, inputs['a'], inputs['b']) + sens["a"], exact_diff_a(y, inputs["a"], inputs["b"]) ) np.testing.assert_allclose( - sens['b'], - exact_diff_b(y, inputs['a'], inputs['b']) + sens["b"], exact_diff_b(y, inputs["a"], inputs["b"]) ) From 1f6c1f9afc1062bab863440d6aafd29dbb79fc01 Mon Sep 17 00:00:00 2001 From: Ferran Brosa Planella Date: Tue, 9 Nov 2021 22:19:10 +0000 Subject: [PATCH 12/12] flake8 --- tests/unit/test_solvers/test_base_solver.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/unit/test_solvers/test_base_solver.py b/tests/unit/test_solvers/test_base_solver.py index 710bc79815..180946765a 100644 --- a/tests/unit/test_solvers/test_base_solver.py +++ b/tests/unit/test_solvers/test_base_solver.py @@ -2,7 +2,6 @@ # Tests for the Base Solver class # import casadi -from casadi.casadi import interpolant import pybamm import numpy as np from scipy.sparse import csr_matrix @@ -304,11 +303,11 @@ def test_inputs_step(self): model.rhs = {v: -1} model.initial_conditions = {v: 1} x = np.array([0, 1]) - interpolant = pybamm.Interpolant(x, x, pybamm.t) + interp = pybamm.Interpolant(x, x, pybamm.t) solver = pybamm.CasadiSolver() for input_key in ["Current input [A]", "Voltage input [V]", "Power input [W]"]: sol = solver.step( - old_solution=None, model=model, dt=1.0, inputs={input_key: interpolant} + old_solution=None, model=model, dt=1.0, inputs={input_key: interp} ) self.assertFalse(input_key in sol.all_inputs[0])