Skip to content

fix(c++,pt-expt): substitute default_fparam in .pt2 compute#5343

Merged
njzjz merged 3 commits intodeepmodeling:masterfrom
wanghan-iapcm:feat-pt-expt-default-fparam
Mar 28, 2026
Merged

fix(c++,pt-expt): substitute default_fparam in .pt2 compute#5343
njzjz merged 3 commits intodeepmodeling:masterfrom
wanghan-iapcm:feat-pt-expt-default-fparam

Conversation

@wanghan-iapcm
Copy link
Copy Markdown
Collaborator

@wanghan-iapcm wanghan-iapcm commented Mar 26, 2026

Summary

  • Store default_fparam values in .pt2 metadata so C++ can read them at init time
  • In DeepPotPTExpt::compute(), substitute stored default values when caller passes empty fparam on a model with has_default_fparam=true
  • Add C++ tests for empty-fparam, explicit-fparam, and LAMMPS-nlist paths using fparam_aparam_default.pt2

Test plan

  • runUnitTests_cc --gtest_filter="*DefaultFParam*PtExpt*" — 10 tests pass (double + float, attrs/empty/explicit/lmp_nlist)
  • runUnitTests_cc --gtest_filter="*PtExpt*" — all 100 PtExpt tests pass, no regressions
  • Verified fparam_aparam_default.pt2 metadata contains default_fparam: [0.25852028]
  • CI: regenerate model files via gen_fparam_aparam.py before C++ tests

Summary by CodeRabbit

  • New Features
    • Models now include and export a default feature-parameter vector so inference will use pre-configured defaults when explicit parameters are omitted.
  • Bug Fixes
    • Stricter validation: missing or size-mismatched default parameters now emit warnings or errors instead of proceeding silently.
  • Tests
    • Added end-to-end inference tests validating empty vs. explicit feature-parameter behavior and verifying energies, forces, and virials.
  • Chores
    • Updated test-data generation to produce and commit reference values for the default-parameter model.

…am is empty

DeepPotPTExpt::compute() crashed when fparam was empty on a model with
has_default_fparam=true because the .pt2 model expects a concrete
[1, dim_fparam] tensor. Store default_fparam values in .pt2 metadata
and substitute them in C++ when the caller passes an empty fparam vector.
Comment thread source/tests/infer/gen_fparam_aparam.py Fixed
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 263de1fdc2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread source/api_cc/src/DeepPotPTExpt.cc Outdated
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 893879aa-7064-44d4-818f-eccb403a3d4a

📥 Commits

Reviewing files that changed from the base of the PR and between e02936d and 3e779f1.

📒 Files selected for processing (2)
  • source/api_cc/src/DeepPotPTExpt.cc
  • source/tests/infer/gen_fparam_aparam.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • source/tests/infer/gen_fparam_aparam.py

📝 Walkthrough

Walkthrough

Exports a model-level default_fparam in serialized metadata, stores the default vector in the C++ backend on model init, and falls back to that vector during inference when caller fparam is empty. Tests and reference-generation were added/updated.

Changes

Cohort / File(s) Summary
Metadata Serialization
deepmd/pt_expt/utils/serialization.py
Serialize default_fparam from model.get_default_fparam() into model metadata so it appears in model_def_script.json for .pte/.pt2 exports.
C++ Backend Header
source/api_cc/include/DeepPotPTExpt.h
Add private std::vector<double> default_fparam_ to hold the default fparam vector alongside existing flags.
C++ Backend Init & Compute
source/api_cc/src/DeepPotPTExpt.cc
On init(), validate/load metadata["default_fparam"] into default_fparam_ when has_default_fparam_ is true; in compute(), if caller fparam is empty and default_fparam_ exists, construct fparam_tensor from default_fparam_ (via torch::from_blob(...).clone()); throw if has_default_fparam_ is true but vector is empty.
Tests — removed
source/api_cc/tests/test_deeppot_a_fparam_aparam_ptexpt.cc
Removed typed test fixture that asserted .pt2 model had a default fparam; other non-default tests retained.
Tests — added
source/api_cc/tests/test_deeppot_default_fparam_ptexpt.cc
New typed test fixture and cases validating default-fparam usage (empty input), explicit fparam, and neighbor-list API; compares energies/forces/virials to generated references.
Reference generation
source/tests/infer/gen_fparam_aparam.py
Add generation path for .pt2 model with default_fparam, compute and emit C++-formatted expected energies/forces/virials for tests; note .pth is committed and not regenerated.

Sequence Diagram

sequenceDiagram
    participant Export as Exporter
    participant Serializer as Serializer
    participant Model as SavedModel(.pt2/.pte)
    participant CPPInit as C++Backend:init
    participant CPPCompute as C++Backend:compute
    participant User as UserCode

    Export->>Serializer: call model.get_default_fparam()
    Serializer->>Model: write metadata["default_fparam"]
    
    User->>CPPInit: load model
    CPPInit->>Model: read metadata
    CPPInit->>CPPInit: set has_default_fparam_ / default_fparam_
    
    User->>CPPCompute: compute(coords, aparam, fparam=empty)
    alt caller-provided fparam non-empty
        CPPCompute->>CPPCompute: use provided fparam tensor
    else default_fparam_ exists
        CPPCompute->>CPPCompute: construct fparam_tensor from default_fparam_ (torch::from_blob(...).clone())
    else
        CPPCompute->>CPPCompute: use empty fparam tensor / error if expected
    end
    CPPCompute->>User: return energies / forces / virials
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

bug

Suggested reviewers

  • iProzd
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: storing and using default_fparam values in .pt2 models during C++ compute operations in the pt-expt component.

✏️ 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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@deepmd/pt_expt/utils/serialization.py`:
- Around line 197-198: The metadata currently stores model.get_default_fparam()
(a torch.Tensor for the PT backend) which is not JSON-serializable; change the
code that builds metadata (the dict keys "has_default_fparam" /
"default_fparam") to convert the tensor to a plain Python list (e.g., call
.tolist() or otherwise map it to serializable types) when
model.has_default_fparam() is True so that subsequent json.dumps(...) calls
(used in the .pte/.pt2 export paths) succeed; update usage around the
model.get_default_fparam() call to store a list instead of a tensor.

In `@source/api_cc/src/DeepPotPTExpt.cc`:
- Around line 639-650: The code currently trusts metadata["default_fparam"]
contents; instead validate in init() that metadata contains a JSON array, that
each element is numeric (not null/string/object) and that its length matches
dfparam (or dfparam_.size()) before populating default_fparam_. If any check
fails, throw a deepmd::deepmd_exception with a clear message; only after
validation iterate over metadata["default_fparam"].as_array() and push_back
as_double() into default_fparam_. Use the existing symbols has_default_fparam_,
metadata, default_fparam_, dfparam (or dfparam_), init(), and compute() to
locate and implement these checks.

In `@source/api_cc/tests/test_deeppot_default_fparam_ptexpt.cc`:
- Around line 82-86: The test should skip when the pt2 experiment support itself
is unavailable, not just when BUILD_PYTORCH is off: in SetUp() keep the existing
BUILD_PYTORCH guard and then detect pt2 support before calling dp.init(...pt2);
if DeepPotPTExpt exposes a runtime query (e.g., dp.supports_pt2() or
dp.has_pt2_support()) call that and GTEST_SKIP() when false; if support is only
exposed as a compile-time macro in DeepPotPTExpt.h, check that macro (e.g.,
`#ifndef` DEEP_POT_PT2_ENABLED) and skip similarly—ensure the
dp.init("../../tests/infer/fparam_aparam_default.pt2") call is only reached when
pt2 support is present.

In `@source/tests/infer/gen_fparam_aparam.py`:
- Around line 207-214: The unused local binding v_d from the call to
dp_default.eval (e_d, f_d, v_d, ae_d, av_d = dp_default.eval(...)) triggers Ruff
RUF059; rename it to a throwaway name such as _v_d or _ to indicate it is
intentionally unused (e.g., change v_d to _v_d in the assignment) so the linter
stops flagging it while leaving the rest of the unpacking and the
dp_default.eval call (coord, box, atype, fparam=fparam_val, aparam=aparam_val,
atomic=True) unchanged.
🪄 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: dc8e997f-8806-44eb-bd0f-1b12af631a88

📥 Commits

Reviewing files that changed from the base of the PR and between e97967b and 263de1f.

📒 Files selected for processing (6)
  • deepmd/pt_expt/utils/serialization.py
  • source/api_cc/include/DeepPotPTExpt.h
  • source/api_cc/src/DeepPotPTExpt.cc
  • source/api_cc/tests/test_deeppot_a_fparam_aparam_ptexpt.cc
  • source/api_cc/tests/test_deeppot_default_fparam_ptexpt.cc
  • source/tests/infer/gen_fparam_aparam.py
💤 Files with no reviewable changes (1)
  • source/api_cc/tests/test_deeppot_a_fparam_aparam_ptexpt.cc

Comment thread deepmd/pt_expt/utils/serialization.py
Comment thread source/api_cc/src/DeepPotPTExpt.cc
Comment thread source/api_cc/tests/test_deeppot_default_fparam_ptexpt.cc
Comment thread source/tests/infer/gen_fparam_aparam.py Outdated
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 26, 2026

Codecov Report

❌ Patch coverage is 73.77049% with 32 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.27%. Comparing base (e97967b) to head (3e779f1).
⚠️ Report is 5 commits behind head on master.

Files with missing lines Patch % Lines
...api_cc/tests/test_deeppot_default_fparam_ptexpt.cc 81.05% 18 Missing ⚠️
source/api_cc/src/DeepPotPTExpt.cc 48.14% 7 Missing and 7 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5343      +/-   ##
==========================================
- Coverage   82.28%   82.27%   -0.01%     
==========================================
  Files         797      798       +1     
  Lines       82100    82214     +114     
  Branches     4003     4020      +17     
==========================================
+ Hits        67557    67644      +87     
- Misses      13336    13355      +19     
- Partials     1207     1215       +8     

☔ View full report in Codecov by Sentry.
📢 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 .pth default_fparam model is committed to git with its own expected
values in test_deeppot_default_fparam_pt.cc. Regenerating it from
gen_fparam_aparam.py produces different weights, breaking those tests.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
source/tests/infer/gen_fparam_aparam.py (1)

202-209: ⚠️ Potential issue | 🟡 Minor

Rename unused unpacked v_d to satisfy Ruff (RUF059).

At Line 202, v_d is unpacked but never used, which will fail lint checks.

💡 Suggested fix
-    e_d, f_d, v_d, ae_d, av_d = dp_default.eval(
+    e_d, f_d, _v_d, ae_d, av_d = dp_default.eval(

As per coding guidelines, **/*.py: Always run ruff check . and ruff format . before committing changes or CI will fail.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/tests/infer/gen_fparam_aparam.py` around lines 202 - 209, The tuple
returned by dp_default.eval is unpacked into e_d, f_d, v_d, ae_d, av_d but v_d
is unused and triggers Ruff RUF059; update the unpack to ignore that value
(e.g., replace v_d with _ or _v_d) where dp_default.eval(...) is called so only
used symbols e_d, f_d, ae_d, av_d remain, then run ruff check/format to verify.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@source/tests/infer/gen_fparam_aparam.py`:
- Around line 202-209: The tuple returned by dp_default.eval is unpacked into
e_d, f_d, v_d, ae_d, av_d but v_d is unused and triggers Ruff RUF059; update the
unpack to ignore that value (e.g., replace v_d with _ or _v_d) where
dp_default.eval(...) is called so only used symbols e_d, f_d, ae_d, av_d remain,
then run ruff check/format to verify.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a6222669-d76f-4e13-b25f-eb13f13a5fba

📥 Commits

Reviewing files that changed from the base of the PR and between 263de1f and e02936d.

📒 Files selected for processing (1)
  • source/tests/infer/gen_fparam_aparam.py

- Warn at init for older .pt2 models missing default_fparam metadata;
  explicit fparam callers still work
- Throw clear error at compute time if fparam is empty and default
  values are unavailable (instead of cryptic torch shape mismatch)
- Validate default_fparam_ length matches dim_fparam at init
- Rename unused v_d to _v_d in gen script
@njzjz njzjz added this pull request to the merge queue Mar 28, 2026
Merged via the queue into deepmodeling:master with commit 206ace0 Mar 28, 2026
70 checks passed
wanghan-iapcm pushed a commit to wanghan-iapcm/deepmd-kit that referenced this pull request Mar 29, 2026
TestInferDeepPotDefaultFParamPtExpt was defined in both
test_deeppot_a_fparam_aparam_ptexpt.cc and
test_deeppot_default_fparam_ptexpt.cc (upstream deepmodeling#5343). Duplicate
class names in the same gtest binary cause undefined behavior.

Removed ours since upstream's version is strictly stronger: it checks
against absolute expected values (which implies self-consistency).
Kept NoDefaultFParamPtExpt (unique to our file).
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