feat(lammps): load selected dump frames efficiently - #1041
Conversation
Add f_idx-based sparse frame loading for LAMMPS dump trajectories while preserving requested order and duplicate indices. Stop scanning after the final requested frame and validate invalid selections explicitly. Coding-Agent: Codex Codex-Version: codex-cli 0.144.6 Model: gpt-5.6-sol Reasoning-Effort: xhigh
Merging this PR will not alter performance
|
|
Warning Review limit reached
Next review available in: 58 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughLAMMPS dump loading now supports selecting specific frame indices, including ordered duplicates, with input validation and early termination. The plugin forwards ChangesLAMMPS frame selection
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Caller
participant LAMMPSDumpFormat
participant load_file
participant DumpStream
Caller->>LAMMPSDumpFormat: request frames with f_idx
LAMMPSDumpFormat->>load_file: forward f_idx
load_file->>DumpStream: iterate complete dump frames
DumpStream-->>load_file: yield frames through last requested index
load_file-->>LAMMPSDumpFormat: return ordered selected lines
LAMMPSDumpFormat-->>Caller: construct selected system
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/test_lammps_dump_skipload.py (1)
87-93: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider adding a case for the
TypeErrorpath.Coverage here checks empty/negative/out-of-range but not
f_idxcontaining a non-integer element (e.g.f_idx=["a"]), which_normalize_frame_indicesis supposed to reject withTypeError.✅ Suggested addition
def test_invalid_frame_indices(self): with self.assertRaisesRegex(ValueError, "must not be empty"): dump.load_file(self.dump_file, f_idx=[]) with self.assertRaisesRegex(ValueError, "non-negative"): dump.load_file(self.dump_file, f_idx=[-1]) with self.assertRaisesRegex(IndexError, "out of range"): dump.load_file(self.dump_file, f_idx=[5]) + with self.assertRaisesRegex(TypeError, "only integers"): + dump.load_file(self.dump_file, f_idx=["a"])🤖 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 `@tests/test_lammps_dump_skipload.py` around lines 87 - 93, Add a TypeError assertion to test_invalid_frame_indices for a non-integer frame index such as f_idx=["a"], matching the rejection behavior of _normalize_frame_indices and checking the expected error message.
🤖 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 `@tests/test_lammps_dump_skipload.py`:
- Around line 87-93: Add a TypeError assertion to test_invalid_frame_indices for
a non-integer frame index such as f_idx=["a"], matching the rejection behavior
of _normalize_frame_indices and checking the expected error message.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 04b3650b-88e9-4513-a996-777a60b2d15e
📒 Files selected for processing (3)
dpdata/formats/lammps/dump.pydpdata/plugins/lammps.pytests/test_lammps_dump_skipload.py
Add the non-integer element case suggested in review, plus the bool rejection and the non-iterable scalar case, so both `TypeError` branches of `_normalize_frame_indices` are exercised. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1041 +/- ##
==========================================
+ Coverage 87.63% 87.65% +0.02%
==========================================
Files 90 90
Lines 9209 9257 +48
==========================================
+ Hits 8070 8114 +44
- Misses 1139 1143 +4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Addressed the review nitpick in b68b96d: |
wanghan-iapcm
left a comment
There was a problem hiding this comment.
The f_idx implementation itself is well done -- order/duplicate preservation, early termination, and the atom-id remap under selection all check out, and I confirmed System(f, f_idx=[1,2]) matches System(f).sub_system([1,2]) exactly. Two things I'd like addressed before merge. Note that CodeRabbit was rate limited on this PR and never actually reviewed it.
Ignore blank separators, clamp final-frame trailers, and strengthen the early-termination probe across read APIs. Coding-Agent: Codex Codex-Version: codex-cli 0.144.6 Model: gpt-5.6-sol Reasoning-Effort: xhigh
wanghan-iapcm
left a comment
There was a problem hiding this comment.
Both threads verified at ce9dfa7.
Blank-line regression: fixed and genuinely guarded. _iter_frames now skips blank separators and split_traj clamps each frame after its declared atom records. Base vs head:
| input | base 212ab49 |
head ce9dfa7 |
|---|---|---|
conf.5.dump |
5 frames | 5 frames |
| + trailing newline | 5 frames | 5 frames |
| blank line between frames | TypeError: 'NoneType' object is not subscriptable |
5 frames |
load_file(...) + ["# end of run"] |
5 x 11 lines | 5 x 11 lines |
test_blank_trailer_is_ignored_and_extra_text_is_clamped fails at the pre-fix head b68b96d (AssertionError: True is not false) and passes here, so it is a real regression test.
Early-stop probe: CountingStringIO now has teeth. I re-ran the two adversarial loaders I used to break the old one -- a full-file readline() loader with no early termination, and one that stops two frames late. Both now fail (1120 not less than or equal to 463 and 911 not less than or equal to 463); the real implementation passes. The probe backs the speedup claim properly.
Also spot-checked f_idx end to end: [0], [1], [4], [3,1], [1,1,0] and the bare int 2 all match sub_system exactly, and empty, negative, out-of-range, float, string, bool and np.bool_ mask inputs all raise the documented errors. Full suite at head is 2242 tests with the same 45 pre-existing optional-dependency errors as the base run.
Thanks for the thorough hardening.
Pre-apply the resolved LAMMPS dump parser so the upstream merge can recognize the overlapping deepmodeling#1041 changes. Agent: ChatGPT Model: GPT-5.6 Sol
Resolve dpdata/formats/lammps/dump.py by preserving deepmodeling#1041 sparse frame loading together with deepmodeling#1045 incomplete/commented-frame handling. Validation: py_compile and targeted synthetic parser tests. Agent: ChatGPT Model: GPT-5.6 Sol
Fixes #616. ## Problem Both symptoms in the issue reproduce on master, using `tests/poscars/conf.5.dump` as the source: ```python # last frame truncated mid-write IndexError: too many indices for array: array is 1-dimensional, but 2 were indexed # a "#####" comment line inside an ATOMS block ValueError: invalid literal for int() with base 10: '#####' ``` Neither message points at the frame that is actually damaged. ## Causes 1. `split_traj` measured the gap between the first two `ITEM: TIMESTEP` markers and reused that length for every frame. A truncated final frame — or a single extra line anywhere in the file — shifted every later frame out of alignment, so intact frames were corrupted too, not just the damaged one. 2. Every block consumer parses its lines as numbers, so a blank or `#` line inside a section aborted the whole read. 3. A frame missing a section, or short on atom lines, produced a ragged array several calls deeper, in `get_atype` / `safe_get_posi`. ## Changes - `split_traj` slices each frame at its own marker, so frame lengths may differ. - `_get_block` drops blank and `#` lines from the block it returns, which covers `get_atype`, `safe_get_posi`, `get_dumpbox`, `get_natoms`, and `get_spin` at once. - `system_data` checks each frame for its three required sections, a parsable atom count, three box-bound lines, and exactly that many atom lines with the column count the header declares. Frames that fail are reported and skipped: ``` UserWarning: incomplete frame 4 in the dump file (0 atom lines for 2 atoms); it is ignored ``` This mirrors how `vasp/outcar` already handles a damaged ionic step (`_IncompleteForceTableError`). A file with no usable frame raises `no complete frame found in the dump file`, and a file with no marker at all raises `not a LAMMPS dump file` instead of `TypeError: 'NoneType' object is not subscriptable`. The issue also asks for velocities and a `reorder=False` option. Those are feature requests rather than the reported crash, so they are left out of this PR. ## Validation - `tests/test_lammps_dump_incomplete.py`, 6 new cases: unchanged behaviour on an intact file, truncated last frame, truncated middle frame (asserting later frames keep their exact coordinates), comment lines, no usable frame, and not-a-dump-file. 5 of the 6 fail on master — the sixth is the no-regression baseline. - `python -m unittest discover` — 2236 passed, 28 skipped. - `ruff format` / `ruff check` clean. ## Note for reviewers #1041 also touches `dpdata/formats/lammps/dump.py`, but only `load_file` and the new `_iter_frames` / `_normalize_frame_indices` helpers. This PR touches `_get_block`, `split_traj`, and `system_data`, so the two are independent in content; whichever merges second may need a trivial context rebase. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added streaming iteration over LAMMPS trajectory frames. * Added validated frame selection by index, including support for custom ordering and duplicate selections. * **Bug Fixes** * Improved LAMMPS dump parsing when files contain blank lines, comments, trailing annotations, or concatenated sections. * Incomplete or truncated frames are skipped with a warning while preserving subsequent complete frames. * Atom data is safely limited to valid declared rows. * Clear errors are reported when no complete frames or valid dump structure are available. * **Tests** * Added coverage for incomplete frames, irregular formatting, invalid files, and complete trajectory preservation. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: njzjz-bot <njzjz.bot@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: A bot of @njzjz <48687836+njzjz-bot@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Summary
f_idxsupport tolammps/dumpso callers can load arbitrary non-negative frame indices directlySystem.sub_systembegin/stepselectionsITEM: TIMESTEPboundaries instead of assuming a fixed block lengthExample:
Performance benchmark
The benchmark compares direct sparse loading against the existing workflow of loading the complete trajectory and then calling
sub_system(indices).System(..., f_idx=indices)System(...).sub_system(indices)Direct sparse loading was 14.38x faster and reduced peak RSS by 64.39% for this workload. Both methods returned the same 10 selected frames. Because the final frame was selected, both methods traversed the full file; the improvement comes from avoiding storage and parsing of unselected frames.
Validation
python -m unittest test_lammps_dump_skipload.py test_lammps_dump_to_system.py test_lammps_dump_unfold.py test_lammps_dump_shift_origin.py test_lammps_dump_idx.py test_lammps_read_from_trajs.py test_lammps_spin.py— 70 tests passedruff check dpdata/ tests/test_lammps_dump_skipload.py— passedruff format --check dpdata/ tests/test_lammps_dump_skipload.py— passeddpdata --helpanddpdata --version— passedparmeddependency is not installed in the environment, and 43 tests were skippedCloses #367.
Coding agent: Codex
Codex version: codex-cli 0.144.6
Model: gpt-5.6-sol
Reasoning effort: xhigh
Summary by CodeRabbit
New Features
Bug Fixes