Skip to content

fix(pt,dpmodel): drop out-of-rcut neighbors in _format_nlist pad branch#5529

Merged
njzjz merged 2 commits into
deepmodeling:masterfrom
wanghan-iapcm:fix/format-nlist-pad-rcut-filter
Jun 16, 2026
Merged

fix(pt,dpmodel): drop out-of-rcut neighbors in _format_nlist pad branch#5529
njzjz merged 2 commits into
deepmodeling:masterfrom
wanghan-iapcm:fix/format-nlist-pad-rcut-filter

Conversation

@wanghan-iapcm

@wanghan-iapcm wanghan-iapcm commented Jun 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

forward_lower's _format_nlist only filtered out-of-rcut neighbors in its sort branch (n_nnei > nnei or extra_nlist_sort). When the input neighbor-list width is <= nnei (sum(sel)) and extra_nlist_sort is False, it took the pad branch and returned the nlist unchanged — never dropping neighbors beyond rcut.

The C++/LAMMPS path (DeepPotPT.cccopy_from_nlistpadding) builds the neighbor list with rcut + skin and does not rcut-filter before forward_lower (the in-code comment in commonPT.h is explicit: "No truncation or distance sorting is done — the model's format_nlist handles that"). Its width is the per-atom neighbor count, which on sparse systems is <= nnei — exactly the case in discussion #5438 (width 39 < 100). In that regime, out-of-rcut neighbors leak into the descriptor. Because the pad branch does not re-sort, the leaked contribution is order-dependent: reversing the nlist changes the energy by ~1e-4 (se_r) / ~4e-6 (se_a).

This is the same root condition as #5438; that PR (#5524) closed it only for compressed se_a (by forcing extra_nlist_sort). The uncompressed paths and se_r/se_t remained exposed.

Fix

The pad branch now also drops neighbors with rr > rcut (no re-sort — these descriptors reduce over neighbors order-independently). Applied to both the pt and dpmodel backends (dpmodel is shared by pt_expt).

The exported pt_expt graph is unaffected: export forces extra_nlist_sort=True, so it always takes the sort branch; the new pad-branch code is eager-only.

Verification

  • New regression test test_format_nlist_overcut.py: over-cut (rcut+2) forward_lower, as-is and reversed, must match the rcut-bounded reference for se_a and se_r (energy + force, 1e-10). The reversed cases fail without this fix and pass with it.
  • After the fix: se_a reversed over-cut rel = 0.0, se_r rel = 4.4e-16 (were 4.3e-6 / 1.4e-4).
  • Existing suites green on GPU: test_jit (all models), test_forward_lower, test_permutation, pt_expt descriptor + ener-model export, universal dp/pt model consistency.

Summary by CodeRabbit

Summary by CodeRabbit

  • Bug Fixes

    • Improved neighbor-list handling to drop neighbor candidates whose distances exceed the cutoff when using over-cut neighbor lists, preserving neighbor order while ensuring out-of-cutoff entries are excluded.
  • Tests

    • Added regression coverage for over-cut neighbor lists in the PT backend and common DP model backend, validating energy/forces consistency across descriptor types and forward/reversed neighbor ordering.

forward_lower's _format_nlist only filtered neighbors beyond rcut in its
sort branch (n_nnei > nnei or extra_nlist_sort). When the input neighbor list
width is <= nnei (sum of sel) and extra_nlist_sort is False, the pad branch
returned the nlist unchanged. The C++/LAMMPS path builds the neighbor list
with rcut+skin and does NOT rcut-filter before forward_lower, so on sparse
systems (per-atom neighbor count <= nnei, e.g. discussion deepmodeling#5438: width 39 <
100) out-of-rcut neighbors leaked into the descriptor. Because the pad branch
does not re-sort, the leaked contribution is order-dependent: reversing the
nlist changes the energy by ~1e-4 (se_r) / ~4e-6 (se_a).

Fix: the pad branch now also drops neighbors with rr > rcut (no re-sort needed,
these descriptors reduce over neighbors order-independently). Applied to both
the pt and dpmodel backends (dpmodel is shared by pt_expt). The exported
pt_expt graph is unaffected: export forces extra_nlist_sort=True, so it takes
the sort branch.

Regression test test_format_nlist_overcut.py: over-cut (rcut+2) forward_lower,
as-is and reversed, must match the rcut-bounded reference for se_a and se_r.
The reversed cases fail without this fix.
@dosubot dosubot Bot added the bug label Jun 13, 2026
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR modifies neighbor-list formatting in both dpmodel and pt model implementations to filter out neighbors with distances exceeding the cutoff radius in the non-sorted path, and adds regression tests validating this behavior across descriptor flavors and neighbor orderings.

