diff --git a/agglomeration_poisson/CMakeLists.txt b/agglomeration_poisson/CMakeLists.txt new file mode 100644 index 00000000..6dfe9edc --- /dev/null +++ b/agglomeration_poisson/CMakeLists.txt @@ -0,0 +1,33 @@ +# Set the name of the project and target: +SET(TARGET "agglomeration_poisson") + +# Declare all source files the target consists of. +SET(TARGET_SRC + ${TARGET}.cc + source/agglomeration_handler.cc + source/mapping_box.cc +) + +# Add include directory +INCLUDE_DIRECTORIES(include) + + +CMAKE_MINIMUM_REQUIRED(VERSION 3.13.4) + +FIND_PACKAGE(deal.II 9.6.0 + HINTS ${deal.II_DIR} ${DEAL_II_DIR} ../ ../../ $ENV{DEAL_II_DIR} + ) +IF(NOT ${deal.II_FOUND}) + MESSAGE(FATAL_ERROR "\n" + "*** Could not locate a (sufficiently recent) version of deal.II. ***\n\n" + "You may want to either pass a flag -DDEAL_II_DIR=/path/to/deal.II to cmake\n" + "or set an environment variable \"DEAL_II_DIR\" that contains this path." + ) +ENDIF() + +DEAL_II_INITIALIZE_CACHED_VARIABLES() +PROJECT(${TARGET}) +DEAL_II_INVOKE_AUTOPILOT() + +# Define SOURCE_DIR for mesh file paths +target_compile_definitions(${TARGET} PRIVATE SOURCE_DIR="${CMAKE_SOURCE_DIR}") diff --git a/agglomeration_poisson/README.md b/agglomeration_poisson/README.md new file mode 100644 index 00000000..d01b5c73 --- /dev/null +++ b/agglomeration_poisson/README.md @@ -0,0 +1,135 @@ +# A Discontinuous Galerkin solver for the Poisson problem on general polytopal meshes generated through mesh agglomeration + +## Running the code: + +As in the tutorial programs, type + +`cmake -DDEAL_II_DIR=/path/to/deal.II .` + +on the command line to configure the program. After that you can compile with `make` and run with either `make run` or using + +`./DG_advection_reaction` + +on the command line. + +## The problem: +This program solves the problem, for $\Omega \in \mathbb{R^2}$ + +@f[ +\begin{cases} b \cdot \nabla u + c u = f \qquad \text{in } \Omega \\ +\qquad \qquad u=g \qquad \text{on } \partial_{-}\Omega \end{cases} +@f] + +where $g \in L^2(\partial_{-}\Omega)$ and $\partial_{-}\Omega=\{ x \in +\partial \Omega: b(x)\cdot n(x) <0\}$ is the inflow part of the +boundary, with $b=(b_1,b_2) \in \mathbb{R^2}$. As we know from +classical DG theory, we need to ensure that +@f[ +c(x) - \frac{1}{2}\nabla \cdot b \geq \gamma_0 >0 +@f] +for some positive $\gamma_0$ so that we have coercivity in $L^2$ at the continuous level. Discrete coercivity is achieved by using a stronger norm which takes care of jumps, see Di Pietro and Ern [2] for details. + + +## The weak formulation: + +As trial space we choose $V_h = \{ v_h \in L^2(\Omega): v_h \in P^1(\mathbb{T_h})\} \notin H^1(\Omega)$. If we integrate by parts and sum over all cells + +@f[ +\sum_{T \in \mathbb{T}_h} \Bigl( (-u,\beta \cdot \nabla v_h) _T + (c +u,v_h)_T + \bigl<(b \cdot n) u ,v_h \bigr>_{\partial T} \Bigr) = +(f,v_h)_{\Omega} +@f] + +and use the so-called DG magic formula and exploit the property $[bu]_{\mathbb{F}^i} = 0$ where $\mathbb{F}^i$ are set of internal faces we obtain the (unstable!) formulation: + +Find $u_h \in V_h$: + +@f[ + a_h(u_h,v_h) + b_h(u_h,v_h)=l(v_h) \qquad \forall v_h \in V_h +@f] +where +@f[ +a_h(u,v_h)=\sum_{T \in \mathbb{T}_h} \Bigl( (-u,b \cdot \nabla v_h) _T + (c u,v_h)_T \Bigr) +@f] + +@f[ +b_h(u,v_h)= \sum_{F \not \in \partial_{-}\Omega} \bigl< \{ bu\}, [v_h]\bigr>_F +@f] + +@f[ + l(v_h)= (f,v_h)_{\Omega} - \sum_{F \in \partial_{-}\Omega} \bigl< (b \cdot n) g,v_h \bigr>_F +@f] + +It's well known this formulation is coercive only in $L^2$, hence the formulation is unstable as we don't "see" the derivatives. To stabilize this, we can use a jump-penalty term, i.e. our $b_h$ is replaced by: + +@f[ +b_h^s(u_h,v_h)=b_h(u_h,v_h)+ \sum_{F \in \mathbb{F}^i} \bigl< c_F +[u_h],[v_h] \bigr> +@f] + +where $c_F>0$ is a function on each edge such that $c_F \geq \theta |b \cdot n|$ for some positive $\theta$. In this program, $\theta=\frac{1}{2}$ and $c_F = \frac{1}{2} |b \cdot n|$, which corresponds to an upwind formulation. Notice that consistency is trivially achieved, as $[u]_{\mathbb{F}^i} =0$. This formulation is stable in the energy norm + +@f[ + |||\cdot ||| = \Bigl(||\cdot||_{0,\Omega}^2 + \sum_{F \in + \mathbb{F}}||c_F^{\frac{1}{2}}[\cdot] ||_{0,F}^2 + \Bigr)^{\frac{1}{2}} +@f] + +(well defined on $H^1(\Omega) + V_h$) and moreover we have the a-priori bound: + +@f[ +|||u-u_h||| \leq C h^{k+\frac{1}{2}}||u||_{k+1,\Omega} +@f] + +valid for $u \in H^{k+1}(\Omega)$. + +See Brezzi-Marini-Süli [3] for more details. + +## A-posteriori error estimator: + +The estimator is the one proposed by Georgoulis, Edward Hall and Charalambos Makridakis in [3]. This approach is quite different with respect to other works in the field, as the authors are trying to develop an estimator for the original hyperbolic problem, rather than taking the hyperbolic regime as the vanishing diffusivity limit. + +The reliability is: + +@f[ +|||u-u_h|||^2 \leq C || \sqrt{b \cdot n}[u_h]||_{\Gamma^{-}}^2 + C +\sum_{T \in \mathbb{T}_h}\Bigl( ||\beta (g-u_h^+)||_{\partial_{-}T +\cap \partial_{-} \Omega}^2 +||f-c u_h - \Pi(f- cu_h)||_T^2 \Bigr) +@f] + +where: + +- $\Pi$ is the (local) $L^2$ orthogonal projection onto $V_h$ + +- $\Gamma$ is the skeleton of the mesh + +- $c$ is constant + +- $\beta = |b \cdot n|$ + +- $u_h^+$ is the interior trace from the current cell $T$ of a the finite element function $u_h$. + +## Test case: + +The following test case has been taken from [3]. Consider: +- $c=1$ +- $b=(1,1)$ +- $f$ to be such that the exact solution is $u(x,y)=\tanh(100(x+y-\frac{1}{2}))$ +This solution has an internal layer along the line $y=\frac{1}{2} -x$, hence we would like to see that part of the domain to be much more refined than the rest. + +The next image is the 3D view of the numerical solution: + +![Screenshot](./doc/images/warp_by_scalar_solution_layer.png) + +More interestingly, we see that the estimator has been able to capture the layer. Here a bulk-chasing criterion is used, with bottom fraction ´0.5´ and no coarsening. This mesh is obtained after 12 refinement cycles. +![Screenshot](./doc/images/refined_mesh_internal_layer.png) + +If we look at the decrease of the energy norm of the error in the globally refined case and in the adaptively case, with respect to the DoFs, we obtain: + +![Screenshot](./doc/images/adaptive_vs_global_refinement.png) + +## References +* [1] Emmanuil H. Georgoulis, Edward Hall and Charalambos Makridakis (2013), Error Control for Discontinuous Galerkin Methods for First Order Hyperbolic Problems. DOI: [10.1007/978-3-319-01818-8_8 +](https://link.springer.com/chapter/10.1007%2F978-3-319-01818-8_8) +* [2] Di Pietro, Daniele Antonio and Ern, Alexandre (2012), Mathematical Aspects of Discontinuous Galerkin Methods. ISBN: [978-3-642-22980-0](https://www.springer.com/gp/book/9783642229794) +* [3] Franco Brezzi, Luisa Donatella Marini and Endre Süli (2004) Discontinuous Galerkin Methods for First-Order Hyperbolic Problems. DOI: [10.1142/S0218202504003866](https://doi.org/10.1142/S0218202504003866) diff --git a/agglomeration_poisson/agglomeration_poisson.cc b/agglomeration_poisson/agglomeration_poisson.cc new file mode 100644 index 00000000..2cabee53 --- /dev/null +++ b/agglomeration_poisson/agglomeration_poisson.cc @@ -0,0 +1,950 @@ +/* ----------------------------------------------------------------------------- + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright (C) 2025 by Marco Feder, Pasquale Claudio Africa, Xinping Gui, + * Andrea Cangiani + * + * This file is part of the deal.II code gallery. + * + * ----------------------------------------------------------------------------- + */ + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +struct ConvergenceInfo +{ + ConvergenceInfo() = default; + void + add(const std::pair> + &dofs_and_errs) + { + vec_data.push_back(dofs_and_errs); + } + + void + print() + { + Assert(vec_data.size() > 0, ExcInternalError()); + for (const auto &dof_and_errs : vec_data) + std::cout << std::left << std::setw(24) << std::scientific + << "N DoFs: " << dof_and_errs.first << std::endl; + + for (const auto &dof_and_errs : vec_data) + std::cout << std::left << std::setw(24) << std::scientific + << "L2 error: " << dof_and_errs.second.first << std::endl; + for (const auto &dof_and_errs : vec_data) + std::cout << std::left << std::setw(24) << std::scientific + << "H1 error: " << dof_and_errs.second.second << std::endl; + } + + std::vector>> + vec_data; +}; + +enum class GridType +{ + grid_generator, // hyper_cube or hyper_ball + unstructured // square generated with gmsh, unstructured +}; + +enum class PartitionerType +{ + metis, + rtree, + no_partition +}; + +enum SolutionType +{ + linear, // x+y-1 + quadratic, // x^2+y^2-1 + product, // xy(x-1)(y-1) + product_sine // sin(pi*x)*sin(pi*y) +}; + +template +class RightHandSide : public Function +{ +public: + RightHandSide(const SolutionType &sol_type = SolutionType::linear) + : Function() + { + solution_type = sol_type; + } + + virtual void + value_list(const std::vector> &points, + std::vector &values, + const unsigned int /*component*/) const override; + +private: + SolutionType solution_type; +}; + +template +void +RightHandSide::value_list(const std::vector> &points, + std::vector &values, + const unsigned int /*component*/) const +{ + if (solution_type == SolutionType::linear) + { + for (unsigned int i = 0; i < values.size(); ++i) + values[i] = 0.; // Laplacian of linear function + } + else if (solution_type == SolutionType::quadratic) + { + for (unsigned int i = 0; i < values.size(); ++i) + values[i] = -4.; // quadratic (radial) solution + } + else if (solution_type == SolutionType::product) + { + for (unsigned int i = 0; i < values.size(); ++i) + values[i] = -2. * points[i][0] * (points[i][0] - 1.) - + 2. * points[i][1] * (points[i][1] - 1.); + } + else if (solution_type == SolutionType::product_sine) + { + // 2pi^2*sin(pi*x)*sin(pi*y) + for (unsigned int i = 0; i < values.size(); ++i) + values[i] = 2. * numbers::PI * numbers::PI * + std::sin(numbers::PI * points[i][0]) * + std::sin(numbers::PI * points[i][1]); + } + else + { + Assert(false, ExcNotImplemented()); + } +} + +template +class SolutionLinear : public Function +{ +public: + SolutionLinear() + : Function() + {} + + virtual double + value(const Point &p, const unsigned int component = 0) const override; + + virtual void + value_list(const std::vector> &points, + std::vector &values, + const unsigned int /*component*/) const override; + + virtual Tensor<1, dim> + gradient(const Point &p, + const unsigned int component = 0) const override; +}; + +template +double +SolutionLinear::value(const Point &p, const unsigned int) const +{ + double sum = 0; + for (unsigned int d = 0; d < dim; ++d) + sum += p[d]; + + return sum - 1; // p[0]+p[1]+p[2]-1 +} + +template +Tensor<1, dim> +SolutionLinear::gradient(const Point &p, const unsigned int) const +{ + (void)p; + Tensor<1, dim> return_value; + for (unsigned int d = 0; d < dim; ++d) + return_value[d] = 0.; + return return_value; +} + +template +void +SolutionLinear::value_list(const std::vector> &points, + std::vector &values, + const unsigned int /*component*/) const +{ + for (unsigned int i = 0; i < values.size(); ++i) + values[i] = this->value(points[i]); +} + +template +class SolutionQuadratic : public Function +{ +public: + SolutionQuadratic() + : Function() + { + Assert(dim == 2, ExcNotImplemented()); + } + + virtual double + value(const Point &p, const unsigned int component = 0) const override; + + virtual void + value_list(const std::vector> &points, + std::vector &values, + const unsigned int /*component*/) const override; + + virtual Tensor<1, dim> + gradient(const Point &p, + const unsigned int component = 0) const override; +}; + +template +double +SolutionQuadratic::value(const Point &p, const unsigned int) const +{ + return p[0] * p[0] + p[1] * p[1] - 1; // ball, radial solution +} + +template +Tensor<1, dim> +SolutionQuadratic::gradient(const Point &p, const unsigned int) const +{ + Tensor<1, dim> return_value; + return_value[0] = 2. * p[0]; + return_value[1] = 2. * p[1]; + return return_value; +} + +template +void +SolutionQuadratic::value_list(const std::vector> &points, + std::vector &values, + const unsigned int /*component*/) const +{ + for (unsigned int i = 0; i < values.size(); ++i) + values[i] = this->value(points[i]); +} + +template +class SolutionProduct : public Function +{ +public: + SolutionProduct() + : Function() + { + Assert(dim == 2, ExcNotImplemented()); + } + + virtual double + value(const Point &p, const unsigned int component = 0) const override; + + virtual void + value_list(const std::vector> &points, + std::vector &values, + const unsigned int /*component*/) const override; + + virtual Tensor<1, dim> + gradient(const Point &p, + const unsigned int component = 0) const override; + + virtual void + gradient_list(const std::vector> &points, + std::vector> &gradients, + const unsigned int /*component*/) const override; +}; + +template +double +SolutionProduct::value(const Point &p, const unsigned int) const +{ + return p[0] * (p[0] - 1.) * p[1] * (p[1] - 1.); // square +} + +template +Tensor<1, dim> +SolutionProduct::gradient(const Point &p, const unsigned int) const +{ + Tensor<1, dim> return_value; + return_value[0] = (-1 + 2 * p[0]) * (-1 + p[1]) * p[1]; + return_value[1] = (-1 + 2 * p[1]) * (-1 + p[0]) * p[0]; + return return_value; +} + +template +void +SolutionProduct::value_list(const std::vector> &points, + std::vector &values, + const unsigned int /*component*/) const +{ + for (unsigned int i = 0; i < values.size(); ++i) + values[i] = this->value(points[i]); +} + +template +void +SolutionProduct::gradient_list(const std::vector> &points, + std::vector> &gradients, + const unsigned int /*component*/) const +{ + for (unsigned int i = 0; i < gradients.size(); ++i) + gradients[i] = this->gradient(points[i]); +} + +template +class SolutionProductSine : public Function +{ +public: + SolutionProductSine() + : Function() + { + Assert(dim == 2, ExcNotImplemented()); + } + + virtual double + value(const Point &p, const unsigned int component = 0) const override; + + virtual void + value_list(const std::vector> &points, + std::vector &values, + const unsigned int /*component*/) const override; + + virtual Tensor<1, dim> + gradient(const Point &p, + const unsigned int component = 0) const override; +}; + +template +double +SolutionProductSine::value(const Point &p, const unsigned int) const +{ + return std::sin(numbers::PI * p[0]) * std::sin(numbers::PI * p[1]); +} + +template +Tensor<1, dim> +SolutionProductSine::gradient(const Point &p, + const unsigned int) const +{ + Tensor<1, dim> return_value; + return_value[0] = + numbers::PI * std::cos(numbers::PI * p[0]) * std::sin(numbers::PI * p[1]); + return_value[1] = + numbers::PI * std::cos(numbers::PI * p[1]) * std::sin(numbers::PI * p[0]); + return return_value; +} + +template +void +SolutionProductSine::value_list(const std::vector> &points, + std::vector &values, + const unsigned int /*component*/) const +{ + for (unsigned int i = 0; i < values.size(); ++i) + values[i] = this->value(points[i]); +} + +template +class Poisson +{ +private: + void + make_grid(); + void + setup_agglomeration(); + void + assemble_system(); + void + solve(); + void + output_results(); + + Triangulation tria; + MappingQ1 mapping; + FE_DGQ dg_fe; + std::unique_ptr> ah; + AffineConstraints constraints; + SparsityPattern sparsity; + DynamicSparsityPattern dsp; + SparseMatrix system_matrix; + Vector solution; + Vector system_rhs; + std::unique_ptr> cached_tria; + std::unique_ptr> rhs_function; + std::unique_ptr> analytical_solution; + +public: + Poisson(const GridType &grid_type = GridType::grid_generator, + const PartitionerType &partitioner_type = PartitionerType::rtree, + const SolutionType &solution_type = SolutionType::linear, + const unsigned int = 0, + const unsigned int = 0, + const unsigned int fe_degree = 1); + void + run(); + + types::global_dof_index + get_n_dofs() const; + + std::pair + get_error() const; + + GridType grid_type; + PartitionerType partitioner_type; + SolutionType solution_type; + unsigned int extraction_level; + unsigned int n_subdomains; + double penalty_constant = 60.; // 10*(p+1)(p+d) for p = 1 and d = 2 => 60 + double l2_err; + double semih1_err; +}; + +template +Poisson::Poisson(const GridType &grid_type, + const PartitionerType &partitioner_type, + const SolutionType &solution_type, + const unsigned int extraction_level, + const unsigned int n_subdomains, + const unsigned int fe_degree) + : mapping() + , dg_fe(fe_degree) + , grid_type(grid_type) + , partitioner_type(partitioner_type) + , solution_type(solution_type) + , extraction_level(extraction_level) + , n_subdomains(n_subdomains) + , penalty_constant(10. * (fe_degree + 1) * (fe_degree + dim)) +{ + // Initialize manufactured solution + if (solution_type == SolutionType::linear) + analytical_solution = std::make_unique>(); + else if (solution_type == SolutionType::quadratic) + analytical_solution = std::make_unique>(); + else if (solution_type == SolutionType::product) + analytical_solution = std::make_unique>(); + else if (solution_type == SolutionType::product_sine) + analytical_solution = std::make_unique>(); + + rhs_function = std::make_unique>(solution_type); + constraints.close(); +} + +template +void +Poisson::make_grid() +{ + GridIn grid_in; + if (grid_type == GridType::unstructured) + { + if constexpr (dim == 2) + { + grid_in.attach_triangulation(tria); + std::ifstream gmsh_file( + std::string(SOURCE_DIR) + + "/meshes/t3.msh"); // unstructured square made by triangles + grid_in.read_msh(gmsh_file); + tria.refine_global(2); // 4 + } + else if constexpr (dim == 3) + { + // We avoid to import large 3D meshes, and we just distort a unit cube + GridGenerator::hyper_cube(tria, 0., 1.); + tria.refine_global(5); + GridTools::distort_random(0.1, tria); + } + } + else + { + // We avoid to import large 3D meshes, and we just distort a unit cube + GridGenerator::hyper_cube(tria, 0., 1.); + tria.refine_global(5); + } + + std::cout << "Size of tria: " << tria.n_active_cells() << std::endl; + cached_tria = std::make_unique>(tria, mapping); + ah = std::make_unique>(*cached_tria); + + if (partitioner_type == PartitionerType::metis) + { + // Partition the triangulation with graph partitioner. + auto start = std::chrono::system_clock::now(); + GridTools::partition_triangulation(n_subdomains, + tria, + SparsityTools::Partitioner::metis); + + std::vector< + std::vector::active_cell_iterator>> + cells_per_subdomain(n_subdomains); + for (const auto &cell : tria.active_cell_iterators()) + cells_per_subdomain[cell->subdomain_id()].push_back(cell); + + // For every subdomain, agglomerate elements together + for (std::size_t i = 0; i < n_subdomains; ++i) + ah->define_agglomerate(cells_per_subdomain[i]); + + std::chrono::duration wctduration = + (std::chrono::system_clock::now() - start); + std::cout << "METIS built in " << wctduration.count() + << " seconds [Wall Clock]" << std::endl; + } + else if (partitioner_type == PartitionerType::rtree) + { + // Partition with Rtree + + namespace bgi = boost::geometry::index; + static constexpr unsigned int max_elem_per_node = + PolyUtils::constexpr_pow(2, dim); // 2^dim + std::vector, + typename Triangulation::active_cell_iterator>> + boxes(tria.n_active_cells()); + unsigned int i = 0; + for (const auto &cell : tria.active_cell_iterators()) + boxes[i++] = std::make_pair(mapping.get_bounding_box(cell), cell); + + auto start = std::chrono::system_clock::now(); + auto tree = pack_rtree>(boxes); + + CellsAgglomerator agglomerator{tree, + extraction_level}; + const auto vec_agglomerates = agglomerator.extract_agglomerates(); + + // Flag elements for agglomeration + for (const auto &agglo : vec_agglomerates) + ah->define_agglomerate(agglo); + + std::chrono::duration wctduration = + (std::chrono::system_clock::now() - start); + std::cout << "R-tree agglomerates built in " << wctduration.count() + << " seconds [Wall Clock]" << std::endl; + } + else if (partitioner_type == PartitionerType::no_partition) + { + } + else + { + Assert(false, ExcMessage("Wrong partitioning.")); + } + n_subdomains = ah->n_agglomerates(); + std::cout << "N subdomains = " << n_subdomains << std::endl; +} + +template +void +Poisson::setup_agglomeration() +{ + if (partitioner_type == PartitionerType::no_partition) + { + // No partitioning means that each cell is a master cell + for (const auto &cell : tria.active_cell_iterators()) + ah->define_agglomerate({cell}); + } + + ah->distribute_agglomerated_dofs(dg_fe); + ah->create_agglomeration_sparsity_pattern(dsp); + sparsity.copy_from(dsp); + + { + std::string partitioner; + if (partitioner_type == PartitionerType::metis) + partitioner = "metis"; + else if (partitioner_type == PartitionerType::rtree) + partitioner = "rtree"; + else + partitioner = "no_partitioning"; + + const std::string filename = + "grid" + partitioner + "_" + std::to_string(n_subdomains) + ".vtu"; + std::ofstream output(filename); + + DataOut data_out; + data_out.attach_dof_handler(ah->agglo_dh); + + Vector agglomerated(tria.n_active_cells()); + Vector agglo_idx(tria.n_active_cells()); + for (const auto &cell : tria.active_cell_iterators()) + { + agglomerated[cell->active_cell_index()] = + ah->get_relationships()[cell->active_cell_index()]; + agglo_idx[cell->active_cell_index()] = cell->subdomain_id(); + } + data_out.add_data_vector(agglomerated, + "agglo_relationships", + DataOut::type_cell_data); + data_out.add_data_vector(agglo_idx, + "agglomerated_idx", + DataOut::type_cell_data); + data_out.build_patches(mapping); + data_out.write_vtu(output); + } +} + +template +void +Poisson::assemble_system() +{ + system_matrix.reinit(sparsity); + solution.reinit(ah->n_dofs()); + system_rhs.reinit(ah->n_dofs()); + + const unsigned int quadrature_degree = dg_fe.get_degree() + 1; + const unsigned int face_quadrature_degree = dg_fe.get_degree() + 1; + + ah->initialize_fe_values(QGauss(quadrature_degree), + update_gradients | update_JxW_values | + update_quadrature_points | update_JxW_values | + update_values, + QGauss(face_quadrature_degree)); + + const unsigned int dofs_per_cell = ah->n_dofs_per_cell(); + std::cout << "DoFs per cell: " << dofs_per_cell << std::endl; + + FullMatrix cell_matrix(dofs_per_cell, dofs_per_cell); + Vector cell_rhs(dofs_per_cell); + + // Next, we define the four dofsxdofs matrices needed to assemble jumps and + // averages. + FullMatrix M11(dofs_per_cell, dofs_per_cell); + FullMatrix M12(dofs_per_cell, dofs_per_cell); + FullMatrix M21(dofs_per_cell, dofs_per_cell); + FullMatrix M22(dofs_per_cell, dofs_per_cell); + + std::vector local_dof_indices(dofs_per_cell); + + for (const auto &polytope : ah->polytope_iterators()) + { + cell_matrix = 0; + cell_rhs = 0; + const auto &agglo_values = ah->reinit(polytope); + polytope->get_dof_indices(local_dof_indices); + + const auto &q_points = agglo_values.get_quadrature_points(); + const unsigned int n_qpoints = q_points.size(); + std::vector rhs(n_qpoints); + rhs_function->value_list(q_points, rhs); + + for (unsigned int q_index : agglo_values.quadrature_point_indices()) + { + for (unsigned int i = 0; i < dofs_per_cell; ++i) + { + for (unsigned int j = 0; j < dofs_per_cell; ++j) + { + cell_matrix(i, j) += agglo_values.shape_grad(i, q_index) * + agglo_values.shape_grad(j, q_index) * + agglo_values.JxW(q_index); + } + cell_rhs(i) += agglo_values.shape_value(i, q_index) * + rhs[q_index] * agglo_values.JxW(q_index); + } + } + + // Face terms + const unsigned int n_faces = polytope->n_faces(); + AssertThrow(n_faces > 0, + ExcMessage( + "Invalid element: at least 4 faces are required.")); + + auto polygon_boundary_vertices = polytope->polytope_boundary(); + for (unsigned int f = 0; f < n_faces; ++f) + { + if (polytope->at_boundary(f)) + { + // std::cout << "at boundary!" << std::endl; + const auto &fe_face = ah->reinit(polytope, f); + + const unsigned int dofs_per_cell = fe_face.dofs_per_cell; + + const auto &face_q_points = fe_face.get_quadrature_points(); + std::vector analytical_solution_values( + face_q_points.size()); + analytical_solution->value_list(face_q_points, + analytical_solution_values, + 1); + + // Get normal vectors seen from each agglomeration. + const auto &normals = fe_face.get_normal_vectors(); + + const double penalty = + penalty_constant / std::fabs(polytope->diameter()); + + for (unsigned int q_index : fe_face.quadrature_point_indices()) + { + for (unsigned int i = 0; i < dofs_per_cell; ++i) + { + for (unsigned int j = 0; j < dofs_per_cell; ++j) + { + cell_matrix(i, j) += + (-fe_face.shape_value(i, q_index) * + fe_face.shape_grad(j, q_index) * + normals[q_index] - + fe_face.shape_grad(i, q_index) * normals[q_index] * + fe_face.shape_value(j, q_index) + + (penalty)*fe_face.shape_value(i, q_index) * + fe_face.shape_value(j, q_index)) * + fe_face.JxW(q_index); + } + cell_rhs(i) += + (penalty * analytical_solution_values[q_index] * + fe_face.shape_value(i, q_index) - + fe_face.shape_grad(i, q_index) * normals[q_index] * + analytical_solution_values[q_index]) * + fe_face.JxW(q_index); + } + } + } + else + { + const auto &neigh_polytope = polytope->neighbor(f); + + // This is necessary to loop over internal faces only once. + if (polytope->index() < neigh_polytope->index()) + { + unsigned int nofn = + polytope->neighbor_of_agglomerated_neighbor(f); + + const auto &fe_faces = + ah->reinit_interface(polytope, neigh_polytope, f, nofn); + const auto &fe_faces0 = fe_faces.first; + const auto &fe_faces1 = fe_faces.second; + + std::vector + local_dof_indices_neighbor(dofs_per_cell); + + M11 = 0.; + M12 = 0.; + M21 = 0.; + M22 = 0.; + + const auto &normals = fe_faces0.get_normal_vectors(); + + const double penalty = + penalty_constant / std::fabs(polytope->diameter()); + + // M11 + for (unsigned int q_index : + fe_faces0.quadrature_point_indices()) + { + for (unsigned int i = 0; i < dofs_per_cell; ++i) + { + for (unsigned int j = 0; j < dofs_per_cell; ++j) + { + M11(i, j) += + (-0.5 * fe_faces0.shape_grad(i, q_index) * + normals[q_index] * + fe_faces0.shape_value(j, q_index) - + 0.5 * fe_faces0.shape_grad(j, q_index) * + normals[q_index] * + fe_faces0.shape_value(i, q_index) + + (penalty)*fe_faces0.shape_value(i, q_index) * + fe_faces0.shape_value(j, q_index)) * + fe_faces0.JxW(q_index); + + M12(i, j) += + (0.5 * fe_faces0.shape_grad(i, q_index) * + normals[q_index] * + fe_faces1.shape_value(j, q_index) - + 0.5 * fe_faces1.shape_grad(j, q_index) * + normals[q_index] * + fe_faces0.shape_value(i, q_index) - + (penalty)*fe_faces0.shape_value(i, q_index) * + fe_faces1.shape_value(j, q_index)) * + fe_faces1.JxW(q_index); + + // A10 + M21(i, j) += + (-0.5 * fe_faces1.shape_grad(i, q_index) * + normals[q_index] * + fe_faces0.shape_value(j, q_index) + + 0.5 * fe_faces0.shape_grad(j, q_index) * + normals[q_index] * + fe_faces1.shape_value(i, q_index) - + (penalty)*fe_faces1.shape_value(i, q_index) * + fe_faces0.shape_value(j, q_index)) * + fe_faces1.JxW(q_index); + + // A11 + M22(i, j) += + (0.5 * fe_faces1.shape_grad(i, q_index) * + normals[q_index] * + fe_faces1.shape_value(j, q_index) + + 0.5 * fe_faces1.shape_grad(j, q_index) * + normals[q_index] * + fe_faces1.shape_value(i, q_index) + + (penalty)*fe_faces1.shape_value(i, q_index) * + fe_faces1.shape_value(j, q_index)) * + fe_faces1.JxW(q_index); + } + } + } + + neigh_polytope->get_dof_indices(local_dof_indices_neighbor); + + constraints.distribute_local_to_global(M11, + local_dof_indices, + system_matrix); + constraints.distribute_local_to_global( + M12, + local_dof_indices, + local_dof_indices_neighbor, + system_matrix); + constraints.distribute_local_to_global( + M21, + local_dof_indices_neighbor, + local_dof_indices, + system_matrix); + constraints.distribute_local_to_global( + M22, local_dof_indices_neighbor, system_matrix); + } // Loop only once trough internal faces + } + } // Loop over faces of current cell + + // distribute DoFs + constraints.distribute_local_to_global( + cell_matrix, cell_rhs, local_dof_indices, system_matrix, system_rhs); + } // Loop over cells +} + +template +void +Poisson::solve() +{ + SparseDirectUMFPACK A_direct; + A_direct.initialize(system_matrix); + A_direct.vmult(solution, system_rhs); +} + +template +void +Poisson::output_results() +{ + { + std::string partitioner; + if (partitioner_type == PartitionerType::metis) + partitioner = "metis"; + else if (partitioner_type == PartitionerType::rtree) + partitioner = "rtree"; + else + partitioner = "no_partitioning"; + + const std::string filename = "interpolated_solution" + partitioner + "_" + + std::to_string(n_subdomains) + ".vtu"; + std::ofstream output(filename); + + DataOut data_out; + Vector interpolated_solution; + PolyUtils::interpolate_to_fine_grid(*ah, + interpolated_solution, + solution, + true /*on_the_fly*/); + data_out.attach_dof_handler(ah->output_dh); + data_out.add_data_vector(interpolated_solution, + "u", + DataOut::type_dof_data); + + Vector agglo_idx(tria.n_active_cells()); + + // Mark fine cells belonging to the same agglomerate. + for (const auto &polytope : ah->polytope_iterators()) + { + const types::global_cell_index polytope_index = polytope->index(); + const auto &patch_of_cells = polytope->get_agglomerate(); // fine cells + // Flag them + for (const auto &cell : patch_of_cells) + agglo_idx[cell->active_cell_index()] = polytope_index; + } + + data_out.add_data_vector(agglo_idx, + "agglo_idx", + DataOut::type_cell_data); + + data_out.build_patches(mapping); + data_out.write_vtu(output); + + // Compute L2 and semiH1 norm of error + std::vector errors; + PolyUtils::compute_global_error(*ah, + solution, + *analytical_solution, + {VectorTools::L2_norm, + VectorTools::H1_seminorm}, + errors); + l2_err = errors[0]; + semih1_err = errors[1]; + std::cout << "Error (L2): " << l2_err << std::endl; + std::cout << "Error (H1): " << semih1_err << std::endl; + } +} + +template +inline types::global_dof_index +Poisson::get_n_dofs() const +{ + return ah->n_dofs(); +} + +template +inline std::pair +Poisson::get_error() const +{ + return std::make_pair(l2_err, semih1_err); +} + +template +void +Poisson::run() +{ + make_grid(); + setup_agglomeration(); + auto start = std::chrono::high_resolution_clock::now(); + assemble_system(); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = + std::chrono::duration_cast(stop - start); + + std::cout << "Time taken by assemble_system(): " << duration.count() / 1e6 + << " seconds" << std::endl; + solve(); + output_results(); +} + +int +main() +{ + // Testing p-convergence + ConvergenceInfo convergence_info; + std::cout << "Testing p-convergence" << std::endl; + { + for (unsigned int fe_degree : {1, 2, 3}) + + { + std::cout << "Fe degree: " << fe_degree << std::endl; + Poisson<2> poisson_problem{GridType::unstructured, + PartitionerType::rtree, + SolutionType::product_sine, + 4 /*extraction_level*/, + 256, //,364 /*0*/, + fe_degree}; + poisson_problem.run(); + convergence_info.add( + std::make_pair>( + poisson_problem.get_n_dofs(), poisson_problem.get_error())); + } + } + convergence_info.print(); + + std::cout << std::endl; + return 0; +} diff --git a/agglomeration_poisson/doc/author b/agglomeration_poisson/doc/author new file mode 100644 index 00000000..74d91324 --- /dev/null +++ b/agglomeration_poisson/doc/author @@ -0,0 +1,4 @@ +Marco Feder +Pasquale Claudio Africa +Xinping Gui +Andrea Cangiani diff --git a/agglomeration_poisson/doc/builds-on b/agglomeration_poisson/doc/builds-on new file mode 100644 index 00000000..154f8bf8 --- /dev/null +++ b/agglomeration_poisson/doc/builds-on @@ -0,0 +1 @@ +step-12 step-74 diff --git a/agglomeration_poisson/doc/entry-name b/agglomeration_poisson/doc/entry-name new file mode 100644 index 00000000..0cdacd6e --- /dev/null +++ b/agglomeration_poisson/doc/entry-name @@ -0,0 +1 @@ +An agglomeration-based solver for the Poisson problem diff --git a/agglomeration_poisson/doc/images/adaptive_vs_global_refinement.png b/agglomeration_poisson/doc/images/adaptive_vs_global_refinement.png new file mode 100644 index 00000000..70d30393 Binary files /dev/null and b/agglomeration_poisson/doc/images/adaptive_vs_global_refinement.png differ diff --git a/agglomeration_poisson/doc/images/refined_mesh_internal_layer.png b/agglomeration_poisson/doc/images/refined_mesh_internal_layer.png new file mode 100644 index 00000000..bd38f0ec Binary files /dev/null and b/agglomeration_poisson/doc/images/refined_mesh_internal_layer.png differ diff --git a/agglomeration_poisson/doc/images/warp_by_scalar_solution_layer.png b/agglomeration_poisson/doc/images/warp_by_scalar_solution_layer.png new file mode 100644 index 00000000..f0c30f3e Binary files /dev/null and b/agglomeration_poisson/doc/images/warp_by_scalar_solution_layer.png differ diff --git a/agglomeration_poisson/doc/tooltip b/agglomeration_poisson/doc/tooltip new file mode 100644 index 00000000..8f0328bc --- /dev/null +++ b/agglomeration_poisson/doc/tooltip @@ -0,0 +1 @@ +A Discontinuous Galerkin solver for the Poisson problem on general polytopal meshes generated through mesh agglomeration diff --git a/agglomeration_poisson/include/agglomeration_accessor.h b/agglomeration_poisson/include/agglomeration_accessor.h new file mode 100644 index 00000000..8eb803c8 --- /dev/null +++ b/agglomeration_poisson/include/agglomeration_accessor.h @@ -0,0 +1,794 @@ +/* ----------------------------------------------------------------------------- + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright (C) 2025 by Marco Feder, Pasquale Claudio Africa, Xinping Gui, + * Andrea Cangiani + * + * This file is part of the deal.II code gallery. + * + * ----------------------------------------------------------------------------- + */ + +#ifndef agglomeration_accessor_h +#define agglomeration_accessor_h + +#include + +#include +#include + +#include + +#include + +using namespace dealii; + + +// Forward declarations +#ifndef DOXYGEN +template +class AgglomerationHandler; +template +class AgglomerationIterator; +#endif + + +/** + * Accessor class used by AgglomerationIterator to access agglomeration data. + */ +template +class AgglomerationAccessor +{ +public: + /** + * Type for storing the polygons in an agglomerate. + */ + using AgglomerationContainer = + std::vector::active_cell_iterator>; + + + /** + * Get the DoFs indices associated to the agglomerate. + */ + void + get_dof_indices(std::vector &) const; + + /** + * Return, for a cell, the number of faces. In case the cell is a standard + * cell, then the number of faces is the classical one. If it's a master cell, + * then it returns the number of faces of the agglomeration identified by the + * master cell itself. + */ + unsigned int + n_faces() const; + + /** + * Return the number of deal.II faces that is building a polygon face. + */ + unsigned int + n_agglomerated_faces() const; + + /** + * Return the agglomerate which shares face f. + */ + const AgglomerationIterator + neighbor(const unsigned int f) const; + + /** + * Return the present index (seen from the neighboring agglomerate) of the + * present face f. + */ + unsigned int + neighbor_of_agglomerated_neighbor(const unsigned int f) const; + + /** + * + * This function generalizes the behaviour of cell->face(f)->at_boundary() + * in the case where f is an index out of the range [0,..., n_faces). + * In practice, if you call this function with a standard deal.II cell, you + * have precisely the same result as calling cell->face(f)->at_boundary(). + * Otherwise, if the cell is a master one, you have a boolean returning true + * is that face for the agglomeration is on the boundary or not. + */ + bool + at_boundary(const unsigned int f) const; + + /** + * Return a vector of face iterators describing the boundary of agglomerate. + */ + const std::vector::active_face_iterator> & + polytope_boundary() const; + + /** + * + * Return the volume of a polytope. + */ + double + volume() const; + + /** + * Return the diameter of the present polytopal element. + */ + double + diameter() const; + + /** + * Returns the deal.II cells that build the agglomerate. + */ + AgglomerationContainer + get_agglomerate() const; + + /** + * Return the BoundingBox which bounds the present polytope. In case the + * present polytope is not locally owned, it returns the BoundingBox of that + * ghosted polytope. + */ + const BoundingBox & + get_bounding_box() const; + + /** + * Return the index of the present polytope. + */ + types::global_cell_index + index() const; + + /** + * Returns an active cell iterator for the dof_handler, matching the polytope + * referenced by the input iterator. The type of the returned object is a + * DoFHandler::active_cell_iterator which can be used to initialize + * FiniteElement data. + */ + typename DoFHandler::active_cell_iterator + as_dof_handler_iterator(const DoFHandler &dof_handler) const; + + /** + * Returns the number of classical deal.II cells that are building the present + * polygon. + */ + unsigned int + n_background_cells() const; + + /* Returns true if this polygon is owned by the current processor. On a serial + * Triangulation this returs always true, but may yield false for a + * parallel::distributed::Triangulation. + */ + bool + is_locally_owned() const; + + /** + * The polytopal analogue of CellAccessor::id(). It provides a way to uniquely + * identify cells in a parallel Triangulation such as a + * parallel::distributed::Triangulation. + */ + CellId + id() const; + + /** + * The polytopal analogue of CellAccessor::subdomain_id(). In case of a serial + * Triangulation, it returns the numbers::invalid_subdomain_id. + */ + types::subdomain_id + subdomain_id() const; + + /** + * Returns a vector of indices identifying the children polytopes. + */ + inline const std::vector & + children() const; + + /** + * Returns the FiniteElement object used by the current polytope. + * This function should only be called after the corresponding agglomeration + * handler has invoked distribute_agglomerated_dofs(). + */ + const FiniteElement & + get_fe() const; + + /** + * Sets the active finite element index. + * This function should be called when using hp::FECollection to specify + * which finite element in the collection is assigned to the current polytope. + */ + void + set_active_fe_index(const types::fe_index index) const; + + /** + * Returns the index of the active finite element. + * When using hp::FECollection, this function retrieves the index + * of the finite element assigned to the current polytope. + */ + types::fe_index + active_fe_index() const; + +private: + /** + * Private default constructor. This is not supposed to be used and hence will + * throw. + */ + AgglomerationAccessor(); + + /** + * Private constructor for an agglomerate. This is meant to be invoked by + * the AgglomerationIterator class. It takes as input the master cell of the + * agglomerate and a pointer to the handler. + */ + AgglomerationAccessor( + const typename Triangulation::active_cell_iterator + &master_cell, + const AgglomerationHandler *ah); + + /** + * Same as above, but needed when the argument @p cells is a ghost cell. + */ + AgglomerationAccessor( + const typename Triangulation::active_cell_iterator &cell, + const CellId &cell_id, + const AgglomerationHandler *ah); + + /** + * Default destructor. + */ + ~AgglomerationAccessor() = default; + + + /** + * The unique deal.II cell associated to the present polytope. + */ + typename Triangulation::active_cell_iterator master_cell; + + /** + * The index of the present polytope. + */ + types::global_cell_index present_index; + + /** + * The index of the present polytope. + */ + CellId present_id; + + /** + * The rank owning of the present polytope. + */ + types::subdomain_id present_subdomain_id; + + /** + * A pointer to the Handler. + */ + AgglomerationHandler *handler; + + /** + * Comparison operator for Accessor. Two accessors are equal if they refer to + * the same polytopal element. + */ + bool + operator==(const AgglomerationAccessor &other) const; + + /** + * Compare for inequality. + */ + bool + operator!=(const AgglomerationAccessor &other) const; + + /** + * Move to the next cell in the polytopal mesh. + */ + void + next(); + + /** + * Move to the previous cell in the polytopal mesh. + */ + void + prev(); + + /** + * Returns the slaves of the present agglomeration. + */ + const AgglomerationContainer & + get_slaves() const; + + unsigned int + n_agglomerated_faces_per_cell( + const typename Triangulation::active_cell_iterator &cell) + const; + + template + friend class AgglomerationIterator; +}; + + + +template +unsigned int +AgglomerationAccessor::n_agglomerated_faces_per_cell( + const typename Triangulation::active_cell_iterator &cell) const +{ + unsigned int n_neighbors = 0; + for (const auto &f : cell->face_indices()) + { + const auto &neighboring_cell = cell->neighbor(f); + if ((cell->face(f)->at_boundary()) || + (neighboring_cell->is_active() && + !handler->are_cells_agglomerated(cell, neighboring_cell))) + { + ++n_neighbors; + } + } + return n_neighbors; +} + + + +template +unsigned int +AgglomerationAccessor::n_faces() const +{ + Assert(!handler->is_slave_cell(master_cell), + ExcMessage("You cannot pass a slave cell.")); + return handler->number_of_agglomerated_faces[present_index]; +} + + + +template +const AgglomerationIterator +AgglomerationAccessor::neighbor(const unsigned int f) const +{ + if (!at_boundary(f)) + { + if (master_cell->is_ghost()) + { + // The following path is needed when the present function is called + // from neighbor_of_neighbor() + + const unsigned int sender_rank = master_cell->subdomain_id(); + + const CellId &master_id_ghosted_neighbor = + handler->recv_ghosted_master_id.at(sender_rank) + .at(present_id) + .at(f); + + // Use the id of the master cell to uniquely identify the neighboring + // agglomerate + + return {master_cell, + master_id_ghosted_neighbor, + handler}; // dummy master? + } + + const types::global_cell_index polytope_index = + handler->master2polygon.at(master_cell->active_cell_index()); + + const auto &neigh = + handler->polytope_cache.cell_face_at_boundary.at({polytope_index, f}) + .second; + + + if (neigh->is_locally_owned()) + { + typename DoFHandler::active_cell_iterator cell_dh( + *neigh, &(handler->agglo_dh)); + return {cell_dh, handler}; + } + else + { + // Get master_id from the neighboring ghost polytope. This uniquely + // identifies the neighboring polytope among all processors. + const CellId &master_id_neighbor = + handler->polytope_cache.ghosted_master_id.at({present_id, f}); + + // Use the id of the master cell to uniquely identify the neighboring + // agglomerate + return {neigh, master_id_neighbor, handler}; + } + } + else + { + return {}; + } +} + + + +template +unsigned int +AgglomerationAccessor::neighbor_of_agglomerated_neighbor( + const unsigned int f) const +{ + // First, make sure it's not a boundary face. + if (!at_boundary(f)) + { + const auto &neigh_polytope = + neighbor(f); // returns the neighboring master and id + + AssertThrow(neigh_polytope.state() == IteratorState::valid, + ExcInternalError()); + + unsigned int n_faces_agglomerated_neighbor; + + // if it is locally owned, retrieve the number of faces + if (neigh_polytope->is_locally_owned()) + { + n_faces_agglomerated_neighbor = neigh_polytope->n_faces(); + } + else + { + // The neighboring polytope is not locally owned. We need to get the + // number of its faces from the neighboring rank. + + // First, retrieve the CellId of the neighboring polytope. + const CellId &master_id_neighbor = neigh_polytope->id(); + + // Then, get the neighboring rank + const unsigned int sender_rank = neigh_polytope->subdomain_id(); + + // From the neighboring rank, use the CellId of the neighboring + // polytope to get the number of its faces. + n_faces_agglomerated_neighbor = + handler->recv_n_faces.at(sender_rank).at(master_id_neighbor); + } + + + // Loop over all faces of neighboring agglomerate + for (unsigned int f_out = 0; f_out < n_faces_agglomerated_neighbor; + ++f_out) + { + // Check if same CellId + if (neigh_polytope->neighbor(f_out).state() == IteratorState::valid) + if (neigh_polytope->neighbor(f_out)->id() == present_id) + return f_out; + } + return numbers::invalid_unsigned_int; + } + else + { + // Face is at boundary + return numbers::invalid_unsigned_int; + } +} + +// ------------------------------ inline functions ------------------------- + +template +inline AgglomerationAccessor::AgglomerationAccessor() +{} + + + +template +inline AgglomerationAccessor::AgglomerationAccessor( + const typename Triangulation::active_cell_iterator &cell, + const AgglomerationHandler *ah) +{ + handler = const_cast *>(ah); + if (&(*handler->master_cells_container.end()) == std::addressof(cell)) + { + present_index = handler->master_cells_container.size(); + master_cell = *handler->master_cells_container.end(); + present_id = CellId(); // invalid id (TODO) + present_subdomain_id = numbers::invalid_subdomain_id; + } + else + { + present_index = handler->master2polygon.at(cell->active_cell_index()); + master_cell = cell; + present_id = master_cell->id(); + present_subdomain_id = master_cell->subdomain_id(); + } +} + + + +template +inline AgglomerationAccessor::AgglomerationAccessor( + const typename Triangulation::active_cell_iterator &neigh_cell, + const CellId &master_cell_id, + const AgglomerationHandler *ah) +{ + Assert(neigh_cell->is_ghost(), ExcInternalError()); + // neigh_cell is ghosted + + handler = const_cast *>(ah); + master_cell = neigh_cell; + present_index = numbers::invalid_unsigned_int; + // neigh_cell is ghosted, use the CellId of that agglomerate + present_id = master_cell_id; + present_subdomain_id = master_cell->subdomain_id(); +} + + + +template +inline void +AgglomerationAccessor::get_dof_indices( + std::vector &dof_indices) const +{ + Assert(dof_indices.size() > 0, + ExcMessage( + "The vector of DoFs indices must be already properly resized.")); + if (is_locally_owned()) + { + // Forward the call to the master cell + typename DoFHandler::cell_iterator master_cell_dh( + *master_cell, &(handler->agglo_dh)); + master_cell_dh->get_dof_indices(dof_indices); + } + else + { + const std::vector &recv_dof_indices = + handler->recv_ghost_dofs.at(present_subdomain_id).at(present_id); + + std::copy(recv_dof_indices.cbegin(), + recv_dof_indices.cend(), + dof_indices.begin()); + } +} + + + +template +inline typename AgglomerationAccessor::AgglomerationContainer +AgglomerationAccessor::get_agglomerate() const +{ + auto agglomeration = get_slaves(); + agglomeration.push_back(master_cell); + return agglomeration; +} + + + +template +inline const std::vector::active_face_iterator> & +AgglomerationAccessor::polytope_boundary() const +{ + return handler->polygon_boundary[master_cell]; +} + + + +template +inline double +AgglomerationAccessor::diameter() const +{ + Assert(!handler->is_slave_cell(master_cell), + ExcMessage("The present function cannot be called for slave cells.")); + + if (handler->is_master_cell(master_cell)) + { + // Get the bounding box associated with the master cell + const auto &bdary_pts = + handler->bboxes[present_index].get_boundary_points(); + return (bdary_pts.second - bdary_pts.first).norm(); + } + else + { + // Standard deal.II way to get the measure of a cell. + return master_cell->diameter(); + } +} + + + +template +inline const BoundingBox & +AgglomerationAccessor::get_bounding_box() const +{ + if (is_locally_owned()) + return handler->bboxes[present_index]; + else + return handler->recv_ghosted_bbox.at(present_subdomain_id).at(present_id); +} + + + +template +inline double +AgglomerationAccessor::volume() const +{ + Assert(!handler->is_slave_cell(master_cell), + ExcMessage("The present function cannot be called for slave cells.")); + + if (handler->is_master_cell(master_cell)) + { + return handler->bboxes[present_index].volume(); + } + else + { + return master_cell->measure(); + } +} + + + +template +inline void +AgglomerationAccessor::next() +{ + // Increment the present index and update the polytope + ++present_index; + + // Make sure not to query the CellId if it's past the last + if (present_index < handler->master_cells_container.size()) + { + master_cell = handler->master_cells_container[present_index]; + present_id = master_cell->id(); + present_subdomain_id = master_cell->subdomain_id(); + } +} + + + +template +inline void +AgglomerationAccessor::prev() +{ + // Decrement the present index and update the polytope + --present_index; + master_cell = handler->master_cells_container[present_index]; + present_id = master_cell->id(); +} + + +template +inline bool +AgglomerationAccessor::operator==( + const AgglomerationAccessor &other) const +{ + return present_index == other.present_index; +} + +template +inline bool +AgglomerationAccessor::operator!=( + const AgglomerationAccessor &other) const +{ + return !(*this == other); +} + + + +template +inline types::global_cell_index +AgglomerationAccessor::index() const +{ + return present_index; +} + + + +template +typename DoFHandler::active_cell_iterator +AgglomerationAccessor::as_dof_handler_iterator( + const DoFHandler &dof_handler) const +{ + // Forward the call to the master cell using the right DoFHandler. + return master_cell->as_dof_handler_iterator(dof_handler); +} + + + +template +inline const typename AgglomerationAccessor::AgglomerationContainer & +AgglomerationAccessor::get_slaves() const +{ + return handler->master2slaves.at(master_cell->active_cell_index()); +} + + + +template +inline unsigned int +AgglomerationAccessor::n_background_cells() const +{ + AssertThrow(get_agglomerate().size() > 0, ExcMessage("Empty agglomeration.")); + return get_agglomerate().size(); +} + + + +template +unsigned int +AgglomerationAccessor::n_agglomerated_faces() const +{ + const auto &agglomeration = get_agglomerate(); + unsigned int n_neighbors = 0; + for (const auto &cell : agglomeration) + n_neighbors += n_agglomerated_faces_per_cell(cell); + return n_neighbors; +} + + + +template +inline bool +AgglomerationAccessor::at_boundary(const unsigned int f) const +{ + if (master_cell->is_ghost()) + { + const unsigned int sender_rank = master_cell->subdomain_id(); + return handler->recv_bdary_info.at(sender_rank).at(present_id).at(f); + } + else + { + Assert(!handler->is_slave_cell(master_cell), + ExcMessage( + "This function should not be called for a slave cell.")); + + + typename DoFHandler::active_cell_iterator cell_dh( + *master_cell, &(handler->agglo_dh)); + return handler->at_boundary(cell_dh, f); + } +} + + + +template +inline bool +AgglomerationAccessor::is_locally_owned() const +{ + return master_cell->is_locally_owned(); +} + + + +template +inline CellId +AgglomerationAccessor::id() const +{ + return present_id; +} + + + +template +inline types::subdomain_id +AgglomerationAccessor::subdomain_id() const +{ + return present_subdomain_id; +} + +template +inline const std::vector & +AgglomerationAccessor::children() const +{ + Assert(!handler->parent_child_info.empty(), ExcInternalError()); + return handler->parent_child_info.at( + {present_index, handler->present_extraction_level}); +} + +template +inline const FiniteElement & +AgglomerationAccessor::get_fe() const +{ + typename DoFHandler::active_cell_iterator + master_cell_as_dof_handler_iterator = + master_cell->as_dof_handler_iterator(handler->agglo_dh); + return master_cell_as_dof_handler_iterator->get_fe(); +} + +template +inline void +AgglomerationAccessor::set_active_fe_index( + const types::fe_index index) const +{ + Assert(!handler->is_slave_cell(master_cell), + ExcMessage("The present function cannot be called for slave cells.")); + typename DoFHandler::active_cell_iterator + master_cell_as_dof_handler_iterator = + master_cell->as_dof_handler_iterator(handler->agglo_dh); + master_cell_as_dof_handler_iterator->set_active_fe_index(index); +} + +template +inline types::fe_index +AgglomerationAccessor::active_fe_index() const +{ + typename DoFHandler::active_cell_iterator + master_cell_as_dof_handler_iterator = + master_cell->as_dof_handler_iterator(handler->agglo_dh); + return master_cell_as_dof_handler_iterator->active_fe_index(); +} + +#endif diff --git a/agglomeration_poisson/include/agglomeration_handler.h b/agglomeration_poisson/include/agglomeration_handler.h new file mode 100644 index 00000000..d580ac1e --- /dev/null +++ b/agglomeration_poisson/include/agglomeration_handler.h @@ -0,0 +1,1265 @@ +/* ----------------------------------------------------------------------------- + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright (C) 2025 by Marco Feder, Pasquale Claudio Africa, Xinping Gui, + * Andrea Cangiani + * + * This file is part of the deal.II code gallery. + * + * ----------------------------------------------------------------------------- + */ + +#ifndef agglomeration_handler_h +#define agglomeration_handler_h + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include + +using namespace dealii; + +// Forward declarations +template +class AgglomerationHandler; + +namespace dealii +{ + namespace internal + { + /** + * Helper class to reinit finite element spaces on polytopal cells. + */ + template + class AgglomerationHandlerImplementation; + } // namespace internal +} // namespace dealii + + + +/** + * Helper class for the storage of connectivity information of the polytopal + * grid. + */ +namespace dealii +{ + namespace internal + { + template + class PolytopeCache + { + public: + /** + * Default constructor. + */ + PolytopeCache() = default; + + /** + * Destructor. It simply calls clear() for all of its members. + */ + ~PolytopeCache() = default; + + void + clear() + { + // clear all the members + cell_face_at_boundary.clear(); + interface.clear(); + visited_cell_and_faces.clear(); + } + + /** + * Standard std::set for recording the standard cells and faces (in the + * deal.II lingo) that have been already visited. The first argument of + * the pair identifies the global index of a deal.II cell, while the + * second its local face number. + * + */ + mutable std::set> + visited_cell_and_faces; + + + mutable std::set> + visited_cell_and_faces_id; + + + + /** + * Map that associate the pair of (polytopal index, polytopal face) to + * (b,cell). The latter pair indicates whether or not the present face is + * on boundary. If it's on the boundary, then b is true and cell is an + * invalid cell iterator. Otherwise, b is false and cell points to the + * neighboring polytopal cell. + * + */ + mutable std::map< + std::pair, + std::pair::active_cell_iterator>> + cell_face_at_boundary; + + /** + * Map that associate the **local** pair of (polytope id, polytopal face) + * to the master id of the neighboring **ghosted** cell. + */ + mutable std::map, CellId> + ghosted_master_id; + + /** + * Standard std::map that associated to a pair of neighboring polytopic + * cells (current_polytope, neighboring_polytope) a sequence of + * ({deal_cell,deal_face_index}) which is meant to describe their + * interface. + * Indeed, the pair is identified by the two polytopic global indices, + * while the interface is described by a std::vector of deal.II cells and + * faces. + * + */ + mutable std::map< + std::pair, + std::vector< + std::pair::active_cell_iterator, + unsigned int>>> + interface; + }; + } // namespace internal +} // namespace dealii + + +/** + * + */ +template +class AgglomerationHandler : public Subscriptor +{ +public: + using agglomeration_iterator = AgglomerationIterator; + + using AgglomerationContainer = + typename AgglomerationIterator::AgglomerationContainer; + + + enum CellAgglomerationType + { + master = 0, + slave = 1 + }; + + + + explicit AgglomerationHandler( + const GridTools::Cache &cached_tria); + + AgglomerationHandler() = default; + + ~AgglomerationHandler() + { + // disconnect the signal + tria_listener.disconnect(); + } + + /** + * Iterator to the first polytope. + */ + agglomeration_iterator + begin() const; + + /** + * Iterator to the first polytope. + */ + agglomeration_iterator + begin(); + + /** + * Iterator to one past the last polygonal element. + */ + agglomeration_iterator + end() const; + + /** + * Iterator to one past the last polygonal element. + */ + agglomeration_iterator + end(); + + /** + * Iterator to the last polygonal element. + */ + agglomeration_iterator + last(); + + /** + * Returns an IteratorRange that makes up all the polygonal elements in the + * mesh. + */ + IteratorRange + polytope_iterators() const; + + template + friend class AgglomerationIterator; + + template + friend class AgglomerationAccessor; + + /** + * Distribute degrees of freedom on a grid where some cells have been + * agglomerated. + */ + void + distribute_agglomerated_dofs(const FiniteElement &fe_space); + + /** + * Overload for hp::FECollection. + */ + void + distribute_agglomerated_dofs( + const hp::FECollection &fe_collection_in); + + /** + * + * Set the degree of the quadrature formula to be used and the proper flags + * for the FEValues object on the agglomerated cell. + */ + void + initialize_fe_values( + const Quadrature &cell_quadrature = QGauss(1), + const UpdateFlags &flags = UpdateFlags::update_default, + const Quadrature &face_quadrature = QGauss(1), + const UpdateFlags &face_flags = UpdateFlags::update_default); + + /** + * Overload for hp::FECollection. + */ + void + initialize_fe_values( + const hp::QCollection &cell_qcollection = + hp::QCollection(QGauss(1)), + const UpdateFlags &flags = UpdateFlags::update_default, + const hp::QCollection &face_qcollection = + hp::QCollection(QGauss(1)), + const UpdateFlags &face_flags = UpdateFlags::update_default); + + /** + * Given a Triangulation with some agglomerated cells, create the sparsity + * pattern corresponding to a Discontinuous Galerkin discretization where the + * agglomerated cells are seen as one **unique** cell, with only the DoFs + * associated to the master cell of the agglomeration. + */ + template + void + create_agglomeration_sparsity_pattern( + SparsityPatternType &sparsity_pattern, + const AffineConstraints &constraints = AffineConstraints(), + const bool keep_constrained_dofs = true, + const types::subdomain_id subdomain_id = numbers::invalid_subdomain_id); + + /** + * Store internally that the given cells are agglomerated. The convenction we + * take is the following: + * -1: cell is a master cell + * + * @note cells are assumed to be adjacent one to each other, and no check + * about this is done. + */ + agglomeration_iterator + define_agglomerate(const AgglomerationContainer &cells); + + /** + * Overload for hp::FECollection. + * + * The parameter @p fecollection_size provides the number of finite elements + * in the collection, allowing Polydeal to insert an empty element for + * slave cells internally. + * + * When @p fecollection_size equals 1, this function behaves identically to + * define_agglomerate(const AgglomerationContainer &cells). + */ + agglomeration_iterator + define_agglomerate(const AgglomerationContainer &cells, + const unsigned int fecollection_size); + + + inline const Triangulation & + get_triangulation() const; + + inline const FiniteElement & + get_fe() const; + + inline const Mapping & + get_mapping() const; + + inline const MappingBox & + get_agglomeration_mapping() const; + + inline const std::vector> & + get_local_bboxes() const; + + /** + * Return the mesh size of the polytopal mesh. It simply takes the maximum + * diameter over all the polytopes. + */ + double + get_mesh_size() const; + + inline types::global_cell_index + cell_to_polytope_index( + const typename Triangulation::active_cell_iterator &cell) + const; + + inline decltype(auto) + get_interface() const; + + /** + * Helper function to determine whether or not a cell is a master or a slave + */ + template + inline bool + is_master_cell(const CellIterator &cell) const; + + /** + * Find (if any) the cells that have the given master index. Note that `idx` + * is as it can be equal to -1 (meaning that the cell is a master one). + */ + inline const std::vector< + typename Triangulation::active_cell_iterator> & + get_slaves_of_idx(types::global_cell_index idx) const; + + + inline const LinearAlgebra::distributed::Vector & + get_relationships() const; + + /** + * + * @param master_cell + * @return std::vector< + * typename Triangulation::active_cell_iterator> + */ + inline std::vector< + typename Triangulation::active_cell_iterator> + get_agglomerate( + const typename Triangulation::active_cell_iterator + &master_cell) const; + + /** + * Display the indices of the vector identifying which cell is agglomerated + * with which master. + */ + template + void + print_agglomeration(StreamType &out) + { + for (const auto &cell : tria->active_cell_iterators()) + out << "Cell with index: " << cell->active_cell_index() + << " has associated value: " + << master_slave_relationships[cell->global_active_cell_index()] + << std::endl; + } + + /** + * + * Return a constant reference to the DoFHandler underlying the + * agglomeration. It knows which cell have been agglomerated, and which FE + * spaces are present on each cell of the triangulation. + */ + inline const DoFHandler & + get_dof_handler() const; + + /** + * Returns the number of agglomerate cells in the grid. + */ + unsigned int + n_agglomerates() const; + + /** + * Return the number of agglomerated faces for a generic deal.II cell. + */ + unsigned int + n_agglomerated_faces_per_cell( + const typename Triangulation::active_cell_iterator &cell) + const; + + /** + * Construct a finite element space on the agglomeration. + */ + const FEValues & + reinit(const AgglomerationIterator &polytope) const; + + /** + * For a given polytope and face index, initialize shape functions, normals + * and quadratures rules to integrate there. + */ + const FEValuesBase & + reinit(const AgglomerationIterator &polytope, + const unsigned int face_index) const; + + /** + * + * Return a pair of FEValuesBase object reinited from the two sides of the + * agglomeration. + */ + std::pair &, + const FEValuesBase &> + reinit_interface(const AgglomerationIterator &polytope_in, + const AgglomerationIterator &neigh_polytope, + const unsigned int local_in, + const unsigned int local_outside) const; + + /** + * Return the agglomerated quadrature for the given agglomeration. This + * amounts to loop over all cells in an agglomeration and collecting together + * all the rules. + */ + Quadrature + agglomerated_quadrature( + const AgglomerationContainer &cells, + const typename Triangulation::active_cell_iterator + &master_cell) const; + + + /** + * + * This function generalizes the behaviour of cell->face(f)->at_boundary() + * in the case where f is an index out of the range [0,..., n_faces). + * In practice, if you call this function with a standard deal.II cell, you + * have precisely the same result as calling cell->face(f)->at_boundary(). + * Otherwise, if the cell is a master one, you have a boolean returning true + * is that face for the agglomeration is on the boundary or not. + */ + inline bool + at_boundary( + const typename DoFHandler::active_cell_iterator &cell, + const unsigned int f) const; + + inline unsigned int + n_dofs_per_cell() const noexcept; + + inline types::global_dof_index + n_dofs() const noexcept; + + + + /** + * Return the collection of vertices describing the boundary of the polytope + * associated to the master cell `cell`. The return type is meant to describe + * a sequence of edges (in 2D) or faces (in 3D). + */ + inline const std::vector::active_face_iterator> & + polytope_boundary( + const typename Triangulation::active_cell_iterator &cell); + + + /** + * DoFHandler for the agglomerated space + */ + DoFHandler agglo_dh; + + /** + * DoFHandler for the finest space: classical deal.II space + */ + DoFHandler output_dh; + + std::unique_ptr> box_mapping; + + /** + * This function stores the information needed to identify which polytopes are + * ghosted w.r.t the local partition. The issue this function addresses is due + * to the fact that the layer of ghost cells is made by just one layer of + * deal.II cells. Therefore, the neighboring polytopes will always be made by + * some ghost cells and **artificial** ones. This implies that we need to + * communicate the missing information from the neighboring rank. + */ + void + setup_ghost_polytopes(); + + void + exchange_interface_values(); + + // TODO: move it to private interface + mutable std::map< + types::subdomain_id, + std::map, std::vector>>> + recv_qpoints; + + mutable std::map< + types::subdomain_id, + std::map, std::vector>> + recv_jxws; + + mutable std::map< + types::subdomain_id, + std::map, std::vector>>> + recv_normals; + + mutable std::map< + types::subdomain_id, + std::map, std::vector>>> + recv_values; + + mutable std::map, + std::vector>>>> + recv_gradients; + + /** + * Given the index of a polytopic element, return a DoFHandler iterator + * for which DoFs associated to that polytope can be queried. + */ + inline const typename DoFHandler::active_cell_iterator + polytope_to_dh_iterator(const types::global_cell_index polytope_index) const; + + /** + * + */ + template + void + connect_hierarchy(const CellsAgglomerator &agglomerator); + + /** + * Return the finite element collection passed to + * distribute_agglomerated_dofs(). + */ + inline const hp::FECollection & + get_fe_collection() const; + + /** + * Return whether a hp::FECollection is being used. + */ + inline bool + used_fe_collection() const; + +private: + /** + * Initialize connectivity informations + */ + void + initialize_agglomeration_data( + const std::unique_ptr> &cache_tria); + + void + update_agglomerate( + AgglomerationContainer &polytope, + const typename Triangulation::active_cell_iterator + &master_cell); + + /** + * Reinitialize the agglomeration data. + */ + void + connect_to_tria_signals() + { + // First disconnect existing connections + tria_listener.disconnect(); + tria_listener = tria->signals.any_change.connect( + [&]() { this->initialize_agglomeration_data(this->cached_tria); }); + } + + /** + * Helper function to determine whether or not a cell is a slave cell. + * Instead of returning a boolean, it gives the index of the master cell. If + * it's a master cell, then the it returns -1, by construction. + */ + + inline typename Triangulation::active_cell_iterator & + is_slave_cell_of( + const typename Triangulation::active_cell_iterator &cell); + + /** + * Construct bounding boxes for an agglomeration described by a sequence of + * cells. This fills also the euler vector + */ + void + create_bounding_box(const AgglomerationContainer &polytope); + + + inline types::global_cell_index + get_master_idx_of_cell( + const typename Triangulation::active_cell_iterator &cell) + const; + + /** + * Returns true if the two given cells are agglomerated together. + */ + inline bool + are_cells_agglomerated( + const typename Triangulation::active_cell_iterator &cell, + const typename Triangulation::active_cell_iterator + &other_cell) const; + + /** + * Assign a finite element index on each cell of a triangulation, depending + * if it is a master cell, a slave cell, or a standard deal.II cell. A user + * doesn't need to know the internals of this, the only thing that is + * relevant is that after the call to the present function, DoFs are + * distributed in a different way if a cell is a master, slave, or standard + * cell. + */ + void + initialize_hp_structure(); + + + /** + * Helper function to call reinit on a master cell. + */ + const FEValuesBase & + reinit_master( + const typename DoFHandler::active_cell_iterator &cell, + const unsigned int face_number, + std::unique_ptr> + &agglo_isv_ptr) const; + + + /** + * Helper function to determine whether or not a cell is a slave cell, without + * any information about his parents. + */ + template + inline bool + is_slave_cell(const CellIterator &cell) const; + + + /** + * Initialize all the necessary connectivity information for an + * agglomeration. + */ + void + setup_connectivity_of_agglomeration(); + + + /** + * Record the number of agglomerations on the grid. + */ + unsigned int n_agglomerations; + + + /** + * Vector of indices such that v[cell->active_cell_index()] returns + * { -1 if `cell` is a master cell + * { `cell_master->active_cell_index()`, i.e. the index of the master cell if + * `cell` is a slave cell. + */ + LinearAlgebra::distributed::Vector master_slave_relationships; + + /** + * Same as the one above, but storing cell iterators rather than indices. + * + */ + std::map::active_cell_iterator> + master_slave_relationships_iterators; + + using ScratchData = MeshWorker::ScratchData; + + mutable std::vector number_of_agglomerated_faces; + + /** + * Associate a master cell (hence, a given polytope) to its boundary faces. + * The boundary is described through a vector of face iterators. + * + */ + mutable std::map< + const typename Triangulation::active_cell_iterator, + std::vector::active_face_iterator>> + polygon_boundary; + + + /** + * Vector of `BoundingBoxes` s.t. `bboxes[idx]` equals BBOx associated to the + * agglomeration with master cell indexed by ìdx`. Othwerwise default BBox is + * empty + * + */ + std::vector> bboxes; + + //////////////////////////////////////////////////////// + + + // n_faces + mutable std::map> + local_n_faces; + + mutable std::map> + recv_n_faces; + + + // CellId (including slaves) + mutable std::map> + local_cell_ids_neigh_cell; + + mutable std::map> + recv_cell_ids_neigh_cell; + + + // send to neighborign rank the information that + // - current polytope id + // - face f + // has the following neighboring id. + mutable std::map>> + local_ghosted_master_id; + + mutable std::map>> + recv_ghosted_master_id; + + // CellIds from neighboring rank + mutable std::map>> + local_bdary_info; + + mutable std::map>> + recv_bdary_info; + + // Exchange neighboring bounding boxes + mutable std::map>> + local_ghosted_bbox; + + mutable std::map>> + recv_ghosted_bbox; + + // Exchange DoF indices with ghosted polytopes + mutable std::map>> + local_ghost_dofs; + + mutable std::map>> + recv_ghost_dofs; + + // Exchange qpoints + mutable std::map< + types::subdomain_id, + std::map, std::vector>>> + local_qpoints; + + // Exchange jxws + mutable std::map< + types::subdomain_id, + std::map, std::vector>> + local_jxws; + + // Exchange normals + mutable std::map< + types::subdomain_id, + std::map, std::vector>>> + local_normals; + + // Exchange values + mutable std::map< + types::subdomain_id, + std::map, std::vector>>> + local_values; + + mutable std::map, + std::vector>>>> + local_gradients; + + + + //////////////////////////////////////////////////////// + + const Triangulation *tria; + + const Mapping *mapping; + + std::unique_ptr> cached_tria; + + const MPI_Comm communicator; + + // The FiniteElement space we have on each cell. Currently supported types are + // FE_DGQ and FE_DGP elements. + std::unique_ptr> fe; + + hp::FECollection fe_collection; + + /** + * Eulerian vector describing the new cells obtained by the bounding boxes + */ + LinearAlgebra::distributed::Vector euler_vector; + + + /** + * Use this in reinit(cell) for (non-agglomerated, standard) cells, + * and return the result of scratch.reinit(cell) for cells + */ + mutable std::unique_ptr standard_scratch; + + /** + * Fill this up in reinit(cell), for agglomerated cells, using the custom + * quadrature, and return the result of + * scratch.reinit(cell); + */ + mutable std::unique_ptr agglomerated_scratch; + + + mutable std::unique_ptr> + agglomerated_isv; + + mutable std::unique_ptr> + agglomerated_isv_neigh; + + mutable std::unique_ptr> + agglomerated_isv_bdary; + + boost::signals2::connection tria_listener; + + UpdateFlags agglomeration_flags; + + const UpdateFlags internal_agglomeration_flags = + update_values | update_gradients | update_JxW_values | + update_quadrature_points; + + UpdateFlags agglomeration_face_flags; + + const UpdateFlags internal_agglomeration_face_flags = + update_quadrature_points | update_normal_vectors | update_values | + update_gradients | update_JxW_values | update_inverse_jacobians; + + Quadrature agglomeration_quad; + + Quadrature agglomeration_face_quad; + + // Associate the master cell to the slaves. + std::unordered_map< + types::global_cell_index, + std::vector::active_cell_iterator>> + master2slaves; + + // Map the master cell index with the polytope index + std::map master2polygon; + + + std::vector::active_cell_iterator> + master_disconnected; + + // Dummy FiniteElement objects needed only to generate quadratures + + /** + * Dummy FE_Nothing + */ + FE_Nothing dummy_fe; + + /** + * Dummy FEValues, needed for cell quadratures. + */ + std::unique_ptr> no_values; + + /** + * Dummy FEFaceValues, needed for face quadratures. + */ + std::unique_ptr> no_face_values; + + /** + * A contiguous container for all of the master cells. + */ + std::vector::active_cell_iterator> + master_cells_container; + + friend class internal::AgglomerationHandlerImplementation; + + internal::PolytopeCache polytope_cache; + + /** + * Bool that keeps track whether the mesh is composed also by standard deal.II + * cells as (trivial) polytopes. + */ + bool hybrid_mesh; + + std::map, + std::vector> + parent_child_info; + + unsigned int present_extraction_level; + + // Support for hp::FECollection + bool is_hp_collection = false; // Indicates whether hp::FECollection is used + std::unique_ptr> + hp_fe_collection; // External input FECollection + + // Stores quadrature rules; these QCollections should have the same size as + // hp_fe_collection + hp::QCollection agglomeration_quad_collection; + hp::QCollection agglomeration_face_quad_collection; + + hp::MappingCollection + mapping_collection; // Contains only one mapping object + hp::FECollection + dummy_fe_collection; // Similar to dummy_fe, but as an FECollection + // containing only dummy_fe + // Note: The above two variables provide an hp::FECollection interface but + // actually contain only one element each. + + // Analogous to no_values and no_face_values, but used when different cells + // employ different FEs or quadratures + std::unique_ptr> hp_no_values; + std::unique_ptr> hp_no_face_values; +}; + + + +// ------------------------------ inline functions ------------------------- +template +inline const FiniteElement & +AgglomerationHandler::get_fe() const +{ + return *fe; +} + + + +template +inline const Mapping & +AgglomerationHandler::get_mapping() const +{ + return *mapping; +} + + + +template +inline const MappingBox & +AgglomerationHandler::get_agglomeration_mapping() const +{ + return *box_mapping; +} + + + +template +inline const Triangulation & +AgglomerationHandler::get_triangulation() const +{ + return *tria; +} + + +template +inline const std::vector> & +AgglomerationHandler::get_local_bboxes() const +{ + return bboxes; +} + + + +template +inline types::global_cell_index +AgglomerationHandler::cell_to_polytope_index( + const typename Triangulation::active_cell_iterator &cell) const +{ + return master2polygon.at(cell->active_cell_index()); +} + + + +template +inline decltype(auto) +AgglomerationHandler::get_interface() const +{ + return polytope_cache.interface; +} + + + +template +inline const LinearAlgebra::distributed::Vector & +AgglomerationHandler::get_relationships() const +{ + return master_slave_relationships; +} + + + +template +inline std::vector::active_cell_iterator> +AgglomerationHandler::get_agglomerate( + const typename Triangulation::active_cell_iterator + &master_cell) const +{ + Assert(is_master_cell(master_cell), ExcInternalError()); + auto agglomeration = get_slaves_of_idx(master_cell->active_cell_index()); + agglomeration.push_back(master_cell); + return agglomeration; +} + + + +template +inline const DoFHandler & +AgglomerationHandler::get_dof_handler() const +{ + return agglo_dh; +} + + + +template +inline const std::vector< + typename Triangulation::active_cell_iterator> & +AgglomerationHandler::get_slaves_of_idx( + types::global_cell_index idx) const +{ + return master2slaves.at(idx); +} + + + +template +template +inline bool +AgglomerationHandler::is_master_cell( + const CellIterator &cell) const +{ + return master_slave_relationships[cell->global_active_cell_index()] == -1; +} + + + +/** + * Helper function to determine whether or not a cell is a slave cell, without + * any information about his parents. + */ +template +template +inline bool +AgglomerationHandler::is_slave_cell( + const CellIterator &cell) const +{ + return master_slave_relationships[cell->global_active_cell_index()] >= 0; +} + + + +template +inline bool +AgglomerationHandler::at_boundary( + const typename DoFHandler::active_cell_iterator &cell, + const unsigned int face_index) const +{ + Assert(!is_slave_cell(cell), + ExcMessage("This function should not be called for a slave cell.")); + + return polytope_cache.cell_face_at_boundary + .at({master2polygon.at(cell->active_cell_index()), face_index}) + .first; +} + + +template +inline unsigned int +AgglomerationHandler::n_dofs_per_cell() const noexcept +{ + return fe->n_dofs_per_cell(); +} + + + +template +inline types::global_dof_index +AgglomerationHandler::n_dofs() const noexcept +{ + return agglo_dh.n_dofs(); +} + + + +template +inline const std::vector::active_face_iterator> & +AgglomerationHandler::polytope_boundary( + const typename Triangulation::active_cell_iterator &cell) +{ + return polygon_boundary[cell]; +} + + + +template +inline typename Triangulation::active_cell_iterator & +AgglomerationHandler::is_slave_cell_of( + const typename Triangulation::active_cell_iterator &cell) +{ + return master_slave_relationships_iterators.at(cell->active_cell_index()); +} + + + +template +inline types::global_cell_index +AgglomerationHandler::get_master_idx_of_cell( + const typename Triangulation::active_cell_iterator &cell) const +{ + auto idx = master_slave_relationships[cell->global_active_cell_index()]; + if (idx == -1) + return cell->global_active_cell_index(); + else + return static_cast(idx); +} + + + +template +inline bool +AgglomerationHandler::are_cells_agglomerated( + const typename Triangulation::active_cell_iterator &cell, + const typename Triangulation::active_cell_iterator &other_cell) + const +{ + // if different subdomain, then **by construction** they will not be together + // if (cell->subdomain_id() != other_cell->subdomain_id()) + // return false; + // else + return (get_master_idx_of_cell(cell) == get_master_idx_of_cell(other_cell)); +} + + + +template +inline unsigned int +AgglomerationHandler::n_agglomerates() const +{ + return n_agglomerations; +} + + + +template +inline const typename DoFHandler::active_cell_iterator +AgglomerationHandler::polytope_to_dh_iterator( + const types::global_cell_index polytope_index) const +{ + return master_cells_container[polytope_index]->as_dof_handler_iterator( + agglo_dh); +} + + + +template +AgglomerationIterator +AgglomerationHandler::begin() const +{ + Assert(n_agglomerations > 0, + ExcMessage("No agglomeration has been performed.")); + return {*master_cells_container.begin(), this}; +} + + + +template +AgglomerationIterator +AgglomerationHandler::begin() +{ + Assert(n_agglomerations > 0, + ExcMessage("No agglomeration has been performed.")); + return {*master_cells_container.begin(), this}; +} + + + +template +AgglomerationIterator +AgglomerationHandler::end() const +{ + Assert(n_agglomerations > 0, + ExcMessage("No agglomeration has been performed.")); + return {*master_cells_container.end(), this}; +} + + + +template +AgglomerationIterator +AgglomerationHandler::end() +{ + Assert(n_agglomerations > 0, + ExcMessage("No agglomeration has been performed.")); + return {*master_cells_container.end(), this}; +} + + + +template +AgglomerationIterator +AgglomerationHandler::last() +{ + Assert(n_agglomerations > 0, + ExcMessage("No agglomeration has been performed.")); + return {master_cells_container.back(), this}; +} + + + +template +IteratorRange< + typename AgglomerationHandler::agglomeration_iterator> +AgglomerationHandler::polytope_iterators() const +{ + return IteratorRange< + typename AgglomerationHandler::agglomeration_iterator>( + begin(), end()); +} + +template +template +void +AgglomerationHandler::connect_hierarchy( + const CellsAgglomerator &agglomerator) +{ + parent_child_info = agglomerator.parent_node_to_children_nodes; + present_extraction_level = agglomerator.extraction_level; +} + +template +inline const hp::FECollection & +AgglomerationHandler::get_fe_collection() const +{ + return *hp_fe_collection; +} + +template +inline bool +AgglomerationHandler::used_fe_collection() const +{ + return is_hp_collection; +} + + +#endif diff --git a/agglomeration_poisson/include/agglomeration_iterator.h b/agglomeration_poisson/include/agglomeration_iterator.h new file mode 100644 index 00000000..878e74f8 --- /dev/null +++ b/agglomeration_poisson/include/agglomeration_iterator.h @@ -0,0 +1,304 @@ +/* ----------------------------------------------------------------------------- + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright (C) 2025 by Marco Feder, Pasquale Claudio Africa, Xinping Gui, + * Andrea Cangiani + * + * This file is part of the deal.II code gallery. + * + * ----------------------------------------------------------------------------- + */ + +#ifndef agglomeration_iterator_h +#define agglomeration_iterator_h + + +#include + + +/** + * A class that is used to iterate over polygons. Together with the + * AgglomerationAccessor class this is used to hide the internal implementation + * of the particle class and the particle container. + */ +template +class AgglomerationIterator +{ +public: + using AgglomerationContainer = + typename AgglomerationAccessor::AgglomerationContainer; + + /** + * Empty constructor. This constructore creates an iterator pointing to an + * invalid object. + */ + AgglomerationIterator(); + + /** + * Constructor of the iterator. Takes a reference to the master cell encoding + * the actual polytope. + */ + AgglomerationIterator( + const typename Triangulation::active_cell_iterator &cell, + const AgglomerationHandler *handler); + + /** + * Same as above, needed for ghosted elements. + */ + AgglomerationIterator( + const typename Triangulation::active_cell_iterator + &master_cell, + const CellId &cell_id, + const AgglomerationHandler *handler); + + /** + * Dereferencing operator, returns a reference to an accessor. Usage is thus + * like (*i).get_dof_indices (); + */ + const AgglomerationAccessor & + operator*() const; + + /** + * Dereferencing operator, non-@p const version. + */ + AgglomerationAccessor & + operator*(); + + /** + * Dereferencing operator, returns a pointer of the particle pointed to. + * Usage is thus like i->get_dof_indices (); + * + * There is a @p const and a non-@p const version. + */ + const AgglomerationAccessor * + operator->() const; + + /** + * Dereferencing operator, non-@p const version. + */ + AgglomerationAccessor * + operator->(); + + /** + * Compare for equality. + */ + bool + operator==(const AgglomerationIterator &) const; + + /** + * Compare for inequality. + */ + bool + operator!=(const AgglomerationIterator &) const; + + /** + * Prefix ++ operator: ++iterator. This operator advances + * the iterator to the next element and returns a reference to + * *this. + */ + AgglomerationIterator & + operator++(); + + /** + * Postfix ++ operator: iterator++. This operator advances + * the iterator to the next element, but returns an iterator to the element + * previously pointed to. + */ + AgglomerationIterator + operator++(int); + + /** + * Prefix \-- operator: \--iterator. This operator moves + * the iterator to the previous element and returns a reference to + * *this. + */ + AgglomerationIterator & + operator--(); + + /** + * Postfix \-- operator: iterator\--. This operator moves + * the iterator to the previous element, but returns an iterator to the + * element previously pointed to. + */ + AgglomerationIterator + operator--(int); + + /** + * Return the state of the present iterator. + */ + IteratorState::IteratorStates + state() const; + + /** + * Return the master cell associated to the present polytope. + */ + const typename Triangulation::active_cell_iterator & + master_cell() const; + + /** + * Mark the class as bidirectional iterator and declare some alias which + * are standard for iterators and are used by algorithms to enquire about + * the specifics of the iterators they work on. + */ + using iterator_category = std::bidirectional_iterator_tag; + using value_type = AgglomerationAccessor; + using difference_type = std::ptrdiff_t; + using pointer = AgglomerationAccessor *; + using reference = AgglomerationAccessor &; + +private: + /** + * The accessor to the actual polytope. + */ + AgglomerationAccessor accessor; +}; + + + +// ------------------------------ inline functions ------------------------- + +template +inline AgglomerationIterator::AgglomerationIterator() + : accessor() +{} + + + +template +inline AgglomerationIterator::AgglomerationIterator( + const typename Triangulation::active_cell_iterator + &master_cell, + const AgglomerationHandler *handler) + : accessor(master_cell, handler) +{} + +template +inline AgglomerationIterator::AgglomerationIterator( + const typename Triangulation::active_cell_iterator + &master_cell, + const CellId &cell_id, + const AgglomerationHandler *handler) + : accessor(master_cell, cell_id, handler) +{} + + + +template +inline AgglomerationAccessor & +AgglomerationIterator::operator*() +{ + return accessor; +} + + + +template +inline AgglomerationAccessor * +AgglomerationIterator::operator->() +{ + return &(this->operator*()); +} + + + +template +inline const AgglomerationAccessor & +AgglomerationIterator::operator*() const +{ + return accessor; +} + + + +template +inline const AgglomerationAccessor * +AgglomerationIterator::operator->() const +{ + return &(this->operator*()); +} + + + +template +inline bool +AgglomerationIterator::operator!=( + const AgglomerationIterator &other) const +{ + return accessor != other.accessor; +} + + + +template +inline bool +AgglomerationIterator::operator==( + const AgglomerationIterator &other) const +{ + return accessor == other.accessor; +} + + + +template +inline AgglomerationIterator & +AgglomerationIterator::operator++() +{ + accessor.next(); + return *this; +} + + + +template +inline AgglomerationIterator +AgglomerationIterator::operator++(int) +{ + AgglomerationIterator tmp(*this); + operator++(); + + return tmp; +} + + + +template +inline AgglomerationIterator & +AgglomerationIterator::operator--() +{ + accessor.prev(); + return *this; +} + + + +template +inline AgglomerationIterator +AgglomerationIterator::operator--(int) +{ + AgglomerationIterator tmp(*this); + operator--(); + + return tmp; +} + + + +template +inline IteratorState::IteratorStates +AgglomerationIterator::state() const +{ + return accessor.master_cell.state(); +} + + + +template +inline const typename Triangulation::active_cell_iterator & +AgglomerationIterator::master_cell() const +{ + return accessor.master_cell; +} + + + +#endif diff --git a/agglomeration_poisson/include/agglomerator.h b/agglomeration_poisson/include/agglomerator.h new file mode 100644 index 00000000..79a4d5f4 --- /dev/null +++ b/agglomeration_poisson/include/agglomerator.h @@ -0,0 +1,471 @@ +/* ----------------------------------------------------------------------------- + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright (C) 2025 by Marco Feder, Pasquale Claudio Africa, Xinping Gui, + * Andrea Cangiani + * + * This file is part of the deal.II code gallery. + * + * ----------------------------------------------------------------------------- + */ + +#ifndef agglomerator_h +#define agglomerator_h + + +#include + +#include + +#include +#include +#include + +template +class AgglomerationHandler; + +namespace dealii +{ + namespace internal + { + template + struct Rtree_visitor + : public boost::geometry::index::detail::rtree::visitor< + Value, + typename Options::parameters_type, + Box, + Allocators, + typename Options::node_tag, + true>::type + { + inline Rtree_visitor( + const Translator &translator, + const unsigned int target_level, + std::vector::value>::active_cell_iterator>> + &agglomerates_, + std::vector &n_nodes_per_level, + std::map, + std::vector> &parent_to_children); + + /** + * An alias that identifies an InternalNode of the tree. + */ + using InternalNode = + typename boost::geometry::index::detail::rtree::internal_node< + Value, + typename Options::parameters_type, + Box, + Allocators, + typename Options::node_tag>::type; + + /** + * An alias that identifies a Leaf of the tree. + */ + using Leaf = typename boost::geometry::index::detail::rtree::leaf< + Value, + typename Options::parameters_type, + Box, + Allocators, + typename Options::node_tag>::type; + + /** + * Implements the visitor interface for InternalNode objects. If the node + * belongs to the level next to @p target_level, then fill the bounding + * box vector for that node. + */ + inline void + operator()(const InternalNode &node); + + /** + * Implements the visitor interface for Leaf objects. + */ + inline void + operator()(const Leaf &); + + /** + * Translator interface, required by the boost implementation of the + * rtree. + */ + const Translator &translator; + + /** + * Store the level we are currently visiting. + */ + size_t level; + + /** + * Index used to keep track of the number of different visited nodes + * during recursion/ + */ + size_t node_counter; + + /** + * The level where children are living. + * Before: "we want to extract from the RTree object." + */ + const size_t target_level; + + /** + * A reference to the input vector of vector of BoundingBox objects. This + * vector v has the following property: v[i] = vector with all the mesh + * iterators composing the i-th agglomerate. + */ + std::vector::value>::active_cell_iterator>> + &agglomerates; + + /** + * Store the total number of nodes on each level. + */ + std::vector &n_nodes_per_level; + + /** + * Map that associates to a given node on level l its children, identified + * by their integer index. + */ + std::map, + std::vector> + &parent_node_to_children_nodes; + }; + + + + template + Rtree_visitor::Rtree_visitor( + const Translator &translator, + const unsigned int target_level, + std::vector::value>::active_cell_iterator>> + &agglomerates_, + std::vector &n_nodes_per_level_, + std::map, + std::vector> &parent_to_children) + : translator(translator) + , level(0) + , node_counter(0) + , target_level(target_level) + , agglomerates(agglomerates_) + , n_nodes_per_level(n_nodes_per_level_) + , parent_node_to_children_nodes(parent_to_children) + {} + + + + template + void + Rtree_visitor::operator()( + const Rtree_visitor::InternalNode &node) + { + using elements_type = + typename boost::geometry::index::detail::rtree::elements_type< + InternalNode>::type; // pairs of bounding box and pointer to child + // node + const elements_type &elements = + boost::geometry::index::detail::rtree::elements(node); + + if (level < target_level) + { + size_t level_backup = level; + ++level; + + for (typename elements_type::const_iterator it = elements.begin(); + it != elements.end(); + ++it) + { + boost::geometry::index::detail::rtree::apply_visitor(*this, + *it->second); + } + + level = level_backup; + } + else if (level == target_level) + { + const auto offset = agglomerates.size(); + agglomerates.resize(offset + 1); + size_t level_backup = level; + + ++level; + for (const auto &entry : elements) + { + boost::geometry::index::detail::rtree::apply_visitor( + *this, *entry.second); + } + // Done with node number 'node_counter' on level target_level. + + ++node_counter; // visited all children of an internal node + n_nodes_per_level[target_level]++; + + level = level_backup; + } + else if (level > target_level) + { + // I am on a child (internal) node on a deeper level. + + // Keep visiting until you go to the leafs. + size_t level_backup = level; + + ++level; + + // looping through entries of node + for (const auto &entry : elements) + { + boost::geometry::index::detail::rtree::apply_visitor( + *this, *entry.second); + } + // done with node on level l > target_level (not just + // "target_level+1). + n_nodes_per_level[level_backup]++; + const types::global_cell_index node_idx = + n_nodes_per_level[level_backup] - 1; // so to start from 0 + + parent_node_to_children_nodes[{n_nodes_per_level[level_backup - 1], + level_backup - 1}] + .push_back(node_idx); + + level = level_backup; + } + } + + + + template + void + Rtree_visitor::operator()( + const Rtree_visitor::Leaf &leaf) + { + using elements_type = + typename boost::geometry::index::detail::rtree::elements_type< + Leaf>::type; // pairs of bounding box and pointer to child node + const elements_type &elements = + boost::geometry::index::detail::rtree::elements(leaf); + + if (level == target_level) + { + // If I want to extract from leaf node, i.e. the target_level is the + // last one where leafs are grouped together. + const auto offset = agglomerates.size(); + agglomerates.resize(offset + 1); + + for (const auto &it : elements) + agglomerates[node_counter].push_back(it.second); + + ++node_counter; + n_nodes_per_level[target_level]++; + } + else + { + for (const auto &it : elements) + agglomerates[node_counter].push_back(it.second); + + + if (level == target_level + 1) + { + const unsigned int node_idx = n_nodes_per_level[level]; + + parent_node_to_children_nodes[{n_nodes_per_level[level - 1], + level - 1}] + .push_back(node_idx); + n_nodes_per_level[level]++; + } + } + } + } // namespace internal + + + + /** + * Helper class which handles agglomeration based on the R-tree data + * structure. Notice that the R-tree type is assumed to be an R-star-tree. + */ + template + class CellsAgglomerator + { + public: + template + friend class ::AgglomerationHandler; + + /** + * Constructor. It takes a given rtree and an integer representing the + * index of the level to be extracted. + */ + CellsAgglomerator(const RtreeType &rtree, + const unsigned int extraction_level); + + /** + * Extract agglomerates based on the current tree and the extraction level. + * This function returns a reference to + */ + const std::vector< + std::vector::active_cell_iterator>> & + extract_agglomerates(); + + /** + * Get total number of levels. + */ + inline unsigned int + get_n_levels() const; + + /** + * Return the number of nodes present in level @p level. + */ + inline types::global_cell_index + get_n_nodes_per_level(const unsigned int level) const; + + /** + * This function returns a map which associates to each node on level + * @p extraction_level a list of children. + */ + inline const std::map< + std::pair, + std::vector> & + get_hierarchy() const; + + private: + /** + * Raw pointer to the actual R-tree. + */ + RtreeType *rtree; + + /** + * Extraction level. + */ + const unsigned int extraction_level; + + /** + * Store agglomerates obtained after recursive extraction on nodes of + * level @p extraction_level. + */ + std::vector::active_cell_iterator>> + agglomerates_on_level; + + /** + * Vector storing the number of nodes (and, ultimately, agglomerates) for + * each level. + */ + std::vector n_nodes_per_level; + + /** + * Map which maps a node parent @n on level @p l to a vector of integers + * which stores the index of children. + */ + std::map, + std::vector> + parent_node_to_children_nodes; + }; + + + + template + CellsAgglomerator::CellsAgglomerator( + const RtreeType &tree, + const unsigned int extraction_level_) + : extraction_level(extraction_level_) + { + rtree = const_cast(&tree); + Assert(n_levels(*rtree), ExcMessage("At least two levels are needed.")); + } + + + + template + const std::vector< + std::vector::active_cell_iterator>> & + CellsAgglomerator::extract_agglomerates() + { + AssertThrow(extraction_level <= n_levels(*rtree), + ExcInternalError("You are trying to extract level " + + std::to_string(extraction_level) + + " of the tree, but it only has a total of " + + std::to_string(n_levels(*rtree)) + + " levels.")); + using RtreeView = + boost::geometry::index::detail::rtree::utilities::view; + RtreeView rtv(*rtree); + + n_nodes_per_level.resize(rtv.depth() + + 1); // store how many nodes we have for each level. + + if (rtv.depth() == 0) + { + // The below algorithm does not work for `rtv.depth()==0`, which might + // happen if the number entries in the tree is too small. + agglomerates_on_level.resize(1); + agglomerates_on_level[0].resize(1); + } + else + { + const unsigned int target_level = + std::min(extraction_level, rtv.depth()); + + internal::Rtree_visitor + extractor_visitor(rtv.translator(), + target_level, + agglomerates_on_level, + n_nodes_per_level, + parent_node_to_children_nodes); + + + rtv.apply_visitor(extractor_visitor); + } + return agglomerates_on_level; + } + + + + // ------------------------------ inline functions ------------------------- + + + template + inline unsigned int + CellsAgglomerator::get_n_levels() const + { + return n_levels(*rtree); + } + + + + template + inline types::global_cell_index + CellsAgglomerator::get_n_nodes_per_level( + const unsigned int level) const + { + return n_nodes_per_level[level]; + } + + + + template + inline const std::map< + std::pair, + std::vector> & + CellsAgglomerator::get_hierarchy() const + { + Assert(parent_node_to_children_nodes.size(), + ExcMessage( + "The hierarchy has not been computed. Did you forget to call" + " extract_agglomerates() first?")); + return parent_node_to_children_nodes; + } +} // namespace dealii +#endif diff --git a/agglomeration_poisson/include/mapping_box.h b/agglomeration_poisson/include/mapping_box.h new file mode 100644 index 00000000..6183e5c1 --- /dev/null +++ b/agglomeration_poisson/include/mapping_box.h @@ -0,0 +1,382 @@ +/* ----------------------------------------------------------------------------- + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright (C) 2025 by Marco Feder, Pasquale Claudio Africa, Xinping Gui, + * Andrea Cangiani + * + * This file is part of the deal.II code gallery. + * + * ----------------------------------------------------------------------------- + */ + +#ifndef dealii_mapping_box_h +#define dealii_mapping_box_h + + +#include + +#include +#include + +#include + +#include + + +DEAL_II_NAMESPACE_OPEN + +/** + * @addtogroup mapping + * @{ + */ + +/** + * A class providing a mapping from the reference cell to cells that are + * axiparallel, i.e., that have the shape of rectangles (in 2d) or + * boxes (in 3d) with edges parallel to the coordinate directions. The + * class therefore provides functionality that is equivalent to what, + * for example, MappingQ would provide for such cells. However, knowledge + * of the shape of cells allows this class to be substantially more + * efficient. + * + * Specifically, the mapping is meant for cells for which the mapping from + * the reference to the real cell is a scaling along the coordinate + * directions: The transformation from reference coordinates $\hat {\mathbf + * x}$ to real coordinates $\mathbf x$ on each cell is of the form + * @f{align*}{ + * {\mathbf x}(\hat {\mathbf x}) + * = + * \begin{pmatrix} + * h_x & 0 \\ + * 0 & h_y + * \end{pmatrix} + * \hat{\mathbf x} + * + {\mathbf v}_0 + * @f} + * in 2d, and + * @f{align*}{ + * {\mathbf x}(\hat {\mathbf x}) + * = + * \begin{pmatrix} + * h_x & 0 & 0 \\ + * 0 & h_y & 0 \\ + * 0 & 0 & h_z + * \end{pmatrix} + * \hat{\mathbf x} + * + {\mathbf v}_0 + * @f} + * in 3d, where ${\mathbf v}_0$ is the bottom left vertex and $h_x,h_y,h_z$ + * are the extents of the cell along the axes. + * + * The class is intended for efficiency, and it does not do a whole lot of + * error checking. If you apply this mapping to a cell that does not conform + * to the requirements above, you will get strange results. + */ +template +class MappingBox : public Mapping +{ +public: + MappingBox(const std::vector> &local_boxes, + const std::map + &polytope_translator); + // for documentation, see the Mapping base class + virtual std::unique_ptr> + clone() const override; + + /** + * Return @p true because MappingBox preserves vertex + * locations. + */ + virtual bool + preserves_vertex_locations() const override; + + virtual bool + is_compatible_with(const ReferenceCell &reference_cell) const override; + + /** + * @name Mapping points between reference and real cells + * @{ + */ + + // for documentation, see the Mapping base class + virtual Point + transform_unit_to_real_cell( + const typename Triangulation::cell_iterator &cell, + const Point &p) const override; + + // for documentation, see the Mapping base class + virtual Point + transform_real_to_unit_cell( + const typename Triangulation::cell_iterator &cell, + const Point &p) const override; + + // for documentation, see the Mapping base class + virtual void + transform_points_real_to_unit_cell( + const typename Triangulation::cell_iterator &cell, + const ArrayView> &real_points, + const ArrayView> &unit_points) const override; + + /** + * @} + */ + + /** + * @name Functions to transform tensors from reference to real coordinates + * @{ + */ + + // for documentation, see the Mapping base class + virtual void + transform(const ArrayView> &input, + const MappingKind kind, + const typename Mapping::InternalDataBase &internal, + const ArrayView> &output) const override; + + // for documentation, see the Mapping base class + virtual void + transform(const ArrayView> &input, + const MappingKind kind, + const typename Mapping::InternalDataBase &internal, + const ArrayView> &output) const override; + + // for documentation, see the Mapping base class + virtual void + transform(const ArrayView> &input, + const MappingKind kind, + const typename Mapping::InternalDataBase &internal, + const ArrayView> &output) const override; + + // for documentation, see the Mapping base class + virtual void + transform(const ArrayView> &input, + const MappingKind kind, + const typename Mapping::InternalDataBase &internal, + const ArrayView> &output) const override; + + // for documentation, see the Mapping base class + virtual void + transform(const ArrayView> &input, + const MappingKind kind, + const typename Mapping::InternalDataBase &internal, + const ArrayView> &output) const override; + + /** + * @} + */ + + /** + * @name Interface with FEValues + * @{ + */ + + /** + * Storage for internal data of the mapping. See Mapping::InternalDataBase + * for an extensive description. + * + * This includes data that is computed once when the object is created (in + * get_data()) as well as data the class wants to store from between the + * call to fill_fe_values(), fill_fe_face_values(), or + * fill_fe_subface_values() until possible later calls from the finite + * element to functions such as transform(). The latter class of member + * variables are marked as 'mutable'. + */ + class InternalData : public Mapping::InternalDataBase + { + public: + /** + * Default constructor. + */ + InternalData() = default; + + /** + * Constructor that initializes the object with a quadrature. + */ + InternalData(const Quadrature &quadrature); + + // Documentation see Mapping::InternalDataBase. + virtual void + reinit(const UpdateFlags update_flags, + const Quadrature &quadrature) override; + + /** + * Return an estimate (in bytes) for the memory consumption of this object. + */ + virtual std::size_t + memory_consumption() const override; + + /** + * Extents of the last cell we have seen in the coordinate directions, + * i.e., hx, hy, hz. + */ + mutable Tensor<1, dim> cell_extents; + + /** + * Traslation term in F(\hat{x})=J\hat{x} + c. + */ + mutable Tensor<1, dim> traslation; + + /** + * Reciprocal of the extents of the last cell we have seen in the + * coordinate directions, i.e., hx, + * hy, hz. + */ + mutable Tensor<1, dim> inverse_cell_extents; + + /** + * The volume element + */ + mutable double volume_element; + + /** + * Location of quadrature points of faces or subfaces in 3d with all + * possible orientations. Can be accessed with the correct offset provided + * via QProjector::DataSetDescriptor. Not needed/used for cells. + */ + std::vector> quadrature_points; + }; + +private: + // documentation can be found in Mapping::requires_update_flags() + virtual UpdateFlags + requires_update_flags(const UpdateFlags update_flags) const override; + + // documentation can be found in Mapping::get_data() + virtual std::unique_ptr::InternalDataBase> + get_data(const UpdateFlags, const Quadrature &quadrature) const override; + + using Mapping::get_face_data; + + // documentation can be found in Mapping::get_subface_data() + virtual std::unique_ptr::InternalDataBase> + get_subface_data(const UpdateFlags flags, + const Quadrature &quadrature) const override; + + // documentation can be found in Mapping::fill_fe_values() + virtual CellSimilarity::Similarity + fill_fe_values( + const typename Triangulation::cell_iterator &cell, + const CellSimilarity::Similarity cell_similarity, + const Quadrature &quadrature, + const typename Mapping::InternalDataBase &internal_data, + internal::FEValuesImplementation::MappingRelatedData + &output_data) const override; + + using Mapping::fill_fe_face_values; + + // documentation can be found in Mapping::fill_fe_subface_values() + virtual void + fill_fe_subface_values( + const typename Triangulation::cell_iterator &cell, + const unsigned int face_no, + const unsigned int subface_no, + const Quadrature &quadrature, + const typename Mapping::InternalDataBase &internal_data, + internal::FEValuesImplementation::MappingRelatedData + &output_data) const override; + + // documentation can be found in Mapping::fill_fe_immersed_surface_values() + virtual void + fill_fe_immersed_surface_values( + const typename Triangulation::cell_iterator &cell, + const NonMatching::ImmersedSurfaceQuadrature &quadrature, + const typename Mapping::InternalDataBase &internal_data, + internal::FEValuesImplementation::MappingRelatedData + &output_data) const override; + + /** + * @} + */ + + /** + * Update the cell_extents field of the incoming InternalData object with the + * size of the incoming cell. + */ + void + update_cell_extents( + const typename Triangulation::cell_iterator &cell, + const CellSimilarity::Similarity cell_similarity, + const InternalData &data) const; + + /** + * Compute the quadrature points if the UpdateFlags of the incoming + * InternalData object say that they should be updated. + * + * Called from fill_fe_values. + */ + void + maybe_update_cell_quadrature_points( + const typename Triangulation::cell_iterator &cell, + const InternalData &data, + const ArrayView> &unit_quadrature_points, + std::vector> &quadrature_points) const; + + /** + * Compute the normal vectors if the UpdateFlags of the incoming InternalData + * object say that they should be updated. + */ + void + maybe_update_normal_vectors( + const unsigned int face_no, + const InternalData &data, + std::vector> &normal_vectors) const; + + /** + * Since the Jacobian is constant for this mapping all derivatives of the + * Jacobian are identically zero. Fill these quantities with zeros if the + * corresponding update flags say that they should be updated. + */ + void + maybe_update_jacobian_derivatives( + const InternalData &data, + const CellSimilarity::Similarity cell_similarity, + internal::FEValuesImplementation::MappingRelatedData + &output_data) const; + + + /** + * Compute the volume elements if the UpdateFlags of the incoming + * InternalData object say that they should be updated. + */ + void + maybe_update_volume_elements(const InternalData &data) const; + + /** + * Compute the Jacobians if the UpdateFlags of the incoming + * InternalData object say that they should be updated. + */ + void + maybe_update_jacobians( + const InternalData &data, + const CellSimilarity::Similarity cell_similarity, + internal::FEValuesImplementation::MappingRelatedData + &output_data) const; + + /** + * Compute the inverse Jacobians if the UpdateFlags of the incoming + * InternalData object say that they should be updated. + */ + void + maybe_update_inverse_jacobians( + const InternalData &data, + const CellSimilarity::Similarity cell_similarity, + internal::FEValuesImplementation::MappingRelatedData + &output_data) const; + + /** + * Vector of (local) bounding boxes + */ + std::vector> boxes; + + /** + * Map from global cell index to bounding box index + */ + std::map + polytope_translator; +}; + +/** @} */ + +DEAL_II_NAMESPACE_CLOSE + +#endif diff --git a/agglomeration_poisson/include/poly_utils.h b/agglomeration_poisson/include/poly_utils.h new file mode 100644 index 00000000..8ba4b910 --- /dev/null +++ b/agglomeration_poisson/include/poly_utils.h @@ -0,0 +1,2464 @@ +/* ----------------------------------------------------------------------------- + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright (C) 2025 by Marco Feder, Pasquale Claudio Africa, Xinping Gui, + * Andrea Cangiani + * + * This file is part of the deal.II code gallery. + * + * ----------------------------------------------------------------------------- + */ + +#ifndef poly_utils_h +#define poly_utils_h + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#ifdef DEAL_II_WITH_TRILINOS +# include +#endif + +#ifdef DEAL_II_WITH_CGAL + +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + + +#endif + +#include + + +namespace dealii::PolyUtils::internal +{ + /** + * Helper function to compute the position of index @p index in vector @p v. + */ + inline types::global_cell_index + get_index(const std::vector &v, + const types::global_cell_index index) + { + return std::distance(v.begin(), std::find(v.begin(), v.end(), index)); + } + + + + /** + * Compute the connectivity graph for locally owned regions of a distributed + * triangulation. + */ + template + void + get_face_connectivity_of_cells( + const parallel::fullydistributed::Triangulation + &triangulation, + DynamicSparsityPattern &cell_connectivity, + const std::vector locally_owned_cells) + { + cell_connectivity.reinit(triangulation.n_locally_owned_active_cells(), + triangulation.n_locally_owned_active_cells()); + + + // loop over all cells and their neighbors to build the sparsity + // pattern. note that it's a bit hard to enter all the connections when + // a neighbor has children since we would need to find out which of its + // children is adjacent to the current cell. this problem can be omitted + // if we only do something if the neighbor has no children -- in that + // case it is either on the same or a coarser level than we are. in + // return, we have to add entries in both directions for both cells + for (const auto &cell : triangulation.active_cell_iterators()) + { + if (cell->is_locally_owned()) + { + const unsigned int index = cell->active_cell_index(); + cell_connectivity.add(get_index(locally_owned_cells, index), + get_index(locally_owned_cells, index)); + for (auto f : cell->face_indices()) + if ((cell->at_boundary(f) == false) && + (cell->neighbor(f)->has_children() == false) && + cell->neighbor(f)->is_locally_owned()) + { + const unsigned int other_index = + cell->neighbor(f)->active_cell_index(); + + cell_connectivity.add(get_index(locally_owned_cells, index), + get_index(locally_owned_cells, + other_index)); + cell_connectivity.add(get_index(locally_owned_cells, + other_index), + get_index(locally_owned_cells, index)); + } + } + } + } +} // namespace dealii::PolyUtils::internal + + +namespace dealii::PolyUtils +{ + template + struct Rtree_visitor : public boost::geometry::index::detail::rtree::visitor< + Value, + typename Options::parameters_type, + Box, + Allocators, + typename Options::node_tag, + true>::type + { + inline Rtree_visitor( + const Translator &translator, + unsigned int target_level, + std::vector::value>::active_cell_iterator>> &boxes, + std::vector> &csr); + + + /** + * An alias that identifies an InternalNode of the tree. + */ + using InternalNode = + typename boost::geometry::index::detail::rtree::internal_node< + Value, + typename Options::parameters_type, + Box, + Allocators, + typename Options::node_tag>::type; + + /** + * An alias that identifies a Leaf of the tree. + */ + using Leaf = typename boost::geometry::index::detail::rtree::leaf< + Value, + typename Options::parameters_type, + Box, + Allocators, + typename Options::node_tag>::type; + + /** + * Implements the visitor interface for InternalNode objects. If the node + * belongs to the level next to @p target_level, then fill the bounding box vector for that node. + */ + inline void + operator()(const InternalNode &node); + + /** + * Implements the visitor interface for Leaf objects. + */ + inline void + operator()(const Leaf &); + + /** + * Translator interface, required by the boost implementation of the rtree. + */ + const Translator &translator; + + /** + * Store the level we are currently visiting. + */ + size_t level; + + /** + * Index used to keep track of the number of different visited nodes during + * recursion/ + */ + size_t node_counter; + + size_t next_level_leafs_processed; + /** + * The level where children are living. + * Before: "we want to extract from the RTree object." + */ + const size_t target_level; + + /** + * A reference to the input vector of vector of BoundingBox objects. This + * vector v has the following property: v[i] = vector with all + * of the BoundingBox bounded by the i-th node of the Rtree. + */ + std::vector::value>::active_cell_iterator>> + &agglomerates; + + std::vector> &row_ptr; + }; + + + + template + Rtree_visitor::Rtree_visitor( + const Translator &translator, + const unsigned int target_level, + std::vector::value>::active_cell_iterator>> + &bb_in_boxes, + std::vector> &csr) + : translator(translator) + , level(0) + , node_counter(0) + , next_level_leafs_processed(0) + , target_level(target_level) + , agglomerates(bb_in_boxes) + , row_ptr(csr) + {} + + + + template + void + Rtree_visitor::operator()( + const Rtree_visitor::InternalNode &node) + { + using elements_type = + typename boost::geometry::index::detail::rtree::elements_type< + InternalNode>::type; // pairs of bounding box and pointer to child node + const elements_type &elements = + boost::geometry::index::detail::rtree::elements(node); + + if (level < target_level) + { + size_t level_backup = level; + ++level; + + for (typename elements_type::const_iterator it = elements.begin(); + it != elements.end(); + ++it) + { + boost::geometry::index::detail::rtree::apply_visitor(*this, + *it->second); + } + + level = level_backup; + } + else if (level == target_level) + { + // const unsigned int n_children = elements.size(); + const auto offset = agglomerates.size(); + agglomerates.resize(offset + 1); + row_ptr.resize(row_ptr.size() + 1); + next_level_leafs_processed = 0; + row_ptr.back().push_back( + next_level_leafs_processed); // convention: row_ptr[0]=0 + size_t level_backup = level; + + ++level; + for (const auto &child : elements) + { + boost::geometry::index::detail::rtree::apply_visitor(*this, + *child.second); + } + // Done with node number 'node_counter' + + ++node_counter; // visited all children of an internal node + + level = level_backup; + } + else if (level > target_level) + { + // Keep visiting until you go to the leafs. + size_t level_backup = level; + + ++level; + + for (const auto &child : elements) + { + boost::geometry::index::detail::rtree::apply_visitor(*this, + *child.second); + } + level = level_backup; + row_ptr[node_counter].push_back(next_level_leafs_processed); + } + } + + + + template + void + Rtree_visitor::operator()( + const Rtree_visitor::Leaf &leaf) + { + using elements_type = + typename boost::geometry::index::detail::rtree::elements_type< + Leaf>::type; // pairs of bounding box and pointer to child node + const elements_type &elements = + boost::geometry::index::detail::rtree::elements(leaf); + + + for (const auto &it : elements) + { + agglomerates[node_counter].push_back(it.second); + } + next_level_leafs_processed += elements.size(); + } + + template + inline std::pair< + std::vector>, + std::vector::value>::active_cell_iterator>>> + extract_children_of_level(const Rtree &tree, const unsigned int level) + { + using RtreeView = + boost::geometry::index::detail::rtree::utilities::view; + RtreeView rtv(tree); + + std::vector> csrs; + std::vector::value>::active_cell_iterator>> + agglomerates; + + if (rtv.depth() == 0) + { + // The below algorithm does not work for `rtv.depth()==0`, which might + // happen if the number entries in the tree is too small. + // In this case, simply return a single bounding box. + agglomerates.resize(1); + agglomerates[0].resize(1); + csrs.resize(1); + csrs[0].resize(1); + } + else + { + const unsigned int target_level = + std::min(level, rtv.depth()); + + Rtree_visitor + node_visitor(rtv.translator(), target_level, agglomerates, csrs); + rtv.apply_visitor(node_visitor); + } + AssertDimension(agglomerates.size(), csrs.size()); + + return {csrs, agglomerates}; + } + + + template + Number + compute_h_orthogonal( + const unsigned int face_index, + const std::vector::active_face_iterator> + &polygon_boundary, + const Tensor<1, dim> &deal_normal) + { +#ifdef DEAL_II_WITH_CGAL + + using Kernel = CGAL::Exact_predicates_exact_constructions_kernel; + std::vector candidates; + candidates.reserve(polygon_boundary.size() - 1); + + // Initialize the range of faces to be checked for intersection: they are + // {0,..,n_faces-1}\setminus the current face index face_index. + std::vector face_indices(polygon_boundary.size()); + std::iota(face_indices.begin(), face_indices.end(), 0); // fill the range + face_indices.erase(face_indices.cbegin() + + face_index); // remove current index + + if constexpr (dim == 2) + { + typename Kernel::Segment_2 face_segm( + {polygon_boundary[face_index]->vertex(0)[0], + polygon_boundary[face_index]->vertex(0)[1]}, + {polygon_boundary[face_index]->vertex(1)[0], + polygon_boundary[face_index]->vertex(1)[1]}); + + // Shoot a ray from the midpoint of the face in the orthogonal direction + // given by deal.II normals + const auto &midpoint = CGAL::midpoint(face_segm); + // deal.II normal is always outward, flip the direction + const typename Kernel::Vector_2 orthogonal_direction{-deal_normal[0], + -deal_normal[1]}; + const typename Kernel::Ray_2 ray(midpoint, orthogonal_direction); + for (const auto f : face_indices) + { + typename Kernel::Segment_2 segm({polygon_boundary[f]->vertex(0)[0], + polygon_boundary[f]->vertex(0)[1]}, + {polygon_boundary[f]->vertex(1)[0], + polygon_boundary[f]->vertex( + 1)[1]}); + + if (CGAL::do_intersect(ray, segm)) + candidates.push_back(CGAL::squared_distance(midpoint, segm)); + } + return std::sqrt(CGAL::to_double( + *std::min_element(candidates.cbegin(), candidates.cend()))); + } + else if constexpr (dim == 3) + { + const typename Kernel::Point_3 ¢er{ + polygon_boundary[face_index]->center()[0], + polygon_boundary[face_index]->center()[1], + polygon_boundary[face_index]->center()[2]}; + // deal.II normal is always outward, flip the direction + const typename Kernel::Vector_3 orthogonal_direction{-deal_normal[0], + -deal_normal[1], + -deal_normal[2]}; + const typename Kernel::Ray_3 ray(center, orthogonal_direction); + + for (const auto f : face_indices) + { + // split the face into 2 triangles and compute distances + typename Kernel::Triangle_3 first_triangle( + {polygon_boundary[f]->vertex(0)[0], + polygon_boundary[f]->vertex(0)[1], + polygon_boundary[f]->vertex(0)[2]}, + {polygon_boundary[f]->vertex(1)[0], + polygon_boundary[f]->vertex(1)[1], + polygon_boundary[f]->vertex(1)[2]}, + {polygon_boundary[f]->vertex(3)[0], + polygon_boundary[f]->vertex(3)[1], + polygon_boundary[f]->vertex(3)[2]}); + typename Kernel::Triangle_3 second_triangle( + {polygon_boundary[f]->vertex(0)[0], + polygon_boundary[f]->vertex(0)[1], + polygon_boundary[f]->vertex(0)[2]}, + {polygon_boundary[f]->vertex(3)[0], + polygon_boundary[f]->vertex(3)[1], + polygon_boundary[f]->vertex(3)[2]}, + {polygon_boundary[f]->vertex(2)[0], + polygon_boundary[f]->vertex(2)[1], + polygon_boundary[f]->vertex(2)[2]}); + + // compute point-triangle distance only if the orthogonal ray + // hits the triangle + if (CGAL::do_intersect(ray, first_triangle)) + candidates.push_back( + CGAL::squared_distance(center, first_triangle)); + if (CGAL::do_intersect(ray, second_triangle)) + candidates.push_back( + CGAL::squared_distance(center, second_triangle)); + } + + return std::sqrt(CGAL::to_double( + *std::min_element(candidates.cbegin(), candidates.cend()))); + } + else + { + Assert(false, ExcImpossibleInDim(dim)); + (void)face_index; + (void)polygon_boundary; + return {}; + } + +#else + + Assert(false, ExcNeedsCGAL()); + (void)face_index; + (void)polygon_boundary; + return {}; +#endif + } + + + + /** + * Agglomerate cells together based on their global index. This function is + * **not** efficient and should be used for testing purposes only. + */ + template + void + collect_cells_for_agglomeration( + const Triangulation &tria, + const std::vector &cell_idxs, + std::vector::active_cell_iterator> + &cells_to_be_agglomerated) + { + Assert(cells_to_be_agglomerated.size() == 0, + ExcMessage( + "The vector of cells is supposed to be filled by this function.")); + for (const auto &cell : tria.active_cell_iterators()) + if (std::find(cell_idxs.begin(), + cell_idxs.end(), + cell->active_cell_index()) != cell_idxs.end()) + { + cells_to_be_agglomerated.push_back(cell); + } + } + + + + /** + * Partition with METIS the locally owned regions of the given + * triangulation. + * + * @note The given triangulation must be a parallel::fullydistributed::Triangulation. This is + * required as the partitions generated by p4est, the partitioner for + * parallell::distributed::Triangulation, can generate discontinuous + * partitions which are not supported by the METIS partitioner. + * + */ + template + void + partition_locally_owned_regions(const unsigned int n_partitions, + Triangulation &triangulation, + const SparsityTools::Partitioner partitioner) + { + AssertDimension(dim, spacedim); + Assert(n_partitions > 0, + ExcMessage("Invalid number of partitions, you provided " + + std::to_string(n_partitions))); + + auto parallel_triangulation = + dynamic_cast *>( + &triangulation); + Assert( + (parallel_triangulation != nullptr), + ExcMessage( + "Only fully distributed triangulations are supported. If you are using" + "a parallel::distributed::triangulation, you must convert it to a fully" + "distributed as explained in the documentation.")); + + // check for an easy return + if (n_partitions == 1) + { + for (const auto &cell : parallel_triangulation->active_cell_iterators()) + if (cell->is_locally_owned()) + cell->set_material_id(0); + return; + } + + // collect all locally owned cells + std::vector locally_owned_cells; + for (const auto &cell : triangulation.active_cell_iterators()) + if (cell->is_locally_owned()) + locally_owned_cells.push_back(cell->active_cell_index()); + + DynamicSparsityPattern cell_connectivity; + internal::get_face_connectivity_of_cells(*parallel_triangulation, + cell_connectivity, + locally_owned_cells); + + SparsityPattern sp_cell_connectivity; + sp_cell_connectivity.copy_from(cell_connectivity); + + // partition each locally owned connection graph and get + // back a vector of indices, one per degree + // of freedom (which is associated with a + // cell) + std::vector partition_indices( + parallel_triangulation->n_locally_owned_active_cells()); + SparsityTools::partition(sp_cell_connectivity, + n_partitions, + partition_indices, + partitioner); + + + // finally loop over all cells and set the material ids + for (const auto &cell : parallel_triangulation->active_cell_iterators()) + if (cell->is_locally_owned()) + cell->set_material_id( + partition_indices[internal::get_index(locally_owned_cells, + cell->active_cell_index())]); + } + + + + /** + * Partition with METIS the locally owned regions of the given + * triangulation and insert agglomerates in the polytopic grid. + * + * @note The given triangulation must be a parallel::fullydistributed::Triangulation. This is + * required as the partitions generated by p4est, the partitioner for + * parallell::distributed::Triangulation, can generate discontinuous + * partitions which are not supported by the METIS partitioner. + * + */ + template + void + partition_locally_owned_regions( + AgglomerationHandler &agglomeration_handler, + const unsigned int n_partitions, + Triangulation &triangulation, + const SparsityTools::Partitioner partitioner) + { + AssertDimension(dim, spacedim); + Assert( + agglomeration_handler.n_agglomerates() == 0, + ExcMessage( + "The agglomerated grid must be empty upon calling this function.")); + Assert(n_partitions > 0, + ExcMessage("Invalid number of partitions, you provided " + + std::to_string(n_partitions))); + + auto parallel_triangulation = + dynamic_cast *>( + &triangulation); + Assert( + (parallel_triangulation != nullptr), + ExcMessage( + "Only fully distributed triangulations are supported. If you are using" + "a parallel::distributed::triangulation, you must convert it to a" + "fully distributed as explained in the documentation.")); + + // check for an easy return + if (n_partitions == 1) + { + for (const auto &cell : parallel_triangulation->active_cell_iterators()) + if (cell->is_locally_owned()) + agglomeration_handler.define_agglomerate({cell}); + return; + } + + // collect all locally owned cells + std::vector locally_owned_cells; + for (const auto &cell : triangulation.active_cell_iterators()) + if (cell->is_locally_owned()) + locally_owned_cells.push_back(cell->active_cell_index()); + + DynamicSparsityPattern cell_connectivity; + internal::get_face_connectivity_of_cells(*parallel_triangulation, + cell_connectivity, + locally_owned_cells); + + SparsityPattern sp_cell_connectivity; + sp_cell_connectivity.copy_from(cell_connectivity); + + // partition each locally owned connection graph and get + // back a vector of indices, one per degree + // of freedom (which is associated with a + // cell) + std::vector partition_indices( + parallel_triangulation->n_locally_owned_active_cells()); + SparsityTools::partition(sp_cell_connectivity, + n_partitions, + partition_indices, + partitioner); + + std::vector::active_cell_iterator>> + cells_per_partion_id; + cells_per_partion_id.resize(n_partitions); // number of agglomerates + + // finally loop over all cells and store the ones with same partition index + for (const auto &cell : parallel_triangulation->active_cell_iterators()) + if (cell->is_locally_owned()) + cells_per_partion_id[partition_indices[internal::get_index( + locally_owned_cells, cell->active_cell_index())]] + .push_back(cell); + + // All the cells with the same partition index will be merged together. + for (unsigned int i = 0; i < n_partitions; ++i) + agglomeration_handler.define_agglomerate(cells_per_partion_id[i]); + } + + + + template + std:: + tuple, std::vector, std::vector, double> + compute_quality_metrics(const AgglomerationHandler &ah) + { + static_assert(dim == 2); // only 2D case is implemented. +#ifdef DEAL_II_WITH_CGAL + using Kernel = CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt; + using Polygon_with_holes = typename CGAL::Polygon_with_holes_2; + using Gt = typename CGAL::Segment_Delaunay_graph_traits_2; + using SDG2 = typename CGAL::Segment_Delaunay_graph_2; + using CDT = typename CGAL::Constrained_Delaunay_triangulation_2; + using CDTP = typename CGAL::Constrained_triangulation_plus_2; + using Point = typename CDTP::Point; + using Cid = typename CDTP::Constraint_id; + using Vertex_handle = typename CDTP::Vertex_handle; + + + const auto compute_radius_inscribed_circle = + [](const CGAL::Polygon_2 &polygon) -> double { + SDG2 sdg; + + sdg.insert_segments(polygon.edges_begin(), polygon.edges_end()); + + double sd = 0, sqdist = 0; + typename SDG2::Finite_faces_iterator fit = sdg.finite_faces_begin(); + for (; fit != sdg.finite_faces_end(); ++fit) + { + typename Kernel::Point_2 pp = sdg.primal(fit); + for (int i = 0; i < 3; ++i) + { + assert(!sdg.is_infinite(fit->vertex(i))); + if (fit->vertex(i)->site().is_segment()) + { + typename Kernel::Segment_2 s = + fit->vertex(i)->site().segment(); + sqdist = CGAL::to_double(CGAL::squared_distance(pp, s)); + } + else + { + typename Kernel::Point_2 p = fit->vertex(i)->site().point(); + sqdist = CGAL::to_double(CGAL::squared_distance(pp, p)); + } + } + + if (polygon.bounded_side(pp) == CGAL::ON_BOUNDED_SIDE) + sd = std::max(sqdist, sd); + } + + return std::sqrt(sd); + }; + + const auto mesh_size = [&ah]() -> double { + double hmax = 0.; + for (const auto &polytope : ah.polytope_iterators()) + if (polytope->is_locally_owned()) + { + const double diameter = polytope->diameter(); + if (diameter > hmax) + hmax = diameter; + } + return hmax; + }(); + + + // vectors holding quality metrics + + // ration between radius of radius_inscribed_circle and circumscribed circle + std::vector circle_ratios; + std::vector unformity_factors; // diameter of element over mesh size + std::vector + box_ratio; // ratio between measure of bbox and measure of element. + + const std::vector> &bboxes = ah.get_local_bboxes(); + // Loop over all polytopes and compute metrics. + for (const auto &polytope : ah.polytope_iterators()) + { + if (polytope->is_locally_owned()) + { + const std::vector::active_face_iterator> + &boundary = polytope->polytope_boundary(); + + const double diameter = polytope->diameter(); + const double radius_circumscribed_circle = .5 * diameter; + + CDTP cdtp; + for (unsigned int f = 0; f < boundary.size(); f += 1) + { + // polyline + cdtp.insert_constraint( + {boundary[f]->vertex(0)[0], boundary[f]->vertex(0)[1]}, + {boundary[f]->vertex(1)[0], boundary[f]->vertex(1)[1]}); + } + cdtp.split_subconstraint_graph_into_constraints(); + + CGAL::Polygon_2 outer_polygon; + auto it = outer_polygon.vertices_begin(); + for (typename CDTP::Constraint_id cid : cdtp.constraints()) + { + for (typename CDTP::Vertex_handle vh : + cdtp.vertices_in_constraint(cid)) + { + it = outer_polygon.insert(outer_polygon.vertices_end(), + vh->point()); + } + } + outer_polygon.erase(it); // remove duplicate final point + + const double radius_inscribed_circle = + compute_radius_inscribed_circle(outer_polygon); + + circle_ratios.push_back(radius_inscribed_circle / + radius_circumscribed_circle); + unformity_factors.push_back(diameter / mesh_size); + + // box_ratio + + const auto &agglo_values = ah.reinit(polytope); + const double measure_element = + std::accumulate(agglo_values.get_JxW_values().cbegin(), + agglo_values.get_JxW_values().cend(), + 0.); + box_ratio.push_back(measure_element / + bboxes[polytope->index()].volume()); + } + } + + + + // Get all of the local bounding boxes + double covering_bboxes = 0.; + for (unsigned int i = 0; i < bboxes.size(); ++i) + covering_bboxes += bboxes[i].volume(); + + const double overlap_factor = + Utilities::MPI::sum(covering_bboxes, + ah.get_dof_handler().get_communicator()) / + GridTools::volume(ah.get_triangulation()); // assuming a linear mapping + + + + return {unformity_factors, circle_ratios, box_ratio, overlap_factor}; +#else + + (void)ah; + return {}; +#endif + } + + + /** + * Export each polygon in a csv file as a collection of segments. + */ + template + void + export_polygon_to_csv_file( + const AgglomerationHandler &agglomeration_handler, + const std::string &filename) + { + static_assert(dim == 2); // With 3D, Paraview is much better + std::ofstream myfile; + myfile.open(filename + ".csv"); + + for (const auto &polytope : agglomeration_handler.polytope_iterators()) + if (polytope->is_locally_owned()) + { + const std::vector::active_face_iterator> + &boundary = polytope->polytope_boundary(); + for (unsigned int f = 0; f < boundary.size(); ++f) + { + myfile << boundary[f]->vertex(0)[0]; + myfile << ","; + myfile << boundary[f]->vertex(0)[1]; + myfile << ","; + myfile << boundary[f]->vertex(1)[0]; + myfile << ","; + myfile << boundary[f]->vertex(1)[1]; + myfile << "\n"; + } + } + + + myfile.close(); + } + + + template + inline constexpr T + constexpr_pow(T num, unsigned int pow) + { + return (pow >= sizeof(unsigned int) * 8) ? 0 : + pow == 0 ? 1 : + num * constexpr_pow(num, pow - 1); + } + + + + void + write_to_matrix_market_format(const std::string &filename, + const std::string &matrix_name, + const TrilinosWrappers::SparseMatrix &matrix) + { +#ifdef DEAL_II_WITH_TRILINOS + const Epetra_CrsMatrix &trilinos_matrix = matrix.trilinos_matrix(); + + const int ierr = + EpetraExt::RowMatrixToMatrixMarketFile(filename.c_str(), + trilinos_matrix, + matrix_name.c_str(), + 0 /*description field empty*/, + true /*write header*/); + AssertThrow(ierr == 0, ExcTrilinosError(ierr)); +#else + (void)filename; + (void)matrix_name; + (void)matrix; +#endif + } + + + + namespace internal + { + /** + * Same as the public free function with the same name, but storing + * explicitly the interpolation matrix and performing interpolation through + * matrix-vector product. + */ + template + void + interpolate_to_fine_grid( + const AgglomerationHandler &agglomeration_handler, + VectorType &dst, + const VectorType &src) + { + Assert((dim == spacedim), ExcNotImplemented()); + Assert( + dst.size() == 0, + ExcMessage( + "The destination vector must the empt upon calling this function.")); + + using NumberType = typename VectorType::value_type; + constexpr bool is_trilinos_vector = + std::is_same_v; + using MatrixType = std::conditional_t>; + + MatrixType interpolation_matrix; + + [[maybe_unused]] + typename std::conditional_t + sp; + + // Get some info from the handler + const DoFHandler &agglo_dh = + agglomeration_handler.agglo_dh; + + DoFHandler *output_dh = + const_cast *>( + &agglomeration_handler.output_dh); + const FiniteElement &fe = agglomeration_handler.get_fe(); + const Mapping &mapping = agglomeration_handler.get_mapping(); + const Triangulation &tria = + agglomeration_handler.get_triangulation(); + const auto &bboxes = agglomeration_handler.get_local_bboxes(); + + std::unique_ptr> output_fe; + if (tria.all_reference_cells_are_hyper_cube()) + output_fe = std::make_unique>(fe.degree); + else if (tria.all_reference_cells_are_simplex()) + output_fe = std::make_unique>(fe.degree); + else + AssertThrow(false, ExcNotImplemented()); + + // Setup an auxiliary DoFHandler for output purposes + output_dh->reinit(tria); + output_dh->distribute_dofs(*output_fe); + + const IndexSet &locally_owned_dofs = output_dh->locally_owned_dofs(); + const IndexSet locally_relevant_dofs = + DoFTools::extract_locally_relevant_dofs(*output_dh); + + const IndexSet &locally_owned_dofs_agglo = agglo_dh.locally_owned_dofs(); + + + DynamicSparsityPattern dsp(output_dh->n_dofs(), + agglo_dh.n_dofs(), + locally_relevant_dofs); + + std::vector agglo_dof_indices(fe.dofs_per_cell); + std::vector standard_dof_indices( + fe.dofs_per_cell); + std::vector output_dof_indices( + output_fe->dofs_per_cell); + + Quadrature quad(output_fe->get_unit_support_points()); + FEValues output_fe_values(mapping, + *output_fe, + quad, + update_quadrature_points); + + for (const auto &cell : agglo_dh.active_cell_iterators()) + if (cell->is_locally_owned()) + { + if (agglomeration_handler.is_master_cell(cell)) + { + auto slaves = agglomeration_handler.get_slaves_of_idx( + cell->active_cell_index()); + slaves.emplace_back(cell); + + cell->get_dof_indices(agglo_dof_indices); + + for (const auto &slave : slaves) + { + // addd master-slave relationship + const auto slave_output = + slave->as_dof_handler_iterator(*output_dh); + slave_output->get_dof_indices(output_dof_indices); + for (const auto row : output_dof_indices) + dsp.add_entries(row, + agglo_dof_indices.begin(), + agglo_dof_indices.end()); + } + } + } + + + const auto assemble_interpolation_matrix = [&]() { + FullMatrix local_matrix(fe.dofs_per_cell, fe.dofs_per_cell); + std::vector> reference_q_points(fe.dofs_per_cell); + + // Dummy AffineConstraints, only needed for loc2glb + AffineConstraints c; + c.close(); + + for (const auto &cell : agglo_dh.active_cell_iterators()) + if (cell->is_locally_owned()) + { + if (agglomeration_handler.is_master_cell(cell)) + { + auto slaves = agglomeration_handler.get_slaves_of_idx( + cell->active_cell_index()); + slaves.emplace_back(cell); + + cell->get_dof_indices(agglo_dof_indices); + + const types::global_cell_index polytope_index = + agglomeration_handler.cell_to_polytope_index(cell); + + // Get the box of this agglomerate. + const BoundingBox &box = bboxes[polytope_index]; + + for (const auto &slave : slaves) + { + // add master-slave relationship + const auto slave_output = + slave->as_dof_handler_iterator(*output_dh); + + slave_output->get_dof_indices(output_dof_indices); + output_fe_values.reinit(slave_output); + + local_matrix = 0.; + + const auto &q_points = + output_fe_values.get_quadrature_points(); + for (const auto i : output_fe_values.dof_indices()) + { + const auto &p = box.real_to_unit(q_points[i]); + for (const auto j : output_fe_values.dof_indices()) + { + local_matrix(i, j) = fe.shape_value(j, p); + } + } + c.distribute_local_to_global(local_matrix, + output_dof_indices, + agglo_dof_indices, + interpolation_matrix); + } + } + } + }; + + + if constexpr (std::is_same_v) + { + const MPI_Comm &communicator = tria.get_communicator(); + SparsityTools::distribute_sparsity_pattern(dsp, + locally_owned_dofs, + communicator, + locally_relevant_dofs); + + interpolation_matrix.reinit(locally_owned_dofs, + locally_owned_dofs_agglo, + dsp, + communicator); + dst.reinit(locally_owned_dofs); + assemble_interpolation_matrix(); + } + else if constexpr (std::is_same_v>) + { + sp.copy_from(dsp); + interpolation_matrix.reinit(sp); + dst.reinit(output_dh->n_dofs()); + assemble_interpolation_matrix(); + } + else + { + // PETSc, LA::d::v options not implemented. + (void)agglomeration_handler; + (void)dst; + (void)src; + AssertThrow(false, ExcNotImplemented()); + } + + // If tria is distributed + if (dynamic_cast *>( + &tria) != nullptr) + interpolation_matrix.compress(VectorOperation::add); + + // Finally, perform the interpolation. + interpolation_matrix.vmult(dst, src); + } + } // namespace internal + + + + /** + * Given a vector @p src, typically the solution stemming after the + * agglomerate problem has been solved, this function interpolates @p src + * onto the finer grid and stores the result in vector @p dst. The last + * argument @p on_the_fly does not build any interpolation matrix and allows + * computing the entries in @p dst in a matrix-free fashion. + * + * @note Supported parallel types are TrilinosWrappers::SparseMatrix and + * TrilinosWrappers::MPI::Vector. + */ + template + void + interpolate_to_fine_grid( + const AgglomerationHandler &agglomeration_handler, + VectorType &dst, + const VectorType &src, + const bool on_the_fly = true) + { + Assert((dim == spacedim), ExcNotImplemented()); + Assert( + dst.size() == 0, + ExcMessage( + "The destination vector must the empt upon calling this function.")); + + using NumberType = typename VectorType::value_type; + static constexpr bool is_trilinos_vector = + std::is_same_v; + + static constexpr bool is_supported_vector = + std::is_same_v> || is_trilinos_vector; + static_assert(is_supported_vector); + + // First, check for an easy return + if (on_the_fly == false) + { + return internal::interpolate_to_fine_grid(agglomeration_handler, + dst, + src); + } + else + { + // otherwise, do not create any matrix + if (!agglomeration_handler.used_fe_collection()) + { + // Original version: handle case without hp::FECollection + const Triangulation &tria = + agglomeration_handler.get_triangulation(); + const Mapping &mapping = agglomeration_handler.get_mapping(); + const FiniteElement &original_fe = + agglomeration_handler.get_fe(); + + // We use DGQ (on tensor-product meshes) or DGP (on simplex meshes) + // nodal elements of the same degree as the ones in the + // agglomeration handler to interpolate the solution onto the finer + // grid. + std::unique_ptr> output_fe; + if (tria.all_reference_cells_are_hyper_cube()) + output_fe = std::make_unique>(original_fe.degree); + else if (tria.all_reference_cells_are_simplex()) + output_fe = + std::make_unique>(original_fe.degree); + else + AssertThrow(false, ExcNotImplemented()); + + DoFHandler &output_dh = + const_cast &>(agglomeration_handler.output_dh); + output_dh.reinit(tria); + output_dh.distribute_dofs(*output_fe); + + if constexpr (std::is_same_v) + { + const IndexSet &locally_owned_dofs = + output_dh.locally_owned_dofs(); + dst.reinit(locally_owned_dofs); + } + else if constexpr (std::is_same_v>) + { + dst.reinit(output_dh.n_dofs()); + } + else + { + // PETSc, LA::d::v options not implemented. + (void)agglomeration_handler; + (void)dst; + (void)src; + AssertThrow(false, ExcNotImplemented()); + } + + + + const unsigned int dofs_per_cell = + agglomeration_handler.n_dofs_per_cell(); + const unsigned int output_dofs_per_cell = + output_fe->n_dofs_per_cell(); + Quadrature quad(output_fe->get_unit_support_points()); + FEValues output_fe_values(mapping, + *output_fe, + quad, + update_quadrature_points); + + std::vector local_dof_indices( + dofs_per_cell); + std::vector local_dof_indices_output( + output_dofs_per_cell); + + const auto &bboxes = agglomeration_handler.get_local_bboxes(); + for (const auto &polytope : + agglomeration_handler.polytope_iterators()) + { + if (polytope->is_locally_owned()) + { + polytope->get_dof_indices(local_dof_indices); + const BoundingBox &box = bboxes[polytope->index()]; + + const auto &deal_cells = + polytope->get_agglomerate(); // fine deal.II cells + for (const auto &cell : deal_cells) + { + const auto slave_output = cell->as_dof_handler_iterator( + agglomeration_handler.output_dh); + slave_output->get_dof_indices(local_dof_indices_output); + output_fe_values.reinit(slave_output); + + const auto &qpoints = + output_fe_values.get_quadrature_points(); + + for (unsigned int j = 0; j < output_dofs_per_cell; ++j) + { + const auto &ref_qpoint = + box.real_to_unit(qpoints[j]); + for (unsigned int i = 0; i < dofs_per_cell; ++i) + dst(local_dof_indices_output[j]) += + src(local_dof_indices[i]) * + original_fe.shape_value(i, ref_qpoint); + } + } + } + } + } + else + { + // Handle the hp::FECollection case + const Triangulation &tria = + agglomeration_handler.get_triangulation(); + const Mapping &mapping = agglomeration_handler.get_mapping(); + const hp::FECollection &original_fe_collection = + agglomeration_handler.get_fe_collection(); + + // We use DGQ (on tensor-product meshes) or DGP (on simplex meshes) + // nodal elements of the same degree as the ones in the + // agglomeration handler to interpolate the solution onto the finer + // grid. + hp::FECollection output_fe_collection; + + Assert(original_fe_collection[0].n_components() >= 1, + ExcMessage("Invalid FE: must have at least one component.")); + if (original_fe_collection[0].n_components() == 1) + { + // Scalar case + for (unsigned int i = 0; i < original_fe_collection.size(); ++i) + { + std::unique_ptr> output_fe; + if (tria.all_reference_cells_are_hyper_cube()) + output_fe = std::make_unique>( + original_fe_collection[i].degree); + else if (tria.all_reference_cells_are_simplex()) + output_fe = std::make_unique>( + original_fe_collection[i].degree); + else + AssertThrow(false, ExcNotImplemented()); + output_fe_collection.push_back(*output_fe); + } + } + else if (original_fe_collection[0].n_components() > 1) + { + // System case + for (unsigned int i = 0; i < original_fe_collection.size(); ++i) + { + std::vector *> + base_elements; + std::vector multiplicities; + for (unsigned int b = 0; + b < original_fe_collection[i].n_base_elements(); + ++b) + { + if (dynamic_cast *>( + &original_fe_collection[i].base_element(b))) + base_elements.push_back( + new FE_Nothing()); + else + { + if (tria.all_reference_cells_are_hyper_cube()) + base_elements.push_back(new FE_DGQ( + original_fe_collection[i] + .base_element(b) + .degree)); + else if (tria.all_reference_cells_are_simplex()) + base_elements.push_back( + new FE_SimplexDGP( + original_fe_collection[i] + .base_element(b) + .degree)); + else + AssertThrow(false, ExcNotImplemented()); + } + multiplicities.push_back( + original_fe_collection[i].element_multiplicity(b)); + } + + FESystem output_fe_system(base_elements, + multiplicities); + for (const auto *ptr : base_elements) + delete ptr; + output_fe_collection.push_back(output_fe_system); + } + } + + + DoFHandler &output_dh = + const_cast &>(agglomeration_handler.output_dh); + output_dh.reinit(tria); + for (const auto &polytope : + agglomeration_handler.polytope_iterators()) + { + if (polytope->is_locally_owned()) + { + const auto &deal_cells = + polytope->get_agglomerate(); // fine deal.II cells + const unsigned int active_fe_idx = + polytope->active_fe_index(); + + for (const auto &cell : deal_cells) + { + const typename DoFHandler::active_cell_iterator + slave_cell_dh_iterator = + cell->as_dof_handler_iterator(output_dh); + slave_cell_dh_iterator->set_active_fe_index( + active_fe_idx); + } + } + } + output_dh.distribute_dofs(output_fe_collection); + + if constexpr (std::is_same_v) + { + const IndexSet &locally_owned_dofs = + output_dh.locally_owned_dofs(); + dst.reinit(locally_owned_dofs); + } + else if constexpr (std::is_same_v>) + { + dst.reinit(output_dh.n_dofs()); + } + else + { + // PETSc, LA::d::v options not implemented. + (void)agglomeration_handler; + (void)dst; + (void)src; + AssertThrow(false, ExcNotImplemented()); + } + + const auto &bboxes = agglomeration_handler.get_local_bboxes(); + for (const auto &polytope : + agglomeration_handler.polytope_iterators()) + { + if (polytope->is_locally_owned()) + { + const unsigned int active_fe_idx = + polytope->active_fe_index(); + const unsigned int dofs_per_cell = + polytope->get_fe().dofs_per_cell; + const unsigned int output_dofs_per_cell = + output_fe_collection[active_fe_idx].n_dofs_per_cell(); + Quadrature quad(output_fe_collection[active_fe_idx] + .get_unit_support_points()); + FEValues output_fe_values( + mapping, + output_fe_collection[active_fe_idx], + quad, + update_quadrature_points); + std::vector local_dof_indices( + dofs_per_cell); + std::vector + local_dof_indices_output(output_dofs_per_cell); + + polytope->get_dof_indices(local_dof_indices); + const BoundingBox &box = bboxes[polytope->index()]; + + const auto &deal_cells = + polytope->get_agglomerate(); // fine deal.II cells + for (const auto &cell : deal_cells) + { + const auto slave_output = cell->as_dof_handler_iterator( + agglomeration_handler.output_dh); + slave_output->get_dof_indices(local_dof_indices_output); + output_fe_values.reinit(slave_output); + + const auto &qpoints = + output_fe_values.get_quadrature_points(); + + for (unsigned int j = 0; j < output_dofs_per_cell; ++j) + { + const unsigned int component_idx_of_this_dof = + slave_output->get_fe() + .system_to_component_index(j) + .first; + const auto &ref_qpoint = + box.real_to_unit(qpoints[j]); + for (unsigned int i = 0; i < dofs_per_cell; ++i) + dst(local_dof_indices_output[j]) += + src(local_dof_indices[i]) * + original_fe_collection[active_fe_idx] + .shape_value_component( + i, ref_qpoint, component_idx_of_this_dof); + } + } + } + } + } + } + } + + + + /** + * Construct the interpolation matrix from the DG space defined the + * polytopic elements defined in @p agglomeration_handler to the DG space + * defined on the @p DoFHandler associated to standard shapes. The + * interpolation matrix is assumed to be default-constructed and is filled + * inside this function. + */ + template + void + fill_interpolation_matrix( + const AgglomerationHandler &agglomeration_handler, + MatrixType &interpolation_matrix) + { + Assert((dim == spacedim), ExcNotImplemented()); + + using NumberType = typename MatrixType::value_type; + constexpr bool is_trilinos_matrix = + std::is_same_v; + + [[maybe_unused]] + typename std::conditional_t + sp; + + // Get some info from the handler + const DoFHandler &agglo_dh = agglomeration_handler.agglo_dh; + + DoFHandler *output_dh = + const_cast *>(&agglomeration_handler.output_dh); + const Mapping &mapping = agglomeration_handler.get_mapping(); + const FiniteElement &fe = agglomeration_handler.get_fe(); + const Triangulation &tria = + agglomeration_handler.get_triangulation(); + const auto &bboxes = agglomeration_handler.get_local_bboxes(); + + // Setup an auxiliary DoFHandler for output purposes + output_dh->reinit(tria); + output_dh->distribute_dofs(fe); + + const IndexSet &locally_owned_dofs = output_dh->locally_owned_dofs(); + const IndexSet locally_relevant_dofs = + DoFTools::extract_locally_relevant_dofs(*output_dh); + + const IndexSet &locally_owned_dofs_agglo = agglo_dh.locally_owned_dofs(); + + + DynamicSparsityPattern dsp(output_dh->n_dofs(), + agglo_dh.n_dofs(), + locally_relevant_dofs); + + std::vector agglo_dof_indices(fe.dofs_per_cell); + std::vector standard_dof_indices(fe.dofs_per_cell); + std::vector output_dof_indices(fe.dofs_per_cell); + + Quadrature quad(fe.get_unit_support_points()); + FEValues output_fe_values(mapping, + fe, + quad, + update_quadrature_points); + + for (const auto &cell : agglo_dh.active_cell_iterators()) + if (cell->is_locally_owned()) + { + if (agglomeration_handler.is_master_cell(cell)) + { + auto slaves = agglomeration_handler.get_slaves_of_idx( + cell->active_cell_index()); + slaves.emplace_back(cell); + + cell->get_dof_indices(agglo_dof_indices); + + for (const auto &slave : slaves) + { + // addd master-slave relationship + const auto slave_output = + slave->as_dof_handler_iterator(*output_dh); + slave_output->get_dof_indices(output_dof_indices); + for (const auto row : output_dof_indices) + dsp.add_entries(row, + agglo_dof_indices.begin(), + agglo_dof_indices.end()); + } + } + } + + + const auto assemble_interpolation_matrix = [&]() { + FullMatrix local_matrix(fe.dofs_per_cell, fe.dofs_per_cell); + std::vector> reference_q_points(fe.dofs_per_cell); + + // Dummy AffineConstraints, only needed for loc2glb + AffineConstraints c; + c.close(); + + for (const auto &cell : agglo_dh.active_cell_iterators()) + if (cell->is_locally_owned()) + { + if (agglomeration_handler.is_master_cell(cell)) + { + auto slaves = agglomeration_handler.get_slaves_of_idx( + cell->active_cell_index()); + slaves.emplace_back(cell); + + cell->get_dof_indices(agglo_dof_indices); + + const types::global_cell_index polytope_index = + agglomeration_handler.cell_to_polytope_index(cell); + + // Get the box of this agglomerate. + const BoundingBox &box = bboxes[polytope_index]; + + for (const auto &slave : slaves) + { + // add master-slave relationship + const auto slave_output = + slave->as_dof_handler_iterator(*output_dh); + + slave_output->get_dof_indices(output_dof_indices); + output_fe_values.reinit(slave_output); + + local_matrix = 0.; + + const auto &q_points = + output_fe_values.get_quadrature_points(); + for (const auto i : output_fe_values.dof_indices()) + { + const auto &p = box.real_to_unit(q_points[i]); + for (const auto j : output_fe_values.dof_indices()) + { + local_matrix(i, j) = fe.shape_value(j, p); + } + } + c.distribute_local_to_global(local_matrix, + output_dof_indices, + agglo_dof_indices, + interpolation_matrix); + } + } + } + }; + + + if constexpr (std::is_same_v) + { + const MPI_Comm &communicator = tria.get_communicator(); + SparsityTools::distribute_sparsity_pattern(dsp, + locally_owned_dofs, + communicator, + locally_relevant_dofs); + + interpolation_matrix.reinit(locally_owned_dofs, + locally_owned_dofs_agglo, + dsp, + communicator); + assemble_interpolation_matrix(); + } + else if constexpr (std::is_same_v>) + { + sp.copy_from(dsp); + interpolation_matrix.reinit(sp); + assemble_interpolation_matrix(); + } + else + { + // PETSc, LA::d::v options not implemented. + (void)agglomeration_handler; + AssertThrow(false, ExcNotImplemented()); + } + + // If tria is distributed + if (dynamic_cast *>( + &tria) != nullptr) + interpolation_matrix.compress(VectorOperation::add); + } + + + + /** + * Similar to VectorTools::compute_global_error(), but customized for + * polytopic elements. Aside from the solution vector and a reference + * function, this function takes in addition a vector @p norms with types + * VectorTools::NormType to be computed and later stored in the last + * argument @p global_errors. + * In case of a parallel vector, the local errors are collected over each + * processor and later a classical reduction operation is performed. + */ + template + void + compute_global_error(const AgglomerationHandler &agglomeration_handler, + const VectorType &solution, + const Function &exact_solution, + const std::vector &norms, + std::vector &global_errors) + { + Assert(solution.size() > 0, + ExcNotImplemented( + "Solution vector must be non-empty upon calling this function.")); + Assert(std::any_of(norms.cbegin(), + norms.cend(), + [](VectorTools::NormType norm_type) { + return (norm_type == + VectorTools::NormType::H1_seminorm || + norm_type == VectorTools::NormType::L2_norm); + }), + ExcMessage("Norm type not supported")); + global_errors.resize(norms.size()); + std::fill(global_errors.begin(), global_errors.end(), 0.); + + // Vector storing errors local to the current processor. + std::vector local_errors(norms.size()); + std::fill(local_errors.begin(), local_errors.end(), 0.); + + // Get some info from the handler + const unsigned int dofs_per_cell = agglomeration_handler.n_dofs_per_cell(); + + const bool compute_semi_H1 = + std::any_of(norms.cbegin(), + norms.cend(), + [](VectorTools::NormType norm_type) { + return norm_type == VectorTools::NormType::H1_seminorm; + }); + + std::vector local_dof_indices(dofs_per_cell); + for (const auto &polytope : agglomeration_handler.polytope_iterators()) + { + if (polytope->is_locally_owned()) + { + const auto &agglo_values = agglomeration_handler.reinit(polytope); + polytope->get_dof_indices(local_dof_indices); + + const auto &q_points = agglo_values.get_quadrature_points(); + const unsigned int n_qpoints = q_points.size(); + std::vector analyical_sol_at_qpoints(n_qpoints); + exact_solution.value_list(q_points, analyical_sol_at_qpoints); + std::vector> grad_analyical_sol_at_qpoints( + n_qpoints); + + if (compute_semi_H1) + exact_solution.gradient_list(q_points, + grad_analyical_sol_at_qpoints); + + for (unsigned int q_index : agglo_values.quadrature_point_indices()) + { + double solution_at_qpoint = 0.; + Tensor<1, dim> grad_solution_at_qpoint; + for (unsigned int i = 0; i < dofs_per_cell; ++i) + { + solution_at_qpoint += solution(local_dof_indices[i]) * + agglo_values.shape_value(i, q_index); + + if (compute_semi_H1) + grad_solution_at_qpoint += + solution(local_dof_indices[i]) * + agglo_values.shape_grad(i, q_index); + } + // L2 + local_errors[0] += std::pow((analyical_sol_at_qpoints[q_index] - + solution_at_qpoint), + 2) * + agglo_values.JxW(q_index); + + // H1 seminorm + if (compute_semi_H1) + for (unsigned int d = 0; d < dim; ++d) + local_errors[1] += + std::pow((grad_analyical_sol_at_qpoints[q_index][d] - + grad_solution_at_qpoint[d]), + 2) * + agglo_values.JxW(q_index); + } + } + } + + // Perform reduction and take sqrt of each error + global_errors[0] = Utilities::MPI::reduce( + local_errors[0], + agglomeration_handler.get_triangulation().get_communicator(), + [](const double a, const double b) { return a + b; }); + + global_errors[0] = std::sqrt(global_errors[0]); + + if (compute_semi_H1) + { + global_errors[1] = Utilities::MPI::reduce( + local_errors[1], + agglomeration_handler.get_triangulation().get_communicator(), + [](const double a, const double b) { return a + b; }); + global_errors[1] = std::sqrt(global_errors[1]); + } + } + + + + /** + * Utility function that builds the multilevel hierarchy from the tree level + * @p starting_level. This function fills the vector of + * @p AgglomerationHandlers objects by distributing degrees of freedom on + * each level of the hierarchy. It returns the total number of levels in the + * hierarchy. + */ + template + unsigned int + construct_agglomerated_levels( + const Triangulation &tria, + std::vector>> + &agglomeration_handlers, + const FE_DGQ &fe_dg, + const Mapping &mapping, + const unsigned int starting_tree_level) + { + const auto parallel_tria = + dynamic_cast *>(&tria); + + GridTools::Cache cached_tria(tria); + Assert(parallel_tria->n_active_cells() > 0, ExcInternalError()); + + const MPI_Comm comm = parallel_tria->get_communicator(); + ConditionalOStream pcout(std::cout, + (Utilities::MPI::this_mpi_process(comm) == 0)); + + // Start building R-tree + namespace bgi = boost::geometry::index; + static constexpr unsigned int max_elem_per_node = + constexpr_pow(2, dim); // 2^dim + std::vector, + typename Triangulation::active_cell_iterator>> + boxes(parallel_tria->n_locally_owned_active_cells()); + unsigned int i = 0; + for (const auto &cell : parallel_tria->active_cell_iterators()) + if (cell->is_locally_owned()) + boxes[i++] = std::make_pair(mapping.get_bounding_box(cell), cell); + + auto tree = pack_rtree>(boxes); + Assert(n_levels(tree) >= 2, ExcMessage("At least two levels are needed.")); + pcout << "Total number of available levels: " << n_levels(tree) + << std::endl; + + pcout << "Starting level: " << starting_tree_level << std::endl; + const unsigned int total_tree_levels = + n_levels(tree) - starting_tree_level + 1; + + + // Resize the agglomeration handlers to the right size + + agglomeration_handlers.resize(total_tree_levels); + // Loop through the available levels and set AgglomerationHandlers up. + for (unsigned int extraction_level = starting_tree_level; + extraction_level <= n_levels(tree); + ++extraction_level) + { + agglomeration_handlers[extraction_level - starting_tree_level] = + std::make_unique>(cached_tria); + CellsAgglomerator agglomerator{tree, + extraction_level}; + const auto agglomerates = agglomerator.extract_agglomerates(); + agglomeration_handlers[extraction_level - starting_tree_level] + ->connect_hierarchy(agglomerator); + + // Flag elements for agglomeration + unsigned int agglo_index = 0; + for (unsigned int i = 0; i < agglomerates.size(); ++i) + { + const auto &agglo = agglomerates[i]; // i-th agglomerate + for (const auto &el : agglo) + { + el->set_material_id(agglo_index); + } + ++agglo_index; + } + + const unsigned int n_local_agglomerates = agglo_index; + unsigned int total_agglomerates = + Utilities::MPI::sum(n_local_agglomerates, comm); + pcout << "Total agglomerates per (tree) level: " << extraction_level + << ": " << total_agglomerates << std::endl; + + + // Now, perform agglomeration within each locally owned partition + std::vector< + std::vector::active_cell_iterator>> + cells_per_subdomain(n_local_agglomerates); + for (const auto &cell : parallel_tria->active_cell_iterators()) + if (cell->is_locally_owned()) + cells_per_subdomain[cell->material_id()].push_back(cell); + + // For every subdomain, agglomerate elements together + for (std::size_t i = 0; i < cells_per_subdomain.size(); ++i) + agglomeration_handlers[extraction_level - starting_tree_level] + ->define_agglomerate(cells_per_subdomain[i]); + + agglomeration_handlers[extraction_level - starting_tree_level] + ->initialize_fe_values(QGauss(fe_dg.degree + 1), + update_values | update_gradients | + update_JxW_values | update_quadrature_points, + QGauss(fe_dg.degree + 1), + update_JxW_values); + agglomeration_handlers[extraction_level - starting_tree_level] + ->distribute_agglomerated_dofs(fe_dg); + } + + return total_tree_levels; + } + + + + /** + * Utility to compute jump terms when the interface is locally owned, i.e. + * both elements are locally owned. + */ + template + void + assemble_local_jumps_and_averages(FullMatrix &M11, + FullMatrix &M12, + FullMatrix &M21, + FullMatrix &M22, + const FEValuesBase &fe_faces0, + const FEValuesBase &fe_faces1, + const double penalty_constant, + const double h_f) + { + const std::vector> &normals = fe_faces0.get_normal_vectors(); + const unsigned int dofs_per_cell = + M11.m(); // size of local matrices equals the #DoFs + for (unsigned int q_index : fe_faces0.quadrature_point_indices()) + { + const Tensor<1, dim> &normal = normals[q_index]; + for (unsigned int i = 0; i < dofs_per_cell; ++i) + { + for (unsigned int j = 0; j < dofs_per_cell; ++j) + { + M11(i, j) += (-0.5 * fe_faces0.shape_grad(i, q_index) * normal * + fe_faces0.shape_value(j, q_index) - + 0.5 * fe_faces0.shape_grad(j, q_index) * normal * + fe_faces0.shape_value(i, q_index) + + (penalty_constant / h_f) * + fe_faces0.shape_value(i, q_index) * + fe_faces0.shape_value(j, q_index)) * + fe_faces0.JxW(q_index); + M12(i, j) += (0.5 * fe_faces0.shape_grad(i, q_index) * normal * + fe_faces1.shape_value(j, q_index) - + 0.5 * fe_faces1.shape_grad(j, q_index) * normal * + fe_faces0.shape_value(i, q_index) - + (penalty_constant / h_f) * + fe_faces0.shape_value(i, q_index) * + fe_faces1.shape_value(j, q_index)) * + fe_faces1.JxW(q_index); + M21(i, j) += (-0.5 * fe_faces1.shape_grad(i, q_index) * normal * + fe_faces0.shape_value(j, q_index) + + 0.5 * fe_faces0.shape_grad(j, q_index) * normal * + fe_faces1.shape_value(i, q_index) - + (penalty_constant / h_f) * + fe_faces1.shape_value(i, q_index) * + fe_faces0.shape_value(j, q_index)) * + fe_faces1.JxW(q_index); + M22(i, j) += (0.5 * fe_faces1.shape_grad(i, q_index) * normal * + fe_faces1.shape_value(j, q_index) + + 0.5 * fe_faces1.shape_grad(j, q_index) * normal * + fe_faces1.shape_value(i, q_index) + + (penalty_constant / h_f) * + fe_faces1.shape_value(i, q_index) * + fe_faces1.shape_value(j, q_index)) * + fe_faces1.JxW(q_index); + } + } + } + } + /** + * Same as above, but for a ghosted neighbor. + */ + template + void + assemble_local_jumps_and_averages_ghost( + FullMatrix &M11, + FullMatrix &M12, + FullMatrix &M21, + FullMatrix &M22, + const FEValuesBase &fe_faces0, + const std::vector> &recv_values, + const std::vector>> &recv_gradients, + const std::vector &recv_jxws, + const double penalty_constant, + const double h_f) + { + Assert( + (recv_values.size() > 0 && recv_gradients.size() && recv_jxws.size()), + ExcMessage( + "Not possible to assemble jumps and averages at a ghosted interface.")); + const unsigned int dofs_per_cell = M11.m(); + const std::vector> &normals = fe_faces0.get_normal_vectors(); + for (unsigned int q_index : fe_faces0.quadrature_point_indices()) + { + const Tensor<1, dim> &normal = normals[q_index]; + for (unsigned int i = 0; i < dofs_per_cell; ++i) + { + for (unsigned int j = 0; j < dofs_per_cell; ++j) + { + M11(i, j) += (-0.5 * fe_faces0.shape_grad(i, q_index) * normal * + fe_faces0.shape_value(j, q_index) - + 0.5 * fe_faces0.shape_grad(j, q_index) * normal * + fe_faces0.shape_value(i, q_index) + + (penalty_constant / h_f) * + fe_faces0.shape_value(i, q_index) * + fe_faces0.shape_value(j, q_index)) * + fe_faces0.JxW(q_index); + M12(i, j) += (0.5 * fe_faces0.shape_grad(i, q_index) * normal * + recv_values[j][q_index] - + 0.5 * recv_gradients[j][q_index] * normal * + fe_faces0.shape_value(i, q_index) - + (penalty_constant / h_f) * + fe_faces0.shape_value(i, q_index) * + recv_values[j][q_index]) * + recv_jxws[q_index]; + M21(i, j) += + (-0.5 * recv_gradients[i][q_index] * normal * + fe_faces0.shape_value(j, q_index) + + 0.5 * fe_faces0.shape_grad(j, q_index) * normal * + recv_values[i][q_index] - + (penalty_constant / h_f) * recv_values[i][q_index] * + fe_faces0.shape_value(j, q_index)) * + recv_jxws[q_index]; + M22(i, j) += + (0.5 * recv_gradients[i][q_index] * normal * + recv_values[j][q_index] + + 0.5 * recv_gradients[j][q_index] * normal * + recv_values[i][q_index] + + (penalty_constant / h_f) * recv_values[i][q_index] * + recv_values[j][q_index]) * + recv_jxws[q_index]; + } + } + } + } + + + /** + * Utility function to assemble the SIPDG Laplace matrix. + * @note Supported matrix types are Trilinos types and native SparseMatrix + * objects provided by deal.II. + */ + template + void + assemble_dg_matrix(MatrixType &system_matrix, + const FiniteElement &fe_dg, + const AgglomerationHandler &ah) + { + static_assert( + (std::is_same_v || + std::is_same_v>)); + + Assert((dynamic_cast *>(&fe_dg) || + dynamic_cast *>(&fe_dg) || + dynamic_cast *>(&fe_dg)), + ExcMessage("FE type not supported.")); + + AffineConstraints constraints; + constraints.close(); + const double penalty_constant = + 10 * (fe_dg.degree + dim) * (fe_dg.degree + 1); + TrilinosWrappers::SparsityPattern dsp; + const_cast &>(ah) + .create_agglomeration_sparsity_pattern(dsp); + system_matrix.reinit(dsp); + const unsigned int dofs_per_cell = fe_dg.n_dofs_per_cell(); + FullMatrix cell_matrix(dofs_per_cell, dofs_per_cell); + FullMatrix M11(dofs_per_cell, dofs_per_cell); + FullMatrix M12(dofs_per_cell, dofs_per_cell); + FullMatrix M21(dofs_per_cell, dofs_per_cell); + FullMatrix M22(dofs_per_cell, dofs_per_cell); + std::vector local_dof_indices(dofs_per_cell); + std::vector local_dof_indices_neighbor( + dofs_per_cell); + + for (const auto &polytope : ah.polytope_iterators()) + { + if (polytope->is_locally_owned()) + { + cell_matrix = 0.; + const auto &agglo_values = ah.reinit(polytope); + for (unsigned int q_index : agglo_values.quadrature_point_indices()) + { + for (unsigned int i = 0; i < dofs_per_cell; ++i) + { + for (unsigned int j = 0; j < dofs_per_cell; ++j) + { + cell_matrix(i, j) += + agglo_values.shape_grad(i, q_index) * + agglo_values.shape_grad(j, q_index) * + agglo_values.JxW(q_index); + } + } + } + // get volumetric DoFs + polytope->get_dof_indices(local_dof_indices); + // Assemble face terms + unsigned int n_faces = polytope->n_faces(); + const double h_f = polytope->diameter(); + for (unsigned int f = 0; f < n_faces; ++f) + { + if (polytope->at_boundary(f)) + { + // Get normal vectors seen from each agglomeration. + const auto &fe_face = ah.reinit(polytope, f); + const auto &normals = fe_face.get_normal_vectors(); + for (unsigned int q_index : + fe_face.quadrature_point_indices()) + { + const Tensor<1, dim> &normal = normals[q_index]; + for (unsigned int i = 0; i < dofs_per_cell; ++i) + { + for (unsigned int j = 0; j < dofs_per_cell; ++j) + { + cell_matrix(i, j) += + (-fe_face.shape_value(i, q_index) * + fe_face.shape_grad(j, q_index) * normal - + fe_face.shape_grad(i, q_index) * normal * + fe_face.shape_value(j, q_index) + + (penalty_constant / h_f) * + fe_face.shape_value(i, q_index) * + fe_face.shape_value(j, q_index)) * + fe_face.JxW(q_index); + } + } + } + } + else + { + const auto &neigh_polytope = polytope->neighbor(f); + if (polytope->id() < neigh_polytope->id()) + { + unsigned int nofn = + polytope->neighbor_of_agglomerated_neighbor(f); + Assert(neigh_polytope->neighbor(nofn)->id() == + polytope->id(), + ExcMessage("Mismatch.")); + const auto &fe_faces = ah.reinit_interface( + polytope, neigh_polytope, f, nofn); + const auto &fe_faces0 = fe_faces.first; + if (neigh_polytope->is_locally_owned()) + { + // use both fevalues + const auto &fe_faces1 = fe_faces.second; + M11 = 0.; + M12 = 0.; + M21 = 0.; + M22 = 0.; + assemble_local_jumps_and_averages(M11, + M12, + M21, + M22, + fe_faces0, + fe_faces1, + penalty_constant, + h_f); + // distribute DoFs accordingly + // fluxes + neigh_polytope->get_dof_indices( + local_dof_indices_neighbor); + constraints.distribute_local_to_global( + M11, local_dof_indices, system_matrix); + constraints.distribute_local_to_global( + M12, + local_dof_indices, + local_dof_indices_neighbor, + system_matrix); + constraints.distribute_local_to_global( + M21, + local_dof_indices_neighbor, + local_dof_indices, + system_matrix); + constraints.distribute_local_to_global( + M22, local_dof_indices_neighbor, system_matrix); + } + else + { + // neigh polytope is ghosted, so retrieve necessary + // metadata. + types::subdomain_id neigh_rank = + neigh_polytope->subdomain_id(); + const auto &recv_jxws = + ah.recv_jxws.at(neigh_rank) + .at({neigh_polytope->id(), nofn}); + const auto &recv_values = + ah.recv_values.at(neigh_rank) + .at({neigh_polytope->id(), nofn}); + const auto &recv_gradients = + ah.recv_gradients.at(neigh_rank) + .at({neigh_polytope->id(), nofn}); + M11 = 0.; + M12 = 0.; + M21 = 0.; + M22 = 0.; + // there's no FEFaceValues on the other side (it's + // ghosted), so we just pass the actual data we have + // recevied from the neighboring ghosted polytope + assemble_local_jumps_and_averages_ghost( + M11, + M12, + M21, + M22, + fe_faces0, + recv_values, + recv_gradients, + recv_jxws, + penalty_constant, + h_f); + // distribute DoFs accordingly + // fluxes + neigh_polytope->get_dof_indices( + local_dof_indices_neighbor); + constraints.distribute_local_to_global( + M11, local_dof_indices, system_matrix); + constraints.distribute_local_to_global( + M12, + local_dof_indices, + local_dof_indices_neighbor, + system_matrix); + constraints.distribute_local_to_global( + M21, + local_dof_indices_neighbor, + local_dof_indices, + system_matrix); + constraints.distribute_local_to_global( + M22, local_dof_indices_neighbor, system_matrix); + } // ghosted polytope case + } // only once + } // internal face + } // face loop + constraints.distribute_local_to_global(cell_matrix, + local_dof_indices, + system_matrix); + } // locally owned polytopes + } + system_matrix.compress(VectorOperation::add); + } + + + + /** + * Compute SIPDG matrix as well as rhs vector. + * @note Hardcoded for $f=1$ and simplex elements. + * TODO: Pass Function object for boundary conditions and forcing term. + */ + template + void + assemble_dg_matrix_on_standard_mesh(MatrixType &system_matrix, + VectorType &system_rhs, + const Mapping &mapping, + const FiniteElement &fe_dg, + const DoFHandler &dof_handler) + { + static_assert( + (std::is_same_v || + std::is_same_v>)); + + Assert((dynamic_cast *>(&fe_dg) != nullptr), + ExcNotImplemented( + "Implemented only for simplex meshes for the time being.")); + + Assert(dof_handler.get_triangulation().all_reference_cells_are_simplex(), + ExcNotImplemented()); + + const double penalty_constant = .5 * fe_dg.degree * (fe_dg.degree + 1); + AffineConstraints constraints; + constraints.close(); + + const IndexSet &locally_owned_dofs = dof_handler.locally_owned_dofs(); + const IndexSet locally_relevant_dofs = + DoFTools::extract_locally_relevant_dofs(dof_handler); + + DynamicSparsityPattern dsp(locally_relevant_dofs); + DoFTools::make_flux_sparsity_pattern(dof_handler, dsp); + SparsityTools::distribute_sparsity_pattern(dsp, + dof_handler.locally_owned_dofs(), + dof_handler.get_communicator(), + locally_relevant_dofs); + + system_matrix.reinit(locally_owned_dofs, + locally_owned_dofs, + dsp, + dof_handler.get_communicator()); + + system_rhs.reinit(locally_owned_dofs, dof_handler.get_communicator()); + + const unsigned int quadrature_degree = fe_dg.degree + 1; + FEFaceValues fe_faces0(mapping, + fe_dg, + QGaussSimplex(quadrature_degree), + update_values | update_JxW_values | + update_gradients | update_quadrature_points | + update_normal_vectors); + + + FEValues fe_values(mapping, + fe_dg, + QGaussSimplex(quadrature_degree), + update_values | update_JxW_values | + update_gradients | update_quadrature_points); + + FEFaceValues fe_faces1(mapping, + fe_dg, + QGaussSimplex(quadrature_degree), + update_values | update_JxW_values | + update_gradients | update_quadrature_points | + update_normal_vectors); + const unsigned int dofs_per_cell = fe_dg.n_dofs_per_cell(); + + FullMatrix cell_matrix(dofs_per_cell, dofs_per_cell); + Vector cell_rhs(dofs_per_cell); + + FullMatrix M11(dofs_per_cell, dofs_per_cell); + FullMatrix M12(dofs_per_cell, dofs_per_cell); + FullMatrix M21(dofs_per_cell, dofs_per_cell); + FullMatrix M22(dofs_per_cell, dofs_per_cell); + + std::vector local_dof_indices(dofs_per_cell); + + // Loop over standard deal.II cells + for (const auto &cell : dof_handler.active_cell_iterators()) + { + if (cell->is_locally_owned()) + { + cell_matrix = 0.; + cell_rhs = 0.; + + fe_values.reinit(cell); + + // const auto &q_points = fe_values.get_quadrature_points(); + // const unsigned int n_qpoints = q_points.size(); + + for (unsigned int q_index : fe_values.quadrature_point_indices()) + { + for (unsigned int i = 0; i < dofs_per_cell; ++i) + { + for (unsigned int j = 0; j < dofs_per_cell; ++j) + { + cell_matrix(i, j) += fe_values.shape_grad(i, q_index) * + fe_values.shape_grad(j, q_index) * + fe_values.JxW(q_index); + } + cell_rhs(i) += + fe_values.shape_value(i, q_index) * 1. * + fe_values.JxW(q_index); // TODO: pass functional + } + } + + // distribute volumetric DoFs + cell->get_dof_indices(local_dof_indices); + double hf = 0.; + for (const auto f : cell->face_indices()) + { + const double extent1 = + cell->measure() / cell->face(f)->measure(); + + if (cell->face(f)->at_boundary()) + { + hf = (1. / extent1 + 1. / extent1); + fe_faces0.reinit(cell, f); + + const auto &normals = fe_faces0.get_normal_vectors(); + for (unsigned int q_index : + fe_faces0.quadrature_point_indices()) + { + for (unsigned int i = 0; i < dofs_per_cell; ++i) + { + for (unsigned int j = 0; j < dofs_per_cell; ++j) + { + cell_matrix(i, j) += + (-fe_faces0.shape_value(i, q_index) * + fe_faces0.shape_grad(j, q_index) * + normals[q_index] - + fe_faces0.shape_grad(i, q_index) * + normals[q_index] * + fe_faces0.shape_value(j, q_index) + + (penalty_constant * hf) * + fe_faces0.shape_value(i, q_index) * + fe_faces0.shape_value(j, q_index)) * + fe_faces0.JxW(q_index); + } + cell_rhs(i) += + 0.; // TODO: add bdary conditions functional + } + } + } + else + { + const auto &neigh_cell = cell->neighbor(f); + if (cell->global_active_cell_index() < + neigh_cell->global_active_cell_index()) + { + const double extent2 = + neigh_cell->measure() / + neigh_cell->face(cell->neighbor_of_neighbor(f)) + ->measure(); + hf = (1. / extent1 + 1. / extent2); + fe_faces0.reinit(cell, f); + fe_faces1.reinit(neigh_cell, + cell->neighbor_of_neighbor(f)); + + std::vector + local_dof_indices_neighbor(dofs_per_cell); + + M11 = 0.; + M12 = 0.; + M21 = 0.; + M22 = 0.; + + const auto &normals = fe_faces0.get_normal_vectors(); + // M11 + for (unsigned int q_index : + fe_faces0.quadrature_point_indices()) + { + for (unsigned int i = 0; i < dofs_per_cell; ++i) + { + for (unsigned int j = 0; j < dofs_per_cell; ++j) + { + M11(i, j) += + (-0.5 * fe_faces0.shape_grad(i, q_index) * + normals[q_index] * + fe_faces0.shape_value(j, q_index) - + 0.5 * fe_faces0.shape_grad(j, q_index) * + normals[q_index] * + fe_faces0.shape_value(i, q_index) + + (penalty_constant * hf) * + fe_faces0.shape_value(i, q_index) * + fe_faces0.shape_value(j, q_index)) * + fe_faces0.JxW(q_index); + + M12(i, j) += + (0.5 * fe_faces0.shape_grad(i, q_index) * + normals[q_index] * + fe_faces1.shape_value(j, q_index) - + 0.5 * fe_faces1.shape_grad(j, q_index) * + normals[q_index] * + fe_faces0.shape_value(i, q_index) - + (penalty_constant * hf) * + fe_faces0.shape_value(i, q_index) * + fe_faces1.shape_value(j, q_index)) * + fe_faces1.JxW(q_index); + + // A10 + M21(i, j) += + (-0.5 * fe_faces1.shape_grad(i, q_index) * + normals[q_index] * + fe_faces0.shape_value(j, q_index) + + 0.5 * fe_faces0.shape_grad(j, q_index) * + normals[q_index] * + fe_faces1.shape_value(i, q_index) - + (penalty_constant * hf) * + fe_faces1.shape_value(i, q_index) * + fe_faces0.shape_value(j, q_index)) * + fe_faces1.JxW(q_index); + + // A11 + M22(i, j) += + (0.5 * fe_faces1.shape_grad(i, q_index) * + normals[q_index] * + fe_faces1.shape_value(j, q_index) + + 0.5 * fe_faces1.shape_grad(j, q_index) * + normals[q_index] * + fe_faces1.shape_value(i, q_index) + + (penalty_constant * hf) * + fe_faces1.shape_value(i, q_index) * + fe_faces1.shape_value(j, q_index)) * + fe_faces1.JxW(q_index); + } + } + } + + // distribute DoFs accordingly + + neigh_cell->get_dof_indices(local_dof_indices_neighbor); + + constraints.distribute_local_to_global( + M11, local_dof_indices, system_matrix); + constraints.distribute_local_to_global( + M12, + local_dof_indices, + local_dof_indices_neighbor, + system_matrix); + constraints.distribute_local_to_global( + M21, + local_dof_indices_neighbor, + local_dof_indices, + system_matrix); + constraints.distribute_local_to_global( + M22, local_dof_indices_neighbor, system_matrix); + + } // check idx neighbors + } // over faces + } + constraints.distribute_local_to_global(cell_matrix, + cell_rhs, + local_dof_indices, + system_matrix, + system_rhs); + } + } + system_matrix.compress(VectorOperation::add); + system_rhs.compress(VectorOperation::add); + } + + +} // namespace dealii::PolyUtils + +#endif diff --git a/agglomeration_poisson/meshes/t1.geo b/agglomeration_poisson/meshes/t1.geo new file mode 100644 index 00000000..6023f35f --- /dev/null +++ b/agglomeration_poisson/meshes/t1.geo @@ -0,0 +1,138 @@ +// ----------------------------------------------------------------------------- +// +// Gmsh GEO tutorial 1 +// +// Geometry basics, elementary entities, physical groups +// +// ----------------------------------------------------------------------------- + +// The simplest construction in Gmsh's scripting language is the +// `affectation'. The following command defines a new variable `lc': + +lc = 1e-2; + +// This variable can then be used in the definition of Gmsh's simplest +// `elementary entity', a `Point'. A Point is uniquely identified by a tag (a +// strictly positive integer; here `1') and defined by a list of four numbers: +// three coordinates (X, Y and Z) and the target mesh size (lc) close to the +// point: + +Point(1) = {0, 0, 0, lc}; + +// The distribution of the mesh element sizes will then be obtained by +// interpolation of these mesh sizes throughout the geometry. Another method to +// specify mesh sizes is to use general mesh size Fields (see `t10.geo'). A +// particular case is the use of a background mesh (see `t7.geo'). + +// If no target mesh size of provided, a default uniform coarse size will be +// used for the model, based on the overall model size. + +// We can then define some additional points. All points should have different +// tags: + +Point(2) = {.1, 0, 0, lc}; +Point(3) = {.1, .3, 0, lc}; +Point(4) = {0, .3, 0, lc}; + +// Curves are Gmsh's second type of elementary entities, and, amongst curves, +// straight lines are the simplest. A straight line is identified by a tag and +// is defined by a list of two point tags. In the commands below, for example, +// the line 1 starts at point 1 and ends at point 2. +// +// Note that curve tags are separate from point tags - hence we can reuse tag +// `1' for our first curve. And as a general rule, elementary entity tags in +// Gmsh have to be unique per geometrical dimension. + +Line(1) = {1, 2}; +Line(2) = {3, 2}; +Line(3) = {3, 4}; +Line(4) = {4, 1}; + +// The third elementary entity is the surface. In order to define a simple +// rectangular surface from the four curves defined above, a curve loop has +// first to be defined. A curve loop is also identified by a tag (unique amongst +// curve loops) and defined by an ordered list of connected curves, a sign being +// associated with each curve (depending on the orientation of the curve to form +// a loop): + +Curve Loop(1) = {4, 1, -2, 3}; + +// We can then define the surface as a list of curve loops (only one here, +// representing the external contour, since there are no holes--see `t4.geo' for +// an example of a surface with a hole): + +Plane Surface(1) = {1}; + +// At this level, Gmsh knows everything to display the rectangular surface 1 and +// to mesh it. An optional step is needed if we want to group elementary +// geometrical entities into more meaningful groups, e.g. to define some +// mathematical ("domain", "boundary"), functional ("left wing", "fuselage") or +// material ("steel", "carbon") properties. +// +// Such groups are called "Physical Groups" in Gmsh. By default, if physical +// groups are defined, Gmsh will export in output files only mesh elements that +// belong to at least one physical group. (To force Gmsh to save all elements, +// whether they belong to physical groups or not, set `Mesh.SaveAll=1;', or +// specify `-save_all' on the command line.) Physical groups are also identified +// by tags, i.e. strictly positive integers, that should be unique per dimension +// (0D, 1D, 2D or 3D). Physical groups can also be given names. +// +// Here we define a physical curve that groups the left, bottom and right curves +// in a single group (with prescribed tag 5); and a physical surface with name +// "My surface" (with an automatic tag) containing the geometrical surface 1: + +Physical Curve(5) = {1, 2, 4}; +Physical Surface("My surface") = {1}; + +// Now that the geometry is complete, you can +// - either open this file with Gmsh and select `2D' in the `Mesh' module to +// create a mesh; then select `Save' to save it to disk in the default format +// (or use `File->Export' to export in other formats); +// - or run `gmsh t1.geo -2` to mesh in batch mode on the command line. + +// You could also uncomment the following lines in this script: +// +// Mesh 2; +// Save "t1.msh"; +// +// which would lead Gmsh to mesh and save the mesh every time the file is +// parsed. (To simply parse the file from the command line, you can use `gmsh +// t1.geo -') + +// By default, Gmsh saves meshes in the latest version of the Gmsh mesh file +// format (the `MSH' format). You can save meshes in other mesh formats by +// specifying a filename with a different extension in the GUI, on the command +// line or in scripts. For example +// +// Save "t1.unv"; +// +// will save the mesh in the UNV format. You can also save the mesh in older +// versions of the MSH format: +// +// - In the GUI: open `File->Export', enter your `filename.msh' and then pick +// the version in the dropdown menu. +// - On the command line: use the `-format' option (e.g. `gmsh file.geo -format +// msh2 -2'). +// - In a `.geo' script: add `Mesh.MshFileVersion = x.y;' for any version +// number `x.y'. +// - As an alternative method, you can also not specify the format explicitly, +// and just choose a filename with the `.msh2' or `.msh4' extension. + +// Note that starting with Gmsh 3.0, models can be built using other geometry +// kernels than the default built-in kernel. By specifying +// +// SetFactory("OpenCASCADE"); +// +// any subsequent command in the `.geo' file would be handled by the OpenCASCADE +// geometry kernel instead of the built-in kernel. Different geometry kernels +// have different features. With OpenCASCADE, instead of defining the surface by +// successively defining 4 points, 4 curves and 1 curve loop, one can define the +// rectangular surface directly with +// +// Rectangle(2) = {.2, 0, 0, .1, .3}; +// +// The underlying curves and points could be accessed with the `Boundary' or +// `CombinedBoundary' operators. +// +// See e.g. `t16.geo', `t18.geo', `t19.geo' or `t20.geo' for complete examples +// based on OpenCASCADE, and `examples/boolean' for more. diff --git a/agglomeration_poisson/meshes/t1.msh b/agglomeration_poisson/meshes/t1.msh new file mode 100644 index 00000000..963222f4 --- /dev/null +++ b/agglomeration_poisson/meshes/t1.msh @@ -0,0 +1,1738 @@ +$MeshFormat +4.1 0 8 +$EndMeshFormat +$PhysicalNames +1 +2 6 "My surface" +$EndPhysicalNames +$Entities +4 4 1 0 +1 0 0 0 0 +2 0.1 0 0 0 +3 0.1 0.3 0 0 +4 0 0.3 0 0 +1 0 0 0 0.1 0 0 1 5 2 1 -2 +2 0.1 0 0 0.1 0.3 0 1 5 2 3 -2 +3 0 0.3 0 0.1 0.3 0 0 2 3 -4 +4 0 0 0 0 0.3 0 1 5 2 4 -1 +1 0 0 0 0.1 0.3 0 1 6 4 4 1 -2 3 +$EndEntities +$Nodes +9 428 1 428 +0 1 0 1 +1 +0 0 0 +0 2 0 1 +2 +0.1 0 0 +0 3 0 1 +3 +0.1 0.3 0 +0 4 0 1 +4 +0 0.3 0 +1 1 0 9 +5 +6 +7 +8 +9 +10 +11 +12 +13 +0.009999999999982483 0 0 +0.019999999999956 0 0 +0.02999999999992728 0 0 +0.03999999999989585 0 0 +0.0499999999998685 0 0 +0.05999999999989426 0 0 +0.06999999999991974 0 0 +0.07999999999994738 0 0 +0.08999999999997287 0 0 +1 2 0 29 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +0.1 0.2900000000000151 0 +0.1 0.2800000000000253 0 +0.1 0.2700000000000325 0 +0.1 0.2600000000000614 0 +0.1 0.250000000000112 0 +0.1 0.2400000000001409 0 +0.1 0.2300000000001698 0 +0.1 0.2200000000001987 0 +0.1 0.2100000000002276 0 +0.1 0.2000000000002565 0 +0.1 0.1900000000002854 0 +0.1 0.1800000000003035 0 +0.1 0.1700000000003324 0 +0.1 0.1600000000003613 0 +0.1 0.1500000000003848 0 +0.1 0.140000000000366 0 +0.1 0.1300000000003393 0 +0.1 0.1200000000003111 0 +0.1 0.1100000000002829 0 +0.1 0.1000000000002547 0 +0.1 0.0900000000002287 0 +0.1 0.08000000000020444 0 +0.1 0.07000000000017481 0 +0.1 0.0600000000001506 0 +0.1 0.05000000000012819 0 +0.1 0.04000000000010256 0 +0.1 0.03000000000008235 0 +0.1 0.02000000000005631 0 +0.1 0.01000000000002815 0 +1 3 0 9 +43 +44 +45 +46 +47 +48 +49 +50 +51 +0.09000000000002764 0.3 0 +0.08000000000005529 0.3 0 +0.07000000000008294 0.3 0 +0.06000000000011058 0.3 0 +0.05000000000013687 0.3 0 +0.04000000000011003 0.3 0 +0.03000000000008349 0.3 0 +0.02000000000005478 0.3 0 +0.01000000000002821 0.3 0 +1 4 0 29 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +0 0.2900000000000151 0 +0 0.2800000000000253 0 +0 0.2700000000000325 0 +0 0.2600000000000614 0 +0 0.250000000000112 0 +0 0.2400000000001409 0 +0 0.2300000000001698 0 +0 0.2200000000001987 0 +0 0.2100000000002276 0 +0 0.2000000000002565 0 +0 0.1900000000002854 0 +0 0.1800000000003035 0 +0 0.1700000000003324 0 +0 0.1600000000003613 0 +0 0.1500000000003848 0 +0 0.140000000000366 0 +0 0.1300000000003393 0 +0 0.1200000000003111 0 +0 0.1100000000002829 0 +0 0.1000000000002547 0 +0 0.0900000000002287 0 +0 0.08000000000020444 0 +0 0.07000000000017481 0 +0 0.0600000000001506 0 +0 0.05000000000012819 0 +0 0.04000000000010256 0 +0 0.03000000000008235 0 +0 0.02000000000005631 0 +0 0.01000000000002815 0 +2 1 0 348 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +0.05 0.2450000000001263 0 +0.05054811538445409 0.1859502481355593 0 +0.05 0.05500000000013938 0 +0.05 0.1250000000003253 0 +0.06274999999993494 0.09000000000023234 0 +0.06572018662968322 0.2161096050181721 0 +0.06475710279129733 0.1551634135255872 0 +0.03315297242747328 0.2171009220530309 0 +0.03279063511191351 0.154884451563044 0 +0.03106704794779445 0.08115039576411633 0 +0.06896260309973415 0.2693385709578048 0 +0.0308333333333806 0.03083333333337629 0 +0.03083333333329117 0.2691666666667056 0 +0.06916666666662084 0.03083333333337913 0 +0.02827370967195559 0.1086574502746814 0 +0.07380370015478221 0.06452146512520114 0 +0.07391710104247737 0.1138903368085722 0 +0.07473943243155116 0.2397558260739265 0 +0.07433142859558997 0.1910268964729967 0 +0.02426923821626153 0.2412860866794283 0 +0.0263548127565276 0.1896622410561906 0 +0.0229872913751175 0.05548887749876044 0 +0.02480002497875594 0.1314222941487107 0 +0.07910042275678612 0.1372763487356272 0 +0.04999215674184012 0.2783163061561515 0 +0.04961068761097346 0.02091473007260978 0 +0.04623545460295388 0.1066000597883617 0 +0.07937026567239143 0.170948152237535 0 +0.02134350195183574 0.1709687952189772 0 +0.08031027573906815 0.04729071584008062 0 +0.04992980161488487 0.07335766544340117 0 +0.04990502147463992 0.2251812114664001 0 +0.04990531842073707 0.2049105804117036 0 +0.04999921225437838 0.1449973684977225 0 +0.04933621945525658 0.1642435546549742 0 +0.08151737201214378 0.08372212982056626 0 +0.08352874647380565 0.2554106422797782 0 +0.01777622497811298 0.2574475493168146 0 +0.01709962231089754 0.09197473690423899 0 +0.01810593287339941 0.01648759332414372 0 +0.08344001510871472 0.281498284041761 0 +0.0826028717526731 0.01733445282522036 0 +0.01658415059517015 0.2837421952493434 0 +0.08233897852363155 0.2228056449658614 0 +0.0167026152150823 0.2233350609186064 0 +0.01738323041333457 0.07255730064411528 0 +0.0181697188304752 0.2048319806295879 0 +0.04638023893468582 0.03818693693699882 0 +0.05361976106550141 0.2618130630631198 0 +0.01710233995352946 0.1483224916130734 0 +0.08338755101368024 0.1542722193425674 0 +0.08458209207853569 0.2052680781098871 0 +0.01696270647653648 0.03969694240767237 0 +0.03463040854960306 0.2524337598966716 0 +0.06377770449444689 0.1758902517322844 0 +0.03667069583681541 0.1738368848582159 0 +0.08347018300179916 0.1003948061783683 0 +0.06394130752243481 0.04637299102362936 0 +0.03653013701759925 0.06578115393646219 0 +0.03500097682353641 0.01540209779922195 0 +0.06499902317646952 0.2845979022008345 0 +0.06516218900421256 0.01543745010124377 0 +0.0348378109957842 0.2845625498987851 0 +0.06583677953759381 0.2520521212395572 0 +0.06219003663279881 0.1350676783287303 0 +0.01574201344960638 0.1199079887784824 0 +0.04647601623420582 0.08938567065958745 0 +0.03547859727405338 0.120902495279881 0 +0.085781544872437 0.03365729264921174 0 +0.08550706377663976 0.1231417427342262 0 +0.0625358733865147 0.1046551865550269 0 +0.06051709708115725 0.2354620231864379 0 +0.06355591521485543 0.2006991021660219 0 +0.03924290468210864 0.2354081183150001 0 +0.03568245975204389 0.200851572011665 0 +0.03794372124876114 0.136159071047091 0 +0.03265148211051816 0.09597171483031657 0 +0.08630191661732232 0.1831846401493144 0 +0.03384926303585132 0.0445684366823401 0 +0.01354492646955578 0.1845750459605247 0 +0.06106798505542358 0.06398116975843209 0 +0.0864482951951978 0.0601392163441599 0 +0.08614226355478008 0.2695935331566467 0 +0.01364814217288737 0.2690158205532557 0 +0.01264577279089544 0.1056250670634742 0 +0.06662230967653722 0.07712141279312491 0 +0.06299583876503147 0.1216782629014432 0 +0.08792418629480367 0.2327972609537691 0 +0.01280060219651622 0.2437337699362268 0 +0.08609159620020478 0.07477698397927987 0 +0.01355926459523447 0.1592666955076678 0 +0.01160725750583438 0.05506888328593672 0 +0.06185212776060149 0.1872814615073608 0 +0.02955837181475597 0.22722236472961 0 +0.04112490613780621 0.1872116579042079 0 +0.07197470286317675 0.2240522321526481 0 +0.009608749445596683 0.1347720931168929 0 +0.04185507736299517 0.2698968090598955 0 +0.05921570462299655 0.0307984980354148 0 +0.01231268900762929 0.02939158538131865 0 +0.01196094258556898 0.08172578628424082 0 +0.08644780272642469 0.1449982008961914 0 +0.01076799067561051 0.1953089897712102 0 +0.08972652472277889 0.1946190689607215 0 +0.02801217781631896 0.1444667688919828 0 +0.08793462506118246 0.1104712017617358 0 +0.07236694623394009 0.09685126149639753 0 +0.08916006907666457 0.1644846921910006 0 +0.05488502701920165 0.1143051285947626 0 +0.06995951036669724 0.1437520731169471 0 +0.04449809899059991 0.1550000000003318 0 +0.05499298469691018 0.2158424572987196 0 +0.08865405214249947 0.2470464859834661 0 +0.05482092289041024 0.2893538495423276 0 +0.04516430812763438 0.01061755508924944 0 +0.07167609074800561 0.05485292829514939 0 +0.03799206552957737 0.1116187507982865 0 +0.0745352835276368 0.1243675548571719 0 +0.03865827501047617 0.05484137642730898 0 +0.01016372559676839 0.2346189340840709 0 +0.03053757816216812 0.1658499263090955 0 +0.0100745716606108 0.2148856681724055 0 +0.06951696131805786 0.16568331067982 0 +0.09056140444711706 0.2144445071743106 0 +0.07304671075963266 0.1800689457381428 0 +0.05847712967845763 0.2716550170861172 0 +0.04150732351623179 0.02847708806455778 0 +0.02561961340380368 0.2901475895451768 0 +0.07403691682459027 0.2900138169382549 0 +0.07456594428606797 0.009956616766848283 0 +0.0254340557138128 0.009956616766811907 0 +0.04406688487806656 0.2160710369100532 0 +0.05485058306148541 0.1554672051369757 0 +0.02596069221728731 0.1799518624891759 0 +0.05485745907404958 0.1951365167504928 0 +0.08952644165957237 0.09312309937773679 0 +0.07313235375158235 0.2068996664069839 0 +0.04534960155148386 0.254490625681937 0 +0.06058256249105268 0.2249931478510014 0 +0.05417990078637135 0.09706073431041382 0 +0.09025634415707355 0.04268014244880614 0 +0.01167647942457499 0.1738462866467073 0 +0.01054802603501498 0.01054802603501248 0 +0.0894519739650159 0.2894519739650166 0 +0.08945197396498686 0.01054802603501311 0 +0.01054802603498207 0.2894519739650157 0 +0.05624218119070878 0.0105842462262364 0 +0.04362985896575824 0.2889676746129535 0 +0.02648994660885548 0.06598302640871324 0 +0.0899946968616251 0.1342770708401287 0 +0.009183593288985326 0.06527634148640436 0 +0.02232760819969652 0.1593209867872886 0 +0.09038352219572646 0.02500369012041596 0 +0.04546597336601778 0.06449398503331662 0 +0.03847636359767918 0.1444160037398148 0 +0.05969564872004614 0.1467291223603274 0 +0.07225284074888731 0.03967580578245979 0 +0.05292285456385665 0.1751244717902934 0 +0.02265388079549884 0.2145594409592408 0 +0.05449288254478868 0.04537928236672177 0 +0.05417489445796438 0.08380187784678204 0 +0.0398654715970089 0.07528758302506194 0 +0.02575412631903288 0.2505827931642753 0 +0.02747889934608191 0.2595775201830429 0 +0.02170500837475626 0.0826950062903813 0 +0.03907570946208217 0.2262461027107026 0 +0.02708359185949765 0.19874509832434 0 +0.05197934893202358 0.1342230277861822 0 +0.04369030100042338 0.1965117381313139 0 +0.08988581429501376 0.1760070802566965 0 +0.05918786588502435 0.2445280447541958 0 +0.0249045177408304 0.1225009763951987 0 +0.05942703164006184 0.1651904357338523 0 +0.02729436550837997 0.2784443655084437 0 +0.07238703501925962 0.2787807442712225 0 +0.07258093724862336 0.02123538079696003 0 +0.0263051700951848 0.0219760096620463 0 +0.0732109954854986 0.2601189469268534 0 +0.07536686286056139 0.07442877523793248 0 +0.07134182058957263 0.08536364348632947 0 +0.05074489086465172 0.2351696307154092 0 +0.03959396976866018 0.1640365823896879 0 +0.008482994169897003 0.09659266577292673 0 +0.07667097364374564 0.2486252024440786 0 +0.008901385873800009 0.04524029101458225 0 +0.008851758566264438 0.2525905890425867 0 +0.07519594005930835 0.2165809951374477 0 +0.008461442835776593 0.124331996337931 0 +0.09219147357301301 0.2759869392014165 0 +0.009345747157783899 0.2773657346000576 0 +0.09216491385311046 0.2240094826188619 0 +0.06029800481814775 0.05550788221441919 0 +0.06297527467090494 0.2614831740726207 0 +0.09125923499799551 0.08225241052050698 0 +0.04059229881994354 0.2449737038594487 0 +0.008913969930682195 0.1444069179102568 0 +0.09045272770318037 0.2603343571995565 0 +0.0219684394002455 0.03086241199029427 0 +0.07880320964023606 0.1464728598509963 0 +0.02484237991133253 0.04635803362425656 0 +0.04396011841695914 0.04656015907032773 0 +0.02503753629550534 0.1004532096827508 0 +0.04083187331193745 0.279474483062767 0 +0.05882046132219398 0.02243695204532652 0 +0.07924010636116184 0.1617548638035362 0 +0.04158775628724041 0.09766296035302267 0 +0.0176326008989182 0.06262609944920183 0 +0.007802456233339302 0.2050053277147376 0 +0.0553325331925154 0.2528454884039866 0 +0.03775844937000811 0.2612463313616875 0 +0.009153754141296013 0.02032934084088869 0 +0.008697996037006973 0.1137468563384837 0 +0.007705553257577777 0.07391188568302795 0 +0.007388182494492298 0.2245679326350902 0 +0.09237197722301263 0.1540438020776386 0 +0.09297400424968633 0.2048663308490807 0 +0.02590988303027883 0.07469068077825801 0 +0.04601913099614288 0.1182184306528137 0 +0.07795548621283217 0.2325795230210734 0 +0.02214519702075033 0.231686511974194 0 +0.0426844790548328 0.1289748183937485 0 +0.02186637252787416 0.2658878987150536 0 +0.007635356271593157 0.03686576376075164 0 +0.03764881342934839 0.0365354701969014 0 +0.09200265686230161 0.1030892837256152 0 +0.07751647273630853 0.1068703206245375 0 +0.06211760301237225 0.03853510913482864 0 +0.06409838268806048 0.1122324054826614 0 +0.07741276758430943 0.270499998494138 0 +0.04065933054005839 0.2074498786667809 0 +0.0794881207738028 0.09225736068119396 0 +0.0194726918286418 0.138294604272525 0 +0.07963486133969913 0.1976241560230619 0 +0.04308949248794483 0.179135596246291 0 +0.07427740691518843 0.154516456719909 0 +0.09248062392344414 0.06572430673852409 0 +0.07859350057830801 0.02933032339263595 0 +0.09097258895024138 0.0539820595887426 0 +0.06050889809506288 0.2084000327413684 0 +0.007811079460721944 0.1535902785784738 0 +0.02029050041575317 0.1109315798633786 0 +0.0171649067132013 0.1285383255082901 0 +0.03529633408295845 0.1034940242879032 0 +0.03276974269602714 0.1295099262262039 0 +0.03508562009494713 0.007090615064644444 0 +0.06483669469883671 0.292901076126318 0 +0.06507072262973704 0.006962634779173811 0 +0.03486961190939045 0.2928599683228167 0 +0.03762717193829498 0.08740454261408351 0 +0.09328428224504741 0.03426822504372375 0 +0.03267714225682072 0.2432240056008732 0 +0.06957076117847305 0.1312887520917548 0 +0.06792153753755181 0.243398284332335 0 +0.008055225143452957 0.2618107917825502 0 +0.05886452133001877 0.07300004142731505 0 +0.0583726450535849 0.279966331508429 0 +0.04159619813630198 0.01998516601433792 0 +0.05770944853517258 0.1280790723487847 0 +0.08310461225965655 0.2401918827159945 0 +0.03170865106431683 0.0592954746311794 0 +0.02744964216671469 0.03810895592899793 0 +0.09366771463897386 0.1249662516695441 0 +0.01706020422852418 0.04837060556624167 0 +0.02956699236750787 0.2072564821074409 0 +0.02753131149629445 0.08994160101431456 0 +0.07140249908087784 0.1984422239925652 0 +0.08196629317099362 0.06665670346940292 0 +0.007263909887263928 0.1652203338266004 0 +0.01946313461587017 0.1929983425237252 0 +0.08160954277426327 0.1161389421612408 0 +0.08063724282719031 0.03910369874478932 0 +0.04561467116847179 0.08184746791778325 0 +0.07995936980507164 0.05593222036630124 0 +0.0812601253411579 0.2634258218150575 0 +0.06494601675698042 0.276337884243233 0 +0.03496520536876691 0.02364388728120497 0 +0.06740513621744119 0.06155463688116048 0 +0.01959526280899734 0.2742319368554494 0 +0.01788625500504694 0.2923851847153637 0 +0.08211374499494083 0.00761481528463436 0 +0.08244486645083859 0.2923351092972475 0 +0.01788625500499238 0.007614815284592383 0 +0.05097994050174223 0.2692355348401679 0 +0.05152843866259782 0.02982749157037912 0 +0.05727520055083977 0.1810616082913745 0 +0.02491947040933389 0.1514675631093398 0 +0.02953414542171475 0.1155991677084943 0 +0.09394320303389062 0.1850000000002947 0 +0.007277672465595041 0.1876944703847356 0 +0.08143924363156149 0.1886614836088362 0 +0.06894846976955363 0.2333735061029037 0 +0.04415348774613554 0.1710266348349135 0 +0.03157387695397465 0.2346595716904153 0 +0.03457931057597129 0.1925446575441201 0 +0.06692855104004682 0.06912082168378259 0 +0.09276446285372204 0.2538544115503919 0 +0.04489475047712498 0.1381038787386249 0 +0.03040917207941887 0.05211043977276909 0 +0.05517307866874612 0.1066157924804903 0 +0.09283681255660445 0.1174124447727151 0 +0.03260092786678878 0.07266030245967273 0 +0.0812331765476625 0.1791167625140187 0 +0.08011542947898623 0.1294482434853152 0 +0.06443054692348887 0.09735371201199657 0 +0.09444211200794878 0.07500000000018964 0 +0.09314264619676564 0.01704095819659698 0 +0.07114775889503565 0.1731285823923618 0 +0.005528484533526912 0.1050000000002688 0 +0.02085776710355431 0.1848666994004982 0 +0.005933761631814893 0.2445860503126269 0 +0.02458777264467034 0.2217187889947955 0 +0.03362812490221619 0.1822701219383052 0 +0.007025211448917843 0.08775152000904408 0 +0.01817922245098949 0.2483491102001623 0 +0.02937401273165067 0.1732393236257428 0 +0.06211072877739447 0.0823107164802587 0 +0.08197096589763478 0.2131204553146573 0 +0.05670993919440361 0.2020791816188625 0 +0.09268354283250728 0.2403469978196536 0 +0.05405150025213687 0.06342080298794142 0 +0.09333891661157011 0.2670315111763577 0 +0.05442961424492167 0.03700411522558245 0 +0.05543739297787539 0.2302015033048121 0 +0.01784467785483068 0.02422137745801584 0 +0.04487861018696128 0.2312602942253297 0 +0.04591256597034615 0.2633364728013615 0 +0.07204512868959898 0.04704811023532979 0 +0.0185827654564731 0.1782425768464799 0 +0.007201175790053448 0.1799278034093976 0 +0.03080711587892923 0.1370999020925938 0 +0.01782956359788098 0.2394597286879341 0 +0.05596606163481172 0.1402542992432406 0 +0.09329829986159814 0.2835391428782651 0 +0.0659070322741566 0.1935852454156864 0 +0.005957701887137733 0.2839630174669918 0 +0.06914422050179833 0.105308870496532 0 +0.0940879561409582 0.1447598676879229 0 +0.04508135047271206 0.2395066189223159 0 +0.0345006000490075 0.2759759629601667 0 +0.06573048439668083 0.02428980139960194 0 +0.06942217020324594 0.1862040329128916 0 +0.01523415281172113 0.1657246195974483 0 +0.005520959423096741 0.005520959423117196 0 +0.09447904057691205 0.2944790405769065 0 +0.09447904057690285 0.005520959423097624 0 +0.005520959423088359 0.2944790405769313 0 +0.05477676715668044 0.1213414078344629 0 +0.01733595015259304 0.1002568005964955 0 +$EndNodes +$Elements +4 844 1 844 +1 1 1 10 +1 1 5 +2 5 6 +3 6 7 +4 7 8 +5 8 9 +6 9 10 +7 10 11 +8 11 12 +9 12 13 +10 13 2 +1 2 1 30 +11 3 14 +12 14 15 +13 15 16 +14 16 17 +15 17 18 +16 18 19 +17 19 20 +18 20 21 +19 21 22 +20 22 23 +21 23 24 +22 24 25 +23 25 26 +24 26 27 +25 27 28 +26 28 29 +27 29 30 +28 30 31 +29 31 32 +30 32 33 +31 33 34 +32 34 35 +33 35 36 +34 36 37 +35 37 38 +36 38 39 +37 39 40 +38 40 41 +39 41 42 +40 42 2 +1 4 1 30 +41 4 52 +42 52 53 +43 53 54 +44 54 55 +45 55 56 +46 56 57 +47 57 58 +48 58 59 +49 59 60 +50 60 61 +51 61 62 +52 62 63 +53 63 64 +54 64 65 +55 65 66 +56 66 67 +57 67 68 +58 68 69 +59 69 70 +60 70 71 +61 71 72 +62 72 73 +63 73 74 +64 74 75 +65 75 76 +66 76 77 +67 77 78 +68 78 79 +69 79 80 +70 80 1 +2 1 2 774 +71 216 33 305 +72 105 228 283 +73 106 227 284 +74 167 97 198 +75 122 233 317 +76 181 126 245 +77 54 164 270 +78 167 332 338 +79 183 160 349 +80 332 145 338 +81 75 76 172 +82 34 33 216 +83 349 160 389 +84 239 127 344 +85 127 247 344 +86 118 164 334 +87 57 200 390 +88 193 117 264 +89 80 223 291 +90 335 111 400 +91 163 121 309 +92 79 80 291 +93 164 118 302 +94 111 234 400 +95 180 78 291 +96 193 264 339 +97 67 68 177 +98 233 149 317 +99 121 163 269 +100 164 54 334 +101 137 186 306 +102 105 194 228 +103 106 195 227 +104 168 20 399 +105 191 114 213 +106 113 192 212 +107 176 86 267 +108 124 176 267 +109 170 116 259 +110 20 168 271 +111 171 130 232 +112 110 318 353 +113 306 186 350 +114 199 83 234 +115 139 199 234 +116 175 82 249 +117 174 88 246 +118 182 104 230 +119 168 124 271 +120 97 167 308 +121 57 58 200 +122 230 29 417 +123 370 205 382 +124 200 169 390 +125 99 205 370 +126 251 81 261 +127 152 251 261 +128 131 182 295 +129 124 168 299 +130 201 109 232 +131 61 62 183 +132 149 221 351 +133 203 87 315 +134 182 230 417 +135 107 197 323 +136 285 203 315 +137 245 126 297 +138 167 198 332 +139 82 175 314 +140 82 173 215 +141 110 221 318 +142 228 143 283 +143 227 142 284 +144 221 149 330 +145 173 82 365 +146 86 176 219 +147 78 79 291 +148 173 135 421 +149 187 85 260 +150 232 109 422 +151 24 23 184 +152 286 107 323 +153 75 172 231 +154 107 189 298 +155 125 202 239 +156 202 127 239 +157 197 107 298 +158 329 242 352 +159 213 114 236 +160 152 219 371 +161 219 176 371 +162 27 26 188 +163 108 188 250 +164 90 242 329 +165 145 190 236 +166 46 47 194 +167 8 9 195 +168 139 234 242 +169 234 111 242 +170 281 159 304 +171 116 170 274 +172 136 201 262 +173 172 76 265 +174 130 171 320 +175 237 317 351 +176 34 216 274 +177 237 94 317 +178 194 47 228 +179 195 9 227 +180 87 203 253 +181 67 177 276 +182 30 29 230 +183 172 102 287 +184 102 172 343 +185 144 273 289 +186 138 196 272 +187 145 236 412 +188 135 205 421 +189 88 212 246 +190 82 215 249 +191 192 112 212 +192 115 191 213 +193 113 212 310 +194 181 119 393 +195 161 335 400 +196 295 182 417 +197 213 87 253 +198 89 185 235 +199 183 62 369 +200 128 281 304 +201 252 321 367 +202 314 175 392 +203 187 260 311 +204 192 86 219 +205 135 173 365 +206 177 68 268 +207 119 181 245 +208 268 69 292 +209 35 34 274 +210 220 151 379 +211 347 96 353 +212 137 216 305 +213 318 162 353 +214 176 124 299 +215 171 232 422 +216 114 191 235 +217 151 220 384 +218 88 174 391 +219 135 238 253 +220 150 198 350 +221 188 108 285 +222 131 188 285 +223 20 19 399 +224 147 220 286 +225 220 107 286 +226 93 178 419 +227 94 179 420 +228 112 192 219 +229 96 196 353 +230 78 180 303 +231 217 86 319 +232 153 217 319 +233 211 140 257 +234 120 211 257 +235 209 141 255 +236 121 209 255 +237 142 210 256 +238 210 122 256 +239 208 123 254 +240 143 208 254 +241 252 146 321 +242 138 240 307 +243 148 298 301 +244 266 118 334 +245 84 248 301 +246 188 26 250 +247 301 248 377 +248 89 191 262 +249 282 321 428 +250 266 169 394 +251 238 115 253 +252 46 194 326 +253 8 195 325 +254 37 316 318 +255 59 60 202 +256 148 197 298 +257 104 182 279 +258 22 21 204 +259 190 87 236 +260 65 320 348 +261 183 127 288 +262 61 183 288 +263 170 259 347 +264 307 240 402 +265 181 73 293 +266 133 180 278 +267 184 23 296 +268 160 183 369 +269 198 97 350 +270 132 184 296 +271 53 54 270 +272 6 7 211 +273 44 45 209 +274 11 12 210 +275 49 50 208 +276 18 193 399 +277 126 181 293 +278 192 113 319 +279 182 131 279 +280 16 15 269 +281 191 89 235 +282 201 89 262 +283 282 95 321 +284 85 220 241 +285 220 147 241 +286 93 254 358 +287 178 105 283 +288 374 101 392 +289 24 184 368 +290 184 158 368 +291 108 250 382 +292 178 93 290 +293 320 171 348 +294 9 10 227 +295 47 48 228 +296 276 177 312 +297 249 113 310 +298 89 201 232 +299 230 104 383 +300 180 133 303 +301 132 217 313 +302 302 93 358 +303 32 186 305 +304 73 181 393 +305 136 262 372 +306 155 249 310 +307 312 177 322 +308 179 94 307 +309 241 111 335 +310 105 178 363 +311 257 92 278 +312 103 312 322 +313 90 329 345 +314 246 154 373 +315 174 246 373 +316 235 185 410 +317 91 258 309 +318 185 130 312 +319 64 222 409 +320 199 159 281 +321 167 189 308 +322 194 141 326 +323 195 140 325 +324 189 167 427 +325 127 183 349 +326 273 129 289 +327 91 206 273 +328 206 129 273 +329 134 218 290 +330 123 226 415 +331 156 235 410 +332 130 185 366 +333 63 64 409 +334 251 152 333 +335 241 335 396 +336 309 258 354 +337 87 213 236 +338 16 269 401 +339 198 150 383 +340 119 263 393 +341 146 268 292 +342 41 40 233 +343 224 121 413 +344 270 123 415 +345 316 162 318 +346 217 132 397 +347 249 155 374 +348 175 249 374 +349 104 190 332 +350 190 145 332 +351 85 187 384 +352 308 189 379 +353 300 174 373 +354 169 266 390 +355 203 135 253 +356 233 40 330 +357 117 258 264 +358 258 144 264 +359 204 21 271 +360 158 184 370 +361 333 152 371 +362 121 269 413 +363 97 306 350 +364 185 312 410 +365 117 193 376 +366 87 190 315 +367 272 196 357 +368 312 103 410 +369 130 276 312 +370 191 115 262 +371 81 218 275 +372 218 134 275 +373 19 18 399 +374 27 188 295 +375 188 131 295 +376 190 104 279 +377 184 132 313 +378 101 214 392 +379 267 217 397 +380 186 32 380 +381 83 240 272 +382 240 138 272 +383 185 89 366 +384 221 110 351 +385 124 204 271 +386 212 88 310 +387 186 137 305 +388 212 112 246 +389 39 38 221 +390 118 244 302 +391 136 314 392 +392 215 113 249 +393 251 144 289 +394 100 300 373 +395 257 278 404 +396 313 217 346 +397 121 255 309 +398 187 137 306 +399 200 58 294 +400 115 213 253 +401 100 243 394 +402 137 187 311 +403 74 75 231 +404 232 130 366 +405 106 284 364 +406 284 179 364 +407 263 165 388 +408 196 138 407 +409 256 122 317 +410 127 202 288 +411 202 60 288 +412 151 308 379 +413 175 374 392 +414 215 173 414 +415 153 215 414 +416 140 195 337 +417 195 106 337 +418 141 194 336 +419 194 105 336 +420 165 263 428 +421 100 394 411 +422 118 243 244 +423 243 134 244 +424 149 233 330 +425 26 25 250 +426 118 266 394 +427 178 283 419 +428 179 284 420 +429 83 199 281 +430 298 84 301 +431 245 90 345 +432 319 113 398 +433 197 148 367 +434 95 197 367 +435 189 107 379 +436 310 88 344 +437 204 132 296 +438 22 204 296 +439 174 300 391 +440 158 370 382 +441 96 259 375 +442 193 18 376 +443 176 299 371 +444 162 347 353 +445 216 116 274 +446 290 218 406 +447 250 25 368 +448 76 77 265 +449 86 217 267 +450 59 202 294 +451 107 220 379 +452 108 203 285 +453 122 225 386 +454 259 166 375 +455 86 192 319 +456 68 69 268 +457 260 116 311 +458 125 200 294 +459 224 43 361 +460 223 5 362 +461 13 225 360 +462 51 226 359 +463 202 125 294 +464 146 292 321 +465 292 165 321 +466 233 122 386 +467 62 63 369 +468 77 78 303 +469 250 158 382 +470 116 216 311 +471 216 137 311 +472 200 125 300 +473 208 143 328 +474 49 208 328 +475 211 7 325 +476 141 209 326 +477 210 142 327 +478 209 45 326 +479 11 210 327 +480 140 211 325 +481 114 235 377 +482 154 275 331 +483 211 120 362 +484 209 121 361 +485 122 210 360 +486 123 208 359 +487 259 96 347 +488 166 259 260 +489 259 116 260 +490 299 98 371 +491 110 196 407 +492 196 96 357 +493 196 110 353 +494 197 95 323 +495 88 239 344 +496 277 117 376 +497 220 85 384 +498 223 120 291 +499 329 157 345 +500 150 350 380 +501 69 70 292 +502 158 250 368 +503 304 159 341 +504 218 81 289 +505 129 218 289 +506 147 329 352 +507 39 221 330 +508 201 136 395 +509 159 199 378 +510 33 32 305 +511 43 44 361 +512 50 51 359 +513 5 6 362 +514 12 13 360 +515 71 72 263 +516 154 331 373 +517 384 187 416 +518 205 99 421 +519 199 139 340 +520 231 126 293 +521 321 95 367 +522 6 211 362 +523 44 209 361 +524 210 12 360 +525 208 50 359 +526 58 59 294 +527 169 200 411 +528 92 207 304 +529 207 128 304 +530 321 165 428 +531 102 229 287 +532 144 258 273 +533 258 91 273 +534 300 125 391 +535 55 56 266 +536 120 223 362 +537 121 224 361 +538 225 122 360 +539 226 123 359 +540 350 186 380 +541 109 201 395 +542 101 349 389 +543 190 279 315 +544 204 124 397 +545 21 20 271 +546 369 63 409 +547 282 157 323 +548 95 282 323 +549 128 240 281 +550 240 83 281 +551 90 245 297 +552 135 203 387 +553 73 74 293 +554 229 126 287 +555 239 88 391 +556 203 108 387 +557 276 130 320 +558 132 204 397 +559 126 231 287 +560 206 105 363 +561 128 207 364 +562 66 67 276 +563 231 172 287 +564 108 205 387 +565 38 37 318 +566 235 156 377 +567 270 164 358 +568 123 270 358 +569 205 135 387 +570 269 163 401 +571 205 108 382 +572 74 231 293 +573 115 238 372 +574 65 66 320 +575 237 138 307 +576 94 237 307 +577 103 252 324 +578 252 148 324 +579 92 304 341 +580 244 134 290 +581 93 244 290 +582 126 229 297 +583 150 230 383 +584 247 127 349 +585 54 55 334 +586 117 277 354 +587 277 163 354 +588 105 206 336 +589 106 207 337 +590 317 149 351 +591 143 228 328 +592 142 227 327 +593 227 10 327 +594 228 48 328 +595 298 189 427 +596 84 298 427 +597 151 384 416 +598 104 332 383 +599 332 198 383 +600 98 299 339 +601 299 168 339 +602 265 77 303 +603 214 109 395 +604 147 286 329 +605 286 157 329 +606 156 301 377 +607 229 139 381 +608 225 42 386 +609 206 91 355 +610 207 92 356 +611 72 73 393 +612 129 206 363 +613 207 106 364 +614 60 61 288 +615 81 251 289 +616 280 102 343 +617 32 31 380 +618 219 152 403 +619 221 38 318 +620 160 222 408 +621 222 109 408 +622 133 278 341 +623 215 153 398 +624 113 215 398 +625 214 101 389 +626 155 310 344 +627 71 263 388 +628 28 27 295 +629 109 214 408 +630 37 36 316 +631 23 22 296 +632 280 133 341 +633 89 232 366 +634 266 56 390 +635 244 93 302 +636 161 272 357 +637 264 98 339 +638 217 153 346 +639 134 243 331 +640 252 103 322 +641 146 252 322 +642 238 82 314 +643 263 119 428 +644 162 316 347 +645 316 170 347 +646 155 247 374 +647 247 101 374 +648 291 120 404 +649 180 291 404 +650 133 265 303 +651 166 260 396 +652 260 85 396 +653 18 17 376 +654 218 129 406 +655 238 135 365 +656 42 41 386 +657 230 150 342 +658 30 230 342 +659 229 102 340 +660 139 229 340 +661 255 91 309 +662 222 64 348 +663 112 219 403 +664 111 241 352 +665 241 147 352 +666 109 222 422 +667 14 224 413 +668 5 223 423 +669 43 224 424 +670 224 14 424 +671 223 80 423 +672 225 13 425 +673 42 225 425 +674 226 51 426 +675 237 110 407 +676 177 268 322 +677 17 16 401 +678 222 160 409 +679 145 248 338 +680 248 84 338 +681 238 314 372 +682 52 415 426 +683 64 65 348 +684 415 226 426 +685 94 256 317 +686 17 277 376 +687 28 295 417 +688 159 280 341 +689 275 134 331 +690 243 100 331 +691 124 267 397 +692 301 156 324 +693 148 301 324 +694 335 166 396 +695 7 8 325 +696 45 46 326 +697 10 11 327 +698 48 49 328 +699 120 257 404 +700 236 114 412 +701 40 39 330 +702 242 90 381 +703 110 237 351 +704 128 364 402 +705 157 286 323 +706 144 251 333 +707 193 339 399 +708 339 168 399 +709 82 238 365 +710 277 17 401 +711 254 123 358 +712 119 245 345 +713 187 306 416 +714 279 131 315 +715 268 146 322 +716 257 140 356 +717 255 141 355 +718 131 285 315 +719 41 233 386 +720 297 229 381 +721 242 111 352 +722 247 155 344 +723 31 30 342 +724 66 276 320 +725 143 254 419 +726 142 256 420 +727 178 290 406 +728 234 83 400 +729 101 247 349 +730 138 237 407 +731 114 248 412 +732 125 239 391 +733 264 144 333 +734 85 241 396 +735 240 128 402 +736 98 264 333 +737 265 133 343 +738 172 265 343 +739 55 266 334 +740 139 242 381 +741 258 117 354 +742 98 333 371 +743 280 159 378 +744 102 280 378 +745 154 246 405 +746 15 14 413 +747 282 119 345 +748 157 282 345 +749 52 53 415 +750 91 255 355 +751 92 257 356 +752 25 24 368 +753 179 307 402 +754 243 118 394 +755 248 114 377 +756 246 112 405 +757 148 252 367 +758 83 272 400 +759 272 161 400 +760 200 300 411 +761 278 92 341 +762 248 145 412 +763 99 313 346 +764 36 35 385 +765 80 1 423 +766 51 4 426 +767 14 3 424 +768 1 5 423 +769 3 43 424 +770 13 2 425 +771 2 42 425 +772 4 52 426 +773 70 71 388 +774 56 57 390 +775 165 292 388 +776 292 70 388 +777 283 143 419 +778 284 142 420 +779 364 179 402 +780 29 28 417 +781 262 115 372 +782 392 214 395 +783 136 392 395 +784 170 316 385 +785 316 36 385 +786 133 280 343 +787 254 93 419 +788 256 94 420 +789 331 100 373 +790 261 112 403 +791 152 261 403 +792 261 81 418 +793 112 261 405 +794 263 72 393 +795 90 297 381 +796 314 136 372 +797 274 170 385 +798 35 274 385 +799 342 150 380 +800 394 169 411 +801 269 15 413 +802 53 270 415 +803 81 275 418 +804 99 414 421 +805 163 277 401 +806 278 180 404 +807 275 154 418 +808 184 313 370 +809 140 337 356 +810 141 336 355 +811 163 309 354 +812 164 302 358 +813 99 346 414 +814 335 161 375 +815 166 335 375 +816 119 282 428 +817 313 99 370 +818 306 97 416 +819 97 308 416 +820 336 206 355 +821 337 207 356 +822 103 324 410 +823 129 363 406 +824 363 178 406 +825 300 100 411 +826 153 319 398 +827 357 96 375 +828 308 151 416 +829 199 340 378 +830 160 369 409 +831 340 102 378 +832 31 342 380 +833 161 357 375 +834 405 261 418 +835 154 405 418 +836 324 156 410 +837 214 389 408 +838 414 173 421 +839 167 338 427 +840 338 84 427 +841 222 348 422 +842 348 171 422 +843 346 153 414 +844 389 160 408 +$EndElements diff --git a/agglomeration_poisson/meshes/t2.geo b/agglomeration_poisson/meshes/t2.geo new file mode 100644 index 00000000..89d25bf5 --- /dev/null +++ b/agglomeration_poisson/meshes/t2.geo @@ -0,0 +1,28 @@ +// ----------------------------------------------------------------------------- +// +// Gmsh GEO tutorial 11 +// +// Unstructured quadrangular meshes +// +// ----------------------------------------------------------------------------- + +// We have seen in tutorials `t3.geo' and `t6.geo' that extruded and transfinite +// meshes can be "recombined" into quads, prisms or hexahedra by using the +// "Recombine" keyword. Unstructured meshes can be recombined in the same +// way. Let's define a simple geometry with an analytical mesh size field: + +Point(1) = {-1.25, -.5, 0}; Point(2) = {1.25, -.5, 0}; +Point(3) = {1.25, 1.25, 0}; Point(4) = {-1.25, 1.25, 0}; + +Line(1) = {1, 2}; Line(2) = {2, 3}; +Line(3) = {3, 4}; Line(4) = {4, 1}; + +Curve Loop(4) = {1, 2, 3, 4}; Plane Surface(100) = {4}; + +// Field[1] = MathEval; +// Field[1].F = "0.01*(1.0+30.*(y-x*x)*(y-x*x) + (1-x)*(1-x))"; +Background Field = 1; + +// To generate quadrangles instead of triangles, we can simply add + +Recombine Surface{100}; \ No newline at end of file diff --git a/agglomeration_poisson/meshes/t2.msh b/agglomeration_poisson/meshes/t2.msh new file mode 100644 index 00000000..5d128b01 --- /dev/null +++ b/agglomeration_poisson/meshes/t2.msh @@ -0,0 +1,364 @@ +$MeshFormat +4.1 0 8 +$EndMeshFormat +$Entities +4 4 1 0 +1 -1.25 -0.5 0 0 +2 1.25 -0.5 0 0 +3 1.25 1.25 0 0 +4 -1.25 1.25 0 0 +1 -1.25 -0.5 0 1.25 -0.5 0 0 2 1 -2 +2 1.25 -0.5 0 1.25 1.25 0 0 2 2 -3 +3 -1.25 1.25 0 1.25 1.25 0 0 2 3 -4 +4 -1.25 -0.5 0 -1.25 1.25 0 0 2 4 -1 +100 -1.25 -0.5 0 1.25 1.25 0 0 4 1 2 3 4 +$EndEntities +$Nodes +9 102 1 102 +0 1 0 1 +1 +-1.25 -0.5 0 +0 2 0 1 +2 +1.25 -0.5 0 +0 3 0 1 +3 +1.25 1.25 0 +0 4 0 1 +4 +-1.25 1.25 0 +1 1 0 9 +5 +6 +7 +8 +9 +10 +11 +12 +13 +-0.9999999999996921 -0.5 0 +-0.7500000000002168 -0.5 0 +-0.5000000000010191 -0.5 0 +-0.2500000000018128 -0.5 0 +-1.539435245945242e-12 -0.5 0 +0.2499999999987685 -0.5 0 +0.4999999999990765 -0.5 0 +0.7499999999993843 -0.5 0 +0.9999999999996918 -0.5 0 +1 2 0 5 +14 +15 +16 +17 +18 +1.25 -0.2083333333334702 0 +1.25 0.08333333333306003 0 +1.25 0.374999999999612 0 +1.25 0.6666666666669445 0 +1.25 0.9583333333334725 0 +1 3 0 9 +19 +20 +21 +22 +23 +24 +25 +26 +27 +0.9999999999996921 1.25 0 +0.7500000000002168 1.25 0 +0.5000000000010191 1.25 0 +0.2500000000018128 1.25 0 +1.539435245945242e-12 1.25 0 +-0.2499999999987685 1.25 0 +-0.4999999999990765 1.25 0 +-0.7499999999993843 1.25 0 +-0.9999999999996918 1.25 0 +1 4 0 5 +28 +29 +30 +31 +32 +-1.25 0.9583333333334703 0 +-1.25 0.66666666666694 0 +-1.25 0.375000000000388 0 +-1.25 0.08333333333305548 0 +-1.25 -0.2083333333334725 0 +2 100 0 70 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +-0.09832313296136799 0.3683953750203416 0 +0.5171187417618349 0.5500914175488165 0 +-0.5950662784461108 0.09943224189452268 0 +-0.6400897216585079 0.700022122788047 0 +0.3281652535000364 -0.0140746970674548 0 +0.09259766418535602 0.7546548658121655 0 +-0.1624243018807245 -0.0672607323786556 0 +0.8000694685550173 -0.02684243999138831 0 +-0.2798600062450662 0.8719824313403125 0 +0.7648496842689214 0.8186218988048181 0 +-0.7929704665905351 0.4114359163862225 0 +-0.85763312638216 0.8440904584040968 0 +0.4431395482495333 0.8285524188542943 0 +0.2582336310047977 0.4289782525459785 0 +-0.8646591863088906 -0.1625070382937182 0 +0.8966982224709901 0.3450974096534021 0 +-0.5620159144168502 0.3088363749819478 0 +-0.400586151287993 -0.0891325312352246 0 +0.04379884949398591 -0.2714444826562595 0 +0.5328748805060167 -0.2575782441408355 0 +0.6402218940136691 0.229360246422473 0 +-0.5446555661269338 0.9855096269079129 0 +0.09778646116810956 0.1894211941780469 0 +-0.3630571133768193 0.1259014430339827 0 +-0.2970088443760474 0.6066769121828981 0 +-0.8945891727633589 0.649567730789146 0 +-0.8514832599026646 0.09264161228678842 0 +-0.03869562181464721 1.023273212925538 0 +1.010918321824889 -0.2398913060285651 0 +1.008491014715635 0.5918696913954601 0 +-0.04376125666827668 0.6000084693147865 0 +0.2475515822960489 0.9896836261813564 0 +0.3080147573221095 0.6226563441381057 0 +-0.6760612874351772 -0.3090661718236499 0 +0.5862331833008537 0.9990093416725687 0 +-0.2029202346695422 -0.2844379875232418 0 +0.9976266382972021 0.119047360237154 0 +0.7728999907565692 -0.255326156932941 0 +1.024452171219613 0.9976879683860711 0 +-0.2729732536620909 1.029252089896009 0 +0.288976480638883 -0.2613054285848186 0 +0.5716749679501403 0.7253662326827277 0 +-0.5800508244080359 0.5095848239265953 0 +0.5732684149571884 -0.0153962406170322 0 +-0.1322201629677858 0.1528930840394495 0 +-1.022376488185384 1.01799212901863 0 +-1.044486197799518 -0.2045894104819422 0 +-0.4314836960713059 0.7915538274898013 0 +-1.032584703958603 0.8574555269601758 0 +-0.7976861478856049 1.032691233252323 0 +1.020904052830701 0.7513658287896386 0 +-1.045530212317143 -0.01883793154188736 0 +0.7816820779419098 0.4931946667362471 0 +0.3688146307020789 0.2812918898429694 0 +-0.7312444940720553 0.2786012531425121 0 +-0.1247411858489312 0.8221722139202079 0 +0.07877004354204205 -0.03696868411895102 0 +-1.063512566390326 0.4174919198336549 0 +0.1197664567109984 0.5311103009335271 0 +0.4572278403958363 1.090260960579954 0 +-0.6900113721136172 0.8367479513225612 0 +-0.3313305555248957 0.3482791832000949 0 +1.020189964678092 -0.003153224035542601 0 +-0.4461050947267712 -0.296281878681066 0 +0.8570560858812439 0.182045251296853 0 +-0.8358305277000119 -0.3078146298420761 0 +0.1054517676749012 0.3907521701589055 0 +0.8553724779434739 1.016353144702078 0 +-0.6316795229278442 -0.109822059123027 0 +-0.9875167722670536 0.2741536050645538 0 +$EndNodes +$Elements +9 121 1 121 +0 1 15 1 +1 1 +0 2 15 1 +2 2 +0 3 15 1 +3 3 +0 4 15 1 +4 4 +1 1 1 10 +5 1 5 +6 5 6 +7 6 7 +8 7 8 +9 8 9 +10 9 10 +11 10 11 +12 11 12 +13 12 13 +14 13 2 +1 2 1 6 +15 2 14 +16 14 15 +17 15 16 +18 16 17 +19 17 18 +20 18 3 +1 3 1 10 +21 3 19 +22 19 20 +23 20 21 +24 21 22 +25 22 23 +26 23 24 +27 24 25 +28 25 26 +29 26 27 +30 27 4 +1 4 1 6 +31 4 28 +32 28 29 +33 29 30 +34 30 31 +35 31 32 +36 32 1 +2 100 3 85 +37 65 45 64 38 +38 78 82 26 27 +39 60 64 22 23 +40 86 34 65 46 +41 72 60 23 24 +42 52 11 12 70 +43 12 13 61 70 +44 86 53 85 34 +45 45 65 34 74 +46 36 58 43 75 +47 39 50 96 68 +48 49 87 59 35 +49 68 51 89 39 +50 36 93 44 58 +51 56 35 101 50 +52 19 71 18 3 +53 86 46 99 55 +54 35 59 47 101 +55 73 37 89 51 +56 9 10 73 51 +57 73 52 76 37 +58 70 40 76 52 +59 54 25 26 82 +60 85 53 97 48 +61 20 21 92 67 +62 96 66 6 7 +63 63 57 94 33 +64 86 55 89 37 +65 80 41 72 54 +66 33 99 91 63 +67 50 39 77 56 +68 54 93 36 80 +69 27 4 28 78 +70 5 79 32 1 +71 77 39 89 55 +72 35 56 94 49 +73 96 50 101 66 +74 54 72 24 25 +75 51 68 8 9 +76 52 73 10 11 +77 95 40 70 61 +78 58 29 30 90 +79 88 41 80 57 +80 87 43 102 59 +81 45 74 42 67 +82 94 57 75 49 +83 44 81 29 58 +84 64 60 88 38 +85 21 22 64 92 +86 37 76 53 86 +87 72 41 88 60 +88 84 59 102 31 +89 40 97 53 76 +90 33 77 55 99 +91 85 62 83 42 +92 16 62 85 48 +93 63 91 65 38 +94 57 63 38 88 +95 16 17 83 62 +96 42 74 34 85 +97 68 96 7 8 +98 66 98 5 6 +99 15 95 61 14 +100 67 92 64 45 +101 95 69 97 40 +102 16 48 97 69 +103 17 18 71 83 +104 20 100 71 19 +105 42 100 20 67 +106 65 91 99 46 +107 98 66 101 47 +108 100 42 83 71 +109 58 90 102 43 +110 16 69 95 15 +111 82 78 81 44 +112 59 84 79 47 +113 49 75 43 87 +114 29 81 78 28 +115 31 32 79 84 +116 33 94 56 77 +117 54 82 44 93 +118 5 98 47 79 +119 57 80 36 75 +120 31 102 90 30 +121 14 61 13 2 +$EndElements diff --git a/agglomeration_poisson/meshes/t3.geo b/agglomeration_poisson/meshes/t3.geo new file mode 100644 index 00000000..4436276b --- /dev/null +++ b/agglomeration_poisson/meshes/t3.geo @@ -0,0 +1,28 @@ +// ----------------------------------------------------------------------------- +// +// Gmsh GEO tutorial 11 +// +// Unstructured quadrangular meshes +// +// ----------------------------------------------------------------------------- + +// We have seen in tutorials `t3.geo' and `t6.geo' that extruded and transfinite +// meshes can be "recombined" into quads, prisms or hexahedra by using the +// "Recombine" keyword. Unstructured meshes can be recombined in the same +// way. Let's define a simple geometry with an analytical mesh size field: + +Point(1) = {0., 0., 0}; Point(2) = {1., 0., 0}; +Point(3) = {1., 1., 0}; Point(4) = {0., 1., 0}; + +Line(1) = {1, 2}; Line(2) = {2, 3}; +Line(3) = {3, 4}; Line(4) = {4, 1}; + +Curve Loop(4) = {1, 2, 3, 4}; Plane Surface(100) = {4}; + +// Field[1] = MathEval; +// Field[1].F = "0.01*(1.0+30.*(y-x*x)*(y-x*x) + (1-x)*(1-x))"; +Background Field = 1; + +// To generate quadrangles instead of triangles, we can simply add + +Recombine Surface{100}; \ No newline at end of file diff --git a/agglomeration_poisson/meshes/t3.msh b/agglomeration_poisson/meshes/t3.msh new file mode 100644 index 00000000..08ec6b77 --- /dev/null +++ b/agglomeration_poisson/meshes/t3.msh @@ -0,0 +1,382 @@ +$MeshFormat +4.1 0 8 +$EndMeshFormat +$Entities +4 4 1 0 +1 0 0 0 0 +2 1 0 0 0 +3 1 1 0 0 +4 0 1 0 0 +1 0 0 0 1 0 0 0 2 1 -2 +2 1 0 0 1 1 0 0 2 2 -3 +3 0 1 0 1 1 0 0 2 3 -4 +4 0 0 0 0 1 0 0 2 4 -1 +100 0 0 0 1 1 0 0 4 1 2 3 4 +$EndEntities +$Nodes +9 108 1 108 +0 1 0 1 +1 +0 0 0 +0 2 0 1 +2 +1 0 0 +0 3 0 1 +3 +1 1 0 +0 4 0 1 +4 +0 1 0 +1 1 0 7 +5 +6 +7 +8 +9 +10 +11 +0.1249999999997731 0 0 +0.2499999999994107 0 0 +0.374999999999046 0 0 +0.4999999999986922 0 0 +0.6249999999990107 0 0 +0.7499999999993404 0 0 +0.8749999999996702 0 0 +1 2 0 7 +12 +13 +14 +15 +16 +17 +18 +1 0.1249999999997731 0 +1 0.2499999999994107 0 +1 0.374999999999046 0 +1 0.4999999999986922 0 +1 0.6249999999990107 0 +1 0.7499999999993404 0 +1 0.8749999999996702 0 +1 3 0 7 +19 +20 +21 +22 +23 +24 +25 +0.8749999999995015 1 0 +0.7500000000003476 1 0 +0.6250000000012155 1 0 +0.5000000000020615 1 0 +0.3750000000015629 1 0 +0.2500000000010419 1 0 +0.1250000000005209 1 0 +1 4 0 7 +26 +27 +28 +29 +30 +31 +32 +0 0.8749999999995015 0 +0 0.7500000000003476 0 +0 0.6250000000012155 0 +0 0.5000000000020615 0 +0 0.3750000000015629 0 +0 0.2500000000010419 0 +0 0.1250000000005209 0 +2 100 0 76 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +0.503501210952674 0.5305839901209262 0 +0.6942407700434752 0.6798621195953681 0 +0.3206341550738262 0.6968598453070916 0 +0.6769393310403389 0.3069458642867186 0 +0.3060189717879025 0.3229547154352804 0 +0.4867599217093032 0.7950271064161099 0 +0.2040175824919035 0.4835528617084321 0 +0.7926509560572483 0.5126839902182272 0 +0.5128646815587427 0.2073338332504069 0 +0.8392508566777073 0.8139831352234977 0 +0.1860844363657626 0.8392712019596287 0 +0.8140435920095974 0.1607975324360289 0 +0.1607966467592726 0.1862067607410803 0 +0.5508382332704997 0.6244740025302096 0 +0.3601191571983056 0.5684523279534492 0 +0.5969176378959661 0.4622951541503035 0 +0.4455186052276263 0.3799205198266247 0 +0.6222813900350821 0.8487323410419246 0 +0.1513741490112091 0.6225906059959003 0 +0.84784383128208 0.378128153976362 0 +0.3779481325802844 0.1523890841137806 0 +0.3719857633831656 0.8893223089876053 0 +0.889128196678698 0.6280386633289134 0 +0.1107096319053588 0.3719118844951486 0 +0.6280967071932586 0.1110055038827389 0 +0.4308509710180405 0.6710747048951622 0 +0.334061493425355 0.4344021003471804 0 +0.6594246712171535 0.5715285257732502 0 +0.5602136872407 0.3385736243784459 0 +0.4462815025779339 0.8913638390052673 0 +0.1085594630173113 0.4458179219241999 0 +0.8908681638441919 0.5536706834588853 0 +0.5537152226704879 0.1092900472702152 0 +0.731826769150998 0.8988216172351036 0 +0.1011935139974098 0.7318503656403255 0 +0.8988167872544847 0.2682261532693396 0 +0.2682236232881479 0.1012750181307538 0 +0.8730092650990028 0.7240647505683416 0 +0.7241400641280884 0.1270837420319705 0 +0.2760172229683994 0.8730643835132147 0 +0.1270275798675799 0.2761499260468754 0 +0.5919509445920679 0.7345204078151798 0 +0.2640773889525423 0.5956657197441078 0 +0.7277737674301493 0.4104521545407531 0 +0.4069046912558426 0.2688610821336825 0 +0.5438374419139871 0.8791137588528191 0 +0.1206797703020509 0.5436966939992822 0 +0.8778584871418256 0.4563929386001843 0 +0.4560772247802358 0.122015046692696 0 +0.367529277632855 0.7784363054940432 0 +0.7886783313038435 0.6398938192552009 0 +0.2105357569132224 0.3605795881872284 0 +0.6389334988909361 0.2114819951281795 0 +0.9028848636438731 0.902884865794588 0 +0.09711513422304896 0.9028848636462924 0 +0.9028848658136241 0.0971151363591552 0 +0.09711513635072225 0.09711513424948652 0 +0.7370185046385402 0.7941855903152235 0 +0.2062631004804109 0.7370141125252966 0 +0.7934931167346435 0.2633360516249019 0 +0.2630124346220526 0.2066494067601173 0 +0.801524995972853 0.7148893548287539 0 +0.2865416927366418 0.8000908126573411 0 +0.7144340528033952 0.1987431376655361 0 +0.1984233033966108 0.2857651340123553 0 +0.4847726232734851 0.5689026898049103 0 +0.5409513541683244 0.5361388899809862 0 +0.4833125112932423 0.4597674230337673 0 +0.7995128737200753 0.904499878503802 0 +0.09554631046526975 0.7995200971207616 0 +0.904470753876745 0.2005266226161684 0 +0.2005056778287199 0.09561066399559177 0 +0.9064543058193955 0.8264398800501755 0 +0.82645870073321 0.09356716827651582 0 +0.1735867980507265 0.906463920764619 0 +0.09355871740558798 0.1736313945069894 0 +$EndNodes +$Elements +9 127 1 127 +0 1 15 1 +1 1 +0 2 15 1 +2 2 +0 3 15 1 +3 3 +0 4 15 1 +4 4 +1 1 1 8 +5 1 5 +6 5 6 +7 6 7 +8 7 8 +9 8 9 +10 9 10 +11 10 11 +12 11 2 +1 2 1 8 +13 2 12 +14 12 13 +15 13 14 +16 14 15 +17 15 16 +18 16 17 +19 17 18 +20 18 3 +1 3 1 8 +21 3 19 +22 19 20 +23 20 21 +24 21 22 +25 22 23 +26 23 24 +27 24 25 +28 25 4 +1 4 1 8 +29 4 26 +30 26 27 +31 27 28 +32 28 29 +33 29 30 +34 30 31 +35 31 32 +36 32 1 +2 100 3 91 +37 56 30 31 73 +38 54 23 24 72 +39 57 9 10 71 +40 55 16 17 70 +41 58 46 74 38 +42 59 47 75 39 +43 60 48 76 40 +44 61 49 77 41 +45 60 34 74 46 +46 58 35 75 47 +47 61 36 76 48 +48 59 37 77 49 +49 62 54 82 38 +50 64 55 83 40 +51 63 56 84 39 +52 65 57 85 41 +53 78 38 74 50 +54 79 39 75 51 +55 80 40 76 52 +56 81 41 77 53 +57 50 74 34 90 +58 51 75 35 91 +59 52 76 36 92 +60 53 77 37 93 +61 54 62 22 23 +62 56 63 29 30 +63 55 64 15 16 +64 57 65 8 9 +65 12 88 11 2 +66 5 89 32 1 +67 19 86 18 3 +68 26 87 25 4 +69 35 58 38 82 +70 34 60 40 83 +71 37 59 39 84 +72 36 61 41 85 +73 50 21 22 78 +74 51 28 29 79 +75 52 14 15 80 +76 53 7 8 81 +77 47 100 33 98 +78 48 99 33 100 +79 47 98 46 58 +80 48 60 46 99 +81 49 100 47 59 +82 49 61 48 100 +83 38 78 22 62 +84 39 79 29 63 +85 40 80 15 64 +86 41 81 8 65 +87 21 50 90 66 +88 28 51 91 67 +89 14 52 92 68 +90 7 53 93 69 +91 70 42 90 94 +92 71 44 92 96 +93 72 43 91 95 +94 73 45 93 97 +95 101 66 90 42 +96 102 67 91 43 +97 103 68 92 44 +98 104 69 93 45 +99 21 66 101 20 +100 28 67 102 27 +101 14 68 103 13 +102 7 69 104 6 +103 42 70 17 105 +104 43 72 24 107 +105 44 71 10 106 +106 45 73 31 108 +107 83 55 70 94 +108 85 57 71 96 +109 82 54 72 95 +110 84 56 73 97 +111 101 42 105 86 +112 102 43 107 87 +113 103 44 106 88 +114 104 45 108 89 +115 83 94 90 34 +116 82 95 91 35 +117 85 96 92 36 +118 84 97 93 37 +119 20 101 86 19 +120 27 102 87 26 +121 13 103 88 12 +122 6 104 89 5 +123 17 18 86 105 +124 24 25 87 107 +125 10 11 88 106 +126 31 32 89 108 +127 33 99 46 98 +$EndElements diff --git a/agglomeration_poisson/source/agglomeration_handler.cc b/agglomeration_poisson/source/agglomeration_handler.cc new file mode 100644 index 00000000..96e3ef92 --- /dev/null +++ b/agglomeration_poisson/source/agglomeration_handler.cc @@ -0,0 +1,1649 @@ +/* ----------------------------------------------------------------------------- + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright (C) 2025 by Marco Feder, Pasquale Claudio Africa, Xinping Gui, + * Andrea Cangiani + * + * This file is part of the deal.II code gallery. + * + * ----------------------------------------------------------------------------- + */ + +#include + +#include + +template +AgglomerationHandler::AgglomerationHandler( + const GridTools::Cache &cache_tria) + : cached_tria(std::make_unique>( + cache_tria.get_triangulation(), + cache_tria.get_mapping())) + , communicator(cache_tria.get_triangulation().get_communicator()) +{ + Assert(dim == spacedim, ExcNotImplemented("Not available with codim > 0")); + Assert(dim == 2 || dim == 3, ExcImpossibleInDim(1)); + Assert((dynamic_cast *>( + &cached_tria->get_triangulation()) == nullptr), + ExcNotImplemented()); + Assert(cached_tria->get_triangulation().n_active_cells() > 0, + ExcMessage( + "The triangulation must not be empty upon calling this function.")); + + n_agglomerations = 0; + hybrid_mesh = false; + initialize_agglomeration_data(cached_tria); +} + + + +template +typename AgglomerationHandler::agglomeration_iterator +AgglomerationHandler::define_agglomerate( + const AgglomerationContainer &cells) +{ + Assert(cells.size() > 0, ExcMessage("No cells to be agglomerated.")); + + if (cells.size() == 1) + hybrid_mesh = true; // mesh is made also by classical cells + + // First index drives the selection of the master cell. After that, store the + // master cell. + const types::global_cell_index global_master_idx = + cells[0]->global_active_cell_index(); + const types::global_cell_index master_idx = cells[0]->active_cell_index(); + master_cells_container.push_back(cells[0]); + master_slave_relationships[global_master_idx] = -1; + + const typename DoFHandler::active_cell_iterator cell_dh = + cells[0]->as_dof_handler_iterator(agglo_dh); + cell_dh->set_active_fe_index(CellAgglomerationType::master); + + // Store slave cells and save the relationship with the parent + std::vector::active_cell_iterator> + slaves; + slaves.reserve(cells.size() - 1); + // exclude first cell since it's the master cell + for (auto it = ++cells.begin(); it != cells.end(); ++it) + { + slaves.push_back(*it); + master_slave_relationships[(*it)->global_active_cell_index()] = + global_master_idx; // mark each slave + master_slave_relationships_iterators[(*it)->active_cell_index()] = + cells[0]; + + const typename DoFHandler::active_cell_iterator cell = + (*it)->as_dof_handler_iterator(agglo_dh); + cell->set_active_fe_index(CellAgglomerationType::slave); // slave cell + + // If we have a p::d::T, check that all cells are in the same subdomain. + // If serial, just check that the subdomain_id is invalid. + Assert(((*it)->subdomain_id() == tria->locally_owned_subdomain() || + tria->locally_owned_subdomain() == numbers::invalid_subdomain_id), + ExcInternalError()); + } + + master_slave_relationships_iterators[master_idx] = + cells[0]; // set iterator to master cell + + // Store the slaves of each master + master2slaves[master_idx] = slaves; + // Save to which polygon this agglomerate correspond + master2polygon[master_idx] = n_agglomerations; + + ++n_agglomerations; // an agglomeration has been performed, record it + + create_bounding_box(cells); // fill the vector of bboxes + + // Finally, return a polygonal iterator to the polytope just constructed. + return {cells[0], this}; +} + +template +typename AgglomerationHandler::agglomeration_iterator +AgglomerationHandler::define_agglomerate( + const AgglomerationContainer &cells, + const unsigned int fecollection_size) +{ + Assert(cells.size() > 0, ExcMessage("No cells to be agglomerated.")); + + if (cells.size() == 1) + hybrid_mesh = true; // mesh is made also by classical cells + + // First index drives the selection of the master cell. After that, store the + // master cell. + const types::global_cell_index global_master_idx = + cells[0]->global_active_cell_index(); + const types::global_cell_index master_idx = cells[0]->active_cell_index(); + master_cells_container.push_back(cells[0]); + master_slave_relationships[global_master_idx] = -1; + + const typename DoFHandler::active_cell_iterator cell_dh = + cells[0]->as_dof_handler_iterator(agglo_dh); + cell_dh->set_active_fe_index(CellAgglomerationType::master); + + // Store slave cells and save the relationship with the parent + std::vector::active_cell_iterator> + slaves; + slaves.reserve(cells.size() - 1); + // exclude first cell since it's the master cell + for (auto it = ++cells.begin(); it != cells.end(); ++it) + { + slaves.push_back(*it); + master_slave_relationships[(*it)->global_active_cell_index()] = + global_master_idx; // mark each slave + master_slave_relationships_iterators[(*it)->active_cell_index()] = + cells[0]; + + const typename DoFHandler::active_cell_iterator cell = + (*it)->as_dof_handler_iterator(agglo_dh); + cell->set_active_fe_index( + fecollection_size); // slave cell (the last index) + + // If we have a p::d::T, check that all cells are in the same subdomain. + // If serial, just check that the subdomain_id is invalid. + Assert(((*it)->subdomain_id() == tria->locally_owned_subdomain() || + tria->locally_owned_subdomain() == numbers::invalid_subdomain_id), + ExcInternalError()); + } + + master_slave_relationships_iterators[master_idx] = + cells[0]; // set iterator to master cell + + // Store the slaves of each master + master2slaves[master_idx] = slaves; + // Save to which polygon this agglomerate correspond + master2polygon[master_idx] = n_agglomerations; + + ++n_agglomerations; // an agglomeration has been performed, record it + + create_bounding_box(cells); // fill the vector of bboxes + + // Finally, return a polygonal iterator to the polytope just constructed. + return {cells[0], this}; +} + + +template +void +AgglomerationHandler::initialize_fe_values( + const Quadrature &cell_quadrature, + const UpdateFlags &flags, + const Quadrature &face_quadrature, + const UpdateFlags &face_flags) +{ + agglomeration_quad = cell_quadrature; + agglomeration_flags = flags; + agglomeration_face_quad = face_quadrature; + agglomeration_face_flags = face_flags | internal_agglomeration_face_flags; + + + no_values = + std::make_unique>(*mapping, + dummy_fe, + agglomeration_quad, + update_quadrature_points | + update_JxW_values); // only for quadrature + no_face_values = std::make_unique>( + *mapping, + dummy_fe, + agglomeration_face_quad, + update_quadrature_points | update_JxW_values | + update_normal_vectors); // only for quadrature +} + +template +void +AgglomerationHandler::initialize_fe_values( + const hp::QCollection &cell_qcollection, + const UpdateFlags &flags, + const hp::QCollection &face_qcollection, + const UpdateFlags &face_flags) +{ + agglomeration_quad_collection = cell_qcollection; + agglomeration_flags = flags; + agglomeration_face_quad_collection = face_qcollection; + agglomeration_face_flags = face_flags | internal_agglomeration_face_flags; + + mapping_collection = hp::MappingCollection(*mapping); + dummy_fe_collection = hp::FECollection(dummy_fe); + hp_no_values = std::make_unique>( + mapping_collection, + dummy_fe_collection, + agglomeration_quad_collection, + update_quadrature_points | update_JxW_values); // only for quadrature + + hp_no_face_values = std::make_unique>( + mapping_collection, + dummy_fe_collection, + agglomeration_face_quad_collection, + update_quadrature_points | update_JxW_values | + update_normal_vectors); // only for quadrature +} + + + +template +unsigned int +AgglomerationHandler::n_agglomerated_faces_per_cell( + const typename Triangulation::active_cell_iterator &cell) const +{ + unsigned int n_neighbors = 0; + for (const auto &f : cell->face_indices()) + { + const auto &neighboring_cell = cell->neighbor(f); + if ((cell->face(f)->at_boundary()) || + (neighboring_cell->is_active() && + !are_cells_agglomerated(cell, neighboring_cell))) + { + ++n_neighbors; + } + } + return n_neighbors; +} + + + +template +void +AgglomerationHandler::initialize_agglomeration_data( + const std::unique_ptr> &cache_tria) +{ + tria = &(cache_tria->get_triangulation()); + mapping = &(cache_tria->get_mapping()); + + agglo_dh.reinit(*tria); + + if (const auto parallel_tria = dynamic_cast< + const dealii::parallel::TriangulationBase *>(&*tria)) + { + const std::weak_ptr cells_partitioner = + parallel_tria->global_active_cell_index_partitioner(); + master_slave_relationships.reinit( + cells_partitioner.lock()->locally_owned_range(), communicator); + } + else + { + master_slave_relationships.reinit(tria->n_active_cells(), MPI_COMM_SELF); + } + + polytope_cache.clear(); + bboxes.clear(); + + // First, update the pointer + cached_tria = std::make_unique>( + cache_tria->get_triangulation(), cache_tria->get_mapping()); + + connect_to_tria_signals(); + n_agglomerations = 0; +} + + + +template +void +AgglomerationHandler::distribute_agglomerated_dofs( + const FiniteElement &fe_space) +{ + if (dynamic_cast *>(&fe_space)) + fe = std::make_unique>(fe_space.degree); + else if (dynamic_cast *>(&fe_space)) + fe = std::make_unique>(fe_space.degree); + else + AssertThrow( + false, + ExcNotImplemented( + "Currently, this interface supports only DGQ and DGP bases.")); + + box_mapping = std::make_unique>( + bboxes, + master2polygon); // construct bounding box mapping + + if (hybrid_mesh) + { + // the mesh is composed by standard and agglomerate cells. initialize + // classes needed for standard cells in order to treat that finite + // element space as defined on a standard shape and not on the + // BoundingBox. + standard_scratch = + std::make_unique(*mapping, + *fe, + QGauss(2 * fe_space.degree + 2), + internal_agglomeration_flags); + } + + + fe_collection.push_back(*fe); // master + fe_collection.push_back( + FE_Nothing(fe->reference_cell())); // slave + + initialize_hp_structure(); + + // in case the tria is distributed, communicate ghost information with + // neighboring ranks + const bool needs_ghost_info = + dynamic_cast *>(&*tria) != + nullptr; + if (needs_ghost_info) + setup_ghost_polytopes(); + + setup_connectivity_of_agglomeration(); + + if (needs_ghost_info) + exchange_interface_values(); +} + +template +void +AgglomerationHandler::distribute_agglomerated_dofs( + const hp::FECollection &fe_collection_in) +{ + is_hp_collection = true; + + hp_fe_collection = std::make_unique>( + fe_collection_in); // copy the input collection + + box_mapping = std::make_unique>( + bboxes, + master2polygon); // construct bounding box mapping + + + if (hybrid_mesh) + { + AssertThrow(false, + ExcNotImplemented( + "Hybrid mesh is not implemented for hp::FECollection.")); + } + + for (unsigned int i = 0; i < fe_collection_in.size(); ++i) + { + if (dynamic_cast *>(&fe_collection_in[i])) + { + // System case + for (unsigned int b = 0; b < fe_collection_in[i].n_base_elements(); + ++b) + { + if (!(dynamic_cast *>( + &fe_collection_in[i].base_element(b)) || + dynamic_cast *>( + &fe_collection_in[i].base_element(b)) || + dynamic_cast *>( + &fe_collection_in[i].base_element(b)))) + AssertThrow( + false, + ExcNotImplemented( + "Currently, this interface supports only DGQ and DGP bases.")); + } + } + else + { + // Scalar case + if (!(dynamic_cast *>(&fe_collection_in[i]) || + dynamic_cast *>(&fe_collection_in[i]))) + AssertThrow( + false, + ExcNotImplemented( + "Currently, this interface supports only DGQ and DGP bases.")); + } + fe_collection.push_back(fe_collection_in[i]); + } + + Assert(fe_collection[0].n_components() >= 1, + ExcMessage("Invalid FE: must have at least one component.")); + if (fe_collection[0].n_components() == 1) + { + fe_collection.push_back(FE_Nothing()); + } + else if (fe_collection[0].n_components() > 1) + { + std::vector *> base_elements; + std::vector multiplicities; + for (unsigned int b = 0; b < fe_collection[0].n_base_elements(); ++b) + { + base_elements.push_back(new FE_Nothing()); + multiplicities.push_back(fe_collection[0].element_multiplicity(b)); + } + FESystem fe_system_nothing(base_elements, multiplicities); + for (const auto *ptr : base_elements) + delete ptr; + fe_collection.push_back(fe_system_nothing); + } + + initialize_hp_structure(); + + // in case the tria is distributed, communicate ghost information with + // neighboring ranks + const bool needs_ghost_info = + dynamic_cast *>(&*tria) != + nullptr; + if (needs_ghost_info) + setup_ghost_polytopes(); + + setup_connectivity_of_agglomeration(); + + if (needs_ghost_info) + exchange_interface_values(); +} + +template +void +AgglomerationHandler::create_bounding_box( + const AgglomerationContainer &polytope) +{ + Assert(n_agglomerations > 0, + ExcMessage("No agglomeration has been performed.")); + Assert(dim > 1, ExcNotImplemented()); + + std::vector> pts; // store all the vertices + for (const auto &cell : polytope) + for (const auto i : cell->vertex_indices()) + pts.push_back(cell->vertex(i)); + + bboxes.emplace_back(pts); +} + + + +template +void +AgglomerationHandler::setup_connectivity_of_agglomeration() +{ + Assert(master_cells_container.size() > 0, + ExcMessage("No agglomeration has been performed.")); + Assert( + agglo_dh.n_dofs() > 0, + ExcMessage( + "The DoFHandler associated to the agglomeration has not been initialized." + "It's likely that you forgot to distribute the DoFs. You may want" + "to check if a call to `initialize_hp_structure()` has been done.")); + + number_of_agglomerated_faces.resize(master2polygon.size(), 0); + for (const auto &cell : master_cells_container) + { + internal::AgglomerationHandlerImplementation:: + setup_master_neighbor_connectivity(cell, *this); + } + + if (Utilities::MPI::job_supports_mpi()) + { + // communicate the number of faces + recv_n_faces = Utilities::MPI::some_to_some(communicator, local_n_faces); + + // send information about boundaries and neighboring polytopes id + recv_bdary_info = + Utilities::MPI::some_to_some(communicator, local_bdary_info); + + recv_ghosted_master_id = + Utilities::MPI::some_to_some(communicator, local_ghosted_master_id); + } +} + + + +template +void +AgglomerationHandler::exchange_interface_values() +{ + const unsigned int dofs_per_cell = fe->dofs_per_cell; + for (const auto &polytope : polytope_iterators()) + { + if (polytope->is_locally_owned()) + { + const unsigned int n_faces = polytope->n_faces(); + for (unsigned int f = 0; f < n_faces; ++f) + { + if (!polytope->at_boundary(f)) + { + const auto &neigh_polytope = polytope->neighbor(f); + if (!neigh_polytope->is_locally_owned()) + { + // Neighboring polytope is ghosted. + + // Compute shape functions at the interface + const auto ¤t_fe = reinit(polytope, f); + + std::vector> qpoints_to_send = + current_fe.get_quadrature_points(); + + const std::vector &jxws_to_send = + current_fe.get_JxW_values(); + + const std::vector> &normals_to_send = + current_fe.get_normal_vectors(); + + + const types::subdomain_id neigh_rank = + neigh_polytope->subdomain_id(); + + std::pair cell_and_face{ + polytope->id(), f}; + // Prepare data to send + local_qpoints[neigh_rank].emplace(cell_and_face, + qpoints_to_send); + + local_jxws[neigh_rank].emplace(cell_and_face, + jxws_to_send); + + local_normals[neigh_rank].emplace(cell_and_face, + normals_to_send); + + + const unsigned int n_qpoints = qpoints_to_send.size(); + + // TODO: check `agglomeration_flags` before computing + // values and gradients. + std::vector> values_per_qpoints( + dofs_per_cell); + + std::vector>> + gradients_per_qpoints(dofs_per_cell); + + for (unsigned int i = 0; i < dofs_per_cell; ++i) + { + values_per_qpoints[i].resize(n_qpoints); + gradients_per_qpoints[i].resize(n_qpoints); + for (unsigned int q = 0; q < n_qpoints; ++q) + { + values_per_qpoints[i][q] = + current_fe.shape_value(i, q); + gradients_per_qpoints[i][q] = + current_fe.shape_grad(i, q); + } + } + + local_values[neigh_rank].emplace(cell_and_face, + values_per_qpoints); + local_gradients[neigh_rank].emplace( + cell_and_face, gradients_per_qpoints); + } + } + } + } + } + + // Finally, exchange with neighboring ranks + recv_qpoints = Utilities::MPI::some_to_some(communicator, local_qpoints); + recv_jxws = Utilities::MPI::some_to_some(communicator, local_jxws); + recv_normals = Utilities::MPI::some_to_some(communicator, local_normals); + recv_values = Utilities::MPI::some_to_some(communicator, local_values); + recv_gradients = Utilities::MPI::some_to_some(communicator, local_gradients); +} + + + +template +Quadrature +AgglomerationHandler::agglomerated_quadrature( + const typename AgglomerationHandler::AgglomerationContainer + &cells, + const typename Triangulation::active_cell_iterator + &master_cell) const +{ + Assert(is_master_cell(master_cell), + ExcMessage("This must be a master cell.")); + + std::vector> vec_pts; + std::vector vec_JxWs; + + if (!is_hp_collection) + { + // Original version: handle case without hp::FECollection + for (const auto &dummy_cell : cells) + { + no_values->reinit(dummy_cell); + auto q_points = no_values->get_quadrature_points(); // real qpoints + const auto &JxWs = no_values->get_JxW_values(); + + std::transform(q_points.begin(), + q_points.end(), + std::back_inserter(vec_pts), + [&](const Point &p) { return p; }); + std::transform(JxWs.begin(), + JxWs.end(), + std::back_inserter(vec_JxWs), + [&](const double w) { return w; }); + } + } + else + { + // Handle the hp::FECollection case + const auto &master_cell_as_dh_iterator = + master_cell->as_dof_handler_iterator(agglo_dh); + for (const auto &dummy_cell : cells) + { + // The following verbose call is necessary to handle cases where + // different slave cells on different polytopes use different + // quadrature rules. If the hp::QCollection contains multiple + // elements, calling hp_no_values->reinit(dummy_cell) won't work + // because it cannot infer the correct quadrature rule. By explicitly + // passing the active FE index as q_index, and setting mapping_index + // and fe_index to 0, we ensure that the dummy cell uses the same + // quadrature rule as its corresponding master cell. This assumes a + // one-to-one correspondence between hp::QCollection and + // hp::FECollection, which is the convention in deal.II. However, this + // implementation does not support cases where hp::QCollection and + // hp::FECollection have different sizes. + // TODO: Refactor the architecture to better handle numerical + // integration for hp::QCollection. + hp_no_values->reinit(dummy_cell, + master_cell_as_dh_iterator->active_fe_index(), + 0, + 0); + auto q_points = hp_no_values->get_present_fe_values() + .get_quadrature_points(); // real qpoints + const auto &JxWs = + hp_no_values->get_present_fe_values().get_JxW_values(); + + std::transform(q_points.begin(), + q_points.end(), + std::back_inserter(vec_pts), + [&](const Point &p) { return p; }); + std::transform(JxWs.begin(), + JxWs.end(), + std::back_inserter(vec_JxWs), + [&](const double w) { return w; }); + } + } + + // Map back each point in real space by using the map associated to the + // bounding box. + std::vector> unit_points(vec_pts.size()); + const auto &bbox = + bboxes[master2polygon.at(master_cell->active_cell_index())]; + unit_points.reserve(vec_pts.size()); + + for (unsigned int i = 0; i < vec_pts.size(); i++) + unit_points[i] = bbox.real_to_unit(vec_pts[i]); + + return Quadrature(unit_points, vec_JxWs); +} + + + +template +void +AgglomerationHandler::initialize_hp_structure() +{ + Assert(agglo_dh.get_triangulation().n_cells() > 0, + ExcMessage( + "Triangulation must not be empty upon calling this function.")); + Assert(n_agglomerations > 0, + ExcMessage("No agglomeration has been performed.")); + + agglo_dh.distribute_dofs(fe_collection); + // euler_mapping = std::make_unique< + // MappingFEField>>( euler_dh, euler_vector); +} + + + +template +const FEValues & +AgglomerationHandler::reinit( + const AgglomerationIterator &polytope) const +{ + // Assert(euler_mapping, + // ExcMessage("The mapping describing the physical element stemming + // from " + // "agglomeration has not been set up.")); + + const auto &deal_cell = polytope->as_dof_handler_iterator(agglo_dh); + + // First check if the polytope is made just by a single cell. If so, use + // classical FEValues + // if (polytope->n_background_cells() == 1) + // return standard_scratch->reinit(deal_cell); + + const auto &agglo_cells = polytope->get_agglomerate(); + + Quadrature agglo_quad = agglomerated_quadrature(agglo_cells, deal_cell); + + if (!is_hp_collection) + { + // Original version: handle case without hp::FECollection + agglomerated_scratch = std::make_unique(*box_mapping, + fe_collection[0], + agglo_quad, + agglomeration_flags); + } + else + { + // Handle the hp::FECollection case + agglomerated_scratch = std::make_unique(*box_mapping, + polytope->get_fe(), + agglo_quad, + agglomeration_flags); + } + return agglomerated_scratch->reinit(deal_cell); +} + + + +template +const FEValuesBase & +AgglomerationHandler::reinit_master( + const typename DoFHandler::active_cell_iterator &cell, + const unsigned int face_index, + std::unique_ptr> + &agglo_isv_ptr) const +{ + return internal::AgglomerationHandlerImplementation:: + reinit_master(cell, face_index, agglo_isv_ptr, *this); +} + + + +template +const FEValuesBase & +AgglomerationHandler::reinit( + const AgglomerationIterator &polytope, + const unsigned int face_index) const +{ + // Assert(euler_mapping, + // ExcMessage("The mapping describing the physical element stemming + // from " + // "agglomeration has not been set up.")); + + const auto &deal_cell = polytope->as_dof_handler_iterator(agglo_dh); + Assert(is_master_cell(deal_cell), ExcMessage("This should be true.")); + + return internal::AgglomerationHandlerImplementation:: + reinit_master(deal_cell, face_index, agglomerated_isv_bdary, *this); +} + + + +template +std::pair &, + const FEValuesBase &> +AgglomerationHandler::reinit_interface( + const AgglomerationIterator &polytope_in, + const AgglomerationIterator &neigh_polytope, + const unsigned int local_in, + const unsigned int local_neigh) const +{ + // If current and neighboring polytopes are both locally owned, then compute + // the jump in the classical way without needing information about ghosted + // entities. + if (polytope_in->is_locally_owned() && neigh_polytope->is_locally_owned()) + { + const auto &cell_in = polytope_in->as_dof_handler_iterator(agglo_dh); + const auto &neigh_cell = + neigh_polytope->as_dof_handler_iterator(agglo_dh); + + const auto &fe_in = + internal::AgglomerationHandlerImplementation:: + reinit_master(cell_in, local_in, agglomerated_isv, *this); + const auto &fe_out = + internal::AgglomerationHandlerImplementation:: + reinit_master(neigh_cell, local_neigh, agglomerated_isv_neigh, *this); + std::pair &, + const FEValuesBase &> + my_p(fe_in, fe_out); + + return my_p; + } + else + { + Assert((polytope_in->is_locally_owned() && + !neigh_polytope->is_locally_owned()), + ExcInternalError()); + + const auto &cell = polytope_in->as_dof_handler_iterator(agglo_dh); + const auto &bbox = bboxes[master2polygon.at(cell->active_cell_index())]; + // const double bbox_measure = bbox.volume(); + + const unsigned int neigh_rank = neigh_polytope->subdomain_id(); + const CellId &neigh_id = neigh_polytope->id(); + + // Retrieve qpoints,JxWs, normals sent previously from the neighboring + // rank. + std::vector> &real_qpoints = + recv_qpoints.at(neigh_rank).at({neigh_id, local_neigh}); + + const auto &JxWs = recv_jxws.at(neigh_rank).at({neigh_id, local_neigh}); + + std::vector> &normals = + recv_normals.at(neigh_rank).at({neigh_id, local_neigh}); + + // Apply the necessary scalings due to the bbox. + std::vector> final_unit_q_points; + std::transform(real_qpoints.begin(), + real_qpoints.end(), + std::back_inserter(final_unit_q_points), + [&](const Point &p) { + return bbox.real_to_unit(p); + }); + + // std::vector scale_factors(final_unit_q_points.size()); + // std::vector scaled_weights(final_unit_q_points.size()); + // std::vector> scaled_normals(final_unit_q_points.size()); + + // Since we received normal vectors from a neighbor, we have to swap + // the + // // sign of the vector in order to have outward normals. + // for (unsigned int q = 0; q < final_unit_q_points.size(); ++q) + // { + // for (unsigned int direction = 0; direction < spacedim; ++direction) + // scaled_normals[q][direction] = + // normals[q][direction] * (bbox.side_length(direction)); + + // scaled_normals[q] *= -1; + + // scaled_weights[q] = + // (JxWs[q] * scaled_normals[q].norm()) / bbox_measure; + // scaled_normals[q] /= scaled_normals[q].norm(); + // } + for (unsigned int q = 0; q < final_unit_q_points.size(); ++q) + normals[q] *= -1; + + + NonMatching::ImmersedSurfaceQuadrature surface_quad( + final_unit_q_points, JxWs, normals); + + agglomerated_isv = + std::make_unique>( + *box_mapping, *fe, surface_quad, agglomeration_face_flags); + + + agglomerated_isv->reinit(cell); + + std::pair &, + const FEValuesBase &> + my_p(*agglomerated_isv, *agglomerated_isv); + + return my_p; + } +} + + + +template +template +void +AgglomerationHandler::create_agglomeration_sparsity_pattern( + SparsityPatternType &dsp, + const AffineConstraints &constraints, + const bool keep_constrained_dofs, + const types::subdomain_id subdomain_id) +{ + Assert(n_agglomerations > 0, + ExcMessage("The agglomeration has not been set up correctly.")); + Assert(dsp.empty(), + ExcMessage( + "The Sparsity pattern must be empty upon calling this function.")); + + const IndexSet &locally_owned_dofs = agglo_dh.locally_owned_dofs(); + const IndexSet locally_relevant_dofs = + DoFTools::extract_locally_relevant_dofs(agglo_dh); + + if constexpr (std::is_same_v) + dsp.reinit(locally_owned_dofs.size(), + locally_owned_dofs.size(), + locally_relevant_dofs); + else if constexpr (std::is_same_v) + dsp.reinit(locally_owned_dofs, communicator); + else + AssertThrow(false, ExcNotImplemented()); + + // Create the sparsity pattern corresponding only to volumetric terms. The + // fluxes needed by DG methods will be filled later. + DoFTools::make_sparsity_pattern( + agglo_dh, dsp, constraints, keep_constrained_dofs, subdomain_id); + + + if (!is_hp_collection) + { + // Original version: handle case without hp::FECollection + const unsigned int dofs_per_cell = agglo_dh.get_fe(0).n_dofs_per_cell(); + std::vector current_dof_indices(dofs_per_cell); + std::vector neighbor_dof_indices(dofs_per_cell); + + // Loop over all locally owned polytopes, find the neighbor (also ghosted) + // and add fluxes to the sparsity pattern. + for (const auto &polytope : polytope_iterators()) + { + if (polytope->is_locally_owned()) + { + const unsigned int n_current_faces = polytope->n_faces(); + polytope->get_dof_indices(current_dof_indices); + for (unsigned int f = 0; f < n_current_faces; ++f) + { + const auto &neigh_polytope = polytope->neighbor(f); + if (neigh_polytope.state() == IteratorState::valid) + { + neigh_polytope->get_dof_indices(neighbor_dof_indices); + constraints.add_entries_local_to_global( + current_dof_indices, + neighbor_dof_indices, + dsp, + keep_constrained_dofs, + {}); + } + } + } + } + } + else + { + // Handle the hp::FECollection case + + // Loop over all locally owned polytopes, find the neighbor (also ghosted) + // and add fluxes to the sparsity pattern. + for (const auto &polytope : polytope_iterators()) + { + if (polytope->is_locally_owned()) + { + const unsigned int current_dofs_per_cell = + polytope->get_fe().dofs_per_cell; + std::vector current_dof_indices( + current_dofs_per_cell); + + const unsigned int n_current_faces = polytope->n_faces(); + polytope->get_dof_indices(current_dof_indices); + for (unsigned int f = 0; f < n_current_faces; ++f) + { + const auto &neigh_polytope = polytope->neighbor(f); + if (neigh_polytope.state() == IteratorState::valid) + { + const unsigned int neighbor_dofs_per_cell = + neigh_polytope->get_fe().dofs_per_cell; + std::vector neighbor_dof_indices( + neighbor_dofs_per_cell); + + neigh_polytope->get_dof_indices(neighbor_dof_indices); + constraints.add_entries_local_to_global( + current_dof_indices, + neighbor_dof_indices, + dsp, + keep_constrained_dofs, + {}); + } + } + } + } + } + + + + if constexpr (std::is_same_v) + dsp.compress(); +} + + + +template +void +AgglomerationHandler::setup_ghost_polytopes() +{ + [[maybe_unused]] const auto parallel_triangulation = + dynamic_cast *>(&*tria); + Assert(parallel_triangulation != nullptr, ExcInternalError()); + + const unsigned int n_dofs_per_cell = fe->dofs_per_cell; + std::vector global_dof_indices(n_dofs_per_cell); + for (const auto &polytope : polytope_iterators()) + if (polytope->is_locally_owned()) + { + const CellId &master_cell_id = polytope->id(); + + const auto polytope_dh = polytope->as_dof_handler_iterator(agglo_dh); + polytope_dh->get_dof_indices(global_dof_indices); + + + const auto &agglomerate = polytope->get_agglomerate(); + + for (const auto &cell : agglomerate) + { + // interior, locally owned, cell + for (const auto &f : cell->face_indices()) + { + if (!cell->at_boundary(f)) + { + const auto &neighbor = cell->neighbor(f); + if (neighbor->is_ghost()) + { + // key of the map: the rank to which send the data + const types::subdomain_id neigh_rank = + neighbor->subdomain_id(); + + // inform the "standard" neighbor about the neighboring + // id and its master cell + local_cell_ids_neigh_cell[neigh_rank].emplace( + cell->id(), master_cell_id); + + // inform the neighboring rank that this master cell + // (hence polytope) has the following DoF indices + local_ghost_dofs[neigh_rank].emplace( + master_cell_id, global_dof_indices); + + // ...same for bounding boxes + const auto &bbox = bboxes[polytope->index()]; + local_ghosted_bbox[neigh_rank].emplace(master_cell_id, + bbox); + } + } + } + } + } + + recv_cell_ids_neigh_cell = + Utilities::MPI::some_to_some(communicator, local_cell_ids_neigh_cell); + + // Exchange with neighboring ranks the neighboring bounding boxes + recv_ghosted_bbox = + Utilities::MPI::some_to_some(communicator, local_ghosted_bbox); + + // Exchange with neighboring ranks the neighboring ghosted DoFs + recv_ghost_dofs = + Utilities::MPI::some_to_some(communicator, local_ghost_dofs); +} + + + +namespace dealii +{ + namespace internal + { + template + class AgglomerationHandlerImplementation + { + public: + static const FEValuesBase & + reinit_master( + const typename DoFHandler::active_cell_iterator &cell, + const unsigned int face_index, + std::unique_ptr> + &agglo_isv_ptr, + const AgglomerationHandler &handler) + { + Assert(handler.is_master_cell(cell), + ExcMessage("This cell must be a master one.")); + + AgglomerationIterator it{cell, &handler}; + const auto &neigh_polytope = it->neighbor(face_index); + + const CellId polytope_in_id = cell->id(); + + // Retrieve the bounding box of the agglomeration + const auto &bbox = + handler.bboxes[handler.master2polygon.at(cell->active_cell_index())]; + + CellId polytope_out_id; + if (neigh_polytope.state() == IteratorState::valid) + polytope_out_id = neigh_polytope->id(); + else + polytope_out_id = polytope_in_id; // on the boundary. Same id + + const auto &common_face = handler.polytope_cache.interface.at( + {polytope_in_id, polytope_out_id}); + + std::vector> final_unit_q_points; + std::vector final_weights; + std::vector> final_normals; + + if (!handler.is_hp_collection) + { + // Original version: handle case without hp::FECollection + const unsigned int expected_qpoints = + common_face.size() * handler.agglomeration_face_quad.size(); + final_unit_q_points.reserve(expected_qpoints); + final_weights.reserve(expected_qpoints); + final_normals.reserve(expected_qpoints); + + + for (const auto &[deal_cell, local_face_idx] : common_face) + { + handler.no_face_values->reinit(deal_cell, local_face_idx); + + const auto &q_points = + handler.no_face_values->get_quadrature_points(); + const auto &JxWs = handler.no_face_values->get_JxW_values(); + const auto &normals = + handler.no_face_values->get_normal_vectors(); + + const unsigned int n_qpoints_agglo = q_points.size(); + + for (unsigned int q = 0; q < n_qpoints_agglo; ++q) + { + final_unit_q_points.push_back( + bbox.real_to_unit(q_points[q])); + final_weights.push_back(JxWs[q]); + final_normals.push_back(normals[q]); + } + } + } + else + { + // Handle the hp::FECollection case + unsigned int higher_order_quad_index = cell->active_fe_index(); + if (neigh_polytope.state() == IteratorState::valid) + if (handler + .agglomeration_face_quad_collection[cell->active_fe_index()] + .size() < + handler + .agglomeration_face_quad_collection[neigh_polytope + ->active_fe_index()] + .size()) + higher_order_quad_index = neigh_polytope->active_fe_index(); + + const unsigned int expected_qpoints = + common_face.size() * + handler + .agglomeration_face_quad_collection[higher_order_quad_index] + .size(); + final_unit_q_points.reserve(expected_qpoints); + final_weights.reserve(expected_qpoints); + final_normals.reserve(expected_qpoints); + + for (const auto &[deal_cell, local_face_idx] : common_face) + { + handler.hp_no_face_values->reinit( + deal_cell, local_face_idx, higher_order_quad_index, 0, 0); + + const auto &q_points = + handler.hp_no_face_values->get_present_fe_values() + .get_quadrature_points(); + const auto &JxWs = + handler.hp_no_face_values->get_present_fe_values() + .get_JxW_values(); + const auto &normals = + handler.hp_no_face_values->get_present_fe_values() + .get_normal_vectors(); + + const unsigned int n_qpoints_agglo = q_points.size(); + + for (unsigned int q = 0; q < n_qpoints_agglo; ++q) + { + final_unit_q_points.push_back( + bbox.real_to_unit(q_points[q])); + final_weights.push_back(JxWs[q]); + final_normals.push_back(normals[q]); + } + } + } + + + NonMatching::ImmersedSurfaceQuadrature surface_quad( + final_unit_q_points, final_weights, final_normals); + + if (!handler.is_hp_collection) + { + agglo_isv_ptr = + std::make_unique>( + *(handler.box_mapping), + *(handler.fe), + surface_quad, + handler.agglomeration_face_flags); + } + else + { + agglo_isv_ptr = + std::make_unique>( + *(handler.box_mapping), + cell->get_fe(), + surface_quad, + handler.agglomeration_face_flags); + } + + agglo_isv_ptr->reinit(cell); + + return *agglo_isv_ptr; + } + + + + /** + * Given an agglomeration described by the master cell `master_cell`, + * this function: + * - enumerates the faces of the agglomeration + * - stores who is the neighbor, the local face indices from outside and + * inside*/ + static void + setup_master_neighbor_connectivity( + const typename Triangulation::active_cell_iterator + &master_cell, + const AgglomerationHandler &handler) + { + Assert( + handler.master_slave_relationships[master_cell + ->global_active_cell_index()] == + -1, + ExcMessage("The present cell with index " + + std::to_string(master_cell->global_active_cell_index()) + + "is not a master one.")); + + const auto &agglomeration = handler.get_agglomerate(master_cell); + const types::global_cell_index current_polytope_index = + handler.master2polygon.at(master_cell->active_cell_index()); + + CellId current_polytope_id = master_cell->id(); + + + std::set visited_polygonal_neighbors; + + std::map face_to_neigh_id; + + std::map is_face_at_boundary; + + // same as above, but with CellId + std::set visited_polygonal_neighbors_id; + unsigned int ghost_counter = 0; + + for (const auto &cell : agglomeration) + { + const types::global_cell_index cell_index = + cell->active_cell_index(); + + const CellId cell_id = cell->id(); + + for (const auto f : cell->face_indices()) + { + const auto &neighboring_cell = cell->neighbor(f); + + const bool valid_neighbor = + neighboring_cell.state() == IteratorState::valid; + + if (valid_neighbor) + { + if (neighboring_cell->is_locally_owned() && + !handler.are_cells_agglomerated(cell, neighboring_cell)) + { + // - cell is not on the boundary, + // - it's not agglomerated with the neighbor. If so, + // it's a neighbor of the present agglomeration + // std::cout << " (from rank) " + // << Utilities::MPI::this_mpi_process( + // handler.communicator) + // << std::endl; + + // std::cout + // << "neighbor locally owned? " << std::boolalpha + // << neighboring_cell->is_locally_owned() << + // std::endl; + // if (neighboring_cell->is_ghost()) + // handler.ghosted_indices.push_back( + // neighboring_cell->active_cell_index()); + + // a new face of the agglomeration has been + // discovered. + handler.polygon_boundary[master_cell].push_back( + cell->face(f)); + + // global index of neighboring deal.II cell + const types::global_cell_index neighboring_cell_index = + neighboring_cell->active_cell_index(); + + // master cell for the neighboring polytope + const auto &master_of_neighbor = + handler.master_slave_relationships_iterators.at( + neighboring_cell_index); + + const auto nof = cell->neighbor_of_neighbor(f); + + if (handler.is_slave_cell(neighboring_cell)) + { + // index of the neighboring polytope + const types::global_cell_index + neighbor_polytope_index = + handler.master2polygon.at( + master_of_neighbor->active_cell_index()); + + CellId neighbor_polytope_id = + master_of_neighbor->id(); + + if (visited_polygonal_neighbors.find( + neighbor_polytope_index) == + std::end(visited_polygonal_neighbors)) + { + // found a neighbor + + const unsigned int n_face = + handler.number_of_agglomerated_faces + [current_polytope_index]; + + handler.polytope_cache.cell_face_at_boundary[{ + current_polytope_index, n_face}] = { + false, master_of_neighbor}; + + is_face_at_boundary[n_face] = true; + + ++handler.number_of_agglomerated_faces + [current_polytope_index]; + + visited_polygonal_neighbors.insert( + neighbor_polytope_index); + } + + + if (handler.polytope_cache.visited_cell_and_faces + .find({cell_index, f}) == + std::end(handler.polytope_cache + .visited_cell_and_faces)) + { + handler.polytope_cache + .interface[{current_polytope_id, + neighbor_polytope_id}] + .emplace_back(cell, f); + + handler.polytope_cache.visited_cell_and_faces + .insert({cell_index, f}); + } + + + if (handler.polytope_cache.visited_cell_and_faces + .find({neighboring_cell_index, nof}) == + std::end(handler.polytope_cache + .visited_cell_and_faces)) + { + handler.polytope_cache + .interface[{neighbor_polytope_id, + current_polytope_id}] + .emplace_back(neighboring_cell, nof); + + handler.polytope_cache.visited_cell_and_faces + .insert({neighboring_cell_index, nof}); + } + } + else + { + // neighboring cell is a master + + // save the pair of neighboring cells + const types::global_cell_index + neighbor_polytope_index = + handler.master2polygon.at( + neighboring_cell_index); + + CellId neighbor_polytope_id = + neighboring_cell->id(); + + if (visited_polygonal_neighbors.find( + neighbor_polytope_index) == + std::end(visited_polygonal_neighbors)) + { + // found a neighbor + const unsigned int n_face = + handler.number_of_agglomerated_faces + [current_polytope_index]; + + + handler.polytope_cache.cell_face_at_boundary[{ + current_polytope_index, n_face}] = { + false, neighboring_cell}; + + is_face_at_boundary[n_face] = true; + + ++handler.number_of_agglomerated_faces + [current_polytope_index]; + + visited_polygonal_neighbors.insert( + neighbor_polytope_index); + } + + + + if (handler.polytope_cache.visited_cell_and_faces + .find({cell_index, f}) == + std::end(handler.polytope_cache + .visited_cell_and_faces)) + { + handler.polytope_cache + .interface[{current_polytope_id, + neighbor_polytope_id}] + .emplace_back(cell, f); + + handler.polytope_cache.visited_cell_and_faces + .insert({cell_index, f}); + } + + if (handler.polytope_cache.visited_cell_and_faces + .find({neighboring_cell_index, nof}) == + std::end(handler.polytope_cache + .visited_cell_and_faces)) + { + handler.polytope_cache + .interface[{neighbor_polytope_id, + current_polytope_id}] + .emplace_back(neighboring_cell, nof); + + handler.polytope_cache.visited_cell_and_faces + .insert({neighboring_cell_index, nof}); + } + } + } + else if (neighboring_cell->is_ghost()) + { + const auto nof = cell->neighbor_of_neighbor(f); + + // from neighboring rank,receive the association + // between standard cell ids and neighboring polytope. + // This tells to the current rank that the + // neighboring cell has the following CellId as master + // cell. + const auto &check_neigh_poly_ids = + handler.recv_cell_ids_neigh_cell.at( + neighboring_cell->subdomain_id()); + + const CellId neighboring_cell_id = + neighboring_cell->id(); + + const CellId &check_neigh_polytope_id = + check_neigh_poly_ids.at(neighboring_cell_id); + + // const auto master_index = + // master_indices[ghost_counter]; + + if (visited_polygonal_neighbors_id.find( + check_neigh_polytope_id) == + std::end(visited_polygonal_neighbors_id)) + { + handler.polytope_cache.cell_face_at_boundary[{ + current_polytope_index, + handler.number_of_agglomerated_faces + [current_polytope_index]}] = {false, + neighboring_cell}; + + + // record the cell id of the neighboring polytope + handler.polytope_cache.ghosted_master_id[{ + current_polytope_id, + handler.number_of_agglomerated_faces + [current_polytope_index]}] = + check_neigh_polytope_id; + + + const unsigned int n_face = + handler.number_of_agglomerated_faces + [current_polytope_index]; + + face_to_neigh_id[n_face] = check_neigh_polytope_id; + + is_face_at_boundary[n_face] = false; + + + // increment number of faces + ++handler.number_of_agglomerated_faces + [current_polytope_index]; + + visited_polygonal_neighbors_id.insert( + check_neigh_polytope_id); + + // ghosted polytope has been found, increment + // ghost counter + ++ghost_counter; + } + + + + if (handler.polytope_cache.visited_cell_and_faces_id + .find({cell_id, f}) == + std::end( + handler.polytope_cache.visited_cell_and_faces_id)) + { + handler.polytope_cache + .interface[{current_polytope_id, + check_neigh_polytope_id}] + .emplace_back(cell, f); + + // std::cout << "ADDED (" + // << cell->active_cell_index() << ") + // BETWEEN " + // << current_polytope_id << " e " + // << check_neigh_polytope_id << + // std::endl; + + handler.polytope_cache.visited_cell_and_faces_id + .insert({cell_id, f}); + } + + + if (handler.polytope_cache.visited_cell_and_faces_id + .find({neighboring_cell_id, nof}) == + std::end( + handler.polytope_cache.visited_cell_and_faces_id)) + { + handler.polytope_cache + .interface[{check_neigh_polytope_id, + current_polytope_id}] + .emplace_back(neighboring_cell, nof); + + handler.polytope_cache.visited_cell_and_faces_id + .insert({neighboring_cell_id, nof}); + } + } + } + else if (cell->face(f)->at_boundary()) + { + // Boundary face of a boundary cell. + // Note that the neighboring cell must be invalid. + + handler.polygon_boundary[master_cell].push_back( + cell->face(f)); + + if (visited_polygonal_neighbors.find( + std::numeric_limits::max()) == + std::end(visited_polygonal_neighbors)) + { + // boundary face. Notice that `neighboring_cell` is + // invalid here. + handler.polytope_cache.cell_face_at_boundary[{ + current_polytope_index, + handler.number_of_agglomerated_faces + [current_polytope_index]}] = {true, + neighboring_cell}; + + const unsigned int n_face = + handler.number_of_agglomerated_faces + [current_polytope_index]; + + is_face_at_boundary[n_face] = true; + + ++handler.number_of_agglomerated_faces + [current_polytope_index]; + + visited_polygonal_neighbors.insert( + std::numeric_limits::max()); + } + + + + if (handler.polytope_cache.visited_cell_and_faces.find( + {cell_index, f}) == + std::end(handler.polytope_cache.visited_cell_and_faces)) + { + handler.polytope_cache + .interface[{current_polytope_id, current_polytope_id}] + .emplace_back(cell, f); + + handler.polytope_cache.visited_cell_and_faces.insert( + {cell_index, f}); + } + } + } // loop over faces + } // loop over all cells of agglomerate + + + + if (ghost_counter > 0) + { + const auto parallel_triangulation = dynamic_cast< + const dealii::parallel::TriangulationBase *>( + &(*handler.tria)); + + const unsigned int n_faces_current_poly = + handler.number_of_agglomerated_faces[current_polytope_index]; + + // Communicate to neighboring ranks that current_polytope_id has + // a number of faces equal to n_faces_current_poly faces: + // current_polytope_id -> n_faces_current_poly + for (const unsigned int neigh_rank : + parallel_triangulation->ghost_owners()) + { + handler.local_n_faces[neigh_rank].emplace(current_polytope_id, + n_faces_current_poly); + + handler.local_bdary_info[neigh_rank].emplace( + current_polytope_id, is_face_at_boundary); + + handler.local_ghosted_master_id[neigh_rank].emplace( + current_polytope_id, face_to_neigh_id); + } + } + } + }; + + + + } // namespace internal +} // namespace dealii + + + +template class AgglomerationHandler<1>; +template void +AgglomerationHandler<1>::create_agglomeration_sparsity_pattern( + DynamicSparsityPattern &sparsity_pattern, + const AffineConstraints &constraints, + const bool keep_constrained_dofs, + const types::subdomain_id subdomain_id); + +template void +AgglomerationHandler<1>::create_agglomeration_sparsity_pattern( + TrilinosWrappers::SparsityPattern &sparsity_pattern, + const AffineConstraints &constraints, + const bool keep_constrained_dofs, + const types::subdomain_id subdomain_id); + +template class AgglomerationHandler<2>; +template void +AgglomerationHandler<2>::create_agglomeration_sparsity_pattern( + DynamicSparsityPattern &sparsity_pattern, + const AffineConstraints &constraints, + const bool keep_constrained_dofs, + const types::subdomain_id subdomain_id); + +template void +AgglomerationHandler<2>::create_agglomeration_sparsity_pattern( + TrilinosWrappers::SparsityPattern &sparsity_pattern, + const AffineConstraints &constraints, + const bool keep_constrained_dofs, + const types::subdomain_id subdomain_id); + +template class AgglomerationHandler<3>; +template void +AgglomerationHandler<3>::create_agglomeration_sparsity_pattern( + DynamicSparsityPattern &sparsity_pattern, + const AffineConstraints &constraints, + const bool keep_constrained_dofs, + const types::subdomain_id subdomain_id); + +template void +AgglomerationHandler<3>::create_agglomeration_sparsity_pattern( + TrilinosWrappers::SparsityPattern &sparsity_pattern, + const AffineConstraints &constraints, + const bool keep_constrained_dofs, + const types::subdomain_id subdomain_id); diff --git a/agglomeration_poisson/source/mapping_box.cc b/agglomeration_poisson/source/mapping_box.cc new file mode 100644 index 00000000..7e65a2eb --- /dev/null +++ b/agglomeration_poisson/source/mapping_box.cc @@ -0,0 +1,983 @@ +/* ----------------------------------------------------------------------------- + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright (C) 2025 by Marco Feder, Pasquale Claudio Africa, Xinping Gui, + * Andrea Cangiani + * + * This file is part of the deal.II code gallery. + * + * ----------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include + +#include + +DEAL_II_NAMESPACE_OPEN + +DeclExceptionMsg( + ExcCellNotAssociatedWithBox, + "You are using MappingBox, but the incoming element is not associated with a" + "Bounding Box Cartesian."); + + + +/** + * Return whether the incoming element has a BoundingBox associated to it. + * Simplicial and quad-hex meshes are supported. + */ +template +bool +has_box(const CellType &cell, + const std::map + &translator) +{ + Assert((cell->reference_cell().is_hyper_cube() || + cell->reference_cell().is_simplex()), + ExcNotImplemented()); + Assert((translator.find(cell->active_cell_index()) != translator.cend()), + ExcCellNotAssociatedWithBox()); + + return true; +} + + + +template +MappingBox::MappingBox( + const std::vector> &input_boxes, + const std::map + &global_to_polytope) +{ + Assert(input_boxes.size() > 0, + ExcMessage("Invalid number of bounding boxes.")); + + // copy boxes and map + boxes.resize(input_boxes.size()); + for (unsigned int i = 0; i < input_boxes.size(); ++i) + boxes[i] = input_boxes[i]; + polytope_translator = global_to_polytope; +} + + + +template +MappingBox::InternalData::InternalData(const Quadrature &q) + : cell_extents(numbers::signaling_nan>()) + , traslation(numbers::signaling_nan>()) + , inverse_cell_extents(numbers::signaling_nan>()) + , volume_element(numbers::signaling_nan()) + , quadrature_points(q.get_points()) +{} + + + +template +void +MappingBox::InternalData::reinit(const UpdateFlags update_flags, + const Quadrature &) +{ + // store the flags in the internal data object so we can access them + // in fill_fe_*_values(). use the transitive hull of the required + // flags + this->update_each = update_flags; +} + + + +template +std::size_t +MappingBox::InternalData::memory_consumption() const +{ + return (Mapping::InternalDataBase::memory_consumption() + + MemoryConsumption::memory_consumption(cell_extents) + + MemoryConsumption::memory_consumption(traslation) + + MemoryConsumption::memory_consumption(inverse_cell_extents) + + MemoryConsumption::memory_consumption(volume_element)); +} + + + +template +bool +MappingBox::preserves_vertex_locations() const +{ + return true; +} + + + +template +bool +MappingBox::is_compatible_with( + const ReferenceCell &reference_cell) const +{ + Assert(dim == reference_cell.get_dimension(), + ExcMessage("The dimension of your mapping (" + + Utilities::to_string(dim) + + ") and the reference cell cell_type (" + + Utilities::to_string(reference_cell.get_dimension()) + + " ) do not agree.")); + + return reference_cell.is_hyper_cube() || reference_cell.is_simplex(); +} + + + +template +UpdateFlags +MappingBox::requires_update_flags(const UpdateFlags in) const +{ + // this mapping is pretty simple in that it can basically compute + // every piece of information wanted by FEValues without requiring + // computing any other quantities. boundary forms are one exception + // since they can be computed from the normal vectors without much + // further ado + UpdateFlags out = in; + if (out & update_boundary_forms) + out |= update_normal_vectors; + + return out; +} + + + +template +std::unique_ptr::InternalDataBase> +MappingBox::get_data(const UpdateFlags update_flags, + const Quadrature &q) const +{ + std::unique_ptr::InternalDataBase> data_ptr = + std::make_unique(); + data_ptr->reinit(requires_update_flags(update_flags), q); + + return data_ptr; +} + + + +template +std::unique_ptr::InternalDataBase> +MappingBox::get_subface_data( + const UpdateFlags update_flags, + const Quadrature &quadrature) const +{ + (void)update_flags; + (void)quadrature; + DEAL_II_NOT_IMPLEMENTED(); + return {}; +} + + + +template +void +MappingBox::update_cell_extents( + const typename Triangulation::cell_iterator &cell, + const CellSimilarity::Similarity cell_similarity, + const InternalData &data) const +{ + // Compute start point and sizes along axes. The vertices to be looked at + // are 1, 2, 4 compared to the base vertex 0. + if (cell_similarity != CellSimilarity::translation) + { + const BoundingBox ¤t_box = + boxes[polytope_translator.at(cell->active_cell_index())]; + const std::pair, Point> &bdary_points = + current_box.get_boundary_points(); + + for (unsigned int d = 0; d < dim; ++d) + { + const double cell_extent_d = current_box.side_length(d); + data.cell_extents[d] = cell_extent_d; + + data.traslation[d] = + .5 * (bdary_points.first[d] + + bdary_points.second[d]); // midpoint of each interval + + Assert(cell_extent_d != 0., + ExcMessage("Cell does not appear to be Cartesian!")); + data.inverse_cell_extents[d] = 1. / cell_extent_d; + } + } +} + + + +namespace +{ + template + void + transform_quadrature_points( + const BoundingBox &box, + const ArrayView> &unit_quadrature_points, + std::vector> &quadrature_points) + { + for (unsigned int i = 0; i < quadrature_points.size(); ++i) + quadrature_points[i] = box.unit_to_real(unit_quadrature_points[i]); + } +} // namespace + + + +template +void +MappingBox::maybe_update_cell_quadrature_points( + const typename Triangulation::cell_iterator &cell, + const InternalData &data, + const ArrayView> &unit_quadrature_points, + std::vector> &quadrature_points) const +{ + if (data.update_each & update_quadrature_points) + transform_quadrature_points( + boxes[polytope_translator.at(cell->active_cell_index())], + unit_quadrature_points, + quadrature_points); +} + + + +template +void +MappingBox::maybe_update_normal_vectors( + const unsigned int face_no, + const InternalData &data, + std::vector> &normal_vectors) const +{ + // compute normal vectors. All normals on a face have the same value. + if (data.update_each & update_normal_vectors) + { + Assert(face_no < GeometryInfo::faces_per_cell, ExcInternalError()); + std::fill(normal_vectors.begin(), + normal_vectors.end(), + GeometryInfo::unit_normal_vector[face_no]); + } +} + + + +template +void +MappingBox::maybe_update_jacobian_derivatives( + const InternalData &data, + const CellSimilarity::Similarity cell_similarity, + internal::FEValuesImplementation::MappingRelatedData + &output_data) const +{ + if (cell_similarity != CellSimilarity::translation) + { + if (data.update_each & update_jacobian_grads) + for (unsigned int i = 0; i < output_data.jacobian_grads.size(); ++i) + output_data.jacobian_grads[i] = DerivativeForm<2, dim, spacedim>(); + + if (data.update_each & update_jacobian_pushed_forward_grads) + for (unsigned int i = 0; + i < output_data.jacobian_pushed_forward_grads.size(); + ++i) + output_data.jacobian_pushed_forward_grads[i] = Tensor<3, spacedim>(); + + if (data.update_each & update_jacobian_2nd_derivatives) + for (unsigned int i = 0; + i < output_data.jacobian_2nd_derivatives.size(); + ++i) + output_data.jacobian_2nd_derivatives[i] = + DerivativeForm<3, dim, spacedim>(); + + if (data.update_each & update_jacobian_pushed_forward_2nd_derivatives) + for (unsigned int i = 0; + i < output_data.jacobian_pushed_forward_2nd_derivatives.size(); + ++i) + output_data.jacobian_pushed_forward_2nd_derivatives[i] = + Tensor<4, spacedim>(); + + if (data.update_each & update_jacobian_3rd_derivatives) + for (unsigned int i = 0; + i < output_data.jacobian_3rd_derivatives.size(); + ++i) + output_data.jacobian_3rd_derivatives[i] = + DerivativeForm<4, dim, spacedim>(); + + if (data.update_each & update_jacobian_pushed_forward_3rd_derivatives) + for (unsigned int i = 0; + i < output_data.jacobian_pushed_forward_3rd_derivatives.size(); + ++i) + output_data.jacobian_pushed_forward_3rd_derivatives[i] = + Tensor<5, spacedim>(); + } +} + + + +template +void +MappingBox::maybe_update_volume_elements( + const InternalData &data) const +{ + if (data.update_each & update_volume_elements) + { + double volume = data.cell_extents[0]; + for (unsigned int d = 1; d < dim; ++d) + volume *= data.cell_extents[d]; + data.volume_element = volume; + } +} + + + +template +void +MappingBox::maybe_update_jacobians( + const InternalData &data, + const CellSimilarity::Similarity cell_similarity, + internal::FEValuesImplementation::MappingRelatedData + &output_data) const +{ + // "compute" Jacobian at the quadrature points, which are all the + // same + if (data.update_each & update_jacobians) + if (cell_similarity != CellSimilarity::translation) + for (unsigned int i = 0; i < output_data.jacobians.size(); ++i) + { + output_data.jacobians[i] = DerivativeForm<1, dim, spacedim>(); + for (unsigned int j = 0; j < dim; ++j) + output_data.jacobians[i][j][j] = data.cell_extents[j]; + } +} + + + +template +void +MappingBox::maybe_update_inverse_jacobians( + const InternalData &data, + const CellSimilarity::Similarity cell_similarity, + internal::FEValuesImplementation::MappingRelatedData + &output_data) const +{ + // "compute" inverse Jacobian at the quadrature points, which are + // all the same + if (data.update_each & update_inverse_jacobians) + if (cell_similarity != CellSimilarity::translation) + for (unsigned int i = 0; i < output_data.inverse_jacobians.size(); ++i) + { + output_data.inverse_jacobians[i] = Tensor<2, dim>(); + for (unsigned int j = 0; j < dim; ++j) + output_data.inverse_jacobians[i][j][j] = + data.inverse_cell_extents[j]; + } +} + + + +template +CellSimilarity::Similarity +MappingBox::fill_fe_values( + const typename Triangulation::cell_iterator &cell, + const CellSimilarity::Similarity cell_similarity, + const Quadrature &quadrature, + const typename Mapping::InternalDataBase &internal_data, + internal::FEValuesImplementation::MappingRelatedData + &output_data) const +{ + Assert(has_box(cell, polytope_translator), ExcCellNotAssociatedWithBox()); + + // convert data object to internal data for this class. fails with + // an exception if that is not possible + Assert(dynamic_cast(&internal_data) != nullptr, + ExcInternalError()); + const InternalData &data = static_cast(internal_data); + + + update_cell_extents(cell, cell_similarity, data); + + maybe_update_cell_quadrature_points(cell, + data, + quadrature.get_points(), + output_data.quadrature_points); + + // compute Jacobian determinant. all values are equal and are the + // product of the local lengths in each coordinate direction + if (data.update_each & (update_JxW_values | update_volume_elements)) + if (cell_similarity != CellSimilarity::translation) + { + double J = data.cell_extents[0]; + for (unsigned int d = 1; d < dim; ++d) + J *= data.cell_extents[d]; + data.volume_element = J; + if (data.update_each & update_JxW_values) + for (unsigned int i = 0; i < output_data.JxW_values.size(); ++i) + output_data.JxW_values[i] = quadrature.weight(i); + } + + + maybe_update_jacobians(data, cell_similarity, output_data); + maybe_update_jacobian_derivatives(data, cell_similarity, output_data); + maybe_update_inverse_jacobians(data, cell_similarity, output_data); + + return cell_similarity; +} + + + +template +void +MappingBox::fill_fe_subface_values( + const typename Triangulation::cell_iterator &cell, + const unsigned int face_no, + const unsigned int subface_no, + const Quadrature &quadrature, + const typename Mapping::InternalDataBase &internal_data, + internal::FEValuesImplementation::MappingRelatedData + &output_data) const +{ + (void)cell; + (void)face_no; + (void)subface_no; + (void)quadrature; + (void)internal_data; + (void)output_data; + DEAL_II_NOT_IMPLEMENTED(); +} + + + +template +void +MappingBox::fill_fe_immersed_surface_values( + const typename Triangulation::cell_iterator &cell, + const NonMatching::ImmersedSurfaceQuadrature &quadrature, + const typename Mapping::InternalDataBase &internal_data, + internal::FEValuesImplementation::MappingRelatedData + &output_data) const +{ + AssertDimension(dim, spacedim); + Assert(has_box(cell, polytope_translator), ExcCellNotAssociatedWithBox()); + + // Convert data object to internal data for this class. Fails with an + // exception if that is not possible. + Assert(dynamic_cast(&internal_data) != nullptr, + ExcInternalError()); + const InternalData &data = static_cast(internal_data); + + + update_cell_extents(cell, CellSimilarity::none, data); + + maybe_update_cell_quadrature_points(cell, + data, + quadrature.get_points(), + output_data.quadrature_points); + + if (data.update_each & update_normal_vectors) + for (unsigned int i = 0; i < output_data.normal_vectors.size(); ++i) + output_data.normal_vectors[i] = quadrature.normal_vector(i); + + if (data.update_each & update_JxW_values) + for (unsigned int i = 0; i < output_data.JxW_values.size(); ++i) + output_data.JxW_values[i] = quadrature.weight(i); + + maybe_update_volume_elements(data); + maybe_update_jacobians(data, CellSimilarity::none, output_data); + maybe_update_jacobian_derivatives(data, CellSimilarity::none, output_data); + maybe_update_inverse_jacobians(data, CellSimilarity::none, output_data); +} + + + +template +void +MappingBox::transform( + const ArrayView> &input, + const MappingKind mapping_kind, + const typename Mapping::InternalDataBase &mapping_data, + const ArrayView> &output) const +{ + AssertDimension(input.size(), output.size()); + Assert(dynamic_cast(&mapping_data) != nullptr, + ExcInternalError()); + const InternalData &data = static_cast(mapping_data); + + switch (mapping_kind) + { + case mapping_covariant: + { + Assert(data.update_each & update_covariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_covariant_transformation")); + + for (unsigned int i = 0; i < output.size(); ++i) + for (unsigned int d = 0; d < dim; ++d) + output[i][d] = input[i][d] * data.inverse_cell_extents[d]; + return; + } + + case mapping_contravariant: + { + Assert(data.update_each & update_contravariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_contravariant_transformation")); + + for (unsigned int i = 0; i < output.size(); ++i) + for (unsigned int d = 0; d < dim; ++d) + output[i][d] = input[i][d] * data.cell_extents[d]; + return; + } + case mapping_piola: + { + Assert(data.update_each & update_contravariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_contravariant_transformation")); + Assert(data.update_each & update_volume_elements, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_volume_elements")); + + for (unsigned int i = 0; i < output.size(); ++i) + for (unsigned int d = 0; d < dim; ++d) + output[i][d] = + input[i][d] * data.cell_extents[d] / data.volume_element; + return; + } + default: + DEAL_II_NOT_IMPLEMENTED(); + } +} + + + +template +void +MappingBox::transform( + const ArrayView> &input, + const MappingKind mapping_kind, + const typename Mapping::InternalDataBase &mapping_data, + const ArrayView> &output) const +{ + AssertDimension(input.size(), output.size()); + Assert(dynamic_cast(&mapping_data) != nullptr, + ExcInternalError()); + const InternalData &data = static_cast(mapping_data); + + switch (mapping_kind) + { + case mapping_covariant: + { + Assert(data.update_each & update_covariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_covariant_transformation")); + + for (unsigned int i = 0; i < output.size(); ++i) + for (unsigned int d1 = 0; d1 < dim; ++d1) + for (unsigned int d2 = 0; d2 < dim; ++d2) + output[i][d1][d2] = + input[i][d1][d2] * data.inverse_cell_extents[d2]; + return; + } + + case mapping_contravariant: + { + Assert(data.update_each & update_contravariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_contravariant_transformation")); + + for (unsigned int i = 0; i < output.size(); ++i) + for (unsigned int d1 = 0; d1 < dim; ++d1) + for (unsigned int d2 = 0; d2 < dim; ++d2) + output[i][d1][d2] = input[i][d1][d2] * data.cell_extents[d2]; + return; + } + + case mapping_covariant_gradient: + { + Assert(data.update_each & update_covariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_covariant_transformation")); + + for (unsigned int i = 0; i < output.size(); ++i) + for (unsigned int d1 = 0; d1 < dim; ++d1) + for (unsigned int d2 = 0; d2 < dim; ++d2) + output[i][d1][d2] = input[i][d1][d2] * + data.inverse_cell_extents[d2] * + data.inverse_cell_extents[d1]; + return; + } + + case mapping_contravariant_gradient: + { + Assert(data.update_each & update_contravariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_contravariant_transformation")); + + for (unsigned int i = 0; i < output.size(); ++i) + for (unsigned int d1 = 0; d1 < dim; ++d1) + for (unsigned int d2 = 0; d2 < dim; ++d2) + output[i][d1][d2] = input[i][d1][d2] * data.cell_extents[d2] * + data.inverse_cell_extents[d1]; + return; + } + + case mapping_piola: + { + Assert(data.update_each & update_contravariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_contravariant_transformation")); + Assert(data.update_each & update_volume_elements, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_volume_elements")); + + for (unsigned int i = 0; i < output.size(); ++i) + for (unsigned int d1 = 0; d1 < dim; ++d1) + for (unsigned int d2 = 0; d2 < dim; ++d2) + output[i][d1][d2] = input[i][d1][d2] * data.cell_extents[d2] / + data.volume_element; + return; + } + + case mapping_piola_gradient: + { + Assert(data.update_each & update_contravariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_contravariant_transformation")); + Assert(data.update_each & update_volume_elements, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_volume_elements")); + + for (unsigned int i = 0; i < output.size(); ++i) + for (unsigned int d1 = 0; d1 < dim; ++d1) + for (unsigned int d2 = 0; d2 < dim; ++d2) + output[i][d1][d2] = input[i][d1][d2] * data.cell_extents[d2] * + data.inverse_cell_extents[d1] / + data.volume_element; + return; + } + + default: + DEAL_II_NOT_IMPLEMENTED(); + } +} + + + +template +void +MappingBox::transform( + const ArrayView> &input, + const MappingKind mapping_kind, + const typename Mapping::InternalDataBase &mapping_data, + const ArrayView> &output) const +{ + AssertDimension(input.size(), output.size()); + Assert(dynamic_cast(&mapping_data) != nullptr, + ExcInternalError()); + const InternalData &data = static_cast(mapping_data); + + switch (mapping_kind) + { + case mapping_covariant: + { + Assert(data.update_each & update_covariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_covariant_transformation")); + + for (unsigned int i = 0; i < output.size(); ++i) + for (unsigned int d1 = 0; d1 < dim; ++d1) + for (unsigned int d2 = 0; d2 < dim; ++d2) + output[i][d1][d2] = + input[i][d1][d2] * data.inverse_cell_extents[d2]; + return; + } + + case mapping_contravariant: + { + Assert(data.update_each & update_contravariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_contravariant_transformation")); + + for (unsigned int i = 0; i < output.size(); ++i) + for (unsigned int d1 = 0; d1 < dim; ++d1) + for (unsigned int d2 = 0; d2 < dim; ++d2) + output[i][d1][d2] = input[i][d1][d2] * data.cell_extents[d2]; + return; + } + + case mapping_covariant_gradient: + { + Assert(data.update_each & update_covariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_covariant_transformation")); + + for (unsigned int i = 0; i < output.size(); ++i) + for (unsigned int d1 = 0; d1 < dim; ++d1) + for (unsigned int d2 = 0; d2 < dim; ++d2) + output[i][d1][d2] = input[i][d1][d2] * + data.inverse_cell_extents[d2] * + data.inverse_cell_extents[d1]; + return; + } + + case mapping_contravariant_gradient: + { + Assert(data.update_each & update_contravariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_contravariant_transformation")); + + for (unsigned int i = 0; i < output.size(); ++i) + for (unsigned int d1 = 0; d1 < dim; ++d1) + for (unsigned int d2 = 0; d2 < dim; ++d2) + output[i][d1][d2] = input[i][d1][d2] * data.cell_extents[d2] * + data.inverse_cell_extents[d1]; + return; + } + + case mapping_piola: + { + Assert(data.update_each & update_contravariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_contravariant_transformation")); + Assert(data.update_each & update_volume_elements, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_volume_elements")); + + for (unsigned int i = 0; i < output.size(); ++i) + for (unsigned int d1 = 0; d1 < dim; ++d1) + for (unsigned int d2 = 0; d2 < dim; ++d2) + output[i][d1][d2] = input[i][d1][d2] * data.cell_extents[d2] / + data.volume_element; + return; + } + + case mapping_piola_gradient: + { + Assert(data.update_each & update_contravariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_contravariant_transformation")); + Assert(data.update_each & update_volume_elements, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_volume_elements")); + + for (unsigned int i = 0; i < output.size(); ++i) + for (unsigned int d1 = 0; d1 < dim; ++d1) + for (unsigned int d2 = 0; d2 < dim; ++d2) + output[i][d1][d2] = input[i][d1][d2] * data.cell_extents[d2] * + data.inverse_cell_extents[d1] / + data.volume_element; + return; + } + + default: + DEAL_II_NOT_IMPLEMENTED(); + } +} + + + +template +void +MappingBox::transform( + const ArrayView> &input, + const MappingKind mapping_kind, + const typename Mapping::InternalDataBase &mapping_data, + const ArrayView> &output) const +{ + AssertDimension(input.size(), output.size()); + Assert(dynamic_cast(&mapping_data) != nullptr, + ExcInternalError()); + const InternalData &data = static_cast(mapping_data); + + switch (mapping_kind) + { + case mapping_covariant_gradient: + { + Assert(data.update_each & update_contravariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_covariant_transformation")); + + for (unsigned int q = 0; q < output.size(); ++q) + for (unsigned int i = 0; i < spacedim; ++i) + for (unsigned int j = 0; j < spacedim; ++j) + for (unsigned int k = 0; k < spacedim; ++k) + { + output[q][i][j][k] = input[q][i][j][k] * + data.inverse_cell_extents[j] * + data.inverse_cell_extents[k]; + } + return; + } + default: + DEAL_II_NOT_IMPLEMENTED(); + } +} + + + +template +void +MappingBox::transform( + const ArrayView> &input, + const MappingKind mapping_kind, + const typename Mapping::InternalDataBase &mapping_data, + const ArrayView> &output) const +{ + AssertDimension(input.size(), output.size()); + Assert(dynamic_cast(&mapping_data) != nullptr, + ExcInternalError()); + const InternalData &data = static_cast(mapping_data); + + switch (mapping_kind) + { + case mapping_contravariant_hessian: + { + Assert(data.update_each & update_covariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_covariant_transformation")); + Assert(data.update_each & update_contravariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_contravariant_transformation")); + + for (unsigned int q = 0; q < output.size(); ++q) + for (unsigned int i = 0; i < spacedim; ++i) + for (unsigned int j = 0; j < spacedim; ++j) + for (unsigned int k = 0; k < spacedim; ++k) + { + output[q][i][j][k] = input[q][i][j][k] * + data.cell_extents[i] * + data.inverse_cell_extents[j] * + data.inverse_cell_extents[k]; + } + return; + } + + case mapping_covariant_hessian: + { + Assert(data.update_each & update_covariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_covariant_transformation")); + + for (unsigned int q = 0; q < output.size(); ++q) + for (unsigned int i = 0; i < spacedim; ++i) + for (unsigned int j = 0; j < spacedim; ++j) + for (unsigned int k = 0; k < spacedim; ++k) + { + output[q][i][j][k] = input[q][i][j][k] * + (data.inverse_cell_extents[i] * + data.inverse_cell_extents[j]) * + data.inverse_cell_extents[k]; + } + + return; + } + + case mapping_piola_hessian: + { + Assert(data.update_each & update_covariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_covariant_transformation")); + Assert(data.update_each & update_contravariant_transformation, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_contravariant_transformation")); + Assert(data.update_each & update_volume_elements, + typename FEValuesBase::ExcAccessToUninitializedField( + "update_volume_elements")); + + for (unsigned int q = 0; q < output.size(); ++q) + for (unsigned int i = 0; i < spacedim; ++i) + for (unsigned int j = 0; j < spacedim; ++j) + for (unsigned int k = 0; k < spacedim; ++k) + { + output[q][i][j][k] = + input[q][i][j][k] * + (data.cell_extents[i] / data.volume_element * + data.inverse_cell_extents[j]) * + data.inverse_cell_extents[k]; + } + + return; + } + + default: + DEAL_II_NOT_IMPLEMENTED(); + } +} + + + +template +Point +MappingBox::transform_unit_to_real_cell( + const typename Triangulation::cell_iterator &cell, + const Point &p) const +{ + Assert(has_box(cell, polytope_translator), ExcCellNotAssociatedWithBox()); + Assert(dim == spacedim, ExcNotImplemented()); + + return boxes[polytope_translator.at(cell->active_cell_index())].unit_to_real( + p); +} + + + +template +Point +MappingBox::transform_real_to_unit_cell( + const typename Triangulation::cell_iterator &cell, + const Point &p) const +{ + Assert(has_box(cell, polytope_translator), ExcCellNotAssociatedWithBox()); + Assert(dim == spacedim, ExcNotImplemented()); + + return boxes[polytope_translator.at(cell->active_cell_index())].real_to_unit( + p); +} + + + +template +void +MappingBox::transform_points_real_to_unit_cell( + const typename Triangulation::cell_iterator &cell, + const ArrayView> &real_points, + const ArrayView> &unit_points) const +{ + Assert(has_box(cell, polytope_translator), ExcCellNotAssociatedWithBox()); + AssertDimension(real_points.size(), unit_points.size()); + + if (dim != spacedim) + DEAL_II_NOT_IMPLEMENTED(); + for (unsigned int i = 0; i < real_points.size(); ++i) + unit_points[i] = + boxes[polytope_translator.at(cell->active_cell_index())].real_to_unit( + real_points[i]); +} + + + +template +std::unique_ptr> +MappingBox::clone() const +{ + return std::make_unique>(*this); +} + + +//--------------------------------------------------------------------------- +// explicit instantiations +template class MappingBox<1>; +template class MappingBox<2>; +template class MappingBox<3>; + + +DEAL_II_NAMESPACE_CLOSE