Source code for scine_puffin.jobs.turbomole_single_point

# -*- 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, List
import os
from scine_puffin.config import Configuration
from .templates.job import calculation_context, job_configuration_wrapper
from .templates.turbomole_job import TurbomoleJob
from ..utilities.turbomole_helper import TurbomoleHelper
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 TurbomoleSinglePoint(TurbomoleJob): """ A job calculating the electronic energy for a given structure with the Turbomole program. **Order Name** ``turbomole_single_point`` **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: self_consistence_criterion : float The self consistence criterion corresponding to the maximum energy change between two SCF cycles resulting in convergence. Default value ist 1E-6. cartesian_constraints : List[int] A list of atom indices of the atoms which positions will be constrained during the optimization. max_scf_iterations : int The number of allowed SCF cycles until convergence. Default value is 30. transform_coordinates : bool Switch to transform the input coordinates from redundant internal to cartesian coordinates. Setting this value to True and hence performing the calculation in Cartesian coordinates is helpful in rare occasions where the calculation with redundant internal coordinates fails. The optimization will take more time but is more likely to end successfully. The default is True. scf_damping : bool Increases damping during the SCF by modifying the $scfdamp parameter in the control file. The default is False. scf_orbitalshift : float Shifts virtual orbital energies upwards. Default value is 0.1. calculate_loewdin_charges : bool Calculates the Loewdin partial charges. The default is False. spin_mode : string Sets the spin mode. If no spin mode is set, Turbomole's default for the corresponding system is chosen. Options are: restricted, unrestricted. **Required Packages** - SCINE: Database (present by default) - SCINE: Utils (present by default) - The Turbomole program has to be available **Generated Data** If successful the following data will be generated and added to the database: Properties The ``electronic_energy`` associated with the structure. ``atomic_charges`` for all atoms, if requested. """ def __init__(self) -> None: super().__init__() self.input_structure = "system.xyz" self.tm_helper = TurbomoleHelper() # Executes a single point calculation using the dscf script
[docs] def execute_single_point_calculation(self, job: db.Job) -> None: if job.cores > 1: os.environ["PARA_ARCH"] = "SMP" os.environ["PARNODES"] = str(job.cores) self.tm_helper.execute(os.path.join(self.smp_turboexe, "ridft")) else: self.tm_helper.execute("{}".format(os.path.join(self.turboexe, "ridft")))
[docs] @job_configuration_wrapper def run(self, manager: db.Manager, calculation: db.Calculation, config: Configuration) -> bool: # Gather all required collections structures = manager.get_collection("structures") calculations = manager.get_collection("calculations") properties = manager.get_collection("properties") # Link calculation if needed if not calculation.has_link(): calculation.link(calculations) # Get structure and number of atoms structure = db.Structure(calculation.get_structures()[0]) structure.link(structures) natoms = len(structure.get_atoms()) # Get model model = calculation.get_model() # Get job job = calculation.get_job() if not self.turboexe: calculation.set_status(db.Status.FAILED) raise RuntimeError("Turbomole executables are not available.") calculation_settings = calculation.get_settings() # Do calculations with calculation_context(self): # Prepare calculation self.prepare_calculation(structure, calculation_settings, model, job) # Execute Program self.execute_single_point_calculation(job) # Parse output file parsed_energy = self.tm_helper.parse_energy_file() # Get loewdin charges if requested atomic_charges_set, atomic_charges = self.tm_helper.get_loewdin_charges(natoms, calculation_settings) spin_mode = self.tm_helper.evaluate_spin_mode(calculation_settings) # After the calculation, verify that the connection to the database still exists self.verify_connection() # Capture raw output stdout_path = os.path.join(self.work_dir, "output") stderr_path = os.path.join(self.work_dir, "errors") with open(stdout_path, "r") as stdout, open(stderr_path, "r") as stderr: error = stderr.read() calculation.set_raw_output(stdout.read() + "\n" + error) # A sanity check if not os.path.exists(os.path.join(self.work_dir, "success")): if len(error.strip()) > 0: calculation.set_comment(error.replace("\n", " ")) else: calculation.set_comment( "Single Point Job Error: Turbomole Single Point job failed with an unspecified error." ) calculation.set_status(db.Status.FAILED) return False # Update model model.program = "turbomole" model.spin_mode = spin_mode model.version = config.programs()[model.program]["version"] calculation.set_model(model) # Generate database results db_results = calculation.get_results() db_results.clear() calculation.set_results(db_results) # Store energy self.store_property( properties, "electronic_energy", "NumberProperty", parsed_energy, model, calculation, structure, ) # Store atomic charges if requested if atomic_charges_set: self.store_property( properties, "loewdin_charges", "VectorProperty", atomic_charges, model, calculation, structure, ) calculation.set_status(db.Status.COMPLETE) return True
[docs] @staticmethod def required_programs() -> List[str]: return ["database", "utils", "turbomole"]