-
Notifications
You must be signed in to change notification settings - Fork 78
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ProjwfcParser
: add support for non-collinear and spinorbit calculat…
…ions (#546) To support the new spin polarization modes, new subclasses of the data tool `RealhydrogenOrbital`, which is based on the `OrbitalData` data plugin, had to be created: * `NoncollinearHydrogenOrbital`: includes the spin projection `s_z` along the z-axis. Spin-up and spin-down states are treated as separate orbitals * `SpinorbitHydrogenOrbital`: includes the `total_angular_momentum` quantum number `j`, the `angular_momentum` quantum number `l` and the total `magnetic_number` `m_j`.
- Loading branch information
Showing
31 changed files
with
2,061 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Empty file.
65 changes: 65 additions & 0 deletions
65
aiida_quantumespresso/tools/data/orbital/noncollinearhydrogen.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# -*- coding: utf-8 -*- | ||
"""A module defining hydrogen-like orbitals with non-collinear spin component.""" | ||
|
||
from aiida.common.exceptions import ValidationError | ||
|
||
from aiida.tools.data.orbital.realhydrogen import RealhydrogenOrbital | ||
|
||
|
||
def validate_spin(value): | ||
"""Validate the value of the spin projection s_z.""" | ||
if not isinstance(value, (int, float)): | ||
raise ValidationError('spin projection (s_z) must be float') | ||
|
||
return value | ||
|
||
|
||
class NoncollinearHydrogenOrbital(RealhydrogenOrbital): | ||
"""Orbitals for hydrogen, with non-collinear spin. | ||
The class largely follows the conventions used by wannier90 Object to | ||
handle the generation of real hydrogen orbitals and their hybrids, has | ||
methods for producing s, p, d, f, and sp, sp2, sp3, sp3d, sp3d2 hybrids. | ||
This method does not deal with the cannonical hydrogen orbitals which | ||
contain imaginary components. The orbitals described here are chiefly | ||
concerned with the symmetric aspects of the oribitals without the context | ||
of space. Therefore diffusitivity, position and atomic labels should be | ||
handled in the OrbitalData class. Following the notation of table 3.1, 3.2 | ||
of Wannier90 user guide http://www.wannier.org/doc/user_guide.pdf A brief | ||
description of what is meant by each of these labels: :param radial_nodes: | ||
the number of radial nodes (or inflections) if no radial nodes are | ||
supplied, defaults to 0 :param angular_momentum: Angular quantum number, | ||
using real orbitals :param magnetic_number: Magnetic quantum number, using | ||
real orbitals :param spin: spin z-projection s_z The conventions regarding | ||
L and M correpsond to those used in wannier90 for all L greater than 0 the | ||
orbital is not hyrbridized see table 3.1 and for L less than 0 the orbital | ||
is hybridized see table 3.2. M then indexes all the possible orbitals from | ||
0 to 2L for L > 0 and from 0 to (-L) for L < 0. | ||
""" | ||
|
||
_base_fields_required = RealhydrogenOrbital._base_fields_required | ||
|
||
_base_fields_optional = tuple( | ||
list(filter(lambda x: x[0] != 'spin', RealhydrogenOrbital._base_fields_optional)) + [ | ||
('spin', validate_spin, 0.0), | ||
] | ||
) | ||
|
||
def __str__(self): | ||
"""Printable representation of the orbital.""" | ||
orb_dict = self.get_orbital_dict() | ||
try: | ||
orb_name = self.get_name_from_quantum_numbers( | ||
orb_dict['angular_momentum'], magnetic_number=orb_dict['magnetic_number'] | ||
) | ||
position_string = '{:.4f},{:.4f},{:.4f}'.format( | ||
orb_dict['position'][0], orb_dict['position'][1], orb_dict['position'][2] | ||
) | ||
out_string = 'r{} {} (s_z={}) orbital {} @ {}'.format( | ||
orb_dict['radial_nodes'], orb_name, orb_dict['spin'], | ||
"for kind '{}'".format(orb_dict['kind_name']) if orb_dict['kind_name'] else '', position_string | ||
) | ||
except KeyError: | ||
# Should not happen, but we want it not to crash in __str__ | ||
out_string = '(not all parameters properly set)' | ||
return out_string |
143 changes: 143 additions & 0 deletions
143
aiida_quantumespresso/tools/data/orbital/spinorbithydrogen.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
# -*- coding: utf-8 -*- | ||
"""A module defining hydrogen-like orbitals. | ||
The orbitals are defined in the simultaneous eigen-basis of H, J^2, L^2, S^2 | ||
and J_z operators, useful for projecting densities with spin-orbit coupling. | ||
""" | ||
|
||
from aiida.common.exceptions import ValidationError | ||
|
||
from aiida.tools.data.orbital.orbital import Orbital | ||
|
||
|
||
def validate_j(value): | ||
"""Validate the value of the total angular momentum.""" | ||
if not isinstance(value, (int, float)): | ||
raise ValidationError('total angular momentum (j) must be float') | ||
|
||
if not value % 0.5 == 0.0: | ||
raise ValidationError('total angular momentum (j) must be a half integer') | ||
|
||
if value < 0.5 or value > 3.5: | ||
raise ValidationError('total angular momentum (j) must be between 0.5 and 3.5') | ||
|
||
return value | ||
|
||
|
||
def validate_l(value): | ||
"""Validate the value of the angular momentum.""" | ||
if not isinstance(value, int): | ||
raise ValidationError('angular momentum (l) must be integer') | ||
|
||
if value < 0 or value > 3: | ||
raise ValidationError('angular momentum (l) must be between 0 and 3') | ||
|
||
return value | ||
|
||
|
||
def validate_mj(value): | ||
"""Validate the value of the magnetic number.""" | ||
if not isinstance(value, (int, float)): | ||
raise ValidationError('magnetic number (m_j) must be float') | ||
|
||
# without knowing j we cannot validate the value of m_j. We will call an additional function | ||
# in the validate function | ||
|
||
return value | ||
|
||
|
||
def validate_kind_name(value): | ||
"""Validate the value of the kind_name.""" | ||
if value is not None and not isinstance(value, str): | ||
raise ValidationError('kind_name must be a string') | ||
|
||
return value | ||
|
||
|
||
def validate_n(value): | ||
"""Validate the value of the number of radial nodes.""" | ||
if not isinstance(value, int): | ||
raise ValidationError('number of radial nodes (n) must be integer') | ||
|
||
if value < 0 or value > 2: | ||
raise ValidationError('number of radial nodes (n) must be between 0 and 2') | ||
|
||
return value | ||
|
||
|
||
class SpinorbitHydrogenOrbital(Orbital): | ||
"""Orbitals for hydrogen with spin-orbit interaction. | ||
The orbital is defined in the common basis of H, J^2, L^2, S^2, J_z | ||
operators, indexed by the quantum numbers n, j, l, j_z. | ||
A brief description of what is meant by each of these labels: | ||
:param radial_nodes: the number of radial nodes (or inflections) if no radial | ||
nodes are supplied, defaults to 0 | ||
:param angular_momentum: Angular quantum number l | ||
:param total_angular_momentum: Total angular momentum number j | ||
:param magnetic_number: Magnetic quantum number m_j | ||
The total angular momentum quantum number j takes values `|l-s| <= j <= | ||
l+s` in integer steps and the magnetic number takes values from -j to +j in | ||
integer steps. | ||
""" | ||
|
||
_base_fields_required = tuple( | ||
list(Orbital._base_fields_required) + | ||
[('total_angular_momentum', | ||
validate_j), ('angular_momentum', validate_l), ('magnetic_number', validate_mj), ('radial_nodes', validate_n)] | ||
) | ||
|
||
_base_fields_optional = tuple(list(Orbital._base_fields_optional) + [ | ||
('kind_name', validate_kind_name, None), | ||
]) | ||
|
||
def __str__(self): | ||
"""Printable representation of the orbital.""" | ||
orb_dict = self.get_orbital_dict() | ||
try: | ||
orb_name = 'j={},l={},m_j={}'.format( | ||
orb_dict['total_angular_momentum'], orb_dict['angular_momentum'], orb_dict['magnetic_number'] | ||
) | ||
position_string = '{:.4f},{:.4f},{:.4f}'.format( | ||
orb_dict['position'][0], orb_dict['position'][1], orb_dict['position'][2] | ||
) | ||
out_string = 'r{} {} orbital {} @ {}'.format( | ||
orb_dict['radial_nodes'], orb_name, | ||
"for kind '{}'".format(orb_dict['kind_name']) if orb_dict['kind_name'] else '', position_string | ||
) | ||
except KeyError: | ||
# Should not happen, but we want it not to crash in __str__ | ||
out_string = '(not all parameters properly set)' | ||
return out_string | ||
|
||
def _validate_keys(self, input_dict): | ||
"""Validate the keys otherwise raise ValidationError. | ||
Does basic validation from the parent followed by validations for the | ||
quantum numbers. Raises exceptions should the input_dict fail the | ||
valiation or if it contains any unsupported keywords. :param | ||
input_dict: the dictionary of keys to be validated :return | ||
validated_dict: a validated dictionary | ||
""" | ||
validated_dict = super()._validate_keys(input_dict) | ||
|
||
# Validate m knowing the value of l | ||
total_angular_momentum = validated_dict['total_angular_momentum'] # j quantum number, must be there | ||
angular_momentum = validated_dict['angular_momentum'] # l quantum number, must be there | ||
accepted_range = [abs(angular_momentum - 0.5), angular_momentum + 0.5] | ||
if total_angular_momentum < min(accepted_range) or total_angular_momentum > max(accepted_range): | ||
raise ValidationError( | ||
'the total angular momentum must be in the range [{}, {}]'.format( | ||
min(accepted_range), max(accepted_range) | ||
) | ||
) | ||
magnetic_number = validated_dict['magnetic_number'] # m quantum number, must be there | ||
accepted_range = [-total_angular_momentum, total_angular_momentum] | ||
if magnetic_number < min(accepted_range) or magnetic_number > max(accepted_range): | ||
raise ValidationError( | ||
'the magnetic number must be in the range [{}, {}]'.format(min(accepted_range), max(accepted_range)) | ||
) | ||
|
||
return validated_dict |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
&PROJWFC | ||
degauss = 2.0000000000d-02 | ||
deltae = 1.0000000000d-01 | ||
emax = 7.9029595890d+00 | ||
emin = 5.9029595890d+00 | ||
kresolveddos = .false. | ||
lbinary_data = .false. | ||
lsym = .true. | ||
lwrite_overlaps = .false. | ||
ngauss = 0 | ||
outdir = './out/' | ||
plotboxes = .false. | ||
prefix = 'aiida' | ||
tdosinboxes = .false. | ||
/ |
Oops, something went wrong.