zstar 0.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
zstar/__init__.py ADDED
@@ -0,0 +1,72 @@
1
+ """
2
+ Zstar: A code for calculations of Born effective charge and dielectric responses.
3
+
4
+ Zstar provides a set of tools for computing Born effective charge, polarization
5
+ and phonon-related properties of materials from first-principles calculations.
6
+ It is designed to simplify and automate data generation, analysis, and post-
7
+ processing for materials scientists and engineers.
8
+
9
+ Typical capabilities include:
10
+ - Pre- and post-processing workflows for polarization analyses.
11
+ - Phonon data generation and post-processing utilities.
12
+ - Structure analysis and symmetry consistency checks.
13
+ - Utilities for handling Wyckoff positions and irreducible representations.
14
+
15
+ The code is intended to interface with common first-principles and lattice
16
+ dynamics frameworks (e.g. ABACUS, Phonopy, PYATB), while keeping the
17
+ Python-side workflows as lightweight and scriptable as possible.
18
+
19
+ Copyright (c) 2025 Zstar Developers.
20
+ Author: Xudong Zhu
21
+ Author email: zhuxudong@ustc.edu.cn
22
+
23
+ This software is released under the GPL v3.0 License.
24
+ See the accompanying LICENSE file for details.
25
+ """
26
+
27
+ # ---------------------------------------------------------------------------
28
+ # Public package metadata
29
+ # ---------------------------------------------------------------------------
30
+
31
+ # NOTE:
32
+ # Keep this version in sync with the version declared in pyproject.toml.
33
+ __version__ = "0.0.2"
34
+
35
+ # ---------------------------------------------------------------------------
36
+ # Public API surface
37
+ # ---------------------------------------------------------------------------
38
+
39
+ # Expose commonly used submodules at the package level so that users can write:
40
+ # import zstar
41
+ # zstar.calc_kappa(...)
42
+ #
43
+ # instead of
44
+ # from zstar import calc_kappa
45
+ # calc_kappa(...)
46
+
47
+ from . import (
48
+ calc_kappa,
49
+ deal_polar,
50
+ gen_polar,
51
+ get_wyckoff,
52
+ group_modesDB,
53
+ phonon_gen,
54
+ phonon_post,
55
+ read_irrep,
56
+ stru_analyzer,
57
+ verify_born_symmetry,
58
+ )
59
+
60
+ __all__ = [
61
+ "__version__",
62
+ "calc_kappa",
63
+ "deal_polar",
64
+ "gen_polar",
65
+ "get_wyckoff",
66
+ "group_modesDB",
67
+ "phonon_gen",
68
+ "phonon_post",
69
+ "read_irrep",
70
+ "stru_analyzer",
71
+ "verify_born_symmetry",
72
+ ]
zstar/calc_kappa.py ADDED
@@ -0,0 +1,439 @@
1
+ import os
2
+ import re
3
+ import math
4
+ import shutil
5
+ import numpy as np
6
+ from . import read_irrep
7
+ from . import get_wyckoff
8
+ import matplotlib.pyplot as plt
9
+ from .stru_analyzer import stru_analyzer, compute_cell_volume_from_returns # 新增导入
10
+
11
+
12
+
13
+ def filter_small_elements(Z, zero_tolerance):
14
+ # 计算矩阵的范数
15
+ norm_Z = np.linalg.norm(Z)
16
+
17
+ # 将相对于矩阵范数很小的元素置零
18
+ Z[np.abs(Z) < zero_tolerance * norm_Z] = 0
19
+
20
+ return Z
21
+
22
+
23
+
24
+ def read_and_extract_matrices(filename):
25
+ with open(filename, "r") as file:
26
+ lines = file.readlines()
27
+
28
+ matrices = [] # 用于存储所有的3x3矩阵
29
+ for line in lines:
30
+ parts = line.strip().split()
31
+ if parts:
32
+ # 检查并移除可选的星号
33
+ if parts[0] == '*':
34
+ parts.pop(0)
35
+
36
+ # 至此,parts[0] 是行号,parts[1] 是原子名称
37
+ # 检查是否为数据行(确保列表有足够的长度)
38
+ if len(parts) > 2 and (parts[0].isdigit() or parts[0].lstrip('-').isdigit()):
39
+ # 移除行号和原子名称
40
+ nums = parts[2:]
41
+ # 确保我们有足够的数字来形成一个3x3矩阵
42
+ if len(nums) >= 9:
43
+ # 提取3x3矩阵的元素
44
+ matrix = [[float(nums[j * 3 + k]) for k in range(3)] for j in range(3)]
45
+ matrices.append(matrix)
46
+
47
+ return matrices
48
+
49
+ def read_masses_from_yaml(filename):
50
+ with open(filename, "r") as file:
51
+ content = file.read()
52
+
53
+ pattern = r"primitive_cell:(.*?)unit_cell:"
54
+ match = re.search(pattern, content, re.DOTALL)
55
+ extracted_text = match.group(1).strip() if match else ""
56
+
57
+ pattern = r"mass:\s+([\d.]+)"
58
+ mass_list = [float(mass) for mass in re.findall(pattern, extracted_text)]
59
+ return mass_list
60
+
61
+ def normalize_eigenvectors(eigenvectors, frequencies, mass_list):
62
+ num_modes, num_atoms, _ = eigenvectors.shape
63
+ mass_sqrt_list = np.sqrt(mass_list)
64
+
65
+ mass_vectors = eigenvectors / mass_sqrt_list[:, np.newaxis]
66
+ norm_factors = np.linalg.norm(mass_vectors, axis=(1, 2), keepdims=True)
67
+ normalized_vectors = mass_vectors / norm_factors
68
+
69
+ return normalized_vectors
70
+
71
+ def read_born_file(born_file_path):
72
+ """
73
+ 读取 BORN 文件并提取第 2 行数据填充到 3x3 的电介质张量矩阵中。
74
+ """
75
+ # 检查文件是否存在
76
+ if not os.path.exists(born_file_path):
77
+ print(f"Error: {born_file_path} does not exist.")
78
+ return None
79
+
80
+ # 初始化 3x3 电介质张量矩阵
81
+ elec_dielectric_tensor = np.zeros((3, 3))
82
+
83
+ try:
84
+ # 打开文件并读取内容
85
+ with open(born_file_path, 'r') as file:
86
+ lines = file.readlines()
87
+
88
+ # 提取第 2 行数据(去掉注释行)
89
+ data_line = lines[1].strip().split()
90
+
91
+ # 检查数据行是否有 9 个数值
92
+ if len(data_line) != 9:
93
+ raise ValueError(f"The second line does not contain 9 values: {data_line}")
94
+
95
+ # 将数据填充到 3x3 电介质张量中
96
+ elec_dielectric_tensor[0, :] = list(map(float, data_line[:3]))
97
+ elec_dielectric_tensor[1, :] = list(map(float, data_line[3:6]))
98
+ elec_dielectric_tensor[2, :] = list(map(float, data_line[6:9]))
99
+
100
+ print("Electronic part of dielectric tensor:")
101
+ print(elec_dielectric_tensor)
102
+
103
+ except Exception as e:
104
+ print(f"Error reading BORN file: {e}")
105
+ return None
106
+
107
+ return elec_dielectric_tensor
108
+
109
+
110
+ def deal_q_vector(
111
+ zero_tolerance: float = 1e-3,
112
+ ir_tolerance: float = 5e-2,
113
+ ir_choose: str = 'ir',
114
+ plot_switch: bool = False,
115
+ mode: str = 'db', # 新增:默认 db
116
+ stru_file: str = 'STRU', # 新增:可指定结构文件
117
+ irreps_file: str = 'irreps.yaml' # 新增:可指定 irreps.yaml
118
+ ):
119
+ # ---- 新增:模式分治(最小改动) ----
120
+ if (mode or 'db').lower() == 'smodes':
121
+ # 沿用你原来的 smodes 体积&活动性获取
122
+ cell_volume, irrep_info = get_wyckoff.get_wyckoff_position(stru_file)
123
+ else:
124
+ # db/default:不再调用 smodes;体积由 stru_analyzer 计算
125
+ a0, lattice_vectors, *_ = stru_analyzer(stru_file)
126
+ cell_volume = compute_cell_volume_from_returns(a0, lattice_vectors)
127
+ irrep_info = None # 交给 process_modes 自动按数据库分类
128
+ # ---- 你的原逻辑从这里继续 ----
129
+
130
+
131
+ fq = 'qpoints.yaml'
132
+ with open(fq, 'r') as file:
133
+ data = file.read()
134
+ # print(data)
135
+ # natom_pattern = r"natom:\s+(\d+)"
136
+ natom_pattern = re.compile(r'natom:\s+([\d.-]+)')
137
+ frequency_pattern = re.compile(r'frequency:\s+([\d.-]+)')
138
+ eigenvector_pattern = re.compile(r'- \[\s*([-.\d]+),\s*([-.\d]+)\s*\]')
139
+ frequencies = []
140
+ eigenvectors = []
141
+ unit_factor_Thz_to_cm = 33.35641
142
+ unit_factor_angle_to_abacus = 21.49068
143
+ unit_factor_angle_to_vasp = 15.633302
144
+ unit_factor_angle_via_abacus_to_cm = 716.856
145
+ unit_factor_angle_via_vasp_to_cm = 521.471
146
+ epsilon_0 = 8.8541878128e-12 #F/m
147
+
148
+ # 查找所有频率和原子矢量
149
+ matches = re.search(natom_pattern, data)
150
+ if matches:
151
+ natom_value = int(matches.group(1))
152
+ # print("natom value:", natom_value)
153
+ # 查找所有频率和原子矢量
154
+ matches = re.findall(frequency_pattern, data)
155
+ for match in matches:
156
+ frequencies.append(float(match))
157
+ matches = re.findall(eigenvector_pattern, data)
158
+ for match in matches:
159
+ #eigenvectors.append([float(match[0]), float(match[1])])
160
+ eigenvectors.append([float(match[0])])
161
+
162
+ # freq_cm = np.zeros( len(frequencies) )
163
+ # for i in range(len(frequencies)):
164
+ # freq_cm[i] = frequencies[i] * unit_factor_Thz_to_cm
165
+
166
+ # 频率单位变成了 meter^-1
167
+ freq_array = np.array(frequencies)
168
+ freq_cm_array = freq_array * unit_factor_Thz_to_cm
169
+ freq_meter_array = freq_array * unit_factor_Thz_to_cm * 100
170
+ freq_s_1_array = freq_cm_array * 1e12 #* 2 * 3.141592653
171
+
172
+ # 主逻辑
173
+ eigenvectors = np.array(eigenvectors).reshape((len(frequencies), natom_value, 3))
174
+ mass_list = read_masses_from_yaml("phonopy_disp.yaml")
175
+ print("提取的 mass 列表:", mass_list)
176
+ mass_sqrt_list = [math.sqrt(float(mass)) for mass in mass_list]
177
+ print("原子质量的平方根列表:", mass_sqrt_list)
178
+
179
+ normalized_vectors = normalize_eigenvectors(eigenvectors, frequencies, mass_list)
180
+
181
+ # 打印和验证结果
182
+ for i, (freq, vectors) in enumerate(zip(frequencies, normalized_vectors)):
183
+ print(f"Frequency {i+1} : {freq}")
184
+ for j, vector in enumerate(vectors):
185
+ print(f"Atom {j+1} - Vector: {vector}")
186
+ print(f"Sum of vector squared: {np.sum(vectors ** 2)}")
187
+
188
+ # 使用函数读取文件并提取矩阵,之后修改为 rpolar 传递值过来
189
+ # born = read_and_extract_matrices("Z-BORN-all.out")
190
+ born_candidates = ["Z-BORN-all.out", "Z-BORN-symm.out"]
191
+ existing_files = [f for f in born_candidates if os.path.exists(f)]
192
+ if not existing_files:
193
+ raise FileNotFoundError("当前目录下未找到 Z-BORN-all.out 或 Z-BORN-symm.out")
194
+ latest_born_file = max(existing_files, key=os.path.getmtime)
195
+
196
+ born = read_and_extract_matrices(latest_born_file)
197
+
198
+ # 打印结果
199
+ for i, matrix in enumerate(born):
200
+ print(f"Matrix {i + 1}:")
201
+ for row in matrix:
202
+ print(row)
203
+ print()
204
+
205
+ # result is mode effective charge
206
+ result = np.zeros((len(frequencies), 3, 1))
207
+ for i in range(len(frequencies)):
208
+ # for i in range(3):
209
+ for j in range(0, natom_value):
210
+ # print(f"Result is:", result)
211
+ transpose_vector = eigenvectors[i][j][:, np.newaxis]
212
+ # transpose_vector = new_norm_vertors[i][j][:, np.newaxis]
213
+ vector_charge = np.dot(born[j], transpose_vector) / mass_sqrt_list[j]
214
+ # print(f"Vector Charge {j+1} with mass_sqrt {mass_sqrt_list[j]} is:", vector_charge)
215
+ result[i] += vector_charge
216
+ mode_vector_length = np.linalg.norm(result[i])
217
+ print(f"Frequency {i+1} : {freq_cm_array[i]} cm^-1 mode has [mode effective charge]: {mode_vector_length}\n{result[i]}")
218
+ # print(f"Final Result is:", result)
219
+
220
+ print(freq_cm_array)
221
+ print(freq_meter_array)
222
+
223
+ # cell_volume, irrep_info = get_wyckoff.get_wyckoff_position('STRU')
224
+ # 单位 A^3
225
+ # A = 1e-10 meter, cm = 1e-2 meter, 相差 1e-8
226
+ cell_volume_m3 = cell_volume * 1e-30
227
+ omega_square = 4 * 3.141592653 * (1.602176634 * 10 ** -19) ** 2 / (1.66053907 * 10 ** -27) / epsilon_0
228
+ omega_collect_cm = 4.7412637842196854e-23
229
+ omega_collect_s = 0.04261235257138875
230
+ constant = (1.602176634 * 1e-19) ** 2 / (1.66053907 * 1e-27) / epsilon_0 / (( 2 * 3.141592653 * 3 * 1e10 ) ** 2 )
231
+ print(f"omega_square {omega_square}")
232
+ print(f"cell_volume_cm3 {cell_volume_m3}")
233
+ # indices_to_process = [ 5, 6, 9, 10, 11, ]
234
+ # mode_band_indices_flat, all_bands_combined = read_irrep.process_modes(irrep_info, read_irrep.read_irreps_yaml())
235
+ ir_mode_band_indices_flat, ir_bands_combined, raman_mode_band_indices_flat, raman_bands_combined = \
236
+ read_irrep.process_modes(irrep_info, read_irrep.read_irreps_yaml(irreps_file))
237
+
238
+ mode_band_indices_flat = ir_mode_band_indices_flat
239
+ if ir_choose == 'ir':
240
+ indices_IR = ir_bands_combined # [4, 5, 11, 12, 17, 18, 8, 15 , 9] # [1, 2, 6, 7, 10, 13, 14, 16]
241
+ else:
242
+ print(f"Choose all indexes: {len(frequencies)} ")
243
+ indices_IR = list(range(1, len(frequencies) + 1))
244
+ print(f"IR indexes: {indices_IR} ")
245
+ dielectric_tensor = np.zeros((3, 3))
246
+ elec_dielectric_tensor = np.zeros((3, 3))
247
+ for index_temp in indices_IR:
248
+ i = index_temp - 1
249
+ if i >= 0 and i < len(freq_meter_array):
250
+ vector_temp = np.array(result[i])
251
+ freq_temp = freq_cm_array[i]
252
+ vector_transpose = vector_temp.reshape(1, 3)
253
+ result_matrix = np.dot(vector_temp, vector_transpose) / (freq_temp ** 2)
254
+ print(f"index: {index_temp} Result for i= {i}:\n", result_matrix)
255
+ dielectric_tensor += result_matrix
256
+ else:
257
+ print(f"Index {i} is out of range.")
258
+
259
+ if plot_switch:
260
+ # 参数设置
261
+ omega_freq = np.linspace(0, 800, 800) # 自变量频率 (cm^-1)
262
+ gamma = 60 # 阻尼因子 (cm^-1)
263
+
264
+ # 初始化总介电函数张量
265
+ epsilon_omega_real = np.zeros((3, 3, len(omega_freq)))
266
+ epsilon_omega_imag = np.zeros((3, 3, len(omega_freq)))
267
+
268
+ # 遍历每个模式并计算频率依赖的介电函数
269
+ for index_temp in indices_IR:
270
+ i = index_temp - 1
271
+ if i >= 0 and i < len(freq_cm_array):
272
+ # 模式的张量分量和特征频率
273
+ vector_temp = np.array(result[i])
274
+ freq_temp = freq_cm_array[i]
275
+
276
+ # 计算模式电荷平方
277
+ mode_charge_square = np.dot(vector_temp, vector_temp.T)
278
+
279
+ # 计算频率依赖的介电函数 (实部和虚部)
280
+ for j, omega in enumerate(omega_freq):
281
+ denominator = freq_temp**2 - omega**2 - 1j * gamma * omega
282
+ epsilon_temp = mode_charge_square / denominator
283
+ epsilon_omega_real[:, :, j] += np.real(epsilon_temp)
284
+ epsilon_omega_imag[:, :, j] += np.imag(epsilon_temp)
285
+ else:
286
+ print(f"Index {i} is out of range.")
287
+
288
+ epsilon_omega_real = epsilon_omega_real * constant / cell_volume_m3
289
+ epsilon_omega_imag = epsilon_omega_imag * constant / cell_volume_m3
290
+
291
+ # 保存实部数据
292
+ real_filename = "ph_dielectric_function_with_omega_real.dat"
293
+ with open(real_filename, "w") as real_file:
294
+ real_file.write("# Frequency(cm^-1) xx xy xz yx yy yz zx zy zz\n")
295
+ for k, omega in enumerate(omega_freq):
296
+ real_file.write(f"{omega:.6f} ")
297
+ for i in range(3):
298
+ for j in range(3):
299
+ real_file.write(f"{epsilon_omega_real[i, j, k]:.6e} ")
300
+ real_file.write("\n")
301
+ print(f"Saved real part data to {real_filename}")
302
+
303
+ # 保存虚部数据
304
+ imag_filename = "ph_dielectric_function_with_omega_imag.dat"
305
+ with open(imag_filename, "w") as imag_file:
306
+ imag_file.write("# Frequency(cm^-1) xx xy xz yx yy yz zx zy zz\n")
307
+ for k, omega in enumerate(omega_freq):
308
+ imag_file.write(f"{omega:.6f} ")
309
+ for i in range(3):
310
+ for j in range(3):
311
+ imag_file.write(f"{epsilon_omega_imag[i, j, k]:.6e} ")
312
+ imag_file.write("\n")
313
+ print(f"Saved imaginary part data to {imag_filename}")
314
+
315
+ # 绘制两套图
316
+ labels = [["xx", "xy", "xz"], ["yx", "yy", "yz"], ["zx", "zy", "zz"]]
317
+
318
+ # 单位转换:从 cm^-1 到 GHz THz
319
+ # omega_freq_ghz = omega_freq * 29.9792458 / 1000 # 1 cm^-1 = 29.9792458 GHz
320
+ # 常数定义
321
+ speed_of_light = 299792458 # 光速 m/s
322
+
323
+ # 单位转换:从 cm^-1 到 Hz
324
+ omega_freq_hz = omega_freq * 100 * speed_of_light # omega_freq 为 cm^-1
325
+ omega_freq_mhz = omega_freq_hz / 1e6 # 1 MHz = 10^6 Hz
326
+ omega_freq_ghz = omega_freq_hz / 1e9 # 1 GHz = 10^9 Hz
327
+ omega_freq_thz = omega_freq_hz / 1e12 # 1 THz = 10^12 Hz
328
+
329
+ if os.path.exists("./figures"):
330
+ shutil.rmtree("./figures")
331
+ os.mkdir("./figures")
332
+ # 绘制并保存图像
333
+ for i in range(3):
334
+ for j in range(3):
335
+ # 获取当前脚标
336
+ label = labels[i][j]
337
+
338
+ # 第一套图:横轴为 cm^-1
339
+ plt.figure(figsize=(8, 6))
340
+ plt.plot(omega_freq, epsilon_omega_real[i, j, :], label=f"Real $\\mathrm{{Re}}[\\epsilon_{{ph}}]$[{label}]", color='blue')
341
+ plt.plot(omega_freq, epsilon_omega_imag[i, j, :], label=f"Imag $\\mathrm{{Im}}[\\epsilon_{{ph}}]$[{label}]", color='red')
342
+ plt.xlabel("Frequency (cm$^{-1}$)", fontsize=12)
343
+ plt.ylabel(f"Phonon Dielectric Function $\\epsilon_{{ph}}$[{label}]", fontsize=12)
344
+ plt.title(f"Phonon Dielectric Function $\\epsilon_{{ph}}$[{label}] vs Frequency (cm$^{-1}$)", fontsize=14)
345
+ plt.legend(fontsize=10)
346
+ plt.grid()
347
+ plt.xlim(min(omega_freq), max(omega_freq)) # 设置 x 轴范围
348
+ filename_cm1 = f"./figures/epsilon_ph_{label}_cm-1.png"
349
+ plt.savefig(filename_cm1, dpi=300)
350
+ plt.close()
351
+ print(f"Saved plot to {filename_cm1}")
352
+
353
+ # 第二套图:横轴为 THz
354
+ plt.figure(figsize=(8, 6))
355
+ plt.plot(omega_freq_thz, epsilon_omega_real[i, j, :], label=f"Real $\\mathrm{{Re}}[\\epsilon_{{ph}}]$[{label}]", color='blue')
356
+ plt.plot(omega_freq_thz, epsilon_omega_imag[i, j, :], label=f"Imag $\\mathrm{{Im}}[\\epsilon_{{ph}}]$[{label}]", color='red')
357
+ plt.xlabel("Frequency (THz)", fontsize=12)
358
+ plt.ylabel(f"Phonon Dielectric Function $\\epsilon_{{ph}}$[{label}]", fontsize=12)
359
+ plt.title(f"Phonon Dielectric Function $\\epsilon_{{ph}}$[{label}] vs Frequency (THz)", fontsize=14)
360
+ plt.legend(fontsize=10)
361
+ plt.xlim(min(omega_freq_thz), max(omega_freq_thz)) # 设置 x 轴范围
362
+ plt.grid()
363
+ filename_thz = f"./figures/THz_epsilon_ph_{label}.png"
364
+ plt.savefig(filename_thz, dpi=300)
365
+ plt.close()
366
+ print(f"Saved plot to {filename_thz}")
367
+
368
+ # 第三套图:横轴为 GHz
369
+ plt.figure(figsize=(8, 6))
370
+ plt.plot(omega_freq_ghz, epsilon_omega_real[i, j, :], label=f"Real $\\mathrm{{Re}}[\\epsilon_{{ph}}]$[{label}]", color='blue')
371
+ plt.plot(omega_freq_ghz, epsilon_omega_imag[i, j, :], label=f"Imag $\\mathrm{{Im}}[\\epsilon_{{ph}}]$[{label}]", color='red')
372
+ plt.xlabel("Frequency (GHz)", fontsize=12)
373
+ plt.ylabel(f"Phonon Dielectric Function $\\epsilon_{{ph}}$[{label}]", fontsize=12)
374
+ plt.title(f"Phonon Dielectric Function $\\epsilon_{{ph}}$[{label}] vs Frequency (GHz)", fontsize=14)
375
+ plt.legend(fontsize=10)
376
+ plt.xlim(min(omega_freq_ghz), 800) # 设置 x 轴范围
377
+ plt.grid()
378
+ filename_ghz = f"./figures/GHz_epsilon_ph_{label}.png"
379
+ plt.savefig(filename_ghz, dpi=300)
380
+ plt.close()
381
+ print(f"Saved plot to {filename_ghz}")
382
+
383
+ # 第四套图:横轴为 MHz
384
+ plt.figure(figsize=(8, 6))
385
+ plt.plot(omega_freq_mhz, epsilon_omega_real[i, j, :], label=f"Real $\\mathrm{{Re}}[\\epsilon_{{ph}}]$[{label}]", color='blue')
386
+ plt.plot(omega_freq_mhz, epsilon_omega_imag[i, j, :], label=f"Imag $\\mathrm{{Im}}[\\epsilon_{{ph}}]$[{label}]", color='red')
387
+ plt.xlabel("Frequency (MHz)", fontsize=12)
388
+ plt.ylabel(f"Phonon Dielectric Function $\\epsilon_{{ph}}$[{label}]", fontsize=12)
389
+ plt.title(f"Phonon Dielectric Function $\\epsilon_{{ph}}$[{label}] vs Frequency (MHz)", fontsize=14)
390
+ plt.legend(fontsize=10)
391
+ plt.xlim(min(omega_freq_mhz), 2000) # 设置 x 轴范围
392
+ plt.grid()
393
+ filename_mhz = f"./figures/MHz_epsilon_ph_{label}.png"
394
+ plt.savefig(filename_mhz, dpi=300)
395
+ plt.close()
396
+ print(f"Saved plot to {filename_mhz}")
397
+
398
+ # 检查 indices_IR 是否为空
399
+ if not indices_IR:
400
+ dielectric_tensor = np.zeros((3, 3))
401
+ # 计算 result 的范数
402
+ norm_result = np.linalg.norm(np.array(result))
403
+ print(f"NORM of whole result is {norm_result}")
404
+ # 遍历 result 数组
405
+ for i in range(len(result)):
406
+ # 检查 result[i] 相对于整体范数是否小于 zero_tolerance
407
+ if abs(np.linalg.norm(result[i])) / norm_result < ir_tolerance:
408
+ print(f"Freq [{i+1}] norm = {np.linalg.norm(result[i])} is smaller than the tolerance {ir_tolerance}.")
409
+ # 可以在这里做进一步处理,如跳过当前元素
410
+ continue
411
+ # 如果 result[i] 大于或等于容忍度,可以在这里处理
412
+ # 例如打印或其他逻辑
413
+ print(f"PICK this result[{i+1}] norm = {np.linalg.norm(result[i])} is PICKed.")
414
+ if i >= 0 and i < len(freq_meter_array):
415
+ vector_temp = np.array(result[i])
416
+ freq_temp = freq_cm_array[i]
417
+ vector_transpose = vector_temp.reshape(1, 3)
418
+ result_matrix = np.dot(vector_temp, vector_transpose) / (freq_temp ** 2)
419
+ print(f"Result for index {i+1}:\n", result_matrix)
420
+ dielectric_tensor += result_matrix
421
+ else:
422
+ print(f"Index {i} is out of range.")
423
+
424
+ print(f"Phonon Dielectric tensor is:\n{dielectric_tensor}")
425
+ dielectric_tensor = dielectric_tensor * constant / cell_volume_m3
426
+ dielectric_tensor = filter_small_elements(dielectric_tensor, zero_tolerance)
427
+ print(f"Phonon Dielectric tensor in SI unit epsilon_0 without numerical error:\n{dielectric_tensor}")
428
+
429
+ born_file_path = "BORN" # 替换为实际的 BORN 文件路径
430
+ if os.path.exists(born_file_path): # 检查 BORN 文件是否存在
431
+ elec_dielectric_tensor = read_born_file(born_file_path)
432
+ if elec_dielectric_tensor is not None: # 确保读取成功
433
+ total_dielectric_tensor = dielectric_tensor + elec_dielectric_tensor
434
+ print(f"Total Dielectric tensor in SI unit epsilon_0 without numerical error:\n{total_dielectric_tensor}")
435
+
436
+ return 0
437
+
438
+ if __name__ == "__main__":
439
+ deal_q_vector()