From ed97029ceac50cb6ad1bc259849b9bd5a074f44f Mon Sep 17 00:00:00 2001 From: Miki Bonacci Date: Thu, 8 Feb 2024 13:53:55 +0100 Subject: [PATCH] API docs as jn --- .../{per_site => intra_site}/charge.py | 2 +- .../{per_site => intra_site}/mass.py | 2 +- .../{per_site => intra_site}/position.py | 2 +- .../{per_site => intra_site}/symbols.py | 2 +- .../properties/property_collector.py | 19 +- .../structure/properties/property_utils.py | 8 +- aiida_atomistic/data/structure/structure.py | 9 +- .../user_guide/structuredata/API_start.ipynb | 467 ++++++++++++++++++ 8 files changed, 489 insertions(+), 22 deletions(-) rename aiida_atomistic/data/structure/properties/{per_site => intra_site}/charge.py (99%) rename aiida_atomistic/data/structure/properties/{per_site => intra_site}/mass.py (99%) rename aiida_atomistic/data/structure/properties/{per_site => intra_site}/position.py (96%) rename aiida_atomistic/data/structure/properties/{per_site => intra_site}/symbols.py (98%) create mode 100644 docs/source/user_guide/structuredata/API_start.ipynb diff --git a/aiida_atomistic/data/structure/properties/per_site/charge.py b/aiida_atomistic/data/structure/properties/intra_site/charge.py similarity index 99% rename from aiida_atomistic/data/structure/properties/per_site/charge.py rename to aiida_atomistic/data/structure/properties/intra_site/charge.py index 98648c2..239a773 100644 --- a/aiida_atomistic/data/structure/properties/per_site/charge.py +++ b/aiida_atomistic/data/structure/properties/intra_site/charge.py @@ -12,7 +12,7 @@ class Charge(BaseProperty): """ The charge property. """ - domain = "per-site" + domain = "intra-site" default_kind_threshold = 0.45 # units... maybe specify in the docs. value: List[float] = Field(default=None) diff --git a/aiida_atomistic/data/structure/properties/per_site/mass.py b/aiida_atomistic/data/structure/properties/intra_site/mass.py similarity index 99% rename from aiida_atomistic/data/structure/properties/per_site/mass.py rename to aiida_atomistic/data/structure/properties/intra_site/mass.py index e42b332..b571527 100644 --- a/aiida_atomistic/data/structure/properties/per_site/mass.py +++ b/aiida_atomistic/data/structure/properties/intra_site/mass.py @@ -13,7 +13,7 @@ class Mass(BaseProperty): """ The mass property. """ - domain = "per-site" + domain = "intra-site" default_kind_threshold = 1e-3 # units... maybe specify in the docs. value: List[float] = Field(default=None) diff --git a/aiida_atomistic/data/structure/properties/per_site/position.py b/aiida_atomistic/data/structure/properties/intra_site/position.py similarity index 96% rename from aiida_atomistic/data/structure/properties/per_site/position.py rename to aiida_atomistic/data/structure/properties/intra_site/position.py index 2e6de8f..cd855f6 100644 --- a/aiida_atomistic/data/structure/properties/per_site/position.py +++ b/aiida_atomistic/data/structure/properties/intra_site/position.py @@ -9,7 +9,7 @@ class Positions(BaseProperty): """ The sites property. """ - domain = "per-site" + domain = "intra-site" #kind_threshold: float = Field(default=1e-3) value: List[Tuple[float,float,float]] = Field(default=None) kind_tags: List[str] = Field(default=None) # thgis should be in position class? Unified, I think so. tipo alla fine del to kind fai un check e resetti. diff --git a/aiida_atomistic/data/structure/properties/per_site/symbols.py b/aiida_atomistic/data/structure/properties/intra_site/symbols.py similarity index 98% rename from aiida_atomistic/data/structure/properties/per_site/symbols.py rename to aiida_atomistic/data/structure/properties/intra_site/symbols.py index 8de4394..31e83f1 100644 --- a/aiida_atomistic/data/structure/properties/per_site/symbols.py +++ b/aiida_atomistic/data/structure/properties/intra_site/symbols.py @@ -13,7 +13,7 @@ class Symbols(BaseProperty): """ The symbols property, intended as the chemical symbols for each atom(site). """ - domain = "per-site" + domain = "intra-site" # units... maybe specify in the docs. value: List[Literal[_valid_symbols]] diff --git a/aiida_atomistic/data/structure/properties/property_collector.py b/aiida_atomistic/data/structure/properties/property_collector.py index d2f3dfa..e267150 100644 --- a/aiida_atomistic/data/structure/properties/property_collector.py +++ b/aiida_atomistic/data/structure/properties/property_collector.py @@ -6,10 +6,10 @@ from aiida_atomistic.data.structure.properties.globals.cell import Cell from aiida_atomistic.data.structure.properties.globals.pbc import Pbc -from aiida_atomistic.data.structure.properties.per_site.position import Positions -from aiida_atomistic.data.structure.properties.per_site.symbols import Symbols -from aiida_atomistic.data.structure.properties.per_site.mass import Mass -from aiida_atomistic.data.structure.properties.per_site.charge import Charge +from aiida_atomistic.data.structure.properties.intra_site.position import Positions +from aiida_atomistic.data.structure.properties.intra_site.symbols import Symbols +from aiida_atomistic.data.structure.properties.intra_site.mass import Mass +from aiida_atomistic.data.structure.properties.intra_site.charge import Charge from aiida_atomistic.data.structure.properties.custom import CustomProperty @@ -72,8 +72,9 @@ def __init__( self._inspect_properties(properties) - def get_valid_properties(self,): + """def get_supported_properties(self,): return list(typing.get_type_hints(self.__class__).keys()) + """ def get_property_attribute(self, key): # In AiiDA this could be self.base.attrs['properties'][key] or similar @@ -86,11 +87,11 @@ def _inspect_properties(self,properties): have a defined prefix. """ for pname,pvalue in properties.items(): - if pname not in self.get_valid_properties(): - raise NotImplementedError(f"Property '{pname}' is not yet supported.\nSupported properties are: {self.get_valid_properties()}") + if pname not in self.get_supported_properties(): + raise NotImplementedError(f"Property '{pname}' is not yet supported.\nSupported properties are: {self.get_supported_properties()}") # custom properties: - #elif pname in self.get_valid_properties(): - # raise NotImplementedError(f"Property '{pname}' is not yet supported.\nSupported properties are: {self.get_valid_properties()}") + #elif pname in self.get_supported_properties(): + # raise NotImplementedError(f"Property '{pname}' is not yet supported.\nSupported properties are: {self.get_supported_properties()}") elif not pvalue: raise ValueError(f"Property '{pname}' has not value provided.") elif len(pvalue)==0: diff --git a/aiida_atomistic/data/structure/properties/property_utils.py b/aiida_atomistic/data/structure/properties/property_utils.py index 16a31ae..35945ff 100644 --- a/aiida_atomistic/data/structure/properties/property_utils.py +++ b/aiida_atomistic/data/structure/properties/property_utils.py @@ -154,14 +154,14 @@ def _database_wise_setter(self, pname, pvalue): self._parent.base.attributes.set("_property_attributes",property_attributes) return - def get_valid_properties(self): + def get_supported_properties(self): # Get the implemented properties - return self._valid_properties.copy() + return list(self._valid_properties.copy()) - def get_defined_properties(self): + def get_stored_properties(self): # Get the properties that you already set property_attributes = self._parent.base.attributes.get("_property_attributes") - return list(set(self.get_valid_properties()).intersection( + return list(set(self.get_supported_properties()).intersection( property_attributes.keys() )) diff --git a/aiida_atomistic/data/structure/structure.py b/aiida_atomistic/data/structure/structure.py index 4173f3b..39b5b6c 100644 --- a/aiida_atomistic/data/structure/structure.py +++ b/aiida_atomistic/data/structure/structure.py @@ -783,14 +783,13 @@ def properties(self): @properties.setter def properties(self,value): raise AttributeError("After the initialization, `properties` is a read-only attribute") - - + def to_dict(self): """ Returns a dictionary with the properties defined. Used to generate new StructureData with some changed/updated properties. """ - return self.base.attributes.get('_property_attributes') + return copy.deepcopy(self.base.attributes.get('_property_attributes')) def get_kinds(self, use_kind_tag = False, use_kind_name = True): @@ -830,9 +829,9 @@ def get_kinds(self, use_kind_tag = False, use_kind_name = True): # Step 1: kind_properties = [] kinds_values = {} - for single_property in self.properties.get_defined_properties(): + for single_property in self.properties.get_stored_properties(): prop = getattr(self.properties,single_property) - if prop.domain == "per-site" and not single_property in ["symbols","positions"]: + if prop.domain == "intra-site" and not single_property in ["symbols","positions"]: kinds_per_property = prop.to_kinds() kind_properties.append(kinds_per_property[0]) kinds_values[single_property] = kinds_per_property[1] diff --git a/docs/source/user_guide/structuredata/API_start.ipynb b/docs/source/user_guide/structuredata/API_start.ipynb new file mode 100644 index 0000000..6010fd8 --- /dev/null +++ b/docs/source/user_guide/structuredata/API_start.ipynb @@ -0,0 +1,467 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# The StructureData object" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here we present the new `StructureData` class and we explain the basics on the API. \n", + "This is an extended version of the `orm.StructureData` implemented in `aiida-core`.\n", + "\n", + "Each property of the system is now stored in the `properties` attribute. Among the supported properties, we have:\n", + "\n", + "- cell\n", + "- periodic boundary conditions (PBC)\n", + "- positions\n", + "- symbols \n", + "- masses\n", + "- electronic charge\n", + "- magnetization - TOBE added\n", + "- Hubbard U and V parameters - TOBE added\n", + "\n", + "Some of these properties are related to the sites/atoms (e.g. atomic positions, symbols, electronic charge) and some are related to the whole structure (e.g. PBC, cell). So, each property will have an attribute `domain`, which can be \"intra-site\", \"inter-site\", \"global\". \n", + "\n", + "
\n", + " Important: we deprecate the kind-based definition of the properties, now site-based. This simplifies multiple properties defintion and respect the philosophy of a code-agnostic representation of the structure. The kinds determination can be done using the built-in `get_kinds` method of the StructureData. It is also possible to provide a user-defined set of kinds, as you will see in the following section.\n", + "
\n", + "\n", + "The possibility to have user defined custom properties is discussed in another section (TOBE ADDED).\n", + "\n", + "To explore the available properties in detail, please go to the corresponding pages (TOBE ADDED)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[93m\u001b[1mWarning\u001b[0m: \u001b[22mYou are currently using a post release development version of AiiDA: 2.4.0.post0\u001b[0m\n", + "\u001b[93m\u001b[1mWarning\u001b[0m: \u001b[22mBe aware that this is not recommended for production and is not officially supported.\u001b[0m\n", + "\u001b[93m\u001b[1mWarning\u001b[0m: \u001b[22mDatabases used with this version may not be compatible with future releases of AiiDA\u001b[0m\n", + "\u001b[93m\u001b[1mWarning\u001b[0m: \u001b[22mas you might not be able to automatically migrate your data.\n", + "\u001b[0m\n" + ] + }, + { + "data": { + "text/plain": [ + "Profile" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from aiida import orm, load_profile\n", + "load_profile()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## [Initialization of a StructureData instance](#toc0_)\n", + "One of the principle of the new StructureData is the fact that it is \"just\" a container of the information about a given structure: this means that, after that instances of this class are immutable. After the initialization, it is not possible to change the stored properties.\n", + "\n", + "### [How to define the properties](#toc0_)\n", + "Properties should be contained in a dictionary, where the keys are the names of the properties, and the values are dictionaries of this type:\n", + "\n", + "```python\n", + "{\n", + " \"value\":value_of_the_property,\n", + " ... #other possible attributes of the property \n", + "}\n", + "```\n", + "\n", + "for example, we can provide the `positions` property by means of the following dictionary:\n", + "```python\n", + "{\n", + " \"value\":[[1,1,1],[2,3,3],[3,4,5],[1,2,3]],\n", + " \"kind_tags\":[\"H1\",\"H2\",\"H3\",\"H4\"],\n", + "}\n", + "```\n", + "\n", + "where the `kind_tags` key provides user-defined set of kinds. This key is optional, as the kinds can (should) be determined at the plugin level.\n", + "\n", + "Here below, an example in which we define a structure together with the mass and charge properties:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "properties = {\n", + " \"cell\":{\"value\":[[3.5, 0.0, 0.0], [0.0, 3.5, 0.0], [0.0, 0.0, 3.5]]},\n", + " \n", + " \"pbc\":{\"value\":[True,True,True]},\n", + " \n", + " \"positions\":{\"value\":[[0.0, 0.0, 0.0],[1.5, 1.5, 1.5]],},\n", + " \n", + " \"symbols\":{\"value\":[\"Li\", \"Li\"]},\n", + " \n", + " \"mass\":{\"value\":[6.941,6.945],},\n", + " \n", + " \"charge\":{\"value\":[1,0]}\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from aiida_atomistic.data.structure.structure import StructureData\n", + "\n", + "structure = StructureData(properties = properties)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can inspect the properties by accessing the corresponding attribute (tab completion is enabled):" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Mass(parent=, value=[6.941, 6.945], domain='intra-site', default_kind_threshold=0.001)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "structure.properties.mass" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[6.941, 6.945]" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "structure.properties.mass.value" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'intra-site'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "structure.properties.mass.domain" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Additionally, we can access the property using the `get_property_attribute` method:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'value': [6.941, 6.945]}" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "structure.properties.get_property_attribute(\"mass\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Inspect the supported and stored properties from the StructureData instance:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['positions', 'cell', 'pbc', 'mass', 'symbols', 'custom', 'charge']" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "structure.properties.get_supported_properties()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['positions', 'cell', 'pbc', 'mass', 'symbols', 'charge']" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "structure.properties.get_stored_properties()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Missing: access the supported property from the class object\n", + "\n", + "```python\n", + "In [1]: StructureData.get_supported_properties()\n", + "Out [2]: ['custom', 'pbc', 'symbols', 'charge', 'positions', 'mass', 'cell']\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The immutability of the StructureData instance" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A crucial aspect of the new `StructureData` is that it is immutable even if the node is not stored, i.e. the API does not support on-the-fly or interactive modifications (it will raise errors). This helps in avoiding unexpected \n", + "behaviour coming from a step-by-step defintion of the structure, e.g. incosistencies between properties definitions, which are then not cross-checked again.\n", + "\n", + "One has to define a new `StructureData` instance by scratch.\n", + "To make user life simpler, we provide a `to_dict` method, which can be used to generate the properties dictionary. This can be updated and used for a new StructureData instance:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'cell': {'value': [[3.5, 0.0, 0.0], [0.0, 3.5, 0.0], [0.0, 0.0, 3.5]]},\n", + " 'pbc': {'value': [True, True, True]},\n", + " 'positions': {'value': [[0.0, 0.0, 0.0], [1.5, 1.5, 1.5]]},\n", + " 'symbols': {'value': ['Li', 'Li']},\n", + " 'mass': {'value': [6.941, 6.945]},\n", + " 'charge': {'value': [1, 0]}}" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "new_properties = structure.to_dict()\n", + "new_properties" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "new_properties[\"mass\"][\"value\"] = [6.941,6.941]" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "new_structure = StructureData(properties=new_properties)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[6.941, 6.941]" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "new_structure.properties.mass.value" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### How to get kinds\n", + "\n", + "It is possible to get a list of kinds using the `get_kinds` method. This will generate the corresponding predicted kinds for all the properties (the \"intra-site\" ones) and then generate the list of global different kinds. \n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Li\n", + "Li\n" + ] + }, + { + "data": { + "text/plain": [ + "([0, 1],\n", + " ['Li0', 'Li1'],\n", + " {'mass': [6.9415, 6.945500000000001], 'charge': [1.1250000000000002, 0.225]})" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "structure.get_kinds()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Kinds are determined using, for each property, a given threshold. There is a default threshold:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.001" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "structure.properties.mass.default_kind_threshold" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Basically, it will be possible to override the threshold to be used for each property (TOBE implemented):\n", + "\n", + "```python\n", + "structure.get_kinds(thr={\"mass\":0.1})\n", + "```" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aiida-muonic", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.11" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}