Source code for psiresp.conformer


from typing import Optional, List
from typing_extensions import Literal
from pydantic import Field

import numpy as np
import qcelemental as qcel

from . import base
from .orientation import Orientation
from .moleculebase import BaseMolecule
from .utils import require_package


[docs]class Conformer(BaseMolecule): """Class to manage one conformer of a molecule. It must hold at least one orientation. """ orientations: List[Orientation] = [] is_optimized: bool = False _qc_id: Optional[int] = None @property def n_orientations(self): return len(self.orientations)
[docs] def add_orientation_with_coordinates(self, coordinates, units="angstrom"): qcmol = self.qcmol_with_coordinates(coordinates, units=units) self.orientations.append(Orientation(qcmol=qcmol))
[docs] def set_optimized_geometry(self, coordinates, units="bohr"): cf = qcel.constants.conversion_factor(units, "bohr") dct = self.qcmol.dict() dct["geometry"] = coordinates * cf self.qcmol = type(self.qcmol)(**dct) self.is_optimized = True
[docs]class ConformerGenerationOptions(base.Model): """Options for generating conformers""" n_conformer_pool: int = Field( default=4000, description="Number of initial conformers to generate" ) n_max_conformers: int = Field( default=0, description="Maximum number of conformers to keep" ) rms_tolerance: float = Field( default=0.05, description="RMS tolerance for pruning conformers" ) energy_window: float = Field( default=30, description=("Energy window (kcal/mol) within which to keep conformers. " "This is the range from the lowest energetic conformer"), ) keep_original_conformer: bool = Field( default=True, description="Whether to keep the original conformer in the molecule" ) minimize: Optional[Literal["uff", "mmff94"]] = Field( default=None, description="Whether to minimize geometries with the specified force field" )
[docs] def generate_coordinates(self, qcmol: qcel.models.Molecule) -> np.ndarray: """Generate conformer coordinates in angstrom""" original = np.array([qcmol.geometry]) original *= qcel.constants.conversion_factor("bohr", "angstrom") if not self.n_max_conformers: return original else: require_package("rdkit") from .rdutils import generate_diverse_conformer_coordinates rdkwargs = self.dict() keep = rdkwargs.pop("keep_original_conformer") coords = generate_diverse_conformer_coordinates(qcmol, **rdkwargs) if keep: coords = np.concatenate([original, coords]) return coords