• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

materialsproject / pymatgen / 4075885785

pending completion
4075885785

push

github

Shyue Ping Ong
Merge branch 'master' of github.com:materialsproject/pymatgen

96 of 96 new or added lines in 27 files covered. (100.0%)

81013 of 102710 relevant lines covered (78.88%)

0.79 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.23
/pymatgen/phonon/gruneisen.py
1
# Copyright (c) Pymatgen Development Team.
2
# Distributed under the terms of the MIT License.
3

4
"""
1✔
5
This module provides classes to define a Grueneisen band structure.
6
"""
7

8
from __future__ import annotations
1✔
9

10
import numpy as np
1✔
11
import scipy.constants as const
1✔
12
from monty.dev import requires
1✔
13
from monty.json import MSONable
1✔
14

15
from pymatgen.core import Structure
1✔
16
from pymatgen.core.lattice import Lattice
1✔
17
from pymatgen.core.units import amu_to_kg
1✔
18
from pymatgen.phonon.bandstructure import (
1✔
19
    PhononBandStructure,
20
    PhononBandStructureSymmLine,
21
)
22
from pymatgen.phonon.dos import PhononDos
1✔
23

24
try:
1✔
25
    import phonopy
1✔
26
    from phonopy.phonon.dos import TotalDos
1✔
27
except ImportError as ex:
×
28
    print(ex)
×
29
    phonopy = None
×
30

31
__author__ = "A. Bonkowski, J. George, G. Petretto"
1✔
32
__copyright__ = "Copyright 2021, The Materials Project"
1✔
33
__version__ = "0.1"
1✔
34
__maintainer__ = "A. Bonkowski, J. George"
1✔
35
__email__ = "alexander.bonkowski@rwth-aachen.de, janine.george@bam.de"
1✔
36
__status__ = "Production"
1✔
37
__date__ = "Apr 11, 2021"
1✔
38

39

40
class GruneisenParameter(MSONable):
1✔
41
    """
42
    Class for Grueneisen parameters on a regular grid.
43
    """
44

45
    def __init__(
1✔
46
        self,
47
        qpoints,
48
        gruneisen,
49
        frequencies,
50
        multiplicities=None,
51
        structure=None,
52
        lattice=None,
53
    ):
54
        """
55
        Args:
56
            qpoints: list of qpoints as numpy arrays, in frac_coords of the given lattice by default
57
            gruneisen: list of gruneisen parameters as numpy arrays, shape: (3*len(structure), len(qpoints))
58
            frequencies: list of phonon frequencies in THz as a numpy array with shape (3*len(structure), len(qpoints))
59
            multiplicities: list of multiplicities
60
            structure: The crystal structure (as a pymatgen Structure object) associated with the gruneisen parameters.
61
            lattice: The reciprocal lattice as a pymatgen Lattice object. Pymatgen uses the physics convention of
62
                     reciprocal lattice vectors WITH a 2*pi coefficient
63
        """
64
        self.qpoints = qpoints
1✔
65
        self.gruneisen = gruneisen
1✔
66
        self.frequencies = frequencies
1✔
67
        self.multiplicities = multiplicities
1✔
68
        self.lattice = lattice
1✔
69
        self.structure = structure
1✔
70

71
    def average_gruneisen(self, t=None, squared=True, limit_frequencies=None):
1✔
72
        """
73
        Calculates the average of the Gruneisen based on the values on the regular grid.
74
        If squared is True the average will use the squared value of the Gruneisen and a squared root
75
        is performed on the final result.
76
        Values associated to negative frequencies will be ignored.
77
        See Scripta Materialia 129, 88 for definitions.
78
        Adapted from classes in abipy that have been written by Guido Petretto (UCLouvain).
79

80
        Args:
81
            t: the temperature at which the average Gruneisen will be evaluated. If None the acoustic Debye
82
                temperature is used (see acoustic_debye_temp).
83
            squared: if True the average is performed on the squared values of the Grueneisen.
84
            limit_frequencies: if None (default) no limit on the frequencies will be applied.
85
                Possible values are "debye" (only modes with frequencies lower than the acoustic Debye
86
                temperature) and "acoustic" (only the acoustic modes, i.e. the first three modes).
87

88
        Returns:
89
            The average Gruneisen parameter
90
        """
