feat(data): add dpdata format conversion#5565
Conversation
📝 WalkthroughWalkthroughAdds automatic dpdata-based format conversion (e.g., extxyz → LMDB/HDF5) for DeePMD training and validation datasets. Introduces ChangesAutomatic dpdata Format Conversion for Training/Validation Datasets
Sequence Diagram(s)sequenceDiagram
participant User as User Config
participant Entrypoint as Training Entrypoint (pt/pd/pt_expt)
participant ProcessSystems as process_systems()
participant Converter as _convert_system_by_dpdata()
participant Cache as _DPDATA_CONVERSION_CACHE / .deepmd_dpdata_cache
participant DataSystem as LmdbDataSystem / DeepmdDataSystem
User->>Entrypoint: training_data with format="extxyz", out_format="lmdb"
Entrypoint->>ProcessSystems: process_systems(systems, fmt="extxyz", out_fmt="lmdb")
ProcessSystems->>Converter: _convert_system_by_dpdata(path, fmt, out_fmt)
Converter->>Cache: check freshness (mtime vs. cache)
alt cache fresh
Cache-->>Converter: return cached LMDB path
else cache stale or missing
Converter->>Converter: acquire .lock file
Converter->>Converter: dpdata.MultiSystems → temp output
Converter->>Cache: move to cache path, update _DPDATA_CONVERSION_CACHE
Converter->>Converter: release lock
end
Converter-->>ProcessSystems: [lmdb_path]
ProcessSystems-->>Entrypoint: [lmdb_path]
Entrypoint->>DataSystem: LmdbDataSystem(lmdb_path, type_map, ...)
DataSystem-->>Entrypoint: batches (type, natoms_vec, coord, box, ...)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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.
Actionable comments posted: 4
🧹 Nitpick comments (4)
deepmd/utils/data_system.py (4)
1147-1162: ⚖️ Poor tradeoffRecursive mtime scan may be slow for large source directories.
_source_mtimewalks the entire source directory tree to find the latest modification time. For datasets with many files, this could add noticeable latency on every cache freshness check. Consider caching the computed mtime or using a faster heuristic (e.g., only checking top-level directory mtime plus a sample of files).🤖 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/utils/data_system.py` around lines 1147 - 1162, The `_source_mtime` function performs a full recursive directory walk using source.rglob("*") to find the latest modification time across all files, which becomes inefficient for large source directories. To improve performance, implement a caching mechanism to store previously computed mtimes so that repeated calls for the same source directory do not re-scan the entire tree, or alternatively replace the full recursive scan with a faster heuristic that only examines the top-level directory mtime and a representative sample of files rather than traversing every single file.
786-796: 💤 Low valueMixed-type detection scans all frames at initialization.
_detect_mixed_typeiterates through every frame in the LMDB dataset comparing atom types, which could be slow for very large datasets (thousands of frames). Consider caching this property in the LMDB metadata during conversion, or adding a sampling heuristic for large datasets.🤖 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/utils/data_system.py` around lines 786 - 796, The _detect_mixed_type method iterates through all frames in the dataset to check for mixed atom types, which is inefficient for large datasets. Implement caching by storing the detection result as an instance variable after the first call, and consider adding a sampling heuristic for datasets with many frames such that for very large datasets (e.g., more than a configurable threshold), only a sample of frames are checked instead of all frames. Update the method to return the cached result on subsequent calls and use the sampling strategy to limit iterations while still maintaining reasonable confidence in the mixed-type detection.
1395-1408: 💤 Low valueSingle-LMDB fast-path only; consider documenting multi-LMDB limitation.
The LMDB routing only handles the case where
systemsresolves to exactly one LMDB path. If multiple LMDB paths are provided (or conversion produces multiple systems), they fall through toDeepmdDataSystem. Consider adding a log warning or updating docstring to clarify this behavior.🤖 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/utils/data_system.py` around lines 1395 - 1408, The code currently only provides optimized handling for a single LMDB system through LmdbDataSystem, while multiple LMDB systems silently fall through to DeepmdDataSystem. Add a log warning message when multiple LMDB paths are detected (when len(systems) > 1 and all are LMDB) to alert users that they will be handled through the standard DeepmdDataSystem path rather than the optimized LmdbDataSystem, and update the function's docstring to document this single-LMDB fast-path behavior and clarify what happens with multiple LMDB inputs.
1219-1277: ⚖️ Poor tradeoffStale lock files may persist after process crashes.
If a process crashes after creating the lock file (line 1242) but before the
finallyblock runs (e.g., SIGKILL), the.lockfile will remain. Subsequent processes will wait 5 minutes before timing out. Consider adding stale-lock detection using the PID written to the lock file, or a timestamp-based staleness check.🤖 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/utils/data_system.py` around lines 1219 - 1277, The _convert_system_by_dpdata function creates lock files to coordinate between processes, but if a process crashes after creating the lock file but before the finally block executes, the lock file persists causing other processes to wait 5 minutes before timing out. Add stale lock detection logic in the except FileExistsError block before calling _wait_for_conversion. Read the PID from the existing lock file and check if that process is still running using platform-appropriate methods (e.g., os.kill with signal 0 on Unix, or process existence checks). If the process is not running or if the lock file is older than a reasonable threshold (e.g., 10 minutes), remove the stale lock file and retry the lock acquisition instead of waiting. This prevents indefinite hangs on stale locks from crashed processes.
🤖 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 `@deepmd/pd/entrypoints/main.py`:
- Around line 123-144: The current LMDB validation checks for both
training_systems and validation_systems only reject LMDB when the result is a
single system (len(...) == 1), but the error messages indicate that Paddle does
not support LMDB data in general. Remove the len(...) == 1 condition from both
the training_systems check (around line 123) and the validation_systems check
(around line 139) so that any LMDB dataset is rejected regardless of whether
it's a single system or multiple systems in the list. This ensures that any
LMDB-resolved dataset triggers the NotImplementedError with a clear message,
preventing less clear failures downstream.
In `@deepmd/pt_expt/entrypoints/main.py`:
- Around line 118-132: The current code in _get_neighbor_stat_data only
validates the single-LMDB case with `if len(systems) == 1 and
is_lmdb(systems[0])`, but when format-based conversion produces multiple LMDB
paths, this check is skipped and execution falls through to get_data() instead
of raising an appropriate error for list-form LMDB systems. Add validation
guards in both _get_neighbor_stat_data and _build_data_system functions to
ensure that after process_systems() is called, if any LMDB systems are returned,
they are validated to not be in list form (similar to what _detect_lmdb_path
does), and raise a clear error before reaching the fallback get_data() or
DeepmdDataSystem paths.
In `@deepmd/pt/entrypoints/main.py`:
- Around line 197-204: Add a validation guard before the existing condition that
checks `len(systems) == 1 and is_lmdb(systems[0])` to prevent multiple LMDB
paths from being passed to DpLoaderSet. The guard should use
`isinstance(systems, list)` combined with `any(isinstance(s, str) and is_lmdb(s)
for s in systems)` to detect when systems is a list containing LMDB paths and
raise a clear ValueError message explaining that LMDB datasets must be passed as
a scalar string rather than as a list.
In `@deepmd/utils/data_system.py`:
- Around line 848-852: Add a defensive check at the beginning of the
`_stack_frames` method to guard against empty frames lists. Before accessing
`frames[0]` at line 864, add validation to check if the frames list is empty and
handle this edge case appropriately, such as raising a more informative error or
returning early. This will prevent IndexError when the sampler yields an empty
batch due to malformed LMDB data, since both `_load_set` and `get_batch` call
this method with frames lists derived from sampler indices.
---
Nitpick comments:
In `@deepmd/utils/data_system.py`:
- Around line 1147-1162: The `_source_mtime` function performs a full recursive
directory walk using source.rglob("*") to find the latest modification time
across all files, which becomes inefficient for large source directories. To
improve performance, implement a caching mechanism to store previously computed
mtimes so that repeated calls for the same source directory do not re-scan the
entire tree, or alternatively replace the full recursive scan with a faster
heuristic that only examines the top-level directory mtime and a representative
sample of files rather than traversing every single file.
- Around line 786-796: The _detect_mixed_type method iterates through all frames
in the dataset to check for mixed atom types, which is inefficient for large
datasets. Implement caching by storing the detection result as an instance
variable after the first call, and consider adding a sampling heuristic for
datasets with many frames such that for very large datasets (e.g., more than a
configurable threshold), only a sample of frames are checked instead of all
frames. Update the method to return the cached result on subsequent calls and
use the sampling strategy to limit iterations while still maintaining reasonable
confidence in the mixed-type detection.
- Around line 1395-1408: The code currently only provides optimized handling for
a single LMDB system through LmdbDataSystem, while multiple LMDB systems
silently fall through to DeepmdDataSystem. Add a log warning message when
multiple LMDB paths are detected (when len(systems) > 1 and all are LMDB) to
alert users that they will be handled through the standard DeepmdDataSystem path
rather than the optimized LmdbDataSystem, and update the function's docstring to
document this single-LMDB fast-path behavior and clarify what happens with
multiple LMDB inputs.
- Around line 1219-1277: The _convert_system_by_dpdata function creates lock
files to coordinate between processes, but if a process crashes after creating
the lock file but before the finally block executes, the lock file persists
causing other processes to wait 5 minutes before timing out. Add stale lock
detection logic in the except FileExistsError block before calling
_wait_for_conversion. Read the PID from the existing lock file and check if that
process is still running using platform-appropriate methods (e.g., os.kill with
signal 0 on Unix, or process existence checks). If the process is not running or
if the lock file is older than a reasonable threshold (e.g., 10 minutes), remove
the stale lock file and retry the lock acquisition instead of waiting. This
prevents indefinite hangs on stale locks from crashed processes.
🪄 Autofix (Beta)
❌ Autofix failed (check again to retry)
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: 54161e1f-c01b-4956-b3a7-a6ab0d33cb04
📒 Files selected for processing (7)
deepmd/pd/entrypoints/main.pydeepmd/pt/entrypoints/main.pydeepmd/pt_expt/entrypoints/main.pydeepmd/utils/argcheck.pydeepmd/utils/data_system.pypyproject.tomlsource/tests/common/test_data_system_conversion.py
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #5565 +/- ##
==========================================
- Coverage 78.58% 77.65% -0.93%
==========================================
Files 1050 1053 +3
Lines 120637 121200 +563
Branches 4356 4385 +29
==========================================
- Hits 94801 94123 -678
- Misses 24278 25533 +1255
+ Partials 1558 1544 -14 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Note Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it. An unexpected error occurred while generating fixes: Not Found - https://docs.github.com/rest/git/refs#get-a-reference |
Break the LMDB/data-system import cycle, reject ambiguous multi-LMDB results across backends, reject LMDB on Paddle, and guard empty LMDB frame batches with direct regressions. Coding-Agent: Codex Codex-Version: codex-cli 0.144.4 Model: gpt-5.6-sol Reasoning-Effort: xhigh
Resolve the data-loader conflicts while preserving dpdata format conversion, LMDB routing, and the new multi-LMDB validation behavior. Coding-Agent: Codex Codex-Version: codex-cli 0.144.4 Model: gpt-5.6-sol Reasoning-Effort: xhigh
|
Possible reviewers based on changed lines, exact file history, and exact-file review history:
No review request was made automatically. Coding agent: Codex |
Load the dpmodel LMDB helpers only after the legacy data-system module has initialized, preventing backend imports from re-entering a partially initialized module. Coding-Agent: Codex Codex-Version: codex-cli 0.144.4 Model: gpt-5.6-sol Reasoning-Effort: xhigh
Summary
formatandout_formatoptions for dpdata-backed conversionlmdband cache converted datasets under/home/jzzeng/codes/deepmd-kit/.deepmd_dpdata_cachedpdata>=1.0.1a runtime dependencyCloses #5237
Tests
ruff check .ruff format --check .pytest source/tests/common/test_data_system_conversion.py -qpytest source/tests/common/dpmodel/test_lmdb_data.py::TestLmdbDataReader::test_is_lmdb -qpytest source/tests/tf/test_dp_test.py::TestDPTestEner::test_1frame -qsrun --gres=gpu:1 dp train input.jsonwith extxyz input, default LMDB conversion, no--skip-neighbor-statsrun --gres=gpu:1 dp --pt train input.jsonwith extxyz input, default LMDB conversion, no--skip-neighbor-statsrun --gres=gpu:1 dp --jax train input.jsonwith extxyz input, default LMDB conversion, no--skip-neighbor-stat(environment used CPU JAX fallback because CUDA jaxlib is unavailable)srun --gres=gpu:1 dp --pt-expt train input.jsonverified conversion and neighbor statistics; this environment then hits the existing pt-expt tensor serialization error during model constructionSummary by CodeRabbit
New Features
.extxyz) with automatic conversion to LMDB or other formatsformatandout_format/output_formatfor training and validation datasets to enable flexible data preprocessingTests