Skip to content

fix(pt): allow concurrent stat cache reads#5773

Queued
OutisLi wants to merge 7 commits into
deepmodeling:masterfrom
OutisLi:pr/stat
Queued

fix(pt): allow concurrent stat cache reads#5773
OutisLi wants to merge 7 commits into
deepmodeling:masterfrom
OutisLi:pr/stat

Conversation

@OutisLi

@OutisLi OutisLi commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

What

  • Add the PyTorch-only training.stat_file_mode option with update (default) and read-only read modes.
  • Centralize statistics-cache path preparation, require an existing cache in read mode, and open it without write access.
  • Guard HDF5 cache save and directory-creation operations against read-only paths.
  • Update the DPA4 water example and add coverage for default/update behavior, read-only access, missing caches, and configuration validation.

Why

Multiple PyTorch training processes may need to consume the same completed statistics cache concurrently. Opening the cache in update mode permits writes and can cause HDF5 contention or accidental mutation. A read-only mode lets workers share an existing cache safely while preserving the current writable behavior for cache creation and updates.

Impact

  • Omitting stat_file_mode keeps the existing update behavior.
  • stat_file_mode: "read" requires a complete existing cache and rejects writes or directory creation.
  • The option is exposed through input validation and is supported by the PyTorch training path.

Checks

  • Added unit coverage for writable defaults, read-only cache access, missing-cache errors, and configuration normalization.
  • Tests were not rerun in this checkout per request.

Summary by CodeRabbit

  • New Features
    • Added stat_file_mode (update/read) to control on-disk statistics cache access, including rank-aware handling in multi-process training.
  • Bug Fixes
    • Enforced strict read-only HDF5 cache protections (write attempts now fail).
    • In read mode, missing or incomplete caches now fail fast with clearer errors; required cache items are validated before use.
    • Improved consistency of environment-matrix stats loading by delegating cache vs compute behavior.
    • Adjusted bias-stat computation to respect configured bias keys.
  • Documentation
    • Updated the water example config to set stat_file_mode: "update".
  • Tests
    • Added coverage for cache modes, completeness validation, read-only behavior, and config validation.

Add explicit read-only access while preserving update mode by default.
@OutisLi
OutisLi requested a review from njzjz July 12, 2026 09:38
@OutisLi
OutisLi marked this pull request as ready for review July 12, 2026 09:38
Copilot AI review requested due to automatic review settings July 12, 2026 09:38
@dosubot dosubot Bot added the enhancement label Jul 12, 2026

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

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: de54483e-1e39-481e-b9e6-2d1b9d955d43

📥 Commits

Reviewing files that changed from the base of the PR and between d62b215 and 2626abf.

📒 Files selected for processing (10)
  • deepmd/dpmodel/utils/env_mat_stat.py
  • deepmd/pt/model/descriptor/repflows.py
  • deepmd/pt/model/descriptor/repformers.py
  • deepmd/pt/model/descriptor/se_a.py
  • deepmd/pt/model/descriptor/se_atten.py
  • deepmd/pt/model/descriptor/se_r.py
  • deepmd/pt/model/descriptor/se_t.py
  • deepmd/pt/model/descriptor/se_t_tebd.py
  • deepmd/utils/env_mat_stat.py
  • source/tests/pt/test_stat_file_mode.py

📝 Walkthrough

Walkthrough

Training now supports stat_file_mode values of update and read. PT rank 0 prepares the statistics cache accordingly, read-only paths reject writes, required cache entries are validated, output-bias statistics use configured keys, descriptor loading centralizes cache decisions, and tests cover the new flow.

Changes

Statistics cache mode handling

Layer / File(s) Summary
Statistics cache mode configuration
deepmd/utils/argcheck.py, examples/water/dpa4/input.json
Documents and validates stat_file_mode, defaults it to update, requires stat_file for read mode, and updates the example configuration.
Rank-aware cache preparation
deepmd/pt/entrypoints/main.py
Prepares statistics paths in read or update mode for rank 0 and leaves nonzero ranks without a cache path.
Read-only path enforcement
deepmd/utils/path.py, source/tests/common/test_path.py
Blocks dataset writes and group creation on read-only HDF5 paths and tests the guards.
Statistics completeness validation
deepmd/dpmodel/utils/stat.py, deepmd/dpmodel/fitting/general_fitting.py, deepmd/pt/model/atomic_model/sezm_atomic_model.py, deepmd/pt/model/task/fitting.py, deepmd/pt/utils/stat.py
Requires expected statistics entries in read-only caches before restoration or fitting-stat computation.
Descriptor cache loading
deepmd/utils/env_mat_stat.py, deepmd/dpmodel/utils/env_mat_stat.py, deepmd/pt/model/descriptor/*
Declares required environment-stat keys and centralizes lazy sampling, cache validation, loading, and computation.
Output bias statistic selection
deepmd/pt/model/atomic_model/base_atomic_model.py
Restricts output-statistic computation to self.bias_keys for both bias-adjustment modes.
Cache mode behavior tests
source/tests/pt/test_stat_file_mode.py, source/tests/common/dpmodel/test_observed_type.py, source/tests/pt/test_observed_type.py
Tests writable defaults, read-only loading, missing and incomplete caches, multiple readers, configuration validation, and mode-dependent observed-type restoration.

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

Sequence Diagram(s)

sequenceDiagram
  participant TrainingConfig
  participant PTEntrypoint
  participant StatisticsCache
  participant DescriptorStats
  participant HDF5
  TrainingConfig->>PTEntrypoint: provide stat_file and stat_file_mode
  PTEntrypoint->>StatisticsCache: prepare cache on rank 0
  StatisticsCache->>HDF5: open in read or update mode
  HDF5-->>StatisticsCache: return cache path
  StatisticsCache->>DescriptorStats: load_or_compute_stats with cache and data
  DescriptorStats->>HDF5: validate required items
  HDF5-->>DescriptorStats: restore stats or report missing items
Loading

Possibly related PRs

Suggested labels: bug

Suggested reviewers: njzjz

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 54.05% 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 captures the main change: adding read-only stat cache support for concurrent reads.
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.
✨ 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 (1)
source/tests/pt/test_stat_file_mode.py (1)

85-97: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider adding tests for _prepare_stat_file_path edge cases.

The tests cover the main scenarios well. Two edge cases in _prepare_stat_file_path that lack direct test coverage:

  1. Invalid stat_file_mode (lines 130-134 in main.py) — raises ValueError with a descriptive message
  2. stat_file=None with stat_file_mode="read" (lines 135-138 in main.py) — raises ValueError

These are caught upstream by argcheck.py validation, so the risk is low, but direct unit tests would protect against regressions if the function is called from new call sites.

🤖 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/pt/test_stat_file_mode.py` around lines 85 - 97, Extend
test_stat_file_mode_configuration_validation with direct unit coverage for
_prepare_stat_file_path: assert invalid stat_file_mode raises ValueError with a
descriptive message, and assert stat_file=None with stat_file_mode="read" also
raises ValueError. Use the existing configuration/test fixtures and preserve the
current normalization assertions.
🤖 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/pt/test_stat_file_mode.py`:
- Around line 85-97: Extend test_stat_file_mode_configuration_validation with
direct unit coverage for _prepare_stat_file_path: assert invalid stat_file_mode
raises ValueError with a descriptive message, and assert stat_file=None with
stat_file_mode="read" also raises ValueError. Use the existing
configuration/test fixtures and preserve the current normalization assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9de43e18-1401-4328-97ca-38177d11aaaa

📥 Commits

Reviewing files that changed from the base of the PR and between 98c3ab0 and c95b4d6.

📒 Files selected for processing (5)
  • deepmd/pt/entrypoints/main.py
  • deepmd/utils/argcheck.py
  • deepmd/utils/path.py
  • examples/water/dpa4/input.json
  • source/tests/pt/test_stat_file_mode.py

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

Opening a completed HDF5 statistics cache with mode r is the right direction, but the current implementation does not yet provide the documented load-only semantics.

Cache completeness depends on model-specific type-map suffixes, descriptor hashes, and required statistic keys, so checking only the top-level path in the entrypoint cannot establish that the cache is complete. I recommend making the statistics-loading layer distinguish these cases explicitly: load an existing item; compute and save a missing item only when the path is writable; and fail immediately with the missing statistic name when the path is read-only. This avoids sampling data before an eventual generic write error.

The cache-opening helper is also infrastructure rather than entrypoint logic. A focused module such as deepmd/utils/stat_file.py would keep creation/opening policy separate from main.py, while the generic read-only guards should remain in deepmd/utils/path.py and be covered in source/tests/common/test_path.py.

The CodeRabbit edge-test suggestion is reasonable but secondary. In particular, its statement that both cases are caught upstream is not correct for stat_file_mode="read" without stat_file.

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

Comment thread deepmd/pt/entrypoints/main.py
Comment thread deepmd/utils/argcheck.py
Comment thread source/tests/pt/test_stat_file_mode.py
@codecov

codecov Bot commented Jul 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.66667% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.34%. Comparing base (6c3b985) to head (2626abf).

Files with missing lines Patch % Lines
deepmd/pt/entrypoints/main.py 94.73% 1 Missing ⚠️
deepmd/pt/model/descriptor/repflows.py 0.00% 1 Missing ⚠️
deepmd/pt/model/descriptor/se_t.py 0.00% 1 Missing ⚠️
deepmd/utils/env_mat_stat.py 93.75% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5773      +/-   ##
==========================================
- Coverage   78.58%   78.34%   -0.24%     
==========================================
  Files        1050     1050              
  Lines      120637   120656      +19     
  Branches     4356     4354       -2     
==========================================
- Hits        94801    94528     -273     
- Misses      24278    24568     +290     
- Partials     1558     1560       +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.

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

Caution

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

⚠️ Outside diff range comments (1)
deepmd/dpmodel/utils/stat.py (1)

91-103: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Make observed_type optional in read-only restores

_collect_and_set_observed_type skips _save_observed_type_to_file whenever preset_observed_type is provided, so caches created through that path can legitimately lack observed_type. In read mode, the unconditional _require_stat_file_items(..., ["observed_type"]) turns that into a hard FileNotFoundError even though the rest of the cache is usable. Relax this guard or fall back to recomputing when the entry is missing.

🤖 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 `@deepmd/dpmodel/utils/stat.py` around lines 91 - 103, The read-only restore
path should tolerate caches without an observed_type entry. Update
_restore_observed_type_from_file to avoid unconditionally requiring
observed_type, return the stored values when the file exists, and return None
when it is absent so callers can recompute them.
🤖 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.

Outside diff comments:
In `@deepmd/dpmodel/utils/stat.py`:
- Around line 91-103: The read-only restore path should tolerate caches without
an observed_type entry. Update _restore_observed_type_from_file to avoid
unconditionally requiring observed_type, return the stored values when the file
exists, and return None when it is absent so callers can recompute them.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 96803061-6577-4f51-9f78-1e6cc4e48250

📥 Commits

Reviewing files that changed from the base of the PR and between c95b4d6 and 6864259.

📒 Files selected for processing (8)
  • deepmd/dpmodel/fitting/general_fitting.py
  • deepmd/dpmodel/utils/stat.py
  • deepmd/pt/model/atomic_model/sezm_atomic_model.py
  • deepmd/pt/model/task/fitting.py
  • deepmd/pt/utils/stat.py
  • deepmd/utils/argcheck.py
  • source/tests/common/test_path.py
  • source/tests/pt/test_stat_file_mode.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • deepmd/utils/argcheck.py

OutisLi added 2 commits July 15, 2026 22:10
Keep update-mode fallback coverage while asserting that read-only caches reject missing observed-type statistics.

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

High — cannot reuse a regular energy-model stat cache produced by update

The new read-only preflight unconditionally requires bias_atom_<key> and std_atom_<key> for every supplied output key. However, the real PT atomic-model path calls:

keys=list(self.atomic_output_def().keys())

atomic_output_def() includes runtime-derived outputs in addition to fitting outputs, including mask. A standard energy model therefore has at least:

["energy", "mask"]

The update path filters keys against labels in the training data. Since mask has no label, the cache only gets:

bias_atom_energy
std_atom_energy

not bias_atom_mask / std_atom_mask.

Reusing that same model and cache with stat_file_mode: read then fails before sampling with FileNotFoundError for the missing mask entries. That breaks the primary workflow this PR enables: one process writes the cache with update, then multiple training processes share it with read.

Please make BaseAtomicModel.change_out_bias() pass only the outputs that need persistent bias/std statistics—for example:

keys=self.bias_keys
# or: list(self.fitting_output_def().keys())

This keeps derived outputs such as mask out of the read-cache completeness check.

Please also add an end-to-end regression test that:

  1. writes a directory/HDF5 stat cache using a real PT energy model in update mode;
  2. reloads it with the same model in read mode; and
  3. verifies loading succeeds without invoking the sampler.

The current tests call compute_output_stats() directly with keys=["energy"], so they bypass the atomic_output_def() path that injects mask.

— OpenClaw (model: custom-chat-jinzhezeng-group/gpt-5.6-terra)

@OutisLi

OutisLi commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed in d62b215:

  • BaseAtomicModel.change_out_bias() now uses self.bias_keys, so runtime-derived outputs such as mask are excluded from persistent cache requirements in both bias-adjustment modes.
  • Added an end-to-end regression test that writes an HDF5 cache in update mode with a real PT energy model, reloads it in read mode with a fresh model, and verifies that the sampler is never invoked.

Validation:

  • source/tests/pt/test_stat_file_mode.py: 8 passed
  • output-bias consistency test: passed
  • change-by-statistic bias test: passed
  • Ruff and pre-commit hooks: passed

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

High — read mode still samples data when descriptor statistics are missing

_prepare_stat_file_path() verifies only that the cache root exists. Descriptor statistics are stored one level deeper under the type-map group and the descriptor-specific EnvMatStatSe.get_hash() group. If a cache exists but that hash group is absent—for example, because it was generated for different descriptor parameters or is incomplete—the descriptor's compute_input_stats() treats the path as a cache miss, calls the lazy sampler, and then tries to save the result. The save is rejected only at mkdir() with ValueError("Cannot mkdir to read-only path").

That violates the advertised read-mode semantics: an incomplete cache should fail before sampling, rather than performing an expensive data pass and surfacing a generic write error. Please make the descriptor-stat loading path distinguish a read-only miss and raise a clear FileNotFoundError before invoking merged() (preferably centrally in EnvMatStat.load_or_compute_stats, while also validating the required datasets).

Please add a regression test using a read-only cache that contains the type-map root but omits the descriptor-hash statistics group. Its sampler should fail if called, and the expected result should be FileNotFoundError with no cache mutation.

— OpenClaw (model: custom-chat-jinzhezeng-group/gpt-5.6-terra)

@OutisLi
OutisLi requested a review from njzjz-bot July 17, 2026 03:40

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

Reviewed the latest head (2626abf). The prior read-only cache issues are addressed:

  • output-stat validation now scopes required entries to persistent bias keys, excluding derived outputs such as mask;
  • missing or partial descriptor-stat groups fail before the lazy sampler runs;
  • the update → read path is covered by end-to-end HDF5 regression tests.

The implementation and tests look good to me.

— OpenClaw (model: custom-chat-jinzhezeng-group/gpt-5.6-terra)

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

  • @iProzd — 150 commits on changed files (deepmd/dpmodel/fitting/general_fitting.py, deepmd/dpmodel/utils/stat.py, deepmd/pt/entrypoints/main.py, deepmd/pt/model/atomic_model/base_atomic_model.py, deepmd/pt/model/descriptor/repflows.py, deepmd/pt/model/descriptor/repformers.py, deepmd/pt/model/descriptor/se_a.py, deepmd/pt/model/descriptor/se_atten.py, deepmd/pt/model/descriptor/se_r.py, deepmd/pt/model/descriptor/se_t.py, deepmd/pt/model/descriptor/se_t_tebd.py, deepmd/pt/model/task/fitting.py, deepmd/pt/utils/stat.py, deepmd/utils/argcheck.py, deepmd/utils/env_mat_stat.py, deepmd/utils/path.py, source/tests/common/dpmodel/test_observed_type.py, source/tests/pt/test_observed_type.py).
  • @wanghan-iapcm — 74 commits on changed files; 51 reviews on exact changed files (deepmd/dpmodel/fitting/general_fitting.py, deepmd/dpmodel/utils/env_mat_stat.py, deepmd/dpmodel/utils/stat.py, deepmd/pt/entrypoints/main.py, deepmd/pt/model/atomic_model/base_atomic_model.py, deepmd/pt/model/atomic_model/sezm_atomic_model.py, deepmd/pt/model/descriptor/repflows.py, deepmd/pt/model/descriptor/repformers.py, deepmd/pt/model/descriptor/se_a.py, deepmd/pt/model/descriptor/se_atten.py, deepmd/pt/model/descriptor/se_r.py, deepmd/pt/model/descriptor/se_t.py, deepmd/pt/model/descriptor/se_t_tebd.py, deepmd/pt/model/task/fitting.py, deepmd/pt/utils/stat.py, deepmd/utils/argcheck.py, examples/water/dpa4/input.json).

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 iProzd and wanghan-iapcm July 18, 2026 07:25
@OutisLi
OutisLi added this pull request to the merge queue Jul 18, 2026
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.

4 participants