File: test_dynamic_structure_factor.py

package info (click to toggle)
phonopy 2.47.1-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 29,380 kB
  • sloc: python: 43,690; xml: 12,080; ansic: 3,227; cpp: 525; sh: 213; makefile: 20
file content (316 lines) | stat: -rw-r--r-- 9,502 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
"""Tests for dynamic structure factor."""

from __future__ import annotations

from collections.abc import Callable

import numpy as np
from numpy.typing import ArrayLike

from phonopy.api_phonopy import Phonopy
from phonopy.spectrum.dynamic_structure_factor import atomic_form_factor_WK1995

# D. Waasmaier and A. Kirfel, Acta Cryst. A51, 416 (1995)
# f(Q) = \sum_i a_i \exp((-b_i Q^2) + c
# Q is in angstron^-1
# a1, b1, a2, b2, a3, b3, a4, b4, a5, b5, c
f_params = {
    "Na": [
        3.148690,
        2.594987,
        4.073989,
        6.046925,
        0.767888,
        0.070139,
        0.995612,
        14.1226457,
        0.968249,
        0.217037,
        0.045300,
    ],  # 1+
    "Cl": [
        1.061802,
        0.144727,
        7.139886,
        1.171795,
        6.524271,
        19.467656,
        2.355626,
        60.320301,
        35.829404,
        0.000436,
        -34.916604,
    ],  # 1-
    "Si": [
        5.275329,
        2.631338,
        3.191038,
        33.730728,
        1.511514,
        0.081119,
        1.356849,
        86.288640,
        2.519114,
        1.170087,
        0.145073,
    ],  # neutral
    "Pb": [
        32.505656,
        1.047035,
        20.014240,
        6.670321,
        14.645661,
        0.105279,
        5.029499,
        16.525040,
        1.760138,
        0.105279,
        4.044678,
    ],  # 4+
    "Pb0": [
        16.419567,
        0.105499,
        32.738592,
        1.055049,
        6.530247,
        25.025890,
        2.342742,
        80.906596,
        19.916475,
        6.664449,
        4.049824,
    ],
    "Te": [
        6.660302,
        33.031656,
        6.940756,
        0.025750,
        19.847015,
        5.065547,
        1.557175,
        84.101613,
        17.802427,
        0.487660,
        -0.806668,
    ],
}  # neutral

data_AFF_Si = """1617.345756 1873.066252 503.856264  11.001319   8.079923   0.055787
932.668536   4.059507 124.346263  12.115047   0.020839   7.462799
442.713078   8.428824  53.809376  13.270402   2.033486   4.808897
277.504172   0.742826  29.008863  14.488787   3.570989   2.622653
  7.700914 190.329529  17.434621  15.811378   0.956863   4.567044
128.242061  27.189284  11.021400  17.307531   3.613591   1.213426
 42.672187  88.737326   6.948860  19.094662   4.045577   0.061621
  5.078139 112.962641   3.919888  21.394182   3.381237   0.000000
 17.263664  94.427377   1.063912  24.636357   0.316365   2.361231
109.690680   1.039754   4.324087  22.750438   1.942733   0.087975"""

data_b_Si = """1207.369580 1398.268247 376.135235   8.212628   6.031767   0.041645
710.488237   3.092451  94.724496   9.229001   0.015875   5.685011
344.213037   6.553479  41.837230  10.317846   1.581052   3.738956
220.257270   0.589587  23.024565  11.499866   2.834322   2.081621
  6.240743 154.241134  14.128841  12.813381   0.775432   3.701087
106.128611  22.500894   9.120923  14.323103   2.990480   1.004189
 36.068376  75.004623   5.873477  16.139634   3.419496   0.052085
  4.384648  97.536024   3.384573  18.472509   2.919482   0.000000
 15.229216  83.299522   0.938535  21.733070   0.279083   2.082970
 98.876152   0.937243   3.897770  20.507447   1.751197   0.079302 """

data_AFF_NaCl = """13.224708 710.969308 311.677433   2.873317  36.549683  21.483221
 13.145117 168.331089  83.948759  11.537634  27.187230  21.646819
  4.999420  75.939632  40.711985  22.059842  16.300749  21.908902
  1.389114  44.997343  25.647179   9.988515  28.129482  22.174846
  4.475042  26.807966  19.185649   0.607678  37.194983  22.317920
  5.456779  18.725760  16.551901   5.143921  32.097030  22.156059
  9.026793  12.399434   0.143074  36.016896  16.372369  21.360950
  0.021718  22.320823   0.478572  33.239975  18.712015  19.205739
  0.466439  29.112946   7.255176  19.464002  24.860848  14.174948
 25.956886  23.258523   0.301924   8.709088  35.161032   5.947678"""

data_b_NaCl = """40.928422 2200.339853 963.242578   5.345042  67.990960  39.950117
 40.938414 524.240900 260.035243  21.856090  51.501594  40.951936
 15.679344 238.164364 126.202848  42.560396  31.449289  42.148068
  4.376745 141.775245  79.243867  19.614922  55.239203  43.331623
 14.081177  84.353999  58.692973   1.212815  74.234377  44.206389
 16.990664  58.306028  49.697402  10.407351  64.939771  44.331889
 27.480380  37.747754   0.292186  73.554041  47.766181  42.920026
  0.063631  65.398838   0.977764  67.912112  52.478607  38.287570
  1.284951  80.200648  14.420648  38.687349  66.322982  27.156660
 65.710583  58.879603   0.494887  14.275135  89.011137   9.748886 """


