3__author__ = "Patrick Louden (plouden@nd.edu)"
4__copyright__ = "Copyright (c) 2004-present The University of Notre Dame. All Rights Reserved."
14from scipy.optimize import curve_fit
15from argparse import RawDescriptionHelpFormatter
16from scipy import stats
25# Fit function we are optimizing data to
26def func(x, Vs, Z1, Z2, ml, w, Vl):
27 ''' from Mathematica CForm
28 0.5*(Vl + ml*(x - Z1))*(1 - Tanh((x - Z1)/w)) +
29 0.5*Vs*(Tanh((x - Z1)/w) - Tanh((x - Z2)/w)) +
30 0.5*(Vl - ml*(x - Z2))*(1 + Tanh((x - Z2)/w))
33 return ( 0.5*(Vl+ml*(x-Z1))*(1.0-np.tanh((x-Z1)/w))+0.5*Vs*(np.tanh((x-Z1)/w)-np.tanh((x-Z2)/w))+0.5*(Vl-ml*(x-Z2))*(1.0+np.tanh((x-Z2)/w)) )
40#This function reads in the .rnemd file and extracts the actual momentum flux, as well as the zPositions and the average velocities therein.
41def rnemdExtractor(rnemdFileName):
42 if os.path.exists(rnemdFileName):
43 rnemdFile = open(rnemdFileName, 'r')
45 print('Error: cannot open ' + rnemdFileName)
48 rnemdZPos = np.array([])
49 rnemdV = np.array([]).reshape(0, 3)
52 line = rnemdFile.readline()
54 if "Actual flux:" in line or "actual flux:" in line:
56 line = rnemdFile.readline()
57 rnemdJzP = np.array([[float(line.split()[4][:-1])], [float(line.split()[5][:-1])], [float(line.split()[6])]])
59 if len(line.split()) > 6:
60 print("Error in the .rnemd file, found more than 6 expected columns.")
62 rnemdZPos = np.append(rnemdZPos, np.array([float(line.split()[0])]))
63 rnemdV = np.vstack( [rnemdV, [ float(line.split()[2]), float(line.split()[3]), float(line.split()[4]) ] ] )
65 #Now let's determine what kind of shearing simulation was performed
66 # ie, JzPx, JzPy, or JzPxy
67 if (rnemdJzP[2] != 0.0):
68 print("Error: JzPz Actual Exchange total was found to be >0.0 in (.rnemd) file.")
70 if (rnemdJzP[0] == 0.0 and rnemdJzP[1] == 0.0):
71 print("Error: both JzPx and JzPy Actual Exchange totals were found to be 0.0 in (.rnemd) file. ")
73 elif (rnemdJzP[0] != 0.0 and rnemdJzP[1] != 0.0):
74 print("Error: both JzPx and JzPy Actual Exchange totals were found to be >0.0 in (.rnemd) file.\n This script only calculates friction coefficients for single dimensional momentum fluxes.")
77 #After sanity checking, we now set a variable jzType to store if the imposed momentum flux is in the x- (0) or y-direction (1).
78 #jzType will be used when we iterate through rnemdV
79 #jzType = 0 -> flux was in x-dimension
80 #jzType = 1 -> flux was in y-dimension
81 elif (rnemdJzP[0] == 0.0 or rnemdJzP[1] == 0.0):
82 if np.abs(rnemdJzP[0]) > np.abs(rnemdJzP [1]):
87 print("Read in " + str(rnemdFileName))
88 return (rnemdJzP, jzType, rnemdZPos, rnemdV)
97def velFitter(rnemdJzP, jzType, rnemdZPos, rnemdV, Vs, Z1, Z2, m, w, Vl, numDel, outputFileName):
98 rnemdVx = np.array([]).reshape(0, 1)
99 rnemdVy = np.array([]).reshape(0, 1)
101 for i in range(0, len(rnemdV)):
102 rnemdVx = np.append(rnemdVx, rnemdV[i][0])
103 rnemdVy = np.append(rnemdVy, rnemdV[i][1])
105 #Remove data points from the start and last end of the vector of values of rnemdVx and rnemdVy to smooth fits
106 for i in range(0, numDel):
107 rnemdVx = np.delete(rnemdVx, 0)
108 rnemdVy = np.delete(rnemdVy, 0)
109 rnemdZPos = np.delete(rnemdZPos, 0)
110 for i in range(0, numDel+1):
111 rnemdVx = np.delete(rnemdVx, len(rnemdVx)-1)
112 rnemdVy = np.delete(rnemdVy, len(rnemdVy)-1)
113 rnemdZPos = np.delete(rnemdZPos, len(rnemdZPos)-1)
116 #curve_fit(function to fit to, x-data, y-data, [initial guess of param 1, initial guess of param 2,...]
118 popt, pcov = curve_fit(func, rnemdZPos, rnemdVx, [Vs, Z1, Z2, m, w, Vl], ftol=0.01)
120 popt, pcov = curve_fit(func, rnemdZPos, rnemdVy, [Vs, Z1, Z2, m, w, Vl], ftol=0.01)
123 #write out the raw data and the fit for visual inspection of quality of fit
124 print("Writing out fit file")
125 fitFileName = outputFileName + "FIT"
126 fitFile = open(fitFileName, "w")
127 fitFile.write("# zPosition <V_{i}> fit value" + "\n")
128 for i in range(0, len(rnemdZPos)):
129 y = func(rnemdZPos[i], popt[0], popt[1], popt[2], popt[3], popt[4], popt[5])
131 fitFile.write(str(rnemdZPos[i]) + "\t" + str(rnemdVx[i]) + "\t" + str(y) + "\n")
133 fitFile.write(str(rnemdZPos[i]) + "\t" + str(rnemdVy[i]) + "\t" + str(y) + "\n")
136 #print "popt = ", popt
145def calcShearRate(jzType, rnemdZPos, rnemdV):
146 # jzType = 0 -> flux was in x-dimension
147 # jzType = 1 -> flux was in y-dimension
149 velLiq = (rnemdV[0][jzType] + rnemdV[len(rnemdV)-1][jzType])/2.0
151 if (len(rnemdV) % 2) == 0 :
152 velSol = ( rnemdV[(len(rnemdV) / 2)-1][jzType] + rnemdV[(len(rnemdV)/ 2)][jzType]) / 2.0
153 elif (len(rnemdV) % 2) != 0:
154 velSol = ( rnemdV[(len(rnemdV) + 1)/2][jzType] + rnemdV[((len(rnemdV)+1)/2)+1][jzType]) / 2.0
156 shearRate = velSol - velLiq
165def lambdaCalc(rnemdJzP, jzType, popt):
166 print("Performing lambda calculation")
167 #popt = Vs, Z1, Z2, ml, w, Vl
169 zLiq = (popt[0] - popt[5]) / popt[3] + popt[1]
170 zSol = popt[1] + 0.5*popt[4]
174 lambda1 = rnemdJzP[0] / (delta * popt[3])
176 lambda1 = rnemdJzP[1] / (delta * popt[3])
179 return (zLiq, zSol, delta, lambda1)
191def writeOutputFile(outputFileName, rnemdFileName, popt):
192 outFile = open(outputFileName, "w")
194 outFile.write("##################################################### \n")
195 outFile.write("## This output file was generated by slipLength on " + str(datetime.datetime.now()) + " ## \n#\n")
196 outFile.write("# The velocity profile found in " + str(rnemdFileName) + " was fit by \n")
197 outFile.write("# \ty = (Vl + ml(x-Z1))*(0.5(1-tanh((x-Z1)/w))) + Vs*0.5(tanh((x-Z1)/2)-tanh((x-Z2)/w)) + (Vl-ml(x-Z2))*(0.5(1+tanh((x-Z2/w)))) \n")
198 outFile.write("# where (Vs, Vl, Z1, Z2, ml, w) are fit parameters, and here y = V(i) and x = z \n")
199 outFile.write("# Vs = velocity of the solid\n# Vl = velocity of the liquid at the interface \n")
200 outFile.write("# Z1 = z-position of the lower interface \n# Z2 = z-position of the upper interface \n")
201 outFile.write("# ml = slope of the velocity profile in the lower liquid region of the box \n")
203 outFile.write("# w = width of the interface \n#\n")
204 outFile.write("# Obtained optomized parameters \n")
205 outFile.write("# \t Vs = " + str(popt[0]) + "\n")
206 outFile.write("# \t Vl = " + str(popt[5]) + "\n")
207 outFile.write("# \t Z1 = " + str(popt[1]) + "\n")
208 outFile.write("# \t Z2 = " + str(popt[2]) + "\n")
209 outFile.write("# \t ml = " + str(popt[3]) + "\n")
210 outFile.write("# \t w = " + str(popt[4]) + "\n#\n")
212 if (float(popt[0]) < float(popt[5])):
213 print("Bad fit detected, Vs < Vl")
214 outFile.write("# WARNING Vs < Vl \n#\n")
220def writeOutputLambda(outputFileName, rnemdJzP, jzType, zLiq, zSol, delta, lambda1, shearRate):
221 outFile = open(outputFileName, "a")
222 outFile.write("# The interfacial slip length, \lambda, was calculated by \n")
223 outFile.write("# \tlambda = Jz(p) / (dV/dz)_{liquid} / delta \n")
224 outFile.write("# where Jz(p) is the imposed momentum flux, (dV/dz)_{liquid} is the slope of the velocity profile in the liquid \n")
225 outFile.write("# portion of the simulation box, and delta is the slip length determined by projecting the velocity profile of \n")
226 outFile.write("# liquid into the solid. \n#\n")
227 outFile.write("# zLiq = " + str(zLiq) + "\n")
228 outFile.write("# zSol = " + str(zSol) + "\n")
229 outFile.write("# \delta (angstroms) = " + str(delta) + "\n")
231 outFile.write("# Jz(px) = " + str((float(rnemdJzP[0]))) + "\n#\n")
233 outFile.write("# Jz(py) = " + str((float(rnemdJzP[1]))) + "\n#\n")
234 outFile.write("# shear rate (angstroms / fs) = " + str((float(shearRate))) + "\n" )
235 outFile.write("# \lambda (amu/fs/angstrom/angstrom) = " + str((float(lambda1))) + "\n")
238 outFile.write("# WARNING NEGATIVE LAMBDA VALUE FOUND \n#\n#\n")
252 parser = argparse.ArgumentParser(
253 description='OpenMD solid/liquid slip length and slip-boundary friction coefficient calculator for orthorhombic systems.',
254 #formatter_class=RawDescriptionHelpFormatter,
255 epilog="Example: slipLength -i shearSim.rnemd -o shearSim.vfit -z1 30.4 -z2 75.6 -w 4.5 -Vs 1.2e-5 -Vl 1.0e-6 -m 2.2e-6 -d 2")
256 parser.add_argument("-i", "--rnemd-file=", action="store", dest="rnemdFileName", help="use specified input (.rnemd) file")
257 parser.add_argument("-o", "--vfit-file=", action="store", dest="vfitFileName", help="use specified output (.vfit) file")
258 parser.add_argument("-z1", "--lowerGibbsZ=", action="store", type=float, dest="z1", help="the location of the lower Gibbs dividing surface")
259 parser.add_argument("-z2", "--upperGibbsZ=", action="store", type=float, dest="z2", help="the location of the upper Gibbs dividing surface")
260 parser.add_argument("-l", "--lowerZVal=", action="store", nargs='?', type=float, dest="l", help="the initial estimate of the lower interface location (default=z1)")
261 parser.add_argument("-u", "--upperZVal=", action="store", nargs='?', type=float, dest="u", help="the initial estimate of the upper interface location (default=z2)")
262 parser.add_argument("-w", "--intWidth=", action="store", type=float, dest="w", help="the width of the interface")
263 parser.add_argument("-Vs", "--solidVel=", action="store", type=float, dest="Vs", help="the initial estimate of the velocity of the solid")
264 parser.add_argument("-Vl", "--liquidVel=", action="store", type=float, dest="Vl", help="the initial estimate of the velocity of the liquid")
265 parser.add_argument("-m", "--liquidSlope=", action="store", type=float, dest="m", help="the initial estimate of the slope in the liquid")
266 parser.add_argument("-d", "--toDelete=", action="store", default=0, type=int, dest="toDel", help="the number of data points to be deleted from the beginning and end of the velocity profile. (default=0)")
271 if len(sys.argv) == 1:
274 args = parser.parse_args()
276 if (not args.rnemdFileName):
278 parser.error("No input-file was specified")
280 if (not args.vfitFileName):
282 parser.error("No output-file was specified")
286 parser.error("No lower Gibbs dividing surface specified")
290 parser.error("No upper Gibbs dividing surface specified")
300 parser.error("No width of interface specified")
304 parser.error("No initial estimate of the solid velocity specified")
308 parser.error("No initial estimate of the liquid velocity specified")
312 parser.error("No initial estimate of the slope in the liquid velocity profile specified")
319 #Call functions here, pass appropriate variables.
322 (rnemdJzP, jzType, rnemdZPos, rnemdV) = rnemdExtractor(args.rnemdFileName)
323 (popt, pcov) = velFitter(rnemdJzP, jzType, rnemdZPos, rnemdV, args.Vs, args.l, args.u, args.m, args.w, args.Vl, args.toDel, args.vfitFileName)
327 (shearRate) = calcShearRate(jzType, rnemdZPos, rnemdV)
328 (zLiq, zSol, delta, lambda1) = lambdaCalc(rnemdJzP, jzType, popt)
332 writeOutputFile(args.vfitFileName, args.rnemdFileName, popt)
333 writeOutputLambda(args.vfitFileName, rnemdJzP, jzType, zLiq, zSol, delta, lambda1, shearRate)
336 print("Summary of calculation")
338 print("Jz(p) = ", float(rnemdJzP[0]))
340 print("Jz(p) = ", float(rnemdJzP[1]))
342 print("Shear rate (angstroms / fs) = ", float(shearRate))
343 print("Slip length \delta (angstroms) = ", float(delta))
344 print("Friction coefficient \lambda (amu / fs / angstroms^2) = ", float(lambda1))
347if __name__ == "__main__":