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)
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
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
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()
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])