- specific thresholds
- standard thresholds
- example on how properties are changing (ex. classification of magmoms wrt threshold and so on)
In this section we will go deeply into the kinds
property and we will explain how to set them both manually and automatically. We suggest the user to use the automatic kinds generation function in the StructureDataMutable
and then check the results before storing the actual AiiDA StructureData
node.
The manual kinds re-assignement should be done only for simple cases (i.e. few atoms), and can be done easily (using the mutable StructureData
):
structure.properties.kinds = ['Fe1', 'Fe2']
Why do we need to define kinds?
Some codes, like Quantum ESPRESSO, distinguish the atoms using the concept of kind
.
This concept allows you to specify distinct atoms (even of the same element) and their associated properties in your simulation, e.g. two iron atoms with different magnetic momenta (see below examples).
Automatic kinds generation¶
In the following, it is shown how to generate kinds in an automatic fashion.
This routine considers only site
properties (charges
, magmoms
and so on), not global
ones (e.g. pbc
, Hubbard
and so on). For now, we don’t consider the weights
property.
Let’s suppose to start from magnetic iron unit cell:
from aiida import load_profile
load_profile()
from aiida_atomistic import StructureDataMutable
structure_dict = {
'pbc': [True, True, True],
'cell': [
[2.8403, 0.0, 0.0],
[0.0, 2.8403,0.0],
[0.0, 0.0, 2.8403],
],
'symbols': ['Fe', 'Fe'],
'positions': [[0.0, 0.0, 0.0], [1.42015, 1.42015, 1.42015]],
'magmoms': [[2.5, 0.0, 0.0], [2.1, 0.0, 0.0]],
}
structure = StructureDataMutable(**structure_dict)
structure.properties.magmoms
[[2.5, 0.0, 0.0], [2.1, 0.0, 0.0]]
This structure contains two Fe atoms with a different magnetic moment (the magmoms
property).
They should correspond to two different kinds. As already mentioned above, we can set this manually:
structure.properties.kinds = ['Fe1', 'Fe2']
such that now the sites
(derived) property will be:
structure.properties.sites
[<SiteImmutable: kind name 'Fe1' @ 0.0,0.0,0.0>,
<SiteImmutable: kind name 'Fe2' @ 1.42015,1.42015,1.42015>]
Another possibility is to do it automatically using the built-in routine, the get_kinds
method of the structure instance.
Let’s suppose we just want to generate the kinds, to check that the classification is done correctly. Directly invoking the get_kinds
method will returns a dictionary containing all the properties and, in particular, the kinds
. In particular, the value of the properties will be the one assigned by the classification algorithm.
structure.get_kinds()
{'kinds': ['Fe1', 'Fe2'],
'masses': array([55.845, 55.845]),
'charges': array([0., 0.]),
'magmoms': array([[2.5, 0. , 0. ],
[2.1, 0. , 0. ]]),
'symbols': ['Fe', 'Fe'],
'positions': [[0.0, 0.0, 0.0], [1.42015, 1.42015, 1.42015]]}
We find indeed two kinds, with two the same properties but the magmoms
.
These dictionary can be directly used to build a new instance of the structure: new_structure = StructureData(**structure.get_kinds())
In the explanation of the classification algorithm, we mentioned that some default thresholds for the properties are used. The corresponding values are contained in the _DEFAULT_THRESHOLDS
variable, as defined in the package:
from aiida_atomistic.data.structure.mixin import _DEFAULT_THRESHOLDS
_DEFAULT_THRESHOLDS
{'charges': 0.1, 'masses': 0.0001, 'magmoms': 0.0001}
we see that for magmoms, the threshold is 1e-4 for each component. These are reasonable thresholds, which however can be overridden, as show in the following.
Defining custom thresholds for the properties¶
We can provide a dictionary of custom threshold to the get_kinds
method.
This can be done easily, providing the custom_thr
dictionary input, where the keys are the properties, the values being the new thresholds.
Let’s suppose to impose a larger threshold for magnetic moments, so that we find only one kind in our iron lattice.
structure.get_kinds(custom_thr={'magmoms': 0.5})
{'kinds': ['Fe1', 'Fe1'],
'masses': array([55.845, 55.845]),
'charges': array([0., 0.]),
'magmoms': array([[2.5, 0. , 0. ],
[2.5, 0. , 0. ]]),
'symbols': ['Fe', 'Fe'],
'positions': [[0.0, 0.0, 0.0], [1.42015, 1.42015, 1.42015]]}
We find, indeed, only one kind. Moreover, we see that the magmoms values is now the same, indicating that they are of the same kind. This value is chosen to be the upper value of the given box in the property space (see the explanation on the classification algorithm for more details).
This works also for multiple properties at the same time. Let’s suppose to have also charges:
Excluding some properties in the kinds determination¶
It is also possible to ignore some property in the kinds determination. This should be used with particular attention, as using directly its results can lead to inconsistencies in calculations. To exclude a property in the analysis, we can just put it in the exclude
input list (where the elements are strings of names for the properties):
structure.get_kinds(exclude=["magmoms"])['kinds']
['Fe1', 'Fe1']
Automatically updating the kinds for a given (mutable) StructureData
¶
It is possible to automatically update the kinds of a given StructureDataMutable
, without the need of returning the dictionary or list with the new kinds (i.e., without invoking directly the get_kinds
method).
We provide the set_automatic_kinds
method to do it:
structure.set_automatic_kinds(exclude=[],custom_thr={})
structure.properties.kinds
['Fe1', 'Fe2']
This will automatically populate the structure instance with the correct kind names and value of the properties.
The classification algorithm explained¶
The classification algorithm is basically is composed of two steps:
1 - for each property, compute the kinds; 2 - considering the whole set of properties, compute the unique set of kinds.
the main part is the first step. Basically, for each property, we divide the property space into sectors, where each sector corresponds to regions where the property value for two atoms is to be considered the same, within the threshold. The lowest value between these sites is taken as representative value of the property.
Below, we show the example for the kinds determination with respect to the charge property. We suppose to have four iron sites with, respectively, charges 0, 0.05, 0.12 and 0.19. A graphical representation of the algorithm follows:
In this case, two kinds are determined. The first kind (site 0 and 1) will have charge = 0, the second one (site 2 and 3) will have charge = 0.12 (the value of site 2). Indeed:
test_structure = StructureDataMutable(
charges=[0.0, 0.05, 0.12, 0.19],
symbols=['Fe', 'Fe', 'Fe', 'Fe'],
positions=[[0.0, 0.0, 0.0], [1.42015, 1.42015, 1.42015], [0.0, 0.0, 0.1], [0.1, 0.0, 0.1]]
)
kinds = test_structure.get_kinds()
print(f"Kinds: {kinds['kinds']},\nCharges: {kinds['charges']}")
Kinds: ['Fe1', 'Fe1', 'Fe2', 'Fe2'],
Charges: [0. 0. 0.12 0.12]