11"""Convert MDIO v1 schema Dataset to Xarray DataSet and write it in Zarr."""
22
3- import logging
4-
53import numcodecs
64import numpy as np
75import zarr
2725from mdio .converters .type_converter import to_numpy_dtype
2826from mdio .core .zarr_io import zarr_warnings_suppress_unstable_numcodecs_v3
2927
30- logger = logging .getLogger (__name__ )
31-
3228
3329def _import_numcodecs_zfpy () -> "type[numcodecs.ZFPY]" :
3430 """Helper to import the optional dependency at runtime."""
@@ -212,18 +208,31 @@ def to_xarray_dataset(mdio_ds: Dataset) -> xr_Dataset: # noqa: PLR0912, PLR0915
212208 dtype = to_numpy_dtype (v .data_type )
213209 original_chunks = _get_zarr_chunks (v , all_named_dims = all_named_dims )
214210
211+ # Determine the final encoding chunks early to ensure Dask array alignment
212+ # For non-shardable types with sharding configured, we'll use shard_shape as chunk_shape
213+ shard_shape = _get_zarr_shards (v )
214+ is_non_shardable = isinstance (v .data_type , StructuredType ) or dtype .kind == "V"
215+ zarr_format = zarr .config .get ("default_zarr_format" )
216+
217+ # Determine what the final encoding chunks will be
218+ if shard_shape is not None and zarr_format == ZarrFormat .V3 and is_non_shardable :
219+ encoding_chunks = shard_shape
220+ else :
221+ encoding_chunks = original_chunks
222+
215223 # For efficient lazy array creation with Dask use larger chunks to minimize the task graph size
216- # Initialize with original chunks for lazy array creation
217- lazy_chunks = original_chunks
218- if shape != original_chunks :
219- # Compute automatic chunk sizes based on heuristics, respecting original chunks where possible
220- auto_chunks = normalize_chunks ("auto" , shape = shape , dtype = dtype , previous_chunks = original_chunks )
224+ # Initialize with encoding chunks for lazy array creation to ensure alignment
225+ lazy_chunks = encoding_chunks
226+ if shape != encoding_chunks :
227+ # Compute automatic chunk sizes based on heuristics, respecting encoding chunks where possible
228+ auto_chunks = normalize_chunks ("auto" , shape = shape , dtype = dtype , previous_chunks = encoding_chunks )
221229
222230 # Extract the primary (uniform) chunk size for each dimension, ignoring any variable remainder chunks
223231 uniform_auto = tuple (dim_chunks [0 ] for dim_chunks in auto_chunks )
224232
225- # Ensure creation chunks are at least as large as the original chunks to avoid splitting chunks
226- lazy_chunks = tuple (max (auto , orig ) for auto , orig in zip (uniform_auto , original_chunks , strict = True ))
233+ # Ensure creation chunks are at least as large as the encoding chunks to avoid splitting chunks
234+ # This is critical for proper Zarr write alignment
235+ lazy_chunks = tuple (max (auto , enc ) for auto , enc in zip (uniform_auto , encoding_chunks , strict = True ))
227236
228237 data = dask_array .full (shape = shape , dtype = dtype , chunks = lazy_chunks , fill_value = _get_fill_value (v .data_type ))
229238
@@ -238,36 +247,13 @@ def to_xarray_dataset(mdio_ds: Dataset) -> xr_Dataset: # noqa: PLR0912, PLR0915
238247 if v .long_name :
239248 data_array .attrs ["long_name" ] = v .long_name
240249
241- zarr_format = zarr . config . get ( "default_zarr_format" )
250+ # Use pre-computed zarr_format from above
242251 fill_value_key = "_FillValue" if zarr_format == ZarrFormat .V2 else "fill_value"
243252 fill_value = _get_fill_value (v .data_type ) if v .name != "headers" else None
244253
245- # Handle sharding for Zarr v3
246- # Note: Sharding is only supported for simple scalar types, not structured types
247- # (e.g., headers) or void/bytes types (e.g., raw_headers) because Zarr's sharding
248- # codec cannot handle these dtypes. For non-shardable types, we use the shard shape
249- # as the chunk shape to maintain consistent I/O patterns across all variables.
250- shard_shape = _get_zarr_shards (v )
251- # Check for non-shardable types: structured types and void/bytes types (dtype.kind == 'V')
252- is_non_shardable = isinstance (v .data_type , StructuredType ) or dtype .kind == "V"
253-
254- # When sharding is configured for Zarr v3 but variable has non-shardable dtype,
255- # use shard shape as chunk shape to maintain consistent I/O patterns
256- if shard_shape is not None and zarr_format == ZarrFormat .V3 and is_non_shardable :
257- dtype_desc = "structured" if isinstance (v .data_type , StructuredType ) else "void/bytes"
258- logger .warning (
259- "Sharding is not supported for %s dtypes. Variable '%s' will use regular "
260- "chunking with chunk shape %s (shard shape) instead of sharding." ,
261- dtype_desc ,
262- v .name ,
263- shard_shape ,
264- )
265- chunks_to_use = shard_shape
266- else :
267- chunks_to_use = original_chunks
268-
254+ # Use pre-computed encoding_chunks from above (handles non-shardable types)
269255 encoding = {
270- "chunks" : chunks_to_use ,
256+ "chunks" : encoding_chunks ,
271257 fill_value_key : fill_value ,
272258 }
273259
0 commit comments