def test_force_field_custom_handler(mock_entry_point_plugins): """Tests a force field can make use of a custom parameter handler registered through the entrypoint plugin system. """ # Construct a simple FF which only uses the custom handler. force_field_contents = "\n".join([ "<?xml version='1.0' encoding='ASCII'?>", "<SMIRNOFF version='0.3' aromaticity_model='OEAroModel_MDL'>", " <CustomHandler version='0.3'></CustomHandler>", "</SMIRNOFF>", ]) # An exception should be raised when plugins aren't allowed. with pytest.raises(KeyError) as error_info: ForceField(force_field_contents) assert ( "Cannot find a registered parameter handler class for tag 'CustomHandler'" in error_info.value.args[0]) # Otherwise the FF should be created as expected. force_field = ForceField(force_field_contents, load_plugins=True) parameter_handler = force_field.get_parameter_handler("CustomHandler") assert parameter_handler is not None assert parameter_handler.__class__.__name__ == "CustomHandler"
def _build_system(mol, constrained): if constrained: parsley = ForceField("openff-1.0.0.offxml") else: parsley = ForceField("openff_unconstrained-1.0.0.offxml") mol = Molecule.from_file(get_data_file_path("molecules/" + mol), file_format="sdf") if type(mol) == Molecule: off_top = mol.to_topology() positions = mol.conformers[0] elif type(mol) == list: # methane_multiconformer case is a list of two mols off_top = Topology() for mol_i in mol: off_top.add_molecule(mol_i) positions = (np.vstack([mol[0].conformers[0], mol[1].conformers[0]]) * unit.angstrom) from openff.toolkit.utils.toolkits import ( AmberToolsToolkitWrapper, RDKitToolkitWrapper, ToolkitRegistry, ) toolkit_registry = ToolkitRegistry( toolkit_precedence=[RDKitToolkitWrapper, AmberToolsToolkitWrapper]) omm_sys = parsley.create_openmm_system(off_top, toolkit_registry=toolkit_registry) return omm_sys, positions, off_top
def _get_forcefield(cls, **kwargs): if "ff_path" in kwargs: try: return ForceField(kwargs['ff_path'], allow_cosmetic_attributes=True) except Exception as e: print( "Specified forcefield cannot be found. Fallback to default forcefield" ) return ForceField('test_forcefields/smirnoff99Frosst.offxml')
def test_residues(): pdb = app.PDBFile(get_test_file_path("ALA_GLY/ALA_GLY.pdb")) traj = md.load(get_test_file_path("ALA_GLY/ALA_GLY.pdb")) mol = Molecule(get_test_file_path("ALA_GLY/ALA_GLY.sdf"), file_format="sdf") top = OFFBioTop.from_openmm(pdb.topology, unique_molecules=[mol]) top.mdtop = traj.top assert top.n_topology_atoms == 29 assert top.mdtop.n_residues == 4 assert [r.name for r in top.mdtop.residues] == ["ACE", "ALA", "GLY", "NME"] ff = ForceField("openff-1.3.0.offxml") off_sys = Interchange.from_smirnoff(ff, top) # Assign positions and box vectors in order to run MM off_sys.positions = pdb.positions off_sys.box = [4.8, 4.8, 4.8] # Just ensure that a single-point energy can be obtained without error get_openmm_energies(off_sys) assert len(top.mdtop.select("resname ALA")) == 10 assert [*off_sys.topology.mdtop.residues][-1].n_atoms == 6
def get_parameter_handler_from_forcefield(self, parameter_handler_name, forcefield): """ It returns a parameter handler from the forcefield based on its name. Parameters ---------- parameter_handler_name : str The name of the parameter handler that is requested forcefield : an openforcefield.typing.engines.smirnoff.ForceField object The forcefield from which the parameter handler will be obtained Returns ------- parameter_handler : an openforcefield.typing.engines.smirnoff.parameters.ParameterHandler object The ParameterHandler that was requested """ from openff.toolkit.typing.engines.smirnoff import ForceField if isinstance(forcefield, str): forcefield = ForceField(forcefield) elif isinstance(forcefield, ForceField): pass else: raise Exception('Invalid forcefield type') return forcefield.get_parameter_handler(parameter_handler_name)
def _load_force_field() -> Union["ForceField", "ForceFieldSource"]: """Load in the force field to use in the benchmark.""" from openff.evaluator.forcefield import ForceFieldSource from openff.toolkit.typing.engines.smirnoff import ForceField if os.path.isfile("force-field.offxml") and os.path.isfile( "force-field.json"): raise RuntimeError( "Two valid force fields were found: force-field.offxml and " "force-field.json") elif os.path.isfile("force-field.offxml"): force_field = ForceField("force-field.offxml") elif os.path.isfile("force-field.json"): force_field = ForceFieldSource.from_json("force-field.json") else: raise RuntimeError( "No valid force field could be found. Either a SMIRNOFF force field " "(named force-field.offxml) or an OpenFF Evaluator force field source " "(named force-field.json) must be present in the current directory." ) return force_field
def test_amber_energy(): """Basic test to see if the amber energy driver is functional""" mol = Molecule.from_smiles("CCO") mol.generate_conformers(n_conformers=1) top = mol.to_topology() top.mdtop = md.Topology.from_openmm(top.to_openmm()) parsley = ForceField("openff_unconstrained-1.0.0.offxml") off_sys = Interchange.from_smirnoff(parsley, top) off_sys.box = [4, 4, 4] off_sys.positions = mol.conformers[0] omm_energies = get_gromacs_energies(off_sys, mdp="cutoff_hbonds") amb_energies = get_amber_energies(off_sys) omm_energies.compare( amb_energies, custom_tolerances={ "Bond": 3.6 * kj_mol, "Angle": 0.2 * kj_mol, "Torsion": 1.9 * kj_mol, "vdW": 1.5 * kj_mol, "Electrostatics": 36.5 * kj_mol, }, )
def test_openmm_roundtrip(): mol = Molecule.from_smiles("CCO") mol.generate_conformers(n_conformers=1) top = mol.to_topology() omm_top = top.to_openmm() parsley = ForceField("openff_unconstrained-1.0.0.offxml") off_sys = Interchange.from_smirnoff(parsley, top) off_sys.box = [4, 4, 4] off_sys.positions = mol.conformers[0].value_in_unit(simtk_unit.nanometer) omm_sys = off_sys.to_openmm() converted = from_openmm( topology=omm_top, system=omm_sys, ) converted.topology = off_sys.topology converted.box = off_sys.box converted.positions = off_sys.positions get_openmm_energies(off_sys).compare( get_openmm_energies(converted), custom_tolerances={"Nonbonded": 1.5 * simtk_unit.kilojoule_per_mole}, )
def test_coverage_filter_allowed(): """ Make sure the coverage filter removes the correct molecules. """ coverage_filter = workflow_components.CoverageFilter(allowed_ids={"b83"}) mols = get_stereoisomers() # we have to remove duplicated records # remove duplicates from the set molecule_container = get_container(mols) result = coverage_filter.apply(molecule_container.molecules, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) forcefield = ForceField("openff_unconstrained-1.0.0.offxml") # now see if any molecules do not have b83 parameters_by_id = {} for molecule in result.molecules: labels = forcefield.label_molecules(molecule.to_topology())[0] covered_types = set([ label.id for types in labels.values() for label in types.values() ]) # now store the smiles under the ids for parameter in covered_types: parameters_by_id.setdefault(parameter, []).append(molecule.to_smiles()) expected = parameters_by_id["b83"] for molecule in result.molecules: assert molecule.to_smiles() in expected assert "dihedrals" not in molecule.properties
def load_forcefield(forcefield="openff_unconstrained-1.2.0"): # get a forcefield try: ff = ForceField("%s.offxml" % forcefield) except: raise NotImplementedError return ff
def test_cutoff_electrostatics(): ion_ff = ForceField(get_test_file_path("ions.offxml")) ions = Topology.from_molecules([ Molecule.from_smiles("[#3]"), Molecule.from_smiles("[#17]"), ]) out = Interchange.from_smirnoff(ion_ff, ions) out.box = [4, 4, 4] * unit.nanometer gmx = [] lmp = [] for d in np.linspace(0.75, 0.95, 5): positions = np.zeros((2, 3)) * unit.nanometer positions[1, 0] = d * unit.nanometer out.positions = positions out["Electrostatics"].method = "cutoff" gmx.append( get_gromacs_energies(out, mdp="auto").energies["Electrostatics"].m) lmp.append( get_lammps_energies(out).energies["Electrostatics"].m_as( unit.kilojoule / unit.mol)) assert np.sum(np.sqrt(np.square(np.asarray(lmp) - np.asarray(gmx)))) < 1e-3
def test_combine_molecules_offxml_plugin_deepdiff(tmpdir, coumarin, rfree_data): """Make sure that systems made from molecules using the xml method match offxmls with plugins""" # we need to recalculate the Nonbonded terms using the fake Rfree coumarin_copy = coumarin.copy(deep=True) # apply symmetry to make sure systems match MBISCharges.apply_symmetrisation(coumarin_copy) with tmpdir.as_cwd(): # make the offxml using the plugin interface _combine_molecules_offxml( molecules=[coumarin_copy], parameters=elements, rfree_data=rfree_data, filename="openff.offxml", water_model="tip3p", ) offxml = ForceField( "openff.offxml", load_plugins=True, allow_cosmetic_attributes=True ) # check the plugin is being used vdw = offxml.get_parameter_handler("QUBEKitvdWTS") # make sure we have the parameterize tags assert len(vdw._cosmetic_attribs) == 1 assert len(vdw.parameters) == 28 alpha = rfree_data.pop("alpha") beta = rfree_data.pop("beta") lj = LennardJones612(free_parameters=rfree_data, alpha=alpha, beta=beta) # get new Rfree data lj.run(coumarin_copy) coumarin_copy.write_parameters("coumarin.xml") coumarin_ref_system = xmltodict.parse( XmlSerializer.serialize( app.ForceField("coumarin.xml").createSystem( topology=coumarin_copy.to_openmm_topology(), nonbondedCutoff=9 * unit.angstroms, removeCMMotion=False, ) ) ) coumarin_off_system = xmltodict.parse( XmlSerializer.serialize( offxml.create_openmm_system( Molecule.from_rdkit(coumarin_copy.to_rdkit()).to_topology() ) ) ) coumarin_diff = DeepDiff( coumarin_ref_system, coumarin_off_system, ignore_order=True, significant_digits=6, exclude_regex_paths="mass", ) assert len(coumarin_diff) == 1 for item in coumarin_diff["iterable_item_added"].values(): assert item["@k"] == "0"
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 test_torsion_smirnoff_coverage(public_client, monkeypatch): molecule: Molecule = Molecule.from_mapped_smiles( "[H:1][C:2]([H:7])([H:8])" "[C:3]([H:9])([H:10])" "[C:4]([H:11])([H:12])" "[C:5]([H:13])([H:14])[H:6]") dihedrals = [(0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5)] collection = TorsionDriveResultCollection( entries={ "http://localhost:442": [ TorsionDriveResult( record_id=ObjectId(str(i + 1)), cmiles=molecule.to_smiles(mapped=True), inchi_key=molecule.to_inchikey(), ) for i in range(len(dihedrals)) ] }) monkeypatch.setattr( TorsionDriveResultCollection, "to_records", lambda self: [( TorsionDriveRecord( id=entry.record_id, qc_spec=QCSpecification( driver=DriverEnum.gradient, method="scf", basis="sto-3g", program="psi4", ), optimization_spec=OptimizationSpecification( program="geometric", keywords={}), initial_molecule=[ObjectId(1)], status=RecordStatusEnum.complete, client=public_client, keywords=TDKeywords(dihedrals= [dihedrals[int(entry.record_id) - 1]], grid_spacing=[]), final_energy_dict={}, optimization_history={}, minimum_positions={}, ), molecule, ) for address, entries in self.entries.items() for entry in entries], ) coverage = collection.smirnoff_coverage(ForceField("openff-1.3.0.offxml"), driven_only=True) assert {*coverage} == {"Bonds", "Angles", "ProperTorsions"} assert {*coverage["Bonds"].values()} == {3} assert {*coverage["Angles"].values()} == {3} assert {*coverage["ProperTorsions"].values()} == {1, 3}
def test_get_water_fail(): """Make sure an error is rasied if we requested a non-supported water model""" with pytest.raises(NotImplementedError): _add_water_model( ForceField("openff_unconstrained-2.0.0.offxml"), water_model="tip4p", using_plugin=True, )
def hydrogen_chloride_force_field(library_charge: bool, charge_increment: bool) -> ForceField: """Returns a SMIRNOFF force field which is able to parameterize hydrogen chloride.""" # Create the FF force_field = ForceField() # Add a Vdw handler. vdw_handler = vdWHandler(version=0.3) vdw_handler.method = "cutoff" vdw_handler.cutoff = 6.0 * simtk_unit.angstrom vdw_handler.scale14 = 1.0 vdw_handler.add_parameter({ "smirks": "[#1:1]", "epsilon": 0.0 * simtk_unit.kilojoules_per_mole, "sigma": 1.0 * simtk_unit.angstrom, }) vdw_handler.add_parameter({ "smirks": "[#17:1]", "epsilon": 2.0 * simtk_unit.kilojoules_per_mole, "sigma": 2.0 * simtk_unit.angstrom, }) force_field.register_parameter_handler(vdw_handler) # Add an electrostatic, a library charge and a charge increment handler. electrostatics_handler = ElectrostaticsHandler(version=0.3) electrostatics_handler.cutoff = 6.0 * simtk_unit.angstrom electrostatics_handler.method = "PME" force_field.register_parameter_handler(electrostatics_handler) if library_charge: library_charge_handler = LibraryChargeHandler(version=0.3) library_charge_handler.add_parameter( parameter_kwargs={ "smirks": "[#1:1]", "charge1": 1.0 * simtk_unit.elementary_charge, }) library_charge_handler.add_parameter( parameter_kwargs={ "smirks": "[#17:1]", "charge1": -1.0 * simtk_unit.elementary_charge, }) force_field.register_parameter_handler(library_charge_handler) if charge_increment: charge_increment_handler = ChargeIncrementModelHandler(version=0.3) charge_increment_handler.add_parameter( parameter_kwargs={ "smirks": "[#1:1]-[#17:2]", "charge_increment1": -1.0 * simtk_unit.elementary_charge, "charge_increment2": 1.0 * simtk_unit.elementary_charge, }) force_field.register_parameter_handler(charge_increment_handler) return force_field
def test_combine_molecules_deepdiff_offxml( acetone, openff, coumarin, tmpdir, rfree_data ): """When not optimising anything make sure we can round trip openff parameters""" with tmpdir.as_cwd(): openff.run(acetone) acetone_ref_system = xmltodict.parse(open("serialised.xml").read()) openff.run(coumarin) coumarin_ref_system = xmltodict.parse(open("serialised.xml").read()) _combine_molecules_offxml( molecules=[acetone, coumarin], parameters=[], rfree_data=rfree_data, filename="combined.offxml", ) # load up new systems and compare combinded_ff = ForceField("combined.offxml") assert combinded_ff.author == f"QUBEKit_version_{qubekit.__version__}" acetone_combine_system = xmltodict.parse( XmlSerializer.serialize( combinded_ff.create_openmm_system( topology=Molecule.from_rdkit(acetone.to_rdkit()).to_topology() ) ) ) acetone_diff = DeepDiff( acetone_ref_system, acetone_combine_system, ignore_order=True, significant_digits=6, ) # should only be a difference in torsions with zero k values as openff drops these assert len(acetone_diff) == 1 for item in acetone_diff["iterable_item_added"].values(): assert item["@k"] == "0" coumarin_combine_system = xmltodict.parse( XmlSerializer.serialize( combinded_ff.create_openmm_system( topology=Molecule.from_rdkit(coumarin.to_rdkit()).to_topology() ) ) ) coumarin_diff = DeepDiff( coumarin_ref_system, coumarin_combine_system, ignore_order=True, significant_digits=6, ) assert len(coumarin_diff) == 1 for item in coumarin_diff["iterable_item_added"].values(): assert item["@k"] == "0"
def test_offxml_round_trip(tmpdir, openff, molecule): """ Test round tripping offxml parameters through qubekit """ with tmpdir.as_cwd(): mol = Ligand.from_file(get_data(molecule)) offmol = Molecule.from_file(get_data(molecule)) openff.run(mol) mol.to_offxml("test.offxml") # build another openmm system and serialise to compare with deepdiff offxml = ForceField("test.offxml") assert offxml.author == f"QUBEKit_version_{qubekit.__version__}" qubekit_system = offxml.create_openmm_system( topology=offmol.to_topology()) qubekit_xml = xmltodict.parse( openmm.XmlSerializer.serialize(qubekit_system)) with open("qubekit_xml", "w") as output: output.write(openmm.XmlSerializer.serialize(qubekit_system)) openff_system = xmltodict.parse(open("serialised.xml").read()) offxml_diff = DeepDiff( qubekit_xml, openff_system, ignore_order=True, significant_digits=6, ) # the only difference should be in torsions with a 0 barrier height which are excluded from an offxml for item in offxml_diff["iterable_item_removed"].values(): assert item["@k"] == "0" # load both systems and compute the energy qubekit_top = load_topology( mol.to_openmm_topology(), system=qubekit_system, xyz=mol.openmm_coordinates(), ) qubekit_energy = energy_decomposition_system(qubekit_top, qubekit_system, platform="Reference") ref_system = XmlSerializer.deserializeSystem( open("serialised.xml").read()) parm_top = load_topology(mol.to_openmm_topology(), system=ref_system, xyz=mol.openmm_coordinates()) ref_energy = energy_decomposition_system(parm_top, ref_system, platform="Reference") # compare the decomposed energies of the groups for force_group, energy in ref_energy: for qube_force, qube_e in qubekit_energy: if force_group == qube_force: assert energy == pytest.approx(qube_e, abs=2e-3)
def test_to_lammps_single_mols(mol, n_mols): """ Test that Interchange.to_openmm Interchange.to_lammps report sufficiently similar energies. TODO: Tighten tolerances TODO: Test periodic and non-periodic """ parsley = ForceField("openff_unconstrained-1.0.0.offxml") mol = Molecule.from_smiles(mol) mol.generate_conformers(n_conformers=1) top = OFFBioTop.from_molecules(n_mols * [mol]) top.mdtop = md.Topology.from_openmm(top.to_openmm()) mol.conformers[0] -= np.min(mol.conformers) * omm_unit.angstrom top.box_vectors = np.eye(3) * np.asarray([10, 10, 10]) * omm_unit.nanometer if n_mols == 1: positions = mol.conformers[0] elif n_mols == 2: positions = np.vstack( [mol.conformers[0], mol.conformers[0] + 3 * omm_unit.nanometer]) positions = positions * omm_unit.angstrom openff_sys = Interchange.from_smirnoff(parsley, top) openff_sys.positions = positions.value_in_unit(omm_unit.nanometer) openff_sys.box = top.box_vectors reference = get_openmm_energies( off_sys=openff_sys, round_positions=3, ) lmp_energies = get_lammps_energies( off_sys=openff_sys, round_positions=3, ) _write_lammps_input( off_sys=openff_sys, file_name="tmp.in", ) lmp_energies.compare( reference, custom_tolerances={ "Nonbonded": 100 * omm_unit.kilojoule_per_mole, "Electrostatics": 100 * omm_unit.kilojoule_per_mole, "vdW": 100 * omm_unit.kilojoule_per_mole, "Torsion": 3e-5 * omm_unit.kilojoule_per_mole, }, )
def test_scaled_de_energy(): """For a molecule with 1-4 interactions make sure the scaling is correctly applied. Note that only nonbonded parameters are non zero. """ ff = ForceField(load_plugins=True) ff.get_parameter_handler("Electrostatics") ff.get_parameter_handler( "ChargeIncrementModel", { "version": "0.3", "partial_charge_method": "formal_charge" }, ) vdw_handler = ff.get_parameter_handler("vdW") vdw_handler.add_parameter({ "smirks": "[*:1]", "epsilon": 0.0 * unit.kilojoule_per_mole, "sigma": 1.0 * unit.angstrom, }) double_exp = ff.get_parameter_handler("DoubleExponential") double_exp.alpha = 18.7 double_exp.beta = 3.3 double_exp.scale14 = 1 double_exp.add_parameter({ "smirks": "[#6X4:1]", "r_min": 3.816 * unit.angstrom, "epsilon": 0.1094 * unit.kilocalorie_per_mole, }) double_exp.add_parameter({ "smirks": "[#1:1]-[#6X4]", "r_min": 2.974 * unit.angstrom, "epsilon": 0.0157 * unit.kilocalorie_per_mole, }) ethane = Molecule.from_smiles("CC") ethane.generate_conformers(n_conformers=1) off_top = ethane.to_topology() omm_top = off_top.to_openmm() system_no_scale = ff.create_openmm_system(topology=off_top) energy_no_scale = evaluate_energy(system=system_no_scale, topology=omm_top, positions=ethane.conformers[0]) # now scale 1-4 by half double_exp.scale14 = 0.5 system_scaled = ff.create_openmm_system(topology=off_top) energy_scaled = evaluate_energy(system=system_scaled, topology=omm_top, positions=ethane.conformers[0]) assert double_exp.scale14 * energy_no_scale == pytest.approx(energy_scaled, abs=1e-6)
def test_sort_smirnoff_dict(): from collections import OrderedDict from openff.toolkit.typing.engines.smirnoff import ForceField from openff.toolkit.utils.utils import sort_smirnoff_dict forcefield = ForceField("test_forcefields/test_forcefield.offxml") smirnoff_dict = forcefield._to_smirnoff_data() # Ensure data is not created or destroyed # dict.__eq__ does not check order assert smirnoff_dict == OrderedDict( sort_smirnoff_dict(forcefield._to_smirnoff_data()))
def test_catch_bond_order_interpolation_bonds(self): from openff.toolkit.tests.test_forcefield import xml_ff_bo forcefield = ForceField( get_data_file_path("test_forcefields/test_forcefield.offxml"), xml_ff_bo, ) top = Molecule.from_smiles("CCO").to_topology() with pytest.raises(SMIRNOFFParameterAttributeNotImplementedError, match="length_bondorder"): Interchange.from_smirnoff(force_field=forcefield, topology=top)
def test_unsupported_mixing_rule(): molecules = [create_ethanol()] pdbfile = app.PDBFile(get_data_file_path("systems/test_systems/1_ethanol.pdb")) topology = OFFBioTop.from_openmm(pdbfile.topology, unique_molecules=molecules) topology.mdtop = md.Topology.from_openmm(topology.to_openmm()) forcefield = ForceField("test_forcefields/test_forcefield.offxml") openff_sys = Interchange.from_smirnoff(force_field=forcefield, topology=topology) openff_sys["vdW"].mixing_rule = "geometric" with pytest.raises(UnsupportedExportError, match="default NonbondedForce"): openff_sys.to_openmm(combine_nonbonded_forces=True)
def test_combine_cli_all_offxml( run_cli, tmpdir, acetone, coumarin, openff, rdkit_workflow, parameters, expected ): """ Test combining offxmls via the cli with different parameters. """ workflow = rdkit_workflow.copy(deep=True) workflow.non_bonded = get_protocol(protocol_name="5b") with tmpdir.as_cwd(): openff.run(coumarin) openff.run(acetone) # make some dummy dirs os.mkdir("QUBEKit_acetone") os.mkdir("QUBEKit_coumarin") result = workflow._build_initial_results(molecule=acetone) result.to_file(os.path.join("QUBEKit_acetone", "workflow_result.json")) result = workflow._build_initial_results(molecule=coumarin) result.to_file(os.path.join("QUBEKit_coumarin", "workflow_result.json")) output = run_cli.invoke( combine, args=["combined.offxml", "-offxml", *parameters] ) assert output.exit_code == 0 assert "2 molecules found, combining..." in output.output if expected != 0: ff = ForceField( "combined.offxml", allow_cosmetic_attributes=True, load_plugins=True ) # make sure we have used the plugin method vdw_handler = ff.get_parameter_handler("QUBEKitvdWTS") assert len(vdw_handler.parameters) == 32 assert "parameterize" in vdw_handler._cosmetic_attribs assert len(getattr(vdw_handler, "_parameterize").split(",")) == expected else: # we are using the normal format so make sure it complies ff = ForceField("combined.offxml") vdw_handler = ff.get_parameter_handler("vdW") assert len(vdw_handler.parameters) == 34
def test_catch_virtual_sites(self): from openff.toolkit.tests.test_forcefield import TestForceFieldVirtualSites forcefield = ForceField( get_data_file_path("test_forcefields/test_forcefield.offxml"), TestForceFieldVirtualSites. xml_ff_virtual_sites_monovalent_match_once, ) top = Molecule.from_smiles("CCO").to_topology() with pytest.raises(SMIRNOFFHandlersNotImplementedError, match="VirtualSites"): Interchange.from_smirnoff(force_field=forcefield, topology=top)
def checkTorsion(molList, ff_name): """ Take mollist and check if the molecules in a list match a specific torsion id Parameters ---------- molList : List of objects List of oemols with datatags generated in genData function Returns ------- molList : list of objects List of oemol objects that have a datatag "IDMatch" that contain the torsion id involved in the QCA torsion drive """ matches = [] count = 0 mols = [] for mol in molList: molecule = Molecule.from_mapped_smiles(mol.GetData("cmiles")) topology = Topology.from_molecules(molecule) # Let's label using the Parsley force field forcefield = ForceField(ff_name, allow_cosmetic_attributes=True) # Run the molecule labeling molecule_force_list = forcefield.label_molecules(topology) params = [] # Print out a formatted description of the torsion parameters applied to this molecule for mol_idx, mol_forces in enumerate(molecule_force_list): # print(f'Forces for molecule {mol_idx}') for force_tag, force_dict in mol_forces.items(): if force_tag == "ProperTorsions": for (atom_indices, parameter) in force_dict.items(): params.append(parameter.id) if atom_indices == mol.GetData("TDindices") or tuple( reversed(atom_indices) ) == mol.GetData("TDindices"): count += 1 mol.SetData("IDMatch", parameter.id) mols.append(mol) print( "Out of " + str(len(molList)) + " molecules, " + str(count) + " were processed with checkTorsion()" ) return mols
def test_residue_names_in_gro_file(self): """Test that residue names > 5 characters don't break .gro file output""" benzene = Molecule.from_file(get_test_file_path("benzene.sdf")) benzene.name = "supercalifragilisticexpialidocious" top = OFFBioTop.from_molecules(benzene) top.mdtop = md.Topology.from_openmm(top.to_openmm()) # Populate an entire interchange because ... force_field = ForceField("openff-1.0.0.offxml") out = Interchange.from_smirnoff(force_field, top) out.box = [4, 4, 4] out.positions = benzene.conformers[0] # ... the easiest way to check the validity of the files # is to see if GROMACS can run them get_gromacs_energies(out)
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_smirnoff_hack(): """Test basic behavior of smirnoff_hack.py, in particular the compatibility with Molecule API""" from openff.toolkit.topology import Molecule, Topology from openff.toolkit.typing.engines.smirnoff import ForceField from openff.toolkit.utils.toolkits import (AmberToolsToolkitWrapper, RDKitToolkitWrapper, ToolkitRegistry) from forcebalance import smirnoff_hack top = Topology.from_molecules(Molecule.from_smiles("CCO")) parsley = ForceField("openff-1.0.0.offxml") registry = ToolkitRegistry() registry.register_toolkit(RDKitToolkitWrapper) registry.register_toolkit(AmberToolsToolkitWrapper) parsley.create_openmm_system(top, toolkit_registry=registry)
def test_from_openmm_single_mols(mol, n_mols): """ Test that ForceField.create_openmm_system and Interchange.to_openmm produce objects with similar energies TODO: Tighten tolerances TODO: Test periodic and non-periodic """ parsley = ForceField(get_test_file_path("parsley.offxml")) mol = Molecule.from_smiles(mol) mol.generate_conformers(n_conformers=1) top = Topology.from_molecules(n_mols * [mol]) mol.conformers[0] -= np.min(mol.conformers) * simtk_unit.angstrom top.box_vectors = np.eye(3) * np.asarray([15, 15, 15]) * simtk_unit.nanometer if n_mols == 1: positions = mol.conformers[0] elif n_mols == 2: positions = np.vstack( [mol.conformers[0], mol.conformers[0] + 3 * simtk_unit.nanometer] ) positions = positions * simtk_unit.angstrom toolkit_system = parsley.create_openmm_system(top) native_system = Interchange.from_smirnoff( force_field=parsley, topology=top ).to_openmm() toolkit_energy = _get_openmm_energies( omm_sys=toolkit_system, box_vectors=toolkit_system.getDefaultPeriodicBoxVectors(), positions=positions, ) native_energy = _get_openmm_energies( omm_sys=native_system, box_vectors=native_system.getDefaultPeriodicBoxVectors(), positions=positions, ) toolkit_energy.compare(native_energy)