def serialise_system(self): """Create the OpenMM system; parametrise using frost; serialise the system.""" # Load the molecule using openforcefield pdb_file = app.PDBFile(f'{self.molecule.name}.pdb') # Now we need the connection info try using smiles string from rdkit rdkit = RDKit() molecule = Molecule.from_smiles( rdkit.get_smiles(f'{self.molecule.name}.pdb')) # Make the openMM system omm_topology = pdb_file.topology off_topology = Topology.from_openmm(omm_topology, unique_molecules=[molecule]) # Load the smirnoff99Frosst force field. forcefield = ForceField('test_forcefields/smirnoff99Frosst.offxml') # Parametrize the topology and create an OpenMM System. system = forcefield.create_openmm_system(off_topology) # Serialise the OpenMM system into the xml file with open('serialised.xml', 'w+') as out: out.write(XmlSerializer.serializeSystem(system))
def min_ffxml(mol, ffxml): # make copy of the input mol oe_mol = oechem.OEGraphMol(mol) try: # create openforcefield molecule ==> prone to triggering Exception off_mol = Molecule.from_openeye(oe_mol) # load in force field ff = ForceField(ffxml) # create components for OpenMM system topology = Topology.from_molecules(molecules=[off_mol]) # create openmm system ==> prone to triggering Exception #system = ff.create_openmm_system(topology, charge_from_molecules=[off_mol]) system = ff.create_openmm_system(topology) except Exception as e: smilabel = oechem.OEGetSDData(oe_mol, "SMILES QCArchive") print( ' >>> openforcefield failed to create OpenMM system: ' f"'{oe_mol.GetTitle()}' '{smilabel}'") print(f"{e}\n") return print(" >>> successful OpenMM system creation for openforcefield " f"mol \"{oe_mol.GetTitle()}\"")
def minimise_energy_all_confs(mol, models = None, epsilon = 4, allow_undefined_stereo = True, **kwargs ): from simtk import unit from simtk.openmm import LangevinIntegrator from simtk.openmm.app import Simulation, HBonds, NoCutoff from rdkit import Chem from rdkit.Geometry import Point3D import mlddec import copy import tqdm mol = Chem.AddHs(mol, addCoords = True) if models is None: models = mlddec.load_models(epsilon) charges = mlddec.get_charges(mol, models) from openforcefield.utils.toolkits import RDKitToolkitWrapper, ToolkitRegistry from openforcefield.topology import Molecule, Topology from openforcefield.typing.engines.smirnoff import ForceField # from openforcefield.typing.engines.smirnoff.forcefield import PME import parmed import numpy as np forcefield = ForceField(get_data_filename("modified_smirnoff99Frosst.offxml")) #FIXME better way of identifying file location tmp = copy.deepcopy(mol) tmp.RemoveAllConformers() #XXX workround for speed beacuse seemingly openforcefield records all conformer informations, which takes a long time. but I think this is a ill-practice molecule = Molecule.from_rdkit(tmp, allow_undefined_stereo = allow_undefined_stereo) molecule.partial_charges = unit.Quantity(np.array(charges), unit.elementary_charge) topology = Topology.from_molecules(molecule) openmm_system = forcefield.create_openmm_system(topology, charge_from_molecules= [molecule]) structure = parmed.openmm.topsystem.load_topology(topology.to_openmm(), openmm_system) system = structure.createSystem(nonbondedMethod=NoCutoff, nonbondedCutoff=1*unit.nanometer, constraints=HBonds) integrator = LangevinIntegrator(273*unit.kelvin, 1/unit.picosecond, 0.002*unit.picoseconds) simulation = Simulation(structure.topology, system, integrator) out_mol = copy.deepcopy(mol) for i in tqdm.tqdm(range(out_mol.GetNumConformers())): conf = mol.GetConformer(i) structure.coordinates = unit.Quantity(np.array([np.array(conf.GetAtomPosition(i)) for i in range(mol.GetNumAtoms())]), unit.angstroms) simulation.context.setPositions(structure.positions) simulation.minimizeEnergy() # simulation.step(1) coords = simulation.context.getState(getPositions = True).getPositions(asNumpy = True).value_in_unit(unit.angstrom) conf = out_mol.GetConformer(i) for j in range(out_mol.GetNumAtoms()): conf.SetAtomPosition(j, Point3D(*coords[j])) return out_mol
def build_context(offxml, molfile): """ Build an OpenMM Context from a offxml file and a molecule file """ forcefield = ForceField(offxml, allow_cosmetic_attributes=True) molecule = OffMolecule.from_file(molfile) system = forcefield.create_openmm_system(molecule.to_topology()) integrator = openmm.VerletIntegrator(1.0 * unit.femtoseconds) platform = openmm.Platform.getPlatformByName('Reference') context = openmm.Context(system, integrator, platform) return context
def create_openmm_system(conversion, molecules): """ Create an OpenMM system using the input MOL2 file and force field file. """ molecule = Molecule.from_openeye(molecules[0]) topology = Topology.from_molecules([molecule]) ff = ForceField(conversion.ff) system = ff.create_openmm_system(topology) return topology, system
def _create_impropers_only_system( smiles: str = "CC1=C(C(=O)C2=C(C1=O)N3CC4C(C3(C2COC(=O)N)OC)N4)N", ) -> mm.System: """Create a simulation that contains only improper torsion terms, by parameterizing with openff-1.2.0 and deleting all terms but impropers """ molecule = Molecule.from_smiles(smiles, allow_undefined_stereo=True) g = esp.Graph(molecule) topology = Topology.from_molecules(molecule) forcefield = ForceField("openff-1.2.0.offxml") openmm_system = forcefield.create_openmm_system(topology) # delete all forces except PeriodicTorsionForce is_torsion = ( lambda force: "PeriodicTorsionForce" in force.__class__.__name__) for i in range(openmm_system.getNumForces())[::-1]: if not is_torsion(openmm_system.getForce(i)): openmm_system.removeForce(i) assert openmm_system.getNumForces() == 1 torsion_force = openmm_system.getForce(0) assert is_torsion(torsion_force) # set k = 0 for any torsion that's not an improper indices = set( map( tuple, esp.graphs.utils.offmol_indices.improper_torsion_indices(molecule), )) num_impropers_retained = 0 for i in range(torsion_force.getNumTorsions()): ( p1, p2, p3, p4, periodicity, phase, k, ) = torsion_force.getTorsionParameters(i) if (p1, p2, p3, p4) in indices: num_impropers_retained += 1 else: torsion_force.setTorsionParameters(i, p1, p2, p3, p4, periodicity, phase, 0.0) assert (num_impropers_retained > 0 ) # otherwise this molecule is not a useful test case! return openmm_system, topology, g
def create_openmm_system_from_smiles(conversion, smiles): """ Create an OpenMM system using SMILES and force field file. """ molecule = Molecule.from_smiles(smiles) topology = Topology.from_molecules([molecule]) ff = ForceField(conversion.ff) system = ff.create_openmm_system(topology) molecule.generate_conformers(n_conformers=1) return topology, system
def min_ffxml(mol, ofs, ffxml): """ Minimize the mol with force field input from FFXML file. Parameters ---------- mol : OpenEye single-conformer molecule ofs : OpenEye output filestream ffxml : string name of FFXML file """ # make copy of the input mol oe_mol = oechem.OEGraphMol(mol) try: # create openforcefield molecule ==> prone to triggering Exception off_mol = Molecule.from_openeye(oe_mol) # load in force field ff = ForceField(ffxml) # create components for OpenMM system topology = Topology.from_molecules(molecules=[off_mol]) # create openmm system ==> prone to triggering Exception #system = ff.create_openmm_system(topology, charge_from_molecules=[off_mol]) system = ff.create_openmm_system(topology) except Exception as e: smilabel = oechem.OEGetSDData(oe_mol, "SMILES QCArchive") print( ' >>> openforcefield failed to create OpenMM system: ' f'{oe_mol.GetTitle()} {smilabel}: {e}') return positions = structure.extractPositionsFromOEMol(oe_mol) # minimize structure with ffxml newpos, energy = run_openmm(topology, system, positions) # save geometry, save energy as tag, write mol to file oe_mol.SetCoords(oechem.OEFloatArray(newpos)) oechem.OESetSDData(oe_mol, "Energy FFXML", str(energy)) oechem.OEWriteConstMolecule(ofs, oe_mol) return
def _rdkit_parameteriser(cls, mol, **kwargs): from rdkit import Chem from openforcefield.utils.toolkits import RDKitToolkitWrapper, ToolkitRegistry """ Creates a parameterised system from rdkit molecule Parameters ---------- mol : rdkit.Chem.Mol """ try: forcefield = ForceField('test_forcefields/smirnoff99Frosst.offxml') molecule = Molecule.from_rdkit( mol, allow_undefined_stereo=cls.allow_undefined_stereo) if hasattr(cls, "_ddec_charger"): molecule.partial_charges = unit.Quantity( np.array(cls._ddec_charger(mol, cls.rf)), unit.elementary_charge) else: from openforcefield.utils.toolkits import AmberToolsToolkitWrapper molecule.compute_partial_charges_am1bcc( toolkit_registry=AmberToolsToolkitWrapper()) topology = Topology.from_molecules(molecule) openmm_system = forcefield.create_openmm_system( topology, charge_from_molecules=[molecule]) ligand_pmd = parmed.openmm.topsystem.load_topology( topology.to_openmm(), openmm_system, molecule._conformers[0]) except Exception as e: raise ValueError("Parameterisation Failed : {}".format(e)) #TODO ligand_pmd.title = cls.smiles for i in ligand_pmd.residues: i.name = 'LIG' tmp_dir = tempfile.mkdtemp() # We need all molecules as both pdb files (as packmol input) # and mdtraj.Trajectory for restoring bonds later. pdb_filename = tempfile.mktemp(suffix=".pdb", dir=tmp_dir) Chem.MolToPDBFile(mol, pdb_filename) cls.pdb_filename = pdb_filename cls.ligand_pmd = ligand_pmd
def __init__(self, dihedral_benchmark): """ It initializes an OpenMMEnergeticProfile object. Parameters ---------- dihedral_benchmark : an peleffybenchmarktools.dihedrals.DihedralBenchmark object The DihedralBenchmark object that will be used to obtain the energetic profile """ super().__init__(dihedral_benchmark) from openforcefield.typing.engines.smirnoff import ForceField mol = self.dihedral_benchmark.molecule ff = ForceField(mol.forcefield + '.offxml') self._omm_top = mol.off_molecule.to_topology() self._omm_system = ff.create_openmm_system(self._omm_top)
def main(): parser = argparse.ArgumentParser(description='Parameterizes a small \ molecule ligand for use with OpenMM \ using OpenFF') parser.add_argument('-l', '--ligand', action='store', nargs=1, dest='ligand', help='The ligand .sdf file to generate \ parameters for') parser.add_argument('-i', '--input_directory', action='store', nargs=1, dest='input', default=['./'], help='Directory where \ input pdb files are stored') parser.add_argument('-o', '--output_directory', action='store', nargs=1, dest='output', default=['./'], help='Directory where \ output log should be written') args = vars(parser.parse_args()) #Load SDF file from minimize_lig.py lig_sdf = args['input'][0] + '/' + args['ligand'][0] lig_name = lig_sdf.split('.sdf')[-2] lig_off_molecule = Molecule(args['output'][0] + '/' + lig_sdf) force_field = ForceField('test_forcefields/smirnoff99Frosst.offxml') start = time.time() ligand_system = force_field.create_openmm_system( lig_off_molecule.to_topology()) end = time.time() print(end - start) with open(lig_name + '.xml', 'w') as f: f.write(XmlSerializer.serialize(ligand_system))
def generateSMIRNOFFStructureRDK(ligand_file): """ Given an RDKit molecule, create an OpenMM System and use to generate a ParmEd structure using the SMIRNOFF forcefield parameters. """ if ligand_file.endswith('.mol'): new_file = ligand_file.replace('.mol', '.sdf') shutil.copyfile(ligand_file, new_file) ligand_file = new_file ligand_off_molecule = Molecule.from_file(ligand_file) force_field = ForceField('test_forcefields/smirnoff99Frosst.offxml') ligand_system = force_field.create_openmm_system( ligand_off_molecule.to_topology()) # Read in the coordinates of the ligand from the PDB file Chem.MolToPDBFile(Chem.MolFromMolFile(ligand_file, removeHs=False), "ligand.pdb") ligand_pdbfile = PDBFile("ligand.pdb") # Convert OpenMM System object containing ligand parameters into a ParmEd Structure. ligand_structure = parmed.openmm.load_topology( ligand_pdbfile.topology, ligand_system, xyz=ligand_pdbfile.positions) return ligand_structure
def test_merge_system(): """Test merging of a system created from AMBER and another created from SMIRNOFF.""" from .utils import create_system_from_amber, get_amber_file_path, get_alkethoh_file_path # Create System from AMBER prmtop_filename, inpcrd_filename = get_amber_file_path( 'cyclohexane_ethanol_0.4_0.6') system0, topology0, positions0 = create_system_from_amber( prmtop_filename, inpcrd_filename) # TODO: from openeye import oechem # Load simple OEMol alkethoh_mol2_filepath = get_alkethoh_file_path('AlkEthOH_c100')[0] ifs = oechem.oemolistream(alkethoh_mol2_filepath) mol = oechem.OEMol() flavor = oechem.OEIFlavor_Generic_Default | oechem.OEIFlavor_MOL2_Default | oechem.OEIFlavor_MOL2_Forcefield ifs.SetFlavor(oechem.OEFormat_MOL2, flavor) oechem.OEReadMolecule(ifs, mol) oechem.OETriposAtomNames(mol) # Load forcefield file. AlkEthOH_offxml_filename = utils.get_data_file_path( 'test_forcefields/Frosst_AlkEthOH.offxml') forcefield = ForceField(AlkEthOH_offxml_filename) # Create OpenMM System and Topology. off_mol = Molecule.from_openeye(mol, allow_undefined_stereo=True) off_top = Topology.from_molecules([off_mol]) system1 = forcefield.create_openmm_system(off_top) topology1 = structure.generateTopologyFromOEMol(mol) positions1 = structure.extractPositionsFromOEMol(mol) structure.merge_system(topology0, topology1, system0, system1, positions0, positions1, verbose=True)
def generateSMIRNOFFStructure(oemol): """ Given an OpenEye molecule (oechem.OEMol), create an OpenMM System and use to generate a ParmEd structure using the SMIRNOFF forcefield parameters. Parameters ---------- oemol : openeye.oechem.OEMol OpenEye molecule Returns ------- molecule_structure : parmed.Structure The resulting Structure """ warnings.warn(DEPRECATION_WARNING_TEXT, PendingDeprecationWarning) from openforcefield.topology import Molecule, Topology from openforcefield.typing.engines.smirnoff import ForceField off_mol = Molecule.from_openeye(oemol) off_top = Topology.from_molecules([off_mol]) mol_ff = ForceField('test_forcefields/smirnoff99Frosst.offxml') # Create OpenMM System and Topology. omm_top = generateTopologyFromOEMol(oemol) # If it's a nonperiodic box, then we can't use default (PME) settings if omm_top.getPeriodicBoxVectors() is None: mol_ff.get_parameter_handler("Electrostatics", {})._method = 'Coulomb' system = mol_ff.create_openmm_system(off_top) # Convert to ParmEd structure. import parmed xyz = extractPositionsFromOEMol(oemol) molecule_structure = parmed.openmm.load_topology(omm_top, system, xyz=xyz) return molecule_structure
def _openeye_parameteriser(cls, mol, **kwargs): """ Creates a parameterised system from openeye molecule Parameters ---------- mol : oechem.OEMol """ try: forcefield = ForceField('test_forcefields/smirnoff99Frosst.offxml') molecule = Molecule.from_openeye( mol, allow_undefined_stereo=cls.allow_undefined_stereo) from openforcefield.utils.toolkits import OpenEyeToolkitWrapper molecule.compute_partial_charges_am1bcc( toolkit_registry=OpenEyeToolkitWrapper()) topology = Topology.from_molecules(molecule) openmm_system = forcefield.create_openmm_system( topology, charge_from_molecules=[molecule]) ligand_pmd = parmed.openmm.topsystem.load_topology( topology.to_openmm(), openmm_system, molecule._conformers[0]) except Exception as e: raise ValueError("Parameterisation Failed : {}".format(e)) #TODO ligand_pmd.title = cls.smiles for i in ligand_pmd.residues: i.name = 'LIG' tmp_dir = tempfile.mkdtemp() # We need all molecules as both pdb files (as packmol input) # and mdtraj.Trajectory for restoring bonds later. pdb_filename = tempfile.mktemp(suffix=".pdb", dir=tmp_dir) from openeye import oechem # OpenEye Python toolkits oechem.OEWriteMolecule(oechem.oemolostream(pdb_filename), mol) cls.pdb_filename = pdb_filename cls.ligand_pmd = ligand_pmd
def test_component_combination(): """Test that a system still yields the same energy after rebuilding it out of its components """ from simtk import openmm from .utils import compare_system_energies, get_packmol_pdb_file_path # We've had issues where subsequent instances of a molecule might have zero charges # Here we'll try to catch this (and also explicitly check the charges) by re-building # a system out of its components # Create an OpenMM System from mol2 files containing a cyclohexane-ethanol mixture. AlkEthOH_offxml_filename = utils.get_data_file_path( 'test_forcefields/Frosst_AlkEthOH.offxml') forcefield = ForceField(AlkEthOH_offxml_filename) pdbfile = openmm.app.PDBFile( get_packmol_pdb_file_path('cyclohexane_ethanol_0.4_0.6')) sdf_file_paths = [ utils.get_data_file_path( os.path.join('systems', 'monomers', name + '.sdf')) for name in ('ethanol', 'cyclohexane') ] molecules = [Molecule.from_file(file_path) for file_path in sdf_file_paths] topology = Topology.from_openmm(pdbfile.topology, unique_molecules=molecules) system = forcefield.create_openmm_system(topology) # Convert System to a ParmEd Structure structure = parmed.openmm.topsystem.load_topology(topology.to_openmm(), system, pdbfile.positions) # Split the Structure into components, then re-compose it out of its components tmp = structure.split() strs, nums = [], [] for s, n in tmp: strs.append(s) nums.append(n) nums = [len(n) for n in nums] # Re-compose Structure from components new_structure = strs[0] * nums[0] for idx in range(1, len(nums)): new_structure += strs[idx] * nums[idx] # Swap in coordinates again new_structure.positions = structure.positions # Create System newsys = new_structure.createSystem(nonbondedMethod=openmm.app.NoCutoff, constraints=None, implicitSolvent=None) # Cross check energies groups0, groups1, energy0, energy1 = compare_system_energies( pdbfile.topology, pdbfile.topology, system, newsys, pdbfile.positions, verbose=False) # Also check that that the number of components is equal to the number I expect if not len(nums) == 2: print("Error: Test system has incorrect number of components.") raise Exception( 'Incorrect number of components in cyclohexane/ethanol test system.' ) # Also check that none of residues have zero charge for resnr in range(len(structure.residues)): abscharges = [ abs(structure.residues[resnr].atoms[idx].charge) for idx in range(len(structure.residues[resnr].atoms)) ] if sum(abscharges) == 0: raise Exception( 'Error: Residue %s in cyclohexane-ethanol test system has a charge of zero, which is incorrect.' % resnr)
def _build_reduced_system(self, original_force_field, topology, scale_amount=None): """Produces an OpenMM system containing only forces for the specified parameter, optionally perturbed by the amount specified by `scale_amount`. Parameters ---------- original_force_field: openforcefield.typing.engines.smirnoff.ForceField The force field to create the system from (and optionally perturb). topology: openforcefield.topology.Topology The topology of the system to apply the force field to. scale_amount: float, optional The optional amount to perturb the parameter by. Returns ------- simtk.openmm.System The created system. simtk.unit.Quantity The new value of the perturbed parameter. """ # As this method deals mainly with the toolkit, we stick to # simtk units here. from openforcefield.typing.engines.smirnoff import ForceField parameter_tag = self.parameter_key.tag parameter_smirks = self.parameter_key.smirks parameter_attribute = self.parameter_key.attribute original_handler = original_force_field.get_parameter_handler(parameter_tag) original_parameter = original_handler.parameters[parameter_smirks] if self.use_subset_of_force_field: force_field = ForceField() handler = copy.deepcopy(original_force_field.get_parameter_handler(parameter_tag)) force_field.register_parameter_handler(handler) else: force_field = copy.deepcopy(original_force_field) handler = force_field.get_parameter_handler(parameter_tag) parameter_index = None value_list = None if hasattr(original_parameter, parameter_attribute): parameter_value = getattr(original_parameter, parameter_attribute) else: attribute_split = re.split(r'(\d+)', parameter_attribute) assert len(parameter_attribute) == 2 assert hasattr(original_parameter, attribute_split[0]) parameter_attribute = attribute_split[0] parameter_index = int(attribute_split[1]) - 1 value_list = getattr(original_parameter, parameter_attribute) parameter_value = value_list[parameter_index] if scale_amount is not None: existing_parameter = handler.parameters[parameter_smirks] if np.isclose(parameter_value.value_in_unit(parameter_value.unit), 0.0): # Careful thought needs to be given to this. Consider cases such as # epsilon or sigma where negative values are not allowed. parameter_value = (scale_amount if scale_amount > 0.0 else 0.0) * parameter_value.unit else: parameter_value *= (1.0 + scale_amount) if value_list is None: setattr(existing_parameter, parameter_attribute, parameter_value) else: value_list[parameter_index] = parameter_value setattr(existing_parameter, parameter_attribute, value_list) system = force_field.create_openmm_system(topology) if not self.enable_pbc: disable_pbc(system) return system, parameter_value
Chem.rdmolops.AssignAtomChiralTagsFromStructure( rdkit_mol_h ) #very important, else you'll repeat the errors from before AllChem.ComputeGasteigerCharges( rdkit_mol_h ) #thats not at all mandatory nore recommended. It's just to go faster here. Don't do that in your code !!! try: #here is where the magic happens mol = Molecule.from_rdkit( rdkit_mol_h) #create a openff molecule from the rdkit one top = mol.to_topology() #extract the topology ligand_system = force_field.create_openmm_system( top, charge_from_molecules=[mol] ) #create our openmm system (that's the simplest version here) #classical setup of an openmm simulation #here we'll do just a bit of minimization integrator = openmm.LangevinIntegrator(300 * unit.kelvin, 91 / unit.picosecond, 0.002 * unit.picoseconds) simulation = openmm.app.Simulation(top, ligand_system, integrator) simulation.context.setPositions(mol.conformers[0]) print(" starting minimization") state = simulation.context.getState(getEnergy=True, getForces=True) print(' Starting pot energy:', state.getPotentialEnergy()) simulation.minimizeEnergy(tolerance=0, maxIterations=10000) state = simulation.context.getState(getPositions=True, getEnergy=True,
def serialise_system(self): """Create the OpenMM system; parametrise using frost; serialise the system.""" # Create an openFF molecule from the rdkit molecule off_molecule = Molecule.from_rdkit(self.molecule.rdkit_mol, allow_undefined_stereo=True) # Make the OpenMM system off_topology = off_molecule.to_topology() # Load the smirnoff99Frosst force field. forcefield = ForceField('test_forcefields/smirnoff99Frosst.offxml') try: # Parametrize the topology and create an OpenMM System. system = forcefield.create_openmm_system(off_topology) except (UnassignedValenceParameterException, TypeError, UnassignedValenceParameterException): # If this does not work then we have a moleucle that is not in SMIRNOFF so we must add generics # and remove the charge handler to get some basic parameters for the moleucle new_bond = BondHandler.BondType( smirks='[*:1]~[*:2]', length="0 * angstrom", k="0.0 * angstrom**-2 * mole**-1 * kilocalorie") new_angle = AngleHandler.AngleType( smirks='[*:1]~[*:2]~[*:3]', angle="0.0 * degree", k="0.0 * mole**-1 * radian**-2 * kilocalorie") new_torsion = ProperTorsionHandler.ProperTorsionType( smirks='[*:1]~[*:2]~[*:3]~[*:4]', periodicity1="1", phase1="0.0 * degree", k1="0.0 * mole**-1 * kilocalorie", periodicity2="2", phase2="180.0 * degree", k2="0.0 * mole**-1 * kilocalorie", periodicity3="3", phase3="0.0 * degree", k3="0.0 * mole**-1 * kilocalorie", periodicity4="4", phase4="180.0 * degree", k4="0.0 * mole**-1 * kilocalorie", idivf1="1.0", idivf2="1.0", idivf3="1.0", idivf4="1.0") new_vdw = vdWHandler.vdWType(smirks='[*:1]', epsilon=0 * unit.kilocalories_per_mole, sigma=0 * unit.angstroms) new_generics = { 'Bonds': new_bond, 'Angles': new_angle, 'ProperTorsions': new_torsion, 'vdW': new_vdw } for key, val in new_generics.items(): forcefield.get_parameter_handler(key).parameters.insert(0, val) # This has to be removed as sqm will fail with unknown elements del forcefield._parameter_handlers['ToolkitAM1BCC'] # Parametrize the topology and create an OpenMM System. system = forcefield.create_openmm_system(off_topology) # This will tag the molecule so run.py knows that generics have been used. self.fftype = 'generics' # Serialise the OpenMM system into the xml file with open('serialised.xml', 'w+') as out: out.write(XmlSerializer.serializeSystem(system))
async def run(io): print(dir(parmed)) if not os.path.exists(datapath + 'complex.xml') or not os.path.exists(datapath + 'complex.pdb'): print('1: loading Ligand molecule') ligand_off_molecule = Molecule(datapath + 'ligand.sdf') # Load the SMIRNOFF-format Parsley force field #force_field = ForceField('openff_unconstrained-1.0.0.offxml') print("2: Loading the Force Field") force_field = ForceField('openff_unconstrained-1.2.0.offxml') # Parametrize the ligand molecule by creating a Topology object from it print("3: Create Ligand System") ligand_system = force_field.create_openmm_system( ligand_off_molecule.to_topology()) # Read in the coordinates of the ligand from the PDB file ligand_pdbfile = PDBFile(datapath + 'ligand.pdb') # Convert OpenMM System object containing ligand parameters into a ParmEd Structure. print("4: Transforming Ligand System to Parmed") ligand_structure = parmed.openmm.load_topology( ligand_pdbfile.topology, ligand_system, xyz=ligand_pdbfile.positions) print("5: Loading the protein pdb file") receptor_pdbfile = PDBFile(datapath + 'receptor.pdb') # Load the AMBER protein force field through OpenMM. omm_forcefield = app.ForceField('amber14-all.xml') # Parameterize the protein. print("6: Create protein system") receptor_system = omm_forcefield.createSystem( receptor_pdbfile.topology) # Convert the protein System into a ParmEd Structure. print("7: Convert protein system to parmed") receptor_structure = parmed.openmm.load_topology( receptor_pdbfile.topology, receptor_system, xyz=receptor_pdbfile.positions) print("8: Combinding protein & ligand system") complex_structure = receptor_structure + ligand_structure print(dir(complex_structure)) print("9: Create Openmm system") # Convert the Structure to an OpenMM System in vacuum. complex_system = complex_structure.createSystem( nonbondedMethod=NoCutoff, nonbondedCutoff=9.0 * unit.angstrom, constraints=HBonds, removeCMMotion=False) complex_structure.save(datapath + 'complex.pdb', overwrite=True) complex_structure = parmed.load_file(datapath + 'complex.pdb') with open(datapath + 'complex.xml', 'w') as f: f.write(XmlSerializer.serialize(complex_system)) complex_structure = parmed.load_file(datapath + 'complex.pdb') with open(datapath + 'complex.xml', 'r') as f: complex_system = XmlSerializer.deserialize(f.read()) print(dir(complex_structure)) platform = openmm.Platform.getPlatformByName('OpenCL') properties = {'OpenCLPrecision': 'mixed'} integrator = openmm.LangevinIntegrator(300 * unit.kelvin, 91 / unit.picosecond, 0.002 * unit.picoseconds) simulation = openmm.app.Simulation(complex_structure, complex_system, integrator, platform) simulation.context.setPositions(complex_structure.positions) print(" starting minimization") state = simulation.context.getState(getEnergy=True, getForces=True) lastEnergy = state.getPotentialEnergy() potEnergyValue = state.getPotentialEnergy().value_in_unit( unit.kilojoules_per_mole) m = ' Starting pot energy: {:.3f} kJ/mol'.format(potEnergyValue) print(m) await io.emit("setMessage", m) # io.emit("setMessage", m) t0 = time.time() emit_freq = 1 maxIter = 20 # iterations=1 for i in range(100): simulation.minimizeEnergy(tolerance=0, maxIterations=maxIter) state = simulation.context.getState(getPositions=True, getEnergy=True) currentEnergy = state.getPotentialEnergy() positions = state.getPositions( asNumpy=True) * 10 #convert to angstroms p = positions._value.flatten().tolist() # if(abs(lastEnergy._value-currentEnergy._value)<100): # m =' Last pot energy:'+ currentEnergy.__str__()+ " step: {}".format(i+1) # await io.emit("setPositions", {'positions':p, 'message':m}) # break lastEnergy = currentEnergy #print("positions", p[0], p[1], p[2]) m = ' Current pot energy: {:.3f} kJ/mol - step: {:d}'.format( currentEnergy.value_in_unit(unit.kilojoules_per_mole), i + 1) #print(m) if not (i + 1) % emit_freq: await io.emit("setPositions", { 'positions': p, 'message': m, 'step': i }) # io.emit("setPositions", {'positions':p, 'message':m}) # await io.emit("setEnergy", m) #simulation.context.setPositions(complex_structure.positions) state = simulation.context.getState(getPositions=True, getEnergy=True, getForces=True) print(' Final pot energy:', state.getPotentialEnergy()) print(" in ", time.time() - t0) return state
def minimize(dat_file, lst_angle, pdb_dir, sdf_dir, coor_dir=None, xml_dir=None): # The simulation configuration time_step = 2 * unit.femtoseconds # simulation timestep temperature = 300 * unit.kelvin # simulation temperature friction = 1 / unit.picosecond # collision rate minimize_tolerance = 1e-5 * unit.kilojoule / unit.mole minimize_iteration_step = 1000000 forcefield = ForceField('openff-1.1.1.offxml') list_name, list_atoms = read_data(dat_file) list_energies = [] for i, name in enumerate(list_name): pdbfile = PDBFile(pdb_dir + '/' + pdb_format.format(name)) uni_mol = Molecule.from_file(sdf_dir + '/' + sdf_format.format(name)) list_energy = [] previous_structure = pdbfile.getPositions() for angle in lst_angle: # Load the structure topo = pdbfile.topology topo_ff = Topology.from_openmm(topo, [uni_mol]) system = forcefield.create_openmm_system(topo_ff) restrain_force = make_restrain_torsion(list_atoms[i], float(angle), 1e6) system.addForce(restrain_force) integrator = openmm.LangevinIntegrator(temperature, friction, time_step) simulation = openmm.app.Simulation(topo, system, integrator) #positions = pdbfile.getPositions() #simulation.context.setPositions(positions) simulation.context.setPositions(previous_structure) #simulation.context.setVelocitiesToTemperature(temperature) simulation.minimizeEnergy(tolerance=minimize_tolerance, maxIterations=minimize_iteration_step) energy_list = extract_energy(simulation) sum_energy = 0.0 for j in range(len(energy_list) - 1): sum_energy += energy_list[j] list_energy.append([sum_energy, energy_list[-1]]) previous_structure = simulation.context.getState( getPositions=True).getPositions() if coor_dir != None: write_xyz( previous_structure, topo, coor_dir + '/' + str(name) + '_' + str(angle) + '.xyz') if xml_dir != None: write_xml( topo, xml_dir + '/' + str(name) + '_' + str(angle) + '.xml') list_energies.append(list_energy) # Check f = open('check.log', 'a') f.write(str(i) + '\n') f.close() return list_energies
import os from openforcefield.topology import Molecule as Off_Molecule from openforcefield.topology import Topology as Off_Topology from openforcefield.typing.engines.smirnoff import ForceField test_ff = ForceField("../../forcefield/param_valence.offxml", allow_cosmetic_attributes=True) for f in os.listdir('.'): if f.endswith('mol2'): print(f) off_molecule = Off_Molecule.from_file(f) off_topology = Off_Topology.from_molecules(off_molecule) test_ff.create_openmm_system(off_topology)
class MoleculeVacuumSimulation(object): """ Simluate a single molecule system in vaccum. Parameters ---------- g : `espaloma.Graph` Input molecular graph. n_samples : `int` Number of samples to collect. n_steps_per_sample : `int` Number of steps between each sample. temperature : `float * unit.kelvin` Temperature for the simluation. collision_rate : `float / unit.picosecond` Collision rate. timestep : `float * unit.femtosecond` Time step. Methods ------- simulation_from_graph : Create simluation from molecule. run : Run the simluation. """ def __init__( self, forcefield="test_forcefields/smirnoff99Frosst.offxml", n_samples=100, n_steps_per_sample=1000, temperature=TEMPERATURE, collision_rate=COLLISION_RATE, step_size=STEP_SIZE, ): self.n_samples = n_samples self.n_steps_per_sample = n_steps_per_sample self.temperature = temperature self.collision_rate = collision_rate self.step_size = step_size if isinstance(forcefield, str): self.forcefield = ForceField(forcefield) else: # TODO: type assertion self.forcefield = forcefield def simulation_from_graph(self, g): """ Create simulation from moleucle """ # assign partial charge g.mol.assign_partial_charges("gasteiger") # faster # parameterize topology topology = g.mol.to_topology() # create openmm system system = self.forcefield.create_openmm_system( topology, # TODO: # figure out whether `sqm` should be so slow charge_from_molecules=[g.mol], ) # use langevin integrator integrator = openmm.LangevinIntegrator(self.temperature, self.collision_rate, self.step_size) # initialize simulation simulation = Simulation(topology=topology, system=system, integrator=integrator) import openforcefield # get conformer g.mol.generate_conformers( toolkit_registry=openforcefield.utils.RDKitToolkitWrapper(), ) # put conformer in simulation simulation.context.setPositions(g.mol.conformers[0]) # minimize energy simulation.minimizeEnergy() # set velocities simulation.context.setVelocitiesToTemperature(self.temperature) return simulation def run(self, g, in_place=True): """ Collect samples from simulation. Parameters ---------- g : `esp.Graph` Input graph. in_place : `bool` If ture, Returns ------- samples : `torch.Tensor`, `shape=(n_samples, n_nodes, 3)` `in_place=True` Sample. graph : `esp.Graph` Modified graph. """ # build simulation simulation = self.simulation_from_graph(g) # initialize empty list for samples. samples = [] # loop through number of samples for _ in range(self.n_samples): # run MD for `self.n_steps_per_sample` steps simulation.step(self.n_steps_per_sample) # append samples to `samples` samples.append( simulation.context.getState(getPositions=True).getPositions( asNumpy=True).value_in_unit(DISTANCE_UNIT)) # put samples into an array samples = np.array(samples) # put samples into tensor samples = torch.tensor(samples, dtype=torch.float32) if in_place is True: g.heterograph.nodes["n1"].data["xyz"] = samples.permute(1, 0, 2) # require gradient for force matching g.heterograph.nodes["n1"].data["xyz"].requires_grad = True return g return samples
def serialise_system(self): """Create the OpenMM system; parametrise using frost; serialise the system.""" # Create an openFF molecule from the rdkit molecule off_molecule = Molecule.from_rdkit(self.molecule.rdkit_mol, allow_undefined_stereo=True) # Make the OpenMM system off_topology = off_molecule.to_topology() forcefield = ForceField("openff_unconstrained-1.0.0.offxml") try: # Parametrise the topology and create an OpenMM System. system = forcefield.create_openmm_system(off_topology) except ( UnassignedValenceParameterException, UnassignedBondParameterException, UnassignedProperTorsionParameterException, UnassignedAngleParameterException, UnassignedMoleculeChargeException, TypeError, ): # If this does not work then we have a molecule that is not in SMIRNOFF so we must add generics # and remove the charge handler to get some basic parameters for the moleucle new_bond = BondHandler.BondType( smirks="[*:1]~[*:2]", length="0 * angstrom", k="0.0 * angstrom**-2 * mole**-1 * kilocalorie", ) new_angle = AngleHandler.AngleType( smirks="[*:1]~[*:2]~[*:3]", angle="0.0 * degree", k="0.0 * mole**-1 * radian**-2 * kilocalorie", ) new_torsion = ProperTorsionHandler.ProperTorsionType( smirks="[*:1]~[*:2]~[*:3]~[*:4]", periodicity1="1", phase1="0.0 * degree", k1="0.0 * mole**-1 * kilocalorie", periodicity2="2", phase2="180.0 * degree", k2="0.0 * mole**-1 * kilocalorie", periodicity3="3", phase3="0.0 * degree", k3="0.0 * mole**-1 * kilocalorie", periodicity4="4", phase4="180.0 * degree", k4="0.0 * mole**-1 * kilocalorie", idivf1="1.0", idivf2="1.0", idivf3="1.0", idivf4="1.0", ) new_vdw = vdWHandler.vdWType( smirks="[*:1]", epsilon=0 * unit.kilocalories_per_mole, sigma=0 * unit.angstroms, ) new_generics = { "Bonds": new_bond, "Angles": new_angle, "ProperTorsions": new_torsion, "vdW": new_vdw, } for key, val in new_generics.items(): forcefield.get_parameter_handler(key).parameters.insert(0, val) # This has to be removed as sqm will fail with unknown elements del forcefield._parameter_handlers["ToolkitAM1BCC"] del forcefield._parameter_handlers["Electrostatics"] # Parametrize the topology and create an OpenMM System. system = forcefield.create_openmm_system(off_topology) # This will tag the molecule so run.py knows that generics have been used. self.fftype = "generics" # Serialise the OpenMM system into the xml file with open("serialised.xml", "w+") as out: out.write(XmlSerializer.serializeSystem(system))
path = f'LIG{i}' print(f'Processing LIG{i}') # try: if 1: # DO LIGAND THINGS try: ligand_off_molecule = Molecule(f'{path}/LIG{i}_h.sdf') except Exception as e: print(e) continue ligand_pdbfile = PDBFile(f'{path}/LIG{i}_h.pdb') force_field = ForceField( 'openff_unconstrained-1.1.0.offxml' ) #smirnoff99Frosst.offxml') #'openff-1.0.0.offxml') ligand_system = force_field.create_openmm_system( ligand_off_molecule.to_topology()) ligand_structure = parmed.openmm.load_topology( ligand_pdbfile.topology, ligand_system, xyz=ligand_pdbfile.positions) if 1: # DO PROTEIN THINGS receptor_file = 'receptor.pdb' fixed_receptor_file = f'{path}/fixed_receptor.pdb' omm_forcefield = app.ForceField('amber14-all.xml') fixer = PDBFixer(receptor_file) #filename='receptor.pdb') missingresidues = fixer.findMissingResidues() rezez = fixer.findNonstandardResidues() fixer.replaceNonstandardResidues() fixer.removeHeterogens(keepWater=False) missingatoms = fixer.findMissingAtoms()
amber_struct.positions) print(amber_energy) # prepare openff system mol = Molecule.from_file(f'{prefix}.mol2') #mol = Molecule.from_file('%s.mol2' %(prefix)) #print(mol.to_smiles()) from utils import fix_carboxylate_bond_orders fix_carboxylate_bond_orders(mol) print(mol.to_smiles()) off_top = mol.to_topology() #off_top.box_vectors = [[48, 0, 0], [0, 48, 0], [0, 0, 48]] * unit.angstrom #print('off_box', off_top.box_vectors) #print('amb_box', pmd_struct.box) try: off_sys = ff.create_openmm_system(mol.to_topology(),)#allow_nonintegral_charges=True) except Exception as e: print(e) continue #nonbonded_force = [force for force in off_sys.getForces() if isinstance(force, openmm.NonbondedForce)][0] #nonbonded_force.createExceptionsFromBonds([(bond.atom1.molecule_atom_index, # bond.atom2.molecule_atom_index) for bond in mol.bonds], # ) #nonbonded_force.set with open('off_sys.xml','w') as of: of.write(XmlSerializer.serialize(off_sys)) off_energy = calc_energy(off_sys, off_top, amber_struct.positions, #mol.conformers[0] )
# In[269]: print(pdb_file_path) # In[270]: from openforcefield.topology import * from openforcefield.typing.engines.smirnoff import ForceField omm_topology = pdbfile.topology off_topology = Topology.from_openmm(omm_topology, unique_molecules=[acetylacetone]) forcefield = ForceField('openff-1.0.0.offxml') system = forcefield.create_openmm_system(off_topology) # In[ ]: # In[271]: from simtk import * # Langevin Dynamics: time_step = 2 * unit.femtoseconds temperature = 300 * unit.kelvin friction = 1 / unit.picosecond # collision rate integrator = openmm.LangevinIntegrator(temperature, friction, time_step) # Lenghth of simulation: num_steps = 1
def subtract_nonbonded_force( g, forcefield="test_forcefields/smirnoff99Frosst.offxml", ): # get the forcefield from str if isinstance(forcefield, str): forcefield = ForceField(forcefield) # partial charge g.mol.assign_partial_charges("gasteiger") # faster # parametrize topology topology = g.mol.to_topology() # create openmm system system = forcefield.create_openmm_system( topology, charge_from_molecules=[g.mol], ) # use langevin integrator, although it's not super useful here integrator = openmm.LangevinIntegrator(TEMPERATURE, COLLISION_RATE, STEP_SIZE) # create simulation simulation = Simulation(topology=topology, system=system, integrator=integrator) # get forces forces = list(system.getForces()) # loop through forces for force in forces: name = force.__class__.__name__ # turn off angle if "Angle" in name: for idx in range(force.getNumAngles()): id1, id2, id3, angle, k = force.getAngleParameters(idx) force.setAngleParameters(idx, id1, id2, id3, angle, 0.0) elif "Bond" in name: for idx in range(force.getNumBonds()): id1, id2, length, k = force.getBondParameters(idx) force.setBondParameters( idx, id1, id2, length, 0.0, ) elif "Torsion" in name: for idx in range(force.getNumTorsions()): ( id1, id2, id3, id4, periodicity, phase, k, ) = force.getTorsionParameters(idx) force.setTorsionParameters( idx, id1, id2, id3, id4, periodicity, phase, 0.0, ) force.updateParametersInContext(simulation.context) # the snapshots xs = (Quantity( g.nodes["n1"].data["xyz"].detach().numpy(), esp.units.DISTANCE_UNIT, ).value_in_unit(unit.nanometer).transpose((1, 0, 2))) # loop through the snapshots energies = [] derivatives = [] for x in xs: simulation.context.setPositions(x) state = simulation.context.getState( getEnergy=True, getParameters=True, getForces=True, ) energy = state.getPotentialEnergy().value_in_unit( esp.units.ENERGY_UNIT, ) derivative = state.getForces(asNumpy=True).value_in_unit( esp.units.FORCE_UNIT, ) energies.append(energy) derivatives.append(derivative) # put energies to a tensor energies = torch.tensor( energies, dtype=torch.get_default_dtype(), ).flatten()[None, :] derivatives = torch.tensor( np.stack(derivatives, axis=1), dtype=torch.get_default_dtype(), ) # subtract the energies g.heterograph.apply_nodes( lambda node: {"u_ref": node.data["u_ref"] - energies}, ntype="g", ) g.heterograph.apply_nodes( lambda node: {"u_ref_prime": node.data["u_ref_prime"] - derivatives}, ntype="n1", ) return g
class SMIRNOFF(OpenMM): """ Derived from Engine object for carrying out OpenMM calculations that use the SMIRNOFF force field. """ def __init__(self, name="openmm", **kwargs): self.valkwd = ['ffxml', 'pdb', 'mol2', 'platname', 'precision', 'mmopts', 'vsite_bonds', 'implicit_solvent', 'restrain_k', 'freeze_atoms'] super(SMIRNOFF,self).__init__(name=name, **kwargs) def readsrc(self, **kwargs): """ SMIRNOFF simulations always require the following passed in via kwargs: Parameters ---------- pdb : string Name of a .pdb file containing the topology of the system mol2 : list A list of .mol2 file names containing the molecule/residue templates of the system Also provide 1 of the following, containing the coordinates to be used: mol : Molecule forcebalance.Molecule object coords : string Name of a file (readable by forcebalance.Molecule) This could be the same as the pdb argument from above. """ pdbfnm = kwargs.get('pdb') # Determine the PDB file name. if not pdbfnm: raise RuntimeError('Name of PDB file not provided.') elif not os.path.exists(pdbfnm): logger.error("%s specified but doesn't exist\n" % pdbfnm) raise RuntimeError if 'mol' in kwargs: self.mol = kwargs['mol'] elif 'coords' in kwargs: if not os.path.exists(kwargs['coords']): logger.error("%s specified but doesn't exist\n" % kwargs['coords']) raise RuntimeError self.mol = Molecule(kwargs['coords']) else: logger.error('Must provide either a molecule object or coordinate file.\n') raise RuntimeError # Here we cannot distinguish the .mol2 files linked by the target # vs. the .mol2 files to be provided by the force field. # But we can assume that these files should exist when this function is called. self.mol2 = kwargs.get('mol2') if self.mol2: for fnm in self.mol2: if not os.path.exists(fnm): if hasattr(self, 'FF') and fnm in self.FF.fnms: continue logger.error("%s doesn't exist" % fnm) raise RuntimeError else: logger.error("Must provide a list of .mol2 files.\n") self.abspdb = os.path.abspath(pdbfnm) mpdb = Molecule(pdbfnm) for i in ["chain", "atomname", "resid", "resname", "elem"]: self.mol.Data[i] = mpdb.Data[i] # Store a separate copy of the molecule for reference restraint positions. self.ref_mol = deepcopy(self.mol) def prepare(self, pbc=False, mmopts={}, **kwargs): """ Prepare the calculation. Note that we don't create the Simulation object yet, because that may depend on MD integrator parameters, thermostat, barostat etc. This is mostly copied and modified from openmmio.py's OpenMM.prepare(), but we are calling ForceField() from the OpenFF toolkit and ignoring AMOEBA stuff. """ self.pdb = PDBFile(self.abspdb) # Create the OpenFF ForceField object. if hasattr(self, 'FF'): self.offxml = [self.FF.offxml] self.forcefield = self.FF.openff_forcefield else: self.offxml = listfiles(kwargs.get('offxml'), 'offxml', err=True) self.forcefield = OpenFF_ForceField(*self.offxml) ## Load mol2 files for smirnoff topology openff_mols = [] for fnm in self.mol2: try: mol = OffMolecule.from_file(fnm) except Exception as e: logger.error("Error when loading %s" % fnm) raise e openff_mols.append(mol) self.off_topology = OffTopology.from_openmm(self.pdb.topology, unique_molecules=openff_mols) # used in create_simulation() self.mod = Modeller(self.pdb.topology, self.pdb.positions) ## OpenMM options for setting up the System. self.mmopts = dict(mmopts) ## Specify frozen atoms and restraint force constant if 'restrain_k' in kwargs: self.restrain_k = kwargs['restrain_k'] if 'freeze_atoms' in kwargs: self.freeze_atoms = kwargs['freeze_atoms'][:] ## Set system options from ForceBalance force field options. fftmp = False if hasattr(self,'FF'): self.mmopts['rigidWater'] = self.FF.rigid_water if not all([os.path.exists(f) for f in self.FF.fnms]): # If the parameter files don't already exist, create them for the purpose of # preparing the engine, but then delete them afterward. fftmp = True self.FF.make(np.zeros(self.FF.np)) ## Set system options from periodic boundary conditions. self.pbc = pbc ## print warning for 'nonbonded_cutoff' keywords if 'nonbonded_cutoff' in kwargs: logger.warning("nonbonded_cutoff keyword ignored because it's set in the offxml file\n") ## Generate OpenMM-compatible positions self.xyz_omms = [] for I in range(len(self.mol)): position = self.mol.xyzs[I] * angstrom # xyz_omm = [Vec3(i[0],i[1],i[2]) for i in xyz]*angstrom # An extra step with adding virtual particles # mod = Modeller(self.pdb.topology, xyz_omm) # LPW commenting out because we don't have virtual sites yet. # mod.addExtraParticles(self.forcefield) if self.pbc: # Obtain the periodic box if self.mol.boxes[I].alpha != 90.0 or self.mol.boxes[I].beta != 90.0 or self.mol.boxes[I].gamma != 90.0: logger.error('OpenMM cannot handle nonorthogonal boxes.\n') raise RuntimeError box_omm = np.diag([self.mol.boxes[I].a, self.mol.boxes[I].b, self.mol.boxes[I].c]) * angstrom else: box_omm = None # Finally append it to list. self.xyz_omms.append((position, box_omm)) ## Build a topology and atom lists. Top = self.pdb.topology Atoms = list(Top.atoms()) Bonds = [(a.index, b.index) for a, b in list(Top.bonds())] # vss = [(i, [system.getVirtualSite(i).getParticle(j) for j in range(system.getVirtualSite(i).getNumParticles())]) \ # for i in range(system.getNumParticles()) if system.isVirtualSite(i)] self.AtomLists = defaultdict(list) self.AtomLists['Mass'] = [a.element.mass.value_in_unit(dalton) if a.element is not None else 0 for a in Atoms] self.AtomLists['ParticleType'] = ['A' if m >= 1.0 else 'D' for m in self.AtomLists['Mass']] self.AtomLists['ResidueNumber'] = [a.residue.index for a in Atoms] self.AtomMask = [a == 'A' for a in self.AtomLists['ParticleType']] self.realAtomIdxs = [i for i, a in enumerate(self.AtomMask) if a is True] if hasattr(self,'FF') and fftmp: for f in self.FF.fnms: os.unlink(f) def update_simulation(self, **kwargs): """ Create the simulation object, or update the force field parameters in the existing simulation object. This should be run when we write a new force field XML file. """ if len(kwargs) > 0: self.simkwargs = kwargs # Because self.forcefield is being updated in forcebalance.forcefield.FF.make() # there is no longer a need to create a new force field object here. try: self.system = self.forcefield.create_openmm_system(self.off_topology) except Exception as error: logger.error("Error when creating system for %s" % self.mol2) raise error # Commenting out all virtual site stuff for now. # self.vsinfo = PrepareVirtualSites(self.system) self.nbcharges = np.zeros(self.system.getNumParticles()) #---- # If the virtual site parameters have changed, # the simulation object must be remade. #---- # vsprm = GetVirtualSiteParameters(self.system) # if hasattr(self,'vsprm') and len(self.vsprm) > 0 and np.max(np.abs(vsprm - self.vsprm)) != 0.0: # if hasattr(self, 'simulation'): # delattr(self, 'simulation') # self.vsprm = vsprm.copy() if hasattr(self, 'simulation'): UpdateSimulationParameters(self.system, self.simulation) else: self.create_simulation(**self.simkwargs) def optimize(self, shot=0, align=True, crit=1e-4): return super(SMIRNOFF,self).optimize(shot=shot, align=align, crit=crit, disable_vsite=True) def interaction_energy(self, fraga, fragb): """ Calculate the interaction energy for two fragments. Because this creates two new objects and requires passing in the mol2 argument, the codes are copied and modified from the OpenMM class. """ self.update_simulation() if self.name == 'A' or self.name == 'B': logger.error("Don't name the engine A or B!\n") raise RuntimeError # Create two subengines. if hasattr(self,'target'): if not hasattr(self,'A'): self.A = SMIRNOFF(name="A", mol=self.mol.atom_select(fraga), mol2=self.mol2, target=self.target) if not hasattr(self,'B'): self.B = SMIRNOFF(name="B", mol=self.mol.atom_select(fragb), mol2=self.mol2, target=self.target) else: if not hasattr(self,'A'): self.A = SMIRNOFF(name="A", mol=self.mol.atom_select(fraga), mol2=self.mol2, platname=self.platname, \ precision=self.precision, offxml=self.offxml, mmopts=self.mmopts) if not hasattr(self,'B'): self.B = SMIRNOFF(name="B", mol=self.mol.atom_select(fragb), mol2=self.mol2, platname=self.platname, \ precision=self.precision, offxml=self.offxml, mmopts=self.mmopts) # Interaction energy needs to be in kcal/mol. D = self.energy() A = self.A.energy() B = self.B.energy() return (D - A - B) / 4.184 def get_smirks_counter(self): """Get a counter for the time of appreance of each SMIRKS""" smirks_counter = Counter() molecule_force_list = self.forcefield.label_molecules(self.off_topology) for mol_idx, mol_forces in enumerate(molecule_force_list): for force_tag, force_dict in mol_forces.items(): # e.g. force_tag = 'Bonds' for parameter in force_dict.values(): smirks_counter[parameter.smirks] += 1 return smirks_counter
def compute_conformer_energies_from_file(filename): # Load in the molecule and its conformers. # Note that all conformers of the same molecule are loaded as separate Molecule objects # If using a OFF Toolkit version before 0.7.0, loading SDFs through RDKit and OpenEye may provide # different behavior in some cases. So, here we force loading through RDKit to ensure the correct behavior rdktkw = RDKitToolkitWrapper() loaded_molecules = Molecule.from_file(filename, toolkit_registry=rdktkw) # Collatate all conformers of the same molecule # NOTE: This isn't necessary if you have already loaded or created multi-conformer molecules; # it is just needed because our SDF reader does not automatically collapse conformers. molecules = [loaded_molecules[0]] for molecule in loaded_molecules[1:]: if molecule == molecules[-1]: for conformer in molecule.conformers: molecules[-1].add_conformer(conformer) else: molecules.append(molecule) n_molecules = len(molecules) n_conformers = sum([mol.n_conformers for mol in molecules]) print( f'{n_molecules} unique molecule(s) loaded, with {n_conformers} total conformers' ) # Load the openff-1.1.0 force field appropriate for vacuum calculations (without constraints) from openforcefield.typing.engines.smirnoff import ForceField forcefield = ForceField('openff_unconstrained-1.1.0.offxml') # Loop over molecules and minimize each conformer for molecule in molecules: # If the molecule doesn't have a name, set mol.name to be the hill formula if molecule.name == '': molecule.name = Topology._networkx_to_hill_formula( molecule.to_networkx()) print('%s : %d conformers' % (molecule.name, molecule.n_conformers)) # Make a temporary copy of the molecule that we can update for each minimization mol_copy = Molecule(molecule) # Make an OpenFF Topology so we can parameterize the system off_top = molecule.to_topology() print( f"Parametrizing {molecule.name} (may take a moment to calculate charges)" ) system = forcefield.create_openmm_system(off_top) # Use OpenMM to compute initial and minimized energy for all conformers integrator = openmm.VerletIntegrator(1 * unit.femtoseconds) platform = openmm.Platform.getPlatformByName('Reference') omm_top = off_top.to_openmm() simulation = openmm.app.Simulation(omm_top, system, integrator, platform) # Print text header print( 'Conformer Initial PE Minimized PE RMS between initial and minimized conformer' ) output = [[ 'Conformer', 'Initial PE (kcal/mol)', 'Minimized PE (kcal/mol)', 'RMS between initial and minimized conformer (Angstrom)' ]] for conformer_index, conformer in enumerate(molecule.conformers): simulation.context.setPositions(conformer) orig_potential = simulation.context.getState( getEnergy=True).getPotentialEnergy() simulation.minimizeEnergy() min_state = simulation.context.getState(getEnergy=True, getPositions=True) min_potential = min_state.getPotentialEnergy() # Calculate the RMSD between the initial and minimized conformer min_coords = min_state.getPositions() min_coords = np.array([[atom.x, atom.y, atom.z] for atom in min_coords]) * unit.nanometer mol_copy._conformers = None mol_copy.add_conformer(conformer) mol_copy.add_conformer(min_coords) rdmol = mol_copy.to_rdkit() rmslist = [] rdMolAlign.AlignMolConformers(rdmol, RMSlist=rmslist) minimization_rms = rmslist[0] # Save the minimized conformer to file mol_copy._conformers = None mol_copy.add_conformer(min_coords) mol_copy.to_file( f'{molecule.name}_conf{conformer_index+1}_minimized.sdf', file_format='sdf') print( '%5d / %5d : %8.3f kcal/mol %8.3f kcal/mol %8.3f Angstroms' % (conformer_index + 1, molecule.n_conformers, orig_potential / unit.kilocalories_per_mole, min_potential / unit.kilocalories_per_mole, minimization_rms)) output.append([ str(conformer_index + 1), f'{orig_potential/unit.kilocalories_per_mole:.3f}', f'{min_potential/unit.kilocalories_per_mole:.3f}', f'{minimization_rms:.3f}' ]) # Write the results out to CSV with open(f'{molecule.name}.csv', 'w') as of: for line in output: of.write(','.join(line) + '\n') # Clean up OpenMM Simulation del simulation, integrator