Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/mutable helper class #22

Merged
merged 24 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5f25772
Introducing the kind checks.
mikibonacci Mar 2, 2024
88db339
Starting to migrate to mkdocs
mikibonacci Mar 3, 2024
0d0d892
StructureData developer guide,
mikibonacci Mar 3, 2024
3ce21d6
First profiling for the atomistic vs orm StructureData generation.
mikibonacci Mar 3, 2024
8073366
Comment on profiling
mikibonacci Mar 3, 2024
6a77b2b
Additional notes on profilings
mikibonacci Mar 3, 2024
5df4776
Adding a last scaling test, to be commented and inserted in the githu…
mikibonacci Mar 3, 2024
07b1fed
Adding the automatic kind generation.
mikibonacci Mar 5, 2024
4341bcb
Adding the `allow_kinds` input in the StructureData init.
mikibonacci Mar 5, 2024
0762995
Adding the `to_legacy_structuredata` method in atomistc.StructureData
mikibonacci Mar 8, 2024
cccc60f
- Adding the developer docs
mikibonacci Apr 7, 2024
adb66ba
Added user guide with examples
mikibonacci Apr 12, 2024
b8aab3e
Fixing https://github.com/aiidateam/aiida-atomistic/issues/16
mikibonacci Apr 19, 2024
5dcd3c9
This solves issue https://github.com/aiidateam/aiida-atomistic/issues/10
mikibonacci May 8, 2024
9c88cf2
New implementation, following the pydantic PR.
mikibonacci May 15, 2024
4e646d1
Added the support to read charges also from ASE Atoms object.
mikibonacci May 15, 2024
035a050
Reverting back to non-pydantic implementation (Just removing the Model
mikibonacci May 15, 2024
2fac55f
Attaching the properties to the Sites.
mikibonacci May 28, 2024
8423553
adding the possiblity to provide lists in the constructor BUT we are
mikibonacci May 29, 2024
0bf175c
Moving the get_kinds and _to_kinds (now a private method) into the
mikibonacci May 29, 2024
406b2ed
Removed kind related methods which now are no more needed in the
mikibonacci May 29, 2024
963be51
Utilities for cell and sites update
mikibonacci Jul 15, 2024
2f4e31c
New StructureData and StructureDataMutable
mikibonacci Jul 17, 2024
1950aa0
Allowing also magmom to be provided as floats: they
mikibonacci Jul 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 0 additions & 39 deletions .pre-commit-config.yaml

This file was deleted.

46 changes: 28 additions & 18 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,37 @@ def supported_properties():
"""
Should be updated every time I add properties.
"""
return ['cell', 'pbc', 'positions', 'symbols', 'mass', 'charge', 'custom']
return [
"cell",
"pbc",
"position",
"symbol",
"mass",
"charge",
"magmom",
"kind_name",
"weight",
]


@pytest.fixture
def example_properties():
def example_structure_dict():
"""
Return the dictionary of properties as to be used in the standards tests.
"""
unit_cell = [[3.5, 0.0, 0.0], [0.0, 3.5, 0.0], [0.0, 0.0, 3.5]]
atomic_positions = [[0.0, 0.0, 0.0],[1.5, 1.5, 1.5]]
symbols = ["Li"]*2
mass = [6.941,6.941]
charge = [1,0]

properties = {
"cell":{"value":unit_cell},
"pbc":{"value":[True,True,True]},
"positions":{"value":atomic_positions,},
"symbols":{"value":symbols},
"mass":{"value":mass,},
"charge":{"value":charge}
}
return properties
structure_dict = {
"pbc": (True, True, True),
"cell": [[0.0, 1.8, 1.8], [1.8, 0.0, 1.8], [1.8, 1.8, 0.0]],
"sites": [
{
"symbol": "Cu",
"kind_name": "Cu2",
"position": [0.0, 0.0, 0.0],
"mass": 63.546,
"charge": 1.0,
"magmom": 0.0,
}
],
}

return structure_dict
1 change: 0 additions & 1 deletion docs/.gitignore

This file was deleted.

41 changes: 0 additions & 41 deletions docs/Makefile

This file was deleted.

12 changes: 1 addition & 11 deletions docs/source/index.md → docs/docs/index.md
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,6 @@ StructureData documentation is in the User guide/StructureData section.

In principle, this should collect all the [AiiDA data types related to material science](https://aiida.readthedocs.io/projects/aiida-core/en/latest/topics/data_types.html#materials-science-data-types).


```{toctree}
:maxdepth: 2
:hidden: true

user_guide/index
developer_guide/index
apidoc/aiida_atomistic
```

If you use this plugin for your research, please cite the following work:

> Author Name1, Author Name2, *Paper title*, Jornal Name XXX, YYYY (Year).
Expand All @@ -31,5 +21,5 @@ If you use AiiDA for your research, please cite the following work:

``aiida-atomistic`` is released under the MIT license.

Please contact mikibonacci@psi.ch for information concerning ``aiida-atomistic`` and
Please contact miki.bonacci@psi.ch for information concerning ``aiida-atomistic`` and
the [`AiiDA mailing list`](http://www.aiida.net/mailing-list/) for questions concerning ``aiida``.
Empty file.
Empty file.
222 changes: 222 additions & 0 deletions docs/docs/source/user_guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
# The new `StructureData` API

## The `atomistic.StructureData` and `atomistic.StructureDataMutable` classes

Two main rules: (i) immutability, and (ii) site-based. This means that our node will be just a container of the crystal structure + properties, and it cannot really be modified in any way.
We will only provide `from_*`, `get_*` and `to_*` methods. Each site property will be defined as site-based and not kind-based, at variance with the old `orm.StructureData`. Kinds now can be defined as a property of each site (`kind_name`).

The idea is to provide another python class which is just the mutable version of the `atomistic.StructureData` used to build, manipulate the crystal structure before the effective AiiDA node initialization. For now, let's call this class `StructureDataMutable`. This two classes have the same data structure, i.e. the same `properties` and the same `from_*`, `get_*` and `to_*` methods. The only difference is that the `atomistic.StructureDataMutable` has also `set_*` methods which can be used to mutate the properties. **Rule**: no property can be modified directly (i.e. accessing it); this is useful to avoid the introduction of inconsistencies in the structure instance.


## How to initialize the `StructureData`(s)

As both `StructureData` and `StructureDataMutable` share the same data structure, they also share the same constructor input parameter, which is just a python dictionary. The format of this dictionary exactly reflects how the data are store in the AiiDA database:

```python=

from aiida_atomistic.data.structure.core import StructureData
from aiida_atomistic.data.structure.mutable import StructureDataMutable

structure_dict = {
'cell':[[2.75,2.75,0],[0,2.75,2.75],[2.75,0,2.75]],
'pbc': [True,True,True],
'sites':[
{
'symbol':'Si',
'position':[3/4, 3/4, 3/4],
},
{
'symbol':'Si',
'position':[1/2, 1/2, 1/2],
},
],
}

mutable_structure = StructureDataMutable(data=structure_dict)
structure = StructureData(data=structure_dict)
```

When this dictionary is provided to the constructor, validation check for each of the provided property is done (**for now, only pbc and cell**).
Then, you can access the properties directly:

```python=
print("immutable pbc: ",structure.pbc)
print("mutable pbc: ",structure.pbc)

print("immutable cell: ",structure.cell)
print("mutable cell: ",structure.cell)

print("immutable sites: ",structure.sites)
print("mutable sites: ",structure.sites)
```

the expected output is:

```shell=
immutable pbc: [ True True True]
mutable pbc: [ True True True]
immutable cell: [[2.75 2.75 0. ]
[0. 2.75 2.75]
[2.75 0. 2.75]]
mutable cell: [[2.75 2.75 0. ]
[0. 2.75 2.75]
[2.75 0. 2.75]]
immutable sites: [<Site: kind name 'Si' @ 0.75,0.75,0.75>, <Site: kind name 'Si' @ 0.5,0.5,0.5>]
mutable sites: [<Site: kind name 'Si' @ 0.75,0.75,0.75>, <Site: kind name 'Si' @ 0.5,0.5,0.5>]
```

To inspect the properties of a single site, we can access it:

```python=
print(structure.sites[0].symbol,structure.sites[0].position) # output: Si [0.75 0.75 0.75]
```

All the properties can be accessed via tab completion, and a list of the supported properties can be accessed via `structure.get_property_names()`.

For now, other supported properties are `charge` (not yet `tot_charge`), `kind_name`, `mass`.
For example, we can initialize a charged structure in this way:


```python=
structure_dict = {
'cell':[[2.75,2.75,0],[0,2.75,2.75],[2.75,0,2.75]],
'pbc': [True,True,True],
'sites':[
{
'symbol':'Si',
'position':[3/4, 3/4, 3/4],
'charge': +1,
'kind_name': 'Si2',
},
{
'symbol':'Si',
'position':[1/2, 1/2, 1/2],
'kind_name': 'Si1',
},
],
}

mutable_structure = StructureDataMutable(data=structure_dict)
structure = StructureData(data=structure_dict)
```

then, `structure.sites[0].charge` will be equal to 1. When the plugins will be adapted, with this information we can build the correct input file for the corresponding quantum engine.

### Initialization from ASE or Pymatgen

If we already have an ASE Atoms or a Pymatgen Structure object, we can use the `from_ase` and `from_pymatgen` methods:

```python=
from ase.build import bulk
atoms = bulk('Cu', 'fcc', a=3.6)
atoms.set_initial_charges([1,])
atoms.set_tags(["2"])

mutable_structure = StructureDataMutable.from_ase(atoms)
structure = StructureData.from_ase(atoms)

structure.to_dict()
```

This should have as output:

```shell=
{'pbc': (True, True, True),
'cell': [[0.0, 1.8, 1.8], [1.8, 0.0, 1.8], [1.8, 1.8, 0.0]],
'sites': [{'symbol': 'Cu',
'kind_name': 'Cu2',
'position': [0.0, 0.0, 0.0],
'mass': 63.546,
'charge': 1.0,
'magmom': 0.0}]}
```

This support also the properties like charges (coming soon: magmoms and so on). In the same way, for pymatgen we can proceed as follows:

```python=
from pymatgen.core import Lattice, Structure, Molecule

coords = [[0, 0, 0], [0.75,0.5,0.75]]
lattice = Lattice.from_parameters(a=3.84, b=3.84, c=3.84, alpha=120,
beta=90, gamma=60)
struct = Structure(lattice, ["Si", "Si"], coords)

struct.add_oxidation_state_by_site([1,0])

mutable_structure = StructureDataMutable.from_pymatgen(struct)

mutable_structure.to_dict()
```

the output being:

```shell=
{'pbc': (True, True, True),
'cell': [[3.84, 0.0, 2.351321854362918e-16],
[1.92, 2.7152900397563426, -1.919999999999999],
[0.0, 0.0, 3.84]],
'sites': [{'symbol': 'Si',
'weights': 28.0855,
'position': [0.0, 0.0, 0.0],
'charge': 1,
'kind_name': 'Si'},
{'symbol': 'Si',
'weights': 28.0855,
'position': [3.84, 1.3576450198781713, 1.9200000000000006],
'charge': 0,
'kind_name': 'Si0'}]}
```

Moreover, we also provide `to_ase` and `to_pymatgen` methods to obtain the corresponding instances. Also this methods for now only support charges, among the new properties.

## Mutation of a structure

Let's suppose you want to update some property in the `StructureData` before to use it in a calculation. You cannot. The way to go is either to use ASE or Pymatgen to modify you object and store it back into `StructureData`, or to use the `StructureDataMutable` and its mutation methods, and then convert it into `StructureData`.
The latter method is the preferred one, as you then have support also for additional properties (to be implemented) like hubbard, which is not supported by the former.

`StructureDataMutable` contains several `set_` methods and more, needed to update a structure:

```python=
from aiida import orm
structure = orm.load_node(<StructureData pk>)

mutable_structure = structure.to_mutable_structuredata()
mutable_structure.set_charges([1,0])
mutable_structure.set_kind_names(['Si2','Si1'])

new_structure = mutable_structure.to_structuredata()
```

Other available methods are `add_atom`, `pop_atom`, `update_site` and so on.
Indeed, we can also start from scratch:

```python=
mutable_structure = StructureDataMutable()
mutable_structure.set_cell([[0.0, 1.8, 1.8], [1.8, 0.0, 1.8], [1.8, 1.8, 0.0]])
mutable_structure.add_atom({
'symbol':'Si',
'position':[3/4, 3/4, 3/4],
'charge': 1,
'kind_name': 'Si2'
})

mutable_structure.add_atom({
'symbol':'Si',
'position':[1/2, 1/2, 1/2],
'charge': 0,
'kind_name': 'Si1'
})
```


## Slicing a structure

It is possible to *slice* a structure, i.e. returning only a part of it (in terms of sites). Let's that you have an heterostructure and you want to obtain only the first layer, composed of the first 4 atoms over 10 total. This works for both `StructureDataMutable` and `StructureData` (we return a new `StructureData` instance).

```python=
sliced_structure = structure[:4]
```

## Backward compatibility support

We can use the `to_legacy` method to return the corresponding `orm.StructureData` instance, in case a given plugin does not yet support the new `StructureData`.
Loading
Loading