libMesh
Loading...
Searching...
No Matches
kokkos_vector_ops_oracle_runners.h
Go to the documentation of this file.
1#ifndef KOKKOS_VECTOR_OPS_ORACLE_RUNNERS_H
2#define KOKKOS_VECTOR_OPS_ORACLE_RUNNERS_H
3
4// Device-side runners for the Kokkos vector-ops oracle tests, plus a
5// host-only trait check. Each device test uploads its fixture data,
6// launches a kernel that evaluates the vector algebra in device code -
7// mostly through storage-backed refs - then mirrors the results back
8// and compares them against the host oracle (see
9// kokkos_vector_ops_oracle_fixtures.h for the strategy and the result
10// ordering contract).
11//
12// The kernels use parallel_for over a single index: the goal is to
13// validate the algebra's semantics in device code, not its
14// performance, and one thread computing every result keeps the
15// store-order contract with the host oracle trivial.
16
18
19#include <cstdio>
20
21namespace libMeshTest
22{
23namespace KokkosVectorOracle
24{
25
26// Runs the vector-algebra sweep for one (a, b, c) input triple: each
27// quantity the host oracle records, recomputed in device code (often
28// through a differently-spelled expression - the host's compound
29// assignments and add_scaled() calls become plain ref arithmetic
30// here) and stored in the oracle's order. The kernel's offset
31// assertions (active in asserting builds) check the device-side
32// counts; build_host_oracle() has matching asserts for the host side.
33template <typename StoragePolicy, typename Vec>
34static int
36{
37 const auto a = make_vector<Vec>(info.ax, info.ay, info.az);
38 const auto b = make_vector<Vec>(info.bx, info.by, info.bz);
39 const auto c = make_vector<Vec>(info.cx, info.cy, info.cz);
40
41 const auto expected = build_host_oracle(a, b, c);
42
43 auto d_a = libMesh::Kokkos::upload_vector_storage<StoragePolicy>(std::vector<Vec>{a}, "vector_ops_a");
44 auto d_b = libMesh::Kokkos::upload_vector_storage<StoragePolicy>(std::vector<Vec>{b}, "vector_ops_b");
45 auto d_c = libMesh::Kokkos::upload_vector_storage<StoragePolicy>(std::vector<Vec>{c}, "vector_ops_c");
46 auto d_vectors = libMesh::Kokkos::make_vector_storage<StoragePolicy>("vector_ops_vectors", vector_results);
47 ::Kokkos::View<double *> d_scalars("vector_ops_scalars", scalar_results);
48
49 ::Kokkos::parallel_for(
50 1,
51 KOKKOS_LAMBDA(int) {
52 const auto a_ref = libMesh::Kokkos::make_vector_ref(d_a, 0);
53 const auto b_ref = libMesh::Kokkos::make_vector_ref(d_b, 0);
54 const auto c_ref = libMesh::Kokkos::make_vector_ref(d_c, 0);
55
56 const Vec copied = libMesh::Kokkos::copy_vector<Vec>(a_ref);
57 const Vec mix = a_ref + b_ref - c_ref;
58 const Vec scaled = Real(1.25) * a_ref + Real(-0.5) * b_ref + Real(0.25) * c_ref;
59 const Vec plus_assign = a_ref + b_ref;
60 const Vec minus_assign = a_ref - b_ref;
61 const Vec accum = Real(1.25) * a_ref + Real(-0.5) * b_ref + Real(0.25) * c_ref;
62 const Vec divided = a_ref / Real(5.0);
63 const Vec outer_right = Real(5.0) * a_ref;
64 const Vec outer_left = a_ref * Real(5.0);
65 const Vec mult_assign = a_ref * Real(5.0);
66 const Vec div_assign = a_ref / Real(5.0);
67
68 const Real dot = libMesh::Kokkos::vector_dot(a_ref, b_ref);
69 const Real contract = a_ref.contract(b_ref);
70 const Real norm = mix.norm();
71 const Real norm_sq = mix.norm_sq();
72 const Vec zero; // By default initialization
73 const Vec assign_zero = zero;
74
75 unsigned int vector_offset = 0;
76 libMesh::Kokkos::store_vector(d_vectors, vector_offset++, copied);
77 libMesh::Kokkos::store_vector(d_vectors, vector_offset++, mix);
78 libMesh::Kokkos::store_vector(d_vectors, vector_offset++, scaled);
79 libMesh::Kokkos::store_vector(d_vectors, vector_offset++, accum);
80 libMesh::Kokkos::store_vector(d_vectors, vector_offset++, plus_assign);
81 libMesh::Kokkos::store_vector(d_vectors, vector_offset++, minus_assign);
82 libMesh::Kokkos::store_vector(d_vectors, vector_offset++, divided);
83 libMesh::Kokkos::store_vector(d_vectors, vector_offset++, outer_right);
84 libMesh::Kokkos::store_vector(d_vectors, vector_offset++, outer_left);
85 libMesh::Kokkos::store_vector(d_vectors, vector_offset++, mult_assign);
86 libMesh::Kokkos::store_vector(d_vectors, vector_offset++, div_assign);
87
88 unsigned int scalar_offset = 0;
89 d_scalars(scalar_offset++) = a_ref * b_ref;
90 d_scalars(scalar_offset++) = dot;
91 d_scalars(scalar_offset++) = contract;
92 d_scalars(scalar_offset++) = norm;
93 d_scalars(scalar_offset++) = norm_sq;
94 d_scalars(scalar_offset++) = zero.is_zero() ? 1.0 : 0.0;
95 d_scalars(scalar_offset++) = mix.is_zero() ? 1.0 : 0.0;
96 d_scalars(scalar_offset++) = (a_ref == a_ref) ? 1.0 : 0.0;
97 d_scalars(scalar_offset++) = (a_ref == b_ref) ? 1.0 : 0.0;
98 d_scalars(scalar_offset++) = (a_ref != a_ref) ? 1.0 : 0.0;
99 d_scalars(scalar_offset++) = (a_ref != b_ref) ? 1.0 : 0.0;
100 d_scalars(scalar_offset++) = assign_zero.is_zero() ? 1.0 : 0.0;
101
102 const Vec xvec = make_vector<Vec>(1.3);
103 d_scalars(scalar_offset++) = libMesh::Kokkos::vector_solid_angle(xvec, xvec, xvec);
104
105#if LIBMESH_DIM > 1
106 const Vec yvec = make_vector<Vec>(0.0, 2.7);
107 const Vec xydiag = make_vector<Vec>(3.1, 3.1);
108 d_scalars(scalar_offset++) = libMesh::Kokkos::vector_solid_angle(xvec, xvec, yvec);
109 d_scalars(scalar_offset++) = libMesh::Kokkos::vector_solid_angle(xvec, yvec, xydiag);
110#endif
111
112#if LIBMESH_DIM > 2
113 const Vec xypdiag = make_vector<Vec>(0.8, -0.8);
114 const Vec zvec = make_vector<Vec>(0.0, 0.0, 1.1);
115 const Vec xzdiag = make_vector<Vec>(0.0, 0.7, 0.7);
116 const Vec icosa1 = make_vector<Vec>(1.0, golden_ratio, 0.0);
117 const Vec icosa2 = make_vector<Vec>(-1.0, golden_ratio, 0.0);
118 const Vec icosa3 = make_vector<Vec>(0.0, 1.0, golden_ratio);
119 d_scalars(scalar_offset++) = libMesh::Kokkos::vector_solid_angle(xydiag, yvec, zvec);
120 d_scalars(scalar_offset++) = libMesh::Kokkos::vector_solid_angle(xvec, yvec, xzdiag);
121 d_scalars(scalar_offset++) = libMesh::Kokkos::vector_solid_angle(xypdiag, xydiag, zvec);
122 d_scalars(scalar_offset++) = libMesh::Kokkos::vector_solid_angle(icosa1, icosa2, icosa3);
123#endif
124
125#if LIBMESH_DIM > 2
126 const Vec cross = a_ref.cross(b_ref);
127 Vec unit_cross = cross;
128 if (cross.norm() > unit_tol)
129 unit_cross = libMesh::Kokkos::vector_unit<Vec>(cross);
130
131 libMesh::Kokkos::store_vector(d_vectors, vector_offset++, cross);
132 libMesh::Kokkos::store_vector(d_vectors, vector_offset++, unit_cross);
133#endif
134
135 libmesh_assert_equal_to(vector_offset, vector_results);
136 libmesh_assert_equal_to(scalar_offset, scalar_results);
137 });
138 ::Kokkos::fence();
139
140 return libMeshTest::KokkosOracle::compare_device_vectors(d_vectors, expected.vectors, tol) +
141 libMeshTest::KokkosOracle::compare_device_scalars(d_scalars, expected.scalars, tol);
142}
143
144template <typename StoragePolicy, typename Vec>
145int
146run_vector_cases(const char * suite_name)
147{
148 int fail = 0;
149
150 for (const auto & info : cases)
151 {
152 const int f = test_vector_ops_case<StoragePolicy, Vec>(info);
153 std::printf("[%s] [%s] [%s] %s (%d failures)\n",
154 suite_name,
155 libMesh::Kokkos::storage_policy_name<StoragePolicy>(),
156 info.name,
157 f ? "FAIL" : "PASS",
158 f);
159 fail += f;
160 }
161
162 return fail;
163}
164
165// Compile-time trait checks with no device analogue: MetaPhysicL's
166// ReplaceAlgebraicType combined with TensorTools::IncrementRank must
167// map TypeVector and VectorValue containers to the matching tensor
168// containers. Evaluated as constexpr bools so a regression shows up
169// as a test failure rather than a build error.
170inline int
172{
173 int fail = 0;
174
175#ifdef LIBMESH_HAVE_METAPHYSICL
176 typedef typename MetaPhysicL::ReplaceAlgebraicType<
177 std::vector<libMesh::TypeVector<double>>,
179 typename MetaPhysicL::ValueType<std::vector<libMesh::TypeVector<double>>>::type>::type>::type
180 ReplacedType;
181 constexpr bool typevector_assertion =
182 std::is_same<ReplacedType, std::vector<libMesh::TensorValue<double>>>::value;
183 fail += typevector_assertion ? 0 : 1;
184
185 typedef typename MetaPhysicL::ReplaceAlgebraicType<
186 std::vector<libMesh::VectorValue<double>>,
188 typename MetaPhysicL::ValueType<std::vector<libMesh::VectorValue<double>>>::type>::type>::type
189 ReplacedValueType;
190 constexpr bool vectorvalue_assertion =
191 std::is_same<ReplacedValueType, std::vector<libMesh::TensorValue<double>>>::value;
192 fail += vectorvalue_assertion ? 0 : 1;
193#endif
194
195 return fail;
196}
197
198// The point of this test is mixing representations inside one
199// expression: a device ref on one side of an operator and an
200// independent host-built owning value (captured into the lambda) on
201// the other - with the ref on the left in some expressions (a_ref + b)
202// and on the right in others (a - b_ref). test_vector_ops_case()
203// pairs its refs with each other or with values derived from them, so
204// it hits these combinations only incidentally.
205template <typename StoragePolicy, typename Vec>
206static int
208{
209 int fail = 0;
210
211 const auto a = make_vector<Vec>(2.0, 3.0, 4.0);
212 const auto b = make_vector<Vec>(5.0, -6.0, 7.0);
213 const auto c = make_vector<Vec>(1.25, -0.5, 2.0);
214
215 auto d_a = libMesh::Kokkos::upload_vector_storage<StoragePolicy>(std::vector<Vec>{a}, "mixed_vector_a");
216 auto d_b = libMesh::Kokkos::upload_vector_storage<StoragePolicy>(std::vector<Vec>{b}, "mixed_vector_b");
217
218 auto d_vectors =
219 libMesh::Kokkos::make_vector_storage<StoragePolicy>("mixed_vector_vectors", (LIBMESH_DIM > 2) ? 5 : 3);
220 ::Kokkos::View<double *> d_scalars("mixed_vector_scalars", (LIBMESH_DIM > 2) ? 7 : 5);
221
222 const auto ref_sum = a + b;
223 const auto ref_diff = a - b;
224 const auto ref_scaled = 1.5 * a;
225 const auto ref_dot = a * b;
226 const auto ref_contract = a.contract(b);
227 const auto ref_solid_angle =
229 const auto ref_cross_norm_sq = libMesh::cross_norm_sq(as_type_vector(a), as_type_vector(b));
230
231#if LIBMESH_DIM > 2
232 const auto ref_cross = a.cross(b);
233 auto ref_unit_cross = ref_cross;
234 if (ref_cross.norm() > unit_tol)
235 ref_unit_cross = ref_cross.unit();
236#endif
237
238 ::Kokkos::parallel_for(
239 1,
240 KOKKOS_LAMBDA(int) {
241 const auto a_ref = libMesh::Kokkos::make_vector_ref(d_a, 0);
242 const auto b_ref = libMesh::Kokkos::make_vector_ref(d_b, 0);
243
244 const auto sum = a_ref + b;
245 const auto diff = a - b_ref;
246 const auto scaled = Real(1.5) * a_ref;
247
248 libMesh::Kokkos::store_vector(d_vectors, 0, sum);
249 libMesh::Kokkos::store_vector(d_vectors, 1, diff);
250 libMesh::Kokkos::store_vector(d_vectors, 2, scaled);
251
252 d_scalars(0) = a_ref * b;
253 d_scalars(1) = b_ref.contract(a);
254 d_scalars(2) = (a_ref == a) ? 1.0 : 0.0;
255 d_scalars(3) = (a_ref != b) ? 1.0 : 0.0;
256 d_scalars(4) = libMesh::Kokkos::vector_solid_angle(a_ref, b, c);
257
258#if LIBMESH_DIM > 2
259 const auto cross = a_ref.cross(b);
260 Vec unit_cross = cross;
261 if (cross.norm() > unit_tol)
262 unit_cross = libMesh::Kokkos::vector_unit<Vec>(cross);
263
264 libMesh::Kokkos::store_vector(d_vectors, 3, cross);
265 libMesh::Kokkos::store_vector(d_vectors, 4, unit_cross);
266 d_scalars(5) = libMesh::Kokkos::vector_cross_norm_sq(a_ref, b);
267 d_scalars(6) = (cross == libMesh::Kokkos::vector_cross<Vec>(a, b_ref)) ? 1.0 : 0.0;
268#endif
269 });
270 ::Kokkos::fence();
271
273 d_vectors,
274 [&]() {
275 std::vector<Vec> ref = {ref_sum, ref_diff, ref_scaled};
276#if LIBMESH_DIM > 2
277 ref.push_back(ref_cross);
278 ref.push_back(ref_unit_cross);
279#endif
280 return ref;
281 }(),
282 tol);
283
285 d_scalars,
286 [&]() {
287 std::vector<Real> ref = {ref_dot, ref_contract, 1.0, 1.0, ref_solid_angle};
288#if LIBMESH_DIM > 2
289 ref.push_back(ref_cross_norm_sq);
290 ref.push_back(1.0);
291#endif
292 return ref;
293 }(),
294 tol);
295
296 return fail;
297}
298
299// Runs the device suites under both storage layouts and with both
300// TypeVector and VectorValue, so a layout- or type-specific
301// regression can't hide behind the default configuration; the
302// host-only trait checks run once at the end.
303inline int
305{
306 int total_fail = 0;
307
308 total_fail += run_vector_cases<libMesh::Kokkos::layout_left_storage_policy, libMesh::TypeVector<Real>>(
309 "typevector_kernel_oracle");
310 total_fail += run_vector_cases<libMesh::Kokkos::layout_right_storage_policy, libMesh::TypeVector<Real>>(
311 "typevector_kernel_oracle");
312 total_fail += run_vector_cases<libMesh::Kokkos::layout_left_storage_policy, libMesh::VectorValue<Real>>(
313 "vectorvalue_kernel_oracle");
314 total_fail += run_vector_cases<libMesh::Kokkos::layout_right_storage_policy, libMesh::VectorValue<Real>>(
315 "vectorvalue_kernel_oracle");
316
317 const int mixed_typevector_left =
318 test_mixed_representation_ops<libMesh::Kokkos::layout_left_storage_policy, libMesh::TypeVector<Real>>();
319 std::printf("[vector_mixed_representation_oracle] [%s] [typevector] %s (%d failures)\n",
320 libMesh::Kokkos::storage_policy_name<libMesh::Kokkos::layout_left_storage_policy>(),
321 mixed_typevector_left ? "FAIL" : "PASS",
322 mixed_typevector_left);
323 total_fail += mixed_typevector_left;
324
325 const int mixed_typevector_right =
326 test_mixed_representation_ops<libMesh::Kokkos::layout_right_storage_policy, libMesh::TypeVector<Real>>();
327 std::printf("[vector_mixed_representation_oracle] [%s] [typevector] %s (%d failures)\n",
328 libMesh::Kokkos::storage_policy_name<libMesh::Kokkos::layout_right_storage_policy>(),
329 mixed_typevector_right ? "FAIL" : "PASS",
330 mixed_typevector_right);
331 total_fail += mixed_typevector_right;
332
333 const int mixed_vectorvalue_left =
334 test_mixed_representation_ops<libMesh::Kokkos::layout_left_storage_policy, libMesh::VectorValue<Real>>();
335 std::printf("[vector_mixed_representation_oracle] [%s] [vectorvalue] %s (%d failures)\n",
336 libMesh::Kokkos::storage_policy_name<libMesh::Kokkos::layout_left_storage_policy>(),
337 mixed_vectorvalue_left ? "FAIL" : "PASS",
338 mixed_vectorvalue_left);
339 total_fail += mixed_vectorvalue_left;
340
341 const int mixed_vectorvalue_right =
342 test_mixed_representation_ops<libMesh::Kokkos::layout_right_storage_policy, libMesh::VectorValue<Real>>();
343 std::printf("[vector_mixed_representation_oracle] [%s] [vectorvalue] %s (%d failures)\n",
344 libMesh::Kokkos::storage_policy_name<libMesh::Kokkos::layout_right_storage_policy>(),
345 mixed_vectorvalue_right ? "FAIL" : "PASS",
346 mixed_vectorvalue_right);
347 total_fail += mixed_vectorvalue_right;
348
349 const int host_fail = test_vector_host_only_traits();
350 std::printf("[vector_host_traits_oracle] %s (%d failures)\n",
351 host_fail ? "FAIL" : "PASS",
352 host_fail);
353 total_fail += host_fail;
354
355 return total_fail;
356}
357
358} // namespace KokkosVectorOracle
359} // namespace libMeshTest
360
361#endif
static const Real b
int compare_device_scalars(const ::Kokkos::View< double * > &d_values, const std::vector< Real > &ref_values, const double tol)
int compare_device_vectors(const ViewType &d_values, const std::vector< VectorType > &ref_values, const double tol)
host_oracle< Vec > build_host_oracle(const Vec &a, const Vec &b, const Vec &c)
static int test_vector_ops_case(const vector_case &info)
libMesh::TypeVector< Real > as_type_vector(const libMesh::TypeVector< Real > &v)
LIBMESH_DEVICE_INLINE vector_ref< typename std::remove_reference< ViewType >::type > make_vector_ref(ViewType &&view, const unsigned int index)
LIBMESH_DEVICE_INLINE void store_vector(const ViewType &view, const unsigned int i, const VectorType &v)
Copies the components of the vector v into entry i of the vector view view, overwriting whatever that...
LIBMESH_DEVICE_INLINE auto vector_dot(const LeftVector &left, const RightVector &right)
LIBMESH_DEVICE_INLINE auto vector_solid_angle(const VectorA &v01, const VectorB &v02, const VectorC &v03)
LIBMESH_DEVICE_INLINE auto vector_cross_norm_sq(const LeftVector &left, const RightVector &right)
LIBMESH_DEVICE_INLINE T cross_norm_sq(const TypeVector< T > &b, const TypeVector< T > &c)
Compute |b x c|^2 without creating the extra temporary produced by calling b.cross(c)....
LIBMESH_DEVICE_INLINE T solid_angle(const TypeVector< T > &v01, const TypeVector< T > &v02, const TypeVector< T > &v03)
static const bool value
Definition xdr_io.C:55