Source code for bbprep._internal.generators.scanner_by_selector
import itertools as it
from collections import abc
import numpy as np
import stk
import stko
from rdkit.Chem import AllChem
from bbprep._internal.ensemble.ensemble import Conformer, Ensemble
from bbprep.selectors import Selector
from .generator import Generator
[docs]
class SelectorDistanceScanner(Generator):
"""Generate conformers by scanning over one selector."""
def __init__(
self,
selector: Selector,
scanned_changes: abc.Iterable[float],
) -> None:
"""Initialise generator."""
self._selector = selector
self._scanned_changes = scanned_changes
[docs]
def generate_conformers(
self,
molecule: stk.BuildingBlock,
) -> Ensemble:
# Optimise the initial ligand structure.
molecule = stko.MMFF( # type:ignore[assignment]
ignore_inter_interactions=False
).optimize(
mol=molecule,
)
ensemble = Ensemble(base_molecule=molecule)
rdkit_molecule = molecule.to_rdkit_mol()
AllChem.SanitizeMol(rdkit_molecule) # type: ignore[attr-defined]
rdkit_properties = AllChem.MMFFGetMoleculeProperties( # type: ignore[attr-defined]
rdkit_molecule, mmffVariant="MMFF94s"
)
selected_ids = self._selector.select_atoms(molecule)
atom_positions = self._selector.get_atomic_positions(molecule)
initial_value = float(
np.linalg.norm(atom_positions[1] - atom_positions[0])
)
key = tuple(i for i in selected_ids)
matched_changes = {
key: [
round(initial_value + test, 2)
for test in self._scanned_changes
]
}
test_molecule = molecule.clone()
keys, values = zip(*matched_changes.items(), strict=False)
permutations_dicts = [
dict(zip(keys, v, strict=False)) for v in it.product(*values)
]
for cid, permutation in enumerate(permutations_dicts):
rdkit_molecule = test_molecule.to_rdkit_mol()
AllChem.SanitizeMol(rdkit_molecule) # type: ignore[attr-defined]
rdkit_properties = AllChem.MMFFGetMoleculeProperties( # type: ignore[attr-defined]
rdkit_molecule
)
ff = AllChem.MMFFGetMoleculeForceField( # type: ignore[attr-defined]
rdkit_molecule,
rdkit_properties,
)
for change in permutation:
actual_value = permutation[change]
# Add constraint.
ff.MMFFAddDistanceConstraint(
change[0],
change[1],
False, # noqa: FBT003
actual_value - 0.01,
actual_value + 0.01,
1.0e5,
)
ff.Minimize(maxIts=500)
pos_mat = rdkit_molecule.GetConformer(-1).GetPositions()
test_molecule = molecule.with_position_matrix(pos_mat)
ensemble.add_conformer(
conformer=Conformer(
molecule=test_molecule,
conformer_id=cid,
source="distscan",
permutation=permutation,
),
)
return ensemble