Ejemplo n.º 1
0
def model_impedance(circuit, guess, freq, impedance, name='circuit'):
    model = CustomCircuit(name='Custom {}'.format(name),
                          circuit=circuit,
                          initial_guess=guess)
    model.fit(freq, impedance, method='lm', bounds=(-np.inf, np.inf))
    # print(repr(model.predict(np.geomspace(0.001,2e6,100),True)))
    return model
    def five_transmission_calc_impedance(self):
        self.data_Z, self.data_freq = file_select()
        circuit = 'R0-p(CPE0,R1-p(CPE1,R2))'
        initial_guess = [20, .1, .1, 50, 50, .1, 50]
        circuit = CustomCircuit(circuit, initial_guess=initial_guess)
        circuit.fit(self.data_freq, self.data_Z)
        Z_fit = circuit.predict(self.data_freq)

        fig, ax = plt.subplots()
        plot_nyquist(ax, self.data_Z, fmt="o")
        plot_nyquist(ax, Z_fit, fmt="-")
        plt.legend(["Data", "Fit"])

        self.master.withdraw()
        self.eqc_calc_menu = tkinter.Toplevel()

        img_canvas = tkinter.Canvas(self.eqc_calc_menu,
                                    width=self.five_transmission_pic.width(),
                                    height=self.five_transmission_pic.height())
        img_canvas.grid(row=0, column=0)
        img_canvas.create_image(0,
                                0,
                                image=self.five_transmission_pic,
                                anchor="nw")
        img_canvas.update()

        self.graph_canvas = FigureCanvasTkAgg(fig, self.eqc_calc_menu)
        self.graph_canvas.draw()
        self.graph_canvas.get_tk_widget().grid(row=0, rowspan=3, column=1)

        return_btn = tkinter.Button(
            self.eqc_calc_menu,
            text="Return to Main Menu",
            font=("Helvetica", 14),
            bg="OliveDrab1",
            command=lambda:
            [self.eqc_calc_menu.destroy(),
             self.master.deiconify()])
        return_btn.grid(row=3, column=0)

        exit_btn = tkinter.Button(
            self.eqc_calc_menu,
            text="Exit Program",
            font=("Helvetica", 14),
            bg="tomato",
            command=lambda:
            [self.eqc_calc_menu.destroy(),
             self.master.destroy()])
        exit_btn.grid(row=4, column=0)

        stats = tkinter.Label(self.eqc_calc_menu,
                              text=circuit,
                              font=("Helvetica", 12),
                              justify="left")
        stats.grid(row=2, column=0)
Ejemplo n.º 3
0
def test_model_io():
    # get example data
    data = np.genfromtxt(os.path.join(".", "data", "exampleData.csv"),
                         delimiter=',')

    frequencies = data[:, 0]
    Z = data[:, 1] + 1j * data[:, 2]

    randles = CustomCircuit(
        initial_guess=[None, .005, .1, .005, .1, .001, 200],
        circuit='R0-p(R1,C1)-p(R1,C1)-Wo1',
        constants={'R0': 0.01})
    randles.fit(frequencies, Z)

    randles.save('./test_io.json')
    randles2 = CustomCircuit()
    randles2.load('./test_io.json')
    print(randles2)

    assert randles == randles2
Ejemplo n.º 4
0
def test_CustomCircuit():
    initial_guess = [.01, .005, .1, .005, .1, .001, 200]
    custom_string = 'R0-p(R1,C1)-p(R2,C2)-Wo1'
    custom_circuit = CustomCircuit(initial_guess=initial_guess,
                                   circuit=custom_string)

    # check get_param_names()
    full_names, all_units = custom_circuit.get_param_names()
    assert full_names == ['R0', 'R1', 'C1', 'R2', 'C2', 'Wo1_0', 'Wo1_1']
    assert all_units == ['Ohm', 'Ohm', 'F', 'Ohm', 'F', 'Ohm', 'sec']

    # check _is_fit()
    assert not custom_circuit._is_fit()

    initial_guess = [.01, .005, .1]
    custom_string = 'R0-p(R1,C1)'
    custom_circuit = CustomCircuit(initial_guess=initial_guess,
                                   circuit=custom_string, name='Test')

    assert str(custom_circuit) == \
        '\nName: Test\n' + \
        'Circuit string: R0-p(R1,C1)\n' + \
        'Fit: False\n' + \
        '\nInitial guesses:\n' + \
        '     R0 = 1.00e-02 [Ohm]\n' + \
        '     R1 = 5.00e-03 [Ohm]\n' + \
        '     C1 = 1.00e-01 [F]\n'

    # check that it rejects improper inputs
    # enforcing the length of initial_guess
    try:
        initial_guess = [.01, .005, .1, .005, .1, .001, 200]
        custom_string = 'R0-p(R1,CPE1)-p(R1,C1)-Wo1'
        custom_circuit = CustomCircuit(initial_guess=initial_guess,
                                       circuit=custom_string)
    except(AssertionError):
        pass
    else:
        raise Exception('unhandled error occurred')

    return
