Skip to content

feat(lmdb): align backend with DeePMD-kit#1012

Open
OutisLi wants to merge 6 commits into
deepmodeling:masterfrom
OutisLi:pr/lmdb
Open

feat(lmdb): align backend with DeePMD-kit#1012
OutisLi wants to merge 6 commits into
deepmodeling:masterfrom
OutisLi:pr/lmdb

Conversation

@OutisLi

@OutisLi OutisLi commented Jul 12, 2026

Copy link
Copy Markdown

Summary

  • replace the legacy LMDB backend with the per-frame msgpack schema consumed by DeePMD-kit, including global type maps, mixed compositions, and system partition metadata
  • add schema-driven custom field handling, safe staged publication, automatic LMDB map growth, reader lifecycle guards, and an ordered dump_systems API for prob_sys_size
  • document the format and add protocol-independent regression coverage for interoperability, atomic types, custom data, malformed metadata, and failure recovery

Test plan

  • python -m unittest test_lmdb test_lmdb_custom_dtype test_deepmd_comp test_deepmd_mixed test_multisystems (438 tests)
  • ruff check dpdata/ tests/test_lmdb.py tests/test_lmdb_custom_dtype.py
  • git diff --check
  • validate output with DeePMD-kit's LmdbDataReader
  • read 100-frame samples from MPTrj and OMat24 LMDB datasets

Full discovery ran 2,194 tests. The remaining 46 errors are pre-existing optional-dependency/environment failures (for example, unavailable parmed); no LMDB tests failed.

Summary by CodeRabbit

  • New Features
    • Added DeePMD-compatible LMDB support with a flat, frame-indexed layout and enhanced options for loading/writing (mixed_type, type_map, max_frames).
    • Introduced dump_systems to export systems without formula-based merging.
  • Documentation
    • Added an LMDB systems documentation page and linked it in the systems navigation.
  • Bug Fixes
    • Strengthened LMDB validation and overwrite/publish safety, including handling malformed/inconsistent databases.
    • Improved atom-name sorting with atom-type–indexed field remapping/validation.
  • Tests
    • Expanded LMDB coverage for interoperability, on-disk structure, dumping semantics, migration, and robustness/error handling.
  • Chores
    • Switched dependency from msgpack-numpy to msgpack.

Replace the legacy LMDB schema with a safe, mixed-type format that preserves system partitions and interoperates with DeePMD-kit training datasets.
@dosubot dosubot Bot added size:XXL This PR changes 1000+ lines, ignoring generated files. deepmd DeePMD-kit format enhancement New feature or request labels Jul 12, 2026
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 196a23d7-ecc0-474a-b402-fd844c5e6252

📥 Commits

Reviewing files that changed from the base of the PR and between 67f8007 and 6bc0f7d.

📒 Files selected for processing (2)
  • dpdata/formats/lmdb/format.py
  • tests/test_lmdb.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/test_lmdb.py
  • dpdata/formats/lmdb/format.py

📝 Walkthrough

Walkthrough

The LMDB backend was rewritten around a flat DeePMD-kit-compatible database schema, staged batched writes, atomic publication, validated frame loading, composition grouping, atom-order canonicalization, and custom field protocols. Documentation, exports, dependencies, and interoperability and robustness tests were updated.

Changes

LMDB backend rewrite

Layer / File(s) Summary
Protocol and public API
dpdata/formats/lmdb/format.py, dpdata/format.py, dpdata/formats/lmdb/__init__.py, pyproject.toml
Defines the flat LMDB metadata and frame protocol, portable array encoding, field validation, dump APIs, exports, type annotations, and the msgpack dependency.
Staged writing and publication
dpdata/formats/lmdb/format.py, tests/test_lmdb.py
Adds batched staged writes, map growth, metadata and key validation, partition-preserving dumps, overwrite handling, atomic replacement, and reader-conflict protection.
Reading and frame aggregation
dpdata/formats/lmdb/format.py, dpdata/system.py, tests/test_lmdb.py
Loads and validates frame streams, resolves schemas and type maps, canonicalizes atom order, remaps type-indexed fields, groups frames by composition, and enforces frame and field constraints.
Custom field protocols
tests/test_lmdb_custom_dtype.py
Validates custom field names, shapes, axes, static data, virtual-atom removal, DeePMD mappings, and automatic dtype registration.
Documentation and compatibility coverage
docs/systems/index.rst, docs/systems/lmdb.md, tests/test_lmdb.py
Documents LMDB layout and workflows, and tests reference-format interoperability, encoded records, malformed metadata, frame limits, and runtime annotations.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant LMDBFormat
  participant StagedDatabase
  participant PublishedDatabase
  participant MultiSystems
  LMDBFormat->>StagedDatabase: validate and write frame records
  StagedDatabase->>PublishedDatabase: atomically publish validated data
  MultiSystems->>LMDBFormat: load metadata and frames
  LMDBFormat->>MultiSystems: canonicalize and group compositions
