InitialSpin

class InitialSpin(scaled_spins=None)

Class for representing an initial spin configuration.

The initial scaled spin for each atom should be given as a number between -1 and 1 corresponding to the net spin-polarization in fractions of the atomic polarization according to Hund’s rule.

This should be either:

  • A sequence of floating point numbers, whose position in the list corresponds to the atom index in the configuration
  • A list of tuples each of an atom index and corresponding scaled spin value.
  • A list of tuples each of a PeriodicTableElement object and a scaled spin value.
  • A combination of the latter two.

For non-collinear spin systems the tuple has four numbers, atom index, scaled spin, theta, phi, where the latter two are spherical coordinates as PhysicalQuantity object of type Degree or Radians.

Parameters:scaled_spins (list) – The initial scaled spins for each atom.
Default: 1.0 for each atom..

Usage Examples

There are two basic ways to specify the InitialSpin of the atoms.

  1. Using a simple list. In this case, scaled_spins is a list containing the initial scaled spin for each atom, i.e. the number of entries in the list must be equal to the number of atoms in the system (in the central region for DeviceConfiguration). Example: Set initial spin 1, -0.5, 1 on the first three atoms, 0 for the rest (the configuration contains 50 atoms).

    bulk_configuration.setCalculator(
        calculator=LCAOCalculator(),
        initial_spin=InitialSpin(scaled_spins=[1.,-0.5,1.]+[0.,]*47)
        )
    
  2. Identify atoms, or groups of atoms, and set their initial spin individually. In this case, scaled_spins is a list of tuples, each with two elements. The first entry in the tuple can be an integer, to select an individual atom, or an element (like Carbon), to select all atoms of a certain element. The second entry is the initial scaled spin to set for the selected atoms.

    Example: The previous example may be specified the following way:

    bulk_configuration.setCalculator(
        calculator=LCAOCalculator(),
        initial_spin=InitialSpin(scaled_spins=[(0,1.),(1,-0.5),(2,1.)])
        )
    

    Set the spin 1 on Iron atoms, except for atoms with index 3 and 7.

    bulk_configuration.setCalculator(
         calculator = LCAOCalculator(),
         initial_spin = InitialSpin(scaled_spins=[(Iron,1.),(3,-1), (7,-1)])
         )
    
    • Later entries in the list override earlier ones.
    • The initial scaled spin is set to 1.0 for atoms for which another value is not specified.
    • The sign (positive or negative) of the specified initial spin determines whether the system has a net surplus of spin-up or spin-down electrons.

For noncollinear spin you may specify the spin direction in physical spherical coordinates \((r,\theta, \phi)\), where \(\theta\) is the angle with the z axis, and \(\phi\) the polar angle in the x-y plane relative to the x-axis. The collinear case is \(\theta=0\) rad or \(\theta=\pi\) rad.

bulk_configuration.setCalculator(
     calculator = LCAOCalculator(),
     initial_spin = InitialSpin(scaled_spins=[
         (Iron, 1.0, 0.5*numpy.pi*Radians, 0.4*numpy.pi*Radians),
         (3,    0.8, 270*Degrees         , 216*Degrees)])
     )

Also see RandomSpin for an alternative way to set the initial spin.

Notes

The simplest way to obtain a spin-polarized calculation is to use InitialSpin , i.e. without any parameters, which means that the initial scaled spin is set to 1.0 for all atoms. This is usually the safest way to obtain good convergence in spin-polarized in a MoleculeConfiguration and a BulkConfiguration. In a spin-polarized DeviceConfiguration, however, a more careful analysis is often needed in order to obtain reliable convergence. This will be discussed below.

The initial scaled spin for each atom specifies the degree of polarization for unpaired electrons (either spin-up or spin-down) within a given valence electron shell. To understand this properly, it is best to consider a specific example.

The initial spins for the different atoms in a configuration are independent of each other, so without loss of generality we can limit the discussion to one atom. Let us introduce the symbol \(\alpha\) to denote the initial scaled spin for that atom.

For definiteness, let us look at bulk Fe, which has a total of 8 valence electrons (4s23d6).