Ejemplo n.º 5
0
def fit(data_dir, filename, plot: str = ""):

    array = open_file_numpy(data_dir, filename)
    params = get_params(data_dir, filename)
    print(array.shape)
    data = get_data_numpy(array)
    (freq_log, imped_log, phase, imag_imped, real_imped) = data
    freq = np.power(10, freq_log)

    # np.savetxt("zzz.csv", np.concatenate((freq, real_imped, -imag_imped), axis=1), delimiter=",")
    # f, Z = preprocessing.readCSV("zzz.csv")
    f = freq[:, 0]
    Z = np.array(real_imped + 1j * -imag_imped, dtype=np.complex)[:, 0]

    circuit_str_5a = 'R0-p(R1,CPE1)-p(R2,CPE2)-p(R3,CPE3)'

    R0_guesses = [6850]
    R1_guesses = [1E7, 1E6]
    C1_guesses = [5E-8, 1E-8, 5E-9]
    CPP_guesses = [0.8, 0.9, 1.0]
    R2_guesses = [1E7, 1E5, 1E6, 1E4]
    C2_guesses = [1E-6, 1E-7, 1E-8]
    CPP2_guesses = [0.9, 0.8, 1.0]
    R3_guesses = [1E7, 1E5, 1E6]
    C3_guesses = [1E-5, 1E-6, 1E-7, 1E-8]
    CPP3_guesses = [0.5]
    initial_guesses_5a = []

    for R0 in R0_guesses:
        for R1 in R1_guesses:
            for C1 in C1_guesses:
                for CPP in CPP_guesses:
                    for R2 in R2_guesses:
                        for C2 in C2_guesses:
                            for CPP2 in CPP2_guesses:
                                for R3 in R3_guesses:
                                    for CPP3 in CPP3_guesses:
                                        for C3 in C3_guesses:
                                            initial_guesses_5a.append([
                                                R0, R1, C1, CPP, R2, C2, CPP2,
                                                R3, C3, CPP3
                                            ])
    num_init_guesses = len(initial_guesses_5a)
    print(f"{num_init_guesses} guess combinations for {circuit_str_5a}")
    assert all(initial_guesses_5a), "Cannot use 0 for any initial guesses"
    chi_sentinal = 0.1
    num_remaining = num_init_guesses
    fitted_dict = {}
    for initial_guess in initial_guesses_5a:

        # print(params.voltage, initial_guess)
        num_remaining -= 1
        circuit = CustomCircuit(circuit=circuit_str_5a,
                                initial_guess=initial_guess)
        circuit.fit(
            f,
            Z,
            global_opt=False,
            bounds=(
                # R0        R1      C1     CPP1         R2      C2      CPP2      R3    C3      CPP3"
                [6800, 0, 1E-9, 0.78, 0, 0, 0.78, 0, 0, 0.48],
                [7000, np.inf, 1E-6, 1, np.inf, 1E-5, 1, np.inf, 1E-5, 0.52]))

        fitted_params = circuit.parameters_
        R0, R1, C1, CPP, R2, C2, CPP2, R3, C3, CPP3 = circuit.parameters_
        Z_fit = circuit.predict(f)

        dof = len(Z) - len(
            initial_guess
        )  # Degrees of freedom - i.e. number of data points - number of variables

        Re_Z = np.real(Z)  # Extract real part of Z
        Re_Z_fit = np.real(Z_fit)  # Extract real part of Z_fit
        variance_re = sum(
            (Re_Z - Re_Z.mean())**2) / (len(Re_Z) - 1)  # Variance
        Chi_square_re = sum(((Re_Z - Re_Z_fit)**2) / variance_re)

        Im_Z = np.imag(Z)  # Extract real part of Z
        Im_Z_fit = np.imag(Z_fit)  # Extract real part of Z_fit
        variance_im = sum(
            (Im_Z - Im_Z.mean())**2) / (len(Im_Z) - 1)  # Variance
        Chi_square_im = sum(((Im_Z - Im_Z_fit)**2) / variance_im)

        Zsq = np.sqrt(abs(Re_Z**2 + Im_Z**2))
        Zsq_fit = np.sqrt(abs(Re_Z_fit**2 + Im_Z_fit**2))
        variance = sum((Zsq - Zsq.mean())**2) / (len(Z) - 1)  # Variance
        Chi_square = sum(((Zsq - Zsq_fit)**2) / variance)
        chi_square_diag = sqrt(Chi_square_re**2 + Chi_square_im**2)

        red_Chi_sq = Chi_square / dof  # Reduced Chi squared
        std_err = np.sqrt(red_Chi_sq) * 100
        fitted_dict[tuple(initial_guess)] = (fitted_params, red_Chi_sq, f, Z,
                                             Z_fit, circuit_str_5a,
                                             params.voltage)

        divisor = 60 if num_init_guesses > 60 else num_init_guesses
        if num_remaining in range(1, num_init_guesses,
                                  int(num_init_guesses / divisor)):
            print(f"{initial_guess}  -  {num_remaining} guesses left")

        # if chi_square_diag < chi_sentinal or chi_square_diag < 0.005 or Chi_square_re < chi_re_sentinal or Chi_square_re < 0.0038:
        if red_Chi_sq < chi_sentinal:
            chi_sentinal = red_Chi_sq * 1.02
            print(
                f"Fit {params.voltage}, red_chi2={red_Chi_sq:.8f}, chi2_re={Chi_square_re:.3f}, chi2_im={Chi_square_im:.3f}, std_err={std_err:.4f}, circuit={circuit_str_5a}\t {num_remaining} guesses left"
            )

    fitted_tuple = sorted(fitted_dict.items(), key=lambda item: item[1][1])
    sorted_fitted_dict = OrderedDict(fitted_tuple)
    # pprint.pprint(sorted_fitted_dict)
    return sorted_fitted_dict
