Skip to content

fix(tf): handle multiframe DeepSpin extension - #5851

Merged
njzjz merged 2 commits into
deepmodeling:masterfrom
njzjz-bot:fix/tf-deepspin-multiframe-extension-5660
Aug 2, 2026
Merged

fix(tf): handle multiframe DeepSpin extension#5851
njzjz merged 2 commits into
deepmodeling:masterfrom
njzjz-bot:fix/tf-deepspin-multiframe-extension-5660

Conversation

@njzjz-bot

@njzjz-bot njzjz-bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Closes #5660.

Summary

  • build TensorFlow DeepSpin physical and virtual coordinate buffers for every input frame in both direct and external-neighbor-list paths
  • separate extended coordinate frame strides from original-atom aparam strides during NULL-atom selection
  • copy physical force, magnetic force, atomic energy, and atomic virial outputs with explicit source/destination frame offsets
  • derive the atomic-energy source width from the TensorFlow tensor, since spin models emit energies only for physical atoms while C++ preserves extended virtual slots
  • add distinct two-frame direct and InputNlist atomic regressions for both double and float

Why existing tests missed this

All TensorFlow DeepSpin C++ tests used scalar-energy, single-frame calls. The extension helpers therefore only needed frame-zero buffers, and frame-zero copyback filled every asserted output. No test exercised the documented vector-energy overload with nframes > 1, so undersized extension buffers, incorrect inferred frame counts, and missing output frame offsets remained invisible.

The new tests perturb both coordinates and spins in frame 2, evaluate each frame separately, and compare every batched slice against its one-frame reference. They also assert the frame references differ, preventing duplicated-frame behavior from passing.

Validation

  • ruff format .
  • ruff check .
  • clang-format --dry-run --Werror on all changed C++ files
  • built deepmd_op, deepmd_backend_tf, and runUnitTests_cc
  • all 16 TestInferDeepSpin and TestInferDeepSpinNopbc typed tests passed
  • focused four-case two-frame matrix passed: direct/InputNlist × double/float

Coding agent: Codex
Codex version: codex-cli 0.144.4
Model: gpt-5.6-sol
Reasoning effort: xhigh

Summary by CodeRabbit

  • New Features

    • Added support for processing multiple frames in spin-model calculations.
    • Extended coordinate and neighbor-list handling to support frame-aware inputs.
    • Preserved correct real-atom outputs across force, magnetic-force, energy, and virial results.
  • Bug Fixes

    • Improved validation of coordinate, spin, and output dimensions.
    • Corrected handling of virtual-atom slots and per-frame output layouts.
    • Fixed multi-frame behavior for standard and LMP neighbor-list calculations.

Build TensorFlow DeepSpin virtual-atom coordinates for every frame and use
explicit frame strides when copying physical, magnetic, and atomic outputs
back from extended buffers.

Derive atomic-energy frame width from the TensorFlow output so physical-only
energies do not interleave frames in the extended atom layout.

Coding-Agent: Codex
Codex-Version: codex-cli 0.144.4
Model: gpt-5.6-sol
Reasoning-Effort: xhigh
@dosubot dosubot Bot added the bug label Jul 16, 2026
@github-actions github-actions Bot added the C++ label Jul 17, 2026
@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.02198% with 40 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.22%. Comparing base (6c3b985) to head (23ef557).
⚠️ Report is 66 commits behind head on master.

Files with missing lines Patch % Lines
source/api_cc/src/DeepSpinTF.cc 69.51% 12 Missing and 13 partials ⚠️
source/api_cc/tests/test_deeppot_tf_spin.cc 84.69% 15 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5851      +/-   ##
==========================================
+ Coverage   78.58%   79.22%   +0.64%     
==========================================
  Files        1050     1072      +22     
  Lines      120637   125176    +4539     
  Branches     4356     4552     +196     
==========================================
+ Hits        94801    99171    +4370     
- Misses      24278    24374      +96     
- Partials     1558     1631      +73     

