Skip to content

Commit

Permalink
wip #54 incorporating Soumik changes used in paper
Browse files Browse the repository at this point in the history
  • Loading branch information
joshkamm committed Feb 17, 2024
1 parent 586fc35 commit f3eb546
Show file tree
Hide file tree
Showing 2 changed files with 212 additions and 36 deletions.
5 changes: 3 additions & 2 deletions examples/suzuki/suzuki.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from conformational_sampling.ase_stko_optimizer import ASE
from conformational_sampling.config import Config
from conformational_sampling.gsm import stk_gsm, stk_se_de_gsm
from conformational_sampling.gsm import stk_de_gsm, stk_se_gsm
from conformational_sampling.main import (ConformerEnsembleOptimizer, bind_ligands, bind_to_dimethyl_Pd, load_stk_mol, load_stk_mol_list, stk_list_to_xyz_file, suzuki_ligand_conf_gen)
from conformational_sampling.utils import stk_metal

Expand Down Expand Up @@ -58,11 +58,12 @@
driving_coordinates = [('ADD',56,80),('BREAK',1,56),('BREAK',1,80)]

job_index=0
stk_se_de_gsm(
stk_se_gsm(
stk_mol=conformer_mols[job_index],
driving_coordinates=driving_coordinates,
config=config,
)
stk_de_gsm(config=config)
# stk_gsm(
# stk_mol=optimized_stk_mol,
# driving_coordinates=driving_coordinates,
Expand Down
243 changes: 209 additions & 34 deletions src/conformational_sampling/gsm.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
import stk

from conformational_sampling.config import Config
from conformational_sampling.analyze import ts_node

OPT_STEPS = 10 # 10 for debugging, 50 for production

def stk_mol_to_gsm_objects(stk_mol: stk.Molecule):
ELEMENT_TABLE = elements.ElementData()
Expand Down Expand Up @@ -137,11 +140,11 @@ def stk_gsm(stk_mol: stk.Molecule, driving_coordinates, config: Config):

nifty.printcool("REACTANT GEOMETRY NOT FIXED!!! OPTIMIZING")
optimizer.optimize(
molecule=reactant,
refE=reactant.energy,
opt_steps=50,
# path=path
)
molecule=reactant,
refE=reactant.energy,
opt_steps=50,
# path=path
)

se_gsm = SE_GSM.from_options(
reactant=reactant,
Expand All @@ -150,9 +153,9 @@ def stk_gsm(stk_mol: stk.Molecule, driving_coordinates, config: Config):
optimizer=optimizer,
xyz_writer=manage_xyz.write_std_multixyz,
driving_coords=driving_coordinates,
DQMAG_MAX=0.5, #default value is 0.8
ADD_NODE_TOL=0.01, #default value is 0.1
CONV_TOL = 0.0005,
DQMAG_MAX=0.5, # default value is 0.8
ADD_NODE_TOL=0.01, # default value is 0.1
CONV_TOL=0.0005,
)

# run pyGSM, setting up restart if necessary
Expand Down Expand Up @@ -185,7 +188,7 @@ def stk_gsm_command_line(stk_mol: stk.Molecule, driving_coordinates, config: Con
)


def stk_se_de_gsm(stk_mol: stk.Molecule, driving_coordinates, config: Config):
def stk_se_gsm(stk_mol: stk.Molecule, driving_coordinates, config: Config):

nifty.printcool(" Building the LOT")
atoms, xyz, geom = stk_mol_to_gsm_objects(stk_mol)
Expand Down Expand Up @@ -256,26 +259,26 @@ def stk_se_de_gsm(stk_mol: stk.Molecule, driving_coordinates, config: Config):

nifty.printcool("REACTANT GEOMETRY NOT FIXED!!! OPTIMIZING")
optimizer.optimize(
molecule=reactant,
refE=reactant.energy,
opt_steps=50,
# path=path
)
molecule=reactant,
refE=reactant.energy,
opt_steps=OPT_STEPS,
# path=path
)

se_gsm = SE_GSM.from_options(
reactant=reactant,
nnodes=20,
optimizer=optimizer,
xyz_writer=manage_xyz.write_std_multixyz,
driving_coords=driving_coordinates,
DQMAG_MAX=0.5, #default value is 0.8
ADD_NODE_TOL=0.01, #default value is 0.1
CONV_TOL = 0.0005,
DQMAG_MAX=0.5, # default value is 0.8
ADD_NODE_TOL=0.01, # default value is 0.1
CONV_TOL=0.0005,
)