Ejemplo n.º 6
0
def fit(data_dir, filename, plot: str = ""):

    array = open_file_numpy(data_dir, filename)
    params = get_params(data_dir, filename)
    print(array.shape)
    data = get_data_numpy(array)
    (freq_log, imped_log, phase, imag_imped, real_imped) = data
    freq = np.power(10, freq_log)

    # np.savetxt("zzz.csv", np.concatenate((freq, real_imped, -imag_imped), axis=1), delimiter=",")
    # f, Z = preprocessing.readCSV("zzz.csv")
    f = freq[:, 0]
    Z = np.array(real_imped + 1j * -imag_imped, dtype=np.complex)[:, 0]

    circuit_str_5a = 'R0-p(R1-p(R2,CPE2),CPE1)'

    R0_guesses = [6850]
    R1_guesses = [1E7, 1E6, 1E8, 5E8]
    C1_guesses = [5E-8, 1E-8, 5E-9, 5E-7, 1E-6]  # , 5E-9, 5E-8, 1E-8]
    CPP_guesses = [0.8, 0.9, 1.0]
    R2_guesses = [1E5, 1E6, 1E7, 1E8]
    C2_guesses = [5E-6, 1E-6, 1E-7, 1E-8]
    CPP2_guesses = [0.8, 0.9, 1.0]
    initial_guesses_5a = []

    for R0 in R0_guesses:
        for R1 in R1_guesses:
            for C1 in C1_guesses:
                for CPP in CPP_guesses:
                    for R2 in R2_guesses:
                        for C2 in C2_guesses:
                            for CPP2 in CPP2_guesses:
                                initial_guesses_5a.append([R0, R1, R2, C2, CPP2, C1, CPP])
    num_init_guesses = len(initial_guesses_5a)
    print(f"{num_init_guesses} guess combinations")
    assert all(initial_guesses_5a), "Cannot use 0 for any initial guesses"
    chi_sentinal = 1.0
    chi_re_sentinal = 1.0
    num_remaining = num_init_guesses
    fitted_dict = {}
    for initial_guess in initial_guesses_5a:
        # print(params.voltage, initial_guess)
        num_remaining -= 1
        circuit = CustomCircuit(circuit=circuit_str_5a, initial_guess=initial_guess)
        circuit.fit(f, Z, global_opt=False, bounds=(
            # R0        R1      R2          C2      CPP2        C1     CPP1
            [6800,      0,      0,          1E-9,      0.78,     1E-9,      0.78],
            # [7000,      1E8,    1E-5,      1,      1E8,      1E-5,      1]
            [7000,      np.inf, np.inf,   1E-5,       1,        1E-5,     1]
        ))

        fitted_params = circuit.parameters_
        R0, R1, R2, C2, CPP2, C1, CPP = circuit.parameters_
        Z_fit = circuit.predict(f)

        # dof = len(Z)-len(initial_guess)                        # Degrees of freedom - i.e. number of data points - number of variables

        Re_Z = np.real(Z)                                        # Extract real part of Z
        Re_Z_fit = np.real(Z_fit)                                # Extract real part of Z_fit
        variance_re = sum((Re_Z-Re_Z.mean())**2) / (len(Re_Z)-1)    # Variance
        Chi_square_re = sum(((Re_Z-Re_Z_fit)**2)/variance_re)

        Im_Z = np.imag(Z)                                        # Extract real part of Z
        Im_Z_fit = np.imag(Z_fit)                                # Extract real part of Z_fit
        variance_im = sum((Im_Z-Im_Z.mean())**2) / (len(Im_Z)-1)    # Variance
        Chi_square_im = sum(((Im_Z-Im_Z_fit)**2)/variance_im)

        variance = sum((Z-Z.mean())**2) / (len(Z)-1)    # Variance
        Chi_square = sum(((Z-Z_fit)**2)/variance)
        chi_square_diag = sqrt(Chi_square_re**2 + Chi_square_im**2)

        red_Chi_sq = Chi_square_re  # Reduced Chi squared

        fitted_dict[tuple(initial_guess)] = (fitted_params, red_Chi_sq, f, Z, Z_fit, circuit_str_5a, params.voltage)

        if num_remaining in range(1, num_init_guesses, int(num_init_guesses / 60)):
            print(f"{initial_guess}  -  {num_remaining} guesses left")

        if chi_square_diag < chi_sentinal or chi_square_diag < 0.01 or Chi_square_re < chi_re_sentinal or Chi_square_re < 0.005:
            if chi_square_diag < chi_sentinal:
                chi_sentinal = chi_square_diag * 1.1
            if Chi_square_re < chi_re_sentinal:
                chi_re_sentinal = Chi_square_re * 1.1

            print(f"Fit {params.voltage}, chi2_re={Chi_square_re:.3f}, chi2_im={Chi_square_im:.3f}, chi2_diag={chi_square_diag:.3f}, circuit={circuit_str_5a}\t {num_remaining} guesses left")

            if plot in ("all", "both"):
                import matplotlib.pyplot as plt
                fig, ax = plt.subplots()
                plot_nyquist(ax, Z, fmt='o')
                plot_nyquist(ax, Z_fit, fmt='-')
                plt.legend(['Data', 'Fit'])
                plt.title(f"{params.voltage}_{red_Chi_sq:.4f}")
                plt.gcf().text(
                    0.8, 0.7,
                    f" R0={R0:.0f}\n\n R1={R1:.0f}\n C1= {C1}\n CPE1={CPP}\n\n R2={R2:.0f}\n C2={C2}\n CPE2={CPP2}\n\n {circuit_str_5a}",
                    fontsize=12)

                fig.set_size_inches(8.5, 5.5, forward=True)
                chi_str = f"{red_Chi_sq: .6f}".replace('.', '_')
                plt.savefig(Rf'{data_dir}\all\{params.voltage}_chi_{chi_str}_{circuit_str_5a}.png',
                            bbox_inches='tight', dpi=500, transparent=True)
                plt.close()
                # plt.show()

    fitted_tuple = sorted(fitted_dict.items(), key=lambda item: item[1][1])
    sorted_fitted_dict = OrderedDict(fitted_tuple)
    # pprint.pprint(sorted_fitted_dict)
    return sorted_fitted_dict