91
        if t is None:
1✔
92
            t = self.acoustic_debye_temp
1✔
93

94
        w = self.frequencies  # in THz
1✔
95
        wdkt = w * const.tera / (const.value("Boltzmann constant in Hz/K") * t)
1✔
96
        exp_wdkt = np.exp(wdkt)
1✔
97
        cv = np.choose(
1✔
98
            w > 0, (0, const.value("Boltzmann constant in eV/K") * wdkt**2 * exp_wdkt / (exp_wdkt - 1) ** 2)
99
        )  # in eV
100

101
        gamma = self.gruneisen
1✔
102

103
        if squared:
1✔
104
            gamma = gamma**2
1✔
105

106
        if limit_frequencies == "debye":
1✔
107
            acoustic_debye_freq = self.acoustic_debye_temp * const.value("Boltzmann constant in Hz/K") / const.tera
1✔
108
            ind = np.where((w >= 0) & (w <= acoustic_debye_freq))
1✔
109
        elif limit_frequencies == "acoustic":
1✔
110
            w_acoustic = w[:, :3]
1✔
111
            ind = np.where(w_acoustic >= 0)
1✔
112
        elif limit_frequencies is None:
1✔
113
            ind = np.where(w >= 0)
1✔
114
        else:
115
            raise ValueError(f"{limit_frequencies} is not an accepted value for limit_frequencies.")
×
116

117
        weights = self.multiplicities
1✔
118
        g = np.dot(weights[ind[0]], np.multiply(cv, gamma)[ind]).sum() / np.dot(weights[ind[0]], cv[ind]).sum()
1✔
119

120
        if squared:
1✔
121
            g = np.sqrt(g)
1✔
122

123
        return g
1✔
124

125
    def thermal_conductivity_slack(self, squared=True, limit_frequencies=None, theta_d=None, t=None):
1✔
126
        """
127
        Calculates the thermal conductivity at the acoustic Debye temperature with the Slack formula,
128
        using the average Gruneisen.
129
        Adapted from abipy.
130

131
        Args:
132
            squared (bool): if True the average is performed on the squared values of the Gruenisen
133
            limit_frequencies: if None (default) no limit on the frequencies will be applied.
134
                Possible values are "debye" (only modes with frequencies lower than the acoustic Debye
135
                temperature) and "acoustic" (only the acoustic modes, i.e. the first three modes).
136
            theta_d: the temperature used to estimate the average of the Gruneisen used in the
137
                Slack formula. If None the acoustic Debye temperature is used (see
138
                acoustic_debye_temp). Will also be considered as the Debye temperature in the
139
                Slack formula.
140
            t: temperature at which the thermal conductivity is estimated. If None the value at
141
                the calculated acoustic Debye temperature is given. The value is obtained as a
142
                simple rescaling of the value at the Debye temperature.
143

144
        Returns:
145
            The value of the thermal conductivity in W/(m*K)
146
        """
147
        average_mass = np.mean([s.specie.atomic_mass for s in self.structure]) * amu_to_kg
1✔
148
        if theta_d is None:
1✔
149
            theta_d = self.acoustic_debye_temp
1✔
150
        mean_g = self.average_gruneisen(t=theta_d, squared=squared, limit_frequencies=limit_frequencies)
1✔
151

152
        f1 = 0.849 * 3 * (4 ** (1.0 / 3.0)) / (20 * np.pi**3 * (1 - 0.514 * mean_g**-1 + 0.228 * mean_g**-2))