def _get_func_AFF(f_params: dict):
    """Return atomic_form_factor function."""

    def func(symbol: str, Q: float) -> float:
        return atomic_form_factor_WK1995(Q, f_params[symbol])

    return func


def test_IXS_G_to_L_Si(ph_si: Phonopy):
    """Test of dynamic structure factor calculation."""
    degeneracies = [[[0, 1], [2], [3], [4, 5]]] * 10
    _test_IXS_G_to_L(
        ph_si, data_b_Si, data_AFF_Si, {"Si": 4.1491}, degeneracies, verbose=True
    )


def test_IXS_G_to_L_NaCl(ph_nacl: Phonopy):
    """Test of dynamic structure factor calculation with NaCl."""
    degeneracies = [[[0, 1], [2], [3, 4], [5]]] * 6 + [[[0, 1], [2, 3], [4], [5]]] * 4
    _test_IXS_G_to_L(
        ph_nacl,
        data_b_NaCl,
        data_AFF_NaCl,
        {"Na": 3.63, "Cl": 9.5770},
        degeneracies,
        verbose=False,
    )


def _test_IXS_G_to_L(
    phonon: Phonopy,
    data_b: str,
    data_AFF: str,
    scattering_lengths: dict,
    degeneracies: list,
    verbose: bool = True,
):
    mesh = [5, 5, 5]
    phonon.run_mesh(mesh, is_mesh_symmetry=False, with_eigenvectors=True)

    directions = np.array(
        [
            [0.5, 0.5, 0.5],
        ]
    )
    n_points = 11  # Gamma point is excluded, so will be len(S) = 10.
    G_points_cubic = ([7, 1, 1],)

    # Atomic form factor
    _run(
        phonon,
        G_points_cubic,
        directions,
        func_AFF=_get_func_AFF(f_params),
        n_points=n_points,
    )
    Q, S = phonon.get_dynamic_structure_factor()
    data_cmp = np.reshape([float(x) for x in data_AFF.split()], (-1, 6))
    if verbose:
        for S_at_Q in S:
            print(("%10.6f " * 6) % tuple(S_at_Q))
        for qpt in Q:
            print(qpt)

    # Treatment of degeneracy
    for j, deg_set in enumerate(degeneracies):
        for deg in deg_set:
            np.testing.assert_allclose(
                S[j, deg].sum(), data_cmp[j, deg].sum(), atol=1e-5
            )

    # Scattering lengths
    _run(
        phonon,
        G_points_cubic,
        directions,
        scattering_lengths=scattering_lengths,
        n_points=n_points,
    )
    Q, S = phonon.get_dynamic_structure_factor()
    data_cmp = np.reshape([float(x) for x in data_b.split()], (-1, 6))
    if verbose:
        for S_at_Q in S:
            print(("%10.6f " * 6) % tuple(S_at_Q))
        for qpt in Q:
            print(qpt)

    # Treatment of degeneracy
    for j, deg_set in enumerate(degeneracies):
        for deg in deg_set:
            np.testing.assert_allclose(
                S[j, deg].sum(), data_cmp[j, deg].sum(), atol=1e-5
            )


def plot_f_Q(f_params: dict):
    """Plot f_Q."""
    import matplotlib.pyplot as plt

    x = np.linspace(0.0, 6.0, 101)
    for elem in ("Si", "Na", "Cl", "Pb", "Pb0", "Te"):
        y = [atomic_form_factor_WK1995(Q, f_params[elem]) for Q in x]
        plt.plot(x, y, label=elem)
    plt.xlim(xmin=0)
    plt.ylim(ymin=0)
    plt.legend()
    plt.show()


def show(phonon):
    """Show the calculation result along many directions."""
    directions = np.array(
        [
            [0.5, 0.5, 0.5],
            [-0.5, 0.5, 0.5],
            [0.5, -0.5, 0.5],
            [0.5, 0.5, -0.5],
            [0.5, -0.5, -0.5],
            [-0.5, 0.5, -0.5],
            [-0.5, -0.5, 0.5],
            [-0.5, -0.5, -0.5],
        ]
    )
    G_points_cubic = ([7, 1, 1],)
    _run(phonon, G_points_cubic, directions, verbose=True)


def _run(
    phonon: Phonopy,
    G_points_cubic: tuple[list],
    directions: ArrayLike,
    func_AFF: Callable | None = None,
    scattering_lengths: dict | None = None,
    n_points: int = 51,
):
    P = [[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]
    G_to_L = np.array(
        [directions[0] * x for x in np.arange(0, n_points) / float(n_points - 1)]
    )

    phonon.run_band_structure([G_to_L])

    T = 300
    for G_cubic in G_points_cubic:
        G_prim = np.dot(G_cubic, P)
        for direction in directions:
            direction_prim = np.dot(direction, P)
            G_to_L = np.array(
                [
                    direction_prim * x
                    for x in np.arange(1, n_points) / float(n_points - 1)
                ]
            )
            if func_AFF is not None:
                phonon.init_dynamic_structure_factor(
                    G_to_L + G_prim,
                    T,
                    atomic_form_factor_func=func_AFF,
                    freq_min=1e-3,
                )
            elif scattering_lengths is not None:
                phonon.init_dynamic_structure_factor(
                    G_to_L + G_prim,
                    T,
                    scattering_lengths=scattering_lengths,
                    freq_min=1e-3,
                )
            else:
                raise SyntaxError
            dsf = phonon.dynamic_structure_factor
            assert dsf is not None
            for _, _ in enumerate(dsf):
                pass