Source code for aiida_vasp.utils.opthold

"""
Module containing the OptionHolder class
"""

from typing import Optional

from aiida.orm import Dict
from pydantic import BaseModel, Field, ValidationError

# pylint:disable=raise-missing-from


[docs] class OptionContainer(BaseModel): """ Base class for a container of options """
[docs] def aiida_dict(self) -> Dict: """Return an ``aiida.orm.Dict`` presentation""" python_dict = self.model_dump() return Dict(dict=python_dict)
[docs] @classmethod def aiida_validate(cls, input_dict: dict | Dict, namespace: None | str = None) -> None: # pylint:disable=unused-argument """ Validate a dictionary/Dict node, this can be used as the validator for the Port accepting the inputs This is used as validator for the `spec.input` call. """ if isinstance(input_dict, Dict): input_dict = input_dict.get_dict() try: cls(**input_dict) except ValidationError as error: return str(error) return None
[docs] @classmethod def aiida_serialize(cls, python_dict: dict) -> Dict: """ serialize a dictionary into Dict This method can be passed as a `serializer` key word parameter of for the `spec.input` call. """ obj = cls(**python_dict) return obj.aiida_dict()
[docs] @classmethod def aiida_description(cls) -> str: """ Return a string for the options of a OptionContains in a human-readable format. """ template = '{:>{width_name}s}: {:10s} \n{default:>{width_name2}}: {}' entries = [] for name, field in cls.model_fields.items(): # Each entry is name, type, doc, default value entries.append([name, str(field.annotation.__name__), field.description, field.default]) max_width_name = max(len(entry[0]) for entry in entries) + 2 lines = [] for entry in entries: lines.append( template.format( *entry, width_name=max_width_name, width_name2=max_width_name + 10, default='Default', ) ) return '\n'.join(lines)
[docs] class CalcSettingsConfig(OptionContainer): """Schema for the .settings port used by both VaspCalculation and VaspWorkChain""" parser_setting: Optional[dict] = Field(description='Settings for the parser') ADDITIONAL_RETRIEVE_LIST: Optional[list] = Field(description='Additional list of files to be retrieved') ADDITIONAL_RETRIEVE_TEMPORARY_LIST: Optional[list] = Field( description=('Additional list of files to be retrieved, but not store in the storage') ) PROVENANCE_EXCLUDE_LIST: Optional[list] = Field( description=('Additional list of files to be retrieved, but not store in the storage') ) ALWAYS_STORE: Optional[list] = Field( description=('Additional list of files to be retrieved, but not store in the storage') ) skip_param_validation: Optional[bool] = Field( description='Skip the validation of the input parameters', default=False ) unsupported_parameters: Optional[dict] = Field(description='None-standard VASP parameters that are valid')
[docs] class RelaxOptions(OptionContainer): """Options for VaspRelaxWorkChain""" algo: str = Field(description='The algorithm to use for relaxation', examples=['cg', 'rd'], default='cg') energy_cutoff: Optional[float] = Field( description='The cut off energy difference when the relaxation is stopped (e.g. EDIFF)', default=None, ) force_cutoff: float = Field( description='The maximum force when the relaxation is stopped (e.g. EDIFFG)', default=0.03, ) steps: int = Field(description='Number of relaxation steps to perform (eg. NSW)', default=60) positions: bool = Field(description='If True, perform relaxation of the atomic positions', default=True) shape: bool = Field(description='If True, perform relaxation of the cell shape', default=True) volume: bool = Field(description='If True, perform relaxation of the cell volume', default=True) convergence_on: bool = Field(description='If True, perform convergence checks within the workchain', default=True) convergence_absolute: bool = Field( description='If True, use absolute values where possible when performing convergence checks', default=False, ) convergence_max_iterations: int = Field(description='Maximum iterations for convergence checking', default=5) convergence_positions: float = Field( description=( 'The cutoff value for the convergence check on positions in Angstram. A negative value by pass the check.' ), default=0.1, ) convergence_volume: float = Field( description='The cutoff value for the convergence check on volume between the two structures.' ' A negative value by pass the check.', default=0.01, ) convergence_shape_lengths: float = Field( description='The cutoff value for the convergence check on the lengths of the unit cell' ' vectors, between input and the outputs structure. A negative value by pass' ' the check.', default=0.1, ) convergence_shape_angles: float = Field( description='The cutoff value for the convergence check on the angles of the unit cell vectors, ' 'between input and the outputs structure. A' ' negative value by pass the check.', default=0.1, ) convergence_mode: str = Field( description="Mode of the convergence check for positions. 'inout' for checking input/output structure, " "or 'last' to check only the change of" ' the last step.', examples=['inout', 'last'], default='last', ) reuse: bool = Field( description='Whether reuse the previous calculation by copying over the remote folder', default=False, ) clean_reuse: bool = Field( description='Whether to perform a final cleaning of the reused calculations', default=True ) keep_sp_workdir: bool = Field( description='Whether to keep the workdir of the final singlepoint calculation', default=False ) perform: bool = Field(description="Do not perform any relaxation if set to 'False'", default=True) hybrid_calc_bootstrap: bool = Field( description='Whether to bootstrap hybrid calculation by performing standard DFT first', default=False ) hybrid_calc_bootstrap_wallclock: int = Field( description='Wall time limit in second for the bootstrap calculation', default=3600 ) keep_magnetization: bool = Field( description='Whether to keep magnetization from the previous calculation if possible', default=False ) double_relax_mode: bool = Field( description='Experimental: Run in double relax mode - launch of the sub workflow is only performed up to two ' 'times without checking convergence in the end. This is useful for cases where the convergence is difficult ' 'due to change of basis set with variable cell and high-throughput studies.', default=False, ) residual_forces_check: bool = Field( description='Whether to perform residual force check after relaxation to ensure the forces are below threshold', default=True, )
# TODO: implement pyandtic checks # @classmethod # def validate_dict(cls, input_dict, port=None): # """Check mutually exclusive fields""" # super().validate_dict(input_dict, port) # if isinstance(input_dict, orm.Dict): # input_dict = input_dict.get_dict() # force_cut = input_dict.get('force_cutoff') # energy_cut = input_dict.get('energy_cutoff') # if force_cut is None and energy_cut is None: # raise InputValidationError("Either 'force_cutoff' or 'energy_cutoff' should be supplied") # if (force_cut is not None) and (energy_cut is not None): # raise InputValidationError("Cannot set both 'force_cutoff' and 'energy_cutoff'")
[docs] class ConvOptions(OptionContainer): """Template for the Dict node controlling the workchain behaviour""" cutoff_start: float = Field(description='The starting cut-off energy', default=300.0) cutoff_stop: float = Field(description='The Final cut-off energy', default=700.0) cutoff_step: float = Field(description='Step size of the cut-off energy', default=50.0) kspacing_start: float = Field(description='The starting kspacing', default=0.07) kspacing_stop: float = Field(description='The final kspacing', default=0.02) kspacing_step: float = Field(description='Step size of the cut-off energy', default=0.01) cutoff_kconv: float = Field(description='The cut-off energy used for kpoints convergence tests', default=450.0) kspacing_cutconv: float = Field( description='The kpoints spacing used for cut-off energy convergence tests', default=0.07 )
[docs] class BandOptions(OptionContainer): """Options for VaspRelaxWorkChain""" symprec: float = Field(description='Precision of the symmetry determination', default=0.01) band_mode: str = Field( description=( 'Mode for generating the band path. Choose from: bradcrack, pymatgen,seekpath-aiida and latimer-munro.' ), examples=['bradcrack', 'pymatgen', 'seekpath', 'seekpath-aiida', 'latimer-munro'], default='seekpath-aiida', ) # TODO: enable explicit seekpath passing band_kpoints_distance: float = Field( description='Spacing for band distances for automatic kpoints generation, used by seekpath-aiida mode.', default=0.025, ) line_density: float = Field( description='Density of the point along the path, used by the sumo interface.', default=20, ) dos_kpoints_distance: float = Field( description=( 'Kpoints for running DOS calculations in A^-1 * 2pi. Will perform non-SCF DOS calculation is supplied.' ), default=0.03, ) only_dos: bool = Field( description='Flag for running only DOS calculations', default=False, ) run_dos: bool = Field( description='Flag for running DOS calculations', default=False, ) additional_band_analysis_parameters: dict = Field( description='Additional keyword arguments for the seekpath/ interface, available keys are:' " ['with_time_reversal', 'reference_distance', 'recipe', 'threshold', 'symprec', 'angle_tolerance']", default={}, ) kpoints_per_split: int = Field( description='Number of kpoints per split for the band structure calculation', default=80, ) hybrid_reuse_wavecar: bool = Field( description='Whether to reuse the WAVECAR from the previous relax/singlepoint calculation', default=False )