]> https://gitweb.dealii.org/ - dealii.git/commitdiff
Add DoFTools::extract_level_constant_modes() 16869/head
authorPeter Munch <peterrmuench@gmail.com>
Wed, 3 Apr 2024 11:49:21 +0000 (13:49 +0200)
committerPeter Munch <peterrmuench@gmail.com>
Sun, 7 Apr 2024 20:22:36 +0000 (22:22 +0200)
doc/news/changes/minor/20240407Munch [new file with mode: 0644]
include/deal.II/dofs/dof_tools.h
source/dofs/dof_renumbering.cc
source/dofs/dof_tools.cc
source/dofs/dof_tools.inst.in
tests/mpi/extract_constant_modes_04.cc [new file with mode: 0644]
tests/mpi/extract_constant_modes_04.mpirun=10.with_p4est=true.output [new file with mode: 0644]
tests/mpi/extract_constant_modes_04.mpirun=4.with_p4est=true.output [new file with mode: 0644]

diff --git a/doc/news/changes/minor/20240407Munch b/doc/news/changes/minor/20240407Munch
new file mode 100644 (file)
index 0000000..999d88c
--- /dev/null
@@ -0,0 +1,4 @@
+New: The new function DoFTools::extract_level_constant_modes()
+allows to extract constant modes on multigrid levels.
+<br>
+(Peter Munch, 2024/04/07)
index bb380b64401f0fc5b76969f8a0c1dccbb5155aae..7d1d3659453a85d6ea12284d83cdaea9d43215c3 100644 (file)
@@ -1371,6 +1371,16 @@ namespace DoFTools
   extract_constant_modes(const DoFHandler<dim, spacedim> &dof_handler,
                          const ComponentMask             &component_mask,
                          std::vector<std::vector<bool>>  &constant_modes);
+
+  /**
+   * Same as above but for multigrid levels.
+   */
+  template <int dim, int spacedim>
+  void
+  extract_level_constant_modes(const unsigned int               level,
+                               const DoFHandler<dim, spacedim> &dof_handler,
+                               const ComponentMask             &component_mask,
+                               std::vector<std::vector<bool>>  &constant_modes);
   /** @} */
 
   /**
index f53ca729de839d1b02115d9194e23b4aa95ad5fa..d4f060248b32037f14fdd48b84fa3d9c63dfafec 100644 (file)
@@ -723,12 +723,26 @@ namespace DoFRenumbering
         renumbering, start, end, component_order_arg, true);
     (void)result;
 
-    Assert(result == 0 || result == dof_handler.n_dofs(level),
+    // If we don't have a renumbering (i.e., when there is 1 component) then
+    // return
+    if (Utilities::MPI::max(renumbering.size(),
+                            dof_handler.get_communicator()) == 0)
+      return;
+
+    // verify that the last numbered
+    // degree of freedom is either
+    // equal to the number of degrees
+    // of freedom in total (the
+    // sequential case) or in the
+    // distributed case at least
+    // makes sense
+    Assert((result == dof_handler.locally_owned_mg_dofs(level).n_elements()) ||
+             ((dof_handler.locally_owned_mg_dofs(level).n_elements() <
+               dof_handler.n_dofs(level)) &&
+              (result <= dof_handler.n_dofs(level))),
            ExcInternalError());
 
-    if (Utilities::MPI::max(renumbering.size(),
-                            dof_handler.get_communicator()) > 0)
-      dof_handler.renumber_dofs(level, renumbering);
+    dof_handler.renumber_dofs(level, renumbering);
   }
 
 
index 4b0c558b55180dd982e56f3c382309ed09cc1548..3fd1a6641c692e22cbb376d2fb6c223161813293 100644 (file)
@@ -178,7 +178,7 @@ namespace DoFTools
     // the output array dofs_by_component lists for each dof the
     // corresponding vector component. if the DoFHandler is based on a
     // parallel distributed triangulation then the output array is index by
-    // dof.locally_owned_dofs().index_within_set(indices[i])
+    // dof_handler.locally_owned_dofs().index_within_set(indices[i])
     //
     // if an element is non-primitive then we assign to each degree of
     // freedom the following component:
@@ -190,16 +190,23 @@ namespace DoFTools
     //   in the component_mask that corresponds to this shape function
     template <int dim, int spacedim>
     void
-    get_component_association(const DoFHandler<dim, spacedim> &dof,
-                              const ComponentMask             &component_mask,
-                              std::vector<unsigned char> &dofs_by_component)
+    get_component_association(
+      const DoFHandler<dim, spacedim> &dof_handler,
+      const ComponentMask             &component_mask,
+      std::vector<unsigned char>      &dofs_by_component,
+      const unsigned int               mg_level = numbers::invalid_unsigned_int)
     {
       const dealii::hp::FECollection<dim, spacedim> &fe_collection =
-        dof.get_fe_collection();
+        dof_handler.get_fe_collection();
       Assert(fe_collection.n_components() < 256, ExcNotImplemented());
-      Assert(dofs_by_component.size() == dof.n_locally_owned_dofs(),
-             ExcDimensionMismatch(dofs_by_component.size(),
-                                  dof.n_locally_owned_dofs()));
+
+      const auto &locally_owned_dofs =
+        (mg_level == numbers::invalid_unsigned_int) ?
+          dof_handler.locally_owned_dofs() :
+          dof_handler.locally_owned_mg_dofs(mg_level);
+
+      AssertDimension(dofs_by_component.size(),
+                      locally_owned_dofs.n_elements());
 
       // next set up a table for the degrees of freedom on each of the
       // cells (regardless of the fact whether it is listed in the
@@ -219,18 +226,40 @@ namespace DoFTools
 
       // then loop over all cells and do the work
       std::vector<types::global_dof_index> indices;
-      for (const auto &c :
-           dof.active_cell_iterators() | IteratorFilters::LocallyOwnedCell())
-        {
-          const types::fe_index fe_index      = c->active_fe_index();
-          const unsigned int    dofs_per_cell = c->get_fe().n_dofs_per_cell();
-          indices.resize(dofs_per_cell);
-          c->get_dof_indices(indices);
-          for (unsigned int i = 0; i < dofs_per_cell; ++i)
-            if (dof.locally_owned_dofs().is_element(indices[i]))
-              dofs_by_component[dof.locally_owned_dofs().index_within_set(
-                indices[i])] = local_component_association[fe_index][i];
-        }
+
+      const auto runner = [&](const auto &task) {
+        if (mg_level == numbers::invalid_unsigned_int)
+          {
+            for (const auto &cell : dof_handler.active_cell_iterators() |
+                                      IteratorFilters::LocallyOwnedCell())
+              {
+                indices.resize(cell->get_fe().n_dofs_per_cell());
+                cell->get_dof_indices(indices);
+
+                task(cell);
+              }
+          }
+        else
+          {
+            for (const auto &cell :
+                 dof_handler.cell_iterators_on_level(mg_level))
+              if (cell->is_locally_owned_on_level())
+                {
+                  indices.resize(cell->get_fe().n_dofs_per_cell());
+                  cell->get_mg_dof_indices(indices);
+
+                  task(cell);
+                }
+          }
+      };
+
+      runner([&](const auto &cell) {
+        const types::fe_index fe_index = cell->active_fe_index();
+        for (unsigned int i = 0; i < cell->get_fe().n_dofs_per_cell(); ++i)
+          if (locally_owned_dofs.is_element(indices[i]))
+            dofs_by_component[locally_owned_dofs.index_within_set(indices[i])] =
+              local_component_association[fe_index][i];
+      });
     }
 
 
@@ -1235,104 +1264,133 @@ namespace DoFTools
   }
 
 
-
-  template <int dim, int spacedim>
-  void
-  extract_constant_modes(const DoFHandler<dim, spacedim> &dof_handler,
-                         const ComponentMask             &component_mask,
-                         std::vector<std::vector<bool>>  &constant_modes)
+  namespace internal
   {
-    // If there are no locally owned DoFs, return with an empty
-    // constant_modes object:
-    if (dof_handler.n_locally_owned_dofs() == 0)
+    template <int dim, int spacedim>
+    void
+    extract_constant_modes(const DoFHandler<dim, spacedim> &dof_handler,
+                           const ComponentMask             &component_mask,
+                           const unsigned int               mg_level,
+                           std::vector<std::vector<bool>>  &constant_modes)
+    {
+      const auto &locally_owned_dofs =
+        (mg_level == numbers::invalid_unsigned_int) ?
+          dof_handler.locally_owned_dofs() :
+          dof_handler.locally_owned_mg_dofs(mg_level);
+
+      // If there are no locally owned DoFs, return with an empty
+      // constant_modes object:
+      if (locally_owned_dofs.n_elements() == 0)
+        {
+          constant_modes = std::vector<std::vector<bool>>(0);
+          return;
+        }
+
+      const unsigned int n_components = dof_handler.get_fe(0).n_components();
+      Assert(component_mask.represents_n_components(n_components),
+             ExcDimensionMismatch(n_components, component_mask.size()));
+
+      std::vector<unsigned char> dofs_by_component(
+        locally_owned_dofs.n_elements());
+      internal::get_component_association(dof_handler,
+                                          component_mask,
+                                          dofs_by_component,
+                                          mg_level);
+      unsigned int n_selected_dofs = 0;
+      for (unsigned int i = 0; i < n_components; ++i)
+        if (component_mask[i] == true)
+          n_selected_dofs +=
+            std::count(dofs_by_component.begin(), dofs_by_component.end(), i);
+
+      // Find local numbering within the selected components
+      std::vector<unsigned int> component_numbering(
+        locally_owned_dofs.n_elements(), numbers::invalid_unsigned_int);
+      for (unsigned int i = 0, count = 0; i < locally_owned_dofs.n_elements();
+           ++i)
+        if (component_mask[dofs_by_component[i]])
+          component_numbering[i] = count++;
+
+      // get the element constant modes and find a translation table between
+      // index in the constant modes and the components.
+      //
+      // TODO: We might be able to extend this also for elements which do not
+      // have the same constant modes, but that is messy...
+      const dealii::hp::FECollection<dim, spacedim> &fe_collection =
+        dof_handler.get_fe_collection();
+      std::vector<Table<2, bool>> element_constant_modes;
+      std::vector<std::vector<std::pair<unsigned int, unsigned int>>>
+        constant_mode_to_component_translation(n_components);
       {
-        constant_modes = std::vector<std::vector<bool>>(0);
-        return;
-      }
+        unsigned int n_constant_modes              = 0;
+        int          first_non_empty_constant_mode = -1;
+        for (unsigned int f = 0; f < fe_collection.size(); ++f)
+          {
+            std::pair<Table<2, bool>, std::vector<unsigned int>> data =
+              fe_collection[f].get_constant_modes();
 
-    const unsigned int n_components = dof_handler.get_fe(0).n_components();
-    Assert(component_mask.represents_n_components(n_components),
-           ExcDimensionMismatch(n_components, component_mask.size()));
+            // Store the index of the current element if it is the first that
+            // has non-empty constant modes.
+            if (first_non_empty_constant_mode < 0 && data.first.n_rows() > 0)
+              {
+                first_non_empty_constant_mode = f;
+                // This is the first non-empty constant mode, so we figure out
+                // the translation between index in the constant modes and the
+                // components
+                for (unsigned int i = 0; i < data.second.size(); ++i)
+                  if (component_mask[data.second[i]])
+                    constant_mode_to_component_translation[data.second[i]]
+                      .emplace_back(n_constant_modes++, i);
+              }
 
-    std::vector<unsigned char> dofs_by_component(
-      dof_handler.n_locally_owned_dofs());
-    internal::get_component_association(dof_handler,
-                                        component_mask,
-                                        dofs_by_component);
-    unsigned int n_selected_dofs = 0;
-    for (unsigned int i = 0; i < n_components; ++i)
-      if (component_mask[i] == true)
-        n_selected_dofs +=
-          std::count(dofs_by_component.begin(), dofs_by_component.end(), i);
-
-    // Find local numbering within the selected components
-    const IndexSet &locally_owned_dofs = dof_handler.locally_owned_dofs();
-    std::vector<unsigned int> component_numbering(
-      locally_owned_dofs.n_elements(), numbers::invalid_unsigned_int);
-    for (unsigned int i = 0, count = 0; i < locally_owned_dofs.n_elements();
-         ++i)
-      if (component_mask[dofs_by_component[i]])
-        component_numbering[i] = count++;
-
-    // get the element constant modes and find a translation table between
-    // index in the constant modes and the components.
-    //
-    // TODO: We might be able to extend this also for elements which do not
-    // have the same constant modes, but that is messy...
-    const dealii::hp::FECollection<dim, spacedim> &fe_collection =
-      dof_handler.get_fe_collection();
-    std::vector<Table<2, bool>> element_constant_modes;
-    std::vector<std::vector<std::pair<unsigned int, unsigned int>>>
-      constant_mode_to_component_translation(n_components);
-    {
-      unsigned int n_constant_modes              = 0;
-      int          first_non_empty_constant_mode = -1;
-      for (unsigned int f = 0; f < fe_collection.size(); ++f)
-        {
-          std::pair<Table<2, bool>, std::vector<unsigned int>> data =
-            fe_collection[f].get_constant_modes();
+            // Add the constant modes of this element to the list and assert
+            // that there are as many constant modes as for the other elements
+            // (or zero constant modes).
+            element_constant_modes.push_back(data.first);
+            Assert(element_constant_modes.back().n_rows() == 0 ||
+                     element_constant_modes.back().n_rows() ==
+                       element_constant_modes[first_non_empty_constant_mode]
+                         .n_rows(),
+                   ExcInternalError());
+          }
+        AssertIndexRange(first_non_empty_constant_mode, fe_collection.size());
 
-          // Store the index of the current element if it is the first that has
-          // non-empty constant modes.
-          if (first_non_empty_constant_mode < 0 && data.first.n_rows() > 0)
-            {
-              first_non_empty_constant_mode = f;
-              // This is the first non-empty constant mode, so we figure out the
-              // translation between index in the constant modes and the
-              // components
-              for (unsigned int i = 0; i < data.second.size(); ++i)
-                if (component_mask[data.second[i]])
-                  constant_mode_to_component_translation[data.second[i]]
-                    .emplace_back(n_constant_modes++, i);
-            }
+        // Now we know the number of constant modes and resize the return vector
+        // accordingly
+        constant_modes.clear();
+        constant_modes.resize(n_constant_modes,
+                              std::vector<bool>(n_selected_dofs, false));
+      }
 
-          // Add the constant modes of this element to the list and assert that
-          // there are as many constant modes as for the other elements (or zero
-          // constant modes).
-          element_constant_modes.push_back(data.first);
-          Assert(
-            element_constant_modes.back().n_rows() == 0 ||
-              element_constant_modes.back().n_rows() ==
-                element_constant_modes[first_non_empty_constant_mode].n_rows(),
-            ExcInternalError());
-        }
-      AssertIndexRange(first_non_empty_constant_mode, fe_collection.size());
+      // Loop over all owned cells and ask the element for the constant modes
+      std::vector<types::global_dof_index> dof_indices;
 
-      // Now we know the number of constant modes and resize the return vector
-      // accordingly
-      constant_modes.clear();
-      constant_modes.resize(n_constant_modes,
-                            std::vector<bool>(n_selected_dofs, false));
-    }
+      const auto runner = [&](const auto &task) {
+        if (mg_level == numbers::invalid_unsigned_int)
+          {
+            for (const auto &cell : dof_handler.active_cell_iterators() |
+                                      IteratorFilters::LocallyOwnedCell())
+              {
+                dof_indices.resize(cell->get_fe().n_dofs_per_cell());
+                cell->get_dof_indices(dof_indices);
 
-    // Loop over all owned cells and ask the element for the constant modes
-    std::vector<types::global_dof_index> dof_indices;
-    for (const auto &cell : dof_handler.active_cell_iterators() |
-                              IteratorFilters::LocallyOwnedCell())
-      {
-        dof_indices.resize(cell->get_fe().n_dofs_per_cell());
-        cell->get_dof_indices(dof_indices);
+                task(cell);
+              }
+          }
+        else
+          {
+            for (const auto &cell :
+                 dof_handler.cell_iterators_on_level(mg_level))
+              if (cell->is_locally_owned_on_level())
+                {
+                  dof_indices.resize(cell->get_fe().n_dofs_per_cell());
+                  cell->get_mg_dof_indices(dof_indices);
+
+                  task(cell);
+                }
+          }
+      };
 
+      runner([&](const auto &cell) {
         for (unsigned int i = 0; i < dof_indices.size(); ++i)
           if (locally_owned_dofs.is_element(dof_indices[i]))
             {
@@ -1347,7 +1405,37 @@ namespace DoFTools
                     element_constant_modes[cell->active_fe_index()](
                       indices.second, i);
             }
-      }
+      });
+    }
+  } // namespace internal
+
+
+
+  template <int dim, int spacedim>
+  void
+  extract_constant_modes(const DoFHandler<dim, spacedim> &dof_handler,
+                         const ComponentMask             &component_mask,
+                         std::vector<std::vector<bool>>  &constant_modes)
+  {
+    internal::extract_constant_modes(dof_handler,
+                                     component_mask,
+                                     numbers::invalid_unsigned_int,
+                                     constant_modes);
+  }
+
+
+
+  template <int dim, int spacedim>
+  void
+  extract_level_constant_modes(const unsigned int               level,
+                               const DoFHandler<dim, spacedim> &dof_handler,
+                               const ComponentMask             &component_mask,
+                               std::vector<std::vector<bool>>  &constant_modes)
+  {
+    internal::extract_constant_modes(dof_handler,
+                                     component_mask,
+                                     level,
+                                     constant_modes);
   }
 
 
index e4613113b1bcfd2e574a6474da572112d83fc601..864e81037a85f2136a3deb48595df9a729703941 100644 (file)
@@ -193,6 +193,12 @@ for (deal_II_dimension : DIMENSIONS)
       const ComponentMask                 &selected_components,
       std::vector<std::vector<bool>>      &constant_modes);
 
+    template void DoFTools::extract_level_constant_modes<deal_II_dimension>(
+      const unsigned int                   level,
+      const DoFHandler<deal_II_dimension> &dof_handler,
+      const ComponentMask                 &selected_components,
+      std::vector<std::vector<bool>>      &constant_modes);
+
     template void DoFTools::get_active_fe_indices<deal_II_dimension>(
       const DoFHandler<deal_II_dimension> &dof_handler,
       std::vector<unsigned int>           &active_fe_indices);
diff --git a/tests/mpi/extract_constant_modes_04.cc b/tests/mpi/extract_constant_modes_04.cc
new file mode 100644 (file)
index 0000000..fa8754d
--- /dev/null
@@ -0,0 +1,129 @@
+// ------------------------------------------------------------------------
+//
+// SPDX-License-Identifier: LGPL-2.1-or-later
+// Copyright (C) 2010 - 2023 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.
+//
+// ------------------------------------------------------------------------
+
+
+
+// test DoFTools::extract_level_constant_modes (see also
+// extract_constant_modes_01.cc).
+
+#include <deal.II/base/tensor.h>
+
+#include <deal.II/distributed/tria.h>
+
+#include <deal.II/dofs/dof_accessor.h>
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/dofs/dof_renumbering.h>
+#include <deal.II/dofs/dof_tools.h>
+
+#include <deal.II/fe/fe_dgq.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_system.h>
+
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/grid_out.h>
+#include <deal.II/grid/tria.h>
+#include <deal.II/grid/tria_accessor.h>
+#include <deal.II/grid/tria_iterator.h>
+
+#include "../tests.h"
+
+
+
+template <int dim>
+void
+test()
+{
+  unsigned int myid = Utilities::MPI::this_mpi_process(MPI_COMM_WORLD);
+  parallel::distributed::Triangulation<dim> tr(
+    MPI_COMM_WORLD,
+    Triangulation<dim>::none,
+    parallel::distributed::Triangulation<dim>::construct_multigrid_hierarchy);
+
+  std::vector<unsigned int> sub(2);
+  sub[0] = Utilities::MPI::n_mpi_processes(MPI_COMM_WORLD);
+  sub[1] = 1;
+  GridGenerator::subdivided_hyper_rectangle(
+    static_cast<Triangulation<dim> &>(tr), sub, Point<2>(0, 0), Point<2>(1, 1));
+
+  FESystem<dim>   fe(FE_Q<dim>(1), 2, FE_DGQ<dim>(0), 1);
+  DoFHandler<dim> dofh(tr);
+  dofh.distribute_dofs(fe);
+  dofh.distribute_mg_dofs();
+
+  if (Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0)
+    deallog << "Total dofs=" << dofh.n_dofs() << std::endl;
+
+  // extract constant modes and print
+  if (myid == 0)
+    for (unsigned int c = 0; c < fe.n_components(); ++c)
+      {
+        ComponentMask mask(fe.n_components(), false);
+        mask.set(c, true);
+
+        std::vector<std::vector<bool>> constant_modes;
+        DoFTools::extract_level_constant_modes(0, dofh, mask, constant_modes);
+
+        for (unsigned int i = 0; i < constant_modes.size(); ++i)
+          {
+            for (unsigned int j = 0; j < constant_modes[i].size(); ++j)
+              deallog << (constant_modes[i][j] ? '1' : '0') << ' ';
+            deallog << std::endl;
+          }
+      }
+
+  // renumber dofs and do the same again
+  DoFRenumbering::component_wise(dofh, 0);
+  if (myid == 0)
+    for (unsigned int c = 0; c < fe.n_components(); ++c)
+      {
+        ComponentMask mask(fe.n_components(), false);
+        mask.set(c, true);
+
+        std::vector<std::vector<bool>> constant_modes;
+        DoFTools::extract_level_constant_modes(0, dofh, mask, constant_modes);
+
+        for (unsigned int i = 0; i < constant_modes.size(); ++i)
+          {
+            for (unsigned int j = 0; j < constant_modes[i].size(); ++j)
+              deallog << (constant_modes[i][j] ? '1' : '0') << ' ';
+            deallog << std::endl;
+          }
+      }
+}
+
+
+int
+main(int argc, char *argv[])
+{
+  Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv, 1);
+
+  unsigned int myid = Utilities::MPI::this_mpi_process(MPI_COMM_WORLD);
+
+  deallog.push(Utilities::int_to_string(myid));
+
+  if (myid == 0)
+    {
+      initlog();
+
+      deallog.push("2d");
+      test<2>();
+      deallog.pop();
+    }
+  else
+    {
+      deallog.push("2d");
+      test<2>();
+      deallog.pop();
+    }
+}
diff --git a/tests/mpi/extract_constant_modes_04.mpirun=10.with_p4est=true.output b/tests/mpi/extract_constant_modes_04.mpirun=10.with_p4est=true.output
new file mode 100644 (file)
index 0000000..ad36070
--- /dev/null
@@ -0,0 +1,8 @@
+
+DEAL:0:2d::Total dofs=54
+DEAL:0:2d::1 1 1 1 
+DEAL:0:2d::1 1 1 1 
+DEAL:0:2d::1 
+DEAL:0:2d::1 1 1 1 
+DEAL:0:2d::1 1 1 1 
+DEAL:0:2d::1 
diff --git a/tests/mpi/extract_constant_modes_04.mpirun=4.with_p4est=true.output b/tests/mpi/extract_constant_modes_04.mpirun=4.with_p4est=true.output
new file mode 100644 (file)
index 0000000..0378ef0
--- /dev/null
@@ -0,0 +1,8 @@
+
+DEAL:0:2d::Total dofs=24
+DEAL:0:2d::1 1 1 1 
+DEAL:0:2d::1 1 1 1 
+DEAL:0:2d::1 
+DEAL:0:2d::1 1 1 1 
+DEAL:0:2d::1 1 1 1 
+DEAL:0:2d::1 

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.