Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Internal coordinate interpolation #166

Open
wants to merge 48 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
ee743f5
Updates to internal.py and molecule.py for interpolation
leeping May 2, 2023
b27b022
Add interpolation code
leeping May 5, 2023
f289ea0
sum -> np.sum
hjnpark May 10, 2023
968aa3b
temporary commit, commenting needs to be done
hjnpark May 13, 2023
82d2536
Implement erisemax; fix some regression tests
leeping May 15, 2023
30c5b38
Merge branch 'master' of github.com:leeping/geomeTRIC into interpolate
leeping May 29, 2023
372bbcd
pull from lpw/interpolate and more comments
hjnpark Jun 7, 2023
0e01123
Merge branch 'interpolate' of github.com:leeping/geomeTRIC into inter…
leeping Jul 6, 2023
3585112
args can be passed by users for the interpolation method
hjnpark Aug 23, 2023
4de83d5
ignoring nframes and prealign when an interpolated trajectory is prov…
hjnpark Aug 23, 2023
55c0cc6
Merge pull request #165 from hjnpark/interpolate
leeping Sep 10, 2023
fe335db
Merge branch 'interpolate' of github.com:leeping/geomeTRIC into inter…
leeping Sep 21, 2023
626a5c4
Merge branch 'master' of github.com:leeping/geomeTRIC into interpolate
leeping Sep 21, 2023
9987360
Resolve an inconsistency in how G condition number was being evaluated
leeping Sep 21, 2023
225f85d
Improve newCartesian() performance
leeping Sep 22, 2023
72bc66c
Bug fix - check if rigid is in (None, False)
leeping Sep 22, 2023
5a68400
Fix a bug in ov(vi, vi)
leeping Sep 22, 2023
83f32ed
Tweak two tests in test_openmm.py; still need one that breaks the IC
leeping Sep 22, 2023
cf35814
Change default behavior of EqualSpacing back to the linear interpolator
leeping Sep 29, 2023
952a6be
Interpolate parameter bug fixed
hjnpark Sep 29, 2023
025f49b
Merge pull request #169 from hjnpark/interpolate
leeping Sep 29, 2023
65f2cef
Merge branch 'master' into interpolate
leeping Sep 30, 2023
cfba8c5
Implement improved dihedral angle syncing
leeping Oct 3, 2023
09d48a9
Enable warning messages for dihedral angle clash
leeping Oct 4, 2023
0a7326d
Default align reac/prod; split large dihedral angle changes
leeping Oct 8, 2023
3bbf667
interpolation parameter bug fixed
hjnpark Oct 9, 2023
1d037ee
Lower diagnostic thresholds for dihedrals
leeping Oct 12, 2023
04d0231
Merge branch 'interpolate_syncdihedrals2' of github.com:leeping/geome…
leeping Oct 12, 2023
b9e0a0a
Add group_atoms_by_topology() method
leeping Oct 18, 2023
e2da27f
Don't quit when damping hits 0.2; tweak dihedral diagnostic; align_fr…
leeping Oct 19, 2023
60bb0c3
Merge pull request #172 from leeping/interpolate_syncdihedrals2
leeping Oct 24, 2023
0e0f81c
Implement extrapolation feature
leeping Oct 24, 2023
11419ac
Merge branch 'tmin' into interpolate
leeping Oct 30, 2023
ef8c5cd
mintrust can be lower than convergence_drms ; conmethod 1 bugfix
leeping Oct 31, 2023
862d22b
Bug fix for extrapolation not activated
leeping Nov 4, 2023
aa14539
Merge branch 'master' of github.com:leeping/geomeTRIC into interpolate
leeping Nov 6, 2023
f0a1e37
A strange merge conflict in interpolate branch
leeping Nov 6, 2023
00ce6af
Merge branch 'interpolate' of github.com:leeping/geomeTRIC into inter…
leeping Nov 6, 2023
fd41651
Make align_system option more global
leeping Dec 6, 2023
d5a6170
Add atom deletion support to molecule.py, improve handling of CONECT …
leeping Jan 11, 2024
d8e897e
typo
hjnpark Mar 8, 2024
363de19
Merge pull request #195 from hjnpark/interpolate
hjnpark Mar 8, 2024
b0b5631
Merge branch 'master' of github.com:leeping/geomeTRIC into molecule_d…
leeping Mar 13, 2024
086ff87
Fix a "zero divide by zero" error
leeping Mar 13, 2024
90b0bff
Add interpolation "fast mode" and two new initial guess methods
leeping Mar 13, 2024
0196551
Merge branch 'interpolate' of github.com:leeping/geomeTRIC into inter…
leeping Mar 13, 2024
8d01391
Merge branch 'interpolate' of github.com:leeping/geomeTRIC into inter…
leeping Mar 13, 2024
99dcc3c
Merge branch 'molecule_delatom' into interpolate
leeping Mar 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
779 changes: 667 additions & 112 deletions geometric/internal.py

