Skip to content

Commit b21143d

Browse files
authored
Merge pull request #1 from pierre-24/add_kp
Allow to compute the spectra and displacements at other points in the Brilouin zone
2 parents e7d34ff + 3759b6c commit b21143d

File tree

6 files changed

+50
-22
lines changed

6 files changed

+50
-22
lines changed

phonopy_vibspec/phonons_analyzer.py

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import pathlib
22
import numpy
33

4+
from numpy.typing import NDArray
5+
46
import phonopy
57
from phonopy.interface import calculator as phonopy_calculator
68

7-
from typing import Optional, List, Tuple
9+
from typing import Optional, List, Tuple, Union
810

911
from phonopy_vibspec import logger
12+
from phonopy.units import VaspToCm
1013
from phonopy_vibspec.spectra import RamanSpectrum, InfraredSpectrum
1114
from phonopy_vibspec.vesta import VestaVector, make_vesta_file
1215

13-
THZ_TO_INV_CM = 33.35641
14-
1516
# [(value, coef), ...]
1617
# see https://en.wikipedia.org/wiki/Finite_difference_coefficient
1718
TWO_POINTS_STENCIL = [(-1, -.5), (1, .5)] # two-points, centered
@@ -21,35 +22,37 @@
2122

2223

2324
class PhononsAnalyzer:
24-
"""Use Phonopy to extract phonon frequencies and eigenmodes, as well as irreps
25+
"""Use Phonopy to extract phonon frequencies and eigenmodes, as well as irreps, at a given q-point
26+
(default is Gamma).
2527
"""
2628

2729
DC_GEOMETRY_TEMPLATE = 'dielec_mode{:04d}_step{:02d}.vasp'
2830
VESTA_MODE_TEMPLATE = 'mode{:04d}.vesta'
2931

30-
def __init__(self, phonon: phonopy.Phonopy):
31-
self.phonotopy = phonon
32+
def __init__(self, phonon: phonopy.Phonopy, q: Union[NDArray, Tuple[float, float, float]] = (.0, .0, .0)):
33+
self.phonopy = phonon
34+
self.q = q
3235
self.structure = phonon.primitive
3336

3437
# get eigenvalues and eigenvectors at gamma point
3538
# See https://github.com/phonopy/phonopy/issues/308#issuecomment-1769736200
3639
l_logger.info('Symmetrize force constant')
37-
self.phonotopy.symmetrize_force_constants()
38-
39-
l_logger.info('Run mesh')
40-
self.phonotopy.run_mesh([1, 1, 1], with_eigenvectors=True)
40+
self.phonopy.symmetrize_force_constants()
4141

42-
mesh_dict = phonon.get_mesh_dict()
42+
l_logger.info('Fetch dynamical matrix at q=({})'.format(', '.join('{:.3f}'.format(x) for x in q)))
43+
self.phonopy.dynamical_matrix.run(self.q)
44+
dm = self.phonopy.dynamical_matrix.dynamical_matrix
45+
eigv, eigf = numpy.linalg.eigh(dm)
4346

4447
self.N = self.structure.get_number_of_atoms()
4548
l_logger.info('Analyze {} modes (including acoustic)'.format(3 * self.N))
46-
self.frequencies = mesh_dict['frequencies'][0] * THZ_TO_INV_CM # in [cm⁻¹]
49+
self.frequencies = numpy.sqrt(numpy.abs(eigv.real)) * numpy.sign(eigv.real) * VaspToCm # in [cm⁻¹]
4750

48-
l_logger.info('The 5 first modes are {}'.format(
51+
l_logger.info('The 5 first modes are at (in cm⁻¹) {}'.format(
4952
', '.join('{:.3f}'.format(x) for x in self.frequencies[:5]))
5053
)
5154

52-
self.eigenvectors = mesh_dict['eigenvectors'][0].real.T # in [Å sqrt(AMU)]
55+
self.eigenvectors = eigf.real.T # in [Å sqrt(AMU)]
5356

5457
# compute displacements with Eq. 4 of 10.1039/C7CP01680H
5558
sqrt_masses = numpy.repeat(numpy.sqrt(self.structure.masses), 3)
@@ -59,7 +62,7 @@ def __init__(self, phonon: phonopy.Phonopy):
5962
self.irrep_labels = ['A'] * (self.N * 3)
6063

6164
try:
62-
self.phonotopy.set_irreps([0, 0, 0])
65+
self.phonopy.set_irreps(q)
6366
self.irreps = phonon.get_irreps()
6467

6568
# TODO: that's internal API, so subject to change!
@@ -74,7 +77,8 @@ def from_phonopy(
7477
cls,
7578
phonopy_yaml: str = 'phonopy_disp.yaml',
7679
force_constants_filename: str = 'force_constants.hdf5',
77-
born_filename: Optional[str] = None
80+
born_filename: Optional[str] = None,
81+
q: Union[NDArray, Tuple[float, float, float]] = (.0, .0, .0)
7882
) -> 'PhononsAnalyzer':
7983
"""
8084
Use the Python interface of Phonopy, see https://phonopy.github.io/phonopy/phonopy-module.html.
@@ -86,7 +90,7 @@ def from_phonopy(
8690
phonopy_yaml=phonopy_yaml,
8791
force_constants_filename=force_constants_filename,
8892
born_filename=born_filename,
89-
))
93+
), q=q)
9094

9195
def infrared_spectrum(self, modes: Optional[List[int]] = None) -> InfraredSpectrum:
9296
"""
@@ -96,11 +100,11 @@ def infrared_spectrum(self, modes: Optional[List[int]] = None) -> InfraredSpectr
96100

97101
l_logger.info('Create IR spectrum object')
98102

99-
born_tensor = self.phonotopy.nac_params['born']
103+
born_tensor = self.phonopy.nac_params['born']
100104

101105
# select modes if any
102106
if modes is None:
103-
modes = list(range(3, 3 * self.N))
107+
modes = list(range(3 if numpy.allclose(self.q, [.0, .0, .0]) else 0, 3 * self.N))
104108
else:
105109
for mode in modes:
106110
if mode < 0 or mode >= 3 * self.N:
@@ -139,7 +143,11 @@ def prepare_raman(
139143

140144
# select modes if any
141145
if modes is None:
142-
modes = list(range(3, 3 * self.N))
146+
modes = list(range(3 if numpy.allclose(self.q, [.0, .0, .0]) else 0, 3 * self.N))
147+
else:
148+
for mode in modes:
149+
if mode < 0 or mode >= 3 * self.N:
150+
raise IndexError(mode)
143151

144152
frequencies = [self.frequencies[m] for m in modes]
145153
irrep_labels = [self.irrep_labels[m] for m in modes]

phonopy_vibspec/scripts/__init__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@ def list_of_modes(inp: str) -> List[int]:
99
raise argparse.ArgumentTypeError('invalid (space-separated) list of integers `{}` for modes'.format(inp))
1010

1111

12+
def q_point(inp: str) -> Tuple[float, float, float]:
13+
chunks = inp.split()
14+
if len(chunks) != 3:
15+
raise argparse.ArgumentTypeError('invalid (space-separated) q-point `{}`, must contains 3 elements'.format(inp))
16+
17+
try:
18+
return tuple(float(x) for x in chunks)
19+
except ValueError:
20+
raise argparse.ArgumentTypeError('invalid (space-separated) list of floats `{}` for q-point'.format(inp))
21+
22+
1223
def add_common_args(parser: argparse.ArgumentParser):
1324
parser.add_argument(
1425
'-c', '--phonopy', type=str, help='Phonopy YAML containing the cells', default='phonopy_disp.yaml')
@@ -17,6 +28,12 @@ def add_common_args(parser: argparse.ArgumentParser):
1728

1829
parser.add_argument('-m', '--modes', type=list_of_modes, help='List of modes (1-based)', default='')
1930

31+
parser.add_argument(
32+
'-q',
33+
type=q_point,
34+
help='q-point at which this should be computed (default is gamma)',
35+
default='0 0 0')
36+
2037

2138
def interval(s_interval: str) -> Tuple[float, float]:
2239
"""get interval

phonopy_vibspec/scripts/prepare_raman.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def main():
2323
phonons = PhononsAnalyzer.from_phonopy(
2424
phonopy_yaml=args.phonopy,
2525
force_constants_filename=args.fc,
26+
q=args.q
2627
)
2728

2829
raman_spectrum = phonons.prepare_raman(

phonopy_vibspec/scripts/spectrum_ir.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ def main():
2121
phonons = PhononsAnalyzer.from_phonopy(
2222
phonopy_yaml=args.phonopy,
2323
force_constants_filename=args.fc,
24-
born_filename=args.born
24+
born_filename=args.born,
25+
q=args.q
2526
)
2627

2728
ir_spectrum = phonons.infrared_spectrum(modes=args.modes if len(args.modes) > 0 else None)

phonopy_vibspec/scripts/vesta_modes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def main():
4747
phonons = PhononsAnalyzer.from_phonopy(
4848
phonopy_yaml=args.phonopy,
4949
force_constants_filename=args.fc,
50+
q=args.q
5051
)
5152

5253
phonons.make_vesta_for_modes(

tests/test_vibspec.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def test_prepare_raman_SiO2(context_SiO2, tmp_path):
6969

7070
spectrum = phonons.prepare_raman(tmp_path)
7171

72-
assert spectrum.cell_volume == phonons.phonotopy.unitcell.volume
72+
assert spectrum.cell_volume == phonons.phonopy.unitcell.volume
7373
assert len(spectrum.modes) == 24 # skip acoustic
7474
assert numpy.allclose(spectrum.frequencies, phonons.frequencies[3:])
7575
assert spectrum.irrep_labels == phonons.irrep_labels[3:]

0 commit comments

Comments
 (0)