Source code for scine_puffin.jobs.scine_irc_scan

# -*- coding: utf-8 -*-
from __future__ import annotations
__copyright__ = """ This code is licensed under the 3-clause BSD license.
Copyright ETH Zurich, Department of Chemistry and Applied Biosciences, Reiher Group.
See LICENSE.txt for details.
"""

from typing import TYPE_CHECKING

from ..utilities import scine_helper
from scine_puffin.config import Configuration
from .templates.job import calculation_context, job_configuration_wrapper
from .templates.scine_optimization_job import OptimizationJob
from scine_puffin.utilities.imports import module_exists, MissingDependency

if module_exists("scine_database") or TYPE_CHECKING:
    import scine_database as db
else:
    db = MissingDependency("scine_database")


[docs]class ScineIrcScan(OptimizationJob): """ A job scanning a single intrinsic reaction coordinate. **Order Name** ``scine_irc_scan`` **Optional Settings** Optional settings are read from the ``settings`` field, which is part of any ``Calculation`` stored in a SCINE Database. Possible settings for this job are: All settings recognized by ReaDuct's IRC task. For a complete list see the `ReaDuct manual <https://scine.ethz.ch/download/readuct>`_ Common examples are: stop_on_error : bool If ``False``, the optimization does not need to fully converge but will be accepted as a success even if it reaches the maximum amounts of optimization cycles. Also, the resulting structures will be flagged as ``minimum_guess`` if this option is set ot be ``False``. (Default: ``True``) irc_mode : int The mode to follow during the IRC scan. By default, the first mode (0). (mode with the larges imaginary frequency will be followed). All settings that are recognized by the SCF program chosen. Common examples are: max_scf_iterations : int The number of allowed SCF cycles until convergence. **Required Packages** - SCINE: Database (present by default) - SCINE: Readuct (present by default) - SCINE: Utils (present by default) - A program implementing the SCINE Calculator interface, e.g. Sparrow **Generated Data** If successful the following data will be generated and added to the database: Structures Both the forward and backward optimized structures will be added to the database. They will be flagged as: ``minimum_optimized`` or ``minimum_guess`` if ``stop_on_error`` is set to ``False``. Properties The ``electronic_energy`` associated with both forward and backward structures. """ def __init__(self) -> None: super().__init__() self.name = "Scine IRC Job"
[docs] @job_configuration_wrapper def run(self, manager: db.Manager, calculation: db.Calculation, config: Configuration) -> bool: import scine_readuct as readuct # Get structure structure = db.Structure(calculation.get_structures()[0], self._structures) settings_manager, program_helper = self.create_helpers(structure) # actual calculation with calculation_context(self): systems, keys = settings_manager.prepare_readuct_task( structure, calculation, calculation.get_settings(), config["resources"] ) if program_helper is not None: program_helper.calculation_preprocessing(self.get_calc(keys[0], systems), calculation.get_settings()) # Task specific operation settings_manager.task_settings["output"] = ["forward", "backward"] stop_on_error = settings_manager.task_settings.get("stop_on_error", True) systems, success = readuct.run_irc_task(systems, keys, **settings_manager.task_settings) self.verify_connection() # check valid connection results_check, results_err = self.expected_results_check(systems, ["forward", "backward"]) if not results_check: self.raise_named_exception(results_err) scine_helper.update_model(self.get_calc(keys[0], systems), self._calculation, config) is_surface = structure.has_property("surface_atom_indices") label = db.Label.SURFACE_OPTIMIZED if is_surface else db.Label.MINIMUM_OPTIMIZED if not success and not stop_on_error: label = db.Label.SURFACE_GUESS if is_surface else db.Label.MINIMUM_GUESS calculation.set_comment( "Optimization did not fully converge for one or both sides. 'forward' and " "'backward' structures are stored as '" + db.Label.MINIMUM_GUESS.name + "'." ) # clear results db_results = calculation.get_results() db_results.clear() calculation.set_results(db_results) for name in ["forward", "backward"]: # structure new_structure = self.create_new_structure(systems[name], label) db_results.add_structure(new_structure.id()) # properties self.store_energy(self.get_calc(name, systems), new_structure) self.transfer_properties(structure, new_structure) if program_helper is not None: program_helper.calculation_postprocessing( calculation, structure, new_structure ) calculation.set_results(calculation.get_results() + db_results) return self.postprocess_calculation_context()