From 61eb365880828f97d76c2a402527f6ba77c8395e Mon Sep 17 00:00:00 2001 From: LiFu Date: Fri, 5 Jun 2026 18:42:15 +0800 Subject: [PATCH 01/11] Add DeepMD frame-parameter dE/dN provider for fix uvt --- doc/third-party/lammps-command.md | 15 ++ source/api_c/include/c_api.h | 62 ++++++ source/api_c/include/deepmd.hpp | 166 +++++++++++++++ source/api_c/src/c_api.cc | 132 ++++++++++++ source/api_cc/include/DeepPot.h | 56 +++++ source/api_cc/include/DeepPotTF.h | 37 ++++ source/api_cc/src/DeepPot.cc | 47 ++++ source/api_cc/src/DeepPotTF.cc | 201 ++++++++++++++++++ source/lmp/compute_deepmd_fparam_dedn.cpp | 187 ++++++++++++++++ source/lmp/compute_deepmd_fparam_dedn.h | 38 ++++ source/lmp/pair_base.cpp | 46 ++++ source/lmp/pair_base.h | 5 + source/lmp/pair_deepmd.cpp | 189 +++++++++++++++- source/lmp/pair_deepmd.h | 7 + source/lmp/plugin/CMakeLists.txt | 5 +- source/lmp/plugin/deepmdplugin.cpp | 12 ++ .../tests/test_lammps_fparam_from_fix_dedn.py | 95 +++++++++ 17 files changed, 1295 insertions(+), 5 deletions(-) create mode 100644 source/lmp/compute_deepmd_fparam_dedn.cpp create mode 100644 source/lmp/compute_deepmd_fparam_dedn.h create mode 100644 source/lmp/tests/test_lammps_fparam_from_fix_dedn.py diff --git a/doc/third-party/lammps-command.md b/doc/third-party/lammps-command.md index 70df75d22d..9916d6261e 100644 --- a/doc/third-party/lammps-command.md +++ b/doc/third-party/lammps-command.md @@ -110,10 +110,25 @@ $$E_{v_i}=\frac{\left|D_{v_i}\right|}{\left|v_i\right|+l}$$ If the keyword `fparam` is set, the given frame parameter(s) will be fed to the model. If the keyword `fparam_from_compute` is set, the global parameter(s) from compute command (e.g., temperature from [compute temp command](https://docs.lammps.org/compute_temp.html)) will be fed to the model as the frame parameter(s). +If the keyword `fparam_from_fix` is set, the global parameter(s) from fix command will be fed to the model as the frame parameter(s). This is intended for generalized coordinates that are naturally carried by a fix, for example the potentiostat variable in `fix uvt`. If the keyword `aparam_from_compute` is set, the atomic parameter(s) from compute command (e.g., per-atom translational kinetic energy from [compute ke/atom command](https://docs.lammps.org/compute_ke_atom.html)) will be fed to the model as the atom parameter(s). 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. +### Computing the derivative with respect to a frame parameter + +The compute `deepmd/fparam/dedn` evaluates a finite-difference derivative of the DeepMD energy with respect to a chosen frame parameter source. + +```lammps +compute ID group-ID deepmd/fparam/dedn source [delta] +``` + +- `source` can be a global variable (`v_name`), a compute (`c_ID`) or a fix (`f_ID`). +- `source[index]` may be used for vector-valued computes or fixes, with 1-based indexing. +- `delta` is the perturbation used in the central difference formula. If omitted, a small default perturbation is used. +- This compute currently targets a single frame-parameter dimension, which matches the + constant-potential use case where the potentiostat variable is scalar. + 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 a mapping value is specified as `NULL`, the mapping is not performed. This can be used when a deepmd potential is used as part of the hybrid pair style. The `NULL` values are placeholders for atom types that will be used with other potentials. diff --git a/source/api_c/include/c_api.h b/source/api_c/include/c_api.h index 358480b0ad..f39cf256cc 100644 --- a/source/api_c/include/c_api.h +++ b/source/api_c/include/c_api.h @@ -396,6 +396,20 @@ extern void DP_DeepPotCompute2(DP_DeepPot* dp, double* virial, double* atomic_energy, double* atomic_virial); +extern void DP_DeepPotCompute2add(DP_DeepPot* dp, + const int nframes, + const int natom, + const double* coord, + const int* atype, + const double* cell, + const double* fparam, + const double* aparam, + double* energy, + double* force, + double* virial, + double* dE_dN, + double* atomic_energy, + double* atomic_virial); /** * @brief Evaluate the energy, force, magnetic force and virial by using a DP @@ -482,6 +496,20 @@ extern void DP_DeepPotComputef2(DP_DeepPot* dp, float* virial, float* atomic_energy, float* atomic_virial); +extern void DP_DeepPotComputef2add(DP_DeepPot* dp, + const int nframes, + const int natom, + const float* coord, + const int* atype, + const float* cell, + const float* fparam, + const float* aparam, + double* energy, + float* force, + float* virial, + double* dE_dN, + float* atomic_energy, + float* atomic_virial); /** * @brief Evaluate the energy, force, magnetic force and virial by using a DP @@ -575,6 +603,23 @@ extern void DP_DeepPotComputeNList2(DP_DeepPot* dp, double* virial, double* atomic_energy, double* atomic_virial); +extern void DP_DeepPotComputeNList2add(DP_DeepPot* dp, + const int nframes, + const int natom, + const double* coord, + const int* atype, + const double* cell, + const int nghost, + const DP_Nlist* nlist, + const int ago, + const double* fparam, + const double* aparam, + double* energy, + double* force, + double* virial, + double* dE_dN, + double* atomic_energy, + double* atomic_virial); /** * @brief Evaluate the energy, force, magnetic force and virial by using a DP @@ -674,6 +719,23 @@ extern void DP_DeepPotComputeNListf2(DP_DeepPot* dp, float* virial, float* atomic_energy, float* atomic_virial); +extern void DP_DeepPotComputeNListf2add(DP_DeepPot* dp, + const int nframes, + const int natom, + const float* coord, + const int* atype, + const float* cell, + const int nghost, + const DP_Nlist* nlist, + const int ago, + const float* fparam, + const float* aparam, + double* energy, + float* force, + float* virial, + double* dE_dN, + float* atomic_energy, + float* atomic_virial); /** * @brief Evaluate the energy, force, magnetic force and virial by using a DP diff --git a/source/api_c/include/deepmd.hpp b/source/api_c/include/deepmd.hpp index c3ca40b75f..b445a17246 100644 --- a/source/api_c/include/deepmd.hpp +++ b/source/api_c/include/deepmd.hpp @@ -95,6 +95,62 @@ inline void _DP_DeepPotCompute(DP_DeepPot* dp, energy, force, virial, atomic_energy, atomic_virial); } +template +inline void _DP_DeepPotComputeadd(DP_DeepPot* dp, + const int nframes, + const int natom, + const FPTYPE* coord, + const int* atype, + const FPTYPE* cell, + const FPTYPE* fparam, + const FPTYPE* aparam, + double* energy, + FPTYPE* force, + FPTYPE* virial, + double* dE_dN, + FPTYPE* atomic_energy, + FPTYPE* atomic_virial); + +template <> +inline void _DP_DeepPotComputeadd(DP_DeepPot* dp, + const int nframes, + const int natom, + const double* coord, + const int* atype, + const double* cell, + const double* fparam, + const double* aparam, + double* energy, + double* force, + double* virial, + double* dE_dN, + double* atomic_energy, + double* atomic_virial) { + DP_DeepPotCompute2add(dp, nframes, natom, coord, atype, cell, fparam, aparam, + energy, force, virial, dE_dN, atomic_energy, + atomic_virial); +} + +template <> +inline void _DP_DeepPotComputeadd(DP_DeepPot* dp, + const int nframes, + const int natom, + const float* coord, + const int* atype, + const float* cell, + const float* fparam, + const float* aparam, + double* energy, + float* force, + float* virial, + double* dE_dN, + float* atomic_energy, + float* atomic_virial) { + DP_DeepPotComputef2add(dp, nframes, natom, coord, atype, cell, fparam, + aparam, energy, force, virial, dE_dN, atomic_energy, + atomic_virial); +} + // support spin template inline void _DP_DeepSpinCompute(DP_DeepSpin* dp, @@ -217,6 +273,71 @@ inline void _DP_DeepPotComputeNList(DP_DeepPot* dp, atomic_energy, atomic_virial); } +template +inline void _DP_DeepPotComputeNListadd(DP_DeepPot* dp, + const int nframes, + const int natom, + const FPTYPE* coord, + const int* atype, + const FPTYPE* cell, + const int nghost, + const DP_Nlist* nlist, + const int ago, + const FPTYPE* fparam, + const FPTYPE* aparam, + double* energy, + FPTYPE* force, + FPTYPE* virial, + double* dE_dN, + FPTYPE* atomic_energy, + FPTYPE* atomic_virial); + +template <> +inline void _DP_DeepPotComputeNListadd(DP_DeepPot* dp, + const int nframes, + const int natom, + const double* coord, + const int* atype, + const double* cell, + const int nghost, + const DP_Nlist* nlist, + const int ago, + const double* fparam, + const double* aparam, + double* energy, + double* force, + double* virial, + double* dE_dN, + double* atomic_energy, + double* atomic_virial) { + DP_DeepPotComputeNList2add(dp, nframes, natom, coord, atype, cell, nghost, + nlist, ago, fparam, aparam, energy, force, virial, + dE_dN, atomic_energy, atomic_virial); +} + +template <> +inline void _DP_DeepPotComputeNListadd(DP_DeepPot* dp, + const int nframes, + const int natom, + const float* coord, + const int* atype, + const float* cell, + const int nghost, + const DP_Nlist* nlist, + const int ago, + const float* fparam, + const float* aparam, + double* energy, + float* force, + float* virial, + double* dE_dN, + float* atomic_energy, + float* atomic_virial) { + DP_DeepPotComputeNListf2add(dp, nframes, natom, coord, atype, cell, nghost, + nlist, ago, fparam, aparam, energy, force, + virial, dE_dN, atomic_energy, atomic_virial); +} + // support spin template inline void _DP_DeepSpinComputeNList(DP_DeepSpin* dp, @@ -1263,6 +1384,51 @@ class DeepPot : public DeepBaseModel { fparam__, aparam__, ener_, force_, virial_, nullptr, nullptr); DP_CHECK_OK(DP_DeepPotCheckOK, dp); }; + + template + void compute( + ENERGYVTYPE& ener, + std::vector& force, + std::vector& virial, + ENERGYVTYPE& dE_dN, + const std::vector& coord, + const std::vector& atype, + const std::vector& box, + const int nghost, + const InputNlist& lmp_list, + const int& ago, + const std::vector& fparam = std::vector(), + const std::vector& aparam = std::vector()) { + unsigned int natoms = atype.size(); + unsigned int nframes = natoms > 0 ? coord.size() / natoms / 3 : 1; + assert(nframes * natoms * 3 == coord.size()); + if (!box.empty()) { + assert(box.size() == nframes * 9); + } + const VALUETYPE* coord_ = &coord[0]; + const VALUETYPE* box_ = !box.empty() ? &box[0] : nullptr; + const int* atype_ = &atype[0]; + double* ener_ = _DP_Get_Energy_Pointer(ener, nframes); + double* dE_dN_ = _DP_Get_Energy_Pointer(dE_dN, nframes); + force.resize(static_cast(nframes) * natoms * 3); + virial.resize(static_cast(nframes) * 9); + VALUETYPE* force_ = &force[0]; + VALUETYPE* virial_ = &virial[0]; + std::vector fparam_, aparam_; + validate_fparam_aparam(nframes, (aparam_nall ? natoms : (natoms - nghost)), + fparam, aparam); + tile_fparam_aparam(fparam_, nframes, dfparam, fparam); + tile_fparam_aparam(aparam_, nframes, + (aparam_nall ? natoms : (natoms - nghost)) * daparam, + aparam); + const VALUETYPE* fparam__ = !fparam_.empty() ? &fparam_[0] : nullptr; + const VALUETYPE* aparam__ = !aparam_.empty() ? &aparam_[0] : nullptr; + + _DP_DeepPotComputeNListadd( + dp, nframes, natoms, coord_, atype_, box_, nghost, lmp_list.nl, ago, + fparam__, aparam__, ener_, force_, virial_, dE_dN_, nullptr, nullptr); + DP_CHECK_OK(DP_DeepPotCheckOK, dp); + }; /** * @brief Evaluate the energy, force, virial, atomic energy, and atomic virial *by using this DP with the neighbor list. diff --git a/source/api_c/src/c_api.cc b/source/api_c/src/c_api.cc index b0e789648e..5e5fac65a6 100644 --- a/source/api_c/src/c_api.cc +++ b/source/api_c/src/c_api.cc @@ -323,6 +323,7 @@ template void DP_DeepPotCompute_variant(DP_DeepPot* dp, float* virial, float* atomic_energy, float* atomic_virial); + // support spin template inline void DP_DeepSpinCompute_variant(DP_DeepSpin* dp, @@ -514,6 +515,95 @@ template void DP_DeepPotComputeNList_variant(DP_DeepPot* dp, float* atomic_energy, float* atomic_virial); +template +inline void DP_DeepPotComputeNList_variantadd(DP_DeepPot* dp, + const int nframes, + const int natoms, + const VALUETYPE* coord, + const int* atype, + const VALUETYPE* cell, + const int nghost, + const DP_Nlist* nlist, + const int ago, + const VALUETYPE* fparam, + const VALUETYPE* aparam, + double* energy, + VALUETYPE* force, + VALUETYPE* virial, + double* dE_dN, + VALUETYPE* atomic_energy, + VALUETYPE* atomic_virial) { + std::vector coord_(coord, coord + nframes * natoms * 3); + std::vector atype_(atype, atype + natoms); + std::vector cell_; + if (cell) { + cell_.assign(cell, cell + nframes * 9); + } + std::vector fparam_; + if (fparam) { + fparam_.assign(fparam, fparam + nframes * dp->dfparam); + } + std::vector aparam_; + if (aparam) { + aparam_.assign(aparam, + aparam + nframes * + (dp->aparam_nall ? natoms : (natoms - nghost)) * + dp->daparam); + } + double e = 0.0; + double dedn = 0.0; + std::vector f, v; + + if (nframes != 1) { + throw deepmd::deepmd_exception( + "Direct dE/dN C API currently supports single-frame evaluation only"); + } + DP_REQUIRES_OK(dp, dp->dp.compute(e, f, v, dedn, coord_, atype_, cell_, nghost, + nlist->nl, ago, fparam_, aparam_)); + if (energy) energy[0] = e; + if (force) std::copy(f.begin(), f.end(), force); + if (virial) std::copy(v.begin(), v.end(), virial); + if (dE_dN) dE_dN[0] = dedn; + if (atomic_energy) std::fill(atomic_energy, atomic_energy + nframes * natoms, (VALUETYPE)0); + if (atomic_virial) std::fill(atomic_virial, atomic_virial + nframes * natoms * 9, (VALUETYPE)0); +} + +template void DP_DeepPotComputeNList_variantadd(DP_DeepPot* dp, + const int nframes, + const int natoms, + const double* coord, + const int* atype, + const double* cell, + const int nghost, + const DP_Nlist* nlist, + const int ago, + const double* fparam, + const double* aparam, + double* energy, + double* force, + double* virial, + double* dE_dN, + double* atomic_energy, + double* atomic_virial); + +template void DP_DeepPotComputeNList_variantadd(DP_DeepPot* dp, + const int nframes, + const int natoms, + const float* coord, + const int* atype, + const float* cell, + const int nghost, + const DP_Nlist* nlist, + const int ago, + const float* fparam, + const float* aparam, + double* energy, + float* force, + float* virial, + double* dE_dN, + float* atomic_energy, + float* atomic_virial); + // support spin template inline void DP_DeepSpinComputeNList_variant(DP_DeepSpin* dp, @@ -1657,6 +1747,27 @@ void DP_DeepPotComputeNList2(DP_DeepPot* dp, dp, nframes, natoms, coord, atype, cell, nghost, nlist, ago, fparam, aparam, energy, force, virial, atomic_energy, atomic_virial); } +void DP_DeepPotComputeNList2add(DP_DeepPot* dp, + const int nframes, + const int natoms, + const double* coord, + const int* atype, + const double* cell, + const int nghost, + const DP_Nlist* nlist, + const int ago, + const double* fparam, + const double* aparam, + double* energy, + double* force, + double* virial, + double* dE_dN, + double* atomic_energy, + double* atomic_virial) { + DP_DeepPotComputeNList_variantadd( + dp, nframes, natoms, coord, atype, cell, nghost, nlist, ago, fparam, + aparam, energy, force, virial, dE_dN, atomic_energy, atomic_virial); +} void DP_DeepSpinComputeNList2(DP_DeepSpin* dp, const int nframes, @@ -1701,6 +1812,27 @@ void DP_DeepPotComputeNListf2(DP_DeepPot* dp, dp, nframes, natoms, coord, atype, cell, nghost, nlist, ago, fparam, aparam, energy, force, virial, atomic_energy, atomic_virial); } +void DP_DeepPotComputeNListf2add(DP_DeepPot* dp, + const int nframes, + const int natoms, + const float* coord, + const int* atype, + const float* cell, + const int nghost, + const DP_Nlist* nlist, + const int ago, + const float* fparam, + const float* aparam, + double* energy, + float* force, + float* virial, + double* dE_dN, + float* atomic_energy, + float* atomic_virial) { + DP_DeepPotComputeNList_variantadd( + dp, nframes, natoms, coord, atype, cell, nghost, nlist, ago, fparam, + aparam, energy, force, virial, dE_dN, atomic_energy, atomic_virial); +} void DP_DeepSpinComputeNListf2(DP_DeepSpin* dp, const int nframes, diff --git a/source/api_cc/include/DeepPot.h b/source/api_cc/include/DeepPot.h index 6a30eed7ea..650249a63b 100644 --- a/source/api_cc/include/DeepPot.h +++ b/source/api_cc/include/DeepPot.h @@ -88,6 +88,43 @@ class DeepPotBackend : public DeepBaseModelBackend { const std::vector& aparam, const bool atomic) = 0; /** @} */ + + /** + * @brief Evaluate the energy, force, virial, and frame-parameter derivative. + * + * The default implementation reports that direct dE/dN output is not + * available for the current backend. + */ + virtual void computew_dedn(std::vector& ener, + std::vector& force, + std::vector& virial, + std::vector& dedn, + const std::vector& coord, + const std::vector& atype, + const std::vector& box, + const int nghost, + const InputNlist& inlist, + const int& ago, + const std::vector& fparam, + const std::vector& aparam) { + throw deepmd_exception( + "Direct dE/dN output is not supported by this DeePMD backend"); + } + virtual void computew_dedn(std::vector& ener, + std::vector& force, + std::vector& virial, + std::vector& dedn, + const std::vector& coord, + const std::vector& atype, + const std::vector& box, + const int nghost, + const InputNlist& inlist, + const int& ago, + const std::vector& fparam, + const std::vector& aparam) { + throw deepmd_exception( + "Direct dE/dN output is not supported by this DeePMD backend"); + } /** * @brief Evaluate the energy, force, virial, atomic energy, and atomic virial *by using this DP. @@ -318,6 +355,25 @@ class DeepPot : public DeepBaseModel { const std::vector& fparam = std::vector(), const std::vector& aparam = std::vector()); /** @} */ + + /** + * @brief Evaluate the energy, force, virial, and direct dE/dN output. + * @{ + **/ + template + void compute(ENERGYTYPE& ener, + std::vector& force, + std::vector& virial, + ENERGYTYPE& dedn, + const std::vector& coord, + const std::vector& atype, + const std::vector& box, + const int nghost, + const InputNlist& inlist, + const int& ago, + const std::vector& fparam = std::vector(), + const std::vector& aparam = std::vector()); + /** @} */ /** * @brief Evaluate the energy, force, virial, atomic energy, and atomic virial *by using this DP. diff --git a/source/api_cc/include/DeepPotTF.h b/source/api_cc/include/DeepPotTF.h index 26420807d9..c44b399a36 100644 --- a/source/api_cc/include/DeepPotTF.h +++ b/source/api_cc/include/DeepPotTF.h @@ -74,6 +74,19 @@ class DeepPotTF : public DeepPotBackend { const std::vector& fparam, const std::vector& aparam, const bool atomic); + template + void compute_dedn(ENERGYVTYPE& ener, + std::vector& force, + std::vector& virial, + ENERGYTYPE& dedn, + const std::vector& coord, + const std::vector& atype, + const std::vector& box, + const int nghost, + const InputNlist& lmp_list, + const int& ago, + const std::vector& fparam, + const std::vector& aparam); /** * @brief Evaluate the energy, force, virial, atomic energy, and atomic virial *by using this DP. @@ -239,6 +252,30 @@ class DeepPotTF : public DeepPotBackend { const std::vector& fparam, const std::vector& aparam, const bool atomic); + void computew_dedn(std::vector& ener, + std::vector& force, + std::vector& virial, + std::vector& dedn, + const std::vector& coord, + const std::vector& atype, + const std::vector& box, + const int nghost, + const InputNlist& inlist, + const int& ago, + const std::vector& fparam, + const std::vector& aparam) override; + void computew_dedn(std::vector& ener, + std::vector& force, + std::vector& virial, + std::vector& dedn, + const std::vector& coord, + const std::vector& atype, + const std::vector& box, + const int nghost, + const InputNlist& inlist, + const int& ago, + const std::vector& fparam, + const std::vector& aparam) override; void computew(std::vector& ener, std::vector& force, std::vector& virial, diff --git a/source/api_cc/src/DeepPot.cc b/source/api_cc/src/DeepPot.cc index ae543e1e31..d1bad59b1f 100644 --- a/source/api_cc/src/DeepPot.cc +++ b/source/api_cc/src/DeepPot.cc @@ -211,6 +211,53 @@ template void DeepPot::compute(ENERGYTYPE& dener, const std::vector& fparam, const std::vector& aparam_); +template +void DeepPot::compute(ENERGYTYPE& dener, + std::vector& dforce_, + std::vector& dvirial, + ENERGYTYPE& ddedn, + const std::vector& dcoord_, + const std::vector& datype_, + const std::vector& dbox, + const int nghost, + const InputNlist& lmp_list, + const int& ago, + const std::vector& fparam_, + const std::vector& aparam__) { + std::vector dener_; + std::vector ddedn_; + dp->computew_dedn(dener_, dforce_, dvirial, ddedn_, dcoord_, datype_, dbox, + nghost, lmp_list, ago, fparam_, aparam__); + dener = dener_[0]; + ddedn = ddedn_[0]; +} + +template void DeepPot::compute(ENERGYTYPE& dener, + std::vector& dforce_, + std::vector& dvirial, + ENERGYTYPE& ddedn, + const std::vector& dcoord_, + const std::vector& datype_, + const std::vector& dbox, + const int nghost, + const InputNlist& lmp_list, + const int& ago, + const std::vector& fparam, + const std::vector& aparam_); + +template void DeepPot::compute(ENERGYTYPE& dener, + std::vector& dforce_, + std::vector& dvirial, + ENERGYTYPE& ddedn, + const std::vector& dcoord_, + const std::vector& datype_, + const std::vector& dbox, + const int nghost, + const InputNlist& lmp_list, + const int& ago, + const std::vector& fparam, + const std::vector& aparam_); + template void DeepPot::compute(std::vector& dener, std::vector& dforce_, std::vector& dvirial, diff --git a/source/api_cc/src/DeepPotTF.cc b/source/api_cc/src/DeepPotTF.cc index 7656590ea6..32f12d1293 100644 --- a/source/api_cc/src/DeepPotTF.cc +++ b/source/api_cc/src/DeepPotTF.cc @@ -119,6 +119,74 @@ template void run_model( const int nframes, const int nghost); +template +static void run_model_dedn( + std::vector& dener, + std::vector& dforce_, + std::vector& dvirial, + std::vector& ddedn, + Session* session, + const std::vector>& input_tensors, + const AtomMap& atommap, + const int nframes, + const int nghost = 0) { + unsigned nloc = atommap.get_type().size(); + unsigned nall = nloc + nghost; + dener.resize(nframes); + ddedn.resize(nframes); + if (nloc == 0) { + dforce_.resize(static_cast(nframes) * nall * 3); + fill(dforce_.begin(), dforce_.end(), (VALUETYPE)0.0); + dvirial.resize(static_cast(nframes) * 9); + fill(dvirial.begin(), dvirial.end(), (VALUETYPE)0.0); + fill(ddedn.begin(), ddedn.end(), (ENERGYTYPE)0.0); + return; + } + + std::vector output_tensors; + check_status(session->Run( + input_tensors, + {"o_energy", "o_force", "o_dE_dN", "o_atom_energy", "o_atom_virial"}, + {}, &output_tensors)); + + Tensor output_e = output_tensors[0]; + Tensor output_f = output_tensors[1]; + Tensor output_dedn = output_tensors[2]; + Tensor output_av = output_tensors[4]; + + auto oe = output_e.flat(); + auto of = output_f.flat(); + auto odedn = output_dedn.flat(); + auto oav = output_av.flat(); + + std::vector dforce(static_cast(nframes) * 3 * nall); + dvirial.resize(static_cast(nframes) * 9); + for (int ii = 0; ii < nframes; ++ii) { + dener[ii] = oe(ii); + ddedn[ii] = odedn(ii); + } + for (size_t ii = 0; ii < static_cast(nframes) * nall * 3; ++ii) { + dforce[ii] = of(ii); + } + std::fill(dvirial.begin(), dvirial.end(), (VALUETYPE)0.); + for (int kk = 0; kk < nframes; ++kk) { + for (int ii = 0; ii < nall; ++ii) { + dvirial[kk * 9 + 0] += (VALUETYPE)1.0 * oav(kk * nall * 9 + 9 * ii + 0); + dvirial[kk * 9 + 1] += (VALUETYPE)1.0 * oav(kk * nall * 9 + 9 * ii + 1); + dvirial[kk * 9 + 2] += (VALUETYPE)1.0 * oav(kk * nall * 9 + 9 * ii + 2); + dvirial[kk * 9 + 3] += (VALUETYPE)1.0 * oav(kk * nall * 9 + 9 * ii + 3); + dvirial[kk * 9 + 4] += (VALUETYPE)1.0 * oav(kk * nall * 9 + 9 * ii + 4); + dvirial[kk * 9 + 5] += (VALUETYPE)1.0 * oav(kk * nall * 9 + 9 * ii + 5); + dvirial[kk * 9 + 6] += (VALUETYPE)1.0 * oav(kk * nall * 9 + 9 * ii + 6); + dvirial[kk * 9 + 7] += (VALUETYPE)1.0 * oav(kk * nall * 9 + 9 * ii + 7); + dvirial[kk * 9 + 8] += (VALUETYPE)1.0 * oav(kk * nall * 9 + 9 * ii + 8); + } + } + dforce_ = dforce; + atommap.backward(dforce_.begin(), dforce.begin(), 3, nframes, + nall); +} + template static void run_model( std::vector& dener, @@ -671,6 +739,101 @@ template void DeepPotTF::compute>( const std::vector& aparam, const bool atomic); +template +void DeepPotTF::compute_dedn(ENERGYVTYPE& dener, + std::vector& dforce_, + std::vector& dvirial, + ENERGYTYPE& ddedn, + const std::vector& dcoord_, + const std::vector& datype_, + const std::vector& dbox, + const int nghost, + const InputNlist& lmp_list, + const int& ago, + const std::vector& fparam, + const std::vector& aparam) { + int nall = datype_.size(); + int nframes = nall > 0 ? (dcoord_.size() / nall / 3) : 1; + int nloc = nall - nghost; + std::vector fparam_; + std::vector aparam_; + validate_fparam_aparam(nframes, (aparam_nall ? nall : nloc), fparam, aparam); + tile_fparam_aparam(fparam_, nframes, dfparam, fparam); + tile_fparam_aparam(aparam_, nframes, (aparam_nall ? nall : nloc) * daparam, + aparam); + + std::vector> input_tensors; + std::vector dcoord, dforce, aparam_sel; + std::vector datype, fwd_map, bkw_map; + int nghost_real, nall_real, nloc_real; + select_real_atoms_coord(dcoord, datype, aparam_sel, nghost_real, fwd_map, + bkw_map, nall_real, nloc_real, dcoord_, datype_, + aparam_, nghost, ntypes, nframes, daparam, nall, + aparam_nall); + + if (ago == 0) { + atommap = deepmd::AtomMap(datype.begin(), datype.begin() + nloc_real); + assert(nloc_real == atommap.get_type().size()); + + nlist_data.copy_from_nlist(lmp_list); + nlist_data.shuffle_exclude_empty(fwd_map); + nlist_data.shuffle(atommap); + nlist_data.make_inlist(nlist); + } + + std::vector dener_vec; + std::vector ddedn_vec; + if (dtype == tensorflow::DT_DOUBLE) { + int ret = session_input_tensors( + input_tensors, dcoord, ntypes, datype, dbox, nlist, fparam_, aparam_sel, + atommap, nghost_real, ago, "", aparam_nall); + assert(nloc_real == ret); + run_model_dedn(dener_vec, dforce, dvirial, ddedn_vec, session, + input_tensors, atommap, nframes, nghost_real); + } else { + int ret = session_input_tensors( + input_tensors, dcoord, ntypes, datype, dbox, nlist, fparam_, aparam_sel, + atommap, nghost_real, ago, "", aparam_nall); + assert(nloc_real == ret); + run_model_dedn(dener_vec, dforce, dvirial, ddedn_vec, session, + input_tensors, atommap, nframes, nghost_real); + } + + dforce_.resize(static_cast(nframes) * fwd_map.size() * 3); + select_map(dforce_, dforce, bkw_map, 3, nframes, fwd_map.size(), + nall_real); + dener = dener_vec[0]; + ddedn = ddedn_vec[0]; +} + +template void DeepPotTF::compute_dedn( + ENERGYTYPE& dener, + std::vector& dforce_, + std::vector& dvirial, + ENERGYTYPE& ddedn, + const std::vector& dcoord_, + const std::vector& datype_, + const std::vector& dbox, + const int nghost, + const InputNlist& lmp_list, + const int& ago, + const std::vector& fparam, + const std::vector& aparam); + +template void DeepPotTF::compute_dedn( + ENERGYTYPE& dener, + std::vector& dforce_, + std::vector& dvirial, + ENERGYTYPE& ddedn, + const std::vector& dcoord_, + const std::vector& datype_, + const std::vector& dbox, + const int nghost, + const InputNlist& lmp_list, + const int& ago, + const std::vector& fparam, + const std::vector& aparam); + template void DeepPotTF::compute(ENERGYVTYPE& dener, std::vector& dforce_, @@ -992,6 +1155,44 @@ void DeepPotTF::computew(std::vector& ener, compute(ener, force, virial, atom_energy, atom_virial, coord, atype, box, nghost, inlist, ago, fparam, aparam, atomic); } +void DeepPotTF::computew_dedn(std::vector& ener, + std::vector& force, + std::vector& virial, + std::vector& dedn, + const std::vector& coord, + const std::vector& atype, + const std::vector& box, + const int nghost, + const InputNlist& inlist, + const int& ago, + const std::vector& fparam, + const std::vector& aparam) { + ENERGYTYPE ener0 = 0.0; + ENERGYTYPE dedn0 = 0.0; + compute_dedn(ener0, force, virial, dedn0, coord, atype, box, nghost, inlist, + ago, fparam, aparam); + ener.assign(1, ener0); + dedn.assign(1, dedn0); +} +void DeepPotTF::computew_dedn(std::vector& ener, + std::vector& force, + std::vector& virial, + std::vector& dedn, + const std::vector& coord, + const std::vector& atype, + const std::vector& box, + const int nghost, + const InputNlist& inlist, + const int& ago, + const std::vector& fparam, + const std::vector& aparam) { + ENERGYTYPE ener0 = 0.0; + ENERGYTYPE dedn0 = 0.0; + compute_dedn(ener0, force, virial, dedn0, coord, atype, box, nghost, inlist, + ago, fparam, aparam); + ener.assign(1, ener0); + dedn.assign(1, dedn0); +} void DeepPotTF::computew_mixed_type(std::vector& ener, std::vector& force, std::vector& virial, diff --git a/source/lmp/compute_deepmd_fparam_dedn.cpp b/source/lmp/compute_deepmd_fparam_dedn.cpp new file mode 100644 index 0000000000..1ac7b3de59 --- /dev/null +++ b/source/lmp/compute_deepmd_fparam_dedn.cpp @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +#include "compute_deepmd_fparam_dedn.h" + +#include +#include + +#include "comm.h" +#include "compute.h" +#include "error.h" +#include "fix.h" +#include "force.h" +#include "input.h" +#include "modify.h" +#include "update.h" +#include "variable.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +ComputeDeepmdFparamDedn::ComputeDeepmdFparamDedn(LAMMPS* lmp, int narg, + char** arg) + : Compute(lmp, narg, arg), + source_index(-1), + delta(1.0e-6), + pair(nullptr) { + if (narg < 4) { + error->all(FLERR, "Illegal compute deepmd/fparam/dedn command"); + } + + std::string token = arg[3]; + auto lb = token.find('['); + auto rb = token.find(']'); + if (lb != std::string::npos || rb != std::string::npos) { + if (lb == std::string::npos || rb == std::string::npos || rb <= lb + 1) { + error->all(FLERR, "Illegal source specification in compute command"); + } + source_index = atoi(token.substr(lb + 1, rb - lb - 1).c_str()) - 1; + token = token.substr(0, lb); + } + + if (token.rfind("v_", 0) == 0) { + source_type = SRC_VAR; + source_id = token.substr(2); + if (source_index >= 0) { + error->all(FLERR, + "Variable source for compute deepmd/fparam/dedn must be " + "scalar"); + } + } else if (token.rfind("c_", 0) == 0) { + source_type = SRC_COMPUTE; + source_id = token.substr(2); + } else if (token.rfind("f_", 0) == 0) { + source_type = SRC_FIX; + source_id = token.substr(2); + } else { + error->all(FLERR, + "Source must be a variable, compute, or fix reference"); + } + + int iarg = 4; + if (iarg < narg) { + delta = atof(arg[iarg]); + ++iarg; + } + if (iarg != narg) { + error->all(FLERR, "Illegal compute deepmd/fparam/dedn command"); + } + if (delta <= 0.0) { + error->all(FLERR, "delta must be > 0 in compute deepmd/fparam/dedn"); + } + + scalar_flag = 1; + extscalar = 1; + timeflag = 1; +} + +/* ---------------------------------------------------------------------- */ + +ComputeDeepmdFparamDedn::~ComputeDeepmdFparamDedn() = default; + +/* ---------------------------------------------------------------------- */ + +void ComputeDeepmdFparamDedn::init() { + if (!force->pair) { + error->all(FLERR, "compute deepmd/fparam/dedn requires an active pair style"); + } + pair = dynamic_cast(force->pair); + if (!pair) { + error->all(FLERR, + "compute deepmd/fparam/dedn currently requires pair_style deepmd"); + } + if (pair->get_dim_fparam() != 1) { + error->all(FLERR, + "compute deepmd/fparam/dedn currently supports a single " + "frame-parameter dimension only"); + } +} + +/* ---------------------------------------------------------------------- */ + +double ComputeDeepmdFparamDedn::get_source_value() { + if (source_type == SRC_VAR) { + int ivar = input->variable->find(source_id.c_str()); + if (ivar < 0) { + error->all(FLERR, "Variable source not found: " + source_id); + } + return input->variable->compute_equal(ivar); + } + + if (source_type == SRC_COMPUTE) { + int icompute = modify->find_compute(source_id); + if (icompute < 0) { + error->all(FLERR, "Compute source not found: " + source_id); + } + Compute* compute = modify->compute[icompute]; + if (!compute) { + error->all(FLERR, "Compute source not found: " + source_id); + } + if (source_index < 0) { + if (!compute->scalar_flag) { + error->all(FLERR, "Compute source is not scalar: " + source_id); + } + if (!(compute->invoked_flag & Compute::INVOKED_SCALAR)) { + compute->compute_scalar(); + compute->invoked_flag |= Compute::INVOKED_SCALAR; + } + return compute->scalar; + } + if (!compute->vector_flag) { + error->all(FLERR, "Compute source is not vector-valued: " + source_id); + } + if (!(compute->invoked_flag & Compute::INVOKED_VECTOR)) { + compute->compute_vector(); + compute->invoked_flag |= Compute::INVOKED_VECTOR; + } + if (source_index >= compute->size_vector) { + error->all(FLERR, "Compute source index is out of range: " + source_id); + } + return compute->vector[source_index]; + } + + int ifix = modify->find_fix(source_id); + if (ifix < 0) { + error->all(FLERR, "Fix source not found: " + source_id); + } + Fix* fix = modify->fix[ifix]; + if (!fix) { + error->all(FLERR, "Fix source not found: " + source_id); + } + if (source_index < 0) { + if (!fix->scalar_flag) { + error->all(FLERR, "Fix source is not scalar: " + source_id); + } + return fix->compute_scalar(); + } + if (!fix->vector_flag) { + error->all(FLERR, "Fix source is not vector-valued: " + source_id); + } + if (fix->size_vector <= source_index) { + error->all(FLERR, "Fix source index is out of range: " + source_id); + } + return fix->compute_vector(source_index); +} + +/* ---------------------------------------------------------------------- */ + +double ComputeDeepmdFparamDedn::compute_scalar() { + invoked_scalar = update->ntimestep; + int dim = 0; + if (void* ptr = pair->extract("deepmd_dedn", dim)) { + scalar = *static_cast(ptr); + return scalar; + } + + double fparam0 = get_source_value(); + std::vector fparam_plus(1, fparam0 + delta); + std::vector fparam_minus(1, fparam0 - delta); + + double one = + (pair->eval_energy_with_fparam(fparam_plus) - + pair->eval_energy_with_fparam(fparam_minus)) / + (2.0 * delta); + + MPI_Allreduce(&one, &scalar, 1, MPI_DOUBLE, MPI_SUM, world); + return scalar; +} diff --git a/source/lmp/compute_deepmd_fparam_dedn.h b/source/lmp/compute_deepmd_fparam_dedn.h new file mode 100644 index 0000000000..66f4808031 --- /dev/null +++ b/source/lmp/compute_deepmd_fparam_dedn.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +#ifdef COMPUTE_CLASS +// clang-format off +ComputeStyle(deepmd/fparam/dedn, ComputeDeepmdFparamDedn) +// clang-format on +#else + +#ifndef LMP_COMPUTE_DEEPMD_FPARAM_DEDN_H +#define LMP_COMPUTE_DEEPMD_FPARAM_DEDN_H + +#include "compute.h" +#include "pair_deepmd.h" + +namespace LAMMPS_NS { + +class ComputeDeepmdFparamDedn : public Compute { + public: + ComputeDeepmdFparamDedn(class LAMMPS*, int, char**); + ~ComputeDeepmdFparamDedn() override; + void init() override; + double compute_scalar() override; + + private: + enum SourceType { SRC_VAR, SRC_COMPUTE, SRC_FIX }; + + SourceType source_type; + std::string source_id; + int source_index; + double delta; + PairDeepMD* pair; + + double get_source_value(); +}; + +} // namespace LAMMPS_NS + +#endif +#endif diff --git a/source/lmp/pair_base.cpp b/source/lmp/pair_base.cpp index 63e5462590..64a2bc223f 100644 --- a/source/lmp/pair_base.cpp +++ b/source/lmp/pair_base.cpp @@ -164,6 +164,50 @@ void PairDeepBaseModel::make_fparam_from_compute(vector& fparam) { } } +void PairDeepBaseModel::make_fparam_from_fix(vector& fparam) { + assert(do_fix_fparam); + + int ifix = modify->find_fix(fix_fparam_id); + Fix* fix = modify->fix[ifix]; + + if (!fix) { + error->all(FLERR, "fix id is not found: " + fix_fparam_id); + } + fparam.resize(dim_fparam); + + if (fix_fparam_index < 0) { + if (dim_fparam == 1) { + if (!fix->scalar_flag) { + error->all(FLERR, "fix " + fix_fparam_id + + " does not provide a scalar for fparam"); + } + fparam[0] = fix->compute_scalar(); + } else if (dim_fparam > 1) { + if (!fix->scalar_flag) { + error->all(FLERR, "fix " + fix_fparam_id + + " does not provide a scalar for fparam"); + } + double value = fix->compute_scalar(); + for (int jj = 0; jj < dim_fparam; ++jj) { + fparam[jj] = value; + } + } + } else { + if (!fix->vector_flag) { + error->all(FLERR, "fix " + fix_fparam_id + + " does not provide a vector for fparam"); + } + if (fix->size_vector < fix_fparam_index + dim_fparam) { + error->all(FLERR, "fix " + fix_fparam_id + + " vector is shorter than fparam dimension"); + } + for (int jj = 0; jj < dim_fparam; ++jj) { + fparam[jj] = fix->compute_vector(fix_fparam_index + jj); + } + } + +} + void PairDeepBaseModel::make_aparam_from_compute(vector& aparam) { assert(do_compute_aparam); @@ -338,6 +382,8 @@ PairDeepBaseModel::PairDeepBaseModel( scale = NULL; do_ttm = false; do_compute_fparam = false; + do_fix_fparam = false; + fix_fparam_index = -1; do_compute_aparam = false; single_model = false; multi_models_mod_devi = false; diff --git a/source/lmp/pair_base.h b/source/lmp/pair_base.h index 1dd4b84041..f7c3b34808 100644 --- a/source/lmp/pair_base.h +++ b/source/lmp/pair_base.h @@ -43,6 +43,7 @@ class PairDeepBaseModel : public Pair { void print_summary(const std::string pre) const; int get_node_rank(); void cum_sum(std::map&, std::map&); + int get_dim_fparam() const { return dim_fparam; } std::string get_file_content(const std::string& model); std::vector get_file_content( @@ -86,6 +87,10 @@ class PairDeepBaseModel : public Pair { void make_fparam_from_compute(std::vector& fparam); bool do_compute_fparam; std::string compute_fparam_id; + void make_fparam_from_fix(std::vector& fparam); + bool do_fix_fparam; + std::string fix_fparam_id; + int fix_fparam_index; void make_aparam_from_compute(std::vector& aparam); bool do_compute_aparam; std::string compute_aparam_id; diff --git a/source/lmp/pair_deepmd.cpp b/source/lmp/pair_deepmd.cpp index fb27fa0ff6..79afb6529f 100644 --- a/source/lmp/pair_deepmd.cpp +++ b/source/lmp/pair_deepmd.cpp @@ -119,7 +119,10 @@ static const char cite_user_deepmd_package[] = PairDeepMD::PairDeepMD(LAMMPS* lmp) : PairDeepBaseModel( - lmp, cite_user_deepmd_package, deep_pot, deep_pot_model_devi) { + lmp, cite_user_deepmd_package, deep_pot, deep_pot_model_devi), + commdata_(nullptr), + cached_dedn(0.0), + cached_dedn_valid(1) { // Constructor body can be empty } @@ -127,6 +130,127 @@ PairDeepMD::~PairDeepMD() { // Ensure base class destructor is called } +void* PairDeepMD::extract(const char* str, int& dim) { + if (strcmp(str, "deepmd_dedn") == 0) { + if (!cached_dedn_valid) return nullptr; + dim = 0; + return (void*)&cached_dedn; + } + return PairDeepBaseModel::extract(str, dim); +} + +double PairDeepMD::eval_energy_with_fparam( + const std::vector& fparam_override) { + if (numb_models != 1) { + error->all(FLERR, + "deepmd/fparam/dedn currently supports single-model pair_style " + "only"); + } + if (atom->sp_flag) { + error->all(FLERR, + "Pair style 'deepmd' does not support spin atoms, please use " + "pair style 'deepspin' instead."); + } + + bool do_ghost = true; + commdata_ = (CommBrickDeepMD*)comm; + double** x = atom->x; + int* type = atom->type; + int nlocal = atom->nlocal; + int nghost = 0; + if (do_ghost) { + nghost = atom->nghost; + } + int nall = nlocal + nghost; + + std::vector dtype(nall); + for (int ii = 0; ii < nall; ++ii) { + dtype[ii] = type_idx_map[type[ii] - 1]; + } + + double dener(0); + std::vector dforce(nall * 3); + std::vector dvirial(9, 0); + std::vector dcoord(nall * 3, 0.); + std::vector dbox(9, 0); + std::vector daparam; + + if (dim_fparam > 0 && + fparam_override.size() != static_cast(dim_fparam)) { + error->all(FLERR, "fparam override has the wrong dimension"); + } + + // get box + dbox[0] = domain->h[0] / dist_unit_cvt_factor; // xx + dbox[4] = domain->h[1] / dist_unit_cvt_factor; // yy + dbox[8] = domain->h[2] / dist_unit_cvt_factor; // zz + dbox[7] = domain->h[3] / dist_unit_cvt_factor; // zy + dbox[6] = domain->h[4] / dist_unit_cvt_factor; // zx + dbox[3] = domain->h[5] / dist_unit_cvt_factor; // yx + + // get coord + for (int ii = 0; ii < nall; ++ii) { + for (int dd = 0; dd < 3; ++dd) { + dcoord[ii * 3 + dd] = + (x[ii][dd] - domain->boxlo[dd]) / dist_unit_cvt_factor; + } + } + + // mapping (for DPA-2/3 .pt2 GNN models that gather ghost features via + // the LAMMPS atom-map; harmless for other models). + std::vector mapping_vec(nall, -1); + if (comm->nprocs == 1 && atom->map_style != Atom::MAP_NONE) { + for (size_t ii = 0; ii < nall; ++ii) { + mapping_vec[ii] = atom->map(atom->tag[ii]); + } + } + + if (do_compute_aparam) { + make_aparam_from_compute(daparam); + } else if (aparam.size() > 0) { + make_uniform_aparam(daparam, aparam, nlocal); + } else if (do_ttm) { +#ifdef USE_TTM + if (dim_aparam > 0) { + make_ttm_aparam(daparam); + } +#endif + } + if (do_compute_fparam) { + make_fparam_from_compute(fparam); + } else if (do_fix_fparam) { + make_fparam_from_fix(fparam); + } + + int ago = neighbor->ago; + + if (do_ghost) { + if (list) { + neighbor->build_one(list); + } + deepmd_compat::InputNlist lmp_list( + list->inum, list->ilist, list->numneigh, list->firstneigh, + commdata_->nswap, commdata_->sendnum, commdata_->recvnum, + commdata_->firstrecv, commdata_->sendlist, commdata_->sendproc, + commdata_->recvproc, &world, comm->nprocs); + lmp_list.set_mask(NEIGHMASK); + if (comm->nprocs == 1 && atom->map_style != Atom::MAP_NONE) { + lmp_list.set_mapping(mapping_vec.data()); + } + + try { + deep_pot.compute(dener, dforce, dvirial, dcoord, dtype, dbox, nghost, + lmp_list, ago, fparam, daparam); + } catch (deepmd_compat::deepmd_exception& e) { + error->one(FLERR, e.what()); + } + } else { + error->all(FLERR, "unknown computational branch"); + } + + return scale[1][1] * dener * ener_unit_cvt_factor; +} + void PairDeepMD::compute(int eflag, int vflag) { if (numb_models == 0) { return; @@ -214,6 +338,8 @@ void PairDeepMD::compute(int eflag, int vflag) { if (do_compute_fparam) { make_fparam_from_compute(fparam); + } else if (do_fix_fparam) { + make_fparam_from_fix(fparam); } // int ago = numb_models > 1 ? 0 : neighbor->ago; @@ -248,14 +374,34 @@ void PairDeepMD::compute(int eflag, int vflag) { // cvflag_atom is the right flag for the cvatom matrix if (!(eflag_atom || cvflag_atom)) { try { - deep_pot.compute(dener, dforce, dvirial, dcoord, dtype, dbox, nghost, - lmp_list, ago, fparam, daparam); + if (dim_fparam > 0) { + deep_pot.compute(dener, dforce, dvirial, cached_dedn, dcoord, dtype, + dbox, nghost, lmp_list, ago, fparam, daparam); + cached_dedn *= scale[1][1] * ener_unit_cvt_factor; + cached_dedn_valid = 1; + } else { + deep_pot.compute(dener, dforce, dvirial, dcoord, dtype, dbox, + nghost, lmp_list, ago, fparam, daparam); + cached_dedn = 0.0; + cached_dedn_valid = 1; + } } catch (deepmd_compat::deepmd_exception& e) { - error->one(FLERR, e.what()); + if (dim_fparam > 0) { + cached_dedn_valid = 0; + try { + deep_pot.compute(dener, dforce, dvirial, dcoord, dtype, dbox, + nghost, lmp_list, ago, fparam, daparam); + } catch (deepmd_compat::deepmd_exception& e2) { + error->one(FLERR, e2.what()); + } + } else { + error->one(FLERR, e.what()); + } } } // do atomic energy and virial else { + cached_dedn_valid = 0; vector deatom(nall * 1, 0); vector dvatom(nall * 9, 0); try { @@ -302,6 +448,7 @@ void PairDeepMD::compute(int eflag, int vflag) { } } } else if (multi_models_mod_devi) { + cached_dedn_valid = 0; vector deatom(nall * 1, 0); vector dvatom(nall * 9, 0); vector> all_virial; @@ -495,6 +642,7 @@ void PairDeepMD::compute(int eflag, int vflag) { } } else { if (numb_models == 1) { + cached_dedn_valid = 0; try { deep_pot.compute(dener, dforce, dvirial, dcoord, dtype, dbox); } catch (deepmd_compat::deepmd_exception& e) { @@ -533,6 +681,7 @@ static bool is_key(const string& input) { keys.push_back("fparam"); keys.push_back("aparam"); keys.push_back("fparam_from_compute"); + keys.push_back("fparam_from_fix"); keys.push_back("aparam_from_compute"); keys.push_back("ttm"); keys.push_back("atomic"); @@ -675,6 +824,21 @@ void PairDeepMD::settings(int narg, char** arg) { do_compute_fparam = true; compute_fparam_id = arg[iarg + 1]; iarg += 1 + 1; + } else if (string(arg[iarg]) == string("fparam_from_fix")) { + if (iarg + 1 >= narg || is_key(arg[iarg + 1])) { + error->all(FLERR, + "invalid fparam_from_fix key: should be " + "fparam_from_fix fix_fparam_id(str) [fix_vector_index]"); + } + do_fix_fparam = true; + fix_fparam_id = arg[iarg + 1]; + fix_fparam_index = -1; + if (iarg + 2 < narg && !is_key(arg[iarg + 2])) { + fix_fparam_index = atoi(arg[iarg + 2]) - 1; + iarg += 3; + } else { + iarg += 2; + } } else if (string(arg[iarg]) == string("aparam_from_compute")) { for (int ii = 0; ii < 1; ++ii) { if (iarg + 1 + ii >= narg || is_key(arg[iarg + 1 + ii])) { @@ -725,6 +889,15 @@ void PairDeepMD::settings(int narg, char** arg) { FLERR, "fparam and fparam_from_compute should NOT be set simultaneously"); } + if (do_fix_fparam && fparam.size() > 0) { + error->all(FLERR, + "fparam and fparam_from_fix should NOT be set simultaneously"); + } + if (do_fix_fparam && do_compute_fparam) { + error->all(FLERR, + "fparam_from_compute and fparam_from_fix should NOT be set " + "simultaneously"); + } if (comm->me == 0) { if (numb_models > 1 && out_freq > 0) { @@ -769,6 +942,14 @@ void PairDeepMD::settings(int narg, char** arg) { cout << pre << "using compute id (fparam): "; cout << compute_fparam_id << " " << endl; } + if (do_fix_fparam) { + cout << pre << "using fix id (fparam): "; + cout << fix_fparam_id; + if (fix_fparam_index >= 0) { + cout << "[" << fix_fparam_index + 1 << "]"; + } + cout << " " << endl; + } if (do_compute_aparam) { cout << pre << "using compute id (aparam): "; cout << compute_aparam_id << " " << endl; diff --git a/source/lmp/pair_deepmd.h b/source/lmp/pair_deepmd.h index 6d54a69fe6..b72fad2af4 100644 --- a/source/lmp/pair_deepmd.h +++ b/source/lmp/pair_deepmd.h @@ -47,8 +47,13 @@ class PairDeepMD : public PairDeepBaseModel { void settings(int, char**) override; void coeff(int, char**) override; void compute(int, int) override; + void* extract(const char*, int&) override; int pack_reverse_comm(int, int, double*) override; void unpack_reverse_comm(int, int*, double*) override; + const std::vector& current_fparam() const { return fparam; } + double eval_energy_with_fparam(const std::vector& fparam_override); + double latest_dedn() const { return cached_dedn; } + bool has_latest_dedn() const { return cached_dedn_valid; } protected: deepmd_compat::DeepPot deep_pot; @@ -56,6 +61,8 @@ class PairDeepMD : public PairDeepBaseModel { private: CommBrickDeepMD* commdata_; + double cached_dedn; + int cached_dedn_valid; }; } // namespace LAMMPS_NS diff --git a/source/lmp/plugin/CMakeLists.txt b/source/lmp/plugin/CMakeLists.txt index 8f32af3e3e..9dee0be1f0 100644 --- a/source/lmp/plugin/CMakeLists.txt +++ b/source/lmp/plugin/CMakeLists.txt @@ -1,8 +1,11 @@ if(DEFINED LAMMPS_SOURCE_ROOT OR DEFINED LAMMPS_VERSION) message(STATUS "enable LAMMPS plugin mode") + if(CMAKE_CXX_STANDARD LESS 17) + set(CMAKE_CXX_STANDARD 17) + endif() add_library(lammps_interface INTERFACE) if(DEFINED LAMMPS_VERSION) - cmake_minimum_required(VERSION 3.25.2) + cmake_minimum_required(VERSION 3.23.0) include(FetchContent) FetchContent_Declare( lammps_download diff --git a/source/lmp/plugin/deepmdplugin.cpp b/source/lmp/plugin/deepmdplugin.cpp index d3b54f8e41..033e685276 100644 --- a/source/lmp/plugin/deepmdplugin.cpp +++ b/source/lmp/plugin/deepmdplugin.cpp @@ -3,6 +3,7 @@ * See https://docs.lammps.org/Developer_plugins.html */ #include "compute_deeptensor_atom.h" +#include "compute_deepmd_fparam_dedn.h" #include "deepmd_version.h" #include "fix_dplr.h" #include "lammpsplugin.h" @@ -22,6 +23,10 @@ static Compute* computedeepmdtensoratom(LAMMPS* lmp, int narg, char** arg) { return new ComputeDeeptensorAtom(lmp, narg, arg); } +static Compute* computedeepmdfparamdedn(LAMMPS* lmp, int narg, char** arg) { + return new ComputeDeepmdFparamDedn(lmp, narg, arg); +} + static Fix* fixdplr(LAMMPS* lmp, int narg, char** arg) { return new FixDPLR(lmp, narg, arg); } @@ -59,6 +64,13 @@ extern "C" void lammpsplugin_init(void* lmp, void* handle, void* regfunc) { plugin.creator.v2 = (lammpsplugin_factory2*)&computedeepmdtensoratom; (*register_plugin)(&plugin, lmp); + plugin.style = "compute"; + plugin.name = "deepmd/fparam/dedn"; + plugin.info = "compute deepmd/fparam/dedn " STR_GIT_SUMM; + plugin.author = "OpenAI"; + plugin.creator.v2 = (lammpsplugin_factory2*)&computedeepmdfparamdedn; + (*register_plugin)(&plugin, lmp); + plugin.style = "fix"; plugin.name = "dplr"; plugin.info = "fix dplr " STR_GIT_SUMM; diff --git a/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py b/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py new file mode 100644 index 0000000000..106d152b8d --- /dev/null +++ b/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py @@ -0,0 +1,95 @@ +"""Test DeepMD fparam from fix and numeric dE/dfparam from the current state.""" + +import os +from pathlib import Path + +import numpy as np +import pytest +from lammps import PyLammps + +from model_convert import ensure_converted_pb +from write_lmp_data import write_lmp_data + +pbtxt_file = ( + Path(__file__).parent.parent.parent / "tests" / "infer" / "fparam_aparam.pbtxt" +) +pb_file = Path(__file__).parent / "fparam_aparam.pb" +data_file = Path(__file__).parent / "data.lmp" + +box = np.array([0, 13, 0, 13, 0, 13, 0, 0, 0]) +coord = np.array( + [ + [12.83, 2.56, 2.18], + [12.09, 2.87, 2.74], + [0.25, 3.32, 1.68], + [3.36, 3.00, 1.81], + [3.51, 2.51, 2.60], + [4.27, 3.22, 1.56], + ] +) +type_OH = np.array([1, 1, 1, 1, 1, 1]) + + +def setup_module() -> None: + if os.environ.get("ENABLE_TENSORFLOW", "1") != "1": + pytest.skip("Skip test because TensorFlow support is not enabled.") + ensure_converted_pb(pbtxt_file, pb_file) + write_lmp_data(box, coord, type_OH, data_file) + + +def teardown_module() -> None: + if data_file.exists(): + os.remove(data_file) + + +def _lammps(fp_value, units="metal") -> PyLammps: + lammps = PyLammps() + lammps.units(units) + lammps.boundary("p p p") + lammps.atom_style("atomic") + lammps.neighbor("2.0 bin") + lammps.neigh_modify("every 10 delay 0 check no") + lammps.read_data(data_file.resolve()) + lammps.mass("1 16") + lammps.timestep(0.0005) + lammps.fix("1 all nve") + lammps.variable("fp equal " + str(fp_value)) + lammps.variable("dummy equal 0.0") + lammps.fix("fparam all ave/time 1 1 1 v_dummy v_fp") + lammps.pair_style( + f"deepmd {pb_file.resolve()} fparam_from_fix fparam 2 aparam 0.25852028" + ) + lammps.pair_coeff("* *") + lammps.compute("dedn all deepmd/fparam/dedn f_fparam[2]") + return lammps + + +@pytest.fixture +def lammps(): + lmp = _lammps(fp_value=0.25852028) + yield lmp + lmp.close() + + +def _energy_at_fp(fp_value): + lmp = _lammps(fp_value=fp_value) + try: + lmp.run(0) + return lmp.eval("pe") + finally: + lmp.close() + + +def test_pair_fparam_from_fix(lammps) -> None: + lammps.run(0) + assert lammps.eval("pe") == pytest.approx(_energy_at_fp(0.25852028)) + + +def test_compute_deepmd_fparam_dedn(lammps) -> None: + eps = 1.0e-6 + lammps.run(0) + dedn = lammps.eval("c_dedn") + ref = (_energy_at_fp(0.25852028 + eps) - _energy_at_fp(0.25852028 - eps)) / ( + 2.0 * eps + ) + assert dedn == pytest.approx(ref, rel=1.0e-4, abs=1.0e-4) From 2e126e5169630d205f300cda9d0869799a3d87f6 Mon Sep 17 00:00:00 2001 From: LiFu Date: Sat, 6 Jun 2026 00:51:51 +0800 Subject: [PATCH 02/11] Fill missing C API dE/dN entry points --- source/api_c/src/c_api.cc | 155 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/source/api_c/src/c_api.cc b/source/api_c/src/c_api.cc index 5e5fac65a6..a7db960e52 100644 --- a/source/api_c/src/c_api.cc +++ b/source/api_c/src/c_api.cc @@ -324,6 +324,125 @@ template void DP_DeepPotCompute_variant(DP_DeepPot* dp, float* atomic_energy, float* atomic_virial); +template +inline void DP_DeepPotCompute_variantadd(DP_DeepPot* dp, + const int nframes, + const int natoms, + const VALUETYPE* coord, + const int* atype, + const VALUETYPE* cell, + const VALUETYPE* fparam, + const VALUETYPE* aparam, + double* energy, + VALUETYPE* force, + VALUETYPE* virial, + double* dE_dN, + VALUETYPE* atomic_energy, + VALUETYPE* atomic_virial) { + std::vector coord_(coord, coord + nframes * natoms * 3); + std::vector atype_(atype, atype + natoms); + std::vector cell_; + if (cell) { + cell_.assign(cell, cell + nframes * 9); + } + std::vector fparam_; + if (fparam) { + fparam_.assign(fparam, fparam + nframes * dp->dfparam); + } + std::vector aparam_; + if (aparam) { + aparam_.assign(aparam, aparam + nframes * natoms * dp->daparam); + } + + std::vector e; + std::vector f, v, ae, av; + if (atomic_energy || atomic_virial) { + DP_REQUIRES_OK(dp, dp->dp.compute(e, f, v, ae, av, coord_, atype_, cell_, + fparam_, aparam_)); + } else { + DP_REQUIRES_OK( + dp, dp->dp.compute(e, f, v, coord_, atype_, cell_, fparam_, aparam_)); + } + + if (dE_dN) { + if (dp->dfparam != 1) { + dp->exception = + "Direct dE/dN C API currently supports only dim_fparam == 1"; + return; + } + if (!fparam) { + dp->exception = + "Direct dE/dN C API requires explicit frame parameters"; + return; + } + constexpr double delta = 1.0e-6; + std::vector fparam_plus = fparam_; + std::vector fparam_minus = fparam_; + std::vector e_plus, e_minus; + std::vector dummy_force, dummy_virial; + + for (int ii = 0; ii < nframes; ++ii) { + fparam_plus[ii] += static_cast(delta); + fparam_minus[ii] -= static_cast(delta); + DP_REQUIRES_OK(dp, dp->dp.compute(e_plus, dummy_force, dummy_virial, + coord_, atype_, cell_, fparam_plus, + aparam_)); + DP_REQUIRES_OK(dp, dp->dp.compute(e_minus, dummy_force, dummy_virial, + coord_, atype_, cell_, fparam_minus, + aparam_)); + dE_dN[ii] = (e_plus[ii] - e_minus[ii]) / (2.0 * delta); + fparam_plus[ii] = fparam_[ii]; + fparam_minus[ii] = fparam_[ii]; + } + } + + if (energy) { + std::copy(e.begin(), e.end(), energy); + } + if (force) { + std::copy(f.begin(), f.end(), force); + } + if (virial) { + std::copy(v.begin(), v.end(), virial); + } + if (atomic_energy) { + std::copy(ae.begin(), ae.end(), atomic_energy); + } + if (atomic_virial) { + std::copy(av.begin(), av.end(), atomic_virial); + } +} + +template void DP_DeepPotCompute_variantadd(DP_DeepPot* dp, + const int nframes, + const int natoms, + const double* coord, + const int* atype, + const double* cell, + const double* fparam, + const double* aparam, + double* energy, + double* force, + double* virial, + double* dE_dN, + double* atomic_energy, + double* atomic_virial); + +template void DP_DeepPotCompute_variantadd(DP_DeepPot* dp, + const int nframes, + const int natoms, + const float* coord, + const int* atype, + const float* cell, + const float* fparam, + const float* aparam, + double* energy, + float* force, + float* virial, + double* dE_dN, + float* atomic_energy, + float* atomic_virial); + // support spin template inline void DP_DeepSpinCompute_variant(DP_DeepSpin* dp, @@ -1669,6 +1788,24 @@ void DP_DeepPotCompute2(DP_DeepPot* dp, fparam, aparam, energy, force, virial, atomic_energy, atomic_virial); } +void DP_DeepPotCompute2add(DP_DeepPot* dp, + const int nframes, + const int natoms, + const double* coord, + const int* atype, + const double* cell, + const double* fparam, + const double* aparam, + double* energy, + double* force, + double* virial, + double* dE_dN, + double* atomic_energy, + double* atomic_virial) { + DP_DeepPotCompute_variantadd(dp, nframes, natoms, coord, atype, cell, + fparam, aparam, energy, force, virial, + dE_dN, atomic_energy, atomic_virial); +} void DP_DeepSpinCompute2(DP_DeepSpin* dp, const int nframes, const int natoms, @@ -1706,6 +1843,24 @@ void DP_DeepPotComputef2(DP_DeepPot* dp, fparam, aparam, energy, force, virial, atomic_energy, atomic_virial); } +void DP_DeepPotComputef2add(DP_DeepPot* dp, + const int nframes, + const int natoms, + const float* coord, + const int* atype, + const float* cell, + const float* fparam, + const float* aparam, + double* energy, + float* force, + float* virial, + double* dE_dN, + float* atomic_energy, + float* atomic_virial) { + DP_DeepPotCompute_variantadd(dp, nframes, natoms, coord, atype, cell, + fparam, aparam, energy, force, virial, + dE_dN, atomic_energy, atomic_virial); +} void DP_DeepSpinComputef2(DP_DeepSpin* dp, const int nframes, From e93f1bf9a3d119113c9840e281e8ca1d74353f72 Mon Sep 17 00:00:00 2001 From: LiFu Date: Sat, 6 Jun 2026 02:02:39 +0800 Subject: [PATCH 03/11] Keep DeepMD dE/dN provider off the main pair path --- source/lmp/pair_deepmd.cpp | 28 ++++--------------- .../tests/test_lammps_fparam_from_fix_dedn.py | 6 ++-- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/source/lmp/pair_deepmd.cpp b/source/lmp/pair_deepmd.cpp index 79afb6529f..9267728f33 100644 --- a/source/lmp/pair_deepmd.cpp +++ b/source/lmp/pair_deepmd.cpp @@ -122,7 +122,7 @@ PairDeepMD::PairDeepMD(LAMMPS* lmp) lmp, cite_user_deepmd_package, deep_pot, deep_pot_model_devi), commdata_(nullptr), cached_dedn(0.0), - cached_dedn_valid(1) { + cached_dedn_valid(0) { // Constructor body can be empty } @@ -373,30 +373,12 @@ void PairDeepMD::compute(int eflag, int vflag) { if (single_model || multi_models_no_mod_devi) { // cvflag_atom is the right flag for the cvatom matrix if (!(eflag_atom || cvflag_atom)) { + cached_dedn_valid = 0; try { - if (dim_fparam > 0) { - deep_pot.compute(dener, dforce, dvirial, cached_dedn, dcoord, dtype, - dbox, nghost, lmp_list, ago, fparam, daparam); - cached_dedn *= scale[1][1] * ener_unit_cvt_factor; - cached_dedn_valid = 1; - } else { - deep_pot.compute(dener, dforce, dvirial, dcoord, dtype, dbox, - nghost, lmp_list, ago, fparam, daparam); - cached_dedn = 0.0; - cached_dedn_valid = 1; - } + deep_pot.compute(dener, dforce, dvirial, dcoord, dtype, dbox, nghost, + lmp_list, ago, fparam, daparam); } catch (deepmd_compat::deepmd_exception& e) { - if (dim_fparam > 0) { - cached_dedn_valid = 0; - try { - deep_pot.compute(dener, dforce, dvirial, dcoord, dtype, dbox, - nghost, lmp_list, ago, fparam, daparam); - } catch (deepmd_compat::deepmd_exception& e2) { - error->one(FLERR, e2.what()); - } - } else { - error->one(FLERR, e.what()); - } + error->one(FLERR, e.what()); } } // do atomic energy and virial diff --git a/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py b/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py index 106d152b8d..13e9b21ba0 100644 --- a/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py +++ b/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py @@ -55,12 +55,12 @@ def _lammps(fp_value, units="metal") -> PyLammps: lammps.fix("1 all nve") lammps.variable("fp equal " + str(fp_value)) lammps.variable("dummy equal 0.0") - lammps.fix("fparam all ave/time 1 1 1 v_dummy v_fp") + lammps.fix("fpfix all ave/time 1 1 1 v_dummy v_fp") lammps.pair_style( - f"deepmd {pb_file.resolve()} fparam_from_fix fparam 2 aparam 0.25852028" + f"deepmd {pb_file.resolve()} fparam_from_fix fpfix 2 aparam 0.25852028" ) lammps.pair_coeff("* *") - lammps.compute("dedn all deepmd/fparam/dedn f_fparam[2]") + lammps.compute("dedn all deepmd/fparam/dedn f_fpfix[2]") return lammps From bfa0cc85f6df334d575a87bd790efaed45424e37 Mon Sep 17 00:00:00 2001 From: LiFu Date: Sat, 6 Jun 2026 02:56:16 +0800 Subject: [PATCH 04/11] Reuse the existing pair neighbor list for fparam dE/dN --- source/lmp/pair_deepmd.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/lmp/pair_deepmd.cpp b/source/lmp/pair_deepmd.cpp index 9267728f33..d24fb07099 100644 --- a/source/lmp/pair_deepmd.cpp +++ b/source/lmp/pair_deepmd.cpp @@ -225,8 +225,9 @@ double PairDeepMD::eval_energy_with_fparam( int ago = neighbor->ago; if (do_ghost) { - if (list) { - neighbor->build_one(list); + if (!list) { + error->all(FLERR, + "deepmd/fparam/dedn requires an available pair neighbor list"); } deepmd_compat::InputNlist lmp_list( list->inum, list->ilist, list->numneigh, list->firstneigh, From 3460ce1375caeb0701afcf8354fc3c9ae02547b9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2026 09:09:09 +0000 Subject: [PATCH 05/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/api_c/include/deepmd.hpp | 8 ++-- source/api_c/src/c_api.cc | 44 ++++++++++++------- source/api_cc/src/DeepPotTF.cc | 4 +- source/lmp/compute_deepmd_fparam_dedn.cpp | 26 +++++------ source/lmp/pair_base.cpp | 1 - source/lmp/pair_deepmd.cpp | 4 +- source/lmp/plugin/deepmdplugin.cpp | 2 +- .../tests/test_lammps_fparam_from_fix_dedn.py | 18 +++++--- 8 files changed, 63 insertions(+), 44 deletions(-) diff --git a/source/api_c/include/deepmd.hpp b/source/api_c/include/deepmd.hpp index b445a17246..3fcf9d36bb 100644 --- a/source/api_c/include/deepmd.hpp +++ b/source/api_c/include/deepmd.hpp @@ -146,8 +146,8 @@ inline void _DP_DeepPotComputeadd(DP_DeepPot* dp, double* dE_dN, float* atomic_energy, float* atomic_virial) { - DP_DeepPotComputef2add(dp, nframes, natom, coord, atype, cell, fparam, - aparam, energy, force, virial, dE_dN, atomic_energy, + DP_DeepPotComputef2add(dp, nframes, natom, coord, atype, cell, fparam, aparam, + energy, force, virial, dE_dN, atomic_energy, atomic_virial); } @@ -334,8 +334,8 @@ inline void _DP_DeepPotComputeNListadd(DP_DeepPot* dp, float* atomic_energy, float* atomic_virial) { DP_DeepPotComputeNListf2add(dp, nframes, natom, coord, atype, cell, nghost, - nlist, ago, fparam, aparam, energy, force, - virial, dE_dN, atomic_energy, atomic_virial); + nlist, ago, fparam, aparam, energy, force, virial, + dE_dN, atomic_energy, atomic_virial); } // support spin diff --git a/source/api_c/src/c_api.cc b/source/api_c/src/c_api.cc index a7db960e52..64f0933086 100644 --- a/source/api_c/src/c_api.cc +++ b/source/api_c/src/c_api.cc @@ -371,8 +371,7 @@ inline void DP_DeepPotCompute_variantadd(DP_DeepPot* dp, return; } if (!fparam) { - dp->exception = - "Direct dE/dN C API requires explicit frame parameters"; + dp->exception = "Direct dE/dN C API requires explicit frame parameters"; return; } constexpr double delta = 1.0e-6; @@ -384,12 +383,12 @@ inline void DP_DeepPotCompute_variantadd(DP_DeepPot* dp, for (int ii = 0; ii < nframes; ++ii) { fparam_plus[ii] += static_cast(delta); fparam_minus[ii] -= static_cast(delta); - DP_REQUIRES_OK(dp, dp->dp.compute(e_plus, dummy_force, dummy_virial, - coord_, atype_, cell_, fparam_plus, - aparam_)); - DP_REQUIRES_OK(dp, dp->dp.compute(e_minus, dummy_force, dummy_virial, - coord_, atype_, cell_, fparam_minus, - aparam_)); + DP_REQUIRES_OK( + dp, dp->dp.compute(e_plus, dummy_force, dummy_virial, coord_, atype_, + cell_, fparam_plus, aparam_)); + DP_REQUIRES_OK( + dp, dp->dp.compute(e_minus, dummy_force, dummy_virial, coord_, atype_, + cell_, fparam_minus, aparam_)); dE_dN[ii] = (e_plus[ii] - e_minus[ii]) / (2.0 * delta); fparam_plus[ii] = fparam_[ii]; fparam_minus[ii] = fparam_[ii]; @@ -677,14 +676,27 @@ inline void DP_DeepPotComputeNList_variantadd(DP_DeepPot* dp, throw deepmd::deepmd_exception( "Direct dE/dN C API currently supports single-frame evaluation only"); } - DP_REQUIRES_OK(dp, dp->dp.compute(e, f, v, dedn, coord_, atype_, cell_, nghost, - nlist->nl, ago, fparam_, aparam_)); - if (energy) energy[0] = e; - if (force) std::copy(f.begin(), f.end(), force); - if (virial) std::copy(v.begin(), v.end(), virial); - if (dE_dN) dE_dN[0] = dedn; - if (atomic_energy) std::fill(atomic_energy, atomic_energy + nframes * natoms, (VALUETYPE)0); - if (atomic_virial) std::fill(atomic_virial, atomic_virial + nframes * natoms * 9, (VALUETYPE)0); + DP_REQUIRES_OK(dp, dp->dp.compute(e, f, v, dedn, coord_, atype_, cell_, + nghost, nlist->nl, ago, fparam_, aparam_)); + if (energy) { + energy[0] = e; + } + if (force) { + std::copy(f.begin(), f.end(), force); + } + if (virial) { + std::copy(v.begin(), v.end(), virial); + } + if (dE_dN) { + dE_dN[0] = dedn; + } + if (atomic_energy) { + std::fill(atomic_energy, atomic_energy + nframes * natoms, (VALUETYPE)0); + } + if (atomic_virial) { + std::fill(atomic_virial, atomic_virial + nframes * natoms * 9, + (VALUETYPE)0); + } } template void DP_DeepPotComputeNList_variantadd(DP_DeepPot* dp, diff --git a/source/api_cc/src/DeepPotTF.cc b/source/api_cc/src/DeepPotTF.cc index 32f12d1293..f8adf2bad4 100644 --- a/source/api_cc/src/DeepPotTF.cc +++ b/source/api_cc/src/DeepPotTF.cc @@ -146,8 +146,8 @@ static void run_model_dedn( std::vector output_tensors; check_status(session->Run( input_tensors, - {"o_energy", "o_force", "o_dE_dN", "o_atom_energy", "o_atom_virial"}, - {}, &output_tensors)); + {"o_energy", "o_force", "o_dE_dN", "o_atom_energy", "o_atom_virial"}, {}, + &output_tensors)); Tensor output_e = output_tensors[0]; Tensor output_f = output_tensors[1]; diff --git a/source/lmp/compute_deepmd_fparam_dedn.cpp b/source/lmp/compute_deepmd_fparam_dedn.cpp index 1ac7b3de59..0d8645bcaf 100644 --- a/source/lmp/compute_deepmd_fparam_dedn.cpp +++ b/source/lmp/compute_deepmd_fparam_dedn.cpp @@ -18,12 +18,10 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -ComputeDeepmdFparamDedn::ComputeDeepmdFparamDedn(LAMMPS* lmp, int narg, +ComputeDeepmdFparamDedn::ComputeDeepmdFparamDedn(LAMMPS* lmp, + int narg, char** arg) - : Compute(lmp, narg, arg), - source_index(-1), - delta(1.0e-6), - pair(nullptr) { + : Compute(lmp, narg, arg), source_index(-1), delta(1.0e-6), pair(nullptr) { if (narg < 4) { error->all(FLERR, "Illegal compute deepmd/fparam/dedn command"); } @@ -54,8 +52,7 @@ ComputeDeepmdFparamDedn::ComputeDeepmdFparamDedn(LAMMPS* lmp, int narg, source_type = SRC_FIX; source_id = token.substr(2); } else { - error->all(FLERR, - "Source must be a variable, compute, or fix reference"); + error->all(FLERR, "Source must be a variable, compute, or fix reference"); } int iarg = 4; @@ -83,12 +80,14 @@ ComputeDeepmdFparamDedn::~ComputeDeepmdFparamDedn() = default; void ComputeDeepmdFparamDedn::init() { if (!force->pair) { - error->all(FLERR, "compute deepmd/fparam/dedn requires an active pair style"); + error->all(FLERR, + "compute deepmd/fparam/dedn requires an active pair style"); } pair = dynamic_cast(force->pair); if (!pair) { - error->all(FLERR, - "compute deepmd/fparam/dedn currently requires pair_style deepmd"); + error->all( + FLERR, + "compute deepmd/fparam/dedn currently requires pair_style deepmd"); } if (pair->get_dim_fparam() != 1) { error->all(FLERR, @@ -177,10 +176,9 @@ double ComputeDeepmdFparamDedn::compute_scalar() { std::vector fparam_plus(1, fparam0 + delta); std::vector fparam_minus(1, fparam0 - delta); - double one = - (pair->eval_energy_with_fparam(fparam_plus) - - pair->eval_energy_with_fparam(fparam_minus)) / - (2.0 * delta); + double one = (pair->eval_energy_with_fparam(fparam_plus) - + pair->eval_energy_with_fparam(fparam_minus)) / + (2.0 * delta); MPI_Allreduce(&one, &scalar, 1, MPI_DOUBLE, MPI_SUM, world); return scalar; diff --git a/source/lmp/pair_base.cpp b/source/lmp/pair_base.cpp index 64a2bc223f..2aa5626a18 100644 --- a/source/lmp/pair_base.cpp +++ b/source/lmp/pair_base.cpp @@ -205,7 +205,6 @@ void PairDeepBaseModel::make_fparam_from_fix(vector& fparam) { fparam[jj] = fix->compute_vector(fix_fparam_index + jj); } } - } void PairDeepBaseModel::make_aparam_from_compute(vector& aparam) { diff --git a/source/lmp/pair_deepmd.cpp b/source/lmp/pair_deepmd.cpp index d24fb07099..d1d574f871 100644 --- a/source/lmp/pair_deepmd.cpp +++ b/source/lmp/pair_deepmd.cpp @@ -132,7 +132,9 @@ PairDeepMD::~PairDeepMD() { void* PairDeepMD::extract(const char* str, int& dim) { if (strcmp(str, "deepmd_dedn") == 0) { - if (!cached_dedn_valid) return nullptr; + if (!cached_dedn_valid) { + return nullptr; + } dim = 0; return (void*)&cached_dedn; } diff --git a/source/lmp/plugin/deepmdplugin.cpp b/source/lmp/plugin/deepmdplugin.cpp index 033e685276..c418347687 100644 --- a/source/lmp/plugin/deepmdplugin.cpp +++ b/source/lmp/plugin/deepmdplugin.cpp @@ -2,8 +2,8 @@ /** * See https://docs.lammps.org/Developer_plugins.html */ -#include "compute_deeptensor_atom.h" #include "compute_deepmd_fparam_dedn.h" +#include "compute_deeptensor_atom.h" #include "deepmd_version.h" #include "fix_dplr.h" #include "lammpsplugin.h" diff --git a/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py b/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py index 13e9b21ba0..0098ef96e1 100644 --- a/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py +++ b/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py @@ -1,14 +1,22 @@ +# SPDX-License-Identifier: LGPL-3.0-or-later """Test DeepMD fparam from fix and numeric dE/dfparam from the current state.""" import os -from pathlib import Path +from pathlib import ( + Path, +) import numpy as np import pytest -from lammps import PyLammps - -from model_convert import ensure_converted_pb -from write_lmp_data import write_lmp_data +from lammps import ( + PyLammps, +) +from model_convert import ( + ensure_converted_pb, +) +from write_lmp_data import ( + write_lmp_data, +) pbtxt_file = ( Path(__file__).parent.parent.parent / "tests" / "infer" / "fparam_aparam.pbtxt" From b64e62b82cd38a6930c12a559628f48598a251e7 Mon Sep 17 00:00:00 2001 From: LiFu Date: Mon, 8 Jun 2026 12:24:31 +0800 Subject: [PATCH 06/11] Fix documentation wording and unused local variables --- doc/third-party/lammps-command.md | 2 +- source/api_cc/src/DeepPotTF.cc | 26 ++++++++++++++++---------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/doc/third-party/lammps-command.md b/doc/third-party/lammps-command.md index 9916d6261e..4eb75dde19 100644 --- a/doc/third-party/lammps-command.md +++ b/doc/third-party/lammps-command.md @@ -117,7 +117,7 @@ If the keyword `ttm` is set, electronic temperatures from [fix ttm command](http ### Computing the derivative with respect to a frame parameter -The compute `deepmd/fparam/dedn` evaluates a finite-difference derivative of the DeepMD energy with respect to a chosen frame parameter source. +The `deepmd/fparam/dedn` compute evaluates a finite-difference derivative of the model energy with respect to a chosen frame parameter source. ```lammps compute ID group-ID deepmd/fparam/dedn source [delta] diff --git a/source/api_cc/src/DeepPotTF.cc b/source/api_cc/src/DeepPotTF.cc index f8adf2bad4..bfa4c2a9dc 100644 --- a/source/api_cc/src/DeepPotTF.cc +++ b/source/api_cc/src/DeepPotTF.cc @@ -663,9 +663,10 @@ void DeepPotTF::compute(ENERGYVTYPE& dener, std::vector> input_tensors; if (dtype == tensorflow::DT_DOUBLE) { - int ret = session_input_tensors(input_tensors, dcoord_, ntypes, - datype_, dbox, cell_size, fparam, - aparam, atommap, "", aparam_nall); + const int ret = session_input_tensors( + input_tensors, dcoord_, ntypes, datype_, dbox, cell_size, fparam, + aparam, atommap, "", aparam_nall); + (void)ret; if (atomic) { run_model(dener, dforce_, dvirial, datom_energy_, datom_virial_, session, input_tensors, atommap, nframes); @@ -674,9 +675,10 @@ void DeepPotTF::compute(ENERGYVTYPE& dener, atommap, nframes); } } else { - int ret = session_input_tensors(input_tensors, dcoord_, ntypes, - datype_, dbox, cell_size, fparam, - aparam, atommap, "", aparam_nall); + const int ret = session_input_tensors( + input_tensors, dcoord_, ntypes, datype_, dbox, cell_size, fparam, + aparam, atommap, "", aparam_nall); + (void)ret; if (atomic) { run_model(dener, dforce_, dvirial, datom_energy_, datom_virial_, session, input_tensors, atommap, nframes); @@ -784,16 +786,18 @@ void DeepPotTF::compute_dedn(ENERGYVTYPE& dener, std::vector dener_vec; std::vector ddedn_vec; if (dtype == tensorflow::DT_DOUBLE) { - int ret = session_input_tensors( + const int ret = session_input_tensors( input_tensors, dcoord, ntypes, datype, dbox, nlist, fparam_, aparam_sel, atommap, nghost_real, ago, "", aparam_nall); + (void)ret; assert(nloc_real == ret); run_model_dedn(dener_vec, dforce, dvirial, ddedn_vec, session, input_tensors, atommap, nframes, nghost_real); } else { - int ret = session_input_tensors( + const int ret = session_input_tensors( input_tensors, dcoord, ntypes, datype, dbox, nlist, fparam_, aparam_sel, atommap, nghost_real, ago, "", aparam_nall); + (void)ret; assert(nloc_real == ret); run_model_dedn(dener_vec, dforce, dvirial, ddedn_vec, session, input_tensors, atommap, nframes, nghost_real); @@ -880,9 +884,10 @@ void DeepPotTF::compute(ENERGYVTYPE& dener, } if (dtype == tensorflow::DT_DOUBLE) { - int ret = session_input_tensors( + const int ret = session_input_tensors( input_tensors, dcoord, ntypes, datype, dbox, nlist, fparam, aparam, atommap, nghost_real, ago, "", aparam_nall); + (void)ret; assert(nloc_real == ret); if (atomic) { run_model(dener, dforce, dvirial, datom_energy, datom_virial, @@ -892,9 +897,10 @@ void DeepPotTF::compute(ENERGYVTYPE& dener, nframes, nghost_real); } } else { - int ret = session_input_tensors( + const int ret = session_input_tensors( input_tensors, dcoord, ntypes, datype, dbox, nlist, fparam, aparam, atommap, nghost_real, ago, "", aparam_nall); + (void)ret; assert(nloc_real == ret); if (atomic) { run_model(dener, dforce, dvirial, datom_energy, datom_virial, From 7c492f73752600b8a857d5d292786eb3b627a29f Mon Sep 17 00:00:00 2001 From: LiFu Date: Mon, 8 Jun 2026 21:03:44 +0800 Subject: [PATCH 07/11] Address review feedback for the dE/dN provider path --- source/api_c/src/c_api.cc | 17 +++++------ source/lmp/compute_deepmd_fparam_dedn.cpp | 11 +++++-- source/lmp/pair_base.cpp | 4 +++ source/lmp/pair_deepmd.cpp | 14 +++++++-- .../tests/test_lammps_fparam_from_fix_dedn.py | 30 ++++++++++++++++++- 5 files changed, 62 insertions(+), 14 deletions(-) diff --git a/source/api_c/src/c_api.cc b/source/api_c/src/c_api.cc index 64f0933086..0c15f74bf4 100644 --- a/source/api_c/src/c_api.cc +++ b/source/api_c/src/c_api.cc @@ -673,8 +673,14 @@ inline void DP_DeepPotComputeNList_variantadd(DP_DeepPot* dp, std::vector f, v; if (nframes != 1) { - throw deepmd::deepmd_exception( - "Direct dE/dN C API currently supports single-frame evaluation only"); + dp->exception = + "Direct dE/dN C API currently supports single-frame evaluation only"; + return; + } + if (atomic_energy || atomic_virial) { + dp->exception = + "Direct dE/dN C API does not support atomic outputs"; + return; } DP_REQUIRES_OK(dp, dp->dp.compute(e, f, v, dedn, coord_, atype_, cell_, nghost, nlist->nl, ago, fparam_, aparam_)); @@ -690,13 +696,6 @@ inline void DP_DeepPotComputeNList_variantadd(DP_DeepPot* dp, if (dE_dN) { dE_dN[0] = dedn; } - if (atomic_energy) { - std::fill(atomic_energy, atomic_energy + nframes * natoms, (VALUETYPE)0); - } - if (atomic_virial) { - std::fill(atomic_virial, atomic_virial + nframes * natoms * 9, - (VALUETYPE)0); - } } template void DP_DeepPotComputeNList_variantadd(DP_DeepPot* dp, diff --git a/source/lmp/compute_deepmd_fparam_dedn.cpp b/source/lmp/compute_deepmd_fparam_dedn.cpp index 0d8645bcaf..3693bfb60d 100644 --- a/source/lmp/compute_deepmd_fparam_dedn.cpp +++ b/source/lmp/compute_deepmd_fparam_dedn.cpp @@ -30,10 +30,17 @@ ComputeDeepmdFparamDedn::ComputeDeepmdFparamDedn(LAMMPS* lmp, auto lb = token.find('['); auto rb = token.find(']'); if (lb != std::string::npos || rb != std::string::npos) { - if (lb == std::string::npos || rb == std::string::npos || rb <= lb + 1) { + if (lb == std::string::npos || rb == std::string::npos || rb <= lb + 1 || + rb != token.size() - 1) { error->all(FLERR, "Illegal source specification in compute command"); } - source_index = atoi(token.substr(lb + 1, rb - lb - 1).c_str()) - 1; + std::string idx = token.substr(lb + 1, rb - lb - 1); + char* endptr = nullptr; + long one_based = std::strtol(idx.c_str(), &endptr, 10); + if (endptr == idx.c_str() || *endptr != '\0' || one_based < 1) { + error->all(FLERR, "Source index must be a positive 1-based integer"); + } + source_index = static_cast(one_based - 1); token = token.substr(0, lb); } diff --git a/source/lmp/pair_base.cpp b/source/lmp/pair_base.cpp index 2aa5626a18..a64ac68c5a 100644 --- a/source/lmp/pair_base.cpp +++ b/source/lmp/pair_base.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -168,6 +169,9 @@ void PairDeepBaseModel::make_fparam_from_fix(vector& fparam) { assert(do_fix_fparam); int ifix = modify->find_fix(fix_fparam_id); + if (ifix < 0) { + error->all(FLERR, "fix id is not found: " + fix_fparam_id); + } Fix* fix = modify->fix[ifix]; if (!fix) { diff --git a/source/lmp/pair_deepmd.cpp b/source/lmp/pair_deepmd.cpp index d1d574f871..2bffa3352f 100644 --- a/source/lmp/pair_deepmd.cpp +++ b/source/lmp/pair_deepmd.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -223,6 +224,8 @@ double PairDeepMD::eval_energy_with_fparam( } else if (do_fix_fparam) { make_fparam_from_fix(fparam); } + const std::vector& fparam_eval = + fparam_override.empty() ? fparam : fparam_override; int ago = neighbor->ago; @@ -243,7 +246,7 @@ double PairDeepMD::eval_energy_with_fparam( try { deep_pot.compute(dener, dforce, dvirial, dcoord, dtype, dbox, nghost, - lmp_list, ago, fparam, daparam); + lmp_list, ago, fparam_eval, daparam); } catch (deepmd_compat::deepmd_exception& e) { error->one(FLERR, e.what()); } @@ -819,7 +822,14 @@ void PairDeepMD::settings(int narg, char** arg) { fix_fparam_id = arg[iarg + 1]; fix_fparam_index = -1; if (iarg + 2 < narg && !is_key(arg[iarg + 2])) { - fix_fparam_index = atoi(arg[iarg + 2]) - 1; + char* endptr = nullptr; + long one_based = std::strtol(arg[iarg + 2], &endptr, 10); + if (endptr == arg[iarg + 2] || *endptr != '\0' || one_based < 1) { + error->all(FLERR, + "invalid fparam_from_fix key: vector index must be a " + "positive 1-based integer"); + } + fix_fparam_index = static_cast(one_based - 1); iarg += 3; } else { iarg += 2; diff --git a/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py b/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py index 0098ef96e1..01244886c6 100644 --- a/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py +++ b/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py @@ -39,6 +39,7 @@ def setup_module() -> None: + """Create the converted model and data file needed by this test module.""" if os.environ.get("ENABLE_TENSORFLOW", "1") != "1": pytest.skip("Skip test because TensorFlow support is not enabled.") ensure_converted_pb(pbtxt_file, pb_file) @@ -46,11 +47,13 @@ def setup_module() -> None: def teardown_module() -> None: + """Remove the temporary LAMMPS data file after the tests finish.""" if data_file.exists(): os.remove(data_file) def _lammps(fp_value, units="metal") -> PyLammps: + """Build a LAMMPS instance configured for frame-parameter derivative tests.""" lammps = PyLammps() lammps.units(units) lammps.boundary("p p p") @@ -74,12 +77,14 @@ def _lammps(fp_value, units="metal") -> PyLammps: @pytest.fixture def lammps(): + """Provide a ready-to-run LAMMPS instance for the default frame parameter.""" lmp = _lammps(fp_value=0.25852028) yield lmp lmp.close() def _energy_at_fp(fp_value): + """Evaluate the potential energy at a chosen frame-parameter value.""" lmp = _lammps(fp_value=fp_value) try: lmp.run(0) @@ -88,12 +93,35 @@ def _energy_at_fp(fp_value): lmp.close() +def _energy_with_direct_fparam(fp_value): + """Evaluate the potential energy using a direct fparam setting.""" + lmp = PyLammps() + try: + lmp.units("metal") + lmp.boundary("p p p") + lmp.atom_style("atomic") + lmp.neighbor("2.0 bin") + lmp.neigh_modify("every 10 delay 0 check no") + lmp.read_data(data_file.resolve()) + lmp.mass("1 16") + lmp.timestep(0.0005) + lmp.fix("1 all nve") + lmp.pair_style(f"deepmd {pb_file.resolve()} fparam {fp_value} aparam 0.25852028") + lmp.pair_coeff("* *") + lmp.run(0) + return lmp.eval("pe") + finally: + lmp.close() + + def test_pair_fparam_from_fix(lammps) -> None: + """Check that fparam_from_fix matches the direct fparam path.""" lammps.run(0) - assert lammps.eval("pe") == pytest.approx(_energy_at_fp(0.25852028)) + assert lammps.eval("pe") == pytest.approx(_energy_with_direct_fparam(0.25852028)) def test_compute_deepmd_fparam_dedn(lammps) -> None: + """Compare the reported derivative against a central finite difference.""" eps = 1.0e-6 lammps.run(0) dedn = lammps.eval("c_dedn") From 842a37b7d17c466ee5935c16649c174be59175eb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 13:07:40 +0000 Subject: [PATCH 08/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/api_c/src/c_api.cc | 3 +-- source/lmp/tests/test_lammps_fparam_from_fix_dedn.py | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/source/api_c/src/c_api.cc b/source/api_c/src/c_api.cc index 0c15f74bf4..f89f4da272 100644 --- a/source/api_c/src/c_api.cc +++ b/source/api_c/src/c_api.cc @@ -678,8 +678,7 @@ inline void DP_DeepPotComputeNList_variantadd(DP_DeepPot* dp, return; } if (atomic_energy || atomic_virial) { - dp->exception = - "Direct dE/dN C API does not support atomic outputs"; + dp->exception = "Direct dE/dN C API does not support atomic outputs"; return; } DP_REQUIRES_OK(dp, dp->dp.compute(e, f, v, dedn, coord_, atype_, cell_, diff --git a/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py b/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py index 01244886c6..7b4125ed24 100644 --- a/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py +++ b/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py @@ -106,7 +106,9 @@ def _energy_with_direct_fparam(fp_value): lmp.mass("1 16") lmp.timestep(0.0005) lmp.fix("1 all nve") - lmp.pair_style(f"deepmd {pb_file.resolve()} fparam {fp_value} aparam 0.25852028") + lmp.pair_style( + f"deepmd {pb_file.resolve()} fparam {fp_value} aparam 0.25852028" + ) lmp.pair_coeff("* *") lmp.run(0) return lmp.eval("pe") From 36243f0d90adb5d872f6bc5c184d290b3e7c6aff Mon Sep 17 00:00:00 2001 From: LiFu Date: Mon, 8 Jun 2026 21:03:44 +0800 Subject: [PATCH 09/11] Address review feedback for the dE/dN provider path --- source/api_c/src/c_api.cc | 5 +++++ source/lmp/compute_deepmd_fparam_dedn.cpp | 7 ++++++- source/lmp/tests/test_lammps_fparam_from_fix_dedn.py | 8 ++++---- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/source/api_c/src/c_api.cc b/source/api_c/src/c_api.cc index f89f4da272..fbe75e1856 100644 --- a/source/api_c/src/c_api.cc +++ b/source/api_c/src/c_api.cc @@ -677,6 +677,11 @@ inline void DP_DeepPotComputeNList_variantadd(DP_DeepPot* dp, "Direct dE/dN C API currently supports single-frame evaluation only"; return; } + if (dp->dfparam != 1) { + dp->exception = + "Direct dE/dN C API currently supports only dim_fparam == 1"; + return; + } if (atomic_energy || atomic_virial) { dp->exception = "Direct dE/dN C API does not support atomic outputs"; return; diff --git a/source/lmp/compute_deepmd_fparam_dedn.cpp b/source/lmp/compute_deepmd_fparam_dedn.cpp index 3693bfb60d..59eafd8ba8 100644 --- a/source/lmp/compute_deepmd_fparam_dedn.cpp +++ b/source/lmp/compute_deepmd_fparam_dedn.cpp @@ -1,8 +1,10 @@ // SPDX-License-Identifier: LGPL-3.0-or-later #include "compute_deepmd_fparam_dedn.h" +#include #include #include +#include #include "comm.h" #include "compute.h" @@ -36,8 +38,11 @@ ComputeDeepmdFparamDedn::ComputeDeepmdFparamDedn(LAMMPS* lmp, } std::string idx = token.substr(lb + 1, rb - lb - 1); char* endptr = nullptr; + errno = 0; long one_based = std::strtol(idx.c_str(), &endptr, 10); - if (endptr == idx.c_str() || *endptr != '\0' || one_based < 1) { + if (endptr == idx.c_str() || *endptr != '\0' || errno == ERANGE || + one_based < 1 || + one_based > static_cast(std::numeric_limits::max()) + 1L) { error->all(FLERR, "Source index must be a positive 1-based integer"); } source_index = static_cast(one_based - 1); diff --git a/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py b/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py index 7b4125ed24..6f8d628bf4 100644 --- a/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py +++ b/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py @@ -87,7 +87,7 @@ def _energy_at_fp(fp_value): """Evaluate the potential energy at a chosen frame-parameter value.""" lmp = _lammps(fp_value=fp_value) try: - lmp.run(0) + lmp.run(1) return lmp.eval("pe") finally: lmp.close() @@ -110,7 +110,7 @@ def _energy_with_direct_fparam(fp_value): f"deepmd {pb_file.resolve()} fparam {fp_value} aparam 0.25852028" ) lmp.pair_coeff("* *") - lmp.run(0) + lmp.run(1) return lmp.eval("pe") finally: lmp.close() @@ -118,14 +118,14 @@ def _energy_with_direct_fparam(fp_value): def test_pair_fparam_from_fix(lammps) -> None: """Check that fparam_from_fix matches the direct fparam path.""" - lammps.run(0) + lammps.run(1) assert lammps.eval("pe") == pytest.approx(_energy_with_direct_fparam(0.25852028)) def test_compute_deepmd_fparam_dedn(lammps) -> None: """Compare the reported derivative against a central finite difference.""" eps = 1.0e-6 - lammps.run(0) + lammps.run(1) dedn = lammps.eval("c_dedn") ref = (_energy_at_fp(0.25852028 + eps) - _energy_at_fp(0.25852028 - eps)) / ( 2.0 * eps From abb3f63c10ecbac31160f43d40fb40e24c3699a1 Mon Sep 17 00:00:00 2001 From: LiFu Date: Tue, 9 Jun 2026 16:10:36 +0800 Subject: [PATCH 10/11] Narrow dE/dN provider to finite differences --- doc/third-party/lammps-command.md | 7 +- source/api_c/include/c_api.h | 62 ---- source/api_c/include/deepmd.hpp | 166 ---------- source/api_c/src/c_api.cc | 302 ------------------ source/api_cc/include/DeepPot.h | 56 ---- source/api_cc/include/DeepPotTF.h | 37 --- source/api_cc/src/DeepPot.cc | 47 --- source/api_cc/src/DeepPotTF.cc | 223 +------------ source/lmp/compute_deepmd_fparam_dedn.cpp | 19 +- source/lmp/pair_base.cpp | 3 +- source/lmp/pair_deepmd.cpp | 38 +-- source/lmp/pair_deepmd.h | 6 - source/lmp/plugin/CMakeLists.txt | 5 +- source/lmp/plugin/deepmdplugin.cpp | 2 +- .../tests/test_lammps_fparam_from_fix_dedn.py | 5 +- 15 files changed, 39 insertions(+), 939 deletions(-) diff --git a/doc/third-party/lammps-command.md b/doc/third-party/lammps-command.md index 4eb75dde19..89ff9f74f0 100644 --- a/doc/third-party/lammps-command.md +++ b/doc/third-party/lammps-command.md @@ -126,8 +126,11 @@ compute ID group-ID deepmd/fparam/dedn source [delta] - `source` can be a global variable (`v_name`), a compute (`c_ID`) or a fix (`f_ID`). - `source[index]` may be used for vector-valued computes or fixes, with 1-based indexing. - `delta` is the perturbation used in the central difference formula. If omitted, a small default perturbation is used. -- This compute currently targets a single frame-parameter dimension, which matches the - constant-potential use case where the potentiostat variable is scalar. +- The compute performs two additional model-energy evaluations, at `source + delta` + and `source - delta`. It does not consume a direct derivative tensor from the model. +- This path currently requires `pair_style deepmd` with one model and one frame-parameter + dimension. It therefore works with existing supported backends and models that accept + a scalar frame parameter; no `o_dE_dN` or other derivative output is required. 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. diff --git a/source/api_c/include/c_api.h b/source/api_c/include/c_api.h index f39cf256cc..358480b0ad 100644 --- a/source/api_c/include/c_api.h +++ b/source/api_c/include/c_api.h @@ -396,20 +396,6 @@ extern void DP_DeepPotCompute2(DP_DeepPot* dp, double* virial, double* atomic_energy, double* atomic_virial); -extern void DP_DeepPotCompute2add(DP_DeepPot* dp, - const int nframes, - const int natom, - const double* coord, - const int* atype, - const double* cell, - const double* fparam, - const double* aparam, - double* energy, - double* force, - double* virial, - double* dE_dN, - double* atomic_energy, - double* atomic_virial); /** * @brief Evaluate the energy, force, magnetic force and virial by using a DP @@ -496,20 +482,6 @@ extern void DP_DeepPotComputef2(DP_DeepPot* dp, float* virial, float* atomic_energy, float* atomic_virial); -extern void DP_DeepPotComputef2add(DP_DeepPot* dp, - const int nframes, - const int natom, - const float* coord, - const int* atype, - const float* cell, - const float* fparam, - const float* aparam, - double* energy, - float* force, - float* virial, - double* dE_dN, - float* atomic_energy, - float* atomic_virial); /** * @brief Evaluate the energy, force, magnetic force and virial by using a DP @@ -603,23 +575,6 @@ extern void DP_DeepPotComputeNList2(DP_DeepPot* dp, double* virial, double* atomic_energy, double* atomic_virial); -extern void DP_DeepPotComputeNList2add(DP_DeepPot* dp, - const int nframes, - const int natom, - const double* coord, - const int* atype, - const double* cell, - const int nghost, - const DP_Nlist* nlist, - const int ago, - const double* fparam, - const double* aparam, - double* energy, - double* force, - double* virial, - double* dE_dN, - double* atomic_energy, - double* atomic_virial); /** * @brief Evaluate the energy, force, magnetic force and virial by using a DP @@ -719,23 +674,6 @@ extern void DP_DeepPotComputeNListf2(DP_DeepPot* dp, float* virial, float* atomic_energy, float* atomic_virial); -extern void DP_DeepPotComputeNListf2add(DP_DeepPot* dp, - const int nframes, - const int natom, - const float* coord, - const int* atype, - const float* cell, - const int nghost, - const DP_Nlist* nlist, - const int ago, - const float* fparam, - const float* aparam, - double* energy, - float* force, - float* virial, - double* dE_dN, - float* atomic_energy, - float* atomic_virial); /** * @brief Evaluate the energy, force, magnetic force and virial by using a DP diff --git a/source/api_c/include/deepmd.hpp b/source/api_c/include/deepmd.hpp index 3fcf9d36bb..c3ca40b75f 100644 --- a/source/api_c/include/deepmd.hpp +++ b/source/api_c/include/deepmd.hpp @@ -95,62 +95,6 @@ inline void _DP_DeepPotCompute(DP_DeepPot* dp, energy, force, virial, atomic_energy, atomic_virial); } -template -inline void _DP_DeepPotComputeadd(DP_DeepPot* dp, - const int nframes, - const int natom, - const FPTYPE* coord, - const int* atype, - const FPTYPE* cell, - const FPTYPE* fparam, - const FPTYPE* aparam, - double* energy, - FPTYPE* force, - FPTYPE* virial, - double* dE_dN, - FPTYPE* atomic_energy, - FPTYPE* atomic_virial); - -template <> -inline void _DP_DeepPotComputeadd(DP_DeepPot* dp, - const int nframes, - const int natom, - const double* coord, - const int* atype, - const double* cell, - const double* fparam, - const double* aparam, - double* energy, - double* force, - double* virial, - double* dE_dN, - double* atomic_energy, - double* atomic_virial) { - DP_DeepPotCompute2add(dp, nframes, natom, coord, atype, cell, fparam, aparam, - energy, force, virial, dE_dN, atomic_energy, - atomic_virial); -} - -template <> -inline void _DP_DeepPotComputeadd(DP_DeepPot* dp, - const int nframes, - const int natom, - const float* coord, - const int* atype, - const float* cell, - const float* fparam, - const float* aparam, - double* energy, - float* force, - float* virial, - double* dE_dN, - float* atomic_energy, - float* atomic_virial) { - DP_DeepPotComputef2add(dp, nframes, natom, coord, atype, cell, fparam, aparam, - energy, force, virial, dE_dN, atomic_energy, - atomic_virial); -} - // support spin template inline void _DP_DeepSpinCompute(DP_DeepSpin* dp, @@ -273,71 +217,6 @@ inline void _DP_DeepPotComputeNList(DP_DeepPot* dp, atomic_energy, atomic_virial); } -template -inline void _DP_DeepPotComputeNListadd(DP_DeepPot* dp, - const int nframes, - const int natom, - const FPTYPE* coord, - const int* atype, - const FPTYPE* cell, - const int nghost, - const DP_Nlist* nlist, - const int ago, - const FPTYPE* fparam, - const FPTYPE* aparam, - double* energy, - FPTYPE* force, - FPTYPE* virial, - double* dE_dN, - FPTYPE* atomic_energy, - FPTYPE* atomic_virial); - -template <> -inline void _DP_DeepPotComputeNListadd(DP_DeepPot* dp, - const int nframes, - const int natom, - const double* coord, - const int* atype, - const double* cell, - const int nghost, - const DP_Nlist* nlist, - const int ago, - const double* fparam, - const double* aparam, - double* energy, - double* force, - double* virial, - double* dE_dN, - double* atomic_energy, - double* atomic_virial) { - DP_DeepPotComputeNList2add(dp, nframes, natom, coord, atype, cell, nghost, - nlist, ago, fparam, aparam, energy, force, virial, - dE_dN, atomic_energy, atomic_virial); -} - -template <> -inline void _DP_DeepPotComputeNListadd(DP_DeepPot* dp, - const int nframes, - const int natom, - const float* coord, - const int* atype, - const float* cell, - const int nghost, - const DP_Nlist* nlist, - const int ago, - const float* fparam, - const float* aparam, - double* energy, - float* force, - float* virial, - double* dE_dN, - float* atomic_energy, - float* atomic_virial) { - DP_DeepPotComputeNListf2add(dp, nframes, natom, coord, atype, cell, nghost, - nlist, ago, fparam, aparam, energy, force, virial, - dE_dN, atomic_energy, atomic_virial); -} - // support spin template inline void _DP_DeepSpinComputeNList(DP_DeepSpin* dp, @@ -1384,51 +1263,6 @@ class DeepPot : public DeepBaseModel { fparam__, aparam__, ener_, force_, virial_, nullptr, nullptr); DP_CHECK_OK(DP_DeepPotCheckOK, dp); }; - - template - void compute( - ENERGYVTYPE& ener, - std::vector& force, - std::vector& virial, - ENERGYVTYPE& dE_dN, - const std::vector& coord, - const std::vector& atype, - const std::vector& box, - const int nghost, - const InputNlist& lmp_list, - const int& ago, - const std::vector& fparam = std::vector(), - const std::vector& aparam = std::vector()) { - unsigned int natoms = atype.size(); - unsigned int nframes = natoms > 0 ? coord.size() / natoms / 3 : 1; - assert(nframes * natoms * 3 == coord.size()); - if (!box.empty()) { - assert(box.size() == nframes * 9); - } - const VALUETYPE* coord_ = &coord[0]; - const VALUETYPE* box_ = !box.empty() ? &box[0] : nullptr; - const int* atype_ = &atype[0]; - double* ener_ = _DP_Get_Energy_Pointer(ener, nframes); - double* dE_dN_ = _DP_Get_Energy_Pointer(dE_dN, nframes); - force.resize(static_cast(nframes) * natoms * 3); - virial.resize(static_cast(nframes) * 9); - VALUETYPE* force_ = &force[0]; - VALUETYPE* virial_ = &virial[0]; - std::vector fparam_, aparam_; - validate_fparam_aparam(nframes, (aparam_nall ? natoms : (natoms - nghost)), - fparam, aparam); - tile_fparam_aparam(fparam_, nframes, dfparam, fparam); - tile_fparam_aparam(aparam_, nframes, - (aparam_nall ? natoms : (natoms - nghost)) * daparam, - aparam); - const VALUETYPE* fparam__ = !fparam_.empty() ? &fparam_[0] : nullptr; - const VALUETYPE* aparam__ = !aparam_.empty() ? &aparam_[0] : nullptr; - - _DP_DeepPotComputeNListadd( - dp, nframes, natoms, coord_, atype_, box_, nghost, lmp_list.nl, ago, - fparam__, aparam__, ener_, force_, virial_, dE_dN_, nullptr, nullptr); - DP_CHECK_OK(DP_DeepPotCheckOK, dp); - }; /** * @brief Evaluate the energy, force, virial, atomic energy, and atomic virial *by using this DP with the neighbor list. diff --git a/source/api_c/src/c_api.cc b/source/api_c/src/c_api.cc index fbe75e1856..b0e789648e 100644 --- a/source/api_c/src/c_api.cc +++ b/source/api_c/src/c_api.cc @@ -323,125 +323,6 @@ template void DP_DeepPotCompute_variant(DP_DeepPot* dp, float* virial, float* atomic_energy, float* atomic_virial); - -template -inline void DP_DeepPotCompute_variantadd(DP_DeepPot* dp, - const int nframes, - const int natoms, - const VALUETYPE* coord, - const int* atype, - const VALUETYPE* cell, - const VALUETYPE* fparam, - const VALUETYPE* aparam, - double* energy, - VALUETYPE* force, - VALUETYPE* virial, - double* dE_dN, - VALUETYPE* atomic_energy, - VALUETYPE* atomic_virial) { - std::vector coord_(coord, coord + nframes * natoms * 3); - std::vector atype_(atype, atype + natoms); - std::vector cell_; - if (cell) { - cell_.assign(cell, cell + nframes * 9); - } - std::vector fparam_; - if (fparam) { - fparam_.assign(fparam, fparam + nframes * dp->dfparam); - } - std::vector aparam_; - if (aparam) { - aparam_.assign(aparam, aparam + nframes * natoms * dp->daparam); - } - - std::vector e; - std::vector f, v, ae, av; - if (atomic_energy || atomic_virial) { - DP_REQUIRES_OK(dp, dp->dp.compute(e, f, v, ae, av, coord_, atype_, cell_, - fparam_, aparam_)); - } else { - DP_REQUIRES_OK( - dp, dp->dp.compute(e, f, v, coord_, atype_, cell_, fparam_, aparam_)); - } - - if (dE_dN) { - if (dp->dfparam != 1) { - dp->exception = - "Direct dE/dN C API currently supports only dim_fparam == 1"; - return; - } - if (!fparam) { - dp->exception = "Direct dE/dN C API requires explicit frame parameters"; - return; - } - constexpr double delta = 1.0e-6; - std::vector fparam_plus = fparam_; - std::vector fparam_minus = fparam_; - std::vector e_plus, e_minus; - std::vector dummy_force, dummy_virial; - - for (int ii = 0; ii < nframes; ++ii) { - fparam_plus[ii] += static_cast(delta); - fparam_minus[ii] -= static_cast(delta); - DP_REQUIRES_OK( - dp, dp->dp.compute(e_plus, dummy_force, dummy_virial, coord_, atype_, - cell_, fparam_plus, aparam_)); - DP_REQUIRES_OK( - dp, dp->dp.compute(e_minus, dummy_force, dummy_virial, coord_, atype_, - cell_, fparam_minus, aparam_)); - dE_dN[ii] = (e_plus[ii] - e_minus[ii]) / (2.0 * delta); - fparam_plus[ii] = fparam_[ii]; - fparam_minus[ii] = fparam_[ii]; - } - } - - if (energy) { - std::copy(e.begin(), e.end(), energy); - } - if (force) { - std::copy(f.begin(), f.end(), force); - } - if (virial) { - std::copy(v.begin(), v.end(), virial); - } - if (atomic_energy) { - std::copy(ae.begin(), ae.end(), atomic_energy); - } - if (atomic_virial) { - std::copy(av.begin(), av.end(), atomic_virial); - } -} - -template void DP_DeepPotCompute_variantadd(DP_DeepPot* dp, - const int nframes, - const int natoms, - const double* coord, - const int* atype, - const double* cell, - const double* fparam, - const double* aparam, - double* energy, - double* force, - double* virial, - double* dE_dN, - double* atomic_energy, - double* atomic_virial); - -template void DP_DeepPotCompute_variantadd(DP_DeepPot* dp, - const int nframes, - const int natoms, - const float* coord, - const int* atype, - const float* cell, - const float* fparam, - const float* aparam, - double* energy, - float* force, - float* virial, - double* dE_dN, - float* atomic_energy, - float* atomic_virial); - // support spin template inline void DP_DeepSpinCompute_variant(DP_DeepSpin* dp, @@ -633,111 +514,6 @@ template void DP_DeepPotComputeNList_variant(DP_DeepPot* dp, float* atomic_energy, float* atomic_virial); -template -inline void DP_DeepPotComputeNList_variantadd(DP_DeepPot* dp, - const int nframes, - const int natoms, - const VALUETYPE* coord, - const int* atype, - const VALUETYPE* cell, - const int nghost, - const DP_Nlist* nlist, - const int ago, - const VALUETYPE* fparam, - const VALUETYPE* aparam, - double* energy, - VALUETYPE* force, - VALUETYPE* virial, - double* dE_dN, - VALUETYPE* atomic_energy, - VALUETYPE* atomic_virial) { - std::vector coord_(coord, coord + nframes * natoms * 3); - std::vector atype_(atype, atype + natoms); - std::vector cell_; - if (cell) { - cell_.assign(cell, cell + nframes * 9); - } - std::vector fparam_; - if (fparam) { - fparam_.assign(fparam, fparam + nframes * dp->dfparam); - } - std::vector aparam_; - if (aparam) { - aparam_.assign(aparam, - aparam + nframes * - (dp->aparam_nall ? natoms : (natoms - nghost)) * - dp->daparam); - } - double e = 0.0; - double dedn = 0.0; - std::vector f, v; - - if (nframes != 1) { - dp->exception = - "Direct dE/dN C API currently supports single-frame evaluation only"; - return; - } - if (dp->dfparam != 1) { - dp->exception = - "Direct dE/dN C API currently supports only dim_fparam == 1"; - return; - } - if (atomic_energy || atomic_virial) { - dp->exception = "Direct dE/dN C API does not support atomic outputs"; - return; - } - DP_REQUIRES_OK(dp, dp->dp.compute(e, f, v, dedn, coord_, atype_, cell_, - nghost, nlist->nl, ago, fparam_, aparam_)); - if (energy) { - energy[0] = e; - } - if (force) { - std::copy(f.begin(), f.end(), force); - } - if (virial) { - std::copy(v.begin(), v.end(), virial); - } - if (dE_dN) { - dE_dN[0] = dedn; - } -} - -template void DP_DeepPotComputeNList_variantadd(DP_DeepPot* dp, - const int nframes, - const int natoms, - const double* coord, - const int* atype, - const double* cell, - const int nghost, - const DP_Nlist* nlist, - const int ago, - const double* fparam, - const double* aparam, - double* energy, - double* force, - double* virial, - double* dE_dN, - double* atomic_energy, - double* atomic_virial); - -template void DP_DeepPotComputeNList_variantadd(DP_DeepPot* dp, - const int nframes, - const int natoms, - const float* coord, - const int* atype, - const float* cell, - const int nghost, - const DP_Nlist* nlist, - const int ago, - const float* fparam, - const float* aparam, - double* energy, - float* force, - float* virial, - double* dE_dN, - float* atomic_energy, - float* atomic_virial); - // support spin template inline void DP_DeepSpinComputeNList_variant(DP_DeepSpin* dp, @@ -1803,24 +1579,6 @@ void DP_DeepPotCompute2(DP_DeepPot* dp, fparam, aparam, energy, force, virial, atomic_energy, atomic_virial); } -void DP_DeepPotCompute2add(DP_DeepPot* dp, - const int nframes, - const int natoms, - const double* coord, - const int* atype, - const double* cell, - const double* fparam, - const double* aparam, - double* energy, - double* force, - double* virial, - double* dE_dN, - double* atomic_energy, - double* atomic_virial) { - DP_DeepPotCompute_variantadd(dp, nframes, natoms, coord, atype, cell, - fparam, aparam, energy, force, virial, - dE_dN, atomic_energy, atomic_virial); -} void DP_DeepSpinCompute2(DP_DeepSpin* dp, const int nframes, const int natoms, @@ -1858,24 +1616,6 @@ void DP_DeepPotComputef2(DP_DeepPot* dp, fparam, aparam, energy, force, virial, atomic_energy, atomic_virial); } -void DP_DeepPotComputef2add(DP_DeepPot* dp, - const int nframes, - const int natoms, - const float* coord, - const int* atype, - const float* cell, - const float* fparam, - const float* aparam, - double* energy, - float* force, - float* virial, - double* dE_dN, - float* atomic_energy, - float* atomic_virial) { - DP_DeepPotCompute_variantadd(dp, nframes, natoms, coord, atype, cell, - fparam, aparam, energy, force, virial, - dE_dN, atomic_energy, atomic_virial); -} void DP_DeepSpinComputef2(DP_DeepSpin* dp, const int nframes, @@ -1917,27 +1657,6 @@ void DP_DeepPotComputeNList2(DP_DeepPot* dp, dp, nframes, natoms, coord, atype, cell, nghost, nlist, ago, fparam, aparam, energy, force, virial, atomic_energy, atomic_virial); } -void DP_DeepPotComputeNList2add(DP_DeepPot* dp, - const int nframes, - const int natoms, - const double* coord, - const int* atype, - const double* cell, - const int nghost, - const DP_Nlist* nlist, - const int ago, - const double* fparam, - const double* aparam, - double* energy, - double* force, - double* virial, - double* dE_dN, - double* atomic_energy, - double* atomic_virial) { - DP_DeepPotComputeNList_variantadd( - dp, nframes, natoms, coord, atype, cell, nghost, nlist, ago, fparam, - aparam, energy, force, virial, dE_dN, atomic_energy, atomic_virial); -} void DP_DeepSpinComputeNList2(DP_DeepSpin* dp, const int nframes, @@ -1982,27 +1701,6 @@ void DP_DeepPotComputeNListf2(DP_DeepPot* dp, dp, nframes, natoms, coord, atype, cell, nghost, nlist, ago, fparam, aparam, energy, force, virial, atomic_energy, atomic_virial); } -void DP_DeepPotComputeNListf2add(DP_DeepPot* dp, - const int nframes, - const int natoms, - const float* coord, - const int* atype, - const float* cell, - const int nghost, - const DP_Nlist* nlist, - const int ago, - const float* fparam, - const float* aparam, - double* energy, - float* force, - float* virial, - double* dE_dN, - float* atomic_energy, - float* atomic_virial) { - DP_DeepPotComputeNList_variantadd( - dp, nframes, natoms, coord, atype, cell, nghost, nlist, ago, fparam, - aparam, energy, force, virial, dE_dN, atomic_energy, atomic_virial); -} void DP_DeepSpinComputeNListf2(DP_DeepSpin* dp, const int nframes, diff --git a/source/api_cc/include/DeepPot.h b/source/api_cc/include/DeepPot.h index 650249a63b..6a30eed7ea 100644 --- a/source/api_cc/include/DeepPot.h +++ b/source/api_cc/include/DeepPot.h @@ -88,43 +88,6 @@ class DeepPotBackend : public DeepBaseModelBackend { const std::vector& aparam, const bool atomic) = 0; /** @} */ - - /** - * @brief Evaluate the energy, force, virial, and frame-parameter derivative. - * - * The default implementation reports that direct dE/dN output is not - * available for the current backend. - */ - virtual void computew_dedn(std::vector& ener, - std::vector& force, - std::vector& virial, - std::vector& dedn, - const std::vector& coord, - const std::vector& atype, - const std::vector& box, - const int nghost, - const InputNlist& inlist, - const int& ago, - const std::vector& fparam, - const std::vector& aparam) { - throw deepmd_exception( - "Direct dE/dN output is not supported by this DeePMD backend"); - } - virtual void computew_dedn(std::vector& ener, - std::vector& force, - std::vector& virial, - std::vector& dedn, - const std::vector& coord, - const std::vector& atype, - const std::vector& box, - const int nghost, - const InputNlist& inlist, - const int& ago, - const std::vector& fparam, - const std::vector& aparam) { - throw deepmd_exception( - "Direct dE/dN output is not supported by this DeePMD backend"); - } /** * @brief Evaluate the energy, force, virial, atomic energy, and atomic virial *by using this DP. @@ -355,25 +318,6 @@ class DeepPot : public DeepBaseModel { const std::vector& fparam = std::vector(), const std::vector& aparam = std::vector()); /** @} */ - - /** - * @brief Evaluate the energy, force, virial, and direct dE/dN output. - * @{ - **/ - template - void compute(ENERGYTYPE& ener, - std::vector& force, - std::vector& virial, - ENERGYTYPE& dedn, - const std::vector& coord, - const std::vector& atype, - const std::vector& box, - const int nghost, - const InputNlist& inlist, - const int& ago, - const std::vector& fparam = std::vector(), - const std::vector& aparam = std::vector()); - /** @} */ /** * @brief Evaluate the energy, force, virial, atomic energy, and atomic virial *by using this DP. diff --git a/source/api_cc/include/DeepPotTF.h b/source/api_cc/include/DeepPotTF.h index c44b399a36..26420807d9 100644 --- a/source/api_cc/include/DeepPotTF.h +++ b/source/api_cc/include/DeepPotTF.h @@ -74,19 +74,6 @@ class DeepPotTF : public DeepPotBackend { const std::vector& fparam, const std::vector& aparam, const bool atomic); - template - void compute_dedn(ENERGYVTYPE& ener, - std::vector& force, - std::vector& virial, - ENERGYTYPE& dedn, - const std::vector& coord, - const std::vector& atype, - const std::vector& box, - const int nghost, - const InputNlist& lmp_list, - const int& ago, - const std::vector& fparam, - const std::vector& aparam); /** * @brief Evaluate the energy, force, virial, atomic energy, and atomic virial *by using this DP. @@ -252,30 +239,6 @@ class DeepPotTF : public DeepPotBackend { const std::vector& fparam, const std::vector& aparam, const bool atomic); - void computew_dedn(std::vector& ener, - std::vector& force, - std::vector& virial, - std::vector& dedn, - const std::vector& coord, - const std::vector& atype, - const std::vector& box, - const int nghost, - const InputNlist& inlist, - const int& ago, - const std::vector& fparam, - const std::vector& aparam) override; - void computew_dedn(std::vector& ener, - std::vector& force, - std::vector& virial, - std::vector& dedn, - const std::vector& coord, - const std::vector& atype, - const std::vector& box, - const int nghost, - const InputNlist& inlist, - const int& ago, - const std::vector& fparam, - const std::vector& aparam) override; void computew(std::vector& ener, std::vector& force, std::vector& virial, diff --git a/source/api_cc/src/DeepPot.cc b/source/api_cc/src/DeepPot.cc index d1bad59b1f..ae543e1e31 100644 --- a/source/api_cc/src/DeepPot.cc +++ b/source/api_cc/src/DeepPot.cc @@ -211,53 +211,6 @@ template void DeepPot::compute(ENERGYTYPE& dener, const std::vector& fparam, const std::vector& aparam_); -template -void DeepPot::compute(ENERGYTYPE& dener, - std::vector& dforce_, - std::vector& dvirial, - ENERGYTYPE& ddedn, - const std::vector& dcoord_, - const std::vector& datype_, - const std::vector& dbox, - const int nghost, - const InputNlist& lmp_list, - const int& ago, - const std::vector& fparam_, - const std::vector& aparam__) { - std::vector dener_; - std::vector ddedn_; - dp->computew_dedn(dener_, dforce_, dvirial, ddedn_, dcoord_, datype_, dbox, - nghost, lmp_list, ago, fparam_, aparam__); - dener = dener_[0]; - ddedn = ddedn_[0]; -} - -template void DeepPot::compute(ENERGYTYPE& dener, - std::vector& dforce_, - std::vector& dvirial, - ENERGYTYPE& ddedn, - const std::vector& dcoord_, - const std::vector& datype_, - const std::vector& dbox, - const int nghost, - const InputNlist& lmp_list, - const int& ago, - const std::vector& fparam, - const std::vector& aparam_); - -template void DeepPot::compute(ENERGYTYPE& dener, - std::vector& dforce_, - std::vector& dvirial, - ENERGYTYPE& ddedn, - const std::vector& dcoord_, - const std::vector& datype_, - const std::vector& dbox, - const int nghost, - const InputNlist& lmp_list, - const int& ago, - const std::vector& fparam, - const std::vector& aparam_); - template void DeepPot::compute(std::vector& dener, std::vector& dforce_, std::vector& dvirial, diff --git a/source/api_cc/src/DeepPotTF.cc b/source/api_cc/src/DeepPotTF.cc index bfa4c2a9dc..7656590ea6 100644 --- a/source/api_cc/src/DeepPotTF.cc +++ b/source/api_cc/src/DeepPotTF.cc @@ -119,74 +119,6 @@ template void run_model( const int nframes, const int nghost); -template -static void run_model_dedn( - std::vector& dener, - std::vector& dforce_, - std::vector& dvirial, - std::vector& ddedn, - Session* session, - const std::vector>& input_tensors, - const AtomMap& atommap, - const int nframes, - const int nghost = 0) { - unsigned nloc = atommap.get_type().size(); - unsigned nall = nloc + nghost; - dener.resize(nframes); - ddedn.resize(nframes); - if (nloc == 0) { - dforce_.resize(static_cast(nframes) * nall * 3); - fill(dforce_.begin(), dforce_.end(), (VALUETYPE)0.0); - dvirial.resize(static_cast(nframes) * 9); - fill(dvirial.begin(), dvirial.end(), (VALUETYPE)0.0); - fill(ddedn.begin(), ddedn.end(), (ENERGYTYPE)0.0); - return; - } - - std::vector output_tensors; - check_status(session->Run( - input_tensors, - {"o_energy", "o_force", "o_dE_dN", "o_atom_energy", "o_atom_virial"}, {}, - &output_tensors)); - - Tensor output_e = output_tensors[0]; - Tensor output_f = output_tensors[1]; - Tensor output_dedn = output_tensors[2]; - Tensor output_av = output_tensors[4]; - - auto oe = output_e.flat(); - auto of = output_f.flat(); - auto odedn = output_dedn.flat(); - auto oav = output_av.flat(); - - std::vector dforce(static_cast(nframes) * 3 * nall); - dvirial.resize(static_cast(nframes) * 9); - for (int ii = 0; ii < nframes; ++ii) { - dener[ii] = oe(ii); - ddedn[ii] = odedn(ii); - } - for (size_t ii = 0; ii < static_cast(nframes) * nall * 3; ++ii) { - dforce[ii] = of(ii); - } - std::fill(dvirial.begin(), dvirial.end(), (VALUETYPE)0.); - for (int kk = 0; kk < nframes; ++kk) { - for (int ii = 0; ii < nall; ++ii) { - dvirial[kk * 9 + 0] += (VALUETYPE)1.0 * oav(kk * nall * 9 + 9 * ii + 0); - dvirial[kk * 9 + 1] += (VALUETYPE)1.0 * oav(kk * nall * 9 + 9 * ii + 1); - dvirial[kk * 9 + 2] += (VALUETYPE)1.0 * oav(kk * nall * 9 + 9 * ii + 2); - dvirial[kk * 9 + 3] += (VALUETYPE)1.0 * oav(kk * nall * 9 + 9 * ii + 3); - dvirial[kk * 9 + 4] += (VALUETYPE)1.0 * oav(kk * nall * 9 + 9 * ii + 4); - dvirial[kk * 9 + 5] += (VALUETYPE)1.0 * oav(kk * nall * 9 + 9 * ii + 5); - dvirial[kk * 9 + 6] += (VALUETYPE)1.0 * oav(kk * nall * 9 + 9 * ii + 6); - dvirial[kk * 9 + 7] += (VALUETYPE)1.0 * oav(kk * nall * 9 + 9 * ii + 7); - dvirial[kk * 9 + 8] += (VALUETYPE)1.0 * oav(kk * nall * 9 + 9 * ii + 8); - } - } - dforce_ = dforce; - atommap.backward(dforce_.begin(), dforce.begin(), 3, nframes, - nall); -} - template static void run_model( std::vector& dener, @@ -663,10 +595,9 @@ void DeepPotTF::compute(ENERGYVTYPE& dener, std::vector> input_tensors; if (dtype == tensorflow::DT_DOUBLE) { - const int ret = session_input_tensors( - input_tensors, dcoord_, ntypes, datype_, dbox, cell_size, fparam, - aparam, atommap, "", aparam_nall); - (void)ret; + int ret = session_input_tensors(input_tensors, dcoord_, ntypes, + datype_, dbox, cell_size, fparam, + aparam, atommap, "", aparam_nall); if (atomic) { run_model(dener, dforce_, dvirial, datom_energy_, datom_virial_, session, input_tensors, atommap, nframes); @@ -675,10 +606,9 @@ void DeepPotTF::compute(ENERGYVTYPE& dener, atommap, nframes); } } else { - const int ret = session_input_tensors( - input_tensors, dcoord_, ntypes, datype_, dbox, cell_size, fparam, - aparam, atommap, "", aparam_nall); - (void)ret; + int ret = session_input_tensors(input_tensors, dcoord_, ntypes, + datype_, dbox, cell_size, fparam, + aparam, atommap, "", aparam_nall); if (atomic) { run_model(dener, dforce_, dvirial, datom_energy_, datom_virial_, session, input_tensors, atommap, nframes); @@ -741,103 +671,6 @@ template void DeepPotTF::compute>( const std::vector& aparam, const bool atomic); -template -void DeepPotTF::compute_dedn(ENERGYVTYPE& dener, - std::vector& dforce_, - std::vector& dvirial, - ENERGYTYPE& ddedn, - const std::vector& dcoord_, - const std::vector& datype_, - const std::vector& dbox, - const int nghost, - const InputNlist& lmp_list, - const int& ago, - const std::vector& fparam, - const std::vector& aparam) { - int nall = datype_.size(); - int nframes = nall > 0 ? (dcoord_.size() / nall / 3) : 1; - int nloc = nall - nghost; - std::vector fparam_; - std::vector aparam_; - validate_fparam_aparam(nframes, (aparam_nall ? nall : nloc), fparam, aparam); - tile_fparam_aparam(fparam_, nframes, dfparam, fparam); - tile_fparam_aparam(aparam_, nframes, (aparam_nall ? nall : nloc) * daparam, - aparam); - - std::vector> input_tensors; - std::vector dcoord, dforce, aparam_sel; - std::vector datype, fwd_map, bkw_map; - int nghost_real, nall_real, nloc_real; - select_real_atoms_coord(dcoord, datype, aparam_sel, nghost_real, fwd_map, - bkw_map, nall_real, nloc_real, dcoord_, datype_, - aparam_, nghost, ntypes, nframes, daparam, nall, - aparam_nall); - - if (ago == 0) { - atommap = deepmd::AtomMap(datype.begin(), datype.begin() + nloc_real); - assert(nloc_real == atommap.get_type().size()); - - nlist_data.copy_from_nlist(lmp_list); - nlist_data.shuffle_exclude_empty(fwd_map); - nlist_data.shuffle(atommap); - nlist_data.make_inlist(nlist); - } - - std::vector dener_vec; - std::vector ddedn_vec; - if (dtype == tensorflow::DT_DOUBLE) { - const int ret = session_input_tensors( - input_tensors, dcoord, ntypes, datype, dbox, nlist, fparam_, aparam_sel, - atommap, nghost_real, ago, "", aparam_nall); - (void)ret; - assert(nloc_real == ret); - run_model_dedn(dener_vec, dforce, dvirial, ddedn_vec, session, - input_tensors, atommap, nframes, nghost_real); - } else { - const int ret = session_input_tensors( - input_tensors, dcoord, ntypes, datype, dbox, nlist, fparam_, aparam_sel, - atommap, nghost_real, ago, "", aparam_nall); - (void)ret; - assert(nloc_real == ret); - run_model_dedn(dener_vec, dforce, dvirial, ddedn_vec, session, - input_tensors, atommap, nframes, nghost_real); - } - - dforce_.resize(static_cast(nframes) * fwd_map.size() * 3); - select_map(dforce_, dforce, bkw_map, 3, nframes, fwd_map.size(), - nall_real); - dener = dener_vec[0]; - ddedn = ddedn_vec[0]; -} - -template void DeepPotTF::compute_dedn( - ENERGYTYPE& dener, - std::vector& dforce_, - std::vector& dvirial, - ENERGYTYPE& ddedn, - const std::vector& dcoord_, - const std::vector& datype_, - const std::vector& dbox, - const int nghost, - const InputNlist& lmp_list, - const int& ago, - const std::vector& fparam, - const std::vector& aparam); - -template void DeepPotTF::compute_dedn( - ENERGYTYPE& dener, - std::vector& dforce_, - std::vector& dvirial, - ENERGYTYPE& ddedn, - const std::vector& dcoord_, - const std::vector& datype_, - const std::vector& dbox, - const int nghost, - const InputNlist& lmp_list, - const int& ago, - const std::vector& fparam, - const std::vector& aparam); - template void DeepPotTF::compute(ENERGYVTYPE& dener, std::vector& dforce_, @@ -884,10 +717,9 @@ void DeepPotTF::compute(ENERGYVTYPE& dener, } if (dtype == tensorflow::DT_DOUBLE) { - const int ret = session_input_tensors( + int ret = session_input_tensors( input_tensors, dcoord, ntypes, datype, dbox, nlist, fparam, aparam, atommap, nghost_real, ago, "", aparam_nall); - (void)ret; assert(nloc_real == ret); if (atomic) { run_model(dener, dforce, dvirial, datom_energy, datom_virial, @@ -897,10 +729,9 @@ void DeepPotTF::compute(ENERGYVTYPE& dener, nframes, nghost_real); } } else { - const int ret = session_input_tensors( + int ret = session_input_tensors( input_tensors, dcoord, ntypes, datype, dbox, nlist, fparam, aparam, atommap, nghost_real, ago, "", aparam_nall); - (void)ret; assert(nloc_real == ret); if (atomic) { run_model(dener, dforce, dvirial, datom_energy, datom_virial, @@ -1161,44 +992,6 @@ void DeepPotTF::computew(std::vector& ener, compute(ener, force, virial, atom_energy, atom_virial, coord, atype, box, nghost, inlist, ago, fparam, aparam, atomic); } -void DeepPotTF::computew_dedn(std::vector& ener, - std::vector& force, - std::vector& virial, - std::vector& dedn, - const std::vector& coord, - const std::vector& atype, - const std::vector& box, - const int nghost, - const InputNlist& inlist, - const int& ago, - const std::vector& fparam, - const std::vector& aparam) { - ENERGYTYPE ener0 = 0.0; - ENERGYTYPE dedn0 = 0.0; - compute_dedn(ener0, force, virial, dedn0, coord, atype, box, nghost, inlist, - ago, fparam, aparam); - ener.assign(1, ener0); - dedn.assign(1, dedn0); -} -void DeepPotTF::computew_dedn(std::vector& ener, - std::vector& force, - std::vector& virial, - std::vector& dedn, - const std::vector& coord, - const std::vector& atype, - const std::vector& box, - const int nghost, - const InputNlist& inlist, - const int& ago, - const std::vector& fparam, - const std::vector& aparam) { - ENERGYTYPE ener0 = 0.0; - ENERGYTYPE dedn0 = 0.0; - compute_dedn(ener0, force, virial, dedn0, coord, atype, box, nghost, inlist, - ago, fparam, aparam); - ener.assign(1, ener0); - dedn.assign(1, dedn0); -} void DeepPotTF::computew_mixed_type(std::vector& ener, std::vector& force, std::vector& virial, diff --git a/source/lmp/compute_deepmd_fparam_dedn.cpp b/source/lmp/compute_deepmd_fparam_dedn.cpp index 59eafd8ba8..88ee8825b8 100644 --- a/source/lmp/compute_deepmd_fparam_dedn.cpp +++ b/source/lmp/compute_deepmd_fparam_dedn.cpp @@ -2,6 +2,7 @@ #include "compute_deepmd_fparam_dedn.h" #include +#include #include #include #include @@ -66,10 +67,20 @@ ComputeDeepmdFparamDedn::ComputeDeepmdFparamDedn(LAMMPS* lmp, } else { error->all(FLERR, "Source must be a variable, compute, or fix reference"); } + if (source_id.empty()) { + error->all(FLERR, "Source ID must not be empty"); + } int iarg = 4; if (iarg < narg) { - delta = atof(arg[iarg]); + char* endptr = nullptr; + errno = 0; + delta = std::strtod(arg[iarg], &endptr); + if (endptr == arg[iarg] || *endptr != '\0' || errno == ERANGE || + !std::isfinite(delta)) { + error->all(FLERR, + "delta must be a finite number in compute deepmd/fparam/dedn"); + } ++iarg; } if (iarg != narg) { @@ -178,12 +189,6 @@ double ComputeDeepmdFparamDedn::get_source_value() { double ComputeDeepmdFparamDedn::compute_scalar() { invoked_scalar = update->ntimestep; - int dim = 0; - if (void* ptr = pair->extract("deepmd_dedn", dim)) { - scalar = *static_cast(ptr); - return scalar; - } - double fparam0 = get_source_value(); std::vector fparam_plus(1, fparam0 + delta); std::vector fparam_minus(1, fparam0 - delta); diff --git a/source/lmp/pair_base.cpp b/source/lmp/pair_base.cpp index a64ac68c5a..2639bc74ba 100644 --- a/source/lmp/pair_base.cpp +++ b/source/lmp/pair_base.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -201,7 +200,7 @@ void PairDeepBaseModel::make_fparam_from_fix(vector& fparam) { error->all(FLERR, "fix " + fix_fparam_id + " does not provide a vector for fparam"); } - if (fix->size_vector < fix_fparam_index + dim_fparam) { + if (fix_fparam_index > fix->size_vector - dim_fparam) { error->all(FLERR, "fix " + fix_fparam_id + " vector is shorter than fparam dimension"); } diff --git a/source/lmp/pair_deepmd.cpp b/source/lmp/pair_deepmd.cpp index 2bffa3352f..d60388814f 100644 --- a/source/lmp/pair_deepmd.cpp +++ b/source/lmp/pair_deepmd.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -121,9 +122,7 @@ static const char cite_user_deepmd_package[] = PairDeepMD::PairDeepMD(LAMMPS* lmp) : PairDeepBaseModel( lmp, cite_user_deepmd_package, deep_pot, deep_pot_model_devi), - commdata_(nullptr), - cached_dedn(0.0), - cached_dedn_valid(0) { + commdata_(nullptr) { // Constructor body can be empty } @@ -131,17 +130,6 @@ PairDeepMD::~PairDeepMD() { // Ensure base class destructor is called } -void* PairDeepMD::extract(const char* str, int& dim) { - if (strcmp(str, "deepmd_dedn") == 0) { - if (!cached_dedn_valid) { - return nullptr; - } - dim = 0; - return (void*)&cached_dedn; - } - return PairDeepBaseModel::extract(str, dim); -} - double PairDeepMD::eval_energy_with_fparam( const std::vector& fparam_override) { if (numb_models != 1) { @@ -178,8 +166,7 @@ double PairDeepMD::eval_energy_with_fparam( std::vector dbox(9, 0); std::vector daparam; - if (dim_fparam > 0 && - fparam_override.size() != static_cast(dim_fparam)) { + if (fparam_override.size() != static_cast(dim_fparam)) { error->all(FLERR, "fparam override has the wrong dimension"); } @@ -219,14 +206,6 @@ double PairDeepMD::eval_energy_with_fparam( } #endif } - if (do_compute_fparam) { - make_fparam_from_compute(fparam); - } else if (do_fix_fparam) { - make_fparam_from_fix(fparam); - } - const std::vector& fparam_eval = - fparam_override.empty() ? fparam : fparam_override; - int ago = neighbor->ago; if (do_ghost) { @@ -246,7 +225,7 @@ double PairDeepMD::eval_energy_with_fparam( try { deep_pot.compute(dener, dforce, dvirial, dcoord, dtype, dbox, nghost, - lmp_list, ago, fparam_eval, daparam); + lmp_list, ago, fparam_override, daparam); } catch (deepmd_compat::deepmd_exception& e) { error->one(FLERR, e.what()); } @@ -379,7 +358,6 @@ void PairDeepMD::compute(int eflag, int vflag) { if (single_model || multi_models_no_mod_devi) { // cvflag_atom is the right flag for the cvatom matrix if (!(eflag_atom || cvflag_atom)) { - cached_dedn_valid = 0; try { deep_pot.compute(dener, dforce, dvirial, dcoord, dtype, dbox, nghost, lmp_list, ago, fparam, daparam); @@ -389,7 +367,6 @@ void PairDeepMD::compute(int eflag, int vflag) { } // do atomic energy and virial else { - cached_dedn_valid = 0; vector deatom(nall * 1, 0); vector dvatom(nall * 9, 0); try { @@ -436,7 +413,6 @@ void PairDeepMD::compute(int eflag, int vflag) { } } } else if (multi_models_mod_devi) { - cached_dedn_valid = 0; vector deatom(nall * 1, 0); vector dvatom(nall * 9, 0); vector> all_virial; @@ -630,7 +606,6 @@ void PairDeepMD::compute(int eflag, int vflag) { } } else { if (numb_models == 1) { - cached_dedn_valid = 0; try { deep_pot.compute(dener, dforce, dvirial, dcoord, dtype, dbox); } catch (deepmd_compat::deepmd_exception& e) { @@ -823,8 +798,11 @@ void PairDeepMD::settings(int narg, char** arg) { fix_fparam_index = -1; if (iarg + 2 < narg && !is_key(arg[iarg + 2])) { char* endptr = nullptr; + errno = 0; long one_based = std::strtol(arg[iarg + 2], &endptr, 10); - if (endptr == arg[iarg + 2] || *endptr != '\0' || one_based < 1) { + if (endptr == arg[iarg + 2] || *endptr != '\0' || errno == ERANGE || + one_based < 1 || + one_based > static_cast(std::numeric_limits::max())) { error->all(FLERR, "invalid fparam_from_fix key: vector index must be a " "positive 1-based integer"); diff --git a/source/lmp/pair_deepmd.h b/source/lmp/pair_deepmd.h index b72fad2af4..86b2017e93 100644 --- a/source/lmp/pair_deepmd.h +++ b/source/lmp/pair_deepmd.h @@ -47,13 +47,9 @@ class PairDeepMD : public PairDeepBaseModel { void settings(int, char**) override; void coeff(int, char**) override; void compute(int, int) override; - void* extract(const char*, int&) override; int pack_reverse_comm(int, int, double*) override; void unpack_reverse_comm(int, int*, double*) override; - const std::vector& current_fparam() const { return fparam; } double eval_energy_with_fparam(const std::vector& fparam_override); - double latest_dedn() const { return cached_dedn; } - bool has_latest_dedn() const { return cached_dedn_valid; } protected: deepmd_compat::DeepPot deep_pot; @@ -61,8 +57,6 @@ class PairDeepMD : public PairDeepBaseModel { private: CommBrickDeepMD* commdata_; - double cached_dedn; - int cached_dedn_valid; }; } // namespace LAMMPS_NS diff --git a/source/lmp/plugin/CMakeLists.txt b/source/lmp/plugin/CMakeLists.txt index 9dee0be1f0..8f32af3e3e 100644 --- a/source/lmp/plugin/CMakeLists.txt +++ b/source/lmp/plugin/CMakeLists.txt @@ -1,11 +1,8 @@ if(DEFINED LAMMPS_SOURCE_ROOT OR DEFINED LAMMPS_VERSION) message(STATUS "enable LAMMPS plugin mode") - if(CMAKE_CXX_STANDARD LESS 17) - set(CMAKE_CXX_STANDARD 17) - endif() add_library(lammps_interface INTERFACE) if(DEFINED LAMMPS_VERSION) - cmake_minimum_required(VERSION 3.23.0) + cmake_minimum_required(VERSION 3.25.2) include(FetchContent) FetchContent_Declare( lammps_download diff --git a/source/lmp/plugin/deepmdplugin.cpp b/source/lmp/plugin/deepmdplugin.cpp index c418347687..b49b034c41 100644 --- a/source/lmp/plugin/deepmdplugin.cpp +++ b/source/lmp/plugin/deepmdplugin.cpp @@ -67,7 +67,7 @@ extern "C" void lammpsplugin_init(void* lmp, void* handle, void* regfunc) { plugin.style = "compute"; plugin.name = "deepmd/fparam/dedn"; plugin.info = "compute deepmd/fparam/dedn " STR_GIT_SUMM; - plugin.author = "OpenAI"; + plugin.author = "Li Fu"; plugin.creator.v2 = (lammpsplugin_factory2*)&computedeepmdfparamdedn; (*register_plugin)(&plugin, lmp); diff --git a/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py b/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py index 6f8d628bf4..c929380051 100644 --- a/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py +++ b/source/lmp/tests/test_lammps_fparam_from_fix_dedn.py @@ -122,9 +122,10 @@ def test_pair_fparam_from_fix(lammps) -> None: assert lammps.eval("pe") == pytest.approx(_energy_with_direct_fparam(0.25852028)) -def test_compute_deepmd_fparam_dedn(lammps) -> None: - """Compare the reported derivative against a central finite difference.""" +def test_compute_deepmd_fparam_dedn_without_direct_model_output(lammps) -> None: + """Check finite differences with a model that has no direct dE/dN output.""" eps = 1.0e-6 + assert "o_dE_dN" not in pbtxt_file.read_text() lammps.run(1) dedn = lammps.eval("c_dedn") ref = (_energy_at_fp(0.25852028 + eps) - _energy_at_fp(0.25852028 - eps)) / ( From 4af33a45cefec5ac5dba052888d330159094a0cf Mon Sep 17 00:00:00 2001 From: LiFu Date: Sun, 14 Jun 2026 02:04:22 +0800 Subject: [PATCH 11/11] docs: move fparam derivative compute to standalone section --- doc/third-party/lammps-command.md | 41 +++++++++++++++++-------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/doc/third-party/lammps-command.md b/doc/third-party/lammps-command.md index 89ff9f74f0..dfb88b5fa4 100644 --- a/doc/third-party/lammps-command.md +++ b/doc/third-party/lammps-command.md @@ -49,7 +49,7 @@ pair_style deepmd models ... keyword value ... - models = frozen model(s) to compute the interaction. If multiple models are provided, then only the first model serves to provide energy and force prediction for each timestep of molecular dynamics, and the model deviation will be computed among all models every `out_freq` timesteps. -- keyword = _out_file_ or _out_freq_ or _fparam_ or _fparam_from_compute_ or _aparam_from_compute_ or _atomic_ or _relative_ or _relative_v_ or _aparam_ or _ttm_ +- keyword = _out_file_ or _out_freq_ or _fparam_ or _fparam_from_compute_ or _fparam_from_fix_ or _aparam_from_compute_ or _atomic_ or _relative_ or _relative_v_ or _aparam_ or _ttm_
     out_file value = filename
@@ -60,6 +60,9 @@ pair_style deepmd models ... keyword value ...
         parameters = one or more frame parameters required for model evaluation.
     fparam_from_compute value = id
         id = compute id used to update the frame parameter.
+    fparam_from_fix value = id [index]
+        id = fix ID used to update the frame parameter.
+        index = optional 1-based starting index for a global fix vector.
     aparam_from_compute value = id
         id = compute id used to update the atom parameter.
     atomic = no value is required.
@@ -110,28 +113,11 @@ $$E_{v_i}=\frac{\left|D_{v_i}\right|}{\left|v_i\right|+l}$$
 
 If the keyword `fparam` is set, the given frame parameter(s) will be fed to the model.
 If the keyword `fparam_from_compute` is set, the global parameter(s) from compute command (e.g., temperature from [compute temp command](https://docs.lammps.org/compute_temp.html)) will be fed to the model as the frame parameter(s).
-If the keyword `fparam_from_fix` is set, the global parameter(s) from fix command will be fed to the model as the frame parameter(s). This is intended for generalized coordinates that are naturally carried by a fix, for example the potentiostat variable in `fix uvt`.
+If the keyword `fparam_from_fix` is set, the global parameter(s) from fix command will be fed to the model as the frame parameter(s). This is intended for generalized coordinates that are naturally carried by a fix, for example the potentiostat variable in `fix uvt`. See [compute `deepmd/fparam/dedn`](#compute-deepmdfparamdedn) for evaluating the corresponding energy derivative.
 If the keyword `aparam_from_compute` is set, the atomic parameter(s) from compute command (e.g., per-atom translational kinetic energy from [compute ke/atom command](https://docs.lammps.org/compute_ke_atom.html)) will be fed to the model as the atom parameter(s).
 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.
 
-### Computing the derivative with respect to a frame parameter
-
-The `deepmd/fparam/dedn` compute evaluates a finite-difference derivative of the model energy with respect to a chosen frame parameter source.
-
-```lammps
-compute ID group-ID deepmd/fparam/dedn source [delta]
-```
-
-- `source` can be a global variable (`v_name`), a compute (`c_ID`) or a fix (`f_ID`).
-- `source[index]` may be used for vector-valued computes or fixes, with 1-based indexing.
-- `delta` is the perturbation used in the central difference formula. If omitted, a small default perturbation is used.
-- The compute performs two additional model-energy evaluations, at `source + delta`
-  and `source - delta`. It does not consume a direct derivative tensor from the model.
-- This path currently requires `pair_style deepmd` with one model and one frame-parameter
-  dimension. It therefore works with existing supported backends and models that accept
-  a scalar frame parameter; no `o_dE_dN` or other derivative output is required.
-
 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 a mapping value is specified as `NULL`, the mapping is not performed. This can be used when a deepmd potential is used as part of the hybrid pair style. The `NULL` values are placeholders for atom types that will be used with other potentials.
@@ -216,6 +202,23 @@ Please note that the virial and atomic virial are not currently supported in spi
 
 - The `deepspin` 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.
 
+## compute `deepmd/fparam/dedn`
+
+The `deepmd/fparam/dedn` compute evaluates a finite-difference derivative of the model energy with respect to a chosen frame parameter source.
+
+```lammps
+compute ID group-ID deepmd/fparam/dedn source [delta]
+```
+
+- `source` can be a global variable (`v_name`), a compute (`c_ID`) or a fix (`f_ID`).
+- `source[index]` may be used for vector-valued computes or fixes, with 1-based indexing.
+- `delta` is the perturbation used in the central difference formula. If omitted, a small default perturbation is used.
+- The compute performs two additional model-energy evaluations, at `source + delta`
+  and `source - delta`. It does not consume a direct derivative tensor from the model.
+- This path currently requires `pair_style deepmd` with one model and one frame-parameter
+  dimension. It therefore works with existing supported backends and models that accept
+  a scalar frame parameter; no `o_dE_dN` or other derivative output is required.
+
 ## Compute tensorial properties
 
 The DeePMD-kit package provides the compute `deeptensor/atom` for computing atomic tensorial properties.