irrep 3.2.0__tar.gz → 3.3.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.
Files changed (113) hide show
  1. {irrep-3.2.0 → irrep-3.3.0}/PKG-INFO +1 -1
  2. {irrep-3.2.0 → irrep-3.3.0}/irrep/_version.py +3 -3
  3. {irrep-3.2.0 → irrep-3.3.0}/irrep/altermagnetic_transformer.py +17 -8
  4. {irrep-3.2.0 → irrep-3.3.0}/irrep/bandstructure.py +19 -4
  5. {irrep-3.2.0 → irrep-3.3.0}/irrep/kpoint.py +1 -1
  6. {irrep-3.2.0 → irrep-3.3.0}/irrep/kpoint_gpaw.py +20 -8
  7. {irrep-3.2.0 → irrep-3.3.0}/irrep/spacegroup.py +22 -1
  8. irrep-3.3.0/irrep/storage.py +69 -0
  9. {irrep-3.2.0 → irrep-3.3.0}/irrep/symmetry_operation.py +4 -1
  10. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/test_gpaw.py +30 -0
  11. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/test_spacegroup.py +21 -1
  12. {irrep-3.2.0 → irrep-3.3.0}/irrep/utility.py +11 -0
  13. {irrep-3.2.0 → irrep-3.3.0}/irrep.egg-info/PKG-INFO +1 -1
  14. {irrep-3.2.0 → irrep-3.3.0}/irrep.egg-info/SOURCES.txt +1 -0
  15. {irrep-3.2.0 → irrep-3.3.0}/irrep.egg-info/scm_file_list.json +1 -0
  16. irrep-3.3.0/irrep.egg-info/scm_version.json +8 -0
  17. irrep-3.2.0/irrep.egg-info/scm_version.json +0 -8
  18. {irrep-3.2.0 → irrep-3.3.0}/.github/workflows/python-publish.yml +0 -0
  19. {irrep-3.2.0 → irrep-3.3.0}/.github/workflows/tests.yml +0 -0
  20. {irrep-3.2.0 → irrep-3.3.0}/.gitignore +0 -0
  21. {irrep-3.2.0 → irrep-3.3.0}/INSTALL +0 -0
  22. {irrep-3.2.0 → irrep-3.3.0}/LICENSE +0 -0
  23. {irrep-3.2.0 → irrep-3.3.0}/MANIFEST.in +0 -0
  24. {irrep-3.2.0 → irrep-3.3.0}/README.md +0 -0
  25. {irrep-3.2.0 → irrep-3.3.0}/irrep/__init__.py +0 -0
  26. {irrep-3.2.0 → irrep-3.3.0}/irrep/cli.py +0 -0
  27. {irrep-3.2.0 → irrep-3.3.0}/irrep/data/msg_numbers.data +0 -0
  28. {irrep-3.2.0 → irrep-3.3.0}/irrep/ebrs.py +0 -0
  29. {irrep-3.2.0 → irrep-3.3.0}/irrep/gvectors.py +0 -0
  30. {irrep-3.2.0 → irrep-3.3.0}/irrep/or_solutions_obtainer.py +0 -0
  31. {irrep-3.2.0 → irrep-3.3.0}/irrep/parsers/__init__.py +0 -0
  32. {irrep-3.2.0 → irrep-3.3.0}/irrep/parsers/abinit.py +0 -0
  33. {irrep-3.2.0 → irrep-3.3.0}/irrep/parsers/common.py +0 -0
  34. {irrep-3.2.0 → irrep-3.3.0}/irrep/parsers/espresso.py +0 -0
  35. {irrep-3.2.0 → irrep-3.3.0}/irrep/parsers/gpaw.py +0 -0
  36. {irrep-3.2.0 → irrep-3.3.0}/irrep/parsers/parse_files_bandstructure.py +0 -0
  37. {irrep-3.2.0 → irrep-3.3.0}/irrep/parsers/vasp.py +0 -0
  38. {irrep-3.2.0 → irrep-3.3.0}/irrep/parsers/wannier90.py +0 -0
  39. {irrep-3.2.0 → irrep-3.3.0}/irrep/spacegroup_irreps.py +0 -0
  40. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/__init__.py +0 -0
  41. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/conftest.py +0 -0
  42. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/ref_data/Fe_qe_dmn_TR=False.pkl +0 -0
  43. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/ref_data/Fe_qe_dmn_TR=False_irred.pkl +0 -0
  44. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/ref_data/Fe_qe_dmn_TR=True.pkl +0 -0
  45. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/ref_data/Fe_qe_dmn_TR=True_irred.pkl +0 -0
  46. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/ref_data/magmom_Fe_qe_dmn.txt +0 -0
  47. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/ref_data/spacegroup_Te_TR.npz +0 -0
  48. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/ref_data/spacegroup_Te_TR_product.npz +0 -0
  49. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/ref_data/spacegroup_Te_noTR.npz +0 -0
  50. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/ref_data/spacegroup_Te_noTR_product.npz +0 -0
  51. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/ref_data/symm_matrix_block.npz +0 -0
  52. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/ref_data/symm_matrix_full.npz +0 -0
  53. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/test_abinit_scalar.py +0 -0
  54. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/test_abinit_spinor.py +0 -0
  55. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/test_altermagnetic.py +0 -0
  56. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/test_dmn.py +0 -0
  57. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/test_ebr_decomposition.py +0 -0
  58. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/test_espresso_hdf5.py +0 -0
  59. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/test_espresso_spinor.py +0 -0
  60. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/test_espresso_spinpol.py +0 -0
  61. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/test_paw.py +0 -0
  62. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/test_refUC.py +0 -0
  63. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/test_symmetry_indicators.py +0 -0
  64. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/test_symsep.py +0 -0
  65. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/test_vasp_scalar.py +0 -0
  66. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/test_vasp_spinor.py +0 -0
  67. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/test_wannier_scalar.py +0 -0
  68. {irrep-3.2.0 → irrep-3.3.0}/irrep/tests/test_wannier_spinor.py +0 -0
  69. {irrep-3.2.0 → irrep-3.3.0}/irrep.egg-info/dependency_links.txt +0 -0
  70. {irrep-3.2.0 → irrep-3.3.0}/irrep.egg-info/entry_points.txt +0 -0
  71. {irrep-3.2.0 → irrep-3.3.0}/irrep.egg-info/requires.txt +0 -0
  72. {irrep-3.2.0 → irrep-3.3.0}/irrep.egg-info/top_level.txt +0 -0
  73. {irrep-3.2.0 → irrep-3.3.0}/pyproject.toml +0 -0
  74. {irrep-3.2.0 → irrep-3.3.0}/setup.cfg +0 -0
  75. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi/POSCAR +0 -0
  76. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi/README.md +0 -0
  77. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi/WAVECAR +0 -0
  78. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi/images/bands.png +0 -0
  79. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi/images/checktopomat.png +0 -0
  80. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi/inputs_maxK/INCAR +0 -0
  81. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi/inputs_maxK/KPOINTS +0 -0
  82. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi/inputs_maxK/POSCAR +0 -0
  83. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi/inputs_maxK/POTCAR +0 -0
  84. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi/inputs_scf/INCAR +0 -0
  85. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi/inputs_scf/KPOINTS +0 -0
  86. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi/inputs_scf/POSCAR +0 -0
  87. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi/inputs_scf/POTCAR +0 -0
  88. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi2Se3/README.md +0 -0
  89. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi2Se3/images/bands.png +0 -0
  90. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi2Se3/images/checktopomat.png +0 -0
  91. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi2Se3/inputs/Bi.txt +0 -0
  92. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi2Se3/inputs/Se.txt +0 -0
  93. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi2Se3/inputs/bands.in +0 -0
  94. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi2Se3/inputs/nscf_IrRep.in +0 -0
  95. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi2Se3/inputs/pp.bands.in +0 -0
  96. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi2Se3/inputs/scf.in +0 -0
  97. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi2Se3/out/Bi2Se3.save/charge-density.dat +0 -0
  98. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi2Se3/out/Bi2Se3.save/data-file-schema.xml +0 -0
  99. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi2Se3/out/Bi2Se3.save/paw.txt +0 -0
  100. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi2Se3/out/Bi2Se3.save/wfc1.dat +0 -0
  101. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi2Se3/out/Bi2Se3.save/wfc2.dat +0 -0
  102. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi2Se3/out/Bi2Se3.save/wfc3.dat +0 -0
  103. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi2Se3/out/Bi2Se3.save/wfc4.dat +0 -0
  104. {irrep-3.2.0 → irrep-3.3.0}/tutorial/Bi2Se3/out/Bi2Se3.xml +0 -0
  105. {irrep-3.2.0 → irrep-3.3.0}/tutorial/SnTe/README.md +0 -0
  106. {irrep-3.2.0 → irrep-3.3.0}/tutorial/SnTe/images/bands.png +0 -0
  107. {irrep-3.2.0 → irrep-3.3.0}/tutorial/SnTe/images/checktopomat.png +0 -0
  108. {irrep-3.2.0 → irrep-3.3.0}/tutorial/SnTe/images/refUC.png +0 -0
  109. {irrep-3.2.0 → irrep-3.3.0}/tutorial/SnTe/inputs/Sn.xml +0 -0
  110. {irrep-3.2.0 → irrep-3.3.0}/tutorial/SnTe/inputs/Te.xml +0 -0
  111. {irrep-3.2.0 → irrep-3.3.0}/tutorial/SnTe/inputs/parameters.in +0 -0
  112. {irrep-3.2.0 → irrep-3.3.0}/tutorial/SnTe/maxK_WFK +0 -0
  113. {irrep-3.2.0 → irrep-3.3.0}/uploadpypi.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: irrep
