def filter_function(data_row): n_components = data_row["N Components"] for index in range(n_components): smiles = data_row[f"Component {index + 1}"] molecule = Molecule.from_smiles(smiles, allow_undefined_stereo=True) if not all([x.element.symbol in allowed_elements for x in molecule.atoms]): return False return True
def smiles_to_svg(smiles: str, torsion_indices: (int, int), image_width: int = 200, image_height: int = 200) -> str: """Renders a 2D representation of a molecule based on its SMILES representation as an SVG string. Parameters ---------- smiles The SMILES pattern. torsion_indices The torsion indices for the molecule. image_width The width to make the final SVG. image_height The height to make the final SVG. Returns ------- The 2D SVG representation. """ # Parse the SMILES into an RDKit molecule smiles_parser = Chem.rdmolfiles.SmilesParserParams() smiles_parser.removeHs = False oe_conformed = False try: oe_molecule, status = smiles2oemol(smiles) openff_molecule = Molecule.from_openeye(oe_molecule) rdkit_molecule = openff_molecule.to_rdkit() oe_conformed = True except: rdkit_molecule = Chem.MolFromSmiles(smiles, smiles_parser) # Generate a set of 2D coordinates. Chem.rdDepictor.Compute2DCoords(rdkit_molecule) drawer = rdMolDraw2D.MolDraw2DSVG(image_width, image_height) torsion_bonds = [] if oe_conformed: for i in range(len(torsion_indices) - 1): if rdkit_molecule.GetBondBetweenAtoms(torsion_indices[i], torsion_indices[i+1]): torsion_bonds.append(rdkit_molecule.GetBondBetweenAtoms(torsion_indices[i], torsion_indices[i+1]).GetIdx()) rdMolDraw2D.PrepareAndDrawMolecule(drawer, rdkit_molecule, highlightBonds = torsion_bonds) drawer.FinishDrawing() svg_content = drawer.GetDrawingText() return svg_content
def setUp(self): # TODO: Harmonize with test_system_generator.py infrastructure # Read test molecules from openforcefield.topology import Molecule filename = get_data_filename("minidrugbank/MiniDrugBank-without-unspecified-stereochemistry.sdf") molecules = Molecule.from_file(filename, allow_undefined_stereo=True) # Filter molecules as appropriate self.molecules = self.filter_molecules(molecules) # Suppress DEBUG logging from various packages import logging for name in ['parmed', 'matplotlib']: logging.getLogger(name).setLevel(logging.WARNING)
def test_ff_mol2(test_ff, mol2_fnm): """ Test creating system with mol2 file """ from openforcefield.topology import Molecule as Off_Molecule from openforcefield.topology import Topology as Off_Topology try: off_molecule = Off_Molecule.from_file(mol2_fnm) off_topology = Off_Topology.from_molecules(off_molecule) test_ff.create_openmm_system(off_topology) molecule_labels = test_ff.label_molecules(off_topology)[0] except Exception as e: return False, str(e), None return True, '', molecule_labels
def test_load_one_mol_sdf_without_charge(self, toolkit): """Test loading one molecule from a .sdf file WITHOUT charges""" registry = make_registry(toolkit) ethanol = get_data_file_path("molecules/ethanol.sdf") assert Molecule.from_file(ethanol).partial_charges is None mols_out = generate_conformers( molecule=ethanol, forcefield="openff-1.0.0.offxml", registry=registry, ) assert len(mols_out) > 1 assert mols_out[0].partial_charges is not None
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) # 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 get_energies(self, resolution=30, get_thetas=False): """ It returns the energies of this energetic profile calculator. Parameters ---------- resolution : float The resolution, in degrees, that is applied when rotating the dihedral rotatable bond. Default is 30 degrees get_thetas : bool Whether to return thetas (dihedral angles) or not. Default is False Returns ------- dihedral_energies : a simtk.unit.Quantity object The array of energies represented with a simtk's Quantity object thetas : list[float] The array of thetas, only if requested in the corresponding function parameter """ mol_with_conformers = \ self.dihedral_benchmark.generate_dihedral_conformers( resolution) from openforcefield.topology import Molecule mol = Molecule.from_rdkit(mol_with_conformers) dihedral_energies = list() for i, conformer in enumerate(mol.conformers): energy = self._calc_energy(conformer) dihedral_energies.append(energy) if get_thetas: from rdkit.Chem import rdMolTransforms thetas = list() for conf in mol_with_conformers.GetConformers(): thetas.append( rdMolTransforms.GetDihedralDeg( conf, *self.dihedral_benchmark.atom_indexes)) return dihedral_energies, thetas return dihedral_energies
def _build_molecule_arrays(self): """Converts the input substance into a list of molecules and a list of counts for how many of each there should be as determined by the `max_molecules` input and the substances respective mole fractions. Returns ------- list of openforcefield.topology.Molecule The list of molecules. list of int The number of each molecule which should be added to the system. """ from openforcefield.topology import Molecule molecules = [] for component in self.substance.components: molecule = Molecule.from_smiles(component.smiles) molecules.append(molecule) # Determine how many molecules of each type will be present in the system. molecules_per_component = self.substance.get_molecules_per_component( self.max_molecules, count_exact_amount=self.count_exact_amount) number_of_molecules = [0] * self.substance.number_of_components for index, component in enumerate(self.substance.components): number_of_molecules[index] = molecules_per_component[ component.identifier] total_n_molecules = sum(number_of_molecules) if not self.count_exact_amount: n_exact_molecule = sum( amount.value for component in self.substance.components for amount in self.substance.amounts[component.identifier] if isinstance(amount, ExactAmount)) total_n_molecules -= n_exact_molecule if total_n_molecules > self.max_molecules: raise ValueError( f"The number of molecules to create ({total_n_molecules}) is " f"greater than the maximum number requested ({self.max_molecules})." ) return molecules, number_of_molecules, None
def test_OEMol_to_omm_ff(molecule=smiles_to_oemol('CC')): """ Generating openmm objects for simulation from an OEMol object Parameters ---------- molecule : openeye.oechem.OEMol Returns ------- system : openmm.System openmm system object positions : unit.quantity positions of the system topology : app.topology.Topology openmm compatible topology object """ import simtk.openmm.app as app import simtk.unit as unit from perses.utils.openeye import OEMol_to_omm_ff from simtk import openmm from openmmforcefields.generators import SystemGenerator from openforcefield.topology import Molecule #default arguments for SystemGenerators barostat = None forcefield_files = ['amber14/protein.ff14SB.xml', 'amber14/tip3p.xml'] forcefield_kwargs = { 'removeCMMotion': False, 'ewaldErrorTolerance': 1e-4, 'nonbondedMethod': app.NoCutoff, 'constraints': app.HBonds, 'hydrogenMass': 4 * unit.amus } small_molecule_forcefield = 'gaff-2.11' system_generator = SystemGenerator( forcefields=forcefield_files, barostat=barostat, forcefield_kwargs=forcefield_kwargs, small_molecule_forcefield=small_molecule_forcefield, molecules=[Molecule.from_openeye(molecule)], cache=None) system, positions, topology = OEMol_to_omm_ff(molecule, system_generator) assert (type(system) == type(openmm.System()) ), "An openmm.System has not been generated from OEMol_to_omm_ff()" return system, positions, topology
def test_fragmentation_apply(): """ Make sure that fragmentation is working. """ fragmenter = workflow_components.WBOFragmenter() assert fragmenter.is_available() # check that a molecule with no rotatable bonds fails if we dont want the parent back benzene = Molecule.from_file(get_data("benzene.sdf"), "sdf") result = fragmenter.apply([benzene, ], processors=1) assert result.n_molecules == 0 # now try ethanol ethanol = Molecule.from_file(get_data("methanol.sdf"), "sdf") fragmenter.include_parent = True result = fragmenter.apply([ethanol, ], processors=1) assert result.n_molecules == 1 # now try a molecule which should give fragments diphenhydramine = Molecule.from_smiles("O(CCN(C)C)C(c1ccccc1)c2ccccc2") fragmenter.include_parent = False result = fragmenter.apply([diphenhydramine, ], processors=1) assert result.n_molecules == 4 for molecule in result.molecules: assert "dihedrals" in molecule.properties
def generate_fitting_task( self, molecule: off.Molecule, fragment: bool, attributes: MoleculeAttributes, fragment_parent_mapping: Optional[Dict[int, int]] = None, dihedrals: Optional[List[Tuple[int, int, int, int]]] = None, ) -> Union[TorsionTask, OptimizationTask, HessianTask]: """ For the given collection workflow generate a task schema for the input molecule. """ if molecule.n_conformers < self.target_conformers: molecule.generate_conformers(n_conformers=self.target_conformers, clear_existing=False) # build a dict of the data data = dict( name=self.collection_workflow, attributes=attributes, provenance=self.provenance(), fragment=fragment, fragment_parent_mapping=fragment_parent_mapping, molecule=molecule, dihedrals=dihedrals, ) if self.collection_workflow == "torsion1d": task = TorsionTask(**data) elif self.collection_workflow == "optimization": task = OptimizationTask(**data) elif self.collection_workflow == "hessian": task = HessianTask(**data) else: raise NotImplementedError( f"The collection workflow {self.collection_workflow} is not supported." ) return task
def biphenyl_workflow(target) -> OptimizationSchema: """ Create a workflow schema which targets the rotatable bond in ethane. """ mol = Molecule.from_file(get_data("biphenyl.sdf"), "sdf") workflow = WorkflowFactory() # turn off bespoke terms we want fast fitting workflow.generate_bespoke_terms = False workflow.expand_torsion_terms = False fb = ForceBalanceOptimizer() target = target() fb.set_optimization_target(target=target) workflow.set_optimizer(optimizer=fb) schema = workflow.fitting_schema_from_molecules(molecules=mol) return schema.tasks[0]
def get_smirnoff_params(mol: oechem.OEMol) -> {"id": ["atom_indices"]}: """For the given molecule, finds the SMIRNOFF params and their atom indices""" off_mol = Molecule.from_openeye(mol, allow_undefined_stereo=True) try: topology = Topology.from_molecules(off_mol) except Exception as e: return {} molecule_force_list = utilize_params_util.SMIRNOFF.label_molecules(topology) params = defaultdict(list) for force_tag, force_dict in molecule_force_list[0].items(): for (atom_index, parameter) in force_dict.items(): params[parameter.id].append(atom_index) return params
def label_molecule(self, molecule: off.Molecule) -> Dict[str, str]: """ Type the molecule with the forcefield and return a molecule parameter dictionary. Parameters ---------- molecule: off.Molecule The openforcefield.topology.Molecule that should be labeled by the forcefield. Returns ------- Dict[str, str] A dictionary of each parameter assigned to molecule organised by parameter handler type. """ return self.forcefield.label_molecules(molecule.to_topology())[0]
def test_to_file_fileformat_invalid(self): """ Checks for invalid file format """ from openforcefield.tests.test_forcefield import create_ethanol from openforcefield.topology import Molecule, Topology topology = Topology() mol = Molecule.from_pdb_and_smiles( get_data_file_path("systems/test_systems/1_ethanol.pdb"), "CCO") topology.add_molecule(mol) positions = mol.conformers[0] fname = "ethanol_file.pdb" with pytest.raises(NotImplementedError): topology.to_file(fname, positions, file_format="AbC")
def filter_function(data_row): n_components = data_row["N Components"] for index in range(n_components): smiles = data_row[f"Component {index + 1}"] molecule = Molecule.from_smiles(smiles, allow_undefined_stereo=True) if np.isclose(sum([atom.formal_charge for atom in molecule.atoms]), 0.0): continue return False return True
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 _get_rdkit_mcs_mapping(fragment: Molecule, parent: Molecule) -> Dict[int, int]: """ Use rdkit MCS function to find the maximum mapping between the fragment and parent molecule. """ from rdkit import Chem from rdkit.Chem import rdFMCS parent_rdkit = parent.to_rdkit() fragment_rdkit = fragment.to_rdkit() mcs = rdFMCS.FindMCS( [parent_rdkit, fragment_rdkit], atomCompare=rdFMCS.AtomCompare.CompareElements, bondCompare=rdFMCS.BondCompare.CompareAny, ringMatchesRingOnly=True, completeRingsOnly=True, ) # make a new molecule from the mcs match_mol = Chem.MolFromSmarts(mcs.smartsString) # get the mcs parent/fragment mapping matches_parent = parent_rdkit.GetSubstructMatch(match_mol) matches_fragment = fragment_rdkit.GetSubstructMatch(match_mol) mapping = dict(zip(matches_fragment, matches_parent)) return mapping
def gen_tid_molecules_list(molecule_attributes, molecules_list_dict, forcefield): # gen dictionary with keys, including all tids in the input forcefield ff_torsion_param_list = forcefield.get_parameter_handler( 'ProperTorsions').parameters tid_molecules_list = {} for torsion_param in ff_torsion_param_list: tid_molecules_list[torsion_param.id] = [] for idx, (mol_index, mol_attr) in enumerate(molecule_attributes.items()): mapped_smiles = mol_attr[ 'canonical_isomeric_explicit_hydrogen_mapped_smiles'] qcjson_mol = molecules_list_dict[mol_index][0] oemol = cmiles.utils.load_molecule(qcjson_mol) off_mol = Off_Molecule.from_openeye(oemol, allow_undefined_stereo=True) torsions_coverage, center_tids = smirnoff_analysis_torsions( forcefield, off_mol) filtered_torsions_coverage = filter_torsions_coverage( torsions_coverage, oemol) for tid, indices_list in filtered_torsions_coverage.items(): for indices in indices_list: covered_tids = [] i, j, k, l = indices tids = center_tids[tuple(sorted([j, k]))] for i in tids: if i not in covered_tids: covered_tids.append(i) tid_molecules_list[tid].append({ 'mol_index': mol_index, 'indices': indices, 'covered_tids': covered_tids }) print("\n## Torsion parameter: matched molecules ##\n" + '-' * 90) print( f"{'idx':<7} {'ID':7s} {'SMIRKS Pattern':70s} {'Number of molecules matched'}" ) for idx, (tid, molecules_list) in enumerate(tid_molecules_list.items()): torsion_param = get_torsion_definition(ff_torsion_param_list, tid) print( f'{idx:<7} {torsion_param.id:7s} {torsion_param.smirks:70s} {len(molecules_list)}' ) print('-' * 90) return tid_molecules_list
def select_torsions(molecules_list_dict, molecule_attributes, forcefield, target_coverage=3): torsions_dict = {} smirks_torsions_counter = Counter() i_mol = 0 for mol_index, mol_attr in molecule_attributes.items(): print(f'{i_mol:<7d}: {mol_index}') i_mol += 1 mapped_smiles = mol_attr[ 'canonical_isomeric_explicit_hydrogen_mapped_smiles'] # round trip from QCFractal molecule to OpenEye molecule then to Off Molecule # this is needed for now to ensure atom indeices are consistent qcjson_mol = molecules_list_dict[mol_index][0] oemol = cmiles.utils.load_molecule(qcjson_mol) off_mol = Off_Molecule.from_openeye(oemol, allow_undefined_stereo=True) torsions_coverage = smirnoff_analyze_torsions(forcefield, off_mol) for smirks, torsion_idx_list in torsions_coverage.items(): for atom_indices in torsion_idx_list: if smirks_torsions_counter[smirks] < target_coverage: smirks_torsions_counter[smirks] += 1 canonical_torsion_index = cmiles.utils.to_canonical_label( mapped_smiles, atom_indices) torsions_dict[canonical_torsion_index] = { 'initial_molecules': molecules_list_dict[mol_index], 'atom_indices': [atom_indices], 'attributes': mol_attr, } print( f" - torsion {atom_indices} added for smirks {smirks}" ) else: print( f" - torsion {atom_indices} skipped because {smirks} have {smirks_torsions_counter[smirks]} already" ) print("\n## Selected Torsion Coverage ##\n" + '-' * 90) ff_torsion_param_list = forcefield.get_parameter_handler( 'ProperTorsions').parameters n_covered = 0 for param in ff_torsion_param_list: count = smirks_torsions_counter[param.smirks] print(f"{param.smirks:80s} : {count:7d}") if count > 0: n_covered += 1 print('-' * 90) print(f'{n_covered} / {len(ff_torsion_param_list)} torsion SMIRKs covered') return torsions_dict
def smirnoff_analyze_parameter_coverage(forcefield, tgt_opts): printcool("SMIRNOFF Parameter Coverage Analysis") assert hasattr(forcefield, 'offxml'), "Only SMIRNOFF Force Field is supported" parameter_assignment_data = defaultdict(list) parameter_counter = Counter() # The openforcefield.typing.engines.smirnoff.ForceField object should now be contained in forcebalance.forcefield.FF ff = forcefield.openff_forcefield # analyze each target for tgt_option in tgt_opts: target_path = os.path.join('targets', tgt_option['name']) # aggregate mol2 file paths from all targets mol2_paths = [] if tgt_option['type'] == 'OPTGEOTARGET_SMIRNOFF': # parse optgeo_options_txt and get the names of the mol2 files optgeo_options_txt = os.path.join(target_path, tgt_option['optgeo_options_txt']) sys_opts = forcebalance.opt_geo_target.OptGeoTarget.parse_optgeo_options(optgeo_options_txt) mol2_paths = [os.path.join(target_path,fnm) for sysopt in sys_opts.values() for fnm in sysopt['mol2']] elif tgt_option['type'].endswith('_SMIRNOFF'): mol2_paths = [os.path.join(target_path,fnm) for fnm in tgt_option['mol2']] # analyze SMIRKs terms for mol_fnm in mol2_paths: # we work with one file at a time to avoid the topology sliently combine "same" molecules openff_mol = OffMolecule.from_file(mol_fnm) off_topology = OffTopology.from_molecules([openff_mol]) molecule_force_list = ff.label_molecules(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 atom_indices, parameter in force_dict.items(): param_dict = {'id': parameter.id, 'smirks': parameter.smirks, 'type':force_tag, 'atoms': list(atom_indices),} parameter_assignment_data[mol_fnm].append(param_dict) parameter_counter[parameter.smirks] += 1 # write out parameter assignment data out_json_path = os.path.join(forcefield.root, 'smirnoff_parameter_assignments.json') with open(out_json_path, 'w') as jsonfile: json.dump(parameter_assignment_data, jsonfile, indent=2) logger.info("Force field assignment data written to %s\n" % out_json_path) # print parameter coverages logger.info("%4s %-100s %10s\n" % ("idx", "Parameter", "Count")) logger.info("-"*118 + '\n') n_covered = 0 for i,p in enumerate(forcefield.plist): smirks = p.split('/')[-1] logger.info('%4i %-100s : %10d\n' % (i, p, parameter_counter[smirks])) if parameter_counter[smirks] > 0: n_covered += 1 logger.info("SNIRNOFF Parameter Coverage Analysis result: %d/%d parameters are covered.\n" % (n_covered, len(forcefield.plist))) logger.info("-"*118 + '\n')
def test_torsiondrive_index(): """ Test generating an index using torsiondrive, this should tag the atoms in the torsion. """ mol = Molecule.from_file(get_data("methanol.sdf")) mol.properties["atom_map"] = {4: 0, 0: 1, 1: 2, 5: 3} factory = TorsiondriveDatasetFactory() index = factory.create_index(mol) tags = ["[C:2]", "[H:1]", "[O:3]", "[H:4]"] for tag in tags: assert tag in index
def filter_function(physical_property): substance = physical_property.substance for component in substance.components: molecule = Molecule.from_smiles(component.smiles, allow_undefined_stereo=True) if not all([ x.element.symbol in allowed_elements for x in molecule.atoms ]): return False return True
def from_directory( cls: Type[T], directory: str, name: str, options: Dict[str, Any] ) -> T: from openforcefield.topology import Molecule input_molecule_path = os.path.join(directory, options["mol2"]) input_molecule = Molecule.from_file( input_molecule_path, allow_undefined_stereo=True ) return cls( name=name, molecule=input_molecule.to_smiles(explicit_hydrogens=False), options=options, )
def test_rmsd_filter(): """ Test the RMSD conformer filter method. """ import copy from simtk import unit rmsd_filter = workflow_components.RMSDCutoffConformerFilter(cutoff=1) mol = Molecule.from_smiles("CCCC") # make a lot of conformers for the molecule mol.generate_conformers(n_conformers=1000, rms_cutoff=0.5 * unit.angstrom, toolkit_registry=RDKitToolkitWrapper()) ref_mol = copy.deepcopy(mol) result = rmsd_filter.apply([mol, ], processors=1) # now make sure the number of conformers is different assert result.molecules[0].n_conformers != ref_mol.n_conformers
def test_bespoke_target_torsion_smirks(): """ Generate bespoke torsion smirks only for the target torsions and make sure the intended atoms are covered. """ gen = SmirksGenerator() mol = Molecule.from_file(get_data("OCCO.sdf")) torsion_smirks = gen._get_bespoke_torsion_smirks(molecule=mol, central_bonds=[(1, 2)]) # there should be 3 unique smirks for this molecule # H-C-C-H, H-C-C-O, O-C-C-O assert len(torsion_smirks) == 3 for smirk in torsion_smirks: atoms = condense_matches(mol.chemical_environment_matches( smirk.smirks)) assert compare_matches(atoms, smirk.atoms) is True
def test_packmol_paracetamol(): from openforcefield.topology import Molecule # Test something a bit more tricky than water molecules = [Molecule.from_smiles("CC(=O)NC1=CC=C(C=C1)O")] trajectory, _ = packmol.pack_box(molecules, [1], box_size=([20] * 3) * unit.angstrom) assert trajectory is not None assert trajectory.n_chains == 1 assert trajectory.n_residues == 1 assert trajectory.n_atoms == 20 assert trajectory.topology.n_bonds == 20
def mol2_to_smiles(file_path): """Loads a receptor from a mol2 file. Parameters ---------- file_path: str The file path to the mol2 file. Returns ------- str The smiles descriptor of the loaded receptor molecule """ receptor_molecule = Molecule.from_file(file_path, 'MOL2') return receptor_molecule.to_smiles()
def test_generate_smirks(bespoke_smirks): """ Test the main worker method to gather bespoke and non bespoke terms. """ gen = SmirksGenerator() gen.target_smirks = [ SmirksType.Vdw, ] gen.generate_bespoke_terms = bespoke_smirks mol = Molecule.from_smiles("CC") smirks_list = gen.generate_smirks(molecule=mol) # we only request one parameter type types = set([smirk.type for smirk in smirks_list]) assert len(types) == 1
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