Skip to content

Commit e410d3a

Browse files
committed
Fix some bugs in workflow.
1 parent e2ce29a commit e410d3a

18 files changed

Lines changed: 1118 additions & 1234 deletions

File tree

deepks/io/input/dispatcher.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,19 @@ def dispatch_command(config):
3131
scf_soft = config.get('scf_soft', 'pyscf')
3232
backend = get_scf_backend(scf_soft)
3333
backend.collect_stats(**config)
34+
elif task_type == 'scf_task':
35+
# Low-level per-task SCF runner (called by BatchTask in iterate workflow)
36+
from deepks.physics.backends.pyscf.run import main as scf_run_main
37+
# Strip orchestration-only keys that pyscf.run.main() doesn't understand
38+
scf_config = {k: v for k, v in config.items()
39+
if k not in ('type', 'scf_soft')}
40+
scf_run_main(**scf_config)
41+
elif task_type == 'train_task':
42+
# Low-level training runner (called by BatchTask in iterate workflow)
43+
from deepks.ml.train.train import main as train_main
44+
train_config = {k: v for k, v in config.items()
45+
if k not in ('type',)}
46+
train_main(**train_config)
3447
elif task_type == 'iterate':
3548
# Use new iterate workflow
3649
from deepks.workflows.iterate import run_iterate_workflow

deepks/io/utils.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import os
44
import shutil
5+
import warnings
56
from collections.abc import Mapping
67
from glob import glob
78
from itertools import chain
@@ -96,6 +97,15 @@ def save_yaml(data, file_path):
9697
yaml.dump(data, fp)
9798

9899

100+
def dump_yaml_str(data):
101+
"""Serialize *data* to a YAML string (ruamel.yaml safe mode)."""
102+
import io
103+
buf = io.StringIO()
104+
yaml = YAML(typ='safe', pure=True)
105+
yaml.dump(data, buf)
106+
return buf.getvalue()
107+
108+
99109
def load_array(file):
100110
ext = os.path.splitext(file)[-1]
101111
if 'npy' in ext:
@@ -192,3 +202,51 @@ def create_dir(dirname, backup=False):
192202
os.makedirs(dirname)
193203
else:
194204
assert dirname.is_dir(), f'{dirname} is not a dir'
205+
206+
207+
# ---------------------------------------------------------------------------
208+
# Array shape coercion helpers
209+
# ---------------------------------------------------------------------------
210+
211+
def coerce_box(arr, nframes, fname="box.npy"):
212+
"""Ensure box array has shape (nframes, 9); accept (nframes, 3, 3)."""
213+
if arr.shape == (nframes, 3, 3):
214+
warnings.warn(f"{fname}: got shape {arr.shape}, reshaping to ({nframes}, 9).")
215+
return arr.reshape(nframes, 9)
216+
if arr.shape != (nframes, 9):
217+
raise ValueError(f"{fname}: expected shape ({nframes}, 9), got {arr.shape}.")
218+
return arr
219+
220+
221+
def coerce_energy(arr, nframes, fname="energy.npy"):
222+
"""Ensure energy array has shape (nframes, 1); accept (nframes,)."""
223+
if arr.shape == (nframes,):
224+
warnings.warn(f"{fname}: got shape {arr.shape}, reshaping to ({nframes}, 1).")
225+
return arr.reshape(nframes, 1)
226+
if arr.shape != (nframes, 1):
227+
raise ValueError(f"{fname}: expected shape ({nframes}, 1), got {arr.shape}.")
228+
return arr
229+
230+
231+
def coerce_stress(arr, nframes, fname="stress.npy"):
232+
"""Ensure stress array has shape (nframes, 6) upper-triangle (xx,xy,xz,yy,yz,zz).
233+
234+
Accepted input shapes:
235+
(nframes, 6) -- already upper-triangle, returned as-is.
236+
(nframes, 3,3) -- full matrix, reshaped then upper-triangle sliced.
237+
(nframes, 9) -- full flat, upper-triangle sliced.
238+
"""
239+
if arr.shape == (nframes, 6):
240+
return arr
241+
if arr.shape == (nframes, 3, 3):
242+
warnings.warn(
243+
f"{fname}: got shape {arr.shape}, reshaping to ({nframes}, 9) "
244+
f"then taking upper-triangle to ({nframes}, 6)."
245+
)
246+
arr = arr.reshape(nframes, 9)
247+
if arr.shape == (nframes, 9):
248+
warnings.warn(f"{fname}: got shape {arr.shape}, taking upper-triangle to ({nframes}, 6).")
249+
return arr[:, [0, 1, 2, 4, 5, 8]]
250+
raise ValueError(
251+
f"{fname}: expected ({nframes},6), ({nframes},9), or ({nframes},3,3), got {arr.shape}."
252+
)

deepks/ml/train/train.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,6 @@ def main(train_paths, test_paths=None,
257257
# print("all train time:",end-start)
258258

259259

260-
if __name__ == "__main__":
261-
from deepks.__main__ import train_cli as cli
262-
cli()
260+
# This module is not intended to be run directly.
261+
# Training is invoked via PythonTask from the iterate workflow (template.py),
262+
# which calls main() directly in-process.

deepks/orchestration/scheduler/job/dispatcher.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,15 @@ def submit_jobs(self,
227227
instance_id)
228228
job_record.dump()
229229
else :
230-
# finished job, append a None to list
230+
# finished job: try to clean up leftover .sub and tag files
231+
job_uuid = job_record.get_uuid(cur_hash) if job_record.check_submitted(cur_hash) else None
232+
if job_uuid is not None:
233+
try:
234+
context = self.context_fn(work_path, self.session, job_uuid)
235+
context.clean()
236+
except Exception:
237+
pass
238+
# append a None to list
231239
job_list.append(None)
232240
assert(len(job_list) == nchunks)
233241
job_handler = {

deepks/orchestration/scheduler/job/shell.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,39 @@ def _default_item(resources, key, value) :
99

1010
class Shell(Batch) :
1111

12+
def __init__(self, context, uuid_names=True):
13+
super().__init__(context, uuid_names=uuid_names)
14+
self.proc = None
15+
1216
def check_status(self) :
1317
if self.check_finish_tag():
1418
return JobStatus.finished
1519
elif self.check_running():
1620
return JobStatus.running
1721
else:
1822
return JobStatus.terminated
19-
## note: check_submitted_tag() only tells us the .sub file exists (was submitted),
20-
## not that the process is still running. Using it here caused an infinite loop
21-
## when a job failed: the .sub file remains, so check_status() kept returning
22-
## 'running' forever instead of 'terminated'.
2323

2424
def check_running(self):
25+
# Primary check: use the stored Popen object (reliable, no ps-grep fragility)
26+
if self.proc is not None:
27+
if self.proc.poll() is None:
28+
# process is still alive
29+
return True
30+
# process has exited; give the finish tag a moment to be flushed
31+
time.sleep(2)
32+
return False
33+
# Fallback for recovered jobs (proc not available): use ps grep
2534
uuid_names = self.context.job_uuid
26-
## Check if the uuid.sub is running on remote machine
27-
cnt = 0
28-
ret, stdin, stdout, stderr = self.context.block_call("ps aux | grep %s"%uuid_names)
35+
ret, stdin, stdout, stderr = self.context.block_call("ps aux | grep %s" % uuid_names)
2936
response_list = stdout.read().decode('utf-8').split("\n")
3037
for response in response_list:
31-
if uuid_names + ".sub" in response:
38+
if uuid_names + ".sub" in response:
3239
return True
3340
return False
34-
41+
3542
def exec_sub_script(self, script_str):
3643
self.context.write_file(self.sub_script_name, script_str)
37-
self.proc = self.context.call('cd %s && exec bash %s' % (self.context.remote_root, self.sub_script_name))
44+
self.proc = self.context.call('cd %s && bash %s' % (self.context.remote_root, self.sub_script_name))
3845

3946
def default_resources(self, res_) :
4047
if res_ is None :

deepks/orchestration/workflow/task.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,11 +190,13 @@ class BatchTask(AbstructTask):
190190
- outlog: the file to redirect stdout to.
191191
- errlog: the file to redirect stderr to.
192192
- *_files: the files to forward or backward.
193+
- write_files: dict of {filename: content} to write into workdir during preprocess.
193194
'''
194-
def __init__(self, cmds,
195-
dispatcher=None, resources=None,
196-
outlog='log', errlog='err',
195+
def __init__(self, cmds,
196+
dispatcher=None, resources=None,
197+
outlog='log', errlog='err',
197198
forward_files=None, backward_files=None,
199+
write_files=None,
198200
**task_args):
199201
super().__init__(**task_args)
200202
self.cmds = check_list(cmds)
@@ -209,10 +211,18 @@ def __init__(self, cmds,
209211
self.errlog = errlog
210212
self.forward_files = check_list(forward_files)
211213
self.backward_files = check_list(backward_files)
212-
214+
self.write_files = write_files or {}
215+
216+
def preprocess(self):
217+
super().preprocess()
218+
for fname, content in self.write_files.items():
219+
fpath = self.workdir / fname
220+
fpath.parent.mkdir(parents=True, exist_ok=True)
221+
fpath.write_text(content)
222+
213223
def execute(self):
214224
tdict = self.make_dict(base=self.workdir)
215-
self.dispatcher.run_jobs([tdict], group_size=1, work_path='.',
225+
self.dispatcher.run_jobs([tdict], group_size=1, work_path='.',
216226
resources=self.resources, forward_task_deref=True,
217227
outlog=self.outlog, errlog=self.errlog)
218228

deepks/physics/backends/abacus/utils.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,26 +44,49 @@ def read_csr(file, dtype=torch.float64):
4444
all_values = []
4545
max_iR = 0
4646
dim = 0
47+
48+
def _read_tokens(f, n, convert):
49+
"""Read exactly n tokens from f, skipping comment/blank lines."""
50+
tokens = []
51+
while len(tokens) < n:
52+
line = f.readline()
53+
if not line:
54+
break
55+
s = line.strip()
56+
if not s or s.startswith('#'):
57+
continue
58+
tokens.extend(s.split())
59+
return [convert(t) for t in tokens[:n]]
60+
4761
with open(file, 'r') as f:
4862
dim = int(f.readline().split()[-1])
4963
num = int(f.readline().split()[-1])
5064
for _ in range(num):
5165
nnz = 0
5266
while nnz == 0:
53-
r = f.readline().split()
54-
if len(r) == 0:
67+
line = f.readline()
68+
if not line:
5569
break
56-
Rx, Ry, Rz, nnz = int(r[0]), int(r[1]), int(r[2]), int(r[3])
70+
s = line.strip()
71+
if not s or s.startswith('#'):
72+
continue
73+
r = s.split()
74+
if len(r) < 4:
75+
continue
76+
try:
77+
Rx, Ry, Rz, nnz = int(r[0]), int(r[1]), int(r[2]), int(r[3])
78+
except ValueError:
79+
continue
5780
iRx = R2iR(Rx)
5881
iRy = R2iR(Ry)
5982
iRz = R2iR(Rz)
6083
if max_iR < max(iRx, iRy, iRz):
6184
max_iR = max(iRx, iRy, iRz)
6285
if nnz == 0:
6386
break
64-
data = [float(x) for x in f.readline().split()]
65-
indices = [int(x) for x in f.readline().split()]
66-
indptr = [int(x) for x in f.readline().split()]
87+
data = _read_tokens(f, nnz, float)
88+
indices = _read_tokens(f, nnz, int)
89+
indptr = _read_tokens(f, dim + 1, int)
6790
matrix = csr_matrix((data, indices, indptr), shape=(dim, dim))
6891
matrix_coo = matrix.tocoo()
6992
rows = matrix_coo.row

deepks/physics/backends/pyscf/run.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,6 @@ def main(systems, model_file="model.pth", basis='ccpvdz',
249249
print('group finished')
250250

251251

252-
if __name__ == "__main__":
253-
from deepks.__main__ import scf_cli as cli
254-
cli()
252+
# This module is not intended to be run directly.
253+
# SCF calculations are invoked via BatchTask from the iterate workflow (template.py),
254+
# which calls make_scf_task() using the pyscf backend runner.

0 commit comments

Comments
 (0)