Source code for pychum.engine.orca._dataclasses

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from enum import Enum


[docs] class BlockType(Enum):
[docs] NEB = "neb"
[docs] GEOM = "geom"
[docs] class OrcaBlock(ABC): @abstractmethod
[docs] def block_type(self) -> BlockType: pass
@dataclass
[docs] class UnitConversion:
[docs] inp: str
[docs] out: str
@dataclass
[docs] class Atom:
[docs] x: float = 0.0
[docs] y: float = 0.0
[docs] z: float = 0.0
[docs] symbol: str = field(default=None)
[docs] is_ghost: bool = False
[docs] embedding_potential: bool = False
[docs] is_frozen: bool = False # Not applied to anything but cartesian
[docs] isotope: float | None = None
[docs] nuclear_charge: float | None = None
[docs] fragment_number: int | None = None
[docs] is_dummy: bool = False
[docs] point_charge: float | None = None
[docs] bond_atom: int | None = None # Index of bonded atom (for internal coordinates)
[docs] bond_length: float | None = None # Bond length (for internal coordinates)
[docs] angle_atom: int | None = None # Index of angle atom (for internal coordinates)
[docs] angle: float | None = None # Bond angle (for internal coordinates)
[docs] dihedral_atom: int | None = None # Index of dihedral atom (for internal coordinates)
[docs] dihedral: float | None = None # Dihedral angle (for internal coordinates)
[docs] is_frozen_x: bool = False # Cartesian only
[docs] is_frozen_y: bool = False # Cartesian only
[docs] is_frozen_z: bool = False # Cartesian only
[docs] def __post_init__(self): if self.point_charge is not None: self.symbol = "Q" elif self.symbol is None: msg = "Atom symbol is required unless it's a point charge." raise ValueError(msg)
@dataclass
[docs] class Coords:
[docs] charge: int
[docs] multiplicity: int
[docs] fmt: str
[docs] filedat: str = ""
[docs] atoms: list[Atom] = field(default_factory=list)
@dataclass
[docs] class GeomScan:
[docs] atoms: list[int]
[docs] range: list[float]
[docs] points: int
@dataclass
[docs] class GeomBlock(OrcaBlock):
[docs] bonds: list[GeomScan] = field(default_factory=list)
[docs] dihedrals: list[GeomScan] = field(default_factory=list)
[docs] angles: list[GeomScan] = field(default_factory=list)
[docs] def block_type(self) -> BlockType: return BlockType.GEOM
@dataclass
[docs] class LBFGSSettings:
[docs] reparam_on_restart: bool = False
[docs] memory: int = 20
[docs] precondition: bool = True
[docs] dr: float = 0.002
[docs] restart_on_maxmove: bool = True
@dataclass
[docs] class FIRESettings:
[docs] init_damp: float = 0.1
[docs] damp_decr: float = 0.99
[docs] step_incr: float = 1.1
[docs] step_decr: float = 0.5
[docs] max_step: float = 5.0
[docs] retention: int = 5
@dataclass
[docs] class ReparamSettings:
[docs] interp: str = "linear"
[docs] every: int = 0
[docs] tol: float = 0.0
[docs] def __post_init__(self): valid_interps = {"linear", "cubic"} if self.interp.lower() not in valid_interps: msg = f"Interp must be one of {valid_interps}, got '{self.interp}'" raise ValueError(msg)
@dataclass
[docs] class ConvTolSettings:
[docs] units: str = "Eh/Bohr"
[docs] maxfp_i: float = 0.005
[docs] rmsfp_i: float = 0.003
[docs] maxf_ci: float = 0.0005
[docs] rmsf_ci: float = 0.0003
[docs] turn_on_ci: float = 0.02
[docs] scale: int = 10
@dataclass
[docs] class IDPPSettings:
[docs] tol_maxf: float = 0.01
[docs] maxmove: float = 0.05
[docs] alpha: float = 0.01
[docs] nmax: int = 3000
[docs] quatern: bool = True
[docs] ksp: float = 1.0
[docs] debug: bool = False
@dataclass
[docs] class OptimSettings:
[docs] method: str = "LBFGS"
[docs] maxmove: float = 0.2
[docs] stepsize: float = 1.0
[docs] maxiter: float = 500
[docs] local: bool = False
[docs] def __post_init__(self): valid_methods = {"LBFGS", "VPO", "FIRE"} if self.method.upper() not in valid_methods: msg = f"Method must be one of {valid_methods}, got '{self.method}'" raise ValueError(msg)
@dataclass
[docs] class FreeEndSettings:
[docs] use: bool = False
[docs] opt_type: str = "PERP"
[docs] ec: float = 0.0
[docs] ec_end: float = 0.0
[docs] kappa: float = 1.0
[docs] def __post_init__(self): valid_opt_types = {"PERP", "CONTOUR", "FULL"} if self.opt_type.upper() not in valid_opt_types: msg = f"opt_type must be one of {valid_opt_types}, got '{self.opt_type}'" raise ValueError(msg)
@dataclass
[docs] class ZoomSettings:
[docs] tol_turn_on: float = 0.0
[docs] offset: int = 2
[docs] auto: bool = True
[docs] tol_scale: int = 10
[docs] alpha: float = 0.5
[docs] interpolation: str = "linear"
[docs] printfulltrj: bool = True
[docs] def __post_init__(self): valid_interpolations = {"linear", "cubic"} if self.interpolation.lower() not in valid_interpolations: msg = ( f"interpolation must be one of {valid_interpolations}," " got '{self.interpolation}'" ) raise ValueError(msg)
@dataclass
[docs] class SpringSettings:
[docs] spring_kind: str = "image"
[docs] const1: float = 0.01
[docs] const2: float = 0.1
[docs] energy_weighted: bool = True
[docs] perpspring: str = "no"
[docs] llt_cos: bool = True
[docs] def __post_init__(self): valid_springkinds = {"image", "dof", "ideal"} valid_perpsprings = {"no", "cos", "tan", "cosTan", "DNEB"} if self.spring_kind.lower() not in valid_springkinds: msg = ( f"spring_kind must be one of {valid_springkinds}," " got '{self.spring_kind}'" ) raise ValueError(msg) if self.perpspring.lower() not in valid_perpsprings: msg = ( f"perpstring must be one of {valid_perpsprings}," " got '{self.perpspring}'" ) raise ValueError(msg)
@dataclass
[docs] class RestartSettings:
[docs] gbw_basename: str = None
[docs] allxyz: str = None
[docs] def __post_init__(self): if self.gbw_basename and self.allxyz: msg = "Only one of gbw_basename or allxyz should be provided." raise ValueError(msg)
@dataclass
[docs] class TSGuessSettings:
[docs] xyz_struct: str = None
[docs] pdb_struct: str = None
[docs] ts_img: int = -1
[docs] def __post_init__(self): if self.xyz_struct and self.pdb_struct: msg = "Only one of xyz_struct or pdb_struct should be provided." raise ValueError(msg)
@dataclass
[docs] class FixCenterSettings:
[docs] active: bool = True
[docs] remove_extern_force: bool = True
@dataclass
[docs] class NebBlock(OrcaBlock):
[docs] end_xyz: str
[docs] nimgs: int
[docs] convtype: str = "CIONLY"
[docs] printlevel: int = 4
[docs] neb_ts: bool = False
[docs] neb_ci: bool = False
[docs] quatern: str = "ALWAYS"
[docs] climbingimage: bool = True
[docs] check_scf_conv: bool = True
[docs] preopt: bool = False
[docs] nsteps_foundintermediate: int = 30
[docs] abortif_foundintermediate: bool = False
[docs] npts_interpol: int = 10
[docs] interpolation: str = "IDPP"
[docs] tangent: str = "IMPROVED"
[docs] lbfgs_settings: LBFGSSettings = field(default_factory=LBFGSSettings)
[docs] fire_settings: FIRESettings = field(default_factory=FIRESettings)
[docs] reparam_settings: ReparamSettings = field(default_factory=ReparamSettings)
[docs] idpp_settings: IDPPSettings = field(default_factory=IDPPSettings)
[docs] zoom_settings: ZoomSettings = field(default_factory=ZoomSettings)
[docs] optim_settings: OptimSettings = field(default_factory=OptimSettings)
[docs] convtol_settings: ConvTolSettings = field(default_factory=ConvTolSettings)
[docs] free_end_settings: FreeEndSettings = field(default_factory=FreeEndSettings)
[docs] spring_settings: SpringSettings = field(default_factory=SpringSettings)
[docs] restart_settings: RestartSettings = field(default_factory=RestartSettings)
[docs] tsguess_settings: TSGuessSettings = field(default_factory=TSGuessSettings)
[docs] fix_center_settings: FixCenterSettings = field(default_factory=FixCenterSettings)
[docs] def __post_init__(self): valid_convtypes = {"all", "cionly"} valid_quaterns = {"no", "startonly", "always"} valid_tangents = {"improved", "original"} valid_interpolations = {"IDPP", "LINEAR", "XTB1TS", "XTB1", "XTB2TS", "XTB2"} if self.convtype.lower() not in valid_convtypes: msg = ( f"Convergence type must be one of {valid_convtypes}," " got '{self.convtype}'" ) raise ValueError(msg) if self.quatern.lower() not in valid_quaterns: msg = f"quatern must be one of {valid_quaterns}, got '{{self.quatern}}'" raise ValueError(msg) if self.tangent.lower() not in valid_tangents: msg = f"tangent must be one of {valid_tangents}, got '{{self.tangent}}'" raise ValueError(msg) if self.interpolation.upper() not in valid_interpolations: msg = ( f"interpolation must be one of {valid_interpolations}," " got '{self.interpolation}'" ) raise ValueError(msg)
[docs] def block_type(self) -> BlockType: return BlockType.NEB
@dataclass
[docs] class OrcaConfig:
[docs] kwlines: str
[docs] coords: Coords
[docs] blocks: dict[BlockType, OrcaBlock] = field(default_factory=dict)
[docs] extra_blocks: dict[str, str] = field(default_factory=dict)
[docs] def add_block(self, block: OrcaBlock): self.blocks[block.block_type()] = block
# @dataclass # class OrcaConfig: # coords: Coords # kwlines: List[KWLine] = None # orca_geom: Optional[OrcaGeom] = None