22
33from __future__ import annotations
44
5- import copy
5+ import logging
66from abc import ABC
77from abc import abstractmethod
88from typing import TYPE_CHECKING
1919from mdio .builder .schemas .v1 .variable import CoordinateMetadata
2020from mdio .builder .schemas .v1 .variable import VariableMetadata
2121from mdio .builder .templates .types import CoordinateSpec
22+ from mdio .builder .templates .types import DimCoordinateTypes
2223
2324if TYPE_CHECKING :
2425 from mdio .builder .schemas .v1 .dataset import Dataset
2526 from mdio .builder .templates .types import SeismicDataDomain
2627
28+ logger = logging .getLogger (__name__ )
29+
2730
2831class AbstractDatasetTemplate (ABC ):
2932 """Abstract base class that defines the template method for Dataset building factory.
@@ -46,12 +49,6 @@ def __init__(self, data_domain: SeismicDataDomain) -> None:
4649 self ._var_chunk_shape : tuple [int , ...] = ()
4750 self .synthesize_missing_dims : tuple [str , ...] = ()
4851
49- # TEMPORARY (removed with declare_coordinate_specs): set when grid overrides mutate this
50- # template in-place (dims collapsed into 'trace', extra coordinates added). Once mutated,
51- # the runtime layout intentionally diverges from the static declare_coordinate_specs()
52- # contract, so the drift guard in build_dataset() must not run.
53- self ._grid_overrides_applied : bool = False
54-
5552 self ._builder : MDIODatasetBuilder | None = None
5653 self ._dim_sizes : tuple [int , ...] = ()
5754 self ._units : dict [str , AllUnitModel ] = {}
@@ -114,6 +111,38 @@ def declare_coordinate_specs(self) -> tuple[CoordinateSpec, ...]:
114111 )
115112 return tuple (specs )
116113
114+ def declare_dim_coordinate_types (self ) -> DimCoordinateTypes :
115+ """Declare data types for each dimension coordinate in this template.
116+
117+ Returns:
118+ A dictionary mapping dimension name to ScalarType.
119+ """
120+ return dict .fromkeys (self .dimension_names , ScalarType .INT32 )
121+
122+ def _dim_dtype (self , name : str ) -> ScalarType :
123+ """Return the declared dtype for a dimension coordinate.
124+
125+ Args:
126+ name: The dimension name.
127+
128+ Returns:
129+ The declared ScalarType, defaulting to INT32.
130+ """
131+ return self .declare_dim_coordinate_types ().get (name , ScalarType .INT32 )
132+
133+ def _add_dimension_coordinate (self , name : str ) -> None :
134+ """Add a single dimension coordinate.
135+
136+ Args:
137+ name: The dimension name.
138+ """
139+ self ._builder .add_coordinate (
140+ name ,
141+ dimensions = (name ,),
142+ data_type = self ._dim_dtype (name ),
143+ metadata = CoordinateMetadata (units_v1 = self .get_unit_by_key (name )),
144+ )
145+
117146 def build_dataset (
118147 self ,
119148 name : str ,
@@ -122,6 +151,12 @@ def build_dataset(
122151 ) -> Dataset :
123152 """Template method that builds the dataset.
124153
154+ .. deprecated:: 1.2
155+ ``build_dataset`` is deprecated and is planned for removal in a future release. SEG-Y
156+ ingestion now builds datasets from a resolved schema via the schema-driven
157+ factory (:func:`mdio.ingestion.dataset_factory.build_mdio_dataset`); use
158+ :func:`mdio.segy_to_mdio` for ingestion.
159+
125160 Args:
126161 name: The name of the dataset.
127162 sizes: The sizes of the dimensions.
@@ -133,6 +168,11 @@ def build_dataset(
133168 Raises:
134169 ValueError: If coordinate already exists from subclass override.
135170 """
171+ logger .warning (
172+ "AbstractDatasetTemplate.build_dataset is deprecated as of 1.2 and is planned for "
173+ "removal in a future release; SEG-Y ingestion builds datasets via the schema-driven factory. "
174+ "Use `mdio.segy_to_mdio` for ingestion."
175+ )
136176 self ._dim_sizes = sizes
137177
138178 attributes = self ._load_dataset_attributes () or {}
@@ -154,10 +194,7 @@ def build_dataset(
154194 except ValueError as exc : # coordinate may already exist
155195 if "same name twice" not in str (exc ):
156196 raise
157- # Skip the static drift guard when grid overrides have transformed the template: the
158- # runtime layout no longer matches the declared (override-free) specs by design.
159- if not self ._grid_overrides_applied :
160- self ._validate_declared_coordinate_specs ()
197+ self ._validate_declared_coordinate_specs ()
161198 self ._add_variables ()
162199 self ._add_trace_mask ()
163200
@@ -174,30 +211,6 @@ def add_units(self, units: dict[str, AllUnitModel]) -> None:
174211 raise ValueError (msg )
175212 self ._units |= units
176213
177- def apply_resolved_dimensions (
178- self ,
179- dim_names : tuple [str , ...],
180- chunk_shape : tuple [int , ...],
181- ) -> None :
182- """Update the template's dimension layout from a resolved schema.
183-
184- Supported entry point for the ingestion pipeline to push back dimension names
185- and chunk shape after the SchemaResolver has applied grid overrides
186- (e.g. NonBinned, HasDuplicates), instead of mutating private attributes.
187-
188- Args:
189- dim_names: Final ordered dimension names.
190- chunk_shape: Chunk shape matching ``dim_names`` length.
191-
192- Raises:
193- ValueError: If ``len(chunk_shape) != len(dim_names)``.
194- """
195- if len (chunk_shape ) != len (dim_names ):
196- msg = f"chunk_shape length { len (chunk_shape )} does not match dim_names length { len (dim_names )} "
197- raise ValueError (msg )
198- self ._dim_names = tuple (dim_names )
199- self ._var_chunk_shape = tuple (chunk_shape )
200-
201214 def _validate_declared_coordinate_specs (self ) -> None :
202215 """Fail the build if :meth:`declare_coordinate_specs` drifted from the built coordinates.
203216
@@ -206,11 +219,8 @@ def _validate_declared_coordinate_specs(self) -> None:
206219 :meth:`_add_coordinates`, this guard ensures the two never diverge in name, dimensions,
207220 or dtype. The ingestion ``SchemaResolver`` trusts the declared specs, so silent drift
208221 would corrupt resolved schemas. The check runs for every template (built-in and
209- user-defined) on every ``build_dataset`` call that does not apply grid overrides. Grid
210- overrides mutate the template in-place (collapsing dims into ``trace`` and adding
211- coordinates), so the runtime layout intentionally diverges from the declared specs and
212- the guard is skipped for those builds. It is removed once ``_add_coordinates`` is derived
213- from the resolved schema and the duplication no longer exists.
222+ user-defined) on every ``build_dataset`` call. It is removed once ``_add_coordinates``
223+ is derived from the resolved schema and the duplication no longer exists.
214224
215225 Raises:
216226 ValueError: If the declared specs do not match the built non-dimension coordinates.
@@ -266,32 +276,32 @@ def trace_domain(self) -> str:
266276 @property
267277 def spatial_dimension_names (self ) -> tuple [str , ...]:
268278 """Returns the names of the dimensions excluding the last axis."""
269- return copy . deepcopy ( self ._dim_names [:- 1 ])
279+ return self ._dim_names [:- 1 ]
270280
271281 @property
272282 def dimension_names (self ) -> tuple [str , ...]:
273283 """Returns the names of the dimensions."""
274- return copy . deepcopy ( self ._dim_names )
284+ return self ._dim_names
275285
276286 @property
277287 def calculated_dimension_names (self ) -> tuple [str , ...]:
278- """Returns the names of the dimensions."""
279- return copy . deepcopy ( self ._calculated_dims )
288+ """Returns the names of the calculated dimensions."""
289+ return self ._calculated_dims
280290
281291 @property
282292 def physical_coordinate_names (self ) -> tuple [str , ...]:
283293 """Returns the names of the physical (world) coordinates."""
284- return copy . deepcopy ( self ._physical_coord_names )
294+ return self ._physical_coord_names
285295
286296 @property
287297 def logical_coordinate_names (self ) -> tuple [str , ...]:
288298 """Returns the names of the logical (grid) coordinates."""
289- return copy . deepcopy ( self ._logical_coord_names )
299+ return self ._logical_coord_names
290300
291301 @property
292302 def coordinate_names (self ) -> tuple [str , ...]:
293303 """Returns names of all coordinates."""
294- return copy . deepcopy ( self ._physical_coord_names + self ._logical_coord_names )
304+ return self ._physical_coord_names + self ._logical_coord_names
295305
296306 @property
297307 def full_chunk_shape (self ) -> tuple [int , ...]:
@@ -354,6 +364,15 @@ def _load_dataset_attributes(self) -> dict[str, Any]:
354364 The dataset attributes as a dictionary
355365 """
356366
367+ @property
368+ def units (self ) -> dict [str , AllUnitModel ]:
369+ """Return a copy of the template's configured units.
370+
371+ Read-only view for collaborators (e.g. ingestion unit resolution) so they do not
372+ reach into the private ``_units`` mapping.
373+ """
374+ return dict (self ._units )
375+
357376 def get_unit_by_key (self , key : str ) -> AllUnitModel | None :
358377 """Get units by variable/dimension/coordinate name. Returns None if not found."""
359378 return self ._units .get (key , None )
@@ -375,12 +394,7 @@ def _add_coordinates(self) -> None:
375394 """
376395 # Add dimension coordinates
377396 for name in self ._dim_names :
378- self ._builder .add_coordinate (
379- name ,
380- dimensions = (name ,),
381- data_type = ScalarType .INT32 ,
382- metadata = CoordinateMetadata (units_v1 = self .get_unit_by_key (name )),
383- )
397+ self ._add_dimension_coordinate (name )
384398
385399 # Add non-dimension coordinates
386400 # Note: coordinate_names may be modified at runtime by grid overrides,
@@ -400,7 +414,7 @@ def _add_coordinates(self) -> None:
400414 raise
401415
402416 def _add_trace_mask (self ) -> None :
403- """Add trace mask variables ."""
417+ """Add trace mask variable ."""
404418 self ._builder .add_variable (
405419 name = "trace_mask" ,
406420 dimensions = self .spatial_dimension_names ,
@@ -410,7 +424,7 @@ def _add_trace_mask(self) -> None:
410424 )
411425
412426 def _add_trace_headers (self , header_dtype : StructuredType ) -> None :
413- """Add trace mask variables ."""
427+ """Add trace headers variable ."""
414428 chunk_grid = RegularChunkGrid (configuration = RegularChunkShape (chunk_shape = self .full_chunk_shape [:- 1 ]))
415429 self ._builder .add_variable (
416430 name = "headers" ,
0 commit comments