# -*- coding: utf-8 -*-
"""
Created on Mon May 9 15:56:07 2022
@author: mkolmang
"""
import numpy as np
import os
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
import mplcursors
import json
[docs]class Modulus:
"""
A class to analyze imaginary part of complex electric modulus
"""
def __init__(self):
"""
Returns
-------
None.
"""
pass
[docs] def create_analysis_file(self):
"""
Creates a file to save the fit results based on the choice of fit function
Provides option to use an existing file and creates a new file if not found
Returns
-------
None.
"""
res = input(str("Do you want to use an existing file to save fit results? \n eg: existing file to save HN parameters, y or n:"))
global ana_file
if res == 'y':
ex_file = input("Enter the analysis_file_name:")
try:
f = open(ex_file)
except FileNotFoundError as e:
print(f"{e}")
else:
if os.path.isfile(ex_file):
print("file exists")
else:
ex_file = input("Enter the analysis_file_name:")
f = open(ex_file,'w')
f.write(str('File') + '\t' + str('T') + '\t' + str('beta')+ '\t' + str('Mmax') + '\t'+ str('fmax') + '\n')
print(f'{"file created"}',ex_file)
ana_file = ex_file
return()
[docs] def select_range(self,x,y):
"""
Selects the region of interest to fit data using mplcursors
allows two clicks to select the lower and upper bound of the x-axis
and returns the selected x and y vaues for fitting
Returns
-------
x1 : array
log frequency
y1 : array
log real part of complex conductivity
"""
x = list(x)
y = list(y)
plt.figure(1)
plt.style.use("seaborn-whitegrid")
plt.scatter(x,y,marker='s',color='b',facecolors='none', s=100,linewidth=2)
plt.ylabel('log $\epsilon$"')
plt.xlabel('log f')
plt.legend()
plt.style.use("seaborn-whitegrid")
mplcursors.cursor(hover=True)
zoom_ok = False
plt.title('zoom or pan')
while not zoom_ok:
zoom_ok = plt.waitforbuttonpress()
plt.title('press space when ready to select points')
plt.title('only two clicks are allowed, select the range')
val = plt.ginput(2)
val.sort()
x_min,x_max = val[0][0], val[1][0]
tolerance = 0.03
p1 = round(x_min,3)
p2 = round(x_max,3)
low_x = p1 - tolerance
high_x = p2 + tolerance
print(low_x, high_x)
indices = []
indices.clear()
for i,j in zip(x,y):
if i<= high_x and i>=low_x :
k = x.index(i)
indices.append(k)
a,b = indices[0], indices[-1]
x1 = x[a:b+1]
y1 = y[a:b+1]
print(x1)
print(y1)
x2 = np.array(x1)
y2 = np.array(y1)
print(val)
print("x_lower_limit",x_min, "x_upper_limit",x_max)
return x2,y2
[docs] def modulus_function(self,x,b,mm,fm):
"""
Strechted exponential modulus function to fit the electrical modulus data
Parameters
----------
x : float
log frequency
b : float
strechted exponent
mm : float
Modulus maximum
fm : float
frequency maximum
Returns
-------
y : array
estimated log conductivity based on supplied parameters.
"""
f= 10**x
y_s = ((b*(fm/f)+(f/fm)**b))
y_s2 = b/(1+b)
y = mm/((1-b)+y_s2*y_s)
return y
[docs] def dump_parameters(self):
"""
dumps the initial fit parameters for fit function as a dictionary in a json file
to load it during curve fitting
Returns
-------
None
"""
beta = float(input("enter the beta value:"))
Mmax = float(input("enter the Modulus maximum value:"))
f = float(input("enter the frequency max:"))
fmax = 10**f
par = {"beta": beta, "Mmax": Mmax, "fmax": fmax}
with open('mod.json',"w") as outfile:
json.dump(par,outfile)
with open('mod.json',"r") as openfile:
loaded_par = json.load(openfile)
print("dumped_parameters",loaded_par)
return ()
[docs] def fit(self,x,y):
"""
Fits the conductivity data with choice of fit function
The fit parameters are declared as global variables to be saved
via save_fit function
The initial fit parameters are taken from json file and the final
fit parameters are dumped in the same json file to be used for next
iteration.
Parameters
----------
x : array
log frequency.
y : array
log conductivity.
Returns
-------
None.
"""
x1 = np.array(x)
y1= np.array(y)
global popt1
global beta,mm,fmax
plt.figure()
try:
open('mod.json')
except FileNotFoundError as e:
print(f'{e}' + '\n', "Please dump initial fit parameters using dump.parameters method")
else:
with open('mod.json',"r") as openfile:
loaded_par = json.load(openfile)
#fm = 10**loaded_par['fmax']
p0 = [loaded_par['beta'], loaded_par['Mmax'],loaded_par['fmax']]
mod = self.modulus_function
popt1, pcov1 = curve_fit(mod, x1, y1, p0,bounds = ((0,0,1e-1), (1,np.inf,1e7)))
yfit= mod(x1,*popt1)
plt.scatter(x1,y1,marker='s',color='b',facecolors='none',label='data',s=100,linewidth=2)
plt.plot(x1,yfit,'m--', label='fit',linewidth=2)
plt.xlabel('log ( f [Hz])')
plt.ylabel('M"')
plt.legend()
plt.show()
beta,mm,fm = popt1[:]
fmax = np.log10(fm)
print("fit parameters:\n", popt1)
fit_par = {"beta": beta, "Mmax": mm,"fmax":fmax}
with open('mod.json',"w") as outfile:
json.dump(fit_par,outfile)
with open('mod.json',"r") as openfile:
loaded_par = json.load(openfile)
print("fit parameters dumped for next iteration",loaded_par)
return()
[docs] def save_fit(self,T):
"""
saves the fit parameters of fit function in a file,
the file must be created via create_analysis_file function
Parameters
----------
T : float
Temperature,or can also be an integer
that corresponds to a file number during analysis.
Returns
-------
None.
"""
res_file = open(ana_file,'a')
res_file.write( f'{T}' + '\t' + f'{beta:.03f}' + '\t' + f'{mm:.03f}' + '\t' + f'{fmax:.03f}' +"\n")
return ()