/home/runner/work/DiFfRG_current/DiFfRG_current/DiFfRG/include/DiFfRG/discretization/mesh/h_adaptivity.hh Source File#

DiFfRG: /home/runner/work/DiFfRG_current/DiFfRG_current/DiFfRG/include/DiFfRG/discretization/mesh/h_adaptivity.hh Source File
DiFfRG
Discretization Framework for functional Renormalization Group flows
h_adaptivity.hh
Go to the documentation of this file.
1#pragma once
2
3// external libraries
4#include <deal.II/dofs/dof_handler.h>
5#include <deal.II/dofs/dof_tools.h>
6#include <deal.II/grid/grid_refinement.h>
7#include <deal.II/grid/tria.h>
8#include <deal.II/lac/vector.h>
9#include <deal.II/numerics/derivative_approximation.h>
10#include <deal.II/numerics/error_estimator.h>
11#include <deal.II/numerics/solution_transfer.h>
12
13// DiFfRG
15#include <DiFfRG/common/mpi.hh>
18
19namespace DiFfRG
20{
21 using namespace dealii;
27 template <typename Assembler>
28 class HAdaptivity : public AbstractAdaptor<typename Assembler::Discretization::VectorType>
29 {
30 using Discretization = typename Assembler::Discretization;
31 using VectorType = typename Discretization::VectorType;
32 static constexpr uint dim = Discretization::dim;
33
34 public:
36 : assembler(assembler), discretization(assembler.get_discretization())
37 {
38 adapt_t = config.get_double("/discretization/adaptivity/start_adapt_at", 0.0);
39 adapt_dt = config.get_double("/discretization/adaptivity/adapt_dt", 1e-1);
40 adapt_level = config.get_uint("/discretization/adaptivity/level", 0);
41 adapt_upper = config.get_double("/discretization/adaptivity/refine_percent", 1e-1);
42 adapt_lower = config.get_double("/discretization/adaptivity/coarsen_percent", 5e-2);
43 }
44
45 virtual ~HAdaptivity() = default;
46
54 virtual bool operator()(const double t, VectorType &sol) override
55 {
56 if (adapt_level > 0 && t >= adapt_t - 1e-12 * adapt_dt && (t - last_adapt + 1e-12 * adapt_dt) >= adapt_dt) {
57 if (!adapt(sol)) return false;
58 last_adapt = t;
59 return true;
60 }
61 return false;
62 }
63
69 virtual bool adapt(VectorType &solution) override
70 {
71 auto &triangulation = discretization.get_triangulation();
72 auto &dof_handler = discretization.get_dof_handler(0);
73 auto &constraints = discretization.get_constraints(0);
74
75 // Both readers below -- refinement_indicator() here and SolutionTransfer further down -- take
76 // arbitrary global dof indices, which the vector we are handed cannot answer: it holds only
77 // this rank's rows, and deal.II's non-ghosted read path is `ptr[index - local_begin]` with no
78 // bounds check outside debug builds. A partition-boundary read therefore does not fail, it
79 // returns whatever is next in memory. Read both through a fully-replicated view instead.
81 assembler.reinit_solution_view(view);
82 view.refresh(solution);
83
84 Vector<double> indicator(triangulation.n_active_cells());
85 indicator = 0;
86 assembler.refinement_indicator(indicator, view.get());
87
88 if constexpr (Discretization::Mesh::is_parallel) {
89 // The indicator is filled by a mesh_loop with assemble_own_cells, so on a partitioned mesh
90 // each rank only contributes its own cells and every other entry is still zero. Refining on
91 // that unreduced vector is not a crash -- it silently refines a different set of cells on
92 // every rank, which then diverge. Sum first; the mesh is replicated, so after this every
93 // rank holds the identical, complete indicator.
94 MPI::sum_reduce(triangulation.get_mpi_communicator(), indicator.data(),
95 static_cast<int>(indicator.size()));
96 }
97
98 GridRefinement::refine_and_coarsen_fixed_fraction(triangulation, indicator, adapt_upper, adapt_lower);
99
100 if (triangulation.n_levels() > adapt_level)
101 for (const auto &cell : triangulation.active_cell_iterators_on_level(adapt_level))
102 cell->clear_refine_flag();
103 for (const auto &cell : triangulation.active_cell_iterators_on_level(0))
104 cell->clear_coarsen_flag();
105
106 bool any_refined = false;
107 for (const auto &cell : triangulation.active_cell_iterators())
108 if (cell->refine_flag_set()) {
109 any_refined = true;
110 break;
111 }
112 // Agreed: this early return decides whether execute_coarsening_and_refinement() -- a
113 // collective on a parallel triangulation -- is reached at all. The reduced indicator above
114 // should already make every rank decide identically; this makes a hang impossible rather
115 // than unlikely.
116 if (!MPI::any_of(triangulation.get_mpi_communicator(), any_refined)) return false;
117
118 SolutionTransfer<dim, VectorType> solution_trans(dof_handler);
119
120 // A copy, not the view itself: reinit_vector() below resizes `solution`, and the serial view
121 // aliases its source rather than snapshotting it. SolutionTransfer also has to keep reading
122 // this right through execute_coarsening_and_refinement(), which is where the packing happens.
123 VectorType previous_solution = view.get();
124 triangulation.prepare_coarsening_and_refinement();
125 solution_trans.prepare_for_coarsening_and_refinement(previous_solution);
126 triangulation.execute_coarsening_and_refinement();
127
128 discretization.reinit();
129 assembler.reinit();
130 assembler.reinit_vector(solution);
131
132 // The write side needs no distributed handling. SolutionTransfer is built with
133 // average_values = false, so interpolate() ends in compress(VectorOperation::insert), and on
134 // a replicated mesh every rank unpacks every cell to bit-identical values -- identical
135 // replica, identical mesh, deterministic interpolation -- so it does not matter which rank's
136 // insert the owner keeps. (With average_values = true this would instead be a sum over
137 // n_ranks redundant contributions, correct only because the valence is inflated by the same
138 // factor, and no longer exact.)
139 solution_trans.interpolate(solution);
140 constraints.distribute(solution);
141
142 return true;
143 }
144
145 protected:
146 Assembler &assembler;
148
151 };
152} // namespace DiFfRG
Implement a simple interface to do all adaptivity tasks, i.e. solution transfer, reinit of dofHandler...
Definition abstract_adaptor.hh:11
A hierarchical configuration tree, readable from JSON and TOML files.
Definition config_tree.hh:32
Implement a simple interface to do all adaptivity tasks, i.e. solution transfer, reinit of dofHandler...
Definition h_adaptivity.hh:29
HAdaptivity(Assembler &assembler, const ConfigTree &config)
Definition h_adaptivity.hh:35
typename Discretization::VectorType VectorType
Definition h_adaptivity.hh:31
typename Assembler::Discretization Discretization
Definition h_adaptivity.hh:30
double adapt_dt
Definition h_adaptivity.hh:149
double adapt_upper
Definition h_adaptivity.hh:149
double adapt_lower
Definition h_adaptivity.hh:149
static constexpr uint dim
Definition h_adaptivity.hh:32
double adapt_t
Definition h_adaptivity.hh:149
Discretization & discretization
Definition h_adaptivity.hh:147
virtual ~HAdaptivity()=default
virtual bool adapt(VectorType &solution) override
Force an adaptation and transfer the solution sol to the new mes.
Definition h_adaptivity.hh:69
uint adapt_level
Definition h_adaptivity.hh:150
double last_adapt
Definition h_adaptivity.hh:149
virtual bool operator()(const double t, VectorType &sol) override
Check if an adaptation step should be done and tranfer the given solution to the new mesh.
Definition h_adaptivity.hh:54
Assembler & assembler
Definition h_adaptivity.hh:146
A read-only, fully-replicated view of the solution.
Definition solution_view.hh:48
void refresh(const VectorType &owned)
Make the view reflect owned.
Definition solution_view.hh:63
const VectorType & get() const
Definition solution_view.hh:65
bool any_of(MPI_Comm comm, bool value)
Collective agreement on a predicate. Used to turn a rank-local decision (an abort,...
void sum_reduce(MPI_Comm comm, int *data, int size)
Definition complex_math.hh:10
@ config
/discretization/threads.
unsigned int uint
Definition utils.hh:24