MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
util.hpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11#pragma once
12
13#include <algorithm>
14#include <array>
15#include <cstdlib>
16#include <iostream>
17#include <unordered_map>
18#include <utility>
19#include <variant>
20#include <vector>
21#include <type_traits>
22#include <numeric>
23#include <iomanip>
24
27#ifdef MFEM_USE_MPI
28#include "../fe/fe_base.hpp"
29#include "../fespace.hpp"
30#include "../pfespace.hpp"
31#include "../../mesh/mesh.hpp"
33
34#include "fieldoperator.hpp"
35#include "parameterspace.hpp"
36#include "tuple.hpp"
37
38namespace mfem::future
39{
40
41template<typename... Ts>
42constexpr auto to_array(const std::tuple<Ts...>& tuple)
43{
44 constexpr auto get_array = [](const Ts&... x) { return std::array<typename std::common_type<Ts...>::type, sizeof...(Ts)> { x... }; };
45 return std::apply(get_array, tuple);
46}
47
48namespace detail
49{
50
51template <typename lambda, std::size_t... i>
52constexpr void for_constexpr(lambda&& f,
53 std::integral_constant<std::size_t, i>... Is)
54{
55 f(Is...);
56}
57
58
59template <std::size_t... n, typename lambda, typename... arg_types>
60constexpr void for_constexpr(lambda&& f,
61 std::integer_sequence<std::size_t, n...>,
62 arg_types... args)
63{
64 (detail::for_constexpr(f, args..., std::integral_constant<std::size_t,n> {}),
65 ...);
66}
67
68} // namespace detail
69
70template <typename lambda, std::size_t... i>
71constexpr void for_constexpr(lambda&& f,
72 std::integer_sequence<std::size_t, i ... >)
73{
74 (f(std::integral_constant<std::size_t, i> {}), ...);
75}
76
77template <typename lambda>
78constexpr void for_constexpr(lambda&& f, std::integer_sequence<std::size_t>) {}
79
80template <int... n, typename lambda>
81constexpr void for_constexpr(lambda&& f)
82{
83 detail::for_constexpr(f, std::make_integer_sequence<std::size_t, n> {}...);
84}
85
86template <typename lambda, typename arg_t>
87constexpr void for_constexpr_with_arg(lambda&& f, arg_t&& arg,
88 std::integer_sequence<std::size_t>)
89{
90 // Base case - do nothing for empty sequence
91}
92
93template <typename lambda, typename arg_t, std::size_t i, std::size_t... Is>
94constexpr void for_constexpr_with_arg(lambda&& f, arg_t&& arg,
95 std::integer_sequence<std::size_t, i, Is...>)
96{
97 f(std::integral_constant<std::size_t, i> {}, get<i>(arg));
98 for_constexpr_with_arg(f, std::forward<arg_t>(arg),
99 std::integer_sequence<std::size_t, Is...> {});
100}
101
102template <typename lambda, typename arg_t>
103constexpr void for_constexpr_with_arg(lambda&& f, arg_t&& arg)
104{
105 using indices =
106 std::make_index_sequence<tuple_size<std::remove_reference_t<arg_t>>::value>;
107 for_constexpr_with_arg(std::forward<lambda>(f), std::forward<arg_t>(arg),
108 indices{});
109}
110
111template <std::size_t I, typename Tuple, std::size_t... Is>
112std::array<bool, sizeof...(Is)>
113make_dependency_array(const Tuple& inputs, std::index_sequence<Is...>)
114{
115 return { (get<I>(inputs).GetFieldId() == get<Is>(inputs).GetFieldId())... };
116}
117
118template <typename... input_ts, std::size_t... Is>
120 std::index_sequence<Is...>)
121{
122 constexpr std::size_t N = sizeof...(input_ts);
123
124 if constexpr (N == 0)
125 return std::unordered_map<int, std::array<bool, 0>> {};
126
127 std::unordered_map<int, std::array<bool, N>> map;
128
129 (void)std::initializer_list<int>
130 {
131 (
132 map[get<Is>(inputs).GetFieldId()] =
133 make_dependency_array<Is>(inputs, std::make_index_sequence<N>{}),
134 0
135 )...
136 };
137
138 return map;
139}
140
141// @brief Create a dependency map from a tuple of inputs.
142//
143// @param inputs a tuple of objects derived from FieldOperator.
144// @returns an unordered_map where the keys are the field IDs and the values
145// are arrays of booleans indicating which inputs depend on each field ID.
146template <typename... input_ts>
148{
149 return make_dependency_map_impl(inputs, std::index_sequence_for<input_ts...> {});
150}
151
152// @brief Get the type name of a template parameter T.
153//
154// Convenient helper function for debugging.
155// Usage example
156// ```c++
157// mfem::out << get_type_name<int>() << std::endl;
158// ```
159// prints "int".
160template <typename T>
161constexpr auto get_type_name() -> std::string_view
162{
163#if defined(__clang__)
164 constexpr auto prefix = std::string_view {"[T = "};
165 constexpr auto suffix = "]";
166 constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
167#elif defined(__GNUC__)
168 constexpr auto prefix = std::string_view {"with T = "};
169 constexpr auto suffix = "; ";
170 constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
171#elif defined(_MSC_VER)
172 constexpr auto prefix = std::string_view {"get_type_name<"};
173 constexpr auto suffix = ">(void)";
174 constexpr auto function = std::string_view{__FUNCSIG__};
175#else
176#error Unsupported compiler
177#endif
178
179 const auto start = function.find(prefix) + prefix.size();
180 const auto end = function.find(suffix);
181 const auto size = end - start;
182
183 return function.substr(start, size);
184}
185
186template <typename Tuple, std::size_t... Is>
187void print_tuple_impl(const Tuple& t, std::index_sequence<Is...>)
188{
189 ((out << (Is == 0 ? "" : ", ") << std::get<Is>(t)), ...);
190}
191
192// @brief Helper function to print a single tuple.
193//
194// @param t The tuple to print.
195template <typename... Args>
196void print_tuple(const std::tuple<Args...>& t)
197{
198 out << "(";
199 print_tuple_impl(t, std::index_sequence_for<Args...> {});
200 out << ")";
201}
202
203/// @brief Pretty print an mfem::DenseMatrix to out
204///
205/// Formatted s.t. the output is
206/// [[v00, v01, ..., v0n],
207/// [v10, v11, ..., v1n],
208/// ..., vmn]]
209/// which is compatible with numpy syntax.
210///
211/// @param out ostream to print to
212/// @param A mfem::DenseMatrix to print
213inline
214void pretty_print(std::ostream &out, const mfem::DenseMatrix &A)
215{
216 // Determine the max width of any entry in scientific notation
217 int max_width = 0;
218 for (int i = 0; i < A.NumRows(); ++i)
219 {
220 for (int j = 0; j < A.NumCols(); ++j)
221 {
222 std::ostringstream oss;
223 oss << std::scientific << std::setprecision(2) << A(i, j);
224 max_width = std::max(max_width, static_cast<int>(oss.str().length()));
225 }
226 }
227
228 out << "[\n";
229 for (int i = 0; i < A.NumRows(); ++i)
230 {
231 out << " [";
232 for (int j = 0; j < A.NumCols(); ++j)
233 {
234 out << std::setw(max_width) << std::scientific << std::setprecision(2) <<
235 A(i, j);
236
237 if (j < A.NumCols() - 1)
238 {
239 out << ", ";
240 }
241 }
242 out << "]";
243 if (i < A.NumRows() - 1)
244 {
245 out << ",\n";
246 }
247 else
248 {
249 out << "\n";
250 }
251 }
252 out << "]\n";
253}
254
255/// @brief Pretty print an mfem::Vector to out
256///
257/// Formatted s.t. the output is [v0, v1, ..., vn] which
258/// is compatible with numpy syntax.
259///
260/// @param v vector of vectors to print
261inline
263{
264 out << "[";
265 for (int i = 0; i < v.Size(); i++)
266 {
267 out << v(i);
268 if (i < v.Size() - 1)
269 {
270 out << ", ";
271 }
272 }
273 out << "]\n";
274}
275
276/// @brief Pretty print an mfem::Array to out
277///
278/// T has to have an overloaded operator<<
279///
280/// Formatted s.t. the output is [v0, v1, ..., vn] which
281/// is compatible with numpy syntax.
282///
283/// @param v vector of vectors to print
284template <typename T>
286{
287 out << "[";
288 for (int i = 0; i < v.Size(); i++)
289 {
290 out << v[i];
291 if (i < v.Size() - 1)
292 {
293 out << ", ";
294 }
295 }
296 out << "]\n";
297}
298
299/// @brief Pretty prints an unordered map of std::array to out
300///
301/// Useful for printing the output of make_dependency_map
302///
303/// @param map unordered map to print
304/// @tparam T type of array elements
305/// @tparam N size of array
306template<typename K, typename T, std::size_t N>
307void pretty_print(const std::unordered_map<K,std::array<T,N>>& map)
308{
309 out << "{";
310 std::size_t count = 0;
311 for (const auto& [key, value] : map)
312 {
313 out << key << ": [";
314 for (std::size_t i = 0; i < N; i++)
315 {
316 out << value[i];
317 if (i < N-1) { out << ", "; }
318 }
319 out << "]";
320 if (count < map.size() - 1)
321 {
322 out << ", ";
323 }
324 count++;
325 }
326 out << "}\n";
327}
328
329inline
330void print_mpi_root(const std::string& msg)
331{
332 auto myrank = Mpi::WorldRank();
333 if (myrank == 0)
334 {
335 out << msg << std::endl;
336 out.flush(); // Ensure output is flushed
337 }
338}
339
340/// @brief print with MPI rank synchronization
341///
342/// @param msg Message to print
343inline
344void print_mpi_sync(const std::string& msg)
345{
346 auto myrank = static_cast<size_t>(Mpi::WorldRank());
347 auto nranks = static_cast<size_t>(Mpi::WorldSize());
348
349 if (nranks == 1)
350 {
351 // Single process case - just print directly
352 out << msg << std::endl;
353 return;
354 }
355
356 // First gather string lengths
357 size_t msg_len = msg.length();
358 std::vector<size_t> lengths(nranks);
359 MPI_Gather(&msg_len, 1, MPITypeMap<size_t>::mpi_type,
360 lengths.data(), 1, MPITypeMap<size_t>::mpi_type,
361 0, MPI_COMM_WORLD);
362
363 if (myrank == 0)
364 {
365 // Rank 0: Allocate receive buffer based on gathered lengths
366 std::vector<std::string> messages(nranks);
367 messages[0] = msg; // Store rank 0's message
368
369 // Receive messages from other ranks
370 for (size_t r = 1; r < nranks; r++)
371 {
372 std::vector<char> buffer(lengths[r] + 1);
373 MPI_Recv(buffer.data(), static_cast<int>(lengths[r]), MPI_CHAR,
374 static_cast<int>(r), 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
375 messages[r] = std::string(buffer.data(), static_cast<size_t>(lengths[r]));
376 }
377
378 // Print all messages in rank order
379 for (size_t r = 0; r < nranks; r++)
380 {
381 out << "[Rank " << r << "] " << messages[r] << std::endl;
382 }
383 out.flush();
384 }
385 else
386 {
387 // Other ranks: Send message to rank 0
388 MPI_Send(const_cast<char*>(msg.c_str()), static_cast<int>(msg_len), MPI_CHAR,
389 0, 0, MPI_COMM_WORLD);
390 }
391
392 // Final barrier to ensure completion
393 MPI_Barrier(MPI_COMM_WORLD);
394}
395
396/// @brief Pretty print an mfem::Vector with MPI rank
397///
398/// @param v vector to print
399inline
401{
402 std::stringstream ss;
403 ss << "[";
404 for (int i = 0; i < v.Size(); i++)
405 {
406 ss << v(i);
407 if (i < v.Size() - 1) { ss << ", "; }
408 }
409 ss << "]";
410
411 print_mpi_sync(ss.str());
412}
413
414
415template <typename ... Ts>
416constexpr auto decay_types(tuple<Ts...> const &)
418
419template <typename T>
420using decay_tuple = decltype(decay_types(std::declval<T>()));
421
422template <class F> struct FunctionSignature;
423
424template <typename output_t, typename... input_ts>
425struct FunctionSignature<output_t(input_ts...)>
426{
427 using return_t = output_t;
428 using parameter_ts = tuple<input_ts...>;
429};
430
431template <class T> struct create_function_signature;
432
433// Specialization for member functions (lambdas)
434template <typename output_t, typename T, typename... input_ts>
435struct create_function_signature<output_t (T::*)(input_ts...) const>
436{
437 using type = FunctionSignature<output_t(input_ts...)>;
438};
439
440// Specialization for function pointers
441template <typename output_t, typename... input_ts>
442struct create_function_signature<output_t (*)(input_ts...)>
443{
444 using type = FunctionSignature<output_t(input_ts...)>;
445};
446
447template <typename T>
448constexpr int GetFieldId()
449{
450 return T::GetFieldId();
451}
452
453template <typename Tuple, std::size_t... Is>
454constexpr auto extract_field_ids_impl(Tuple&& t, std::index_sequence<Is...>)
455{
456 return std::array<int, sizeof...(Is)>
457 {
458 std::decay_t<decltype(std::get<Is>(t))>{}.GetFieldId()...
459 };
460}
461
462/// @brief Extracts field IDs from a tuple of objects derived from FieldOperator.
463///
464/// @param t the tuple to extract field IDs from.
465/// @returns an array of field IDs.
466template <typename... Ts>
467constexpr auto extract_field_ids(const std::tuple<Ts...>& t)
468{
469 return extract_field_ids_impl(t, std::index_sequence_for<Ts...> {});
470}
471
472/// @brief Helper function to check if an element is in the array.
473///
474/// @param arr the array to search in.
475/// @param size the size of the array.
476/// @param value the value to search for.
477/// @returns true if the value is found, false otherwise.
478constexpr bool contains(const int* arr, std::size_t size, int value)
479{
480 for (std::size_t i = 0; i < size; ++i)
481 {
482 if (arr[i] == value)
483 {
484 return true;
485 }
486 }
487 return false;
488}
489
490/// @brief Function to count unique field IDs in a tuple.
491///
492/// @param t the tuple to count unique field IDs from.
493/// @returns the number of unique field IDs.
494template <typename... Ts>
495constexpr std::size_t count_unique_field_ids(const std::tuple<Ts...>& t)
496{
497 auto ids = extract_field_ids(t);
498 constexpr std::size_t size = sizeof...(Ts);
499
500 std::array<int, size> unique_ids = {};
501 std::size_t unique_count = 0;
502
503 for (std::size_t i = 0; i < size; ++i)
504 {
505 if (!contains(unique_ids.data(), unique_count, ids[i]))
506 {
507 unique_ids[unique_count] = ids[i];
508 ++unique_count;
509 }
510 }
511
512 return unique_count;
513}
514
515/// @brief Get marked entries from an std::array based on a marker array.
516///
517/// @param a the std::array to get entries from.
518/// @param marker the marker std::array indicating which entries to get.
519/// @returns a std::vector containing the marked entries.
520template <typename T, std::size_t N>
522 const std::array<T, N> &a,
523 const std::array<bool, N> &marker)
524{
525 std::vector<T> r;
526 for (int i = 0; i < N; i++)
527 {
528 if (marker[i])
529 {
530 r.push_back(a[i]);
531 }
532 }
533 return r;
534}
535
536/// @brief Filter fields from a tuple based on their field IDs.
537///
538/// @param t the tuple to filter fields from.
539/// @returns a tuple containing only the fields with field IDs not equal to -1.
540template <typename... Ts>
541constexpr auto filter_fields(const std::tuple<Ts...>& t)
542{
543 return std::tuple_cat(
544 std::conditional_t<Ts::GetFieldId() != -1, std::tuple<Ts>, std::tuple<>> {}...);
545}
546
547/// @brief FieldDescriptor struct
548///
549/// This struct is used to store information about a field.
551{
553 std::variant<const FiniteElementSpace *,
554 const ParFiniteElementSpace *,
555 const ParameterSpace *>;
556
557 /// Field ID
558 std::size_t id;
559
560 /// Field variant
562
563 /// Default constructor
565 id(SIZE_MAX), data(data_variant_t{}) {}
566
567 /// Constructor
568 template <typename T>
569 FieldDescriptor(std::size_t field_id, const T* v) :
570 id(field_id), data(v) {}
571};
572
573namespace dfem
574{
575template <class... T> constexpr bool always_false = false;
576}
577
578/// @brief Entity struct
579///
580/// This struct is used to store information about an entity type.
581namespace Entity
582{
583struct Element;
584struct BoundaryElement;
585struct Face;
586struct BoundaryFace;
587}
588
589/// @brief ThreadBlocks struct
590///
591/// This struct is used to store information about thread blocks
592/// for GPU dispatch.
594{
595 int x = 1;
596 int y = 1;
597 int z = 1;
598};
599
600#if defined(MFEM_USE_CUDA_OR_HIP_LANG)
601template <typename func_t>
602__global__ void forall_kernel_shmem(func_t f, int n)
603{
604 int i = blockIdx.x;
605 extern __shared__ real_t shmem[];
606 if (i < n)
607 {
608 f(i, shmem);
609 }
610}
611#endif
612
613template <typename func_t>
614void forall(func_t f,
615 const int &N,
616 const ThreadBlocks &blocks,
617 int num_shmem = 0,
618 real_t *shmem = nullptr)
619{
620 internal::RequireKernelCompilation();
621
622#if defined(MFEM_USE_CUDA_OR_HIP_LANG)
624 {
625 // int gridsize = (N + Z - 1) / Z;
626 int num_bytes = num_shmem * sizeof(decltype(shmem));
627 dim3 block_size(blocks.x, blocks.y, blocks.z);
629#if defined(MFEM_USE_CUDA)
630 MFEM_GPU_CHECK(cudaGetLastError());
631#elif defined(MFEM_USE_HIP)
632 MFEM_GPU_CHECK(hipGetLastError());
633#endif
634 MFEM_DEVICE_SYNC;
635 return;
636 }
637#endif
639 {
640 MFEM_ASSERT(!((bool)num_shmem != (bool)shmem),
641 "Backend::CPU needs a pre-allocated shared memory block");
642 for (int i = 0; i < N; i++)
643 {
644 f(i, shmem);
645 }
646 }
647 else
648 {
649 MFEM_ABORT("no compute backend available");
650 }
651}
652
653/// @todo To be removed.
654class FDJacobian : public Operator
655{
656public:
657 FDJacobian(const Operator &op, const Vector &x, real_t fixed_eps = 0.0) :
658 Operator(op.Height(), op.Width()),
659 op(op),
660 x(x),
661 fixed_eps(fixed_eps)
662 {
663 f.UseDevice(x.UseDevice());
664 f.SetSize(Height());
665
666 xpev.UseDevice(x.UseDevice());
667 xpev.SetSize(Width());
668
669 op.Mult(x, f);
670
671 const real_t xnorm_local = x.Norml2();
672 MPI_Allreduce(&xnorm_local, &xnorm, 1, MPITypeMap<real_t>::mpi_type, MPI_SUM,
673 MPI_COMM_WORLD);
674 }
675
676 void Mult(const Vector &v, Vector &y) const override;
677
678 virtual MemoryClass GetMemoryClass() const override
679 {
681 }
682
683private:
684 const Operator &op;
685 Vector x, f;
686 mutable Vector xpev;
687 real_t lambda = 1.0e-6;
688 real_t fixed_eps;
689 real_t xnorm;
690};
691
692/// @brief Find the index of a field descriptor in a vector of field descriptors.
693///
694/// @param id the field ID to search for.
695/// @param fields the vector of field descriptors.
696/// @returns the index of the field descriptor with the given ID,
697/// or SIZE_MAX if not found.
698inline
699std::size_t FindIdx(const std::size_t& id,
700 const std::vector<FieldDescriptor>& fields)
701{
702 for (std::size_t i = 0; i < fields.size(); i++)
703 {
704 if (fields[i].id == id)
705 {
706 return i;
707 }
708 }
709 return SIZE_MAX;
710}
711
712/// @brief Get the vdof size of a field descriptor.
713///
714/// @param f the field descriptor.
715/// @returns the vdof size of the field descriptor.
716inline
718{
719 return std::visit([](auto arg)
720 {
721 if (arg == nullptr)
722 {
723 MFEM_ABORT("FieldDescriptor data is nullptr");
724 }
725
726 using T = std::decay_t<decltype(arg)>;
727 if constexpr (std::is_same_v<T, const FiniteElementSpace *> ||
728 std::is_same_v<T, const ParFiniteElementSpace *>)
729 {
730 return arg->GetVSize();
731 }
732 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
733 {
734 return arg->GetVSize();
735 }
736 else
737 {
738 static_assert(dfem::always_false<T>, "can't use GetVSize on type");
739 }
740 return 0; // Unreachable, but avoids compiler warning
741 }, f.data);
742}
743
744/// @brief Get the element vdofs of a field descriptor.
745///
746/// @note Can't be used with ParameterSpace.
747///
748/// @param f the field descriptor.
749/// @param el the element index.
750/// @param vdofs the array to store the element vdofs.
751inline
752void GetElementVDofs(const FieldDescriptor &f, int el, Array<int> &vdofs)
753{
754 return std::visit([&](auto arg)
755 {
756 if (arg == nullptr)
757 {
758 MFEM_ABORT("FieldDescriptor data is nullptr");
759 }
760
761 using T = std::decay_t<decltype(arg)>;
762 if constexpr (std::is_same_v<T, const FiniteElementSpace *>)
763 {
764 arg->GetElementVDofs(el, vdofs);
765 }
766 else if constexpr (std::is_same_v<T, const ParFiniteElementSpace *>)
767 {
768 arg->GetElementVDofs(el, vdofs);
769 }
770 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
771 {
772 MFEM_ABORT("internal error");
773 }
774 else
775 {
776 static_assert(dfem::always_false<T>, "can't use GetElementVdofs on type");
777 }
778 }, f.data);
779}
780
781/// @brief Get the true dof size of a field descriptor.
782///
783/// @param f the field descriptor.
784/// @returns the true dof size of the field descriptor.
785inline
787{
788 return std::visit([](auto arg)
789 {
790 if (arg == nullptr)
791 {
792 MFEM_ABORT("FieldDescriptor data is nullptr");
793 }
794
795 using T = std::decay_t<decltype(arg)>;
796 if constexpr (std::is_same_v<T, const FiniteElementSpace *>)
797 {
798 return arg->GetTrueVSize();
799 }
800 else if constexpr (std::is_same_v<T, const ParFiniteElementSpace *>)
801 {
802 return arg->GetTrueVSize();
803 }
804 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
805 {
806 return arg->GetTrueVSize();
807 }
808 else
809 {
810 static_assert(dfem::always_false<T>, "can't use GetTrueVSize on type");
811 }
812 return 0; // Unreachable, but avoids compiler warning
813 }, f.data);
814}
815
816/// @brief Get the vdim of a field descriptor.
817///
818/// @param f the field descriptor.
819/// @returns the vdim of the field descriptor.
820inline
822{
823 return std::visit([](auto && arg)
824 {
825 using T = std::decay_t<decltype(arg)>;
826 if constexpr (std::is_same_v<T, const FiniteElementSpace *>)
827 {
828 return arg->GetVDim();
829 }
830 else if constexpr (std::is_same_v<T, const ParFiniteElementSpace *>)
831 {
832 return arg->GetVDim();
833 }
834 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
835 {
836 return arg->GetVDim();
837 }
838 else
839 {
840 static_assert(dfem::always_false<T>, "can't use GetVDim on type");
841 }
842 return 0; // Unreachable, but avoids compiler warning
843 }, f.data);
844}
845
846/// @brief Get the spatial dimension of a field descriptor.
847///
848/// @param f the field descriptor.
849/// @tparam entity_t the entity type (see Entity).
850/// @returns the spatial dimension of the field descriptor.
851template <typename entity_t>
853{
854 return std::visit([](auto && arg)
855 {
856 using T = std::decay_t<decltype(arg)>;
857 if constexpr (std::is_same_v<T, const FiniteElementSpace *> ||
858 std::is_same_v<T, const ParFiniteElementSpace *>)
859 {
860 if constexpr (std::is_same_v<entity_t, Entity::Element>)
861 {
862 return arg->GetMesh()->Dimension();
863 }
864 else if constexpr (std::is_same_v<entity_t, Entity::BoundaryElement>)
865 {
866 return arg->GetMesh()->Dimension() - 1;
867 }
868 }
869 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
870 {
871 return arg->Dimension();
872 }
873 else
874 {
875 static_assert(dfem::always_false<T>, "can't use GetDimension on type");
876 }
877 return 0; // Unreachable, but avoids compiler warning
878 }, f.data);
879}
880
881
882/// @brief Get the prolongation operator for a field descriptor.
883///
884/// @param f the field descriptor.
885/// @returns the prolongation operator for the field descriptor.
886inline
888{
889 return std::visit([](auto&& arg) -> const Operator*
890 {
891 using T = std::decay_t<decltype(arg)>;
892 if constexpr (std::is_same_v<T, const FiniteElementSpace *> ||
893 std::is_same_v<T, const ParFiniteElementSpace *>)
894 {
895 return arg->GetProlongationMatrix();
896 }
897 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
898 {
899 return arg->GetProlongationMatrix();
900 }
901 else
902 {
903 static_assert(dfem::always_false<T>, "can't use GetProlongation on type");
904 }
905 return nullptr; // Unreachable, but avoids compiler warning
906 }, f.data);
907}
908
909/// @brief Get the element restriction operator for a field descriptor.
910///
911/// @param f the field descriptor.
912/// @param o the element dof ordering.
913/// @returns the element restriction operator for the field descriptor in
914/// specified ordering.
915inline
918{
919 return std::visit([&o](auto&& arg) -> const Operator*
920 {
921 using T = std::decay_t<decltype(arg)>;
922 if constexpr (std::is_same_v<T, const FiniteElementSpace *>
923 || std::is_same_v<T, const ParFiniteElementSpace *>)
924 {
925 return arg->GetElementRestriction(o);
926 }
927 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
928 {
929 return arg->GetElementRestriction(o);
930 }
931 else
932 {
933 static_assert(dfem::always_false<T>,
934 "can't use get_element_restriction on type");
935 }
936 return nullptr; // Unreachable, but avoids compiler warning
937 }, f.data);
938}
939
940/// @brief Get the face restriction operator for a field descriptor.
941///
942/// @param f the field descriptor.
943/// @param o the face dof ordering.
944/// @param ft the face type
945/// @param m indicator if single or double valued
946/// @returns the face restriction operator for the field descriptor in
947/// specified ordering.
948inline
951 FaceType ft,
952 L2FaceValues m)
953{
954 return std::visit([&o, &ft, &m](auto&& arg) -> const Operator*
955 {
956 using T = std::decay_t<decltype(arg)>;
957 if constexpr (std::is_same_v<T, const FiniteElementSpace *> ||
958 std::is_same_v<T, const ParFiniteElementSpace *>)
959 {
960 return arg->GetFaceRestriction(o, ft, m);
961 }
962 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
963 {
964 // ParameterSpace does not support face restrictions
965 MFEM_ABORT("internal error");
966 }
967 else
968 {
969 static_assert(dfem::always_false<T>,
970 "can't use get_face_restriction on type");
971 }
972 return nullptr; // Unreachable, but avoids compiler warning
973 }, f.data);
974}
975
976/// @brief Get the restriction operator for a field descriptor.
977///
978/// @param f the field descriptor.
979/// @param o the element dof ordering.
980/// @returns the restriction operator for the field descriptor in
981/// specified ordering.
982template <typename entity_t>
983inline
985 const ElementDofOrdering &o)
986{
987 if constexpr (std::is_same_v<entity_t, Entity::Element>)
988 {
989 return get_element_restriction(f, o);
990 }
991 else if constexpr (std::is_same_v<entity_t, Entity::BoundaryElement>)
992 {
995 }
996 MFEM_ABORT("restriction not implemented for Entity");
997 return nullptr;
998}
999
1000/// @brief Get a transpose restriction callback for a field descriptor.
1001///
1002/// @param f the field descriptor.
1003/// @param o the element dof ordering.
1004/// @param fop the field operator.
1005/// @returns a tuple containing a std::function with the transpose
1006/// restriction callback and it's height.
1007template <typename entity_t, typename fop_t>
1008inline std::tuple<std::function<void(const Vector&, Vector&)>, int>
1010 const FieldDescriptor &f,
1011 const ElementDofOrdering &o,
1012 const fop_t &fop)
1013{
1014 if constexpr (is_sum_fop<fop_t>::value)
1015 {
1016 auto RT = [=](const Vector &v_e, Vector &v_l)
1017 {
1018 v_l += v_e;
1019 };
1020 return std::make_tuple(RT, 1);
1021 }
1022 else
1023 {
1024 const Operator *R = get_restriction<entity_t>(f, o);
1025 std::function<void(const Vector&, Vector&)> RT = [=](const Vector &x, Vector &y)
1026 {
1027 R->AddMultTranspose(x, y);
1028 };
1029 return std::make_tuple(RT, R->Height());
1030 }
1031 return std::make_tuple(
1032 std::function<void(const Vector&, Vector&)>([](const Vector&, Vector&)
1033 {
1034 /* no-op */
1035 }), 0); // Never reached, but avoids compiler warning.
1036}
1037
1038/// @brief Apply the prolongation operator to a field.
1039///
1040/// @param field the field descriptor.
1041/// @param x the input vector in tdofs.
1042/// @param field_l the output vector in vdofs.
1043inline
1044void prolongation(const FieldDescriptor field, const Vector &x, Vector &field_l)
1045{
1046 const auto P = get_prolongation(field);
1047 field_l.SetSize(P->Height());
1048 P->Mult(x, field_l);
1049}
1050
1051/// @brief Apply the prolongation operator to a vector of fields.
1052///
1053/// x is a long vector containing the data for all fields on tdofs and
1054/// fields contains the information about each individual field to retrieve
1055/// it's corresponding prolongation.
1056///
1057/// @param fields the array of field descriptors.
1058/// @param x the input vector in tdofs.
1059/// @param fields_l the array of output vectors in vdofs.
1060/// @tparam N the number of fields.
1061/// @tparam M the number of output fields.
1062template <std::size_t N, std::size_t M>
1063void prolongation(const std::array<FieldDescriptor, N> fields,
1064 const Vector &x,
1065 std::array<Vector, M> &fields_l)
1066{
1067 int data_offset = 0;
1068 for (int i = 0; i < N; i++)
1069 {
1070 const auto P = get_prolongation(fields[i]);
1071 const int width = P->Width();
1072 // const Vector x_i(x.GetData() + data_offset, width);
1073 const Vector x_i(const_cast<Vector&>(x), data_offset, width);
1074 fields_l[i].SetSize(P->Height());
1075
1076 P->Mult(x_i, fields_l[i]);
1077 data_offset += width;
1078 }
1079}
1080
1081/// @brief Apply the prolongation operator to a vector of fields.
1082///
1083/// x is a long vector containing the data for all fields on tdofs and
1084/// fields contains the information about each individual field to retrieve
1085/// it's corresponding prolongation.
1086///
1087/// @param fields the array of field descriptors.
1088/// @param x the input vector in tdofs.
1089/// @param fields_l the array of output vectors in vdofs.
1090inline
1091void prolongation(const std::vector<FieldDescriptor> fields,
1092 const Vector &x,
1093 std::vector<Vector> &fields_l)
1094{
1095 int data_offset = 0;
1096 for (std::size_t i = 0; i < fields.size(); i++)
1097 {
1098 const auto P = get_prolongation(fields[i]);
1099 const int width = P->Width();
1100 const Vector x_i(const_cast<Vector&>(x), data_offset, width);
1101 fields_l[i].SetSize(P->Height());
1102 P->Mult(x_i, fields_l[i]);
1103 data_offset += width;
1104 }
1105}
1106
1107inline
1108void get_lvectors(const std::vector<FieldDescriptor> fields,
1109 const Vector &x,
1110 std::vector<Vector> &fields_l)
1111{
1112 int data_offset = 0;
1113 for (std::size_t i = 0; i < fields.size(); i++)
1114 {
1115 const int sz = GetVSize(fields[i]);
1116 fields_l[i].SetSize(sz);
1117
1118 const Vector x_i(const_cast<Vector&>(x), data_offset, sz);
1119 fields_l[i] = x_i;
1120
1121 data_offset += sz;
1122 }
1123}
1124
1125/// @brief Get a transpose prolongation callback for a field descriptor.
1126///
1127/// In the special case of a one field operator, the transpose prolongation
1128/// is a simple sum of the local vector that is reduced to the global vector.
1129///
1130/// @param f the field descriptor.
1131/// @param fop the field operator.
1132/// @param mpi_comm the MPI communicator.
1133/// @tparam fop_t the field operator type.
1134template <typename fop_t>
1135inline
1136std::function<void(const Vector&, Vector&)> get_prolongation_transpose(
1137 const FieldDescriptor &f,
1138 const fop_t &fop,
1139 MPI_Comm mpi_comm)
1140{
1141 if constexpr (is_sum_fop<fop_t>::value)
1142 {
1143 auto PT = [=](const Vector &r_local, Vector &y)
1144 {
1145 MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
1146 real_t local_sum = r_local.Sum();
1147 MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM, mpi_comm);
1148 };
1149 return PT;
1150 }
1151 else if constexpr (is_identity_fop<fop_t>::value)
1152 {
1153 auto PT = [=](const Vector &r_local, Vector &y)
1154 {
1155 y = r_local;
1156 };
1157 return PT;
1158 }
1159 const Operator *P = get_prolongation(f);
1160 auto PT = [=](const Vector &r_local, Vector &y)
1161 {
1162 P->MultTranspose(r_local, y);
1163 };
1164 return PT;
1165}
1166
1167/// @brief Apply the restriction operator to a field.
1168///
1169/// @param u the field descriptor.
1170/// @param u_l the input vector in vdofs.
1171/// @param field_e the output vector in edofs.
1172/// @param ordering the element dof ordering.
1173/// @tparam entity_t the entity type (see Entity).
1174template <typename entity_t>
1176 const Vector &u_l,
1177 Vector &field_e,
1178 ElementDofOrdering ordering)
1179{
1180 const auto R = get_restriction<entity_t>(u, ordering);
1181 MFEM_ASSERT(R->Width() == u_l.Size(),
1182 "restriction not applicable to given data size");
1183 const int height = R->Height();
1184 field_e.SetSize(height);
1185 R->Mult(u_l, field_e);
1186}
1187
1188/// @brief Apply the restriction operator to a vector of fields.
1189///
1190/// @param u the vector of field descriptors.
1191/// @param u_l the vector of input vectors in vdofs.
1192/// @param fields_e the vector of output vectors in edofs.
1193/// @param ordering the element dof ordering.
1194/// @param offset the array index offset to start writing in fields_e.
1195/// @tparam entity_t the entity type (see Entity).
1196template <typename entity_t>
1197void restriction(const std::vector<FieldDescriptor> u,
1198 const std::vector<Vector> &u_l,
1199 std::vector<Vector> &fields_e,
1200 ElementDofOrdering ordering,
1201 const int offset = 0)
1202{
1203 for (std::size_t i = 0; i < u.size(); i++)
1204 {
1205 const auto R = get_restriction<entity_t>(u[i], ordering);
1206 MFEM_ASSERT(R->Width() == u_l[i].Size(),
1207 "restriction not applicable to given data size");
1208 const int height = R->Height();
1209 fields_e[i + offset].SetSize(height);
1210 R->Mult(u_l[i], fields_e[i + offset]);
1211 }
1212}
1213
1214// TODO: keep this temporarily
1215template <std::size_t N, std::size_t M>
1216void element_restriction(const std::array<FieldDescriptor, N> u,
1217 const std::array<Vector, N> &u_l,
1218 std::array<Vector, M> &fields_e,
1219 ElementDofOrdering ordering,
1220 const int offset = 0)
1221{
1222 for (int i = 0; i < N; i++)
1223 {
1224 const auto R = get_element_restriction(u[i], ordering);
1225 MFEM_ASSERT(R->Width() == u_l[i].Size(),
1226 "element restriction not applicable to given data size");
1227 const int height = R->Height();
1228 fields_e[i + offset].SetSize(height);
1229 R->Mult(u_l[i], fields_e[i + offset]);
1230 }
1231}
1232
1233/// @brief Get the number of entities of a given type.
1234///
1235/// @param mesh the mesh.
1236/// @tparam entity_t the entity type (see Entity).
1237/// @returns the number of entities of the given type.
1238template <typename entity_t>
1240{
1241 if constexpr (std::is_same_v<entity_t, Entity::Element>)
1242 {
1243 return mesh.GetNE();
1244 }
1245 else if constexpr (std::is_same_v<entity_t, Entity::BoundaryElement>)
1246 {
1247 return mesh.GetNBE();
1248 }
1249 else
1250 {
1251 static_assert(dfem::always_false<entity_t>, "can't use GetNumEntites on type");
1252 }
1253 return 0; // Unreachable, but avoids compiler warning
1254}
1255
1256/// @brief Get the GetDofToQuad object for a given entity type.
1257///
1258/// This function retrieves the DofToQuad object for a given field descriptor
1259/// and integration rule.
1260///
1261/// @param f the field descriptor.
1262/// @param ir the integration rule.
1263/// @param mode the mode of the DofToQuad object.
1264/// @tparam entity_t the entity type (see Entity).
1265template <typename entity_t>
1266inline
1268 const IntegrationRule &ir,
1269 DofToQuad::Mode mode)
1270{
1271 return std::visit([&ir, &mode](auto&& arg) -> const DofToQuad*
1272 {
1273 using T = std::decay_t<decltype(arg)>;
1274 if constexpr (std::is_same_v<T, const FiniteElementSpace *>
1275 || std::is_same_v<T, const ParFiniteElementSpace *>)
1276 {
1277 if constexpr (std::is_same_v<entity_t, Entity::Element>)
1278 {
1279 return &arg->GetTypicalFE()->GetDofToQuad(ir, mode);
1280 }
1281 else if constexpr (std::is_same_v<entity_t, Entity::BoundaryElement>)
1282 {
1283 return &arg->GetTypicalTraceElement()->GetDofToQuad(ir, mode);
1284 }
1285 }
1286 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
1287 {
1288 return &arg->GetDofToQuad();
1289 }
1290 else
1291 {
1292 static_assert(dfem::always_false<T>, "can't use GetDofToQuad on type");
1293 }
1294 return nullptr; // Unreachable, but avoids compiler warning
1295 }, f.data);
1296}
1297
1298/// @brief Check the compatibility of a field operator type with a
1299/// FieldDescriptor.
1300///
1301/// This function checks if the field operator type is compatible with the
1302/// FieldDescriptor type.
1303///
1304/// @param f the field descriptor.
1305/// @tparam field_operator_t the field operator type.
1306template <typename field_operator_t>
1308{
1309 std::visit([](auto && arg)
1310 {
1311 using T = std::decay_t<decltype(arg)>;
1312 if constexpr (std::is_same_v<T, const FiniteElementSpace *> ||
1313 std::is_same_v<T, const ParFiniteElementSpace *>)
1314 {
1315 if constexpr (std::is_same_v<field_operator_t, Value<>>)
1316 {
1317 // Supported by all FE spaces
1318 }
1319 else if constexpr (std::is_same_v<field_operator_t, Gradient<>>)
1320 {
1321 MFEM_ASSERT(arg->GetTypicalElement()->GetMapType() ==
1323 "Gradient not compatible with FE");
1324 }
1325 else
1326 {
1328 "FieldOperator not compatible with FiniteElementSpace");
1329 }
1330 }
1331 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
1332 {
1333 if constexpr (std::is_same_v<field_operator_t, Identity<>>)
1334 {
1335 // Only supported field operation for ParameterSpace
1336 }
1337 else
1338 {
1340 "FieldOperator not compatible with ParameterSpace");
1341 }
1342 }
1343 else
1344 {
1346 "Operator not compatible with FE");
1347 }
1348 }, f.data);
1349}
1350
1351/// @brief Get the size on quadrature point for a field operator type
1352/// and FieldDescriptor combination.
1353///
1354/// @tparam entity_t the entity type (see Entity).
1355/// @tparam field_operator_t the field operator type.
1356/// @param f the field descriptor.
1357/// @returns the size on quadrature point.
1358template <typename entity_t, typename field_operator_t>
1359int GetSizeOnQP(const field_operator_t &, const FieldDescriptor &f)
1360{
1361 // CheckCompatibility<field_operator_t>(f);
1362
1364 {
1365 return GetVDim(f);
1366 }
1368 {
1369 return GetVDim(f) * GetDimension<entity_t>(f);
1370 }
1372 {
1373 return GetVDim(f);
1374 }
1375 else if constexpr (is_sum_fop<field_operator_t>::value)
1376 {
1377 return 1;
1378 }
1379 else
1380 {
1381 MFEM_ABORT("can't get size on quadrature point for field descriptor");
1382 }
1383 return 0; // Unreachable, but avoids compiler warning
1384}
1385
1386/// @brief Create a map from field operator types to FieldDescriptor indices.
1387///
1388/// @param fields the vector of field descriptors.
1389/// @param fops the field operator types.
1390/// @tparam entity_t the entity type (see Entity).
1391/// @returns an array mapping field operator types to field descriptor indices.
1392template <typename entity_t, typename field_operator_ts>
1393std::array<size_t, tuple_size<field_operator_ts>::value>
1395 const std::vector<FieldDescriptor> &fields,
1396 field_operator_ts &fops)
1397{
1398 std::array<size_t, tuple_size<field_operator_ts>::value> map;
1399
1400 auto find_id = [](const std::vector<FieldDescriptor> &fields, std::size_t i)
1401 {
1402 auto it = std::find_if(begin(fields), end(fields),
1403 [&](const FieldDescriptor &field)
1404 {
1405 return field.id == i;
1406 });
1407
1408 if (it == fields.end())
1409 {
1410 return SIZE_MAX;
1411 }
1412 return static_cast<size_t>(it - fields.begin());
1413 };
1414
1415 auto f = [&](auto &fop, auto &map)
1416 {
1417 if constexpr (std::is_same_v<std::decay_t<decltype(fop)>, Weight>)
1418 {
1419 // TODO-bug: stealing dimension from the first field
1420 fop.dim = GetDimension<entity_t>(fields[0]);
1421 fop.vdim = 1;
1422 fop.size_on_qp = 1;
1423 map = SIZE_MAX;
1424 }
1425 else
1426 {
1427 int i = find_id(fields, fop.GetFieldId());
1428 if (i != -1)
1429 {
1430 fop.dim = GetDimension<entity_t>(fields[i]);
1431 fop.vdim = GetVDim(fields[i]);
1432 fop.size_on_qp = GetSizeOnQP<entity_t>(fop, fields[i]);
1433 map = i;
1434 }
1435 else
1436 {
1437 MFEM_ABORT("can't find field for id: " << fop.GetFieldId());
1438 }
1439 }
1440 };
1441
1442 for_constexpr<tuple_size<field_operator_ts>::value>([&](auto idx)
1443 {
1444 f(get<idx>(fops), map[idx]);
1445 });
1446
1447 return map;
1448}
1449
1450/// @brief Wrap input memory for a given set of inputs.
1451template <typename input_t, std::size_t... i>
1452std::array<DeviceTensor<3>, sizeof...(i)> wrap_input_memory(
1453 std::array<Vector, sizeof...(i)> &input_qp_mem, int num_qp, int num_entities,
1454 const input_t &inputs, std::index_sequence<i...>)
1455{
1456 return {DeviceTensor<3>(input_qp_mem[i].Write(), get<i>(inputs).size_on_qp, num_qp, num_entities) ...};
1457}
1458
1459/// @brief Create input memory for a given set of inputs.
1460template <typename input_t, std::size_t... i>
1461std::array<Vector, sizeof...(i)> create_input_qp_memory(
1462 int num_qp,
1463 int num_entities,
1464 input_t &inputs,
1465 std::index_sequence<i...>)
1466{
1467 return {Vector(get<i>(inputs).size_on_qp * num_qp * num_entities)...};
1468}
1469
1470/// @brief DofToQuadMap struct
1471///
1472/// This struct is used to store the mapping from degrees of freedom to
1473/// quadrature points for a given field operator type.
1475{
1476 /// Enumeration for the indices of the mappings B and G.
1478 {
1481 DOF
1483
1484 /// @brief Basis functions evaluated at quadrature points.
1485 ///
1486 /// This is a 3D tensor with dimensions (num_qp, dim, num_dofs).
1488
1489 /// @brief Gradient of the basis functions evaluated at quadrature points.
1490 ///
1491 /// This is a 3D tensor with dimensions (num_qp, dim, num_dofs).
1493
1494 /// Reverse mapping indicating which input this map belongs to.
1495 int which_input = -1;
1496};
1497
1498/// @brief Get the size on quadrature point for a given set of inputs.
1499///
1500/// @param inputs the inputs tuple.
1501/// @returns a vector containing the size on quadrature point for each input.
1502template <typename input_t, std::size_t... i>
1503std::vector<int> get_input_size_on_qp(
1504 const input_t &inputs,
1505 std::index_sequence<i...>)
1506{
1507 return {get<i>(inputs).size_on_qp...};
1508}
1509
1524
1525template <std::size_t num_fields, std::size_t num_inputs, std::size_t num_outputs>
1527{
1529 std::array<int, 8> offsets;
1530 std::array<std::array<int, 2>, num_inputs> input_dtq_sizes;
1531 std::array<std::array<int, 2>, num_outputs> output_dtq_sizes;
1532 std::array<int, num_fields> field_sizes;
1534 std::array<int, num_inputs> input_sizes;
1535 std::array<int, num_inputs> shadow_sizes;
1537 std::array<int, 6> temp_sizes;
1538};
1539
1540template <typename entity_t, std::size_t num_fields, std::size_t num_inputs, std::size_t num_outputs, typename input_t>
1543 const std::array<DofToQuadMap, num_inputs> &input_dtq_maps,
1544 const std::array<DofToQuadMap, num_outputs> &output_dtq_maps,
1545 const std::vector<FieldDescriptor> &fields,
1546 const int &num_entities,
1547 const input_t &inputs,
1548 const int &num_qp,
1549 const std::vector<int> &input_size_on_qp,
1550 const int &residual_size_on_qp,
1551 const ElementDofOrdering &dof_ordering,
1552 const int &derivative_action_field_idx = -1)
1553{
1554 std::array<int, 8> offsets = {0};
1555 int total_size = 0;
1556
1557 offsets[SharedMemory::Index::INPUT_DTQ] = total_size;
1558 std::array<std::array<int, 2>, num_inputs> input_dtq_sizes;
1559 int max_dtq_qps = 0;
1560 int max_dtq_dofs = 0;
1561 for (std::size_t i = 0; i < num_inputs; i++)
1562 {
1563 auto a = input_dtq_maps[i].B.GetShape();
1564 input_dtq_sizes[i][0] = a[0] * a[1] * a[2];
1565 auto b = input_dtq_maps[i].G.GetShape();
1566 input_dtq_sizes[i][1] = b[0] * b[1] * b[2];
1567
1568 max_dtq_qps = std::max(max_dtq_qps, a[DofToQuadMap::Index::QP]);
1569 max_dtq_dofs = std::max(max_dtq_dofs, a[DofToQuadMap::Index::DOF]);
1570
1571 total_size += std::accumulate(std::begin(input_dtq_sizes[i]),
1572 std::end(input_dtq_sizes[i]),
1573 0);
1574 }
1575
1576 offsets[SharedMemory::Index::OUTPUT_DTQ] = total_size;
1577 std::array<std::array<int, 2>, num_outputs> output_dtq_sizes;
1578 for (std::size_t i = 0; i < num_outputs; i++)
1579 {
1580 auto a = output_dtq_maps[i].B.GetShape();
1581 output_dtq_sizes[i][0] = a[0] * a[1] * a[2];
1582 auto b = output_dtq_maps[i].G.GetShape();
1583 output_dtq_sizes[i][1] = b[0] * b[1] * b[2];
1584
1585 max_dtq_qps = std::max(max_dtq_qps, a[DofToQuadMap::Index::QP]);
1586 max_dtq_dofs = std::max(max_dtq_dofs, a[DofToQuadMap::Index::DOF]);
1587
1588 total_size += std::accumulate(std::begin(output_dtq_sizes[i]),
1589 std::end(output_dtq_sizes[i]),
1590 0);
1591 }
1592
1593 offsets[SharedMemory::Index::FIELD] = total_size;
1594 std::array<int, num_fields> field_sizes;
1595 for (std::size_t i = 0; i < num_fields; i++)
1596 {
1597 field_sizes[i] =
1598 num_entities
1599 ? (get_restriction<entity_t>(fields[i], dof_ordering)->Height()
1600 / num_entities)
1601 : 0;
1602 }
1603 total_size += std::accumulate(
1604 std::begin(field_sizes), std::end(field_sizes), 0);
1605
1606 offsets[SharedMemory::Index::DIRECTION] = total_size;
1607 int direction_size = 0;
1608 if (derivative_action_field_idx != -1)
1609 {
1610 direction_size =
1611 num_entities ? (get_restriction<entity_t>(
1612 fields[derivative_action_field_idx], dof_ordering)
1613 ->Height()
1614 / num_entities)
1615 : 0;
1616 total_size += direction_size;
1617 }
1618
1619 offsets[SharedMemory::Index::INPUT] = total_size;
1620 std::array<int, num_inputs> input_sizes;
1621 for (std::size_t i = 0; i < num_inputs; i++)
1622 {
1623 input_sizes[i] = input_size_on_qp[i] * num_qp;
1624 }
1625 total_size += std::accumulate(
1626 std::begin(input_sizes), std::end(input_sizes), 0);
1627
1628 offsets[SharedMemory::Index::SHADOW] = total_size;
1629 std::array<int, num_inputs> shadow_sizes{0};
1630 if (derivative_action_field_idx != -1)
1631 {
1632 for (std::size_t i = 0; i < num_inputs; i++)
1633 {
1634 shadow_sizes[i] = input_size_on_qp[i] * num_qp;
1635 }
1636 total_size += std::accumulate(
1637 std::begin(shadow_sizes), std::end(shadow_sizes), 0);
1638 }
1639
1640 offsets[SharedMemory::Index::OUTPUT] = total_size;
1641 const int residual_size = residual_size_on_qp;
1642 total_size += residual_size * num_qp;
1643
1644 offsets[SharedMemory::Index::TEMP] = total_size;
1645 constexpr int num_temp = 6;
1646 std::array<int, num_temp> temp_sizes = {0};
1647 // TODO-bug: this assumes q1d >= d1d
1648 const int q1d = max_dtq_qps;
1649 [[maybe_unused]] const int d1d = max_dtq_dofs;
1650
1651 // TODO-bug: this depends on the dimension
1652 constexpr int hardcoded_temp_num = 6;
1653 for (std::size_t i = 0; i < hardcoded_temp_num; i++)
1654 {
1655 // TODO-bug: over-allocates if q1d <= d1d
1656 temp_sizes[i] = q1d * q1d * q1d;
1657 }
1658 total_size += std::accumulate(
1659 std::begin(temp_sizes), std::end(temp_sizes), 0);
1660
1662 {
1663 total_size,
1664 offsets,
1665 input_dtq_sizes,
1666 output_dtq_sizes,
1667 field_sizes,
1668 direction_size,
1669 input_sizes,
1670 shadow_sizes,
1671 residual_size,
1672 temp_sizes
1673 };
1674}
1675
1676template <typename shmem_info_t>
1677void print_shared_memory_info(shmem_info_t &shmem_info)
1678{
1679 out << "Shared Memory Info\n"
1680 << "total size: " << shmem_info.total_size
1681 << " " << "(" << shmem_info.total_size * real_t(sizeof(real_t))/1024.0 << "kb)";
1682 out << "\ninput dtq sizes (B G): ";
1683 for (auto &i : shmem_info.input_dtq_sizes)
1684 {
1685 out << "(";
1686 for (int j = 0; j < 2; j++)
1687 {
1688 out << i[j];
1689 if (j < 1)
1690 {
1691 out << " ";
1692 }
1693 }
1694 out << ") ";
1695 }
1696 out << "\noutput dtq sizes (B G): ";
1697 for (auto &i : shmem_info.output_dtq_sizes)
1698 {
1699 out << "(";
1700 for (int j = 0; j < 2; j++)
1701 {
1702 out << i[j];
1703 if (j < 1)
1704 {
1705 out << " ";
1706 }
1707 }
1708 out << ") ";
1709 }
1710 out << "\nfield sizes: ";
1711 for (auto &i : shmem_info.field_sizes)
1712 {
1713 out << i << " ";
1714 }
1715 out << "\ndirection size: ";
1716 out << shmem_info.direction_size << " ";
1717 out << "\ninput sizes: ";
1718 for (auto &i : shmem_info.input_sizes)
1719 {
1720 out << i << " ";
1721 }
1722 out << "\nshadow sizes: ";
1723 for (auto &i : shmem_info.shadow_sizes)
1724 {
1725 out << i << " ";
1726 }
1727 out << "\ntemp sizes: ";
1728 for (auto &i : shmem_info.temp_sizes)
1729 {
1730 out << i << " ";
1731 }
1732 out << "\noffsets: ";
1733 for (auto &i : shmem_info.offsets)
1734 {
1735 out << i << " ";
1736 }
1737 out << "\n\n";
1738}
1739
1740template <std::size_t N>
1741MFEM_HOST_DEVICE inline
1742std::array<DofToQuadMap, N> load_dtq_mem(
1743 void *mem,
1744 int offset,
1745 const std::array<std::array<int, 2>, N> &sizes,
1746 const std::array<DofToQuadMap, N> &dtq)
1747{
1748 std::array<DofToQuadMap, N> f;
1749 for (std::size_t i = 0; i < N; i++)
1750 {
1751 if (dtq[i].which_input != -1)
1752 {
1753 const auto [nqp_b, dim_b, ndof_b] = dtq[i].B.GetShape();
1754 const auto B = Reshape(&dtq[i].B[0], nqp_b, dim_b, ndof_b);
1755 auto mem_Bi = Reshape(reinterpret_cast<real_t *>(mem) + offset, nqp_b, dim_b,
1756 ndof_b);
1757
1758 MFEM_FOREACH_THREAD(q, x, nqp_b)
1759 {
1760 MFEM_FOREACH_THREAD(d, y, ndof_b)
1761 {
1762 for (int b = 0; b < dim_b; b++)
1763 {
1764 auto v = B(q, b, d);
1765 mem_Bi(q, b, d) = v;
1766 }
1767 }
1768 }
1769
1770 offset += sizes[i][0];
1771
1772 const auto [nqp_g, dim_g, ndof_g] = dtq[i].G.GetShape();
1773 const auto G = Reshape(&dtq[i].G[0], nqp_g, dim_g, ndof_g);
1774 auto mem_Gi = Reshape(reinterpret_cast<real_t *>(mem) + offset, nqp_g, dim_g,
1775 ndof_g);
1776
1777 MFEM_FOREACH_THREAD(q, x, nqp_g)
1778 {
1779 MFEM_FOREACH_THREAD(d, y, ndof_g)
1780 {
1781 for (int b = 0; b < dim_g; b++)
1782 {
1783 mem_Gi(q, b, d) = G(q, b, d);
1784 }
1785 }
1786 }
1787
1788 offset += sizes[i][1];
1789
1790 f[i] = DofToQuadMap{DeviceTensor<3, const real_t>(&mem_Bi[0], nqp_b, dim_b, ndof_b),
1791 DeviceTensor<3, const real_t>(&mem_Gi[0], nqp_g, dim_g, ndof_g),
1792 dtq[i].which_input};
1793 }
1794 else
1795 {
1796 // When which_input is -1, just copy the original DofToQuadMap with empty data.
1797 f[i] = dtq[i];
1798 }
1799 }
1800 return f;
1801}
1802
1803template <std::size_t num_fields>
1804MFEM_HOST_DEVICE inline
1805std::array<DeviceTensor<1>, num_fields>
1807 void *mem,
1808 int offset,
1809 const std::array<int, num_fields> &sizes,
1810 const std::array<DeviceTensor<2>, num_fields> &fields_e,
1811 const int &entity_idx)
1812{
1813 std::array<DeviceTensor<1>, num_fields> f;
1814
1815 for_constexpr<num_fields>([&](auto field_idx)
1816 {
1817 int block_size = MFEM_THREAD_SIZE(x) *
1818 MFEM_THREAD_SIZE(y) *
1819 MFEM_THREAD_SIZE(z);
1820 int tid = MFEM_THREAD_ID(x) +
1821 MFEM_THREAD_SIZE(x) *
1822 (MFEM_THREAD_ID(y) + MFEM_THREAD_SIZE(y) * MFEM_THREAD_ID(z));
1823 for (int k = tid; k < sizes[field_idx]; k += block_size)
1824 {
1825 reinterpret_cast<real_t *>(mem)[offset + k] =
1826 fields_e[field_idx](k, entity_idx);
1827 }
1828
1829 f[field_idx] =
1830 DeviceTensor<1>(&reinterpret_cast<real_t *> (mem)[offset], sizes[field_idx]);
1831
1832 offset += sizes[field_idx];
1833 });
1834
1835 return f;
1836}
1837
1838MFEM_HOST_DEVICE inline
1840 void *mem,
1841 int offset,
1842 const int &size,
1844 const int &entity_idx)
1845{
1846 int block_size = MFEM_THREAD_SIZE(x) *
1847 MFEM_THREAD_SIZE(y) *
1848 MFEM_THREAD_SIZE(z);
1849 int tid = MFEM_THREAD_ID(x) +
1850 MFEM_THREAD_SIZE(x) *
1851 (MFEM_THREAD_ID(y) + MFEM_THREAD_SIZE(y) * MFEM_THREAD_ID(z));
1852 for (int k = tid; k < size; k += block_size)
1853 {
1854 reinterpret_cast<real_t *>(mem)[offset + k] = direction(k, entity_idx);
1855 }
1856 MFEM_SYNC_THREAD;
1857
1858 return DeviceTensor<1>(
1859 &reinterpret_cast<real_t *>(mem)[offset], size);
1860}
1861
1862template <std::size_t N>
1863MFEM_HOST_DEVICE inline
1864std::array<DeviceTensor<2>, N> load_input_mem(
1865 void *mem,
1866 int offset,
1867 const std::array<int, N> &sizes,
1868 const int &num_qp)
1869{
1870 std::array<DeviceTensor<2>, N> f;
1871 for (std::size_t i = 0; i < N; i++)
1872 {
1873 f[i] = DeviceTensor<2>(&reinterpret_cast<real_t *>(mem)[offset],
1874 sizes[i] / num_qp,
1875 num_qp);
1876 offset += sizes[i];
1877 }
1878 return f;
1879}
1880
1881MFEM_HOST_DEVICE inline
1883 void *mem,
1884 int offset,
1885 const int &residual_size,
1886 const int &num_qp)
1887{
1888 return DeviceTensor<2>(reinterpret_cast<real_t *>(mem) + offset, residual_size,
1889 num_qp);
1890}
1891
1892template <std::size_t N>
1893MFEM_HOST_DEVICE inline
1894std::array<DeviceTensor<1>, 6> load_scratch_mem(
1895 void *mem,
1896 int offset,
1897 const std::array<int, N> &sizes)
1898{
1899 std::array<DeviceTensor<1>, N> f;
1900 for (std::size_t i = 0; i < N; i++)
1901 {
1902 f[i] = DeviceTensor<1>(&reinterpret_cast<real_t *>(mem)[offset], sizes[i]);
1903 offset += sizes[i];
1904 }
1905 return f;
1906}
1907
1908template <typename shared_mem_info_t, std::size_t num_inputs, std::size_t num_outputs, std::size_t num_fields>
1909MFEM_HOST_DEVICE inline
1911 void *shmem,
1912 const shared_mem_info_t &shmem_info,
1913 const std::array<DofToQuadMap, num_inputs> &input_dtq_maps,
1914 const std::array<DofToQuadMap, num_outputs> &output_dtq_maps,
1915 const std::array<DeviceTensor<2>, num_fields> &wrapped_fields_e,
1916 const int &num_qp,
1917 const int &e)
1918{
1919 auto input_dtq_shmem =
1921 shmem,
1922 shmem_info.offsets[SharedMemory::Index::INPUT_DTQ],
1923 shmem_info.input_dtq_sizes,
1924 input_dtq_maps);
1925
1926 auto output_dtq_shmem =
1928 shmem,
1929 shmem_info.offsets[SharedMemory::Index::OUTPUT_DTQ],
1930 shmem_info.output_dtq_sizes,
1931 output_dtq_maps);
1932
1933 auto fields_shmem =
1935 shmem,
1936 shmem_info.offsets[SharedMemory::Index::FIELD],
1937 shmem_info.field_sizes,
1938 wrapped_fields_e,
1939 e);
1940
1941 // These functions don't copy, they simply create a `DeviceTensor` object
1942 // that points to correct chunks of the shared memory pool.
1943 auto input_shmem =
1945 shmem,
1946 shmem_info.offsets[SharedMemory::Index::INPUT],
1947 shmem_info.input_sizes,
1948 num_qp);
1949
1950 auto residual_shmem =
1952 shmem,
1953 shmem_info.offsets[SharedMemory::Index::OUTPUT],
1954 shmem_info.residual_size,
1955 num_qp);
1956
1957 auto scratch_mem =
1959 shmem,
1960 shmem_info.offsets[SharedMemory::Index::TEMP],
1961 shmem_info.temp_sizes);
1962
1963 MFEM_SYNC_THREAD;
1964
1965 // nvcc needs make_tuple to be fully qualified
1967 input_dtq_shmem, output_dtq_shmem, fields_shmem,
1968 input_shmem, residual_shmem, scratch_mem);
1969}
1970
1971template <typename shared_mem_info_t, std::size_t num_inputs, std::size_t num_outputs, std::size_t num_fields>
1972MFEM_HOST_DEVICE inline
1974 void *shmem,
1975 const shared_mem_info_t &shmem_info,
1976 const std::array<DofToQuadMap, num_inputs> &input_dtq_maps,
1977 const std::array<DofToQuadMap, num_outputs> &output_dtq_maps,
1978 const std::array<DeviceTensor<2>, num_fields> &wrapped_fields_e,
1979 const DeviceTensor<2> &wrapped_direction_e,
1980 const int &num_qp,
1981 const int &e)
1982{
1983 auto input_dtq_shmem =
1985 shmem,
1986 shmem_info.offsets[SharedMemory::Index::INPUT_DTQ],
1987 shmem_info.input_dtq_sizes,
1988 input_dtq_maps);
1989
1990 auto output_dtq_shmem =
1992 shmem,
1993 shmem_info.offsets[SharedMemory::Index::OUTPUT_DTQ],
1994 shmem_info.output_dtq_sizes,
1995 output_dtq_maps);
1996
1997 auto fields_shmem =
1999 shmem,
2000 shmem_info.offsets[SharedMemory::Index::FIELD],
2001 shmem_info.field_sizes,
2002 wrapped_fields_e,
2003 e);
2004
2005 auto direction_shmem =
2007 shmem,
2008 shmem_info.offsets[SharedMemory::Index::DIRECTION],
2009 shmem_info.direction_size,
2010 wrapped_direction_e,
2011 e);
2012
2013 // These methods don't copy, they simply create a `DeviceTensor` object
2014 // that points to correct chunks of the shared memory pool.
2015 auto input_shmem =
2017 shmem,
2018 shmem_info.offsets[SharedMemory::Index::INPUT],
2019 shmem_info.input_sizes,
2020 num_qp);
2021
2022 auto shadow_shmem =
2024 shmem,
2025 shmem_info.offsets[SharedMemory::Index::SHADOW],
2026 shmem_info.input_sizes,
2027 num_qp);
2028
2029 auto residual_shmem =
2031 shmem,
2032 shmem_info.offsets[SharedMemory::Index::OUTPUT],
2033 shmem_info.residual_size,
2034 num_qp);
2035
2036 auto scratch_mem =
2038 shmem,
2039 shmem_info.offsets[SharedMemory::Index::TEMP],
2040 shmem_info.temp_sizes);
2041
2042 MFEM_SYNC_THREAD;
2043
2044 // nvcc needs make_tuple to be fully qualified
2046 input_dtq_shmem, output_dtq_shmem, fields_shmem,
2047 direction_shmem, input_shmem, shadow_shmem,
2048 residual_shmem, scratch_mem);
2049}
2050
2051template <std::size_t... i>
2052MFEM_HOST_DEVICE inline
2053std::array<DeviceTensor<2>, sizeof...(i)> get_local_input_qp(
2054 const std::array<DeviceTensor<3>, sizeof...(i)> &input_qp_global, int e,
2055 std::index_sequence<i...>)
2056{
2057 return
2058 {
2060 &input_qp_global[i](0, 0, e),
2061 input_qp_global[i].GetShape()[0],
2062 input_qp_global[i].GetShape()[1]) ...
2063 };
2064}
2065
2066template <std::size_t N>
2067MFEM_HOST_DEVICE inline
2068void set_zero(std::array<DeviceTensor<2>, N> &v)
2069{
2070 for (std::size_t i = 0; i < N; i++)
2071 {
2072 int size = v[i].GetShape()[0] * v[i].GetShape()[1];
2073 auto vi = Reshape(&v[i][0], size);
2074 for (int j = 0; j < size; j++)
2075 {
2076 vi[j] = 0.0;
2077 }
2078 }
2079}
2080
2081template <std::size_t n>
2082MFEM_HOST_DEVICE inline
2084{
2085 int s = 1;
2086 for (int i = 0; i < n; i++)
2087 {
2088 s *= u.GetShape()[i];
2089 }
2090 auto ui = Reshape(&u[0], s);
2091 for (int j = 0; j < s; j++)
2092 {
2093 ui[j] = 0.0;
2094 }
2095}
2096
2097/// @brief Copy data from DeviceTensor u to DeviceTensor v
2098///
2099/// @param u source DeviceTensor
2100/// @param v destination DeviceTensor
2101/// @tparam n DeviceTensor rank
2102template <int n>
2103MFEM_HOST_DEVICE inline
2105{
2106 int s = 1;
2107 for (int i = 0; i < n; i++)
2108 {
2109 s *= u.GetShape()[i];
2110 }
2111 auto ui = Reshape(&u[0], s);
2112 auto vi = Reshape(&v[0], s);
2113 for (int j = 0; j < s; j++)
2114 {
2115 vi[j] = ui[j];
2116 }
2117}
2118
2119/// @brief Copy data from array of DeviceTensor u to array of DeviceTensor v
2120///
2121/// @param u source DeviceTensor array
2122/// @param v destination DeviceTensor array
2123/// @tparam n DeviceTensor rank
2124/// @tparam m number of DeviceTensors
2125template <int n, std::size_t m>
2126MFEM_HOST_DEVICE inline
2127void copy(std::array<DeviceTensor<n>, m> &u,
2128 std::array<DeviceTensor<n>, m> &v)
2129{
2130 for (int i = 0; i < m; i++)
2131 {
2132 copy(u[i], v[i]);
2133 }
2134}
2135
2136/// @brief Wraps plain data in DeviceTensors for fields
2137///
2138/// @param fields array of field data
2139/// @param field_sizes for each field, number of values stored for each entity
2140/// @param num_entities number of entities (elements, faces, etc) in mesh
2141/// @tparam num_fields number of fields
2142/// @return array of field data wrapped in DeviceTensors
2143template <std::size_t num_fields>
2144std::array<DeviceTensor<2>, num_fields> wrap_fields(
2145 std::vector<Vector> &fields,
2146 std::array<int, num_fields> &field_sizes,
2147 const int &num_entities)
2148{
2149 std::array<DeviceTensor<2>, num_fields> f;
2150
2151 for_constexpr<num_fields>([&](auto i)
2152 {
2153 f[i] = DeviceTensor<2>(fields[i].ReadWrite(), field_sizes[i], num_entities);
2154 });
2155
2156 return f;
2157}
2158
2159/// @brief Accumulates the sizes of field operators on quadrature points for
2160/// dependent inputs
2161///
2162/// @tparam input_t Type of input field operators tuple
2163/// @tparam num_fields Number of fields
2164/// @tparam i Parameter pack indices for field operators
2165///
2166/// @param inputs Tuple of input field operators
2167/// @param kinput_is_dependent Array indicating which inputs are dependent
2168/// @param input_to_field Array mapping input indices to field indices
2169/// @param fields Array of field descriptors
2170/// @param seq Index sequence for inputs
2171///
2172/// @return Sum of sizes on quadrature points for all dependent inputs
2173///
2174/// @details
2175/// This function accumulates the sizes needed on quadrature points for all
2176/// dependent input field operators. For each dependent input, it calculates the
2177/// size required on quadrature points using GetSizeOnQP() and adds it to the
2178/// total. Non-dependent inputs contribute zero to the total size.
2179template <typename input_t, std::size_t num_fields, std::size_t... i>
2181 const input_t &inputs,
2182 std::array<bool, sizeof...(i)> &kinput_is_dependent,
2183 const std::array<int, sizeof...(i)> &input_to_field,
2184 const std::array<FieldDescriptor, num_fields> &fields,
2185 std::index_sequence<i...> seq)
2186{
2187 MFEM_CONTRACT_VAR(seq); // 'seq' is needed for doxygen
2188 return (... + [](auto &input, auto is_dependent, auto field)
2189 {
2190 if (!is_dependent)
2191 {
2192 return 0;
2193 }
2194 return GetSizeOnQP(input, field);
2195 }
2196 (get<i>(inputs),
2197 get<i>(kinput_is_dependent),
2198 fields[input_to_field[i]]));
2199}
2200
2201template <
2202 typename entity_t,
2203 typename field_operator_ts,
2204 std::size_t N = tuple_size<field_operator_ts>::value,
2205 std::size_t... Is>
2206std::array<DofToQuadMap, N> create_dtq_maps_impl(
2207 field_operator_ts &fops,
2208 std::vector<const DofToQuad*> &dtqs,
2209 const std::array<size_t, N> &field_map,
2210 std::index_sequence<Is...>)
2211{
2212 auto f = [&](auto fop, std::size_t idx)
2213 {
2214 [[maybe_unused]] auto g = [&](int idx)
2215 {
2216 auto dtq = dtqs[field_map[idx]];
2217
2218 int value_dim = 1;
2219 int grad_dim = 1;
2220
2221 if ((dtq->mode != DofToQuad::Mode::TENSOR) &&
2222 (!is_identity_fop<decltype(fop)>::value))
2223 {
2224 value_dim = dtq->FE->GetRangeDim() ? dtq->FE->GetRangeDim() : 1;
2225 grad_dim = dtq->FE->GetDim();
2226 }
2227
2228 return std::tuple{dtq, value_dim, grad_dim};
2229 };
2230
2231 if constexpr (is_value_fop<decltype(fop)>::value ||
2232 is_gradient_fop<decltype(fop)>::value)
2233 {
2234 auto [dtq, value_dim, grad_dim] = g(idx);
2235 return DofToQuadMap
2236 {
2237 DeviceTensor<3, const real_t>(dtq->B.Read(), dtq->nqpt, value_dim, dtq->ndof),
2238 DeviceTensor<3, const real_t>(dtq->G.Read(), dtq->nqpt, grad_dim, dtq->ndof),
2239 static_cast<int>(idx)
2240 };
2241 }
2242 else if constexpr (std::is_same_v<decltype(fop), Weight>)
2243 {
2244 return DofToQuadMap
2245 {
2246 DeviceTensor<3, const real_t>(nullptr, 1, 1, 1),
2247 DeviceTensor<3, const real_t>(nullptr, 1, 1, 1),
2248 -1
2249 };
2250 }
2251 else if constexpr (is_identity_fop<decltype(fop)>::value ||
2252 is_sum_fop<decltype(fop)>::value)
2253 {
2254 auto [dtq, value_dim, grad_dim] = g(idx);
2255 return DofToQuadMap
2256 {
2257 DeviceTensor<3, const real_t>(nullptr, dtq->nqpt, value_dim, dtq->ndof),
2258 DeviceTensor<3, const real_t>(nullptr, dtq->nqpt, grad_dim, dtq->ndof),
2259 -1
2260 };
2261 }
2262 else
2263 {
2264 static_assert(dfem::always_false<decltype(fop)>,
2265 "field operator type is not implemented");
2266 }
2267 return DofToQuadMap
2268 {
2269 DeviceTensor<3, const real_t>(nullptr, 0, 0, 0),
2270 DeviceTensor<3, const real_t>(nullptr, 0, 0, 0),
2271 -1
2272 }; // Unreachable, but avoids compiler warning
2273 };
2274 return std::array<DofToQuadMap, N>
2275 {
2276 f(get<Is>(fops), Is)...
2277 };
2278}
2279
2280/// @brief Create DofToQuad maps for a given set of field operators.
2281///
2282/// @param fops field operators
2283/// @param dtqmaps DofToQuad maps
2284/// @param to_field_map mapping from input indices to field indices
2285/// @tparam entity_t type of the entity
2286/// @return array of DofToQuad maps
2287template <
2288 typename entity_t,
2289 typename field_operator_ts,
2290 std::size_t num_fields>
2291std::array<DofToQuadMap, num_fields> create_dtq_maps(
2292 field_operator_ts &fops,
2293 std::vector<const DofToQuad*> &dtqmaps,
2294 const std::array<size_t, num_fields> &to_field_map)
2295{
2297 fops, dtqmaps,
2298 to_field_map,
2299 std::make_index_sequence<num_fields> {});
2300}
2301
2302} // namespace mfem::future
2303#endif
int Size() const
Return the logical size of the array.
Definition array.hpp:192
Data type dense matrix using column-major storage.
Definition densemat.hpp:24
A basic generic Tensor class, appropriate for use on the GPU.
Definition dtensor.hpp:84
MFEM_HOST_DEVICE auto & GetShape() const
Returns the shape of the tensor.
Definition dtensor.hpp:131
static bool Allows(unsigned long b_mask)
Return true if any of the backends in the backend mask, b_mask, are allowed.
Definition device.hpp:271
static MemoryClass GetDeviceMemoryClass()
Get the current Device MemoryClass. This is the MemoryClass used by most MFEM device kernels to acces...
Definition device.hpp:306
Structure representing the matrices/tensors needed to evaluate (in reference space) the values,...
Definition fe_base.hpp:141
Mode
Type of data stored in the arrays B, Bt, G, and Gt.
Definition fe_base.hpp:154
@ TENSOR
Tensor product representation using 1D matrices/tensors with dimensions using 1D number of quadrature...
Definition fe_base.hpp:165
Abstract data type element.
Definition element.hpp:29
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:210
Class for an integration rule - an Array of IntegrationPoint.
Definition intrules.hpp:96
Mesh data type.
Definition mesh.hpp:67
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1390
int GetNBE() const
Returns number of boundary elements.
Definition mesh.hpp:1393
static int WorldRank()
Return the MPI rank in MPI_COMM_WORLD.
static int WorldSize()
Return the size of MPI_COMM_WORLD.
Abstract operator.
Definition operator.hpp:27
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition operator.hpp:68
virtual void Mult(const Vector &x, Vector &y) const =0
Operator application: y=A(x).
int NumCols() const
Get the number of columns (size of input) of the Operator. Synonym with Width().
Definition operator.hpp:77
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
Definition operator.hpp:74
int NumRows() const
Get the number of rows (size of output) of the Operator. Synonym with Height().
Definition operator.hpp:71
virtual void AddMultTranspose(const Vector &x, Vector &y, const real_t a=1.0) const
Operator transpose application: y+=A^t(x) (default) or y+=a*A^t(x).
Definition operator.cpp:58
virtual void MultTranspose(const Vector &x, Vector &y) const
Action of the transpose operator: y=A^t(x). The default behavior in class Operator is to generate an ...
Definition operator.hpp:102
Abstract parallel finite element space.
Definition pfespace.hpp:31
Vector data type.
Definition vector.hpp:82
real_t Norml2() const
Returns the l2 norm of the vector.
Definition vector.cpp:968
int Size() const
Returns the size of the vector.
Definition vector.hpp:234
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
Definition vector.hpp:145
real_t Sum() const
Return the sum of the vector entries.
Definition vector.cpp:1246
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:633
virtual MemoryClass GetMemoryClass() const override
Return the MemoryClass preferred by the Operator.
Definition util.hpp:678
void Mult(const Vector &v, Vector &y) const override
Operator application: y=A(x).
Definition doperator.cpp:55
FDJacobian(const Operator &op, const Vector &x, real_t fixed_eps=0.0)
Definition util.hpp:657
Base class for parametric spaces.
Weight FieldOperator.
real_t b
Definition lissajous.cpp:42
real_t a
Definition lissajous.cpp:41
string direction
constexpr bool always_false
Definition util.hpp:575
constexpr auto decay_types(tuple< Ts... > const &) -> tuple< std::remove_cv_t< std::remove_reference_t< Ts > >... >
constexpr auto filter_fields(const std::tuple< Ts... > &t)
Filter fields from a tuple based on their field IDs.
Definition util.hpp:541
const Operator * get_element_restriction(const FieldDescriptor &f, ElementDofOrdering o)
Get the element restriction operator for a field descriptor.
Definition util.hpp:916
MFEM_HOST_DEVICE constexpr auto type(const tuple< T... > &t)
a function intended to be used for extracting the ith type from a tuple.
Definition tuple.hpp:376
const Operator * get_face_restriction(const FieldDescriptor &f, ElementDofOrdering o, FaceType ft, L2FaceValues m)
Get the face restriction operator for a field descriptor.
Definition util.hpp:949
void prolongation(const FieldDescriptor field, const Vector &x, Vector &field_l)
Apply the prolongation operator to a field.
Definition util.hpp:1044
constexpr bool contains(const int *arr, std::size_t size, int value)
Helper function to check if an element is in the array.
Definition util.hpp:478
MFEM_HOST_DEVICE auto unpack_shmem(void *shmem, const shared_mem_info_t &shmem_info, const std::array< DofToQuadMap, num_inputs > &input_dtq_maps, const std::array< DofToQuadMap, num_outputs > &output_dtq_maps, const std::array< DeviceTensor< 2 >, num_fields > &wrapped_fields_e, const int &num_qp, const int &e)
Definition util.hpp:1910
void restriction(const FieldDescriptor u, const Vector &u_l, Vector &field_e, ElementDofOrdering ordering)
Apply the restriction operator to a field.
Definition util.hpp:1175
void GetElementVDofs(const FieldDescriptor &f, int el, Array< int > &vdofs)
Get the element vdofs of a field descriptor.
Definition util.hpp:752
void print_mpi_root(const std::string &msg)
Definition util.hpp:330
const Operator * get_prolongation(const FieldDescriptor &f)
Get the prolongation operator for a field descriptor.
Definition util.hpp:887
void get_lvectors(const std::vector< FieldDescriptor > fields, const Vector &x, std::vector< Vector > &fields_l)
Definition util.hpp:1108
std::array< Vector, sizeof...(i)> create_input_qp_memory(int num_qp, int num_entities, input_t &inputs, std::index_sequence< i... >)
Create input memory for a given set of inputs.
Definition util.hpp:1461
MFEM_HOST_DEVICE DeviceTensor< 1 > load_direction_mem(void *mem, int offset, const int &size, const DeviceTensor< 2 > &direction, const int &entity_idx)
Definition util.hpp:1839
MFEM_HOST_DEVICE std::array< DeviceTensor< 2 >, N > load_input_mem(void *mem, int offset, const std::array< int, N > &sizes, const int &num_qp)
Definition util.hpp:1864
decltype(decay_types(std::declval< T >())) decay_tuple
Definition util.hpp:420
int GetNumEntities(const mfem::Mesh &mesh)
Get the number of entities of a given type.
Definition util.hpp:1239
constexpr auto get_type_name() -> std::string_view
Definition util.hpp:161
void pretty_print(std::ostream &out, const mfem::DenseMatrix &A)
Pretty print an mfem::DenseMatrix to out.
Definition util.hpp:214
void print_tuple(const std::tuple< Args... > &t)
Definition util.hpp:196
std::array< bool, sizeof...(Is)> make_dependency_array(const Tuple &inputs, std::index_sequence< Is... >)
Definition util.hpp:113
MFEM_HOST_DEVICE void copy(DeviceTensor< n > &u, DeviceTensor< n > &v)
Copy data from DeviceTensor u to DeviceTensor v.
Definition util.hpp:2104
constexpr auto extract_field_ids(const std::tuple< Ts... > &t)
Extracts field IDs from a tuple of objects derived from FieldOperator.
Definition util.hpp:467
void print_mpi_sync(const std::string &msg)
print with MPI rank synchronization
Definition util.hpp:344
std::array< DeviceTensor< 3 >, sizeof...(i)> wrap_input_memory(std::array< Vector, sizeof...(i)> &input_qp_mem, int num_qp, int num_entities, const input_t &inputs, std::index_sequence< i... >)
Wrap input memory for a given set of inputs.
Definition util.hpp:1452
std::tuple< std::function< void(const Vector &, Vector &)>, int > get_restriction_transpose(const FieldDescriptor &f, const ElementDofOrdering &o, const fop_t &fop)
Get a transpose restriction callback for a field descriptor.
Definition util.hpp:1009
std::array< DeviceTensor< 2 >, num_fields > wrap_fields(std::vector< Vector > &fields, std::array< int, num_fields > &field_sizes, const int &num_entities)
Wraps plain data in DeviceTensors for fields.
Definition util.hpp:2144
std::vector< int > get_input_size_on_qp(const input_t &inputs, std::index_sequence< i... >)
Get the size on quadrature point for a given set of inputs.
Definition util.hpp:1503
MFEM_HOST_DEVICE std::array< DofToQuadMap, N > load_dtq_mem(void *mem, int offset, const std::array< std::array< int, 2 >, N > &sizes, const std::array< DofToQuadMap, N > &dtq)
Definition util.hpp:1742
SharedMemoryInfo< num_fields, num_inputs, num_outputs > get_shmem_info(const std::array< DofToQuadMap, num_inputs > &input_dtq_maps, const std::array< DofToQuadMap, num_outputs > &output_dtq_maps, const std::vector< FieldDescriptor > &fields, const int &num_entities, const input_t &inputs, const int &num_qp, const std::vector< int > &input_size_on_qp, const int &residual_size_on_qp, const ElementDofOrdering &dof_ordering, const int &derivative_action_field_idx=-1)
Definition util.hpp:1542
const Operator * get_restriction(const FieldDescriptor &f, const ElementDofOrdering &o)
Get the restriction operator for a field descriptor.
Definition util.hpp:984
std::array< size_t, tuple_size< field_operator_ts >::value > create_descriptors_to_fields_map(const std::vector< FieldDescriptor > &fields, field_operator_ts &fops)
Create a map from field operator types to FieldDescriptor indices.
Definition util.hpp:1394
void element_restriction(const std::array< FieldDescriptor, N > u, const std::array< Vector, N > &u_l, std::array< Vector, M > &fields_e, ElementDofOrdering ordering, const int offset=0)
Definition util.hpp:1216
MFEM_HOST_DEVICE constexpr tuple< T... > make_tuple(const T &... args)
helper function for combining a list of values into a tuple
Definition tuple.hpp:212
constexpr std::size_t count_unique_field_ids(const std::tuple< Ts... > &t)
Function to count unique field IDs in a tuple.
Definition util.hpp:495
constexpr void for_constexpr_with_arg(lambda &&f, arg_t &&arg, std::integer_sequence< std::size_t >)
Definition util.hpp:87
std::array< DofToQuadMap, num_fields > create_dtq_maps(field_operator_ts &fops, std::vector< const DofToQuad * > &dtqmaps, const std::array< size_t, num_fields > &to_field_map)
Create DofToQuad maps for a given set of field operators.
Definition util.hpp:2291
auto make_dependency_map_impl(tuple< input_ts... > inputs, std::index_sequence< Is... >)
Definition util.hpp:119
constexpr auto to_array(const std::tuple< Ts... > &tuple)
Definition util.hpp:42
int accumulate_sizes_on_qp(const input_t &inputs, std::array< bool, sizeof...(i)> &kinput_is_dependent, const std::array< int, sizeof...(i)> &input_to_field, const std::array< FieldDescriptor, num_fields > &fields, std::index_sequence< i... > seq)
Accumulates the sizes of field operators on quadrature points for dependent inputs.
Definition util.hpp:2180
const DofToQuad * GetDofToQuad(const FieldDescriptor &f, const IntegrationRule &ir, DofToQuad::Mode mode)
Get the GetDofToQuad object for a given entity type.
Definition util.hpp:1267
std::array< DofToQuadMap, N > create_dtq_maps_impl(field_operator_ts &fops, std::vector< const DofToQuad * > &dtqs, const std::array< size_t, N > &field_map, std::index_sequence< Is... >)
Definition util.hpp:2206
constexpr void for_constexpr(lambda &&f, std::integer_sequence< std::size_t, i ... >)
Definition util.hpp:71
MFEM_HOST_DEVICE std::array< DeviceTensor< 2 >, sizeof...(i)> get_local_input_qp(const std::array< DeviceTensor< 3 >, sizeof...(i)> &input_qp_global, int e, std::index_sequence< i... >)
Definition util.hpp:2053
void CheckCompatibility(const FieldDescriptor &f)
Check the compatibility of a field operator type with a FieldDescriptor.
Definition util.hpp:1307
std::function< void(const Vector &, Vector &)> get_prolongation_transpose(const FieldDescriptor &f, const fop_t &fop, MPI_Comm mpi_comm)
Get a transpose prolongation callback for a field descriptor.
Definition util.hpp:1136
std::size_t FindIdx(const std::size_t &id, const std::vector< FieldDescriptor > &fields)
Find the index of a field descriptor in a vector of field descriptors.
Definition util.hpp:699
MFEM_HOST_DEVICE std::array< DeviceTensor< 1 >, num_fields > load_field_mem(void *mem, int offset, const std::array< int, num_fields > &sizes, const std::array< DeviceTensor< 2 >, num_fields > &fields_e, const int &entity_idx)
Definition util.hpp:1806
void forall(func_t f, const int &N, const ThreadBlocks &blocks, int num_shmem=0, real_t *shmem=nullptr)
Definition util.hpp:614
int GetVDim(const FieldDescriptor &f)
Get the vdim of a field descriptor.
Definition util.hpp:821
int GetSizeOnQP(const field_operator_t &, const FieldDescriptor &f)
Get the size on quadrature point for a field operator type and FieldDescriptor combination.
Definition util.hpp:1359
MFEM_HOST_DEVICE DeviceTensor< 2 > load_residual_mem(void *mem, int offset, const int &residual_size, const int &num_qp)
Definition util.hpp:1882
auto make_dependency_map(tuple< input_ts... > inputs)
Definition util.hpp:147
constexpr int GetFieldId()
Definition util.hpp:448
int GetVSize(const FieldDescriptor &f)
Get the vdof size of a field descriptor.
Definition util.hpp:717
auto get_marked_entries(const std::array< T, N > &a, const std::array< bool, N > &marker)
Get marked entries from an std::array based on a marker array.
Definition util.hpp:521
void pretty_print_mpi(const mfem::Vector &v)
Pretty print an mfem::Vector with MPI rank.
Definition util.hpp:400
MFEM_HOST_DEVICE std::array< DeviceTensor< 1 >, 6 > load_scratch_mem(void *mem, int offset, const std::array< int, N > &sizes)
Definition util.hpp:1894
int GetDimension(const FieldDescriptor &f)
Get the spatial dimension of a field descriptor.
Definition util.hpp:852
MFEM_HOST_DEVICE void set_zero(std::array< DeviceTensor< 2 >, N > &v)
Definition util.hpp:2068
int GetTrueVSize(const FieldDescriptor &f)
Get the true dof size of a field descriptor.
Definition util.hpp:786
constexpr auto extract_field_ids_impl(Tuple &&t, std::index_sequence< Is... >)
Definition util.hpp:454
void print_tuple_impl(const Tuple &t, std::index_sequence< Is... >)
Definition util.hpp:187
void print_shared_memory_info(shmem_info_t &shmem_info)
Definition util.hpp:1677
MFEM_HOST_DEVICE zero & get(zero &x)
let zero be accessed like a tuple
Definition tensor.hpp:281
__global__ void forall_kernel_shmem(func_t f, int n)
Definition util.hpp:602
real_t u(const Vector &xvec)
Definition lor_mms.hpp:22
T * Write(Memory< T > &mem, int size, bool on_dev=true)
Get a pointer for write access to mem with the mfem::Device's DeviceMemoryClass, if on_dev = true,...
Definition device.hpp:386
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition globals.hpp:66
MemoryClass
Memory classes identify sets of memory types.
T * ReadWrite(Memory< T > &mem, int size, bool on_dev=true)
Get a pointer for read+write access to mem with the mfem::Device's DeviceMemoryClass,...
Definition device.hpp:403
MFEM_HOST_DEVICE DeviceTensor< sizeof...(Dims), T > Reshape(T *ptr, Dims... dims)
Wrap a pointer as a DeviceTensor with automatically deduced template parameters.
Definition dtensor.hpp:138
float real_t
Definition config.hpp:46
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
Definition fespace.hpp:49
std::function< real_t(const Vector &)> f(real_t mass_coeff)
Definition lor_mms.hpp:30
FaceType
Definition mesh.hpp:49
@ HIP_MASK
Biwise-OR of all HIP backends.
Definition device.hpp:98
@ CPU_MASK
Biwise-OR of all CPU backends.
Definition device.hpp:94
@ CUDA_MASK
Biwise-OR of all CUDA backends.
Definition device.hpp:96
Helper struct to convert a C++ type to an MPI type.
DofToQuadMap struct.
Definition util.hpp:1475
DeviceTensor< 3, const real_t > G
Gradient of the basis functions evaluated at quadrature points.
Definition util.hpp:1492
Index
Enumeration for the indices of the mappings B and G.
Definition util.hpp:1478
int which_input
Reverse mapping indicating which input this map belongs to.
Definition util.hpp:1495
DeviceTensor< 3, const real_t > B
Basis functions evaluated at quadrature points.
Definition util.hpp:1487
FieldDescriptor struct.
Definition util.hpp:551
std::size_t id
Field ID.
Definition util.hpp:558
FieldDescriptor(std::size_t field_id, const T *v)
Constructor.
Definition util.hpp:569
data_variant_t data
Field variant.
Definition util.hpp:561
FieldDescriptor()
Default constructor.
Definition util.hpp:564
std::variant< const FiniteElementSpace *, const ParFiniteElementSpace *, const ParameterSpace * > data_variant_t
Definition util.hpp:552
std::array< int, num_fields > field_sizes
Definition util.hpp:1532
std::array< std::array< int, 2 >, num_inputs > input_dtq_sizes
Definition util.hpp:1530
std::array< std::array< int, 2 >, num_outputs > output_dtq_sizes
Definition util.hpp:1531
std::array< int, num_inputs > shadow_sizes
Definition util.hpp:1535
std::array< int, num_inputs > input_sizes
Definition util.hpp:1534
std::array< int, 6 > temp_sizes
Definition util.hpp:1537
std::array< int, 8 > offsets
Definition util.hpp:1529
ThreadBlocks struct.
Definition util.hpp:594
This is a class that mimics most of std::tuple's interface, except that it is usable in CUDA kernels ...
Definition tuple.hpp:150