3
- Version: 3.2.0
3
+ Version: 3.3.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)
@@ -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.2.0'
22
- __version_tuple__ = version_tuple = (3, 2, 0)
21
+ __version__ = version = '3.3.0'
22
+ __version_tuple__ = version_tuple = (3, 3, 0)
23
23
 
24
- __commit_id__ = commit_id = 'g5a82e29d3'
24
+ __commit_id__ = commit_id = 'g4f43f138d'
@@ -42,27 +42,29 @@ class AltermagneticTransformer:
42
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
43
 
44
44
  @classmethod
45
- def from_gpaw(cls, calculator, symmetrizer_up, nskip_symmetries=0):
45
+ def from_gpaw(cls, calculator, symmetrizer_up, nskip_symmetries=0, tol_magmom=1e-4):
46
46
  symop = find_altermagnetic_symmetry(lattice=calculator.atoms.cell,
47
47
  positions=calculator.atoms.get_scaled_positions(),
48
48
  typat=calculator.atoms.get_atomic_numbers(),
49
49
  magmom=calculator.get_magnetic_moments(),
50
- nskip=nskip_symmetries)
50
+ nskip=nskip_symmetries,
51
+ tol=tol_magmom)
51
52
  return cls(calculator, symmetrizer_up, rotation_latt=symop.rotation, translation_latt=symop.translation)
52
53
 
53
54
  def get_kpoint_down(self, ikirr, kpoints_up):
54
55
  ik_origin = self.alter_map[ikirr]
55
56
  KPtransformed = kpoints_up[ik_origin].get_transformed_copy(
56
57
  symmetry_operation=self.symmetry_operations[ikirr],
57
- k_new=self.k_intermediate[ikirr])
58
+ k_new=self.k_intermediate[ikirr], store=False)
58
59
  KPtransformed = KPtransformed.get_transformed_copy(
59
60
  symmetry_operation=self.alter_symop,
60
- k_new=kpoints_up[ikirr].k)
61
+ k_new=kpoints_up[ikirr].k, store=False)
61
62
  # TODO : unify the transformations, using just their product
62
63
  return KPtransformed
63
64
 
64
65
 
65
- def find_altermagnetic_symmetry(lattice, positions, typat, magmom, nskip=0):
66
+ def find_altermagnetic_symmetry(lattice, positions, typat, magmom, nskip=0,
67
+ all=False, tol=1e-4):
66
68
  magmom = np.array(magmom)
67
69
  assert magmom.ndim == 1, "Magnetic moments must be a 1D array."
68
70
  positions = np.array(positions)
@@ -76,15 +78,22 @@ def find_altermagnetic_symmetry(lattice, positions, typat, magmom, nskip=0):
76
78
  if nskip >= len(spacegroup_nomag.symmetries) // 2:
