"""
Contains van der Waals radii
"""
import collections
from decimal import Decimal
from typing import Dict, Union
from .datum import Datum, print_variables
from .exceptions import DataUnavailableError
from .periodic_table import periodictable
[docs]class VanderWaalsRadii:
r"""Van der Waals radii sets.
Parameters
----------
context : str
{'MANTINA2009'}
Origin of loaded data.
Attributes
----------
vdwr : Dict[str, Datum]
Each van der Waals radius is an entry in `vdwr`, where key is the
"Fe"-cased element symbol if generic or symbol-prefixed label
if specialized within element. The value is a Datum object with
`lbl` the same as key, `units` and `data` value as Decimal object.
doi : str
The DOI of the current context.
name : str
The name of the context ('MANTINA2009')
native_units : str
The units the original data was provided in.
year : int
The year the context was created.
"""
def __init__(self, context: str = "MANTINA2009"):
self.vdwr: Dict[str, Datum] = collections.OrderedDict()
from .data import mantina_2009_vanderwaals_radii
if context == "MANTINA2009":
self.doi = mantina_2009_vanderwaals_radii["doi"]
self.native_units = mantina_2009_vanderwaals_radii["units"]
# TypedDict wont be in until 3.8, have to ignore heterogeneous dicts for now
for vdwr in mantina_2009_vanderwaals_radii["vanderwaals_radii"]: # type: ignore
self.vdwr[vdwr[0]] = Datum(vdwr[0], self.native_units, Decimal(vdwr[1]), doi=self.doi)
else:
raise KeyError("Context set as '{}', only contexts {'MANTINA2009', } are currently supported")
self.name = context
self.year = int(mantina_2009_vanderwaals_radii["date"][:4]) # type: ignore
def __str__(self) -> str:
return "VanderWaalsRadii(context='{}')".format(self.name)
[docs] def get(
self, atom: Union[int, str], *, return_tuple: bool = False, units: str = "bohr", missing: float = None
) -> Union[float, "Datum"]:
r"""
Access a van der Waals radius for species ``atom``.
Parameters
----------
atom
Identifier for element or nuclide, e.g., ``H``, ``C``, ``Al``.
units
Units of returned value. To return in native unit (MANTINA2009: angstrom), pass it explicitly.
Only relevant for ``return_tuple=False`` since ``True`` returns underlying data structure with native units.
missing
How to handle when ``atom`` is valid but outside the available data range. When ``None``, raises DataUnavailableError.
When a float, returns that float, so supply in ``units`` units. Supplying a float is a more compact assurance
that a call will work over all the periodic table than the equivalent
.. code-block:: python
try:
rad = qcel.vdwradii.get(atom)
except qcel.DataUnavailableError:
rad = 4.0
Only relevant for ``return_tuple=False``.
return_tuple
See below.
Returns
-------
float
When ``return_tuple=False``, value of Van der Waals radius. If multiple defined for element, returns largest.
qcelemental.Datum
When ``return_tuple=True``, Datum with units, description, uncertainty, and value of van der Waals radius as Decimal (preserving significant figures).
If multiple defined for element, returns largest.
Raises
------
NotAnElementError
If `atom` cannot be resolved into an element or nuclide or label.
DataUnavailableError
If `atom` is a valid element or nuclide but not one for which a van der Waals radius is available and `missing=None`.
"""
if atom in self.vdwr.keys():
# catch extra labels like 'C_sp3'
identifier = atom
else:
identifier = periodictable.to_E(atom)
try:
assert isinstance(identifier, str) # Should be string by now
qca = self.vdwr[identifier]
except KeyError as e:
if missing is not None and return_tuple is False:
return missing
else:
raise DataUnavailableError("vanderwaals radius", identifier) from e
if return_tuple:
return qca
else:
return qca.to_units(units)
[docs] def string_representation(self) -> str:
"""Print name, value, and units of all van der Waals radii."""
return print_variables(self.vdwr)
[docs] def write_c_header(self, filename: str = "vdwrad.h", missing: float = 2.0) -> None:
r"""Write C header file defining Van der Waals radii array.
Parameters
----------
filename
File name for header. Note that changing this won't change the header guard.
missing
In order that the C array be atomic-number indexable and that it span the
periodic table, this value is used anywhere data is missing.
"""
text = [
"#ifndef _qcelemental_vdwrad_h_",
"#define _qcelemental_vdwrad_h_",
"",
"/* This file is autogenerated from the QCElemental python module */",
"",
"const double vanderwaals_radii[] = {",
]
for el in periodictable.E:
try:
qca = self.vdwr[el]
text.append("{}, /*- [{}] {} {} -*/".format(qca.data, qca.units, qca.label, qca.comment))
except KeyError:
text.append(
"{:.2f}, /*- [{}] {} {} -*/".format(
missing, self.native_units, el, "Default value for missing data"
)
)
text.append("};")
text.append("#endif /* header guard */")
text.append("")
with open(filename, "w") as handle:
handle.write("\n".join(text))
print("File written ({}). Remember to add license and clang-format it.".format(filename))
# singleton
vdwradii = VanderWaalsRadii("MANTINA2009")