diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 234821eeb..c82eec2f8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,7 +28,7 @@ repos: # Python - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.6 + rev: v0.12.7 hooks: - id: ruff args: ["--fix"] diff --git a/dpgen/data/gen.py b/dpgen/data/gen.py index f0a88bcb4..5c10657d2 100644 --- a/dpgen/data/gen.py +++ b/dpgen/data/gen.py @@ -1071,7 +1071,7 @@ def coll_vasp_md(jdata): # convert outcars valid_outcars = [] for jj in scale: - for kk in range(pert_numb): + for kk in range(pert_numb + 1): path_work = os.path.join(f"scale-{jj:.3f}", "%06d" % kk) # noqa: UP031 outcar = os.path.join(path_work, "OUTCAR") # dlog.info("OUTCAR",outcar) diff --git a/dpgen/generator/lib/abacus_scf.py b/dpgen/generator/lib/abacus_scf.py index f04dbdd96..21f1e1377 100644 --- a/dpgen/generator/lib/abacus_scf.py +++ b/dpgen/generator/lib/abacus_scf.py @@ -213,9 +213,38 @@ def make_abacus_scf_stru( ): sys_data_copy = copy.deepcopy(sys_data) # re-construct the path of files by pporb + file name - fp_pp_files = [os.path.join(pporb, i) for i in fp_pp_files] + # when element in sys_data is part of type_map/fp_pp_files + # we need to only pass the pp_file in sys_data, but not all pp_files + if type_map is None: + type_map = sys_data_copy["atom_names"] + + missing_atoms = set(sys_data_copy["atom_names"]) - set(type_map) + if len(missing_atoms) > 0: + raise ValueError( + f"Some atoms in sys_data are not in type_map: {missing_atoms}. " + "Please provide a valid type_map." + ) + + if len(fp_pp_files) != len(type_map): + raise ValueError( + "The length of fp_pp_files should be equal to the length of type_map." + ) + if fp_orb_files is not None and len(fp_orb_files) != len(type_map): + raise ValueError( + "The length of fp_orb_files should be equal to the length of type_map." + ) + + fp_pp_files = [ + os.path.join(pporb, fp_pp_files[type_map.index(atom_name)]) + for atom_name in sys_data_copy["atom_names"] + ] + if fp_orb_files is not None: - fp_orb_files = [os.path.join(pporb, i) for i in fp_orb_files] + fp_orb_files = [ + os.path.join(pporb, fp_orb_files[type_map.index(atom_name)]) + for atom_name in sys_data_copy["atom_names"] + ] + if fp_dpks_descriptor is not None: fp_dpks_descriptor = os.path.join(pporb, fp_dpks_descriptor) diff --git a/dpgen/generator/lib/cp2k.py b/dpgen/generator/lib/cp2k.py index a73a94d7a..af151cc50 100644 --- a/dpgen/generator/lib/cp2k.py +++ b/dpgen/generator/lib/cp2k.py @@ -1,5 +1,127 @@ import numpy as np +atomic_numbers = { + "H": 1, + "He": 2, + "Li": 3, + "Be": 4, + "B": 5, + "C": 6, + "N": 7, + "O": 8, + "F": 9, + "Ne": 10, + "Na": 11, + "Mg": 12, + "Al": 13, + "Si": 14, + "P": 15, + "S": 16, + "Cl": 17, + "Ar": 18, + "K": 19, + "Ca": 20, + "Sc": 21, + "Ti": 22, + "V": 23, + "Cr": 24, + "Mn": 25, + "Fe": 26, + "Co": 27, + "Ni": 28, + "Cu": 29, + "Zn": 30, + "Ga": 31, + "Ge": 32, + "As": 33, + "Se": 34, + "Br": 35, + "Kr": 36, + "Rb": 37, + "Sr": 38, + "Y": 39, + "Zr": 40, + "Nb": 41, + "Mo": 42, + "Tc": 43, + "Ru": 44, + "Rh": 45, + "Pd": 46, + "Ag": 47, + "Cd": 48, + "In": 49, + "Sn": 50, + "Sb": 51, + "Te": 52, + "I": 53, + "Xe": 54, + "Cs": 55, + "Ba": 56, + "La": 57, + "Ce": 58, + "Pr": 59, + "Nd": 60, + "Pm": 61, + "Sm": 62, + "Eu": 63, + "Gd": 64, + "Tb": 65, + "Dy": 66, + "Ho": 67, + "Er": 68, + "Tm": 69, + "Yb": 70, + "Lu": 71, + "Hf": 72, + "Ta": 73, + "W": 74, + "Re": 75, + "Os": 76, + "Ir": 77, + "Pt": 78, + "Au": 79, + "Hg": 80, + "Tl": 81, + "Pb": 82, + "Bi": 83, + "Po": 84, + "At": 85, + "Rn": 86, + "Fr": 87, + "Ra": 88, + "Ac": 89, + "Th": 90, + "Pa": 91, + "U": 92, + "Np": 93, + "Pu": 94, + "Am": 95, + "Cm": 96, + "Bk": 97, + "Cf": 98, + "Es": 99, + "Fm": 100, + "Md": 101, + "No": 102, + "Lr": 103, + "Rf": 104, + "Db": 105, + "Sg": 106, + "Bh": 107, + "Hs": 108, + "Mt": 109, + "Ds": 110, + "Rg": 111, + "Cn": 112, + "Nh": 113, + "Fl": 114, + "Mc": 115, + "Lv": 116, + "Ts": 117, + "Og": 118, +} + + default_config = { "GLOBAL": {"PROJECT": "DPGEN"}, "FORCE_EVAL": { @@ -121,6 +243,46 @@ def iterdict(d, out_list, flag=None, indent=0): out_list.insert(index, " " * indent + k + " " + v) +def calculate_multiplicity(atom_names, atom_types, charge=0): + """ + Calculate the multiplicity based on atom species, quantities, and system charge. + + This function provides a basic heuristic for determining multiplicity: + - Even number of electrons -> singlet (multiplicity = 1) + - Odd number of electrons -> doublet (multiplicity = 2) + + Note: This approach assumes that an odd electron count always results in a doublet state. + It does not account for systems with multiple unpaired electrons, which can have higher + multiplicities (e.g., triplet, quartet, etc.). Users should be aware of this limitation + and use the function accordingly. + + :param atom_names: List of element symbols. + :param atom_types: List of atom type indices. + :param charge: System charge (default: 0). + :return: Multiplicity. + """ + # Calculate the total number of electrons + total_electrons = 0 + for idx in atom_types: + element = atom_names[idx] + try: + total_electrons += atomic_numbers[element] + except KeyError: + raise ValueError(f"Unknown element '{element}' encountered in atom_names.") + + # Subtract/add electrons based on system charge + # Positive charge means we remove electrons, negative charge means we add electrons + total_electrons -= charge + + # Determine multiplicity based on the total number of electrons + # Even number of electrons -> singlet (multiplicity = 1) + # Odd number of electrons -> doublet (multiplicity = 2) + if total_electrons % 2 == 0: + return 1 + else: + return 2 + + def make_cp2k_input(sys_data, fp_params): # covert cell to cell string cell = sys_data["cells"][0] @@ -132,14 +294,27 @@ def make_cp2k_input(sys_data, fp_params): cell_c = np.array2string(cell[2, :]) cell_c = cell_c[1:-1] + atom_names = sys_data["atom_names"] + atom_types = sys_data["atom_types"] + # Get system charge if provided, default to 0 + charge = sys_data.get("charge", 0) + dft_params = fp_params.get("FORCE_EVAL", {}).get("DFT", {}) + if "MULTIPLICITY" in dft_params: + multiplicity = dft_params["MULTIPLICITY"] + else: + multiplicity = calculate_multiplicity(atom_names, atom_types, charge) + # get update from user user_config = fp_params # get update from cell cell_config = { "FORCE_EVAL": {"SUBSYS": {"CELL": {"A": cell_a, "B": cell_b, "C": cell_c}}} } + # get update for multiplicity + multiplicity_config = {"FORCE_EVAL": {"DFT": {"MULTIPLICITY": multiplicity}}} update_dict(default_config, user_config) update_dict(default_config, cell_config) + update_dict(default_config, multiplicity_config) # output list input_str = [] iterdict(default_config, input_str) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 0f6953721..600e5d827 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -1305,7 +1305,7 @@ def make_model_devi(iter_index, jdata, mdata): type_map=jdata["type_map"], ) if shuffle_poscar: - system.data["coords"] = rng.permuted(system.data["coords"], axis=1) + rng.shuffle(system.data["coords"], axis=1) if jdata.get("model_devi_nopbc", False): system.remove_pbc() system.to_lammps_lmp(os.path.join(conf_path, lmp_name)) @@ -2477,7 +2477,6 @@ def _select_by_model_devi_adaptive_trust_low( for tt in modd_system_task: with warnings.catch_warnings(): warnings.simplefilter("ignore") - model_devi = np.loadtxt(os.path.join(tt, "model_devi.out")) model_devi = _read_model_devi_file( tt, model_devi_f_avg_relative, model_devi_merge_traj ) @@ -4524,8 +4523,9 @@ def post_fp_cp2k(iter_index, jdata, rfailed=None): _sys = dpdata.LabeledSystem( oo, fmt="cp2kdata/e_f", type_map=jdata["type_map"] ) - all_sys.append(_sys) - icount += 1 + if len(_sys) > 0: + all_sys.append(_sys) + icount += 1 if (all_sys is not None) and (len(all_sys) > 0): sys_data_path = os.path.join(work_path, f"data.{ss}") diff --git a/examples/run/dp2.x-lammps-cp2k/methane/param-ch4.json b/examples/run/dp2.x-lammps-cp2k/methane/param-ch4.json index 356dbb921..8e733f2d8 100644 --- a/examples/run/dp2.x-lammps-cp2k/methane/param-ch4.json +++ b/examples/run/dp2.x-lammps-cp2k/methane/param-ch4.json @@ -23,8 +23,6 @@ ] ], "sys_batch_size": [ - 8, - 8, 8, 8 ], diff --git a/tests/data/test_gen_bulk_abacus.py b/tests/data/test_gen_bulk_abacus.py index 728a6964e..520589829 100644 --- a/tests/data/test_gen_bulk_abacus.py +++ b/tests/data/test_gen_bulk_abacus.py @@ -76,7 +76,6 @@ def test(self): def testSTRU(self): jdata = self.jdata jdata["from_poscar_path"] = "./Cu.STRU" - jdata["potcars"] = ["abacus.in/Cu_ONCV_PBE-1.0.upf"] make_super_cell_STRU(jdata) make_abacus_relax(jdata, {"fp_resources": {}}) make_scale_ABACUS(jdata)