@@ -411,6 +411,244 @@ TEST(Dataset, isel) {
411411 << " Inline range should end at 5" ;
412412}
413413
414+ TEST (Dataset, iselWithStride) {
415+ // Tests the integrity of data that is written with a strided slice.
416+ std::string iselPath = " zarrs/acceptance" ;
417+ { // Scoping the dataset creation to ensure the variables are cleaned up
418+ // before the testing.
419+ auto json_vars = GetToyExample ();
420+ auto dataset = mdio::Dataset::from_json (json_vars, iselPath,
421+ mdio::constants::kCreateClean );
422+ ASSERT_TRUE (dataset.status ().ok ()) << dataset.status ();
423+ auto ds = dataset.value ();
424+
425+ mdio::RangeDescriptor<mdio::Index> desc1 = {" inline" , 0 , 256 , 2 };
426+ auto sliceRes = ds.isel (desc1);
427+ ASSERT_TRUE (sliceRes.status ().ok ()) << sliceRes.status ();
428+ ds = sliceRes.value ();
429+
430+ auto ilVarRes = ds.variables .get <mdio::dtypes::uint32_t >(" inline" );
431+ ASSERT_TRUE (ilVarRes.status ().ok ()) << ilVarRes.status ();
432+ auto ilVar = ilVarRes.value ();
433+
434+ auto ilDataRes = mdio::from_variable<mdio::dtypes::uint32_t >(ilVar);
435+ ASSERT_TRUE (ilDataRes.status ().ok ()) << ilDataRes.status ();
436+ auto ilData = ilDataRes.value ();
437+
438+ auto ilAccessor = ilData.get_data_accessor ().data ();
439+ for (uint32_t i = 0 ; i < 128 ; i++) {
440+ ilAccessor[i] = i * 2 ;
441+ }
442+
443+ auto ilFut = ilVar.Write (ilData);
444+
445+ ASSERT_TRUE (ilFut.status ().ok ()) << ilFut.status ();
446+
447+ // --- Begin new QC data generation for the "image" variable (float32) ---
448+ auto imageVarRes = ds.variables .get <mdio::dtypes::float32_t >(" image" );
449+ ASSERT_TRUE (imageVarRes.status ().ok ()) << imageVarRes.status ();
450+ auto imageVar = imageVarRes.value ();
451+
452+ auto imageDataRes = mdio::from_variable<mdio::dtypes::float32_t >(imageVar);
453+ ASSERT_TRUE (imageDataRes.status ().ok ()) << imageDataRes.status ();
454+ auto imageData = imageDataRes.value ();
455+
456+ auto imageAccessor = imageData.get_data_accessor ().data ();
457+ for (uint32_t i = 0 ; i < 128 ; i++) {
458+ for (uint32_t j = 0 ; j < 512 ; j++) {
459+ for (uint32_t k = 0 ; k < 384 ; k++) {
460+ imageAccessor[i * (512 * 384 ) + j * 384 + k] =
461+ static_cast <float >(i * 2 ) + j * 0 .1f + k * 0 .01f ;
462+ }
463+ }
464+ }
465+
466+ auto imageWriteFut = imageVar.Write (imageData);
467+ ASSERT_TRUE (imageWriteFut.status ().ok ()) << imageWriteFut.status ();
468+ } // end of scoping the dataset creation to ensure the variables are cleaned
469+ // up before the testing.
470+
471+ auto reopenedDsFut = mdio::Dataset::Open (iselPath, mdio::constants::kOpen );
472+ ASSERT_TRUE (reopenedDsFut.status ().ok ()) << reopenedDsFut.status ();
473+ auto reopenedDs = reopenedDsFut.value ();
474+
475+ auto inlineVarRes =
476+ reopenedDs.variables .get <mdio::dtypes::uint32_t >(" inline" );
477+ ASSERT_TRUE (inlineVarRes.status ().ok ()) << inlineVarRes.status ();
478+ auto inlineVar = inlineVarRes.value ();
479+
480+ auto inlineDataFut = inlineVar.Read ();
481+ ASSERT_TRUE (inlineDataFut.status ().ok ()) << inlineDataFut.status ();
482+ auto inlineData = inlineDataFut.value ();
483+ auto inlineAccessor = inlineData.get_data_accessor ().data ();
484+ for (uint32_t i = 0 ; i < 256 ; i++) {
485+ if (i % 2 == 0 ) {
486+ ASSERT_EQ (inlineAccessor[i], i) << " Expected inline value to be " << i
487+ << " but got " << inlineAccessor[i];
488+ } else {
489+ ASSERT_EQ (inlineAccessor[i], 0 )
490+ << " Expected inline value to be 0 but got " << inlineAccessor[i];
491+ }
492+ }
493+
494+ auto imageVarResReopen =
495+ reopenedDs.variables .get <mdio::dtypes::float32_t >(" image" );
496+ ASSERT_TRUE (imageVarResReopen.status ().ok ()) << imageVarResReopen.status ();
497+ auto imageVarReopen = imageVarResReopen.value ();
498+
499+ auto imageDataFut = imageVarReopen.Read ();
500+ ASSERT_TRUE (imageDataFut.status ().ok ()) << imageDataFut.status ();
501+ auto imageDataFull = imageDataFut.value ();
502+ auto imageAccessorFull = imageDataFull.get_data_accessor ().data ();
503+
504+ // Instead of checking all 256x512x384 elements (which can be very time
505+ // consuming), we check a few sample indices. For full "image" variable, for
506+ // every full inline index i: if (i % 2 == 0): the expected value is i +
507+ // j*0.1f + k*0.01f, otherwise NaN.
508+ std::vector<uint32_t > sample_i = {0 , 1 , 2 ,
509+ 255 }; // mix of even and odd indices
510+ std::vector<uint32_t > sample_j = {0 , 256 , 511 };
511+ std::vector<uint32_t > sample_k = {0 , 100 , 383 };
512+
513+ for (auto i : sample_i) {
514+ for (auto j : sample_j) {
515+ for (auto k : sample_k) {
516+ size_t index = i * (512 * 384 ) + j * 384 + k;
517+ float actual = imageAccessorFull[index];
518+
519+ if (i % 2 == 0 ) {
520+ // For even indices, we expect a specific value
521+ float expected = static_cast <float >(i) + j * 0 .1f + k * 0 .01f ;
522+ ASSERT_FLOAT_EQ (actual, expected)
523+ << " QC mismatch in image variable at (" << i << " , " << j << " , "
524+ << k << " )" ;
525+ } else {
526+ // For odd indices, we expect NaN
527+ ASSERT_TRUE (std::isnan (actual))
528+ << " Expected NaN at (" << i << " , " << j << " , " << k
529+ << " ) but got " << actual;
530+ }
531+ }
532+ }
533+ }
534+ // --- End new QC check for the "image" variable ---
535+ }
536+
537+ TEST (Dataset, iselWithStrideAndExistingData) {
538+ std::string testPath = " zarrs/slice_scale_test" ;
539+ float scaleFactor = 2 .5f ;
540+
541+ // --- Step 1: Initialize the entire image variable with QC values and Write
542+ // it ---
543+ {
544+ // Create a new dataset
545+ auto json_vars = GetToyExample ();
546+ auto dataset = mdio::Dataset::from_json (json_vars, testPath,
547+ mdio::constants::kCreateClean );
548+ ASSERT_TRUE (dataset.status ().ok ()) << dataset.status ();
549+ auto ds = dataset.value ();
550+
551+ // Get the "image" variable (expected to be float32_t type)
552+ auto imageVarRes = ds.variables .get <mdio::dtypes::float32_t >(" image" );
553+ ASSERT_TRUE (imageVarRes.status ().ok ()) << imageVarRes.status ();
554+ auto imageVar = imageVarRes.value ();
555+
556+ auto imageDataRes = mdio::from_variable<mdio::dtypes::float32_t >(imageVar);
557+ ASSERT_TRUE (imageDataRes.status ().ok ()) << imageDataRes.status ();
558+ auto imageData = imageDataRes.value ();
559+ auto imageAccessor = imageData.get_data_accessor ().data ();
560+
561+ // Initialize the entire "image" variable with QC values.
562+ // For this test, we assume dimensions 256 x 512 x 384.
563+ for (uint32_t i = 0 ; i < 256 ; i++) {
564+ for (uint32_t j = 0 ; j < 512 ; j++) {
565+ for (uint32_t k = 0 ; k < 384 ; k++) {
566+ imageAccessor[i * (512 * 384 ) + j * 384 + k] =
567+ static_cast <float >(i) + j * 0 .1f + k * 0 .01f ;
568+ }
569+ }
570+ }
571+
572+ auto writeFut = imageVar.Write (imageData);
573+ ASSERT_TRUE (writeFut.status ().ok ()) << writeFut.status ();
574+ } // End of Step 1
575+
576+ // --- Step 2: Slice with stride of 2 and scale the values of the "image"
577+ // variable ---
578+ {
579+ // Re-open the dataset for modifications.
580+ auto reopenedDsFut = mdio::Dataset::Open (testPath, mdio::constants::kOpen );
581+ ASSERT_TRUE (reopenedDsFut.status ().ok ()) << reopenedDsFut.status ();
582+ auto ds = reopenedDsFut.value ();
583+
584+ // Slice the dataset along the "inline" dimension using a stride of 2.
585+ mdio::RangeDescriptor<mdio::Index> desc = {" inline" , 0 , 256 , 2 };
586+ auto sliceRes = ds.isel (desc);
587+ ASSERT_TRUE (sliceRes.status ().ok ()) << sliceRes.status ();
588+ auto ds_slice = sliceRes.value ();
589+
590+ // Get the "image" variable from the sliced dataset.
591+ auto imageVarRes = ds_slice.variables .get <mdio::dtypes::float32_t >(" image" );
592+ ASSERT_TRUE (imageVarRes.status ().ok ()) << imageVarRes.status ();
593+ auto imageVar = imageVarRes.value ();
594+
595+ auto imageDataFut = imageVar.Read ();
596+ ASSERT_TRUE (imageDataFut.status ().ok ()) << imageDataFut.status ();
597+ auto imageData = imageDataFut.value ();
598+ auto imageAccessor = imageData.get_data_accessor ().data ();
599+
600+ // The sliced "image" now has dimensions 128 x 512 x 384 because we selected
601+ // every 2nd index. Scale each element in the slice by 'scaleFactor'
602+ for (uint32_t ii = 0 ; ii < 128 ;
603+ ii++) { // 'ii' corresponds to original index i = ii * 2.
604+ for (uint32_t j = 0 ; j < 512 ; j++) {
605+ for (uint32_t k = 0 ; k < 384 ; k++) {
606+ size_t index = ii * (512 * 384 ) + j * 384 + k;
607+ imageAccessor[index] *= scaleFactor;
608+ }
609+ }
610+ }
611+ // Write the updated (scaled) data back to the dataset.
612+ auto writeFut = imageVar.Write (imageData);
613+ ASSERT_TRUE (writeFut.status ().ok ()) << writeFut.status ();
614+ } // End of Step 2
615+
616+ // --- Step 3: Read the entire image variable and validate QC values ---
617+ {
618+ // Re-open the dataset for the final validation.
619+ auto reopenedDsFut = mdio::Dataset::Open (testPath, mdio::constants::kOpen );
620+ ASSERT_TRUE (reopenedDsFut.status ().ok ()) << reopenedDsFut.status ();
621+ auto ds = reopenedDsFut.value ();
622+
623+ auto imageVarRes = ds.variables .get <mdio::dtypes::float32_t >(" image" );
624+ ASSERT_TRUE (imageVarRes.status ().ok ()) << imageVarRes.status ();
625+ auto imageVar = imageVarRes.value ();
626+
627+ auto imageReadFut = imageVar.Read ();
628+ ASSERT_TRUE (imageReadFut.status ().ok ()) << imageReadFut.status ();
629+ auto imageData = imageReadFut.value ();
630+ auto imageAccessor = imageData.get_data_accessor ().data ();
631+
632+ // Validate the values over the entire "image" variable.
633+ // For even inline indices (i % 2 == 0) we expect the initial QC value
634+ // scaled by 'scaleFactor'. For odd inline indices, the original QC values
635+ // should remain.
636+ for (uint32_t i = 0 ; i < 256 ; i++) {
637+ for (uint32_t j = 0 ; j < 512 ; j++) {
638+ for (uint32_t k = 0 ; k < 384 ; k++) {
639+ size_t index = i * (512 * 384 ) + j * 384 + k;
640+ float baseValue = static_cast <float >(i) + j * 0 .1f + k * 0 .01f ;
641+ float expected = (i % 2 == 0 ) ? baseValue * scaleFactor : baseValue;
642+ auto val = imageAccessor[index];
643+ ASSERT_FLOAT_EQ (val, expected)
644+ << " Mismatch at (" << i << " , " << j << " , " << k
645+ << " ): expected " << expected << " , but got " << val;
646+ }
647+ }
648+ }
649+ } // End of Step 3
650+ }
651+
414652TEST (Dataset, selValue) {
415653 std::string path = " zarrs/selTester.mdio" ;
416654 auto dsRes = makePopulated (path);
0 commit comments