Skip to content

Commit 6e250cd

Browse files
committed
support DoF coupling in FESystem
1 parent d35e35c commit 6e250cd

9 files changed

+395
-10
lines changed

source/fe/fe_system.cc

+55
Original file line numberDiff line numberDiff line change
@@ -2009,6 +2009,61 @@ FESystem<dim, spacedim>::initialize(
20092009
Assert(index == this->n_dofs_per_line(), ExcInternalError());
20102010
});
20112011

2012+
2013+
// Compute local_dof_sparsity_pattern if any of our base elements contains a
2014+
// non-empty one (empty denotes the default of all DoFs coupling within a
2015+
// cell). Note the we currently only handle coupling within a base element and
2016+
// not between two different base elements. Handling the latter could be
2017+
// doable if the underlying element happens to be identical, but we currently
2018+
// have no functionality to compute the coupling between different elements
2019+
// with a pattern (for example FE_Q_iso_Q1 with different degrees).
2020+
{
2021+
// Does any of our base elements not couple all DoFs?
2022+
const bool have_nonempty = [&]() -> bool {
2023+
for (unsigned int b = 0; b < this->n_base_elements(); ++b)
2024+
{
2025+
if (!this->base_element(b).get_local_dof_sparsity_pattern().empty() &&
2026+
(this->element_multiplicity(b) > 0))
2027+
return true;
2028+
}
2029+
return false;
2030+
}();
2031+
2032+
if (have_nonempty)
2033+
{
2034+
this->local_dof_sparsity_pattern.reinit(this->n_dofs_per_cell(),
2035+
this->n_dofs_per_cell());
2036+
2037+
// by default, everything couples:
2038+
this->local_dof_sparsity_pattern.fill(true);
2039+
2040+
// Find shape functions within the same base element. If we do, grab the
2041+
// coupling from that base element pattern:
2042+
for (unsigned int i = 0; i < this->n_dofs_per_cell(); ++i)
2043+
for (unsigned int j = 0; j < this->n_dofs_per_cell(); ++j)
2044+
{
2045+
const auto vi = this->system_to_base_index(i);
2046+
const auto vj = this->system_to_base_index(j);
2047+
2048+
const auto base_index_i = vi.first.first;
2049+
const auto base_index_j = vj.first.first;
2050+
if (base_index_i == base_index_j)
2051+
{
2052+
const auto shape_index_i = vi.second;
2053+
const auto shape_index_j = vj.second;
2054+
2055+
const auto &pattern = this->base_element(base_index_i)
2056+
.get_local_dof_sparsity_pattern();
2057+
2058+
if (!pattern.empty())
2059+
this->local_dof_sparsity_pattern(i, j) =
2060+
pattern(shape_index_i, shape_index_j);
2061+
}
2062+
}
2063+
}
2064+
}
2065+
2066+
20122067
// wait for all of this to finish
20132068
init_tasks.join_all();
20142069
}
File renamed without changes.
File renamed without changes.

tests/fe/assemble_q_iso_q1_02.cc