1✔
153
        f2 = (const.k * theta_d / const.hbar) ** 2
1✔
154
        f3 = const.k * average_mass * self.structure.volume ** (1.0 / 3.0) * 1e-10 / (const.hbar * mean_g**2)
1✔
155
        k = f1 * f2 * f3
1✔
156

157
        if t is not None:
1✔
158
            k *= theta_d / t
1✔
159

160
        return k
1✔
161

162
    @property  # type: ignore
1✔
163
    @requires(phonopy, "This method requires phonopy to be installed")
1✔
164
    def tdos(self):
1✔
165
        """
166
        The total DOS (re)constructed from the gruneisen.yaml file
167
        """
168

169
        # Here, we will reuse phonopy classes
170
        class TempMesh:
1✔
171
            """
172
            Temporary Class
173
            """
174

175
        a = TempMesh()
1✔
176
        a.frequencies = np.transpose(self.frequencies)
1✔
177
        a.weights = self.multiplicities
1✔
178

179
        b = TotalDos(a)
1✔
180
        b.run()
1✔
181

182
        return b
1✔
183

184
    @property
1✔
185
    def phdos(self):
1✔
186
        """
187
        Returns: PhononDos object
188
        """
189
        return PhononDos(self.tdos.frequency_points, self.tdos.dos)
1✔
190

191
    @property
1✔
192
    def debye_temp_limit(self):
1✔
193
        """
194
        Debye temperature in K. Adapted from apipy.
195
        """
196
        from scipy.interpolate import UnivariateSpline
1✔
197

198
        f_mesh = self.tdos.frequency_points * const.tera
1✔
199
        dos = self.tdos.dos
1✔
200

201
        i_a = UnivariateSpline(f_mesh, dos * f_mesh**2, s=0).integral(f_mesh[0], f_mesh[-1])
1✔
202
        i_b = UnivariateSpline(f_mesh, dos, s=0).integral(f_mesh[0], f_mesh[-1])
1✔
203

204
        integrals = i_a / i_b
1✔
205
        t_d = np.sqrt(5 / 3 * integrals) / const.value("Boltzmann constant in Hz/K")
1✔
206

207
        return t_d
1✔
208

209
    def debye_temp_phonopy(self, freq_max_fit=None):
1✔
210
        """
211
        Get Debye temperature in K as implemented in phonopy.
212
        Args:
213
            freq_max_fit: Maximum frequency to include for fitting.
214
                          Defaults to include first quartile of frequencies.
215

216
        Returns:
217
            Debye temperature in K.
218
        """
219
        # Use of phonopy classes to compute Debye frequency
220
        t = self.tdos
1✔
221
        t.set_Debye_frequency(num_atoms=self.structure.num_sites, freq_max_fit=freq_max_fit)
1✔
222
        f_d = t.get_Debye_frequency()  # in THz
1✔
223
        # f_d in THz is converted in a temperature (K)
224
        t_d = const.value("Planck constant") * f_d * const.tera / const.value("Boltzmann constant")
1✔
225

226
        return t_d
1✔
227

228
    @property
1✔
229
    def acoustic_debye_temp(self):
1✔
230
        """
231
        Acoustic Debye temperature in K, i.e. the Debye temperature divided by nsites**(1/3).
232
        Adapted from abipy.
233
        """
234
        return self.debye_temp_limit / self.structure.num_sites ** (1 / 3)
1✔
235

236

237
class GruneisenPhononBandStructure(PhononBandStructure):
1✔
238
    """
239
    This is the most generic phonon band structure data possible
240
    it's defined by a list of qpoints + frequencies for each of them.
241
    Additional information may be given for frequencies at Gamma, where
242
    non-analytical contribution may be taken into account.
243
    """
244

245
    def __init__(
1✔
246
        self,
247
        qpoints,
248
        frequencies,
249
        gruneisenparameters,
250
        lattice,
251
        eigendisplacements=None,
252
        labels_dict=None,
253
        coords_are_cartesian=False,
254
        structure=None,
255
    ):
