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

Solve Drive Cycle (A,V,W) in Experiment #1524

Merged
merged 2 commits into from
Nov 10, 2021
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
87 changes: 87 additions & 0 deletions examples/scripts/experiment_drive_cycle.py
Original file line number Diff line number Diff line change
@@ -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()
2 changes: 1 addition & 1 deletion pybamm/experiments/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
124 changes: 80 additions & 44 deletions pybamm/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,44 +170,79 @@ 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):
# 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last input here is the time that is evaluated by the interpolant. You could do the following:

drive_cycle_interpolant = pybamm.Interpolant(
                    op[0][:, 0], op[0][:, 1], timescale * pybamm.t - pybamm.InputParameter("start time")
)

and pass the appropriate "start time" to the model inputs when solving the model at each step

)
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, # doesn't matter
"Voltage input [V]": drive_cycle_interpolant,
"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, # doesn't matter
"Voltage input [V]": 0, # doesn't matter
"Power input [W]": drive_cycle_interpolant,
}
else:
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
Expand Down Expand Up @@ -319,7 +354,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
Expand All @@ -339,7 +374,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))
Expand Down Expand Up @@ -422,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:
Expand All @@ -439,8 +475,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,
)
Expand Down Expand Up @@ -691,7 +729,7 @@ 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]
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(
Expand All @@ -712,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
Expand Down
6 changes: 6 additions & 0 deletions pybamm/solvers/base_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,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]']
ext_and_inputs = {**external_variables, **inputs}

# Check that any inputs that may affect the scaling have not changed
Expand Down