+230
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
// ------------------------------------------------------------------------
2+
//
3+
// SPDX-License-Identifier: LGPL-2.1-or-later
4+
// Copyright (C) 2013 - 2024 by the deal.II authors
5+
//
6+
// This file is part of the deal.II library.
7+
//
8+
// Part of the source code is dual licensed under Apache-2.0 WITH
9+
// LLVM-exception OR LGPL-2.1-or-later. Detailed license information
10+
// governing the source code and code contributions can be found in
11+
// LICENSE.md and CONTRIBUTING.md at the top level directory of deal.II.
12+
//
13+
// ------------------------------------------------------------------------
14+
15+
16+
// Assemble a 1d,2d,3d vector Poisson problem with FE_Q_iso_Q1 elements to
17+
// check that the sparsity pattern is computed correctly.
18+
19+
#include <deal.II/base/function.h>
20+
21+
#include <deal.II/dofs/dof_handler.h>
22+
#include <deal.II/dofs/dof_tools.h>
23+
24+
#include <deal.II/fe/fe_q.h>
25+
#include <deal.II/fe/fe_q_iso_q1.h>
26+
#include <deal.II/fe/fe_system.h>
27+
#include <deal.II/fe/fe_values.h>
28+
29+
#include <deal.II/grid/grid_generator.h>
30+
#include <deal.II/grid/tria.h>
31+
32+
#include <deal.II/lac/affine_constraints.h>
33+
#include <deal.II/lac/dynamic_sparsity_pattern.h>
34+
#include <deal.II/lac/solver_cg.h>
35+
#include <deal.II/lac/sparse_matrix.h>
36+
#include <deal.II/lac/vector.h>
37+
38+
#include <deal.II/numerics/vector_tools.h>
39+
40+
#include "../tests.h"
41+
42+
43+
44+
template <int dim>
45+
class Step4
46+
{
47+
public:
48+
Step4();
49+
void
50+
run();
51+
52+
private:
53+
void
54+
make_grid();
55+
void
56+
setup_system();
57+
void
58+
assemble_preconditioner();
59+
60+
Triangulation<dim> triangulation;
61+
62+
FESystem<dim> fe_precondition;
63+
DoFHandler<dim> dof_handler_precondition;
64+
65+
AffineConstraints<double> constraints;
66+
67+
SparsityPattern prec_pattern;
68+
SparseMatrix<double> preconditioner_matrix;
69+
70+
Vector<double> system_rhs;
71+
};
72+
73+
74+
template <int dim>
75+
class RightHandSide : public Function<dim>
76+
{
77+
public:
78+
RightHandSide()
79+
: Function<dim>()
80+
{}
81+
82+
virtual double
83+
value(const Point<dim> &p, const unsigned int component = 0) const;
84+
};
85+
86+
87+
88+
template <int dim>
89+
double
90+
RightHandSide<dim>::value(const Point<dim> &p,
91+
const unsigned int /*component*/) const
92+
{
93+
double return_value = 0;
94+
for (unsigned int i = 0; i < dim; ++i)
95+
return_value += 4 * std::pow(p[i], 4);
96+
97+
return return_value;
98+
}
99+
100+
101+
template <int dim>
102+
Step4<dim>::Step4()
103+
: fe_precondition(FE_Q_iso_Q1<dim>(2), dim)
104+
, dof_handler_precondition(triangulation)
105+
{}
106+
107+
108+
template <int dim>
109+
void
110+
Step4<dim>::make_grid()
111+
{
112+
GridGenerator::hyper_cube(triangulation, -1, 1);
113+
triangulation.refine_global(1);
114+
}
115+
116+
117+
118+
template <int dim>
119+
void
120+
Step4<dim>::setup_system()
121+
{
122+
dof_handler_precondition.distribute_dofs(fe_precondition);
123+
124+
constraints.clear();
125+
std::map<unsigned int, double> boundary_values;
126+
VectorTools::interpolate_boundary_values(dof_handler_precondition,
127+
0,
128+
Functions::ZeroFunction<dim>(dim),
129+
constraints);
130+
constraints.close();
131+
132+
{
133+
DynamicSparsityPattern c_sparsity(dof_handler_precondition.n_dofs());
134+
DoFTools::make_sparsity_pattern(dof_handler_precondition,
135+
c_sparsity,
136+
constraints,
137+
false);
138+
prec_pattern.copy_from(c_sparsity);
139+
preconditioner_matrix.reinit(prec_pattern);
140+
}
141+
142+
system_rhs.reinit(dof_handler_precondition.n_dofs());
143+
}
144+
145+
146+
147+
template <int dim>
148+
void
149+
Step4<dim>::assemble_preconditioner()
150+
{
151+
QIterated<dim> quadrature_formula(QGauss<1>(2), 3);
152+
153+
const RightHandSide<dim> right_hand_side;
154+
155+
FEValues<dim> fe_values(fe_precondition,
156+
quadrature_formula,
157+
update_values | update_gradients |
158+
update_quadrature_points | update_JxW_values);
159+
160+
const unsigned int dofs_per_cell = fe_precondition.dofs_per_cell;
161+
const unsigned int n_q_points = quadrature_formula.size();
162+
163+
FullMatrix<double> cell_matrix(dofs_per_cell, dofs_per_cell);
164+
Vector<double> cell_rhs(dofs_per_cell);
165+
166+
std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
167+
168+
typename DoFHandler<dim>::active_cell_iterator
169+
cell = dof_handler_precondition.begin_active(),
170+
endc = dof_handler_precondition.end();
171+
172+
for (; cell != endc; ++cell)
173+
{
174+
fe_values.reinit(cell);
175+
cell_matrix = 0;
176+
cell_rhs = 0;
177+
178+
for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
179+
for (unsigned int i = 0; i < dofs_per_cell; ++i)
180+
{
181+
for (unsigned int j = 0; j < dofs_per_cell; ++j)
182+
cell_matrix(i, j) +=
183+
(fe_values.shape_grad(i, q_point) *
184+
fe_values.shape_grad(j, q_point) * fe_values.JxW(q_point));
185+
}
186+
187+
cell->get_dof_indices(local_dof_indices);
188+
constraints.distribute_local_to_global(cell_matrix,
189+
local_dof_indices,
190+
preconditioner_matrix);
191+
}
192+
preconditioner_matrix.compress(VectorOperation::add);
193+
}
194+
195+
196+
197+
template <int dim>
198+
void
199+
Step4<dim>::run()
200+
{
201+
deallog.push("dim " + std::to_string(dim));
202+
203+
make_grid();
204+
205+
setup_system();
206+
assemble_preconditioner();
207+
deallog << "nnz: " << preconditioner_matrix.n_nonzero_elements() << std::endl;
208+
209+
deallog.pop();
210+
}
211+
212+
213+
int
214+
main(int argc, char **argv)
215+
{
216+
initlog(true);
217+
218+
{
219+
Step4<1> test;
220+
test.run();
221+
}
222+
{
223+
Step4<2> test;
224+
test.run();
225+
}
226+
{
227+
Step4<3> test;
228+
test.run();
229+
}
230+
}

