@@ -129,6 +129,8 @@ class MDIOAccessor:
129129 we can directly unpack it and use it further down our code.
130130 """
131131
132+ _stats_keys = {"mean" , "std" , "rms" , "min" , "max" }
133+
132134 _array_load_function_mapper = {
133135 "zarr" : open_zarr_array ,
134136 "dask" : open_zarr_array_dask ,
@@ -154,16 +156,13 @@ def __init__(
154156
155157 # Set private attributes for public interface.
156158 # Pep8 complains because they are defined outside __init__
157- self ._binary_header = None
158159 self ._chunks = None
159160 self ._live_mask = None
160161 self ._root = None
161162 self ._n_dim = None
162163 self ._orig_chunks = None
163164 self ._store = None
164165 self ._shape = None
165- self ._stats = None
166- self ._text_header = None
167166 self ._trace_count = None
168167
169168 # Private attributes
@@ -205,13 +204,25 @@ def _validate_store(self, storage_options):
205204
206205 def _connect (self ):
207206 """Open the zarr root."""
208- if self .mode in {"r" , "r+" }:
209- self .root = zarr .open_consolidated (store = self .store , mode = self .mode )
210- elif self .mode == "w" :
211- self .root = zarr .open (store = self .store , mode = "r+" )
212- else :
213- msg = f"Invalid mode: { self .mode } "
214- raise ValueError (msg )
207+ try :
208+ if self .mode in {"r" , "r+" }:
209+ self .root = zarr .open_consolidated (store = self .store , mode = self .mode )
210+ elif self .mode == "w" :
211+ self .root = zarr .open (store = self .store , mode = "r+" )
212+ else :
213+ msg = f"Invalid mode: { self .mode } "
214+ raise ValueError (msg )
215+ except KeyError as e :
216+ msg = (
217+ f"MDIO file not found or corrupt at { self .store .path } . "
218+ "Please check the URL or ensure it is not a deprecated "
219+ "version of MDIO file."
220+ )
221+ raise MDIONotFoundError (msg ) from e
222+
223+ def _consolidate_metadata (self ) -> None :
224+ """Flush optimized MDIO metadata, run after modifying it."""
225+ zarr .consolidate_metadata (self .root .store )
215226
216227 def _deserialize_grid (self ):
217228 """Deserialize grid from Zarr metadata."""
@@ -220,12 +231,6 @@ def _deserialize_grid(self):
220231 def _set_attributes (self ):
221232 """Deserialize attributes from Zarr metadata."""
222233 self .trace_count = self .root .attrs ["trace_count" ]
223- self .stats = {
224- key : self .root .attrs [key ] for key in ["mean" , "std" , "rms" , "min" , "max" ]
225- }
226-
227- self .text_header = self ._metadata_group .attrs ["text_header" ]
228- self .binary_header = self ._metadata_group .attrs ["binary_header" ]
229234
230235 # Grid based attributes
231236 self .shape = self .grid .shape
@@ -332,26 +337,28 @@ def trace_count(self, value: int) -> None:
332337 @property
333338 def text_header (self ) -> list :
334339 """Get seismic text header."""
335- return self ._text_header
340+ return self ._metadata_group . attrs [ "text_header" ]
336341
337342 @text_header .setter
338343 def text_header (self , value : list ) -> None :
339344 """Validate and set seismic text header."""
340- if not isinstance (value , list ):
345+ if not isinstance (value , list ) or len ( value ) != 40 :
341346 raise AttributeError ("Text header must be a list of str with 40 elements" )
342- self ._text_header = value
347+ self ._metadata_group .attrs ["text_header" ] = value
348+ self ._consolidate_metadata ()
343349
344350 @property
345351 def binary_header (self ) -> dict :
346352 """Get seismic binary header metadata."""
347- return self ._binary_header
353+ return self ._metadata_group . attrs [ "binary_header" ]
348354
349355 @binary_header .setter
350356 def binary_header (self , value : dict ) -> None :
351357 """Validate and set seismic binary header metadata."""
352358 if not isinstance (value , dict ):
353359 raise AttributeError ("Binary header has to be a dictionary type collection" )
354- self ._binary_header = value
360+ self ._metadata_group .attrs ["binary_header" ] = value
361+ self ._consolidate_metadata ()
355362
356363 @property
357364 def chunks (self ) -> tuple [int , ...]:
@@ -366,12 +373,16 @@ def chunks(self, value: tuple[int, ...]) -> None:
366373 @property
367374 def stats (self ) -> dict :
368375 """Get global statistics like min/max/rms/std."""
369- return self ._stats
376+ return { key : self .root . attrs [ key ] for key in self . _stats_keys }
370377
371378 @stats .setter
372379 def stats (self , value : dict ) -> None :
373380 """Set global statistics like min/max/rms/std."""
374- self ._stats = value
381+ if not isinstance (value , dict ) or not self ._stats_keys .issubset (value .keys ()):
382+ msg = f"For settings status, you must provide keys: { self ._stats_keys } "
383+ raise AttributeError (msg )
384+ self .root .attrs .update (value )
385+ self ._consolidate_metadata ()
375386
376387 @property
377388 def _metadata_group (self ) -> zarr .Group :
@@ -404,6 +415,7 @@ def __getitem__(self, item: int | tuple) -> npt.ArrayLike | da.Array | tuple:
404415 def __setitem__ (self , key : int | tuple , value : npt .ArrayLike ) -> None :
405416 """Data setter."""
406417 self ._traces [key ] = value
418+ self ._live_mask [key ] = True
407419
408420 def coord_to_index (
409421 self ,
@@ -644,10 +656,10 @@ def __init__(
644656 memory_cache_size : int = 0 ,
645657 disk_cache : bool = False ,
646658 ): # TODO: Disabled all caching by default, sometimes causes performance issues
647- """Initialize super class with `r+ ` permission."""
659+ """Initialize accessor class with `w ` permission."""
648660 super ().__init__ (
649661 mdio_path_or_buffer = mdio_path_or_buffer ,
650- mode = "r+ " ,
662+ mode = "w " ,
651663 access_pattern = access_pattern ,
652664 storage_options = storage_options ,
653665 return_metadata = return_metadata ,
0 commit comments