Atom#

For the full technical reference see Atom.

Atom class describes an atom. It is hashable and can be used as a dictionary key. The hash is based on the Atom.name and Atom.index.

Import#

>>> # Explicit import
>>> from wulfric.atom import Atom
>>> # Recommended import
>>> from wulfric import Atom

Creation#

Creation of an Atom object is straightforward:

>>> atom = Atom()
>>> atom.name
'X'
>>> atom.position
array([0., 0., 0.])
>>> atom.type
'X'
>>> atom = Atom(name='X', index=1, position=(1,0,1))
>>> atom = Atom(name='X', spin=2)
>>> atom.spin_vector
array([0., 0., 2.])
>>> atom.magmom
array([-0., -0., -4.])
>>> atom = Atom(name='X', magmom=(2,0,0))
>>> atom.spin_vector
array([-1., -0., -0.])
>>> atom = Atom(name='X', spin=(0,0,1))
>>> atom.magmom
array([-0., -0., -2.])
>>> atom = Atom(name='X', spin=(0,0,1), g_factor=-1)
>>> atom.magmom
array([0., 0., 1.])

For the full list of constructor parameters see Atom documentation.

Identification#

Name and index#

Identification of the atom is bounded to its Atom.name and Atom.index. Two atoms are considered to be equal if they have the same index and name:

>>> atom1 = Atom(name='Fe', index=1)
>>> atom2 = Atom(name='Fe', index=2)
>>> atom3 = Atom(name='Cr', index=1, position=(1, 1, 0), spin=0.5)
>>> atom4 = Atom(name='Cr', index=1, position=(1, 0, 0), spin=1.5)
>>> atom1 == atom2
False
>>> atom1 == atom3
False
>>> atom1 != atom3
True
>>> # Note that neither position's nor spin's values do not matter
>>> atom3 == atom4
True

Usually index is automatically generated when a set of atoms appear in some context. For example, when atoms are added to the Crystal object, the index is silently assigned to each atom.

Note

If the index of both atoms is not defined, then you can still compare it to other atoms with a different name:

>>> atom1 = Atom(name='Fe')
>>> atom2 = Atom(name='Cr')
>>> atom1 == atom2
False
>>> atom1 != atom2
True

However, if two atoms have the same name and the index is not defined in at least one of them, then the comparison fails:

>>> atom1 = Atom(name='Fe')
>>> atom2 = Atom(name='Fe', index=1)
>>> atom1 == atom2
Traceback (most recent call last):
...
ValueError: Index is not defined for the atom Fe.

Full name#

For the convenience the Atom.fullname attribute is defined, so one can consult the unique identifier of an Atom:

>>> atom1 = Atom(name='Fe', index=1)
>>> atom2 = Atom(name='Fe', index=2)
>>> atom3 = Atom(name='Cr', index=3)
>>> atom1.fullname
'Fe__1'
>>> atom2.fullname
'Fe__2'
>>> atom3.fullname
'Cr__3'

Fullname is defined even if the atom does not have an index:

>>> atom = Atom(name='Fe')
>>> atom.fullname
'Fe'

Atom's type#

Atom.type is derived automatically from its name, it cannot be changed directly.

>>> atom.name = 'Cr1'
>>> atom.name
'Cr1'
>>> atom.type
'Cr'
>>> atom.type = "Se"
Traceback (most recent call last):
...
AttributeError: property 'type' of 'Atom' object has no setter

Use as a dictionary key#

__hash__() is defined for an Atom class, therefore, you can use it as a dictionary key. Hash is calculated from the atom Atom.name and Atom.index. Atom.index has to be defined if you want to use the atom as a key.

>>> atom1 = Atom("Cr1")
>>> atom2 = Atom("Cr2")
>>> dictionary = {atom1: 1, atom2: 2}
Traceback (most recent call last):
...
ValueError: Index is not defined for the 'Cr1' atom ...
>>> atom1.index = 1
>>> # It does not make much sense to have the same indices, but
>>> # we want to highlight that only the combination of atom's
>>> #name and index has to be unique
>>> atom2.index = 1
>>> dictionary = {atom1: 1, atom2: 2}
>>> dictionary[atom1]
1
>>> dictionary[atom2]
2

Position#

The position of the atom can be access and set via Atom.position. At the level of the logic of pure Atom class no units (Angstroms, Bohr, relative, absolute, ...) are assumed for the atom's position. This uncertainty is deliberate, since it allows the user to use Atom in various contexts. For example, when atom is used inside a Crystal instance, the position is usually considered to be in relative coordinates.

>>> atom = Atom(name="Cr")
>>> # It has a default value
>>> atom.position
array([0., 0., 0.])
>>> atom.position = [1, 2, 3]
>>> atom.position
array([1., 2., 3.])

Magnetic properties#