tests/fe/assemble_q_iso_q1_02.output

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
DEAL:dim 1::nnz: 11
3+
DEAL:dim 2::nnz: 228
4+
DEAL:dim 3::nnz: 3381

tests/fe/deformed_projection.h

+2-9
Original file line numberDiff line numberDiff line change
@@ -365,17 +365,10 @@ create_mass_matrix(const Mapping<dim> &mapping,
365365
}
366366
}
367367
}
368-
// transfer everything into the
369-
// global object. lock the
370-
// matrix meanwhile
368+
// transfer everything into the global object
371369
for (unsigned int i = 0; i < dofs_per_cell; ++i)
372370
for (unsigned int j = 0; j < dofs_per_cell; ++j)
373-
if ((n_components == 1) || (cell_matrix(i, j) != 0.0))
374-
/*
375-
(fe.system_to_component_index(i).first ==
376-
fe.system_to_component_index(j).first))
377-
*/
378-
matrix.add(dof_indices[i], dof_indices[j], cell_matrix(i, j));
371+
matrix.add(dof_indices[i], dof_indices[j], cell_matrix(i, j));
379372

380373
for (unsigned int i = 0; i < dofs_per_cell; ++i)
381374
rhs_vector(dof_indices[i]) += cell_vector(i);

tests/fe/local_sparsity_01.cc

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// ------------------------------------------------------------------------
2+
//
3+
// SPDX-License-Identifier: LGPL-2.1-or-later
4+
// Copyright (C) 2024 - 2025 by the deal.II authors
5+
//
6+
// This file is part of the deal.II library.
7+
//
8+
// Part of the source code is dual licensed under Apache-2.0 WITH
9+
// LLVM-exception OR LGPL-2.1-or-later. Detailed license information
10+
// governing the source code and code contributions can be found in
11+
// LICENSE.md and CONTRIBUTING.md at the top level directory of deal.II.
12+
//
13+
// ------------------------------------------------------------------------
14+
15+
// Check FiniteElement::get_local_dof_sparsity_pattern() for FESystem.
16+
17+
#include <deal.II/dofs/dof_handler.h>
18+
#include <deal.II/dofs/dof_tools.h>
19+
20+
#include "deal.II/fe/fe_system.h"
21+
#include <deal.II/fe/fe_dgq.h>
22+
#include <deal.II/fe/fe_q.h>
23+
#include <deal.II/fe/fe_q_iso_q1.h>
24+
#include <deal.II/fe/mapping_q1.h>
25+
26+
#include <deal.II/grid/grid_generator.h>
27+
#include <deal.II/grid/tria.h>
28+
29+
#include <deal.II/lac/dynamic_sparsity_pattern.h>
30+
#include <deal.II/lac/sparsity_pattern.h>
31+
32+
#include "../tests.h"
33+
34+
template <int dim>
35+
void
36+
test(const FiniteElement<dim> &fe, const unsigned int n_subdivisions = 1)
37+
{
38+
const auto &pattern = fe.get_local_dof_sparsity_pattern();
39+
deallog << fe.get_name() << ":\n";
40+
41+
if (!pattern.empty())
42+
{
43+
for (unsigned int i = 0; i < pattern.size(0); ++i)
44+
{
45+
for (unsigned int j = 0; j < pattern.size(1); ++j)
46+
deallog << (pattern(i, j) == true ? "X" : ".");
47+
deallog << "\n";
48+
}
49+
deallog << std::endl;
50+
}
51+
}
52+
53+
int
54+
main()
55+
{
56+
initlog();
57+
58+
// The simplest case is a single iso Q1:
59+
test<1>(FE_Q_iso_Q1<1>(2), 1);
60+
// The 2 iso Q1 elements couple using their pattern:
61+
test<1>(FESystem<1, 1>(FE_DGQ<1>(1), 1, FE_Q_iso_Q1<1>(2), 2), 1);
62+
// The coupling between the first two to the third copy is a full coupling
63+
// currently, because we don't detect this yet:
64+
test<1>(FESystem<1, 1>(FE_Q_iso_Q1<1>(2), 2, FE_Q_iso_Q1<1>(2), 1), 1);
65+
// Different iso_Q1 degrees always couple fully (off diagonal blocks):
66+
test<1>(FESystem<1, 1>(FE_Q_iso_Q1<1>(2), 1, FE_Q_iso_Q1<1>(3), 1), 1);
67+
}

0 commit comments

Comments
 (0)