ion-CSP 2.1.4__py3-none-any.whl → 2.1.8__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 +2 -2
- ion_CSP/convert_SMILES.py +32 -10
- ion_CSP/empirical_estimate.py +136 -18
- ion_CSP/gen_opt.py +83 -33
- ion_CSP/identify_molecules.py +15 -0
- ion_CSP/log_and_time.py +55 -8
- ion_CSP/mlp_opt.py +52 -6
- ion_CSP/model/model.pt +0 -0
- ion_CSP/model/options/README.md +5 -0
- ion_CSP/model/options/model.ckpt-4000000.pt +0 -0
- ion_CSP/param/INCAR_0 +16 -0
- ion_CSP/param/INCAR_1 +19 -0
- ion_CSP/param/INCAR_2 +19 -0
- ion_CSP/param/INCAR_3 +19 -0
- ion_CSP/param/POTCAR_C +2319 -0
- ion_CSP/param/POTCAR_H +1563 -0
- ion_CSP/param/POTCAR_N +2351 -0
- ion_CSP/param/POTCAR_O +2487 -0
- ion_CSP/param/g16_sub.sh +21 -0
- ion_CSP/param/sub_final.sh +91 -0
- ion_CSP/param/sub_ori.sh +74 -0
- ion_CSP/param/sub_supple.sh +56 -0
- ion_CSP/read_mlp_density.py +15 -1
- ion_CSP/task_manager.py +2 -2
- ion_CSP/upload_download.py +0 -1
- ion_CSP/vasp_processing.py +48 -20
- {ion_csp-2.1.4.dist-info → ion_csp-2.1.8.dist-info}/METADATA +45 -16
- ion_csp-2.1.8.dist-info/RECORD +43 -0
- {ion_csp-2.1.4.dist-info → ion_csp-2.1.8.dist-info}/licenses/LICENSE +1 -1
- {ion_csp-2.1.4.dist-info → ion_csp-2.1.8.dist-info}/top_level.txt +0 -1
- ion_csp-2.1.4.dist-info/RECORD +0 -28
- {run → ion_CSP/run}/__init__.py +0 -0
- {run → ion_CSP/run}/main_CSP.py +0 -0
- {run → ion_CSP/run}/main_EE.py +0 -0
- {run → ion_CSP/run}/run_convert_SMILES.py +0 -0
- {run → ion_CSP/run}/run_empirical_estimate.py +0 -0
- {run → ion_CSP/run}/run_gen_opt.py +0 -0
- {run → ion_CSP/run}/run_read_mlp_density.py +0 -0
- {run → ion_CSP/run}/run_upload_download.py +0 -0
- {run → ion_CSP/run}/run_vasp_processing.py +0 -0
- {ion_csp-2.1.4.dist-info → ion_csp-2.1.8.dist-info}/WHEEL +0 -0
- {ion_csp-2.1.4.dist-info → ion_csp-2.1.8.dist-info}/entry_points.txt +0 -0
ion_CSP/log_and_time.py
CHANGED
@@ -11,7 +11,14 @@ from dpdispatcher.dlog import dlog
|
|
11
11
|
|
12
12
|
|
13
13
|
def log_and_time(func):
|
14
|
-
"""
|
14
|
+
"""
|
15
|
+
Decorator for recording log information and script runtime
|
16
|
+
|
17
|
+
:params
|
18
|
+
func: The function to be decorated
|
19
|
+
|
20
|
+
:return: The decorated function with logging and timing capabilities
|
21
|
+
"""
|
15
22
|
@functools.wraps(func)
|
16
23
|
def wrapper(work_dir, *args, **kwargs):
|
17
24
|
# 使用inspect获取真实脚本文件名
|
@@ -54,20 +61,49 @@ def log_and_time(func):
|
|
54
61
|
|
55
62
|
|
56
63
|
def merge_config(default_config, user_config, key):
|
64
|
+
"""
|
65
|
+
Merge default configuration with user-provided configuration for a specific key.
|
66
|
+
|
67
|
+
:params
|
68
|
+
default_config: The default configuration dictionary.
|
69
|
+
user_config: The user-provided configuration dictionary.
|
70
|
+
key: The key for which the configuration should be merged.
|
71
|
+
|
72
|
+
:return: A merged configuration dictionary for the specified key.
|
73
|
+
"""
|
74
|
+
if key not in default_config:
|
75
|
+
raise KeyError(f"Key '{key}' not found in default configuration.")
|
76
|
+
if key not in user_config:
|
77
|
+
raise KeyError(f"Key '{key}' not found in user configuration.")
|
78
|
+
if not isinstance(default_config[key], dict) or not isinstance(user_config.get(key, {}), dict):
|
79
|
+
raise TypeError(f"Both default and user configurations for '{key}' must be dictionaries.")
|
80
|
+
# 合并两个参数配置,优先使用用户参数配置
|
57
81
|
return {**default_config[key], **user_config.get(key, {})}
|
58
82
|
|
59
83
|
|
60
84
|
class StatusLogger:
|
85
|
+
"""
|
86
|
+
A singleton class to log the status of a workflow, including RUNNING, SUCCESS, FAILURE, and KILLED.
|
87
|
+
It initializes a logger that writes to a log file and a YAML file to record the status of the workflow.
|
88
|
+
The logger captures the process ID and handles termination signals (SIGINT, SIGTERM).
|
89
|
+
"""
|
90
|
+
_name = "WorkflowLogger"
|
61
91
|
_instance = None
|
62
92
|
|
63
93
|
def __new__(cls, *args, **kwargs):
|
94
|
+
"""Ensure that only one instance of StatusLogger is created (Singleton Pattern)"""
|
64
95
|
if not cls._instance:
|
65
96
|
cls._instance = super(StatusLogger, cls).__new__(cls)
|
66
97
|
cls._instance.__init__(*args, **kwargs)
|
67
98
|
return cls._instance
|
68
99
|
|
69
100
|
def __init__(self, work_dir, task_name):
|
70
|
-
"""
|
101
|
+
"""
|
102
|
+
Initialize workflow status logger and generate the .log and .yaml file to record the status
|
103
|
+
|
104
|
+
:params
|
105
|
+
work_dir: The working directory where the log and yaml files will be created
|
106
|
+
task_name: The name of the task to be logged"""
|
71
107
|
# 使用单例模式,避免重复的日志记录,缺点是再重新给定task_name之后会覆盖原来的实例,只能顺序调用
|
72
108
|
self.task_name = task_name
|
73
109
|
log_file = os.path.join(work_dir, "workflow_status.log")
|
@@ -97,12 +133,17 @@ class StatusLogger:
|
|
97
133
|
self._init_yaml()
|
98
134
|
|
99
135
|
def set_running(self):
|
136
|
+
"""
|
137
|
+
Set the current task status to RUNNING and log the event.
|
138
|
+
This method increments the run count and updates the YAML file.
|
139
|
+
"""
|
100
140
|
self.current_status = "RUNNING"
|
101
141
|
self.logger.info(f"{self.task_name} Status: {self.current_status}")
|
102
142
|
self.run_count += 1
|
103
143
|
self._update_yaml()
|
104
144
|
|
105
145
|
def set_success(self):
|
146
|
+
"""Set the current task status to SUCCESS and log the event"""
|
106
147
|
self.current_status = "SUCCESS"
|
107
148
|
self.logger.info(f"{self.task_name} Status: {self.current_status}\n")
|
108
149
|
self._update_yaml()
|
@@ -112,12 +153,16 @@ class StatusLogger:
|
|
112
153
|
return self.current_status == "SUCCESS"
|
113
154
|
|
114
155
|
def set_failure(self):
|
156
|
+
"""Set the current task status to FAILURE and log the event"""
|
115
157
|
self.current_status = "FAILURE"
|
116
158
|
self.logger.error(f"{self.task_name} Status: {self.current_status}\n")
|
117
159
|
self._update_yaml()
|
118
160
|
|
119
161
|
def _signal_handler(self, signum, _):
|
120
|
-
"""
|
162
|
+
"""
|
163
|
+
Handle termination signals and log the event
|
164
|
+
:params
|
165
|
+
signum: The signal number received (e.g., SIGINT, SIGTERM)"""
|
121
166
|
if signum == 2:
|
122
167
|
self.logger.warning(
|
123
168
|
f"Process {os.getpid()} has been interrupted by 'Ctrl + C'\n"
|
@@ -134,6 +179,7 @@ class StatusLogger:
|
|
134
179
|
sys.exit(0)
|
135
180
|
|
136
181
|
def _set_killed(self):
|
182
|
+
"""Set the current task status to KILLED and log the event"""
|
137
183
|
self.current_status = "KILLED"
|
138
184
|
self.logger.warning(f"{self.task_name} Status: {self.current_status}\n")
|
139
185
|
self._update_yaml()
|
@@ -193,11 +239,12 @@ def redirect_dpdisp_logging(custom_log_path):
|
|
193
239
|
|
194
240
|
|
195
241
|
def get_work_dir_and_config():
|
196
|
-
"""
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
242
|
+
"""
|
243
|
+
Get the working directory and user configuration from command line arguments or interactive input.
|
244
|
+
If the working directory is not specified, it prompts the user to input it interactively.
|
245
|
+
It also reads the configuration from a 'config.yaml' file in the specified directory.
|
246
|
+
|
247
|
+
:return: A tuple containing the working directory and the user configuration dictionary.
|
201
248
|
"""
|
202
249
|
parser = argparse.ArgumentParser(
|
203
250
|
description="The full workflow of ionic crystal design for a certain ion combination, including generation, mlp optimization, screening, vasp optimization and analysis."
|
ion_CSP/mlp_opt.py
CHANGED
@@ -14,13 +14,22 @@ base_dir = os.path.dirname(__file__)
|
|
14
14
|
relative_path = './model.pt'
|
15
15
|
file_path = os.path.join(base_dir, relative_path)
|
16
16
|
calc = DP(file_path)
|
17
|
-
|
17
|
+
"""
|
18
18
|
structure optimization with DP model and ASE
|
19
19
|
PSTRESS and fmax should exist in input.dat
|
20
|
-
|
20
|
+
"""
|
21
21
|
|
22
22
|
def get_element_num(elements):
|
23
|
-
|
23
|
+
"""
|
24
|
+
Using the Atoms.symples to Know Element and Number
|
25
|
+
|
26
|
+
:params
|
27
|
+
elements: list of elements in the structure
|
28
|
+
|
29
|
+
:returns
|
30
|
+
element: list of unique elements in the structure
|
31
|
+
ele: dictionary with elements as keys and their counts as values
|
32
|
+
"""
|
24
33
|
element = []
|
25
34
|
ele = {}
|
26
35
|
element.append(elements[0])
|
@@ -32,7 +41,15 @@ def get_element_num(elements):
|
|
32
41
|
return element, ele
|
33
42
|
|
34
43
|
def write_CONTCAR(element, ele, lat, pos, index):
|
35
|
-
|
44
|
+
"""
|
45
|
+
Write CONTCAR file in VASP format
|
46
|
+
|
47
|
+
:params
|
48
|
+
element: list of elements in the structure
|
49
|
+
ele: dictionary of element counts
|
50
|
+
lat: lattice vectors
|
51
|
+
pos: atomic positions in direct coordinates
|
52
|
+
index: index for the output file"""
|
36
53
|
f = open(f'{base_dir}/CONTCAR_'+str(index),'w')
|
37
54
|
f.write('ASE-DPKit-Optimization\n')
|
38
55
|
f.write('1.0\n')
|
@@ -51,7 +68,21 @@ def write_CONTCAR(element, ele, lat, pos, index):
|
|
51
68
|
f.write('%15.10f %15.10f %15.10f\n' % tuple(dpos[i]))
|
52
69
|
|
53
70
|
def write_OUTCAR(element, ele, masses, volume, lat, pos, ene, force, stress, pstress, index):
|
54
|
-
|
71
|
+
"""
|
72
|
+
Write OUTCAR file in VASP format
|
73
|
+
:params
|
74
|
+
element: list of elements in the structure
|
75
|
+
ele: dictionary of element counts
|
76
|
+
masses: total mass of the atoms
|
77
|
+
volume: volume of the unit cell
|
78
|
+
lat: lattice vectors
|
79
|
+
pos: atomic positions in direct coordinates
|
80
|
+
ene: total energy of the system
|
81
|
+
force: forces on the atoms
|
82
|
+
stress: stress tensor components
|
83
|
+
pstress: external pressure
|
84
|
+
index: index for the output file
|
85
|
+
"""
|
55
86
|
f = open(f'{base_dir}/OUTCAR_'+str(index),'w')
|
56
87
|
for x in element:
|
57
88
|
f.write('VRHFIN =' + str(x) + '\n')
|
@@ -88,6 +119,13 @@ def write_OUTCAR(element, ele, masses, volume, lat, pos, ene, force, stress, pst
|
|
88
119
|
f.write('enthalpy TOTEN = %20.6f %20.6f\n' % (enthalpy, enthalpy/na))
|
89
120
|
|
90
121
|
def get_indexes():
|
122
|
+
"""
|
123
|
+
Get the indexes of POSCAR files in the current directory.
|
124
|
+
This function scans the current directory for files starting with 'POSCAR_' and extracts their numeric indexes.
|
125
|
+
|
126
|
+
:returns
|
127
|
+
A sorted list of indexes extracted from the POSCAR files.
|
128
|
+
"""
|
91
129
|
base_dir = os.path.dirname(__file__)
|
92
130
|
POSCAR_files = [f for f in os.listdir(base_dir) if f.startswith('POSCAR_')]
|
93
131
|
indexes = []
|
@@ -100,7 +138,11 @@ def get_indexes():
|
|
100
138
|
return indexes
|
101
139
|
|
102
140
|
def run_opt(index: int):
|
103
|
-
|
141
|
+
"""
|
142
|
+
Using the ASE&DP to Optimize Configures
|
143
|
+
:params
|
144
|
+
index: index of the POSCAR file to be optimized
|
145
|
+
"""
|
104
146
|
if os.path.isfile(f'{base_dir}/OUTCAR'):
|
105
147
|
os.system(f'mv {base_dir}/OUTCAR {base_dir}/OUTCAR-last')
|
106
148
|
fmax, pstress = 0.03, 0
|
@@ -143,6 +185,10 @@ def run_opt(index: int):
|
|
143
185
|
|
144
186
|
|
145
187
|
def main():
|
188
|
+
"""
|
189
|
+
Main function to run the optimization in parallel.
|
190
|
+
It initializes a multiprocessing pool and maps the run_opt function to the indexes of POSCAR files.
|
191
|
+
"""
|
146
192
|
ctx=multiprocessing.get_context("spawn")
|
147
193
|
pool=ctx.Pool(8)
|
148
194
|
indexes = get_indexes()
|
ion_CSP/model/model.pt
ADDED
Binary file
|
Binary file
|
ion_CSP/param/INCAR_0
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
SYSTEM = ION_CSP
|
2
|
+
NPAR = 4
|
3
|
+
PREC = Accurate
|
4
|
+
ENCUT = 800
|
5
|
+
EDIFF = 1e-4 # 1e-6
|
6
|
+
IBRION = -1
|
7
|
+
ISMEAR = 0 ; SIGMA = 0.05
|
8
|
+
#No writing charge density and wavefunction
|
9
|
+
LCHARG = FALSE
|
10
|
+
LWAVE = FALSE
|
11
|
+
#Target Pressure
|
12
|
+
#PSTRESS = 100
|
13
|
+
#Finer optimization
|
14
|
+
EDIFFG = -0.05 # 1e-4
|
15
|
+
KSPACING = 0.25
|
16
|
+
IVDW = 11
|
ion_CSP/param/INCAR_1
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
SYSTEM = ION_CSP
|
2
|
+
NPAR = 4
|
3
|
+
PREC = Accurate
|
4
|
+
ENCUT = 600 # 800
|
5
|
+
EDIFF = 1e-4 # 1e-6
|
6
|
+
IBRION = 2
|
7
|
+
ISIF = 8
|
8
|
+
NSW = 300 # 200
|
9
|
+
ISMEAR = 0 ; SIGMA = 0.05
|
10
|
+
POTIM = 0.05
|
11
|
+
#No writing charge density and wavefunction
|
12
|
+
LCHARG = FALSE
|
13
|
+
LWAVE = FALSE
|
14
|
+
#Target Pressure
|
15
|
+
#PSTRESS = 100
|
16
|
+
#Finer optimization
|
17
|
+
EDIFFG = -0.05 # 1e-4
|
18
|
+
KSPACING = 0.7 # 0.25
|
19
|
+
IVDW = 12
|
ion_CSP/param/INCAR_2
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
SYSTEM = ION_CSP
|
2
|
+
NPAR = 4
|
3
|
+
PREC = Accurate
|
4
|
+
ENCUT = 800
|
5
|
+
EDIFF = 1e-6
|
6
|
+
IBRION = 2
|
7
|
+
ISIF = 8
|
8
|
+
NSW = 300 # 200
|
9
|
+
ISMEAR = 0 ; SIGMA = 0.05
|
10
|
+
POTIM = 0.05
|
11
|
+
#No writing charge density and wavefunction
|
12
|
+
LCHARG = FALSE
|
13
|
+
LWAVE = FALSE
|
14
|
+
#Target Pressure
|
15
|
+
#PSTRESS = 100
|
16
|
+
#Finer optimization
|
17
|
+
EDIFFG = -0.02
|
18
|
+
KSPACING = 0.5
|
19
|
+
IVDW = 12
|
ion_CSP/param/INCAR_3
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
SYSTEM = ION_CSP
|
2
|
+
NPAR = 4
|
3
|
+
PREC = Accurate
|
4
|
+
ENCUT = 800
|
5
|
+
EDIFF = 1e-6
|
6
|
+
IBRION = 2
|
7
|
+
ISIF = 3
|
8
|
+
NSW = 300 # 200
|
9
|
+
ISMEAR = 0 ; SIGMA = 0.05
|
10
|
+
POTIM = 0.05
|
11
|
+
#No writing charge density and wavefunction
|
12
|
+
LCHARG = FALSE
|
13
|
+
LWAVE = FALSE
|
14
|
+
#Target Pressure
|
15
|
+
#PSTRESS = 100
|
16
|
+
#Finer optimization
|
17
|
+
EDIFFG = -0.02
|
18
|
+
KSPACING = 0.5
|
19
|
+
IVDW = 12
|