def test_from_toolkit_packmol_boxes(self, pdb_path, unique_molecules): """ Test loading some pre-prepared PACKMOL-generated systems. These use PDB files already prepared in the toolkit because PDB files are a pain. """ pdb_file_path = get_data_file_path("systems/packmol_boxes/" + pdb_path) pdbfile = openmm.app.PDBFile(pdb_file_path) Topology.from_openmm( pdbfile.topology, unique_molecules=unique_molecules, )
def test_chemical_environments_matches_RDK(self): """Test Topology.chemical_environment_matches""" from simtk.openmm import app toolkit_wrapper = RDKitToolkitWrapper() pdbfile = app.PDBFile( get_data_file_path( "systems/packmol_boxes/cyclohexane_ethanol_0.4_0.6.pdb")) # toolkit_wrapper = RDKitToolkitWrapper() # molecules = [Molecule.from_file(get_data_file_path(name)) for name in ('molecules/ethanol.mol2', # 'molecules/cyclohexane.mol2')] molecules = [] molecules.append(Molecule.from_smiles("CCO")) molecules.append(Molecule.from_smiles("C1CCCCC1")) topology = Topology.from_openmm(pdbfile.topology, unique_molecules=molecules) # Count CCO matches matches = topology.chemical_environment_matches( "[C:1]-[C:2]-[O:3]", toolkit_registry=toolkit_wrapper) assert len(matches) == 143 assert matches[0].topology_atom_indices == (1728, 1729, 1730) matches = topology.chemical_environment_matches( "[H][C:1]([H])([H])-[C:2]([H])([H])-[O:3][H]", toolkit_registry=toolkit_wrapper, ) assert (len(matches) == 1716 ) # 143 * 12 (there are 12 possible hydrogen mappings) assert matches[0].topology_atom_indices == (1728, 1729, 1730) # Search for a substructure that isn't there matches = topology.chemical_environment_matches( "[C][C:1]-[C:2]-[O:3]", toolkit_registry=toolkit_wrapper) assert len(matches) == 0
def test_from_openmm_pdbfile(self, argon_ff, argon_top): pdb_file_path = get_test_file_path("10-argons.pdb") pdbfile = openmm.app.PDBFile(pdb_file_path) mol = Molecule.from_smiles("[#18]") top = Topology.from_openmm(pdbfile.topology, unique_molecules=[mol]) box = pdbfile.topology.getPeriodicBoxVectors() box = box.value_in_unit(nm) * unit.nanometer out = argon_ff.create_openff_system(top) out.box = box out.positions = pdbfile.getPositions() assert np.allclose( out.positions.to(unit.nanometer).magnitude, pdbfile.getPositions().value_in_unit(nm), ) get_openmm_energies(out, hard_cutoff=True).compare( _get_openmm_energies( omm_sys=argon_ff.create_openmm_system(top), box_vectors=pdbfile.topology.getPeriodicBoxVectors(), positions=pdbfile.getPositions(), hard_cutoff=True, ))
def test_from_toolkit_packmol_boxes(self, pdb_path, unique_molecules): """ Test loading some pre-prepared PACKMOL-generated systems. These use PDB files already prepared in the toolkit because PDB files are a pain. """ ff = ForceField("openff-1.0.0.offxml") pdb_file_path = get_data_file_path("systems/packmol_boxes/" + pdb_path) pdbfile = openmm.app.PDBFile(pdb_file_path) top = Topology.from_openmm( pdbfile.topology, unique_molecules=unique_molecules, ) box = pdbfile.topology.getPeriodicBoxVectors() box = box.value_in_unit(nm) * unit.nanometer out = ff.create_openff_system(top) out.box = box out.positions = pdbfile.getPositions() assert np.allclose( out.positions.to(unit.nanometer).magnitude, pdbfile.getPositions().value_in_unit(nm), ) get_openmm_energies(out, hard_cutoff=True).compare( _get_openmm_energies( omm_sys=ff.create_openmm_system(top), box_vectors=pdbfile.topology.getPeriodicBoxVectors(), positions=pdbfile.getPositions(), hard_cutoff=True, ))
def test_from_openmm(self): """Test creation of an OpenFF Topology object from an OpenMM Topology and component molecules""" from simtk.openmm import app pdbfile = app.PDBFile( get_data_file_path( "systems/packmol_boxes/cyclohexane_ethanol_0.4_0.6.pdb")) with pytest.raises(MissingUniqueMoleculesError, match="requires a list of Molecule objects"): Topology.from_openmm(pdbfile.topology) molecules = [create_ethanol(), create_cyclohexane()] topology = Topology.from_openmm(pdbfile.topology, unique_molecules=molecules) assert topology.n_reference_molecules == 2 assert topology.n_topology_molecules == 239
def test_to_from_openmm(self): """Test a round-trip OpenFF -> OpenMM -> OpenFF Topology.""" from simtk.openmm.app import Aromatic # Create OpenFF topology with 1 ethanol and 2 benzenes. ethanol = Molecule.from_smiles("CCO") benzene = Molecule.from_smiles("c1ccccc1") off_topology = Topology.from_molecules( molecules=[ethanol, benzene, benzene]) # Convert to OpenMM Topology. omm_topology = off_topology.to_openmm() # Check that bond orders are preserved. n_double_bonds = sum([b.order == 2 for b in omm_topology.bonds()]) n_aromatic_bonds = sum( [b.type is Aromatic for b in omm_topology.bonds()]) assert n_double_bonds == 6 assert n_aromatic_bonds == 12 # Check that there is one residue for each molecule. assert omm_topology.getNumResidues() == 3 assert omm_topology.getNumChains() == 3 # Convert back to OpenFF Topology. off_topology_copy = Topology.from_openmm( omm_topology, unique_molecules=[ethanol, benzene]) # The round-trip OpenFF Topology is identical to the original. # The reference molecules are the same. assert (off_topology.n_reference_molecules == off_topology_copy.n_reference_molecules) reference_molecules_copy = list(off_topology_copy.reference_molecules) for ref_mol_idx, ref_mol in enumerate( off_topology.reference_molecules): assert ref_mol == reference_molecules_copy[ref_mol_idx] # The number of topology molecules is the same. assert (off_topology.n_topology_molecules == off_topology_copy.n_topology_molecules) # Check atoms. assert off_topology.n_topology_atoms == off_topology_copy.n_topology_atoms for atom_idx, atom in enumerate(off_topology.topology_atoms): atom_copy = off_topology_copy.atom(atom_idx) assert atom.atomic_number == atom_copy.atomic_number # Check bonds. for bond_idx, bond in enumerate(off_topology.topology_bonds): bond_copy = off_topology_copy.bond(bond_idx) bond_atoms = [a.atomic_number for a in bond.atoms] bond_atoms_copy = [a.atomic_number for a in bond_copy.atoms] assert bond_atoms == bond_atoms_copy assert bond.bond_order == bond_copy.bond_order assert bond.bond.is_aromatic == bond_copy.bond.is_aromatic
def _execute(self, directory, available_resources): from openff.toolkit.topology import Molecule, Topology pdb_file = app.PDBFile(self.coordinate_file_path) force_field_source = ForceFieldSource.from_json(self.force_field_path) if not isinstance(force_field_source, SmirnoffForceFieldSource): raise ValueError( "Only SMIRNOFF force fields are supported by this protocol." ) force_field = force_field_source.to_force_field() # Create the molecules to parameterize from the input substance. unique_molecules = [] for component in self.substance.components: molecule = Molecule.from_smiles(smiles=component.smiles) if molecule is None: raise ValueError(f"{component} could not be converted to a Molecule") unique_molecules.append(molecule) # Create the topology to parameterize from the input coordinates and the # expected molecule species. topology = Topology.from_openmm( pdb_file.topology, unique_molecules=unique_molecules ) system = force_field.create_openmm_system(topology) if system is None: raise RuntimeError( "Failed to create a system from the specified topology and molecules." ) system_xml = openmm.XmlSerializer.serialize(system) system_path = os.path.join(directory, "system.xml") with open(system_path, "w") as file: file.write(system_xml) self.parameterized_system = ParameterizedSystem( substance=self.substance, force_field=force_field_source, topology_path=self.coordinate_file_path, system_path=system_path, )
def test_from_openmm_missing_reference(self): """Test creation of an OpenFF Topology object from an OpenMM Topology when missing a unique molecule""" from simtk.openmm import app pdbfile = app.PDBFile( get_data_file_path( "systems/packmol_boxes/cyclohexane_ethanol_0.4_0.6.pdb")) molecules = [create_ethanol()] with pytest.raises( ValueError, match="No match found for molecule C6H12") as excinfo: topology = Topology.from_openmm(pdbfile.topology, unique_molecules=molecules)
def test_openmm_nonbonded_methods(inputs): """See test_nonbonded_method_resolution in openff/toolkit/tests/test_forcefield.py""" vdw_method = inputs["vdw_method"] electrostatics_method = inputs["electrostatics_method"] periodic = inputs["periodic"] result = inputs["result"] molecules = [create_ethanol()] forcefield = ForceField("test_forcefields/test_forcefield.offxml") pdbfile = app.PDBFile(get_data_file_path("systems/test_systems/1_ethanol.pdb")) topology = Topology.from_openmm(pdbfile.topology, unique_molecules=molecules) if not periodic: topology.box_vectors = None if type(result) == int: nonbonded_method = result # The method is validated and may raise an exception if it's not supported. forcefield.get_parameter_handler("vdW", {}).method = vdw_method forcefield.get_parameter_handler( "Electrostatics", {} ).method = electrostatics_method openff_interchange = Interchange.from_smirnoff( force_field=forcefield, topology=topology ) openmm_system = openff_interchange.to_openmm(combine_nonbonded_forces=True) for force in openmm_system.getForces(): if isinstance(force, openmm.NonbondedForce): assert force.getNonbondedMethod() == nonbonded_method break else: raise Exception elif issubclass(result, (BaseException, Exception)): exception = result forcefield.get_parameter_handler("vdW", {}).method = vdw_method forcefield.get_parameter_handler( "Electrostatics", {} ).method = electrostatics_method openff_interchange = Interchange.from_smirnoff( force_field=forcefield, topology=topology ) with pytest.raises(exception): openff_interchange.to_openmm(combine_nonbonded_forces=True) else: raise Exception("uh oh")
def test_from_openmm_duplicate_unique_mol(self): """Check that a DuplicateUniqueMoleculeError is raised if we try to pass in two indistinguishably unique mols""" from simtk.openmm import app pdbfile = app.PDBFile( get_data_file_path( "systems/packmol_boxes/cyclohexane_ethanol_0.4_0.6.pdb")) molecules = [ Molecule.from_file(get_data_file_path(name)) for name in ( "molecules/ethanol.mol2", "molecules/ethanol_reordered.mol2", "molecules/cyclohexane.mol2", ) ] with self.assertRaises(DuplicateUniqueMoleculeError) as context: topology = Topology.from_openmm(pdbfile.topology, unique_molecules=molecules)
def test_liquid_argon(): argon = Molecule.from_smiles("[#18]") pdbfile = app.PDBFile(get_test_file_path("packed-argon.pdb")) top = Topology.from_openmm(pdbfile.topology, unique_molecules=[argon]) argon_ff = ForceField(get_test_file_path("argon.offxml")) out = Interchange.from_smirnoff(argon_ff, top) out.positions = pdbfile.positions omm_energies = get_openmm_energies(out) gmx_energies = get_gromacs_energies( out, mdp="auto", writer="internal", ) omm_energies.compare( gmx_energies, custom_tolerances={ "vdW": 0.008 * simtk_unit.kilojoule_per_mole, }, ) argon_ff_no_switch = deepcopy(argon_ff) argon_ff_no_switch["vdW"].switch_width *= 0 out_no_switch = Interchange.from_smirnoff(argon_ff_no_switch, top) out_no_switch.positions = pdbfile.positions lmp_energies = get_lammps_energies(out_no_switch) omm_energies.compare( lmp_energies, custom_tolerances={ "vdW": 10.5 * simtk_unit.kilojoule_per_mole, }, )
def test_from_openmm_missing_conect(self): """ Test creation of an OpenFF Topology object from an OpenMM Topology when the origin PDB lacks CONECT records """ from simtk.openmm import app pdbfile = app.PDBFile( get_data_file_path("systems/test_systems/1_ethanol_no_conect.pdb")) molecules = [] molecules.append(Molecule.from_smiles("CCO")) with pytest.raises( ValueError, match="No match found for molecule C. This would be a " "very unusual molecule to try and parameterize, " "and it is likely that the data source it was " "read from does not contain connectivity " "information. If this molecule is coming from " "PDB, please ensure that the file contains CONECT " "records.", ) as excinfo: topology = Topology.from_openmm(pdbfile.topology, unique_molecules=molecules)
def compare_condensed_systems(mol, force_field): from openff.evaluator import unit as evaluator_unit from openff.evaluator.utils.packmol import pack_box mass_density = 500 * evaluator_unit.kilogram / evaluator_unit.meter ** 3 trj, assigned_residue_names = pack_box( molecules=[mol], number_of_copies=[100], mass_density=mass_density ) try: openff_top = Topology.from_openmm(trj.top.to_openmm(), unique_molecules=[mol]) except ValueError: print(f"Molecule failed! (conversion from OpenMM)\t{mol.to_inchi()}") return box_vectors = trj.unitcell_vectors[0] * simtk_unit.nanometer openff_top.box_vectors = box_vectors try: toolkit_sys = force_field.create_openmm_system( openff_top, charge_from_molecules=[mol], ) except ( UnassignedBondParameterException, UnassignedAngleParameterException, UnassignedProperTorsionParameterException, ): print(f"Molecule failed! (missing valence parameters)\t{mol.to_inchi()}") return positions = trj.xyz[0] * simtk_unit.nanometer toolkit_energy = _get_openmm_energies( toolkit_sys, box_vectors=box_vectors, positions=positions, ) openff_sys = Interchange.from_smirnoff(force_field=force_field, topology=openff_top) openff_sys.box = box_vectors openff_sys.positions = trj.xyz[0] * unit.nanometer new_sys = openff_sys.to_openmm(combine_nonbonded_forces=True) system_energy = _get_openmm_energies( new_sys, box_vectors=box_vectors, positions=positions, ) # Where energies to not precisely match, inspect all parameters in each force try: toolkit_energy.compare( system_energy, custom_tolerances={ "Bond": 1e-6 * kj_mol, "Angle": 1e-6 * kj_mol, "Torsion": 4e-5 * kj_mol, "Nonbonded": 1e-5 * kj_mol, }, ) except EnergyError as e: if "Torsion" in str(e): _compare_torsion_forces( _get_force(toolkit_sys, openmm.PeriodicTorsionForce), _get_force(new_sys, openmm.PeriodicTorsionForce), ) if "Nonbonded" in str(e): _compare_nonbonded_settings( _get_force(toolkit_sys, openmm.NonbondedForce), _get_force(new_sys, openmm.NonbondedForce), ) _compare_nonbonded_parameters( _get_force(toolkit_sys, openmm.NonbondedForce), _get_force(new_sys, openmm.NonbondedForce), ) if "Bond" in str(e): raise e if "Angle" in str(e): raise e
def _execute(self, directory, available_resources): from openff.toolkit.topology import Molecule, Topology force_field_source = ForceFieldSource.from_json(self.force_field_path) cutoff = pint_quantity_to_openmm(force_field_source.cutoff) # Load in the systems topology openmm_pdb_file = app.PDBFile(self.coordinate_file_path) # Create an OFF topology for better insight into the layout of the system # topology. unique_molecules = {} for component in self.substance: unique_molecule = Molecule.from_smiles(component.smiles) unique_molecules[unique_molecule.to_smiles()] = unique_molecule # Parameterize each component in the system. system_templates = {} for index, (smiles, unique_molecule) in enumerate(unique_molecules.items()): if smiles in ["O", "[H]O[H]", "[H][O][H]"]: component_system = self._build_tip3p_system( cutoff, openmm_pdb_file.topology.getUnitCellDimensions(), ) else: component_directory = os.path.join(directory, str(index)) os.makedirs(component_directory, exist_ok=True) with temporarily_change_directory(component_directory): component_system = self._parameterize_molecule( unique_molecule, force_field_source, cutoff ) system_templates[smiles] = component_system # Apply the parameters to the topology. topology = Topology.from_openmm( openmm_pdb_file.topology, unique_molecules.values() ) # Create the full system object from the component templates. system = self._create_empty_system(cutoff) for topology_molecule in topology.topology_molecules: smiles = topology_molecule.reference_molecule.to_smiles() system_template = system_templates[smiles] index_map = {} for index, topology_atom in enumerate(topology_molecule.atoms): index_map[topology_atom.atom.molecule_particle_index] = index # Append the component template to the full system. self._append_system(system, system_template, index_map) if openmm_pdb_file.topology.getPeriodicBoxVectors() is not None: system.setDefaultPeriodicBoxVectors( *openmm_pdb_file.topology.getPeriodicBoxVectors() ) # Serialize the system object. system_path = os.path.join(directory, "system.xml") with open(system_path, "w") as file: file.write(openmm.XmlSerializer.serialize(system)) self.parameterized_system = ParameterizedSystem( substance=self.substance, force_field=force_field_source, topology_path=self.coordinate_file_path, system_path=system_path, )
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. """ if hasattr(self, 'abspdb'): self.pdb = PDBFile(self.abspdb) else: pdb1 = "%s-1.pdb" % os.path.splitext(os.path.basename( self.mol.fnm))[0] self.mol[0].write(pdb1) self.pdb = PDBFile(pdb1) os.unlink(pdb1) # 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_plugins=True) ## 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) ## 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" ) # Apply the FF parameters to the system. Currently this is the only way to # determine if the FF will apply virtual sites to the system. _, openff_topology = self.forcefield.create_openmm_system( self.off_topology, return_topology=True) ## Generate OpenMM-compatible positions self.xyz_omms = [] for I in range(len(self.mol)): xyz = self.mol.xyzs[I] xyz_omm = ([Vec3(i[0], i[1], i[2]) for i in xyz] # Add placeholder positions for an v-sites. + [Vec3(0.0, 0.0, 0.0)] * openff_topology.n_topology_virtual_sites) * angstrom 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((xyz_omm, box_omm)) # used in create_simulation() openmm_topology = SMIRNOFF._openff_to_openmm_topology(openff_topology) openmm_positions = ( self.pdb.positions.value_in_unit(angstrom) + # Add placeholder positions for an v-sites. [Vec3(0.0, 0.0, 0.0)] * openff_topology.n_topology_virtual_sites) * angstrom self.mod = Modeller(openmm_topology, openmm_positions) ## Build a topology and atom lists. Top = self.mod.getTopology() Atoms = list(Top.atoms()) # 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 reparm(ligands, base): print( '**Running reparameterization of ligand(s) using open force fields\'s SMIRNOFF with openff 2.0.0**' ) # Load already parm'd system in_prmtop = base + '.prmtop' in_crd = base + '.inpcrd' # Create parmed strucuture orig_structure = parmed.amber.AmberParm(in_prmtop, in_crd) # Split orig_stucuture into unique structure instances e.g. protein, water, ligand, etc. pieces = orig_structure.split() for piece in pieces: # TODO: Figure out how to know which piece is which print(f"There are {len(piece[1])} instance(s) of {piece[0]}") # Generate an openff topology for the ligand # Openff Molecule does not support mol2 so conversion is needed ligs_w_sdf = [] for ligand in ligands: obabel[ligand[0], '-O', util.get_base(ligand[0]) + '.sdf']() ligs_w_sdf.append( (ligand[0], ligand[1], util.get_base(ligand[0]) + '.sdf')) # Keep track of ligands that were successfully reparmed so we know to skip them when putting the pieces back together reparmed_pieces = [] complex_structure = parmed.Structure() force_field = ForceField("openff_unconstrained-2.0.0.offxml") for lig in ligs_w_sdf: # Set up openff topology ligand_off_molecule = Molecule(lig[2]) ligand_pdbfile = PDBFile(lig[0]) ligand_off_topology = Topology.from_openmm( ligand_pdbfile.topology, unique_molecules=[ligand_off_molecule], ) # Parameterizing the ligand # Find ligand "piece", reparm, add to the new structure for piece in pieces: new_ligand_structure = None # TODO: Figure out how to know which piece is which if (ligand_off_molecule.n_atoms == len(piece[0].atoms)): if (ligand_off_molecule.n_bonds == len(piece[0].bonds)): if ([ atom.atomic_number for atom in ligand_off_molecule.atoms ] == [atom.element for atom in piece[0].atoms]): print('Found ligand piece', piece) try: # Since the method of matching the piece to ligand is imperfect, ligands that are isomers could mess things up. # So try any piece that matches and see if we get an error print('Reparameterizing ligand using SMIRNOFF') ligand_system = force_field.create_openmm_system( ligand_off_topology) new_ligand_structure = parmed.openmm.load_topology( ligand_off_topology.to_openmm(), ligand_system, xyz=piece[0].positions, ) # A quick check to make sure things were not messed up during param if check_discrepencies(new_ligand_structure, piece): # Add the newly parameterized ligand the complex structure reparmed_pieces.append(piece) new_ligand_structure *= len(piece[1]) complex_structure += parmed.amber.AmberParm.from_structure( new_ligand_structure) break except: pass # Stick all the pieces back together for piece in pieces: if (piece not in reparmed_pieces): curr_structure = parmed.Structure() curr_structure += piece[0] curr_structure *= len(piece[1]) complex_structure += parmed.amber.AmberParm.from_structure( curr_structure) # print("Unique atom names:",sorted(list({atom.atom_type.name for atom in complex_structure})),) # print("Number of unique atom types:", len({atom.atom_type for atom in complex_structure})) # print("Number of unique epsilons:", len({atom.epsilon for atom in complex_structure})) # print("Number of unique sigmas:", len({atom.sigma for atom in complex_structure})) # # Copy over the original coordinates and box vectors complex_structure.coordinates = orig_structure.coordinates complex_structure.box_vectors = orig_structure.box_vectors # Save the newly parameterized system complex_structure.save(base + ".prmtop", overwrite=True) complex_structure.save(base + ".inpcrd", overwrite=True)
def test_packmol_boxes(toolkit_file_path): # TODO: Isolate a set of systems here instead of using toolkit data # TODO: Fix nonbonded energy differences from openff.toolkit.utils import get_data_file_path pdb_file_path = get_data_file_path(toolkit_file_path) pdbfile = openmm.app.PDBFile(pdb_file_path) ethanol = Molecule.from_smiles("CCO") cyclohexane = Molecule.from_smiles("C1CCCCC1") omm_topology = pdbfile.topology off_topology = Topology.from_openmm( omm_topology, unique_molecules=[ethanol, cyclohexane] ) parsley = ForceField("openff_unconstrained-1.0.0.offxml") off_sys = parsley.create_openff_system(off_topology) off_sys.box = np.asarray( pdbfile.topology.getPeriodicBoxVectors() / omm_unit.nanometer, ) off_sys.positions = np.asarray( pdbfile.positions / omm_unit.nanometer, ) sys_from_toolkit = parsley.create_openmm_system(off_topology) omm_energies = get_openmm_energies(off_sys, hard_cutoff=True, electrostatics=False) reference = _get_openmm_energies( sys_from_toolkit, off_sys.box, off_sys.positions, hard_cutoff=True, electrostatics=False, ) omm_energies.compare( reference, custom_tolerances={ "Nonbonded": 2e-2 * omm_unit.kilojoule_per_mole, }, ) # custom_tolerances={"HarmonicBondForce": 1.0} # Compare GROMACS writer and OpenMM export gmx_energies = get_gromacs_energies(off_sys, electrostatics=False) omm_energies_rounded = get_openmm_energies( off_sys, round_positions=8, hard_cutoff=True, electrostatics=False, ) omm_energies_rounded.compare( other=gmx_energies, custom_tolerances={ "Angle": 1e-2 * omm_unit.kilojoule_per_mole, "Torsion": 1e-2 * omm_unit.kilojoule_per_mole, "Nonbonded": 3200 * omm_unit.kilojoule_per_mole, }, )