256
        """
257
        Args:
258
            qpoints: list of qpoint as numpy arrays, in frac_coords of the
259
                given lattice by default
260
            frequencies: list of phonon frequencies in THz as a numpy array with shape
261
                (3*len(structure), len(qpoints)). The First index of the array
262
                refers to the band and the second to the index of the qpoint.
263
            gruneisenparameters: list of Grueneisen parameters with the same structure
264
                frequencies.
265
            lattice: The reciprocal lattice as a pymatgen Lattice object.
266
                Pymatgen uses the physics convention of reciprocal lattice vectors
267
                WITH a 2*pi coefficient.
268
            eigendisplacements: the phonon eigendisplacements associated to the
269
                frequencies in Cartesian coordinates. A numpy array of complex
270
                numbers with shape (3*len(structure), len(qpoints), len(structure), 3).
271
                The first index of the array refers to the band, the second to the index
272
                of the qpoint, the third to the atom in the structure and the fourth
273
                to the Cartesian coordinates.
274
            labels_dict: (dict) of {} this links a qpoint (in frac coords or
275
                Cartesian coordinates depending on the coords) to a label.
276
            coords_are_cartesian: Whether the qpoint coordinates are Cartesian.
277
            structure: The crystal structure (as a pymatgen Structure object)
278
                associated with the band structure. This is needed if we
279
                provide projections to the band structure
280
        """
281
        PhononBandStructure.__init__(
1✔
282
            self,
283
            qpoints,
284
            frequencies,
285
            lattice,
286
            nac_frequencies=None,
287
            eigendisplacements=eigendisplacements,
288
            nac_eigendisplacements=None,
289
            labels_dict=labels_dict,
290
            coords_are_cartesian=coords_are_cartesian,
291
            structure=structure,
292
        )
293
        self.gruneisen = gruneisenparameters
1✔
294

295
    def as_dict(self):
1✔
296
        """
297
        Returns:
298
            MSONable (dict)
299
        """
300
        d = {
1✔
301
            "@module": type(self).__module__,
302
            "@class": type(self).__name__,
303
            "lattice_rec": self.lattice_rec.as_dict(),
304
            "qpoints": [],
305
        }
306
        # qpoints are not Kpoint objects dicts but are frac coords. This makes
307
        # the dict smaller and avoids the repetition of the lattice
308
        for q in self.qpoints:
1✔
309
            d["qpoints"].append(q.as_dict()["fcoords"])
1✔
310
        d["bands"] = self.bands.tolist()
1✔
311
        d["labels_dict"] = {}
1✔
312
        for kpoint_letter, kpoint_object in self.labels_dict.items():
1✔
313
            d["labels_dict"][kpoint_letter] = kpoint_object.as_dict()["fcoords"]
×
314
        # split the eigendisplacements to real and imaginary part for serialization
315
        d["eigendisplacements"] = dict(
1✔
316
            real=np.real(self.eigendisplacements).tolist(), imag=np.imag(self.eigendisplacements).tolist()
317
        )
318
        d["gruneisen"] = self.gruneisen.tolist()
1✔
319
        if self.structure:
1✔
320
            d["structure"] = self.structure.as_dict()
1✔
321

322
        return d
1✔
323

324
    @classmethod
1✔
325
    def from_dict(cls, d):
1✔
326
        """
327
        Args:
328
            d (dict): Dict representation
329

330
        Returns:
331
            GruneisenPhononBandStructure: Phonon band structure with Grueneisen parameters.
332
        """
333
        lattice_rec = Lattice(d["lattice_rec"]["matrix"])
×
334
        eigendisplacements = np.array(d["eigendisplacements"]["real"]) + np.array(d["eigendisplacements"]["imag"]) * 1j
×
335
        structure = Structure.from_dict(d["structure"]) if "structure" in d else None
