From ca74583b5e65f6a230c83b11320c28e070dba68a Mon Sep 17 00:00:00 2001 From: Marco Feder Date: Mon, 24 Nov 2025 17:03:15 +0100 Subject: [PATCH 1/4] Agglomeration example --- agglomeration_poisson/CMakeLists.txt | 47 + .../include/agglomeration_accessor.h | 796 ++++++ .../include/agglomeration_handler.h | 1265 +++++++++ .../include/agglomeration_iterator.h | 305 ++ agglomeration_poisson/include/agglomerator.h | 473 ++++ agglomeration_poisson/include/mapping_box.h | 385 +++ agglomeration_poisson/include/poly_utils.h | 2468 +++++++++++++++++ agglomeration_poisson/meshes/t1.geo | 138 + agglomeration_poisson/meshes/t1.msh | 1738 ++++++++++++ agglomeration_poisson/meshes/t2.geo | 28 + agglomeration_poisson/meshes/t2.msh | 364 +++ agglomeration_poisson/meshes/t3.geo | 28 + agglomeration_poisson/meshes/t3.msh | 382 +++ agglomeration_poisson/poisson.cc | 781 ++++++ .../source/agglomeration_handler.cc | 1653 +++++++++++ agglomeration_poisson/source/mapping_box.cc | 988 +++++++ 16 files changed, 11839 insertions(+) create mode 100644 agglomeration_poisson/CMakeLists.txt create mode 100644 agglomeration_poisson/include/agglomeration_accessor.h create mode 100644 agglomeration_poisson/include/agglomeration_handler.h create mode 100644 agglomeration_poisson/include/agglomeration_iterator.h create mode 100644 agglomeration_poisson/include/agglomerator.h create mode 100644 agglomeration_poisson/include/mapping_box.h create mode 100644 agglomeration_poisson/include/poly_utils.h create mode 100644 agglomeration_poisson/meshes/t1.geo create mode 100644 agglomeration_poisson/meshes/t1.msh create mode 100644 agglomeration_poisson/meshes/t2.geo create mode 100644 agglomeration_poisson/meshes/t2.msh create mode 100644 agglomeration_poisson/meshes/t3.geo create mode 100644 agglomeration_poisson/meshes/t3.msh create mode 100644 agglomeration_poisson/poisson.cc create mode 100644 agglomeration_poisson/source/agglomeration_handler.cc create mode 100644 agglomeration_poisson/source/mapping_box.cc diff --git a/agglomeration_poisson/CMakeLists.txt b/agglomeration_poisson/CMakeLists.txt new file mode 100644 index 00000000..731e7c10 --- /dev/null +++ b/agglomeration_poisson/CMakeLists.txt @@ -0,0 +1,47 @@ +## +# CMake script for the step-8 tutorial program: +## + +# Set the name of the project and target: +SET(TARGET "poisson") + +# Declare all source files the target consists of. Here, this is only +# the one step-X.cc file, but as you expand your project you may wish +# to add other source files as well. If your project becomes much larger, +# you may want to either replace the following statement by something like +# FILE(GLOB_RECURSE TARGET_SRC "source/*.cc") +# FILE(GLOB_RECURSE TARGET_INC "include/*.h") +# SET(TARGET_SRC ${TARGET_SRC} ${TARGET_INC}) +# or switch altogether to the large project CMakeLists.txt file discussed +# in the "CMake in user projects" page accessible from the "User info" +# page of the documentation. +SET(TARGET_SRC + ${TARGET}.cc + source/agglomeration_handler.cc + source/mapping_box.cc + ) + +# Add include directory +INCLUDE_DIRECTORIES(include) + +# Usually, you will not need to modify anything beyond this point... + +CMAKE_MINIMUM_REQUIRED(VERSION 3.13.4) + +FIND_PACKAGE(deal.II 9.7.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/include/agglomeration_accessor.h b/agglomeration_poisson/include/agglomeration_accessor.h new file mode 100644 index 00000000..11489c2a --- /dev/null +++ b/agglomeration_poisson/include/agglomeration_accessor.h @@ -0,0 +1,796 @@ +// ----------------------------------------------------------------------------- +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later +// Copyright (C) XXXX - YYYY by the polyDEAL authors +// +// This file is part of the polyDEAL library. +// +// Detailed license information governing the source code +// can be found in LICENSE.md at the top level directory. +// +// ----------------------------------------------------------------------------- + + +#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..fb48c520 --- /dev/null +++ b/agglomeration_poisson/include/agglomeration_handler.h @@ -0,0 +1,1265 @@ +// ----------------------------------------------------------------------------- +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later +// Copyright (C) XXXX - YYYY by the polyDEAL authors +// +// This file is part of the polyDEAL library. +// +// Detailed license information governing the source code +// can be found in LICENSE.md at the top level directory. +// +// ----------------------------------------------------------------------------- +#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; + + + + //////////////////////////////////////////////////////// + + ObserverPointer> tria; + + ObserverPointer> 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..4fc12ab1 --- /dev/null +++ b/agglomeration_poisson/include/agglomeration_iterator.h @@ -0,0 +1,305 @@ +// ----------------------------------------------------------------------------- +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later +// Copyright (C) XXXX - YYYY by the polyDEAL authors +// +// This file is part of the polyDEAL library. +// +// Detailed license information governing the source code +// can be found in LICENSE.md at the top level directory. +// +// ----------------------------------------------------------------------------- + +#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 \ No newline at end of file diff --git a/agglomeration_poisson/include/agglomerator.h b/agglomeration_poisson/include/agglomerator.h new file mode 100644 index 00000000..0b49469c --- /dev/null +++ b/agglomeration_poisson/include/agglomerator.h @@ -0,0 +1,473 @@ +// ----------------------------------------------------------------------------- +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later +// Copyright (C) XXXX - YYYY by the polyDEAL authors +// +// This file is part of the polyDEAL library. +// +// Detailed license information governing the source code +// can be found in LICENSE.md at the top level directory. +// +// ----------------------------------------------------------------------------- + + +#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 \ No newline at end of file diff --git a/agglomeration_poisson/include/mapping_box.h b/agglomeration_poisson/include/mapping_box.h new file mode 100644 index 00000000..9dbfee85 --- /dev/null +++ b/agglomeration_poisson/include/mapping_box.h @@ -0,0 +1,385 @@ +// ------------------------------------------------------------------------ +// +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright (C) 2001 - 2024 by the deal.II authors +// +// This file is part of the deal.II library. +// +// Part of the source code is dual licensed under Apache-2.0 WITH +// LLVM-exception OR LGPL-2.1-or-later. Detailed license information +// governing the source code and code contributions can be found in +// LICENSE.md and CONTRIBUTING.md at the top level directory of deal.II. +// +// ------------------------------------------------------------------------ + +#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..9fef4e70 --- /dev/null +++ b/agglomeration_poisson/include/poly_utils.h @@ -0,0 +1,2468 @@ +// ----------------------------------------------------------------------------- +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later +// Copyright (C) XXXX - YYYY by the polyDEAL authors +// +// This file is part of the polyDEAL library. +// +// Detailed license information governing the source code +// can be found in LICENSE.md at the top level directory. +// +// ----------------------------------------------------------------------------- + + +#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_mpi_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_mpi_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_mpi_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_mpi_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_mpi_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_mpi_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_mpi_communicator(), + locally_relevant_dofs); + + system_matrix.reinit(locally_owned_dofs, + locally_owned_dofs, + dsp, + dof_handler.get_mpi_communicator()); + + system_rhs.reinit(locally_owned_dofs, dof_handler.get_mpi_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/poisson.cc b/agglomeration_poisson/poisson.cc new file mode 100644 index 00000000..81ed3811 --- /dev/null +++ b/agglomeration_poisson/poisson.cc @@ -0,0 +1,781 @@ +// ----------------------------------------------------------------------------- +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later +// Copyright (C) XXXX - YYYY by the polyDEAL authors +// +// This file is part of the polyDEAL library. +// +// Detailed license information governing the source code +// can be found in LICENSE.md at the top level directory. +// +// ----------------------------------------------------------------------------- + +#include +#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::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/source/agglomeration_handler.cc b/agglomeration_poisson/source/agglomeration_handler.cc new file mode 100644 index 00000000..642ed280 --- /dev/null +++ b/agglomeration_poisson/source/agglomeration_handler.cc @@ -0,0 +1,1653 @@ +// ----------------------------------------------------------------------------- +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later +// Copyright (C) XXXX - YYYY by the polyDEAL authors +// +// This file is part of the polyDEAL library. +// +// Detailed license information governing the source code +// can be found in LICENSE.md at the top level directory. +// +// ----------------------------------------------------------------------------- + + + +#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_mpi_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..d0b06887 --- /dev/null +++ b/agglomeration_poisson/source/mapping_box.cc @@ -0,0 +1,988 @@ +// ------------------------------------------------------------------------ +// +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright (C) 2001 - 2024 by the deal.II authors +// +// This file is part of the deal.II library. +// +// Part of the source code is dual licensed under Apache-2.0 WITH +// LLVM-exception OR LGPL-2.1-or-later. Detailed license information +// governing the source code and code contributions can be found in +// LICENSE.md and CONTRIBUTING.md at the top level directory of deal.II. +// +// ------------------------------------------------------------------------ + +#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 From 6b762d375b52ccb0e18c222e22df76e71570c1e3 Mon Sep 17 00:00:00 2001 From: Pasquale Claudio Africa Date: Fri, 28 Nov 2025 16:19:59 +0100 Subject: [PATCH 2/4] Prepare doc/ and README.md (WIP) --- agglomeration_poisson/README.md | 150 ++++++++++++++++++ agglomeration_poisson/doc/author | 1 + agglomeration_poisson/doc/builds-on | 1 + agglomeration_poisson/doc/entry-name | 1 + .../images/adaptive_vs_global_refinement.png | Bin 0 -> 151689 bytes .../images/refined_mesh_internal_layer.png | Bin 0 -> 127929 bytes .../images/warp_by_scalar_solution_layer.png | Bin 0 -> 185448 bytes agglomeration_poisson/doc/tooltip | 1 + 8 files changed, 154 insertions(+) create mode 100644 agglomeration_poisson/README.md create mode 100644 agglomeration_poisson/doc/author create mode 100644 agglomeration_poisson/doc/builds-on create mode 100644 agglomeration_poisson/doc/entry-name create mode 100644 agglomeration_poisson/doc/images/adaptive_vs_global_refinement.png create mode 100644 agglomeration_poisson/doc/images/refined_mesh_internal_layer.png create mode 100644 agglomeration_poisson/doc/images/warp_by_scalar_solution_layer.png create mode 100644 agglomeration_poisson/doc/tooltip diff --git a/agglomeration_poisson/README.md b/agglomeration_poisson/README.md new file mode 100644 index 00000000..f243a2ab --- /dev/null +++ b/agglomeration_poisson/README.md @@ -0,0 +1,150 @@ +# A posteriori error estimator for first order hyperbolic problems + + +## 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. + +### Parameter file: + +If you run `./DG_advection_reaction parameters.prm`, an error message will tell you that a parameter file has been created for you. You can open it and change some useful parameters like the number of refinement cycles, the advection coefficient, and others. If you don't specify anything, then the default values used for the test case (see paragraph below) will be used. + + + + +## 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/doc/author b/agglomeration_poisson/doc/author new file mode 100644 index 00000000..1fe90537 --- /dev/null +++ b/agglomeration_poisson/doc/author @@ -0,0 +1 @@ +Marco Feder 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..8293f594 --- /dev/null +++ b/agglomeration_poisson/doc/entry-name @@ -0,0 +1 @@ +Adaptive advection-reaction 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 0000000000000000000000000000000000000000..70d30393ce0aa19ea29e9422652b2f80a732c3bf GIT binary patch literal 151689 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 Zkw0sHdScRz4YdqF;OXk;vd$@?2>?ezRe%5h literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..bd38f0ec33ce3ede0c4da5f3eecf2550e254ef12 GIT binary patch literal 127929 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

A}|-z}F91 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ξ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 WJUw>YzLlP}*K;{F*}`Wp-u@rm6F5fz literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..f0c30f3e8b3b950323bf18852ce9ace4e887bfca GIT binary patch literal 185448 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 gpj3 zzFY3SV)^!>q2$G>Uw3E~`Yp{tc$~#aR!?i~r^ApymX6(iHdN)UVm*=~#B2stbVRTt z-urjiq@O!W$I6+9%2fm*VXR0*kt*lIZ5;lQ(V;h6@hGa;zBtJ+R^KbnzU>Xm4Lk`N z4b2#gRu)16Te(ct7!=9D85<;8MGaC-OhKwGg@>O>pml-3%#pL3Ph&fdq-;^rvpZL# zh-%XP0TOCL1`C^MTBc{% zp1NyoKsrU_32_6{R}X~P|2`Ood+UbEa>|;bkoS?oaP3j4{C16{&ykxSrF4EW{P0cU z-gZl3twaC)a36`^uW9vRgx6^S!T5zwW#y10;dGamAmhh*Y(1{*0*i-+6nf~B(WE$*L6mme(bPbKty_3m_cGmW)PMx8>YkW54l(W5$D5y?WQpt7oqw_GM_%W_}sUXFf zijJ%cCb%Fcb1~N5BoL*+`7|+3kC-rUgEV-BOX6!(`NP}(ymmKmF#7oa0y2PIV&-m> z=Ufj;{~$Zee%p4KgI7DYD`CHLb030x%FlyM~>&_Wy(&~i$Z02d=w!qugjAwb}bQ# zAFWd0n2h7@jvCCXcGhn1H$vR9G6aQH#pF#z@+Zp=B7E$8M3;L`91>>;n@+~jbFycL zVv9d)kUVombMuMMzL&|%Ke)2abqrMI<;#3mBeOkeYu_57`7T;Btf4}!%$1)M)5g~6 z@Yo}N!{h4JV5wG2au&qx!{A;-A6nxAjuGB0p&`U4z3uU*0DReUf@}Xb)Sx$R-0^Bs zU5Rg9)8x$eoW)umIPBXWjrkq19DQRsa*vy1+20HvU%jEQ8RM`$yx|#mkumi2A85iV zAh=>~{GlyyugHFSlNQ2S?y>iXJ3Rgj4hvO8d5L;3H(cwglzK#){52oe!vqF=_1-gA zLfJEz8G9drt@CasL1!RkBC-Yk@A%PcJjt9zY)_l?huM|}E(s#x>p%E&%Fe^8;Gj0! zl?Gi6=1-X`C^4-3B9Ht_a(Rq$cd8zSULrJmTPhNZ0TA-p38q|-t_Fkge?@@T!d~>M z*-bt@&w9n9Cj+XJXg#}65Uh|%>N*~T2;_+w@`TUX0t4*iJNm*10C}D(mB$Z^B{O!0 zWY&^Et;ii%wbv>5#NX6$|8wHe3%|5iiQWtUSsrQm`FOZ{QhrdXHwU&}mOu4=y0rO$ z0fDp0weMS+CwC)$@uEcUgyz-A3JKfpXN6J*(t@*GHE` zmo)__n!`w1zBgyOpX(|)eYw5YCzaFBaVD4~-Y|B=Lq9Wxw031q9)hSmIi&`=o8X$g z!HVsP0-2+XK@;Jdd1tT~rNdT#mLVC}Akb zDI?m%Zxs;0zwiA^g|quVt{=W1-X1w*ls{-C2OoYrH?co>9S(QU(XoG#Yb5Ko_Pwaf zdp#wQ_UQ!se^fR3U0{`R&C`vUcU*a6-I&B9ip5neDz$6Ey)6^2^S$zK6@@c=u=sx0 zt5PHEnG?46*E*Amejz#-qT)InCBTE9e^_*BNhPfHcx)3YE6=$$7t5-0S}f_gsZXxw zeP<iX=ibS3rZ*cyX4_2&pdstkrgFcUGS+kD%sUs#K8Os7=rACxdYr%uewRU zlu?x4dJeT_#w5_R6Rz0zxZGS%V^WWSYp$LM&6r`TLmFuWsm_~PiT?SlE<*m267;?l zx1(=Ee$zA#ej{#vt4j(;GoSSxwj+nCm-lL@vA#P)26qn_Eq7)oj@%B9;p~ST4vLPb zMm1YrH3u7(>mFUQ*q!$ApSwrfGPSc8eb>0$@0^@!tK<1g4xaYh6~@{6VM#uQ932v4 zFQ%>VP#fC|p9y5;(P!dMbT)0o#JO~|Rr*aA(XiKNt~-QHH#dnIza@;bpC-Ih00f-7%>>ulFWCo;uA7GQn3j=_!4Y~c5wmi695 z(1GwgSSR^1aZs!6n!Iyc0^#tgtB^^+JI3DBfx`NN&(yrA)TO8ktK_}0D{#s5wqXAB zTb-%$Ot#~q(VY@2`@0z}%se?cnAY;s=8RmF7ti_hwMXn$4O%}j!=uI7}Sy0#>tlb?gGLm0y<2o!&+)iKd?b-wWL)rcM?7!p$r1wyp zM+^*`bHdWWR$8QZ!6>ow$tfq|6JCh8&4ik&nFL^^!y7n3NXxBSeMPKi*vOlm{oS6= ziCx`AWEQlZsL{=C#`LiTJFH0qEkKvueA{Zfm<^qfONSX+G*OJI@b z@lK<4)DDx?yx@%N>eSTa&+C248N`8e@)25ISwxMqoRTr$Evq4Sb6*-o z+CAXc`^dh0uZMVWCF6_fefs`8@8@90AhyOzXIsBdM^G}^JH0l@xhrj69R3wSzLBcDDAvchyo3F*+iKv{p=FJQMdnb}@Ms6p$)YvRCzglV&7Mc^3)*qK)a^*EAA#icgtE zz7bRc>|si|nZg|d4vb($V5CrjhV78c2<#Zen|n~;djpiOk%ZqCm!?RnD~&iA#}YuP zoXZvNDo+IsE}DG7e+vELZ^3u)ycxzls0oV9GAFY&eA zBO>Kl#afrn=f(>%0?W}Os5SOUVO3DE=ay}B5yxr`4T9z5MwV_b8bqhM94((>6X^S4&$ zA&=F_3sqV)0%8KT(n~|qltAoOoirC;@1i}bZ>dUQIV%IJ)UGOd_L^g+N1nh>Mi&l6 zq0Hk1_9=~#>`&m-Cx&vd6uIH6(Xv1}1L!KJa3GKIW%84yySbStOAe?axoE4Q4EaO2 zNP;DwS+zDv?k3M5I)rytmd3Gjez>K$mVN4ymIJEYPUz-ETVIUwNenou{*y;1VOH-x zgN95yT4efnxerXlX{I~+=*ugSxElHPr`@mH*nBSCHgYWH>#QME5hkTD)lH+O9an7R zwSP(Ay2IG(xXG-L2SIhgE{U%Ql*rO)4AbkA*2T;x;Pdu;D}hCMw`pgpJe6JQyQk<1 z(3K$2>miIy<#_?*GXGsNq-SS<*UZ@>T!~h28Hh#+We;9w{aj?Sm8V<@UCeSB;B$5| zUV0m4EwFg+hp*e|sf=VtqX<%sp z^{hiRv)SW(UXcVs_#iVb{Js1;P{-FDrD5&EflcApOCNJxxTE?5v(9 za9R1*b&Zni>IITZ&=g3lNQa;fDp-_Gk$jRb?tHh9;ElKCiYf}pDj?m5R{73xhVR>L zC9!fzQRu%geU0|l|Ixl?n@Xz4a#$#oR{_Bo_#qMFGyyQLc*O5a2{2spa{mC*L!gF$ zsQ1N7$){8f1#fU{ltS>??Vy0!x#h~Sc+*}J7j(#Il@HM4Fu44H`V3P3q>F2=Ll21A z16Vq;8W7tKgjc3N-iuDL+Q!-C2d}_wQB}K`Hf;9bP5!*BEFqBYT0c96p+O@Kp+N>j zODG0}gc}L4DTn)6!PV3(Hca5ED=E;j2LMV~qK1YPK*(60K}{u6X?YYZXf{)d_hnwz z_#n;*jD0a3Vn!5^AjJXoxHfh4=E#$m13`v;!o=abnj9(mLSO$ryO%9zo!9^Wo0_zc z3gOdoliz${Pda)A*Kh8;)wZZDM;MqO-UcZm(y_~?kF~plArCpVV>tJRQ3knYg3?ML zz78L2fmlkssS3EKf2e^_T{F0*jUsKJKPFu$4RVJH%-QUgmg5TL0t!w)4*W7j6K`5KjKd;eBUvSe9ZTgl0dJD7i&^BCB0gi?Vl-Z;L4 zAt660%YQ#kQ(ZlbUXTktP6vubHde&eu5`QkK9YlB5Vi{n9N^d={&eL92JYrXjf0u! zON=@XhcHArJanmF4s7_GMWapN`$0F|px#IP%xOr(%~sY-+71Ds<-KVV(Vx(l&WPa| ziX9dL;0GIDXC(#=M?8FJE3$0P`Q6U$G6&i7e1|+UG0HG#>8{DE27rboKKz;8EP~$l zbDa2KJ@z-|hl|bp<6Hly9evU}m4gJ)w`q^;QJ19ErR+JuxP|@UNoS#hHFoSW^3l5~=s#tMV(+n*n{^Y4@kyZzrX^ zvRo|aAg20+2j*^&>`Fj{aK5SMhD(a6<-`i!_!J7^N~wT>s6~>-yG2_o`4K^=vhjvw zBj))g4`y^u_Qf}Eg|ob@+=H=kHB8QjKX#A+&F^YcTGr9q{K64BU4~SxEQD$#;4g)} zhqWAz+75#+F&?lhc6oJraN-QUak7)M7J;EyW^ta`weMI0OO$zNY55u#MZ&ajt`983 z|CD0k*I7C^3o`uG0{tV6k`@zCPA%jED zBH+6QFman!rAto{A+4|8mzEL4NJE58f#jkcJEvS;V*#}cvqO%Jx-2V{T_EDyGv>>t zVxg^Kq51=wWgx9L9E$fJP-y{0P@s1mL>de#3sQ$Z$L7`;^0aISVi3p$aciZ}(3|hY znU?A^YmR}7*9V^+uG>8wn3h;keq}?4<2~1{vY^og%tTafe+@||<5SjUd<*kDDX0+oh9~~EB zB0*8IG;C8@3WCV|Wr-CPi7nLA6+dl#qhzYUFb58r&6zWm!!qsZbqZ&y)mMDkDkw>w zF?4XKUis=+V&blpr)8+6t&NeU%Y$f*5zWh;%a=Rk!#Yfss~;-Sk^`-`Um2_C*`;%U zu}**s*Tm^>Nz_8152`S2UP`GSkN<|s_c9= zuxU4R`(}XVn(vpy>ZI5#*9GtIQ)$3JQtAio8?>x0Ro7S^Js=YUSt6+I0exP~)to(c zS%RrCLu?V2=UEi%1YZ;kzGuU*V#tc9i<)gX7?0;zuC>X)sj^`!b5ahZVK-x3lTXjQ zv~1oM)myb>w1C0Wr({E8Y={Lx$DY1J?ZvY^EMf-qo4LqJ@L(`hQITKe?6gmD#-Cys zkTjVJb!`3+LNUZ46lb#CN^~H5rEGrIJn^v<)1c46GXmM30UX;api8nLl>=WEyl6Nf zV*xe1Z+azrA`lI#d+ZeUOg++t z`1*;`yddNYM-8uaHweCfC8e1)qvcwc3r-alB~dA!>LHMP=(;r9p}0XrJQ+I&u^V4< zo@I!=!eoyqN6X$M$41gQl9Xu0Z3M2g{Hb-Cr^z%$bRu@&{ujepG)EqWr&SLgQh<)% zl#j!N`A-Ek&N~_9`ux`&5>ZPR%cpa1gmXO9xJ)?(z>OO$vCGDsZZC}X!#d<}@ z`kO1tyfha!VZdm_ z!rLmrm7giJB~>afY4>|sh1w(37^mV-3(KoRv|m~Yl7}wu-u$z1N-r0qEary<}|;6J_v2iIv1HXCdtZ_o$fFyJ+OMPWrs~(h7Cw( z1(wVV#$Tn-OK?QD7Dma&du47i|MLPYbjC9Z5U7XZXJp@2M%|j}E1uS)Go%QRoXs-q zZC=RJ$aCqOPU9!8`CzjQpr9nccBj=cf zg-T0>c!ZDZpA;Z_DBC?{7d8aMNVz|W;HR#!+z?CI_nWX5P554;q(qa$_=M_7MBmf& znOn3T@)wb<&-9j_3~U~v#que5v^J$Woxa2_+|%5r{bJw+7RT2YDkG`H4s!-qdA9N= zd4d#|=e3*e6LVnDWR(;L^PMc=5?5qN)cJZoF(65UzrF-3n)VMv_yqWw;)DSO(uq{w z*kQV2pU@Q>er)bri<3`Pzqe*WJlr{91*J>}G7z&y^zH^ZjU80!N$heWyMASF0x2g( z%p>UA;UyY%pAoXxA0i*%;otqk9W0EYcGybBAYFe)PJ|RbeOX1H4*-r5@5Hp+)|DyD zixzJ#2NLn<`QC}jB713P5|W={&ucGt9FZAr^hrXJMWh^*%t>W%PS7-&FZdwhe4JLG zLzr`~z!ahMhF&A?<_zCkHt2Z)n>|{nf9k;S=hMK*v-9b<7y~oQQ5Sg(Te5lqqg(!) z)~40FPdjY|lBFIg^Hf?_PsH2NCfDH1+-=v|R zY85p5A66ebC~g1v!qL!$t(pIIBLs8`^1NWJXu945ETQ9fQJ%F80+}#fg!f) z^ey_>?7kWQAI_bmpIqkZynHT23|NPhY93HW`IB`^_2kpb!;AJ-tf{tcFf5(ji)ANT zK`qUjD3jCXg{=&L#Yl8$Aah*!5<#*X2Mn{&*FXp^kOYBlCP~8oUA_pc*b+sFdP?j? zwalLg$;s&tRTNxtgc;G{9DB|WVtj}zyWs#=g_dRjsWk3j`uU7n%aF+NV-jQH%MLW$ zhFzm&KEij`PeMR~T_1LIHY})z+S)R5mBR@zB!wOX;dh2Xv85AiNX(DelSsQX9l$)+ zkAgFs z1hgJ;aKf80J};ZP4}SFTy`7U|wVe>p@fx>}bNgU*4vO0h_*KLOViRpL$CLNM=*S)d z3<#;Uxj*(Pz;4vVv*-E8?1r(O(>jjn8&F-AE_QK~V`iAxSh|jQTC54kA-SDSUTiIz z8+O*vR+c@gy}1{M5MOPXfmtv4qvI{A$%>;qgUw?aKCaZSreG-w<-ivEmI3Lf15>JO ziG1FUl33gLIeN)c%o^lm99q``Cfl2(FFzB(-nn7#EA!crp1qgFjC6-ZqGxr8ilXxCVqu_!G*RgJ;w;R5&*Lrq_IPB|rnAnmxpR>BLMCrD6ZrLd8$04OBl>D;g9z66hjWi~__>E-1Kw$BQ?&=a zgsH-qrc11A$ugi0phkF=C)m|c_G3l0WnOeWt+3^+h>I~o9vEgC2WBTrhJO1QcA4xk zr~Dvs>@R|p`->oN;(p_h=7tXzn*N}uxsiG+V>ueoGbwb%ZinOYv9~8_1rviYH~64v zZj}Hi^~W6GjhPG$4Ds!3F|91?Q1K;I3k%Q!rCQoSr!<>#k`wl$f}c_uo(jU_y`y9) z#o_YG-_WhZN2?%^yS6-iT0*pz4DXPWWm!rVX;7YKNbISadvasVJ0Zn&;t zxD7`<{ETA3cZUo9lXr=-JnubAiNVzk2U&lcYuBj05%-&G^|&h3@$l2UK7P3;sQ zp3{$Iub#(PcKuDM#PzQ8$5%5<_03}4w8)J&FB4m_Muv=6znkMz7s7aT_TEds7`_YU z`FtDonu|8lZanpdb2gPYM-17bAyBL5I6PR(eJD9$=jo*oH^l_G`1CZ6RYXVcosADS zKkX`ih3?6+H@ba zdV;#_;N7s~x8CCWLt)QM;V1T}#p`I%@^I1N06;9nW3@jRE%%}=SDW4TyA?bHP)7gQ zr-nD7bH_nA>ho}W{|xnu&%zH^(`oq>T9wlu)x&AHhq^>;&I}oa4FLdm_Y!D&H-JjZ z3SILK@>G6XL~VGwsaz|YZmz?`#|0H_E$PUD z6G=PPQD%Vm#7t~*mW=W7#YkWQ%MB5c%$U!FcgW5?d3lL+JML@20bqaVo_H8gnx325 z*Ii?Z2>bpC0@|#s{O}H2__O*-Y=SoELoehcfwA|@vK&}^;196|SoNxY9g!~asNBJ8 zkL7UJ!S|bfm!;oCe$COc-_}`&!{O_4Ti>Ji=vi*;=dEnd!H?S9eD+UvP55oL)&1id zW2i+k83-f1I$mv9AqVJvc=#v-#BHdvRRRL~x~VW|_nH}NOS?ds6Lqt<@T<0kB40f* z3V@Yomce}Jim6*O@2CLZn4kyFO6P#o?Ewh~&k0ehnK^JZSBPjuCZA@|MDTt@6Du?% z0eFszKMf2moVX3xEhDqHX7!jQNpta7dCKu;kr#Z91FQ$x7Qk8KY9K)y5Efg@24E~r zZO;+H1%Z%hLrz0*b?Vo<3x`!kXWP(O>!w+ln58{1mKmc9$fy1Zx@VsKi4%#}&_91? zwzKY?*_Jz%@co8Lw=S=)7KOuoMzK-%4T|^R!SmV$QA;=1n!O#rUCpgT&wJv39ZJDg z2{s^OD7GG5T1fE`+UcDuu&K3u&eqcIWN@}*GymE}&j?^PAe9CuF{Tu)k%^Q5-yKCy z%HDt}yF3XSImy~1)sGkmiINx!uN~M_2`KO1X`=%UrgwHQ(7$!8TxMZ5Y`;oD)Z>_n zd0y!9{@7RH8QJUiML>qL5;_8rST+pe6qo(u<2J0TPB6Yk;pbu5DZ;I3w*mr9;2&&i znHjgTp?p)L5tM5AjArKf&n`?mNpU&6&8y>8My%po8x3=^UY5@RBR2Eef4X|lcD?@y z$xPEkkHxjsu}Yh*r{lC`UYAC!G!{@!Om{##EuYG>L1DKmbk<**~Yl316o zbvf_1PrSK7psF9G6hI(*8>1Mrz74;UajLqFaC~?ClE>+x-$|zBRmHvR<}9qpQP}&F zkj!tbk!O>kK-OvQJbp(q?-`oQ`go&Y^p&O$KI556sHf*@;`Ew4pa#jyN`;OH4Z2k6 zM{gd2o%lDGH)muiK1b3bR<1Rf)#e4~1H=dSdIL?(@*y{?Y!3@PBPR36YTW11do{P=?Emj4re(Hwdu{&8`l zqu_r)W9tjX^qJpiz0bJsQbPUu#~gtZ;pr3kro9Y0yvGogKT-ZdB9pOpy z>CcL|v`hAz7wl+_4y$)wgGQ2$F67#i3Vpi+zWx#D~{uqSNpia>1<>o^r1Lp2RglzS8%mTTaWNl4qZcp`s1c6Ok z2AIus2q;<6rk>7q{J63b>=cPWSak)C$8JuZ^ zyzL|Xx#n%lWpS`wY2&#(KX$GQTA}~;QJ2Ybc>^%YXst#5C-dmYd9C=vFO+PmPLUG= z?AJ^0B~BkU<0mD>6~6dy#f(aA4QSNwqKi9XUjL<*HcM9fQUyfYOBc`lO1phk{vx}A z+m59lQO7mvwmq@&_-*=UgfMHwiZv{M=rmG$k-Fpp7h9jTC|)m#=XoZsT9Qq(IGc0O zLy!jS`2<&wr!|yJm{KM?s|@8i)3m+`0a4|l^=Az+&9B+|#FAhVLbTDfu`%0OYY%nvgBFzGpJj$I_q~5ihaWyWZlTXCV zSI;KP&}&}A8Z#nxFqD#!NQA3Y{)g5Uu3@KuB_!D!mow_Z{b#aU^( z`J=16Pl_-Q;m-&#FfiOZ!GKV0Qqv+&s`v6BxJ=SxwcKpyZQr4O5xDI^>L~iLbn00A ze>+jZu`y;y`26C;PSUNPx@$T?#{HV3$=#si5d_c$T_i67#=C&RLQPC5etT5frCy>a zb-=LSH-T`Bsje9uZt)JQgyfGy)(V{s9n!#7pHG}IeO`wKC`i?tpzj(kfa5gYsOtjk ziF5#&DBidlp?A+bC1Tj)$KFM<6bg3s!XARl*_n0!H#Y_$%UvjRC4j2q%;YYJ9%7{j z2`RQBvIW6*7xMI2-WIQ^u#mk^&zfAjek+mW8|KESh@^`Ruby0zl>vX#fuOP1wr2ri z{GM1}9ewcb`j@Oz0?ynJNcw5?KXUM5TI%${r1bvI^nUaA>D@`Qqr*Pwnyuk0jSKEh z#76v4mf0~HBwT*#Ah*&Ggo|Z~ftbktC=4DdH0P_5_KrH^c%4sli|ia07ND1b$0>4dQ? z!UdabNc}Pdlrp|+eW-~nIpzsK6)P^Z&KURS8^5I`r%05bZ+R<9bMNXw^_;D2R=Pvp zns#(ZBNG);R2=7;=o>Pn6v$%(%4REe?&cLa6fdCF96@9YEzY-V7X`ytETElUo~OyC z6ZXW4B5JtZFvL)@q=n2aN5j_BX;F@!325zYM$Qoja9?=BqK zBA6nrHtf{vZrdyL=H3#zPo@nHs<3wQAxCyC13VrrR6CA&W@M%0pIZqk2Zyy-rmDWH zs<8q{dc8uqTQV4zZuQ61UlHRYm4xKoDe;}bOpX*qN<-cIJA2+|9^2B82Z}fj`Kl|t zf11bz&7_;Z*x?m$D=^subC5N_1Vjy$nbdAY<9280X9fGbKUvk3(7&-Io-M5l6~ba& zPyM|K`zRO=Ha#0xI;Rhch#CzSuRV>EZ*_R+E{C~M$DdGZ@Imx_ zg1qs)oY!SEfVl{xIa#Q)N3-;#+F@a~|rm+JlV+%`Or*wxrJZfe5GWv#i(Tg28$8UMLMt2o9pgU~cjb@$ze& zM8*v|%L@1W6E|?r1v(KTBM`33as#+|#*P}ZS1z+v&%z8ng z-P+#%X%IojO{TNu7dwU5KJtmbla$J8kx8#%w_B8?ho9?o!I9 z_GXSE2Zymbona4{E4o0Emlr~n-lz}Dxgb~Z52@oib^6$fB=KBop7Y-zL-6ZM^p~(8 zqqIp!ZcGo^;W6&QTum)GlvMizEz(3sQT@~f#F7?lKH}j>&sMlvmY( zNM0+7OT?j`o)xR?_S;5V3ViWGJw>F$xfck{yRm1?+jkF-Qg-30m+#Y6SQ|*wZ?-PJ ze7`9Lc|Lce--#b?Kn4yU&lI-i&$wWo8s;m z;%wM?0aP(%!*v*s3SOKcfuI~%h}h*(On%msZBtd}2eQWg1}XXFB9FuwvpC%Mvjx4O zLlso8xmw4DubsgM;vZ{47U#fk?GQSMSB$I$XzmL3^aQj0{ebD z+^g4y;*y+(oSj3Q1V6fR**Hn9P2l@R7_lOmjK_QdiIH5m5*%~FbFH3ga5e=oM?-G%lZP$|9i@Q;Vl(@%t&zB$v68) zCW4xWw`0p*Rai&to+7ywwAx;pZmMr#_bChKwo1!Ez3;LaEf&~r?#k|cyrSJxaL1nY ziN}m9e`J=p;Sp->NpDi)S6=SJ{^=hQ7@J^4t=^%OS~x=8M{=Mhy?E-pGk@;Ks_+YD z&m@NrsFl0U(?Ej{c0(dOceTTPIc3;!3fSly$gsWpZt_XV4{a!xgor}n6b5$cK9Ah4 z>E#sa?qC(S)rX4WRTOGxm3;`o3k}h_=Pk&B#%2NdUjZDfd;R|JLqw@rY@1WUev$@k zJH-x5*ppgtV3*Fc4O}}^#>+65|Jh-P$u1H6Z;L36QVMQxEF)b!-QC^E>7OSqRRjzc z_wAS*%jY;0grIYq=iu%FCb?S!^G3MDuQwSJ?ivEg6xgzTL<+5Yyi5Cmpu^~7eX&Y^pt z`-ulBv9?a%t~IyY4l!vdwCIIsbR$;Vlh$C)SQfba2xzHMj7(89fFLW?y7+9E zzuLU>Q){OLJBD;7(pM9rnLic0_mILyD~n9$f6TH0SGwuF;mI1PtE(R!L%Gj&2Ohks zkTa5bC;2xMtD>a&cftOfIknKDQ-uqCMws@^>;6NwI8d3?XqN3s#c)9o&jYyRL@Hae9nWXCBcwTzO8rq~+pI^9V?{ zDaR<1DT_eUG;T%0AAf&G+b% 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 z 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 literal 0 HcmV?d00001 diff --git a/agglomeration_poisson/doc/tooltip b/agglomeration_poisson/doc/tooltip new file mode 100644 index 00000000..bed031fa --- /dev/null +++ b/agglomeration_poisson/doc/tooltip @@ -0,0 +1 @@ +Implementation of an a-posteriori error estimator for first order hyperbolic problems From 0841d4377ee88eb517e6099b217adad84aa48836 Mon Sep 17 00:00:00 2001 From: Pasquale Claudio Africa Date: Sat, 29 Nov 2025 16:45:32 +0100 Subject: [PATCH 3/4] Make example compatible with v9.6.0 + update copyright notice --- agglomeration_poisson/CMakeLists.txt | 22 +- .../agglomeration_poisson.cc | 950 ++++++++++++++++++ .../include/agglomeration_accessor.h | 22 +- .../include/agglomeration_handler.h | 28 +- .../include/agglomeration_iterator.h | 23 +- agglomeration_poisson/include/agglomerator.h | 24 +- agglomeration_poisson/include/mapping_box.h | 23 +- agglomeration_poisson/include/poly_utils.h | 60 +- agglomeration_poisson/poisson.cc | 781 -------------- .../source/agglomeration_handler.cc | 58 +- agglomeration_poisson/source/mapping_box.cc | 25 +- 11 files changed, 1075 insertions(+), 941 deletions(-) create mode 100644 agglomeration_poisson/agglomeration_poisson.cc delete mode 100644 agglomeration_poisson/poisson.cc diff --git a/agglomeration_poisson/CMakeLists.txt b/agglomeration_poisson/CMakeLists.txt index 731e7c10..6dfe9edc 100644 --- a/agglomeration_poisson/CMakeLists.txt +++ b/agglomeration_poisson/CMakeLists.txt @@ -1,34 +1,20 @@ -## -# CMake script for the step-8 tutorial program: -## - # Set the name of the project and target: -SET(TARGET "poisson") +SET(TARGET "agglomeration_poisson") -# Declare all source files the target consists of. Here, this is only -# the one step-X.cc file, but as you expand your project you may wish -# to add other source files as well. If your project becomes much larger, -# you may want to either replace the following statement by something like -# FILE(GLOB_RECURSE TARGET_SRC "source/*.cc") -# FILE(GLOB_RECURSE TARGET_INC "include/*.h") -# SET(TARGET_SRC ${TARGET_SRC} ${TARGET_INC}) -# or switch altogether to the large project CMakeLists.txt file discussed -# in the "CMake in user projects" page accessible from the "User info" -# page of the documentation. +# 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) -# Usually, you will not need to modify anything beyond this point... CMAKE_MINIMUM_REQUIRED(VERSION 3.13.4) -FIND_PACKAGE(deal.II 9.7.0 +FIND_PACKAGE(deal.II 9.6.0 HINTS ${deal.II_DIR} ${DEAL_II_DIR} ../ ../../ $ENV{DEAL_II_DIR} ) IF(NOT ${deal.II_FOUND}) 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/include/agglomeration_accessor.h b/agglomeration_poisson/include/agglomeration_accessor.h index 11489c2a..8eb803c8 100644 --- a/agglomeration_poisson/include/agglomeration_accessor.h +++ b/agglomeration_poisson/include/agglomeration_accessor.h @@ -1,15 +1,13 @@ -// ----------------------------------------------------------------------------- -// -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later -// Copyright (C) XXXX - YYYY by the polyDEAL authors -// -// This file is part of the polyDEAL library. -// -// Detailed license information governing the source code -// can be found in LICENSE.md at the top level directory. -// -// ----------------------------------------------------------------------------- - +/* ----------------------------------------------------------------------------- + * + * 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 diff --git a/agglomeration_poisson/include/agglomeration_handler.h b/agglomeration_poisson/include/agglomeration_handler.h index fb48c520..d580ac1e 100644 --- a/agglomeration_poisson/include/agglomeration_handler.h +++ b/agglomeration_poisson/include/agglomeration_handler.h @@ -1,19 +1,18 @@ -// ----------------------------------------------------------------------------- -// -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later -// Copyright (C) XXXX - YYYY by the polyDEAL authors -// -// This file is part of the polyDEAL library. -// -// Detailed license information governing the source code -// can be found in LICENSE.md at the top level directory. -// -// ----------------------------------------------------------------------------- +/* ----------------------------------------------------------------------------- + * + * 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 @@ -54,6 +53,7 @@ #include #include +#include using namespace dealii; @@ -797,9 +797,9 @@ class AgglomerationHandler : public Subscriptor //////////////////////////////////////////////////////// - ObserverPointer> tria; + const Triangulation *tria; - ObserverPointer> mapping; + const Mapping *mapping; std::unique_ptr> cached_tria; diff --git a/agglomeration_poisson/include/agglomeration_iterator.h b/agglomeration_poisson/include/agglomeration_iterator.h index 4fc12ab1..878e74f8 100644 --- a/agglomeration_poisson/include/agglomeration_iterator.h +++ b/agglomeration_poisson/include/agglomeration_iterator.h @@ -1,14 +1,13 @@ -// ----------------------------------------------------------------------------- -// -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later -// Copyright (C) XXXX - YYYY by the polyDEAL authors -// -// This file is part of the polyDEAL library. -// -// Detailed license information governing the source code -// can be found in LICENSE.md at the top level directory. -// -// ----------------------------------------------------------------------------- +/* ----------------------------------------------------------------------------- + * + * 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 @@ -302,4 +301,4 @@ AgglomerationIterator::master_cell() const -#endif \ No newline at end of file +#endif diff --git a/agglomeration_poisson/include/agglomerator.h b/agglomeration_poisson/include/agglomerator.h index 0b49469c..79a4d5f4 100644 --- a/agglomeration_poisson/include/agglomerator.h +++ b/agglomeration_poisson/include/agglomerator.h @@ -1,15 +1,13 @@ -// ----------------------------------------------------------------------------- -// -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later -// Copyright (C) XXXX - YYYY by the polyDEAL authors -// -// This file is part of the polyDEAL library. -// -// Detailed license information governing the source code -// can be found in LICENSE.md at the top level directory. -// -// ----------------------------------------------------------------------------- - +/* ----------------------------------------------------------------------------- + * + * 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 @@ -470,4 +468,4 @@ namespace dealii return parent_node_to_children_nodes; } } // namespace dealii -#endif \ No newline at end of file +#endif diff --git a/agglomeration_poisson/include/mapping_box.h b/agglomeration_poisson/include/mapping_box.h index 9dbfee85..6183e5c1 100644 --- a/agglomeration_poisson/include/mapping_box.h +++ b/agglomeration_poisson/include/mapping_box.h @@ -1,16 +1,13 @@ -// ------------------------------------------------------------------------ -// -// SPDX-License-Identifier: LGPL-2.1-or-later -// Copyright (C) 2001 - 2024 by the deal.II authors -// -// This file is part of the deal.II library. -// -// Part of the source code is dual licensed under Apache-2.0 WITH -// LLVM-exception OR LGPL-2.1-or-later. Detailed license information -// governing the source code and code contributions can be found in -// LICENSE.md and CONTRIBUTING.md at the top level directory of deal.II. -// -// ------------------------------------------------------------------------ +/* ----------------------------------------------------------------------------- + * + * 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 diff --git a/agglomeration_poisson/include/poly_utils.h b/agglomeration_poisson/include/poly_utils.h index 9fef4e70..8ba4b910 100644 --- a/agglomeration_poisson/include/poly_utils.h +++ b/agglomeration_poisson/include/poly_utils.h @@ -1,20 +1,17 @@ -// ----------------------------------------------------------------------------- -// -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later -// Copyright (C) XXXX - YYYY by the polyDEAL authors -// -// This file is part of the polyDEAL library. -// -// Detailed license information governing the source code -// can be found in LICENSE.md at the top level directory. -// -// ----------------------------------------------------------------------------- - +/* ----------------------------------------------------------------------------- + * + * 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 @@ -26,6 +23,8 @@ #include #include +#include + #include #include @@ -48,8 +47,6 @@ #include #include -#include - #ifdef DEAL_II_WITH_TRILINOS # include #endif @@ -841,7 +838,7 @@ namespace dealii::PolyUtils const double overlap_factor = Utilities::MPI::sum(covering_bboxes, - ah.get_dof_handler().get_mpi_communicator()) / + ah.get_dof_handler().get_communicator()) / GridTools::volume(ah.get_triangulation()); // assuming a linear mapping @@ -1091,7 +1088,7 @@ namespace dealii::PolyUtils if constexpr (std::is_same_v) { - const MPI_Comm &communicator = tria.get_mpi_communicator(); + const MPI_Comm &communicator = tria.get_communicator(); SparsityTools::distribute_sparsity_pattern(dsp, locally_owned_dofs, communicator, @@ -1602,7 +1599,7 @@ namespace dealii::PolyUtils if constexpr (std::is_same_v) { - const MPI_Comm &communicator = tria.get_mpi_communicator(); + const MPI_Comm &communicator = tria.get_communicator(); SparsityTools::distribute_sparsity_pattern(dsp, locally_owned_dofs, communicator, @@ -1734,7 +1731,7 @@ namespace dealii::PolyUtils // Perform reduction and take sqrt of each error global_errors[0] = Utilities::MPI::reduce( local_errors[0], - agglomeration_handler.get_triangulation().get_mpi_communicator(), + agglomeration_handler.get_triangulation().get_communicator(), [](const double a, const double b) { return a + b; }); global_errors[0] = std::sqrt(global_errors[0]); @@ -1743,7 +1740,7 @@ namespace dealii::PolyUtils { global_errors[1] = Utilities::MPI::reduce( local_errors[1], - agglomeration_handler.get_triangulation().get_mpi_communicator(), + agglomeration_handler.get_triangulation().get_communicator(), [](const double a, const double b) { return a + b; }); global_errors[1] = std::sqrt(global_errors[1]); } @@ -1774,7 +1771,7 @@ namespace dealii::PolyUtils GridTools::Cache cached_tria(tria); Assert(parallel_tria->n_active_cells() > 0, ExcInternalError()); - const MPI_Comm comm = parallel_tria->get_mpi_communicator(); + const MPI_Comm comm = parallel_tria->get_communicator(); ConditionalOStream pcout(std::cout, (Utilities::MPI::this_mpi_process(comm) == 0)); @@ -2183,9 +2180,9 @@ namespace dealii::PolyUtils constraints.distribute_local_to_global( M22, local_dof_indices_neighbor, system_matrix); } // ghosted polytope case - } // only once - } // internal face - } // face loop + } // only once + } // internal face + } // face loop constraints.distribute_local_to_global(cell_matrix, local_dof_indices, system_matrix); @@ -2231,18 +2228,17 @@ namespace dealii::PolyUtils 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_mpi_communicator(), - locally_relevant_dofs); + 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_mpi_communicator()); + dof_handler.get_communicator()); - system_rhs.reinit(locally_owned_dofs, dof_handler.get_mpi_communicator()); + system_rhs.reinit(locally_owned_dofs, dof_handler.get_communicator()); const unsigned int quadrature_degree = fe_dg.degree + 1; FEFaceValues fe_faces0(mapping, @@ -2449,7 +2445,7 @@ namespace dealii::PolyUtils M22, local_dof_indices_neighbor, system_matrix); } // check idx neighbors - } // over faces + } // over faces } constraints.distribute_local_to_global(cell_matrix, cell_rhs, diff --git a/agglomeration_poisson/poisson.cc b/agglomeration_poisson/poisson.cc deleted file mode 100644 index 81ed3811..00000000 --- a/agglomeration_poisson/poisson.cc +++ /dev/null @@ -1,781 +0,0 @@ -// ----------------------------------------------------------------------------- -// -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later -// Copyright (C) XXXX - YYYY by the polyDEAL authors -// -// This file is part of the polyDEAL library. -// -// Detailed license information governing the source code -// can be found in LICENSE.md at the top level directory. -// -// ----------------------------------------------------------------------------- - -#include -#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::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/source/agglomeration_handler.cc b/agglomeration_poisson/source/agglomeration_handler.cc index 642ed280..96e3ef92 100644 --- a/agglomeration_poisson/source/agglomeration_handler.cc +++ b/agglomeration_poisson/source/agglomeration_handler.cc @@ -1,16 +1,13 @@ -// ----------------------------------------------------------------------------- -// -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later -// Copyright (C) XXXX - YYYY by the polyDEAL authors -// -// This file is part of the polyDEAL library. -// -// Detailed license information governing the source code -// can be found in LICENSE.md at the top level directory. -// -// ----------------------------------------------------------------------------- - - +/* ----------------------------------------------------------------------------- + * + * 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 @@ -22,7 +19,7 @@ AgglomerationHandler::AgglomerationHandler( : cached_tria(std::make_unique>( cache_tria.get_triangulation(), cache_tria.get_mapping())) - , communicator(cache_tria.get_triangulation().get_mpi_communicator()) + , communicator(cache_tria.get_triangulation().get_communicator()) { Assert(dim == spacedim, ExcNotImplemented("Not available with codim > 0")); Assert(dim == 2 || dim == 3, ExcImpossibleInDim(1)); @@ -252,8 +249,8 @@ void AgglomerationHandler::initialize_agglomeration_data( const std::unique_ptr> &cache_tria) { - tria = &cache_tria->get_triangulation(); - mapping = &cache_tria->get_mapping(); + tria = &(cache_tria->get_triangulation()); + mapping = &(cache_tria->get_mapping()); agglo_dh.reinit(*tria); @@ -1328,8 +1325,8 @@ namespace dealii .visited_cell_and_faces)) { handler.polytope_cache - .interface[{ - current_polytope_id, neighbor_polytope_id}] + .interface[{current_polytope_id, + neighbor_polytope_id}] .emplace_back(cell, f); handler.polytope_cache.visited_cell_and_faces @@ -1343,8 +1340,8 @@ namespace dealii .visited_cell_and_faces)) { handler.polytope_cache - .interface[{ - neighbor_polytope_id, current_polytope_id}] + .interface[{neighbor_polytope_id, + current_polytope_id}] .emplace_back(neighboring_cell, nof); handler.polytope_cache.visited_cell_and_faces @@ -1395,8 +1392,8 @@ namespace dealii .visited_cell_and_faces)) { handler.polytope_cache - .interface[{ - current_polytope_id, neighbor_polytope_id}] + .interface[{current_polytope_id, + neighbor_polytope_id}] .emplace_back(cell, f); handler.polytope_cache.visited_cell_and_faces @@ -1409,8 +1406,8 @@ namespace dealii .visited_cell_and_faces)) { handler.polytope_cache - .interface[{ - neighbor_polytope_id, current_polytope_id}] + .interface[{neighbor_polytope_id, + current_polytope_id}] .emplace_back(neighboring_cell, nof); handler.polytope_cache.visited_cell_and_faces @@ -1488,8 +1485,8 @@ namespace dealii handler.polytope_cache.visited_cell_and_faces_id)) { handler.polytope_cache - .interface[{ - current_polytope_id, check_neigh_polytope_id}] + .interface[{current_polytope_id, + check_neigh_polytope_id}] .emplace_back(cell, f); // std::cout << "ADDED (" @@ -1510,8 +1507,8 @@ namespace dealii handler.polytope_cache.visited_cell_and_faces_id)) { handler.polytope_cache - .interface[{ - check_neigh_polytope_id, current_polytope_id}] + .interface[{check_neigh_polytope_id, + current_polytope_id}] .emplace_back(neighboring_cell, nof); handler.polytope_cache.visited_cell_and_faces_id @@ -1559,8 +1556,7 @@ namespace dealii std::end(handler.polytope_cache.visited_cell_and_faces)) { handler.polytope_cache - .interface[{ - current_polytope_id, current_polytope_id}] + .interface[{current_polytope_id, current_polytope_id}] .emplace_back(cell, f); handler.polytope_cache.visited_cell_and_faces.insert( @@ -1568,7 +1564,7 @@ namespace dealii } } } // loop over faces - } // loop over all cells of agglomerate + } // loop over all cells of agglomerate diff --git a/agglomeration_poisson/source/mapping_box.cc b/agglomeration_poisson/source/mapping_box.cc index d0b06887..7e65a2eb 100644 --- a/agglomeration_poisson/source/mapping_box.cc +++ b/agglomeration_poisson/source/mapping_box.cc @@ -1,16 +1,13 @@ -// ------------------------------------------------------------------------ -// -// SPDX-License-Identifier: LGPL-2.1-or-later -// Copyright (C) 2001 - 2024 by the deal.II authors -// -// This file is part of the deal.II library. -// -// Part of the source code is dual licensed under Apache-2.0 WITH -// LLVM-exception OR LGPL-2.1-or-later. Detailed license information -// governing the source code and code contributions can be found in -// LICENSE.md and CONTRIBUTING.md at the top level directory of deal.II. -// -// ------------------------------------------------------------------------ +/* ----------------------------------------------------------------------------- + * + * 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 @@ -32,8 +29,6 @@ #include - - DEAL_II_NAMESPACE_OPEN DeclExceptionMsg( From 0f481defe06ff2dae157a9bb92bb3c1f40fb3131 Mon Sep 17 00:00:00 2001 From: Pasquale Claudio Africa Date: Sat, 29 Nov 2025 16:56:13 +0100 Subject: [PATCH 4/4] Update documentation --- agglomeration_poisson/README.md | 17 +---------------- agglomeration_poisson/doc/author | 5 ++++- agglomeration_poisson/doc/entry-name | 2 +- agglomeration_poisson/doc/tooltip | 2 +- 4 files changed, 7 insertions(+), 19 deletions(-) diff --git a/agglomeration_poisson/README.md b/agglomeration_poisson/README.md index f243a2ab..d01b5c73 100644 --- a/agglomeration_poisson/README.md +++ b/agglomeration_poisson/README.md @@ -1,5 +1,4 @@ -# A posteriori error estimator for first order hyperbolic problems - +# A Discontinuous Galerkin solver for the Poisson problem on general polytopal meshes generated through mesh agglomeration ## Running the code: @@ -13,13 +12,6 @@ on the command line to configure the program. After that you can compile with `m on the command line. -### Parameter file: - -If you run `./DG_advection_reaction parameters.prm`, an error message will tell you that a parameter file has been created for you. You can open it and change some useful parameters like the number of refinement cycles, the advection coefficient, and others. If you don't specify anything, then the default values used for the test case (see paragraph below) will be used. - - - - ## The problem: This program solves the problem, for $\Omega \in \mathbb{R^2}$ @@ -40,8 +32,6 @@ for some positive $\gamma_0$ so that we have coercivity in $L^2$ at the continuo ## 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[ @@ -95,8 +85,6 @@ 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. @@ -121,8 +109,6 @@ where: - $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: @@ -138,7 +124,6 @@ The next image is the 3D view of the numerical solution: 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) diff --git a/agglomeration_poisson/doc/author b/agglomeration_poisson/doc/author index 1fe90537..74d91324 100644 --- a/agglomeration_poisson/doc/author +++ b/agglomeration_poisson/doc/author @@ -1 +1,4 @@ -Marco Feder +Marco Feder +Pasquale Claudio Africa +Xinping Gui +Andrea Cangiani diff --git a/agglomeration_poisson/doc/entry-name b/agglomeration_poisson/doc/entry-name index 8293f594..0cdacd6e 100644 --- a/agglomeration_poisson/doc/entry-name +++ b/agglomeration_poisson/doc/entry-name @@ -1 +1 @@ -Adaptive advection-reaction +An agglomeration-based solver for the Poisson problem diff --git a/agglomeration_poisson/doc/tooltip b/agglomeration_poisson/doc/tooltip index bed031fa..8f0328bc 100644 --- a/agglomeration_poisson/doc/tooltip +++ b/agglomeration_poisson/doc/tooltip @@ -1 +1 @@ -Implementation of an a-posteriori error estimator for first order hyperbolic problems +A Discontinuous Galerkin solver for the Poisson problem on general polytopal meshes generated through mesh agglomeration