def initial_parameter_heuristic_2(self, spectrum): """ Heuristic 2 Initialize a Cole-Cole parameter set with heuristically determined parameters. This heuristic uses the following procedure for the CC parameters: - rho0 is determined by the lowest frequency magnitude - m is determined using a simple line search which samples the space 0.01 - 1.0 - tau values are determined by logspacing the number of terms over the frequency range. - c is set to 0.5 """ print('Heuristic 2') nr_cc_pars = 1 + 3 * self.nr_cc_terms p0 = np.zeros((1, nr_cc_pars)).flatten() # rho0 p0[0] = spectrum[0] # c c_indices = [i * 3 + 3 for i in xrange(self.nr_cc_terms)] p0[c_indices] = 0.5 # tau tau_min = 1.0 / (2 * np.pi * self.frequencies.max()) tau_max = 1.0 / (2 * np.pi * self.frequencies.min()) tau_indices = [i * 3 + 2 for i in xrange(self.nr_cc_terms)] # log-space sampling of tau range # use 2 more tau values for the boundaries: # we do not want to set an initial tau value to one the frequency # boundaries. tau_space = np.logspace(np.log10(tau_min), np.log10(tau_max), self.nr_cc_terms + 2) p0[tau_indices] = np.log(tau_space[1:-1]) # m # m is selected by testing m values within a certain value range for # the smallest phase rms m_indices = [i * 3 + 1 for i in xrange(self.nr_cc_terms)] test_m_values = np.linspace(0.05, 0.9, 10) best_index = -1 best_rms = np.inf for index, test_m in enumerate(test_m_values): p0[m_indices] = test_m test_forward = colecole.cole_log(self.fin, p0) test_phase_rms = self._phase_rms(spectrum, test_forward) if test_phase_rms < best_rms: best_index = index best_rms = test_phase_rms p0[m_indices] = test_m_values[best_index] return p0
def plot_spectrum(self, filename, id): """ Plot the spectrum specified by id to a file. """ print('Plotting spectrum {0} of {1}'.format(id + 1, self.data.shape[0])) # use more frequencies f_e = np.logspace(np.log10(self.frequencies.min()), np.log10(self.frequencies.max()), 100) fin_e = np.hstack((f_e, f_e)) # generate forward response forward_orig_f = colecole.cole_log(self.fin, self.cc_pars[id]) forward = colecole.cole_log(fin_e, self.cc_pars[id]) forward_init = colecole.cole_log(fin_e, self.cc_pars_init[id]) # generate forward response for each CC term forward_cc_terms = [] for i in range(0, (len(self.cc_pars[id]) - 1) / 3): oneterm_cc = [0, (i * 3) + 1, (i * 3) + 2, (i * 3) + 3] forward_cc_terms.append( colecole.cole_log(fin_e, self.cc_pars[id][oneterm_cc])) fig, axes = plt.subplots(3, 1, figsize=(7, 6)) # plot magnitude ax = axes[0] ax.semilogx(f_e, (np.exp(forward_init[0, :])), 'b-', linestyle='dashed', label='initial parameters') ax.semilogx(f_e, (np.exp(forward[0, :])), 'g-', label='fit response') ax.semilogx(self.frequencies, (np.exp(self.data[id, 0: len(self.data[id, :]) / 2])), 'r.', linewidth=2.0, label='data') ax.set_title('magnitude RMS: {0:.3e}'.format(self.magnitude_rms[id])) ax.set_xlabel('frequency [Hz]') ax.set_ylabel(r'$|Z/\rho| [\Omega (m)]$') # plot phase ax = axes[1] ax.semilogx(f_e, -forward[1, :], 'g-', linewidth=2.0, label='fit response') ax.semilogx(self.frequencies, -self.data[id, len(self.data[id, :]) / 2:], 'r.', linewidth=2.0, label='data') ax.semilogx(f_e, -forward_init[1, :], 'b-', linestyle='dashed', label='initial model') # plot single terms colors = ('k', 'gray') for nr, term in enumerate(forward_cc_terms): ax.semilogx(f_e, -term[1, :], '-', color=colors[nr % 2], linestyle='dashed', label='term {0}'.format(nr + 1) ) ax.legend(loc="upper center", ncol=3, bbox_to_anchor=(0, 0, 1, 1), bbox_transform=fig.transFigure) ax.set_title('phase RMS: {0:.3e}'.format(self.phase_rms[id])) ax.set_xlabel('frequency [Hz]') ax.set_ylabel(r'$-\varphi~[mrad]$') # plot phase residuals ax = axes[2] pha_residuals = np.abs(forward_orig_f[1, :] - self.data[id, len(self.data[id, :]) / 2:]) ax.semilogx(self.frequencies, pha_residuals, '-') ax.set_xlabel('frequency [Hz]') ax.set_ylabel(r'abs ($\phi_{inv} - \phi_{ori}$)') ax.set_title( 'RMS: {0}'.format(np.sqrt(np.sum(pha_residuals ** 2)) / self.frequencies.shape[0])) for ax in axes: ax.yaxis.set_major_locator(mpl.ticker.MaxNLocator(5)) fig.tight_layout() fig.subplots_adjust(top=0.8) fig.savefig('{0}.png'.format(filename)) plt.close(fig)
def __residuals(self, p, y, x): """ Compute residuals of the measured data y and the response of the Cole-Cole function cole_log for the x(frequency) values and the parameters p. Used for leastsq function. """ try: erg = colecole.cole_log(x, p) except: raise('error') erg1 = np.hstack((erg[0, :], erg[1, :])) err = y - erg1 # NaN check and set err to 1e10 if(np.isnan(err).any()): err *= 10e10 # y holds the original data # compute weighting factors according to the invers of the phase values # mag_ori = y[:y.shape[0] / 2] # pha_ori = y[y.shape[0] / 2:] # mean_all = np.mean(np.abs(y)) # mean_weight_all = np.abs(y) / mean_all # err /= mean_weight_all # we want both mag and pha to have equal mean values AFTER weighting # pha_mean_weight = np.abs(np.mean(mag_ori)) / np.abs(np.mean(pha_ori)) # print('mean(mag)', np.mean(mag_ori)) # print('mean(pha)', np.mean(pha_ori * pha_mean_weight)) # pha_weighting = np.abs(pha_ori)**2 # # pha_weighting = (np.abs(pha_ori)**4) # pha_weighting = (np.abs(pha_ori))**2 * 10 # # print(pha_weighting) # err[err.shape[0]/2:] *= pha_weighting # err[err.shape[0]/2:] *= pha_mean_weight # # mag # err[0:err.shape[0]/2] *= 100 # implement a simple form of limit checking. If the given Cole-Cole # parameters lie out of certain bounds, increase the residual to a very # large value. This will ensure that this parameter set will not be # used any more. # #rho0 # if(p[0] > 10e6 or np.abs(np.exp(p[0]) - np.exp(y[0])) > 0.5): # err *= 1e10 # m if(p[1] < 0 or (len(p) > 4 and p[4] < 0)): err *= 1e10 if(p[1] > 1 or (len(p) > 4 and p[4] > 1)): err *= 1e10 # tau if(p[2] < -12): err += 1e10 # c if(p[3] < 0 or p[3] > 1): err *= 1e10 # 2 CC terms and tau if(len(p) > 4 and (p[6] < 0 or p[6] > 1)): err *= 1e10 return err
def fit_spectrum(self, spectrum, p0): """ Fit a spectrum, given the data, the frequencies, and the starting parameters. Return the fit results, the magnitude and phase RMS, and the forward response of the fit parameters. """ nr_to_fit = len(spectrum) / 2 y_meas = np.hstack( (spectrum[0:nr_to_fit], spectrum[len(spectrum) / 2:len(spectrum) / 2 + nr_to_fit])) x = np.hstack((self.fin[0:nr_to_fit], self.fin[len(self.fin) / 2:len(self.fin) / 2 + nr_to_fit])) # the actual fitting routine plsq, cov, info, mesg, success = leastsq( self.__residuals, p0, args=(y_meas, x), full_output=1, maxfev=10000000) # plsq,cov,info,mesg,success = leastsq( # self.__residuals, p0, Dfun=self.__Dres, # args=(y_meas, x), maxfev= 10000000, full_output=1) # compute spectral response using fit results forward = colecole.cole_log(self.fin, plsq) # compute mag rms mag_rms = np.sqrt( sum((spectrum[0:len(spectrum) / 2] - forward[0, :]) ** 2)) mag_rms /= (len(spectrum / 2)) # compute pha rms pha_rms = self._phase_rms(spectrum, forward) # compute complex rms rms = np.sqrt( sum((spectrum[0:len(spectrum) / 2] - forward[0, :]) ** 2) + sum((spectrum[len(spectrum) / 2:len(spectrum)] - forward[1, :]) ** 2)) rms /= len(spectrum) # calculate final chi square chisq = sum(info["fvec"] * info["fvec"]) # deegrees of freedom dof = len(self.fin) / 2 - len(p0) # residuals d - f # mx_fvec = self.__residuals(plsq, y_meas, x) # compute fit errors ('aymptotic standard error') np.seterr(all='raise') if cov is not None: errors = np.zeros((plsq.shape[0])) for i, pmin in enumerate(plsq): try: # uncertainties are calculated as per gnuplot, "fixing" the # result - # for non unit values of the # reduced chisq. errors[i] = np.sqrt(cov[i, i]) * np.sqrt(chisq / dof) except FloatingPointError: print('FloatingPointError:') print(cov[i, i]) print(chisq / dof, chisq, dof) errors[i] = np.nan else: errors = None # verbose output of fit results if option is selected plot = False if plot: if cov is not None: print("Fitted parameters at minimum, with 68% C.I.:") for i, pmin in enumerate(plsq): print('{0:02} {1:.10} +/- {2:.10} ({3:.10}%)'.format( i, pmin, errors[i], errors[i] / pmin * 100)) print print "Correlation matrix" for i in range(len(plsq)): for j in range(i + 1): print('{0}'.format( cov[i, j] / np.sqrt(cov[i, i] * cov[j, j]))) print('') print('') # return values fit_parameters = plsq # TODO: Sort CC terms for descending Tau values # sort fit_parameters, mag_rms, pha_rms, forward, errors return fit_parameters, mag_rms, pha_rms, forward, errors