☔ 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.

@njzjz

njzjz commented Jul 18, 2026

Copy link
Copy Markdown
Member

Possible reviewers based on changed lines, exact file history, and exact-file review history:

  • @wanghan-iapcm — 4 commits on changed files; 20 reviews on exact changed files (source/api_cc/src/DeepSpinTF.cc, source/api_cc/src/common.cc).
  • @OutisLi — 12 reviews on exact changed files (source/api_cc/src/common.cc).

No review request was made automatically.

Coding agent: Codex
Codex version: codex-cli 0.144.4
Model: gpt-5.6-sol
Reasoning effort: xhigh

@njzjz
njzjz requested review from OutisLi and wanghan-iapcm and removed request for OutisLi and wanghan-iapcm July 18, 2026 07:26

@wanghan-iapcm wanghan-iapcm left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The core arithmetic here is right, and I checked the central assumption rather than taking it on faith: deepmd/tf/model/ener.py reshapes o_atom_energy to the real-type atom count and the spin fitting emits energies for physical atoms only, so deriving the source stride from output_ae.NumElements() / nframes and leaving the virtual slots zero is correct - and it removes an out-of-bounds read on oae that was there even at nframes == 1. The common.cc change is a provable no-op for the other nine callers of select_real_atoms_coord, since each passes dcoord_ sized exactly nframes * nall * 3.

The regression test is a real one. On master extend_nlist resizes extend_dcoord to extend_nall * 3 and reads only dcoord_[ii * 3 + jj], so a two-frame call returns a one-frame buffer, session_input_tensors infers nframes == 1, and ASSERT_EQ(energy.size(), 2U) fails. The EXPECT_GT(fabs(energy_first - energy_second), EPSILON) guard also stops a duplicated-frame implementation from passing.

One gap inline. Two smaller notes, neither blocking:

  1. Both new tests run with nghost == 0 and a model with dim_aparam == 0, so the new_idx + (ii < nloc ? nloc : nghost) ghost-virtual branch you rewrote is never taken with ghosts present, and the daparam > 0 branch of select_real_atoms_coord - the exact case the new comment cites as motivation ("aparam keeps the caller's original atom stride ... DeepSpin virtual atoms") - is never entered. A LAMMPS-style case with nghost > 0 would cover the more fragile of the two.

  2. The new size checks in extend() and extend_nlist() use assert, which is compiled out under -DNDEBUG, while run_model in the same diff throws deepmd::deepmd_exception for its new checks. Mixed, but the asserts guard caller-supplied dcoord_/dspin_ sizes, which is where a throw earns its keep.

Comment thread source/api_cc/src/DeepSpinTF.cc
Coding-Agent: Codex
Codex-Version: codex-cli 0.144.6
Model: gpt-5.6-sol
Reasoning-Effort: xhigh
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

DeepSpin TensorFlow execution now supports multiple coordinate and spin frames. Extension helpers build frame-indexed virtual atoms, execution paths remap outputs per frame, and tests compare batched results with independent computations.

Changes

Multi-frame DeepSpin execution

Layer / File(s) Summary
Frame-aware coordinate extension
source/api_cc/include/DeepSpinTF.h, source/api_cc/src/DeepSpinTF.cc
Extension helpers accept nframes, validate coordinate and spin sizes, and generate virtual-atom coordinates for every frame.
Direct execution output remapping
source/api_cc/src/DeepSpinTF.cc, source/api_cc/src/common.cc, source/api_cc/tests/test_deeppot_tf_spin.cc
Direct execution validates atomic-energy dimensions and remaps forces, magnetic forces, energies, and virials into real-atom frame layouts.
Neighbor-list execution and regression coverage
source/api_cc/src/DeepSpinTF.cc, source/api_cc/tests/test_deeppot_tf_spin.cc
Neighbor-list output copying uses frame and local/ghost offsets. Tests compare batched two-frame results with independent computations.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DeepSpinTF
  participant ExtensionHelpers
  participant TensorFlow
  participant OutputRemapping
  DeepSpinTF->>ExtensionHelpers: Extend coordinates and spins for nframes
  ExtensionHelpers-->>TensorFlow: Provide frame-indexed virtual-atom inputs
  TensorFlow-->>OutputRemapping: Return batched model outputs
  OutputRemapping-->>DeepSpinTF: Copy real-atom outputs for each frame
