def _moe_ph4_string(pharmacophoric_points: List[PharmacophoricPoint]) -> str: """ Returns a string with the necessary info to create a MOE ph4 file. Parameters ---------- pharmacophoric_points : List of PharmacophoricPoint List with the pharmacophoric points. file_name : str Name of the file containing the pharmacophore. Returns ------ ph4_str : str The pharmacophore string. """ oph_to_moe = { "aromatic ring": "Aro", "hydrophobicity": "Hyd", "hb acceptor": "Acc", "hb donor": "Don", "positive charge": "Cat", "negative charge": "Ani", } now = datetime.datetime.now() ph4_str = "#moe:ph4que" + " " + str(now.year) + "." + str(now.month) + "\n" ph4_str += "#pharmacophore 5 tag t value *\n" ph4_str += "scheme t Unified matchsize i 0 title t s $\n" ph4_str += f"#feature {len(pharmacophoric_points)} expr tt color ix x r y r z r r r ebits ix gbits ix\n" excluded_spheres = [] for element in pharmacophoric_points: if element.feature_name == "excluded volume": excluded_spheres.append(element) continue feat_name = oph_to_moe[element.feature_name] center = puw.get_value(element.center, to_unit="angstroms") radius = str(puw.get_value(element.radius, to_unit="angstroms")) + " " x = str(center[0]) + " " y = str(center[1]) + " " z = str(center[2]) + " " ph4_str += feat_name + " df2f2 " + x + y + z + radius ph4_str += "0 300 " if excluded_spheres: ph4_str += "\n#volumesphere 90 x r y r z r r r\n" for excluded in excluded_spheres: center = puw.get_value(excluded.center, to_unit="angstroms") radius = str(puw.get_value(excluded.radius, to_unit="angstroms")) + " " x = str(center[0]) + " " y = str(center[1]) + " " z = str(center[2]) + " " ph4_str += x + y + z + radius ph4_str += "\n#endpharmacophore" return ph4_str
def _pharmer_dict( pharmacophoric_points: List[PharmacophoricPoint]) -> Dict[str, Any]: """ Returns a Dictionary with the necessary info to construct pharmer pharmacophore. Parameters ---------- pharmacophoric_points : list of openpharmacophore.PharmacophoricPoint Pharmacophore points that will be used to construct the dictionary Returns ------- pharmacophore_dict : dict Dictionary with the necessary info to construct a .json pharmer file. """ pharmer_element_name = { # dictionary to map openpharmacophore feature names to pharmer feature names "aromatic ring": "Aromatic", "hydrophobicity": "Hydrophobic", "hb acceptor": "HydrogenAcceptor", "hb donor": "HydrogenDonor", "included volume": "InclusionSphere", "excluded volume": "ExclusionSphere", "positive charge": "PositiveIon", "negative charge": "NegativeIon", } points = [] for element in pharmacophoric_points: point_dict = {} temp_center = puw.get_value(element.center, to_unit='angstroms') point_dict["name"] = pharmer_element_name[element.feature_name] point_dict["svector"] = {} if element.has_direction: point_dict["hasvec"] = True point_dict["svector"]["x"] = element.direction[0] point_dict["svector"]["y"] = element.direction[1] point_dict["svector"]["z"] = element.direction[2] else: point_dict["hasvec"] = False point_dict["svector"]["x"] = 1 point_dict["svector"]["y"] = 0 point_dict["svector"]["z"] = 0 point_dict["x"] = temp_center[0] point_dict["y"] = temp_center[1] point_dict["z"] = temp_center[2] point_dict["radius"] = puw.get_value(element.radius, to_unit='angstroms') point_dict["enabled"] = True point_dict["vector_on"] = 0 point_dict["minsize"] = "" point_dict["maxsize"] = "" point_dict["selected"] = False points.append(point_dict) pharmacophore_dict = {} pharmacophore_dict["points"] = points return pharmacophore_dict
def test_get_unique_pharmacophoric_points(sample_dynamic_pharmacophore): dynophore = sample_dynamic_pharmacophore dynophore._get_unique_pharmacophoric_points(avg_coordinates=True) assert len(dynophore.unique_pharmacophoric_points) == 5 # Acceptor acceptor = dynophore.unique_pharmacophoric_points[0] center = puw.get_value(acceptor.center, "angstroms") assert acceptor.feature_name == "hb acceptor 1" assert acceptor.atom_indices == {10} assert acceptor.count == 1 assert acceptor.frequency == 1 / 10 assert np.allclose(center, np.array([-0.5, 8.5, 2.4])) # Donor 1 donor_1 = dynophore.unique_pharmacophoric_points[1] center = puw.get_value(donor_1.center, "angstroms") assert donor_1.feature_name == "hb donor 1" assert donor_1.atom_indices == {1} assert donor_1.count == 10 assert donor_1.frequency == 1 assert len(donor_1.timesteps) == 10 assert np.allclose(center, np.array([1.45, 0.45, 3.45])) # Donor 2 donor_2 = dynophore.unique_pharmacophoric_points[3] center = puw.get_value(donor_2.center, "angstroms") assert donor_2.feature_name == "hb donor 2" assert donor_2.atom_indices == {8} assert donor_2.count == 6 assert donor_2.frequency == 6 / 10 assert np.allclose(center, np.array([-4.75, 1.75, -2.15])) # Hydrophobic hydrophobic = dynophore.unique_pharmacophoric_points[4] center = puw.get_value(hydrophobic.center, "angstroms") assert hydrophobic.feature_name == "hydrophobicity 1" assert hydrophobic.atom_indices == {12} assert hydrophobic.count == 1 assert hydrophobic.frequency == 1 / 10 assert np.allclose(center, np.array([-0.5, 8.5, 2.4])) # Aromatic aromatic = dynophore.unique_pharmacophoric_points[2] center = puw.get_value(aromatic.center, "angstroms") assert aromatic.feature_name == "aromatic ring 1" assert aromatic.atom_indices == {2, 3, 4, 5, 6, 7} assert aromatic.count == 4 assert aromatic.frequency == 4 / 10 assert len(aromatic.timesteps) == 4 assert np.allclose(center, np.array([-4.85, 1.65, -2.25]))
def test_from_pharmer(): points, molecular_system, ligand = from_pharmer( "./openpharmacophore/data/pharmacophores/pharmer/1M70.json", load_mol_sys=False) assert len(points) == 5 assert molecular_system is None assert ligand is None assert points[0].feature_name == "hb acceptor" assert points[0].has_direction assert puw.get_value(points[0].radius, "angstroms") == 0.9999999999999999 assert np.allclose(puw.get_value(points[0].center, "angstroms"), np.array([21.352, -14.531, 19.625])) assert np.allclose( points[0].direction, np.array( [-0.6405836470264256, 0.7029084735090229, -0.3091476492414897])) assert points[1].feature_name == "hb acceptor" assert points[1].has_direction assert puw.get_value(points[1].radius, "angstroms") == 0.9999999999999999 assert np.allclose(puw.get_value(points[1].center, "angstroms"), np.array([19.355, -18.32, 23.987])) assert np.allclose( points[1].direction, np.array([0.6859059711903811, 0.09092493673854565, 0.721987295292979])) assert points[2].feature_name == "hb donor" assert points[2].has_direction assert puw.get_value(points[2].radius, "angstroms") == 0.9999999999999999 assert np.allclose(puw.get_value(points[2].center, "angstroms"), np.array([20.977, -16.951, 18.746])) assert np.allclose( points[2].direction, np.array([0.71662539652105, -0.5202950182802607, -0.46447942367105033])) assert points[3].feature_name == "negative charge" assert not points[3].has_direction assert puw.get_value(points[3].radius, "angstroms") == 1.5 assert np.allclose(puw.get_value(points[3].center, "angstroms"), np.array([21.66899, -15.077667, 20.608334])) assert points[4].feature_name == "negative charge" assert not points[4].has_direction assert puw.get_value(points[4].radius, "angstroms") == 1.9999999999999998 assert np.allclose(puw.get_value(points[4].center, "angstroms"), np.array([19.985, -19.404402, 22.8422]))
def __repr__(self) -> str: center = np.around(puw.get_value(self.center, "angstroms"), 2) radius = np.around(puw.get_value(self.radius, "angstroms"), 2) x, y, z = center[0], center[1], center[2] if self.has_direction: direction = np.around(self.direction, 4) xd, yd, zd = direction[0], direction[1], direction[2] return (f"{self.__class__.__name__}(" f"feat_type={self.feature_name}; " f"center=({x}, {y}, {z}); radius: {radius}; " f"direction=({xd}, {yd}, {zd}))") else: return (f"{self.__class__.__name__}(" f"feat_type={self.feature_name}; " f"center=({x}, {y}, {z}); radius={radius})")
def _plip_hydrophobics(hydrophobics: List[PharmacophoricPoint], radius: float) -> List[PharmacophoricPoint]: """ Groups plip hydrophobic points that are to close into a single point. Parameters ---------- hydrophobics : list of openpharmacophore.PharmacophoricPoint List with the hydrophobic points. Returns ------- grouped_points : list of openpharmacophore.PharmacophoricPoint List with the grouped points. """ grouped_points = [] skip = [] for inx, hyd1 in enumerate(hydrophobics): if hyd1 in skip: continue centroid = hyd1.center indices = hyd1.atom_indices count = 0 hyd1_center = puw.get_value(hyd1.center, "angstroms") for hyd2 in hydrophobics[inx + 1:]: if hyd1 == hyd2: skip.append(hyd2) continue hyd2_center = puw.get_value(hyd2.center, "angstroms") distance = np.linalg.norm(hyd1_center - hyd2_center) if distance <= 2.5: centroid += hyd2.center indices.union(hyd2.atom_indices) count += 1 skip.append(hyd2) if count > 0: centroid /= (count + 1) grouped_points.append( PharmacophoricPoint(feat_type="hydrophobicity", center=centroid, radius=radius, direction=None, atom_indices=indices)) return grouped_points
def distance_bewteen_pharmacophoric_points(p1: PharmacophoricPoint, p2: PharmacophoricPoint) -> float: """ Compute the distance in angstroms between two pharmacophoric points. Parameters ---------- p1 : openpharmacophore.pharmacophoric_point A pharmacophoric point p2 : openpharmacophore.pharmacophoric_point A pharmacophoric point Returns ------- float The distance between the pharmacophoric points """ p1_center = puw.get_value(p1.center, "angstroms") p2_center = puw.get_value(p2.center, "angstroms") return np.linalg.norm(p1_center - p2_center)
def test_smarts_hydrophobics(): ligand = Chem.MolFromSmiles( "CC1=CC(=CC(=C1OCCCC2=CC(=NO2)C)C)C3=NOC(=N3)C(F)(F)F") ligand = generate_conformers(ligand, 1, random_seed=1) hydrophobics = SBP()._smarts_hydrophobics(ligand, 1.0) assert len(hydrophobics) == 3 hyd_1 = hydrophobics[0] hyd_2 = hydrophobics[2] hyd_2_center = puw.get_value(hyd_2.center, "angstroms") hyd_2_radius = puw.get_value(hyd_2.radius, "angstroms") assert hyd_1 == PharmacophoricPoint(feat_type="hydrophobicity", center=puw.quantity( (1.4268, -1.9841, -1.4186), "angstroms"), radius=puw.quantity(1.0, "angstroms")) assert np.allclose(np.around(hyd_2_center, 3), (-2.130, -0.542, -0.479), rtol=0, atol=1e-04) assert np.allclose(hyd_2_radius, hyd_2_radius, rtol=0, atol=1e-03)
def to_rdkit(self) -> Tuple[rdkitPharmacophore.Pharmacophore, List[float]]: """ Returns an rdkit pharmacophore with the pharmacophoric_points from the original pharmacophore. rdkit pharmacophores do not store the pharmacophoric_points radii, so they are returned as well. Returns ------- rdkit_pharmacophore : rdkit.Chem.Pharm3D.Pharmacophore The rdkit pharmacophore. radii : list of float List with the radius in angstroms of each pharmacophoric point. """ rdkit_element_name = { # dictionary to map openpharmacophore feature names to rdkit feature names "aromatic ring": "Aromatic", "hydrophobicity": "Hydrophobe", "hb acceptor": "Acceptor", "hb donor": "Donor", "positive charge": "PosIonizable", "negative charge": "NegIonizable", } points = [] radii = [] for element in self._pharmacophoric_points: feat_name = rdkit_element_name[element.feature_name] center = puw.get_value(element.center, to_unit="angstroms") center = Geometry.Point3D(center[0], center[1], center[2]) points.append(ChemicalFeatures.FreeChemicalFeature( feat_name, center )) radius = puw.get_value(element.radius, to_unit="angstroms") radii.append(radius) rdkit_pharmacophore = rdkitPharmacophore.Pharmacophore(points) return rdkit_pharmacophore, radii
def add_to_NGLView(self, view: nv.NGLWidget, palette: str = 'openpharmacophore') -> None: """Add the pharmacophore representation to a view (NGLWidget) from NGLView. Each pharmacophoric element is added to the NGLWidget as a new component. Parameters ---------- view : nglview.NGLWidget View as NGLView widget where the pharmacophore will be added. palette : str or dict Color palette name or dictionary. (Default: 'openpharmacophore') """ first_element_index = len(view._ngl_component_ids) for ii, element in enumerate(self._pharmacophoric_points): # Add Spheres center = puw.get_value(element.center, to_unit="angstroms").tolist() radius = puw.get_value(element.radius, to_unit="angstroms") feature_color = get_color_from_palette_for_feature(element.feature_name, color_palette=palette) label = f"{element.feature_name}_{ii}" view.shape.add_sphere(center, feature_color, radius, label) # Add vectors if element.has_direction: label = f"{element.feature_name}_vector" if element.feature_name == "hb acceptor": end_arrow = puw.get_value(element.center - 2 * radius * puw.quantity(element.direction, "angstroms"), to_unit='angstroms').tolist() view.shape.add_arrow(end_arrow, center, feature_color, 0.2, label) else: end_arrow = puw.get_value(element.center + 2 * radius * puw.quantity(element.direction, "angstroms"), to_unit='angstroms').tolist() view.shape.add_arrow(center, end_arrow, feature_color, 0.2, label) # Add opacity to spheres last_element_index = len(view._ngl_component_ids) for jj in range(first_element_index, last_element_index): view.update_representation(component=jj, opacity=0.8)
def __init__(self, feat_type: str, center: Quantity, radius: Quantity, direction: Optional[ArrayLike] = None, atom_indices: Optional[Sequence] = None) -> None: # Validate center validate_input_quantity(center, {"[L]": 1}, "center", shape=(3, )) # Validate radius validate_input_quantity(radius, {"[L]": 1}, "radius") if puw.get_value(radius, "angstroms") < 0: raise NegativeRadiusError("radius must be a positive quantity") # Validate direction if direction is not None: validate_input_array_like(direction, shape=(3, ), name="direction") # Validate feat type if not isinstance(feat_type, str): raise InvalidFeatureType("feat_type must be a string") # Validate atom_indices if atom_indices is not None: if not isinstance(atom_indices, (list, set, tuple)): raise OpenPharmacophoreTypeError( "atom_indices must be a list, set or tuple of int") if feat_type not in list(feature_to_char.keys()): raise InvalidFeatureType( f"{feat_type} is not a valid feature type. Valid feature names are {list(feature_to_char.keys())}" ) self.center = puw.standardize(center) self.radius = puw.standardize(radius) self.feature_name = feat_type self.short_name = feature_to_char[feat_type] if direction is not None: self.direction = direction / np.linalg.norm(direction) self.has_direction = True else: self.direction = None self.has_direction = False if atom_indices is not None: self.atom_indices = set(atom_indices) else: self.atom_indices = None self.pharmacophore_index = 0 self.count = 1
def test_from_ligandscout(): points = from_ligandscout( "./openpharmacophore/data/pharmacophores/ligandscout/pharmacophore.pml" ) assert len(points) == 4 neg_ion = points[2] assert neg_ion.feature_name == "negative charge" assert np.all( np.around(puw.get_value(neg_ion.center, "angstroms"), 1) == np.array( [-8.0, 10.0, -9.5])) assert puw.get_value(neg_ion.radius, "angstroms") == 1.5 donor = points[0] assert donor.feature_name == "hb donor" assert np.all( np.around(puw.get_value(donor.center, "angstroms"), 1) == np.array( [-8.0, 2.0, -10.0])) assert puw.get_value(donor.radius, "angstroms") == 1.5 dir_expected = np.array( [-5.690445899963379, 0.5822541117668152, -10.5515718460083]) dir_expected /= np.linalg.norm(dir_expected) assert np.all(np.around(donor.direction, 2) == np.around(dir_expected, 2)) ring = points[3] assert ring.feature_name == "aromatic ring" assert np.all( np.around(puw.get_value(ring.center, "angstroms"), 1) == np.array( [0.0, 6.5, -3.0])) assert puw.get_value(ring.radius, "angstroms") == 1.5 dir_expected = np.array( [-3.8126893043518066, 1.7578959465026855, 0.6093783378601074]) dir_expected /= np.linalg.norm(dir_expected) assert np.all(np.around(ring.direction, 2) == np.around(dir_expected, 2)) excluded_vol = points[1] assert excluded_vol.feature_name == "excluded volume" assert np.all( np.around(puw.get_value(excluded_vol.center, "angstroms"), 1) == np.array([5.5, 4.5, -2.0])) assert round(puw.get_value(excluded_vol.radius, "angstroms"), 1) == 1.0
def _ligandscout_xml_tree( pharmacophoric_points: List[PharmacophoricPoint]) -> ET.ElementTree: """ Get an xml element tree necesary to create a ligandscout pharmacophore. Parameters ---------- pharmacophoric_points : openpharmacophore.Pharmacophore Pharmacophore object that will be saved to a file. Returns ------- tree : xml.etree.ElementTree The element tree. """ Feature = namedtuple("Feature", ["name", "id"]) feature_mapper = { # dictionary to map openpharmacophore features to ligandscout "aromatic ring": Feature("AR", "ai_"), "hydrophobicity": Feature("H", "hi_"), "hb acceptor": Feature("HBA", "ha_"), "hb donor": Feature("HBD", "hd_"), "excluded volume": Feature("exclusion", "ev_"), "positive charge": Feature("PI", "pi_"), "negative charge": Feature("NI", "ni_"), } tree = ET.ElementTree("tree") document = ET.Element("pharmacophore") document.set("name", "pharmacophore.pml") document.set("pharmacophoreType", "LIGAND_SCOUT") for i, element in enumerate(pharmacophoric_points): try: feat_name = feature_mapper[element.feature_name].name except: # skip features not supported by ligandscout continue coords = puw.get_value(element.center, to_unit="angstroms") x = str(coords[0]) y = str(coords[1]) z = str(coords[2]) radius = str(puw.get_value(element.radius, to_unit="angstroms")) feat_id = feature_mapper[element.feature_name].id + str(i + 1) is_point = (feat_name == "PI" or feat_name == "NI" or feat_name == "H" or feat_name == "HBD" or feat_name == "HBA") is_vector = element.has_direction and (feat_name == "HBD" or feat_name == "HBA") if is_vector: direction = coords - element.direction dir_x = str(direction[0]) dir_y = str(direction[1]) dir_z = str(direction[2]) vector = ET.SubElement(document, "vector") # Set vector attributes vector.set("name", feat_name) vector.set("featureId", feat_id) vector.set("pointsToLigand", "false") vector.set("hasSyntheticProjectedPoint", "false") vector.set("optional", "false") vector.set("disabled", "false") vector.set("weight", "1.0") # Add origin tag origin = ET.SubElement(vector, "origin") origin.set("x3", x) origin.set("y3", y) origin.set("z3", z) origin.set("tolerance", radius) # Add target tag target = ET.SubElement(vector, "target") target.set("x3", dir_x) target.set("y3", dir_y) target.set("z3", dir_z) target.set("tolerance", radius) elif is_point: point = ET.SubElement(document, "point") # Set point attributes point.set("name", feat_name) point.set("featureId", feat_id) point.set("optional", "false") point.set("disabled", "false") point.set("weight", "1.0") # Add position tag position = ET.SubElement(point, "position") position.set("x3", x) position.set("y3", y) position.set("z3", z) position.set("tolerance", radius) elif feat_name == "AR": direction = element.direction dir_x = str(direction[0]) dir_y = str(direction[1]) dir_z = str(direction[2]) plane = ET.SubElement(document, "plane") # Set plane attributes plane.set("name", feat_name) plane.set("featureId", feat_id) plane.set("optional", "false") plane.set("disabled", "false") plane.set("weight", "1.0") # Add position tag position = ET.SubElement(plane, "position") position.set("x3", x) position.set("y3", y) position.set("z3", z) position.set("tolerance", radius) # Add normal tag normal = ET.SubElement(plane, "normal") normal.set("x3", dir_x) normal.set("y3", dir_y) normal.set("z3", dir_z) normal.set("tolerance", radius) elif feat_name == "exclusion": volume = ET.SubElement(document, "volume") # Set volume attributes volume.set("type", "exclusion") volume.set("featureId", feat_id) volume.set("optional", "false") volume.set("disabled", "false") volume.set("weight", "1.0") # Add position tag position = ET.SubElement(volume, "position") position.set("x3", x) position.set("y3", y) position.set("z3", z) position.set("tolerance", radius) tree._setroot(document) return tree, document
def test_from_moe(): file_name = "./openpharmacophore/data/pharmacophores/moe/gmp.ph4" points = from_moe(file_name) assert len(points) == 10 # HB Acceptor features should be first acceptor = points[0] assert acceptor.feature_name == "hb acceptor" assert np.all( np.around(puw.get_value(acceptor.center, "angstroms"), 2) == np.around( np.array([0.312, 3.0175, -2.44825]), 2)) assert np.around(puw.get_value(acceptor.radius, "angstroms"), 2) == 0.57 acceptor = points[1] assert acceptor.feature_name == "hb acceptor" assert np.all( np.around(puw.get_value(acceptor.center, "angstroms"), 2) == np.around( np.array([-1.95875, 2.536, -3.03625]), 2)) assert np.around(puw.get_value(acceptor.radius, "angstroms"), 2) == 0.62 acceptor_2 = points[2] assert acceptor_2.feature_name == "hb acceptor" assert np.all( np.around(puw.get_value(acceptor_2.center, "angstroms"), 2) == np.around( np.array([-0.755095833333333, 6.3286375, -3.96758333333333]), 2)) assert np.around(puw.get_value(acceptor_2.radius, "angstroms"), 2) == 1.25 # Donor points donor = points[3] assert donor.feature_name == "hb donor" assert np.all( np.around(puw.get_value(donor.center, "angstroms"), 2) == np.around( np.array([1.71, 1.43075, -1.4255]), 2)) assert np.around(puw.get_value(donor.radius, "angstroms"), 2) == 0.51 # Hydrophobic points hyd = points[4] assert hyd.feature_name == "hydrophobicity" assert np.all( np.around(puw.get_value(hyd.center, "angstroms"), 2) == np.around( np.array([2.7895, 2.4035, -1.40875]), 2)) assert np.around(puw.get_value(hyd.radius, "angstroms"), 2) == 0.55 hyd_2 = points[5] assert hyd_2.feature_name == "hydrophobicity" assert np.all( np.around(puw.get_value(hyd_2.center, "angstroms"), 2) == np.around( np.array([-1.54725, -2.979375, -0.961875]), 2)) assert np.around(puw.get_value(hyd_2.radius, "angstroms"), 2) == 0.74 # Aromatic Points aromatic_1 = points[6] assert aromatic_1.feature_name == "aromatic ring" assert np.all( np.around(puw.get_value(aromatic_1.center, "angstroms"), 2) == np. around(np.array([-0.748458333333333, 2.13108333333333, -2.490375]), 2)) assert np.around(puw.get_value(aromatic_1.radius, "angstroms"), 2) == 0.58 aromatic_2 = points[7] assert aromatic_2.feature_name == "aromatic ring" assert np.all( np.around(puw.get_value(aromatic_2.center, "angstroms"), 2) == np.around(np.array([-1.719625, -0.0273333333333334, -2.055625]), 2)) assert np.around(puw.get_value(aromatic_2.radius, "angstroms"), 2) == 0.6 aromatic_3 = points[8] assert aromatic_3.feature_name == "aromatic ring" assert np.all( np.around(puw.get_value(aromatic_3.center, "angstroms"), 2) == np.around( np.array([5.20029166666667, 1.25479166666667, -0.199041666666667]), 2)) assert np.around(puw.get_value(aromatic_3.radius, "angstroms"), 2) == 0.61 aromatic_4 = points[9] assert aromatic_4.feature_name == "aromatic ring" assert np.all( np.around(puw.get_value(aromatic_4.center, "angstroms"), 2) == np.around( np.array([-0.755095833333333, 6.3286375, -3.96758333333333]), 2)) assert np.around(puw.get_value(aromatic_4.radius, "angstroms"), 2) == 1.25
def add_to_NGLView(self, view, feature_name=None, color_palette='openpharmacophore', color=None, opacity=0.5): """ Adding the pharmacophoric point to an NGLview view. Parameters ---------- view : NGLView.widget NGLview object where the point representations is added. color_palette : str or dict, default='openpharmacophore' Color palette to show the point representation. color : str or list Color to show the point representation as HEX or RGB code. opacity : float The level of opacity. Must be a number between 0 and 1. Notes ----- This method does not return a new view but modifies the input object. """ if feature_name is None: try: feature_name = self.feature_name except: pass if color is None: if feature_name is not None: color = get_color_from_palette_for_feature( feature_name, color_palette) else: raise PointWithNoColorError(__documentation_web__) color = convert_color_code(color, to_form='rgb') radius = puw.get_value(self.radius, to_unit='angstroms') center = puw.get_value(self.center, to_unit='angstroms').tolist() try: n_components = len(view._ngl_component_ids) except: n_components = 0 view.shape.add_sphere(center, color, radius, feature_name) view.update_representation(component=n_components, repr_index=0, opacity=opacity) if self.has_direction: arrow_radius = 0.2 end_arrow = puw.get_value(self.center + self.radius * self.direction, to_unit='angstroms').tolist() view.shape.add_arrow(center, end_arrow, color, arrow_radius) view.update_representation(component=n_components + 1, repr_index=0, opacity=0.9)
def _pharmagist_file_info(pharmacophores): """ Get necessary info to create a pharmagist mol2 file to store pharmacophores. Parameters ---------- pharmacophores : list of openpharmacophore.Pharmacophore or openpharmacophore.Pharmacophore Pharmacophore or pharmacophores that will be saved Returns ------- doc : list of list List where each sublist contains a pharmacophore represented as a mol2 string. """ pharmagist_element_name = { "aromatic ring": "AR ", "hydrophobicity": "HYD", "hb acceptor": "ACC", "hb donor": "DON", "positive charge": "CAT", "negative charge": "ANI", } pharmagist_element_specs = { "aromatic ring": "AR ", "hydrophobicity": "HYD", "hb acceptor": "HB ", "hb donor": "HB ", "positive charge": "CHG", "negative charge": "CHG", } if not isinstance(pharmacophores, list): pharmacophores = [pharmacophores] doc = [] # list to store all pharmacophores for pharmacophore in pharmacophores: lines = ["@<TRIPOS>MOLECULE\n", "@<TRIPOS>ATOM\n"] # list to store all lines for a single pharmacophore line = "" index = 0 for element in pharmacophore: try: feat_name = pharmagist_element_name[element.feature_name] except KeyError: continue element_inx = str(index + 1) line += element_inx.rjust(7) line += " " + feat_name # Get point coordinates center = np.around(puw.get_value(element.center, to_unit="angstroms"), 4) # Pad coordinates with zeros to the right. Number of zeros depends on sign if center[0] < 0: x = str(center[0]).ljust(7,"0").rjust(16) else: x = str(center[0]).ljust(6,"0").rjust(16) if center[1] < 0: y = str(center[1]).ljust(7,"0").rjust(10) else: y = str(center[1]).ljust(6,"0").rjust(10) if center[2] < 0: z = str(center[2]).ljust(7,"0").rjust(10) else: z = str(center[2]).ljust(6,"0").rjust(10) line += x + y + z + " " line += pharmagist_element_specs[element.feature_name].rjust(5) line += str(index).rjust(5) line += pharmagist_element_specs[element.feature_name].rjust(6) line += "0.0000\n".rjust(12) lines.append(line) line = "" index += 1 lines.append("@<TRIPOS>BOND\n") for l in lines: doc.append(l) return doc
def _sb_pharmacophore_points( interactions, radius: float, ligand: Chem.Mol, hydrophobics: str = "smarts") -> List[PharmacophoricPoint]: """Static method to obtain a list of pharmacophoric points for the protein-ligand complex. Parameters ---------- interacions : plip.structure.preparation.PLInteraction object containing all interacion data for a single ligand and a protein. radius : float Radius of the spheres of the pharmacophoric points. ligand : rdkit.Chem.Mol The ligand in from the protein-ligand complex. hydrophobics : {"plip", "smarts"} Can be "plip" or "smarts". If the former is chosen the hydrophobic points will be retrieved from the interactions plip calculates. Else smarts patterns will be used. Returns ------- list of openpharmacophore.pharmacophoric_point.PharmacophoricPoint A list of pharmacophoric points. """ radius = puw.quantity(radius, "angstroms") # List with all interactions interactions = interactions.all_itypes # list of pharmacophoric_pharmacophoric_points points = [] # list oh hydrophobic points hydrophobic_points = [] for interaction in interactions: interaction_name = type(interaction).__name__ if interaction_name == "pistack": ligand_center = np.array(interaction.ligandring.center) protein_center = np.array(interaction.proteinring.center) direction = protein_center - ligand_center atom_indices = { atom.idx for atom in interaction.ligandring.atoms } aromatic = PharmacophoricPoint(feat_type="aromatic ring", center=puw.quantity( ligand_center, "angstroms"), radius=radius, direction=direction, atom_indices=atom_indices) points.append(aromatic) elif interaction_name == "hydroph_interaction": if hydrophobics != "plip": continue center = puw.quantity(interaction.ligatom.coords, "angstroms") atom_inx = {interaction.ligatom.idx} hydrophobic = PharmacophoricPoint(feat_type="hydrophobicity", center=center, radius=radius, direction=None, atom_indices=atom_inx) hydrophobic_points.append(hydrophobic) elif interaction_name == "saltbridge": if interaction.protispos: # The ligand has a negative charge center = puw.quantity(interaction.negative.center, "angstroms") atom_indices = { atom.idx for atom in interaction.negative.atoms } charge_sphere = PharmacophoricPoint( feat_type="negative charge", center=center, radius=radius, atom_indices=atom_indices) else: # The ligand has a positive charge center = puw.quantity(interaction.positive.center, "angstroms") atom_indices = { atom.idx for atom in interaction.positive.atoms } charge_sphere = PharmacophoricPoint( feat_type="positive charge", center=center, radius=radius, atom_indices=atom_indices) points.append(charge_sphere) elif interaction_name == "hbond": if interaction.protisdon: # The ligand has an acceptor atom ligand_acceptor_center = np.array(interaction.a.coords) ligand_acceptor_inx = {interaction.a.idx} protein_donor_center = np.array(interaction.d.coords) direction = ligand_acceptor_center - protein_donor_center acceptor = PharmacophoricPoint( feat_type="hb acceptor", center=puw.quantity(ligand_acceptor_center, "angstroms"), radius=radius, direction=direction, atom_indices=ligand_acceptor_inx) points.append(acceptor) else: # The ligand has a donor atom ligand_donor_center = np.array(interaction.d.coords) ligand_donor_inx = {interaction.d.idx} protein_acceptor_center = np.array(interaction.a.coords) direction = protein_acceptor_center - ligand_donor_center donor = PharmacophoricPoint(feat_type="hb donor", center=puw.quantity( ligand_donor_center, "angstroms"), radius=radius, direction=direction, atom_indices=ligand_donor_inx) points.append(donor) else: # TODO: Incorporate other features such as halogenbonds, waterbridges and metal complexes continue # Remove duplicate points points_filtered = [] for p in points: if p not in points_filtered: points_filtered.append(p) # Group hydrophobic interactions within a distance of 2.5 angstroms if len(hydrophobic_points) > 1: hydrophobic_points = StructuredBasedPharmacophore._plip_hydrophobics( hydrophobic_points, radius) if hydrophobics == "smarts": radius = puw.get_value(radius, "angstroms") hydrophobic_points = StructuredBasedPharmacophore._smarts_hydrophobics( ligand, radius) return points_filtered + hydrophobic_points
def test_PharmacophoricPoint(): # First we test that it raises the correct exceptions when the input arguments # are not valid radius = puw.quantity(1.0, "angstroms") center = puw.quantity([1.0, 1.0, 1.0], "angstroms") # Test that center is validated correctly with pytest.raises(exc.IsNotQuantityError, match="center is not a quantity"): PharmacophoricPoint(feat_type="hb donor", center=[1, 2, 3], radius=radius) with pytest.raises(exc.WrongDimensionalityError, match="center has dimensionality"): PharmacophoricPoint(feat_type="hb donor", center=puw.quantity([1.0, 1.0, 1.0], "seconds"), radius=radius) with pytest.raises(exc.BadShapeError, match="center has shape"): PharmacophoricPoint(feat_type="hb donor", center=puw.quantity([1.0, 1.0], "angstroms"), radius=radius) # Test for radius with pytest.raises(exc.IsNotQuantityError, match="radius is not a quantity"): PharmacophoricPoint(feat_type="hb donor", center=center, radius=1.0) with pytest.raises(exc.NegativeRadiusError, match="radius must be a positive"): PharmacophoricPoint(feat_type="hb donor", center=center, radius=puw.quantity(-1.0, "angstroms")) with pytest.raises(exc.WrongDimensionalityError, match="radius has dimensionality"): PharmacophoricPoint(feat_type="hb donor", center=center, radius=puw.quantity(1.0, "seconds")) # Test feature type with pytest.raises(exc.InvalidFeatureType, match="is not a valid feature type"): PharmacophoricPoint(feat_type="rubber duck", center=center, radius=radius) with pytest.raises(exc.OpenPharmacophoreTypeError, match="atom_indices must be"): PharmacophoricPoint(feat_type="hb donor", center=center, radius=radius, atom_indices=1) feat_name = "hb donor" atom_inxs = (3, 4, 5, 6) donor_1 = PharmacophoricPoint(feat_name, center, radius, None, atom_inxs) donor_2 = PharmacophoricPoint(feat_name, center, radius, direction=np.array([1.0, 1.0, 1.0]), atom_indices=atom_inxs) assert donor_1.has_direction == False assert np.allclose(puw.get_value(donor_1.center, "angstroms"), np.array([1.0, 1.0, 1.0])) assert np.allclose(puw.get_value(donor_1.radius, "angstroms"), 1.0) assert atom_inxs == (3, 4, 5, 6) feat_name = "aromatic ring" center = puw.quantity([1.5, -2.0, 3.2], "angstroms") radius = puw.quantity(1.5, "angstroms") direction = np.array([1.0, 1.0, 1.0]) atom_inxs = None ring = PharmacophoricPoint(feat_name, center, radius, direction, atom_inxs) assert ring.has_direction == True assert ring.atom_indices is None assert np.allclose(puw.get_value(ring.center, "angstroms"), np.array([1.5, -2.0, 3.2])) assert np.allclose(puw.get_value(ring.radius, "angstroms"), 1.5) assert np.allclose( ring.direction, np.array([[1.0, 1.0, 1.0]]) / np.linalg.norm(np.array([1.0, 1.0, 1.0]))) assert donor_1 != ring assert donor_2 != donor_1
def validate_input_quantity(quantity, dimensionality, name, dtype=(float, int, np.int64, np.float32), shape=None): """ Check whether a quantity is of the correct dimenstionality, shape and data type Parameters ---------- quantity : pyunitwizard.Quantity The quantity object that will be validated. dimensionality : dict A dictionary with the expected dimensionality of the quantity. name : str The name or label of the quantity that is being validated. dtype : tuple, default=(float, int, np.int64, np.float32) The type or types that the quantity is expected to have shape : tuple, optional The shape that the quantity is expected to have. If the quantity is a scalar this argument shouldn't be passed. Raises ------- IsNotQuantityError If it is not a pyunitwizar quantity. WrongDimensionalityError If the dimensionality doesn't match with the required one. QuantityDataTypeError If the datatype is not of the expected types. BadShapeError If a non scalar quantity has an incorrect shape. """ if not isinstance(quantity, pint.Quantity): raise IsNotQuantityError(f"{name} is not a quantity") quantity_dim = { dim: val for dim, val in puw.get_dimensionality(quantity).items() if val != 0 } if quantity_dim != dimensionality: raise WrongDimensionalityError( f"{name} has dimensionality {quantity_dim}. Expected {dimensionality}." ) quantity_val = puw.get_value(quantity) if isinstance(quantity_val, np.ndarray): quantity_val = quantity_val[0] if not isinstance(quantity_val, dtype): raise QuantityDataTypeError( f"{name} has data type {type(quantity_val)}. Expected {dtype}.") if shape and isinstance(puw.get_value(quantity), np.ndarray): quantity_shape = puw.get_value(quantity).shape if quantity_shape != shape: raise BadShapeError( f"{name} has shape {quantity_shape}. Expected Shape {shape}")