77
79
  raise ValueError(f"nskip={nskip} is too large for the number of symmetries={len(spacegroup_nomag.symmetries)}.")
78
80
  cnt = 0
81
+ if all:
82
+ symop_list = []
79
83
  for symop in spacegroup_nomag.symmetries:
80
84
  atommap, T = get_atom_map(symop, positions)
81
85
  # print (f"Checking symmetry operation: {symop}, {atommap=}, {T=}")
82
86
  assert np.all(typat[atommap] == typat), "Atom types must match under symmetry operation."
83
87
  magmom_transformed = magmom[atommap]
84
- print(f"{magmom_transformed=}, {atommap=},")
85
- if np.allclose(magmom_transformed, -magmom):
88
+ print(f"{magmom_transformed=}, {atommap=}, m-transformed={magmom_transformed + magmom}")
89
+ if np.allclose(magmom_transformed, -magmom, atol=tol):
86
90
  cnt += 1
87
91
  if cnt > nskip:
88
- return symop
92
+ if all:
93
+ symop_list.append(symop)
94
+ else:
95
+ return symop
96
+ if all:
97
+ return symop_list
89
98
  else:
90
99
  raise ValueError("No altermagnetic symmetry operation found for the given magnetic moments.")
@@ -29,7 +29,7 @@ from .spacegroup import SpaceGroup
29
29
  from .spacegroup_irreps import SpaceGroupIrreps
30
30
  from .gvectors import sortIG, calc_gvectors
31
31
  from .utility import get_block_indices, get_mapping_irr, grid_from_kpoints, log_message, UniqueListMod1, restore_full_grid, select_irreducible
32
-
32
+ from .storage import DummyStorage
33
33
  try:
34
34
  from irreptables.ebrs import load_ebr_data
35
35
  except ImportError:
@@ -181,9 +181,10 @@ class BandStructure:
181
181
  efermi=0.0,
182
182
  Ecut=None,
183
183
  kwargs_kpoint=None,
184
+ storage=None,
184
185
  **kwargs_parser):
185
186
  if len(kwargs_parser) > 0:
186
- raise ValueError(f"the following kwargs are not recognized by BandStructure-__init__(): {list(kwargs_parser.keys())}, please use BandStructure.from_???() class methods to parse files instead of calling BandStructure.__init__() directly.")
187
+ raise ValueError(f"the following kwargs are not recognized by BandStructure-__init__(): {list(kwargs_parser.keys())}, \n If you meant the old API, please use BandStructure.from_???() class methods to parse files instead of calling BandStructure.__init__() directly.")
187
188
  self.kpoints = kpoints
188
189
  if kpoints_paw is not None:
189
190
  self.kpoints_paw = kpoints_paw
@@ -193,6 +194,9 @@ class BandStructure:
193
194
  self.efermi = efermi
194
195
  self.Ecut = Ecut
195
196
  self.kwargs_kpoint = kwargs_kpoint
197
+ if storage is None:
198
+ storage = DummyStorage()
199
+ self.storage = storage
196
200
  self.kpoint_getter = kpoint_getter
197
201
  self.kpoint_getter_paw = kpoint_getter_paw
198
202
  self.kplist = kplist
@@ -525,6 +529,8 @@ class BandStructure:
525
529
  spacegroup=None,
526
530
  read_kpoints=True,
527
531
  read_paw=False,
532
+ store_paw=True,
533
+ storage="tmp_irrep_storage",
528
534
  **kwargs
529
535
  ):
530
536
 
@@ -542,6 +548,13 @@ class BandStructure:
542
548
  verbosity=verbosity
543
549
  )
544
550
  if read_paw:
551
+ if isinstance(storage, str):
552
+ if store_paw:
553
+ from .storage import Storage
554
+ storage = Storage(storage)
555
+ else:
556
+ from .storage import DummyStorage
557
+ storage = DummyStorage()
545
558
  spacegroup.set_gpaw(calculator=parser.calculator)
546
559
  from irrep.kpoint_gpaw import OverlapPAW
547
560
  overlap_paw = OverlapPAW(calc=parser.calculator)
@@ -574,7 +587,8 @@ class BandStructure:
574
587
  from irrep.kpoint_gpaw import KpointGPAW
