2626
2727
2828from tests .integration .testing_helpers import get_values
29- from tests .integration .testing_helpers import validate_variable
29+ from tests .integration .testing_helpers import validate_xr_variable
3030
3131from mdio import __version__
3232from mdio .api .io import open_mdio
3333from mdio .api .io import to_mdio
3434from mdio .builder .schemas .v1 .stats import CenteredBinHistogram
3535from mdio .builder .schemas .v1 .stats import SummaryStatistics
36+ from mdio .builder .templates .seismic_3d_poststack import Seismic3DPostStackTemplate
3637from mdio .converters .mdio import mdio_to_segy
3738from mdio .core import Dimension
3839from mdio .creators .mdio import create_empty
3940
41+ UNITS_NONE = None
42+ UNITS_METER = LengthUnitModel (length = LengthUnitEnum .METER )
43+ UNITS_SECOND = TimeUnitModel (time = TimeUnitEnum .SECOND )
44+ UNITS_METER_PER_SECOND = SpeedUnitModel (speed = SpeedUnitEnum .METER_PER_SECOND )
45+ UNITS_FOOT = LengthUnitModel (length = LengthUnitEnum .FOOT )
46+ UNITS_FEET_PER_SECOND = SpeedUnitModel (speed = SpeedUnitEnum .FEET_PER_SECOND )
47+
48+
49+ class PostStack3DVelocityTemplate (Seismic3DPostStackTemplate ):
50+ """Custom template that uses 'velocity' as the default variable name instead of 'amplitude'."""
51+
52+ @property
53+ def _default_variable_name (self ) -> str :
54+ """Override the default variable name."""
55+ return "velocity"
56+
57+ def __init__ (self , data_domain : str , is_metric : bool ) -> None :
58+ super ().__init__ (data_domain )
59+ if is_metric :
60+ self ._units .update (
61+ {
62+ "time" : UNITS_SECOND ,
63+ "cdp_x" : UNITS_METER ,
64+ "cdp_y" : UNITS_METER ,
65+ "velocity" : UNITS_METER_PER_SECOND ,
66+ }
67+ )
68+ else :
69+ self ._units .update (
70+ {
71+ "time" : UNITS_SECOND ,
72+ "cdp_x" : UNITS_FOOT ,
73+ "cdp_y" : UNITS_FOOT ,
74+ "velocity" : UNITS_FEET_PER_SECOND ,
75+ }
76+ )
77+
78+ @property
79+ def _name (self ) -> str :
80+ """Override the name of the template."""
81+ domain_suffix = self ._data_domain .capitalize ()
82+ return f"PostStack3DVelocity{ domain_suffix } "
83+
4084
4185class TestCreateEmptyPostStack3DTimeMdio :
4286 """Tests for create_empty_mdio function."""
@@ -58,35 +102,51 @@ def _get_customized_v10_trace_header_spec(cls) -> HeaderSpec:
58102 @classmethod
59103 def _validate_empty_mdio_dataset (cls , ds : xr_Dataset , has_headers : bool ) -> None :
60104 """Validate an empty MDIO dataset structure and content."""
105+ assert ds .name == "PostStack3DVelocityTime"
61106 # Check that the dataset has the expected shape
62107 assert ds .sizes == {"inline" : 345 , "crossline" : 188 , "time" : 1501 }
63108
64109 # Validate the dimension coordinate variables
65- validate_variable (ds , "inline" , ( 345 ,), ( "inline" ,) , np .int32 , range (1 , 346 ), get_values )
66- validate_variable (ds , "crossline" , ( 188 ,), ( "crossline" ,) , np .int32 , range (1 , 189 ), get_values )
67- validate_variable (ds , "time" , ( 1501 ,), ( "time" ,) , np .int32 , range (0 , 3002 , 2 ), get_values )
110+ validate_xr_variable (ds , "inline" , { "inline" : 345 }, UNITS_NONE , np .int32 , range (1 , 346 ), get_values )
111+ validate_xr_variable (ds , "crossline" , { "crossline" : 188 }, UNITS_NONE , np .int32 , range (1 , 189 ), get_values )
112+ validate_xr_variable (ds , "time" , { "time" : 1501 }, UNITS_SECOND , np .int32 , range (0 , 3002 , 2 ), get_values )
68113
69114 # Validate the non-dimensional coordinate variables (should be empty for empty dataset)
70- validate_variable (ds , "cdp_x" , ( 345 , 188 ), ( "inline" , "crossline" ) , np .float64 , None , None )
71- validate_variable (ds , "cdp_y" , ( 345 , 188 ), ( "inline" , "crossline" ) , np .float64 , None , None )
115+ validate_xr_variable (ds , "cdp_x" , { "inline" : 345 , "crossline" : 188 }, UNITS_METER , np .float64 , None , None )
116+ validate_xr_variable (ds , "cdp_y" , { "inline" : 345 , "crossline" : 188 }, UNITS_METER , np .float64 , None , None )
72117
73118 if has_headers :
74119 # Validate the headers (should be empty for empty dataset)
75120 # Infer the dtype from segy_spec and ignore endianness
76121 header_dtype = cls ._get_customized_v10_trace_header_spec ().dtype .newbyteorder ("native" )
77- validate_variable (ds , "headers" , (345 , 188 ), ("inline" , "crossline" ), header_dtype , None , None )
78- validate_variable (ds , "segy_file_header" , (), (), np .dtype ("U1" ), None , None )
122+ validate_xr_variable (ds , "headers" , {"inline" : 345 , "crossline" : 188 }, UNITS_NONE , header_dtype , None , None )
123+ validate_xr_variable (
124+ ds ,
125+ "segy_file_header" ,
126+ dims = {},
127+ units = UNITS_NONE ,
128+ data_type = np .dtype ("U1" ),
129+ expected_values = None ,
130+ actual_value_generator = None ,
131+ )
79132 else :
80133 assert "headers" not in ds .variables
81134 assert "segy_file_header" not in ds .variables
82-
83135 # Validate the trace mask (should be all True for empty dataset)
84- validate_variable (ds , "trace_mask" , ( 345 , 188 ), ( "inline" , "crossline" ) , np .bool_ , None , None )
136+ validate_xr_variable (ds , "trace_mask" , { "inline" : 345 , "crossline" : 188 }, UNITS_NONE , np .bool_ , None , None )
85137 trace_mask = ds ["trace_mask" ].values
86138 assert not np .any (trace_mask ), "All traces should be marked as dead in empty dataset"
87139
88140 # Validate the amplitude data (should be empty)
89- validate_variable (ds , "amplitude" , (345 , 188 , 1501 ), ("inline" , "crossline" , "time" ), np .float32 , None , None )
141+ validate_xr_variable (
142+ ds ,
143+ "velocity" ,
144+ {"inline" : 345 , "crossline" : 188 , "time" : 1501 },
145+ UNITS_METER_PER_SECOND ,
146+ np .float32 ,
147+ None ,
148+ None ,
149+ )
90150
91151 @classmethod
92152 def _create_empty_mdio (cls , create_headers : bool , output_path : Path , overwrite : bool = True ) -> None :
@@ -101,8 +161,10 @@ def _create_empty_mdio(cls, create_headers: bool, output_path: Path, overwrite:
101161 # If later on, we want to export to SEG-Y, we need to provide the trace header spec.
102162 # The HeaderSpec can be either standard or customized.
103163 headers = cls ._get_customized_v10_trace_header_spec () if create_headers else None
164+
165+ # Create an empty MDIO v1 metric post-stack 3D time velocity dataset
104166 create_empty (
105- mdio_template_name = "PostStack3DTime" ,
167+ mdio_template = PostStack3DVelocityTemplate ( data_domain = "time" , is_metric = True ) ,
106168 dimensions = dims ,
107169 output_path = output_path ,
108170 headers = headers ,
@@ -138,7 +200,7 @@ def test_dataset_metadata(self, mdio_with_headers: Path) -> None:
138200 # Check basic metadata attributes
139201 expected_attrs = {
140202 "apiVersion" : __version__ ,
141- "name" : "PostStack3DTime " ,
203+ "name" : "PostStack3DVelocityTime " ,
142204 }
143205 actual_attrs_json = ds .attrs
144206
@@ -159,7 +221,7 @@ def test_dataset_metadata(self, mdio_with_headers: Path) -> None:
159221 assert attributes is not None
160222 assert len (attributes ) == 3
161223 # Validate all attributes provided by the abstract template
162- assert attributes ["defaultVariableName" ] == "amplitude "
224+ assert attributes ["defaultVariableName" ] == "velocity "
163225 assert attributes ["surveyType" ] == "3D"
164226 assert attributes ["gatherType" ] == "stacked"
165227
@@ -203,9 +265,9 @@ def test_overwrite_behavior(self, empty_mdio_dir: Path) -> None:
203265
204266 def test_populate_empty_dataset (self , mdio_with_headers : Path ) -> None :
205267 """Test showing how to populate empty dataset."""
206- # Open an empty PostStack3DTime dataset with SEG-Y 1.0 headers
268+ # Open an empty PostStack3DVelocityTime dataset with SEG-Y 1.0 headers
207269 # NOTES:
208- # When this empty dataset was created from the 'PostStack3DTime ' template and dimensions,
270+ # When this empty dataset was created from the 'PostStack3DVelocityTime ' template and dimensions,
209271 # * 'inline', 'crossline', and 'time' dimension coordinate variables were created and pre-populated
210272 # * 'cdp_x', 'cdp_y' non-dimensional coordinate variables were created
211273 # * 'amplitude' variable was created (the name of this variable is specified in the template)
0 commit comments