diff --git a/deepmd/utils/argcheck.py b/deepmd/utils/argcheck.py index 1a81a2fefe..13c351731f 100644 --- a/deepmd/utils/argcheck.py +++ b/deepmd/utils/argcheck.py @@ -457,7 +457,7 @@ def model_compression_type_args(): def model_args (): - doc_type_map = 'A list of strings. Give the name to each type of atoms. It is noted that the number of atom type of training system must be less than 128 in a GPU environment.' + doc_type_map = 'A list of strings. Give the name to each type of atoms. It is noted that the number of atom type of training system must be less than 128 in a GPU environment. If not given, type.raw in each system should use the same type indexes, and type_map.raw will take no effect.' doc_data_stat_nbatch = 'The model determines the normalization from the statistics of the data. This key specifies the number of `frames` in each `system` used for statistics.' doc_data_stat_protect = 'Protect parameter for atomic energy regression.' doc_data_bias_nsample = 'The number of training samples in a system to compute and change the energy bias.' diff --git a/doc/data/system.md b/doc/data/system.md index d09761b675..6a6fb4b58f 100644 --- a/doc/data/system.md +++ b/doc/data/system.md @@ -6,8 +6,8 @@ A system should contain system properties, input frame properties, and labeled f ID | Property | Raw file | Required/Optional | Shape | Description -------- | ---------------------- | ------------ | -------------------- | ----------------------- | ----------- -type | Atom type indexes | type.raw | Required | Natoms | Integers that start with 0 -type_map | Atom type names | type_map.raw | Optional | Ntypes | Atom names that map to atom type, which is unnecessart to be contained in the periodic table +type | Atom type indexes | type.raw | Required | Natoms | Integers that start with 0. If both the training parameter {ref}`type_map ` is set and `type_map.raw` is provided, the system atom type should be mapped to `type_map.raw` in `type.raw` and will be mapped to the model atom type when training; otherwise, the system atom type will be always mapped to the model atom type (whether {ref}`type_map ` is set or not) +type_map | Atom type names | type_map.raw | Optional | Ntypes | Atom names that map to atom type, which is unnecessary to be contained in the periodic table. Only works when the training parameter {ref}`type_map ` is set nopbc | Non-periodic system | nopbc | Optional | 1 | If True, this system is non-periodic; otherwise it's periodic The input frame properties contain the following property, the first axis of which is the number of frames: diff --git a/doc/model/dplr.md b/doc/model/dplr.md index bb6ea66084..9893d30713 100644 --- a/doc/model/dplr.md +++ b/doc/model/dplr.md @@ -143,6 +143,7 @@ fix 0 all dplr model ener.pb type_associate 1 3 bond_type 1 fix_modify 0 virial yes ``` The fix command `dplr` calculates the position of WCs by the DW model and back-propagates the long-range interaction on virtual atoms to real toms. +At this time, the training parameter {ref}`type_map ` will be mapped to LAMMPS atom types. ```lammps # compute the temperature of real atoms, excluding virtual atom contribution diff --git a/doc/third-party/lammps-command.md b/doc/third-party/lammps-command.md index 5ec51c6830..23c23136f8 100644 --- a/doc/third-party/lammps-command.md +++ b/doc/third-party/lammps-command.md @@ -54,6 +54,7 @@ and the model deviation will be computed among all models every `out_freq` times pair_style deepmd graph.pb pair_style deepmd graph.pb fparam 1.2 pair_style deepmd graph_0.pb graph_1.pb graph_2.pb out_file md.out out_freq 10 atomic relative 1.0 +pair_coeff * * O H ``` ### Description @@ -76,6 +77,10 @@ If the keyword `fparam` is set, the given frame parameter(s) will be fed to the If the keyword `aparam` is set, the given atomic parameter(s) will be fed to the model, where each atom is assumed to have the same atomic parameter(s). If the keyword `ttm` is set, electronic temperatures from [fix ttm command](https://docs.lammps.org/fix_ttm.html) will be fed to the model as the atomic parameters. +Only a single `pair_coeff` command is used with the deepmd style which specifies atom names. These are mapped to LAMMPS atom types (integers from 1 to Ntypes) by specifying Ntypes additional arguments after `* *` in the `pair_coeff` command. +If atom names are not set in the `pair_coeff` command, the training parameter {ref}`type_map ` will be used by default. +If the training parameter {ref}`type_map ` is not set, atom names in the `pair_coeff` command cannot be set. In this case, atom type indexes in [`type.raw`](../data/system.md) (integers from 0 to Ntypes-1) will map to LAMMPS atom types. + ### Restrictions - The `deepmd` pair style is provided in the USER-DEEPMD package, which is compiled from the DeePMD-kit, visit the [DeePMD-kit website](https://github.com/deepmodeling/deepmd-kit) for more information. @@ -92,6 +97,8 @@ compute ID group-ID deeptensor/atom model_file - deeptensor/atom: the style of this compute - model_file: the name of the binary model file. +At this time, the training parameter {ref}`type_map ` will be mapped to LAMMPS atom types. + ### Examples ```lammps compute dipole all deeptensor/atom dipole.pb diff --git a/doc/third-party/lammps.md b/doc/third-party/lammps.md index 5b9d3fd728..0020db01c5 100644 --- a/doc/third-party/lammps.md +++ b/doc/third-party/lammps.md @@ -4,6 +4,6 @@ Running an MD simulation with LAMMPS is simpler. In the LAMMPS input file, one n ```lammps pair_style deepmd graph.pb -pair_coeff * * +pair_coeff * * O H ``` -where `graph.pb` is the file name of the frozen model. It should be noted that LAMMPS counts atom types starting from 1, therefore, all LAMMPS atom types will be firstly subtracted by 1, and then passed into the DeePMD-kit engine to compute the interactions. +where `graph.pb` is the file name of the frozen model. `pair_coeff` maps atom names (`O H`) with LAMMPS atom types (integers from 1 to Ntypes, i.e. `1 2`). diff --git a/examples/water/lmp/in.lammps b/examples/water/lmp/in.lammps index 81864aad0e..828beb4c68 100644 --- a/examples/water/lmp/in.lammps +++ b/examples/water/lmp/in.lammps @@ -12,7 +12,7 @@ mass 1 16 mass 2 2 pair_style deepmd frozen_model.pb -pair_coeff * * +pair_coeff * * O H velocity all create 330.0 23456789 diff --git a/examples/water/lmp/in.plugin.lammps b/examples/water/lmp/in.plugin.lammps index 7161735c23..7791c7ae77 100644 --- a/examples/water/lmp/in.plugin.lammps +++ b/examples/water/lmp/in.plugin.lammps @@ -15,7 +15,7 @@ mass 2 2 plugin load libdeepmd_lmp.so pair_style deepmd frozen_model.pb -pair_coeff * * +pair_coeff * * O H velocity all create 330.0 23456789 diff --git a/source/lmp/pair_deepmd.cpp b/source/lmp/pair_deepmd.cpp index 0f1e7aa202..e44bf03b7b 100644 --- a/source/lmp/pair_deepmd.cpp +++ b/source/lmp/pair_deepmd.cpp @@ -328,7 +328,7 @@ void PairDeepMD::compute(int eflag, int vflag) vector dtype (nall); for (int ii = 0; ii < nall; ++ii){ - dtype[ii] = type[ii] - 1; + dtype[ii] = type_idx_map[type[ii] - 1]; } double dener (0); @@ -1028,13 +1028,55 @@ void PairDeepMD::coeff(int narg, char **arg) jlo = 0; ihi = n; jhi = n; - if (narg == 2) { + if (narg >= 2) { utils::bounds(FLERR,arg[0],1,atom->ntypes,ilo,ihi,error); utils::bounds(FLERR,arg[1],1,atom->ntypes,jlo,jhi,error); if (ilo != 1 || jlo != 1 || ihi != n || jhi != n) { error->all(FLERR,"deepmd requires that the scale should be set to all atom types, i.e. pair_coeff * *."); } - } + } + if (narg <= 2) { + type_idx_map.resize(numb_types); + for (int ii = 0; ii < numb_types; ++ii){ + type_idx_map[ii] = ii; + } + } else { + int iarg = 2; + + // type_map is a list of strings with undetermined length + // note: although we have numb_types from the model, we do not require + // the number of types in the system matches that in the model + std::vector type_map; + std::string type_map_str; + deep_pot.get_type_map(type_map_str); + // convert the string to a vector of strings + std::istringstream iss(type_map_str); + std::string type_name; + while (iss >> type_name) { + type_map.push_back(type_name); + } + + type_idx_map.clear(); + while (iarg < narg) { + std::string type_name = arg[iarg]; + bool found_element = false; + for (int ii = 0; ii < type_map.size(); ++ii) { + if (type_map[ii] == type_name) { + type_idx_map.push_back(ii); + found_element = true; + break; + } + } + if (!found_element) { + error->all(FLERR, "pair_coeff: element " + type_name + " not found in the model"); + } + iarg += 1; + } + numb_types = type_idx_map.size(); + } + if (numb_types < n) { + error->all(FLERR, "number of types assigned by pair_coeff or in the model is less than the number of types in the system"); + } for (int i = ilo; i <= ihi; i++) { for (int j = MAX(jlo,i); j <= jhi; j++) { setflag[i][j] = 1; diff --git a/source/lmp/pair_deepmd.h.in b/source/lmp/pair_deepmd.h.in index 45bcc03888..74fafcc0f7 100644 --- a/source/lmp/pair_deepmd.h.in +++ b/source/lmp/pair_deepmd.h.in @@ -109,6 +109,7 @@ private: int *counts,*displacements; tagint *tagsend, *tagrecv; double *stdfsend, *stdfrecv; + std::vector type_idx_map; }; } diff --git a/source/lmp/tests/data_type_map.lmp b/source/lmp/tests/data_type_map.lmp new file mode 100644 index 0000000000..c1f10c7c36 --- /dev/null +++ b/source/lmp/tests/data_type_map.lmp @@ -0,0 +1,16 @@ +# the first line must be comment +6 atoms +2 atom types +0.0 13.0 xlo xhi +0.0 13.0 ylo yhi +0.0 13.0 zlo zhi +0.0 0.0 0.0 xy xz yz + +Atoms + +1 2 12.83 2.56 2.18 +2 1 12.09 2.87 2.74 +3 1 0.25 3.32 1.68 +4 2 3.36 3.00 1.81 +5 1 3.51 2.51 2.60 +6 1 4.27 3.22 1.56 diff --git a/source/lmp/tests/test_lammps.py b/source/lmp/tests/test_lammps.py index 014ef70d2a..9815a962a5 100644 --- a/source/lmp/tests/test_lammps.py +++ b/source/lmp/tests/test_lammps.py @@ -13,6 +13,7 @@ pb_file2 = Path(__file__).parent / "graph2.pb" system_file = Path(__file__).parent.parent.parent / "tests" data_file = Path(__file__).parent / "data.lmp" +data_type_map_file = Path(__file__).parent / "data_type_map.lmp" md_file = Path(__file__).parent / "md.out" # this is as the same as python and c++ tests, test_deeppot_a.py @@ -72,9 +73,7 @@ ).split()) - -@pytest.fixture -def lammps() -> PyLammps: +def _lammps(data_file) -> PyLammps: lammps = PyLammps() lammps.units("metal") lammps.boundary("p p p") @@ -86,7 +85,16 @@ def lammps() -> PyLammps: lammps.mass("2 2") lammps.timestep(0.0005) lammps.fix("1 all nve") - yield lammps + return lammps + + +@pytest.fixture +def lammps(): + yield _lammps(data_file=data_file) + +@pytest.fixture +def lammps_type_map(): + yield _lammps(data_file=data_type_map_file) def test_pair_deepmd(lammps): @@ -205,3 +213,12 @@ def test_pair_deepmd_model_devi_atomic_relative_v(lammps): assert md[1] == pytest.approx(np.max(expected_md_v)) assert md[2] == pytest.approx(np.min(expected_md_v)) assert md[3] == pytest.approx(np.sqrt(np.mean(np.square(expected_md_v)))) + +def test_pair_deepmd_type_map(lammps_type_map): + lammps_type_map.pair_style("deepmd {}".format(pb_file.resolve())) + lammps_type_map.pair_coeff("* * H O") + lammps_type_map.run(0) + assert lammps_type_map.eval("pe") == pytest.approx(expected_e) + for ii in range(6): + assert lammps_type_map.atoms[ii].force == pytest.approx(expected_f[ii]) + lammps_type_map.run(1)