Loading

Possibly related PRs

Suggested labels: dpdata

Suggested reviewers: njzjz, iprozd

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.58% 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 clearly summarizes the main change: aligning the LMDB backend with DeePMD-kit.
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.

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.

@codspeed-hq

codspeed-hq Bot commented Jul 12, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

✅ 2 untouched benchmarks


Comparing OutisLi:pr/lmdb (096d5d5) with master (0416b54)

Open in CodSpeed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/test_lmdb_custom_dtype.py (1)

313-324: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Use a context manager for the write env to avoid a leaked handle.

Unlike the other tests here (which use with lmdb.open(...) as env:), this block opens the environment manually and only closes it at Line 324. If msgpack.unpackb/packb raises inside the with env.begin(write=True) block, env is never closed, which can leak the handle and, on some platforms, block shutil.rmtree in tearDown.

♻️ Wrap the environment in a context manager
-        env = lmdb.open(self.lmdb_path, map_size=1 << 30)
-        with env.begin(write=True) as txn:
-            key = b"000000000001"
-            frame = msgpack.unpackb(txn.get(key), raw=False)
-            replacement = np.array([3.0, 4.0])
-            frame["static_data"] = {
-                "type": str(replacement.dtype),
-                "shape": list(replacement.shape),
-                "data": replacement.tobytes(),
-            }
-            txn.put(key, msgpack.packb(frame, use_bin_type=True))
-        env.close()
+        with lmdb.open(self.lmdb_path, map_size=1 << 30) as env:
+            with env.begin(write=True) as txn:
+                key = b"000000000001"
+                frame = msgpack.unpackb(txn.get(key), raw=False)
+                replacement = np.array([3.0, 4.0])
+                frame["static_data"] = {
+                    "type": str(replacement.dtype),
+                    "shape": list(replacement.shape),
+                    "data": replacement.tobytes(),
+                }
+                txn.put(key, msgpack.packb(frame, use_bin_type=True))
🤖 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_lmdb_custom_dtype.py` around lines 313 - 324, Update the LMDB
environment setup in this test to use lmdb.open as a context manager around the
existing write transaction and frame replacement logic. Remove the manual
env.close call while preserving the current key update behavior and transaction
scope.
🤖 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.

Inline comments:
In `@dpdata/formats/lmdb/format.py`:
- Around line 1061-1066: Update the publish flow around _open_publish_guard and
os.replace so the guard is closed before replacing staged_data with target_data,
ensuring overwrite=True works on Windows. Preserve cleanup of the guard and temp
directory, and avoid leaving the directory handle open during os.replace.

---

Nitpick comments:
In `@tests/test_lmdb_custom_dtype.py`:
- Around line 313-324: Update the LMDB environment setup in this test to use
lmdb.open as a context manager around the existing write transaction and frame
replacement logic. Remove the manual env.close call while preserving the current
key update behavior and transaction scope.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b43de27f-e10b-4eec-9e56-4a0e30df6de9

📥 Commits

Reviewing files that changed from the base of the PR and between 0416b54 and 5c34ca8.

📒 Files selected for processing (8)
  • docs/systems/index.rst
  • docs/systems/lmdb.md
  • dpdata/format.py
  • dpdata/formats/lmdb/__init__.py
  • dpdata/formats/lmdb/format.py
  • pyproject.toml
  • tests/test_lmdb.py
  • tests/test_lmdb_custom_dtype.py

Comment thread dpdata/formats/lmdb/format.py
Make protocol-metadata assertions independent of global plugin registration order and reject unsafe Windows overwrite operations explicitly.
@wanghan-iapcm wanghan-iapcm requested review from iProzd, njzjz and wanghan-iapcm and removed request for iProzd July 12, 2026 15:01
Comment thread docs/systems/lmdb.md Outdated
Comment thread dpdata/formats/lmdb/format.py
@codecov

codecov Bot commented Jul 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.57764% with 120 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.82%. Comparing base (0416b54) to head (096d5d5).

Files with missing lines Patch % Lines
dpdata/formats/lmdb/format.py 87.50% 120 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1012      +/-   ##
==========================================
- Coverage   86.87%   86.82%   -0.06%     
==========================================
  Files          89       89              
  Lines        8266     9114     +848     
==========================================
+ Hits         7181     7913     +732     
- Misses       1085     1201     +116     

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

Document the core-field wire names and require python-lmdb 2.0 so duplicate environment detection reliably protects active readers.
@OutisLi OutisLi requested a review from wanghan-iapcm July 14, 2026 07:31

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

Requesting changes for four reproducible compatibility/data-integrity issues. The LMDB-focused suite passes (439 tests), and Ruff lint/format checks pass. Full discovery ran 2,201 tests with 13 unrelated missing-optional-dependency errors (rdkit/parmed); no LMDB tests failed.

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

Comment thread dpdata/formats/lmdb/format.py
Comment thread dpdata/formats/lmdb/format.py
Comment thread dpdata/formats/lmdb/format.py
Comment thread dpdata/formats/lmdb/format.py
@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Jul 14, 2026
Read and migrate the released schema, remap every type axis consistently, and reject cross-system field ownership or unsupported scalar data.
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:XXL This PR changes 1000+ lines, ignoring generated files. labels Jul 15, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
dpdata/formats/lmdb/format.py (1)

1538-1548: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Avoid whole-registry rollback across concurrent readers.

One reader can snapshot the global DTYPES, another can register a schema, and then the first reader’s early close restores its old tuples—erasing the second reader’s committed registration. Serialize registry mutations and rollback only definitions installed by this invocation when they remain unchanged.

🤖 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 `@dpdata/formats/lmdb/format.py` around lines 1538 - 1548, Update the dataset
iteration flow around _register_specs to serialize global DTYPES mutations
across readers and avoid restoring whole registry snapshots. Track the
definitions installed by this invocation, and on early termination remove or
restore only those entries when their current values still match the installed
definitions, preserving concurrent registrations and committed changes.
🧹 Nitpick comments (1)
tests/test_lmdb_custom_dtype.py (1)

451-461: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a writer-side type-axis permutation case.

This test only permutes types while reading because the source and write-time type_map both start as ["H", "O"]. Add a case that writes with ["O", "H", "C"] and reads with that same map to verify _frame_payload places local values into global slots correctly.

🤖 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_lmdb_custom_dtype.py` around lines 451 - 461, Add a writer-side
permutation test alongside test_type_axes_follow_requested_type_map that writes
the system using type_map=["O", "H", "C"] and reads it with the same map, then
assert atom_names and the expected type_values/static_type_values ordering to
verify _frame_payload places local values in the corresponding global slots.
🤖 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.

