20 #include <type_traits>
26 #include "smith/smith_config.hpp"
31 #include "smith/numerics/functional/integral.hpp"
32 #include "smith/numerics/functional/differentiate_wrt.hpp"
33 #include "smith/numerics/functional/element_restriction.hpp"
39 constexpr
int SOURCE = 0;
40 constexpr
int FLUX = 1;
41 constexpr
int VALUE = 0;
42 constexpr
int DERIVATIVE = 1;
58 template <
typename... T>
61 constexpr uint32_t n =
sizeof...(T);
62 bool matching[] = {std::is_same_v<T, differentiate_wrt_this>...};
63 for (uint32_t i = 0; i < n; i++) {
68 return NO_DIFFERENTIATION;
79 constexpr
operator int() {
return ind; }
85 if (mesh.GetNodes() ==
nullptr) {
88 the provided mesh does not have a nodal gridfunction.
89 If you created an mfem::Mesh manually, make sure that the
90 following member functions are invoked before use
92 > mfem::Mesh::EnsureNodes();
93 > mfem::ParMesh::ExchangeFaceNbrData();
95 or else the mfem::Mesh won't be fully initialized
103 int num_elements = mesh.GetNE();
104 for (
int e = 0; e < num_elements; e++) {
105 auto type = mesh.GetElementType(e);
106 if (
type == mfem::Element::POINT ||
type == mfem::Element::WEDGE ||
type == mfem::Element::PYRAMID) {
107 SLIC_ERROR_ROOT(
"Mesh contains unsupported element type");
120 template <
typename function_space>
121 inline std::pair<std::unique_ptr<mfem::ParFiniteElementSpace>, std::unique_ptr<mfem::FiniteElementCollection>>
124 const int dim = mesh->Dimension();
125 std::unique_ptr<mfem::FiniteElementCollection> fec;
127 switch (function_space::family) {
129 fec = std::make_unique<mfem::H1_FECollection>(function_space::order, dim);
132 fec = std::make_unique<mfem::ND_FECollection>(function_space::order, dim);
135 fec = std::make_unique<mfem::RT_FECollection>(function_space::order, dim);
139 fec = std::make_unique<mfem::L2_FECollection>(function_space::order, dim, mfem::BasisType::GaussLobatto);
142 return {
nullptr,
nullptr};
148 std::make_unique<mfem::ParFiniteElementSpace>(mesh, fec.get(), function_space::components, smith::ordering);
150 return std::pair(std::move(fes), std::move(fec));
159 inline void updateFaceNbrData(
const mfem::ParFiniteElementSpace* const_trial_space, mfem::ParGridFunction& trial_pgf,
160 mfem::Vector& trial_tdof_vals)
162 mfem::ParFiniteElementSpace* nonconst_trial_space =
const_cast<mfem::ParFiniteElementSpace*
>(const_trial_space);
165 trial_pgf.MakeRef(nonconst_trial_space, trial_tdof_vals.GetData());
169 trial_pgf.ExchangeFaceNbrData();
176 inline void appendFaceNbrData(
const mfem::ParFiniteElementSpace* trial_space,
const mfem::ParGridFunction& trial_pgf,
177 const int LSize, mfem::Vector& input_L)
179 int num_ghost_elem = trial_space->GetParMesh()->GetNFaceNeighborElements();
180 int components_per_node = trial_space->GetVDim();
181 const mfem::Vector& face_neighbor_data = trial_pgf.FaceNbrData();
184 for (
int n = 0; n < num_ghost_elem; ++n) {
185 mfem::Array<int> old_shared_elem_vdof_ids;
186 trial_space->GetFaceNbrElementVDofs(n, old_shared_elem_vdof_ids);
187 int dofs_per_element = old_shared_elem_vdof_ids.Size() / components_per_node;
190 for (
int m = 0; m < old_shared_elem_vdof_ids.Size(); ++m) {
192 int new_id = (m % dofs_per_element) * components_per_node + m / dofs_per_element + offset;
193 input_L[LSize + new_id] = face_neighbor_data(old_shared_elem_vdof_ids[m]);
196 offset += old_shared_elem_vdof_ids.Size();
204 mfem::Array<HYPRE_BigInt>& face_nbr_glob_vdof_map)
206 int components_per_node = trial_space->GetVDim();
207 if (components_per_node == 1) {
208 face_nbr_glob_vdof_map = trial_space->face_nbr_glob_dof_map;
212 if (components_per_node > 1 && trial_space->GetOrdering() == mfem::Ordering::byNODES) {
213 SLIC_ERROR_ROOT(
"Unsupported: L2 vector field ordered by nodes");
216 face_nbr_glob_vdof_map.SetSize(trial_space->face_nbr_glob_dof_map.Size());
217 int num_ghost_elem = trial_space->GetParMesh()->GetNFaceNeighborElements();
218 const HYPRE_BigInt* face_nbr_glob_dof_index = trial_space->face_nbr_glob_dof_map;
221 for (
int n = 0; n < num_ghost_elem; ++n) {
222 mfem::Array<int> old_shared_elem_vdof_ids;
223 trial_space->GetFaceNbrElementVDofs(n, old_shared_elem_vdof_ids);
224 int dofs_per_element = old_shared_elem_vdof_ids.Size() / components_per_node;
227 for (
int m = 0; m < old_shared_elem_vdof_ids.Size(); ++m) {
228 int new_id = (m % dofs_per_element) * components_per_node + m / dofs_per_element + offset;
229 face_nbr_glob_vdof_map[new_id] = face_nbr_glob_dof_index[old_shared_elem_vdof_ids[m]];
232 offset += old_shared_elem_vdof_ids.Size();
237 template <
typename T, ExecutionSpace exec = smith::default_execution_space>
282 class Functional<test(trials...), exec> {
283 static constexpr
tuple<trials...> trial_spaces{};
284 static constexpr uint32_t num_trial_spaces =
sizeof...(trials);
285 static constexpr
auto Q =
std::max({test::order, trials::order...}) + 1;
287 static constexpr mfem::Geometry::Type elem_geom[4] = {mfem::Geometry::INVALID, mfem::Geometry::SEGMENT,
288 mfem::Geometry::SQUARE, mfem::Geometry::CUBE};
289 static constexpr mfem::Geometry::Type simplex_geom[4] = {mfem::Geometry::INVALID, mfem::Geometry::SEGMENT,
290 mfem::Geometry::TRIANGLE, mfem::Geometry::TETRAHEDRON};
295 template <u
int32_t i>
296 struct operator_paren_return {
297 using type =
typename std::conditional<
298 i == NO_DIFFERENTIATION,
311 Functional(
const mfem::ParFiniteElementSpace* test_fes,
312 std::array<const mfem::ParFiniteElementSpace*, num_trial_spaces> trial_fes)
313 : update_qdata_(false), test_space_(test_fes), trial_space_(trial_fes), mem_type(mfem::Device::GetMemoryType())
317 test_function_space_ = {test::family, test::order, test::components};
319 std::array<Family, num_trial_spaces> trial_families = {trials::family...};
320 std::array<int, num_trial_spaces> trial_orders = {trials::order...};
321 std::array<int, num_trial_spaces> trial_components = {trials::components...};
323 for (uint32_t i = 0; i < num_trial_spaces; i++) {
324 trial_function_spaces_[i] = {trial_families[i], trial_orders[i], trial_components[i]};
326 P_trial_[i] = trial_space_[i]->GetProlongationMatrix();
331 if (trial_function_spaces_[i].family == Family::L2) {
333 input_ldof_values_[i].SetSize(P_trial_[i]->Height(), mem_type);
336 updateFaceNbrData(trial_space_[i], trial_pargrid_functions_[i], input_ldof_values_[i]);
339 input_L_[i].SetSize(input_ldof_values_[i].Size() + trial_pargrid_functions_[i].FaceNbrData().Size(), mem_type);
345 input_L_[i].SetSize(P_trial_[i]->Height(), mem_type);
349 input_E_.push_back({});
350 input_E_buffer_.push_back({});
357 P_test_ = test_space_->GetProlongationMatrix();
359 if (test_function_space_.family == Family::L2) {
361 mfem::ParGridFunction X_pgf;
362 mfem::Vector X_ldof_values(P_test_->Height(), mem_type);
365 output_L_.SetSize(X_ldof_values.Size() + X_pgf.FaceNbrData().Size(), mem_type);
367 output_L_.SetSize(P_test_->Height(), mem_type);
370 output_T_.SetSize(test_fes->GetTrueVSize(), mem_type);
375 for (uint32_t i = 0; i < num_trial_spaces; i++) {
376 grad_.emplace_back(*
this, i);
391 template <
int dim,
int... args,
typename lambda,
typename qpt_data_type = Nothing>
392 void AddDomainIntegral(Dimension<dim>, DependsOn<args...>,
const lambda& integrand, Domain& domain,
393 std::shared_ptr<QuadratureData<qpt_data_type>> qdata =
NoQData)
395 if (domain.mesh_.GetNE() == 0)
return;
397 SLIC_ERROR_ROOT_IF(dim != domain.mesh_.Dimension(),
"invalid mesh dimension for domain integral");
402 std::vector<uint32_t> arg_vec = {args...};
403 for (uint32_t i : arg_vec) {
404 domain.insert_restriction(trial_space_[i], trial_function_spaces_[i]);
406 domain.insert_restriction(test_space_, test_function_space_);
408 using signature = test(decltype(smith::type<args>(trial_spaces))...);
409 integrals_.push_back(
410 MakeDomainIntegral<signature, Q, dim>(domain, integrand, qdata, std::vector<uint32_t>{args...}));
422 template <
int dim,
int... args,
typename lambda>
423 void AddBoundaryIntegral(Dimension<dim>, DependsOn<args...>,
const lambda& integrand, Domain& domain)
425 auto num_bdr_elements = domain.mesh_.GetNBE();
426 if (num_bdr_elements == 0)
return;
428 SLIC_ERROR_ROOT_IF(dim != domain.dim_,
"invalid domain of integration for boundary integral");
432 std::vector<uint32_t> arg_vec = {args...};
433 for (uint32_t i : arg_vec) {
434 domain.insert_restriction(trial_space_[i], trial_function_spaces_[i]);
436 domain.insert_restriction(test_space_, test_function_space_);
438 using signature = test(decltype(smith::type<args>(trial_spaces))...);
439 integrals_.push_back(MakeBoundaryIntegral<signature, Q, dim>(domain, integrand, std::vector<uint32_t>{args...}));
445 template <
int dim,
int... args,
typename Integrand>
446 void AddInteriorFaceIntegral(Dimension<dim>, DependsOn<args...>,
const Integrand& integrand, Domain& domain)
450 std::vector<uint32_t> arg_vec = {args...};
451 for (uint32_t i : arg_vec) {
452 domain.insert_restriction(trial_space_[i], trial_function_spaces_[i]);
454 domain.insert_restriction(test_space_, test_function_space_);
456 using signature = test(decltype(smith::type<args>(trial_spaces))...);
457 integrals_.push_back(
458 MakeInteriorFaceIntegral<signature, Q, dim>(domain, integrand, std::vector<uint32_t>{args...}));
470 template <
int... args,
typename lambda,
typename qpt_data_type = Nothing>
471 void AddAreaIntegral(DependsOn<args...> which_args,
const lambda& integrand, Domain& domain,
472 std::shared_ptr<QuadratureData<qpt_data_type>> data =
NoQData)
474 AddDomainIntegral(Dimension<2>{}, which_args, integrand, domain, data);
486 template <
int... args,
typename lambda,
typename qpt_data_type =
Nothing>
490 AddDomainIntegral(
Dimension<3>{}, which_args, integrand, domain, data);
494 template <
int... args,
typename lambda>
497 AddBoundaryIntegral(
Dimension<2>{}, which_args, integrand, domain);
511 void ActionOfGradient(
const mfem::Vector& input_T, mfem::Vector& output_T, uint32_t which)
const
514 if (trial_function_spaces_[which].family == Family::L2) {
518 P_trial_[which]->Mult(input_T, input_ldof_values_[which]);
519 input_L_[which].SetVector(input_ldof_values_[which], 0);
520 updateFaceNbrData(trial_space_[which], trial_pargrid_functions_[which], input_ldof_values_[which]);
523 if (trial_space_[which]->GetVDim() == 1) {
524 input_L_[which].SetVector(trial_pargrid_functions_[which].FaceNbrData(), input_ldof_values_[which].Size());
526 if (trial_space_[which]->GetOrdering() == mfem::Ordering::byVDIM) {
527 int local_size = input_ldof_values_[which].Size();
528 appendFaceNbrData(trial_space_[which], trial_pargrid_functions_[which], local_size, input_L_[which]);
530 SLIC_ERROR_ROOT(
"Unsupported: L2 vector field ordered by nodes");
534 P_trial_[which]->Mult(input_T, input_L_[which]);
539 for (
auto& integral : integrals_) {
540 if (integral.DependsOn(which)) {
541 Domain& dom = integral.domain_;
544 input_E_buffer_[which].SetSize(
int(G_trial.
ESize()));
545 input_E_[which].Update(input_E_buffer_[which], G_trial.
bOffsets());
546 G_trial.
Gather(input_L_[which], input_E_[which]);
549 output_E_buffer_.SetSize(
int(G_test.
ESize()));
550 output_E_.Update(output_E_buffer_, G_test.
bOffsets());
552 integral.GradientMult(input_E_[which], output_E_, which);
560 if (test_function_space_.family == Family::L2) {
563 mfem::Vector output_ldof_values(P_test_->Height(), mem_type);
564 for (
int j = 0; j < output_ldof_values.Size(); ++j) {
565 output_ldof_values[j] = output_L_[j];
567 P_test_->MultTranspose(output_ldof_values, output_T);
569 P_test_->MultTranspose(output_L_, output_T);
585 template <uint32_t wrt,
typename... T>
588 const mfem::Vector* input_T[] = {&
static_cast<const mfem::Vector&
>(args)...};
591 for (uint32_t i = 0; i < num_trial_spaces; i++) {
592 if (trial_function_spaces_[i].family == Family::L2) {
596 P_trial_[i]->Mult(*input_T[i], input_ldof_values_[i]);
600 input_L_[i].SetVector(input_ldof_values_[i], 0);
603 updateFaceNbrData(trial_space_[i], trial_pargrid_functions_[i], input_ldof_values_[i]);
617 if (trial_space_[i]->GetVDim() == 1) {
619 input_L_[i].SetVector(trial_pargrid_functions_[i].FaceNbrData(), input_ldof_values_[i].Size());
621 if (trial_space_[i]->GetOrdering() == mfem::Ordering::byVDIM) {
624 int local_size = input_ldof_values_[i].Size();
625 appendFaceNbrData(trial_space_[i], trial_pargrid_functions_[i], local_size, input_L_[i]);
634 SLIC_ERROR_ROOT(
"Unsupported: L2 vector field ordered by nodes");
638 P_trial_[i]->Mult(*input_T[i], input_L_[i]);
644 for (
auto& integral : integrals_) {
645 Domain& dom = integral.domain_;
649 for (
auto i : integral.active_trial_spaces_) {
651 input_E_buffer_[i].SetSize(
int(G_trial.
ESize()));
652 input_E_[i].Update(input_E_buffer_[i], G_trial.
bOffsets());
653 G_trial.
Gather(input_L_[i], input_E_[i]);
656 output_E_buffer_.SetSize(
int(G_test.
ESize()));
657 output_E_.Update(output_E_buffer_, G_test.
bOffsets());
658 integral.Mult(t, input_E_, output_E_, wrt, update_qdata_);
665 if (test_function_space_.family == Family::L2) {
668 mfem::Vector output_ldof_values(P_test_->Height(), mem_type);
669 for (
int j = 0; j < output_ldof_values.Size(); ++j) {
670 output_ldof_values[j] = output_L_[j];
672 P_test_->MultTranspose(output_ldof_values, output_T_);
674 P_test_->MultTranspose(output_L_, output_T_);
677 if constexpr (wrt != NO_DIFFERENTIATION) {
684 return {output_T_, grad_[wrt]};
687 if constexpr (wrt == NO_DIFFERENTIATION) {
698 template <
typename... T>
699 auto operator()(
double t,
const T&... args)
701 constexpr
int num_differentiated_arguments = (std::is_same_v<T, differentiate_wrt_this> + ...);
702 static_assert(num_differentiated_arguments <= 1,
703 "Error: Functional::operator() can only differentiate w.r.t. 1 argument a time");
704 static_assert(
sizeof...(T) == num_trial_spaces,
705 "Error: Functional::operator() must take exactly as many arguments as trial spaces");
709 return (*
this)(DifferentiateWRT<i>{}, t, args...);
720 void updateQdata(
bool update_flag) { update_qdata_ = update_flag; }
731 class Gradient :
public mfem::Operator {
737 Gradient(Functional<test(trials...), exec>& f, uint32_t which = 0)
738 : mfem::Operator(f.test_space_->GetTrueVSize(), f.trial_space_[which]->GetTrueVSize()),
740 which_argument(which),
741 test_space_(f.test_space_),
742 trial_space_(f.trial_space_[which]),
743 df_(f.test_space_->GetTrueVSize())
753 virtual void Mult(
const mfem::Vector& dx, mfem::Vector& df)
const override
755 form_.ActionOfGradient(dx, df, which_argument);
759 mfem::Vector& operator()(
const mfem::Vector& dx)
761 form_.ActionOfGradient(dx, df_, which_argument);
765 void initialize_sparsity_pattern()
767 using row_col = std::tuple<int, int>;
769 std::set<row_col> nonzero_entries;
771 for (
auto& integral : form_.integrals_) {
772 if (integral.DependsOn(which_argument)) {
773 Domain& dom = integral.domain_;
774 const auto& G_test = dom.get_restriction(form_.test_function_space_);
775 const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]);
776 for (
const auto& [geom, test_restriction] : G_test.
restrictions) {
777 const auto& trial_restriction = G_trial.
restrictions.at(geom);
780 std::vector<int> test_vdofs(test_restriction.nodes_per_elem * test_restriction.components);
781 std::vector<int> trial_vdofs(trial_restriction.nodes_per_elem * trial_restriction.components);
783 auto num_elements =
static_cast<uint32_t
>(test_restriction.num_elements);
784 for (uint32_t e = 0; e < num_elements; e++) {
785 for (uint32_t i = 0; i < test_restriction.nodes_per_elem; i++) {
786 auto test_dof = test_restriction.dof_info(e, i);
787 for (uint32_t j = 0; j < test_restriction.components; j++) {
788 test_vdofs[i * test_restriction.components + j] = int(test_restriction.GetVDof(test_dof, j).index());
792 for (uint32_t i = 0; i < trial_restriction.nodes_per_elem; i++) {
793 auto trial_dof = trial_restriction.dof_info(e, i);
794 for (uint32_t j = 0; j < trial_restriction.components; j++) {
795 trial_vdofs[i * trial_restriction.components + j] =
796 int(trial_restriction.GetVDof(trial_dof, j).index());
800 for (
int row : test_vdofs) {
801 for (
int col : trial_vdofs) {
802 nonzero_entries.insert({row, col});
810 uint64_t nnz = nonzero_entries.size();
811 int nrows = form_.output_L_.Size();
813 row_ptr.resize(uint32_t(nrows + 1));
818 for (
auto [row, col] : nonzero_entries) {
819 col_ind[uint32_t(nz)] = col;
820 for (
int i = last_row + 1; i <= row; i++) {
821 row_ptr[uint32_t(i)] = nz;
826 for (
int i = last_row + 1; i <= nrows; i++) {
827 row_ptr[uint32_t(i)] = nz;
831 uint64_t max_buffer_size()
833 uint64_t max_entries = 0;
834 for (
auto& integral : form_.integrals_) {
835 if (integral.DependsOn(which_argument)) {
836 Domain& dom = integral.domain_;
837 const auto& G_test = dom.get_restriction(form_.test_function_space_);
838 const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]);
839 for (
const auto& [geom, test_restriction] : G_test.
restrictions) {
840 const auto& trial_restriction = G_trial.
restrictions.at(geom);
841 uint64_t nrows_per_element = test_restriction.nodes_per_elem * test_restriction.components;
842 uint64_t ncols_per_element = trial_restriction.nodes_per_elem * trial_restriction.components;
843 uint64_t entries_per_element = nrows_per_element * ncols_per_element;
844 uint64_t entries_needed = test_restriction.num_elements * entries_per_element;
845 max_entries =
std::max(entries_needed, max_entries);
852 std::unique_ptr<mfem::HypreParMatrix> assemble()
854 if (row_ptr.empty()) {
855 initialize_sparsity_pattern();
860 constexpr
bool sparse_matrix_frees_graph_ptrs =
false;
861 constexpr
bool sparse_matrix_frees_values_ptr =
false;
862 constexpr
bool col_ind_is_sorted =
true;
866 std::vector<int> col_ind_copy = col_ind;
868 int nnz = row_ptr.back();
869 std::vector<double> values(uint32_t(nnz), 0.0);
870 auto A_local = mfem::SparseMatrix(row_ptr.data(), col_ind_copy.data(), values.data(), form_.output_L_.Size(),
871 form_.input_L_[which_argument].Size(), sparse_matrix_frees_graph_ptrs,
872 sparse_matrix_frees_values_ptr, col_ind_is_sorted);
874 std::vector<double> K_elem_buffer(max_buffer_size());
876 for (
auto& integral : form_.integrals_) {
878 if (integral.functional_to_integral_index_.count(which_argument) > 0) {
879 Domain& dom = integral.domain_;
881 uint32_t
id = integral.functional_to_integral_index_.at(which_argument);
882 const auto& G_test = dom.get_restriction(form_.test_function_space_);
883 const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]);
884 for (
const auto& [geom, calculate_element_matrices_func] : integral.element_gradient_[
id]) {
885 const auto& test_restriction = G_test.
restrictions.at(geom);
886 const auto& trial_restriction = G_trial.
restrictions.at(geom);
889 CPUArrayView<double, 3> K_e(K_elem_buffer.data(), test_restriction.num_elements,
890 trial_restriction.nodes_per_elem * trial_restriction.components,
891 test_restriction.nodes_per_elem * test_restriction.components);
892 detail::zero_out(K_e);
895 calculate_element_matrices_func(K_e);
897 const std::vector<int>& element_ids = integral.domain_.get(geom);
899 uint32_t rows_per_elem = uint32_t(test_restriction.nodes_per_elem * test_restriction.components);
900 uint32_t cols_per_elem = uint32_t(trial_restriction.nodes_per_elem * trial_restriction.components);
902 std::vector<DoF> test_vdofs(rows_per_elem);
903 std::vector<DoF> trial_vdofs(cols_per_elem);
905 for (uint32_t e = 0; e < element_ids.size(); e++) {
906 test_restriction.GetElementVDofs(
int(e), test_vdofs);
907 trial_restriction.GetElementVDofs(
int(e), trial_vdofs);
909 for (uint32_t i = 0; i < cols_per_elem; i++) {
910 int col = int(trial_vdofs[i].index());
912 for (uint32_t j = 0; j < rows_per_elem; j++) {
913 int row = int(test_vdofs[j].index());
914 A_local.SearchRow(row, col) += K_e(e, i, j);
922 auto* R = form_.test_space_->Dof_TrueDof_Matrix();
944 mfem::HypreParMatrix* A_hypre;
945 if (
dynamic_cast<const mfem::L2_FECollection*
>(test_space_->FEColl()) ||
946 dynamic_cast<const mfem::L2_FECollection*
>(trial_space_->FEColl())) {
947 int lrows = test_space_->GetVSize();
948 int lcols = trial_space_->GetVSize();
949 HYPRE_BigInt col_offset = trial_space_->GetMyDofOffset();
950 mfem::Array<HYPRE_BigInt> glob_J(A_local.NumNonZeroElems());
952 int* J = A_local.GetJ();
953 for (
int j = 0; j < glob_J.Size(); ++j) {
955 glob_J[j] = J[j] + col_offset;
957 glob_J[j] = form_.face_nbr_glob_vdof_maps_[which_argument][J[j] - lcols];
960 A_hypre =
new mfem::HypreParMatrix(test_space_->GetComm(), lrows, test_space_->GlobalVSize(),
961 trial_space_->GlobalVSize(), A_local.GetI(), glob_J, A_local.GetData(),
962 test_space_->GetDofOffsets(), trial_space_->GetDofOffsets());
966 new mfem::HypreParMatrix(test_space_->GetComm(), test_space_->GlobalVSize(), trial_space_->GlobalVSize(),
967 test_space_->GetDofOffsets(), trial_space_->GetDofOffsets(), &A_local);
970 auto* P = trial_space_->Dof_TrueDof_Matrix();
972 std::unique_ptr<mfem::HypreParMatrix> A(mfem::RAP(R, A_hypre, P));
979 friend auto assemble(Gradient& g) {
return g.assemble(); }
983 Functional<test(trials...), exec>& form_;
985 std::vector<int> row_ptr;
986 std::vector<int> col_ind;
998 uint32_t which_argument;
1001 const mfem::ParFiniteElementSpace* test_space_;
1004 const mfem::ParFiniteElementSpace* trial_space_;
1011 const mfem::ParFiniteElementSpace* test_space_;
1014 std::array<const mfem::ParFiniteElementSpace*, num_trial_spaces> trial_space_;
1016 std::array<FunctionSpace, num_trial_spaces> trial_function_spaces_;
1017 FunctionSpace test_function_space_;
1020 std::array<mfem::Array<HYPRE_BigInt>, num_trial_spaces> face_nbr_glob_vdof_maps_;
1023 mutable std::array<mfem::ParGridFunction, num_trial_spaces> trial_pargrid_functions_;
1026 mutable std::array<mfem::Vector, num_trial_spaces> input_ldof_values_;
1032 const mfem::Operator* P_trial_[num_trial_spaces];
1035 mutable mfem::Vector input_L_[num_trial_spaces];
1037 mutable std::vector<mfem::Vector> input_E_buffer_;
1038 mutable std::vector<mfem::BlockVector> input_E_;
1040 mutable std::vector<Integral> integrals_;
1042 mutable mfem::Vector output_E_buffer_;
1043 mutable mfem::BlockVector output_E_;
1046 mutable mfem::Vector output_L_;
1048 const mfem::Operator* P_test_;
1051 mutable mfem::Vector output_T_;
1054 mutable std::vector<Gradient> grad_;
1056 const mfem::MemoryType mem_type;
many of the functions in this file amount to extracting element indices from an mesh_t like
This file contains helper traits and enumerations for classifying finite elements.
a specialization of smith::Functional for quantities of interest
This file contains the all the necessary functions and macros required for logging as well as a helpe...
Accelerator functionality.
void appendFaceNbrData(const mfem::ParFiniteElementSpace *trial_space, const mfem::ParGridFunction &trial_pgf, const int LSize, mfem::Vector &input_L)
helper functional to reorder the ordering of FaceNbrData for L2 space to byVDIM and append this vecto...
tuple(T...) -> tuple< T... >
Class template argument deduction rule for tuples.
std::pair< std::unique_ptr< mfem::ParFiniteElementSpace >, std::unique_ptr< mfem::FiniteElementCollection > > generateParFiniteElementSpace(mfem::ParMesh *mesh)
create an mfem::ParFiniteElementSpace from one of Smith's tag types: H1, Hcurl, L2
std::shared_ptr< QuadratureData< Nothing > > NoQData
a single instance of a QuadratureData container of Nothings, since they are all interchangeable
void check_for_unsupported_elements(const mfem::Mesh &mesh)
function for verifying that there are no unsupported element types in the mesh
constexpr SMITH_HOST_DEVICE auto type(const tuple< T... > &values)
a function intended to be used for extracting the ith type from a tuple.
constexpr uint32_t index_of_differentiation()
given a list of types, this function returns the index that corresponds to the type dual_vector.
SMITH_HOST_DEVICE auto max(dual< gradient_type > a, double b)
Implementation of max for dual numbers.
ExecutionSpace
enum used for signalling whether or not to perform certain calculations on the CPU or GPU
void rearrangeFaceNbrDofGlobalIndex(const mfem::ParFiniteElementSpace *trial_space, mfem::Array< HYPRE_BigInt > &face_nbr_glob_vdof_map)
helper functional to reorder the face_nbr_glob_dof_map for L2 space to byVDIM
void check_for_missing_nodal_gridfunc(const mfem::Mesh &mesh)
function for verifying that the mesh has been fully initialized
void updateFaceNbrData(const mfem::ParFiniteElementSpace *const_trial_space, mfem::ParGridFunction &trial_pgf, mfem::Vector &trial_tdof_vals)
helper function to locally cast away const on FE space so we can update face neighbor data with Excha...
#define SMITH_MARK_FUNCTION
Definitions of quadrature rules for quads and hexes.
a generalization of mfem::ElementRestriction that works with multiple kinds of element geometries....
uint64_t ESize() const
the size of the "E-vector" associated with this restriction operator
void Gather(const mfem::Vector &L_vector, mfem::BlockVector &E_block_vector) const
"L->E" in mfem parlance, each element gathers the values that belong to it, and stores them in the "E...
void ScatterAdd(const mfem::BlockVector &E_block_vector, mfem::Vector &L_vector) const
"E->L" in mfem parlance, each element scatter-adds its local vector into the appropriate place in the...
std::map< mfem::Geometry::Type, ElementRestriction > restrictions
the individual ElementRestriction operators for each element geometry
mfem::Array< int > bOffsets() const
block offsets used when constructing mfem::HypreParVectors
Compile-time alias for a dimension.
a class for representing a geometric region that can be used for integration
Compile-time alias for index of differentiation.
these classes are a little confusing. These two special types represent the similar (but different) c...
A class for storing and access user-defined types at quadrature points.
This is a class that mimics most of std::tuple's interface, except that it is usable in CUDA kernels ...
Implementation of the tensor class used by Functional.