elfes.data
PyG sample data, datasets, and derived connectivity.
HDF5Dataset and InMemoryDataset use different HDF5 reading paths but
return the same edge-free, single-sample torch_geometric.data.Data structure.
The Data contains batchable sample tensors and compact basis references;
complete basis sets and physical-data descriptions remain on the owning Dataset.
Standard PyTorch DataLoaders invoke each Dataset's __getitems__() for one batch
of indices: the on-disk Dataset reads all selected physical data directly into batch-contiguous storage without constructing physical
Sample objects, while the in-memory Dataset separates views from its existing
collated storage before ordinary PyG collation.
Shape names
n_atoms: number of atoms in the sample.n_blocks: number of stored blocks in one block-sparse orbital matrix.n_values: number of flattened value rows in one electronic-data entry.extra_shape: the entry's fixed trailing shape, possibly empty.grid_shape: the three-dimensional shape of a uniform grid.n_samples: number of samples in a PyG batch.n_edges: number of directed connectivity edges in a batch.
Reading one sample
Every item returned by the on-disk and in-memory Datasets is a Data. The Dataset adds
its stable member ID for tracing after shuffle or batching; the remaining fields
come from the physical Sample:
data.sample_id str
data.num_nodes int = n_atoms
data.atomic_numbers int64 [n_atoms]
data.pos float32 [n_atoms, 3]
data.cell float32 [1, 3, 3]
data.pbc bool [1, 3]
data.magmoms float32 [n_atoms] or [n_atoms, 3] # optional
data.basis_map dict[str, dict[str, Tensor]]
data.physical_data PhysicalDataDict
PyG also permits mapping-style access such as data["pos"], but attribute
access is the usual form for these top-level fields.
data.basis_map maps every basis role available in the Dataset to its per-atom
Torch data. Role names are open strings; common conventions include ao, aux,
and paw_coupled:
role_basis_map = data.basis_map[role]
role_basis_map["atomic_basis_id"] int64 [n_atoms]
role_basis_map["orb_counts"] int64 [n_atoms]
For atom atom_idx, role_basis_map["atomic_basis_id"][atom_idx] indexes
dataset.atomic_basis_tables[role], while role_basis_map["orb_counts"][atom_idx] is the
number of basis functions contributed by that atom. dataset.basis_sets[role]
retains the complete BasisSet; full definitions are not copied into each
Data.
The leading length-one axes of cell and pbc are sample axes: PyG batching
concatenates them into [n_samples, 3, 3] and [n_samples, 3].
When the logical dataset carries input atomic magnetic moments, magmoms is a
node-level tensor in μB and PyG concatenates it along the atom axis. It is
absent for datasets without this input; one dataset does not mix absent,
collinear, and noncollinear forms.
physical_data is always present and may be empty when no physical data was selected. Its globally unique names combine the atomistic and electronic data stored separately in the physical Sample and ELFES HDF5.
SampleToData provides the physical conversion for Sample objects that are already in memory. Because a physical Sample has no intrinsic dataset membership, this direct conversion does not add data.sample_id. Construct one converter from the basis sets shared by a logical dataset, then reuse it for every compatible sample:
converter = SampleToData(basis_sets)
data = converter(sample)
Reading atomistic data
For selected atomistic data, data.physical_data[name] is its values tensor. The owning Dataset exposes physical_data_descriptions[name] with its data_type, precision, and extra_shape.
data_type="scalar" values float32 [1, *extra_shape]
data_type="atom_scalar" values float32 [n_atoms, *extra_shape]
data_type="cartesian_vector" values float32 [1, *extra_shape, 3]
data_type="atom_cartesian_vector" values float32 [n_atoms, *extra_shape, 3]
data_type="cartesian_tensor_2" values float32 [1, *extra_shape, 3, 3]
data_type="atom_cartesian_tensor_2" values float32 [n_atoms, *extra_shape, 3, 3]
The length-one leading axes mark structure-level values so PyG concatenates them along the sample axis. AtomScalar, AtomCartesianVector, and AtomCartesianTensor2 instead use their atom axis as the leading runtime axis and are concatenated in the same order as pos. Their physical arrays place n_atoms immediately before any Cartesian axes; conversion moves that atom axis forward for batching.
Reading electronic data
For selected electronic data, data.physical_data[name] is an ordinary Python
dictionary. Its four possible dictionary structures are published as
OrbData, BlockSparseOrbData, UniformVolumetricData, and
QuadratureVolumetricData; PhysicalDataDict is the named outer mapping shared with atomistic tensors.
The dense OrbMatrix hierarchy and OrbVector share OrbData because their
PyG fields are identical. For example, select the Hamiltonian data with:
hamiltonian_data = data.physical_data["hamiltonian"]
The data dictionary always contains:
hamiltonian_data["num_values"] int64 [1]
hamiltonian_data["values_real"] float32 [n_values, *extra_shape]
hamiltonian_data["values_imag"] float32 [n_values, *extra_shape] # complex data only
Thus, for example, the Hamiltonian's real values are read as
data.physical_data["hamiltonian"]["values_real"]; fields inside an entry
use dictionary keys rather than attribute access.
The entry's data_type, basis_role, pauli, complexity, and fixed
extra_shape describe the whole Dataset and are available from
dataset.physical_data_descriptions[name] rather than repeated in every sample.
A typical access path therefore looks like:
data = dataset[0]
positions = data.pos
hamiltonian = data.physical_data["hamiltonian"]
values_real = hamiltonian["values_real"]
atom_pair_index = hamiltonian["atom_pair_index"]
description = dataset.physical_data_descriptions["hamiltonian"]
Consumers such as losses can accept one typed electronic-data entry without depending on
the complete Data:
def orbital_loss(prediction: OrbData, target: OrbData) -> Tensor:
...
Because physical-data names are dataset-defined, callers use the corresponding
physical_data_descriptions[name].data_type when choosing or statically narrowing one
entry to a specific dictionary type.
The leading values axis is the variable-size axis concatenated by PyG. A
complex entry uses parallel real tensors; ELFES does not place complex
tensors in Data. When Pauli components are present, their component axis is
the final axis of extra_shape.
BlockSparseOrbData similarly names a runtime structure rather than a
coordinate system. Dataset entries contain physical orbital-matrix
components, while model-side operations may reuse the same pair blocks and
boundaries for coupled components.
Block-sparse orbital matrices
data_type="block_sparse_orb_matrix" and
data_type="herm_block_sparse_orb_matrix" add:
block_data["num_blocks"] int64 [1]
block_data["atom_pair_index"] int64 [2, n_blocks]
block_data["pair_shifts"] int64 [n_blocks, 3]
block_data["block_lengths"] int64 [n_blocks]
block_data["atom_pair_index"][:, b] contains sample-local row and column atom
indices, while block_data["pair_shifts"][b] identifies the cell image of the
column atom. General matrices contain actual directed blocks; Hermitian matrices
contain one independent block from each partner pair. The C-order-flattened
spatial values of consecutive blocks are concatenated in
block_data["values_real"] and, when present, block_data["values_imag"];
block_data["block_lengths"] retains their boundaries. Both use the same PyG
dictionary fields, while dataset.physical_data_descriptions[name].data_type retains the
mathematical distinction.
Dense orbital matrices
All dense orbital-matrix data types use only the common value fields. For an
orbital matrix with n_orbitals orbitals:
data_type="orb_matrix"usesn_orbitals**2values in full-matrix C order.data_type="herm_orb_matrix"usesn_orbitals * (n_orbitals + 1) // 2values from the upper triangle, including the diagonal, in NumPytriu_indicesrow-major order; the omitted lower triangle is its conjugate transpose.data_type="triu_orb_matrix"uses the same packed upper order and value count; the omitted strict lower triangle is zero.
Orbital vectors
data_type="orb_vector" also uses only the common value fields. Its orbital
axis becomes the leading values axis, so n_values is the total orbital count
for the entry's basis role.
Uniform volumetric data
data_type="uniform_volumetric" is real and adds:
uniform_data["origin"] float32 [1, 3]
uniform_data["step_vectors"] float32 [1, 3, 3]
uniform_data["shape"] int64 [1, 3]
The three grid axes are flattened in C order into
n_values = grid_shape[0] * grid_shape[1] * grid_shape[2] value rows. The
stored shape reconstructs those axes; grid periodicity is the sample's pbc.
Quadrature volumetric data
data_type="quadrature_volumetric" is real and adds:
quadrature_data["coordinates"] float32 [n_values, 3]
quadrature_data["weights"] float32 [n_values]
Each value row is aligned with one explicit Cartesian quadrature point and weight.
Batching and connectivity
The Dataset output is edge-free: it has no edge_index. A PyG DataLoader
concatenates atom, block, and value fields; converts the length-one count fields
into per-sample arrays; and increments atom_pair_index from sample-local to
batch-global atom indices. Standard PyG batch and ptr fields identify the
atom partition.
create_data_loader() forms edge-free batches or explicitly uses ConnectivityCollator to attach directed model connectivity during CPU collation, while add_connectivity() attaches it to an existing CPU or CUDA Batch. Connectivity is stored as edge_index with shape [2, n_edges] and integer edge_shifts with shape [n_edges, 3]. Models reconstruct differentiable edge displacements from pos, cell, and edge_shifts; displacement vectors are not stored by the Dataset.
add_basis_atom_pairs(batch, basis_set) independently generates output requests from geometry and complete numerical-basis cutoffs. It attaches top-level atom_pair_index and pair_shifts, retaining onsite and Hermitian-half pairs within the sum of the two atomic radii. It never reads labels or connectivity. reindex_herm_block_sparse() aligns Hermitian matrix labels to those keys, conjugate-transposes reverse partners, fills absent partner pairs with zeros, and recomputes packed counts. The basis-radius rule is an output truncation choice rather than an exact sparsity theorem for every operator.
add_nao_overlap() may similarly calculate numerical-basis overlap for an already collated CPU Batch. It passes the batch's packed atom arrays through a Spline or Uniform calculator's internal batch execution and attaches the result as BlockSparseOrbData. When a consumer only needs Γ, add_nao_gamma_overlap() attaches its OrbData(values_real, num_values) representation; add_nao_cholesky() directly attaches the packed upper factor. Temporary NumPy batch arrays remain an internal bridge rather than a parallel public matrix hierarchy, and the physics and native modules do not depend on PyG.
All tensors returned by a Dataset retain ELFES physical units and conventions.
Dataset conversion does not normalize targets, generate model connectivity, or
change the real spherical-harmonic basis. Training code can explicitly compose
the DataLoader and connectivity APIs published here with the accumulators in
elfes.data.stats without changing the stored samples.
ConnectivityCollator
dataclass
ConnectivityCollator(cutoff: float, cpu_threads: int | None = None)
Build CPU connectivity per Batch inside a DataLoader worker.
HDF5Dataset
HDF5Dataset(
paths: StrPath | Sequence[StrPath],
*,
physical_data_selection: Collection[str] | None = None,
)
Bases: Dataset[Data]
On-disk PyG view of one logical ELFES HDF5 dataset.
The input paths are ordered shards of the same logical dataset. Construction
reads their sample IDs and shared dataset definition, but numerical sample
data remains on disk. Integer and batched indexing read the requested
quantity-major arrays directly into continuous storage and return lightweight
edge-free Data views in the requested order without constructing physical
Sample objects.
The common returned Data structure is documented in elfes.data.
HDF5 handles are opened on first access within each process, so DataLoader
workers do not share live handles. Multi-worker DataLoaders should use the
forkserver multiprocessing context and persistent workers. Call close()
when the dataset is no longer needed.
Parameters:
-
(pathsStrPath | Sequence[StrPath]) –One HDF5 path or an ordered sequence of shard paths.
-
(physical_data_selectionCollection[str] | None, default:None) –Physical data to read, selected by globally unique name.
Noneselects all data; an empty collection selects none.
Attributes:
-
sample_ids–Sample IDs in logical dataset order.
-
basis_sets–Dataset-level basis sets keyed by role.
-
physical_data_descriptions–Definitions of physical data returned by the Dataset.
-
physical_data_names–Physical data returned by the Dataset.
-
atomic_basis_tables–Atomic bases indexed by the atomic-basis IDs stored in
Data.
close
close() -> None
Close HDF5 handles opened by the current process.
InMemoryDataset
InMemoryDataset(
collated_data: Data,
slice_dict: dict[str, Any],
*,
sample_ids: tuple[str, ...],
basis_sets: dict[str, BasisSet],
physical_data_descriptions: dict[str, PhysicalDataDescription],
physical_data_names: Collection[str],
)
Bases: Dataset[Data]
In-memory ELFES dataset backed by one collated PyG tensor store.
from_hdf5() bulk-reads the selected quantity-major arrays from all ordered
shards and converts them directly into a combined PyG Data plus nested
sample boundaries. It does not retain HDF5 handles or construct a physical
Sample for every row.
Integer indexing uses PyG separate() to return an edge-free sample whose
tensors view the combined storage. Atom indices remain sample-local until a
standard PyG DataLoader constructs a real batch; batched indexing applies the
same operation to the complete requested index sequence.
The common returned Data structure is documented in elfes.data.
Parameters:
-
(collated_dataData) –Combined PyG data for every sample.
-
(slice_dictdict[str, Any]) –Nested sample boundaries for fields in
collated_data. -
(sample_idstuple[str, ...]) –Sample IDs in logical dataset order.
-
(basis_setsdict[str, BasisSet]) –Dataset-level basis sets keyed by role.
-
(physical_data_descriptionsdict[str, PhysicalDataDescription]) –Definitions of physical data in
collated_data. -
(physical_data_namesCollection[str]) –Physical data present in
collated_data.
Attributes:
-
sample_ids–Sample IDs in logical dataset order.
-
basis_sets–Dataset-level basis sets keyed by role.
-
physical_data_descriptions–Definitions of physical data returned by the Dataset.
-
physical_data_names–Physical data returned by the Dataset.
-
atomic_basis_tables–Atomic bases indexed by the atomic-basis IDs stored in
Data.
from_hdf5
classmethod
from_hdf5(
paths: StrPath | Sequence[StrPath],
*,
physical_data_selection: Collection[str] | None = None,
) -> Self
Read and convert complete HDF5 shards into memory.
Parameters:
-
(pathsStrPath | Sequence[StrPath]) –One HDF5 path or an ordered sequence of shard paths.
-
(physical_data_selectionCollection[str] | None, default:None) –Physical data to read, selected by globally unique name.
Noneselects all data; an empty collection selects none.
BlockSparseOrbData
Bases: TypedDict
Pair-indexed block-sparse orbital values in packed PyG storage.
Here, n_samples is one for an individual Data and the batch size for a
Batch; n_blocks and n_values are the total block and flattened-value
counts across those samples. The mapping defines only the ragged block
structure: Dataset entries use physical orbital-matrix coordinates, while
model operations may reuse the same structure for coupled coordinates.
Attributes:
-
num_values(Tensor) –Per-sample value counts with shape
[n_samples]. -
values_real(Tensor) –Real flattened block values with shape
[n_values, *extra_shape]. -
values_imag(NotRequired[Tensor]) –Optional imaginary flattened block values with shape
[n_values, *extra_shape]. -
num_blocks(Tensor) –Per-sample block counts with shape
[n_samples]. -
atom_pair_index(Tensor) –Row- and column-atom indices with shape
[2, n_blocks]. -
pair_shifts(Tensor) –Column-atom cell shifts with shape
[n_blocks, 3]. -
block_lengths(Tensor) –Packed value counts with shape
[n_blocks].
OrbData
Bases: TypedDict
Packed PyG values of an orbital vector or matrix.
Here, n_samples is one for an individual Data and the batch size for a
Batch; n_values is the total packed value count across those samples.
Attributes:
-
num_values(Tensor) –Per-sample value counts with shape
[n_samples]. -
values_real(Tensor) –Real packed values with shape
[n_values, *extra_shape]. -
values_imag(NotRequired[Tensor]) –Optional imaginary packed values with shape
[n_values, *extra_shape].
QuadratureVolumetricData
Bases: TypedDict
Real values, coordinates, and weights of quadrature grids.
Here, n_samples is one for an individual Data and the batch size for a
Batch; n_values is the total quadrature-point count across those samples.
Attributes:
-
num_values(Tensor) –Per-sample quadrature-point counts with shape
[n_samples]. -
values_real(Tensor) –Real values with shape
[n_values, *extra_shape]. -
coordinates(Tensor) –Cartesian point coordinates with shape
[n_values, 3]. -
weights(Tensor) –Integration weights with shape
[n_values].
UniformVolumetricData
Bases: TypedDict
Real values and geometry of uniform volumetric grids.
Here, n_samples is one for an individual Data and the batch size for a
Batch; n_values is the total grid-point count across those samples.
Attributes:
-
num_values(Tensor) –Per-sample grid-point counts with shape
[n_samples]. -
values_real(Tensor) –Real values with shape
[n_values, *extra_shape]. -
origin(Tensor) –Grid origins with shape
[n_samples, 3]. -
step_vectors(Tensor) –Grid step vectors with shape
[n_samples, 3, 3]. -
shape(Tensor) –Grid shapes with shape
[n_samples, 3].
SampleToData
dataclass
SampleToData(basis_sets: Mapping[str, BasisSet])
Convert one physical Sample into edge-free PyG data.
Geometry and electronic quantities become Torch tensors, while every atom receives its atomic-basis-table index and orbital count for each available basis role. Atom-pair indices remain local to the sample; a later PyG DataLoader performs the index increments required for a real batch.
Parameters:
-
(basis_setsMapping[str, BasisSet]) –Dataset-level basis sets keyed by role. Every converted sample must use the same definitions.
add_basis_atom_pairs
add_basis_atom_pairs(batch: Batch, basis_set: BasisSet) -> Batch
Attach Hermitian-half atom pairs within the sum of numerical-basis radii.
Each atomic radius is the largest shell cutoff in Å. Include all onsite
blocks and pairs satisfying distance < radius_i + radius_j, retaining
the lexicographically first (i, j, S) of each Hermitian partner pair.
This is an explicit output truncation rule, not a claim that every
Hamiltonian vanishes outside these distances.
Only geometry (pos, cell, pbc, atomic_numbers, ptr) is read.
Attach atom_pair_index: [2, P] and pair_shifts: [P, 3], grouped by
sample, on the input device. Labels and model connectivity are untouched.
A layout-only or Gaussian basis cannot supply finite numerical cutoffs.
reindex_herm_block_sparse
reindex_herm_block_sparse(
data: BlockSparseOrbData,
atom_pair_index: Tensor,
pair_shifts: Tensor,
orb_counts: Tensor,
ptr: Tensor,
) -> BlockSparseOrbData
Gather requested blocks, conjugate-transposing partners and filling zeros.
data stores unique lexicographically first Hermitian partner keys, as
returned for an ELFES Hermitian block-sparse matrix. Missing partner pairs
denote zeros. Requested keys may use either direction, in arbitrary order
within each sample; samples must remain grouped in batch order. All atom
indices are batch-global. orb_counts: [N] gives the spatial block shapes
and ptr: [B+1] partitions the atoms. Real/imaginary extra axes are retained.
Returns values in exactly the requested order with recomputed block/value counts. Supports CPU/CUDA and gradients with respect to stored values.
add_connectivity
add_connectivity(
batch: Batch, cutoff: float, *, cpu_threads: int | None = None
) -> Batch
Attach full directed connectivity to a CPU or CUDA batch.
Only discrete connectivity is attached. Models should reconstruct edge
displacements from pos, cell, and edge_shifts so forces
remain differentiable with respect to positions.
create_data_loader
create_data_loader(
dataset: Dataset[Data] | Sequence[Data],
*,
batch_size: int,
with_connectivity: bool,
cutoff: float | None = None,
cpu_threads: int | None = None,
shuffle: bool = False,
num_workers: int = 0,
pin_memory: bool = False,
prefetch_factor: int | None = None,
drop_last: bool = False,
) -> DataLoader[Batch]
Return a DataLoader that forms PyG batches with explicit connectivity.
Worker-based loading uses forkserver so on-disk datasets never inherit
open HDF5 handles, and keeps workers alive across iterations. When
with_connectivity is true, cutoff is required and connectivity is built
independently inside each worker using cpu_threads threads. Otherwise,
batches remain edge-free and cutoff and cpu_threads must not be set.
add_nao_cholesky
add_nao_cholesky(
batch: Batch,
calculator: SplineNumericalOverlapCalculator | UniformNumericalOverlapCalculator,
*,
name: str = "cholesky",
) -> Batch
Calculate and attach packed upper Cholesky factors to a CPU PyG batch.
add_nao_gamma_overlap
add_nao_gamma_overlap(
batch: Batch,
calculator: SplineNumericalOverlapCalculator | UniformNumericalOverlapCalculator,
*,
name: str = "overlap",
) -> Batch
Calculate and attach packed real Γ overlap to a CPU PyG batch.
add_nao_overlap
add_nao_overlap(
batch: Batch,
calculator: SplineNumericalOverlapCalculator | UniformNumericalOverlapCalculator,
*,
name: str = "overlap",
) -> Batch
Calculate and attach numerical-basis overlap to a CPU PyG batch.