4This python script will (hopefully) generate proton disordered configurations of ice-Ih, given an input of the (.xyz) coordinates of the oxygen positions in the lattice.
7__author__ = "Patrick Louden (plouden@nd.edu)"
8__copyright__ = "Copyright (c) 2004-present The University of Notre Dame. All Rights Reserved."
18from fractions import Fraction, gcd
19from argparse import RawDescriptionHelpFormatter
22# define and zero variables used in the program here.
36 self.pos_ = [0.0, 0.0, 0.0]
39 self.isSurface_ = 'false'
42 def setPos(self, pos):
50 self.pos_ = [0.0, 0.0, 0.0]
52 def setPos(self, pos):
61 return math.floor(x + 0.5)
63 return math.ceil(x - 0.5)
65def wrapMe(disp, boxl):
68 scaled = scaled - roundMe(scaled)
74 myLength = math.sqrt(dot(L1, L1))
75 for i in range(len(L1)):
76 L2.append(L1[i] / myLength)
81 for i in range(len(L1)):
82 myDot = myDot + L1[i]*L2[i]
85def addVectors(v1, v2):
87 if (len(v1) != len(v2)):
88 print("Can't add vectors of different sizes!")
90 for i in range(0, len(v1)):
91 newV.append(v1[i] + v2[i])
94def subtractVectors(v1, v2):
96 if (len(v1) != len(v2)):
97 print("Can't subtract vectors of different sizes!")
99 for i in range(0, len(v1)):
100 newV.append(v1[i] - v2[i])
109def readInputFile(inputFileName):
110 inputFile = open(inputFileName, "r")
112 line = inputFile.readline()
113 totOxygens = int(line.split()[0])
114 print("total number of oxygens to read in = ", totOxygens)
116 line = inputFile.readline()
117 Hxx = float(line.split()[1].strip(';'))
118 Hxy = float(line.split()[2].strip(';'))
119 Hxz = float(line.split()[3].strip(';'))
120 Hyx = float(line.split()[4].strip(';'))
121 Hyy = float(line.split()[5].strip(';'))
122 Hyz = float(line.split()[6].strip(';'))
123 Hzx = float(line.split()[7].strip(';'))
124 Hzy = float(line.split()[8].strip(';'))
125 Hzz = float(line.split()[9].strip(';'))
126 Hmat.append([Hxx, Hxy, Hxz])
127 Hmat.append([Hyx, Hyy, Hyz])
128 Hmat.append([Hzx, Hzy, Hzz])
132 line = inputFile.readline()
136 newOxygen = oxygenAtom()
138 posX = float(line.split()[1])
139 posY = float(line.split()[2])
140 posZ = float(line.split()[3])
141 posVec.append([posX, posY, posZ])
142 newOxygen.setPos(posVec)
144 oxygenList.append(newOxygen)
146 print("total number of oxygens read in = ", len(oxygenList))
147 return (oxygenList, Hmat)
152def findNeighbors(oxygenList):
153 print("finding neighbors now")
154 for i in range(0, len(oxygenList)):
155 oxygenA = oxygenList[i]
156 for j in range(i+1, len(oxygenList)):
157 oxygenB = oxygenList[j]
159 if (oxygenA == oxygenB):
160 print("Querying same oxygen!")
162 # Want to wrap x & y displacements, but leave z as is
163 # since z exposes to a surface
164 xDisp = oxygenA.getPos()[0][0] - oxygenB.getPos()[0][0]
165 xDisp = wrapMe(xDisp, Hmat[0][0])
166 yDisp = oxygenA.getPos()[0][1] - oxygenB.getPos()[0][1]
167 yDisp = wrapMe(yDisp, Hmat[1][1])
168 zDisp = oxygenA.getPos()[0][2] - oxygenB.getPos()[0][2]
169 #zDisp = wrapMe(zDisp,Hmat[2][2])
171 dist = np.sqrt( pow(xDisp, 2) + pow(yDisp, 2) + pow(zDisp, 2) )
174 oxygenA.neighbors_.append(j)
175 oxygenB.neighbors_.append(i)
178 for i in range(0, len(oxygenList)):
179 oxygenA = oxygenList[i]
180 if ( len(oxygenA.neighbors_) < 4):
181 nSurfaceAtoms = nSurfaceAtoms + 1
182 oxygenA.isSurface_ = 'true'
183 oxygenA.neighbors_.append(-1)
184 print("nSurfaceAtoms =", nSurfaceAtoms)
186 #sanity check to make sure no-one is a self-neighbor
187 for i in range(0, len(oxygenList)):
188 oxygenA = oxygenList[i]
189 if i in oxygenA.neighbors_ :
190 print(i, oxygenA.neighbors_)
193def randomizeProtons(oxygenList):
195 print("Randomizing protons, begin bucket brigades")
196 # select random oxygen to start with and perform protonBucketBrigades.
197 for i in range(0, 1000):
198 startingOxygenIndex = random.randint(0, len(oxygenList)-1)
199 protonBucketBrigade(oxygenList, startingOxygenIndex)
201 print("attempted", i, "brigades")
204 # check to see if any molecules have to many or still need protons
207 for i in range(0, len(oxygenList)):
208 oxygen = oxygenList[i]
210 if (len(oxygen.donors_) > 2):
212 #print "oxygen no.", i, "has", len(oxygen.donors_),"donors"
213 elif (len(oxygen.acceptors_) > 2):
215 #print "oxygen no.", i, "has", len(oxygen.acceptors_),"acceptors"
217 if (oxygen.isSurface_ == 'true'):
218 numProtons = len(oxygen.donors_) + len(oxygen.acceptors_)
220 surfUnderCoord.append(i)
221 elif (oxygen.isSurface_ == 'false'):
222 numProtons = len(oxygen.donors_) + len(oxygen.acceptors_)
224 bulkUnderCoord.append(i)
226 print("There are", len(bulkUnderCoord), "bulk oxygens needing protons")
227 print("There are", len(surfUnderCoord), "surf. oxygens needing protons")
229 print("Attempting more protonBucketBridages starting with the deficient oxygens")
230 print("starting with bulk undercoordinated oxygens")
231 for j in range(0, 10):
232 for i in range(0, len(bulkUnderCoord)):
233 protonBucketBrigade(oxygenList, bulkUnderCoord[i])
235 print("attempted", j, "brigades")
237 print("moving onto surface undercoordinated oxygens")
238 for j in range(0, 10):
239 for i in range(0, len(surfUnderCoord)):
240 protonBucketBrigade(oxygenList, surfUnderCoord[i])
242 print("attempted", j, "brigades")
245 # re-check to see if any molecules have to many or still need protons
248 for i in range(0, len(oxygenList)):
249 oxygen = oxygenList[i]
251 if (oxygen.isSurface_ == 'true'):
252 numProtons = len(oxygen.donors_) + len(oxygen.acceptors_)
254 surfUnderCoord.append(i)
255 print('surfUC', i, oxygen.neighbors_, oxygen.donors_, oxygen.acceptors_)
256 elif (oxygen.isSurface_ == 'false'):
257 numProtons = len(oxygen.donors_) + len(oxygen.acceptors_)
259 bulkUnderCoord.append(i)
260 print('bulkUC', i, oxygen.neighbors_, oxygen.donors_, oxygen.acceptors_)
263 print("There are", len(bulkUnderCoord), "bulk oxygens needing protons")
264 print("There are", len(surfUnderCoord), "surf. oxygens needing protons")
267 #Let's check to see the total number of donors_ and acceptors_ cites on the surfaces.
268 # If these numbers aren't equal, the lattice can end up frustrated (lacking or excess of protons)
271 for i in range(0, len(oxygenList)):
272 oxygen = oxygenList[i]
273 if (oxygen.isSurface_ == 'true'):
274 numDonors = numDonors + len(oxygen.donors_)
275 numAcceptors = numAcceptors + len(oxygen.acceptors_)
276 print("numDonors =", numDonors)
277 print("numAcceptors =", numAcceptors)
279 for i in range(0, len(oxygenList)):
280 oxygen = oxygenList[i]
282 if (oxygen.isSurface == 'true'):
283 numBonds = len(oxygen.donors_) + len(oxygen.acceptors_)
285 print('\nSurfDeficient')
286 print('UCoxygen', i, oxygen.neighbors_, oxygen.donors_, oxygen.acceptors_)
287 for i in range(0, len(oxygen.neighbors_)):
288 nborOx = oxygenList[oxygen.neighbors_[i]]
289 print("neighbor", oxygen.neighbors_[i], nborOx.neighbors_, nborOx.donors_, nborOx.acceptors_)
291 elif (oxygen.isSurface_ == 'false'):
292 if (len(oxygen.donors_) < 2):
293 print('\nDonorDeficient')
294 print("UCoxygen", i, oxygen.neighbors_, oxygen.donors_, oxygen.acceptors_)
295 for i in range(0, len(oxygen.neighbors_)):
296 nborOx = oxygenList[oxygen.neighbors_[i]]
297 print("neighbor", oxygen.neighbors_[i], nborOx.neighbors_, nborOx.donors_, nborOx.acceptors_)
298 if (len(oxygen.acceptors_) < 2):
299 print('\nAcceptorDeficient')
300 print("UCoxygen", i, oxygen.neighbors_, oxygen.donors_, oxygen.acceptors_)
301 for i in range(0, len(oxygen.neighbors_)):
302 nborOx = oxygenList[oxygen.neighbors_[i]]
303 print("neighbor", oxygen.neighbors_[i], nborOx.neighbors_, nborOx.donors_, nborOx.acceptors_)
307def protonBucketBrigade(oxygenList, startOxygenIndex):
308 # First we need to loop through and create a dummy set of the oxygen. These will be tested and incremented for Hbonds
310 for i in range(0, len(oxygenList)):
311 dummyOxygen = copy.deepcopy(oxygenList[i])
312 dummyList.append(dummyOxygen)
314 # assign starting oxygen to a dummy starting oxygen
315 dummyStart = dummyList[startOxygenIndex]
316 dummyStartIndex = startOxygenIndex
319 # badNeighbors are oxygens already donating to/accepting from current
320 badNbors = set(dummyStart.donors_).union(set(dummyStart.acceptors_))
321 # goodNbors are my nbors not in badnbors
322 goodNbors = set(dummyStart.neighbors_).difference(badNbors)
325 if (len(goodNbors) == 0):
328 # sample next oxygen from good neighbors
329 if (dummyStart.isSurface_ == 'true'):
330 numBonds = len(dummyStart.donors_) + len(dummyStart.acceptors_)
332 if ( len(dummyStart.donors_) < 2):
333 dummyNextIndex = random.sample(goodNbors, 1)[0]
334 dummyNext = dummyList[dummyNextIndex]
336 if (dummyNext.isSurface_ == 'true'):
337 nOnumBonds = len(dummyNext.donors_) + len(dummyNext.acceptors_)
339 if (len(dummyNext.acceptors_) < 2):
340 dummyStart.donors_.append(dummyNextIndex)
341 dummyNext.acceptors_.append(dummyStartIndex)
342 elif (len(dummyNext.acceptors_) == 2):
343 return #nextOxygen has saturated acceptors
344 elif (nOnumBonds == 3):
345 return #nextOxygen has total saturation of bonds
346 elif (nOnumBonds > 3):
347 print("Code is broken, nextOxygen is surface and has > 3 bonds.")
348 print(dummyNextIndex, dummyNext.neighbors_. dummyNext.donors_, dummyNext.acceptors_)
350 elif (dummyNext.isSurface_ == 'false'):
351 if (len(dummyNext.acceptors_) < 2):
352 dummyStart.donors_.append(dummyNextIndex)
353 dummyNext.acceptors_.append(dummyStartIndex)
354 elif (len(dummyNext.acceptors_) == 2):
356 elif (len(dummyNext.acceptors_) > 2):
357 print("Code is broken, nextOxygen is bulk and has > 2 acceptors.")
358 print(dummyNextIndex, dummyNext.neighbors_. dummyNext.donors_, dummyNext.acceptors_)
360 # we are a surface oxygen, but we have 2 donors already.
361 elif ( len(dummyStart.donors_) == 2):
364 elif ( len(dummyStart.donors_) > 2):
365 print("Code is broken, startOxygen is surface and has > 2 donors")
366 print(dummyStartIndex, dummyStart.neighbors_. dummyStart.donors_, dummyStart.acceptors_)
368 # we are a surface oxygen, but have saturation of bonds.
369 elif (numBonds == 3):
373 print("Code is broken, startOxygen has numBonds > 3 ")
374 print(dummyStartIndex, dummyStart.neighbors_. dummyStart.donors_, dummyStart.acceptors_)
376 elif (dummyStart.isSurface_ == 'false'):
377 if (len(dummyStart.donors_) < 2): #startOxygen < 2 donors, therefore can make a bond
378 dummyNextIndex = random.sample(goodNbors, 1)[0]
379 dummyNext = dummyList[dummyNextIndex]
381 if (dummyNext.isSurface_ == 'true'):
382 nOnumBonds = len(dummyNext.donors_) + len(dummyNext.acceptors_)
384 if ( len(dummyNext.acceptors_) < 2): #requirement met, make a bond
385 dummyStart.donors_.append(dummyNextIndex)
386 dummyNext.acceptors_.append(dummyStartIndex)
387 elif ( len(dummyNext.acceptors_) == 2):
388 return # nextOxygen has 2 acceptors, no room for one more.
389 elif (nOnumBonds == 3):
390 return #nextOxygen has 3 bonds, no room for one more.
391 elif (nOnumBonds > 3):
392 print("Code is broken, nextOxygen is surface and has > 3 bonds.")
393 print(dummyNextIndex, dummyNext.neighbors_. dummyNext.donors_, dummyNext.acceptors_)
395 elif (dummyNext.isSurface_ == 'false'):
396 if (len(dummyNext.acceptors_) < 2): #requirement met, make a bond
397 dummyStart.donors_.append(dummyNextIndex)
398 dummyNext.acceptors_.append(dummyStartIndex)
399 elif (len(dummyNext.acceptors_) == 2):
400 return # nextOxygen has 2 acceptors, no room for one more.
401 elif (len(dummyNext.acceptors_) > 2):
402 print("Code is broken, nextOxygen is bulk and has > 2 acceptors.")
403 print(dummyNextIndex, dummyNext.neighbors_. dummyNext.donors_, dummyNext.acceptors_)
406 #startOxygen is not surface and has 2 donors, no more room for additional donors.
407 elif (len(startDummy.donors_) == 2):
410 elif (len(dummyStart.donors_) > 2):
411 print("Code broke, startOxygen is not surface and has > 2 donors.")
412 print(dummyStartIndex, dummyStart.neighbors_. dummyStart.donors_, dummyStart.acceptors_)
415 # update nextOxygen to currentOxygen, begin looping.
416 dummyPrev = dummyStart
417 dummyPrevIndex = dummyStartIndex
418 dummyCurrent = dummyNext
419 dummyCurrentIndex = dummyNextIndex
422 # Now we will loop until we reach back to the starting Oxygen, guaranteeing a zero-net-dipole chain!
423 while (dummyCurrentIndex != dummyStartIndex):
425 badNbors = set(dummyCurrent.donors_).union(set(dummyCurrent.acceptors_))
426 goodNbors = set(dummyCurrent.neighbors_).difference(badNbors)
429 # if currentOxygen is surface, must test previousOxygen for surface or bulk.
430 if (dummyCurrent.isSurface_ == 'true'):
431 #can't test the raw number of donors_ could have a (DAA) set of bonds and donors < 2 would give false positive.
432 numBonds = len(dummyCurrent.donors_) + len(dummyCurrent.acceptors_)
433 if (numBonds < 3): # with < 3 bonds, currentOxygen has room to donate to another oxygen.
434 dummyNextIndex = random.sample(goodNbors, 1)[0]
435 dummyNext = dummyList[dummyNextIndex]
437 # if previous and current both surface, nextOxygen must be a bulk.
438 if (dummyPrev.isSurface_ == 'true'):
439 if (dummyNext.isSurface_ == 'true'):
441 elif (dummyNext.isSurface_ == 'false'):
442 if (len(dummyNext.acceptors_) < 2):
443 dummyCurrent.donors_.append(dummyNextIndex)
444 dummyNext.acceptors_.append(dummyCurrentIndex)
445 elif (len(dummyNext.acceptors_) == 2):
446 return # nextOxygen has 2 acceptors, no room for one more.
447 elif (len(dummyNext.acceptors_) > 2):
448 print("Code is broken (while), nextOxygen is bulk and has > 2 acceptors.")
451 # if previousOxygen is not surface, then nextOxygen can be a surface or bulk oxygen
452 elif (dummyPrev.isSurface_ == 'false'):
454 if (dummyNext.isSurface_ == 'true'):
455 nOnumBonds = len(dummyNext.donors_) + len(dummyNext.acceptors_)
457 if (len(dummyNext.acceptors_) < 2):
458 dummyCurrent.donors_.append(dummyNextIndex)
459 dummyNext.acceptors_.append(dummyCurrentIndex)
460 elif (len(dummyNext.acceptors_) == 2):
461 return # nextOxygen has 2 acceptors, no room for one more.
462 elif (len(dummyNext.acceptors_) > 2):
463 print("Code broken (while), nextOxygen has > 2 acceptors")
464 elif (nOnumBonds == 3):
465 return #nextOxygen has 3 bonds, no room for one more.
466 elif (nOnumBonds > 3):
467 print("Code is broken (while), nextOxygen is surface and has > 3 bonds.")
469 elif (dummyNext.isSurface_ == 'false'):
470 if (len(dummyNext.acceptors_) < 2):
471 dummyCurrent.donors_.append(dummyNextIndex)
472 dummyNext.acceptors_.append(dummyCurrentIndex)
473 elif (len(dummyNext.acceptors_) == 2):
474 return # nextOxygen has 2 acceptors, no room for one more.
475 elif (len(dummyNext.acceptors_) > 2):
476 print("Code is broken (while), nextOxygen is bulk and has > 2 acceptors.")
478 elif (numBonds == 3):
479 return #currentOxygen has a saturation of bonds.
481 print("Code is broken (while), currentOxygen is surface and has > 3 bonds.")
483 #if currentOxygen is not surface, nextOxygen can be bulk or surface.
484 elif (dummyCurrent.isSurface_ == 'false'):
485 dummyNextIndex = random.sample(goodNbors, 1)[0]
486 dummyNext = dummyList[dummyNextIndex]
489 if (dummyNext.isSurface_ == 'true'):
490 nOnumBonds = len(dummyNext.donors_) + len(dummyNext.acceptors_)
492 if (len(dummyNext.acceptors_) < 2):
493 dummyCurrent.donors_.append(dummyNextIndex)
494 dummyNext.acceptors_.append(dummyCurrentIndex)
495 elif (len(dummyNext.acceptors_) == 2):
496 return # nextOxygen has 2 acceptors, no room for one more.
497 elif (len(dummyNext.acceptors_) > 2):
498 print("Code broken (while), nextOxygen has > 2 acceptors")
499 elif (nOnumBonds == 3):
500 return #nextOxygen has 3 bonds, no room for one more.
501 elif (nOnumBonds > 3):
502 print("Code is broken (while), nextOxygen is surface and has > 3 bonds.")
504 elif (dummyNext.isSurface_ == 'false'):
505 if (len(dummyNext.acceptors_) < 2):
506 dummyCurrent.donors_.append(dummyNextIndex)
507 dummyNext.acceptors_.append(dummyCurrentIndex)
508 elif (len(dummyNext.acceptors_) == 2):
509 return # nextOxygen has 2 acceptors, no room for one more.
510 elif (len(dummyNext.acceptors_) > 2):
511 print("Code is broken (while), nextOxygen is bulk and has > 2 acceptors.")
514 # update nextOxygen to currentOxygen, begin looping.
515 dummyPrev = dummyCurrent
516 dummyPrevIndex = dummyCurrentIndex
517 dummyCurrent = dummyNext
518 dummyCurrentIndex = dummyNextIndex
520 #post while loop: test for length of protonLoop, will be set to zero upon break above.
521 if (len(dummyList) > 0):
522 assignBonds(oxygenList, dummyList)
527def assignBonds(oxygenList, dummyList):
528 # If we are here, we have successfully made a chain of hydrogen-bonds.
529 # We will save this by overwriting the oxygenList with the dummyList
530 for i in range(0, len(dummyList)):
531 oxygenList[i] = copy.deepcopy(dummyList[i])
537def monteCarloMethod(oxygenList):
539 print("Assigning all oxygens a random set of bonds now")
540 #First we will assign all of the oxygens a random donor/acceptor set. We can end up with > 2 donors or acceptors here.
542 for i in range(0, len(oxygenList)):
543 oxygenA = oxygenList[i]
545 for j in range(0, len(oxygenA.neighbors_)):
546 oxygenBIndex = oxygenA.neighbors_[j]
548 if (oxygenBIndex not in oxygenA.donors_ and oxygenBIndex not in oxygenA.acceptors_):
549 rand = random.uniform(0, 1)
551 oxygenA.donors_.append(oxygenA.neighbors_[j])
552 if (oxygenBIndex != -1):
553 oxygenList[oxygenA.neighbors_[j]].acceptors_.append(i)
555 oxygenA.acceptors_.append(oxygenA.neighbors_[j])
556 if (oxygenBIndex != -1):
557 oxygenList[oxygenA.neighbors_[j]].donors_.append(i)
558 numProtons = numProtons + len(oxygenA.donors_)
559 print("numProtons =", numProtons)
562 for i in range(0, len(oxygenList)):
563 oxygen = oxygenList[i]
564 if (len(oxygen.donors_) < 2):
565 numUnOr = numUnOr + 1
566 print("Pre MC:", numUnOr, "oxygens with < 2 donors_")
570 print("Calculating net dipole of the system")
571 netDipole = [0.0, 0.0, 0.0]
572 #Next we need to calculate the dipole moment of the system
573 for i in range(0, len(oxygenList)):
574 oxygenA = oxygenList[i]
576 #donor configuration has dipole moment (+1) * rUnit vec between two oxygens
577 for j in range(0, len(oxygenA.donors_)):
578 oxygenB = oxygenList[oxygenA.donors_[j]]
580 rVec = subtractVectors(oxygenA.getPos()[0], oxygenB.getPos()[0])
581 rUnit = normalize(rVec)
583 netDipole = addVectors(netDipole, rUnit)
585 #acceptor configuration has dipole moment (-1) * rUnit vec between two oxygens
586 for j in range(0, len(oxygenA.acceptors_)):
587 oxygenB = oxygenList[oxygenA.acceptors_[j]]
589 rVec = subtractVectors(oxygenA.getPos()[0], oxygenB.getPos()[0])
590 rUnit = normalize(rVec)
592 netDipole = subtractVectors(netDipole, rUnit)
594 #Now we divide by 2.0 since we visited each pair twice
595 for i in range(0, len(netDipole)):
596 netDipole[i] = netDipole[i] / 2.0
597 print("net dipole pre MC moves =", netDipole)
600 nBarCycles = totalCycles / 5.0
601 for nCycles in range(0, totalCycles):
603 kT = 10.0*np.exp(-nCycles/nBarCycles)
606 #Next we will perform MonteCarlo moves where we will flip the donors / acceptors
607 for i in range(0, len(oxygenList)):
608 #oxygenAIndex = random.randint(0,len(oxygenList)-1)
610 oxygenA = oxygenList[oxygenAIndex]
612 oxygenBIndex = random.sample(oxygenA.neighbors_, 1)[0]
613 if (oxygenBIndex != -1):
614 oxygenB = oxygenList[oxygenBIndex]
617 #compute pre-flip dipole moment of Hbond
620 rVec = subtractVectors(oxygenA.getPos()[0], oxygenB.getPos()[0])
621 rUnit = normalize(rVec)
623 if (oxygenBIndex in oxygenA.donors_):
624 for i in range(0, len(netDipole)):
625 oldDipole.append(1.0 * rUnit[i])
627 elif (oxygenBIndex in oxygenA.acceptors_):
628 for i in range(0, len(netDipole)):
629 oldDipole.append(-1.0 * rUnit[i])
632 EpreFlip = calcPairEnergy(oxygenA, oxygenB, netDipole, kT)
634 swapDonorAcceptor(oxygenA, oxygenAIndex, oxygenB, oxygenBIndex)
636 #compute post-flip dipole moment of Hbond
639 rVec = subtractVectors(oxygenA.getPos()[0], oxygenB.getPos()[0])
640 rUnit = normalize(rVec)
642 if (oxygenBIndex in oxygenA.donors_):
643 for i in range(0, len(netDipole)):
644 newDipole.append(1.0 * rUnit[i])
646 elif (oxygenBIndex in oxygenA.acceptors_):
647 for i in range(0, len(netDipole)):
648 newDipole.append(-1.0 * rUnit[i])
651 #compute new net dipole moment of system
653 for i in range(0, len(netDipole)):
654 newNetDipole.append(netDipole[i] - oldDipole[i] + newDipole[i])
656 EpostFlip = calcPairEnergy(oxygenA, oxygenB, newNetDipole, kT)
659 #accept or reject move
660 deltaE = EpostFlip - EpreFlip
661 randNum = random.uniform(0, 1)
663 if (randNum <= np.exp(-deltaE / kT) ):
664 netDipole = newNetDipole
667 swapDonorAcceptor(oxygenA, oxygenAIndex, oxygenB, oxygenBIndex)
669 if (nCycles%100 == 0):
670 print(nCycles, kT, netDipole)
673 #after MC cycles, let's check to see how many oxygens have the right number of donors/acceptors
675 for i in range(0, len(oxygenList)):
676 oxygen = oxygenList[i]
677 if (len(oxygen.donors_) < 2 and len(oxygen.acceptors_) < 2):
678 numUnOr = numUnOr + 1
679 print("Post MC:", numUnOr, "oxygens with < 2 donors and < 2 acceptors")
683def calcPairEnergy(oxygenA, oxygenB, netDipole, kT):
684 #f = weighting for having 2 donors & 2 acceptors
685 #g = weighting for zero-net dipole to the system
689 oxAda = pow(len(oxygenA.donors_) - len(oxygenA.acceptors_), 2.0)
690 oxBda = pow(len(oxygenB.donors_) - len(oxygenB.acceptors_), 2.0)
692 E = f*( oxAda + oxBda ) + g*(np.sqrt(dot(netDipole, netDipole)))
697def swapDonorAcceptor(oxygenA, oxygenAIndex, oxygenB, oxygenBIndex):
698 if (oxygenBIndex in oxygenA.donors_):
700 listAIndex = oxygenA.donors_.index(oxygenBIndex)
701 listBIndex = oxygenB.acceptors_.index(oxygenAIndex)
703 del oxygenA.donors_[listAIndex]
704 del oxygenB.acceptors_[listBIndex]
706 oxygenA.acceptors_.append(oxygenBIndex)
707 oxygenB.donors_.append(oxygenAIndex)
709 elif (oxygenBIndex in oxygenA.acceptors_):
711 listAIndex = oxygenA.acceptors_.index(oxygenBIndex)
712 listBIndex = oxygenB.donors_.index(oxygenAIndex)
714 del oxygenA.acceptors_[listAIndex]
715 del oxygenB.donors_[listBIndex]
717 oxygenA.donors_.append(oxygenBIndex)
718 oxygenB.acceptors_.append(oxygenAIndex)
723def addProtonsToDonors(oxygenList):
724 print("Adding protons to the lattice")
728 for i in range(0, len(oxygenList)):
729 oxygenA = oxygenList[i]
730 numProtons = numProtons + len(oxygenA.donors_)
731 #Should be able to just scan all the donors_, if someone is their second donor, then another oxygen must have it.
732 for j in range(0, len(oxygenA.donors_)):
733 oxygenBIndex = oxygenA.donors_[j]
735 if (oxygenBIndex != -1):
736 oxygenB = oxygenList[oxygenA.donors_[j]]
738 # place protons 1 unit vector away from the donating oxygen
739 # towards the accepting oxygen
740 xDisp = oxygenA.getPos()[0][0] - oxygenB.getPos()[0][0]
741 yDisp = oxygenA.getPos()[0][1] - oxygenB.getPos()[0][1]
742 zDisp = oxygenA.getPos()[0][2] - oxygenB.getPos()[0][2]
744 rVec = [xDisp, yDisp, zDisp]
745 rUnit = normalize(rVec)
747 xPos = oxygenA.getPos()[0][0] + rUnit[0]
748 yPos = oxygenA.getPos()[0][1] + rUnit[1]
749 zPos = oxygenA.getPos()[0][2] + rUnit[2]
750 protonVec = [xPos, yPos, zPos]
753 newProton.setPos(protonVec)
755 protonList.append(newProton)
757 elif (oxygenBIndex == -1):
758 # for the (-1) neigbors, we have to take the negatice of the average of the other 3 HBond vectors
759 aveVec = [0.0, 0.0, 0.0]
760 for k in range(0, len(oxygenA.neighbors_)):
761 oxNeighIndex = oxygenA.neighbors_[k]
763 if (oxNeighIndex != -1):
764 oxygenB = oxygenList[oxygenA.donors_[j]]
766 xDisp = oxygenA.getPos()[0][0] - oxygenB.getPos()[0][0]
767 yDisp = oxygenA.getPos()[0][1] - oxygenB.getPos()[0][1]
768 zDisp = oxygenA.getPos()[0][2] - oxygenB.getPos()[0][2]
770 rVec = [xDisp, yDisp, zDisp]
771 aveVec = addVectors(aveVec, rVec)
773 #normalize aveVec, and multiply by (-1)
774 aveVec = normalize(aveVec)
775 for k in range(0, len(aveVec)):
776 aveVec[k] = -1.0 * aveVec[k]
778 xPos = oxygenA.getPos()[0][0] + aveVec[0]
779 yPos = oxygenA.getPos()[0][1] + aveVec[1]
780 zPos = oxygenA.getPos()[0][2] + aveVec[2]
781 protonVec = [xPos, yPos, zPos]
784 newProton.setPos(protonVec)
786 protonList.append(newProton)
788 print("numProtons = ", numProtons)
789 print("len(oxygenList) =", len(oxygenList))
790 print("len(protonList) =", len(protonList))
795def computeQuats(oxygenList):
797 #for i in range(len(indices)):
798 # DonatingTo.append(list())
799 # AcceptingFrom.append(list())
800 # OldDonor.append(list())
801 # print "Computing Quaternions"
803 # Set up initial rotation matrix
807 RotMat = [ux, uy, uz]
808 totalDipole = [0.0, 0.0, 0.0]
809 #for i in range(len(indices)):
810 # print "doing quats for molecule %d" % i
811 # print "Dlen = %d " % len(DonatingTo[i])
812 # print DonatingTo[i]
813 for i in range(0, len(oxygenList)):
814 oxygenA = oxygenList[i]
815 #print oxygenA.donors_
816 #print oxygenA.acceptors_
818 #myPos = positions[i]
820 #acceptor1 = DonatingTo[i][0]
821 #acceptor2 = DonatingTo[i][1]
823 #acceptor1 = oxygenA.donors_[0]
824 #acceptor2 = oxygenA.donors_[1]
828 if (acceptor1 == -1 and acceptor2 == -1):
829 donor1 = AcceptingFrom[i][0]
830 donor2 = AcceptingFrom[i][1]
831 npos = positions[donor1]
832 apos1 = [npos[0] - myPos[0], npos[1] - myPos[1], npos[2] - myPos[2]]
833 apos1 = wrapVector(apos1)
834 npos = positions[donor2]
835 apos2 = [npos[0] - myPos[0], npos[1] - myPos[1], npos[2] - myPos[2]]
836 apos2 = wrapVector(apos2)
837 tempX = [0.0, 0.0, 0.0]
838 tempY = [0.0, 0.0, 0.0]
839 tempZ = [0.0, 0.0, 0.0]
840 tempZ[0] = 0.5*(apos1[0] + apos2[0])
841 tempZ[1] = 0.5*(apos1[1] + apos2[1])
842 tempZ[2] = 0.5*(apos1[2] + apos2[2])
843 tempX[0] = (apos2[0] - apos1[0])
844 tempX[1] = (apos2[1] - apos1[1])
845 tempX[2] = (apos2[2] - apos1[2])
846 tempZ = normalize(tempZ)
847 tempX = normalize(tempX)
848 tempY = cross(tempX, tempZ)
849 dpos1[0] = tempZ[0] - 0.5*tempY[0]
850 dpos1[1] = tempZ[1] - 0.5*tempY[1]
851 dpos1[2] = tempZ[2] - 0.5*tempY[2]
852 dpos2[0] = tempZ[0] + 0.5*tempY[0]
853 dpos2[1] = tempZ[1] + 0.5*tempY[1]
854 dpos2[2] = tempZ[2] + 0.5*tempY[2]
857 if (acceptor1 == -1):
858 tempVec = [0.0, 0.0, 0.0]
860 thisNeighbor = neighbors[i][j]
861 npos = positions[thisNeighbor]
862 npos1 = [npos[0] - myPos[0], npos[1] - myPos[1], npos[2] - myPos[2]]
863 npos1 = wrapVector(npos1)
864 tempVec[0] = tempVec[0] + npos1[0]
865 tempVec[1] = tempVec[1] + npos1[1]
866 tempVec[2] = tempVec[2] + npos1[2]
867 dpos1 = [-tempVec[0]/3.0, -tempVec[1]/3.0, -tempVec[2]/3.0]
868 dpos1 = normalize(dpos1)
870 a1pos = positions[acceptor1]
871 dpos1 = [a1pos[0] - myPos[0], a1pos[1] - myPos[1], a1pos[2] - myPos[2]]
872 dpos1 = wrapVector(dpos1)
873 dpos1 = normalize(dpos1)
876 if (acceptor2 == -1):
877 tempVec = [0.0, 0.0, 0.0]
879 thisNeighbor = neighbors[i][j]
880 npos = positions[thisNeighbor]
881 npos1 = [npos[0] - myPos[0], npos[1] - myPos[1], npos[2] - myPos[2]]
882 npos1 = wrapVector(npos1)
883 tempVec[0] = tempVec[0] + npos1[0]
884 tempVec[1] = tempVec[1] + npos1[1]
885 tempVec[2] = tempVec[2] + npos1[2]
886 dpos2 = [-tempVec[0]/3.0, -tempVec[1]/3.0, -tempVec[2]/3.0]
887 dpos2 = normalize(dpos2)
889 a2pos = positions[acceptor2]
890 dpos2 = [a2pos[0] - myPos[0], a2pos[1] - myPos[1], a2pos[2] - myPos[2]]
891 dpos2 = wrapVector(dpos2)
892 dpos2 = normalize(dpos2)
895 uz[j] = (dpos1[j] + dpos2[j])/2.0
898 uy[j] = dpos2[j] - dpos1[j]
903 q = [0.0, 0.0, 0.0, 0.0]
905 # RotMat to Quat code is out of OpenMD's SquareMatrix3.hpp code:
911 t = RotMat[0][0] + RotMat[1][1] + RotMat[2][2] + 1.0
914 s = 0.5 / math.sqrt( t )
916 q[1] = (RotMat[1][2] - RotMat[2][1]) * s
917 q[2] = (RotMat[2][0] - RotMat[0][2]) * s
918 q[3] = (RotMat[0][1] - RotMat[1][0]) * s
924 if( ad1 >= ad2 and ad1 >= ad3 ):
925 s = 0.5 / math.sqrt( 1.0 + RotMat[0][0] - RotMat[1][1] - RotMat[2][2] )
926 q[0] = (RotMat[1][2] - RotMat[2][1]) * s
928 q[2] = (RotMat[0][1] + RotMat[1][0]) * s
929 q[3] = (RotMat[0][2] + RotMat[2][0]) * s
930 elif ( ad2 >= ad1 and ad2 >= ad3 ):
931 s = 0.5 / math.sqrt( 1.0 + RotMat[1][1] - RotMat[0][0] - RotMat[2][2] )
932 q[0] = (RotMat[2][0] - RotMat[0][2] ) * s
933 q[1] = (RotMat[0][1] + RotMat[1][0]) * s
935 q[3] = (RotMat[1][2] + RotMat[2][1]) * s
937 s = 0.5 / math.sqrt( 1.0 + RotMat[2][2] - RotMat[0][0] - RotMat[1][1] )
938 q[0] = (RotMat[0][1] - RotMat[1][0]) * s
939 q[1] = (RotMat[0][2] + RotMat[2][0]) * s
940 q[2] = (RotMat[1][2] + RotMat[2][1]) * s
944 totalDipole = [totalDipole[0] + uz[0], totalDipole[1] + uz[1], totalDipole[2] + uz[2]]
945 totalDipole = [-totalDipole[0], -totalDipole[1], -totalDipole[2]]
947 Dipole = math.sqrt(dot(totalDipole, totalDipole))
948 print 'Total Dipole Moment = %d' % Dipole
949 if (Dipole > 40 or Dipole < -40):
950 print "Bad Dipole, starting over"
951 for i in range(len(indices)):
965def writeXYZfile(oxygenList, protonList, Hmat):
966 outFile = open("test.xyz", "w")
968 numObjects = len(oxygenList) + len(protonList)
969 outFile.write(str(numObjects) + "\n")
970 outFile.write("\t%f%s\t%f\t%f\t%f%s\t%f\t%f\t%f%s\t%f\t%f\t%f\n" %
972 Hmat[0][0], Hmat[0][1], Hmat[0][2], ";",
973 Hmat[1][0], Hmat[1][1], Hmat[1][2], ";",
974 Hmat[2][0], Hmat[2][1], Hmat[2][2]))
975 for i in range(0, len(oxygenList)):
976 oxygenA = oxygenList[i]
977 outFile.write("%s\t%f\t%f\t%f\n" % ('O', oxygenA.getPos()[0][0], oxygenA.getPos()[0][1], oxygenA.getPos()[0][2]))
979 for i in range(0, len(protonList)):
980 protonA = protonList[i]
981 outFile.write("%s\t%f\t%f\t%f\n" % ('H', protonA.getPos()[0], protonA.getPos()[1], protonA.getPos()[2]))
984# This function writes out the oxygens with their neighbors, donors, and acceptors
985def writeOxygenfile(oxygenList):
986 outFile = open("oxygen.dat", "w")
988 for i in range(0, len(oxygenList)):
989 oxygenA = oxygenList[i]
990 outFile.write(str(i) + "\t" + str(oxygenA.neighbors_) + "\t" + str(oxygenA.donors_) + "\t" + str(oxygenA.acceptors_) + "\n")
994 parser = argparse.ArgumentParser(
995 description='OpenMD module that generates proton disordered ice-Ih lattices given an (.xyz) coordinate file for the Oxygen lattice.',
996 formatter_class=RawDescriptionHelpFormatter,
997 epilog="Example: protonSampler -i oxygenLattice.xyz -o protonDisorderedIce.omd")
998 parser.add_argument("-i", "--inputFile=", action="store",
999 dest="inputFileName", help="use specified input file")
1000 parser.add_argument("-o", "--outputfile=", action="store", dest="outputFileName", help="use specified output (.omd) file")
1002 if len(sys.argv) == 1:
1005 args = parser.parse_args()
1007 if (not args.inputFileName):
1008 parser.error("No inputFile was specified")
1010 if (not args.outputFileName):
1012 parser.error("No outputFile was specified")
1016 (oxygenList, Hmat) = readInputFile(args.inputFileName)
1017 findNeighbors(oxygenList)
1019 #randomizeProtons(oxygenList)
1020 monteCarloMethod(oxygenList)
1022 protonList = addProtonsToDonors(oxygenList)
1023 writeXYZfile(oxygenList, protonList, Hmat)
1024 writeOxygenfile(oxygenList)
1026 computeQuats(oxygenList)
1029 writeXYZfile(oxygenList, protonList, Hmat)
1032if __name__ == "__main__":
1033 #if len(sys.argv) == 1:
1034 #parser.print_help()
1035 #usage() # need to change to call argeparse stuffs