Internally only four numbers are stored for the description of the magnetic properties of an atom:

  • g-factor (1)

  • spin vector (3)

From this four numbers a variety of properties can be accessed and set. They are summarized in a diagram below:

../../_images/atom-magnetic-properties.png

Semi-private attribute _spin_vector is not intended to be access or set in any way.

All other attributes can be set and accessed:

  • Atom.spin

    >>> atom = Atom(spin = (1,0,0))
    >>> # It always returns one umber - spin value
    >>> atom.spin
    1.0
    >>> # It can be set with a number or three-component vector
    >>> atom.spin = (0,1,0)
    >>> atom.spin
    1.0
    >>> atom.spin_vector
    array([0., 1., 0.])
    >>> # If set with a number, then spin is oriented along z axis
    >>> atom.spin = 2
    >>> atom.spin
    2.0
    >>> atom.spin_vector
    array([0., 0., 2.])
    
  • Atom.spin_vector

    >>> atom = Atom(spin = (1,0,0))
    >>> # It always returns an array of three numbers - spin vector
    >>> atom.spin_vector
    array([1., 0., 0.])
    >>> # It can be set with a number or three-component vector
    >>> atom.spin_vector = (0,1,0)
    >>> atom.spin_vector
    array([0., 1., 0.])
    >>> # If set with a number, then spin is oriented along z axis
    >>> atom.spin_vector = 2
    >>> atom.spin_vector
    array([0., 0., 2.])
    
  • Atom.spin_direction

    >>> atom = Atom(spin = (1.5,0,0))
    >>> # It always returns an array of three numbers - unit vector of spin (spin direction)
    >>> atom.spin_direction
    array([1., 0., 0.])
    >>> # It can be set with a number or three-component vector
    >>> atom.spin_direction = (1,1,0)
    >>> atom.spin_direction
    array([0.70710678, 0.70710678, 0.        ])
    >>> # If set with a number, then spin is oriented along z axis
    >>> atom.spin_direction = 2
    >>> atom.spin_direction
    array([0., 0., 1.])
    >>> # Note that the spin value is not changed
    >>> # round() is used because of the machine zero
    >>> round(atom.spin, 10)
    1.5
    
  • Atom.spin_angles. Two angles in degrees, that define the direction of the spin vector as

    \[\begin{split}\boldsymbol{S} = S \begin{pmatrix} \cos\varphi\sin\theta \\ \sin\varphi\sin\theta \\ \cos\theta \end{pmatrix}\end{split}\]

    \(0^{\circ} \le \theta \le 180^{\circ}\) and \(0^{\circ} \le \varphi \le 360^{\circ}\).

    the order is theta, phi.

    >>> atom = Atom(spin = (1.5,0,0))
    >>> # It always returns one number - angle theta or phi
    >>> atom.spin_angles
    (90.0, 0.0)
    >>> # It can be set with a number
    >>> atom.spin_angles = 90, 90
    >>> atom.spin_angles
    (90.0, 90.0)
    >>> atom.spin_angles = 37, 90
    >>> atom.spin_angles
    (37.0, 90.0)
    >>> # Note that the spin value is not changed
    >>> # round() is used because of the machine zero
    >>> round(atom.spin, 10)
    1.5
    
  • Atom.magmom Magnetic moment of an atom is connected with its spin as

    \[\boldsymbol{\mu} = - g\boldsymbol{S}\]

    where \(g\) is a Atom.g_factor. Bohr magneton is assumed to be equal to \(1\). As with the whole atom class we leave the units for the user (i.e. in order to get an actual value - multiply the result by the bohr magneton in the desired system of units).

    >>> atom = Atom(spin = (1,0,0))
    >>> #g_factor is equal to 2 by default
    >>> atom.g_factor
    2.0
    >>> # It always returns an array of three numbers - spin vector
    >>> atom.magmom
    array([-2., -0., -0.])
    >>> # It can be set with a number or three-component vector
    >>> atom.magmom = (0,1,0)
    >>> atom.magmom
    array([0., 1., 0.])
    >>> # If set with a number, then magnetic moment is oriented along z axis
    >>> atom.magmom = 2
    >>> atom.magmom
    array([0., 0., 2.])
    >>> # Note that the spin is changed as well
    >>> atom.spin_vector
    array([-0., -0., -1.])
    >>> # g_factor return one number and can be set with a number
    >>> atom.g_factor = -1
    >>> atom.spin = (0,1,0)
    >>> atom.spin_vector
    array([0., 1., 0.])
    >>> atom.magmom
    array([0., 1., 0.])
    

Electrical properties#

Charge#

Electrical charge of the atom can be set by assigning a value to the charge attribute:

>>> atom.charge = 1
>>> atom.charge
1.0

The units of the charge depend on the user's interpretation.