Source code for bbprep._internal.modifiers.distanced_functional_groups

import itertools as it

import numpy as np
import stk

from .modifier import Modifier


class DistancedFGs(Modifier):
    """Modify a building block."""

    def __init__(self) -> None:
        """Initialise the process."""
        self._reverse: bool

    def modify(  # type: ignore[override]
        self,
        building_block: stk.BuildingBlock,
        desired_functional_groups: int,
    ) -> stk.BuildingBlock:
        selected_fgs: list[stk.FunctionalGroup]
        if (
            building_block.get_num_functional_groups()
            == desired_functional_groups
        ):
            return building_block.clone()

        if (
            building_block.get_num_functional_groups()
            < desired_functional_groups
        ):
            msg = (
                f"{building_block} has less functional groups than"
                f" asked for ({desired_functional_groups})."
            )
            raise RuntimeError(msg)

        fg_centroids = [
            (
                fg,
                building_block.get_centroid(atom_ids=fg.get_placer_ids()),
            )
            for fg in building_block.get_functional_groups()
        ]
        fg_distances = sorted(
            [
                (i[0], j[0], np.linalg.norm(i[1] - j[1]))
                for i, j in it.combinations(fg_centroids, 2)
            ],
            key=lambda x: x[2],
            reverse=self._reverse,
        )

        selected_fgs = []
        for idx, _ in enumerate(fg_distances):
            if len(selected_fgs) == 0:
                selected_fgs.append(fg_distances[0][0])
                selected_fgs.append(fg_distances[0][1])
            elif fg_distances[idx][0] in set(selected_fgs):
                selected_fgs.append(fg_distances[idx][1])
            elif fg_distances[idx][1] in set(selected_fgs):
                selected_fgs.append(fg_distances[idx][0])

            if len(selected_fgs) == desired_functional_groups:
                break

        return building_block.with_functional_groups(selected_fgs)


[docs] class FurthestFGs(DistancedFGs): """Modify a building block.""" def __init__(self) -> None: """Initialise the process.""" self._reverse = True
[docs] class ClosestFGs(DistancedFGs): """Modify a building block.""" def __init__(self) -> None: """Initialise the process.""" self._reverse = False