Skip to content

Commit

Permalink
Updating the python files for more fine-grained control of ab initio …
Browse files Browse the repository at this point in the history
…interfaces
  • Loading branch information
kaka-zuumi committed May 4, 2024
1 parent f3449d2 commit 02c577d
Show file tree
Hide file tree
Showing 5 changed files with 252 additions and 72 deletions.
39 changes: 30 additions & 9 deletions cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
parser.add_argument("--collisionEnergy", type=float, help="Collision energy in kcal/mol")
parser.add_argument("--impactParameter", type=float, help="Impact parameter in Angstrom")
parser.add_argument("--centerOfMassDistance", type=float, help="Distance between the two molecules' centers of mass in Angstrom")
parser.add_argument('--optimize', action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--production", type=int, help="Supply number of steps for a production run")
parser.add_argument("--interval", type=int, help="How often to print out the energy")
parser.add_argument("--time_step", type=float, help="The time step in fs for a production run")
Expand Down Expand Up @@ -102,6 +103,8 @@ def printenergy(a):
Nprint = args["interval"]
dt = args["time_step"]

optimize_flag = args["optimize"]

if ((Nsteps is None) or (Nprint is None) or (dt is None)):
raise ValueError("For MD, need to specify these three: --production --interval --time_step")

Expand Down Expand Up @@ -183,7 +186,7 @@ def printenergy(a):

if (try_nwchemex):
print("Reading input file '"+input_path+"' as a NWChemEx input file...")
calc = nwchemexcalculator(input_path,n_threads=n_threads)
calc = nwchemexcalculator(input_path,output_path=output_path,n_threads=n_threads)

# To conform to VENUS, we are going to keep the units
# in kcal/mol and Angstroms (which the model was
Expand Down Expand Up @@ -222,19 +225,29 @@ def printenergy(a):
v = [p[i]/masses[i] for i in range(len(p))]
mol.set_velocities(v)

# If there is no momenta file, then try and make momenta
# by assuming a bimolecular reaction
# If there is no momenta file, then do initial sampling
else:
if ((atomsInFirstGroup is None) or (ce is None) or (b is None) or (dCM is None)):
raise ValueError("No initial momenta conditions or file are provided")

Natoms = len(mol)

# If no atoms are specified to be in the first group,
# assume that this is a unimolecular sampling
if (atomsInFirstGroup is None):
atomsInFirstGroup = range(Natoms)

atomsInFirstGroup = [int(i)-1 for i in atomsInFirstGroup.split()]

Natoms = len(mol)
atomsInSecondGroup = []
for i in range(Natoms):
if (i not in atomsInFirstGroup): atomsInSecondGroup.append(i)

if ((len(atomsInFirstGroup) > 0) and (len(atomsInSecondGroup) > 0)):
bimolecular_flag = True
if ((ce is None) or (b is None) or (dCM is None)):
raise ValueError("Lacking an argument for bimolecular sampling (collision energy, impact parameter, of center of mass distance)")
else:
bimolecular_flag = False

print("")
print("GEOMETRY INPUT")
print(" Input geometry file: ", Qfile)
Expand All @@ -243,6 +256,7 @@ def printenergy(a):
print("SAMPLING INPUT")
print(" Input momenta file: ", Pfile)
if (Pfile is None):
print(" Optimize molecules? ", optimize_flag)
print(" Group A sampling method: ", samplingMethod[0])
print(" Group A vibration: ", vibrationSampling[0])
print(" Group A rotation: ", rotationSampling[0])
Expand All @@ -260,16 +274,17 @@ def printenergy(a):

# Sample the internal positions and momenta of each of
# the two molecules
sampler = initialSampling(mol,atomsInFirstGroup,optimize=True,
sampler = initialSampling(mol,atomsInFirstGroup,optimize=optimize_flag,
optimization_file=os.path.join(output_path, "optimization.traj"),
samplingMethodA=samplingMethod[0],vibrationalSampleA=vibrationSampling[0],rotationalSampleA=rotationSampling[0],
samplingMethodB=samplingMethod[1],vibrationalSampleB=vibrationSampling[1],rotationalSampleB=rotationSampling[1])

print("Sampling internal degrees of freedom...")
sampler.sampleRelativeQP()

print("Sampling relative degrees of freedom...")
sampler.sampleAbsoluteQP(ce,dCM=dCM,b=b)
if (bimolecular_flag):
print("Sampling relative degrees of freedom...")
sampler.sampleAbsoluteQP(ce,dCM=dCM,b=b)

########################################################################################

Expand All @@ -293,6 +308,12 @@ def checkGeneralReactionProgress(a):

return stop_flag

# If movecs, Q, and P restarting is available, start it now:
if try_nwchemex:
if (calc.save_movecs_interval):
# calc.save_movecs_count = calc.save_movecs_interval # Save once at the start
calc.save_movecs_count = 0 # Save only once every "save_movecs_interval"

# Now run the dynamics
printenergy(mol)
for i in range(Nsteps):
Expand Down
35 changes: 29 additions & 6 deletions initialSampling.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,18 @@ def getKineticEnergy(self,m,p):
# Separate the two molecules
def separateMolecules(self):
masses = self.mol.get_masses()
mass1 = sum([masses[i] for i in self.atomsInFirstGroup])
mass2 = sum([masses[i] for i in self.atomsInSecondGroup])
q = self.mol.get_positions()
r1 = sum([masses[i]*q[i] for i in self.atomsInFirstGroup]) / mass1
r2 = sum([masses[i]*q[i] for i in self.atomsInSecondGroup]) / mass2
if (len(self.atomsInFirstGroup) > 0):
mass1 = sum([masses[i] for i in self.atomsInFirstGroup])
r1 = sum([masses[i]*q[i] for i in self.atomsInFirstGroup]) / mass1
else:
r1 = np.array([0.0, 0.0, 0.0])
if (len(self.atomsInSecondGroup) > 0):
mass2 = sum([masses[i] for i in self.atomsInSecondGroup])
r2 = sum([masses[i]*q[i] for i in self.atomsInSecondGroup]) / mass2
else:
r2 = np.array([0.0, 0.0, 0.0])

for i in range(self.Natoms):
if i in self.atomsInFirstGroup:
q[i] -= r1
Expand Down Expand Up @@ -897,12 +904,13 @@ def sampleRelativeQP(self):

# Optimize the two molecules with ASE
optimizer = QuasiNewton(
self.mol,maxstep=0.0250, # original value = 0.010,
self.mol,maxstep=0.0100, # original NWChemEx value = 0.0250,
trajectory=self.optimization_file,
)

# The convergence threshold is in the default units (eV and Ang)
optimizer.run(5.0e-3, 10000)
# optimizer.run(1.0e-3, 10000) # original value = 5.0e-3
optimizer.run(4.0e-3, 10000)

###############################################################################

Expand All @@ -918,6 +926,11 @@ def sampleRelativeQP(self):
print("##############################################################")
print("Initializing reactant group ", Nreactant)
print(" with atomic indexes:", reactantIndexes)

if (len(reactantIndexes) == 0):
print("No atoms in reactant group ", Nreactant)
print("Must be unimolecular! Skipping sampling for this group...")
continue

# You must center the molecule first before calculating
# its moment of inertia tensor
Expand Down Expand Up @@ -960,6 +973,16 @@ def sampleRelativeQP(self):
else:
nonzeroEs = np.real(Es[6:]) * self.freq2energy
nonzeroModes = modes[6:]

if (any(nonzeroEs < 0)):
print("WARNING: one of the 'nonzero' modes has a negative frequency...")
print(" for initial sampling, will convert to a positive number")
nonzeroEs = np.abs(nonzeroEs)

if (any(nonzeroEs <= 0.001*self.freq2energy)):
print("WARNING: one of the 'nonzero' modes has a close to zero frequency...")
print(" for initial sampling, will convert to a small nonzero number (0.001 cm-1)")
nonzeroEs[nonzeroEs <= 0.001*self.freq2energy] = 0.001 * self.freq2energy

###############################################################################################

Expand Down
Loading

0 comments on commit 02c577d

Please sign in to comment.