def recompute_relax_torsion_profile(scanxyz, pdb_fnm, dihedralstxt, sysxml_fnm): grid_geo_data, _ = read_scan_xyz(scanxyz) dihedral_list = read_dihedralstxt(dihedralstxt) sysxml_path = os.path.abspath(sysxml_fnm) m = Molecule(pdb_fnm) # create a new tmp dir for running calculations if os.path.exists(tmpdir): shutil.rmtree(tmpdir) os.mkdir(tmpdir) os.chdir(tmpdir) # loop over each geometry in each grid for grid_id, geo in grid_geo_data.items(): folder = 'gid_' + '_'.join(f'{d:+03d}' for d in grid_id) print(f'Running constrained optimization in {folder}') os.mkdir(folder) os.chdir(folder) # copy files m.xyzs = [geo] m.write('frame.pdb') shutil.copy(sysxml_path, 'openmm_system.xml') write_constraints_txt(dihedral_list, grid_id) # run geometric command = "geometric-optimize openmm_system.xml constraints.txt --openmm --pdb frame.pdb --qccnv --reset --epsilon 0.0 --enforce 0.1 --qdata" subprocess.run(command, shell=True, check=True) os.chdir('..') os.chdir('..')
def finish(self): """ Write qdata.txt and scan.xyz file based on converged scan results """ m = Molecule() m.elem = list(self.engine.M.elem) m.qm_energies, m.xyzs, m.comms = [], [], [] for gid in self.grid_ids: m.qm_energies.append(self.grid_energies[gid]) m.xyzs.append(self.grid_final_geometries[gid]) m.comms.append("Dihedral %s Energy %.9f" % (str(gid), self.grid_energies[gid])) m.write('qdata.txt') print("Final scan energies are written to qdata.txt") m.write('scan.xyz') print("Final scan energies are written to scan.xyz")
def finish(self): """ Write qdata.txt and scan.xyz file based on converged scan results """ m = Molecule() m.elem = list(self.engine.M.elem) m.qm_energies, m.xyzs, m.comms = [], [], [] # only print grid with energies for gid in sorted(self.grid_energies.keys()): m.qm_energies.append(self.grid_energies[gid]) m.xyzs.append(self.grid_final_geometries[gid]) m.comms.append("Dihedral %s Energy %.9f" % (str(gid), self.grid_energies[gid])) m.write('qdata.txt') print("Final scan energies are written to qdata.txt") m.write('scan.xyz') print("Final scan energies are written to scan.xyz")
def export_torsiondrive_data(molecule: "Ligand", tdrive_data: "TorsionDriveData") -> None: """ Export the stored torsiondrive data object to a scan.xyz file and qdata.txt file required for ForceBalance. Method taken from <https://github.com/lpwgroup/torsiondrive/blob/ac33066edf447e25e4beaf21c098e52ca0fc6649/torsiondrive/dihedral_scanner.py#L655> Args: molecule: The molecule object which contains the topology. tdrive_data: The results of a torsiondrive on the input molecule. """ from geometric.molecule import Molecule as GEOMol mol = GEOMol() mol.elem = [atom.atomic_symbol for atom in molecule.atoms] mol.qm_energies, mol.xyzs, mol.comms = [], [], [] for angle, grid_data in sorted(tdrive_data.reference_data.items()): mol.qm_energies.append(grid_data.energy) mol.xyzs.append(grid_data.geometry) mol.comms.append(f"Dihedral ({angle},) Energy {grid_data.energy}") mol.write("qdata.txt") mol.write("scan.xyz")
class EngineOpenMM(QMEngine): def load_input(self, input_file): """Input file is the name of the pdb file with the coords in we also require that the xml has the same name""" self.m_pdb = Molecule(input_file)[0] self.M = copy.deepcopy(self.m_pdb) xml_name = os.path.splitext(input_file)[0] + '.xml' # Check the xml file is present assert os.path.exists( xml_name ) is True, "OpenMM requires a pdb and xml file, ensure you have both in the current folder with the same prefix" with open(xml_name) as f: self.xml_content = f.read() def write_input(self): """Write a pdb file with the latest geometry and the input xml file""" self.m_pdb.xyzs[0] = self.M.xyzs[0] self.m_pdb.write('input.pdb') with open('input.xml', 'w') as out: out.write(self.xml_content) def optimize_geomeTRIC(self): """ run the constrained optimization using geomeTRIC package, in 3 steps: 1. Write a constraints.txt file. 2. Write a gradient job input file. 3. Run the job """ # sep 1 self.write_constraints_txt() # step 2 self.write_input() # set3 self.run( 'geometric-optimize --prefix tdrive --qccnv --reset --epsilon 0.0 --enforce 0.1 --qdata --pdb ' 'input.pdb --openmm input.xml constraints.txt', input_files=['input.xml', 'input.pdb', 'constraints.txt'], output_files=['tdrive.log', 'tdrive.xyz', 'qdata.txt'])
def finish(self): """ Write qdata.txt and scan.xyz file based on converged scan results """ m = Molecule() m.elem = list(self.engine.M.elem) m.qm_energies, m.xyzs, m.comms = [], [], [] # optionally writing qm gradients into qdata.txt if avilable writing_gradients = False if len(self.grid_final_gradients) == len(self.grid_final_geometries): m.qm_grads = [] writing_gradients = True # only print grid with energies for gid in sorted(self.grid_energies.keys()): m.qm_energies.append(self.grid_energies[gid]) m.xyzs.append(self.grid_final_geometries[gid]) if writing_gradients: m.qm_grads.append(self.grid_final_gradients[gid]) m.comms.append("Dihedral %s Energy %.9f" % (str(gid), self.grid_energies[gid])) m.write('qdata.txt') print( f"Final scan energies{' and gradients' if writing_gradients else ''} are written to qdata.txt" ) m.write('scan.xyz') print("Final scan energies are written to scan.xyz")
class EngineTerachem(QMEngine): def load_input(self, input_file): """ Load TeraChem input Example input file: coordinates start.xyz run gradient basis 6-31g* method rb3lyp charge 0 spinmult 1 dispersion yes scf diis+a maxit 50 """ self.tera_temp = [] geo_file = None with open(input_file) as terain: for line in terain: # we don't need to change the temp self.tera_temp.append(line) linest = line.strip() if not linest: continue key, value = linest.lower().split(None, 1) if key == 'coordinates': geo_file = value elif key == 'run': if value == 'gradient': self.temp_type = 'gradient' elif value == 'minimize': self.temp_type = 'optimize' # place holder for writing native constraints self.tera_temp.append('$!constraints@here') # check input assert geo_file, 'coordinates key not found in input file %s' % input_file if self.native_opt: assert self.temp_type == 'optimize', "input_file should be a opt job to use native opt" else: assert self.temp_type == 'gradient', "input_file should be a gradient job to use geomeTRIC" # load molecule from separate file, one frame only self.M = Molecule(geo_file)[0] # store the name of geo_file self.tera_geo_file = geo_file def write_input(self): """ Write TeraChem input files, i.e. run.in and start.xyz """ assert hasattr( self, 'tera_temp'), "self.tera_temp not set, call load_input() first" assert hasattr( self, 'tera_geo_file'), "self.tera_temp not set, call load_input() first" with open('run.in', 'w') as terain: for line in self.tera_temp: if line == "$!constraints@here": if hasattr(self, 'constraintsStr'): # self.constraintsStr will be set by self.optimize_native() terain.write(self.constraintsStr) else: terain.write(line) self.M.write(self.tera_geo_file) def optimize_native(self): """ Run the constrained optimization. 1. write a optimization job input file. 2. run the job """ assert self.temp_type == 'optimize', "To use native optimization, the input file be an opt job" if self.extra_constraints is None: self.constraintsStr = '\n$constraint_set\n' for d1, d2, d3, d4, v in self.dihedral_idx_values: # TeraChem use atom index starting from 1 self.constraintsStr += f"dihedral {float(v)} {d1 + 1}_{d2 + 1}_{d3 + 1}_{d4 + 1}\n" self.constraintsStr += '$end\n' else: self.constraintsStr = build_terachem_constraint_string( self.extra_constraints, self.dihedral_idx_values) # write input file self.write_input() # run the job self.run('terachem run.in > run.out', input_files=['run.in', self.tera_geo_file], output_files=['run.out', 'scr']) def optimize_geomeTRIC(self): """ run the constrained optimization using geomeTRIC package, in 3 steps: 1. Write a constraints.txt file. 2. Write a gradient job input file. 3. Run the job """ assert self.temp_type == 'gradient', "To use geomeTRIC package, the input file should have gradient() in it" # step 1 self.write_constraints_txt() # step 2 self.write_input() # step 3 cmd = 'geometric-optimize --prefix tdrive --qccnv --reset --epsilon 0.0 --enforce 0.1 --qdata run.in constraints.txt' self.run(cmd, input_files=['run.in', self.tera_geo_file, 'constraints.txt'], output_files=['tdrive.log', 'tdrive.xyz', 'qdata.txt']) def load_native_output(self): """ Load the optimized geometry and energy into a new molecule object and return """ m = Molecule('scr/optim.xyz')[-1] # read the energy from optim.xyz comment line m.qm_energies = [float(m.comms[0].split(maxsplit=1)[0])] return m
class EngineTerachem(QMEngine): def load_input(self, input_file): """ Load TeraChem input Example input file: coordinates start.xyz run gradient basis 6-31g* method rb3lyp charge 0 spinmult 1 dispersion yes scf diis+a maxit 50 """ self.tera_temp = [] geo_file = None with open(input_file) as terain: for line in terain: # we don't need to change the temp self.tera_temp.append(line) key, value = line.strip().lower().split(None, 1) if key == 'coordinates': geo_file = value elif key == 'run': if value == 'gradient': self.temp_type = 'gradient' elif value == 'minimize': self.temp_type = 'optimize' # place holder for writing native constraints self.tera_temp.append('$!constraints@here') # check input assert geo_file, 'coordinates key not found in input file %s' % input_file if self.native_opt: assert self.temp_type == 'optimize', "input_file should be a opt job to use native opt" else: assert self.temp_type == 'gradient', "input_file should be a gradient job to use geomeTRIC" # load molecule from separate file, one frame only self.M = Molecule(geo_file)[0] # store the name of geo_file self.tera_geo_file = geo_file def write_input(self): """ Write TeraChem input files, i.e. run.in and start.xyz """ assert hasattr(self, 'tera_temp'), "self.tera_temp not set, call load_input() first" assert hasattr(self, 'tera_geo_file'), "self.tera_temp not set, call load_input() first" with open('run.in', 'w') as terain: for line in self.tera_temp: if line == "$!constraints@here": if hasattr(self, 'constraintsStr'): # self.optblockStr will be set by self.optimize_native() terain.write(self.constraintsStr) else: terain.write(line) self.M.write(self.tera_geo_file) def optimize_native(self): """ Run the constrained optimization, following QChem 5.0 manual. 1. write a optimization job input file. 2. run the job """ assert self.temp_type == 'optimize', "To use native optimization, the input file be an opt job" # add the $opt block self.constraintsStr = '\n$constraint_set\n' for d1, d2, d3, d4, v in self.dihedral_idx_values: # Optking use atom index starting from 1 self.constraintsStr += 'dihedral %f %d_%d_%d_%d\n' % (v, d1+1, d2+1, d3+1, d4+1) self.constraintsStr += '$end\n' # write input file self.write_input() # run the job self.run('terachem run.in > run.out', input_files=['run.in', self.tera_geo_file], output_files=['run.out', 'scr']) def optimize_geomeTRIC(self): """ run the constrained optimization using geomeTRIC package, in 3 steps: 1. Write a constraints.txt file. 2. Write a gradient job input file. 3. Run the job """ assert self.temp_type == 'gradient', "To use geomeTRIC package, the input file should have gradient() in it" # step 1 self.write_constraints_txt() # step 2 self.write_input() # step 3 self.run('geometric-optimize --qccnv --reset --epsilon 0.0 run.in constraints.txt > optimize.log', input_files=['run.in', self.tera_geo_file, 'constraints.txt'], output_files=['optimize.log', 'opt.xyz', 'energy.txt']) def load_native_output(self): """ Load the optimized geometry and energy into a new molecule object and return """ m = Molecule('scr/optim.xyz')[-1] # read the energy from optim.xyz comment line m.qm_energies = [float(m.comms[0].split(None, 1)[0])] return m