Changes

Neighbor-list cutoff filtering

Layer / File(s) Summary
dpmodel _format_nlist distance masking
deepmd/dpmodel/model/make_model.py
Computes pairwise distances for candidate neighbors and masks those exceeding rcut by setting indices to -1 in the non-sorted pad-branch.
pt model _format_nlist distance masking
deepmd/pt/model/model/make_model.py
Mirrors dpmodel change by computing pairwise distances and masking out-of-cutoff neighbors in the non-sorted path, replacing prior no-op behavior.
pt model over-cut neighbor regression test
source/tests/pt/model/test_format_nlist_overcut.py
Parameterized test that builds over-cut neighbor lists using rcut + 2.0, verifies energy and forces match canonical reference, covers both descriptor flavors (se_a, se_r) and neighbor orderings.
dpmodel over-cut neighbor regression test
source/tests/common/dpmodel/test_format_nlist_overcut.py
Regression test that builds over-cut neighbor lists and asserts energy and atomic energy match canonical reference for both normal and reversed neighbor orderings.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • njzjz
  • njzjz-bot
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% 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: fixing a bug where out-of-rcut neighbors were not being filtered in the _format_nlist pad branch for both pt and dpmodel backends.
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.

@codecov

codecov Bot commented Jun 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.19%. Comparing base (5d94bd6) to head (d4ee9db).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #5529   +/-   ##
=======================================
  Coverage   82.19%   82.19%           
=======================================
  Files         891      891           
  Lines      101599   101612   +13     
  Branches     4242     4242           
=======================================
+ Hits        83507    83521   +14     
  Misses      16789    16789           
+ Partials     1303     1302    -1     

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

The pt-side test_format_nlist_overcut.py only exercises deepmd/pt. Add the
dpmodel (numpy) counterpart so the deepmd/dpmodel _format_nlist change (shared
by pt_expt) is covered: over-cut call_lower, as-is and reversed, must match the
rcut-bounded reference (reduced + per-atom energy). The reversed case fails with
the dpmodel fix reverted.

@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 (1)
source/tests/common/dpmodel/test_format_nlist_overcut.py (1)

66-73: ⚡ Quick win

Consider adding test coverage for se_a descriptor.

The test currently only covers the se_e2_r (se_r) descriptor. According to the PR objectives, the fix applies to both se_a and se_r descriptors, and the pt backend test covers both. Consider adding a parameterized test or a second test case for se_a to ensure comprehensive coverage of the dpmodel backend as well.

🤖 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/tests/common/dpmodel/test_format_nlist_overcut.py` around lines 66 -
73, The test method test_overcut_matches_canonical currently only covers the
se_e2_r (se_r) descriptor, but the fix applies to both se_a and se_r descriptors
according to the PR objectives. Add test coverage for the se_a descriptor by
parameterizing the test method to cover both descriptor types (se_a and se_r) or
by adding a separate test case that validates the se_a descriptor with the same
overcut matching logic to ensure comprehensive coverage of the dpmodel backend.
🤖 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/tests/common/dpmodel/test_format_nlist_overcut.py`:
- Around line 66-73: The test method test_overcut_matches_canonical currently
only covers the se_e2_r (se_r) descriptor, but the fix applies to both se_a and
se_r descriptors according to the PR objectives. Add test coverage for the se_a
descriptor by parameterizing the test method to cover both descriptor types
(se_a and se_r) or by adding a separate test case that validates the se_a
descriptor with the same overcut matching logic to ensure comprehensive coverage
of the dpmodel backend.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d87efcf2-4707-4808-838b-ba0f1583a1fc

📥 Commits

Reviewing files that changed from the base of the PR and between 2f16866 and d4ee9db.

📒 Files selected for processing (1)
  • source/tests/common/dpmodel/test_format_nlist_overcut.py

@wanghan-iapcm wanghan-iapcm requested review from OutisLi and njzjz June 14, 2026 01:37
@wanghan-iapcm wanghan-iapcm added this pull request to the merge queue Jun 15, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to no response for status checks Jun 16, 2026
@njzjz njzjz added this pull request to the merge queue Jun 16, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jun 16, 2026
@njzjz njzjz added this pull request to the merge queue Jun 16, 2026
Merged via the queue into deepmodeling:master with commit 69409c5 Jun 16, 2026
70 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.

3 participants