This section contains an example of how you can use the
to create complex crystals.
StructureData class we did not
try to have a full set of features to manipulate crystal structures.
Indeed, other libraries such as ASE exist,
and we simply provide easy
ways to convert between the ASE and the AiiDA formats. On the other hand,
we tried to define a “standard” format for structures in AiiDA, that can be
used across different codes.
How to use
Take a look at the following example:
alat = 4. # angstrom cell = [[alat, 0., 0.,], [0., alat, 0.,], [0., 0., alat,], ] s = StructureData(cell=cell) s.append_atom(position=(0.,0.,0.), symbols='Fe') s.append_atom(position=(alat/2.,alat/2.,alat/2.), symbols='O')
With the commands above, we have created a crystal structure
a cubic unit cell and lattice parameter of 4 angstrom, and two atoms in the
cell: one iron (Fe) atom in the origin, and one oxygen (O) at the center of
the cube (this cell has been just chosen as an example and most probably does
As you can see in the example above, both the cell coordinates and the atom coordinates are expressed in angstrom, and the position of the atoms are given in a global absolute reference frame.
In this way, any periodic structure can be defined. If you want to import from ASE in order to specify the coordinates, e.g., in terms of the crystal lattice vectors, see the guide on the conversion to/from ASE below.
When using the
method, further parameters can be passed. In particular, one can specify
the mass of the atom, particularly important if you want e.g. to run a
phonon calculation. If no mass is specified, the mass provided by
NIST (retrieved in October 2014)
is going to be used. The list of
masses is stored in the module
aiida.common.constants, in the
Moreover, in the
of AiiDA we also support the storage of crystal structures with alloys,
vacancies or partial occupancies.
In this case, the argument of the parameter
should be a list of symbols, if you want to consider an alloy;
moreover, you must pass a
weights list, with the same length as
and with values between 0. (no occupancy) and 1. (full occupancy), to specify
the fractional occupancy of that site for each of the symbols specified
symbols list. The sum of
all occupancies must be lower or equal to one; if the sum is lower than one,
it means that there is a given probability of having a vacancy at that
specific site position.
As an example, you could use:
to add a site at the origin of a structure
s consisting of an alloy of
90% of Barium and 10% of Calcium (again, just an example).
The following line instead:
would create a site with 90% probability of being occupied by Calcium, and 10% of being a vacancy.
s.has_vacancies can be used to
verify, respectively, if more than one element if given in the symbols list,
and if the sum of all weights is smaller than one.
if you pass more than one symbol, the property
True, even if only one symbol has occupancy 1 and
all others have occupancy zero:
>>> s = StructureData(cell=[[4,0,0],[0,4,0],[0,0,4]]) >>> s.append_atom(position=(0.,0.,0.), symbols=['Fe', 'O'], weights=[1.,0.]) >>> s.is_alloy True
Internals: Kinds and Sites¶
method works by manipulating the kinds and sites of the current structure.
Kinds are instances of the
Kind class and
represent a chemical species, with given properties (composing element or
elements, occupancies, mass, …) and identified
by a label (normally, simply the element chemical symbol).
Sites are instances of the
and represent instead each single site. Each site refers
identify its properties (which element it is, the mass, …) and to its three
append_atom() works in
the following way:
It creates a new
Kindclass with the properties passed as parameters (i.e., all parameters except
It tries to identify if an identical Kind already exists in the list of kinds of the structure (e.g., in the same atom with the same mass was already previously added). Comparison of kinds is performed using
aiida.orm.nodes.data.structure.Kind.compare_with(), and in particular it returns
Trueif the mass and the list of symbols and of weights are identical (within a threshold). If an identical kind
kis found, it simply adds a new site referencing to kind
kand with the provided
position. Otherwise, it appends
kto the list of kinds of the current structure and then creates the site referencing to
k. The name of the kind is chosen, by default, equal to the name of the chemical symbol (e.g., “Fe” for iron).
If you pass more than one species for the same chemical symbol, but e.g. with different masses, a new kind is created and the name is obtained postponing an integer to the chemical symbol name. For instance, the following lines:
s.append_atom(position = [0,0,0], symbols='Fe', mass = 55.8) s.append_atom(position = [1,1,1], symbols='Fe', mass = 57) s.append_atom(position = [1,1,1], symbols='Fe', mass = 59)
will automatically create three kinds, all for iron, with names
Fe2, and masses 55.8, 57. and 59. respecively.
In case of alloys, the kind name is obtained concatenating all chemical symbols names (and a X is the sum of weights is less than one). The same rules as above are used to append a digit to the kind name, if needed.
Finally, you can simply specify the kind_name to automatically generate a new kind with a specific name. This is the case if you want a name different from the automatically generated one, or for instance if you want to create two different species with the same properties (same mass, symbols, …). This is for instance the case in Quantum ESPRESSO in order to describe an antiferromagnetic cyrstal, with different magnetizations on the different atoms in the unit cell.
In this case, you can for instance use:
s.append_atom(position = [0,0,0], symbols='Fe', mass = 55.845, name='Fe1') s.append_atom(position = [2,2,2], symbols='Fe', mass = 55.845, name='Fe2')
To create two species
Fe2for iron, with the same mass.
You do not need to specify explicitly the mass if the default one is ok for you. However, when you pass explicitly a name and it coincides with the name of an existing species, all properties that you specify must be identical to the ones of the existing species, or the method will raise an exception.
from aiida.orm.nodes.data.structure import Kind, Site s.append_kind(Kind(symbols='Fe', mass=55.845, name='Fe1')) s.append_kind(Kind(symbols='Fe', mass=55.845, name='Fe1')) s.append_site(Site(kind_name='Fe1', position=[0.,0.,0.])) s.append_site(Site(kind_name='Fe2', position=[2.,2.,2.]))
Conversion to/from ASE¶
If you have an AiiDA structure
s, you can get an
ase.Atom object by
just calling the
ase_atoms = s.get_ase()
As we support alloys and vacancies in AiiDA, while
ase.Atom does not,
it is not possible to export to ASE a structure with vacancies or alloys.
If instead you have as ASE Atoms object and you want to load the structure from it, just pass it when initializing the class:
StructureData = DataFactory('structure') # or: # from aiida.orm import StructureData aiida_structure = StructureData(ase = ase_atoms)
Creating multiple species¶
We implemented the possibility of specifying different Kinds (species) in the
ase.atoms and then importing them.
In particular, if you specify atoms with different mass in ASE, during the import phase different kinds will be created:
>>> import ase >>> StructureData = DataFactory("structure") >>> ase_structure = ase.Atoms('Fe2') >>> ase_structure.mass = 55. >>> ase_structure.mass = 56. >>> ase_structure.cell = cell # defines a periodic cell >>> s = StructureData(ase=ase_structure) >>> for kind in s.kinds: >>> print(kind.name, kind.mass) Fe 55.0 Fe1 56.0
Moreover, even if the mass is the same, but you want to get different species,
you can use the ASE
tags to specify the number to append to the element
symbol in order to get the species name:
>>> import ase >>> StructureData = DataFactory("structure") >>> ase_structure = ase.Atoms('Fe2') >>> ase_structure.tag = 1 >>> ase_structure.tag = 2 >>> ase_structure.cell = cell >>> s = StructureData(ase=ase_structure) >>> for kind in s.kinds: >>> print(kind.name) Fe1 Fe2
in complicated cases (multiple tags, masses, …), it is possible that exporting a AiiDA structure to ASE and then importing it again will not perfectly preserve the kinds and kind names.
Conversion to/from pymatgen¶
pymatgen_molecule = aiida_structure.get_pymatgen_molecule() pymatgen_structure = aiida_structure.get_pymatgen_structure()
A single method
get_pymatgen can be
used for both tasks: converting periodic structures (periodic boundary
conditions are met in all three directions) to pymatgen’s Structure and
other structures to pymatgen’s Molecule:
pymatgen_object = aiida_structure.get_pymatgen()
It is also possible to convert pymatgen’s Molecule and Structure objects to AiiDA structures:
StructureData = DataFactory("structure") from_mol = StructureData(pymatgen_molecule=mol) from_struct = StructureData(pymatgen_structure=struct)
Also in this case, a generic converter is provided:
StructureData = DataFactory("structure") from_mol = StructureData(pymatgen=mol) from_struct = StructureData(pymatgen=struct)
Converters work with version 3.0.13 or later of pymatgen. Earlier versions may cause errors.