def qd_opt(mol: Molecule, jobs: Tuple[Optional[Type[Job]], ...], settings: Tuple[Optional[Settings], ...], use_ff: bool = False) -> None: """Perform an optimization of the quantum dot. Performs an inplace update of **mol**. Parameters ---------- mol : |plams.Molecule|_ The to-be optimized molecule. job_recipe : |plams.Settings|_ A Settings instance containing all jon settings. Expects 4 keys: ``"job1"``, ``"job2"``, ``"s1"``, ``"s2"``. forcefield : bool If ``True``, perform the job with CP2K with a user-specified forcefield. """ # Prepare the job settings if use_ff: qd_opt_ff(mol, jobs, settings) return None # Expand arguments job1, job2 = jobs s1, s2 = settings # Extra options for AMSJob if job1 is AMSJob: s1 = Settings(s1) s1.input.ams.constraints.atom = mol.properties.indices if job2 is AMSJob: s2 = Settings(s2) s2.input.ams.constraints.atom = mol.properties.indices # Run the first job and fix broken angles mol.job_geometry_opt(job1, s1, name='QD_opt_part1') fix_carboxyl(mol) fix_h(mol) mol.round_coords() # Run the second job if job2 is not None: mol.job_geometry_opt(job2, s2, name='QD_opt_part2') mol.round_coords() return None
def _preoptimize(mol: Molecule) -> None: """Perform a constrained geometry optimization of **mol** with AMS UFF.""" s = get_template('qd.yaml')['UFF'] s.input.ams.constraints.atom = mol.properties.indices s.input.ams.GeometryOptimization.coordinatetype = 'Cartesian' mol.job_geometry_opt(AMSJob, s, name='E_XYn_preopt')
def get_bde_dE(tot: Molecule, lig: Molecule, core: Iterable[Molecule], job: Type[Job], s: Settings, forcefield: bool = False) -> np.ndarray: """Calculate the bond dissociation energy: dE = dE(mopac) + (dG(uff) - dE(uff)). Parameters ---------- tot : |plams.Molecule|_ The complete intact quantum dot. lig : |plams.Molecule|_ A ligand dissociated from the surface of the quantum dot core : |list|_ [|plams.Molecule|_] A list with one or more quantum dots (*i.e.* **tot**) with **lig** removed. job : |plams.Job|_ A :class:`.Job` subclass. s : |plams.Settings|_ The settings for **job**. """ # Optimize XYn len_core = len(core) if job is AMSJob: s_cp = Settings(s) s_cp.input.ams.GeometryOptimization.coordinatetype = 'Cartesian' lig.job_geometry_opt(job, s_cp, name='E_XYn_opt') elif forcefield: qd_opt_ff(lig, Settings({'job1': Cp2kJob, 's1': s}), name='E_XYn_opt') else: lig.job_geometry_opt(job, s, name='E_XYn_opt') E_lig = lig.properties.energy.E if E_lig in (None, np.nan): logger.error( 'The BDE XYn geometry optimization failed, skipping further jobs') return np.full(len_core, np.nan) # Perform a single point on the full quantum dot if forcefield: qd_opt_ff(tot, Settings({'job1': Cp2kJob, 's1': s}), name='E_QD_opt') else: tot.job_single_point(job, s, name='E_QD_sp') E_tot = tot.properties.energy.E if E_tot in (None, np.nan): logger.error( 'The BDE quantum dot single point failed, skipping further jobs') return np.full(len_core, np.nan) # Perform a single point on the quantum dot(s) - XYn for mol in core: if forcefield: qd_opt_ff(mol, Settings({ 'job1': Cp2kJob, 's1': s }), name='E_QD-XYn_opt') else: mol.job_single_point(job, s, name='E_QD-XYn_sp') E_core = np.fromiter([mol.properties.energy.E for mol in core], count=len_core, dtype=float) # Calculate and return dE dE = (E_lig + E_core) - E_tot return dE