575
588
  kp_gpaw = KpointGPAW.from_gpaw(calc=calculator_gpaw, ibz_index=ik, ispin=spin_channel,
576
589
  RecLattice=spacegroup.reciprocal_lattice,
577
- IBstart=IBstart, IBend=IBend)
590
+ IBstart=IBstart, IBend=IBend,
591
+ storage=storage)
578
592
  return kp_gpaw
579
593
  else:
580
594
  get_kpoint_paw = None
@@ -582,7 +596,8 @@ class BandStructure:
582
596
  bandstr = cls(kplist=kplist_noskip, spacegroup=spacegroup, Ecut=Ecut, mp_grid=mp_grid, efermi=efermi,
583
597
  overlap_paw=overlap_paw if read_paw else None,
584
598
  kpoint_getter_paw=get_kpoint_paw,
585
- kpoint_getter=get_kpoint)
599
+ kpoint_getter=get_kpoint,
600
+ storage=storage)
586
601
  if read_kpoints:
587
602
  bandstr.read_all_kpoints()
588
603
  if read_paw:
@@ -751,7 +751,7 @@ class Kpoint(KpointAbstract):
751
751
 
752
752
 
753
753
 
754
- def get_transformed_copy(self, symmetry_operation, k_new=None):
754
+ def get_transformed_copy(self, symmetry_operation, k_new=None, store=False):
755
755
  """
756
756
  Get a copy of the k-point transformed by a symmetry operation.
757
757
 
@@ -1,4 +1,6 @@
1
1
  import numpy as np
2
+
3
+ from irrep.storage import DummyStorage
2
4
  from .kpoint import KpointAbstract
3
5
  from .utility import cached_einsum
4
6
  from .parsers import ParserGPAW
@@ -24,14 +26,15 @@ class KpointGPAW(KpointAbstract):
24
26
  RecLattice=None,
25
27
  atom_positions=None):
26
28
  super().__init__(kpt=kpt, num_bands=nbands, RecLattice=RecLattice)
27
- self.wavefunction = wavefunction
29
+ self._wavefunction = wavefunction
28
30
  self.proj = proj
29
31
  self.nbands = nbands
30
32
  self.atom_positions = atom_positions
31
33
 
32
34
  @classmethod
33
35
  def from_gpaw(self, calc, ibz_index, ispin, RecLattice,
34
- IBstart, IBend
36
+ IBstart, IBend,
37
+ storage
35
38
  ):
36
39
  ispin = ParserGPAW.spin_channels[ispin]
37
40
  kpt = calc.wfs.kpt_qs[ibz_index][ispin]
@@ -46,13 +49,16 @@ class KpointGPAW(KpointAbstract):
46
49
 
47
50
  wavefunction = np.array([calc.wfs.get_wave_function_array(n, ibz_index, ispin, periodic=True)
48
51
  for n in range(IBstart, IBend)])
52
+ wavefunction = storage.store_array(wavefunction)
53
+
49
54
  return KpointGPAW(kpt=k, wavefunction=wavefunction, proj=proj, nbands=nbands,
50
55
  RecLattice=RecLattice,
51
56
  atom_positions=atom_positions)
52
57
 
58
+ def get_wavefunction(self):
59
+ return self._wavefunction.get()
53
60
 
54
-
55
- def get_transformed_copy(self, symmetry_operation, k_new):
61
+ def get_transformed_copy(self, symmetry_operation, k_new, store=False):
56
62
  """Get a transformed copy of the KpointGPAW object according to the given symmetry operation.
57
63
 
58
64
  Args:
@@ -62,10 +68,16 @@ class KpointGPAW(KpointAbstract):
62
68
  Returns:
63
69
  KpointGPAW: Transformed copy of the KpointGPAW object.