×
336
        return cls(
×
337
            qpoints=d["qpoints"],
338
            frequencies=np.array(d["bands"]),
339
            gruneisenparameters=np.array(d["gruneisen"]),
340
            lattice=lattice_rec,
341
            eigendisplacements=eigendisplacements,
342
            labels_dict=d["labels_dict"],
343
            structure=structure,
344
        )
345

346

347
class GruneisenPhononBandStructureSymmLine(GruneisenPhononBandStructure, PhononBandStructureSymmLine):
1✔
348
    """
349
    This object stores a GruneisenPhononBandStructureSymmLine together with Grueneisen parameters
350
    for every frequency.
351
    """
352

353
    def __init__(
1✔
354
        self,
355
        qpoints,
356
        frequencies,
357
        gruneisenparameters,
358
        lattice,
359
        eigendisplacements=None,
360
        labels_dict=None,
361
        coords_are_cartesian=False,
362
        structure=None,
363
    ):
364
        """
365
        Args:
366
            qpoints: list of qpoints as numpy arrays, in frac_coords of the
367
                given lattice by default
368
            frequencies: list of phonon frequencies in eV as a numpy array with shape
369
                (3*len(structure), len(qpoints))
370
            gruneisenparameters: list of Grueneisen parameters as a numpy array with the
371
                shape (3*len(structure), len(qpoints))
372
            lattice: The reciprocal lattice as a pymatgen Lattice object.
373
                Pymatgen uses the physics convention of reciprocal lattice vectors
374
                WITH a 2*pi coefficient
375
            eigendisplacements: the phonon eigendisplacements associated to the
376
                frequencies in Cartesian coordinates. A numpy array of complex
377
                numbers with shape (3*len(structure), len(qpoints), len(structure), 3).
378
                The first index of the array refers to the band, the second to the index
379
                of the qpoint, the third to the atom in the structure and the fourth
380
                to the Cartesian coordinates.
381
            labels_dict: (dict) of {} this links a qpoint (in frac coords or
382
                Cartesian coordinates depending on the coords) to a label.
383
            coords_are_cartesian: Whether the qpoint coordinates are cartesian.
384
            structure: The crystal structure (as a pymatgen Structure object)
385
                associated with the band structure. This is needed if we
386
                provide projections to the band structure
387
        """
388
        GruneisenPhononBandStructure.__init__(
1✔
389
            self,
390
            qpoints=qpoints,
391
            frequencies=frequencies,
392
            gruneisenparameters=gruneisenparameters,
393
            lattice=lattice,
394
            eigendisplacements=eigendisplacements,
395
            labels_dict=labels_dict,
396
            coords_are_cartesian=coords_are_cartesian,
397
            structure=structure,
398
        )
399

400
        PhononBandStructureSymmLine._reuse_init(
1✔
401
            self, eigendisplacements=eigendisplacements, frequencies=frequencies, has_nac=False, qpoints=qpoints
402
        )
403

404
    @classmethod
1✔
405
    def from_dict(cls, d):
1✔
406
        """
407
        Args:
408
            d: Dict representation
409

410
        Returns: GruneisenPhononBandStructureSummLine
411
        """
412
        lattice_rec = Lattice(d["lattice_rec"]["matrix"])
1✔
413
        eigendisplacements = np.array(d["eigendisplacements"]["real"]) + np.array(d["eigendisplacements"]["imag"]) * 1j
1✔
414
        structure = Structure.from_dict(d["structure"]) if "structure" in d else None
1✔
415
        return cls(
1✔
416
            qpoints=d["qpoints"],
417
            frequencies=np.array(d["bands"]),
418
            gruneisenparameters=np.array(d["gruneisen"]),
419
            lattice=lattice_rec,
420
            eigendisplacements=eigendisplacements,
421
            labels_dict=d["labels_dict"],
422
            structure=structure,
423
        )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc