Skip to content

Commit c36395f

Browse files
committed
update
1 parent e20985d commit c36395f

5 files changed

Lines changed: 169 additions & 99 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from ase.visualize import view
2+
from ase.db import connect
3+
from ase.io import read, write
4+
import os
5+
6+
7+
with connect('test_4_structures.db') as tgt_db:
8+
print(tgt_db.count())
9+
for a_row in tgt_db.select():
10+
an_atoms = a_row.toatoms()
11+
view(an_atoms)
12+
a=1
13+

examples/local_abacus_scf/multi_jobs_scf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
# -----------------------------------------------------------------------------------------------------------------
66
n_parallel_machines = 2
77
local_job_para = {
8-
'n_parallel_jobs': 2,
9-
'cmd_line': 'OMP_NUM_THREADS=1 mpirun -n 12 abacus',
8+
'n_parallel_jobs': 1,
9+
'cmd_line': 'OMP_NUM_THREADS=1 mpirun -n 6 abacus',
1010
}
1111
# -----------------------------------------------------------------------------------------------------------------
1212
local_root = os.getcwd()

examples/local_abacus_scf/public/INPUT

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ pseudo_dir PP_ORB # 所有赝势轨道位置
55
orbital_dir PP_ORB
66
ecutwfc 100 # 标准基组文件统一为 100 (文件名显示的)
77
scf_thr 1e-1 # 正式生产时调低
8-
scf_nmax 100
8+
scf_nmax 1
99
symmetry 0
1010
kspacing 0.08
1111
basis_type lcao
@@ -19,6 +19,6 @@ out_band 1 # output band structure
1919
out_chg 1 # output real space charge
2020
out_wfc_lcao 0 # output the wavefunction coefficients, Availability: Numerical atomic orbital basis
2121
out_mat_hs2 True # output ham and overlap matrix
22-
out_dm1 1 # output density matrix, out_dm with k-point algorithm is not implemented yet.
22+
out_dmr 1 # output density matrix, out_dm with k-point algorithm is not implemented yet.
2323
# mixing_type pulay 这个参数默认方法是 broyden,文档介绍说这个方法比 pulay 快
2424
# mixing_beta 0.7 这个参数我看是跟 nspin 相关,要不就不设置了,用默认的吧。默认的,不同的 nspin 这个值不一样

src/dprep/dpdispatcher_tools.py

Lines changed: 68 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,33 @@
1515
from dpdispatcher import Task, Submission, Machine, Resources
1616
from dprep.get_pp_orb_info import generate_pp_orb_dict
1717
from 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'
2022
logging.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

6780
def 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

9397
def 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

125117
def 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
227225
import sys
228226
from 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

Comments
 (0)