64
70
  """
65
- new_wavefunction = symmetry_operation.rotate_pseudo_wavefunction(psi_n_grid=self.wavefunction, k_origin=self.k, k_target=k_new)
71
+ new_wavefunction = symmetry_operation.rotate_pseudo_wavefunction(psi_n_grid=self.get_wavefunction(), k_origin=self.k, k_target=k_new)
66
72
  new_proj = symmetry_operation.rotate_projection(self.proj, self.k, k_new)
73
+ if store:
74
+ storage = self._wavefunction.storage
75
+ else:
76
+ storage = DummyStorage()
77
+ new_wavefunction = storage.store_array(new_wavefunction)
67
78
  return KpointGPAW(kpt=k_new, wavefunction=new_wavefunction, proj=new_proj, nbands=self.nbands,
68
- RecLattice=self.RecLattice, atom_positions=self.atom_positions)
79
+ RecLattice=self.RecLattice, atom_positions=self.atom_positions,
80
+ )
69
81
 
70
82
 
71
83
  class OverlapPAW:
@@ -96,9 +108,9 @@ class OverlapPAW:
96
108
  include_pseudo=True,
97
109
  bk=None,
98
110
  ):
99
- wf1 = KP1.wavefunction
111
+ wf1 = KP1.get_wavefunction()
100
112
  proj1 = KP1.proj
101
- wf2 = KP2.wavefunction
113
+ wf2 = KP2.get_wavefunction()
102
114
  proj2 = KP2.proj
103
115
  assert wf1.ndim == 4
104
116
  assert wf2.ndim == 4
@@ -24,7 +24,7 @@ import spglib
24
24
  from irrep.parsers import ParserAbinit, ParserEspresso, ParserVasp, ParserW90
25
25
 
26
26
  from .symmetry_operation import SymmetryOperation
27
- from .utility import BOHR, group_numbers, log_message, select_irreducible
27
+ from .utility import BOHR, clean_np_load, group_numbers, log_message, select_irreducible
28
28
  from packaging import version
29
29
  import os
30
30
 
@@ -226,6 +226,27 @@ class SpaceGroup:
226
226
  dic[key] = val
227
227
  return dic
228
228
 
229
+ @classmethod
230
+ def from_dict(cls, dic):
231
+ """
232
+ Create a SpaceGroup object from a dictionary.
233
+ """
234
+ dic_clean = clean_np_load(dic)
235
+ return cls(**dic_clean)
236
+
237
+ @classmethod
238
+ def from_npz(cls, file_path):
239
+ """
240
+ Create a SpaceGroup object from a .npz file.
241
+ """
242
+ return cls.from_dict(np.load(file_path, allow_pickle=True))
243
+
244
+ def to_npz(self, file_path):
245
+ """
246
+ Save the SpaceGroup object to a .npz file.
247
+ """
248
+ np.savez(file_path, **self.as_dict())
249
+
229
250
  @property
230
251
  def size(self):
231
252
  """
@@ -0,0 +1,69 @@
1
+ import os
2
+ import numpy as np
3
+ from warnings import warn
4
+
5
+
6
+ class Storage:
7
+
8
+ def __init__(self, path):
9
+ self.path = path
10
+ os.makedirs(path, exist_ok=True)
11
+ self.files = os.listdir(path)
12
+
13
+ def generate_name(self, name=None):
14
+ """generates a unique name for storing an array"""
15
+ if name is None:
16
+ name = "array"
17
+ existing_files = set(self.files)
18
+ i = 0
19
+ while True:
20
+ name_loc = f"{name}{f"_{i}" if i > 0 else ''}.npy"
21
+ if name_loc not in existing_files:
22
+ return name_loc
23
+ i += 1
24
+
25
+ def store_array(self, array, name=None, ignore_existing=False):
26
+ name = self.generate_name(name)
27
+ if os.name in self.files and not ignore_existing:
28
+ warn(f"File {name} already exists in storage {self.path}. Overwriting.")
29
+ path = os.path.join(self.path, name)
30
+ np.save(path, array)
31
+ self.files.append(name)
32
+ return StoredArray(name=name, storage=self)
33
+
34
+
35
+ def get_array(self, name):
36
+ if name not in self.files:
37
+ raise FileNotFoundError(f"File {name} not found in storage {self.path}.")
38
+ return np.load(os.path.join(self.path, name))
39
+
40
+
41
+ class DummyStorage(Storage):
42
+
43
+ def __init__(self, path=None):
44
+ self.arrays = {}
45
+ self.files = []
46
+
47
+ def store_array(self, array, name=None, ignore_existing=False):
48
+ if name is None:
49
+ name = f"array_{len(self.arrays)}"
50
+ if name in self.arrays and not ignore_existing:
51
+ warn(f"Array {name} already exists in storage. Overwriting.")
52
+ self.arrays[name] = array
53
+ self.files.append(name)
54
+ return StoredArray(name=name, storage=self)
55
+
56
+ def get_array(self, name):
57
+ if name not in self.arrays:
58
+ raise KeyError(f"Array {name} not found in storage.")
59
+ return self.arrays[name]
60
+
61
+
62
+ class StoredArray:
63
+
64
+ def __init__(self, name, storage):
65
+ self.name = name
66
+ self.storage = storage
67
+
68
+ def get(self):
69
+ return self.storage.get_array(self.name)
@@ -280,8 +280,11 @@ class SymmetryOperation():
280
280
 
