From 29dd2073d2b09e1ace91232f1219148f35a558df Mon Sep 17 00:00:00 2001
From: Johannes Heinz <43043310+jh66637@users.noreply.github.com>
Date: Mon, 22 Jan 2024 09:52:34 +0100
Subject: [PATCH] Add factory functions for FERemoteEvaluationCommunicator
---
doc/news/changes/minor/20231231Heinz | 4 +
.../matrix_free/fe_remote_evaluation.h | 441 ++++++++++++++++++
tests/matrix_free/fe_remote_evaluation_03.cc | 308 ++++++++++++
..._p4est=true.mpirun=1.with_cgal=true.output | 3 +
..._p4est=true.mpirun=2.with_cgal=true.output | 7 +
5 files changed, 763 insertions(+)
create mode 100644 doc/news/changes/minor/20231231Heinz
create mode 100644 tests/matrix_free/fe_remote_evaluation_03.cc
create mode 100644 tests/matrix_free/fe_remote_evaluation_03.with_p4est=true.mpirun=1.with_cgal=true.output
create mode 100644 tests/matrix_free/fe_remote_evaluation_03.with_p4est=true.mpirun=2.with_cgal=true.output
diff --git a/doc/news/changes/minor/20231231Heinz b/doc/news/changes/minor/20231231Heinz
new file mode 100644
index 0000000000..306b95c60f
--- /dev/null
+++ b/doc/news/changes/minor/20231231Heinz
@@ -0,0 +1,4 @@
+New: Add utility functions that setup FERemoteEvaluationCommunicator for point-to-point
+interpolation and Nitsche-type mortaring.
+
+(Johannes Heinz, 2023/12/31)
diff --git a/include/deal.II/matrix_free/fe_remote_evaluation.h b/include/deal.II/matrix_free/fe_remote_evaluation.h
index 11bf9f874a..7bf81d25f6 100644
--- a/include/deal.II/matrix_free/fe_remote_evaluation.h
+++ b/include/deal.II/matrix_free/fe_remote_evaluation.h
@@ -19,12 +19,16 @@
#include
+#include
+#include
+
#include
#include
#include
#include
+#include
#include
DEAL_II_NAMESPACE_OPEN
@@ -446,6 +450,76 @@ private:
+/**
+ * The namespace collects convenience functions to setup
+ * FERemoteEvaluationCommunicator for typical use cases.
+ */
+namespace Utilities
+{
+ /**
+ * A factory function for the FERemoteEvaluationCommunicator in the case of
+ * point-to-point interpolation.
+ *
+ * @param[in] matrix_free MatrixFree object that is used to distribute
+ * quadrature points at non-matching faces.
+ * @param[in] non_matching_faces_marked_vertices A vector of boundary face IDs
+ * that relate to non-matching interfaces. Each boundary face ID is
+ * accompanied by a lambda that marks the vertices of cells which are
+ * considered during the search of remote points (quadrature points of faces
+ * with given boundary face ID).
+ * @param[in] quad_no Quadrature number in @p matrix_free.
+ * @param[in] dof_no DoFHandler number in @p matrix_free.
+ * @param[in] tolerance Tolerance to find remote points.
+ */
+ template >
+ FERemoteEvaluationCommunicator
+ setup_remote_communicator_faces_point_to_point_interpolation(
+ const MatrixFree &matrix_free,
+ const std::vector<
+ std::pair()>>>
+ &non_matching_faces_marked_vertices,
+ const unsigned int quad_no = 0,
+ const unsigned int dof_no = 0,
+ const double tolerance = 1e-9);
+
+
+
+ /**
+ * A factory function for the FERemoteEvaluationCommunicator in the case of
+ * Nitsche-type mortaring.
+ *
+ * @param[in] matrix_free MatrixFree object that is used to distribute
+ * @param[in] non_matching_faces_marked_vertices A vector of boundary face IDs
+ * that relate to non-matching interfaces. Each boundary face ID is
+ * accompanied by a lambda that marks the vertices of cells which are
+ * considered during the process of computing intersections.
+ * @param[in] n_q_pnts_1D The number of 1D quadrature points per intersection.
+ * @param[in] dof_no DoFHandler number in @p matrix_free.
+ * distributed to each intersection (given for one coordinate direction).
+ * @param[in] nm_mapping_info In case nm_mapping_info is not a `nullptr` it is
+ * setup for cell based loops, such that it can be used in combination with
+ * FERemoteEvaluation.
+ * @param[in] tolerance Tolerance to find intersections.
+ */
+ template >
+ FERemoteEvaluationCommunicator
+ setup_remote_communicator_faces_nitsche_type_mortaring(
+ const MatrixFree &matrix_free,
+ const std::vector<
+ std::pair()>>>
+ &non_matching_faces_marked_vertices,
+ const unsigned int n_q_pnts_1D,
+ const unsigned int dof_no = 0,
+ NonMatching::MappingInfo *nm_mapping_info = nullptr,
+ const double tolerance = 1e-9);
+} // namespace Utilities
+
+
+
/**
* Class to access data in a matrix-free loop for non-matching discretizations.
* Interfaces are named with FEEvaluation in mind.
@@ -999,6 +1073,373 @@ FERemoteEvaluationCommunicator::CopyInstructions::copy_data_entries(
+namespace Utilities
+{
+ template
+ FERemoteEvaluationCommunicator
+ setup_remote_communicator_faces_point_to_point_interpolation(
+ const MatrixFree &matrix_free,
+ const std::vector<
+ std::pair()>>>
+ &non_matching_faces_marked_vertices,
+ const unsigned int quad_no,
+ const unsigned int dof_no,
+ const double tolerance)
+ {
+ const auto &dof_handler = matrix_free.get_dof_handler(dof_no);
+ const auto &tria = dof_handler.get_triangulation();
+ const auto &mapping = *matrix_free.get_mapping_info().mapping;
+
+ // Communication objects know about the communication pattern. I.e.,
+ // they know about the cells and quadrature points that have to be
+ // evaluated at remote faces. This information is given via
+ // RemotePointEvaluation. Additionally, the communication objects
+ // have to be able to match the quadrature points of the remote
+ // points (that provide exterior information) to the quadrature points
+ // defined at the interior cell. In case of point-to-point interpolation
+ // a vector of pairs with face batch Ids and the number of faces in the
+ // batch is needed. @c FERemoteCommunicationObjectEntityBatches
+ // is a container to store this information.
+ //
+ // We need multiple communication objects (one for each non-matching face
+ // ID).
+ std::vector> comm_objects;
+
+ // Additionally to the communication objects we need a vector
+ // that stores quadrature rules for every face batch.
+ // The quadrature can be empty in case of non non-matching faces,
+ // i.e. boundary faces. Internally this information is needed to correctly
+ // access values over multiple communication objects.
+ std::vector> global_quadrature_vector(
+ matrix_free.n_boundary_face_batches());
+
+ // Get the range of face batches we have to look at during construction of
+ // the communication objects. We only have to look at boundary faces.
+ const auto face_batch_range =
+ std::make_pair(matrix_free.n_inner_face_batches(),
+ matrix_free.n_inner_face_batches() +
+ matrix_free.n_boundary_face_batches());
+
+ // Iterate over all non-matching face IDs.
+ for (const auto &[nm_face, marked_vertices] :
+ non_matching_faces_marked_vertices)
+ {
+ // Construct the communication object for every face ID:
+ // 1) RemotePointEvaluation with user specified function for marked
+ // vertices.
+ auto rpe = std::make_shared>(
+ tolerance, false, 0, marked_vertices);
+
+ // 2) Face batch IDs and number of faces in batch.
+ std::vector>
+ face_batch_id_n_faces;
+
+ // Points that are searched by rpe.
+ std::vector> points;
+
+ // Temporarily setup FEFaceEvaluation to access the quadrature points at
+ // the faces on the non-matching interface.
+ FEFaceEvaluation phi(matrix_free,
+ true,
+ dof_no,
+ quad_no);
+
+ // Iterate over the boundary faces.
+ for (unsigned int bface = 0;
+ bface < face_batch_range.second - face_batch_range.first;
+ ++bface)
+ {
+ const unsigned int face = face_batch_range.first + bface;
+
+ if (matrix_free.get_boundary_id(face) == nm_face)
+ {
+ phi.reinit(face);
+
+ // If @c face is on the current side of the non-matching
+ // interface. Add the face batch ID and the number of faces in
+ // the batch to the corresponding data structure.
+ const unsigned int n_faces =
+ matrix_free.n_active_entries_per_face_batch(face);
+ face_batch_id_n_faces.emplace_back(
+ std::make_pair(face, n_faces));
+
+ // Append the quadrature points to the points we need to search
+ // for.
+ for (unsigned int v = 0; v < n_faces; ++v)
+ {
+ for (unsigned int q : phi.quadrature_point_indices())
+ {
+ const auto point = phi.quadrature_point(q);
+ Point temp;
+ for (unsigned int i = 0; i < dim; ++i)
+ temp[i] = point[i][v];
+
+ points.push_back(temp);
+ }
+ }
+
+ // Insert a quadrature rule of correct size into the global
+ // quadrature vector. First check that each face is only
+ // considered once.
+ Assert(global_quadrature_vector[bface].size() == 0,
+ ExcMessage(
+ "Quadrature for given face already provided."));
+
+ global_quadrature_vector[bface] =
+ Quadrature(phi.n_q_points);
+ }
+ }
+
+ // Reinit RPE and ensure all points are found.
+ rpe->reinit(points, tria, mapping);
+ Assert(rpe->all_points_found(),
+ ExcMessage("Not all remote points found."));
+
+ // Add communication object to the list of objects.
+ FERemoteCommunicationObjectEntityBatches co;
+ co.batch_id_n_entities = face_batch_id_n_faces;
+ co.rpe = rpe;
+ comm_objects.push_back(co);
+ }
+
+ // Reinit the communicator `FERemoteEvaluationCommunicator`
+ // with the communication objects.
+ FERemoteEvaluationCommunicator remote_communicator;
+
+ remote_communicator.reinit_faces(comm_objects,
+ face_batch_range,
+ global_quadrature_vector);
+
+ return remote_communicator;
+ }
+
+
+
+ template
+ FERemoteEvaluationCommunicator
+ setup_remote_communicator_faces_nitsche_type_mortaring(
+ const MatrixFree &matrix_free,
+ const std::vector<
+ std::pair()>>>
+ &non_matching_faces_marked_vertices,
+ const unsigned int n_q_pnts_1D,
+ const unsigned int dof_no,
+ NonMatching::MappingInfo *nm_mapping_info,
+ const double tolerance)
+ {
+ const auto &dof_handler = matrix_free.get_dof_handler(dof_no);
+ const auto &tria = dof_handler.get_triangulation();
+ const auto &mapping = *matrix_free.get_mapping_info().mapping;
+
+ constexpr unsigned int n_lanes = VectorizedArray::size();
+
+ std::pair face_range =
+ std::make_pair(matrix_free.n_inner_face_batches(),
+ matrix_free.n_inner_face_batches() +
+ matrix_free.n_boundary_face_batches());
+
+ std::vector> global_quadrature_vector(
+ (matrix_free.n_inner_face_batches() +
+ matrix_free.n_boundary_face_batches()) *
+ n_lanes);
+
+ // In case of Nitsche-type mortaring a vector of face indices is
+ // needed as communication object.
+ // @c FERemoteCommunicationObjectFaces is a container to store this
+ // information.
+ //
+ // We need multiple communication objects (one for each non-matching face
+ // ID).
+ std::vector> comm_objects;
+
+ // Create bounding boxes and GridTools::Cache which is needed in
+ // the following loop.
+ std::vector> local_boxes;
+ for (const auto &cell : tria.active_cell_iterators())
+ if (cell->is_locally_owned())
+ local_boxes.emplace_back(mapping.get_bounding_box(cell));
+
+ // Create r-tree of bounding boxes
+ const auto local_tree = pack_rtree(local_boxes);
+
+ // Compress r-tree to a minimal set of bounding boxes
+ std::vector>> global_bboxes(1);
+ global_bboxes[0] = extract_rtree_level(local_tree, 0);
+
+ const GridTools::Cache cache(tria, mapping);
+
+ // Iterate over all sides of the non-matching interface.
+ for (const auto &[nm_face, marked_vertices] :
+ non_matching_faces_marked_vertices)
+ {
+ // 1) compute cell face pairs
+ std::vector<
+ std::pair::cell_iterator, unsigned int>>
+ cell_face_pairs;
+
+ std::vector indices;
+
+ for (unsigned int face = face_range.first; face < face_range.second;
+ ++face)
+ {
+ if (matrix_free.get_boundary_id(face) == nm_face)
+ {
+ for (unsigned int v = 0;
+ v < matrix_free.n_active_entries_per_face_batch(face);
+ ++v)
+ {
+ const auto &[c, f] = matrix_free.get_face_iterator(face, v);
+
+ cell_face_pairs.emplace_back(std::make_pair(c, f));
+ indices.push_back(face * n_lanes + v);
+ }
+ }
+ }
+
+ // 2) Create RPE.
+ // In the Nitsche-type case we do not collect points for the setup
+ // of RemotePointEvaluation. Instead we compute intersections between
+ // the faces and setup RemotePointEvaluation with the computed
+ // intersections.
+
+ // Build intersection requests. Intersection requests
+ // correspond to vertices at faces.
+ std::vector>> intersection_requests;
+ for (const auto &[cell, f] : cell_face_pairs)
+ {
+ std::vector> vertices(cell->face(f)->n_vertices());
+ std::copy_n(mapping.get_vertices(cell, f).begin(),
+ cell->face(f)->n_vertices(),
+ vertices.begin());
+ intersection_requests.emplace_back(vertices);
+ }
+
+ // Compute intersection data with user specified function for marked
+ // vertices.
+ auto intersection_data =
+ GridTools::internal::distributed_compute_intersection_locations<
+ dim - 1>(cache,
+ intersection_requests,
+ global_bboxes,
+ marked_vertices(),
+ tolerance);
+
+ // Convert to RPE.
+ std::vector> mapped_quadratures_recv_comp;
+
+ auto rpe =
+ std::make_shared>();
+ rpe->reinit(
+ intersection_data
+ .template convert_to_distributed_compute_point_locations_internal<
+ dim>(n_q_pnts_1D, tria, mapping, &mapped_quadratures_recv_comp),
+ tria,
+ mapping);
+
+ // 3) Fill global quadrature vector.
+ for (unsigned int i = 0; i < intersection_requests.size(); ++i)
+ {
+ const auto idx = indices[i];
+
+ // We do not use a structural binding here, since with
+ // C++17 caputuring structural bindings in lambdas leads
+ // to an ill formed program.
+ const auto &cell = std::get<0>(cell_face_pairs[i]);
+ const auto &f = std::get<1>(cell_face_pairs[i]);
+
+ std::vector> q_points;
+ std::vector weights;
+ for (unsigned int ptr = intersection_data.recv_ptrs[i];
+ ptr < intersection_data.recv_ptrs[i + 1];
+ ++ptr)
+ {
+ const auto &quad = mapped_quadratures_recv_comp[ptr];
+
+ const auto &ps = quad.get_points();
+ std::transform(
+ ps.begin(),
+ ps.end(),
+ std::back_inserter(q_points),
+ [&](const Point &p) {
+ return mapping.project_real_point_to_unit_point_on_face(
+ cell, f, p);
+ });
+
+ const auto &ws = quad.get_weights();
+ weights.insert(weights.end(), ws.begin(), ws.end());
+ }
+ Quadrature quad(q_points, weights);
+
+ Assert(global_quadrature_vector[idx].size() == 0,
+ ExcMessage("Quadrature for given face already provided."));
+
+ global_quadrature_vector[idx] = quad;
+ }
+
+ // Add communication object.
+ FERemoteCommunicationObject co;
+ co.indices = indices;
+ co.rpe = rpe;
+ comm_objects.push_back(co);
+ }
+
+ // Reinit the communicator with the communication objects.
+ FERemoteEvaluationCommunicator remote_communicator;
+
+ remote_communicator.reinit_faces(
+ comm_objects,
+ std::make_pair(0, global_quadrature_vector.size()),
+ global_quadrature_vector);
+
+ if (nm_mapping_info != nullptr)
+ {
+ std::vector<
+ std::pair::cell_iterator, unsigned int>>
+ vector_face_accessors;
+ vector_face_accessors.reserve((matrix_free.n_inner_face_batches() +
+ matrix_free.n_boundary_face_batches()) *
+ n_lanes);
+
+ // fill container for inner face batches
+ unsigned int face_batch = 0;
+ for (; face_batch < matrix_free.n_inner_face_batches(); ++face_batch)
+ {
+ for (unsigned int v = 0; v < n_lanes; ++v)
+ {
+ if (v < matrix_free.n_active_entries_per_face_batch(face_batch))
+ vector_face_accessors.push_back(
+ matrix_free.get_face_iterator(face_batch, v));
+ else
+ vector_face_accessors.push_back(
+ matrix_free.get_face_iterator(face_batch, 0));
+ }
+ }
+ // and boundary face batches
+ for (; face_batch < (matrix_free.n_inner_face_batches() +
+ matrix_free.n_boundary_face_batches());
+ ++face_batch)
+ {
+ for (unsigned int v = 0; v < n_lanes; ++v)
+ {
+ if (v < matrix_free.n_active_entries_per_face_batch(face_batch))
+ vector_face_accessors.push_back(
+ matrix_free.get_face_iterator(face_batch, v));
+ else
+ vector_face_accessors.push_back(
+ matrix_free.get_face_iterator(face_batch, 0));
+ }
+ }
+
+ nm_mapping_info->reinit_faces(vector_face_accessors,
+ global_quadrature_vector);
+ }
+
+ return remote_communicator;
+ }
+} // namespace Utilities
+
+
+
template
template
FERemoteEvaluation::FERemoteEvaluation(
diff --git a/tests/matrix_free/fe_remote_evaluation_03.cc b/tests/matrix_free/fe_remote_evaluation_03.cc
new file mode 100644
index 0000000000..9d12be17e6
--- /dev/null
+++ b/tests/matrix_free/fe_remote_evaluation_03.cc
@@ -0,0 +1,308 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2023 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+// This test checks the correct setup of FERemoteEvaluationCommunicator
+// via the factory functions
+// setup_remote_communicator_faces_point_to_point_interpolation()
+// and setup_remote_communicator_faces_nitsche_type_mortaring().
+
+#include
+
+#include
+#include
+
+#include
+
+#include
+
+#include
+
+#include "../tests.h"
+
+using namespace dealii;
+
+using Number = double;
+
+using VectorType = LinearAlgebra::distributed::Vector;
+
+constexpr unsigned int dim = 2;
+
+template
+struct Coordinates : public Function
+{
+ Coordinates()
+ : Function(dim, 0.0)
+ {}
+
+ double
+ value(const Point &p, const unsigned int component) const final
+ {
+ if (component == 0)
+ return p[component];
+ return p[component];
+ }
+};
+
+
+void
+test_point_to_point(const MatrixFree &matrix_free)
+{
+ const Triangulation &tria =
+ matrix_free.get_dof_handler().get_triangulation();
+
+ std::vector()>>>
+ non_matching_faces_marked_vertices;
+
+ non_matching_faces_marked_vertices.emplace_back(std::make_pair(1, [&]() {
+ std::vector mask(tria.n_vertices(), true);
+
+ for (const auto &cell : tria.active_cell_iterators())
+ for (auto const &f : cell->face_indices())
+ if (cell->face(f)->at_boundary() && cell->face(f)->boundary_id() == 1)
+ for (const auto v : cell->vertex_indices())
+ mask[cell->vertex_index(v)] = false;
+
+ return mask;
+ }));
+
+ auto remote_communicator =
+ Utilities::setup_remote_communicator_faces_point_to_point_interpolation(
+ matrix_free, non_matching_faces_marked_vertices);
+
+ // 1) Allocate vectors and interpolate grid coordinates in DoFs.
+ VectorType src;
+ VectorType dst;
+ matrix_free.initialize_dof_vector(src);
+ matrix_free.initialize_dof_vector(dst);
+
+ VectorTools::interpolate(*matrix_free.get_mapping_info().mapping,
+ matrix_free.get_dof_handler(),
+ Coordinates(),
+ src);
+
+ // 2) Construct remote evaluator...
+ FERemoteEvaluation> phi_remote_eval(
+ remote_communicator, matrix_free.get_dof_handler());
+
+ // ...and precompute remote values.
+ phi_remote_eval.gather_evaluate(src, EvaluationFlags::values);
+
+ // 3) Access values at quadrature points
+ const auto boundary_function =
+ [&](const auto &data, auto &dst, const auto &src, const auto face_range) {
+ FEFaceEvaluation phi(matrix_free, true);
+ auto phi_remote = phi_remote_eval.get_data_accessor();
+
+ for (unsigned int face = face_range.first; face < face_range.second;
+ ++face)
+ {
+ if (matrix_free.get_boundary_id(face) == 1)
+ {
+ phi.reinit(face);
+ phi_remote.reinit(face);
+
+ for (unsigned int q : phi.quadrature_point_indices())
+ {
+ const auto error =
+ phi.quadrature_point(q) - phi_remote.get_value(q);
+
+ // only consider active entries in global error
+ for (unsigned int d = 0; d < dim; ++d)
+ for (unsigned int v = 0;
+ v < matrix_free.n_active_entries_per_face_batch(face);
+ ++v)
+ AssertThrow(std::abs(error[d][v]) < 1e-13,
+ ExcMessage("Error too large."));
+ }
+ }
+ }
+ };
+
+ matrix_free.template loop(
+ {}, {}, boundary_function, dst, src, true);
+}
+
+void
+test_nitsche_type_mortaring(const MatrixFree &matrix_free)
+{
+ const Triangulation &tria =
+ matrix_free.get_dof_handler().get_triangulation();
+
+ std::vector()>>>
+ non_matching_faces_marked_vertices;
+
+ non_matching_faces_marked_vertices.emplace_back(std::make_pair(1, [&]() {
+ std::vector mask(tria.n_vertices(), true);
+
+ for (const auto &cell : tria.active_cell_iterators())
+ for (auto const &f : cell->face_indices())
+ if (cell->face(f)->at_boundary() && cell->face(f)->boundary_id() == 1)
+ for (const auto v : cell->vertex_indices())
+ mask[cell->vertex_index(v)] = false;
+
+ return mask;
+ }));
+
+ typename NonMatching::MappingInfo::AdditionalData
+ additional_data;
+ additional_data.use_global_weights = true;
+
+ NonMatching::MappingInfo nm_mapping_info(
+ *matrix_free.get_mapping_info().mapping,
+ update_quadrature_points,
+ additional_data);
+
+ auto remote_communicator =
+ Utilities::setup_remote_communicator_faces_nitsche_type_mortaring(
+ matrix_free, non_matching_faces_marked_vertices, 4, 0, &nm_mapping_info);
+
+ // 1) Allocate vectors and interpolate grid coordinates in DoFs.
+ VectorType src;
+ VectorType dst;
+ matrix_free.initialize_dof_vector(src);
+ matrix_free.initialize_dof_vector(dst);
+
+ VectorTools::interpolate(*matrix_free.get_mapping_info().mapping,
+ matrix_free.get_dof_handler(),
+ Coordinates(),
+ src);
+
+ // 2) Construct remote evaluator...
+ FERemoteEvaluation phi_remote_eval(
+ remote_communicator, matrix_free.get_dof_handler());
+
+ // ...and precompute remote values.
+ phi_remote_eval.gather_evaluate(src, EvaluationFlags::values);
+
+ // 3) Access values at quadrature points
+ const auto boundary_function =
+ [&](const auto &data, auto &dst, const auto &src, const auto face_range) {
+ FEFacePointEvaluation phi(
+ nm_mapping_info, matrix_free.get_dof_handler().get_fe());
+ auto phi_remote = phi_remote_eval.get_data_accessor();
+
+
+ for (unsigned int face = face_range.first; face < face_range.second;
+ ++face)
+ {
+ if (matrix_free.get_boundary_id(face) == 1)
+ {
+ for (unsigned int v = 0;
+ v < matrix_free.n_active_entries_per_face_batch(face);
+ ++v)
+ {
+ constexpr unsigned int n_lanes =
+ VectorizedArray::size();
+ phi.reinit(face * n_lanes + v);
+ phi_remote.reinit(face * n_lanes + v);
+
+ for (unsigned int q : phi.quadrature_point_indices())
+ {
+ const auto point = phi.real_point(q);
+ Tensor<1, dim, Number> temp;
+ for (unsigned int i = 0; i < dim; ++i)
+ temp[i] = point[i];
+
+ const auto error = temp - phi_remote.get_value(q);
+ for (unsigned int d = 0; d < dim; ++d)
+ AssertThrow(std::abs(error[d]) < 1e-13,
+ ExcMessage("Error too large."));
+ }
+ }
+ }
+ }
+ };
+
+ matrix_free.template loop(
+ {}, {}, boundary_function, dst, src, true);
+}
+
+int
+main(int argc, char **argv)
+{
+ Utilities::MPI::MPI_InitFinalize mpi(argc, argv, 1);
+ MPILogInitAll all;
+
+ // Construct non-matching triangulation.
+ const double length = 1.0;
+ Triangulation tria_left;
+ const unsigned int subdiv_left = 11;
+ GridGenerator::subdivided_hyper_rectangle(tria_left,
+ {subdiv_left, 2 * subdiv_left},
+ {0.0, 0.0},
+ {0.525 * length, length});
+
+ for (const auto &face : tria_left.active_face_iterators())
+ if (face->at_boundary())
+ {
+ face->set_boundary_id(0);
+ if (face->center()[0] > 0.525 * length - 1e-6)
+ face->set_boundary_id(1);
+ }
+
+ Triangulation tria_right;
+ const unsigned int subdiv_right = 4;
+ GridGenerator::subdivided_hyper_rectangle(tria_right,
+ {subdiv_right, 2 * subdiv_right},
+ {0.525 * length, 0.0},
+ {length, length});
+
+ for (const auto &face : tria_right.active_face_iterators())
+ if (face->at_boundary())
+ face->set_boundary_id(0);
+
+ // Merge triangulations with tolerance 0 to ensure no vertices
+ // are merged.
+ parallel::distributed::Triangulation tria(MPI_COMM_WORLD);
+ GridGenerator::merge_triangulations(tria_left,
+ tria_right,
+ tria,
+ /*tolerance*/ 0.,
+ /*copy_manifold_ids*/ false,
+ /*copy_boundary_ids*/ true);
+
+ // Create DoFHandler
+ DoFHandler dof_handler(tria);
+ dof_handler.distribute_dofs(FESystem(FE_DGQ(3), dim));
+
+ // Constraints are not considered in this test
+ AffineConstraints constraints;
+ constraints.close();
+
+ // Setup MatrixFree
+ typename MatrixFree::AdditionalData data;
+ data.mapping_update_flags = update_values;
+ // We are not considering inner faces in this test. Therefore,
+ // data.mapping_update_flags_inner_faces is not set.
+ data.mapping_update_flags_boundary_faces =
+ update_quadrature_points | update_values;
+
+
+ MatrixFree matrix_free;
+ matrix_free.reinit(
+ MappingQ1(), dof_handler, constraints, QGauss(4), data);
+
+ // 2) test point-to-point
+ test_point_to_point(matrix_free);
+
+ deallog << "OK" << std::endl;
+
+ // 3) test Nitsche-type
+ test_nitsche_type_mortaring(matrix_free);
+
+ deallog << "OK" << std::endl;
+
+ return 0;
+}
diff --git a/tests/matrix_free/fe_remote_evaluation_03.with_p4est=true.mpirun=1.with_cgal=true.output b/tests/matrix_free/fe_remote_evaluation_03.with_p4est=true.mpirun=1.with_cgal=true.output
new file mode 100644
index 0000000000..2d64160d8c
--- /dev/null
+++ b/tests/matrix_free/fe_remote_evaluation_03.with_p4est=true.mpirun=1.with_cgal=true.output
@@ -0,0 +1,3 @@
+
+DEAL:0::OK
+DEAL:0::OK
diff --git a/tests/matrix_free/fe_remote_evaluation_03.with_p4est=true.mpirun=2.with_cgal=true.output b/tests/matrix_free/fe_remote_evaluation_03.with_p4est=true.mpirun=2.with_cgal=true.output
new file mode 100644
index 0000000000..abeeb87a92
--- /dev/null
+++ b/tests/matrix_free/fe_remote_evaluation_03.with_p4est=true.mpirun=2.with_cgal=true.output
@@ -0,0 +1,7 @@
+
+DEAL:0::OK
+DEAL:0::OK
+
+DEAL:1::OK
+DEAL:1::OK
+
--
2.39.5