Loading

Possibly related PRs

Suggested reviewers: outisli, njzjz

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: fixing multi-frame TensorFlow DeepSpin extension handling.
Linked Issues check ✅ Passed The changes address issue #5660 by extending all frames, applying frame offsets, and adding direct and neighbor-list regressions.
Out of Scope Changes check ✅ Passed The changes remain within the linked issue scope and support multi-frame DeepSpin extension, output copyback, validation, and regression tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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.

@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.

🧹 Nitpick comments (2)
source/api_cc/src/common.cc (2)

187-194: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Three new size-consistency checks use assert, which is inactive in release builds. All three validate externally-influenced coordinate/spin buffer sizes against the derived nframes, but assert compiles out under NDEBUG, so a caller-supplied size mismatch silently truncates the derived stride instead of failing clearly. The same PR already uses deepmd::deepmd_exception for an analogous check in run_model (DeepSpinTF.cc lines 179-188); mirroring that pattern here keeps the checks active in release builds.

  • source/api_cc/src/common.cc#L187-L194: replace the assert at line 192 with a thrown deepmd::deepmd_exception when dcoord_.size() is not evenly divisible by nframes*3.
  • source/api_cc/src/DeepSpinTF.cc#L1074-L1077: replace the assert validating dcoord/spin sizes in extend() with a thrown exception.
  • source/api_cc/src/DeepSpinTF.cc#L1296-L1297: replace the assert validating dcoord_/dspin_ sizes in extend_nlist() with a thrown exception.
🤖 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/api_cc/src/common.cc` around lines 187 - 194, Replace the
size-validation asserts with active deepmd::deepmd_exception checks in common.cc
lines 187-194, DeepSpinTF.cc lines 1074-1077, and DeepSpinTF.cc lines 1296-1297.
Validate divisibility and coordinate/spin size consistency before deriving or
using strides, following the existing run_model exception pattern; preserve the
current valid-input behavior.

192-192: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider an exception instead of assert for this size check.

This validates that dcoord_'s size is evenly divisible by nframes*3. Under NDEBUG, assert is compiled out, so a mismatched caller-supplied buffer would silently truncate coord_nall instead of failing clearly. See the analogous exception-based check added in DeepSpinTF.cc's run_model (same PR, lines 179-188) for a pattern that stays active in release builds.

🤖 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/api_cc/src/common.cc` at line 192, Replace the assert in the
surrounding coordinate-validation logic with an exception-based size check that
remains active under NDEBUG. Validate that dcoord_.size() exactly equals nframes
multiplied by coord_nall and 3, and throw a clear error on mismatch before any
truncating computation proceeds.
🤖 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.

Nitpick comments:
In `@source/api_cc/src/common.cc`:
- Around line 187-194: Replace the size-validation asserts with active
deepmd::deepmd_exception checks in common.cc lines 187-194, DeepSpinTF.cc lines
1074-1077, and DeepSpinTF.cc lines 1296-1297. Validate divisibility and
coordinate/spin size consistency before deriving or using strides, following the
existing run_model exception pattern; preserve the current valid-input behavior.
- Line 192: Replace the assert in the surrounding coordinate-validation logic
with an exception-based size check that remains active under NDEBUG. Validate
that dcoord_.size() exactly equals nframes multiplied by coord_nall and 3, and
throw a clear error on mismatch before any truncating computation proceeds.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d1533e3d-e80c-4945-8cd1-1c967e157145

