Skip to content

td #

Run torsion drives.

Classes:

  • WFP

    Parameters for torsion drives that use wavefront propagation.

  • Simple

    Parameters for torsion drives that do a simple linear scan.

  • Params

    Parameters for the optimization process.

Functions:

  • bond_to_dihedral_idxs

    Select a dihedral to scan given a central bond to scan around.

  • torsion_drive

    Perform a torsion scan of a molecule around a given dihedral.

WFP dataclass #

Parameters for torsion drives that use wavefront propagation.

Attributes:

  • type (Literal['wavefront']) –

    The type of driver to use.

  • energy_decrease_thresh (float | None) –

    The minimum energy decrease required to continue scanning.

  • energy_upper_limit (float | None) –

    The maximum energy to scan up to.

type class-attribute instance-attribute #

type: Literal['wavefront'] = 'wavefront'

The type of driver to use.

energy_decrease_thresh class-attribute instance-attribute #

energy_decrease_thresh: float | None = None

The minimum energy decrease required to continue scanning.

energy_upper_limit class-attribute instance-attribute #

energy_upper_limit: float | None = None

The maximum energy to scan up to.

Simple dataclass #

Parameters for torsion drives that do a simple linear scan.

Attributes:

  • type (Literal['simple']) –

    The type of driver to use.

type class-attribute instance-attribute #

type: Literal['simple'] = 'simple'

The type of driver to use.

Params dataclass #

Parameters for the optimization process.

Attributes:

  • driver (WFP | Simple) –

    The algorithm to use for the torsion drive.

  • grid_spacing (int) –

    The spacing [deg] between each grid point.

  • opt (Params) –

    The parameters to use for the optimization at each angle.

driver class-attribute instance-attribute #

driver: WFP | Simple = field(default_factory=WFP)

The algorithm to use for the torsion drive.

grid_spacing class-attribute instance-attribute #

grid_spacing: int = 15

The spacing [deg] between each grid point.

opt class-attribute instance-attribute #

opt: Params = field(default_factory=Params)

The parameters to use for the optimization at each angle.

bond_to_dihedral_idxs #

bond_to_dihedral_idxs(
    mol: Molecule, bond: tuple[int, int]
) -> tuple[int, int, int, int]

Select a dihedral to scan given a central bond to scan around.

Parameters:

  • mol (Molecule) –

    The molecule to scan.

  • bond (tuple[int, int]) –

    The bond to scan around.

Returns:

  • tuple[int, int, int, int]

    The indices of the atoms involved in the dihedral.

Source code in tico/td.py
def bond_to_dihedral_idxs(
    mol: "openff.toolkit.Molecule", bond: tuple[int, int]
) -> tuple[int, int, int, int]:
    """Select a dihedral to scan given a central bond to scan around.

    Args:
        mol: The molecule to scan.
        bond: The bond to scan around.

    Returns:
        The indices of the atoms involved in the dihedral.
    """
    idx_2, idx_3 = bond

    atoms_1 = [
        atom
        for atom in mol.atoms[idx_2].bonded_atoms
        if atom.molecule_atom_index != idx_3
    ]
    idx_1 = max(atoms_1, key=lambda atom: atom.atomic_number).molecule_atom_index

    atoms_4 = [
        atom
        for atom in mol.atoms[idx_3].bonded_atoms
        if atom.molecule_atom_index != idx_2
    ]
    idx_4 = max(atoms_4, key=lambda atom: atom.atomic_number).molecule_atom_index

    return idx_1, idx_2, idx_3, idx_4

torsion_drive #

torsion_drive(
    coords: Tensor | list[Tensor],
    bond_idxs: Tensor,
    dihedral: tuple[int, int, int, int],
    energy_fn: EnergyFn,
    atomic_nums: Tensor,
    params: Params | None = None,
) -> dict[int, tuple[Tensor, Tensor]]

Perform a torsion scan of a molecule around a given dihedral.

Parameters:

  • coords (Tensor | list[Tensor]) –

    The initial cartesian coordinates [a0].

  • bond_idxs (Tensor) –

    The indices of the bonds in the molecule to scan.

  • dihedral (tuple[int, int, int, int]) –

    The dihedral to scan.

  • energy_fn (EnergyFn) –

    A function that computes the energy and gradients of the molecule. It should take the cartesian coordinates and return the energy [Eh] and gradients [Eh/a0] in atomic units.

  • atomic_nums (Tensor) –

    The atomic numbers of the atoms in the molecule.

  • params (Params | None, default: None ) –

    The parameters for the torsion drive.

Returns:

  • dict[int, tuple[Tensor, Tensor]]

    The optimized coordinates [a0] and energies [Eh] at each angle [deg].

Source code in tico/td.py
def torsion_drive(
    coords: torch.Tensor | list[torch.Tensor],
    bond_idxs: torch.Tensor,
    dihedral: tuple[int, int, int, int],
    energy_fn: tico.opt.EnergyFn,
    atomic_nums: torch.Tensor,
    params: Params | None = None,
) -> dict[int, tuple[torch.Tensor, torch.Tensor]]:
    """Perform a torsion scan of a molecule around a given dihedral.

    Args:
        coords: The initial cartesian coordinates [a0].
        bond_idxs: The indices of the bonds in the molecule to scan.
        dihedral: The dihedral to scan.
        energy_fn: A function that computes the energy and gradients of the molecule.
            It should take the cartesian coordinates and return the energy [Eh] and
            gradients [Eh/a0] in atomic units.
        atomic_nums: The atomic numbers of the atoms in the molecule.
        params: The parameters for the torsion drive.

    Returns:
        The optimized coordinates [a0] and energies [Eh] at each angle [deg].
    """
    params = params if params is not None else Params()

    if params.driver.type.lower() == "wavefront":
        results = _torsion_drive_wavefront(
            coords, bond_idxs, dihedral, energy_fn, atomic_nums, params
        )
    elif params.driver.type.lower() == "simple":
        results = _torsion_drive_simple(
            coords, bond_idxs, dihedral, energy_fn, atomic_nums, params
        )
    else:
        raise NotImplementedError(f"{params.driver.type} is not supported")

    samples = {}

    for angle in results:
        final_energies = torch.tensor([energy for _, energy in results[angle]])

        lowest_energy_idx = final_energies.argmin()
        lowest_energy_result = results[angle][lowest_energy_idx]

        coords = lowest_energy_result[0].reshape(-1, 3)
        energy = lowest_energy_result[1]

        samples[angle] = coords, energy

    return samples