Large diffs are not rendered by default.

1,347 changes: 1,347 additions & 0 deletions geometric/interpolate.py

Large diffs are not rendered by default.

283 changes: 260 additions & 23 deletions geometric/molecule.py

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion geometric/nifty.py
Original file line number Diff line number Diff line change
Expand Up @@ -843,10 +843,11 @@ def destroyWorkQueue():
# Convenience function to destroy the Work Queue objects.
global WORK_QUEUE, WQIDS
if hasattr(WORK_QUEUE, '_free'):
print("Freeing Work Queue")
WORK_QUEUE._free()
WORK_QUEUE = None
WQIDS = defaultdict(list)
print("Freed Work Queue, sleeping for 5 seconds...")
time.sleep(5)

def queue_up(wq, command, input_files, output_files, tag=None, tgt=None, verbose=True, print_time=60):
"""
Expand Down
7 changes: 6 additions & 1 deletion geometric/optimize.py
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,8 @@ def evaluateStep(self):
colors['energy'] = "\x1b[92m" if Converged_energy else "\x1b[91m"
colors['quality'] = "\x1b[91m"
step_state = StepState.Reject if (Quality < -1.0 or params.transition) else StepState.Poor
# 2023-05-10: In the case of a minimization step that increases energy by more than erisemax, reject the step.
if not params.transition and not self.farConstraints and self.E-self.Eprev > params.erisemax: step_state = StepState.Reject
if 'energy' not in colors: colors['energy'] = "\x1b[92m" if Converged_energy else "\x1b[0m"
if 'quality' not in colors: colors['quality'] = "\x1b[0m"
colors['grms'] = "\x1b[92m" if Converged_grms else "\x1b[0m"
Expand Down Expand Up @@ -674,7 +676,10 @@ def evaluateStep(self):
elif self.farConstraints:
logger.info("\x1b[93mNot rejecting step - far from constraint satisfaction\x1b[0m\n")
else:
logger.info("\x1b[93mRejecting step - quality is lower than %.1f\x1b[0m\n" % (0.0 if params.transition else -1.0))
if not params.transition and not self.farConstraints and self.E-self.Eprev > params.erisemax:
logger.info("\x1b[93mRejecting step - energy increases > %.2f during minimization\x1b[0m\n" % params.erisemax)
else:
logger.info("\x1b[93mRejecting step - quality is lower than %.1f\x1b[0m\n" % (0.0 if params.transition else -1.0))
self.trustprint = "\x1b[1;91mx\x1b[0m"
# Store the rejected step. In case the next step is identical to the rejected one, the next step should be accepted to avoid infinite loops.
self.X_rj = self.X.copy()
Expand Down
72 changes: 71 additions & 1 deletion geometric/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ def __init__(self, **kwargs):
self.enforce = kwargs.get('enforce', 0.0)
# Small eigenvalue threshold
self.epsilon = kwargs.get('epsilon', 1e-5)
# Energy increase threshold; 3e-2 -> about 20 kcal/mol
self.erisemax = kwargs.get('erisemax', 3e-2)
# Interval for checking the coordinate system for changes
self.check = kwargs.get('check', 0)
# More verbose printout
Expand Down Expand Up @@ -222,7 +224,6 @@ def printInfo(self):
elif self.hessian.startswith('file+last:'):
logger.info(' Hessian data will be read from file: %s, then computed for the last step.\n' % self.hessian[5:])


class NEBParams(object):
"""
Container for optimization parameters.
Expand Down Expand Up @@ -256,6 +257,32 @@ def __init__(self, **kwargs):
self.trust = max(self.tmin, self.trust)
self.xyzout = kwargs.get('xyzout', None)