281
281
  @cached_property
282
282
  def is_identity(self):
283
- return np.allclose(self.rotation, np.eye(3)) and np.allclose(self.translation, 0) and not self.time_reversal and not self.inversion
283
+ return self.is_identity_space and not self.time_reversal
284
284
 
285
+ @cached_property
286
+ def is_identity_space(self):
287
+ return np.allclose(self.rotation, np.eye(3)) and np.allclose(self.translation, 0)
285
288
 
286
289
  def _get_operation_type(self):
287
290
  """
@@ -1,3 +1,5 @@
1
+ import pickle
2
+
1
3
  from gpaw import GPAW
2
4
  import numpy as np
3
5
  import pytest
@@ -66,3 +68,31 @@ def test_gpaw_spinorbit(spinor):
66
68
  for k in irr:
67
69
  if abs(irr[k]) > 1e-3:
68
70
  assert k in irref, f"at energy {E}, extra irrep {k}({irr[k]}) found in calculated irreps {irr}, not in reference {irref}"
71
+
72
+
73
+
74
+ def test_gpaw_storage():
75
+ calc = GPAW(f"{TEST_FILES_PATH}/gpaw/Bi-gamma.gpw")
76
+
77
+ print("Fermi level", calc.get_fermi_level())
78
+ bandstructure = BandStructure.from_gpaw(calculator_gpaw=calc,
79
+ IBstart=10,
80
+ IBend=15,
81
+ Ecut=50,
82
+ degen_thresh=2e-4,
83
+ calculate_traces=True,
84
+ irreps=True,
85
+ read_paw=True,
86
+ search_cell=True,
87
+ store_paw=True,
88
+ kplist=[0])
89
+ output_file = f"{TEST_FILES_PATH}/gpaw/Bi-gamma_bandstructure_tmp.pickle"
90
+ bandstructure.pickle(output_file)
91
+ bandstructure2 = pickle.load(open(output_file, "rb"))
92
+ assert len(bandstructure.kpoints_paw) == len(bandstructure2.kpoints_paw), "Number of k-points in bandstructure and loaded bandstructure do not match."
93
+ assert len(bandstructure.kpoints) > 0, "No k-points found in bandstructure."
94
+ for kp1, kp2 in zip(bandstructure.kpoints_paw, bandstructure2.kpoints_paw):
95
+ assert np.allclose(kp1.get_wavefunction(), kp2.get_wavefunction()), f"Wavefunctions differ for k-point {kp1.k} and {kp2.k}"
96
+ assert np.allclose(kp1.proj, kp2.proj), f"Projections differ for k-point {kp1.k} and {kp2.k}"
97
+ bandstructure.spacegroup.show()
98
+ bandstructure.identify_irreps(kpnames=["GM"], verbosity=0)
@@ -1,5 +1,6 @@
1
1
  from irrep.spacegroup import SpaceGroup
2
2
  import numpy as np
3
+ import pytest
3
4
 
4
5
  from irrep.tests.test_dmn import REF_FILES_PATH, TMP_FILES_PATH
5
6
 
@@ -167,7 +168,7 @@ def test_wyckoff_positions_2():
167
168
  # checking correct parsing of the symbolic strings
168
169
  assert MnTe_wyckoffs[4].split(',')[1] == '2.0*x'
169
170
  return
170
-
171
+
171
172
 
172
173
  def test_implicit_multiplication_parsing():
173
174
  """test lines 1060-1061 of spacegroup.py, which use parse_expr with implicit multiplication"""
@@ -176,3 +177,22 @@ def test_implicit_multiplication_parsing():
176
177
  assert parse_expr('2x', transformations=_TRANSFORMS) == 2 * x
177
178
  assert parse_expr('-2x+1', transformations=_TRANSFORMS) == -2 * x + 1
178
179
  assert parse_expr('2x-y', transformations=_TRANSFORMS) == 2 * x - y
180
+
181
+
182
+ @pytest.mark.parametrize("include_TR", [True, False])
183
+ def test_spacegroup_save_load(include_TR):
184
+ spacegroup_Te = SpaceGroup.from_cell(**struct_param_Te,
185
+ include_TR=include_TR)
186
+ tmp_file_path = TMP_DATA_PATH / "spacegroup_Te_TR={}.npz".format(include_TR)
187
+ print(f"spinor : {spacegroup_Te.spinor}, include_TR: {include_TR}")
188
+ np.savez(tmp_file_path, **spacegroup_Te.as_dict())
189
+ spacegroup_Te_loaded = SpaceGroup.from_npz(tmp_file_path)
190
+ for key in spacegroup_Te.__dict__:
191
+ type_key = type(spacegroup_Te.__dict__[key])
192
+ assert type_key == type(spacegroup_Te_loaded.__dict__[key]), f"Type mismatch in attribute {key}, saved {type(getattr(spacegroup_Te, key))}, loaded {type(getattr(spacegroup_Te_loaded, key))}"
193
+ if key in ['real_lattice', 'positions', 'typat', 'magmom', 'number', 'number_str']:
194
+ if type_key == np.ndarray:
195
+ assert np.allclose(getattr(spacegroup_Te, key), getattr(spacegroup_Te_loaded, key)), f"Value mismatch in attribute {key}, saved {getattr(spacegroup_Te, key)}, loaded {getattr(spacegroup_Te_loaded, key)}"
196
+ else:
197
+ assert getattr(spacegroup_Te, key) == getattr(spacegroup_Te_loaded, key), f"Value mismatch in attribute {key}, saved {getattr(spacegroup_Te, key)}, loaded {getattr(spacegroup_Te_loaded, key)}"
198
+ assert spacegroup_Te.equals(spacegroup_Te_loaded), "Loaded spacegroup is not equal to the original"
@@ -798,3 +798,14 @@ class DummyMethodTables(DummyMethod):
798
798
  message = f"{method_name} requires irreptables package to be installed. " \
799
799
  f"Please install irreptables>=3.0.0 via pip: pip install irreptables"
800
800
  super().__init__(message)
801
+
802
+
803
+ def clean_np_load(dict):
804
+ """converts zero-dimensional np.ndarray to python scalars"""
805
+ dict_new = {}
806
+ for key, val in dict.items():
807
+ if isinstance(val, np.ndarray) and val.ndim == 0:
808
+ dict_new[key] = val.item()
809
+ else:
810
+ dict_new[key] = val
811
+ return dict_new
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: irrep
3
- Version: 3.2.0
3
+ Version: 3.3.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)
@@ -20,6 +20,7 @@ irrep/kpoint_gpaw.py
20
20
  irrep/or_solutions_obtainer.py
21
21
  irrep/spacegroup.py
22
22
  irrep/spacegroup_irreps.py
23
+ irrep/storage.py
23
24
  irrep/symmetry_operation.py
24
25
  irrep/utility.py
25
26
  irrep.egg-info/PKG-INFO
@@ -222,6 +222,7 @@
222
222
  "irrep/parsers/wannier90.py",
223
223
  "irrep/spacegroup.py",
224
224
  "irrep/spacegroup_irreps.py",
225
+ "irrep/storage.py",
225
226
  "irrep/symmetry_operation.py",
226
227
  "irrep/tests/__init__.py",
227
228
  "irrep/tests/conftest.py",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "3.3.0",
3
+ "distance": 0,
4
+ "node": "g4f43f138d30ef37a499a297ac9b1ec8ef14717cc",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-10-02"
8
+ }
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "3.2.0",
3
- "distance": 0,
4
- "node": "g5a82e29d3f389776627e6a5438b745c2ee5cd48a",
5
- "dirty": false,
6
- "branch": "HEAD",
7
- "node_date": "2026-08-13"
8
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes