ion-CSP 2.1.5__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/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
- """Decorator for recording log information and script runtime"""
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
- """Initialize workflow status logger and generate the .log and .yaml file to record the status"""
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
- """Handle termination signals and log the event"""
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
- Returns:
198
- tuple: (工作目录路径, 合并后的配置字典)
199
- Raises:
200
- SystemExit: 当输入无效时退出程序
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
- '''Using the Atoms.symples to Know Element&Num'''
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
- '''Write CONTCAR'''
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
- '''Write OUTCAR'''
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
- '''Using the ASE&DP to Optimize Configures'''
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()
@@ -9,6 +9,13 @@ from ion_CSP.identify_molecules import identify_molecules, molecules_information
9
9
  class ReadMlpDensity:
10
10
 
11
11
  def __init__(self, work_dir:str):
12
+ """
13
+ This class is designed to read and process MLP optimized files, specifically CONTCAR files, to calculate and sort their densities.
14
+ The class also provides functionality to process these files using phonopy for symmetry analysis and primitive cell generation.
15
+
16
+ :params
17
+ work_dir: The working directory where the MLP optimized files are located.
18
+ """
12
19
  # 获取脚本的当前目录
13
20
  self.base_dir = work_dir
14
21
  os.chdir(self.base_dir)
@@ -39,7 +46,10 @@ class ReadMlpDensity:
39
46
  """
40
47
  Obtain the atomic mass and unit cell volume from the optimized CONTCAR file, and obtain the ion crystal density. Finally, take n CONTCAR files with the highest density and save them separately for viewing.
41
48
 
42
- :param n_screen: 取前n个最大密度的文件
49
+ :params
50
+ n_screen: The number of CONTCAR files with the highest density to be saved.
51
+ molecules_screen: If True, only consider ionic crystals with original ions.
52
+ detail_log: If True, print detailed information about the molecules identified in the CONTCAR files.
43
53
  """
44
54
  os.chdir(self.base_dir)
45
55
  # 获取所有以'CONTCAR_'开头的文件,并按数字顺序处理
@@ -135,6 +145,10 @@ class ReadMlpDensity:
135
145
  def phonopy_processing_max_density(self, specific_directory :str = None):
136
146
  """
137
147
  Use phonopy to check and generate symmetric primitive cells, reducing the complexity of subsequent optimization calculations, and preventing pyxtal.from_random from generating double proportioned supercells.
148
+
149
+ :params
150
+ specific_directory: If specified, phonopy will process the files in this directory instead of the max_density directory.
151
+ If not specified, it will process the files in the max_density directory.
138
152
  """
139
153
  if specific_directory:
140
154
  self.phonopy_dir = os.path.join(self.base_dir, specific_directory)
ion_CSP/task_manager.py CHANGED
@@ -225,7 +225,7 @@ class TaskManager:
225
225
  pid_file = work_dir / "pid.txt"
226
226
 
227
227
  # 动态加载模块
228
- module_name = f"run.main_{module}"
228
+ module_name = f"ion_CSP.run.main_{module}"
229
229
  spec = importlib.util.find_spec(module_name)
230
230
  if not spec:
231
231
  raise ImportError(f"Module {module_name} not found")
@@ -484,4 +484,3 @@ class SSHBatchJob:
484
484
  def close_connection(self):
485
485
  self.sftp.close()
486
486
  self.client.close()
487
-
@@ -4,6 +4,7 @@ import json
4
4
  import yaml
5
5
  import shutil
6
6
  import logging
7
+ import importlib.resources
7
8
  from ase.io import ParseError
8
9
  from ase.io.vasp import read_vasp_out
9
10
  from dpdispatcher import Machine, Resources, Task, Submission
@@ -13,12 +14,18 @@ from ion_CSP.identify_molecules import identify_molecules, molecules_information
13
14
 
14
15
  class VaspProcessing:
15
16
  def __init__(self, work_dir: str):
17
+ """
18
+ This directory is used to store all the files related to VASP optimizations.
19
+
20
+ :params
21
+ work_dir: The working directory where VASP optimization files will be stored.
22
+ """
16
23
  redirect_dpdisp_logging(os.path.join(work_dir, "dpdispatcher.log"))
17
24
  self.base_dir = work_dir
18
25
  os.chdir(self.base_dir)
19
26
  self.for_vasp_opt_dir = f"{work_dir}/3_for_vasp_opt"
20
27
  self.vasp_optimized_dir = f"{work_dir}/4_vasp_optimized"
21
- self.param_dir = os.path.join(os.path.dirname(__file__), "../../param")
28
+ self.param_dir = importlib.resources.files("ion_CSP.param")
22
29
 
23
30
  def dpdisp_vasp_optimization_tasks(
24
31
  self,
@@ -28,6 +35,10 @@ class VaspProcessing:
28
35
  ):
29
36
  """
30
37
  Based on the dpdispatcher module, prepare and submit files for optimization on remote server or local machine.
38
+ :params
39
+ machine: The machine configuration file, which can be in JSON or YAML format.
40
+ resources: The resources configuration file, which can be in JSON or YAML format.
41
+ nodes: The number of nodes to distribute the optimization tasks across.
31
42
  """
32
43
  # 调整工作目录,减少错误发生
33
44
  os.chdir(self.for_vasp_opt_dir)
@@ -79,7 +90,7 @@ class VaspProcessing:
79
90
  task_dir = os.path.join(self.for_vasp_opt_dir, f"{parent}pop{pop}")
80
91
  os.makedirs(task_dir, exist_ok=True)
81
92
  for file in forward_files:
82
- shutil.copyfile(f"{self.param_dir}/{file}", f"{task_dir}/{file}")
93
+ shutil.copyfile(self.param_dir.joinpath(file), f"{task_dir}/{file}")
83
94
  for job_i in node_jobs[pop]:
84
95
  # 将分配好的POSCAR文件添加到对应的上传文件中
85
96
  forward_files.append(mlp_contcar_files[job_i])
@@ -146,7 +157,11 @@ class VaspProcessing:
146
157
  nodes: int = 1,
147
158
  ):
148
159
  """
149
- Based on the dpdispatcher module, prepare and submit files for optimization on remote server or local machine.
160
+ Based on the dpdispatcher module, prepare and submit files for VASP relaxation on remote server or local machine.
161
+ :params
162
+ machine: The machine configuration file, which can be in JSON or YAML format.
163
+ resources: The resources configuration file, which can be in JSON or YAML format.
164
+ nodes: The number of nodes to distribute the optimization tasks across.
150
165
  """
151
166
  # 调整工作目录,减少错误发生
152
167
  os.chdir(self.vasp_optimized_dir)
@@ -199,7 +214,7 @@ class VaspProcessing:
199
214
  task_dir = os.path.join(self.vasp_optimized_dir, f"{parent}pop{pop}")
200
215
  os.makedirs(task_dir, exist_ok=True)
201
216
  for file in forward_files:
202
- shutil.copyfile(f"{self.param_dir}/{file}", f"{task_dir}/{file}")
217
+ shutil.copyfile(self.param_dir.joinpath(file), f"{task_dir}/{file}")
203
218
  for job_i in node_jobs[pop]:
204
219
  # 将分配好的POSCAR文件添加到对应的上传文件中
205
220
  vasp_dir = vasp_optimized_folders[job_i]
@@ -311,7 +326,7 @@ class VaspProcessing:
311
326
  task_dir = os.path.join(self.for_vasp_opt_dir, f"{parent}pop{pop}")
312
327
  os.makedirs(task_dir, exist_ok=True)
313
328
  for file in forward_files:
314
- shutil.copyfile(f"{self.param_dir}/{file}", f"{task_dir}/{file}")
329
+ shutil.copyfile(self.param_dir.joinpath(file), f"{task_dir}/{file}")
315
330
  for job_i in node_jobs[pop]:
316
331
  # 将分配好的POSCAR文件添加到对应的上传文件中
317
332
  forward_files.append(mlp_contcar_files[job_i])
@@ -622,6 +637,11 @@ class VaspProcessing:
622
637
  with open(csv_file_path, "r") as csvfile:
623
638
  reader = csv.reader(csvfile)
624
639
  # 跳过表头读取第一行结构序号,即符合结构筛选要求的最大密度结构
640
+ header = next(reader)
641
+ if header[0] != "Number":
642
+ raise KeyError(
643
+ "The first column of the CSV file is not 'Number', please check the file format."
644
+ )
625
645
  first_row = next(reader)
626
646
  structure_number = str(first_row[0])
627
647
  # 根据结构序号构建要查找的文件夹路径
@@ -632,24 +652,27 @@ class VaspProcessing:
632
652
  structure_number
633
653
  ):
634
654
  # 查找 CONTCAR 文件
635
- final_contcar_path = os.path.join(
636
- vasp_folder_path, "fine", "final", "CONTCAR"
637
- )
655
+ # final_contcar_path = os.path.join(
656
+ # vasp_folder_path, "fine", "final", "CONTCAR"
657
+ # )
658
+ # print(f"Trying to get the final structure from {vasp_folder_path}")
659
+ # logging.info(
660
+ # f"Trying to get the final structure from {vasp_folder_path}"
661
+ # )
662
+ # if os.path.exists(final_contcar_path):
663
+ # # 复制 CONTCAR 文件到 combo_n 文件夹并重命名为 POSCAR
664
+ # shutil.copy(
665
+ # final_contcar_path, os.path.join(self.base_dir, "POSCAR")
666
+ # )
667
+ # print(f"Renamed CONTCAR to POSCAR in {self.base_dir}, copied from {final_contcar_path}")
668
+ # logging.info(
669
+ # f"Renamed CONTCAR to POSCAR in {self.base_dir}, copied from {final_contcar_path}"
670
+ # )
638
671
  fine_contcar_path = os.path.join(
639
672
  vasp_folder_path, "fine", "CONTCAR"
640
673
  )
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):
674
+ if os.path.exists(fine_contcar_path):
675
+ print(f"CONTCAR not found in {os.path.join(vasp_folder_path, 'fine', 'final')}")
653
676
  logging.info(
654
677
  f"CONTCAR not found in {os.path.join(vasp_folder_path, 'fine', 'final')}"
655
678
  )
@@ -657,10 +680,15 @@ class VaspProcessing:
657
680
  shutil.copy(
658
681
  fine_contcar_path, os.path.join(self.base_dir, "POSCAR")
659
682
  )
683
+ print(f"Renamed CONTCAR to POSCAR in {self.base_dir}, copied from {fine_contcar_path}")
660
684
  logging.info(
661
685
  f"Renamed CONTCAR to POSCAR in {self.base_dir}, copied from {fine_contcar_path}"
662
686
  )
663
687
  else:
664
688
  print(f"Eligible CONTCAR not found in {vasp_folder_path}")
689
+ logging.info(
690
+ f"Eligible CONTCAR not found in {vasp_folder_path}"
691
+ )
665
692
  else:
693
+ print(f"CSV file not found in {self.base_dir}")
666
694
  logging.info(f"CSV file not found in {self.base_dir}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ion_CSP
3
- Version: 2.1.5
3
+ Version: 2.1.8
4
4
  Summary: Crystal Structure Design Software Based on Molecular/Ionic Configuration.
5
5
  Home-page: https://github.com/bagabaga007/ion_CSP
6
6
  Author: Ze Yang
@@ -28,13 +28,16 @@ Dynamic: home-page
28
28
  Dynamic: license-file
29
29
  Dynamic: requires-python
30
30
 
31
- # Crystal Structure Design Software V2.1
31
+ # 基于分子/离子构型的晶体结构设计软件 V2.1
32
32
 
33
33
  ## 项目概述
34
+
34
35
  基于分子/离子构型的晶体结构设计软件通过结合经验公式、机器学习势函数微调、第一性原理分步优化和分子/离子识别技术,实现了从分子/离子构型出发的高效晶体结构设计。该软件采用模块化设计,支持全流程自动化材料筛选,在保证预测精度的同时显著提升计算效率。
35
36
 
36
37
  ## 功能特性
38
+
37
39
  ### 核心功能
40
+
38
41
  - **双模块工作流**
39
42
  - **EE模块**:基于经验评估的离子组合生成
40
43
  - **CSP模块**:基于离子晶体结构预测的优化筛选
@@ -48,13 +51,16 @@ Dynamic: requires-python
48
51
  - 软链接解析显示实际路径
49
52
 
50
53
  ### 技术特性
54
+
51
55
  - 跨平台支持(Linux/Docker)
52
56
  - 基于Python 3.11+的面向对象架构
53
57
  - 集成psutil进程管理
54
58
  - 结构化日志记录系统
55
59
 
56
60
  ## 安装指南
61
+
57
62
  ### 环境要求
63
+
58
64
  | 组件 | 最低版本 |
59
65
  |-------------|----------|
60
66
  | Python | 3.11 |
@@ -71,10 +77,13 @@ Dynamic: requires-python
71
77
  | rdkit | 2024.03.3|
72
78
 
73
79
  ### 安装步骤
80
+
74
81
  ```bash
75
82
  pip install ion-csp
76
83
  ```
84
+
77
85
 
86
+
78
87
  ```bash
79
88
  # 创建虚拟环境
80
89
  python -m venv venv
@@ -87,29 +96,39 @@ pip install -e .
87
96
  ```
88
97
 
89
98
  ## 快速入门
99
+
90
100
  ### 交互模式
101
+
91
102
  ```bash
92
103
  ion-csp
93
104
  ```
105
+
94
106
  启动交互式命令行界面,支持以下操作:
107
+
95
108
  - 模块选择
96
109
  - 日志查看
97
110
  - 进程管理
98
111
 
99
112
  ### 脚本调用
113
+
100
114
  #### EE模块示例
115
+
101
116
  ```bash
102
117
  ./scripts/main_EE.sh examples/example_1
103
118
  ```
119
+
104
120
  从SMILES表格生成离子组合
105
121
 
106
122
  #### CSP模块示例
123
+
107
124
  ```bash
108
125
  ./scripts/main_CSP.sh examples/example_2
109
126
  ```
127
+
110
128
  从离子组合生成并优化晶体结构
111
129
 
112
130
  ## 技术架构
131
+
113
132
  ```mermaid
114
133
  graph TD
115
134
  A[用户界面] --> B[任务管理器]
@@ -118,34 +137,32 @@ graph TD
118
137
  B --> E[日志系统]
119
138
  B --> F[任务调度]
120
139
  C --> G[经验评估引擎]
121
- D --> H[晶体预测引擎]
140
+ D --> H[离子晶体结构预测引擎]
122
141
  E --> I[结构化日志]
123
142
  F --> J[进程终止]
124
143
  ```
125
144
 
126
145
  ## 贡献指南
146
+
127
147
  1. Fork仓库并创建特性分支
128
148
  2. 编写单元测试覆盖新功能
129
149
  3. 提交Pull Request时注明关联Issue
130
150
  4. 遵循PEP8代码规范
131
151
 
132
152
  ## 许可证
133
- 本项目采用MIT许可证,详见LICENSE文件。
134
153
 
135
- ## 技术支持
136
- - **文档更新**:2025年6月
137
- - **最新版本**:v2.1.5
138
- - **问题追踪**:https://github.com/bagabaga007/ion_CSP/issues
154
+ 本项目采用MIT许可证,详见LICENSE文件。
139
155
 
140
156
  ---
141
157
 
142
- # Crystal Structure Design Software V2.1
143
-
144
158
  ## Project Overview
159
+
145
160
  This software enables efficient crystal structure design from molecular/ion configurations by integrating empirical formulas, tuned machine learning potentials, stepwise first-principles optimization, and molecular/ion recognition techniques. The modular architecture ensures extensibility and maintainability while maintaining prediction accuracy.
146
161
 
147
162
  ## Key Features
163
+
148
164
  ### Core Functionalities
165
+
149
166
  - **Dual-Module Workflow**
150
167
  - **EE Module**: Empirical evaluation-based ion combination generation
151
168
  - **CSP Module**: Ion crystal structure prediction and optimization
@@ -159,13 +176,16 @@ This software enables efficient crystal structure design from molecular/ion conf
159
176
  - Symlink resolution for actual log paths
160
177
 
161
178
  ### Technical Specifications
179
+
162
180
  - Cross-platform support (Linux/Docker)
163
181
  - Object-oriented architecture with Python 3.11+
164
182
  - Integrated process management via psutil
165
183
  - Structured logging system
166
184
 
167
185
  ## Installation
186
+
168
187
  ### Prerequisites
188
+
169
189
  | Component | Min Version |
170
190
  |-------------|-------------|
171
191
  | Python | 3.11 |
@@ -182,6 +202,7 @@ This software enables efficient crystal structure design from molecular/ion conf
182
202
  | rdkit | 2024.03.3|
183
203
 
184
204
  ### Installation Steps
205
+
185
206
  ```bash
186
207
  # Create virtual environment
187
208
  python -m venv venv
@@ -194,29 +215,39 @@ pip install -e .
194
215
  ```
195
216
 
196
217
  ## Quick Start
218
+
197
219
  ### Interactive Mode
220
+
198
221
  ```bash
199
222
  ion-csp
200
223
  ```
224
+
201
225
  Launches CLI interface with:
226
+
202
227
  - Module selection
203
228
  - Log management
204
229
  - Process control
205
230
 
206
231
  ### Script Execution
232
+
207
233
  #### EE Module Example
234
+
208
235
  ```bash
209
236
  ./scripts/main_EE.sh examples/example_1
210
237
  ```
238
+
211
239
  Generates ion combinations from SMILES tables
212
240
 
213
241
  #### CSP Module Example
242
+
214
243
  ```bash
215
244
  ./scripts/main_CSP.sh examples/example_2
216
245
  ```
246
+
217
247
  Optimizes crystal structures from ion combinations
218
248
 
219
249
  ## Technical Architecture
250
+
220
251
  ```mermaid
221
252
  graph TD
222
253
  A[User Interface] --> B[Task Manager]
@@ -225,21 +256,18 @@ graph TD
225
256
  B --> E[Log System]
226
257
  B --> F[Task Scheduler]
227
258
  C --> G[Empirical Evaluation Engine]
228
- D --> H[Crystal Prediction Engine]
259
+ D --> H[Ionic Crystal Structure Prediction Engine]
229
260
  E --> I[Structured Logs]
230
261
  F --> J[Process Termination]
231
262
  ```
232
263
 
233
264
  ## Contribution Guide
265
+
234
266
  1. Fork repository and create feature branch
235
267
  2. Write unit tests for new features
236
268
  3. Submit PR with issue reference
237
269
  4. Follow PEP8 coding standards
238
270
 
239
271
  ## License
240
- MIT License, see LICENSE file.
241
272
 
242
- ## Support
243
- - Documentation last updated: June 2025
244
- - Latest version: v2.1.5
245
- - Issue tracker: https://github.com/bagabaga007/ion_CSP/issues
273
+ MIT License, see LICENSE file.