@@ -24,13 +24,13 @@ def get_reasonable_repetitions(n_atoms: int) -> tuple[int, int, int]:
24
24
according to the number of atoms in the system.
25
25
"""
26
26
if n_atoms < 4 :
27
- return ( 3 , 3 , 3 )
27
+ return 3 , 3 , 3
28
28
if 4 <= n_atoms < 15 :
29
- return ( 2 , 2 , 2 )
29
+ return 2 , 2 , 2
30
30
if 15 <= n_atoms < 50 :
31
- return ( 2 , 2 , 1 )
31
+ return 2 , 2 , 1
32
32
33
- return ( 1 , 1 , 1 )
33
+ return 1 , 1 , 1
34
34
35
35
36
36
def eigenvectors_from_displacements (disp , masses ) -> np .ndarray :
@@ -137,8 +137,8 @@ def __init__(
137
137
self .nb_qpoints = len (self .qpoints )
138
138
139
139
# normalize directions for nac_frequencies and nac_eigendisplacements
140
- self .nac_frequencies = []
141
- self .nac_eigendisplacements = []
140
+ self .nac_frequencies : list [ tuple [ list [ float ], np . ndarray ]] = []
141
+ self .nac_eigendisplacements : list [ tuple [ list [ float ], np . ndarray ]] = []
142
142
if nac_frequencies is not None :
143
143
for freq in nac_frequencies :
144
144
self .nac_frequencies .append (([idx / np .linalg .norm (freq [0 ]) for idx in freq [0 ]], freq [1 ]))
@@ -152,13 +152,29 @@ def min_freq(self) -> tuple[Kpoint, float]:
152
152
153
153
return self .qpoints [idx [1 ]], self .bands [idx ]
154
154
155
- def has_imaginary_freq (self , tol : float = 1e-5 ) -> bool :
156
- """True if imaginary frequencies are present in the BS."""
155
+ def has_imaginary_freq (self , tol : float = 1e-3 ) -> bool :
156
+ """True if imaginary frequencies are present anywhere in the band structure. Always True if
157
+ has_imaginary_gamma_freq is True.
158
+
159
+ Args:
160
+ tol: Tolerance for determining if a frequency is imaginary. Defaults to 1e-3.
161
+ """
157
162
return self .min_freq ()[1 ] + tol < 0
158
163
164
+ def has_imaginary_gamma_freq (self , tol : float = 1e-3 ) -> bool :
165
+ """Checks if there are imaginary modes at the gamma point.
166
+
167
+ Args:
168
+ tol: Tolerance for determining if a frequency is imaginary. Defaults to 1e-3.
169
+ """
170
+ gamma_freqs = self .bands [:, 0 ] # frequencies at the Gamma point
171
+ return any (freq < - tol for freq in gamma_freqs )
172
+
159
173
@property
160
174
def has_nac (self ) -> bool :
161
- """True if nac_frequencies are present."""
175
+ """True if nac_frequencies are present (i.e. the band structure has been
176
+ calculated taking into account Born-charge-derived non-analytical corrections at Gamma).
177
+ """
162
178
return len (self .nac_frequencies ) > 0
163
179
164
180
@property
@@ -177,10 +193,10 @@ def get_nac_frequencies_along_dir(self, direction: Sequence) -> np.ndarray | Non
177
193
the frequencies as a numpy array o(3*len(structure), len(qpoints)).
178
194
None if not found.
179
195
"""
180
- versor = [i / np .linalg .norm (direction ) for i in direction ]
181
- for d , f in self .nac_frequencies :
182
- if np .allclose (versor , d ):
183
- return f
196
+ versor = [idx / np .linalg .norm (direction ) for idx in direction ]
197
+ for dist , freq in self .nac_frequencies :
198
+ if np .allclose (versor , dist ):
199
+ return freq
184
200
185
201
return None
186
202
@@ -195,10 +211,10 @@ def get_nac_eigendisplacements_along_dir(self, direction) -> np.ndarray | None:
195
211
the eigendisplacements as a numpy array of complex numbers with shape
196
212
(3*len(structure), len(structure), 3). None if not found.
197
213
"""
198
- versor = [i / np .linalg .norm (direction ) for i in direction ]
199
- for d , e in self .nac_eigendisplacements :
200
- if np .allclose (versor , d ):
201
- return e
214
+ versor = [idx / np .linalg .norm (direction ) for idx in direction ]
215
+ for dist , eigen_disp in self .nac_eigendisplacements :
216
+ if np .allclose (versor , dist ):
217
+ return eigen_disp
202
218
203
219
return None
204
220
@@ -426,45 +442,40 @@ def get_equivalent_qpoints(self, index: int) -> list[int]:
426
442
TODO: now it uses the label we might want to use coordinates instead
427
443
(in case there was a mislabel)
428
444
"""
429
- # if the qpoint has no label it can" t have a repetition along the band
445
+ # if the qpoint has no label it can' t have a repetition along the band
430
446
# structure line object
431
447
432
448
if self .qpoints [index ].label is None :
433
449
return [index ]
434
450
435
451
list_index_qpoints = []
436
- for i in range (self .nb_qpoints ):
437
- if self .qpoints [i ].label == self .qpoints [index ].label :
438
- list_index_qpoints .append (i )
452
+ for idx in range (self .nb_qpoints ):
453
+ if self .qpoints [idx ].label == self .qpoints [index ].label :
454
+ list_index_qpoints .append (idx )
439
455
440
456
return list_index_qpoints
441
457
442
- def get_branch (self , index : int ) -> list [dict ]:
443
- r"""Returns in what branch(es) is the qpoint. There can be several
444
- branches.
458
+ def get_branch (self , index : int ) -> list [dict [str , str | int ]]:
459
+ r"""Returns in what branch(es) is the qpoint. There can be several branches.
445
460
446
461
Args:
447
- index: the qpoint index
462
+ index (int) : the qpoint index
448
463
449
464
Returns:
450
- A list of dictionaries [{"name","start_index","end_index","index"}]
451
- indicating all branches in which the qpoint is. It takes into
452
- account the fact that one qpoint (e.g., \\Gamma) can be in several
453
- branches
465
+ list[dict[str, str | int]]: [{"name","start_index","end_index","index"}]
466
+ indicating all branches in which the qpoint is. It takes into
467
+ account the fact that one qpoint (e.g., \\Gamma) can be in several
468
+ branches
454
469
"""
455
- to_return = []
456
- for i in self .get_equivalent_qpoints (index ):
457
- for b in self .branches :
458
- if b ["start_index" ] <= i <= b ["end_index" ]:
459
- to_return .append (
460
- {
461
- "name" : b ["name" ],
462
- "start_index" : b ["start_index" ],
463
- "end_index" : b ["end_index" ],
464
- "index" : i ,
465
- }
470
+ lst = []
471
+ for pt_idx in self .get_equivalent_qpoints (index ):
472
+ for branch in self .branches :
473
+ start_idx , end_idx = branch ["start_index" ], branch ["end_index" ]
474
+ if start_idx <= pt_idx <= end_idx :
475
+ lst .append (
476
+ {"name" : branch ["name" ], "start_index" : start_idx , "end_index" : end_idx , "index" : pt_idx }
466
477
)
467
- return to_return
478
+ return lst
468
479
469
480
def write_phononwebsite (self , filename : str | PathLike ) -> None :
470
481
"""Write a json file for the phononwebsite:
@@ -606,13 +617,13 @@ def from_dict(cls, dct: dict) -> PhononBandStructureSymmLine:
606
617
eigendisplacements = (
607
618
np .array (dct ["eigendisplacements" ]["real" ]) + np .array (dct ["eigendisplacements" ]["imag" ]) * 1j
608
619
)
609
- structure = Structure .from_dict (dct ["structure" ]) if "structure" in dct else None
620
+ struct = Structure .from_dict (dct ["structure" ]) if "structure" in dct else None
610
621
return cls (
611
622
dct ["qpoints" ],
612
623
np .array (dct ["bands" ]),
613
624
lattice_rec ,
614
625
dct ["has_nac" ],
615
626
eigendisplacements ,
616
627
dct ["labels_dict" ],
617
- structure = structure ,
628
+ structure = struct ,
618
629
)
0 commit comments