Magnetic configurations#
There are several ways of defining a magnetic configuration in Quantum ESPRESSO.
This tutorial focuses on using starting_magnetization
to specify the initial magnetization for each kind in our system.
Using the parameters
#
One way to specify the initial magnetic configuration of your system is by simply using the parameters
input of the PwCalculation
.
Start by loading your default profile:
from aiida import orm, load_profile
load_profile()
Next, set up a basic structure for HCP cobalt, and load the code you have set up for pw.x
:
from ase.build import bulk
structure = orm.StructureData(ase=bulk('Co', 'hcp', 2.5, 4.06))
pw_code = orm.load_code('qe-7.2-pw@localhost')
Obtain a pre-populated builder using the get_builder_from_protocol()
method:
from aiida_quantumespresso.workflows.pw.base import PwBaseWorkChain
builder = PwBaseWorkChain.get_builder_from_protocol(
structure=structure,
code=pw_code,
protocol='fast'
)
In order to set the magnetic moments, you need to change the parameters
input of the PwCalculation
:
parameters = builder.pw.parameters.get_dict()
parameters['SYSTEM']['nspin'] = 2
parameters['SYSTEM']['starting_magnetization'] = {'Co': 1}
builder.pw.parameters = orm.Dict(parameters)
The commands above do the following:
Get the
parameters
input of thepw
namespace as a regular Pythondict
.Set
nspin
to2
so Quantum ESPRESSO does a spin-polarised calculation.Set the
starting_magnetization
for theCo
atomic species or kind to 1.Replace the
parameters
of thePwCalculation
by a newDict
node that has thestarting_magnetization
set.
Tip
Note that starting_magnetization
is a “multidimensional parameter”, which in the aiida-quantumespresso
plugin you shouldn’t specify using indices between brackets as you would in a regular Quantum ESPRESSO input file.
See the corresponding how-to section for more details.
Now we ready to submit the builder to the AiiDA daemon:
from aiida.engine import run_get_node
results, pwbase_node = run_get_node(builder)
Once the calculation is completed, you can for example get the total magnetization in the unit cell from the output parameters:
results['output_parameters'].get_dict()['total_magnetization']
Important
The starting_magnetization
input is defined as “spin polarization” of the atom species, with inputs ranging from -1 (all spins down) to 1 (all spins up).
However, the “magnetization” outputs are always defined in units of Bohr magneton.
We will commonly refer to the values in Bohr magneton as the “magnetic moments” to differentiate them from “magnetization”.
Using the SpinType
#
An alternative way of performing spin-polarised calculations is by specifying a SpinType
and passing it to the get_builder_from_protocol()
method:
from aiida_quantumespresso.common.types import SpinType
builder = PwBaseWorkChain.get_builder_from_protocol(
structure=structure,
code=pw_code,
protocol='fast',
spin_type=SpinType.COLLINEAR
)
In this case, an initial starting_magnetization
will be set for each element based on the following logic:
If the element has a partially occupied d or f shell, calculate what the
starting_magnetization
should be in case we aim for a maximally possible magnetic moment, i.e. 5 or 7 Bohr magneton for d or f shells.For other elements, simply set
starting_magnetization
to 0.1.
Note
The approach outlined above will always set positive values for starting_magnetization
, and hence can only initialise the system in a ferro or ferrimagnetic state.
You can have a look to see what starting_magnetization
was set by inspecting the contents of the parameters
set in the builder:
builder.pw.parameters.get_dict()['SYSTEM']['starting_magnetization']
Once again, try running the calculation:
results, pwbase_node = run_get_node(builder)
The calculation should have the same final total magnetization as the one in section using the parameters.
results['output_parameters'].get_dict()['total_magnetization']
Anti-ferromagnetic configurations#
What if you want to initialize the system in an anti-ferromagnetic state?
In this case, Quantum ESPRESSO requires two different kinds to be specified in the inputs, but looking at the sites
in the input structure:
structure.sites
You can see that although our structure has two sites, they are both of the same kind.
This means you’ll have to create a new `` node with two different kinds.
For this purpose, you can use the create_magnetic_configuration()
calculation function:
from aiida_quantumespresso.calculations.functions.create_magnetic_configuration import create_magnetic_configuration
results = create_magnetic_configuration(structure, [-2, 2])
Note that as create_magnetic_configuration()
is a calculation function, and so its execution is tracked in the provenance:
❯ verdi process list -ap1
PK Created Process label Process State Process status
---- --------- ---------------------------- --------------- ----------------
[...]
695 12s ago create_magnetic_configuration ⏹ Finished [0]
Total results: 8
Report: last time an entry changed state: 3m ago (at 02:06:20 on 2023-05-28)
You can see the in- and outputs of the create_magnetic_configuration()
process using verdi process show
:
❯ verdi process show 695
Property Value
----------- ------------------------------------
type create_magnetic_configuration
state Finished [0]
pk 695
uuid bb8d4026-19c2-4efd-a99d-b55336df0eb3
label create_magnetic_configuration
description
ctime 2023-05-28 04:50:25.887472+02:00
mtime 2023-05-28 04:50:26.098163+02:00
Inputs PK Type
------------------------ ---- -------------
atol 693 Float
magnetic_moment_per_site 692 List
structure 659 StructureData
ztol 694 Float
Outputs PK Type
---------------- ---- -------------
magnetic_moments 697 Dict
structure 696 StructureData
Important
Every time you execute the create_magnetic_configuration()
calculation function, it will store another process with corresponding in- and output nodes to the database!
If you want to avoid this, you can set store_provenance
to False
in the metadata
:
results = create_magnetic_configuration(structure, [-2, 2], metadata={"store_provenance": False})
Let’s have a look at the structure
output:
new_structure = results['structure']
new_structure
As you can see, this is the new {class}`~aiida.orm.StructureData, which this time has two different kind names for each of the Co
sites:
results['structure'].sites
The results
also contain the magnetic_moments
output:
results['magnetic_moments'].get_dict()
This can be passed to the initial_magnetic_moments
input of the get_builder_from_protocol()
method, along with the new_structure
:
builder = PwBaseWorkChain.get_builder_from_protocol(
structure=new_structure,
code=pw_code,
protocol='fast',
spin_type=SpinType.COLLINEAR,
initial_magnetic_moments=results['magnetic_moments']
)
Let’s have another look at the starting_magnetization
input:
builder.pw.parameters['SYSTEM']['starting_magnetization']
As always, we can run the calculation:
results, pwbase_node = run_get_node(builder)
In this case, the total magnetization in the unit cell is zero:
results['output_parameters'].get_dict()['total_magnetization']
But the absolute magnetization is not:
results['output_parameters'].get_dict()['absolute_magnetization']
Passing the magnetic configuration#
Often you’ll want to pass the magnetic configuration obtained from one calculation to the next.
The final magnetic configuration of the pw.x
run can be found in the output_trajectory
output:
trajectory = pwbase_node.outputs.output_trajectory
This aiida.orm.TrajectoryData
node contains a lot of information for each ionic step performed in the pw.x
calculation, all stored as numpy
arrays in the repository.
To see all the arrays stored you can use the get_arraynames()
method:
trajectory.get_arraynames()
We’re interested in the atomic_magnetic_moments
.
Retrieve the final magnetic moments using the get_array()
method:
magnetic_moments = trajectory.get_array('atomic_magnetic_moments')[-1]
Note
Here you retrieved the final array of magnetic moments by specifying the -1
index.
Of course, for a static (scf
) calculation there is only one ionic step in the trajectory.
If you had run a vc-relax
calculation, the arrays in output_trajectory
would contain all the information above for each ionic step.
Have a look at the contents of magnetic_moments
.
In this case, the magnetic moments are stored as a one-dimensional array of length 2.
Note that Quantum ESPRESSO prints the magnetic configuration as a list of magnetic moments:
Magnetic moment per site (integrated on atomic sphere of radius R)
atom 1 (R=0.411) charge= 15.6004 magn= 1.2090
atom 2 (R=0.411) charge= 15.6004 magn= -1.2087
And that, same as for the total magnetization, the magnetic moments are expressed in Bohr magneton.
Due to this discrepancy between the in- and outputs in Quantum ESPRESSO, it becomes a bit challenging to properly pass magnetic configurations between calculations. You’d have to perform roughly the following steps:
Retrieve the final structure and atomic moments. Which structure to use will depend on the calculation you have run.
Check if the structure needs to have different kinds defined in order to specify the magnetic moments.
Properly map the kinds to the the correct magnetic moments, grouping together sites whose magnetic moments are close.
Check if all magnetic moments are very small. In this case continue with a non-magnetic calculation.
Fortunately, the PwCalculation
comes with a handy tool for obtaining the magnetic configuration.
First, obtain the final PwCalculation
from the called
processes of the PwBaseWorkChain
node:
pw_node = pwbase_node.called[-1]
Then, you can simply use the get_magnetic_configuration()
method in the tools
name space:
configuration = pw_node.tools.get_magnetic_configuration()
Note
In case new kinds are needed for the configuration, the create_magnetic_configuration()
calculation function is run inside get_magnetic_configuration()
to generate the new structure.
This step is added to the provenance and hence stored in the database.
The configuration contains both the new structure
and magnetic_moments
, which can then be easily passed to the get_builder_from_protocol()
method:
builder = PwBaseWorkChain.get_builder_from_protocol(
structure=configuration.structure,
code=pw_code,
spin_type=SpinType.COLLINEAR,
initial_magnetic_moments=configuration.magnetic_moments
)
Have a look at the starting_magnetization
input that was generated:
builder.pw.parameters['SYSTEM']['starting_magnetization']
Is it different from the one at the start of the initial run?