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 fragment(self, molecule: Molecule) -> List[FragmentData]: """ Fragment the molecule using the WBOFragmenter. Parameters: molecule: The openff molecule to be fragmented using the provided class settings Returns: A list of FragmentData schema which details how a parent molecule is related to a fragment and which bond we fragmented around. Raises: FragmenterError: If the molecule can not be fragmented. """ from fragmenter import fragment # make sure the molecule has at least one conformer as this can cause issues if molecule.n_conformers == 0: molecule.generate_conformers(n_conformers=1) # set up the fragmenter fragment_factory = fragment.WBOFragmenter( molecule=molecule.to_openeye(), verbose=False) fragments: List[FragmentData] = [] try: # fragment the molecule fragment_factory.fragment( threshold=self.wbo_threshold, keep_non_rotor_ring_substituents=self. keep_non_rotor_ring_substituents, ) # now we work out the relation between the fragment and the parent fragments_data = fragment_factory.to_torsiondrive_json() # now store the data for data in fragments_data.values(): off_frag = Molecule.from_mapped_smiles( data["identifiers"] ["canonical_isomeric_explicit_hydrogen_mapped_smiles"]) # get the fragment parent mapping frag_dihedral = data["dihedral"][0][1:3] # in some cases we get one fragment back which is the parent molecule # we should not work out a mapping if not molecule.is_isomorphic_with(off_frag): mapping = self._get_fragment_parent_mapping( fragment=off_frag, parent=molecule) # get the parent torsion parent_dihedral = tuple( [mapping[i] for i in frag_dihedral]) parent_molecule = molecule else: # reuse the current fragment data as dummy parent data mapping = dict((i, i) for i in range(molecule.n_atoms)) parent_dihedral = frag_dihedral parent_molecule = off_frag # this is the data we need so make the fragmnetdata frag_data = FragmentData( parent_molecule=parent_molecule, parent_torsion=parent_dihedral, fragment_molecule=off_frag, fragment_torsion=frag_dihedral, fragment_attributes=data["identifiers"], fragment_parent_mapping=mapping, ) fragments.append(frag_data) return fragments except RuntimeError: raise FragmenterError( f"The molecule {molecule} could not be fragmented so no fitting target was made." )