How should these electrons be distributed with respect to spin in a spin-polarized calculation? In the simplest picture, we start by filling all the available spin-up states, and when they have been filled we add the remaining electrons into spin-down states. The resulting occupations are shown in the following table (note that the 4s orbitals, both up and down, are filled before the 3d orbitals).

Table 35 Iron in the maximum spin configuration, i.e. \(\alpha=1.0\).
  4s 3d
Spin up \(\mathbf{\uparrow}\) \(\mathbf{\uparrow}\) \(\mathbf{\uparrow}\) \(\mathbf{\uparrow}\) \(\mathbf{\uparrow}\) \(\mathbf{\uparrow}\)
Spin down \(\mathbf{\downarrow}\) \(\mathbf{\downarrow}\)

This configuration corresponds to an initial scaled spin of \(\alpha=1.0\) (or -1, if we reverse the roles of spin-up and spin-down). For obvious reasons, we will refer to this configuration as the maximum spin configuration.

Let us define some symbols for reference. Let \(n_{i,\sigma}\) be the total population of shell \(i\) (4s and 3d, for iron) in the maximum spin configuration, where \(\sigma\) denotes the spin. We also define the maximum spin polarization for each shell,

\[\Delta N_i=n_{i,\uparrow}-n_{i,\downarrow},\]

which thus is 4 for 3d in the case of iron. For 4s the maximum spin polarization is zero, which means that this shell cannot be initially polarized in the calculations. A spin polarization of the 4s shell can still occur as a result of the self-consistent calculation, but it requires that the total population of the 4s shell is lower than two, and this cannot be used as an initial condition, since the total population of each shell,

\[N_i = \sum_{\sigma}n_{i,\sigma},\]

is fixed and constant in the process of assigning the initial populations.

Now, the initial scaled spin \(\alpha\) determines the initial spin occupations \(o_{i,\sigma}\) of the states in shell \(i\) through the following expression:

\[\alpha_i = \frac{o_{i,\uparrow}-o_{i,\downarrow}}{\Delta N_i}.\]

As mentioned, the total number of valence electrons in each shell \(N_i\) must always be conserved, and this poses a constraint on the initial spin configurations that can be constructed in this way. It can easily be verified, that \(\alpha_i=1\) gives exactly \(o_{i,\sigma}=n_{i,\sigma}\).

Let us see what happens with iron if we set \(\alpha=0.5\). As mentioned above, only the 3d shell can be polarized initially, and we find

\[o_{\mathrm{3d},\uparrow}-o_{\mathrm{3d},\downarrow} = \alpha\ \Delta N_\mathrm{3d} = 0.5 \times 4 = 2.\]

Combined with the constraint \(N_\mathrm{3d} = o_{\mathrm{3d},\uparrow}+o_{\mathrm{3d},\downarrow}=6\), we find

\[o_{\mathrm{3d},\uparrow} = 4, \qquad o_{\mathrm{3d},\downarrow} = 2.\]

These populations are equally distributed among the five 3d states for each respective spin.

As a further example, had we instead specified \(\alpha=0.75\) we would have obtained a spin-polarized configuration with 4.5 spin-up and 1.5 spin-down electrons on average per unit-cell in the 3d shell.

Now, the parameter initial_spin only affects the initial spin polarization. The converged spin-polarization could very well be different. Let us again study iron as an example.

The script below calculates and prints the converged Mulliken population of a spin-polarized bulk bcc iron system. Since we do not know what value to use for the initial scaled spin, we use 1.0.

bulk_configuration = BulkConfiguration(
    bravais_lattice=BodyCenteredCubic(2.8665*Angstrom),
    elements=[Iron],
    cartesian_coordinates=[[ 0.,  0.,  0.]]*Angstrom
    )

numerical_accuracy_parameters = NumericalAccuracyParameters(
    k_point_sampling=(10, 10, 10),
    )

calculator = LCAOCalculator(
    exchange_correlation=LSDA.PZ,
    numerical_accuracy_parameters=numerical_accuracy_parameters,
    )

initial_spin = InitialSpin(scaled_spins=[1.0])

bulk_configuration.setCalculator(
    calculator,
    initial_spin=initial_spin,
)

mulliken_population = MullikenPopulation(bulk_configuration)
nlprint(mulliken_population)

iron_spin.py

If you run the script, it will report the following occupations:

+------------------------------------------------------------------------------+
|                                                                              |
| Mulliken Population Report                                                   |
|                                                                              |
| ---------------------------------------------------------------------------- |
|                        |                                                     |
| Element   Total  Shell | Orbitals                                            |
|                        |                                                     |
|                        |     xy     zy  zz-rr     zx  xx-yy                  |
|   0  Fe   5.108  0.704 |  0.140  0.140  0.142  0.140  0.142                  |
|           2.892  0.497 |  0.111  0.111  0.081  0.111  0.081                  |
|                        |      s                                              |
|                 -0.025 | -0.025                                              |
|                 -0.032 | -0.032                                              |
|                        |     xy     zy  zz-rr     zx  xx-yy                  |
|                  3.641 |  0.694  0.694  0.779  0.694  0.779                  |
|                  1.530 |  0.370  0.370  0.210  0.370  0.210                  |
|                        |      s                                              |
|                  0.317 |  0.317                                              |
|                  0.335 |  0.335                                              |
|                        |      y      z      x                                |
|                  0.471 |  0.157  0.157  0.157                                |
|                  0.562 |  0.187  0.187  0.187                                |
+------------------------------------------------------------------------------+

The number show the MullikenPopulation of each orbital, for each spin (for each shell, the spin-up populations are listed on the first line, spin-down on the second one).

If you kept an eye on the calculation, you would have noticed that it converged in 18 iterations. The question is now, could we have improved the convergence rate by choosing a different initial spin polarization?

The converged total MullikenPopulation (on average per unit cell) for the spin-up and spin-down states are 5.11 and 2.89, respectively. Now these numbers also include the 4s and some 5p electrons (since a DoubleZetaPolarized BasisSet was used), but the detailed output shows that the spin polarization of these shells is small. Hence, as a reasonable approximation we can use \(o_{\mathrm{3d},\uparrow}-o_{\mathrm{3d},\downarrow}=5.11-2.89=2.22\).

Thus, the converged result corresponds to a value of \(\alpha=2.22/\Delta N_\mathrm{3d}=0.56\). It can therefore be expected that if we had used this value in the calculation from the beginning, it would have converged faster. This is indeed the case; rerunning the script with the new value of scaled_spins=[0.56], the self-consistent iteration reaches convergence in 10 steps, i.e. we have almost gained a factor 2 in speed!

Note

Note how the resulting populations in the two calculations are identical; we are only setting the initial spin; the self-consistent calculation will find the proper solution anyway, provided the initial value is not too far off. In bulk systems this is, as mentioned above, not very critical, but for transport calculations it can sometimes be very hard to reach convergence unless a good initial value is provided. And, under any circumstance, with a good initial we can save a lot of calculation time.

Finally, let us show how to determine the value that should be assigned to \(\alpha\) in order to generate a given, initial, spin-configuration.

Suppose we wanted to perform a spin-polarized calculation for manganese (7 valence electrons, (4s2 3d5)) with an initial spin occupation as the one shown in the following table.

Table 36 Manganese in an electronic configuration with a majority of spin-down electrons.
  4s 3d
Spin up \(\mathbf{\uparrow}\) \(\mathbf{\uparrow}\) \(\mathbf{\uparrow}\)
Spin down \(\mathbf{\downarrow}\) \(\mathbf{\downarrow}\) \(\mathbf{\downarrow}\) \(\mathbf{\downarrow}\)

We can see that \(o_{\mathrm{3d},\uparrow}-o_{\mathrm{3d},\downarrow}=-1\), as we have one unpaired spin-down electron. The number of unpaired electrons in the maximum spin configuration for manganese is 5 (all 3d electrons in the spin-up state), and thus we need to set \(\alpha=-0.2\) to obtain the desired initial configuration.