Skip to content

fix(pt_expt): fold vacuum reference on CPU tracing device (#6076) - #6079

Closed
OutisLi-Bot wants to merge 1 commit into
deepmodeling:masterfrom
OutisLi-Bot:fix/6076-fold-vacuum-ref-cpu
Closed

OutisLi-Bot wants to merge 1 commit into
deepmodeling:masterfrom
OutisLi-Bot:fix/6076-fold-vacuum-ref-cpu

Conversation

@OutisLi-Bot

@OutisLi-Bot OutisLi-Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes #6076: freezing a vacuum_ref=True DPA4C (and other) checkpoint with pt_expt OOMs on CUDA during fold_vacuum_reference(), because export folded while the model still resided on the GPU. Swapping to fold-after-to("cpu") avoided the OOM but exposed a FakeTensor CPU/CUDA mismatch: numpy buffer reassignment through dpmodel_setattr always placed new buffers on env.DEVICE.

Design

  1. Export order (serialization.py): move the model to the CPU tracing device before fold_vacuum_reference(). The vacuum descriptor evaluates an isolated atom of every type through the descriptor; doing that on CUDA can allocate several GiB even when export already holds most of the GPU.
  2. Root-cause device policy (common.py): _module_buffer_device() places a numpy → buffer assignment on the module's current device (existing same-named buffer, else any parameter/buffer, else env.DEVICE), matching the min_nbor_dist setter. Folded bias_atom_e / vacuum_table therefore stay on CPU with the rest of the model after Module.to("cpu"), so a second to("cpu") hack is unnecessary.
  3. Tests: unit coverage that numpy buffer assignment after .to("cpu") keeps CPU residency even when env.DEVICE is patched to meta, plus a fold residency check on a small DPA4 vacuum-ref model.

Files

Path Change
deepmd/pt_expt/utils/serialization.py to("cpu") before fold
deepmd/pt_expt/common.py _module_buffer_device for numpy buffer setattr
source/tests/pt_expt/utils/test_common.py setattr device residency
source/tests/pt_expt/model/test_dpa4_vacuum_ref.py fold keeps CPU residency

Validation

Ran on box (CPU-only)

  • python -m pytest source/tests/pt_expt/utils/test_common.py — 7 passed (incl. test_dpmodel_setattr_keeps_module_device)
  • python -m pytest source/tests/pt_expt/model/test_dpa4_vacuum_ref.py::test_fold_vacuum_reference_keeps_cpu_residency source/tests/pt_expt/model/test_dpa4_vacuum_ref.py::test_fold_reproduces_the_referenced_model — 2 passed

Not verified here

  • Full CUDA freeze of a DPA4C Nano / large vacuum_ref checkpoint (dp --pt-expt freeze … --lower-kind graph) and numerical equivalence of the resulting .pt2 (issue environment; this box is CPU-only)
  • Host RSS during CPU vacuum-descriptor evaluation on a large type map (noted in the issue as a separate memory concern)

Related

Fixes #6076

Summary by CodeRabbit

  • Bug Fixes
    • Model exports now keep parameters and buffers on the CPU while vacuum-reference data is folded, preserving CPU residency.
    • Assigning NumPy arrays to modules now respects the module’s existing device instead of unexpectedly placing the arrays on the default device.

Freezing with vacuum_ref ran fold_vacuum_reference before moving the
model to the CPU tracing device, so vacuum_descriptor could OOM on CUDA.
Moving to CPU first avoids that allocation on the GPU.

fold_vacuum_reference replaces bias_atom_e (and may set vacuum_table)
through numpy assignment. dpmodel_setattr previously always placed those
buffers on env.DEVICE, so a fold after Module.to("cpu") pulled tensors
back to CUDA and broke FakeTensor tracing. Place new buffers on the
module's current device instead.
@coderabbitai

coderabbitai Bot commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 5d980a03-c4b8-4c4c-8fc8-543d867f5d55
📥 Commits

Reviewing files that changed from the base of the PR and between 025f4f6 and f083124.

📒 Files selected for processing: 4
  • deepmd/pt_expt/common.py
  • deepmd/pt_expt/utils/serialization.py
  • source/tests/pt_expt/model/test_dpa4_vacuum_ref.py
  • source/tests/pt_expt/utils/test_common.py

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.

📝 Walkthrough

NumPy-array buffer assignments now use the module’s current device when available. Export moves the model to CPU before folding the vacuum reference. Regression tests check buffer assignment and model residency during folding.

Changes: CPU-resident model handling

Layer / File(s) Summary
Select the module device for assigned buffers
deepmd/pt_expt/common.py, source/tests/pt_expt/utils/test_common.py
dpmodel_setattr selects a device from the existing same-name buffer, first parameter, first non-None buffer, or env.DEVICE. The regression test checks that an assigned NumPy array remains on CPU when env.DEVICE is meta.
Fold the vacuum reference on CPU
deepmd/pt_expt/utils/serialization.py, source/tests/pt_expt/model/test_dpa4_vacuum_ref.py
Export moves the model to CPU before folding the vacuum reference. The regression test checks that folding disables vacuum_ref and keeps parameters and non-None buffers on CPU.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: wanghan-iapcm, outisli

Merge Risk: ⚪ Minimal · up to f0831

Vacuum-reference folding during export now stays on the CPU tracing device, which avoids the reported CUDA out-of-memory and device-mismatch failures. Regression tests cover the new behavior. No actionable merge-blocking risk was found.

Pre-merge checks: Passed 5
Passed 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 and concisely describes the primary change: folding the vacuum reference on the CPU tracing device.
Linked Issues check Passed For directly linked issue #6076, _trace_and_export_impl now calls model.to("cpu") before model.fold_vacuum_reference(). This prevents vacuum-reference evaluation on CUDA during export. `module…
Out of Scope Changes check Passed The changed implementation and tests stay within issue #6076. The serialization change controls the device used for vacuum-reference folding. The common helper fixes the related NumPy-to-buffer device…
Docstring Coverage Passed Docstring coverage is 81.82% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 11 functions across 4 files.
✨ Finishing Touches: 💡 1
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

def __init__(self) -> None:
self.stat = np.zeros(3, dtype=np.float64)

def call(self, x: np.ndarray) -> np.ndarray:

OutisLi commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator

Superseded by #6080, which retains this PR's buffer-device fix and additionally removes DPA4C's dense vacuum-reference allocation and preserves CUDA-target deployment kernels while folding on CPU. Closing this PR in favor of the combined fix; validation details and limitations are recorded in #6080.

@OutisLi OutisLi closed this Oct 15, 2026
@codecov

codecov Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 77.60%. Comparing base (025f4f6) to head (f083124).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6079      +/-   ##
==========================================
- Coverage   77.85%   77.60%   -0.25%     
==========================================
  Files        1170     1170              
  Lines      140914   140925      +11     
  Branches     5056     5056              
==========================================
- Hits       109705   109364     -341     
- Misses      29325    29679     +354     
+ Partials     1884     1882       -2     

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

yckbz pushed a commit to yckbz/deepmd-kit that referenced this pull request Oct 19, 2026
…els (deepmodeling#6080)

## Summary

Fixes deepmodeling#6076 and supersedes deepmodeling#6079, retaining its module-local NumPy
buffer-device fix and CPU-before-fold export ordering.

This addresses both excessive vacuum-reference allocation and a
CUDA-target DPA4 export regression introduced by moving the eager fold
to CPU.

## Changes

- Evaluate DPA4/DPA4C isolated references using one node per type and
two masked guard edges, instead of sizing a dense neighbor list from
`get_nnei()`. DPA4C's `[999999]` graph sentinel must not become almost
one million padded neighbors per type.
- Introduce an explicit `supports_graph_vacuum()` capability. DPA4/DPA4C
opt in; legacy DPA1/DPA2 reference semantics and explicit graph-disable
routing remain unchanged. Preserve type, charge and native-spin
conditioning.
- During export, temporarily reconstruct only descriptors requiring
references on the CPU with reference kernels. Fold under
`torch.no_grad()`, retaining bias/reference tables on the original
fittings, then restore deployment descriptors and their optimized
bindings before fake tracing. Runtime graph-disable state and
environment settings are restored, including failure paths.
- Retain deepmodeling#6079's device-selection fix for NumPy buffer assignments,
including newly materialized nonpersistent reference tables.

Simply moving a CUDA-bound float32 DPA4 model to CPU does not unbind its
CUDA-only operators. Conversely, disabling deployment kernels throughout
tracing would silently lose acceleration. The scoped
reference-descriptor context avoids both problems without reconstructing
the whole folded model or losing `vacuum_table` through serialization.

## Regression coverage

- Allocation guards prohibit querying dense capacity or calling the
dense adapter for graph references, including sentinel values 999999 and
1999999.
- Float32/float64 DPA4/DPA4C, charge/native-spin combinations,
empty/masked edges, and nonzero learned conditioning.
- Nonzero-mean DPA1 legacy reference energies and disabled graph
lowering.
- Export order, CUDA-only operator dispatch without a CPU
implementation, preservation of the optimized operator in fake tracing,
fparam/aparam reference tables, restoration after failures, and
no-reference fast exit.

## Validation

- **40 passed**: new graph-reference tests, new export-reference tests,
and common wrapper tests, using current Python source and a freshly
rebuilt CPU PyTorch extension (MPI disabled because this host lacks MPI
headers; TensorFlow build disabled).
- **44 passed**: graph-reference tests plus existing dpmodel vacuum and
capability coverage.
- **36 passed**: broader export/kernel-policy/vacuum suite, including
actual CPU `.pt2`/`.pte` compilation, fparam `.pt2`, and archive
reserialization/refreezing. This run used the existing installed CPU
native library with current Python source.
- Additional independent checks covered mixed DPA4+DPA4C linear and
native-spin models, nonpersistent tables and restoration of original
descriptor objects.
- Repository-wide Ruff check/format and `git diff --check` passed.

### Memory sanity check

An untrained default-size DPA4C descriptor with the bundled 89-element
OMat24 type map, float64 and charge/native-spin conditioning, retained
its actual `get_nnei() == 999999`. Vacuum evaluation produced finite
`(89, 207)` output in approximately 0.01–0.03 s, with about 34 MiB
incremental process peak RSS. This is a CPU sanity check, not a
benchmark of the reporter's pretrained checkpoint or a directly
comparable speedup claim.

### Not verified

- Real CUDA kernel execution and CUDA-target AOT compilation on GPU
hardware.
- Full large pretrained-checkpoint GPU/host peak-memory benchmarking.

The CPU dispatch regression uses an operator without a CPU
implementation and checks fake-trace preservation; it is not presented
as a GPU integration test.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Vacuum-reference evaluation now supports graph-based DPA4 and DPA4C
models, including when dense neighbor capacity is unavailable.
Descriptors without graph-based vacuum support continue to use dense
evaluation.
* **Bug Fixes**
* Improved consistency of vacuum-reference folding during model export,
including preserving model settings and keeping folding on the CPU.
* Corrected buffer assignment so arrays follow the model’s device when
possible, rather than always using the default device.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: OutisLi_Bot <198997215+OutisLi-Bot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG][pt_expt][DPA4C] Freezing with vacuum_ref=True causes CUDA OOM during vacuum-reference folding

4 participants