ion-CSP 2.0.8__py3-none-any.whl → 2.1.3__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.
- ion_CSP/__init__.py +16 -5
- ion_CSP/log_and_time.py +10 -4
- ion_CSP/read_mlp_density.py +2 -2
- ion_CSP/task_manager.py +15 -4
- ion_CSP/vasp_processing.py +382 -15
- {ion_csp-2.0.8.dist-info → ion_csp-2.1.3.dist-info}/METADATA +19 -11
- ion_csp-2.1.3.dist-info/RECORD +28 -0
- {ion_csp-2.0.8.dist-info → ion_csp-2.1.3.dist-info}/WHEEL +1 -1
- {ion_csp-2.0.8.dist-info → ion_csp-2.1.3.dist-info}/top_level.txt +1 -0
- run/__init__.py +8 -0
- run/main_CSP.py +133 -0
- run/main_EE.py +133 -0
- run/run_convert_SMILES.py +49 -0
- run/run_empirical_estimate.py +55 -0
- run/run_gen_opt.py +65 -0
- run/run_read_mlp_density.py +42 -0
- run/run_upload_download.py +66 -0
- run/run_vasp_processing.py +42 -0
- ion_csp-2.0.8.dist-info/RECORD +0 -19
- {ion_csp-2.0.8.dist-info → ion_csp-2.1.3.dist-info}/entry_points.txt +0 -0
- {ion_csp-2.0.8.dist-info → ion_csp-2.1.3.dist-info}/licenses/LICENSE +0 -0
ion_CSP/__init__.py
CHANGED
@@ -1,8 +1,19 @@
|
|
1
|
-
|
1
|
+
__author__ = "Ze Yang"
|
2
|
+
__contact__ = "yangze1995007@163.com"
|
3
|
+
__license__ = "MIT"
|
4
|
+
__version__ = "2.1.3"
|
5
|
+
__date__ = "2025-06-11"
|
6
|
+
|
2
7
|
|
3
8
|
try:
|
4
|
-
|
5
|
-
except
|
6
|
-
|
9
|
+
from importlib.metadata import version # python >= 3.11
|
10
|
+
except Exception:
|
11
|
+
try:
|
12
|
+
from importlib_metadata import version
|
13
|
+
except Exception:
|
14
|
+
pass
|
7
15
|
|
8
|
-
|
16
|
+
try:
|
17
|
+
__version__ = version("ion_CSP")
|
18
|
+
except Exception:
|
19
|
+
pass
|
ion_CSP/log_and_time.py
CHANGED
@@ -3,17 +3,24 @@ import sys
|
|
3
3
|
import time
|
4
4
|
import yaml
|
5
5
|
import signal
|
6
|
+
import inspect
|
6
7
|
import logging
|
7
8
|
import argparse
|
9
|
+
import functools
|
8
10
|
from dpdispatcher.dlog import dlog
|
9
11
|
|
10
12
|
|
11
13
|
def log_and_time(func):
|
12
14
|
"""Decorator for recording log information and script runtime"""
|
13
|
-
|
14
|
-
def wrapper(
|
15
|
+
@functools.wraps(func)
|
16
|
+
def wrapper(work_dir, *args, **kwargs):
|
17
|
+
# 使用inspect获取真实脚本文件名
|
18
|
+
module = inspect.getmodule(func)
|
19
|
+
script_path = module.__file__ if module else __file__
|
20
|
+
script_name = os.path.splitext(os.path.basename(script_path))[0]
|
15
21
|
# 获取脚本所在目录, 在该目录下生成日志
|
16
22
|
log_file_path = os.path.join(work_dir, f"{script_name}_output.log")
|
23
|
+
print(f"Log file path: {log_file_path}")
|
17
24
|
# 配置日志记录
|
18
25
|
logging.basicConfig(
|
19
26
|
filename=log_file_path, # 日志文件名
|
@@ -32,7 +39,7 @@ def log_and_time(func):
|
|
32
39
|
logging.error(f"Error occurred: {e}", exc_info=True)
|
33
40
|
raise
|
34
41
|
print(
|
35
|
-
f"The script {script_name} has run successfully, and the output content has been recorded in the
|
42
|
+
f"The script {script_name} has run successfully, and the output content has been recorded in the {script_name}_output.log file in the same directory."
|
36
43
|
)
|
37
44
|
# 获取程序结束时的CPU时间和Wall Clock时间
|
38
45
|
end_cpu, end_clock = time.process_time(), time.perf_counter()
|
@@ -43,7 +50,6 @@ def log_and_time(func):
|
|
43
50
|
f"End running: {script_name}\nWall time: {wall_time:.4f} sec, CPU time: {cpu_time:.4f} sec\n"
|
44
51
|
)
|
45
52
|
return result
|
46
|
-
|
47
53
|
return wrapper
|
48
54
|
|
49
55
|
|
ion_CSP/read_mlp_density.py
CHANGED
@@ -81,8 +81,8 @@ class ReadMlpDensity:
|
|
81
81
|
os.makedirs(self.max_density_dir, exist_ok=True)
|
82
82
|
for density, CONTCAR_filename in sorted_filename[:n_screen]:
|
83
83
|
# 生成新的包含密度值的文件名,并重命名文件
|
84
|
-
# 密度转换为字符串,保留
|
85
|
-
density_str = f'{density:.
|
84
|
+
# 密度转换为字符串,保留3位小数
|
85
|
+
density_str = f'{density:.3f}'
|
86
86
|
mlp_densities.append(density_str)
|
87
87
|
# 保留 CONTCAR 的序数信息,方便回推检查
|
88
88
|
number = CONTCAR_filename.split("_")[1]
|
ion_CSP/task_manager.py
CHANGED
@@ -32,7 +32,7 @@ class TaskManager:
|
|
32
32
|
# 使用 importlib 动态加载
|
33
33
|
spec = importlib.util.spec_from_file_location(
|
34
34
|
"ion_CSP",
|
35
|
-
os.path.join(self.project_root, "src/__init__.py")
|
35
|
+
os.path.join(self.project_root, "src/ion_CSP/__init__.py")
|
36
36
|
)
|
37
37
|
module = importlib.util.module_from_spec(spec)
|
38
38
|
spec.loader.exec_module(module)
|
@@ -47,6 +47,9 @@ class TaskManager:
|
|
47
47
|
self.env = "DOCKER"
|
48
48
|
self.workspace = Path("/app")
|
49
49
|
self.log_dir = Path("/app/logs")
|
50
|
+
conda_env = os.getenv("CONDA_DEFAULT_ENV")
|
51
|
+
env_msg = conda_env if conda_env else "Not Conda Env"
|
52
|
+
self.envs = f"{self.env} ({env_msg})"
|
50
53
|
self.workspace.mkdir(exist_ok=True)
|
51
54
|
|
52
55
|
def _setup_logging(self):
|
@@ -224,8 +227,13 @@ class TaskManager:
|
|
224
227
|
console_log = work_dir / f"main_{module}_console.log"
|
225
228
|
pid_file = work_dir / "pid.txt"
|
226
229
|
|
230
|
+
# 动态加载模块
|
231
|
+
module_name = f"run.main_{module}"
|
232
|
+
spec = importlib.util.find_spec(module_name)
|
233
|
+
if not spec:
|
234
|
+
raise ImportError(f"Module {module_name} not found")
|
227
235
|
# 启动子进程
|
228
|
-
cmd = [
|
236
|
+
cmd = [sys.executable, "-m", module_name, str(work_dir)]
|
229
237
|
|
230
238
|
with open(console_log, "w") as f:
|
231
239
|
process = subprocess.Popen(
|
@@ -245,7 +253,7 @@ class TaskManager:
|
|
245
253
|
process.terminate()
|
246
254
|
return
|
247
255
|
# 创建符号链接
|
248
|
-
output_log = work_dir / f"main_{module}.
|
256
|
+
output_log = work_dir / f"main_{module}_output.log"
|
249
257
|
print(f"Original log file: {output_log}")
|
250
258
|
std_log = Path(self.log_dir) / f"{module}_{process.pid}.log"
|
251
259
|
try:
|
@@ -271,6 +279,9 @@ class TaskManager:
|
|
271
279
|
continue
|
272
280
|
try:
|
273
281
|
file_path = log_file.resolve(strict=True)
|
282
|
+
if not os.path.exists(file_path):
|
283
|
+
os.remove(log_file)
|
284
|
+
continue
|
274
285
|
mtime = file_path.stat().st_mtime
|
275
286
|
log_tasks.append({
|
276
287
|
"pid": 0, # 日志无PID
|
@@ -358,7 +369,7 @@ class TaskManager:
|
|
358
369
|
os.system("clear" if os.name == "posix" else "cls")
|
359
370
|
print("========== Task Execution Sys1tem ==========")
|
360
371
|
print(f"Current Version: {self.version}")
|
361
|
-
print(f"Current Environment: {self.
|
372
|
+
print(f"Current Environment: {self.envs}")
|
362
373
|
print(f"Current Directory: {self.workspace}")
|
363
374
|
print(f"Log Base Directory: {self.log_dir}")
|
364
375
|
print("=" * 50)
|
ion_CSP/vasp_processing.py
CHANGED
@@ -20,7 +20,7 @@ class VaspProcessing:
|
|
20
20
|
self.vasp_optimized_dir = f"{work_dir}/4_vasp_optimized"
|
21
21
|
self.param_dir = os.path.join(os.path.dirname(__file__), "../../param")
|
22
22
|
|
23
|
-
def
|
23
|
+
def dpdisp_vasp_optimization_tasks(
|
24
24
|
self,
|
25
25
|
machine: str,
|
26
26
|
resources: str,
|
@@ -72,7 +72,7 @@ class VaspProcessing:
|
|
72
72
|
"POTCAR_C",
|
73
73
|
"POTCAR_N",
|
74
74
|
"POTCAR_O",
|
75
|
-
"
|
75
|
+
"sub_ori.sh",
|
76
76
|
]
|
77
77
|
backward_files = ["log", "err"]
|
78
78
|
# 将所有参数文件各复制一份到每个 task_dir 目录下
|
@@ -93,7 +93,7 @@ class VaspProcessing:
|
|
93
93
|
)
|
94
94
|
|
95
95
|
remote_task_dir = f"{parent}pop{pop}"
|
96
|
-
command = "chmod +x
|
96
|
+
command = "chmod +x sub_ori.sh && ./sub_ori.sh"
|
97
97
|
task = Task(
|
98
98
|
command=command,
|
99
99
|
task_work_path=remote_task_dir,
|
@@ -139,7 +139,240 @@ class VaspProcessing:
|
|
139
139
|
shutil.rmtree(os.path.join(self.for_vasp_opt_dir, parent))
|
140
140
|
logging.info("Batch VASP optimization completed!!!")
|
141
141
|
|
142
|
-
def
|
142
|
+
def dpdisp_vasp_relaxation_tasks(
|
143
|
+
self,
|
144
|
+
machine: str,
|
145
|
+
resources: str,
|
146
|
+
nodes: int = 1,
|
147
|
+
):
|
148
|
+
"""
|
149
|
+
Based on the dpdispatcher module, prepare and submit files for optimization on remote server or local machine.
|
150
|
+
"""
|
151
|
+
# 调整工作目录,减少错误发生
|
152
|
+
os.chdir(self.vasp_optimized_dir)
|
153
|
+
# 读取machine.json和resources.json的参数
|
154
|
+
if machine.endswith(".json"):
|
155
|
+
machine = Machine.load_from_json(machine)
|
156
|
+
elif machine.endswith(".yaml"):
|
157
|
+
machine = Machine.load_from_yaml(machine)
|
158
|
+
else:
|
159
|
+
raise KeyError("Not supported machine file type")
|
160
|
+
if resources.endswith(".json"):
|
161
|
+
resources = Resources.load_from_json(resources)
|
162
|
+
elif resources.endswith(".yaml"):
|
163
|
+
resources = Resources.load_from_yaml(resources)
|
164
|
+
else:
|
165
|
+
raise KeyError("Not supported resources file type")
|
166
|
+
# 由于dpdispatcher对于远程服务器以及本地运行的forward_common_files的默认存放位置不同,因此需要预先进行判断,从而不改动优化脚本
|
167
|
+
machine_inform = machine.serialize()
|
168
|
+
if machine_inform["context_type"] == "SSHContext":
|
169
|
+
# 如果调用远程服务器,则创建二级目录
|
170
|
+
parent = "data/"
|
171
|
+
elif machine_inform["context_type"] == "LocalContext":
|
172
|
+
# 如果在本地运行作业,则只在后续创建一级目录
|
173
|
+
parent = ""
|
174
|
+
|
175
|
+
# 获取dir文件夹中所有以prefix_name开头的文件,在此实例中为POSCAR_
|
176
|
+
vasp_optimized_folders = [
|
177
|
+
f
|
178
|
+
for f in os.listdir(self.vasp_optimized_dir)
|
179
|
+
if os.path.isdir(f) and f != "data"
|
180
|
+
]
|
181
|
+
# 创建一个嵌套列表来存储每个节点的任务并将文件平均依次分配给每个节点
|
182
|
+
# 例如:对于10个结构文件任务分发给4个节点的情况,则4个节点领到的任务分别[0, 4, 8], [1, 5, 9], [2, 6], [3, 7]
|
183
|
+
node_jobs = [[] for _ in range(nodes)]
|
184
|
+
for index, file in enumerate(vasp_optimized_folders):
|
185
|
+
node_index = index % nodes
|
186
|
+
node_jobs[node_index].append(index)
|
187
|
+
task_list = []
|
188
|
+
for pop in range(nodes):
|
189
|
+
forward_files = [
|
190
|
+
"INCAR_3",
|
191
|
+
"POTCAR_H",
|
192
|
+
"POTCAR_C",
|
193
|
+
"POTCAR_N",
|
194
|
+
"POTCAR_O",
|
195
|
+
"sub_supple.sh",
|
196
|
+
]
|
197
|
+
backward_files = ["log", "err"]
|
198
|
+
# 将所有参数文件各复制一份到每个 task_dir 目录下
|
199
|
+
task_dir = os.path.join(self.vasp_optimized_dir, f"{parent}pop{pop}")
|
200
|
+
os.makedirs(task_dir, exist_ok=True)
|
201
|
+
for file in forward_files:
|
202
|
+
shutil.copyfile(f"{self.param_dir}/{file}", f"{task_dir}/{file}")
|
203
|
+
for job_i in node_jobs[pop]:
|
204
|
+
# 将分配好的POSCAR文件添加到对应的上传文件中
|
205
|
+
vasp_dir = vasp_optimized_folders[job_i]
|
206
|
+
fine_optimized_file = f"{vasp_dir}/fine/CONTCAR"
|
207
|
+
forward_files.append(fine_optimized_file)
|
208
|
+
os.makedirs(
|
209
|
+
os.path.dirname(f"{task_dir}/{fine_optimized_file}"), exist_ok=True
|
210
|
+
)
|
211
|
+
shutil.copyfile(
|
212
|
+
f"{self.vasp_optimized_dir}/{fine_optimized_file}",
|
213
|
+
f"{task_dir}/{fine_optimized_file}",
|
214
|
+
)
|
215
|
+
# 每个POSCAR文件在优化后都取回对应的CONTCAR和OUTCAR输出文件
|
216
|
+
backward_files.append(f"{vasp_dir}/*")
|
217
|
+
backward_files.append(f"{vasp_dir}/fine/*")
|
218
|
+
backward_files.append(f"{vasp_dir}/fine/final/*")
|
219
|
+
|
220
|
+
remote_task_dir = f"{parent}pop{pop}"
|
221
|
+
command = "chmod +x sub_supple.sh && ./sub_supple.sh"
|
222
|
+
task = Task(
|
223
|
+
command=command,
|
224
|
+
task_work_path=remote_task_dir,
|
225
|
+
forward_files=forward_files,
|
226
|
+
backward_files=backward_files,
|
227
|
+
)
|
228
|
+
task_list.append(task)
|
229
|
+
|
230
|
+
submission = Submission(
|
231
|
+
work_base=self.vasp_optimized_dir,
|
232
|
+
machine=machine,
|
233
|
+
resources=resources,
|
234
|
+
task_list=task_list,
|
235
|
+
)
|
236
|
+
submission.run_submission()
|
237
|
+
|
238
|
+
for pop in range(nodes):
|
239
|
+
# 从传回的 pop 文件夹中将结果文件取到 4_vasp_optimized 目录
|
240
|
+
task_dir = os.path.join(self.vasp_optimized_dir, f"{parent}pop{pop}")
|
241
|
+
for job_i in node_jobs[pop]:
|
242
|
+
vasp_dir = vasp_optimized_folders[job_i]
|
243
|
+
shutil.copytree(
|
244
|
+
f"{task_dir}/{vasp_dir}/fine/final",
|
245
|
+
f"{self.vasp_optimized_dir}/{vasp_dir}/fine/final",
|
246
|
+
)
|
247
|
+
# 在成功完成 VASP 分步优化后,删除 4_vasp_optimized /{parent}/pop{n} 文件夹以节省空间
|
248
|
+
shutil.rmtree(task_dir)
|
249
|
+
if machine_inform["context_type"] == "SSHContext":
|
250
|
+
# 如果调用远程服务器,则删除data级目录
|
251
|
+
shutil.rmtree(os.path.join(self.vasp_optimized_dir, parent))
|
252
|
+
logging.info("Batch VASP optimization completed!!!")
|
253
|
+
|
254
|
+
def dpdisp_vasp_complete_tasks(
|
255
|
+
self,
|
256
|
+
machine: str,
|
257
|
+
resources: str,
|
258
|
+
nodes: int = 1,
|
259
|
+
):
|
260
|
+
"""
|
261
|
+
Based on the dpdispatcher module, prepare and submit files for optimization on remote server or local machine.
|
262
|
+
"""
|
263
|
+
# 调整工作目录,减少错误发生
|
264
|
+
os.chdir(self.for_vasp_opt_dir)
|
265
|
+
# 读取machine.json和resources.json的参数
|
266
|
+
if machine.endswith(".json"):
|
267
|
+
machine = Machine.load_from_json(machine)
|
268
|
+
elif machine.endswith(".yaml"):
|
269
|
+
machine = Machine.load_from_yaml(machine)
|
270
|
+
else:
|
271
|
+
raise KeyError("Not supported machine file type")
|
272
|
+
if resources.endswith(".json"):
|
273
|
+
resources = Resources.load_from_json(resources)
|
274
|
+
elif resources.endswith(".yaml"):
|
275
|
+
resources = Resources.load_from_yaml(resources)
|
276
|
+
else:
|
277
|
+
raise KeyError("Not supported resources file type")
|
278
|
+
# 由于dpdispatcher对于远程服务器以及本地运行的forward_common_files的默认存放位置不同,因此需要预先进行判断,从而不改动优化脚本
|
279
|
+
machine_inform = machine.serialize()
|
280
|
+
if machine_inform["context_type"] == "SSHContext":
|
281
|
+
# 如果调用远程服务器,则创建二级目录
|
282
|
+
parent = "data/"
|
283
|
+
elif machine_inform["context_type"] == "LocalContext":
|
284
|
+
# 如果在本地运行作业,则只在后续创建一级目录
|
285
|
+
parent = ""
|
286
|
+
|
287
|
+
# 获取dir文件夹中所有以prefix_name开头的文件,在此实例中为POSCAR_
|
288
|
+
mlp_contcar_files = [
|
289
|
+
f for f in os.listdir(self.for_vasp_opt_dir) if f.startswith("CONTCAR_")
|
290
|
+
]
|
291
|
+
# 创建一个嵌套列表来存储每个节点的任务并将文件平均依次分配给每个节点
|
292
|
+
# 例如:对于10个结构文件任务分发给4个节点的情况,则4个节点领到的任务分别[0, 4, 8], [1, 5, 9], [2, 6], [3, 7]
|
293
|
+
node_jobs = [[] for _ in range(nodes)]
|
294
|
+
for index, file in enumerate(mlp_contcar_files):
|
295
|
+
node_index = index % nodes
|
296
|
+
node_jobs[node_index].append(index)
|
297
|
+
task_list = []
|
298
|
+
for pop in range(nodes):
|
299
|
+
forward_files = [
|
300
|
+
"INCAR_1",
|
301
|
+
"INCAR_2",
|
302
|
+
"INCAR_3",
|
303
|
+
"POTCAR_H",
|
304
|
+
"POTCAR_C",
|
305
|
+
"POTCAR_N",
|
306
|
+
"POTCAR_O",
|
307
|
+
"sub_final.sh",
|
308
|
+
]
|
309
|
+
backward_files = ["log", "err"]
|
310
|
+
# 将所有参数文件各复制一份到每个 task_dir 目录下
|
311
|
+
task_dir = os.path.join(self.for_vasp_opt_dir, f"{parent}pop{pop}")
|
312
|
+
os.makedirs(task_dir, exist_ok=True)
|
313
|
+
for file in forward_files:
|
314
|
+
shutil.copyfile(f"{self.param_dir}/{file}", f"{task_dir}/{file}")
|
315
|
+
for job_i in node_jobs[pop]:
|
316
|
+
# 将分配好的POSCAR文件添加到对应的上传文件中
|
317
|
+
forward_files.append(mlp_contcar_files[job_i])
|
318
|
+
vasp_dir = mlp_contcar_files[job_i].split("CONTCAR_")[1]
|
319
|
+
# 每个POSCAR文件在优化后都取回对应的CONTCAR和OUTCAR输出文件
|
320
|
+
backward_files.append(f"{vasp_dir}/*")
|
321
|
+
backward_files.append(f"{vasp_dir}/fine/*")
|
322
|
+
backward_files.append(f"{vasp_dir}/fine/final/*")
|
323
|
+
shutil.copyfile(
|
324
|
+
f"{self.for_vasp_opt_dir}/{mlp_contcar_files[job_i]}",
|
325
|
+
f"{task_dir}/{mlp_contcar_files[job_i]}",
|
326
|
+
)
|
327
|
+
|
328
|
+
remote_task_dir = f"{parent}pop{pop}"
|
329
|
+
command = "chmod +x sub_final.sh && ./sub_final.sh"
|
330
|
+
task = Task(
|
331
|
+
command=command,
|
332
|
+
task_work_path=remote_task_dir,
|
333
|
+
forward_files=forward_files,
|
334
|
+
backward_files=backward_files,
|
335
|
+
)
|
336
|
+
task_list.append(task)
|
337
|
+
|
338
|
+
submission = Submission(
|
339
|
+
work_base=self.for_vasp_opt_dir,
|
340
|
+
machine=machine,
|
341
|
+
resources=resources,
|
342
|
+
task_list=task_list,
|
343
|
+
)
|
344
|
+
submission.run_submission()
|
345
|
+
|
346
|
+
# 创建用于存放优化后文件的 4_vasp_optimized 目录
|
347
|
+
os.makedirs(self.vasp_optimized_dir, exist_ok=True)
|
348
|
+
mlp_outcar_files = [
|
349
|
+
f for f in os.listdir(self.for_vasp_opt_dir) if f.startswith("OUTCAR_")
|
350
|
+
]
|
351
|
+
for mlp_contcar, mlp_outcar in zip(mlp_contcar_files, mlp_outcar_files):
|
352
|
+
shutil.copyfile(
|
353
|
+
f"{self.for_vasp_opt_dir}/{mlp_contcar}",
|
354
|
+
f"{self.vasp_optimized_dir}/{mlp_contcar}",
|
355
|
+
)
|
356
|
+
shutil.copyfile(
|
357
|
+
f"{self.for_vasp_opt_dir}/{mlp_outcar}",
|
358
|
+
f"{self.vasp_optimized_dir}/{mlp_outcar}",
|
359
|
+
)
|
360
|
+
for pop in range(nodes):
|
361
|
+
# 从传回的 pop 文件夹中将结果文件取到 4_vasp_optimized 目录
|
362
|
+
task_dir = os.path.join(self.for_vasp_opt_dir, f"{parent}pop{pop}")
|
363
|
+
for job_i in node_jobs[pop]:
|
364
|
+
vasp_dir = mlp_contcar_files[job_i].split("CONTCAR_")[1]
|
365
|
+
shutil.copytree(
|
366
|
+
f"{task_dir}/{vasp_dir}", f"{self.vasp_optimized_dir}/{vasp_dir}"
|
367
|
+
)
|
368
|
+
# 在成功完成 VASP 分步优化后,删除 3_for_vasp_opt/{parent}/pop{n} 文件夹以节省空间
|
369
|
+
shutil.rmtree(task_dir)
|
370
|
+
if machine_inform["context_type"] == "SSHContext":
|
371
|
+
# 如果调用远程服务器,则删除data级目录
|
372
|
+
shutil.rmtree(os.path.join(self.for_vasp_opt_dir, parent))
|
373
|
+
logging.info("Batch VASP optimization completed!!!")
|
374
|
+
|
375
|
+
def read_vaspout_save_csv(self, molecules_prior: bool, relaxation: bool = False):
|
143
376
|
"""
|
144
377
|
Read VASP output files in batches and save energy and density to corresponding CSV files in the directory
|
145
378
|
"""
|
@@ -148,6 +381,7 @@ class VaspProcessing:
|
|
148
381
|
numbers, mlp_densities, mlp_energies = [], [], []
|
149
382
|
rough_densities, rough_energies = [], []
|
150
383
|
fine_densities, fine_energies = [], []
|
384
|
+
final_densities, final_energies = [], []
|
151
385
|
ions_checks, packing_coefficients = [], []
|
152
386
|
for folder in os.listdir(vasp_opt_dir):
|
153
387
|
vasp_opt_path = os.path.join(vasp_opt_dir, folder)
|
@@ -166,7 +400,7 @@ class VaspProcessing:
|
|
166
400
|
for line in lines:
|
167
401
|
if "TOTEN" in line:
|
168
402
|
values = line.split()
|
169
|
-
mlp_energy = round(float(values[-2]),
|
403
|
+
mlp_energy = round(float(values[-2]), 1)
|
170
404
|
except FileNotFoundError:
|
171
405
|
logging.error(
|
172
406
|
f" No avalible MLP OUTCAR_{mlp_density}_{number} found"
|
@@ -180,8 +414,8 @@ class VaspProcessing:
|
|
180
414
|
rough_atoms.get_masses()
|
181
415
|
) # 质量单位为原子质量单位(amu)
|
182
416
|
# 1.66054这一转换因子用于将原子质量单位转换为克,以便在宏观尺度上计算密度g/cm³
|
183
|
-
rough_density = round(1.66054 * atoms_masses / atoms_volume,
|
184
|
-
rough_energy = round(rough_atoms.get_total_energy(),
|
417
|
+
rough_density = round(1.66054 * atoms_masses / atoms_volume, 3)
|
418
|
+
rough_energy = round(rough_atoms.get_total_energy(), 1)
|
185
419
|
logging.info(
|
186
420
|
f" MLP_Density: {mlp_density}, MLP_Energy: {mlp_energy}"
|
187
421
|
)
|
@@ -211,9 +445,9 @@ class VaspProcessing:
|
|
211
445
|
) # 质量单位为原子质量单位(amu)
|
212
446
|
# 1.66054这一转换因子用于将原子质量单位转换为克,以便在宏观尺度上计算密度g/cm³
|
213
447
|
fine_density = round(
|
214
|
-
1.66054 * fine_atoms_masses / fine_atoms_volume,
|
448
|
+
1.66054 * fine_atoms_masses / fine_atoms_volume, 3
|
215
449
|
)
|
216
|
-
fine_energy = round(fine_atoms.get_total_energy(),
|
450
|
+
fine_energy = round(fine_atoms.get_total_energy(), 1)
|
217
451
|
logging.info(
|
218
452
|
f" Fine_Density: {fine_density}, Fine_Energy: {fine_energy}"
|
219
453
|
)
|
@@ -226,6 +460,49 @@ class VaspProcessing:
|
|
226
460
|
)
|
227
461
|
fine_density, fine_energy, molecules_flag = False, False, False
|
228
462
|
|
463
|
+
final_density, final_energy = (
|
464
|
+
False,
|
465
|
+
False,
|
466
|
+
)
|
467
|
+
if relaxation:
|
468
|
+
# 读取三级目录下的 OUTCAR 文件
|
469
|
+
final_OUTCAR_file_path = os.path.join(
|
470
|
+
vasp_opt_path, "fine", "final", "OUTCAR"
|
471
|
+
)
|
472
|
+
try:
|
473
|
+
final_atoms = read_vasp_out(final_OUTCAR_file_path)
|
474
|
+
molecules, molecules_flag, initial_information = (
|
475
|
+
identify_molecules(final_atoms)
|
476
|
+
)
|
477
|
+
if not initial_information:
|
478
|
+
raise KeyError("No available initial molecules")
|
479
|
+
final_atoms_volume = (
|
480
|
+
final_atoms.get_volume()
|
481
|
+
) # 体积单位为立方埃(ų)
|
482
|
+
final_atoms_masses = sum(
|
483
|
+
final_atoms.get_masses()
|
484
|
+
) # 质量单位为原子质量单位(amu)
|
485
|
+
# 1.66054这一转换因子用于将原子质量单位转换为克,以便在宏观尺度上计算密度g/cm³
|
486
|
+
final_density = round(
|
487
|
+
1.66054 * final_atoms_masses / final_atoms_volume, 3
|
488
|
+
)
|
489
|
+
final_energy = round(final_atoms.get_total_energy(), 1)
|
490
|
+
logging.info(
|
491
|
+
f" Final_Density: {final_density}, Final_Energy: {final_energy}"
|
492
|
+
)
|
493
|
+
molecules_information(
|
494
|
+
molecules, molecules_flag, initial_information
|
495
|
+
)
|
496
|
+
except (ParseError, FileNotFoundError):
|
497
|
+
logging.error(
|
498
|
+
f" Unfinished final optimization job of CONTCAR_{mlp_density}_{number}"
|
499
|
+
)
|
500
|
+
final_density, final_energy, molecules_flag = (
|
501
|
+
False,
|
502
|
+
False,
|
503
|
+
False,
|
504
|
+
)
|
505
|
+
|
229
506
|
# 读取根目录下的 config.yaml 信息与对应的 .json 文件
|
230
507
|
config_path = os.path.join(self.base_dir, "config.yaml")
|
231
508
|
with open(config_path, "r") as file:
|
@@ -242,9 +519,14 @@ class VaspProcessing:
|
|
242
519
|
property = json.load(file)
|
243
520
|
molecular_volume = float(property["volume"])
|
244
521
|
molecular_volumes += molecular_volume * count
|
245
|
-
|
246
|
-
|
247
|
-
|
522
|
+
if relaxation:
|
523
|
+
packing_coefficient = round(
|
524
|
+
molecular_volumes / final_atoms_volume, 3
|
525
|
+
)
|
526
|
+
else:
|
527
|
+
packing_coefficient = round(
|
528
|
+
molecular_volumes / fine_atoms_volume, 3
|
529
|
+
)
|
248
530
|
except (FileNotFoundError, UnboundLocalError):
|
249
531
|
packing_coefficient = False
|
250
532
|
|
@@ -253,6 +535,8 @@ class VaspProcessing:
|
|
253
535
|
rough_energies.append(rough_energy)
|
254
536
|
fine_densities.append(fine_density)
|
255
537
|
fine_energies.append(fine_energy)
|
538
|
+
final_densities.append(final_density)
|
539
|
+
final_energies.append(final_energy)
|
256
540
|
ions_checks.append(molecules_flag)
|
257
541
|
packing_coefficients.append(packing_coefficient)
|
258
542
|
|
@@ -273,6 +557,19 @@ class VaspProcessing:
|
|
273
557
|
"Fine_Density",
|
274
558
|
"Ions_Check",
|
275
559
|
]
|
560
|
+
if relaxation:
|
561
|
+
header = [
|
562
|
+
"Number",
|
563
|
+
"MLP_E",
|
564
|
+
"Rough_E",
|
565
|
+
"Fine_E",
|
566
|
+
"Final_E",
|
567
|
+
"MLP_Density",
|
568
|
+
"Rough_Density",
|
569
|
+
"Fine_Density",
|
570
|
+
"Final_Density",
|
571
|
+
"Ions_Check",
|
572
|
+
]
|
276
573
|
if packing_coefficients:
|
277
574
|
header.append("Pack_Coef")
|
278
575
|
datas = list(
|
@@ -288,12 +585,82 @@ class VaspProcessing:
|
|
288
585
|
(*packing_coefficients,) if packing_coefficients else (),
|
289
586
|
)
|
290
587
|
)
|
588
|
+
if relaxation:
|
589
|
+
datas = list(
|
590
|
+
zip(
|
591
|
+
numbers,
|
592
|
+
mlp_energies,
|
593
|
+
rough_energies,
|
594
|
+
fine_energies,
|
595
|
+
final_energies,
|
596
|
+
mlp_densities,
|
597
|
+
rough_densities,
|
598
|
+
fine_densities,
|
599
|
+
final_densities,
|
600
|
+
ions_checks,
|
601
|
+
(*packing_coefficients,) if packing_coefficients else (),
|
602
|
+
)
|
603
|
+
)
|
291
604
|
if molecules_prior:
|
292
|
-
# 如果设置了 molecules_prior 参数为 True
|
293
|
-
datas.sort(key=lambda x: (not x[
|
605
|
+
# 如果设置了 molecules_prior 参数为 True,则优先第倒数Ions_Check 为 True 的结果,再根据第6列的 Fine_Density 降序排序
|
606
|
+
datas.sort(key=lambda x: (not x[-2], -float(x[-3])))
|
294
607
|
else:
|
295
608
|
# 否则,直接根据第6列(从0列开始)的 Fine_Density 降序排序
|
296
|
-
datas.sort(key=lambda x: -float(x[
|
609
|
+
datas.sort(key=lambda x: -float(x[-3]))
|
297
610
|
writer.writerow(header)
|
298
611
|
for data in datas:
|
299
612
|
writer.writerow(data)
|
613
|
+
|
614
|
+
def export_max_density_structure(self):
|
615
|
+
"""
|
616
|
+
Read the structure number from the vasp_sensitiy_energy.csv file in the results folder, then search for the corresponding folder based on that sequence number, copy the highest density and highest precision CONTCAR file, and rename it POSCAR
|
617
|
+
"""
|
618
|
+
# 找到 vas_density_energy.csv 文件
|
619
|
+
csv_file_path = os.path.join(self.base_dir, "vasp_density_energy.csv")
|
620
|
+
if os.path.exists(csv_file_path):
|
621
|
+
# 读取 CSV 文件
|
622
|
+
with open(csv_file_path, "r") as csvfile:
|
623
|
+
reader = csv.reader(csvfile)
|
624
|
+
# 跳过表头读取第一行结构序号,即符合结构筛选要求的最大密度结构
|
625
|
+
first_row = next(reader)
|
626
|
+
structure_number = str(first_row[0])
|
627
|
+
# 根据结构序号构建要查找的文件夹路径
|
628
|
+
vasp_optimized_dir = os.path.join(self.base_dir, "4_vasp_optimized")
|
629
|
+
for vasp_folder_name in os.listdir(vasp_optimized_dir):
|
630
|
+
vasp_folder_path = os.path.join(vasp_optimized_dir, vasp_folder_name)
|
631
|
+
if os.path.isdir(vasp_folder_path) and vasp_folder_name.endswith(
|
632
|
+
structure_number
|
633
|
+
):
|
634
|
+
# 查找 CONTCAR 文件
|
635
|
+
final_contcar_path = os.path.join(
|
636
|
+
vasp_folder_path, "fine", "final", "CONTCAR"
|
637
|
+
)
|
638
|
+
fine_contcar_path = os.path.join(
|
639
|
+
vasp_folder_path, "fine", "CONTCAR"
|
640
|
+
)
|
641
|
+
logging.info(
|
642
|
+
f"Trying to get the final structure from {vasp_folder_path}"
|
643
|
+
)
|
644
|
+
if os.path.exists(final_contcar_path):
|
645
|
+
# 复制 CONTCAR 文件到 combo_n 文件夹并重命名为 POSCAR
|
646
|
+
shutil.copy(
|
647
|
+
final_contcar_path, os.path.join(self.base_dir, "POSCAR")
|
648
|
+
)
|
649
|
+
logging.info(
|
650
|
+
f"Renamed CONTCAR to POSCAR in {self.base_dir}, copied from {final_contcar_path}"
|
651
|
+
)
|
652
|
+
elif os.path.exists(fine_contcar_path):
|
653
|
+
logging.info(
|
654
|
+
f"CONTCAR not found in {os.path.join(vasp_folder_path, 'fine', 'final')}"
|
655
|
+
)
|
656
|
+
# 复制 CONTCAR 文件到 combo_n 文件夹并重命名为 POSCAR
|
657
|
+
shutil.copy(
|
658
|
+
fine_contcar_path, os.path.join(self.base_dir, "POSCAR")
|
659
|
+
)
|
660
|
+
logging.info(
|
661
|
+
f"Renamed CONTCAR to POSCAR in {self.base_dir}, copied from {fine_contcar_path}"
|
662
|
+
)
|
663
|
+
else:
|
664
|
+
print(f"Eligible CONTCAR not found in {vasp_folder_path}")
|
665
|
+
else:
|
666
|
+
logging.info(f"CSV file not found in {self.base_dir}")
|