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