Source code for aiida_vasp.common
"""
Common functions and constants
"""
# Name of the override name space
# This is the namespace where raw VASP INCAR tags should reside for VaspWorkChain
import warnings
from functools import wraps
from typing import Any, Callable
from aiida import orm
from aiida.common.exceptions import InputValidationError
from aiida.common.extendeddicts import AttributeDict
from aiida_vasp.assistant.parameters import _BASE_NAMESPACES, ParametersMassage
from aiida_vasp.utils.aiida_utils import convert_dict_case
OVERRIDE_NAMESPACE = 'incar'
# pylint:disable=raise-missing-from
[docs]
def aiida_to_python(entity: Any) -> Any:
"""
Convert AiiDA entity to plain python objects
"""
if not isinstance(entity, orm.Data):
return entity
if isinstance(entity, orm.Dict):
return entity.get_dict()
if isinstance(entity, orm.List):
return entity.get_list()
if isinstance(entity, (orm.Float, orm.Str, orm.Int)):
return entity.value
raise ValueError(f'{entity} cannot be converted to plain python object')
[docs]
def plain_python_args(func: Callable[..., Any]) -> Callable[..., Any]:
"""Ensure that the first argument is a plain dictionary"""
@wraps(func)
def wrapped(*args: Any, **kwargs: Any) -> Any:
new_args = list(args)
new_args[0] = aiida_to_python(args[0])
return func(*new_args, **kwargs)
return wrapped
[docs]
def parameters_validator(node: orm.Dict | None, port: Any = None) -> None:
"""
Validate the parameters input by passing it through the massager
"""
_ = port
if not node:
return
pdict = node.get_dict()
try:
convert_dict_case(pdict, lower=True, raise_convert=node.is_stored)
except ValueError as error:
raise InputValidationError(
f'Case inconsistency found in the parameters dictionary please use lower case keys: {error}'
)
if not node.is_stored:
node.set_dict(convert_dict_case(pdict, lower=True, warn=True))
if OVERRIDE_NAMESPACE not in pdict:
raise InputValidationError(f'Would expect some incar tags supplied under {OVERRIDE_NAMESPACE} key!')
accepted_namespaces = _BASE_NAMESPACES + [OVERRIDE_NAMESPACE]
new_dict = {key: value for key, value in pdict.items() if key in accepted_namespaces}
try:
ParametersMassage(new_dict)
except Exception as error:
raise InputValidationError(f'Cannot validate the input parameters - error from massager: {error}')
[docs]
def warn_deprecated_options(node: orm.Dict | None, port: Any = None) -> None:
"""
Validate the parameters input by passing it through the massager
"""
_ = port
_ = node
warnings.warn('The use of `options` port is deprecated, please use `calc.metadata.options` instead.')
[docs]
@plain_python_args
def site_magnetization_to_magmom(site_dict: dict[str, Any]) -> list[float]:
"""
Convert site mangetization to MAGMOM used for restart
NOTE: to be replaced by stock function in aiida_vasp.utils.workchains
"""
if 'site_magnetization' in site_dict:
site_dict = site_dict['site_magnetization']
site_dict = site_dict['sphere']
to_use = None
for symbol in 'xyz':
if site_dict.get(symbol) and site_dict.get(symbol, {}).get('site_moment'):
to_use = symbol
break
# No avaliable site magnetization for setting MAGMOM, something is wrong
if to_use is None:
raise ValueError('No valid site-projected magnetization avaliable')
# Ensure sorted list
tmp = list(site_dict[to_use]['site_moment'].items())
tmp.sort(key=lambda x: int(x[0]))
return [entry[1]['tot'] for entry in tmp]
[docs]
def nested_update(dict_in: dict[str, Any], update_dict: dict[str, Any], extend_list: bool = False) -> dict[str, Any]:
"""Update the dictionary - combine nested sub-dictionary with update as well"""
warnings.warn('nested_update is deprecated, use updated_nested_dict', DeprecationWarning)
for key, value in update_dict.items():
if key in dict_in and isinstance(value, (dict, AttributeDict)):
nested_update(dict_in[key], value, extend_list=extend_list)
elif key in dict_in and isinstance(value, list) and extend_list:
dict_in[key].extend(value)
else:
dict_in[key] = value
return dict_in
[docs]
def nested_update_dict_node(dict_node: orm.Dict, update_dict: dict[str, Any], extend_list: bool = False) -> orm.Dict:
"""Utility to update a Dict node in a nested way"""
warnings.warn('nested_update_dict_node is deprecated, use updated_nested_dict_node', DeprecationWarning)
pydict = dict_node.get_dict()
nested_update(pydict, update_dict, extend_list=extend_list)
if pydict == dict_node.get_dict():
return dict_node
return orm.Dict(dict=pydict)