class InterpParams(object):
"""
Container for interpolation parameters.
"""
def __init__(self, **kwargs):
# Whether we are optimizing a given trajectory.
self.optimize = kwargs.get('optimize', False)
if self.optimize:
self.nframes = 0
self.align_frags = False
else:
# Number of frames that will be used for the interpolation.
self.nframes = kwargs.get('nframes', 50)
# Whether we want to align the molecules in reactant and product frames.
self.align_frags = kwargs.get('align_frags', False)
# Whether we want to align the initial interpolate trajectory before optimization.
self.align_system = kwargs.get('align_system', True)
# Run in fast mode
self.fast = kwargs.get('fast', False)
# Verbose printout
self.verbose = kwargs.get('verbose', 0)
if 'extrapolate' in kwargs:
self.extrapolate = tuple(kwargs['extrapolate'])
else:
self.extrapolate = None

def str2bool(v):
""" Allows command line options such as "yes" and "True" to be converted into Booleans. """
if isinstance(v, bool):
Expand Down Expand Up @@ -373,6 +400,7 @@ def parse_optimizer_args(*args):
grp_optparam.add_argument('--reset', type=str2bool, help='Reset approximate Hessian to guess when eigenvalues are under epsilon.\n '
'Defaults to True for minimization and False for transition states.\n ')
grp_optparam.add_argument('--epsilon', type=float, help='Small eigenvalue threshold for resetting Hessian, default 1e-5.\n ')
grp_optparam.add_argument('--erisemax', type=float, help='For energy minimization, the maximum value that energy can rise without step being rejected.\n ')
grp_optparam.add_argument('--check', type=int, help='Check coordinates every <N> steps and rebuild coordinate system, disabled by default.\n')
grp_optparam.add_argument('--subfrctor', type=int, help='Project out net force and torque components from nuclear gradient.\n'
'0 = never project; 1 = auto-detect (default); 2 = always project.')
Expand Down Expand Up @@ -516,3 +544,45 @@ def parse_neb_args(*args):
args_dict['engine'] = 'tera'

return args_dict

def parse_interpolate_args(*args):

"""
Read user input from the command line interface.
Designed to be called by interpolate.main() passing in sys.argv[1:]
"""

parser = ArgumentParserWithFile(add_help=False, formatter_class=argparse.RawTextHelpFormatter, fromfile_prefix_chars='@')

grp_univ = parser.add_argument_group('universal', 'Relevant to every job')
grp_univ.add_argument('input', type=str, help='REQUIRED positional argument: xyz file containing reactant and product structures.\n')
grp_univ.add_argument('--optimize', type=str2bool, help='Provide "yes" to optimize an interpolated trajectory.\n'
'The following two arguments, nframes and prealign, will be ignored.\n'
'The input xyz file must contain 5 or more structures.\n')
grp_univ.add_argument('--nframes', type=int, help='Number of frames, needs to be bigger than 5, that will be used to interpolate, default 50\n')
grp_univ.add_argument('--fast', type=str2bool, help='Provide "yes" to run in fast mode (no splicing).\n')
grp_univ.add_argument('--align_frags', type=str2bool, help='Provide "yes" to align fragments in reactant and product structure prior to interpolation.\n')
grp_univ.add_argument('--align_system', type=str2bool, help='Provide "yes" to align the entire system prior to interpolation.\n')
grp_univ.add_argument('--extrapolate', type=int, nargs=2, help='Provide two integers to generate extrapolated frames at the head and tail ends.\n'
'The displacement norm between extrapolated frames is the same as for interpolated frames.\n')

grp_output = parser.add_argument_group('output', 'Control the format and amount of the output')
grp_output.add_argument('--prefix', type=str, help='Specify a prefix for log file and temporary directory.\n'
'Defaults to the input file path (incl. file name with extension removed).\n ')
grp_output.add_argument('--verbose', type=int, help='Set to positive for more verbose printout.\n'
'0 = Default print level. 1 = Basic info about optimization step.\n'
'2 = Include microiterations. 3 = Lots of printout from low-level functions.\n ')

grp_help = parser.add_argument_group('help', 'Get help')
grp_help.add_argument('-h', '--help', action='help', help='Show this help message and exit')

# Keep all arguments whose values are not None, so that the setting of default values
# in InterpParams() and get_molecule_engine() will work properly.
args_dict = {}
for k, v in vars(parser.parse_args(*args)).items():
if v is not None:
args_dict[k] = v

print("LPW debug:", args_dict)

