def multi_fit(data_dir: str, plot: str = "best") -> FittedMultiData: """Fits all files in a folder. if plot = "best", plots best fitted data if plot = "all", plots all if plot = "", plot none """ group_fitted = {} group_fitted_list = [] for filepath in reversed(list(Path(data_dir).glob('*.csv'))): filename = str(filepath.name) array = open_file_numpy(data_dir, filename) data = get_data_numpy(array) (freq_log, imped_log, phase, imag_imped, real_imped) = data single_data = SingleData(data=data, params=get_params(data_dir, filename)) # print(single_data) fitted_dict = fit(data_dir, filename, plot=plot) best_fit, chi2, f, Z, Z_fit, circuit_str, voltage = list(fitted_dict.values())[0] R0, R1, R2, C2, CPP2, C1, CPP = best_fit print(chi2, R0, R1, R2, C2, C1, CPP, CPP2) fit_dc = fit_single_data(stored_data=single_data, fit_params=best_fit, chi2=chi2, circuit=circuit_str, Z_fit=Z_fit) assert fit_dc.voltage == voltage if plot == "best" or "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"{voltage}, chi2={chi2:.3f}") # , R2={best_fit[1]}, C2={best_fit[0]}, R3={R3}, C3={C3} ") # plt.show() fig.set_size_inches(18.5, 10.5, forward=True) plt.savefig(Rf'{data_dir}\{fit_dc.voltage}_bestfit2Rp.png', bbox_inches='tight', dpi=500, transparent=True) with open(R".\res.csv", 'a') as f: f.write(f"{fit_dc.voltage}, \t {R0}, {R1}, {C1}, {CPP}, {R2}, {C2}, {CPP2} \n") group_fitted[fit_dc.voltage] = { 'circuit_str': circuit_str, 'fit': best_fit, 'chi2': chi2, 'data': (f, Z, Z_fit), 'voltage': voltage, 'invsqc1': 1 / (C1 ** 2 * 1000000000000), 'invsqc2': 1 / (1 / C2 ** 2 * 1000000000000) } group_fitted_list.append(fit_dc) group_fitted_dc = FittedMultiData(group_fitted_list) # group_fitted_json = json.dumps(group_fitted, sort_keys=True, indent=4, separators=(',', ': ')) # with open(Rf'{data_dir}\fitted_data.json', 'w') as f: # f.write(group_fitted_json) return group_fitted_dc
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 sub_Zg_series(f, Z, tg, Rg_range, num, show_plot=True): Zgs = [] Z_adjs = [] Rgs = np.logspace(Rg_range[0], Rg_range[1], num=num) for Rg in Rgs: Zg = G([Rg, tg], f) Zgs.append(Zg) Zgs = np.array(Zgs) fig, ax = plt.subplots(figsize=(18, 12)) fig2, (ax1, ax2) = plt.subplots(nrows=2, figsize=(30, 20)) plot_nyquist(ax, Z) for i, Zg in enumerate(Zgs): Z_adj = Z - Zg Z_adjs.append(Z_adj) f_p_idx = np.argmin(np.imag(Z_adj)) f_p = f[f_p_idx] ax.plot(Z_adj.real, -Z_adj.imag, label='%.2f Hz' % f_p, c=(0, i / len(Zgs), .4)) ax.plot(Z_adj[f_p_idx].real, -Z_adj[f_p_idx].imag, 's', c=(0, 0, 0)) ax1.plot(np.log10(f), Z_adj.real, label='%.2f' % Rgs[i], c=(0, i / len(Zgs), .4)) ax2.plot(np.log10(f), Z_adj.imag, label='%.2f' % Rgs[i], c=(0, i / len(Zgs), .4)) # ax.set_xlim(-80, 300) # ax.set_ylim(-80, 300) ax1.grid(True) ax2.grid(True) ax.legend() plt.show() return Z_adjs, ax, ax1, ax2
def plot_model_fit_overview(f_i, z_i, model): #Generate plot data from fitted model z_fit = model.predict(f_i) fig = plt.figure(constrained_layout=True, figsize=(12, 5)) gs = fig.add_gridspec(2, 2) f_ax1 = fig.add_subplot(gs[:, 0]) f_ax1.set_title("Nyquist plot") f_ax2 = fig.add_subplot(gs[0, 1]) f_ax2.set_title("Bode plot") f_ax3 = fig.add_subplot(gs[1, 1]) plot_nyquist(f_ax1, z_i, fmt='o') plot_nyquist(f_ax1, z_fit, fmt='-') plot_bode((f_ax2, f_ax3), f_i, z_i, ls='', marker='s', label='Data') plot_bode((f_ax2, f_ax3), f_i, z_fit, ls='-', marker='', label='Fit') f_ax2.legend() return gs
def test_plot_nyquist(): Z = np.array([1, 2, 3]) + 1j*np.array([2, 3, 4]) _, ax = plt.subplots() ax = plot_nyquist(ax, Z, scale=10) xs, ys = ax.lines[0].get_xydata().T assert (xs == Z.real).all() and (ys == -Z.imag).all()
frequencies5, Z5 = preprocessing.readZPlot( 'C:\\Users\\luowe\\Desktop\\IRP_code\\1st\\5.z') frequencies5, Z5 = preprocessing.ignoreBelowX(frequencies5, Z5) frequencies6, Z6 = preprocessing.readZPlot( 'C:\\Users\\luowe\\Desktop\\IRP_code\\1st\\6.z') frequencies6, Z6 = preprocessing.ignoreBelowX(frequencies6, Z6) frequencies7, Z7 = preprocessing.readZPlot( 'C:\\Users\\luowe\\Desktop\\IRP_code\\1st\\7.z') frequencies7, Z7 = preprocessing.ignoreBelowX(frequencies7, Z7) frequencies8, Z8 = preprocessing.readZPlot( 'C:\\Users\\luowe\\Desktop\\IRP_code\\1st\\8.z') frequencies8, Z8 = preprocessing.ignoreBelowX(frequencies8, Z8) frequencies9, Z9 = preprocessing.readZPlot( 'C:\\Users\\luowe\\Desktop\\IRP_code\\1st\\9.z') frequencies9, Z9 = preprocessing.ignoreBelowX(frequencies9, Z9) frequencies10, Z10 = preprocessing.readZPlot( 'C:\\Users\\luowe\\Desktop\\IRP_code\\1st\\10.z') frequencies10, Z10 = preprocessing.ignoreBelowX(frequencies10, Z10) ''' fig, ax = plt.subplots() plot_nyquist(ax, Z1, fmt='o') plot_nyquist(ax, Z2, fmt='o') plt.legend(['30th', '50th']) plt.show()
def multi_fit(data_dir: str, plot: str = "both", output_dir: str = "") -> FittedMultiData: """Fits all files in a folder. if plot = "best", plots best fitted data if plot = "all", plots all if plot = "", plot none """ data_path = Path(data_dir) output_path = Path( output_dir) if output_dir else data_path.parent / "output" group_fitted = {} group_fitted_list = [] for filepath in list(data_path.glob('*.csv'))[1:]: filename = str(filepath.name) array = open_file_numpy(data_dir, filename) data = get_data_numpy(array) (freq_log, imped_log, phase, imag_imped, real_imped) = data single_data = SingleData(data=data, params=get_params(data_dir, filename)) # print(single_data) fitted_dict = fit(data_dir, filename, plot=plot) if plot in ("all", "both"): number_printed = 21 elif plot == "best": number_printed = 1 for i in range(number_printed): loop_fit, chi2_loop, f_array, Z_loop, Z_fit_loop, circuit_str, voltage = list( fitted_dict.values())[i] fit_dc = FittedSingleData(stored_data=single_data, fit_params=loop_fit, chi2=chi2_loop, circuit=circuit_str, Z_fit=Z_fit_loop) R0, R1, C1, CPP, R2, C2, CPP2, R3, C3, CPP3 = loop_fit import matplotlib.pyplot as plt fig, ax = plt.subplots() plot_nyquist(ax, Z_loop, fmt='o') plot_nyquist(ax, Z_fit_loop, fmt='-') plt.legend(['Data', 'Fit']) plt.title(f"{voltage}_{chi2_loop:.4f}") fig.set_size_inches(8.5, 5.5, forward=True) plt.gcf().text( 1.1, 0.1, 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 R3={R3:.0f}\n C3={C3}\n CPE3={CPP3}\n\n {circuit_str}\n\n chi2v={chi2_loop}", fontsize=12) chi_str = f"{chi2_loop: .8f}".replace('.', '_') plt.savefig( Rf'{output_path}\{voltage}_chi_{chi_str}_{circuit_str}.png', bbox_inches='tight', dpi=500, transparent=True) if i == 0 and plot in ("best", "both"): plt.savefig( Rf'{output_path}\best\{voltage}_chi_{chi_str}_{circuit_str}_b.png', bbox_inches='tight', dpi=500, transparent=True) with open(R".\res_best.csv", 'a') as data_f: data_f.write( f"""{fit_dc.voltage}, \t \n {chi2_loop} {R0}, {R1}, {C1}, {CPP}, {R2}, {C2}, {CPP2}, {R3}, {C3}, {CPP3}, \n""") plt.close() # plt.show() group_fitted[fit_dc.voltage] = { 'circuit_str': circuit_str, 'fit': loop_fit, 'chi2': chi2_loop, 'data': (f_array, Z_loop, Z_fit_loop), 'voltage': voltage, 'invsqc1': 1 / (C1**2), 'invsqc2': 1 / (C2**2) } # group_fitted_json = json.dumps(group_fitted, sort_keys=True, indent=4, separators=(',', ': ')) # with open(Rf'{data_dir}\fitted_data.json', 'w') as f: # f.write(group_fitted_json) group_fitted_list.append(fit_dc) group_fitted_dc = FittedMultiData(group_fitted_list) return group_fitted_dc
def sub_Zg_parallel(f, Z, tg, Rg_range, num, show_plot=True): Y = 1 / Z Zgs, Ygs = [], [] Z_adjs = [] if len(Rg_range) > 1: Rgs = np.logspace(Rg_range[0], Rg_range[1], num=num) else: Rgs = Rg_range for Rg in Rgs: Zg = G([Rg, tg], f) Yg = 1 / Zg Zgs.append(Zg) Ygs.append(1 / Zg) Zgs = np.array(Zgs) Ygs = np.array(Ygs) if show_plot is False: for i, Yg in enumerate(Ygs): Y_adj = Y - Yg Z_adj = 1 / Y_adj Z_adjs.append(Z_adj) return np.array(Z_adjs) else: fig, ax = plt.subplots(figsize=(18, 12)) fig2, (ax1, ax2) = plt.subplots(nrows=2, figsize=(30, 20)) plot_nyquist(ax, Z, label='Initial Data') for i, Yg in enumerate(Ygs): Y_adj = Y - Yg Z_adj = 1 / Y_adj Z_adjs.append(Z_adj) f_p_idx = np.argmin(np.imag(Z_adj)) f_p = f[f_p_idx] ax.plot(Z_adj.real, -Z_adj.imag, '.-', label='#: %i %.2f Hz' % (i, f_p), c=(0, i / len(Ygs), .4)) ax.plot(Z_adj[f_p_idx].real, -Z_adj[f_p_idx].imag, 's', c=(0, 0, 0)) ax1.plot(np.log10(f), Z_adj.real, label='%.2f' % Rgs[i], c=(0, i / len(Ygs), .4)) ax2.plot(np.log10(f), Z_adj.imag, label='%.2f' % Rgs[i], c=(0, i / len(Ygs), .4)) # ax.set_xlim(-80, 300) # ax.set_ylim(-80, 300) ax1.grid(True) ax2.grid(True) ax.legend() return np.array(Z_adjs), [ax, ax1, ax2]
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
def plot(self, ax=None, f_data=None, Z_data=None, kind='altair', **kwargs): """ visualizes the model and optional data as a nyquist, bode, or altair (interactive) plots Parameters ---------- ax: matplotlib.axes axes to plot on f_data: np.array of type float Frequencies of input data (for Bode plots) Z_data: np.array of type complex Impedance data to plot kind: {'altair', 'nyquist', 'bode'} type of plot to visualize Other Parameters ---------------- **kwargs : optional If kind is 'nyquist' or 'bode', used to specify additional `matplotlib.pyplot.Line2D` properties like linewidth, line color, marker color, and labels. If kind is 'altair', used to specify nyquist height as `size` Returns ------- ax: matplotlib.axes axes of the created nyquist plot """ if kind == 'nyquist': if ax is None: fig, ax = plt.subplots(figsize=(5, 5)) if Z_data is not None: ax = plot_nyquist(ax, Z_data, ls='', marker='s', **kwargs) if self._is_fit(): if f_data is not None: f_pred = f_data else: f_pred = np.logspace(5, -3) Z_fit = self.predict(f_pred) ax = plot_nyquist(ax, Z_fit, ls='-', marker='', **kwargs) return ax elif kind == 'bode': if ax is None: fig, ax = plt.subplots(nrows=2, figsize=(5, 5)) if f_data is not None: f_pred = f_data else: f_pred = np.logspace(5, -3) if Z_data is not None: if f_data is None: raise ValueError('f_data must be specified if' + ' Z_data for a Bode plot') ax = plot_bode(ax, f_data, Z_data, ls='', marker='s', **kwargs) if self._is_fit(): Z_fit = self.predict(f_pred) ax = plot_bode(ax, f_pred, Z_fit, ls='-', marker='', **kwargs) return ax elif kind == 'altair': plot_dict = {} if Z_data is not None and f_data is not None: plot_dict['data'] = {'f': f_data, 'Z': Z_data} if self._is_fit(): if f_data is not None: f_pred = f_data else: f_pred = np.logspace(5, -3) Z_fit = self.predict(f_pred) if self.name is not None: name = self.name else: name = 'fit' plot_dict[name] = {'f': f_pred, 'Z': Z_fit, 'fmt': '-'} chart = plot_altair(plot_dict, **kwargs) return chart else: raise ValueError("Kind must be one of 'altair'," + f"'nyquist', or 'bode' (received {kind})")
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()
sys.path.append(os.path.join("./src/")) from base import plot2d import logging logging.getLogger('matplotlib').setLevel(logging.ERROR) if __name__ == '__main__': argvs = sys.argv parser = OptionParser() parser.add_option("--dir", dest="dir", default="./") parser.add_option("--pxyz", dest="pxyz", default=[0.0, 0.0, 0.0], type="float", nargs=3) opt, argc = parser.parse_args(argvs) print(opt, argc) obj = plot2d() frequencies, Z = preprocessing.readCSV('impedance_data.csv') circuit = 'R0-p(R1,C1)-p(R2-Wo1,C2)' initial_guess = [.01, .01, 100, .01, .05, 100, 1] circuit = CustomCircuit(circuit, initial_guess=initial_guess) Z_fit = circuit.predict(frequencies) plot_nyquist(obj.axs, Z) plot_nyquist(obj.axs, Z_fit) obj.SavePng()