Source code for matador.workflows.castep.phonons

# coding: utf-8
# Distributed under the terms of the MIT License.

""" This module implements the :class:`CastepPhononWorkflow`
class, which performs phonon calculations with CASTEP in
multiple steps (only when necessary):

    1. Try to pre-relax structure (skipped if check file
           is already present).
    2. Calculate dynamical matrix.
    3. If ``phonon_fine_kpoint_mp_spacing`` keyword is found,
       interpolate dynamical matrix to form phonon DOS.
    4. If ``phonon_fine_kpoint_path_spacing`` key word is found,
       interpolate dynamical matrix to form phonon dispersion
       on the path given by seekpath.
    5. If ``task=thermodynamics``, perform a CASTEP thermodynamics
       calculation for the temperature-dependence of the free energy.

"""


import os
import copy
import logging
from matador.workflows.workflows import Workflow

LOG = logging.getLogger("run3")


[docs]def castep_full_phonon(computer, calc_doc, seed, **kwargs): """Perform a "full" phonon calculation on a system, i.e. first perform a relaxation in a standardised unit cell, then compute the dynamical matrix, then finally interpolate that dynamical matrix into dispersion curves and DOS. This function is a wrapper for the :class:`CastepPhononWorkflow` class. Parameters: computer (:obj:`matador.compute.ComputeTask`): the object that will be calling CASTEP. calc_doc (dict): dictionary of structure and calculation parameters. seed (str): root seed for the calculation. Raises: RuntimeError: if any part of the calculation fails. Returns: bool: True if Workflow completed successfully, or False otherwise. """ workflow = CastepPhononWorkflow(computer, calc_doc, seed, **kwargs) return workflow.success
[docs]class CastepPhononWorkflow(Workflow): """Perform a "full" phonon calculation on a system, i.e. first perform a relaxation in a standardised unit cell, then compute the dynamical matrix, then finally interpolate that dynamical matrix into dispersion curves and DOS. Attributes: computer (:obj:`ComputeTask`): the object that calls CASTEP. calc_doc (dict): the interim dictionary of structural and calculation parameters. seed (str): the root seed for the calculation. success (bool): the status of the Workflow: only set to True after post-processing method completes. """
[docs] def preprocess(self): """Decide which parts of the Workflow need to be performed, and set the appropriate CASTEP parameters. """ # default todo todo = { "relax": True, "dynmat": True, "vdos": False, "dispersion": False, "thermodynamics": False, } # definition of steps and names steps = { "relax": castep_phonon_prerelax, "dynmat": castep_phonon_dynmat, "vdos": castep_phonon_dos, "dispersion": castep_phonon_dispersion, "thermodynamics": castep_phonon_thermodynamics, } exts = { "relax": { "input": [".cell", ".param"], "output": [".castep", "-out.cell", ".*err"], }, "dynmat": {"input": [".cell", ".param"], "output": [".castep", ".*err"]}, "vdos": { "input": [".cell", ".param"], "output": [".castep", ".phonon", ".phonon_dos", ".*err"], }, "dispersion": { "input": [".cell", ".param"], "output": [".castep", ".phonon", ".*err"], }, "thermodynamics": { "input": [".cell", ".param"], "output": [".castep", ".*err"], }, } if self.calc_doc.get("task").lower() in [ "phonon", "thermodynamics", "phonon+efield", ]: if ( "phonon_fine_kpoint_path" in self.calc_doc or "phonon_fine_kpoint_list" in self.calc_doc or "phonon_fine_kpoint_path_spacing" in self.calc_doc ): todo["dispersion"] = True if "phonon_fine_kpoint_mp_spacing" in self.calc_doc: todo["vdos"] = True if self.calc_doc["task"].lower() == "thermodynamics": todo["thermodynamics"] = True # prepare to do pre-relax if there's no check file if os.path.isfile(self.seed + ".check"): todo["relax"] = False LOG.info( "Restarting from {}.check, so not performing re-relaxation".format( self.seed ) ) for key in todo: if todo[key]: self.add_step( steps[key], key, input_exts=exts[key].get("input"), output_exts=exts[key].get("output"), ) # always standardise the cell so that any phonon calculation can have # post-processing performed after the fact, unless a path has been provided if ( "phonon_fine_kpoint_list" not in self.calc_doc and "phonon_fine_kpoint_path" not in self.calc_doc ): from matador.utils.cell_utils import cart2abc prim_doc, kpt_path = self.computer.get_seekpath_compliant_input( self.calc_doc, self.calc_doc.get("phonon_fine_kpoint_path_spacing", 0.02), ) self.calc_doc.update(prim_doc) self.calc_doc["lattice_abc"] = cart2abc(self.calc_doc["lattice_cart"]) if todo["dispersion"]: self.calc_doc["phonon_fine_kpoint_list"] = kpt_path elif todo["dispersion"] and "phonon_fine_kpoint_path" in self.calc_doc: self._user_defined_kpt_path = True LOG.warning("Using user-defined k-point path for all structures.") self.calc_doc["phonon_fine_kpoint_spacing"] = self.calc_doc.get( "phonon_fine_kpoint_path_spacing", 0.05 ) # always shift phonon grid to include Gamma if "phonon_kpoint_mp_spacing" in self.calc_doc: from matador.utils.cell_utils import calc_mp_grid, shift_to_include_gamma grid = calc_mp_grid( self.calc_doc["lattice_cart"], self.calc_doc["phonon_kpoint_mp_spacing"] ) offset = shift_to_include_gamma(grid) if offset != [0, 0, 0]: self.calc_doc["phonon_kpoint_mp_offset"] = offset LOG.debug("Set phonon MP grid offset to {}".format(offset)) LOG.info("Preprocessing completed: run3 phonon options {}".format(todo))
[docs]def castep_phonon_prerelax(computer, calc_doc, seed): """Run a singleshot geometry optimisation before an SCF-style calculation. This is typically used to ensure phonon calculations start successfully. The phonon calculation will then be restarted from the .check file produced here. Parameters: computer (:obj:`ComputeTask`): the object that will be calling CASTEP. calc_doc (dict): the structure to run on. seed (str): root filename of structure. """ from matador.workflows.castep.common import castep_prerelax LOG.info("Performing CASTEP phonon pre-relax...") required = ["write_checkpoint"] forbidden = [ "phonon_fine_kpoint_list", "phonon_fine_kpoint_path", "phonon_fine_kpoint_mp_spacing", "phonon_fine_kpoint_path_spacing", ] return castep_prerelax( computer, calc_doc, seed, required_keys=required, forbidden_keys=forbidden )
[docs]def castep_phonon_dynmat(computer, calc_doc, seed): """Runs a singleshot phonon dynmat calculation, with no "fine_method" interpolation. Parameters: computer (:obj:`ComputeTask`): the object that will be calling CASTEP. calc_doc (dict): the structure to run on. seed (str): root filename of structure. """ LOG.info("Performing CASTEP dynmat calculation...") dynmat_doc = copy.deepcopy(calc_doc) dynmat_doc["write_checkpoint"] = "ALL" if calc_doc["task"].lower() == "phonon+efield": dynmat_doc["task"] = "phonon+efield" else: dynmat_doc["task"] = "phonon" dynmat_doc["continuation"] = "default" required = ["continuation", "write_checkpoint"] forbidden = [ "phonon_fine_kpoint_list", "phonon_fine_kpoint_path", "phonon_fine_kpoint_mp_spacing", "phonon_fine_kpoint_path_spacing", ] computer.validate_calc_doc(dynmat_doc, required, forbidden) return computer.run_castep_singleshot( dynmat_doc, seed, keep=True, intermediate=True )
[docs]def castep_phonon_dos(computer, calc_doc, seed): """Runs a DOS interpolation on top of a completed phonon calculation. Parameters: computer (:obj:`ComputeTask`): the object that will be calling CASTEP. calc_doc (dict): the structure to run on. seed (str): root filename of structure. """ LOG.info("Performing CASTEP phonon DOS calculation...") dos_doc = copy.deepcopy(calc_doc) dos_doc["task"] = "phonon" dos_doc["phonon_calculate_dos"] = True dos_doc["continuation"] = "default" required = ["phonon_fine_kpoint_mp_spacing"] forbidden = [ "phonon_fine_kpoint_list", "phonon_fine_kpoint_path", "phonon_fine_kpoint_path_spacing", ] computer.validate_calc_doc(dos_doc, required, forbidden) return computer.run_castep_singleshot(dos_doc, seed, keep=True, intermediate=True)
[docs]def castep_phonon_dispersion(computer, calc_doc, seed): """Runs a dispersion interpolation on top of a completed phonon calculation. Parameters: computer (:obj:`ComputeTask`): the object that will be calling CASTEP. calc_doc (dict): the structure to run on. seed (str): root filename of structure. """ LOG.info("Performing CASTEP phonon dispersion calculation...") disp_doc = copy.deepcopy(calc_doc) disp_doc["task"] = "phonon" disp_doc["phonon_calculate_dos"] = False disp_doc["continuation"] = "default" required = [] forbidden = ["phonon_fine_kpoint_mp_spacing"] computer.validate_calc_doc(disp_doc, required, forbidden) return computer.run_castep_singleshot(disp_doc, seed, keep=True, intermediate=True)
[docs]def castep_phonon_thermodynamics(computer, calc_doc, seed): """Runs a "thermodynamics" interpolation on top of a completed phonon calculation, using the phonon_fine_kpoint_mp_grid. Parameters: computer (:obj:`ComputeTask`): the object that will be calling CASTEP. calc_doc (dict): the structure to run on. seed (str): root filename of structure. """ LOG.info("Performing CASTEP thermodynamics calculation...") thermo_doc = copy.deepcopy(calc_doc) thermo_doc["continuation"] = "default" thermo_doc["task"] = "thermodynamics" thermo_doc["phonon_calculate_dos"] = False required = ["phonon_fine_kpoint_mp_spacing"] forbidden = [ "phonon_fine_kpoint_list", "phonon_fine_kpoint_path", "phonon_fine_kpoint_path_spacing", ] computer.validate_calc_doc(thermo_doc, required, forbidden) return computer.run_castep_singleshot( thermo_doc, seed, keep=True, intermediate=True )