Inline comments:
In `@dpdata/formats/lmdb/format.py`:
- Around line 1654-1675: The legacy type migration block should derive active
elements from the validated `atom_types` values rather than
`data["atom_numbs"]`. Validate that every atom type index is within `old_names`,
raise the established `LMDBError` for invalid indices, then compute active names
from the unique referenced indices before checking `missing_active`; preserve
the existing remapping and recount behavior for valid data.
- Around line 368-428: Update _decode_legacy_frame and its legacy object
decoding path so malformed descriptors, including missing type/data/shape keys
and truncated dtype entries, are converted to LMDBFrameError. Extend the decode
boundary’s exception handling to cover the KeyError and IndexError raised by
_decode_legacy_object or _unpack_legacy_dtype, while preserving the existing
contextual error message and exception chaining.
- Around line 1481-1489: Update the type_map handling around _meta_get(meta,
"type_map") to distinguish missing metadata from an explicitly empty stored map.
Reject an empty type_map as malformed instead of allowing the later positional
Type_0/Type_1 fallback, while preserving existing validation and behavior for
non-empty maps and genuinely missing metadata.

---

Outside diff comments:
In `@dpdata/formats/lmdb/format.py`:
- Around line 1538-1548: Update the dataset iteration flow around
_register_specs to serialize global DTYPES mutations across readers and avoid
restoring whole registry snapshots. Track the definitions installed by this
invocation, and on early termination remove or restore only those entries when
their current values still match the installed definitions, preserving
concurrent registrations and committed changes.

---

Nitpick comments:
In `@tests/test_lmdb_custom_dtype.py`:
- Around line 451-461: Add a writer-side permutation test alongside
test_type_axes_follow_requested_type_map that writes the system using
type_map=["O", "H", "C"] and reads it with the same map, then assert atom_names
and the expected type_values/static_type_values ordering to verify
_frame_payload places local values in the corresponding global slots.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 554343ec-37be-4839-a466-e365593127f2

📥 Commits

Reviewing files that changed from the base of the PR and between 096d5d5 and 67f8007.

📒 Files selected for processing (5)
  • docs/systems/lmdb.md
  • dpdata/formats/lmdb/format.py
  • dpdata/system.py
  • tests/test_lmdb.py
  • tests/test_lmdb_custom_dtype.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_lmdb.py

Comment thread dpdata/formats/lmdb/format.py
Comment thread dpdata/formats/lmdb/format.py Outdated
Comment thread dpdata/formats/lmdb/format.py
Wrap malformed legacy descriptors, reject invalid stored type maps, and derive legacy active types from validated atom indices.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

deepmd DeePMD-kit format enhancement New feature or request lgtm This PR has been approved by a maintainer size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants