Skip to content

Commit dcf0ddb

Browse files
committed
fix: generate relabel inputs with matching arguments
Fixes #1910 Coding-Agent: Codex Codex-Version: codex-cli 0.149.0 Model: gpt-5.6-sol Reasoning-Effort: xhigh
1 parent d5ce577 commit dcf0ddb

2 files changed

Lines changed: 122 additions & 35 deletions

File tree

dpgen/tools/relabel.py

Lines changed: 43 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,9 @@ def copy_pp_files(tdir, fp_pp_path, fp_pp_files):
5555
os.chdir(cwd)
5656

5757

58-
def make_vasp(tdir, fp_params):
59-
cwd = os.getcwd()
60-
os.chdir(tdir)
61-
incar = make_vasp_incar(fp_params)
62-
with open("INCAR", "w") as fp:
63-
fp.write(incar)
64-
os.chdir(cwd)
58+
def make_vasp(tdir, fp_incar):
59+
"""Copy a user-provided INCAR into a relabel task directory."""
60+
make_vasp_incar(tdir, fp_incar)
6561

6662

6763
def make_vasp_incar(tdir, fp_incar):
@@ -90,15 +86,39 @@ def make_siesta(tdir, fp_params, fp_pp_path, fp_pp_files):
9086
os.chdir(cwd)
9187

9288

89+
def make_non_vasp_input(tdir, fp_style, fp_jdata, mass_map, fp_pp_path, fp_pp_files):
90+
"""Generate a PWSCF or Siesta input using the matching backend arguments."""
91+
if "user_fp_params" in fp_jdata:
92+
fp_params = fp_jdata["user_fp_params"]
93+
user_input = True
94+
else:
95+
fp_params = fp_jdata["fp_params"]
96+
user_input = False
97+
98+
if fp_style == "pwscf":
99+
make_pwscf(
100+
tdir,
101+
fp_params,
102+
mass_map,
103+
fp_pp_path,
104+
fp_pp_files,
105+
user_input,
106+
)
107+
elif fp_style == "siesta":
108+
make_siesta(tdir, fp_params, fp_pp_path, fp_pp_files)
109+
110+
93111
def create_init_tasks(target_folder, param_file, output, fp_json, verbose=True):
94112
target_folder = os.path.abspath(target_folder)
95113
output = os.path.abspath(output)
96114
tool_path = os.path.join(
97115
os.path.dirname(os.path.realpath(__file__)), "..", "template"
98116
)
99-
jdata = json.load(open(os.path.join(target_folder, param_file)))
117+
with open(os.path.join(target_folder, param_file)) as param_fp:
118+
jdata = json.load(param_fp)
100119
update_mass_map(jdata)
101-
fp_jdata = json.load(open(fp_json))
120+
with open(fp_json) as fp_config:
121+
fp_jdata = json.load(fp_config)
102122
# fp settings
103123
mass_map = jdata["mass_map"]
104124
type_map = jdata["type_map"]
@@ -138,18 +158,10 @@ def create_init_tasks(target_folder, param_file, output, fp_json, verbose=True):
138158
if os.path.lexists("INCAR"):
139159
os.remove("INCAR")
140160
os.symlink(os.path.relpath(os.path.join(output, "INCAR")), "INCAR")
141-
elif fp_style == "pwscf":
142-
try:
143-
fp_params = fp_jdata["user_fp_params"]
144-
user_input = True
145-
except Exception:
146-
fp_params = fp_jdata["fp_params"]
147-
user_input = False
148-
make_pwscf(
149-
".", fp_params, mass_map, fp_pp_files, fp_pp_files, user_input
161+
elif fp_style in {"pwscf", "siesta"}:
162+
make_non_vasp_input(
163+
".", fp_style, fp_jdata, mass_map, fp_pp_path, fp_pp_files
150164
)
151-
elif fp_style == "siesta":
152-
make_siesta(".", fp_params, fp_pp_files, fp_pp_files)
153165
os.chdir(cwd_)
154166

155167

@@ -161,9 +173,11 @@ def create_tasks(
161173
tool_path = os.path.join(
162174
os.path.dirname(os.path.realpath(__file__)), "..", "template"
163175
)
164-
jdata = json.load(open(os.path.join(target_folder, param_file)))
176+
with open(os.path.join(target_folder, param_file)) as param_fp:
177+
jdata = json.load(param_fp)
165178
update_mass_map(jdata)
166-
fp_jdata = json.load(open(fp_json))
179+
with open(fp_json) as fp_config:
180+
fp_jdata = json.load(fp_config)
167181
# goto input
168182
cwd = os.getcwd()
169183
os.chdir(target_folder)
@@ -176,8 +190,10 @@ def create_tasks(
176190
cwd_ = os.getcwd()
177191
os.chdir(target_folder)
178192
fp_pp_path = os.path.abspath(fp_pp_path)
193+
fp_incar = None
194+
if fp_style == "vasp":
195+
fp_incar = os.path.abspath(fp_jdata["fp_incar"])
179196
os.chdir(cwd_)
180-
fp_params = fp_jdata["fp_params"]
181197
# collect tasks from iter dirs
182198
sys_tasks = [[] for ii in sys]
183199
sys_tasks_record = [[] for ii in sys]
@@ -237,7 +253,7 @@ def create_tasks(
237253
os.makedirs(output, exist_ok=True)
238254
if fp_style == "vasp":
239255
copy_pp_files(output, fp_pp_path, fp_pp_files)
240-
make_vasp_incar(fp_params, output)
256+
make_vasp_incar(output, fp_incar)
241257
if fp_style == "pwscf":
242258
copy_pp_files(output, fp_pp_path, fp_pp_files)
243259
if fp_style == "siesta":
@@ -273,18 +289,10 @@ def create_tasks(
273289
if os.path.lexists("INCAR"):
274290
os.remove("INCAR")
275291
os.symlink(os.path.relpath(os.path.join(output, "INCAR")), "INCAR")
276-
elif fp_style == "pwscf":
277-
try:
278-
fp_params = fp_jdata["user_fp_params"]
279-
user_input = True
280-
except Exception:
281-
fp_params = fp_jdata["fp_params"]
282-
user_input = False
283-
make_pwscf(
284-
".", fp_params, mass_map, fp_pp_files, fp_pp_files, user_input
292+
elif fp_style in {"pwscf", "siesta"}:
293+
make_non_vasp_input(
294+
".", fp_style, fp_jdata, mass_map, fp_pp_path, fp_pp_files
285295
)
286-
elif fp_style == "siesta":
287-
make_siesta(".", fp_params, mass_map, fp_pp_files, fp_pp_files)
288296
os.chdir(cwd_)
289297
os.chdir(cwd)
290298

tests/tools/test_relabel.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import json
2+
import tempfile
3+
import unittest
4+
from pathlib import Path
5+
from unittest.mock import patch
6+
7+
from dpgen.tools import relabel
8+
9+
10+
class TestRelabelInputs(unittest.TestCase):
11+
def test_create_tasks_copies_vasp_incar_to_output(self):
12+
with tempfile.TemporaryDirectory() as tmpdir:
13+
root = Path(tmpdir)
14+
job = root / "job"
15+
output = root / "output"
16+
pp_dir = job / "pp"
17+
job.mkdir()
18+
pp_dir.mkdir()
19+
(pp_dir / "H.POTCAR").write_text("potential")
20+
(job / "INCAR.source").write_text("ENCUT = 500\n")
21+
(job / "param.json").write_text(
22+
json.dumps(
23+
{
24+
"mass_map": [1.0],
25+
"type_map": ["H"],
26+
"sys_configs": [],
27+
}
28+
)
29+
)
30+
fp_json = root / "fp.json"
31+
fp_json.write_text(
32+
json.dumps(
33+
{
34+
"fp_style": "vasp",
35+
"fp_pp_path": "pp",
36+
"fp_pp_files": ["H.POTCAR"],
37+
"fp_incar": "INCAR.source",
38+
}
39+
)
40+
)
41+
42+
relabel.create_tasks(job, "param.json", output, fp_json, verbose=False)
43+
44+
self.assertEqual((output / "INCAR").read_text(), "ENCUT = 500\n")
45+
self.assertEqual((output / "H.POTCAR").read_text(), "potential")
46+
47+
@patch("dpgen.tools.relabel.make_pwscf")
48+
def test_pwscf_arguments_match_helper_signature(self, make_pwscf):
49+
params = {"ecut": 100}
50+
relabel.make_non_vasp_input(
51+
"task",
52+
"pwscf",
53+
{"user_fp_params": params},
54+
[1.0],
55+
"/pp",
56+
["H.UPF"],
57+
)
58+
59+
make_pwscf.assert_called_once_with(
60+
"task", params, [1.0], "/pp", ["H.UPF"], True
61+
)
62+
63+
@patch("dpgen.tools.relabel.make_siesta")
64+
def test_siesta_arguments_match_helper_signature(self, make_siesta):
65+
params = {"ecut": 100}
66+
relabel.make_non_vasp_input(
67+
"task",
68+
"siesta",
69+
{"fp_params": params},
70+
[1.0],
71+
"/pp",
72+
["H.psf"],
73+
)
74+
75+
make_siesta.assert_called_once_with("task", params, "/pp", ["H.psf"])
76+
77+
78+
if __name__ == "__main__":
79+
unittest.main()

0 commit comments

Comments
 (0)