se_gsm.set_V0()

se_gsm.nodes[0].gradrms = 0.
se_gsm.nodes[0].gradrms = 0.0
se_gsm.nodes[0].V0 = se_gsm.nodes[0].energy
print(" Initial energy is %1.4f" % se_gsm.nodes[0].energy)
se_gsm.add_GSM_nodeR()
Expand All @@ -284,14 +287,14 @@ def stk_se_de_gsm(stk_mol: stk.Molecule, driving_coordinates, config: Config):
se_gsm.pastts = se_gsm.past_ts()
print("pastts {}".format(se_gsm.pastts))
try:
if se_gsm.pastts == 1: #normal over the hill
if se_gsm.pastts == 1: # normal over the hill
se_gsm.add_GSM_nodeR(1)
se_gsm.add_last_node(2)
elif se_gsm.pastts == 2 or se_gsm.pastts==3: #when cgrad is positive
elif se_gsm.pastts == 2 or se_gsm.pastts == 3: # when cgrad is positive
se_gsm.add_last_node(1)
if se_gsm.nodes[se_gsm.nR-1].gradrms > 5.*se_gsm.options['CONV_TOL']:
se_gsm.add_last_node(1)
elif se_gsm.pastts == 3: #product detected by bonding
elif se_gsm.pastts == 3: # product detected by bonding
se_gsm.add_last_node(1)
except:
print("Failed to add last node, continuing.")
Expand All @@ -301,29 +304,166 @@ def stk_se_de_gsm(stk_mol: stk.Molecule, driving_coordinates, config: Config):
se_gsm.nodes = se_gsm.nodes[:se_gsm.nR]
energies = se_gsm.energies

if se_gsm.TSnode == se_gsm.nR-1:
if se_gsm.TSnode == se_gsm.nR - 1:
print(" The highest energy node is the last")
print(" not continuing with TS optimization.")
se_gsm.tscontinue = False

print(" Number of nodes is ", se_gsm.nnodes)
print(" Warning last node still not optimized fully")
se_gsm.xyz_writer('grown_string_{:03}.xyz'.format(se_gsm.ID), se_gsm.geometries, se_gsm.energies, se_gsm.gradrmss, se_gsm.dEs)
se_gsm.xyz_writer(
"grown_string_{:03}.xyz".format(se_gsm.ID),
se_gsm.geometries,
se_gsm.energies,
se_gsm.gradrmss,
se_gsm.dEs,
)
print(" SSM growth phase over")
se_gsm.done_growing = True

product = se_gsm.nodes[se_gsm.nnodes-1]
# product = se_gsm.nodes[se_gsm.nnodes-1]

# nifty.printcool("OPTIMIZING PRODUCT GEOMETRY")
# optimizer.optimize(
# molecule=product,
# refE=reactant.energy,
# opt_steps=50,
# # path=path
# )


def stk_de_gsm(config: Config):
# geoms = manage_xyz.read_xyzs('opt_converged_001.xyz')
geoms = manage_xyz.read_xyzs("grown_string_000.xyz")
ase_calculator = config.ase_calculator
if ase_calculator is None:
ase_calculator = calculator.XTB()
lot = ASELoT.from_options(ase_calculator, geom=geoms[0])

pes = PES.from_options(lot=lot, ad_idx=0, multiplicity=1)

atom_symbols = manage_xyz.get_atoms(geoms[0])

ELEMENT_TABLE = elements.ElementData()
atoms = [ELEMENT_TABLE.from_symbol(atom) for atom in atom_symbols]
xyz1 = manage_xyz.xyz_to_np(geoms[0])
xyz2 = manage_xyz.xyz_to_np(geoms[-1])

top1 = Topology.build_topology(
xyz1,
atoms,
)

# find union bonds
xyz2 = manage_xyz.xyz_to_np(geoms[-1])
top2 = Topology.build_topology(
xyz2,
atoms,
)

