Skip to content

Commit 279738d

Browse files
authored
merge devel to master and release v0.13.2 (#1789)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added automatic calculation of spin multiplicity for CP2K input generation based on system composition and charge. * **Bug Fixes** * Improved handling of pseudopotential and orbital file mapping for atomic species in ABACUS SCF input preparation. * Prevented empty systems from being included in CP2K post-processing results. * **Refactor** * Standardized reading of model deviation data and improved coordinate shuffling behavior. * Extended perturbation handling in VASP MD data collection. * **Chores** * Updated pre-commit hook version. * Simplified example configuration by removing redundant batch size entries. * **Tests** * Removed unnecessary test data assignment in bulk ABACUS structure test. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
2 parents 9b5fab4 + 41402ad commit 279738d

File tree

7 files changed

+212
-11
lines changed

7 files changed

+212
-11
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ repos:
2828

2929
# Python
3030
- repo: https://github.com/astral-sh/ruff-pre-commit
31-
rev: v0.9.6
31+
rev: v0.12.7
3232
hooks:
3333
- id: ruff
3434
args: ["--fix"]

dpgen/data/gen.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1071,7 +1071,7 @@ def coll_vasp_md(jdata):
10711071
# convert outcars
10721072
valid_outcars = []
10731073
for jj in scale:
1074-
for kk in range(pert_numb):
1074+
for kk in range(pert_numb + 1):
10751075
path_work = os.path.join(f"scale-{jj:.3f}", "%06d" % kk) # noqa: UP031
10761076
outcar = os.path.join(path_work, "OUTCAR")
10771077
# dlog.info("OUTCAR",outcar)

dpgen/generator/lib/abacus_scf.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,38 @@ def make_abacus_scf_stru(
213213
):
214214
sys_data_copy = copy.deepcopy(sys_data)
215215
# re-construct the path of files by pporb + file name
216-
fp_pp_files = [os.path.join(pporb, i) for i in fp_pp_files]
216+
# when element in sys_data is part of type_map/fp_pp_files
217+
# we need to only pass the pp_file in sys_data, but not all pp_files
218+
if type_map is None:
219+
type_map = sys_data_copy["atom_names"]
220+
221+
missing_atoms = set(sys_data_copy["atom_names"]) - set(type_map)
222+
if len(missing_atoms) > 0:
223+
raise ValueError(
224+
f"Some atoms in sys_data are not in type_map: {missing_atoms}. "
225+
"Please provide a valid type_map."
226+
)
227+
228+
if len(fp_pp_files) != len(type_map):
229+
raise ValueError(
230+
"The length of fp_pp_files should be equal to the length of type_map."
231+
)
232+
if fp_orb_files is not None and len(fp_orb_files) != len(type_map):
233+
raise ValueError(
234+
"The length of fp_orb_files should be equal to the length of type_map."
235+
)
236+
237+
fp_pp_files = [
238+
os.path.join(pporb, fp_pp_files[type_map.index(atom_name)])
239+
for atom_name in sys_data_copy["atom_names"]
240+
]
241+
217242
if fp_orb_files is not None:
218-
fp_orb_files = [os.path.join(pporb, i) for i in fp_orb_files]
243+
fp_orb_files = [
244+
os.path.join(pporb, fp_orb_files[type_map.index(atom_name)])
245+
for atom_name in sys_data_copy["atom_names"]
246+
]
247+
219248
if fp_dpks_descriptor is not None:
220249
fp_dpks_descriptor = os.path.join(pporb, fp_dpks_descriptor)
221250

dpgen/generator/lib/cp2k.py

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,127 @@
11
import numpy as np
22

3+
atomic_numbers = {
4+
"H": 1,
5+
"He": 2,
6+
"Li": 3,
7+
"Be": 4,
8+
"B": 5,
9+
"C": 6,
10+
"N": 7,
11+
"O": 8,
12+
"F": 9,
13+
"Ne": 10,
14+
"Na": 11,
15+
"Mg": 12,
16+
"Al": 13,
17+
"Si": 14,
18+
"P": 15,
19+
"S": 16,
20+
"Cl": 17,
21+
"Ar": 18,
22+
"K": 19,
23+
"Ca": 20,
24+
"Sc": 21,
25+
"Ti": 22,
26+
"V": 23,
27+
"Cr": 24,
28+
"Mn": 25,
29+
"Fe": 26,
30+
"Co": 27,
31+
"Ni": 28,
32+
"Cu": 29,
33+
"Zn": 30,
34+
"Ga": 31,
35+
"Ge": 32,
36+
"As": 33,
37+
"Se": 34,
38+
"Br": 35,
39+
"Kr": 36,
40+
"Rb": 37,
41+
"Sr": 38,
42+
"Y": 39,
43+
"Zr": 40,
44+
"Nb": 41,
45+
"Mo": 42,
46+
"Tc": 43,
47+
"Ru": 44,
48+
"Rh": 45,
49+
"Pd": 46,
50+
"Ag": 47,
51+
"Cd": 48,
52+
"In": 49,
53+
"Sn": 50,
54+
"Sb": 51,
55+
"Te": 52,
56+
"I": 53,
57+
"Xe": 54,
58+
"Cs": 55,
59+
"Ba": 56,
60+
"La": 57,
61+
"Ce": 58,
62+
"Pr": 59,
63+
"Nd": 60,
64+
"Pm": 61,
65+
"Sm": 62,
66+
"Eu": 63,
67+
"Gd": 64,
68+
"Tb": 65,
69+
"Dy": 66,
70+
"Ho": 67,
71+
"Er": 68,
72+
"Tm": 69,
73+
"Yb": 70,
74+
"Lu": 71,
75+
"Hf": 72,
76+
"Ta": 73,
77+
"W": 74,
78+
"Re": 75,
79+
"Os": 76,
80+
"Ir": 77,
81+
"Pt": 78,
82+
"Au": 79,
83+
"Hg": 80,
84+
"Tl": 81,
85+
"Pb": 82,
86+
"Bi": 83,
87+
"Po": 84,
88+
"At": 85,
89+
"Rn": 86,
90+
"Fr": 87,
91+
"Ra": 88,
92+
"Ac": 89,
93+
"Th": 90,
94+
"Pa": 91,
95+
"U": 92,
96+
"Np": 93,
97+
"Pu": 94,
98+
"Am": 95,
99+
"Cm": 96,
100+
"Bk": 97,
101+
"Cf": 98,
102+
"Es": 99,
103+
"Fm": 100,
104+
"Md": 101,
105+
"No": 102,
106+
"Lr": 103,
107+
"Rf": 104,
108+
"Db": 105,
109+
"Sg": 106,
110+
"Bh": 107,
111+
"Hs": 108,
112+
"Mt": 109,
113+
"Ds": 110,
114+
"Rg": 111,
115+
"Cn": 112,
116+
"Nh": 113,
117+
"Fl": 114,
118+
"Mc": 115,
119+
"Lv": 116,
120+
"Ts": 117,
121+
"Og": 118,
122+
}
123+
124+
3125
default_config = {
4126
"GLOBAL": {"PROJECT": "DPGEN"},
5127
"FORCE_EVAL": {
@@ -121,6 +243,46 @@ def iterdict(d, out_list, flag=None, indent=0):
121243
out_list.insert(index, " " * indent + k + " " + v)
122244

123245

246+
def calculate_multiplicity(atom_names, atom_types, charge=0):
247+
"""
248+
Calculate the multiplicity based on atom species, quantities, and system charge.
249+
250+
This function provides a basic heuristic for determining multiplicity:
251+
- Even number of electrons -> singlet (multiplicity = 1)
252+
- Odd number of electrons -> doublet (multiplicity = 2)
253+
254+
Note: This approach assumes that an odd electron count always results in a doublet state.
255+
It does not account for systems with multiple unpaired electrons, which can have higher
256+
multiplicities (e.g., triplet, quartet, etc.). Users should be aware of this limitation
257+
and use the function accordingly.
258+
259+
:param atom_names: List of element symbols.
260+
:param atom_types: List of atom type indices.
261+
:param charge: System charge (default: 0).
262+
:return: Multiplicity.
263+
"""
264+
# Calculate the total number of electrons
265+
total_electrons = 0
266+
for idx in atom_types:
267+
element = atom_names[idx]
268+
try:
269+
total_electrons += atomic_numbers[element]
270+
except KeyError:
271+
raise ValueError(f"Unknown element '{element}' encountered in atom_names.")
272+
273+
# Subtract/add electrons based on system charge
274+
# Positive charge means we remove electrons, negative charge means we add electrons
275+
total_electrons -= charge
276+
277+
# Determine multiplicity based on the total number of electrons
278+
# Even number of electrons -> singlet (multiplicity = 1)
279+
# Odd number of electrons -> doublet (multiplicity = 2)
280+
if total_electrons % 2 == 0:
281+
return 1
282+
else:
283+
return 2
284+
285+
124286
def make_cp2k_input(sys_data, fp_params):
125287
# covert cell to cell string
126288
cell = sys_data["cells"][0]
@@ -132,14 +294,27 @@ def make_cp2k_input(sys_data, fp_params):
132294
cell_c = np.array2string(cell[2, :])
133295
cell_c = cell_c[1:-1]
134296

297+
atom_names = sys_data["atom_names"]
298+
atom_types = sys_data["atom_types"]
299+
# Get system charge if provided, default to 0
300+
charge = sys_data.get("charge", 0)
301+
dft_params = fp_params.get("FORCE_EVAL", {}).get("DFT", {})
302+
if "MULTIPLICITY" in dft_params:
303+
multiplicity = dft_params["MULTIPLICITY"]
304+
else:
305+
multiplicity = calculate_multiplicity(atom_names, atom_types, charge)
306+
135307
# get update from user
136308
user_config = fp_params
137309
# get update from cell
138310
cell_config = {
139311
"FORCE_EVAL": {"SUBSYS": {"CELL": {"A": cell_a, "B": cell_b, "C": cell_c}}}
140312
}
313+
# get update for multiplicity
314+
multiplicity_config = {"FORCE_EVAL": {"DFT": {"MULTIPLICITY": multiplicity}}}
141315
update_dict(default_config, user_config)
142316
update_dict(default_config, cell_config)
317+
update_dict(default_config, multiplicity_config)
143318
# output list
144319
input_str = []
145320
iterdict(default_config, input_str)

dpgen/generator/run.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,7 +1305,7 @@ def make_model_devi(iter_index, jdata, mdata):
13051305
type_map=jdata["type_map"],
13061306
)
13071307
if shuffle_poscar:
1308-
system.data["coords"] = rng.permuted(system.data["coords"], axis=1)
1308+
rng.shuffle(system.data["coords"], axis=1)
13091309
if jdata.get("model_devi_nopbc", False):
13101310
system.remove_pbc()
13111311
system.to_lammps_lmp(os.path.join(conf_path, lmp_name))
@@ -2477,7 +2477,6 @@ def _select_by_model_devi_adaptive_trust_low(
24772477
for tt in modd_system_task:
24782478
with warnings.catch_warnings():
24792479
warnings.simplefilter("ignore")
2480-
model_devi = np.loadtxt(os.path.join(tt, "model_devi.out"))
24812480
model_devi = _read_model_devi_file(
24822481
tt, model_devi_f_avg_relative, model_devi_merge_traj
24832482
)
@@ -4524,8 +4523,9 @@ def post_fp_cp2k(iter_index, jdata, rfailed=None):
45244523
_sys = dpdata.LabeledSystem(
45254524
oo, fmt="cp2kdata/e_f", type_map=jdata["type_map"]
45264525
)
4527-
all_sys.append(_sys)
4528-
icount += 1
4526+
if len(_sys) > 0:
4527+
all_sys.append(_sys)
4528+
icount += 1
45294529

45304530
if (all_sys is not None) and (len(all_sys) > 0):
45314531
sys_data_path = os.path.join(work_path, f"data.{ss}")

examples/run/dp2.x-lammps-cp2k/methane/param-ch4.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323
]
2424
],
2525
"sys_batch_size": [
26-
8,
27-
8,
2826
8,
2927
8
3028
],

tests/data/test_gen_bulk_abacus.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ def test(self):
7676
def testSTRU(self):
7777
jdata = self.jdata
7878
jdata["from_poscar_path"] = "./Cu.STRU"
79-
jdata["potcars"] = ["abacus.in/Cu_ONCV_PBE-1.0.upf"]
8079
make_super_cell_STRU(jdata)
8180
make_abacus_relax(jdata, {"fp_resources": {}})
8281
make_scale_ABACUS(jdata)

0 commit comments

Comments
 (0)