Skip to content

feat: add charge spin to cpp runtime#5509

Draft
anyangml wants to merge 9 commits into
deepmodeling:masterfrom
anyangml:feat/support-charge-spin-runtime
Draft

feat: add charge spin to cpp runtime#5509
anyangml wants to merge 9 commits into
deepmodeling:masterfrom
anyangml:feat/support-charge-spin-runtime

Conversation

@anyangml

@anyangml anyangml commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

Release Notes

  • New Features
    • Extended compute interfaces to accept optional charge_spin parameters for model inference.
    • Added capability queries to determine supported charge_spin dimensions.
    • Integrated charge_spin support in LAMMPS pair styles with compute-driven parameter derivation and explicit configuration options.

Copilot AI review requested due to automatic review settings June 9, 2026 06:07
@dosubot dosubot Bot added the new feature label Jun 9, 2026
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR extends DeepMD-kit's inference stack with optional per-call charge/spin conditioning: backend interfaces expose dimension query and overloads accepting charge_spin vectors; DeepPot public API adds charge_spin parameter to all compute variants; PyTorch models build runtime tensors from caller vectors or metadata defaults; and LAMMPS integration enables explicit or compute-sourced charge_spin inputs with mutual-exclusion validation.

Changes

Charge/Spin Conditioning Feature Implementation

Layer / File(s) Summary
Backend interface and query method
source/api_cc/include/DeepPot.h
DeepPotBackend adds dim_chg_spin() query method (defaulting to 0) and new charge/spin-aware computew / computew_mixed_type overloads that accept a charge_spin vector parameter, with default implementations forwarding to existing overloads.
DeepPot public compute API expansion
source/api_cc/include/DeepPot.h
DeepPot public templated compute and compute_mixed_type overloads are extended with optional const std::vector<double>& charge_spin parameter (defaulting to empty) across all variants: single/vector energy output, per-atom energy/virial outputs, and neighbor-list inputs. DeepPot declares int dim_chg_spin() const; method.
DeepPotPTExpt PyTorch export interface
source/api_cc/include/DeepPotPTExpt.h
Internal compute template declarations (compute, compute_nframes, compute_mixed_type_impl) gain charge_spin parameter; dim_chg_spin() override is added; computew and computew_mixed_type public overloads are updated to accept runtime charge_spin and marked override; run_model and run_model_with_comm signatures extended with charge_spin tensor parameter; private dchgspin member stores dimension.
DeepPot template instantiation and forwarding
source/api_cc/src/DeepPot.cc
All compute and compute_mixed_type overload implementations forward charge_spin into backend calls (dp->computew / dp->computew_mixed_type); explicit template instantiations for double and float updated to include new charge_spin parameter across all combinations of energy output, per-atom outputs, and nlist variants.
DeepPotPTExpt runtime charge_spin tensor handling
source/api_cc/src/DeepPotPTExpt.cc
Runtime charge_spin logic reads metadata dimension into dchgspin and initializes default_chg_spin_; charge_spin_tensor is constructed from caller-provided vector when non-empty, otherwise uses default_chg_spin_, or throws if both missing and model requires it; tensor conditionally included in model input list when dchgspin > 0; multi-frame (compute_nframes) and mixed-type (compute_mixed_type_impl) paths slice per-frame/per-block charge_spin; existing computew overloads pass empty {}, new overloads pass runtime value; all template instantiations updated.
LAMMPS base class charge_spin support
source/lmp/pair_base.h, source/lmp/pair_base.cpp
PairDeepBaseModel adds dim_chg_spin member, charge_spin vector, control flags (do_compute_charge_spin, compute_charge_spin_id), and make_charge_spin_from_compute() helper that queries a compute and populates charge_spin in scalar or vector mode depending on dimension.
PairDeepMD charge_spin wiring and configuration
source/lmp/pair_deepmd.cpp
PairDeepMD::compute conditionally populates charge_spin via make_charge_spin_from_compute() and passes it to deep_pot.compute() calls; PairDeepMD::settings recognizes charge_spin and charge_spin_from_compute keywords, queries model dim_chg_spin, parses explicit values or compute ID, clears containers during reset, and validates mutual exclusion of both configuration methods.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • deepmodeling/deepmd-kit#5370: Wires spin/charge-spin tensor into the same run_model plumbing, extending model execution to support spin-aware inference paths alongside charge/spin conditioning.
  • deepmodeling/deepmd-kit#5431: Threads charge_spin as runtime input through Python eval paths and descriptor computation, providing equivalent per-call charge/spin control at the Python interface and model training/evaluation layer.

