2a_DL_workflow_precompute.py — Precompute Phonon Quantities

Precomputation phase of the Disorder Linewidth pipeline (mirrors Notebooks 1-4). Must be run first; its HDF5 outputs are used by 2b and 2c.

What it computes

  1. Band structure — phonon dispersion of the reference crystal along the path defined in config.BAND_LABELS; saved as dl_workflow/crystal_band_structure.png.

  2. Phonon mesh — frequencies and group velocities on config.MESH (default [128, 128, 32]) Γ-centred BZ mesh; saved to dl_workflow/mesh_data.hdf5.

  3. Crystal VDOS and mean group speed — extracted using Lorentzians broadened with η = config.GAMMA_BROADENING; saved to dl_workflow/crystal_vdos_group_vel.hdf5.

  4. Disordered VDOS — obtained from pre-computed frequencies in config.DISORDERED_FREQUENCIES; saved to dl_workflow/disordered_vdos.hdf5.

  5. Density-shifted crystal VDOS and speed — obtained from frequencies scaled by (ρ_dis/ρ_crys)^(1/3) to align with the disordered system; saved to dl_workflow/reduced_density_crystal_vdos_group_vel.hdf5.

Prerequisites: BNE + DL installation (see setup_dl.sh).

cd workflows
python 2a_DL_workflow_precompute.py
  1import os
  2import sys
  3import pathlib
  4sys.path.insert(0, str(pathlib.Path(__file__).parent))
  5
  6import h5py
  7import numpy as np
  8import matplotlib
  9import matplotlib.pyplot as plt
 10import ase
 11from ase.io import read
 12
 13from tqdm import tqdm
 14
 15
 16from phonopy import Phonopy
 17from phonopy import file_IO as phonopy_file_IO
 18
 19from phonopy.interface.calculator import read_crystal_structure
 20from phonopy.phonon.band_structure import get_band_qpoints_and_path_connections
 21
 22
 23from smooth_disorder.structural import obtain_density, THzToCm, THz, Angstrom
 24from smooth_disorder.vis.interactive import *
 25
 26from smooth_disorder.disorder_linewidth import run_band_structure_manual
 27from smooth_disorder.disorder_linewidth import run_phonon_mesh, save_mesh_data_to_files
 28
 29from smooth_disorder.disorder_linewidth import lorentzian_numpy, flatten_arrays_freq_only
 30from smooth_disorder.disorder_linewidth import calculate_vdos_with_frequency
 31from smooth_disorder.disorder_linewidth import save_vdos_data_to_files
 32
 33from smooth_disorder.disorder_linewidth import flatten_arrays
 34from smooth_disorder.disorder_linewidth import calculate_vdos_and_average_speed_with_frequency
 35from smooth_disorder.disorder_linewidth import save_vdos_speed_data_to_files
 36
 37from config import (
 38    CRYSTAL_POSCAR, CRYSTAL_FC2,
 39    DISORDERED_POSCAR, DISORDERED_FREQUENCIES, DISORDERED_DIFFUSIVITY,
 40    SUPERCELL_MATRIX, MESH, GAMMA_CENTER, GAMMA_BROADENING,
 41    BAND_PATH, BAND_LABELS,
 42    DL_WORK_DIR,
 43)
 44
 45WORK_DIR = DL_WORK_DIR
 46os.makedirs(WORK_DIR, exist_ok=True)
 47
 48MESH_SAVE             = f"{WORK_DIR}/mesh_data"
 49CRYSTAL_VEL_SAVE      = f"{WORK_DIR}/crystal_vdos_group_vel"
 50SHIFTED_SAVE          = f"{WORK_DIR}/reduced_density_crystal_vdos_group_vel"
 51DISORDERED_VDOS_SAVE  = f"{WORK_DIR}/disordered_vdos"
 52
 53
 54
 55################################
 56# BAND STRUCTURE VISUALIZATION #
 57################################
 58
 59# Special BZ points for hexagonal graphite — fractional reciprocal-lattice coordinates
 60atoms_ase = ase.io.read(CRYSTAL_POSCAR)
 61print(f"Special points in the BZ")
 62print(ase.dft.kpoints.get_special_points(atoms_ase.cell))
 63
 64print(f"Running the band structure calculation")
 65frequencies, distances, qpoints = run_band_structure_manual(CRYSTAL_POSCAR, CRYSTAL_FC2, SUPERCELL_MATRIX, BAND_PATH, BAND_LABELS)
 66
 67num_paths = len(distances)
 68num_modes = frequencies[0].shape[1]
 69
 70
 71# visualization
 72plt.figure(figsize=(16, 8))
 73
 74for i_path in range(num_paths):
 75    for mode in range(num_modes):
 76        plt.plot(distances[i_path], frequencies[i_path][:, mode]*THzToCm, color=Colors[3])
 77
 78y_min, y_max = 0, 1600
 79plt.vlines(0.0, y_min, y_max, color="black", lw=1)
 80plt.text(0.0-0.003, -50, BAND_LABELS[0])
 81
 82for i_path in range(num_paths):
 83    plt.vlines(distances[i_path][-1], y_min, y_max, color="black", lw=1)
 84    plt.text(distances[i_path][-1]-0.003, -50, BAND_LABELS[i_path+1])
 85
 86plt.ylabel("Frequencies [cm-1]")
 87plt.xlabel("Brillouin zone special points")
 88
 89plt.xlim([-0.01, np.max(distances)+0.01])
 90plt.xticks([])
 91plt.savefig(f"{WORK_DIR}/crystal_band_structure.png", dpi=300)
 92
 93
 94################################################################
 95# OBTAIN FREQUENCIES AND GROUP VELOCITIES IN REFERENCE CRYSTAL #
 96################################################################
 97
 98
 99print(f"Obtaining the crystal group velocities...")
100mesh_dict = run_phonon_mesh(CRYSTAL_POSCAR, CRYSTAL_FC2, SUPERCELL_MATRIX, MESH, GAMMA_CENTER)
101
102save_mesh_data_to_files(MESH_SAVE,
103                        mesh_dict['frequencies_cm'],
104                        mesh_dict['weights'],
105                        mesh_dict['qpoints'],
106                        mesh_dict['group_velocities_ms'])
107
108with h5py.File(f"{MESH_SAVE}.hdf5", "r") as f:
109    frequencies_cm      = np.asarray(f["frequencies_cm"])       # (N_qpts, N_bands) [cm⁻¹]
110    weights             = np.asarray(f["weights"])              # (N_qpts,)
111    qpoints             = np.asarray(f["qpoints"])              # (N_qpts, 3) fractional
112    group_velocities_ms = np.asarray(f["group_velocities_ms"])  # (N_qpts, N_bands, 3) [m/s]
113
114
115plt.figure(figsize=(16, 8))
116
117speed_2d = np.sqrt(
118    np.square(group_velocities_ms).sum(axis=2) / 3
119)
120
121plt.scatter(frequencies_cm, speed_2d, color=Colors[3], s=0.01)
122
123
124plt.xlabel("Frequency [cm^-1]")
125plt.ylabel("Group Velocity [m/s]")
126
127plt.title("Group Velocity of reference crystal")
128
129plt.savefig(f"{WORK_DIR}/crystal_group_velocities.png", dpi=300)
130
131
132#############################################
133# OBTAIN VDOS AND v(w) IN REFERENCE CRYSTAL #
134#############################################
135
136print(f"Obtaining the crystal VDOS")
137
138frequencies_flat, weights_flat, speed_flat, weights_sum = flatten_arrays(
139    frequencies_cm,
140    weights,
141    group_velocities_ms)
142
143vdos_crystal, speed_crystal, freq_crystal = calculate_vdos_and_average_speed_with_frequency(
144    frequencies_flat,
145    weights_flat,
146    speed_flat,
147    GAMMA_BROADENING,
148    CRYSTAL_POSCAR,
149    weights_sum
150)
151
152save_vdos_speed_data_to_files(CRYSTAL_VEL_SAVE, freq_crystal, vdos_crystal, speed_crystal)
153
154
155plt.figure(figsize=(16, 8))
156
157plt.plot(freq_crystal, vdos_crystal, color=Colors[3])
158
159plt.xlabel(r"Frequency [cm$^{-1}$]")
160plt.ylabel(r"VDOS [THz$^{-1}$ nm$^{-3}$]")
161
162plt.title("Vibrational Density of States of reference crystal")
163
164plt.savefig(f"{WORK_DIR}/crystal_vdos.png", dpi=300)
165
166
167
168plt.figure(figsize=(16, 8))
169
170speed_2d = np.sqrt(
171    np.square(group_velocities_ms).sum(axis=2) / 3
172)
173
174plt.scatter(frequencies_cm, speed_2d, color=Colors[0], s=0.01)
175
176# the average of the group velocities at a given frequency
177plt.plot(freq_crystal, speed_crystal, color=Colors[3])
178
179plt.xlabel("Frequency [cm^-1]")
180plt.ylabel("Group Velocity [m/s]")
181
182plt.title("Group Velocity of reference crystal")
183
184plt.savefig(f"{WORK_DIR}/crystal_group_velocities_vs_frequency.png", dpi=300)
185
186
187
188##############################################################
189# VISUALIZE FREQUENCIES AND DIFFUSIVITY IN DISORDERED SYSTEM #
190##############################################################
191
192
193with h5py.File(DISORDERED_FREQUENCIES, "r") as f:
194    print(f.keys())
195    frequencies = np.asarray(f['frequencies'])  # these are already saved in cm^-1
196    weights = np.asarray(f['weights'])
197
198print(f"Obtaining VDOS of the disordered system")
199frequencies_flat, weights_flat, weights_sum = flatten_arrays_freq_only(frequencies, weights)
200
201vdos_disordered, freq_disordered = calculate_vdos_with_frequency(
202    frequencies_flat,
203    weights_flat,
204    GAMMA_BROADENING,
205    DISORDERED_POSCAR,
206    weights_sum
207)
208
209save_vdos_data_to_files(DISORDERED_VDOS_SAVE, freq_disordered, vdos_disordered)
210
211
212
213
214plt.figure(figsize=(16, 8))
215
216plt.plot(freq_disordered, vdos_disordered, color=Colors[0])
217
218plt.xlabel("Frequency [cm$^{-1}$]")
219plt.ylabel("VDOS [THz$^{-1}$ nm$^{-3}$]")
220
221plt.title("Vibrational Density of States of the disordered system")
222
223plt.xticks(np.arange(0, 1800, 100))
224
225plt.savefig(f"{WORK_DIR}/disordered_system_vdos.png", dpi=300)
226
227
228
229plt.figure(figsize=(16, 8))
230
231plt.plot(freq_crystal, vdos_crystal, color=Colors[3], label="reference crystal")
232plt.plot(freq_disordered, vdos_disordered, color=Colors[0], label='disordered system')
233
234plt.xlabel("Frequency [cm^-1]")
235plt.ylabel("VDOS [THz^-1 nm^-3]")
236
237plt.title("Vibrational Densities of States of reference crystal and the disordered system")
238
239plt.xticks(np.arange(0, 2000, 100))
240
241plt.legend(loc='upper left')
242
243plt.savefig(f"{WORK_DIR}/crystal_vs_disordered_system_vdos.png", dpi=300)
244
245
246
247
248
249# read in the diffusivity
250with h5py.File(DISORDERED_DIFFUSIVITY, "r") as f:
251    print(f.keys())
252    diffusivity_plot = np.asarray(f["diffusivity"])  # in m^2/s
253    frequencies_plot_diff = np.asarray(f["frequencies_plot"])  # in cm^-1
254
255
256
257
258plt.figure(figsize=(16, 8))
259
260plt.plot(frequencies_plot_diff, diffusivity_plot*1e6, color=Colors[0])
261
262plt.xlabel("Frequency [cm$^{-1}$]")
263plt.ylabel("Diffusivity [mm$^2$/s]")
264
265plt.title("Diffusivity of modes in the disordered system")
266
267plt.xticks(np.arange(0, 1800, 100))
268
269plt.savefig(f"{WORK_DIR}/disordered_system_diffusivity.png", dpi=300)
270
271
272
273################################################################
274# INFLUENCE OF DENSITY ON FREQUENCY SHIFTS IN THE CRYSTAL VDOS #
275################################################################
276
277
278density_crystal = obtain_density(read(CRYSTAL_POSCAR))
279density_disordered = obtain_density(read(DISORDERED_POSCAR))
280
281density_factor = np.power(density_disordered/density_crystal, 1/3)
282print(f"  ρ_crystal    = {density_crystal:.4f} g/cm³")
283print(f"  ρ_disordered = {density_disordered:.4f} g/cm³")
284print(f"  Density factor = (ρ_dis/ρ_crys)^(1/3) = {density_factor:.4f}")
285
286
287
288# load the mesh data
289with h5py.File(f"{MESH_SAVE}.hdf5", "r") as f:
290    frequencies_cm      = np.asarray(f["frequencies_cm"])       # (N_qpts, N_bands) [cm⁻¹]
291    weights             = np.asarray(f["weights"])              # (N_qpts,)
292    qpoints             = np.asarray(f["qpoints"])              # (N_qpts, 3) fractional
293    group_velocities_ms = np.asarray(f["group_velocities_ms"])  # (N_qpts, N_bands, 3) [m/s]
294
295
296print(f"Applying frequency shifts and recalculating crystal VDOS")
297
298# flatten the arrays for easier VDOS calculation
299frequencies_flat, weights_flat, speed_flat, weights_sum = flatten_arrays(
300    frequencies_cm,
301    weights,
302    group_velocities_ms)
303
304# we shift the frequencies by the multiplicative factor
305shifted_frequencies_flat = frequencies_flat * density_factor
306
307shifted_vdos_crystal, shifted_speed_crystal, shifted_freq_crystal = calculate_vdos_and_average_speed_with_frequency(
308    shifted_frequencies_flat,
309    weights_flat,
310    speed_flat,
311    GAMMA_BROADENING,
312    CRYSTAL_POSCAR,
313    weights_sum
314)
315
316save_vdos_speed_data_to_files(SHIFTED_SAVE, shifted_freq_crystal, shifted_vdos_crystal, shifted_speed_crystal)
317
318
319
320plt.figure(figsize=(16, 8))
321
322plt.plot(shifted_freq_crystal, shifted_vdos_crystal, color=Colors[3])
323
324plt.xlabel("Frequency [cm^-1]")
325plt.ylabel("VDOS [THz^-1 nm^-3]")
326
327plt.title("shifted Vibrational Density of States of the reference crystal")
328
329plt.savefig(f"{WORK_DIR}/crystal_shifted_vdos.png", dpi=300)
330
331
332plt.figure(figsize=(16, 8))
333
334plt.scatter(shifted_frequencies_flat, speed_flat, color=Colors[0], s=0.01)
335plt.plot(shifted_freq_crystal, shifted_speed_crystal, color=Colors[3])
336
337plt.xlabel("Frequency [cm^-1]")
338plt.ylabel("Group Velocity [m/s]")
339
340plt.title("Group Velocity of the reference crystal with shifted frequencies")
341
342plt.savefig(f"{WORK_DIR}/crystal_shifted_group_velocities.png", dpi=300)
343