def join_neighboring_mols(self, mol_A: Chem.Mol, mol_B: Chem.Mol): """ Joins two molecules by first calling _find_closest to find closest. That method does all the thinking. then by calling _join_atoms. :param mol_A: :param mol_B: :return: """ # get closets atoms combo, candidates = self._find_all_closest( mol_A, mol_B) # _find_all_closest is in communal anchor_A, anchor_B, distance = candidates[0] mol = self._join_atoms(combo, anchor_A, anchor_B, distance, linking=True) for anchor_A, anchor_B, distance in candidates[1:]: mol = self._join_atoms(combo, anchor_A, anchor_B, distance, linking=False) mol.SetProp('_Name', mol_A.GetProp('_Name') + '~' + mol_B.GetProp('_Name')) return mol
def merge_pair(self, scaffold: Chem.Mol, fragmentanda: Chem.Mol, mapping: Optional = None) -> Chem.Mol: """ To specify attachments use ``.merge``. To understand what is going on see ``.categorise`` :param scaffold: mol to be added to. :param fragmentanda: mol to be fragmented :param mapping: see ``get_positional_mapping``. Optional in _pre_fragment_pairs :return: """ done_already = [] if self._debug_draw: print('Scaffold') self.draw_nicely(scaffold) print('To be added') self.draw_nicely(fragmentanda) fp = self._pre_fragment_pairs(scaffold, fragmentanda, mapping) # confusingly these are hit indexed. for anchor_index, attachment_details in fp.items(): # anchor index is the fragment-to-added's internal atom that attaches if anchor_index in done_already: continue # fix rings. uniques = { atom.GetIdx() for atom in fragmentanda.GetAtoms() if 'overlapping' not in atom.GetProp('_Category') } team = self._recruit_team(fragmentanda, anchor_index, uniques) other_attachments = list((team & set(fp.keys())) - {anchor_index}) other_attachment_details = [] for other in other_attachments: other_attachment_details.append(fp[other]) done_already.append(other) scaffold = self._merge_part( scaffold, fragmentanda, anchor_index=anchor_index, attachment_details=attachment_details, other_attachments=other_attachments, other_attachment_details=other_attachment_details) name_A = scaffold.GetProp('_Name') name_B = fragmentanda.GetProp('_Name') scaffold.SetProp('_Name', f'{name_A}-{name_B}') if self._debug_draw: print('Merged', scaffold.GetProp('_Name')) self.draw_nicely(scaffold) return scaffold
def from_mol(cls, mol: Chem.Mol): field_names = fields(cls) kwargs = {} for field in field_names: field_name = cls._convert_field_to_sdf_field(field.name) val = mol.GetProp(field_name) val = field.type(val) kwargs[field.name] = val return RABFEResult(**kwargs)
def origin_from_mol(self, mol: Chem.Mol = None): """ these values are stored from Monster for scaffold, chimera and positioned_mol :param mol: Chem.Mol :return: stdev list for each atom """ if mol is None: mol = self.positioned_mol if mol.HasProp('_Origins'): return json.loads(mol.GetProp('_Origins')) origin = [] for atom in mol.GetAtoms(): if atom.HasProp('_Origin'): x = atom.GetProp('_Origin') if x == 'none': origin.append([]) else: origin.append(json.loads(x)) else: origin.append([]) return origin
def template_sorter(t: Chem.Mol) -> int: n_atoms = max([ len([k for k in m if k not in accounted_for]) for m in self.maps[t.GetProp('_Name')] ]) return n_atoms
def simulate_pair(epoch: int, blocker: Chem.Mol, mol: Chem.Mol): verify_rabfe_pair(mol, blocker) mol_name = mol.GetProp("_Name") # generate the core_idxs core_idxs = setup_relative_restraints_by_distance(mol, blocker) mol_coords = get_romol_conf(mol) # original coords num_complex_atoms = complex_coords.shape[0] num_solvent_atoms = solvent_coords.shape[0] # Use core_idxs to generate R, t = rmsd.get_optimal_rotation_and_translation( x1=complex_ref_x0[num_complex_atoms:][ core_idxs[:, 1]], # reference core atoms x2=mol_coords[core_idxs[:, 0]], # mol core atoms ) aligned_mol_coords = rmsd.apply_rotation_and_translation( mol_coords, R, t) ref_coords = complex_ref_x0[num_complex_atoms:] complex_host_coords = complex_ref_x0[:num_complex_atoms] complex_box0 = complex_ref_box0 solvent_host_coords = solvent_ref_x0[:num_solvent_atoms] solvent_box0 = solvent_ref_box0 # compute the free energy of swapping an interacting mol with a non-interacting reference mol complex_decouple_x0 = minimizer.minimize_host_4d( [mol, blocker_mol], complex_system, complex_host_coords, forcefield, complex_box0, [aligned_mol_coords, ref_coords], ) complex_decouple_x0 = np.concatenate( [complex_decouple_x0, aligned_mol_coords, ref_coords]) # compute the free energy of conversion in complex complex_conversion_x0 = minimizer.minimize_host_4d( [mol], complex_system, complex_host_coords, forcefield, complex_box0, [aligned_mol_coords], ) complex_conversion_x0 = np.concatenate( [complex_conversion_x0, aligned_mol_coords]) min_solvent_coords = minimizer.minimize_host_4d([mol], solvent_system, solvent_host_coords, forcefield, solvent_box0) solvent_x0 = np.concatenate([min_solvent_coords, mol_coords]) suffix = f"{mol_name}_{epoch}" seed = np.random.randint(np.iinfo(np.int32).max) # Order of these simulations should match the order in which predictions are computed to ensure # efficient use of parallelism. return { "solvent_conversion": binding_model_solvent_conversion.simulate_futures( ordered_params, mol, solvent_x0, solvent_box0, prefix="solvent_conversion_" + suffix, seed=seed, ), "solvent_decouple": binding_model_solvent_decouple.simulate_futures( ordered_params, mol, solvent_x0, solvent_box0, prefix="solvent_decouple_" + suffix, seed=seed, ), "complex_conversion": binding_model_complex_conversion.simulate_futures( ordered_params, mol, complex_conversion_x0, complex_box0, prefix="complex_conversion_" + suffix, seed=seed, ), "complex_decouple": binding_model_complex_decouple.simulate_futures( ordered_params, mol, blocker_mol, core_idxs, complex_decouple_x0, complex_box0, prefix="complex_decouple_" + suffix, seed=seed, ), "mol": mol, "blocker": blocker, "epoch": epoch, "seed": seed, }