|
| 1 | +#include <catch2/catch_approx.hpp> |
| 2 | +#include <catch2/catch_test_macros.hpp> |
| 3 | + |
| 4 | +#include "pcms/configuration.h" |
| 5 | +#include "pcms/coupler/field_serializer.h" |
| 6 | +#include "pcms/utility/arrays.h" |
| 7 | + |
| 8 | +#include <memory> |
| 9 | +#include <numeric> |
| 10 | +#include <vector> |
| 11 | + |
| 12 | +#ifdef PCMS_ENABLE_OMEGA_H |
| 13 | +#include "field_test_utils.h" |
| 14 | +#include "pcms/field/function_space/lagrange.h" |
| 15 | +#include "pcms/field/function_space/polynomial_reconstruction.hpp" |
| 16 | +#include "pcms/utility/uniform_grid.h" |
| 17 | +#include <Omega_h_library.hpp> |
| 18 | +#endif |
| 19 | + |
| 20 | +#ifdef PCMS_ENABLE_XGC |
| 21 | +#include "pcms/coupler/serializer/xgc.h" |
| 22 | +#include "pcms/field/data/xgc.h" |
| 23 | +#include "pcms/field/function_space/xgc.h" |
| 24 | +#endif |
| 25 | + |
| 26 | +namespace |
| 27 | +{ |
| 28 | + |
| 29 | +#ifdef PCMS_ENABLE_OMEGA_H |
| 30 | +void CheckSerializeDeserialize(pcms::Field<pcms::Real>& field) |
| 31 | +{ |
| 32 | + auto data_before = field.GetDOFHolderDataHost(); |
| 33 | + const auto num_dof = static_cast<pcms::LO>(data_before.extent(0)); |
| 34 | + const auto num_components = static_cast<pcms::LO>(data_before.extent(1)); |
| 35 | + const auto num_values = static_cast<size_t>(data_before.size()); |
| 36 | + |
| 37 | + std::vector<pcms::Real> expected(num_values); |
| 38 | + for (pcms::LO i = 0; i < num_dof; ++i) { |
| 39 | + for (pcms::LO c = 0; c < num_components; ++c) { |
| 40 | + expected[static_cast<size_t>(i) * num_components + c] = data_before(i, c); |
| 41 | + } |
| 42 | + } |
| 43 | + |
| 44 | + std::vector<pcms::Real> buffer(num_values); |
| 45 | + std::vector<pcms::LO> permutation(num_dof); |
| 46 | + std::iota(permutation.begin(), permutation.end(), pcms::LO{0}); |
| 47 | + |
| 48 | + pcms::FieldSerializer<pcms::Real> serializer; |
| 49 | + REQUIRE(serializer.Serialize(field, pcms::make_array_view(buffer), |
| 50 | + pcms::make_const_array_view(permutation)) == |
| 51 | + static_cast<int>(num_values)); |
| 52 | + |
| 53 | + std::vector<pcms::Real> cleared(num_values, -1.0); |
| 54 | + field.SetDOFHolderDataHost( |
| 55 | + pcms::Rank2View<const pcms::Real, pcms::HostMemorySpace>( |
| 56 | + cleared.data(), num_dof, num_components)); |
| 57 | + serializer.Deserialize(field, pcms::make_const_array_view(buffer), |
| 58 | + pcms::make_const_array_view(permutation)); |
| 59 | + |
| 60 | + auto data_after = field.GetDOFHolderDataHost(); |
| 61 | + REQUIRE(data_after.size() == expected.size()); |
| 62 | + for (pcms::LO i = 0; i < num_dof; ++i) { |
| 63 | + for (pcms::LO c = 0; c < num_components; ++c) { |
| 64 | + REQUIRE( |
| 65 | + data_after(i, c) == |
| 66 | + Catch::Approx(expected[static_cast<size_t>(i) * num_components + c])); |
| 67 | + } |
| 68 | + } |
| 69 | +} |
| 70 | +#endif |
| 71 | + |
| 72 | +#ifdef PCMS_ENABLE_XGC |
| 73 | +pcms::ReverseClassificationVertex CreateDummyReverseClassification(int size) |
| 74 | +{ |
| 75 | + pcms::ReverseClassificationVertex rc; |
| 76 | + for (int i = 0; i < size; ++i) { |
| 77 | + rc.Insert(i % 4 == 0 ? pcms::DimID{0, 0} : pcms::DimID{0, 1}, i); |
| 78 | + } |
| 79 | + return rc; |
| 80 | +} |
| 81 | + |
| 82 | +bool InXGCOverlap(int, int id) |
| 83 | +{ |
| 84 | + return id == 0; |
| 85 | +} |
| 86 | +#endif |
| 87 | + |
| 88 | +} // namespace |
| 89 | + |
| 90 | +#ifdef PCMS_ENABLE_XGC |
| 91 | +TEST_CASE("XGCFieldSerializer preserves inactive field entries") |
| 92 | +{ |
| 93 | + static constexpr int data_size = 16; |
| 94 | + auto rc = CreateDummyReverseClassification(data_size); |
| 95 | + pcms::XGCFieldFactory factory(rc, InXGCOverlap, data_size); |
| 96 | + |
| 97 | + std::vector<pcms::Real> data(data_size); |
| 98 | + std::iota(data.begin(), data.end(), 0.0); |
| 99 | + const auto original = data; |
| 100 | + auto field = factory.CreateField<pcms::Real>( |
| 101 | + "", std::make_unique<pcms::XGCFieldData<pcms::Real>>( |
| 102 | + factory.GetXGCLayout(), pcms::FieldMetadata{}, |
| 103 | + pcms::make_array_view(data))); |
| 104 | + pcms::XGCFieldSerializer<pcms::Real> serializer(MPI_COMM_SELF); |
| 105 | + |
| 106 | + auto owned = field.GetLayout().GetOwnedHost(); |
| 107 | + std::vector<pcms::LO> permutation(data_size, -1); |
| 108 | + int entry = 0; |
| 109 | + for (int i = 0; i < data_size; ++i) { |
| 110 | + if (owned[i]) { |
| 111 | + permutation[i] = entry++; |
| 112 | + } |
| 113 | + } |
| 114 | + const int num_owned = entry; |
| 115 | + REQUIRE(num_owned == 4); |
| 116 | + |
| 117 | + std::vector<pcms::Real> buffer(num_owned, -1.0); |
| 118 | + REQUIRE(serializer.Serialize(field, pcms::make_array_view(buffer), |
| 119 | + pcms::make_const_array_view(permutation)) == |
| 120 | + num_owned); |
| 121 | + |
| 122 | + for (int i = 0; i < data_size; ++i) { |
| 123 | + if (owned[i]) { |
| 124 | + REQUIRE(buffer[permutation[i]] == data[i]); |
| 125 | + buffer[permutation[i]] += 100.0; |
| 126 | + } else { |
| 127 | + REQUIRE(permutation[i] == -1); |
| 128 | + } |
| 129 | + } |
| 130 | + |
| 131 | + serializer.Deserialize(field, pcms::make_const_array_view(buffer), |
| 132 | + pcms::make_const_array_view(permutation)); |
| 133 | + |
| 134 | + auto after = pcms::FlattenToRank1View(field.GetDOFHolderDataHost()); |
| 135 | + for (int i = 0; i < data_size; ++i) { |
| 136 | + if (owned[i]) { |
| 137 | + REQUIRE(after[i] == Catch::Approx(original[i] + 100.0)); |
| 138 | + } else { |
| 139 | + REQUIRE(after[i] == Catch::Approx(original[i])); |
| 140 | + } |
| 141 | + } |
| 142 | +} |
| 143 | +#endif |
| 144 | + |
| 145 | +#ifdef PCMS_ENABLE_OMEGA_H |
| 146 | +TEST_CASE("FieldSerializer round-trips a polynomial-reconstruction field") |
| 147 | +{ |
| 148 | + std::vector<pcms::Real> coords{0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0}; |
| 149 | + pcms::Rank2View<pcms::Real, pcms::HostMemorySpace> coords_view(coords.data(), |
| 150 | + 4, 2); |
| 151 | + auto space = pcms::PolynomialReconstructionFunctionSpace::Create( |
| 152 | + coords_view, pcms::CoordinateSystem::Cartesian); |
| 153 | + auto field = space->CreateFunction<pcms::Real>(); |
| 154 | + |
| 155 | + std::vector<pcms::Real> data{5.0, 6.0, 7.0, 8.0}; |
| 156 | + field.SetDOFHolderDataHost( |
| 157 | + pcms::Rank2View<const pcms::Real, pcms::HostMemorySpace>(data.data(), 4, |
| 158 | + 1)); |
| 159 | + |
| 160 | + CheckSerializeDeserialize(field); |
| 161 | +} |
| 162 | + |
| 163 | +TEST_CASE("FieldSerializer round-trips a uniform-grid field") |
| 164 | +{ |
| 165 | + pcms::UniformGrid<2> grid; |
| 166 | + grid.bot_left = {0.0, 0.0}; |
| 167 | + grid.edge_length = {10.0, 10.0}; |
| 168 | + grid.divisions = {3, 3}; |
| 169 | + auto space = pcms::LagrangeFunctionSpace::FromUniformGrid( |
| 170 | + grid, 1, pcms::CoordinateSystem::Cartesian); |
| 171 | + auto field = space->CreateFunction<pcms::Real>(); |
| 172 | + |
| 173 | + std::vector<pcms::Real> data(16); |
| 174 | + for (size_t i = 0; i < data.size(); ++i) { |
| 175 | + data[i] = static_cast<pcms::Real>(i * 10); |
| 176 | + } |
| 177 | + field.SetDOFHolderDataHost( |
| 178 | + pcms::Rank2View<const pcms::Real, pcms::HostMemorySpace>(data.data(), 16, |
| 179 | + 1)); |
| 180 | + |
| 181 | + CheckSerializeDeserialize(field); |
| 182 | +} |
| 183 | + |
| 184 | +TEST_CASE("FieldSerializer round-trips an order-1 Omega_h field") |
| 185 | +{ |
| 186 | + auto lib = Omega_h::Library{}; |
| 187 | + auto mesh = pcms::test::BuildUnitSquare(lib, 0); |
| 188 | + auto space = pcms::test::MakeP1Space(mesh); |
| 189 | + auto field = space->CreateFunction<pcms::Real>(); |
| 190 | + |
| 191 | + pcms::test::SetField( |
| 192 | + field, OMEGA_H_LAMBDA(pcms::Real x, pcms::Real y) { |
| 193 | + return pcms::test::linear_f(x, y); |
| 194 | + }); |
| 195 | + |
| 196 | + CheckSerializeDeserialize(field); |
| 197 | +} |
| 198 | + |
| 199 | +TEST_CASE("FieldSerializer round-trips a multi-component Omega_h field") |
| 200 | +{ |
| 201 | + auto lib = Omega_h::Library{}; |
| 202 | + auto mesh = pcms::test::BuildUnitSquare(lib, 0); |
| 203 | + constexpr int num_components = 3; |
| 204 | + auto space = pcms::LagrangeFunctionSpace::FromMesh( |
| 205 | + mesh, 1, num_components, pcms::CoordinateSystem::Cartesian, "global", |
| 206 | + pcms::LagrangeFunctionSpace::Backend::OmegaH); |
| 207 | + auto field = space->CreateFunction<pcms::Real>(); |
| 208 | + |
| 209 | + const int num_dof = field.GetLayout().GetNumOwnedDofHolder(); |
| 210 | + std::vector<pcms::Real> data(static_cast<size_t>(num_dof) * num_components); |
| 211 | + for (int i = 0; i < num_dof; ++i) { |
| 212 | + for (int c = 0; c < num_components; ++c) { |
| 213 | + data[static_cast<size_t>(i) * num_components + c] = i + 0.25 * c; |
| 214 | + } |
| 215 | + } |
| 216 | + field.SetDOFHolderDataHost( |
| 217 | + pcms::Rank2View<const pcms::Real, pcms::HostMemorySpace>( |
| 218 | + data.data(), num_dof, num_components)); |
| 219 | + |
| 220 | + CheckSerializeDeserialize(field); |
| 221 | +} |
| 222 | + |
| 223 | +TEST_CASE("FieldSerializer round-trips an order-0 Omega_h field") |
| 224 | +{ |
| 225 | + auto lib = Omega_h::Library{}; |
| 226 | + auto mesh = pcms::test::BuildUnitSquare(lib, 0); |
| 227 | + auto space = pcms::test::MakeP0Space(mesh); |
| 228 | + auto field = space->CreateFunction<pcms::Real>(); |
| 229 | + |
| 230 | + const int num_dof = mesh.nelems(); |
| 231 | + std::vector<pcms::Real> data(num_dof); |
| 232 | + std::iota(data.begin(), data.end(), pcms::Real{0}); |
| 233 | + field.SetDOFHolderDataHost( |
| 234 | + pcms::Rank2View<const pcms::Real, pcms::HostMemorySpace>(data.data(), |
| 235 | + num_dof, 1)); |
| 236 | + |
| 237 | + CheckSerializeDeserialize(field); |
| 238 | +} |
| 239 | +#endif |
0 commit comments