import matplotlib.pyplot as plt
from impedance import preprocessing
from impedance.models.circuits import CustomCircuit
from impedance.visualization import plot_nyquist

circuit = 'R0-p(R1,C1)-p(R2-Wo1,C2)'
initial_guess = [.1, .01, .1, .05, .001, 20, 1]
circuit = CustomCircuit(circuit, initial_guess=initial_guess)

if __name__ == '__main__':
    # Read and fit EIS data
    frequencies, Z = preprocessing.readZPlot(
        'C:\\Users\\luowe\\Desktop\\IRP_code\\Battery_Data\\50th\\10.z')
    frequencies, Z = preprocessing.ignoreBelowX(frequencies, Z)
    circuit.fit(frequencies, Z)
    para = circuit.parameters_

    # Generate training data
    f = 'training data.txt'
    with open(f, "a") as file:
        file.write('50' + ',' + str(para[0]) + ',' + str(para[1]) + ',' +
                   str(para[3]) + '\n')
        file.close()

    # Plot data curve and fit curve
    Z_fit = circuit.predict(frequencies)
    fig, ax = plt.subplots()
    plot_nyquist(ax, Z, fmt='o')
    plot_nyquist(ax, Z_fit, fmt='-')
    plt.legend(['Data', 'Fit'])
    plt.show()