📥 Commits

Reviewing files that changed from the base of the PR and between 6c3b985 and 23ef557.

📒 Files selected for processing (4)
  • source/api_cc/include/DeepSpinTF.h
  • source/api_cc/src/DeepSpinTF.cc
  • source/api_cc/src/common.cc
  • source/api_cc/tests/test_deeppot_tf_spin.cc

@njzjz
njzjz requested a review from wanghan-iapcm August 1, 2026 14:57

@wanghan-iapcm wanghan-iapcm left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Addressed, and the test half is the part I like most.

The extension now trims each frame's atomic outputs to the real atoms with ff * extend_nall + ii as the source index, so the two overloads in this file finally agree on what they hand back. What makes it convincing rather than merely plausible is that the assertions were tightened at the same time: on master this path had // EXPECT_EQ(atom_ener.size(), natoms); commented out and EXPECT_EQ(atom_vir.size(), (natoms + 2) * 9); asserting the oversized length, while the neighbor-list overload a few hundred lines down already asserted the correct natoms and natoms * 9. In other words the suite had been adjusted to match the broken output, which is exactly why nothing caught this. Both are now exact at all three call sites, so the old behaviour cannot come back quietly.

I did not build TensorFlow C++ locally, so on the question of whether the new assertions fail unpatched I am reasoning from the stride rather than from a run: pre-fix datom_energy_ keeps the extend_nall = nloc + nloc_spin stride, so its length is natoms + 2 and the re-enabled check cannot pass. Worth stating plainly rather than implying I executed it.

On the red checks: the two failing Test C++ jobs are the Paddle inference library download (file DOWNLOAD cannot compute hash on failed download), not this change. Test C++ (true, false, false, true) passed, and it did install DeepSpinTF.h and run all three test binaries with runUnitTest_cc green, so despite the note about the local build not reaching compilation, this code is compiled and exercised in CI.

Approving.

@njzjz
njzjz enabled auto-merge August 2, 2026 05:30

@wanghan-iapcm wanghan-iapcm left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Correcting myself on one point in my approval above, since it was wrong in a way you might have relied on.

I wrote that Test C++ (true, false, false, true) "did install DeepSpinTF.h and run all three test binaries ... so this code is compiled and exercised in CI". That is not right. The header install is unconditional, so it proves nothing. That job configures with:

cmake -D ENABLE_TENSORFLOW=FALSE -D ENABLE_PYTORCH=FALSE -D ENABLE_PADDLE=TRUE ...

and source/api_cc/CMakeLists.txt removes src/DeepSpinTF.cc from the main LIB_SRC list, compiling it only into deepmd_backend_tf under if(ENABLE_TENSORFLOW). With TensorFlow off, the file this PR changes is never compiled, so the green runUnitTest_cc says nothing about it.

The rest of the picture: of the four Test C++ jobs, one failed on the Paddle inference library download and two were cancelled by fail-fast; the only one that completed is the Paddle-only configuration above. I also said "the two failing jobs" when it was one failure plus two cancellations.

So the accurate statement is that no CI job has compiled or run this change at this head, which combined with the local build not reaching compilation means it currently rests entirely on code review.

I am leaving the approval in place: the fix does what I asked, the two overloads now agree, and the tightened assertions (atom_ener.size() == natoms re-enabled, atom_vir.size() == natoms * 9 replacing the oversized (natoms + 2) * 9) cannot pass on the old stride, so I am confident in it by inspection. But it would be worth getting a TensorFlow-enabled Test C++ job green before merging rather than treating the current checks as evidence.

@njzjz
njzjz added this pull request to the merge queue Aug 2, 2026
Merged via the queue into deepmodeling:master with commit ca7f708 Aug 2, 2026
58 of 63 checks passed
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.

[Code scan] Handle multi-frame TensorFlow DeepSpin extension buffers

3 participants