<jupyter_start><jupyter_text>A look at solution data and processed variables Once you have run a simulation the first thing you want to do is have a look at the data. Most of the examples so far have made use of PyBaMM's handy QuickPlot function but there are other ways to access the data and this notebook will explore them. First off we will generate a standard SPMe model and use QuickPlot to view the default variables.<jupyter_code>%pip install pybamm -q    # install PyBaMM if it is not installed
import pybamm
import numpy as np
import os
import matplotlib.pyplot as plt
os.chdir(pybamm.__path__[0]+'/..')

# load model
model = pybamm.lithium_ion.SPMe()

# set up and solve simulation
simulation = pybamm.Simulation(model)
dt = 90
t_eval = np.arange(0, 3600, dt)  # time in seconds
solution = simulation.solve(t_eval)

quick_plot = pybamm.QuickPlot(solution)
quick_plot.dynamic_plot();<jupyter_output>Note: you may need to restart the kernel to use updated packages.<jupyter_text>Behind the scenes the QuickPlot classed has created some processed variables which can interpolate the model variables for our solution and has also stored the results for the solution steps<jupyter_code>solution.data.keys()
solution.data['Negative particle surface concentration [mol.m-3]'].shape
solution.t.shape<jupyter_output><empty_output><jupyter_text>Notice that the dictionary keys are in the same order as the subplots in the QuickPlot figure. We can add new processed variables to the solution by simply using it like a dictionary. First let's find a few more variables to look at. As you will see there are quite a few:<jupyter_code>keys = list(model.variables.keys())
keys.sort()
print(keys)<jupyter_output>['Ambient temperature', 'Ambient temperature [K]', 'Average negative particle concentration', 'Average negative particle concentration [mol.m-3]', 'Average positive particle concentration', 'Average positive particle concentration [mol.m-3]', 'Battery voltage [V]', 'C-rate', 'Cell temperature', 'Cell temperature [K]', 'Change in measured open circuit voltage', 'Change in measured open circuit voltage [V]', 'Current [A]', 'Current collector current density', 'Current collector current density [A.m-2]', 'Discharge capacity [A.h]', 'Electrode current density', 'Electrode tortuosity', 'Electrolyte concentration', 'Electrolyte concentration [Molar]', 'Electrolyte concentration [mol.m-3]', 'Electrolyte current density', 'Electrolyte current density [A.m-2]', 'Electrolyte flux', 'Electrolyte flux [mol.m-2.s-1]', 'Electrolyte potential', 'Electrolyte potential [V]', 'Electrolyte tortuosity', 'Exchange current density', 'Exchange current density [A.m-2]', 'Exchange current density per volume [A[...]<jupyter_text>If you want to find a particular variable you can search the variables dictionary<jupyter_code>model.variables.search("time")<jupyter_output>Time
Time [h]
Time [min]
Time [s]<jupyter_text>We'll use the time in hours<jupyter_code>solution['Time [h]']<jupyter_output><empty_output><jupyter_text>This created a new processed variable and stored it on the solution object<jupyter_code>solution.data.keys()<jupyter_output><empty_output><jupyter_text>We can see the data by simply accessing the entries attribute of the processed variable<jupyter_code>solution['Time [h]'].entries<jupyter_output><empty_output><jupyter_text>We can also call the method with specified time(s) in SI units of seconds<jupyter_code>time_in_seconds = np.array([0, 600, 900, 1700, 3000 ])
solution['Time [h]'](time_in_seconds)<jupyter_output><empty_output><jupyter_text>If the variable has not already been processed it will be created behind the scenes<jupyter_code>var = 'X-averaged negative electrode temperature [K]'
solution[var](time_in_seconds)<jupyter_output><empty_output><jupyter_text>In this example the simulation was isothermal, so the temperature remains unchanged.  Saving the solutionThe solution can be saved in a number of ways:<jupyter_code># to a pickle file (default)
solution.save_data(
    "outputs.pickle", ["Time [h]", "Current [A]", "Terminal voltage [V]", "Electrolyte concentration [mol.m-3]"]
)
# to a matlab file
# need to give variable names without space
solution.save_data(
    "outputs.mat", 
    ["Time [h]", "Current [A]", "Terminal voltage [V]", "Electrolyte concentration [mol.m-3]"], 
    to_format="matlab",
    short_names={
        "Time [h]": "t", "Current [A]": "I", "Terminal voltage [V]": "V", "Electrolyte concentration [mol.m-3]": "c_e",
    }
)
# to a csv file (time-dependent outputs only, no spatial dependence allowed)
solution.save_data(
    "outputs.csv", ["Time [h]", "Current [A]", "Terminal voltage [V]"], to_format="csv"
)<jupyter_output><empty_output><jupyter_text>Stepping the solverThe previous solution was created in one go with the solve method, but it is also possible to step the solution and look at the results as we go. In doing so, the results are automatically updated at each step.<jupyter_code>dt = 360
time = 0
end_time = solution["Time [s]"].entries[-1]
step_simulation = pybamm.Simulation(model)
while time < end_time:
    step_solution = step_simulation.step(dt)
    print('Time', time)
    print(step_solution["Terminal voltage [V]"].entries)
    time += dt<jupyter_output>Time 0
[3.77047806 3.71250693]
Time 360
[3.77047806 3.71250693 3.68215218]
Time 720
[3.77047806 3.71250693 3.68215218 3.66125574]
Time 1080
[3.77047806 3.71250693 3.68215218 3.66125574 3.64330942]
Time 1440
[3.77047806 3.71250693 3.68215218 3.66125574 3.64330942 3.61166857]
Time 1800
[3.77047806 3.71250693 3.68215218 3.66125574 3.64330942 3.61166857
 3.59709451]
Time 2160
[3.77047806 3.71250693 3.68215218 3.66125574 3.64330942 3.61166857
 3.59709451 3.58821334]
Time 2520
[3.77047806 3.71250693 3.68215218 3.66125574 3.64330942 3.61166857
 3.59709451 3.58821334 3.58056055]
Time 2880
[3.77047806 3.71250693 3.68215218 3.66125574 3.64330942 3.61166857
 3.59709451 3.58821334 3.58056055 3.55158694]
Time 3240
[3.77047806 3.71250693 3.68215218 3.66125574 3.64330942 3.61166857
 3.59709451 3.58821334 3.58056055 3.55158694 3.16842636]<jupyter_text>We can plot the voltages and see that the solutions are the same<jupyter_code>voltage = solution["Terminal voltage [V]"].entries
step_voltage = step_solution["Terminal voltage [V]"].entries
plt.figure()
plt.plot(solution["Time [h]"].entries, voltage, "b-", label="SPMe (continuous solve)")
plt.plot(
    step_solution["Time [h]"].entries, step_voltage, "ro", label="SPMe (stepped solve)"
)
plt.legend()<jupyter_output><empty_output><jupyter_text>ReferencesThe relevant papers for this notebook are:<jupyter_code>pybamm.print_citations()<jupyter_output>[1] <NAME>, <NAME>, <NAME>, <NAME>, and <NAME>. CasADi – A software framework for nonlinear optimization and optimal control. Mathematical Programming Computation, 11(1):1–36, 2019. doi:10.1007/s12532-018-0139-4.
[2] <NAME>, <NAME>, <NAME>, <NAME>, <NAME>, <NAME>, <NAME>, <NAME>, <NAME>, <NAME>, and others. Array programming with NumPy. Nature, 585(7825):357–362, 2020. doi:10.1038/s41586-020-2649-2.
[3] <NAME>, <NAME>, <NAME>, <NAME>, and <NAME>. An asymptotic derivation of a single particle model with electrolyte. Journal of The Electrochemical Society, 166(15):A3693–A3706, 2019. doi:10.1149/2.0341915jes.
[4] <NAME>, <NAME>, <NAME>, <NAME>, and <NAME>. Python Battery Mathematical Modelling (PyBaMM). ECSarXiv. February, 2020. doi:10.1149/osf.io/67ckj.
{'fraction_non_alphanumeric': 0.11674989877176407, 'fraction_numerical': 0.10419759751653394, 'mean_word_length': 5.854764107308048, 'pattern_counts': {'":': 4, '<': 74, '<?xml version=': 0, '>': 73, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '287001', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 3019, 'n_tokens_neox': 2417, 'n_words': 790, 'path': 'examples/notebooks/solution-data-and-processed-variables.ipynb', 'repo_name': 'YannickNoelStephanKuhn/PyBaMM', 'size': '7627'}