Ejemplo n.º 8
0
def test_CustomCircuit():
    initial_guess = [.01, .005, .1, .005, .1, .001, 200]
    custom_string = 'R0-p(R1,C1)-p(R2,C2)-Wo1'
    custom_circuit = CustomCircuit(initial_guess=initial_guess,
                                   circuit=custom_string)

    # check get_param_names()
    full_names, all_units = custom_circuit.get_param_names()
    assert full_names == ['R0', 'R1', 'C1', 'R2', 'C2', 'Wo1_0', 'Wo1_1']
    assert all_units == ['Ohm', 'Ohm', 'F', 'Ohm', 'F', 'Ohm', 'sec']

    # check _is_fit()
    assert not custom_circuit._is_fit()

    # check predictions from initial_guesses
    high_f = np.array([1e9])
    assert np.isclose(np.real(custom_circuit.predict(high_f)[0]),
                      initial_guess[0])

    # __str()__
    initial_guess = [.01, .005, .1]
    custom_string = 'R0-p(R1,C1)'
    custom_circuit = CustomCircuit(initial_guess=initial_guess,
                                   circuit=custom_string)

    assert str(custom_circuit) == \
        '\nCircuit string: R0-p(R1,C1)\n' + \
        'Fit: False\n' + \
        '\nInitial guesses:\n' + \
        '     R0 = 1.00e-02 [Ohm]\n' + \
        '     R1 = 5.00e-03 [Ohm]\n' + \
        '     C1 = 1.00e-01 [F]\n'

    custom_circuit.fit(f, Z)
    assert custom_circuit._is_fit()
    custom_circuit.plot(f_data=f, Z_data=Z)

    # constants and _ in circuit and no name
    circuit = 'R_0-p(R_1,C_1)-Wo_1'
    constants = {'R_0': 0.02, 'Wo_1_1': 200}
    initial_guess = [.005, .1, .001]
    custom_circuit = CustomCircuit(initial_guess=initial_guess,
                                   constants=constants,
                                   circuit=circuit,
                                   name='Test')

    assert str(custom_circuit) == \
        '\nName: Test\n' + \
        'Circuit string: R_0-p(R_1,C_1)-Wo_1\n' + \
        'Fit: False\n' + \
        '\nConstants:\n' + \
        '    R_0 = 2.00e-02 [Ohm]\n' + \
        '  Wo_1_1 = 2.00e+02 [sec]\n' + \
        '\nInitial guesses:\n' + \
        '    R_1 = 5.00e-03 [Ohm]\n' + \
        '    C_1 = 1.00e-01 [F]\n' + \
        '  Wo_1_0 = 1.00e-03 [Ohm]\n'

    # incorrect number of initial guesses
    with pytest.raises(ValueError):
        initial_guess = [.01, .005, .1, .005, .1, .001, 200]
        custom_string = 'R0-p(R1,CPE1)-p(R1,C1)-Wo1'
        custom_circuit = CustomCircuit(initial_guess=initial_guess,
                                       circuit=custom_string)

    # no initial guesses supplied before fitting
    with pytest.raises(ValueError):
        custom_circuit = CustomCircuit()
        custom_circuit.fit(f, Z)

    # incorrect circuit element in circuit
    with pytest.raises(ValueError):
        custom_circuit = CustomCircuit('R0-NotAnElement', initial_guess=[1, 2])
Ejemplo n.º 9
0
def test_model_io():
    # get example data
    data = np.genfromtxt(os.path.join(".", "data", "exampleData.csv"),
                         delimiter=',')

    frequencies = data[:, 0]
    Z = data[:, 1] + 1j * data[:, 2]

    randles = CustomCircuit(
        initial_guess=[None, .005, .1, .005, .1, 0.9, .001, None],
        circuit='R0-p(R1,C1)-p(R1,CPE1)-Wo1',
        constants={
            'R0': 0.01,
            'Wo1_1': 200
        })
    randles.save('./test_io.json')
    randles2 = CustomCircuit()
    randles2.load('./test_io.json')

    assert randles == randles2

    randles.fit(frequencies, Z)
    randles.save('./test_io.json')
    randles2 = CustomCircuit()
    randles2.load('./test_io.json')

    assert str(randles) == str(randles2)
    assert randles == randles2

    fitted_template = CustomCircuit()
    fitted_template.load('test_io.json', fitted_as_initial=True)