# Add bonds to top1 that are present in top2
# It's not clear if we should form the topology so the bonds
# are the same since this might affect the Primitives of the xyz1 (slightly)
# Later we stil need to form the union of bonds, angles and torsions
# However, I think this is important, the way its formulated, for identifiyin
# the number of fragments and blocks, which is used in hybrid TRIC.
for bond in top2.edges():
if bond in top1.edges:
pass
elif (bond[1], bond[0]) in top1.edges():
pass
else:
print(" Adding bond {} to top1".format(bond))
if bond[0] > bond[1]:
top1.add_edge(bond[0], bond[1])
else:
top1.add_edge(bond[1], bond[0])

addtr = True
connect = addcart = False
p1 = PrimitiveInternalCoordinates.from_options(
xyz=xyz1,
atoms=atoms,
connect=connect,
addtr=addtr,
addcart=addcart,
topology=top1,
)

p2 = PrimitiveInternalCoordinates.from_options(
xyz=xyz2,
atoms=atoms,
addtr=addtr,
addcart=addcart,
connect=connect,
topology=top1, # Use the topology of 1 because we fixed it above
)

p1.add_union_primitives(p2)

coord_obj1 = DelocalizedInternalCoordinates.from_options(
xyz=xyz1,
atoms=atoms,
addtr=addtr,
addcart=addcart,
connect=connect,
primitives=p1,
)

# coord_obj2 = DelocalizedInternalCoordinates.from_options(
# xyz=xyz2,
# atoms=atoms,
# addtr=addtr,
# addcart=addcart,
# connect=connect,
# primitives=p2,
# )

reactant = Molecule.from_options(
geom=geoms[0],
PES=pes,
coord_obj=coord_obj1,
Form_Hessian=True,
)

product = Molecule.copy_from_options(
reactant,
xyz=xyz2,
new_node_id=len(geoms) - 1,
copy_wavefunction=False,
)

optimizer = eigenvector_follow.from_options(
Linesearch="backtrack",
OPTTHRESH=0.0005,
DMAX=0.5,
abs_max_step=0.5,
conv_Ediff=0.1,
)

nifty.printcool("OPTIMIZING REACTANT GEOMETRY")
optimizer.optimize(
molecule=reactant,
refE=reactant.energy,
opt_steps=OPT_STEPS,
)

nifty.printcool("OPTIMIZING PRODUCT GEOMETRY")
optimizer.optimize(
molecule=product,
refE=reactant.energy,
opt_steps=50,
# path=path
)

lot = ASELoT.from_options(ase_calculator, geom=geom, ID=1)
pes.lot = lot
molecule=product,
refE=reactant.energy,
opt_steps=OPT_STEPS,
)

# For xTB

de_gsm_max_nodes = 20
path = Path.cwd() / "scratch/001"
path.mkdir(exist_ok=True)

for item in range(de_gsm_max_nodes):
path = Path.cwd() / f"scratch/001/{item}"
path.mkdir(exist_ok=True)

de_gsm = DE_GSM.from_options(
reactant=reactant,
Expand All @@ -334,7 +474,42 @@ def stk_se_de_gsm(stk_mol: stk.Molecule, driving_coordinates, config: Config):
ID=1,
)

# For DFT

# de_gsm_max_nodes = 20
# path = Path.cwd() / 'scratch/002'
# path.mkdir(exist_ok=True)

# for item in range(de_gsm_max_nodes):
# path = Path.cwd() / f'scratch/002/{item}'
# path.mkdir(exist_ok=True)

# de_gsm = DE_GSM.from_options(
# reactant=reactant,
# product=product,
# nnodes=15,
# optimizer=optimizer,
# xyz_writer=manage_xyz.write_std_multixyz,
# ID=2,
# )

de_gsm.go_gsm()

gsm_plot(de_gsm.energies, x=range(len(de_gsm.energies)), title=1)
# TS-Optimization following DE-GSM run

ts_node_energy = ts_node(de_gsm.energies)[1]
ts_node_index = de_gsm.energies.index(ts_node_energy)
ts_node_geom = de_gsm.nodes[ts_node_index]

nifty.printcool("Optimizing TS node")
optimizer.optimize(
molecule=ts_node_geom,
refE=de_gsm.energies[0],
opt_steps=OPT_STEPS,
opt_type="TS",
ictan=de_gsm.ictan[ts_node_index],
)

de_gsm.nodes[ts_node_index] = ts_node_geom

gsm_plot(de_gsm.energies, x=range(len(de_gsm.energies)), title=1)

0 comments on commit f3eb546

Please sign in to comment.