Skip to main content

add-source

Guide for adding a new data source to PhysicsNeMo Curator. Use when adding a remote dataset (HuggingFace, S3, etc.) or local file-based source. Covers discovery questions, file format handling, Mesh/DataArray/AtomicData construction, parallel partitioning, testing, and registration.

معلومات المصدر

المستودع
NVIDIA/physicsnemo-curator
آخر نشاط في المصدر
١١ مايو ٢٠٢٦ في ٠٣:٠٦
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٦٦
التفرعات
٣٠

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
add-source
description
Guide for adding a new data source to PhysicsNeMo Curator. Use when adding a remote dataset (HuggingFace, S3, etc.) or local file-based source. Covers discovery questions, file format handling, Mesh/DataArray/AtomicData construction, parallel partitioning, testing, and registration.
# Adding a New Data Source This skill walks through adding a new `Source` to PhysicsNeMo Curator, from initial research through implementation, testing, and registration. ## Step 0: Gather Requirements Before writing any code, answer these questions. Ask the user if anything is unclear. ### Domain Which submodule does this source belong to? | Domain | Type parameter | Submodule | Dependency group | |--------|---------------|-----------|-----------------| | **mesh** | `Source[Mesh]` | `src/physicsnemo_curator/domains/mesh/` | `mesh` (physicsnemo, pyvista, pyarrow, torch) | | **da** | `Source[xr.DataArray]` | `src/physicsnemo_curator/domains/da/` | `da` (xarray, earth2studio, zarr) | | **atm** | `Source[AtomicData]` | `src/physicsnemo_curator/domains/atm/` | `mesh` (nvalchemi, torch, lmdb) | ### Dataset Information Gather all of the following before proceeding: 1. **Name and URL** — What is the dataset called? Where is it hosted? (HuggingFace `hf://datasets/org/repo`, S3, HTTP, local path) 2. **File format** — What format are the files? (VTK/VTP/VTU, Parquet, HDF5, NetCDF, NumPy, Zarr, LMDB, CSV, custom) 3. **Schema / fields** — What fields does the data contain? (coordinates, velocity, pressure, temperature, connectivity, forces, energies, etc.) 4. **Spatial dimensions** — Is the data 1D, 2D, 3D? Structured grid or unstructured mesh? Molecular graph? 5. **Dataset size** — How large is the full dataset? How many samples? 6. **Organization** — How are files organized? Single file per sample? Run directories (`run_0/`, `run_1/`)? Splits (train/val/test)? Multi-file databases? 7. **License** — What license? Is it permissive enough for inclusion? 8. **Documentation** — Is there a paper, README, or data card? 9. **Parameters** — What varies between samples? (geometry, physics parameters, initial conditions, time steps) 10. **Authentication** — Does accessing the data require tokens or credentials? 11. **Parallel constraints** — Does the data backend have concurrency limitations? (e.g. LMDB single-env-per-process, HDF5 locking) 12. **Backend options** — Is there a fast native reader (Rust) available as well as a Python fallback? ### Output Type Construction For **mesh** sources, determine how to construct `physicsnemo.mesh.Mesh`: - **VTK-based**: Use `pyvista.read()` then `from_pyvista()` (see existing VTK sources) - **Non-VTK** (Parquet, HDF5, NumPy): Construct `Mesh` directly: ```python from physicsnemo.mesh import Mesh mesh = Mesh( points=points_tensor, # torch.Tensor, shape (n_points, 3) cells=cells_tensor, # torch.Tensor, shape (n_cells, nodes_per_cell), dtype=long point_data=point_data_td, # TensorDict with batch_size=[n_points], or None cell_data=cell_data_td, # TensorDict with batch_size=[n_cells], or None global_data=global_data_td,# TensorDict with batch_size=[], or None ) ``` For **da** sources, determine how to construct `xarray.DataArray` with appropriate dimensions, coordinates, and attributes. For **atm** sources, determine how to construct `nvalchemi.data.AtomicData`: ```python from nvalchemi.data import AtomicData data = AtomicData( positions=positions, # torch.Tensor, shape (n_atoms, 3) atomic_numbers=z, # torch.Tensor, shape (n_atoms,) cell=cell, # torch.Tensor, shape (3, 3) or None pbc=pbc, # torch.Tensor, shape (3,) bool energy=energy, # torch.Tensor, shape (1,) or None forces=forces, # torch.Tensor, shape (n_atoms, 3) or None ) ``` ### File Discovery Strategy Each source handles its own file discovery and caching internally. Choose the approach based on dataset organization: | Pattern | Approach | When to use | |---------|----------|------------| | Flat directory of files | `pathlib.Path.glob()` | Local-only sources (VTKSource, ASELMDBSource) | | Remote flat files | `fsspec` + `fs.glob()` | Generic remote file glob | | Run-indexed dirs (`run_0/`, `run_1/`) | `fsspec` + `fs.ls()` + regex | Benchmark datasets with numbered runs (DrivAerML, AhmedML) | | API/backend-driven | Domain library fetcher | Weather reanalysis (ERA5, HRRR via earth2studio) | | Single-file datasets | Direct `fsspec`/`pyarrow` | Parquet tables, Zarr stores | ### Reference: Existing Sources | Source | File | Format | Domain | Good example for | |--------|------|--------|--------|-----------------| | `VTKSource` | `mesh/sources/vtk.py` | VTK | mesh | Local file glob, pyvista/rust backend, relative_path | | `DrivAerMLSource` | `mesh/sources/drivaerml.py` | VTP/VTU | mesh | Remote fsspec, multi-mesh, run_id, mesh_name | | `AhmedMLSource` | `mesh/sources/ahmedml.py` | VTP | mesh | HF remote, CSV metadata, multi-mode, run_id | | `NavierStokesCylinderSource` | `mesh/sources/ns_cylinder.py` | Parquet | mesh | Direct Mesh construction, non-VTK | | `D3PlotSource` | `mesh/sources/d3plot.py` | d3plot | mesh | Binary format, python/rust dual backend | | `AnsysRSTSource` | `mesh/sources/ansys_rst.py` | .rst | mesh | Auto-discovery of results, complex extraction | | `OpenRadiossSource` | `mesh/sources/openradioss.py` | VTK | mesh | Per-timestep multi-file, run_id | | `ERA5Source` | `da/sources/era5.py` | API | da | Multi-backend weather, DataArray, earth2studio | | `HRRRSource` | `da/sources/hrrr.py` | API | da | HRRR 3km analysis, multiple providers | | `ASELMDBSource` | `atm/sources/aselmdb.py` | LMDB | atm | partition_indices, rust fallback, relative_path | | `RandomMeshSource` | `mesh/sources/random.py` | Synthetic | mesh | Testing, minimal source | | `RandomDataArraySource` | `da/sources/random.py` | Synthetic | da | Testing, minimal DA source | | `RandomAtomicSource` | `atm/sources/random.py` | Synthetic | atm | Testing, minimal atomic source | ## Step 1: Create the Source Module Create the source file at the appropriate location: - **mesh**: `src/physicsnemo_curator/domains/mesh/sources/<name>.py` - **da**: `src/physicsnemo_curator/domains/da/sources/<name>.py` - **atm**: `src/physicsnemo_curator/domains/atm/sources/<name>.py` ### Required SPDX Header Every source file MUST start with: ```python # SPDX-FileCopyrightText: Copyright (c) 2025 - 2026 NVIDIA CORPORATION & AFFILIATES. # SPDX-FileCopyrightText: All rights reserved. # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ``` ### Import Block Template ```python """<Dataset name> data source for PhysicsNeMo Curator. Reads <format> data from <location> and yields :class:`~physicsnemo.mesh.Mesh` objects for use in curator pipelines. Examples -------- >>> source = <ClassName>(...) # doctest: +SKIP >>> len(source) # doctest: +SKIP <expected_count> >>> mesh = next(source[0]) # doctest: +SKIP """ from __future__ import annotations import pathlib import tempfile from typing import TYPE_CHECKING, ClassVar, Literal from physicsnemo_curator.core.base import Param, Source from physicsnemo_curator.core.logging import get_logger if TYPE_CHECKING: from collections.abc import Generator from physicsnemo.mesh import Mesh ``` ### Source Class Template ```python _DEFAULT_URL = "<dataset_url>" class <ClassName>(Source[Mesh]): """Read meshes from <Dataset Name>. <2-3 sentence description of the dataset, what it contains, and where it comes from. Include a link to the paper or dataset page.> Parameters ---------- url : str Base URL for the dataset. storage_options : dict[str, object] | None Extra keyword arguments for the fsspec filesystem (e.g. HF token). cache_storage : str Local directory for caching downloaded files. Note ---- - Dataset: `<org/repo> <<url>>`_ - Paper: `arXiv:<id> <https://arxiv.org/abs/<id>>`_ - License: `<license_name> <<license_url>>`_ """ name: ClassVar[str] = "<Display Name>" description: ClassVar[str] = "<Short description for CLI>" @classmethod def params(cls) -> list[Param]: """Return configurable parameters for this source. Returns ------- list[Param] Parameter list for CLI configuration. """ return [ Param( name="url", description="Base dataset URL", type=str, default=_DEFAULT_URL, ), Param( name="cache_storage", description="Local cache directory for downloaded files", type=str, default="", ), # Add dataset-specific params (mesh_type, split, backend, etc.) ] def __init__( self, url: str = _DEFAULT_URL, storage_options: dict[str, object] | None = None, cache_storage: str = "", ) -> None: """Initialize the source. Parameters ---------- url : str Base dataset URL. storage_options : dict[str, object] | None Extra keyword arguments for the fsspec filesystem. cache_storage : str Local cache directory. """ self._url = url self._storage_options = storage_options or {} self._cache_storage = cache_storage or tempfile.mkdtemp( prefix="curator_<name>_" ) self._log = get_logger(self) # Initialize file discovery and data access here # Load lightweight metadata eagerly (e.g. parameter tables) # Defer heavy data loading (geometry, fields) to __getitem__ self._discover_items() def _discover_items(self) -> None: """Eagerly discover available items (files, runs, etc.).""" ... def __len__(self) -> int: """Return the number of items in this source.""" return self._count def __getitem__(self, index: int) -> Generator[Mesh]: """Yield mesh(es) for the given index. Parameters ---------- index : int Zero-based index. Negative indices are supported. Yields ------ Mesh One or more mesh objects for this index. Raises ------ IndexError If *index* is out of range. """ if index < 0: index += len(self) if index < 0 or index >= len(self): msg = f"Index {index} out of range for source with {len(self)} items." raise IndexError(msg) # Load data for this index and construct Mesh yield mesh ``` ### Logging Use the structured component logger: ```python from physicsnemo_curator.core.logging import get_logger class MySource(Source[Mesh]): def __init__(self, ...) -> None: self._log = get_logger(self) self._log.info("Discovered %d items in %s", count, url) def __getitem__(self, index: int) -> Generator[Mesh]: self._log.debug("Reading index %d", index) ... ``` For module-level logging (outside class scope): ```python from physicsnemo_curator.core.logging import get_logger logger = get_logger("MySourceModule") ``` **Do NOT use `logging.getLogger(__name__)`** — always use `get_logger`. ### Key Implementation Patterns #### Pattern A: Local File-Based (VTKSource, ASELMDBSource) Discovers files at construction time via `pathlib.Path.glob()`, then
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub