Everything about defining crystal structure

This section introduces how to define a crystal structure.

Definition of the lattice

For defining a spin Hamiltonian, first we need a magnetic lattice. SpinW builds magnetic lattices on top of crystal structures, so first we need to define a crystal structure. For simple magnetic lattices this seems to be an unnecessary extra step, however to model real systems with space group symmetry, defining a magnetic lattice on top of a crystal structure make things easier.

Before we define a new crystal structure, we initialize an empty SpinW object:

model = spinw
model.lattice

Output

  struct with fields:
        angle: [1.5708 1.5708 1.5708]
    lat_const: [3 3 3]
          sym: [3×4×0 double]
       origin: [0 0 0]
        label: 'P 0'

The spinw.lattice property will store all the information that defines the lattice (without the atoms). The default values give a cubic lattice (angles are stored in radian) with a lattice parameter of 3 Å and no symmetry (spinw.lattice.sym field is empty).

The spinw.genlattice function can be used to modify the lattice:

model.genlattice('lat_const',[2.71 2.71 13],'angled',[90 90 120],'spgr','R -3 m')
model.lattice

Output

  struct with fields:
        angle: [1.5708 1.5708 2.0944]
    lat_const: [2.7100 2.7100 13]
          sym: [3×4×36 double]
       origin: [0 0 0]
        label: 'R -3 m'

Here we modified the lattice parameters and angles (the angled parameter takes angles in units of degree) and defined a space group. Space groups with standard settings can be added simply by using their name, e.g. 'R -3 m' which corresponds to the labels stored in the symmetry.dat file (to see the content of this file type edit symmetry.dat into the Matlab Command Window). The spgr parameter also accepts directly space group operators, for details see section symmetry.

Generation of crystal structure

To add atoms to the lattice we use the spinw.addatom command with at least the position must be given using the r parameter. The position is given in a vector of 3 numbers and in lattice units. This will define a non-magnetic atom. Also a string label can be assigned to each atom using the label parameter:

model.addatom('r',[0 0   0],'label','MCu2')
model.addatom('r',[0 0 1/2],'label','O')
model.unit_cell

Output

  struct with fields:
        r: [3×2 double]
        S: [0.5000 0]
    label: {'MCu2'  'O'}
    color: [3×2 int32]
       ox: [2 0]
      occ: [1 1]
        b: [2×2 double]
       ff: [2×11×2 double]
        A: [-1 -1]
        Z: [29 8]
     biso: [0 0]

After running the above example, the added atoms are stored in the spinw.unit_cell property. This property stores only the symmetry inequivalent atoms and their properties (no symmetry operators are applied at this point). The most important property beside the atomic position is the spin quantum number of the atoms, stored in spinw.unit_cell.S. In the above example we did not give the spin quantum number directly, thus it is from the given label parameter of spinw.addatom. Each label is searched in the magion.dat file and if a match is found the spin value is imported. If no match is found the default spin quantum number of 0 is saved in spinw.unit_cell making the atom non-magnetic, as it happened for oxygen in the above example. The spin quantum number can be also directly given using the S parameter of spinw.addatom.

SpinW does not store the symmetry equivalent atomic positions, but it generates dynamically from spinw.unit_cell using the spinw.atom method. The method returns a structure that contains all atomic positions generated by the space group:

model.atom

Output

  struct with fields:
      r: [3×6 double]
    idx: [1 1 1 2 2 2]
    mag: [1 1 1 0 0 0]

The Matlab struct type output (we will call it structure afterwards) contains all the atomic positions in a matrix with dimensions of (if you ask why is this dimension, check section “matrix-dimensions”), in our case 6 atoms. The output structure has another field, idx which identifies each generated atom via the index of the symmetry inequivalent parent atom in spinw.unit_cell. The third field mag tells if a generated atom magnetic () or not.

To generate only the magnetic atoms, use the spinw.matom method. This function also returns the spin value for each magnetic atom:

model.matom

Output

  struct with fields:
      r: [3×3 double]
    idx: [1 1 1]
      S: [0.5000 0.5000 0.5000]

Since this is one of the functions that is most often called internally, it caches the results. This means that after the first call, the result is stored in spinw.cache and in consecutive calls it simply returns the cached values. There is also a mechanism that automatically clears the cache when any value in spinw.lattice or spinw.unit_cell is changed (see Create Property Listeners in the Matlab documentation). This makes sure that the cached values are always consistent with any other values stored in the spinw object.

Coordinate systems

SpinW uses three different coordinate systems, a lattice coordinate system (also called coordinate system) and a Cartesian coordinate system (also called coordinate system).

Lattice units

The unit vectors of the lattice coordinate system are the lattice vectors , and . In this coordinate system the atomic positions (in spinw.unit_cell.r), translation vectors for bonds (in spinw.couypling.dl) are stored. Also the generated atomic positions of the magnetic lattice are in lattice coordinate system (spinw.matom). Also the magnetic moments can be defined in the lattice coordinate system using the spinw.genmagstr function with the 'unitS' parameter set to 'lu'.

Reciprocal lattice units

The unit vectors of the reciprocal lattice coordinate system are the reciprocal lattice vectors , and . They are calculated from the lattice vectors, see https://en.wikipedia.org/wiki/Reciprocal_lattice. The input of spinw.spinwave requires points in reciprocal lattice units. Generally all momentum that is unitless are in this coordinate system.

Descartes coordinates

Any other quantity is expressed in a Descartes coordinate system that is attached to the lattice according to the following rules:

  • x-axis is parallel to the -axis
  • y-axis is perpendicular to the -axis and lies in the -plane
  • z-axis is perpendicular to the -plane and right handed

Most importantly the exchange and anisotropy matrices, g-tensor, magnetic field vector and magnetic moment vectors are all stored in the coordinate system. Also the components of the spin-spin correlation function and reciprocal lattice vectors (when in Å units). Generally all momentum in Å units are also in this coordinate system.

Lattice transformations

Since the unit cell of complex materials is often not ideal to represent the magnetic structure, for example because of the unnecessary centering of space groups, SpinW allows a general transformation of the crystal lattice using the spinw.newcell method. The spinw.newcell method removes all symmetry operators from the cell and transforms the cell to the new lattice vectors given in units of the original lattice vectors. A typical application of this transformation is when we want to reduce the number of independent magnetic atoms in our model to speed up the calculation. One possibility is to remove the centering of the space group. Let’s assume our crystal structure is defined in the space group . In this example we generate the magnetic lattice and all first neighbor bonds:

model = spinw
model.genlattice('lat_const',[4.5 4.5 4.5],'spgr','F 2 3')
model.addatom('r',[0 0 0],'S',1)
plot(model)
plot(model)
model.gencoupling
model.matom.r

Output

         0    0.5000    0.5000         0
         0    0.5000         0    0.5000
         0         0    0.5000    0.5000
model.table('bond',1)

Output

  24×10 table
    idx    subidx          dl                   dr             length     matom1     idx1     matom2     idx2        matrix    
    ___    ______    ______________    ____________________    ______    ________    ____    ________    ____    ______________
    1       1         1     0     0     0.5    -0.5       0    3.182     'atom_1'    2       'atom_1'    1       ''    ''    ''
    1       2         0    -1     0     0.5    -0.5       0    3.182     'atom_1'    1       'atom_1'    2       ''    ''    ''
    1       3         0     0     0     0.5    -0.5       0    3.182     'atom_1'    4       'atom_1'    3       ''    ''    ''
    1       4         1    -1     0     0.5    -0.5       0    3.182     'atom_1'    3       'atom_1'    4       ''    ''    ''
    1       5         0     0     0    -0.5    -0.5       0    3.182     'atom_1'    2       'atom_1'    1       ''    ''    ''
    1       6        -1    -1     0    -0.5    -0.5       0    3.182     'atom_1'    1       'atom_1'    2       ''    ''    ''
    1       7        -1     0     0    -0.5    -0.5       0    3.182     'atom_1'    4       'atom_1'    3       ''    ''    ''
    1       8         0    -1     0    -0.5    -0.5       0    3.182     'atom_1'    3       'atom_1'    4       ''    ''    ''
    1       9         0     1     0       0     0.5    -0.5    3.182     'atom_1'    4       'atom_1'    1       ''    ''    ''
    1      10         0     0    -1       0     0.5    -0.5    3.182     'atom_1'    1       'atom_1'    4       ''    ''    ''
    1      11         0     0     0       0     0.5    -0.5    3.182     'atom_1'    3       'atom_1'    2       ''    ''    ''
    1      12         0     1    -1       0     0.5    -0.5    3.182     'atom_1'    2       'atom_1'    3       ''    ''    ''
    1      13         0     0     0       0    -0.5    -0.5    3.182     'atom_1'    4       'atom_1'    1       ''    ''    ''
    1      14         0    -1    -1       0    -0.5    -0.5    3.182     'atom_1'    1       'atom_1'    4       ''    ''    ''
    1      15         0    -1     0       0    -0.5    -0.5    3.182     'atom_1'    3       'atom_1'    2       ''    ''    ''
    1      16         0     0    -1       0    -0.5    -0.5    3.182     'atom_1'    2       'atom_1'    3       ''    ''    ''
    1      17         0     0     1    -0.5       0     0.5    3.182     'atom_1'    3       'atom_1'    1       ''    ''    ''
    1      18        -1     0     0    -0.5       0     0.5    3.182     'atom_1'    1       'atom_1'    3       ''    ''    ''
    1      19         0     0     0    -0.5       0     0.5    3.182     'atom_1'    2       'atom_1'    4       ''    ''    ''
    1      20        -1     0     1    -0.5       0     0.5    3.182     'atom_1'    4       'atom_1'    2       ''    ''    ''
    1      21         0     0     0    -0.5       0    -0.5    3.182     'atom_1'    3       'atom_1'    1       ''    ''    ''
    1      22        -1     0    -1    -0.5       0    -0.5    3.182     'atom_1'    1       'atom_1'    3       ''    ''    ''
    1      23         0     0    -1    -0.5       0    -0.5    3.182     'atom_1'    2       'atom_1'    4       ''    ''    ''
    1      24        -1     0     0    -0.5       0    -0.5    3.182     'atom_1'    4       'atom_1'    2       ''    ''    ''

According to the calculation there are 4 magnetic atoms in the crystallographic unit cell and 24 first neighbor bonds (6 per magnetic atom), here each bond is generated once. Details of the bond generation comes in the following section. Now we reduce the crystallographic unit cell to the primitive cell and compare the the result with the original model:

model.newcell('bvect',{[1/2 1/2 0] [1/2 0 1/2] [0 1/2 1/2]})
plot(model)
plot(model)
model.gencoupling
model.matom.r

Output

     0
     0
     0
model.table('bond',1)

Output

  6×10 table
    idx    subidx         dl               dr          length     matom1     idx1     matom2     idx2        matrix    
    ___    ______    _____________    _____________    ______    ________    ____    ________    ____    ______________
    1      1         1     0    -1    1     0    -1    3.182     'atom_1'    1       'atom_1'    1       ''    ''    ''
    1      2         0    -1     1    0    -1     1    3.182     'atom_1'    1       'atom_1'    1       ''    ''    ''
    1      3         1    -1     0    1    -1     0    3.182     'atom_1'    1       'atom_1'    1       ''    ''    ''
    1      4         1     0     0    1     0     0    3.182     'atom_1'    1       'atom_1'    1       ''    ''    ''
    1      5         0     1     0    0     1     0    3.182     'atom_1'    1       'atom_1'    1       ''    ''    ''
    1      6         0     0     1    0     0     1    3.182     'atom_1'    1       'atom_1'    1       ''    ''    ''

The result is a primitive lattice with 1 magnetic atom and 6 bonds only. This is a very useful transformation for example if we want to calculate the spin wave dispersion of the FCC ferromagnet with Heisenberg exchange. In this case removing the symmetry operators does not change the Hamiltonian and the transformed model will contain only a single magnetic atom instead of 4 which will speed up the spin wave calculation. Another useful feature of the spinw.newcell method is that is generates the transformation matrix between reciprocal lattice vectors of the old and new unit cells, moreover this transformation matrix can be stored in the spinw object setting the keepq parameter to true. This means that after the transformation the spin wave dispersion can be still calculated using the original reciprocal lattice. This is useful when one wants to quickly compare a measurement indexed in the centered space group while the Hamiltonian is defined in the primitive cell.

Another useful symmetry transformation command is the spinw.nosym which reduces the symmetry to while keeping all the atoms. This can be used to generate starting lattices for distorted structures, since after the removal of symmetry all atomic positions are decoupled so they can be moved independently.

Atomic properties

Beside storing the position of the atoms and the spin quantum number, spinw store several other atomic properties attached to the symmetry inequivalent positions (see spinw.unit_cell). Some of these properties are unused at present, however we list here all of them for completeness. All atomic properties can be input into the spinw object using the spinw.addatom method.

The most important property is the magnetic form factor. spinw.addatom attempts to determine the magnetic form factor using the given label parameter. It searches the magion.dat file for matching labels and if found, the form factor coefficients are stored in the spinw.unit_cell.ff(1,:,i) matrix, where is the index of the atom. The magnetic form factor coefficients can be also given directly using the formfactn parameter of spinw.addatom by providing directly the coefficients , , , … in a row vector, where the magnetic form factor is calculated as

with .

There is also a color property assigned to each atom that can be input using the color parameter of the spinw.addatom method. Color can be given as an RGB triplet (integer values between 0-255) or as a string which is a valid color name in the color.dat file. The color will appear on the 3D structural plots (see swplot.plotatom).

To calculate density, spinw also stores the atomic number and atomic mass in the Z and A fields of spinw.unit_cell. For natural abundance of isotopes, use -1 as atomic mass. Information on isotope properties including neutron scattering cross section is stored in the isotope.dat file.