Source code for openff.bespokefit.schema.tasks
import abc
from typing import Optional, Tuple, overload
from openff.qcsubmit.procedures import GeometricProcedure
from openff.toolkit.topology import Molecule
from qcelemental.models import AtomicResult
from qcelemental.models.common_models import Model
from qcelemental.models.procedures import OptimizationResult, TorsionDriveResult
from typing_extensions import Literal
from openff.bespokefit._pydantic import BaseModel, Field, conint
[docs]class QCGenerationTask(BaseModel, abc.ABC):
type: Literal["base-task"]
program: str = Field(..., description="The program to use to evaluate the model.")
model: Model = Field(..., description=str(Model.__doc__))
[docs]class HessianTaskSpec(QCGenerationTask):
type: Literal["hessian"] = "hessian"
n_conformers: conint(gt=0) = Field(
10,
description="The maximum number of conformers to generate when computing the "
"hessian. Each conformer will be minimized and the one with the lowest energy "
"will have its hessian computed.",
)
optimization_spec: GeometricProcedure = Field(
GeometricProcedure(),
description="The specification for how to optimize each conformer before "
"computing the hessian.",
)
[docs]class HessianTask(HessianTaskSpec):
smiles: str = Field(
...,
description="A fully indexed SMILES representation of the molecule to compute "
"the hessian for.",
)
[docs]class OptimizationTaskSpec(HessianTaskSpec):
type: Literal["optimization"] = "optimization"
[docs]class OptimizationTask(OptimizationTaskSpec):
smiles: str = Field(
...,
description="A fully indexed SMILES representation of the molecule to optimize.",
)
[docs]class Torsion1DTaskSpec(QCGenerationTask):
type: Literal["torsion1d"] = "torsion1d"
grid_spacing: int = Field(15, description="The spacing between grid angles.")
scan_range: Optional[Tuple[int, int]] = Field(
None, description="The range of grid angles to scan."
)
optimization_spec: GeometricProcedure = Field(
GeometricProcedure(enforce=0.1, reset=True, qccnv=True, epsilon=0.0),
description="The specification for how to optimize the structure at each angle "
"in the scan.",
)
n_conformers: conint(gt=0) = Field(
10,
description="The number of initial conformers to seed the torsion drive with.",
)
[docs]class Torsion1DTask(Torsion1DTaskSpec):
smiles: str = Field(
...,
description="An indexed SMILES representation of the molecule to drive.",
)
central_bond: Tuple[int, int] = Field(
None,
description="The **map** indices of the atoms in the bond to scan around.",
)
@overload
def task_from_result(result: AtomicResult) -> HessianTask:
...
@overload
def task_from_result(result: OptimizationResult) -> OptimizationTask:
...
@overload
def task_from_result(result: TorsionDriveResult) -> Torsion1DTask:
...
[docs]def task_from_result(result):
"""
Convert a result into a task to populate the cache for the result.
"""
if isinstance(result, TorsionDriveResult):
dihedral = result.keywords.dihedrals[0]
off_mol = Molecule.from_qcschema(result.initial_molecule[0])
return Torsion1DTask(
smiles=off_mol.to_smiles(
isomeric=True, explicit_hydrogens=True, mapped=True
),
program=result.extras["program"],
model=result.input_specification.model,
central_bond=(dihedral[1] + 1, dihedral[2] + 1),
grid_spacing=result.keywords.grid_spacing[0],
scan_range=result.keywords.dihedral_ranges,
optimization_spec=GeometricProcedure.from_opt_spec(
result.optimization_spec
),
)
else:
raise NotImplementedError()