symfc 1.0.0__py3-none-any.whl
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.
- symfc/__init__.py +4 -0
- symfc/api_symfc.py +180 -0
- symfc/basis_sets/__init__.py +5 -0
- symfc/basis_sets/basis_sets_O1.py +140 -0
- symfc/basis_sets/basis_sets_O2.py +171 -0
- symfc/basis_sets/basis_sets_O3.py +218 -0
- symfc/basis_sets/basis_sets_base.py +65 -0
- symfc/solvers/__init__.py +5 -0
- symfc/solvers/solver_O2.py +254 -0
- symfc/solvers/solver_O2O3.py +583 -0
- symfc/solvers/solver_O3.py +236 -0
- symfc/solvers/solver_base.py +54 -0
- symfc/spg_reps/__init__.py +6 -0
- symfc/spg_reps/spg_reps_O1.py +74 -0
- symfc/spg_reps/spg_reps_O2.py +73 -0
- symfc/spg_reps/spg_reps_O3.py +72 -0
- symfc/spg_reps/spg_reps_base.py +102 -0
- symfc/utils/__init__.py +1 -0
- symfc/utils/eig_tools.py +241 -0
- symfc/utils/matrix_tools_O1.py +50 -0
- symfc/utils/matrix_tools_O2.py +61 -0
- symfc/utils/matrix_tools_O3.py +342 -0
- symfc/utils/solver_funcs.py +41 -0
- symfc/utils/utils.py +164 -0
- symfc/utils/utils_O1.py +173 -0
- symfc/utils/utils_O2.py +281 -0
- symfc/utils/utils_O3.py +154 -0
- symfc/version.py +3 -0
- symfc-1.0.0.dist-info/LICENSE +28 -0
- symfc-1.0.0.dist-info/METADATA +65 -0
- symfc-1.0.0.dist-info/RECORD +33 -0
- symfc-1.0.0.dist-info/WHEEL +5 -0
- symfc-1.0.0.dist-info/top_level.txt +1 -0
symfc/__init__.py
ADDED
symfc/api_symfc.py
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""Symfc API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
from typing import Optional, Union
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
from symfc.basis_sets import FCBasisSetBase, FCBasisSetO2, FCBasisSetO3
|
|
11
|
+
from symfc.solvers import FCSolverO2, FCSolverO2O3
|
|
12
|
+
from symfc.utils.utils import SymfcAtoms
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Symfc:
|
|
16
|
+
"""Symfc API."""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
supercell: SymfcAtoms,
|
|
21
|
+
displacements: Optional[np.ndarray] = None,
|
|
22
|
+
forces: Optional[np.ndarray] = None,
|
|
23
|
+
use_mkl: bool = False,
|
|
24
|
+
log_level: int = 0,
|
|
25
|
+
):
|
|
26
|
+
"""Init method."""
|
|
27
|
+
self._supercell: SymfcAtoms = supercell
|
|
28
|
+
self._displacements: Optional[np.ndarray] = displacements
|
|
29
|
+
self._forces: Optional[np.ndarray] = forces
|
|
30
|
+
self._use_mkl = use_mkl
|
|
31
|
+
self._log_level = log_level
|
|
32
|
+
|
|
33
|
+
self._basis_set: dict[FCBasisSetBase] = {}
|
|
34
|
+
self._force_constants: dict[np.ndarray] = {}
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def basis_set(self) -> dict[FCBasisSetBase]:
|
|
38
|
+
"""Return basis set instance.
|
|
39
|
+
|
|
40
|
+
Returns
|
|
41
|
+
-------
|
|
42
|
+
dict[FCBasisSet]
|
|
43
|
+
The key is the order of basis set in int.
|
|
44
|
+
|
|
45
|
+
"""
|
|
46
|
+
return self._basis_set
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def force_constants(self) -> dict[np.ndarray]:
|
|
50
|
+
"""Return force constants.
|
|
51
|
+
|
|
52
|
+
Returns
|
|
53
|
+
-------
|
|
54
|
+
dict[np.ndarray]
|
|
55
|
+
The key is the order of force_constants in int.
|
|
56
|
+
|
|
57
|
+
"""
|
|
58
|
+
return self._force_constants
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def displacements(self) -> np.ndarray:
|
|
62
|
+
"""Setter and getter of supercell displacements.
|
|
63
|
+
|
|
64
|
+
ndarray
|
|
65
|
+
shape=(n_snapshot, natom, 3), dtype='double', order='C'
|
|
66
|
+
|
|
67
|
+
"""
|
|
68
|
+
return self._displacements
|
|
69
|
+
|
|
70
|
+
@displacements.setter
|
|
71
|
+
def displacements(self, displacements: Union[np.ndarray, list, tuple]):
|
|
72
|
+
self._displacements = np.array(displacements, dtype="double", order="C")
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def forces(self) -> np.ndarray:
|
|
76
|
+
"""Setter and getter of supercell forces.
|
|
77
|
+
|
|
78
|
+
ndarray
|
|
79
|
+
shape=(n_snapshot, natom, 3), dtype='double', order='C'
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
return self._forces
|
|
83
|
+
|
|
84
|
+
@forces.setter
|
|
85
|
+
def forces(self, forces: Union[np.ndarray, list, tuple]):
|
|
86
|
+
self._forces = np.array(forces, dtype="double", order="C")
|
|
87
|
+
|
|
88
|
+
def run(self, orders: Sequence[int], is_compact_fc=True) -> Symfc:
|
|
89
|
+
"""Run basis set and force constants calculation."""
|
|
90
|
+
if (
|
|
91
|
+
orders is not None
|
|
92
|
+
and self._displacements is not None
|
|
93
|
+
and self._forces is not None
|
|
94
|
+
):
|
|
95
|
+
for order in orders:
|
|
96
|
+
self._compute_basis_set(order)
|
|
97
|
+
self.solve(orders, is_compact_fc=is_compact_fc)
|
|
98
|
+
return self
|
|
99
|
+
|
|
100
|
+
def solve(self, orders: Sequence[int], is_compact_fc=True) -> Symfc:
|
|
101
|
+
"""Calculate force constants.
|
|
102
|
+
|
|
103
|
+
orders : Sequence[int]
|
|
104
|
+
Sequence of fc orders.
|
|
105
|
+
|
|
106
|
+
"""
|
|
107
|
+
self._check_dataset()
|
|
108
|
+
for order in orders:
|
|
109
|
+
if order not in (2, 3):
|
|
110
|
+
raise NotImplementedError("Only order-2 and order-3 are implemented.")
|
|
111
|
+
if order == 2:
|
|
112
|
+
basis_set: FCBasisSetO2 = self._basis_set[2]
|
|
113
|
+
solver_o2 = FCSolverO2(
|
|
114
|
+
basis_set,
|
|
115
|
+
use_mkl=self._use_mkl,
|
|
116
|
+
log_level=self._log_level,
|
|
117
|
+
).solve(self._displacements, self._forces)
|
|
118
|
+
if is_compact_fc:
|
|
119
|
+
self._force_constants[2] = solver_o2.compact_fc
|
|
120
|
+
else:
|
|
121
|
+
self._force_constants[2] = solver_o2.full_fc
|
|
122
|
+
if order == 3:
|
|
123
|
+
basis_set_o2: FCBasisSetO2 = self._basis_set[2]
|
|
124
|
+
basis_set_o3: FCBasisSetO3 = self._basis_set[3]
|
|
125
|
+
solver_o2o3 = FCSolverO2O3(
|
|
126
|
+
[basis_set_o2, basis_set_o3],
|
|
127
|
+
use_mkl=self._use_mkl,
|
|
128
|
+
log_level=self._log_level,
|
|
129
|
+
).solve(self._displacements, self._forces)
|
|
130
|
+
if is_compact_fc:
|
|
131
|
+
fc2, fc3 = solver_o2o3.compact_fc
|
|
132
|
+
else:
|
|
133
|
+
fc2, fc3 = solver_o2o3.full_fc
|
|
134
|
+
self._force_constants[2] = fc2
|
|
135
|
+
self._force_constants[3] = fc3
|
|
136
|
+
return self
|
|
137
|
+
|
|
138
|
+
def _compute_basis_set(self, order: int):
|
|
139
|
+
"""Set order of force constants."""
|
|
140
|
+
if order not in (2, 3):
|
|
141
|
+
raise NotImplementedError("Only fc2 basis set is implemented.")
|
|
142
|
+
|
|
143
|
+
if order == 2:
|
|
144
|
+
basis_set_o2 = FCBasisSetO2(
|
|
145
|
+
self._supercell, use_mkl=self._use_mkl, log_level=self._log_level
|
|
146
|
+
).run()
|
|
147
|
+
self._basis_set[2] = basis_set_o2
|
|
148
|
+
if order == 3:
|
|
149
|
+
basis_set_o3 = FCBasisSetO3(
|
|
150
|
+
self._supercell, use_mkl=self._use_mkl, log_level=self._log_level
|
|
151
|
+
).run()
|
|
152
|
+
self._basis_set[3] = basis_set_o3
|
|
153
|
+
|
|
154
|
+
def _check_dataset(self):
|
|
155
|
+
if self._displacements is None:
|
|
156
|
+
raise RuntimeError("Dispalcements not found.")
|
|
157
|
+
if self._forces is None:
|
|
158
|
+
raise RuntimeError("Forces not found.")
|
|
159
|
+
if self._displacements.shape != self._forces.shape:
|
|
160
|
+
raise RuntimeError("Shape mismatch between dispalcements and forces.")
|
|
161
|
+
if self._displacements.shape != self._forces.shape:
|
|
162
|
+
raise RuntimeError("Shape mismatch between dispalcements and forces.")
|
|
163
|
+
if self._displacements.ndim != 3 or self._displacements.shape[1:] != (
|
|
164
|
+
len(self._supercell),
|
|
165
|
+
3,
|
|
166
|
+
):
|
|
167
|
+
raise RuntimeError(
|
|
168
|
+
"Inconsistent array shape of displacements "
|
|
169
|
+
f"{self._displacements.shape} with respect to supercell "
|
|
170
|
+
f"{len(self._supercell)}."
|
|
171
|
+
)
|
|
172
|
+
if self._forces.ndim != 3 or self._forces.shape[1:] != (
|
|
173
|
+
len(self._supercell),
|
|
174
|
+
3,
|
|
175
|
+
):
|
|
176
|
+
raise RuntimeError(
|
|
177
|
+
"Inconsistent array shape of forces "
|
|
178
|
+
f"{self._forces.shape} with respect to supercell "
|
|
179
|
+
f"{len(self._supercell)}."
|
|
180
|
+
)
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""Symmetry adapted basis sets of 1st order force constants."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
from scipy.sparse import csr_array
|
|
9
|
+
|
|
10
|
+
from symfc.spg_reps import SpgRepsO1
|
|
11
|
+
from symfc.utils.eig_tools import eigsh_projector
|
|
12
|
+
from symfc.utils.matrix_tools_O1 import compressed_projector_sum_rules
|
|
13
|
+
from symfc.utils.utils import SymfcAtoms
|
|
14
|
+
from symfc.utils.utils_O1 import (
|
|
15
|
+
get_compr_coset_reps_sum,
|
|
16
|
+
get_lat_trans_compr_matrix,
|
|
17
|
+
get_lat_trans_decompr_indices,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from .basis_sets_base import FCBasisSetBase
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class FCBasisSetO1Base(FCBasisSetBase):
|
|
24
|
+
"""Base class of FCBasisSetO1."""
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
supercell: SymfcAtoms,
|
|
29
|
+
use_mkl: bool = False,
|
|
30
|
+
log_level: int = 0,
|
|
31
|
+
):
|
|
32
|
+
"""Init method.
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
supercell : SymfcAtoms
|
|
37
|
+
Supercell.
|
|
38
|
+
use_mkl : bool
|
|
39
|
+
Use MKL or not. Default is False.
|
|
40
|
+
log_level : int, optional
|
|
41
|
+
Log level. Default is 0.
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
super().__init__(supercell, use_mkl=use_mkl, log_level=log_level)
|
|
45
|
+
self._spg_reps = SpgRepsO1(supercell)
|
|
46
|
+
|
|
47
|
+
def _get_c_trans(self) -> csr_array:
|
|
48
|
+
trans_perms = self._spg_reps.translation_permutations
|
|
49
|
+
n_lp, N = trans_perms.shape
|
|
50
|
+
decompr_idx = get_lat_trans_decompr_indices(trans_perms)
|
|
51
|
+
c_trans = get_lat_trans_compr_matrix(decompr_idx, N, n_lp)
|
|
52
|
+
return c_trans
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class FCBasisSetO1(FCBasisSetO1Base):
|
|
56
|
+
"""Dense symmetry adapted basis set for 1st order force constants.
|
|
57
|
+
|
|
58
|
+
Attributes
|
|
59
|
+
----------
|
|
60
|
+
basis_set : ndarray
|
|
61
|
+
Compressed force constants basis set. The first dimension n_x (< n_a) is
|
|
62
|
+
given as a result of compression, which is depends on the system.
|
|
63
|
+
shape=(n_x, n_bases), dtype='double'
|
|
64
|
+
full_basis_set : ndarray
|
|
65
|
+
Full (decompressed) force constants basis set. shape=(N * 3,
|
|
66
|
+
n_bases), dtype='double'
|
|
67
|
+
translation_permutations : ndarray
|
|
68
|
+
Atom indices after lattice translations. shape=(lattice_translations,
|
|
69
|
+
supercell_atoms), dtype=int.
|
|
70
|
+
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
supercell: SymfcAtoms,
|
|
76
|
+
use_mkl: bool = False,
|
|
77
|
+
log_level: int = 0,
|
|
78
|
+
):
|
|
79
|
+
"""Init method.
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
----------
|
|
83
|
+
supercell : SymfcAtoms
|
|
84
|
+
Supercell.
|
|
85
|
+
log_level : int, optional
|
|
86
|
+
Log level. Default is 0.
|
|
87
|
+
|
|
88
|
+
"""
|
|
89
|
+
super().__init__(supercell, use_mkl=use_mkl, log_level=log_level)
|
|
90
|
+
self._spg_reps = SpgRepsO1(supercell)
|
|
91
|
+
self._n_a_compression_matrix: Optional[csr_array] = None
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def basis_set(self) -> Optional[np.ndarray]:
|
|
95
|
+
"""Return compressed basis set.
|
|
96
|
+
|
|
97
|
+
n_c = len(compressed_indices).
|
|
98
|
+
|
|
99
|
+
shape=(n_c, n_bases), dtype='double'.
|
|
100
|
+
|
|
101
|
+
"""
|
|
102
|
+
return self._basis_set
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def full_basis_set(self) -> Optional[np.ndarray]:
|
|
106
|
+
"""Return full (decompressed) basis set.
|
|
107
|
+
|
|
108
|
+
shape=(N*3, n_bases), dtype='double'.
|
|
109
|
+
|
|
110
|
+
Data in first dimension is ordered by (N,3).
|
|
111
|
+
|
|
112
|
+
"""
|
|
113
|
+
return self._full_basis_set
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def compact_compression_matrix(self) -> Optional[np.ndarray]:
|
|
117
|
+
"""Return compression matrix for compact basis set."""
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def compression_matrix(self) -> Optional[np.ndarray]:
|
|
122
|
+
"""Return compression matrix."""
|
|
123
|
+
pass
|
|
124
|
+
|
|
125
|
+
def run(self) -> FCBasisSetO1:
|
|
126
|
+
"""Compute compressed force constants basis set."""
|
|
127
|
+
c_trans = self._get_c_trans()
|
|
128
|
+
coset_reps_sum = get_compr_coset_reps_sum(self._spg_reps)
|
|
129
|
+
proj_rt = coset_reps_sum
|
|
130
|
+
|
|
131
|
+
if len(proj_rt.data) == 0:
|
|
132
|
+
raise ValueError("No basis vectors exist.")
|
|
133
|
+
|
|
134
|
+
c_rt = eigsh_projector(proj_rt)
|
|
135
|
+
compress_mat = c_trans @ c_rt
|
|
136
|
+
proj = compressed_projector_sum_rules(compress_mat, self._natom)
|
|
137
|
+
self._basis_set = eigsh_projector(proj)
|
|
138
|
+
self._full_basis_set = compress_mat @ self._basis_set
|
|
139
|
+
|
|
140
|
+
return self
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""Symmetry adapted basis sets of 2nd order force constants."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
from scipy.sparse import csr_array
|
|
9
|
+
|
|
10
|
+
from symfc.spg_reps import SpgRepsO2
|
|
11
|
+
from symfc.utils.eig_tools import (
|
|
12
|
+
dot_product_sparse,
|
|
13
|
+
eigsh_projector,
|
|
14
|
+
eigsh_projector_sumrule,
|
|
15
|
+
)
|
|
16
|
+
from symfc.utils.matrix_tools_O2 import compressed_projector_sum_rules
|
|
17
|
+
from symfc.utils.utils import SymfcAtoms
|
|
18
|
+
from symfc.utils.utils_O2 import (
|
|
19
|
+
get_compr_coset_reps_sum,
|
|
20
|
+
get_lat_trans_compr_matrix,
|
|
21
|
+
get_lat_trans_decompr_indices,
|
|
22
|
+
get_perm_compr_matrix,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
from .basis_sets_base import FCBasisSetBase
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class FCBasisSetO2(FCBasisSetBase):
|
|
29
|
+
"""Dense symmetry adapted basis set for 2nd order force constants.
|
|
30
|
+
|
|
31
|
+
Attributes
|
|
32
|
+
----------
|
|
33
|
+
basis_set : ndarray
|
|
34
|
+
Compressed force constants basis set. The first dimension n_x (< n_a) is
|
|
35
|
+
given as a result of compression, which is depends on the system.
|
|
36
|
+
shape=(n_x * N * 9, n_bases), dtype='double'
|
|
37
|
+
full_basis_set : ndarray
|
|
38
|
+
Full (decompressed) force constants basis set. shape=(N * N * 9,
|
|
39
|
+
n_bases), dtype='double'
|
|
40
|
+
translation_permutations : ndarray
|
|
41
|
+
Atom indices after lattice translations. shape=(lattice_translations,
|
|
42
|
+
supercell_atoms), dtype=int.
|
|
43
|
+
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
supercell: SymfcAtoms,
|
|
49
|
+
use_mkl: bool = False,
|
|
50
|
+
log_level: int = 0,
|
|
51
|
+
):
|
|
52
|
+
"""Init method.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
supercell : SymfcAtoms
|
|
57
|
+
Supercell.
|
|
58
|
+
use_mkl : bool
|
|
59
|
+
Use MKL or not. Default is False.
|
|
60
|
+
log_level : int, optional
|
|
61
|
+
Log level. Default is 0.
|
|
62
|
+
|
|
63
|
+
"""
|
|
64
|
+
super().__init__(supercell, use_mkl=use_mkl, log_level=log_level)
|
|
65
|
+
self._spg_reps = SpgRepsO2(supercell)
|
|
66
|
+
self._n_a_compression_matrix: Optional[csr_array] = None
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def basis_set(self) -> Optional[np.ndarray]:
|
|
70
|
+
"""Return compressed basis set.
|
|
71
|
+
|
|
72
|
+
n_c = len(compressed_indices).
|
|
73
|
+
|
|
74
|
+
shape=(n_c*N*3*3, n_bases), dtype='double'.
|
|
75
|
+
|
|
76
|
+
Data in first dimension is ordered by (n_c,N,3,3).
|
|
77
|
+
|
|
78
|
+
"""
|
|
79
|
+
return self._basis_set
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def compression_matrix(self) -> Optional[csr_array]:
|
|
83
|
+
"""Return compression matrix.
|
|
84
|
+
|
|
85
|
+
This expands fc basis_sets to (N*N*3*3, n_bases).
|
|
86
|
+
|
|
87
|
+
"""
|
|
88
|
+
c_trans = self._get_c_trans()
|
|
89
|
+
return dot_product_sparse(
|
|
90
|
+
c_trans, self._n_a_compression_matrix, use_mkl=self._use_mkl
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def compact_compression_matrix(self) -> Optional[csr_array]:
|
|
95
|
+
"""Return compact compression matrix.
|
|
96
|
+
|
|
97
|
+
This expands basis_sets to (n_a*N*3*3, n_bases).
|
|
98
|
+
|
|
99
|
+
"""
|
|
100
|
+
n_lp = self.translation_permutations.shape[0]
|
|
101
|
+
return self._n_a_compression_matrix / np.sqrt(n_lp)
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def decompression_indices(self) -> np.ndarray:
|
|
105
|
+
"""Return decompression indices in (N,N,3,3) order."""
|
|
106
|
+
trans_perms = self.translation_permutations
|
|
107
|
+
return get_lat_trans_decompr_indices(trans_perms)
|
|
108
|
+
|
|
109
|
+
def run(self) -> FCBasisSetO2:
|
|
110
|
+
"""Compute compressed force constants basis set.
|
|
111
|
+
|
|
112
|
+
compression using C(pt)
|
|
113
|
+
= eigvecs of C(trans).T @ C(perm) @ C(perm).T @ C(trans)
|
|
114
|
+
proj_rpt = c_pt.transpose() @ coset_reps_sum @ c_pt
|
|
115
|
+
|
|
116
|
+
[C_pt @ C_trans.T] @ P_sum @ [C_trans @ C_pt] @ C_rpt
|
|
117
|
+
= C_rpt @ [C_rpt.T @ C_pt @ C_trans.T] @ P_sum @ [C_trans @ C_pt @ C_rpt]
|
|
118
|
+
= C_rpt @ compression_mat.T @ (I - P_sum^(c)) @ compression_mat
|
|
119
|
+
= C_rpt @ compression_mat.T @ (I - Csum(c) @ Csum(c).T) @ compression_mat
|
|
120
|
+
= C_rpt @ proj
|
|
121
|
+
|
|
122
|
+
compress_mat = c_trans @ c_pt @ c_rpt
|
|
123
|
+
|
|
124
|
+
"""
|
|
125
|
+
N = self._natom
|
|
126
|
+
c_trans = self._get_c_trans()
|
|
127
|
+
n_a_compress_mat = self._get_n_a_compress_mat(c_trans)
|
|
128
|
+
compress_mat = dot_product_sparse(
|
|
129
|
+
c_trans, n_a_compress_mat, use_mkl=self._use_mkl
|
|
130
|
+
)
|
|
131
|
+
proj = compressed_projector_sum_rules(compress_mat, N, use_mkl=self._use_mkl)
|
|
132
|
+
eigvecs = eigsh_projector_sumrule(proj)
|
|
133
|
+
|
|
134
|
+
if self._log_level:
|
|
135
|
+
print(f"Final size of basis set: {eigvecs.shape}")
|
|
136
|
+
|
|
137
|
+
self._basis_set = eigvecs
|
|
138
|
+
self._n_a_compression_matrix = n_a_compress_mat
|
|
139
|
+
return self
|
|
140
|
+
|
|
141
|
+
def _get_n_a_compress_mat(self, c_trans: csr_array) -> csr_array:
|
|
142
|
+
"""Return compression matrix without c_trans mutiplied.
|
|
143
|
+
|
|
144
|
+
This compression matrix is preserved as a class instance variable.
|
|
145
|
+
The full compression matrix is obtained by
|
|
146
|
+
|
|
147
|
+
c_trans @ n_a_compression_matrix.
|
|
148
|
+
|
|
149
|
+
The compact compression matrix is obtained by
|
|
150
|
+
|
|
151
|
+
n_a_compression_matrix / sqrt(n_lp).
|
|
152
|
+
|
|
153
|
+
"""
|
|
154
|
+
N = self._natom
|
|
155
|
+
c_perm = get_perm_compr_matrix(N)
|
|
156
|
+
c_pt = dot_product_sparse(c_perm.T, c_trans, use_mkl=self._use_mkl)
|
|
157
|
+
proj_pt = dot_product_sparse(c_pt.T, c_pt, use_mkl=self._use_mkl)
|
|
158
|
+
coset_reps_sum = get_compr_coset_reps_sum(self._spg_reps)
|
|
159
|
+
c_pt = eigsh_projector(proj_pt)
|
|
160
|
+
proj_rpt = dot_product_sparse(coset_reps_sum, c_pt, use_mkl=self._use_mkl)
|
|
161
|
+
proj_rpt = dot_product_sparse(c_pt.T, proj_rpt)
|
|
162
|
+
c_rpt = eigsh_projector(proj_rpt)
|
|
163
|
+
n_a_compress_mat = dot_product_sparse(c_pt, c_rpt, use_mkl=self._use_mkl)
|
|
164
|
+
return n_a_compress_mat
|
|
165
|
+
|
|
166
|
+
def _get_c_trans(self) -> csr_array:
|
|
167
|
+
trans_perms = self._spg_reps.translation_permutations
|
|
168
|
+
n_lp, N = trans_perms.shape
|
|
169
|
+
decompr_idx = get_lat_trans_decompr_indices(trans_perms)
|
|
170
|
+
c_trans = get_lat_trans_compr_matrix(decompr_idx, N, n_lp)
|
|
171
|
+
return c_trans
|