573 derivative_ids_t derivative_ids)
575 if constexpr (!(std::is_same_v<entity_t, Entity::Element> ||
576 std::is_same_v<entity_t, Entity::BoundaryElement>))
579 "entity type not supported in AddIntegrator");
582 static constexpr size_t num_inputs =
585 static constexpr size_t num_outputs =
590 using qf_param_ts =
typename qf_signature::parameter_ts;
591 using qf_output_t =
typename qf_signature::return_t;
594 if constexpr (num_outputs > 1)
597 "more than one output per quadrature functions is not supported right now");
600 if constexpr (std::is_same_v<qf_output_t, void>)
603 "quadrature function has no return value");
607 static_assert(num_qfinputs == num_inputs,
608 "quadrature function inputs and descriptor inputs have to match");
611 static_assert(num_qf_outputs == num_outputs,
612 "quadrature function outputs and descriptor outputs have to match");
614 constexpr auto inout_tuple =
616 constexpr auto filtered_inout_tuple =
filter_fields(inout_tuple);
617 static constexpr size_t num_fields =
620 MFEM_ASSERT(num_fields == solutions.size() + parameters.size(),
621 "Total number of fields doesn't match sum of solutions and parameters."
622 " This indicates that some fields are not used in the integrator,"
623 " which currently is not supported.");
629 auto input_to_field =
631 auto output_to_field =
635 std::vector<int> inputs_vdim(num_inputs);
638 inputs_vdim[i] =
get<i>(inputs).vdim;
642 if constexpr (std::is_same_v<entity_t, Entity::Element>)
646 else if constexpr (std::is_same_v<entity_t, Entity::BoundaryElement>)
651 const auto output_fop =
get<0>(outputs);
652 test_space_field_idx =
FindIdx(output_fop.GetFieldId(), fields);
654 bool use_sum_factorization =
false;
656 if constexpr (std::is_same_v<entity_t, Entity::Element>)
658 entity_element_type =
663 use_tensor_product_structure ==
true)
665 use_sum_factorization =
true;
668 else if constexpr (std::is_same_v<entity_t, Entity::BoundaryElement>)
670 entity_element_type =
675 use_tensor_product_structure ==
true)
677 use_sum_factorization =
true;
683 if (use_sum_factorization)
691 (fields[test_space_field_idx],
692 element_dof_ordering, output_fop);
693 auto &output_e_size = output_e_sz;
695 output_restriction_transpose = output_rt;
696 residual_e.
SetSize(output_e_size);
700 restriction_callback = [element_dof_ordering,
701 solutions_ = this->solutions,
702 parameters_ = this->parameters]
703 (std::vector<Vector> &
sol,
704 const std::vector<Vector> &par,
705 std::vector<Vector> &
f)
708 element_dof_ordering);
710 element_dof_ordering,
715 fields[test_space_field_idx], output_fop, mesh.
GetComm());
718 if constexpr (std::is_same_v<entity_t, Entity::Element>)
722 else if constexpr (std::is_same_v<entity_t, Entity::BoundaryElement>)
729 const int num_qp = integration_rule.
GetNPoints();
731 if constexpr (
is_sum_fop<
decltype(output_fop)>::value)
738 const int residual_lsize =
GetVSize(fields[test_space_field_idx]);
739 residual_l.
SetSize(residual_lsize);
746 std::vector<const DofToQuad*> dtq;
747 for (
const auto &field : fields)
754 const int q1d = (int)floor(std::pow(num_qp, 1.0/
dimension) + 0.5);
756 const int residual_size_on_qp =
758 fields[test_space_field_idx]);
763 const int test_vdim = output_fop.vdim;
764 const int test_op_dim = output_fop.size_on_qp / output_fop.vdim;
765 const int num_test_dof =
766 num_entities ? (output_e_size / output_fop.vdim / num_entities) : 0;
770 auto input_size_on_qp =
773 auto action_shmem_info =
775 (input_dtq_maps, output_dtq_maps, fields, num_entities, inputs, num_qp,
776 input_size_on_qp, residual_size_on_qp, element_dof_ordering);
778 Vector shmem_cache(action_shmem_info.total_size);
785 if (use_sum_factorization)
787 thread_blocks.
x = q1d;
788 thread_blocks.
y = q1d;
789 thread_blocks.
z = q1d;
794 if (use_sum_factorization)
796 thread_blocks.
x = q1d;
797 thread_blocks.
y = q1d;
803 thread_blocks.
x = q1d;
808 action_callbacks.push_back(
824 use_sum_factorization,
838 &restriction_cb = this->restriction_callback,
839 &fields_e_ = this->fields_e,
840 &residual_e_ = this->residual_e,
841 &output_restriction_transpose_ = this->output_restriction_transpose
843 (std::vector<Vector> &
sol,
const std::vector<Vector> &par,
Vector &res)
846 restriction_cb(
sol, par, fields_e_);
849 auto ye =
Reshape(residual_e_.ReadWrite(), test_vdim, num_test_dof, num_entities);
852 action_shmem_info.field_sizes,
855 const bool has_attr = attributes.
Size() > 0;
856 const auto d_attr = attributes.
Read();
857 const auto d_elem_attr = elem_attributes->Read();
859 forall([=] MFEM_HOST_DEVICE (
int e,
void *shmem)
861 if (has_attr && !d_attr[d_elem_attr[e] - 1]) {
return; }
863 auto [input_dtq_shmem, output_dtq_shmem, fields_shmem, input_shmem,
864 residual_shmem, scratch_shmem] =
865 unpack_shmem(shmem, action_shmem_info, input_dtq_maps, output_dtq_maps,
866 wrapped_fields_e, num_qp, e);
869 input_shmem, fields_shmem, input_dtq_shmem, input_to_field, inputs, ir_weights,
870 scratch_shmem,
dimension, use_sum_factorization);
873 qfunc, input_shmem, residual_shmem,
874 residual_size_on_qp, num_qp, q1d,
dimension, use_sum_factorization);
876 auto fhat =
Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
877 auto y =
Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
879 y, fhat, output_fop, output_dtq_shmem[0],
880 scratch_shmem,
dimension, use_sum_factorization);
881 }, num_entities, thread_blocks, action_shmem_info.total_size, shmem_cache.
ReadWrite());
882 output_restriction_transpose_(residual_e_, res);
887 if constexpr (derivative_ids_t::size() != 0)
891 this->output_restriction_transpose](
const std::size_t derivative_id)
893 const size_t d_field_idx =
FindIdx(derivative_id, fields);
894 const auto direction = fields[d_field_idx];
895 const int da_size_on_qp =
900 input_dtq_maps, output_dtq_maps, fields, num_entities, inputs,
901 num_qp, input_size_on_qp, residual_size_on_qp,
902 element_dof_ordering, d_field_idx);
904 Vector shmem_cache(shmem_info.total_size);
909 element_dof_ordering)->
Height());
910 Vector derivative_action_e(output_e_size);
911 derivative_action_e = 0.0;
914 auto it = dependency_map.find(derivative_id);
915 if (it == dependency_map.end())
917 MFEM_ABORT(
"Derivative ID not found in dependency map");
919 const auto input_is_dependent = it->second;
924 Vector inputs_trial_op_dim(num_inputs);
925 int total_trial_op_dim = 0;
931 if (!input_is_dependent[s])
938 itod(idx) = input_size_on_qp[s] /
get<s>(inputs).vdim;
940 total_trial_op_dim +=
static_cast<int>(itod(idx));
946 const size_t d_input_idx = [d_field_idx, &input_to_field]
948 for (
size_t i = 0; i < input_to_field.size(); i++)
950 if (input_to_field[i] == d_field_idx)
955 return size_t(SIZE_MAX);
958 const int trial_vdim =
GetVDim(fields[d_field_idx]);
959 const int num_trial_dof =
961 inputs_vdim[d_input_idx] / num_entities;
962 const int num_trial_dof_1d =
965 Vector Ae_mem(num_test_dof * test_vdim * num_trial_dof * trial_vdim *
972 derivative_qp_caches[derivative_id] =
Vector(test_vdim * test_op_dim *
974 total_trial_op_dim * num_qp * num_entities);
976 auto& fields_ref = this->fields;
977 auto& derivative_qp_caches_ref = this->derivative_qp_caches[derivative_id];
983 derivative_setup_callbacks[derivative_id].push_back(
995 use_sum_factorization,
1006 element_dof_ordering,
1014 inputs_trial_op_dim,
1017 &qpdc_mem = derivative_qp_caches_ref
1018 ](std::vector<Vector> &f_e,
const Vector &dir_l)
mutable
1021 element_dof_ordering);
1022 auto wrapped_fields_e =
wrap_fields(f_e, shmem_info.field_sizes,
1025 shmem_info.direction_size,
1028 auto qpdc =
Reshape(qpdc_mem.ReadWrite(), test_vdim, test_op_dim,
1029 trial_vdim, total_trial_op_dim, num_qp, num_entities);
1031 auto itod =
Reshape(inputs_trial_op_dim.
Read(), num_inputs);
1033 const auto d_elem_attr = elem_attributes->Read();
1034 const bool has_attr = attributes.
Size() > 0;
1035 const auto d_domain_attr = attributes.
Read();
1039 if (has_attr && !d_domain_attr[d_elem_attr[e] - 1]) {
return; }
1041 auto [input_dtq_shmem, output_dtq_shmem, fields_shmem,
1042 direction_shmem, input_shmem,
1043 shadow_shmem_, residual_shmem,
1045 unpack_shmem(shmem, shmem_info, input_dtq_maps, output_dtq_maps,
1046 wrapped_fields_e, wrapped_direction_e, num_qp, e);
1047 auto &shadow_shmem = shadow_shmem_;
1050 input_shmem, fields_shmem, input_dtq_shmem, input_to_field,
1051 inputs, ir_weights, scratch_shmem,
dimension,
1052 use_sum_factorization);
1056 auto qpdc_e =
Reshape(&qpdc(0, 0, 0, 0, 0, e), test_vdim, test_op_dim,
1057 trial_vdim, total_trial_op_dim, num_qp);
1059 qfunc, input_shmem, shadow_shmem, residual_shmem, qpdc_e, itod, da_size_on_qp,
1061 }, num_entities, thread_blocks, shmem_info.total_size,
1067 derivative_action_callbacks[derivative_id].push_back(
1080 use_sum_factorization,
1094 derivative_action_e,
1095 element_dof_ordering,
1096 inputs_trial_op_dim,
1100 &qpdc_mem = derivative_qp_caches_ref,
1103 std::vector<Vector> &f_e,
const Vector &dir_l,
1104 Vector &der_action_l)
mutable
1107 element_dof_ordering);
1109 test_vdim, num_entities);
1110 auto wrapped_fields_e =
wrap_fields(f_e, shmem_info.field_sizes,
1113 shmem_info.direction_size,
1116 auto qpdc =
Reshape(qpdc_mem.Read(), test_vdim, test_op_dim,
1117 trial_vdim, total_trial_op_dim, num_qp, num_entities);
1119 auto itod =
Reshape(inputs_trial_op_dim.
Read(), num_inputs);
1121 const bool has_attr = attributes.
Size() > 0;
1122 const auto d_attr = attributes.
Read();
1123 const auto d_elem_attr = elem_attributes->Read();
1125 derivative_action_e = 0.0;
1128 if (has_attr && !d_attr[d_elem_attr[e] - 1]) {
return; }
1130 auto [input_dtq_shmem, output_dtq_shmem, fields_shmem,
1131 direction_shmem, input_shmem,
1132 shadow_shmem_, residual_shmem,
1134 unpack_shmem(shmem, shmem_info, input_dtq_maps, output_dtq_maps,
1135 wrapped_fields_e, wrapped_direction_e, num_qp, e);
1136 auto &shadow_shmem = shadow_shmem_;
1139 shadow_shmem, direction_shmem, input_dtq_shmem, inputs,
1140 ir_weights, scratch_shmem, input_is_dependent,
dimension,
1141 use_sum_factorization);
1143 auto fhat =
Reshape(&residual_shmem(0, 0), test_vdim,
1144 test_op_dim, num_qp);
1146 auto qpdce =
Reshape(&qpdc(0, 0, 0, 0, 0, e), test_vdim, test_op_dim,
1147 trial_vdim, total_trial_op_dim, num_qp);
1150 use_sum_factorization);
1152 auto y =
Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
1154 y, fhat, output_fop, output_dtq_shmem[0],
1155 scratch_shmem,
dimension, use_sum_factorization);
1156 }, num_entities, thread_blocks, shmem_info.total_size,
1158 or_transpose(derivative_action_e, der_action_l);
1161 assemble_derivative_sparsematrix_callbacks[derivative_id].push_back(
1173 use_sum_factorization,
1191 inputs_trial_op_dim,
1196 &qpdc_mem = derivative_qp_caches_ref,
1197 &fields_ = fields_ref
1200 auto wrapped_fields_e =
wrap_fields(f_e, shmem_info.field_sizes,
1203 shmem_info.direction_size,
1206 auto qpdc =
Reshape(qpdc_mem.Read(), test_vdim, test_op_dim,
1207 trial_vdim, total_trial_op_dim, num_qp, num_entities);
1209 auto itod =
Reshape(inputs_trial_op_dim.
Read(), num_inputs);
1211 auto Ae =
Reshape(Ae_mem.
ReadWrite(), num_test_dof, test_vdim, num_trial_dof,
1212 trial_vdim, num_entities);
1214 const auto d_elem_attr = elem_attributes->Read();
1215 const bool has_attr = attributes.
Size() > 0;
1216 const auto d_domain_attr = attributes.
Read();
1220 if (has_attr && !d_domain_attr[d_elem_attr[e] - 1]) {
return; }
1222 auto [input_dtq_shmem, output_dtq_shmem, fields_shmem,
1223 direction_shmem, input_shmem,
1224 shadow_shmem_, residual_shmem,
1226 unpack_shmem(shmem, shmem_info, input_dtq_maps, output_dtq_maps,
1227 wrapped_fields_e, wrapped_direction_e, num_qp, e);
1229 auto fhat =
Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
1230 auto Aee =
Reshape(&Ae(0, 0, 0, 0, e), num_test_dof, test_vdim, num_trial_dof,
1232 auto qpdce =
Reshape(&qpdc(0, 0, 0, 0, 0, e), test_vdim, test_op_dim,
1233 trial_vdim, total_trial_op_dim, num_qp);
1235 input_dtq_shmem, output_dtq_shmem[0], scratch_shmem,
dimension, q1d,
1236 num_trial_dof_1d, use_sum_factorization);
1237 }, num_entities, thread_blocks, shmem_info.total_size,
1241 for (
size_t s = 0; s < num_inputs; s++)
1243 if (input_is_dependent[s])
1245 trial_field = &fields_[input_to_field[s]];
1249 auto trial_fes = *std::get_if<const ParFiniteElementSpace *>
1250 (&trial_field->data);
1251 auto test_fes = *std::get_if<const ParFiniteElementSpace *>
1252 (&fields_[output_to_field[0]].data);
1254 A =
new SparseMatrix(test_fes->GetVSize(), trial_fes->GetVSize());
1257 num_trial_dof * trial_vdim, num_entities);
1258 for (
int e = 0; e < num_entities; e++)
1260 DenseMatrix Aee(&tmp(0, 0, e), num_test_dof * test_vdim,
1261 num_trial_dof * trial_vdim);
1264 test_fes->GetElementVDofs(e, test_vdofs);
1265 trial_fes->GetElementVDofs(e, trial_vdofs);
1267 if (use_sum_factorization)
1274 if (test_dofmap.
Size() == 0)
1276 test_vdofs_mapped = test_vdofs;
1280 MFEM_ASSERT(test_dofmap.
Size() == num_test_dof,
1281 "internal error: dof map of the test space does not "
1282 "match previously determined number of test space dofs");
1284 for (
int vd = 0; vd < test_vdim; vd++)
1286 for (
int i = 0; i < num_test_dof; i++)
1288 test_vdofs_mapped[i + vd * num_test_dof] =
1289 test_vdofs[test_dofmap[i] + vd * num_test_dof];
1298 if (trial_dofmap.
Size() == 0)
1300 trial_vdofs_mapped = trial_vdofs;
1304 MFEM_ASSERT(trial_dofmap.
Size() == num_trial_dof,
1305 "internal error: dof map of the trial space does not "
1306 "match previously determined number of trial space dofs");
1308 for (
int vd = 0; vd < trial_vdim; vd++)
1310 for (
int i = 0; i < num_trial_dof; i++)
1312 trial_vdofs_mapped[i + vd * num_trial_dof] =
1313 trial_vdofs[trial_dofmap[i] + vd * num_trial_dof];
1318 A->
AddSubMatrix(test_vdofs_mapped, trial_vdofs_mapped, Aee, 1);
1329 auto& assemble_derivative_sparsematrix_callbacks_ref =
1330 this->assemble_derivative_sparsematrix_callbacks[derivative_id];
1332 assemble_derivative_hypreparmatrix_callbacks[derivative_id].push_back(
1337 &spmatcb = assemble_derivative_sparsematrix_callbacks_ref,
1338 &fields_ = fields_ref
1342 for (
const auto &
f : spmatcb)
1347 if (spmat ==
nullptr)
1349 MFEM_ABORT(
"internal error");
1352 bool same_test_and_trial =
false;
1353 for (
size_t s = 0; s < num_inputs; s++)
1355 if (input_is_dependent[s])
1357 if (output_to_field[0] == input_to_field[s])
1359 same_test_and_trial =
true;
1366 for (
size_t s = 0; s < num_inputs; s++)
1368 if (input_is_dependent[s])
1370 trial_field = &fields_[input_to_field[s]];
1374 auto trial_fes = *std::get_if<const ParFiniteElementSpace *>
1375 (&trial_field->
data);
1376 auto test_fes = *std::get_if<const ParFiniteElementSpace *>
1377 (&fields_[output_to_field[0]].data);
1379 if (same_test_and_trial)
1382 test_fes->GlobalVSize(),
1383 test_fes->GetDofOffsets(),
1385 A =
RAP(&tmp, test_fes->Dof_TrueDof_Matrix());
1390 test_fes->GlobalVSize(),
1391 trial_fes->GlobalVSize(),
1392 test_fes->GetDofOffsets(),
1393 trial_fes->GetDofOffsets(),
1395 A =
RAP(test_fes->Dof_TrueDof_Matrix(), &tmp,
1396 trial_fes->Dof_TrueDof_Matrix());