Pipeline serialization¶
In this example, we will show how to serialize and deserialize pipeline elements for saving or use with the API. The datafit is the same as is constructed in particle_swarm_optimiser.ipynb.
import ionworkspipeline as iwp
import numpy as np
import pandas as pd
import pybamm
# Load the model and parameter values
model = pybamm.lithium_ion.SPM()
parameter_values = iwp.ParameterValues("Chen2020")
parameter_values.update(
{
"Negative electrode active material volume fraction": 0.75,
"Positive electrode active material volume fraction": 0.665,
}
)
# Generate the data
t = np.arange(0, 900, 3)
sim = iwp.Simulation(
model, parameter_values=parameter_values, t_eval=[0, 900], t_interp=t
)
sol = sim.solve()
data = pd.DataFrame(
{x: sim.solution[x](t) for x in ["Time [s]", "Current [A]", "Voltage [V]"]}
)
# add noise to the data
sigma = 0.001
# Set seed for reproducibility
np.random.seed(0)
data["Voltage [V]"] += np.random.normal(0, sigma, len(data))
Next, we define the parameters to fit, along with initial guesses and bounds.
parameters = {
"Negative electrode active material volume fraction": iwp.Parameter(
"Negative electrode active material volume fraction",
initial_value=0.5,
bounds=(0, 1),
),
"Positive electrode active material volume fraction": iwp.Parameter(
"Positive electrode active material volume fraction",
initial_value=0.5,
bounds=(0, 1),
),
}
We set up the objective function, which in this case is the current driven objective. This takes the time vs current data and runs the model, comparing the model’s predictions for the voltage with the experimental data.
objective = iwp.objectives.CurrentDriven(data, options={"model": model})
Then, we set up the DataFit object, which takes the objective function, the parameters to fit, and the optimizer as inputs. We also convert the parameters to a direct entry. Finally, we construct a pipeline to demonstrate the serialization and deserialization.
params_for_pipeline = {k: v for k, v in parameter_values.items() if k not in parameters}
params_direct_entry = iwp.direct_entries.DirectEntry(
params_for_pipeline, source="Chen2020"
)
data_fit = iwp.DataFit(
objective,
parameters=parameters,
options={"maxiters": 100, "seed": 0},
)
pipeline = iwp.Pipeline(
{"starting parameters": params_direct_entry, "data fit": data_fit}
)
Next, we run the data fit, passing the parameters that are not being fit as a dictionary.
results = pipeline.run()
print(
f"Negative electrode active material volume fraction: {results['Negative electrode active material volume fraction']}"
)
print(
f"Positive electrode active material volume fraction: {results['Positive electrode active material volume fraction']}"
)
Negative electrode active material volume fraction: 0.7520752651737477
Positive electrode active material volume fraction: 0.6647057764435406
Now, we convert the pipeline to a JSON configuration file
json_config = pipeline.to_config()
# To save the JSON config to a file and view:
# import json
# with open("pipeline_config.json", "w") as f:
# json.dump(json_config, f, indent=4)
…and convert it back into a pipeline element. Then we can run it, and check whether we get the same results.
new_pipeline = iwp.parsers.parse_pipeline(json_config)
results = new_pipeline.run()
print(
f"Negative electrode active material volume fraction: {results['Negative electrode active material volume fraction']}"
)
print(
f"Positive electrode active material volume fraction: {results['Positive electrode active material volume fraction']}"
)
Negative electrode active material volume fraction: 0.7520752651737477
Positive electrode active material volume fraction: 0.6647057764435406