Model Saving/Loading Example

This set of examples shows how to load and import template models in order to make setting up and reproducing circuit fits easier.

[1]:
# Load libraries

import impedance.preprocessing as preprocessing
import impedance.models.circuits as circuits
from impedance.visualization import plot_nyquist
import numpy as np
import matplotlib.pyplot as plt
[2]:
# Load data from the example EIS result
frequencies, Z = preprocessing.readCSV('../../../data/exampleData.csv')

# keep only the impedance data in the first quandrant
frequencies, Z = preprocessing.ignoreBelowX(frequencies, Z)

Example 1. Importing and Exporting Models

Call the circuit.save() function to export the model to a human readable JSON file. The following code generates a test circuit and export it as a template. Here we are using an unfitted model as a template.

[3]:
test_circuit = circuits.CustomCircuit(initial_guess=[.01, .005, .1, .005, .1, .001, 200],
                              circuit='R0-p(R1,C1)-p(R2,C2)-Wo1')

print(test_circuit)

test_circuit.save('template_model.json')

Circuit string: R0-p(R1,C1)-p(R2,C2)-Wo1
Fit: False

Initial guesses:
     R0 = 1.00e-02 [Ohm]
     R1 = 5.00e-03 [Ohm]
     C1 = 1.00e-01 [F]
     R2 = 5.00e-03 [Ohm]
     C2 = 1.00e-01 [F]
  Wo1_0 = 1.00e-03 [Ohm]
  Wo1_1 = 2.00e+02 [sec]

Call the model_io.model_import function to import the model back as a template.

[4]:
loaded_template = circuits.CustomCircuit()
loaded_template.load('template_model.json')

print("Loaded Template")
print(loaded_template)
R0-p(R1,C1)-p(R2,C2)-Wo1
Loaded Template

Circuit string: R0-p(R1,C1)-p(R2,C2)-Wo1
Fit: False

Initial guesses:
     R0 = 1.00e-02 [Ohm]
     R1 = 5.00e-03 [Ohm]
     C1 = 1.00e-01 [F]
     R2 = 5.00e-03 [Ohm]
     C2 = 1.00e-01 [F]
  Wo1_0 = 1.00e-03 [Ohm]
  Wo1_1 = 2.00e+02 [sec]

Example 2. Using imported template model to fit data

After the model has been imported as a template, it can be used as a starting point to fit data. This saves on needing to configure the initial parameters each time a fit is performed and to persist starting conditions across several fitting sessions.

[5]:
fig, ax = plt.subplots(figsize=(5,5))
f_pred = np.logspace(5,-2)
loaded_template.fit(frequencies, Z)

imported_circuit_init = loaded_template.predict(f_pred, use_initial = True)
imported_circuit_fit = loaded_template.predict(f_pred)

plot_nyquist(ax, Z)
plot_nyquist(ax, imported_circuit_init)
plot_nyquist(ax, imported_circuit_fit)

ax.legend(['Data', 'Loaded Template Initial', 'Loaded Template Fit'])

plt.show()
print(loaded_template)
Simulating circuit based on initial parameters
../_images/examples_model_io_example_8_1.png

Circuit string: R0-p(R1,C1)-p(R2,C2)-Wo1
Fit: True

Initial guesses:
     R0 = 1.00e-02 [Ohm]
     R1 = 5.00e-03 [Ohm]
     C1 = 1.00e-01 [F]
     R2 = 5.00e-03 [Ohm]
     C2 = 1.00e-01 [F]
  Wo1_0 = 1.00e-03 [Ohm]
  Wo1_1 = 2.00e+02 [sec]

Fit parameters:
     R0 = 1.65e-02  (+/- 1.54e-04) [Ohm]
     R1 = 8.77e-03  (+/- 1.89e-04) [Ohm]
     C1 = 3.28e+00  (+/- 1.85e-01) [F]
     R2 = 5.31e-03  (+/- 2.06e-04) [Ohm]
     C2 = 2.32e-01  (+/- 1.90e-02) [F]
  Wo1_0 = 6.37e-02  (+/- 2.03e-03) [Ohm]
  Wo1_1 = 2.37e+02  (+/- 1.72e+01) [sec]

Example 3. Using fitted data as a starting point for new fits

Consider the case where a successful fit has been performed and a new set of EIS data is obtained which is similar to the first spectrum. It is useful to use the successfully fitted parameters as a starting point for subsequent fits.

[6]:
# Export the fitted model as a template

loaded_template.save('fitted_template.json')

Using the exported model’s fitted parameters, generate a new circuit using the fitted parameters as initial guesses by suppling the fitted_as_initial parameter as True.

[7]:
fitted_template = circuits.CustomCircuit()
fitted_template.load('fitted_template.json', fitted_as_initial=True)
print(fitted_template)
R0-p(R1,C1)-p(R2,C2)-Wo1

Circuit string: R0-p(R1,C1)-p(R2,C2)-Wo1
Fit: False

Initial guesses:
     R0 = 1.65e-02 [Ohm]
     R1 = 8.77e-03 [Ohm]
     C1 = 3.28e+00 [F]
     R2 = 5.31e-03 [Ohm]
     C2 = 2.32e-01 [F]
  Wo1_0 = 6.37e-02 [Ohm]
  Wo1_1 = 2.37e+02 [sec]

Z2 is a similar impedance spectra that we can fit using the previous fitted parameters as starting points. It has been shifted by 5 mOhm in the real axis and the data has been scaled by 1.5x.

[8]:
Z2 = (0.005 + Z.real)*1.5 + 1.5j*Z.imag
[9]:
fig, ax = plt.subplots(figsize=(10,10))
f_pred = np.logspace(5,-2)
fitted_template.fit(frequencies, Z2)

imported_circuit_init = fitted_template.predict(f_pred, use_initial = True)
imported_circuit_fit = fitted_template.predict(f_pred)

plot_nyquist(ax, Z)
plot_nyquist(ax, Z2)
plot_nyquist(ax, imported_circuit_init)
plot_nyquist(ax, imported_circuit_fit)

ax.legend(['Original Data', 'Shifted Data', 'Template Initial', 'Loaded Template Fit'])

plt.show()

print(fitted_template)
Simulating circuit based on initial parameters
../_images/examples_model_io_example_15_1.png

Circuit string: R0-p(R1,C1)-p(R2,C2)-Wo1
Fit: True

Initial guesses:
     R0 = 1.65e-02 [Ohm]
     R1 = 8.77e-03 [Ohm]
     C1 = 3.28e+00 [F]
     R2 = 5.31e-03 [Ohm]
     C2 = 2.32e-01 [F]
  Wo1_0 = 6.37e-02 [Ohm]
  Wo1_1 = 2.37e+02 [sec]

Fit parameters:
     R0 = 3.22e-02  (+/- 2.31e-04) [Ohm]
     R1 = 1.31e-02  (+/- 2.84e-04) [Ohm]
     C1 = 2.19e+00  (+/- 1.24e-01) [F]
     R2 = 7.96e-03  (+/- 3.10e-04) [Ohm]
     C2 = 1.55e-01  (+/- 1.26e-02) [F]
  Wo1_0 = 9.56e-02  (+/- 3.05e-03) [Ohm]
  Wo1_1 = 2.38e+02  (+/- 1.73e+01) [sec]