"""
FOX.io.cp2k_to_prm
==================
A :class:`TypedMapping<FOX.typed_mapping.TypedMapping>` subclass converting CP2K settings to .prm-compatible values.
Index
-----
.. currentmodule:: FOX.io.cp2k_to_prm
.. autosummary::
PRMMapping
CP2K_TO_PRM
API
---
.. autoclass:: PRMMapping
.. autodata:: CP2K_TO_PRM
:annotation: : MappingProxyType[str, PRMMapping]
A :class:`Mapping<collections.abc.Mapping>` containing :class:`PRMMapping` instances.
.. code:: python
MappingProxyType({
'nonbonded':
PRMMapping(name='nbfix', columns=[2, 3],
key_path=('input', 'force_eval', 'mm', 'forcefield', 'nonbonded', 'lennard-jones'),
key=('epsilon', 'sigma'),
unit=('kcal/mol', 'angstrom'),
default_unit=('kcal/mol', 'kelvin'),
post_process=(None, sigma_to_r2)),
'nonbonded14':
PRMMapping(name='nbfix', columns=[4, 5],
key_path=('input', 'force_eval', 'mm', 'forcefield', 'nonbonded14', 'lennard-jones'),
key=('epsilon', 'sigma'),
unit=('kcal/mol', 'angstrom'),
default_unit=('kcal/mol', 'kelvin'),
post_process=(None, sigma_to_r2)),
'bonds':
PRMMapping(name='bonds', columns=[2, 3],
key_path=('input', 'force_eval', 'mm', 'forcefield', 'bond'),
key=('k', 'r0'),
unit=('kcal/mol/A**2', 'angstrom'),
default_unit=('internal_cp2k', 'bohr'), # TODO: internal_cp2k ?????????
post_process=(None, None)),
'angles':
PRMMapping(name='angles', columns=[3, 4],
key_path=('input', 'force_eval', 'mm', 'forcefield', 'bend'),
key=('k', 'theta0'),
unit=('kcal/mol', 'degree'),
default_unit=('hartree', 'radian'),
post_process=(None, None)),
'urrey-bradley':
PRMMapping(name='angles', columns=[5, 6],
key_path=('input', 'force_eval', 'mm', 'forcefield', 'bend', 'ub'),
key=('k', 'r0'),
unit=('kcal/mol/A**2', 'angstrom'),
default_unit=('internal_cp2k', 'bohr'), # TODO: internal_cp2k ?????????
post_process=(None, None)),
'dihedrals':
PRMMapping(name='dihedrals', columns=[4, 5, 6],
key_path=('input', 'force_eval', 'mm', 'forcefield', 'torsion'),
key=('k', 'm', 'phi0'),
unit=('kcal/mol', 'hartree', 'degree'),
default_unit=('hartree', 'hartree', 'radian'),
post_process=(None, None, None)),
'improper':
PRMMapping(name='improper', columns=[4, 5, 6],
key_path=('input', 'force_eval', 'mm', 'forcefield', 'improper'),
key=('k', 'k', 'phi0'),
unit=('kcal/mol', 'hartree', 'degree'),
default_unit=('hartree', 'hartree', 'radian'),
post_process=(None, return_zero, None)),
})
""" # noqa
from types import MappingProxyType
from typing import (Optional, Iterable, Callable, FrozenSet, TypeVar, Tuple,
Union, Type, Any, ClassVar, Mapping)
from collections import abc
from ..typed_mapping import TypedMapping
try:
from typing import TypedDict
except ImportError: # TypedDict was added in Python 3.8
TypedDict = None
__all__ = ['PRMMapping', 'CP2K_TO_PRM']
KV = TypeVar('KV')
NoneType = type(None)
PostProcess = Callable[[float], float]
[docs]class PRMMapping(TypedMapping):
"""A :class:`TypedMapping<FOX.typed_mapping.TypedMapping>` providing tools for converting CP2K settings to .prm-compatible values.
Parameters
----------
name : :class:`str`
The name of the :class:`PRMContainer<FOX.io.read_prm.PRMContainer>` attribute.
See :attr:`PRMMapping.name`.
columns : :class:`int` or :class:`Iterable<collections.abc.Iterable>` [:class:`int`]
The names relevant :class:`PRMContainer<FOX.io.read_prm.PRMContainer>`
DataFrame columns.
See :attr:`PRMMapping.columns`.
key_path : :class:`str` or :class:`Iterable<collections.abc.Iterable>` [:class:`str`]
The path of CP2K Settings keys leading to the property of interest.
See :attr:`PRMMapping.key_path`.
key : :class:`str` or :class:`Iterable<collections.abc.Iterable>` [:class:`str`]
The key(s) within :attr:`PRMMapping.key_path` containg the actual properties of interest,
*e.g.* ``"epsilon"`` and ``"sigma"``.
See :attr:`PRMMapping.key`.
unit : :class:`str` or :class:`Iterable<collections.abc.Iterable>` [:class:`str`]
The desired output unit.
See :attr:`PRMMapping.unit`.
default_unit : :class:`str` or :class:`Iterable<collections.abc.Iterable>` [:class:`str`, optional]
The default unit as utilized by CP2K.
See :attr:`PRMMapping.default_unit`.
post_process : :class:`Callable<collections.abc.Callable>` or :class:`Iterable<collections.abc.Iterable>` [:class:`Callable<collections.abc.Callable>`]
Callables for post-processing the value of interest.
Set a particular callable to ``None`` to disable post-processing.
See :attr:`PRMMapping.post_process`.
Attributes
----------
name : :class:`str`
The name of the :class:`PRMContainer<FOX.io.read_prm.PRMContainer>` attribute.
columns : :class:`tuple` [:class:`int`]
The names relevant :class:`PRMContainer<FOX.io.read_prm.PRMContainer>`
DataFrame columns.
key_path : :class:`tuple` [:class:`str`]
The path of CP2K Settings keys leading to the property of interest.
key : :class:`tuple` [:class:`str`]
The key(s) within :attr:`PRMMapping.key_path` containg the actual properties of interest,
*e.g.* ``"epsilon"`` and ``"sigma"``.
unit : :class:`tuple` [:class:`str`]
The desired output unit.
default_unit : :class:`tuple` [:class:`str`, optional]
The default unit as utilized by CP2K.
post_process : :class:`tuple` [:class:`Callable<collections.abc.Callable>`, optional]
Callables for post-processing the value of interest.
Set a particular callable to ``None`` to disable post-processing.
""" # noqa
_ATTR: ClassVar[FrozenSet[str]] = frozenset({
'name', 'key', 'columns', 'key_path', 'unit', 'default_unit', 'post_process'
})
def __init__(self, name: str,
key: Union[str, Iterable[str]],
columns: Union[int, Iterable[int]],
key_path: Union[str, Iterable[str]],
unit: Union[str, Iterable[str]],
default_unit: Union[None, str, Iterable[Optional[str]]],
post_process: Union[None, PostProcess, Iterable[Optional[PostProcess]]]) -> None:
"""Initialize a :class:`PRMMapping` instance."""
super().__init__()
self.name = name
self.key = self._to_tuple(key, str)
self.columns = self._to_tuple(columns, int)
self.key_path = self._to_tuple(key_path, str)
self.unit = self._to_tuple(unit, str)
self.default_unit = self._to_tuple(default_unit, (str, NoneType))
self.post_process = self._to_tuple(post_process, (abc.Callable, NoneType))
@staticmethod
def _to_tuple(value: Union[KV, Iterable[KV]],
def_type: Union[Type[KV], Tuple[Type[KV], ...]]) -> Tuple[KV, ...]:
"""Convert **value** into a :class:`tuple`."""
if isinstance(value, tuple):
return value
elif isinstance(value, def_type):
return (value,)
return tuple(value)
if TypedDict is not None:
class _PRMMapping(TypedDict):
name: str
columns: Tuple[str, ...]
key_path: Tuple[str, ...]
key: Tuple[str, ...]
unit: Tuple[str, ...]
default_unit: Tuple[Optional[str], ...]
post_process: Tuple[Optional[PostProcess], ...]
PRMMappingType = Union[PRMMapping, _PRMMapping]
else:
PRMMappingType = PRMMapping
def sigma_to_r2(sigma: float) -> float:
r"""Convert :math:`\sigma` into :math:`\frac{1}{2} R`."""
R = sigma * 2**(1/6)
return R / 2
def return_zero(value: Any) -> int:
"""Return :math:`0`."""
return 0
CP2K_TO_PRM: Mapping[str, PRMMappingType] = MappingProxyType({
'nonbonded':
PRMMapping(name='nbfix', columns=[2, 3],
key_path=('input', 'force_eval', 'mm', 'forcefield', 'nonbonded', 'lennard-jones'), # noqa
key=('epsilon', 'sigma'),
unit=('kcal/mol', 'angstrom'),
default_unit=('kcal/mol', 'kelvin'),
post_process=(None, sigma_to_r2)),
'nonbonded14':
PRMMapping(name='nbfix', columns=[4, 5],
key_path=('input', 'force_eval', 'mm', 'forcefield', 'nonbonded14', 'lennard-jones'), # noqa
key=('epsilon', 'sigma'),
unit=('kcal/mol', 'angstrom'),
default_unit=('kcal/mol', 'kelvin'),
post_process=(None, sigma_to_r2)),
'bonds':
PRMMapping(name='bonds', columns=[2, 3],
key_path=('input', 'force_eval', 'mm', 'forcefield', 'bond'),
key=('k', 'r0'),
unit=('kcal/mol/A**2', 'angstrom'),
default_unit=('internal_cp2k', 'bohr'), # TODO: internal_cp2k ?????????
post_process=(None, None)),
'angles':
PRMMapping(name='angles', columns=[3, 4],
key_path=('input', 'force_eval', 'mm', 'forcefield', 'bend'),
key=('k', 'theta0'),
unit=('kcal/mol', 'degree'),
default_unit=('hartree', 'radian'),
post_process=(None, None)),
'urrey-bradley':
PRMMapping(name='angles', columns=[5, 6],
key_path=('input', 'force_eval', 'mm', 'forcefield', 'bend', 'ub'),
key=('k', 'r0'),
unit=('kcal/mol/A**2', 'angstrom'),
default_unit=('internal_cp2k', 'bohr'), # TODO: internal_cp2k ?????????
post_process=(None, None)),
'dihedrals':
PRMMapping(name='dihedrals', columns=[4, 5, 6],
key_path=('input', 'force_eval', 'mm', 'forcefield', 'torsion'),
key=('k', 'm', 'phi0'),
unit=('kcal/mol', 'hartree', 'degree'),
default_unit=('hartree', 'hartree', 'radian'),
post_process=(None, None, None)),
'improper':
PRMMapping(name='improper', columns=[4, 5, 6],
key_path=('input', 'force_eval', 'mm', 'forcefield', 'improper'),
key=('k', 'k', 'phi0'),
unit=('kcal/mol', 'hartree', 'degree'),
default_unit=('hartree', 'hartree', 'radian'),
post_process=(None, return_zero, None)),
})