irrep 3.1.0__tar.gz → 3.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {irrep-3.1.0 → irrep-3.2.0}/.github/workflows/tests.yml +1 -7
- {irrep-3.1.0 → irrep-3.2.0}/PKG-INFO +1 -1
- {irrep-3.1.0 → irrep-3.2.0}/irrep/__init__.py +1 -1
- {irrep-3.1.0 → irrep-3.2.0}/irrep/_version.py +3 -3
- irrep-3.2.0/irrep/altermagnetic_transformer.py +90 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/bandstructure.py +6 -4
- {irrep-3.1.0 → irrep-3.2.0}/irrep/kpoint_gpaw.py +35 -27
- {irrep-3.1.0 → irrep-3.2.0}/irrep/parsers/espresso.py +10 -5
- {irrep-3.1.0 → irrep-3.2.0}/irrep/parsers/gpaw.py +2 -2
- {irrep-3.1.0 → irrep-3.2.0}/irrep/parsers/vasp.py +1 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/spacegroup.py +37 -4
- {irrep-3.1.0 → irrep-3.2.0}/irrep/symmetry_operation.py +34 -25
- irrep-3.2.0/irrep/tests/test_altermagnetic.py +26 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/test_gpaw.py +3 -2
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/test_spacegroup.py +49 -3
- {irrep-3.1.0 → irrep-3.2.0}/irrep.egg-info/PKG-INFO +1 -1
- {irrep-3.1.0 → irrep-3.2.0}/irrep.egg-info/SOURCES.txt +2 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep.egg-info/scm_file_list.json +5766 -5764
- irrep-3.2.0/irrep.egg-info/scm_version.json +8 -0
- irrep-3.1.0/irrep.egg-info/scm_version.json +0 -8
- {irrep-3.1.0 → irrep-3.2.0}/.github/workflows/python-publish.yml +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/.gitignore +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/INSTALL +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/LICENSE +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/MANIFEST.in +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/README.md +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/cli.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/data/msg_numbers.data +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/ebrs.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/gvectors.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/kpoint.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/or_solutions_obtainer.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/parsers/__init__.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/parsers/abinit.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/parsers/common.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/parsers/parse_files_bandstructure.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/parsers/wannier90.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/spacegroup_irreps.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/__init__.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/conftest.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/ref_data/Fe_qe_dmn_TR=False.pkl +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/ref_data/Fe_qe_dmn_TR=False_irred.pkl +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/ref_data/Fe_qe_dmn_TR=True.pkl +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/ref_data/Fe_qe_dmn_TR=True_irred.pkl +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/ref_data/magmom_Fe_qe_dmn.txt +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/ref_data/spacegroup_Te_TR.npz +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/ref_data/spacegroup_Te_TR_product.npz +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/ref_data/spacegroup_Te_noTR.npz +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/ref_data/spacegroup_Te_noTR_product.npz +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/ref_data/symm_matrix_block.npz +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/ref_data/symm_matrix_full.npz +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/test_abinit_scalar.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/test_abinit_spinor.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/test_dmn.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/test_ebr_decomposition.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/test_espresso_hdf5.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/test_espresso_spinor.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/test_espresso_spinpol.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/test_paw.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/test_refUC.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/test_symmetry_indicators.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/test_symsep.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/test_vasp_scalar.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/test_vasp_spinor.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/test_wannier_scalar.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/tests/test_wannier_spinor.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep/utility.py +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep.egg-info/dependency_links.txt +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep.egg-info/entry_points.txt +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep.egg-info/requires.txt +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/irrep.egg-info/top_level.txt +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/pyproject.toml +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/setup.cfg +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi/POSCAR +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi/README.md +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi/WAVECAR +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi/images/bands.png +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi/images/checktopomat.png +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi/inputs_maxK/INCAR +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi/inputs_maxK/KPOINTS +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi/inputs_maxK/POSCAR +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi/inputs_maxK/POTCAR +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi/inputs_scf/INCAR +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi/inputs_scf/KPOINTS +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi/inputs_scf/POSCAR +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi/inputs_scf/POTCAR +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi2Se3/README.md +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi2Se3/images/bands.png +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi2Se3/images/checktopomat.png +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi2Se3/inputs/Bi.txt +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi2Se3/inputs/Se.txt +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi2Se3/inputs/bands.in +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi2Se3/inputs/nscf_IrRep.in +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi2Se3/inputs/pp.bands.in +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi2Se3/inputs/scf.in +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi2Se3/out/Bi2Se3.save/charge-density.dat +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi2Se3/out/Bi2Se3.save/data-file-schema.xml +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi2Se3/out/Bi2Se3.save/paw.txt +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi2Se3/out/Bi2Se3.save/wfc1.dat +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi2Se3/out/Bi2Se3.save/wfc2.dat +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi2Se3/out/Bi2Se3.save/wfc3.dat +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi2Se3/out/Bi2Se3.save/wfc4.dat +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/Bi2Se3/out/Bi2Se3.xml +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/SnTe/README.md +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/SnTe/images/bands.png +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/SnTe/images/checktopomat.png +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/SnTe/images/refUC.png +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/SnTe/inputs/Sn.xml +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/SnTe/inputs/Te.xml +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/SnTe/inputs/parameters.in +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/tutorial/SnTe/maxK_WFK +0 -0
- {irrep-3.1.0 → irrep-3.2.0}/uploadpypi.sh +0 -0
|
@@ -37,12 +37,6 @@ jobs:
|
|
|
37
37
|
- name: Install irrep and dependencies
|
|
38
38
|
run: |
|
|
39
39
|
pip install -e .
|
|
40
|
-
- name: Set PYTHONPATH
|
|
41
|
-
run: echo "PYTHONPATH=$(pwd):$(pwd)/irreptables" >> $GITHUB_ENV
|
|
42
|
-
- name: Run tests
|
|
43
|
-
run: pytest
|
|
44
|
-
env:
|
|
45
|
-
PYTHONPATH: ${{ env.PYTHONPATH }}
|
|
46
40
|
- name: pytest
|
|
47
41
|
run: |
|
|
48
|
-
pytest
|
|
42
|
+
pytest
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: irrep
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: A tool to get symmetry proberties of ab-initio wavefunctions, irreduible representations and more.
|
|
5
5
|
Author-email: "Stepan S. Tsirkin" <stepan.tsirkin@ehu.eus>
|
|
6
6
|
License: GNU General Public License v3 (GPLv3)
|
|
@@ -2,4 +2,4 @@ try:
|
|
|
2
2
|
from ._version import version as __version__
|
|
3
3
|
except ImportError:
|
|
4
4
|
__version__ = "unknown"
|
|
5
|
-
from .parsers.parse_files_bandstructure import parse_files as parse_bandstructure
|
|
5
|
+
from .parsers.parse_files_bandstructure import parse_files as parse_bandstructure # noqa: F401
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '3.
|
|
22
|
-
__version_tuple__ = version_tuple = (3,
|
|
21
|
+
__version__ = version = '3.2.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (3, 2, 0)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g5a82e29d3'
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from .spacegroup import SpaceGroup
|
|
4
|
+
from .utility import all_close_mod1
|
|
5
|
+
from .symmetry_operation import SymmetryOperation, get_atom_map
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AltermagneticTransformer:
|
|
9
|
+
|
|
10
|
+
def __init__(self, calculator,
|
|
11
|
+
symmetrizer_up,
|
|
12
|
+
alter_symop=None,
|
|
13
|
+
rotation_latt=None,
|
|
14
|
+
translation_latt=None,
|
|
15
|
+
):
|
|
16
|
+
spacegroup_up = symmetrizer_up.spacegroup
|
|
17
|
+
if alter_symop is None:
|
|
18
|
+
alter_symop = SymmetryOperation(rot=rotation_latt,
|
|
19
|
+
trans=translation_latt,
|
|
20
|
+
Lattice=spacegroup_up.Lattice,
|
|
21
|
+
spinor=False
|
|
22
|
+
)
|
|
23
|
+
self.alter_symop = alter_symop
|
|
24
|
+
self.alter_symop.set_gpaw(calculator)
|
|
25
|
+
# self.symops = [symop * alter_symop for symop in spacegroup_up.symmetries]
|
|
26
|
+
self.alter_map = [] # alter_map[i] = j means ki = alter_map_symops[i] * kj
|
|
27
|
+
self.alter_map_isym = []
|
|
28
|
+
self.symmetry_operations = []
|
|
29
|
+
self.k_intermediate = []
|
|
30
|
+
for kirr in symmetrizer_up.kptirr:
|
|
31
|
+
kpt_red = symmetrizer_up.kpoints_all[kirr]
|
|
32
|
+
kpt_red_before_transform = alter_symop.transform_k(kpt_red, inverse=True)
|
|
33
|
+
for ik, kpt in enumerate(symmetrizer_up.kpoints_all):
|
|
34
|
+
if all_close_mod1(kpt, kpt_red_before_transform):
|
|
35
|
+
self.alter_map.append(symmetrizer_up.kpt2kptirr[ik])
|
|
36
|
+
isym = symmetrizer_up.kpt2kptirr_sym[ik]
|
|
37
|
+
self.alter_map_isym.append(isym)
|
|
38
|
+
self.symmetry_operations.append(symmetrizer_up.spacegroup.symmetries[isym])
|
|
39
|
+
self.k_intermediate.append(kpt)
|
|
40
|
+
break
|
|
41
|
+
else:
|
|
42
|
+
raise RuntimeError(f"No corresponding k-point found for R({kpt_red})={kpt_red_before_transform} out of \n{symmetrizer_up.kpoints_all} under the altermagnetic symmetry operation.")
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def from_gpaw(cls, calculator, symmetrizer_up, nskip_symmetries=0):
|
|
46
|
+
symop = find_altermagnetic_symmetry(lattice=calculator.atoms.cell,
|
|
47
|
+
positions=calculator.atoms.get_scaled_positions(),
|
|
48
|
+
typat=calculator.atoms.get_atomic_numbers(),
|
|
49
|
+
magmom=calculator.get_magnetic_moments(),
|
|
50
|
+
nskip=nskip_symmetries)
|
|
51
|
+
return cls(calculator, symmetrizer_up, rotation_latt=symop.rotation, translation_latt=symop.translation)
|
|
52
|
+
|
|
53
|
+
def get_kpoint_down(self, ikirr, kpoints_up):
|
|
54
|
+
ik_origin = self.alter_map[ikirr]
|
|
55
|
+
KPtransformed = kpoints_up[ik_origin].get_transformed_copy(
|
|
56
|
+
symmetry_operation=self.symmetry_operations[ikirr],
|
|
57
|
+
k_new=self.k_intermediate[ikirr])
|
|
58
|
+
KPtransformed = KPtransformed.get_transformed_copy(
|
|
59
|
+
symmetry_operation=self.alter_symop,
|
|
60
|
+
k_new=kpoints_up[ikirr].k)
|
|
61
|
+
# TODO : unify the transformations, using just their product
|
|
62
|
+
return KPtransformed
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def find_altermagnetic_symmetry(lattice, positions, typat, magmom, nskip=0):
|
|
66
|
+
magmom = np.array(magmom)
|
|
67
|
+
assert magmom.ndim == 1, "Magnetic moments must be a 1D array."
|
|
68
|
+
positions = np.array(positions)
|
|
69
|
+
assert positions.ndim == 2 and positions.shape[1] == 3, "Positions must be a 2D array with shape (N, 3)."
|
|
70
|
+
typat = np.array(typat)
|
|
71
|
+
assert typat.ndim == 1, "Atom types must be a 1D array."
|
|
72
|
+
assert len(positions) == len(typat)
|
|
73
|
+
assert len(positions) == len(magmom), "Number of positions, atom types, and magnetic moments must match."
|
|
74
|
+
|
|
75
|
+
spacegroup_nomag = SpaceGroup.from_cell(real_lattice=lattice, positions=positions, typat=typat, include_TR=False)
|
|
76
|
+
if nskip >= len(spacegroup_nomag.symmetries) // 2:
|
|
77
|
+
raise ValueError(f"nskip={nskip} is too large for the number of symmetries={len(spacegroup_nomag.symmetries)}.")
|
|
78
|
+
cnt = 0
|
|
79
|
+
for symop in spacegroup_nomag.symmetries:
|
|
80
|
+
atommap, T = get_atom_map(symop, positions)
|
|
81
|
+
# print (f"Checking symmetry operation: {symop}, {atommap=}, {T=}")
|
|
82
|
+
assert np.all(typat[atommap] == typat), "Atom types must match under symmetry operation."
|
|
83
|
+
magmom_transformed = magmom[atommap]
|
|
84
|
+
print(f"{magmom_transformed=}, {atommap=},")
|
|
85
|
+
if np.allclose(magmom_transformed, -magmom):
|
|
86
|
+
cnt += 1
|
|
87
|
+
if cnt > nskip:
|
|
88
|
+
return symop
|
|
89
|
+
else:
|
|
90
|
+
raise ValueError("No altermagnetic symmetry operation found for the given magnetic moments.")
|
|
@@ -544,7 +544,7 @@ class BandStructure:
|
|
|
544
544
|
if read_paw:
|
|
545
545
|
spacegroup.set_gpaw(calculator=parser.calculator)
|
|
546
546
|
from irrep.kpoint_gpaw import OverlapPAW
|
|
547
|
-
overlap_paw = OverlapPAW(
|
|
547
|
+
overlap_paw = OverlapPAW(calc=parser.calculator)
|
|
548
548
|
|
|
549
549
|
NBin, kpred, Lattice, _spinor, typat, positions, EF_in = parser.parse_header()
|
|
550
550
|
if Ecut is None:
|
|
@@ -682,10 +682,10 @@ class BandStructure:
|
|
|
682
682
|
return True
|
|
683
683
|
return False
|
|
684
684
|
|
|
685
|
+
kpt_coords = [parser.get_kpt_coord(ik) for ik in kplist]
|
|
685
686
|
kplist_noskip = [ik for ik in kplist if not check_skip(parser.get_kpt_coord(ik))]
|
|
686
687
|
|
|
687
688
|
|
|
688
|
-
|
|
689
689
|
# Set Fermi energy
|
|
690
690
|
if isinstance(EF, str) and EF.lower() == "auto" or EF is None:
|
|
691
691
|
if EF_in is None:
|
|
@@ -722,8 +722,10 @@ class BandStructure:
|
|
|
722
722
|
|
|
723
723
|
|
|
724
724
|
# To do: create writer of description for this class
|
|
725
|
-
log_message(
|
|
726
|
-
|
|
725
|
+
log_message(f"Wave function file contains total {NK} k-points and {NBin} bands.", verbosity, 1)
|
|
726
|
+
log_message(f"Saving {len(kpt_coords)} k-points and {NBout} bands in the output.", verbosity, 1)
|
|
727
|
+
log_message(f'Saving k-points with coordinates: \n {kpt_coords}', verbosity, 2)
|
|
728
|
+
log_message(f'Saving bands with band indices: {IBstart + 1} to {IBend + 1}', verbosity, 2)
|
|
727
729
|
log_message(f"Energy cutoff in WAVECAR : {Ecut0}", verbosity, 1)
|
|
728
730
|
log_message(f"Energy cutoff reduced to : {Ecut}", verbosity, 1)
|
|
729
731
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from gpaw.utilities.partition import AtomPartition
|
|
2
1
|
import numpy as np
|
|
3
2
|
from .kpoint import KpointAbstract
|
|
4
3
|
from .utility import cached_einsum
|
|
@@ -24,11 +23,10 @@ class KpointGPAW(KpointAbstract):
|
|
|
24
23
|
def __init__(self, kpt, wavefunction=None, proj=None, nbands=None,
|
|
25
24
|
RecLattice=None,
|
|
26
25
|
atom_positions=None):
|
|
27
|
-
super().__init__(kpt=kpt, num_bands=nbands)
|
|
26
|
+
super().__init__(kpt=kpt, num_bands=nbands, RecLattice=RecLattice)
|
|
28
27
|
self.wavefunction = wavefunction
|
|
29
28
|
self.proj = proj
|
|
30
29
|
self.nbands = nbands
|
|
31
|
-
self.RecLattice = RecLattice
|
|
32
30
|
self.atom_positions = atom_positions
|
|
33
31
|
|
|
34
32
|
@classmethod
|
|
@@ -39,8 +37,8 @@ class KpointGPAW(KpointAbstract):
|
|
|
39
37
|
kpt = calc.wfs.kpt_qs[ibz_index][ispin]
|
|
40
38
|
k = calc.get_ibz_k_points()[ibz_index]
|
|
41
39
|
nbands = IBend - IBstart
|
|
42
|
-
proj =
|
|
43
|
-
|
|
40
|
+
proj = np.hstack([kpt.P_ani[a][IBstart:IBend] for a in range(len(calc.wfs.setups))])
|
|
41
|
+
|
|
44
42
|
# Get projections in ibz
|
|
45
43
|
if RecLattice is None:
|
|
46
44
|
RecLattice = calc.wfs.gd.reciprocal_lattice
|
|
@@ -48,7 +46,10 @@ class KpointGPAW(KpointAbstract):
|
|
|
48
46
|
|
|
49
47
|
wavefunction = np.array([calc.wfs.get_wave_function_array(n, ibz_index, ispin, periodic=True)
|
|
50
48
|
for n in range(IBstart, IBend)])
|
|
51
|
-
return KpointGPAW(kpt=k, wavefunction=wavefunction, proj=proj, nbands=nbands,
|
|
49
|
+
return KpointGPAW(kpt=k, wavefunction=wavefunction, proj=proj, nbands=nbands,
|
|
50
|
+
RecLattice=RecLattice,
|
|
51
|
+
atom_positions=atom_positions)
|
|
52
|
+
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
def get_transformed_copy(self, symmetry_operation, k_new):
|
|
@@ -69,15 +70,27 @@ class KpointGPAW(KpointAbstract):
|
|
|
69
70
|
|
|
70
71
|
class OverlapPAW:
|
|
71
72
|
|
|
72
|
-
def __init__(self,
|
|
73
|
+
def __init__(self, calc, get_soc=True):
|
|
74
|
+
|
|
73
75
|
self.dO_aii = {}
|
|
74
|
-
for a in wfs.
|
|
75
|
-
self.dO_aii[a] = np.copy(
|
|
76
|
-
self.
|
|
76
|
+
for a, setup in enumerate(calc.wfs.setups):
|
|
77
|
+
self.dO_aii[a] = np.copy(setup.dO_ii)
|
|
78
|
+
self.orb_atom_indices = np.cumsum([0] + [dO.shape[0] for dO in self.dO_aii.values()])
|
|
79
|
+
self.dv = np.copy(calc.wfs.gd.dv)
|
|
80
|
+
if get_soc:
|
|
81
|
+
from gpaw.spinorbit import soc
|
|
82
|
+
self.dVL_avii = [
|
|
83
|
+
soc(calc.wfs.setups[a], calc.hamiltonian.xc, calc.density.D_asp[a])
|
|
84
|
+
for a in range(len(calc.wfs.setups))]
|
|
85
|
+
|
|
77
86
|
# print ("Initialized PAW overlap with dO_ii for atoms", list(self.dO_aii.keys()))
|
|
78
87
|
# print ("dO_ii shapes", {a: dO.shape for a, dO in self.dO_aii.items()})
|
|
79
88
|
# print ("dv", self.dv)
|
|
80
89
|
|
|
90
|
+
def get_orb_indices_on_atom(self, atom_index):
|
|
91
|
+
"""Get the orbital indices corresponding to a specific atom index."""
|
|
92
|
+
return self.orb_atom_indices[[atom_index, atom_index + 1]]
|
|
93
|
+
|
|
81
94
|
def product(self, KP1, KP2,
|
|
82
95
|
include_paw=True,
|
|
83
96
|
include_pseudo=True,
|
|
@@ -101,23 +114,18 @@ class OverlapPAW:
|
|
|
101
114
|
else:
|
|
102
115
|
phases = np.exp(-2j * np.pi * (positions @ bk))
|
|
103
116
|
for a, dO_ii in self.dO_aii.items():
|
|
104
|
-
|
|
117
|
+
I1, I2 = self.get_orb_indices_on_atom(a)
|
|
118
|
+
prod += (proj1[:, I1:I2].conj() @ dO_ii @ (proj2[:, I1:I2].T)) * phases[a]
|
|
105
119
|
return prod
|
|
106
120
|
|
|
107
121
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
self.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def new_paw_projection(proj,
|
|
119
|
-
**kwargs):
|
|
120
|
-
ap = AtomPartitionSerial(proj.atom_partition)
|
|
121
|
-
new_projection = proj.new(bcomm=None, atom_partition=ap, **kwargs)
|
|
122
|
-
new_projection.atom_partition = ap
|
|
123
|
-
return new_projection
|
|
122
|
+
def soc(self, KP1, KP2):
|
|
123
|
+
from ase.units import Hartree
|
|
124
|
+
dVsoc = np.zeros((3, KP1.nbands, KP2.nbands), dtype=complex)
|
|
125
|
+
for a, dVL_vii in enumerate(self.dVL_avii):
|
|
126
|
+
I1, I2 = self.get_orb_indices_on_atom(a)
|
|
127
|
+
P1_mi = KP1.proj[:, I1:I2].conj()
|
|
128
|
+
P2_mi = KP2.proj[:, I1:I2].T
|
|
129
|
+
for t in range(3):
|
|
130
|
+
dVsoc[t] += P1_mi @ dVL_vii[t] @ P2_mi
|
|
131
|
+
return dVsoc * Hartree
|
|
@@ -23,10 +23,12 @@ class ParserEspresso(ParserCommon):
|
|
|
23
23
|
outp = myroot.find("output")
|
|
24
24
|
self.bandstr = outp.find("band_structure")
|
|
25
25
|
self.spinor = str2bool(self.bandstr.find("noncolin").text)
|
|
26
|
+
recip_lattice_alat = outp.find("basis_set").find("reciprocal_lattice")
|
|
27
|
+
recip_lattice_alat = np.array([recip_lattice_alat.find(f"b{i}").text.split() for i in range(1, 4)], dtype=float)
|
|
28
|
+
self.recip_lattice_alat_inv = np.linalg.inv(recip_lattice_alat)
|
|
26
29
|
|
|
27
30
|
def parse_header(self, spin_channel=None):
|
|
28
|
-
Ecut0 = float(self.input.find("basis").find("ecutwfc").text)
|
|
29
|
-
Ecut0 *= Hartree_eV
|
|
31
|
+
Ecut0 = float(self.input.find("basis").find("ecutwfc").text) * Hartree_eV
|
|
30
32
|
NK = len(self.bandstr.findall("ks_energies"))
|
|
31
33
|
|
|
32
34
|
self.spin_channel = spin_channel
|
|
@@ -103,6 +105,7 @@ class ParserEspresso(ParserCommon):
|
|
|
103
105
|
def get_kpt_coord(self, ik):
|
|
104
106
|
kptxml = self.bandstr.findall("ks_energies")[ik]
|
|
105
107
|
kpt = np.array(kptxml.find("k_point").text.split(), dtype=float)
|
|
108
|
+
kpt = kpt.dot(self.recip_lattice_alat_inv)
|
|
106
109
|
return kpt
|
|
107
110
|
|
|
108
111
|
def parse_kpoint(self, ik, verbosity=0, getE=True, getWF=True):
|
|
@@ -119,8 +122,7 @@ class ParserEspresso(ParserCommon):
|
|
|
119
122
|
NBin = self.NBin_list[0]
|
|
120
123
|
|
|
121
124
|
if getE:
|
|
122
|
-
Energy = np.array(kptxml.find("eigenvalues").text.split(), dtype=float)[NB_skip: NB_skip + NBin]
|
|
123
|
-
Energy *= Hartree_eV
|
|
125
|
+
Energy = np.array(kptxml.find("eigenvalues").text.split(), dtype=float)[NB_skip: NB_skip + NBin] * Hartree_eV
|
|
124
126
|
else:
|
|
125
127
|
Energy = None
|
|
126
128
|
npw = int(kptxml.find("npw").text)
|
|
@@ -133,6 +135,7 @@ class ParserEspresso(ParserCommon):
|
|
|
133
135
|
checked_files = []
|
|
134
136
|
for extension in ["hdf5", "dat"]:
|
|
135
137
|
for strcase in [str.lower, str.upper]:
|
|
138
|
+
print(f"{ik=}, {wfcname=}, {extension=}, {strcase=}")
|
|
136
139
|
filename = f"{self.prefix}.save/{strcase(wfcname)}.{extension}"
|
|
137
140
|
if os.path.exists(filename):
|
|
138
141
|
if extension == "hdf5":
|
|
@@ -152,7 +155,8 @@ class ParserEspresso(ParserCommon):
|
|
|
152
155
|
for atr in attrnames:
|
|
153
156
|
attributes.append(fWFC.attrs[atr])
|
|
154
157
|
|
|
155
|
-
_gamma_only, _igwx,
|
|
158
|
+
_gamma_only, _igwx, ik_read, _ispin, _nbnd, _ngw, _npol, _scale_factor, xk = attributes
|
|
159
|
+
assert ik_read == ik + 1, f"Found ik={ik_read} in the wavefunction file {filename}, but expected ik={ik + 1}"
|
|
156
160
|
kpt = np.array(xk)
|
|
157
161
|
Miller_Indices = fWFC["MillerIndices"]
|
|
158
162
|
B = np.array([Miller_Indices.attrs[f"bg{i}"] for i in range(1, 4)])
|
|
@@ -178,6 +182,7 @@ class ParserEspresso(ParserCommon):
|
|
|
178
182
|
for ib in range(NBin):
|
|
179
183
|
rec = fWFC.read_record(f"{npwtot * 2}f8")
|
|
180
184
|
WF[ib] = rec[0::2] + 1.0j * rec[1::2]
|
|
185
|
+
assert np.allclose(kpt, self.get_kpt_coord(ik)), f"Found kpt[{ik}] {kpt} in the wavefunction file {filename}, but expected {self.get_kpt_coord(ik)}"
|
|
181
186
|
WF = WF.reshape((NBin, npw, nspinor), order="F")
|
|
182
187
|
return WF, Energy, kg, kpt
|
|
183
188
|
checked_files.append(filename)
|
|
@@ -63,6 +63,7 @@ class ParserGPAW(ParserCommon):
|
|
|
63
63
|
log_message(f"shapes of WFupdw: {[wf.shape for wf in WFupdw]}", self.verbosity, 2)
|
|
64
64
|
ngx, ngy, ngz = WFupdw[0].shape[1:]
|
|
65
65
|
kpt = self.calculator.get_ibz_k_points()[ik]
|
|
66
|
+
assert np.allclose(kpt, self.get_kpt_coord(ik)), f"Found kpt[{ik}] {kpt} in the wavefunction file, but expected {self.get_kpt_coord(ik)}"
|
|
66
67
|
kg, eKG = calc_gvectors(
|
|
67
68
|
kpt,
|
|
68
69
|
RecLattice,
|
|
@@ -157,11 +158,10 @@ def get_soc_gpaw(calc, ik, flatten=True, theta=0, phi=0):
|
|
|
157
158
|
P2_mi = calc.wfs.kpt_qs[q][0].P_ani[a]
|
|
158
159
|
|
|
159
160
|
h_soc[s1, s2] += np.dot(np.dot(P1_mi.conj(), h_ii), P2_mi.T)
|
|
160
|
-
h_soc[:] *= Hartree
|
|
161
161
|
if flatten:
|
|
162
162
|
h_soc_old = h_soc.copy()
|
|
163
163
|
h_soc = np.zeros((2 * m, 2 * m), complex)
|
|
164
164
|
for s1 in range(2):
|
|
165
165
|
for s2 in range(2):
|
|
166
166
|
h_soc[s1::2, s2::2] = h_soc_old[s1, s2]
|
|
167
|
-
return h_soc
|
|
167
|
+
return h_soc * Hartree
|
|
@@ -134,6 +134,7 @@ class ParserVasp(ParserCommon):
|
|
|
134
134
|
assert npw % 2 == 0, f"odd number of coefs {npw} for spinor wavefunctions"
|
|
135
135
|
npw //= nspinor
|
|
136
136
|
kpt = r[1:4]
|
|
137
|
+
assert np.allclose(kpt, self.get_kpt_coord(ik)), f"Found kpt[{ik}] {kpt} in the wavefunction file, but expected {self.get_kpt_coord(ik)}"
|
|
137
138
|
if getE:
|
|
138
139
|
Energy = np.array(r[4: 4 + NBin * 3]).reshape(NBin, 3)[:, 0]
|
|
139
140
|
else:
|
|
@@ -30,6 +30,8 @@ import os
|
|
|
30
30
|
|
|
31
31
|
from pyxtal import Group
|
|
32
32
|
from sympy import Matrix, sympify
|
|
33
|
+
from sympy.parsing.sympy_parser import parse_expr, standard_transformations, implicit_multiplication_application
|
|
34
|
+
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
class SpaceGroup:
|
|
@@ -877,6 +879,7 @@ class SpaceGroup:
|
|
|
877
879
|
mag_symprec=0.05,
|
|
878
880
|
typat=None,
|
|
879
881
|
magmom=None,
|
|
882
|
+
magnetic=True,
|
|
880
883
|
spinor=None,
|
|
881
884
|
verbosity=0,
|
|
882
885
|
**kwargs_cell):
|
|
@@ -908,9 +911,12 @@ class SpaceGroup:
|
|
|
908
911
|
lattice = calculator.atoms.cell
|
|
909
912
|
if typat is None:
|
|
910
913
|
typat = calculator.atoms.get_atomic_numbers()
|
|
911
|
-
if
|
|
912
|
-
magmom
|
|
913
|
-
|
|
914
|
+
if magnetic:
|
|
915
|
+
if magmom is None:
|
|
916
|
+
magmom = calculator.get_magnetic_moments()
|
|
917
|
+
assert magmom.shape == (len(calculator.atoms),)
|
|
918
|
+
else:
|
|
919
|
+
magmom = np.zeros(len(calculator.atoms), dtype=float)
|
|
914
920
|
magmom = group_numbers(magmom, precision=mag_symprec)
|
|
915
921
|
magmom_set = set(magmom)
|
|
916
922
|
if len(magmom_set) > 1:
|
|
@@ -1055,7 +1061,8 @@ class SpaceGroup:
|
|
|
1055
1061
|
for sim_vector in conventional_wyckoffs:
|
|
1056
1062
|
|
|
1057
1063
|
# sim_vector is a string on array shape change to numeric or simbolic
|
|
1058
|
-
|
|
1064
|
+
_TRANSFORMS = standard_transformations + (implicit_multiplication_application,)
|
|
1065
|
+
vector = Matrix([parse_expr(v, transformations=_TRANSFORMS) for v in sim_vector.split(',')])
|
|
1059
1066
|
wyckoff_new_base = inv_transformation_matrix * (vector - origin_shift)
|
|
1060
1067
|
|
|
1061
1068
|
# Change to string again
|
|
@@ -1067,6 +1074,32 @@ class SpaceGroup:
|
|
|
1067
1074
|
return wyckoffs_in_cell_base
|
|
1068
1075
|
|
|
1069
1076
|
|
|
1077
|
+
def get_transformed_group_index(self, symmetry_operation):
|
|
1078
|
+
"""
|
|
1079
|
+
calculates the operations S^-1*g*S for a given symmetry operation S and each symmetry operation g in the group. Returns the index of the transformed operation in the group.
|
|
1080
|
+
Also returns the translation difference between the transformed operation and the corresponding operation in the group.
|
|
1081
|
+
"""
|
|
1082
|
+
transformed_group_index = []
|
|
1083
|
+
translation_difference = []
|
|
1084
|
+
symmetry_operation_inv = symmetry_operation.inverse(mod1=False)
|
|
1085
|
+
for sym in self.symmetries:
|
|
1086
|
+
transformed_sym = symmetry_operation_inv.multiply_keeptransl(sym).multiply_keeptransl(symmetry_operation)
|
|
1087
|
+
sym.show()
|
|
1088
|
+
transformed_sym.show()
|
|
1089
|
+
for i, sym2 in enumerate(self.symmetries):
|
|
1090
|
+
if transformed_sym.equals(sym2, mod1=True) and transformed_sym.time_reversal == sym2.time_reversal:
|
|
1091
|
+
transl_diff = transformed_sym.translation - sym2.translation
|
|
1092
|
+
transl_diff_int = np.round(transl_diff).astype(int)
|
|
1093
|
+
if np.allclose(transl_diff, transl_diff_int):
|
|
1094
|
+
transformed_group_index.append(i)
|
|
1095
|
+
translation_difference.append(transl_diff_int)
|
|
1096
|
+
break
|
|
1097
|
+
else:
|
|
1098
|
+
raise ValueError(f"Transformed symmetry operation {transformed_sym.str()} not found in the group")
|
|
1099
|
+
return np.array(transformed_group_index), np.array(translation_difference).astype(int)
|
|
1100
|
+
|
|
1101
|
+
|
|
1102
|
+
|
|
1070
1103
|
def read_sym_file(fname):
|
|
1071
1104
|
"""
|
|
1072
1105
|
Read symmetry operations from a file.
|
|
@@ -91,7 +91,10 @@ class SymmetryOperation():
|
|
|
91
91
|
positions=None,
|
|
92
92
|
translation_mod1=True, spinor_rotation=None):
|
|
93
93
|
self.ind = ind
|
|
94
|
-
|
|
94
|
+
rot_int = np.array(rot.round(), dtype=int)
|
|
95
|
+
if not np.allclose(rot, rot_int, atol=1e-6):
|
|
96
|
+
raise ValueError(f"Rotation matrix is not integer: {rot}")
|
|
97
|
+
self.rotation = rot_int
|
|
95
98
|
self.positions = positions
|
|
96
99
|
self.time_reversal = bool(time_reversal)
|
|
97
100
|
self.real_lattice = Lattice
|
|
@@ -111,7 +114,7 @@ class SymmetryOperation():
|
|
|
111
114
|
self.spinor_rotation = spinor_rotation
|
|
112
115
|
self.sign = 1 # May be changed later externally
|
|
113
116
|
|
|
114
|
-
def inverse(self):
|
|
117
|
+
def inverse(self, mod1=None):
|
|
115
118
|
"""
|
|
116
119
|
Create the inverse symmetry operation.
|
|
117
120
|
|
|
@@ -120,7 +123,7 @@ class SymmetryOperation():
|
|
|
120
123
|
SymmetryOperation
|
|
121
124
|
A new instance of the symmetry operation representing the inverse.
|
|
122
125
|
"""
|
|
123
|
-
rot_inv =
|
|
126
|
+
rot_inv = self.rotation_inv
|
|
124
127
|
trans_inv = -rot_inv @ self.translation
|
|
125
128
|
spinor_rot_inv = self.spinor_rotation.conj().T
|
|
126
129
|
if self.time_reversal:
|
|
@@ -132,7 +135,7 @@ class SymmetryOperation():
|
|
|
132
135
|
time_reversal=self.time_reversal,
|
|
133
136
|
ind=self.ind,
|
|
134
137
|
spinor=self.spinor,
|
|
135
|
-
translation_mod1=self.translation_mod1,
|
|
138
|
+
translation_mod1=self.translation_mod1 if mod1 is None else mod1,
|
|
136
139
|
spinor_rotation=spinor_rot_inv)
|
|
137
140
|
|
|
138
141
|
def __mul__(self, other):
|
|
@@ -216,6 +219,12 @@ class SymmetryOperation():
|
|
|
216
219
|
return False
|
|
217
220
|
return True
|
|
218
221
|
|
|
222
|
+
@cached_property
|
|
223
|
+
def rotation_inv(self):
|
|
224
|
+
rotinv = np.linalg.inv(self.rotation)
|
|
225
|
+
rotinv_int = np.array(rotinv.round(), dtype=int)
|
|
226
|
+
assert np.allclose(rotinv, rotinv_int, atol=1e-6), f"Inverse Rotation matrix is not integer: {rotinv}"
|
|
227
|
+
return rotinv_int
|
|
219
228
|
|
|
220
229
|
@property
|
|
221
230
|
def lattice(self):
|
|
@@ -458,18 +467,18 @@ class SymmetryOperation():
|
|
|
458
467
|
]
|
|
459
468
|
rotstr = [r + r1 for r, r1 in zip(rotstr, rotstr1)]
|
|
460
469
|
|
|
461
|
-
matrix =
|
|
470
|
+
matrix = self.rotation_inv.T
|
|
462
471
|
if self.time_reversal:
|
|
463
|
-
matrix
|
|
472
|
+
matrix = -matrix
|
|
464
473
|
kstring = "gk = [" + ", ".join(
|
|
465
474
|
[parse_row_transform(r) for r in matrix]
|
|
466
475
|
) + "]"
|
|
467
476
|
|
|
468
477
|
|
|
469
478
|
if write_ref:
|
|
470
|
-
matrix = np.
|
|
479
|
+
matrix = np.linalg.inv(R).T
|
|
471
480
|
if self.time_reversal:
|
|
472
|
-
matrix
|
|
481
|
+
matrix = -matrix
|
|
473
482
|
kstring += " | refUC: gk = [" + ", ".join(
|
|
474
483
|
[parse_row_transform(r) for r in matrix]
|
|
475
484
|
) + "]"
|
|
@@ -655,7 +664,7 @@ class SymmetryOperation():
|
|
|
655
664
|
|
|
656
665
|
return d
|
|
657
666
|
|
|
658
|
-
def transform_r(self, vector, inverse=False):
|
|
667
|
+
def transform_r(self, vector, inverse=False, translation=True):
|
|
659
668
|
"""
|
|
660
669
|
Transform a real-space vector (in lattice coordinates) under the symmetry operation.
|
|
661
670
|
|
|
@@ -670,9 +679,15 @@ class SymmetryOperation():
|
|
|
670
679
|
Transformed vector.
|
|
671
680
|
"""
|
|
672
681
|
if inverse:
|
|
673
|
-
|
|
682
|
+
result = np.array(vector)
|
|
683
|
+
if translation:
|
|
684
|
+
result -= self.translation[..., :]
|
|
685
|
+
return result.dot(self.rotation_inv.T)
|
|
674
686
|
else:
|
|
675
|
-
|
|
687
|
+
result = np.array(vector).dot(self.rotation.T).astype(float)
|
|
688
|
+
if translation:
|
|
689
|
+
result += self.translation[..., :]
|
|
690
|
+
return result
|
|
676
691
|
|
|
677
692
|
|
|
678
693
|
@cached_property
|
|
@@ -702,14 +717,6 @@ class SymmetryOperation():
|
|
|
702
717
|
def det(self):
|
|
703
718
|
return np.linalg.det(self.rotation)
|
|
704
719
|
|
|
705
|
-
|
|
706
|
-
@cached_property
|
|
707
|
-
def rotation_inv(self):
|
|
708
|
-
rotinv = np.linalg.inv(self.rotation)
|
|
709
|
-
rotinv_int = np.array(rotinv.round(), dtype=int)
|
|
710
|
-
assert np.allclose(rotinv, rotinv_int, atol=1e-6), f"Inverse Rotation matrix is not integer: {rotinv}"
|
|
711
|
-
return rotinv_int
|
|
712
|
-
|
|
713
720
|
@cached_property
|
|
714
721
|
def spinor_rotation_TR(self):
|
|
715
722
|
"""
|
|
@@ -862,6 +869,7 @@ class SymmetryOperation():
|
|
|
862
869
|
symmetry = SymmetryGpaw1(self)
|
|
863
870
|
R_aii = AtomRotations(setups.setups, setups.id_a, symmetry).get_R_asii()
|
|
864
871
|
self.R_aii = [R_ii[0] for R_ii in R_aii] # remove unnecessary nesting
|
|
872
|
+
self.orb_atom_indices = np.cumsum([0] + [R.shape[0] for R in self.R_aii])
|
|
865
873
|
|
|
866
874
|
# def get_U_aii_gpaw(self, kpoint):
|
|
867
875
|
# """Phase corrected rotation matrices for the PAW projections."""
|
|
@@ -896,16 +904,17 @@ class SymmetryOperation():
|
|
|
896
904
|
proj_rot : Projections
|
|
897
905
|
the rotated projection coefficients
|
|
898
906
|
"""
|
|
899
|
-
from .kpoint_gpaw import new_paw_projection
|
|
900
|
-
mapped_projections =
|
|
901
|
-
|
|
907
|
+
# from .kpoint_gpaw import new_paw_projection
|
|
908
|
+
mapped_projections = np.zeros_like(projections)
|
|
902
909
|
for a, R_ii in enumerate(self.R_aii):
|
|
903
|
-
|
|
910
|
+
b = self.atom_map[a]
|
|
911
|
+
I1, I2 = self.orb_atom_indices[ [a, a + 1]]
|
|
912
|
+
J1, J2 = self.orb_atom_indices[ [b, b + 1]]
|
|
913
|
+
Pout_ni = (projections[:, I1:I2] @ R_ii.T) # * np.exp(2j * np.pi * k_target @ self.atom_map_T[a])
|
|
904
914
|
if self.time_reversal:
|
|
905
915
|
Pout_ni = np.conj(Pout_ni)
|
|
906
916
|
Pout_ni = Pout_ni * np.exp(2j * np.pi * k_target @ self.atom_map_T[a])
|
|
907
|
-
|
|
908
|
-
mapped_projections.array[..., I1:I2] = Pout_ni
|
|
917
|
+
mapped_projections[:, J1:J2] = Pout_ni
|
|
909
918
|
return mapped_projections
|
|
910
919
|
|
|
911
920
|
def rotate_pseudo_wavefunction(self, psi_n_grid, k_origin, k_target):
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from pytest import approx
|
|
3
|
+
|
|
4
|
+
from irrep.altermagnetic_transformer import find_altermagnetic_symmetry
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_find_altermagnetic_symmetry():
|
|
8
|
+
|
|
9
|
+
a = 4.134
|
|
10
|
+
c = 6.652
|
|
11
|
+
lattice = a * np.array([[1, 0, 0], [-1 / 2, np.sqrt(3) / 2, 0], [0, 0, c / a]])
|
|
12
|
+
positions = np.array(
|
|
13
|
+
[
|
|
14
|
+
[0, 0, 0],
|
|
15
|
+
[0, 0, 1 / 2],
|
|
16
|
+
[1 / 3, 2 / 3, 1 / 4],
|
|
17
|
+
[2 / 3, 1 / 3, 3 / 4],
|
|
18
|
+
]
|
|
19
|
+
)
|
|
20
|
+
magmom = np.array([1, -1, 0, 0])
|
|
21
|
+
symop = find_altermagnetic_symmetry(lattice=lattice, positions=positions, typat=[1, 1, 2, 2], magmom=magmom)
|
|
22
|
+
assert symop.angle == approx(np.pi / 3), f"Expected angle=np.pi/3, got {symop.angle}"
|
|
23
|
+
assert symop.axis == approx([0, 0, 1]), f"Expected axis=[0, 0, 1], got {symop.axis}"
|
|
24
|
+
assert symop.translation == approx([0, 0, 0.5]), f"Expected translation=[0, 0, 0.5], got {symop.translation}"
|
|
25
|
+
assert symop.inversion is False, f"Expected inversion=False, got {symop.inversion}"
|
|
26
|
+
assert symop.time_reversal is False, f"Expected timereversal=False, got {symop.timereversal}"
|
|
@@ -3,6 +3,7 @@ import numpy as np
|
|
|
3
3
|
import pytest
|
|
4
4
|
from irrep.bandstructure import BandStructure
|
|
5
5
|
from pytest import approx
|
|
6
|
+
from .conftest import TEST_FILES_PATH
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
|
|
@@ -10,7 +11,7 @@ from pytest import approx
|
|
|
10
11
|
|
|
11
12
|
@pytest.mark.parametrize("spinor", [True, False])
|
|
12
13
|
def test_gpaw_spinorbit(spinor):
|
|
13
|
-
calc = GPAW("
|
|
14
|
+
calc = GPAW(f"{TEST_FILES_PATH}/gpaw/Bi-gamma.gpw")
|
|
14
15
|
|
|
15
16
|
nspinor = 2 if spinor else 1
|
|
16
17
|
|
|
@@ -26,7 +27,7 @@ def test_gpaw_spinorbit(spinor):
|
|
|
26
27
|
read_paw=not spinor, # for now, read_pae does not work with spinor calculations
|
|
27
28
|
search_cell=True,
|
|
28
29
|
kplist=[0])
|
|
29
|
-
output_file = "
|
|
30
|
+
output_file = f"{TEST_FILES_PATH}/gpaw/Bi-gamma_bandstructure.pickle"
|
|
30
31
|
bandstructure.pickle(output_file)
|
|
31
32
|
# print ("Bandstructure",bandstructure)
|
|
32
33
|
bandstructure.spacegroup.show()
|