Suggested labels

C++, LAMMPS, API

Suggested reviewers

  • njzjz
  • wanghan-iapcm
  • OutisLi
  • iProzd
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding charge-spin support to the C++ runtime API across DeepPot, DeepPotPTExpt, and LAMMPS integration layers.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@anyangml anyangml marked this pull request as draft June 9, 2026 06:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for passing a runtime charge/spin conditioning vector from the LAMMPS pair style and C++ API down into the .pt2 (PTExpt) inference backend, instead of relying solely on defaults embedded in model metadata.

Changes:

  • Extend LAMMPS pair_style deepmd parsing to accept charge_spin and charge_spin_from_compute, and thread the resulting vector into DeepPot::compute(...).
  • Extend the C++ DeepPot / DeepPotBackend interfaces and the PTExpt backend to accept and consume a runtime charge_spin input with fallback to metadata defaults.
  • Add PTExpt .pt2 runtime tensor construction for charge_spin, plus new computew overloads that carry it through.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
source/lmp/pair_deepmd.cpp Adds new pair_style keys for charge/spin and passes charge_spin into runtime inference calls.
source/lmp/pair_base.h Adds storage/flags and a helper to populate charge_spin from a LAMMPS compute.
source/lmp/pair_base.cpp Implements make_charge_spin_from_compute and initializes the new compute flag.
source/api_cc/src/DeepPotPTExpt.cc Threads charge_spin into .pt2 model invocation and adds runtime/default tensor construction.
source/api_cc/src/DeepPot.cc Extends DeepPot::compute overloads to accept charge_spin; adds dim_chg_spin() forwarding.
source/api_cc/include/DeepPotPTExpt.h Declares PTExpt charge_spin-aware overloads and exposes dim_chg_spin().
source/api_cc/include/DeepPot.h Adds dim_chg_spin() to backends and provides default charge_spin-aware computew overloads.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 599 to 602
dim_fparam = deep_pot_model_devi.dim_fparam();
dim_aparam = deep_pot_model_devi.dim_aparam();
dim_chg_spin = deep_pot.dim_chg_spin();
assert(cutoff == deep_pot.cutoff() * dist_unit_cvt_factor);
Comment thread source/lmp/pair_base.cpp
Comment on lines +171 to +176
int icompute = modify->find_compute(compute_charge_spin_id);
Compute* compute = modify->compute[icompute];

if (!compute) {
error->all(FLERR, "compute id is not found: " + compute_charge_spin_id);
}
Comment on lines +515 to +522
auto dbl_options = torch::TensorOptions().dtype(torch::kFloat64);
if (!charge_spin.empty()) {
charge_spin_tensor =
torch::from_blob(const_cast<double*>(charge_spin.data()),
{1, static_cast<std::int64_t>(charge_spin.size())},
dbl_options)
.clone()
.to(device);
Comment on lines +839 to +846
auto dbl_options = torch::TensorOptions().dtype(torch::kFloat64);
if (!charge_spin.empty()) {
charge_spin_tensor =
torch::from_blob(const_cast<double*>(charge_spin.data()),
{1, static_cast<std::int64_t>(charge_spin.size())},
dbl_options)
.clone()
.to(device);
Comment on lines +962 to +967
std::vector<double> frame_chg_spin;
if (!charge_spin.empty()) {
size_t s_dcsp = static_cast<size_t>(dchgspin);
frame_chg_spin.assign(charge_spin.begin() + s_ff * s_dcsp,
charge_spin.begin() + (s_ff + 1) * s_dcsp);
}
Comment on lines +1139 to +1144
std::vector<double> frame_chg_spin;
if (!charge_spin.empty()) {
size_t s_dcsp = static_cast<size_t>(dchgspin);
frame_chg_spin.assign(charge_spin.begin() + s_ff * s_dcsp,
charge_spin.begin() + (s_ff + 1) * s_dcsp);
}
Comment on lines +511 to +514
// Build charge_spin tensor: use runtime value when provided, fall back to
// default_chg_spin_ stored in the .pt2 metadata.
at::Tensor charge_spin_tensor;
if (dchgspin > 0) {
Comment on lines +699 to +704
} else if (string(arg[iarg]) == string("charge_spin")) {
for (int ii = 0; ii < dim_chg_spin; ++ii) {
if (iarg + 1 + ii >= narg || is_key(arg[iarg + 1 + ii])) {
char tmp[1024];
sprintf(tmp, "Illegal charge_spin, the dimension should be %d",
dim_chg_spin);
Comment on lines 251 to +256
if (single_model || multi_models_no_mod_devi) {
// 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);
lmp_list, ago, fparam, daparam, charge_spin);

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
source/lmp/pair_deepmd.cpp (1)

318-320: ⚠️ Potential issue | 🟠 Major

Ensure multi-model deviation propagates charge_spin (or warns when unsupported)

In source/lmp/pair_deepmd.cpp, the multi-model deviation calls deep_pot_model_devi.compute(...) at lines 318-320 and 326-329 without charge_spin, unlike the single-model path which passes charge_spin to deep_pot.compute(). The DeepBaseModelDevi API in source/api_c/include/deepmd.hpp does not provide a compute(...charge_spin...)-style overload (no compute.*charge_spin occurrences), so deviation output cannot be consistent for models with dim_chg_spin > 0 unless the API/caller is extended. Add a warning/error when dim_chg_spin > 0 and multi-model deviation mode is enabled, or implement a charge_spin-aware deviation compute.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@source/lmp/pair_deepmd.cpp` around lines 318 - 320, The multi-model deviation
path calls deep_pot_model_devi.compute(...) without passing charge_spin while
the single-model path uses deep_pot.compute(..., charge_spin...), so when
dim_chg_spin > 0 the deviation results are inconsistent; update the caller in
source/lmp/pair_deepmd.cpp to detect dim_chg_spin > 0 when multi-model deviation
(DeepBaseModelDevi) is active and either (A) emit a clear warning/error that
charge_spin is unsupported for deviation mode (include dim_chg_spin and mode
name in message) or (B) extend the deviation codepath to accept and forward
charge_spin to DeepBaseModelDevi (implement a new compute overload or adapter in
DeepBaseModelDevi and call it from deep_pot_model_devi.compute); reference
deep_pot_model_devi.compute, deep_pot.compute, DeepBaseModelDevi, dim_chg_spin,
and charge_spin when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@source/lmp/pair_deepmd.cpp`:
- Line 601: In the multi-model initialization path, change the source of
dim_chg_spin to be read from deep_pot_model_devi (like cutoff, numb_types,
numb_types_spin, dim_fparam, dim_aparam) and add an assertion that
deep_pot.dim_chg_spin() equals the value from deep_pot_model_devi; specifically,
replace or supplement the current direct read dim_chg_spin =
deep_pot.dim_chg_spin() with reading dim_chg_spin from deep_pot_model_devi
(e.g., dim_chg_spin = deep_pot_model_devi.dim_chg_spin()) and add the same
consistency check/assert between deep_pot and deep_pot_model_devi.

---

Outside diff comments:
In `@source/lmp/pair_deepmd.cpp`:
- Around line 318-320: The multi-model deviation path calls
deep_pot_model_devi.compute(...) without passing charge_spin while the
single-model path uses deep_pot.compute(..., charge_spin...), so when
dim_chg_spin > 0 the deviation results are inconsistent; update the caller in
source/lmp/pair_deepmd.cpp to detect dim_chg_spin > 0 when multi-model deviation
(DeepBaseModelDevi) is active and either (A) emit a clear warning/error that
charge_spin is unsupported for deviation mode (include dim_chg_spin and mode
name in message) or (B) extend the deviation codepath to accept and forward
charge_spin to DeepBaseModelDevi (implement a new compute overload or adapter in
DeepBaseModelDevi and call it from deep_pot_model_devi.compute); reference
deep_pot_model_devi.compute, deep_pot.compute, DeepBaseModelDevi, dim_chg_spin,
and charge_spin when making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 173dd256-e998-4ff9-9db5-ca8571420859

📥 Commits

Reviewing files that changed from the base of the PR and between e05af21 and 57df385.

📒 Files selected for processing (7)
  • source/api_cc/include/DeepPot.h
  • source/api_cc/include/DeepPotPTExpt.h
  • source/api_cc/src/DeepPot.cc
  • source/api_cc/src/DeepPotPTExpt.cc
  • source/lmp/pair_base.cpp
  • source/lmp/pair_base.h
  • source/lmp/pair_deepmd.cpp

numb_types_spin = deep_pot_model_devi.numb_types_spin();
dim_fparam = deep_pot_model_devi.dim_fparam();
dim_aparam = deep_pot_model_devi.dim_aparam();
dim_chg_spin = deep_pot.dim_chg_spin();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Query dim_chg_spin from deep_pot_model_devi for consistency.

In the multi-model initialization path, all other parameters (cutoff, numb_types, numb_types_spin, dim_fparam, dim_aparam) are queried from deep_pot_model_devi and then asserted to match deep_pot (lines 602-606). However, dim_chg_spin is queried only from deep_pot without a corresponding assertion.

🔧 Proposed fix
   dim_fparam = deep_pot_model_devi.dim_fparam();
   dim_aparam = deep_pot_model_devi.dim_aparam();
-  dim_chg_spin = deep_pot.dim_chg_spin();
+  dim_chg_spin = deep_pot_model_devi.dim_chg_spin();
   assert(cutoff == deep_pot.cutoff() * dist_unit_cvt_factor);
   assert(numb_types == deep_pot.numb_types());
   assert(numb_types_spin == deep_pot.numb_types_spin());
   assert(dim_fparam == deep_pot.dim_fparam());
   assert(dim_aparam == deep_pot.dim_aparam());
+  assert(dim_chg_spin == deep_pot.dim_chg_spin());
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
dim_chg_spin = deep_pot.dim_chg_spin();
dim_fparam = deep_pot_model_devi.dim_fparam();
dim_aparam = deep_pot_model_devi.dim_aparam();
dim_chg_spin = deep_pot_model_devi.dim_chg_spin();
assert(cutoff == deep_pot.cutoff() * dist_unit_cvt_factor);
assert(numb_types == deep_pot.numb_types());
assert(numb_types_spin == deep_pot.numb_types_spin());
assert(dim_fparam == deep_pot.dim_fparam());
assert(dim_aparam == deep_pot.dim_aparam());
assert(dim_chg_spin == deep_pot.dim_chg_spin());
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@source/lmp/pair_deepmd.cpp` at line 601, In the multi-model initialization
path, change the source of dim_chg_spin to be read from deep_pot_model_devi
(like cutoff, numb_types, numb_types_spin, dim_fparam, dim_aparam) and add an
assertion that deep_pot.dim_chg_spin() equals the value from
deep_pot_model_devi; specifically, replace or supplement the current direct read
dim_chg_spin = deep_pot.dim_chg_spin() with reading dim_chg_spin from
deep_pot_model_devi (e.g., dim_chg_spin = deep_pot_model_devi.dim_chg_spin())
and add the same consistency check/assert between deep_pot and
deep_pot_model_devi.

@codecov

codecov Bot commented Jun 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 71.00000% with 87 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.75%. Comparing base (e05af21) to head (f24390b).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
source/api_cc/src/DeepPotPTExpt.cc 70.78% 22 Missing and 4 partials ⚠️
...ource/api_cc/tests/test_deeppot_chg_spin_ptexpt.cc 83.80% 23 Missing ⚠️
source/lmp/pair_deepmd.cpp 26.92% 18 Missing and 1 partial ⚠️
source/lmp/pair_base.cpp 18.18% 18 Missing ⚠️
source/api_cc/include/DeepPot.h 92.30% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5509      +/-   ##
==========================================
- Coverage   81.42%   79.75%   -1.68%     
==========================================
  Files         871      872       +1     
  Lines       96951    97201     +250     
  Branches     4241     4271      +30     
==========================================
- Hits        78941    77521    -1420     
- Misses      16708    18542    +1834     
+ Partials     1302     1138     -164     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants