17#include <unordered_map>
41template<
typename... Ts>
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);
51template <
typename lambda, std::size_t... i>
52constexpr void for_constexpr(lambda&&
f,
53 std::integral_constant<std::size_t, i>... Is)
59template <std::size_t... n,
typename lambda,
typename... arg_types>
60constexpr void for_constexpr(lambda&&
f,
61 std::integer_sequence<std::size_t, n...>,
64 (detail::for_constexpr(
f, args..., std::integral_constant<std::size_t,n> {}),
70template <
typename lambda, std::size_t... i>
72 std::integer_sequence<std::size_t, i ... >)
74 (
f(std::integral_constant<std::size_t, i> {}), ...);
77template <
typename lambda>
78constexpr void for_constexpr(lambda&&
f, std::integer_sequence<std::size_t>) {}
80template <
int... n,
typename lambda>
83 detail::for_constexpr(
f, std::make_integer_sequence<std::size_t, n> {}...);
86template <
typename lambda,
typename arg_t>
88 std::integer_sequence<std::size_t>)
93template <
typename lambda,
typename arg_t, std::size_t i, std::size_t... Is>
95 std::integer_sequence<std::size_t, i, Is...>)
97 f(std::integral_constant<std::size_t, i> {},
get<i>(arg));
99 std::integer_sequence<std::size_t, Is...> {});
102template <
typename lambda,
typename arg_t>
106 std::make_index_sequence<tuple_size<std::remove_reference_t<arg_t>>::value>;
111template <std::size_t I,
typename Tuple, std::size_t... Is>
112std::array<bool,
sizeof...(Is)>
115 return { (
get<I>(inputs).GetFieldId() ==
get<Is>(inputs).GetFieldId())... };
118template <
typename... input_ts, std::size_t... Is>
120 std::index_sequence<Is...>)
122 constexpr std::size_t N =
sizeof...(input_ts);
124 if constexpr (N == 0)
125 return std::unordered_map<
int, std::array<bool, 0>> {};
127 std::unordered_map<int, std::array<bool, N>> map;
129 (void)std::initializer_list<int>
132 map[
get<Is>(inputs).GetFieldId()] =
146template <
typename... input_ts>
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__};
176#error Unsupported compiler
179 const auto start = function.find(prefix) + prefix.size();
180 const auto end = function.find(suffix);
181 const auto size = end - start;
183 return function.substr(start, size);
186template <
typename Tuple, std::size_t... Is>
189 ((
out << (Is == 0 ?
"" :
", ") << std::get<Is>(t)), ...);
195template <
typename... Args>
218 for (
int i = 0; i < A.
NumRows(); ++i)
220 for (
int j = 0; j < A.
NumCols(); ++j)
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()));
229 for (
int i = 0; i < A.
NumRows(); ++i)
232 for (
int j = 0; j < A.
NumCols(); ++j)
234 out << std::setw(max_width) << std::scientific << std::setprecision(2) <<
237 if (j < A.NumCols() - 1)
265 for (
int i = 0; i < v.
Size(); i++)
268 if (i < v.Size() - 1)
288 for (
int i = 0; i < v.
Size(); i++)
291 if (i < v.Size() - 1)
306template<
typename K,
typename T, std::
size_t N>
310 std::size_t count = 0;
311 for (
const auto& [key, value] : map)
314 for (std::size_t i = 0; i < N; i++)
317 if (i < N-1) {
out <<
", "; }
320 if (count < map.size() - 1)
335 out << msg << std::endl;
352 out << msg << std::endl;
357 size_t msg_len = msg.length();
358 std::vector<size_t> lengths(nranks);
366 std::vector<std::string> messages(nranks);
370 for (
size_t r = 1; r < nranks; r++)
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]));
379 for (
size_t r = 0; r < nranks; r++)
381 out <<
"[Rank " << r <<
"] " << messages[r] << std::endl;
388 MPI_Send(
const_cast<char*
>(msg.c_str()),
static_cast<int>(msg_len), MPI_CHAR,
389 0, 0, MPI_COMM_WORLD);
393 MPI_Barrier(MPI_COMM_WORLD);
402 std::stringstream ss;
404 for (
int i = 0; i < v.
Size(); i++)
407 if (i < v.Size() - 1) { ss <<
", "; }
415template <
typename ... Ts>
424template <
typename output_t,
typename... input_ts>
434template <
typename output_t,
typename T,
typename... input_ts>
441template <
typename output_t,
typename... input_ts>
450 return T::GetFieldId();
453template <
typename Tuple, std::size_t... Is>
456 return std::array<int,
sizeof...(Is)>
458 std::decay_t<decltype(std::get<Is>(t))>{}.GetFieldId()...
466template <
typename... Ts>
478constexpr bool contains(
const int* arr, std::size_t size,
int value)
480 for (std::size_t i = 0; i < size; ++i)
494template <
typename... Ts>
498 constexpr std::size_t size =
sizeof...(Ts);
500 std::array<int, size> unique_ids = {};
501 std::size_t unique_count = 0;
503 for (std::size_t i = 0; i < size; ++i)
505 if (!
contains(unique_ids.data(), unique_count, ids[i]))
507 unique_ids[unique_count] = ids[i];
520template <
typename T, std::
size_t N>
522 const std::array<T, N> &
a,
523 const std::array<bool, N> &marker)
526 for (
int i = 0; i < N; i++)
540template <
typename... Ts>
543 return std::tuple_cat(
544 std::conditional_t<Ts::GetFieldId() != -1, std::tuple<Ts>, std::tuple<>> {}...);
568 template <
typename T>
584struct BoundaryElement;
600#if defined(MFEM_USE_CUDA_OR_HIP_LANG)
601template <
typename func_t>
605 extern __shared__
real_t shmem[];
613template <
typename func_t>
620 internal::RequireKernelCompilation();
622#if defined(MFEM_USE_CUDA_OR_HIP_LANG)
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());
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++)
649 MFEM_ABORT(
"no compute backend available");
700 const std::vector<FieldDescriptor>& fields)
702 for (std::size_t i = 0; i < fields.size(); i++)
704 if (fields[i].
id ==
id)
719 return std::visit([](
auto arg)
723 MFEM_ABORT(
"FieldDescriptor data is nullptr");
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 *>)
730 return arg->GetVSize();
732 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
734 return arg->GetVSize();
754 return std::visit([&](
auto arg)
758 MFEM_ABORT(
"FieldDescriptor data is nullptr");
761 using T = std::decay_t<
decltype(arg)>;
762 if constexpr (std::is_same_v<T, const FiniteElementSpace *>)
764 arg->GetElementVDofs(el, vdofs);
766 else if constexpr (std::is_same_v<T, const ParFiniteElementSpace *>)
768 arg->GetElementVDofs(el, vdofs);
770 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
772 MFEM_ABORT(
"internal error");
788 return std::visit([](
auto arg)
792 MFEM_ABORT(
"FieldDescriptor data is nullptr");
795 using T = std::decay_t<
decltype(arg)>;
796 if constexpr (std::is_same_v<T, const FiniteElementSpace *>)
798 return arg->GetTrueVSize();
800 else if constexpr (std::is_same_v<T, const ParFiniteElementSpace *>)
802 return arg->GetTrueVSize();
804 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
806 return arg->GetTrueVSize();
823 return std::visit([](
auto && arg)
825 using T = std::decay_t<
decltype(arg)>;
826 if constexpr (std::is_same_v<T, const FiniteElementSpace *>)
828 return arg->GetVDim();
830 else if constexpr (std::is_same_v<T, const ParFiniteElementSpace *>)
832 return arg->GetVDim();
834 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
836 return arg->GetVDim();
851template <
typename entity_t>
854 return std::visit([](
auto && arg)
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 *>)
860 if constexpr (std::is_same_v<entity_t, Entity::Element>)
862 return arg->GetMesh()->Dimension();
864 else if constexpr (std::is_same_v<entity_t, Entity::BoundaryElement>)
866 return arg->GetMesh()->Dimension() - 1;
869 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
871 return arg->Dimension();
889 return std::visit([](
auto&& arg) ->
const Operator*
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 *>)
895 return arg->GetProlongationMatrix();
897 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
899 return arg->GetProlongationMatrix();
919 return std::visit([&o](
auto&& arg) ->
const Operator*
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 *>)
925 return arg->GetElementRestriction(o);
927 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
929 return arg->GetElementRestriction(o);
934 "can't use get_element_restriction on type");
954 return std::visit([&o, &ft, &m](
auto&& arg) ->
const Operator*
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 *>)
960 return arg->GetFaceRestriction(o, ft, m);
962 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
965 MFEM_ABORT(
"internal error");
970 "can't use get_face_restriction on type");
982template <
typename entity_t>
987 if constexpr (std::is_same_v<entity_t, Entity::Element>)
991 else if constexpr (std::is_same_v<entity_t, Entity::BoundaryElement>)
996 MFEM_ABORT(
"restriction not implemented for Entity");
1007template <
typename entity_t,
typename fop_t>
1008inline std::tuple<std::function<void(
const Vector&,
Vector&)>,
int>
1020 return std::make_tuple(RT, 1);
1029 return std::make_tuple(RT, R->
Height());
1031 return std::make_tuple(
1048 P->Mult(x, field_l);
1062template <std::
size_t N, std::
size_t M>
1065 std::array<Vector, M> &fields_l)
1067 int data_offset = 0;
1068 for (
int i = 0; i < N; i++)
1071 const int width = P->Width();
1073 const Vector x_i(
const_cast<Vector&
>(x), data_offset, width);
1074 fields_l[i].SetSize(P->Height());
1076 P->Mult(x_i, fields_l[i]);
1077 data_offset += width;
1093 std::vector<Vector> &fields_l)
1095 int data_offset = 0;
1096 for (std::size_t i = 0; i < fields.size(); 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;
1110 std::vector<Vector> &fields_l)
1112 int data_offset = 0;
1113 for (std::size_t i = 0; i < fields.size(); i++)
1115 const int sz =
GetVSize(fields[i]);
1116 fields_l[i].SetSize(sz);
1118 const Vector x_i(
const_cast<Vector&
>(x), data_offset, sz);
1134template <
typename fop_t>
1145 MFEM_ASSERT(y.Size() == 1,
"output size doesn't match kernel description");
1147 MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM, mpi_comm);
1174template <
typename entity_t>
1181 MFEM_ASSERT(R->Width() == u_l.
Size(),
1182 "restriction not applicable to given data size");
1183 const int height = R->Height();
1185 R->Mult(u_l, field_e);
1196template <
typename entity_t>
1198 const std::vector<Vector> &u_l,
1199 std::vector<Vector> &fields_e,
1201 const int offset = 0)
1203 for (std::size_t i = 0; i <
u.size(); i++)
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]);
1215template <std::
size_t N, std::
size_t M>
1217 const std::array<Vector, N> &u_l,
1218 std::array<Vector, M> &fields_e,
1220 const int offset = 0)
1222 for (
int i = 0; i < N; i++)
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]);
1238template <
typename entity_t>
1241 if constexpr (std::is_same_v<entity_t, Entity::Element>)
1243 return mesh.
GetNE();
1245 else if constexpr (std::is_same_v<entity_t, Entity::BoundaryElement>)
1265template <
typename entity_t>
1271 return std::visit([&ir, &mode](
auto&& arg) ->
const DofToQuad*
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 *>)
1277 if constexpr (std::is_same_v<entity_t, Entity::Element>)
1279 return &arg->GetTypicalFE()->GetDofToQuad(ir, mode);
1281 else if constexpr (std::is_same_v<entity_t, Entity::BoundaryElement>)
1283 return &arg->GetTypicalTraceElement()->GetDofToQuad(ir, mode);
1286 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
1288 return &arg->GetDofToQuad();
1306template <
typename field_operator_t>
1309 std::visit([](
auto && arg)
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 *>)
1315 if constexpr (std::is_same_v<field_operator_t, Value<>>)
1319 else if constexpr (std::is_same_v<field_operator_t, Gradient<>>)
1321 MFEM_ASSERT(arg->GetTypicalElement()->GetMapType() ==
1323 "Gradient not compatible with FE");
1328 "FieldOperator not compatible with FiniteElementSpace");
1331 else if constexpr (std::is_same_v<T, const ParameterSpace *>)
1333 if constexpr (std::is_same_v<field_operator_t, Identity<>>)
1340 "FieldOperator not compatible with ParameterSpace");
1346 "Operator not compatible with FE");
1358template <
typename entity_t,
typename field_operator_t>
1381 MFEM_ABORT(
"can't get size on quadrature point for field descriptor");
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)
1398 std::array<size_t, tuple_size<field_operator_ts>::value> map;
1400 auto find_id = [](
const std::vector<FieldDescriptor> &fields, std::size_t i)
1402 auto it = std::find_if(begin(fields), end(fields),
1405 return field.
id == i;
1408 if (it == fields.end())
1412 return static_cast<size_t>(it - fields.begin());
1415 auto f = [&](
auto &fop,
auto &map)
1417 if constexpr (std::is_same_v<std::decay_t<
decltype(fop)>,
Weight>)
1427 int i = find_id(fields, fop.GetFieldId());
1431 fop.vdim =
GetVDim(fields[i]);
1437 MFEM_ABORT(
"can't find field for id: " << fop.GetFieldId());
1442 for_constexpr<tuple_size<field_operator_ts>::value>([&](
auto idx)
1451template <
typename input_t, std::size_t... i>
1453 std::array<
Vector,
sizeof...(i)> &input_qp_mem,
int num_qp,
int num_entities,
1454 const input_t &inputs, std::index_sequence<i...>)
1460template <
typename input_t, std::size_t... i>
1465 std::index_sequence<i...>)
1467 return {
Vector(
get<i>(inputs).size_on_qp * num_qp * num_entities)...};
1502template <
typename input_t, std::size_t... i>
1504 const input_t &inputs,
1505 std::index_sequence<i...>)
1507 return {
get<i>(inputs).size_on_qp...};
1525template <std::
size_t num_fields, std::
size_t num_inputs, std::
size_t num_outputs>
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,
1549 const std::vector<int> &input_size_on_qp,
1550 const int &residual_size_on_qp,
1552 const int &derivative_action_field_idx = -1)
1554 std::array<int, 8> offsets = {0};
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++)
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];
1571 total_size += std::accumulate(std::begin(input_dtq_sizes[i]),
1572 std::end(input_dtq_sizes[i]),
1577 std::array<std::array<int, 2>, num_outputs> output_dtq_sizes;
1578 for (std::size_t i = 0; i < num_outputs; i++)
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];
1588 total_size += std::accumulate(std::begin(output_dtq_sizes[i]),
1589 std::end(output_dtq_sizes[i]),
1594 std::array<int, num_fields> field_sizes;
1595 for (std::size_t i = 0; i < num_fields; i++)
1603 total_size += std::accumulate(
1604 std::begin(field_sizes), std::end(field_sizes), 0);
1607 int direction_size = 0;
1608 if (derivative_action_field_idx != -1)
1612 fields[derivative_action_field_idx], dof_ordering)
1616 total_size += direction_size;
1620 std::array<int, num_inputs> input_sizes;
1621 for (std::size_t i = 0; i < num_inputs; i++)
1623 input_sizes[i] = input_size_on_qp[i] * num_qp;
1625 total_size += std::accumulate(
1626 std::begin(input_sizes), std::end(input_sizes), 0);
1629 std::array<int, num_inputs> shadow_sizes{0};
1630 if (derivative_action_field_idx != -1)
1632 for (std::size_t i = 0; i < num_inputs; i++)
1634 shadow_sizes[i] = input_size_on_qp[i] * num_qp;
1636 total_size += std::accumulate(
1637 std::begin(shadow_sizes), std::end(shadow_sizes), 0);
1641 const int residual_size = residual_size_on_qp;
1642 total_size += residual_size * num_qp;
1645 constexpr int num_temp = 6;
1646 std::array<int, num_temp> temp_sizes = {0};
1648 const int q1d = max_dtq_qps;
1649 [[maybe_unused]]
const int d1d = max_dtq_dofs;
1652 constexpr int hardcoded_temp_num = 6;
1653 for (std::size_t i = 0; i < hardcoded_temp_num; i++)
1656 temp_sizes[i] = q1d * q1d * q1d;
1658 total_size += std::accumulate(
1659 std::begin(temp_sizes), std::end(temp_sizes), 0);
1676template <
typename shmem_info_t>
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)
1686 for (
int j = 0; j < 2; j++)
1696 out <<
"\noutput dtq sizes (B G): ";
1697 for (
auto &i : shmem_info.output_dtq_sizes)
1700 for (
int j = 0; j < 2; j++)
1710 out <<
"\nfield sizes: ";
1711 for (
auto &i : shmem_info.field_sizes)
1715 out <<
"\ndirection size: ";
1716 out << shmem_info.direction_size <<
" ";
1717 out <<
"\ninput sizes: ";
1718 for (
auto &i : shmem_info.input_sizes)
1722 out <<
"\nshadow sizes: ";
1723 for (
auto &i : shmem_info.shadow_sizes)
1727 out <<
"\ntemp sizes: ";
1728 for (
auto &i : shmem_info.temp_sizes)
1732 out <<
"\noffsets: ";
1733 for (
auto &i : shmem_info.offsets)
1740template <std::
size_t N>
1741MFEM_HOST_DEVICE
inline
1745 const std::array<std::array<int, 2>, N> &sizes,
1746 const std::array<DofToQuadMap, N> &dtq)
1748 std::array<DofToQuadMap, N>
f;
1749 for (std::size_t i = 0; i < N; i++)
1751 if (dtq[i].which_input != -1)
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,
1758 MFEM_FOREACH_THREAD(q, x, nqp_b)
1760 MFEM_FOREACH_THREAD(d, y, ndof_b)
1762 for (
int b = 0;
b < dim_b;
b++)
1764 auto v = B(q,
b, d);
1765 mem_Bi(q,
b, d) = v;
1770 offset += sizes[i][0];
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,
1777 MFEM_FOREACH_THREAD(q, x, nqp_g)
1779 MFEM_FOREACH_THREAD(d, y, ndof_g)
1781 for (
int b = 0;
b < dim_g;
b++)
1783 mem_Gi(q,
b, d) = G(q,
b, d);
1788 offset += sizes[i][1];
1792 dtq[i].which_input};
1803template <std::
size_t num_fields>
1804MFEM_HOST_DEVICE
inline
1805std::array<DeviceTensor<1>, num_fields>
1809 const std::array<int, num_fields> &sizes,
1811 const int &entity_idx)
1813 std::array<DeviceTensor<1>, num_fields>
f;
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)
1825 reinterpret_cast<real_t *
>(mem)[offset + k] =
1826 fields_e[field_idx](k, entity_idx);
1832 offset += sizes[field_idx];
1838MFEM_HOST_DEVICE
inline
1844 const int &entity_idx)
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)
1854 reinterpret_cast<real_t *
>(mem)[offset + k] =
direction(k, entity_idx);
1859 &
reinterpret_cast<real_t *
>(mem)[offset], size);
1862template <std::
size_t N>
1863MFEM_HOST_DEVICE
inline
1867 const std::array<int, N> &sizes,
1870 std::array<DeviceTensor<2>, N>
f;
1871 for (std::size_t i = 0; i < N; i++)
1881MFEM_HOST_DEVICE
inline
1885 const int &residual_size,
1892template <std::
size_t N>
1893MFEM_HOST_DEVICE
inline
1897 const std::array<int, N> &sizes)
1899 std::array<DeviceTensor<1>, N>
f;
1900 for (std::size_t i = 0; i < N; i++)
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
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,
1919 auto input_dtq_shmem =
1923 shmem_info.input_dtq_sizes,
1926 auto output_dtq_shmem =
1930 shmem_info.output_dtq_sizes,
1937 shmem_info.field_sizes,
1947 shmem_info.input_sizes,
1950 auto residual_shmem =
1954 shmem_info.residual_size,
1961 shmem_info.temp_sizes);
1967 input_dtq_shmem, output_dtq_shmem, fields_shmem,
1968 input_shmem, residual_shmem, scratch_mem);
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
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,
1983 auto input_dtq_shmem =
1987 shmem_info.input_dtq_sizes,
1990 auto output_dtq_shmem =
1994 shmem_info.output_dtq_sizes,
2001 shmem_info.field_sizes,
2005 auto direction_shmem =
2009 shmem_info.direction_size,
2010 wrapped_direction_e,
2019 shmem_info.input_sizes,
2026 shmem_info.input_sizes,
2029 auto residual_shmem =
2033 shmem_info.residual_size,
2040 shmem_info.temp_sizes);
2046 input_dtq_shmem, output_dtq_shmem, fields_shmem,
2047 direction_shmem, input_shmem, shadow_shmem,
2048 residual_shmem, scratch_mem);
2051template <std::size_t... i>
2052MFEM_HOST_DEVICE
inline
2054 const std::array<
DeviceTensor<3>,
sizeof...(i)> &input_qp_global,
int e,
2055 std::index_sequence<i...>)
2060 &input_qp_global[i](0, 0, e),
2061 input_qp_global[i].GetShape()[0],
2062 input_qp_global[i].GetShape()[1]) ...
2066template <std::
size_t N>
2067MFEM_HOST_DEVICE
inline
2070 for (std::size_t i = 0; i < N; i++)
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++)
2081template <std::
size_t n>
2082MFEM_HOST_DEVICE
inline
2086 for (
int i = 0; i < n; i++)
2088 s *=
u.GetShape()[i];
2091 for (
int j = 0; j < s; j++)
2103MFEM_HOST_DEVICE
inline
2107 for (
int i = 0; i < n; i++)
2109 s *=
u.GetShape()[i];
2113 for (
int j = 0; j < s; j++)
2125template <
int n, std::
size_t m>
2126MFEM_HOST_DEVICE
inline
2130 for (
int i = 0; i < m; i++)
2143template <std::
size_t num_fields>
2145 std::vector<Vector> &fields,
2146 std::array<int, num_fields> &field_sizes,
2147 const int &num_entities)
2149 std::array<DeviceTensor<2>, num_fields>
f;
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)
2187 MFEM_CONTRACT_VAR(seq);
2188 return (... + [](
auto &input,
auto is_dependent,
auto field)
2197 get<i>(kinput_is_dependent),
2198 fields[input_to_field[i]]));
2203 typename field_operator_ts,
2204 std::size_t N = tuple_size<field_operator_ts>::value,
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...>)
2212 auto f = [&](
auto fop, std::size_t idx)
2214 [[maybe_unused]]
auto g = [&](
int idx)
2216 auto dtq = dtqs[field_map[idx]];
2224 value_dim = dtq->FE->GetRangeDim() ? dtq->FE->GetRangeDim() : 1;
2225 grad_dim = dtq->FE->GetDim();
2228 return std::tuple{dtq, value_dim, grad_dim};
2234 auto [dtq, value_dim, grad_dim] = g(idx);
2239 static_cast<int>(idx)
2242 else if constexpr (std::is_same_v<
decltype(fop),
Weight>)
2254 auto [dtq, value_dim, grad_dim] = g(idx);
2265 "field operator type is not implemented");
2274 return std::array<DofToQuadMap, N>
2289 typename field_operator_ts,
2290 std::size_t num_fields>
2292 field_operator_ts &fops,
2293 std::vector<const DofToQuad*> &dtqmaps,
2294 const std::array<size_t, num_fields> &to_field_map)
2299 std::make_index_sequence<num_fields> {});
int Size() const
Return the logical size of the array.
Data type dense matrix using column-major storage.
A basic generic Tensor class, appropriate for use on the GPU.
MFEM_HOST_DEVICE auto & GetShape() const
Returns the shape of the tensor.
static bool Allows(unsigned long b_mask)
Return true if any of the backends in the backend mask, b_mask, are allowed.
static MemoryClass GetDeviceMemoryClass()
Get the current Device MemoryClass. This is the MemoryClass used by most MFEM device kernels to acces...
Structure representing the matrices/tensors needed to evaluate (in reference space) the values,...
Mode
Type of data stored in the arrays B, Bt, G, and Gt.
@ TENSOR
Tensor product representation using 1D matrices/tensors with dimensions using 1D number of quadrature...
Abstract data type element.
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Class for an integration rule - an Array of IntegrationPoint.
int GetNE() const
Returns number of elements.
int GetNBE() const
Returns number of boundary elements.
static int WorldRank()
Return the MPI rank in MPI_COMM_WORLD.
static int WorldSize()
Return the size of MPI_COMM_WORLD.
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
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().
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
int NumRows() const
Get the number of rows (size of output) of the Operator. Synonym with Height().
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).
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 ...
Abstract parallel finite element space.
real_t Norml2() const
Returns the l2 norm of the vector.
int Size() const
Returns the size of the vector.
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
real_t Sum() const
Return the sum of the vector entries.
void SetSize(int s)
Resize the vector to size s.
virtual MemoryClass GetMemoryClass() const override
Return the MemoryClass preferred by the Operator.
void Mult(const Vector &v, Vector &y) const override
Operator application: y=A(x).
FDJacobian(const Operator &op, const Vector &x, real_t fixed_eps=0.0)
Base class for parametric spaces.
constexpr bool always_false
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.
const Operator * get_element_restriction(const FieldDescriptor &f, ElementDofOrdering o)
Get the element restriction operator for a field descriptor.
MFEM_HOST_DEVICE constexpr auto type(const tuple< T... > &t)
a function intended to be used for extracting the ith type from a tuple.
const Operator * get_face_restriction(const FieldDescriptor &f, ElementDofOrdering o, FaceType ft, L2FaceValues m)
Get the face restriction operator for a field descriptor.
void prolongation(const FieldDescriptor field, const Vector &x, Vector &field_l)
Apply the prolongation operator to a field.
constexpr bool contains(const int *arr, std::size_t size, int value)
Helper function to check if an element is in the array.
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)
void restriction(const FieldDescriptor u, const Vector &u_l, Vector &field_e, ElementDofOrdering ordering)
Apply the restriction operator to a field.
void GetElementVDofs(const FieldDescriptor &f, int el, Array< int > &vdofs)
Get the element vdofs of a field descriptor.
void print_mpi_root(const std::string &msg)
const Operator * get_prolongation(const FieldDescriptor &f)
Get the prolongation operator for a field descriptor.
void get_lvectors(const std::vector< FieldDescriptor > fields, const Vector &x, std::vector< Vector > &fields_l)
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.
MFEM_HOST_DEVICE DeviceTensor< 1 > load_direction_mem(void *mem, int offset, const int &size, const DeviceTensor< 2 > &direction, const int &entity_idx)
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)
decltype(decay_types(std::declval< T >())) decay_tuple
int GetNumEntities(const mfem::Mesh &mesh)
Get the number of entities of a given type.
constexpr auto get_type_name() -> std::string_view
void pretty_print(std::ostream &out, const mfem::DenseMatrix &A)
Pretty print an mfem::DenseMatrix to out.
void print_tuple(const std::tuple< Args... > &t)
std::array< bool, sizeof...(Is)> make_dependency_array(const Tuple &inputs, std::index_sequence< Is... >)
MFEM_HOST_DEVICE void copy(DeviceTensor< n > &u, DeviceTensor< n > &v)
Copy data from DeviceTensor u to DeviceTensor v.
constexpr auto extract_field_ids(const std::tuple< Ts... > &t)
Extracts field IDs from a tuple of objects derived from FieldOperator.
void print_mpi_sync(const std::string &msg)
print with MPI rank synchronization
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.
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.
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.
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.
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)
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)
const Operator * get_restriction(const FieldDescriptor &f, const ElementDofOrdering &o)
Get the restriction operator for a field descriptor.
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.
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)
MFEM_HOST_DEVICE constexpr tuple< T... > make_tuple(const T &... args)
helper function for combining a list of values into a tuple
constexpr std::size_t count_unique_field_ids(const std::tuple< Ts... > &t)
Function to count unique field IDs in a tuple.
constexpr void for_constexpr_with_arg(lambda &&f, arg_t &&arg, std::integer_sequence< std::size_t >)
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.
auto make_dependency_map_impl(tuple< input_ts... > inputs, std::index_sequence< Is... >)
constexpr auto to_array(const std::tuple< Ts... > &tuple)
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.
const DofToQuad * GetDofToQuad(const FieldDescriptor &f, const IntegrationRule &ir, DofToQuad::Mode mode)
Get the GetDofToQuad object for a given entity type.
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... >)
constexpr void for_constexpr(lambda &&f, std::integer_sequence< std::size_t, i ... >)
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... >)
void CheckCompatibility(const FieldDescriptor &f)
Check the compatibility of a field operator type with a FieldDescriptor.
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.
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.
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)
void forall(func_t f, const int &N, const ThreadBlocks &blocks, int num_shmem=0, real_t *shmem=nullptr)
int GetVDim(const FieldDescriptor &f)
Get the vdim of a field descriptor.
int GetSizeOnQP(const field_operator_t &, const FieldDescriptor &f)
Get the size on quadrature point for a field operator type and FieldDescriptor combination.
MFEM_HOST_DEVICE DeviceTensor< 2 > load_residual_mem(void *mem, int offset, const int &residual_size, const int &num_qp)
auto make_dependency_map(tuple< input_ts... > inputs)
constexpr int GetFieldId()
int GetVSize(const FieldDescriptor &f)
Get the vdof size of a field descriptor.
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.
void pretty_print_mpi(const mfem::Vector &v)
Pretty print an mfem::Vector with MPI rank.
MFEM_HOST_DEVICE std::array< DeviceTensor< 1 >, 6 > load_scratch_mem(void *mem, int offset, const std::array< int, N > &sizes)
int GetDimension(const FieldDescriptor &f)
Get the spatial dimension of a field descriptor.
MFEM_HOST_DEVICE void set_zero(std::array< DeviceTensor< 2 >, N > &v)
int GetTrueVSize(const FieldDescriptor &f)
Get the true dof size of a field descriptor.
constexpr auto extract_field_ids_impl(Tuple &&t, std::index_sequence< Is... >)
void print_tuple_impl(const Tuple &t, std::index_sequence< Is... >)
void print_shared_memory_info(shmem_info_t &shmem_info)
MFEM_HOST_DEVICE zero & get(zero &x)
let zero be accessed like a tuple
__global__ void forall_kernel_shmem(func_t f, int n)
real_t u(const Vector &xvec)
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,...
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
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,...
MFEM_HOST_DEVICE DeviceTensor< sizeof...(Dims), T > Reshape(T *ptr, Dims... dims)
Wrap a pointer as a DeviceTensor with automatically deduced template parameters.
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
std::function< real_t(const Vector &)> f(real_t mass_coeff)
@ HIP_MASK
Biwise-OR of all HIP backends.
@ CPU_MASK
Biwise-OR of all CPU backends.
@ CUDA_MASK
Biwise-OR of all CUDA backends.
Helper struct to convert a C++ type to an MPI type.
DeviceTensor< 3, const real_t > G
Gradient of the basis functions evaluated at quadrature points.
Index
Enumeration for the indices of the mappings B and G.
int which_input
Reverse mapping indicating which input this map belongs to.
DeviceTensor< 3, const real_t > B
Basis functions evaluated at quadrature points.
FieldDescriptor(std::size_t field_id, const T *v)
Constructor.
data_variant_t data
Field variant.
FieldDescriptor()
Default constructor.
std::variant< const FiniteElementSpace *, const ParFiniteElementSpace *, const ParameterSpace * > data_variant_t
std::array< int, num_fields > field_sizes
std::array< std::array< int, 2 >, num_inputs > input_dtq_sizes
std::array< std::array< int, 2 >, num_outputs > output_dtq_sizes
std::array< int, num_inputs > shadow_sizes
std::array< int, num_inputs > input_sizes
std::array< int, 6 > temp_sizes
std::array< int, 8 > offsets
This is a class that mimics most of std::tuple's interface, except that it is usable in CUDA kernels ...