@@ -3196,6 +3196,17 @@ def _opsi_write_block_boundaries(
31963196 _write_ndarray_linear_span (stage .maxs , first_block , maxs )
31973197
31983198
3199+ def _opsi_storage_chunk_len (chunk_len : int , block_len : int ) -> int :
3200+ """Return an OPSI sidecar chunk length aligned to whole OPSI blocks."""
3201+ chunk_len = max (1 , int (chunk_len ))
3202+ block_len = max (1 , int (block_len ))
3203+ if chunk_len % block_len == 0 :
3204+ return chunk_len
3205+ if chunk_len >= block_len :
3206+ return (chunk_len // block_len ) * block_len
3207+ return block_len
3208+
3209+
31993210def _opsi_build_stage1 (
32003211 array : blosc2 .NDArray ,
32013212 target : dict ,
@@ -3209,10 +3220,9 @@ def _opsi_build_stage1(
32093220 cparams : dict | blosc2 .CParams | None = None ,
32103221) -> OpsiStageSidecars :
32113222 size = int (array .shape [0 ])
3212- chunk_len = int (array .chunks [0 ])
3223+ source_chunk_len = int (array .chunks [0 ])
32133224 block_len = int (array .blocks [0 ])
3214- if chunk_len % block_len != 0 :
3215- raise ValueError ("OPSI requires chunk length to be a multiple of block length" )
3225+ chunk_len = _opsi_storage_chunk_len (source_chunk_len , block_len )
32163226 nblocks = math .ceil (size / block_len )
32173227 stage = _opsi_stage_create (
32183228 array , token , kind , cycle , size , nblocks , dtype , persistent , chunk_len , block_len , cparams
@@ -3374,7 +3384,7 @@ def _build_opsi_descriptor(
33743384 "positions_path" : positions_sidecar ["path" ],
33753385 "mins_path" : mins_sidecar ["path" ],
33763386 "maxs_path" : maxs_sidecar ["path" ],
3377- "chunk_len" : int (array .chunks [0 ]),
3387+ "chunk_len" : _opsi_storage_chunk_len ( int (array .chunks [0 ]), int ( array . blocks [ 0 ]) ),
33783388 "block_len" : int (array .blocks [0 ]),
33793389 "nblocks" : 0 ,
33803390 "cycles" : 0 ,
0 commit comments