1515from dpdispatcher import Task , Submission , Machine , Resources
1616from dprep .get_pp_orb_info import generate_pp_orb_dict
1717from dprep .post_analysis_tools import copy_failed_folders , save_direct_kpoints
18+ # 新增 import
19+ from dptb .postprocess .write_abacus_csr_file import write_blocks_to_abacus_csr
1820
1921# Configure logging to file 'job_monitor.log'
2022logging .basicConfig (filename = 'job_monitor.log' , level = logging .INFO , format = '%(asctime)s - %(levelname)s - %(message)s' )
2123
22- def submit_job (job_folder , cmd_line ):
23- """Submits a job to the local system using subprocess.
2424
25- Args:
26- job_folder (str): Path to the job directory.
27- cmd_line (str): Command line to execute the job.
25+ # ==========================================
26+ # Helper Functions
27+ # ==========================================
28+
29+ def get_abacus_csr_name (matrix_symbol : str ) -> str :
30+ """根据 matrix_symbol 返回标准的 Abacus CSR 文件名。"""
31+ symbol_map = {
32+ 'DM' : 'dmrs1_nao.csr' ,
33+ 'H' : 'hrs1_nao.csr' ,
34+ 'S' : 'srs1_nao.csr'
35+ }
36+ return symbol_map .get (matrix_symbol , f'{ matrix_symbol .lower ()} rs1_nao.csr' )
37+
38+
39+ # ==========================================
40+ # Main Logic
41+ # ==========================================
2842
29- Returns:
30- subprocess.Popen: Process object representing the submitted job.
31- """
43+ def submit_job (job_folder , cmd_line ):
44+ """Submits a job to the local system using subprocess."""
3245 os .chdir (job_folder )
3346 process = subprocess .Popen (cmd_line , stdout = subprocess .PIPE , stderr = subprocess .PIPE , shell = True )
3447 return process
@@ -65,20 +78,11 @@ def clean_out_files(out_folder_path, rm_out_files_list=['INPUT']):
6578
6679
6780def monitor_jobs (job_queue , active_jobs , cmd_line , clean_files_flag , rm_out_files_list ):
68- """Monitors active jobs and submits new ones from the queue.
69-
70- This function runs in a separate thread and continuously checks the status of active jobs.
71- When a job finishes, it removes it from the active_jobs dictionary and submits a new job from the queue if available.
72-
73- Args:
74- job_queue (Queue): Queue containing job folders to be processed.
75- active_jobs (dict): Dictionary of currently running jobs (job_folder: process).
76- cmd_line (str): Command line to execute for each job.
77- """
81+ """Monitors active jobs and submits new ones from the queue."""
7882 while True :
7983 for job_folder , process in list (active_jobs .items ()):
8084 retcode = process .poll ()
81- if retcode is not None : # Job has finished (poll() returns return code when process terminates)
85+ if retcode is not None : # Job has finished
8286 del active_jobs [job_folder ]
8387 with open (os .path .join (job_folder , 'FINISHED' ), 'w' ) as f :
8488 pass
@@ -91,19 +95,7 @@ def monitor_jobs(job_queue, active_jobs, cmd_line, clean_files_flag, rm_out_file
9195
9296
9397def split_database (db_path , output_prefix , structures_per_split ):
94- """Splits an ase.db database into smaller databases.
95-
96- This function divides a large ASE database into smaller databases, each containing a specified number of structures.
97- This is useful for parallel processing, especially in remote submissions.
98-
99- Args:
100- db_path (str): Path to the input ASE database file.
101- output_prefix (str): Prefix for the output split database files (e.g., 'split_db').
102- structures_per_split (int): Number of structures to include in each split database.
103-
104- Returns:
105- list[str]: List of paths to the created split database files.
106- """
98+ """Splits an ase.db database into smaller databases."""
10799 db = connect (db_path )
108100 total_ids = list (db .get_ids ())
109101 n_splits = math .ceil (len (total_ids ) / structures_per_split ) # Calculate number of splits needed
@@ -123,15 +115,7 @@ def split_database(db_path, output_prefix, structures_per_split):
123115
124116
125117def modify_input_file (parameters ):
126- """
127- Modifies the 'INPUT' file based on the provided parameters.
128-
129- Args:
130- parameters (dict): A dictionary where keys are keywords and values are
131- the new values to set in the INPUT file.
132- For example:
133- {'ntype': '3', 'ecutwfc': '120'}
134- """
118+ """Modifies the 'INPUT' file based on the provided parameters."""
135119 filepath = 'INPUT'
136120 keyword_found_in_file = {} # Track if keyword is found in the file
137121 for keyword in parameters :
@@ -161,7 +145,8 @@ def modify_input_file(parameters):
161145 f .writelines (modified_lines )
162146
163147
164- def prepare_job_directories (db_src_path , common_folder_path , cooking_path , pp_orb_info , has_kpoints = False , prep_with_abacus_test_cmd = None ):
148+ def prepare_job_directories (db_src_path , common_folder_path , cooking_path , pp_orb_info , has_kpoints = False ,
149+ csr_key = 'predicted_data' , prep_with_abacus_test_cmd = None ):
165150 cwd_ = os .getcwd ()
166151 os .makedirs (cooking_path , exist_ok = True )
167152 db_src_path = os .path .abspath (db_src_path )
@@ -171,8 +156,12 @@ def prepare_job_directories(db_src_path, common_folder_path, cooking_path, pp_or
171156 job_dir = os .path .join (cooking_path , a_row .hpc_id )
172157 os .makedirs (job_dir , exist_ok = True )
173158 os .chdir (job_dir )
159+
160+ # 1. Write Structure
174161 write_abacus (os .path .join (job_dir , 'STRU' ), an_atoms , scaled = False ,
175162 pp = pp_orb_info ['pp' ], basis = pp_orb_info ['basis' ])
163+
164+ # 2. Copy Common Files
176165 for item in os .listdir (common_folder_path ):
177166 if os .path .exists (item ):
178167 continue
@@ -181,13 +170,35 @@ def prepare_job_directories(db_src_path, common_folder_path, cooking_path, pp_or
181170 shutil .copytree (src_file , item )
182171 else :
183172 shutil .copy2 (src_file , item ) # copy2 to preserve metadata
184- # ntype = len(set(an_atoms.get_atomic_numbers()))
173+
174+ # 3. Write CSR File (If exists in DB)
175+ # 最小侵入式修改:直接从 data 中读取并写入文件
176+ if 'csr_info' in a_row .data and csr_key in a_row .data :
177+ csr_info = a_row .data ['csr_info' ]
178+ blocks = a_row .data [csr_key ]
179+ matrix_symbol = csr_info .get ('matrix_symbol' , 'H' )
180+
181+ # 获取文件名并拼接当前 job_dir 路径
182+ csr_filename = get_abacus_csr_name (matrix_symbol )
183+ output_csr_path = os .path .join (job_dir , csr_filename )
184+
185+ write_blocks_to_abacus_csr (
186+ matrix_symbol = matrix_symbol ,
187+ atomic_numbers = an_atoms .get_atomic_numbers (),
188+ basis_dict = csr_info ['basis_dict' ],
189+ blocks_dict = blocks ,
190+ output_path = output_csr_path
191+ )
192+
193+ # 4. Modify Input
185194 modify_input_file ({})
186195
196+ # 5. Run abacus test (if needed)
187197 if prep_with_abacus_test_cmd not in (None , 'None' ):
188198 prep_with_abacus_test_cmd = prep_with_abacus_test_cmd + f' -j ./'
189199 os .system (prep_with_abacus_test_cmd )
190200
201+ # 6. Handle Kpoints (if needed)
191202 if has_kpoints :
192203 save_direct_kpoints (kpoints = a_row .data ['kpoints' ])
193204 np .save ('old_bands.npy' , np .array (a_row .data ['bands' ]))
@@ -203,26 +214,13 @@ def create_local_handler_file(n_parallel_jobs,
203214 cmd_line ,
204215 prep_with_abacus_test_cmd = None ,
205216 common_folder_path = 'public' ,
217+ csr_key = 'predicted_data' ,
206218 local_db_name = 'sub_structures.db' ,
207219 clean_files_flag = False ,
208220 rm_out_files_list = [],
209221 has_kpoints = False ,
210222 output_file = 'local_handler.py' ):
211- """
212- Create a local_handler.py file based on the provided parameters.
213-
214- Parameters:
215- n_parallel_jobs: Number of parallel jobs.
216- common_folder_path: Path to the common folder.
217- cmd_line: Command line string to be executed.
218- pp_orb_info: info for pp and orb.
219- prep_with_abacus_test_cmd: command to call abacus-test to prepare local workbase.
220- local_db_name: Name or path of the local database. (Default: 'sub_structures.db')
221- clean_files_flag: Boolean flag to indicate whether to clean files. (Default: False)
222- rm_out_files_list: List of output files to be removed. (Default: [])
223- output_file: Name of the file to be generated. (Default: 'local_handler.py')
224- """
225- # Use f-string to format the file content with the provided parameters
223+ """Create a local_handler.py file based on the provided parameters."""
226224 file_content = f"""import os
227225import sys
228226from ase.calculators.abacus import Abacus
@@ -238,14 +236,14 @@ def create_local_handler_file(n_parallel_jobs,
238236 local_db_name='{ local_db_name } ',
239237 common_folder_path='{ common_folder_path } ',
240238 cmd_line='{ cmd_line } ',
239+ csr_key='{ csr_key } ',
241240 clean_files_flag={ clean_files_flag } ,
242241 pp_orb_info={ pp_orb_info } ,
243242 prep_with_abacus_test_cmd="{ prep_with_abacus_test_cmd } ",
244243 rm_out_files_list={ rm_out_files_list } ,
245244 has_kpoints={ has_kpoints }
246245)
247246"""
248- # Write the formatted content to the specified file
249247 with open (output_file , 'w' ) as f :
250248 f .write (file_content )
251249
@@ -254,16 +252,17 @@ def run_jobs_locally(n_parallel_jobs, cmd_line,
254252 common_folder_path ,
255253 pp_orb_info ,
256254 prep_with_abacus_test_cmd = None ,
255+ csr_key = 'predicted_data' ,
257256 clean_files_flag = False ,
258257 local_db_name = 'sub_structures.db' ,
259258 rm_out_files_list : list = [],
260- has_kpoints = False ,):
261-
259+ has_kpoints = False , ):
262260 cwd_ = os .getcwd ()
263261 cooking_path = os .path .abspath ('cooking' )
264262 common_folder_path = os .path .abspath (common_folder_path )
265263 prepare_job_directories (db_src_path = local_db_name , common_folder_path = common_folder_path , cooking_path = cooking_path ,
266- pp_orb_info = pp_orb_info , has_kpoints = has_kpoints , prep_with_abacus_test_cmd = prep_with_abacus_test_cmd )
264+ pp_orb_info = pp_orb_info , has_kpoints = has_kpoints , csr_key = csr_key ,
265+ prep_with_abacus_test_cmd = prep_with_abacus_test_cmd )
267266
268267 job_queue = Queue ()
269268 job_folder_path_list = []
@@ -281,7 +280,8 @@ def run_jobs_locally(n_parallel_jobs, cmd_line,
281280 active_jobs [job_folder ] = submit_job (job_folder , cmd_line )
282281
283282 # Start monitoring thread
284- monitor_thread = Thread (target = monitor_jobs , args = (job_queue , active_jobs , cmd_line , clean_files_flag , rm_out_files_list ), daemon = True )
283+ monitor_thread = Thread (target = monitor_jobs ,
284+ args = (job_queue , active_jobs , cmd_line , clean_files_flag , rm_out_files_list ), daemon = True )
285285 monitor_thread .start ()
286286
287287 os .chdir (cwd_ )
@@ -294,7 +294,8 @@ def run_jobs_locally(n_parallel_jobs, cmd_line,
294294 log_remaining_time (start_time = start_time , n_completed_jobs = n_completed_jobs , n_total_jobs = n_total_jobs )
295295
296296
297- def run_jobs_remotely (n_parallel_machines , resrc_info , machine_info , local_job_para , db_src_path , common_folder_path , pp_orb_info_path , id_name = None ):
297+ def run_jobs_remotely (n_parallel_machines , resrc_info , machine_info , local_job_para , db_src_path , common_folder_path ,
298+ pp_orb_info_path , check_file_name = 'dmrs1_nao.csr' , id_name = None ):
298299 cwd_ = os .getcwd ()
299300 db_src_path = os .path .abspath (db_src_path )
300301 common_folder_path = os .path .abspath (common_folder_path )
@@ -339,7 +340,8 @@ def run_jobs_remotely(n_parallel_machines, resrc_info, machine_info, local_job_p
339340 with connect ('sub_structures.db' ) as sub_db :
340341 start_index = i * sub_n_mols
341342 end_index = (i + 1 ) * sub_n_mols if i < actual_machine_used - 1 else len (random_idx_list )
342- indices_for_sub = random_idx_list [start_index :end_index ] # Slice the random index list for the current sub
343+ indices_for_sub = random_idx_list [
344+ start_index :end_index ] # Slice the random index list for the current sub
343345 for real_idx in indices_for_sub :
344346 for a_row in src_db .select (id = real_idx + 1 ):
345347 pass
@@ -373,6 +375,6 @@ def run_jobs_remotely(n_parallel_machines, resrc_info, machine_info, local_job_p
373375 copy_failed_folders (
374376 source_dir = cooking_path ,
375377 target_folder_name = 'OUT.ABACUS' ,
376- check_file_name = 'istate.info' ,
378+ check_file_name = check_file_name ,
377379 dump_dir = os .path .join (cwd_ , 'failed_jobs' )
378380 )
0 commit comments