@@ -1778,4 +1778,160 @@ TEST_CASE("unavailable_backend", "[core][parallel]")
17781778 }
17791779#endif
17801780}
1781+
1782+ void joined_dim (std::string const &ext)
1783+ {
1784+ using type = float ;
1785+ using patchType = uint64_t ;
1786+ constexpr size_t patches_per_rank = 5 ;
1787+ constexpr size_t length_of_patch = 10 ;
1788+
1789+ int size{-1 };
1790+ int rank{-1 };
1791+ MPI_Comm_size (MPI_COMM_WORLD, &size);
1792+ MPI_Comm_rank (MPI_COMM_WORLD, &rank);
1793+
1794+ {
1795+ Series s (
1796+ " ../samples/joinedDimParallel." + ext,
1797+ Access::CREATE,
1798+ MPI_COMM_WORLD);
1799+ std::vector<UniquePtrWithLambda<type>> writeFrom (patches_per_rank);
1800+
1801+ auto it = s.writeIterations ()[100 ];
1802+
1803+ Dataset numParticlesDS (
1804+ determineDatatype<patchType>(), {Dataset::JOINED_DIMENSION});
1805+ auto numParticles =
1806+ it.particles [" e" ]
1807+ .particlePatches [" numParticles" ][RecordComponent::SCALAR];
1808+ auto numParticlesOffset =
1809+ it.particles [" e" ]
1810+ .particlePatches [" numParticlesOffset" ][RecordComponent::SCALAR];
1811+ numParticles.resetDataset (numParticlesDS);
1812+ numParticlesOffset.resetDataset (numParticlesDS);
1813+
1814+ auto patchOffset = it.particles [" e" ].particlePatches [" offset" ][" x" ];
1815+ auto patchExtent = it.particles [" e" ].particlePatches [" extent" ][" x" ];
1816+ Dataset particlePatchesDS (
1817+ determineDatatype<float >(), {Dataset::JOINED_DIMENSION});
1818+ patchOffset.resetDataset (particlePatchesDS);
1819+ patchExtent.resetDataset (particlePatchesDS);
1820+
1821+ float start_value = rank * patches_per_rank * length_of_patch;
1822+ for (size_t i = 0 ; i < 5 ; ++i)
1823+ {
1824+ writeFrom[i] = UniquePtrWithLambda<type>(
1825+ new type[length_of_patch],
1826+ [](auto const *ptr) { delete[] ptr; });
1827+ std::iota (
1828+ writeFrom[i].get (),
1829+ writeFrom[i].get () + 10 ,
1830+ start_value + length_of_patch * i);
1831+ patchOffset.store <type>(start_value + length_of_patch * i);
1832+ }
1833+
1834+ auto epx = it.particles [" e" ][" position" ][" x" ];
1835+ Dataset ds (determineDatatype<type>(), {Dataset::JOINED_DIMENSION});
1836+ epx.resetDataset (ds);
1837+
1838+ size_t counter = 0 ;
1839+ for (auto &chunk : writeFrom)
1840+ {
1841+ epx.storeChunk (std::move (chunk), {}, {length_of_patch});
1842+ numParticles.store <patchType>(length_of_patch);
1843+ /*
1844+ * For the sake of the test case, we know that the
1845+ * numParticlesOffset has this value. In general, the purpose of the
1846+ * joined array is that we don't need to know these values, so the
1847+ * specification of particle patches is somewhat difficult.
1848+ */
1849+ numParticlesOffset.store <patchType>(
1850+ start_value + counter++ * length_of_patch);
1851+ patchExtent.store <type>(10 );
1852+ }
1853+ writeFrom.clear ();
1854+ it.close ();
1855+ s.close ();
1856+ }
1857+
1858+ {
1859+ Series s (
1860+ " ../samples/joinedDimParallel." + ext,
1861+ Access::READ_ONLY,
1862+ MPI_COMM_WORLD);
1863+ auto it = s.iterations [100 ];
1864+ auto e = it.particles [" e" ];
1865+
1866+ auto particleData = e[" position" ][" x" ].loadChunk <type>();
1867+ auto numParticles =
1868+ e.particlePatches [" numParticles" ][RecordComponent::SCALAR]
1869+ .load <patchType>();
1870+ auto numParticlesOffset =
1871+ e.particlePatches [" numParticlesOffset" ][RecordComponent::SCALAR]
1872+ .load <patchType>();
1873+ auto patchOffset = e.particlePatches [" offset" ][" x" ].load <type>();
1874+ auto patchExtent = e.particlePatches [" extent" ][" x" ].load <type>();
1875+
1876+ it.close ();
1877+
1878+ // check validity of particle patches
1879+ auto numPatches =
1880+ e.particlePatches [" numParticlesOffset" ][RecordComponent::SCALAR]
1881+ .getExtent ()[0 ];
1882+ REQUIRE (
1883+ e.particlePatches [" numParticles" ][RecordComponent::SCALAR]
1884+ .getExtent ()[0 ] == numPatches);
1885+ for (size_t i = 0 ; i < numPatches; ++i)
1886+ {
1887+ for (size_t j = 0 ; j < numParticles.get ()[i]; ++j)
1888+ {
1889+ REQUIRE (
1890+ patchOffset.get ()[i] <=
1891+ particleData.get ()[numParticlesOffset.get ()[i] + j]);
1892+ REQUIRE (
1893+ particleData.get ()[numParticlesOffset.get ()[i] + j] <
1894+ patchOffset.get ()[i] + patchExtent.get ()[i]);
1895+ }
1896+ }
1897+
1898+ /*
1899+ * Check that joined array joins early writes before later writes from
1900+ * the same rank
1901+ */
1902+ for (size_t i = 0 ; i < size * length_of_patch * patches_per_rank; ++i)
1903+ {
1904+ REQUIRE (float (i) == particleData.get ()[i]);
1905+ }
1906+ for (size_t i = 0 ; i < size * patches_per_rank; ++i)
1907+ {
1908+ REQUIRE (length_of_patch * i == numParticlesOffset.get ()[i]);
1909+ REQUIRE (type (length_of_patch * i) == patchOffset.get ()[i]);
1910+ }
1911+ }
1912+ }
1913+
1914+ TEST_CASE (" joined_dim" , " [parallel]" )
1915+ {
1916+ #if 100000000 * ADIOS2_VERSION_MAJOR + 1000000 * ADIOS2_VERSION_MINOR + \
1917+ 10000 * ADIOS2_VERSION_PATCH + 100 * ADIOS2_VERSION_TWEAK >= \
1918+ 209000000
1919+ constexpr char const *supportsJoinedDims[] = {" bp" , " bp4" , " bp5" };
1920+ #else
1921+ // no zero-size arrays
1922+ std::vector<char const *> supportsJoinedDims;
1923+ #endif
1924+ for (auto const &t : testedFileExtensions ())
1925+ {
1926+ for (auto const supported : supportsJoinedDims)
1927+ {
1928+ if (t == supported)
1929+ {
1930+ joined_dim (t);
1931+ break ;
1932+ }
1933+ }
1934+ }
1935+ }
1936+
17811937#endif // openPMD_HAVE_ADIOS2 && openPMD_HAVE_MPI
0 commit comments