return args_dict
23 changes: 13 additions & 10 deletions geometric/tests/test_openmm.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ def test_dlc_openmm_water3(localizer):
Optimize the geometry of three water molecules using standard delocalized internal coordinates.
The coordinate system will break down and have to be rebuilt.
"""
progress = geometric.optimize.run_optimizer(engine='openmm', pdb=os.path.join(datad,'water3.pdb'), coordsys='dlc', input='tip3p.xml',
converge=['gmax', '1.0e-5'], check=50)
# LPW 2023-09-21 Increased check from 50 to 100; this was "perturbed" by the improvement to InternalCoordinates.newCartesian() .
progress = geometric.optimize.run_optimizer(engine='openmm', pdb=os.path.join(datad,'water3.pdb'), coordsys='dlc', input='tip3p.xml',
converge=['gmax', '1.0e-5'], check=100)

# The results here are in Angstrom
#
ref = np.array([[ 1.19172917, -1.71174316, 0.79961878],
Expand All @@ -46,7 +48,8 @@ def test_dlc_openmm_water3(localizer):
xdiff = (progress.xyzs[-1] - ref).flatten()
rmsd, maxd = geometric.optimize.calc_drms_dmax(progress.xyzs[-1], ref, align=True)
rmsd2, maxd2 = geometric.optimize.calc_drms_dmax(progress.xyzs[-1], ref2, align=True)
print("RMS / Max displacement from reference:", rmsd, maxd)
print("RMS / Max displacement from reference 1:", rmsd, maxd)
print("RMS / Max displacement from reference 2:", rmsd2, maxd2)
# This test is a bit stochastic and doesn't converge to the same minimized geometry every time.
# Check that the energy is 0.01 a.u. above reference. Not really the qm_energy, this is a misnomer
assert progress.qm_energies[-1] < (e_ref + 0.01)
Expand All @@ -62,14 +65,14 @@ def test_dlc_openmm_water12(localizer):
The coordinate system is expected to break down and the optimizer should skip the optimization step
after rebuilding the coordinate system.
"""
progress = geometric.optimize.run_optimizer(engine='openmm', pdb=os.path.join(datad,'water12.pdb'),
progress = geometric.optimize.run_optimizer(engine='openmm', pdb=os.path.join(datad,'water12.pdb'),
coordsys='dlc', input='tip3p.xml', maxiter=20, converge=['maxiter'])

have_skip_step = False
for line in open('tip3p.log').readlines():
if 'Skipping optimization step' in line:
have_skip_step = True
assert have_skip_step
# LPW 2023-09-21: The coordinate system no longer breaks down after the improvement to newCartesian().
# have_skip_step = False
# for line in open('tip3p.log').readlines():
# if 'Skipping optimization step' in line:
# have_skip_step = True
# assert have_skip_step


@addons.using_openmm
Expand Down
15 changes: 11 additions & 4 deletions geometric/xml_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,16 @@ def read_coors_from_xml(M, state_xml):
M.xyzs[0][aid, 2] = z*10.0
return root;

def write_coors_to_xml(M, state_xml, out_xml):
if type(state_xml) is str:
with open(state_xml) as fobj:
xml = fobj.read()
root = ET.fromstring(xml)
elif type(state_xml) is ET.Element:
root = copy.deepcopy(state_xml)
else:
raise IOError("Expected second argument to write_coors_to_xml to be file name or xml.etree.ElementTree.Element type")

def write_coors_to_xml(M, root_template, filename):
root = copy.deepcopy(root_template)
for child in root:
if child.tag == "Positions":
for aid in range(M.na):
Expand All @@ -30,8 +37,8 @@ def write_coors_to_xml(M, root_template, filename):
child[aid].attrib['x'] = "%.16e" % x
child[aid].attrib['y'] = "%.16e" % y
child[aid].attrib['z'] = "%.16e" % z
# write
fout = open(filename, "w")
# write to the output XML file.
fout = open(out_xml, "w")
print(ET.tostring(root).decode("utf-8"), file=fout)
fout.close()

5 changes: 3 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
long_description_content_type="text/markdown",
entry_points={'console_scripts': [
'geometric-optimize = geometric.optimize:main',
'run-ase = geometric.ase_engine:main',
'geometric-neb = geometric.neb:main',
'geometric-neb = geometric.neb:main',
'geometric-interpolate = geometric.interpolate:main',
'run-ase = geometric.ase_engine:main'
]},
install_requires=[
'numpy>=1.11',
Expand Down
Loading