libMesh
Loading...
Searching...
No Matches
dof_map_test.C
Go to the documentation of this file.
1#include <libmesh/equation_systems.h>
2#include <libmesh/mesh.h>
3#include <libmesh/mesh_generation.h>
4#include <libmesh/elem.h>
5#include <libmesh/dof_map.h>
6#include <libmesh/partitioner.h>
7#include <libmesh/replicated_mesh.h>
8
10
11#include "test_comm.h"
12#include "libmesh_cppunit.h"
13
14#include <regex>
15#include <string>
16
17using namespace libMesh;
18
19#ifdef LIBMESH_ENABLE_CONSTRAINTS
20// This class is used by testConstraintLoopDetection
22{
23private:
24
26
27public:
28
29 MyConstraint( System & sys ) : Constraint(), _sys(sys) {}
30
31 virtual ~MyConstraint() {}
32
33 void constrain()
34 {
35 {
36 const dof_id_type constrained_dof_index = 0;
37 DofConstraintRow constraint_row;
38 constraint_row[1] = 1.0;
39 _sys.get_dof_map().add_constraint_row( constrained_dof_index, constraint_row, 0., true);
40 }
41 {
42 const dof_id_type constrained_dof_index = 1;
43 DofConstraintRow constraint_row;
44 constraint_row[0] = 1.0;
45 _sys.get_dof_map().add_constraint_row( constrained_dof_index, constraint_row, 0., true);
46 }
47 }
48};
49
51{
52private:
53
55
56public:
57
58 GhostDofConstraint(System & sys) : Constraint(), _sys(sys) {}
59
60 void constrain() override
61 {
62 const unsigned int sys_num = _sys.number();
63
64 // Constrain the interface DOF on node 3 by the distant DOF on node 0.
65 // On the processor owning element 3, both DOFs are nonlocal, but only
66 // node 3 is directly needed by the local element.
67 const dof_id_type constrained_dof =
68 _sys.get_mesh().node_ref(3).dof_number(sys_num, 0, 0);
69 const dof_id_type dependency_dof =
70 _sys.get_mesh().node_ref(0).dof_number(sys_num, 0, 0);
71
72 // This represents constrained_dof = dependency_dof.
73 DofConstraintRow constraint_row;
74 constraint_row[dependency_dof] = 1.;
75 _sys.get_dof_map().add_constraint_row(constrained_dof, constraint_row);
76 }
77};
78
80{
81public:
82
83 std::unique_ptr<Partitioner> clone() const override
84 {
85 return std::make_unique<GhostDofConstraintPartitioner>(*this);
86 }
87
88 virtual bool partitions_from_scratch () const override { return true; }
89
90protected:
91
92 void _do_partition(MeshBase & mesh, const unsigned int n) override
93 {
94 // Keep the first three elements on processor 0 and move the final
95 // element to another processor, creating a processor interface at node 3.
96 for (auto & elem : mesh.active_element_ptr_range())
97 elem->processor_id() = 0;
98
99 if (n > 1)
100 mesh.elem_ref(3).processor_id() = n - 1;
101 }
102};
103#endif
104
105
106class DofMapTest : public CppUnit::TestCase {
107public:
109
111#if LIBMESH_DIM > 1
114#endif
115#if LIBMESH_DIM > 2
117#endif
118
119#if defined(LIBMESH_ENABLE_EXCEPTIONS)
121#endif
122
123#if defined(LIBMESH_ENABLE_CONSTRAINTS) && defined(LIBMESH_ENABLE_EXCEPTIONS) && LIBMESH_DIM > 1
125#endif
126
127#if defined(LIBMESH_ENABLE_CONSTRAINTS)
129#endif
130
132
134
135private:
136
137public:
138 void setUp()
139 {}
140
141 void tearDown()
142 {}
143
144 void testDofOwner(const ElemType elem_type)
145 {
147
149 System &sys = es.add_system<System> ("SimpleSystem");
150 sys.add_variable("u", THIRD, HIERARCHIC);
151
152 const unsigned int n_elem_per_side = 3;
153 const std::unique_ptr<Elem> test_elem = Elem::build(elem_type);
154 const unsigned int ymax = test_elem->dim() > 1;
155 const unsigned int zmax = test_elem->dim() > 2;
156 const unsigned int ny = ymax * n_elem_per_side;
157 const unsigned int nz = zmax * n_elem_per_side;
158
160 n_elem_per_side,
161 ny,
162 nz,
163 0., 1.,
164 0., ymax,
165 0., zmax,
166 elem_type);
167
168 es.init();
169
170 DofMap & dof_map = sys.get_dof_map();
171 for (dof_id_type id = 0; id != dof_map.n_dofs(); ++id)
172 {
173 const processor_id_type pid = dof_map.dof_owner(id);
174 CPPUNIT_ASSERT(dof_map.first_dof(pid) <= id);
175 CPPUNIT_ASSERT(id < dof_map.end_dof(pid));
176 }
177 }
178
179
180
181 void testDofOwnerOnEdge3() { LOG_UNIT_TEST; testDofOwner(EDGE3); }
182 void testDofOwnerOnQuad9() { LOG_UNIT_TEST; testDofOwner(QUAD9); }
183 void testDofOwnerOnTri6() { LOG_UNIT_TEST; testDofOwner(TRI6); }
184 void testDofOwnerOnHex27() { LOG_UNIT_TEST; testDofOwner(HEX27); }
185
186#if defined(LIBMESH_ENABLE_EXCEPTIONS)
188 {
189 LOG_UNIT_TEST;
190
192
194 System & sys = es.add_system<System> ("SimpleSystem");
195 sys.add_variable("u", SECOND);
196
197 MeshTools::Generation::build_square (mesh,4,4,-1., 1.,-1., 1., QUAD4);
198
199 // We need at least one element per processor to make sure
200 // everyone throws and we don't get out of sync before going on to
201 // future tests.
202 dof_id_type min_local_elem = mesh.n_local_elem();
203 mesh.comm().min(min_local_elem);
204
205 if (!min_local_elem)
206 return;
207
208 // We can't just CPPUNIT_ASSERT_THROW, because we want to make
209 // sure we were thrown from the right place with the right error
210 // message!
211 bool threw_desired_exception = false;
212 try {
213 es.init();
214 }
215 catch (libMesh::LogicError & e) {
216 std::regex msg_regex("only supports FEInterface::max_order");
217 CPPUNIT_ASSERT(std::regex_search(e.what(), msg_regex));
218 threw_desired_exception = true;
219 }
220 catch (...) {
221 CPPUNIT_ASSERT_MESSAGE("Unexpected exception type thrown", false);
222 }
223
224 // If we have more than 4*4 processors, or a poor partitioner, we
225 // might not get an exception on every processor
226 mesh.comm().max(threw_desired_exception);
227
228 CPPUNIT_ASSERT(threw_desired_exception);
229 }
230#endif
231
232#if defined(LIBMESH_ENABLE_CONSTRAINTS) && defined(LIBMESH_ENABLE_EXCEPTIONS)
234 {
235 LOG_UNIT_TEST;
237
239 System & sys = es.add_system<System> ("SimpleSystem");
240 sys.add_variable("u", FIRST);
241
242 MyConstraint my_constraint(sys);
243 sys.attach_constraint_object(my_constraint);
244
245 MeshTools::Generation::build_square (mesh,4,4,-1., 1.,-1., 1., QUAD4);
246
247 // Tell the dof_map to check for constraint loops
248 DofMap & dof_map = sys.get_dof_map();
249 dof_map.set_error_on_constraint_loop(true);
250
251 CPPUNIT_ASSERT_THROW_MESSAGE("Constraint loop not detected", es.init(), libMesh::LogicError);
252 }
253#endif
254
255#if defined(LIBMESH_ENABLE_CONSTRAINTS)
257 {
258 LOG_UNIT_TEST;
259
260 if (TestCommWorld->size() == 1)
261 return;
262
264 mesh.partitioner() = std::make_unique<GhostDofConstraintPartitioner>();
265
266 // Build this four-element line:
267 //
268 // node: 0 ----- 1 ----- 2 ----- 3 ----- 4
269 // elem: 0 1 2 3
270 // owner: 0 0 0 remote
271 //
272 // Node 3 remains owned by processor 0, so its DOF is ghosted on the
273 // processor owning element 3. Node 0 is too distant to be ghosted by
274 // the ordinary element-neighbor send-list construction.
276 const processor_id_type ghost_dof_processor = mesh.elem_ref(3).processor_id();
277 mesh.node_ref(3).processor_id() = 0;
278
280 // The default route should not ghost dependencies of constrained ghost DOFs.
281 System & default_sys = es.add_system<System>("DefaultSystem");
282 default_sys.add_variable("u", FIRST);
283 // The enabled route should add those dependencies to the send list.
284 System & ghost_sys = es.add_system<System>("GhostSystem");
285 ghost_sys.add_variable("u", FIRST);
286
287 GhostDofConstraint default_constraint(default_sys);
288 default_sys.attach_constraint_object(default_constraint);
289 GhostDofConstraint ghost_constraint(ghost_sys);
290 ghost_sys.attach_constraint_object(ghost_constraint);
291
292 // Enable the new behavior only for GhostSystem so DefaultSystem provides
293 // a control route using the original send-list behavior.
294 DofMap & ghost_dof_map = ghost_sys.get_dof_map();
295 ghost_dof_map.ghost_constraints_needed();
296 es.init();
297
298 // Only the processor owning element 3 has the constrained DOF as a ghost
299 // supported by one of its local elements.
300 if (mesh.processor_id() == ghost_dof_processor)
301 {
302 const Elem & local_elem = mesh.elem_ref(3);
303 CPPUNIT_ASSERT_EQUAL(mesh.processor_id(), local_elem.processor_id());
304
305 auto verify_send_list = [&local_elem](System & sys, const bool dependency_is_ghosted)
306 {
307 DofMap & dof_map = sys.get_dof_map();
308 const dof_id_type ghost_constrained_dof =
309 sys.get_mesh().node_ref(3).dof_number(sys.number(), 0, 0);
310 const dof_id_type dependency_dof =
311 sys.get_mesh().node_ref(0).dof_number(sys.number(), 0, 0);
312
313 CPPUNIT_ASSERT(!dof_map.local_index(ghost_constrained_dof));
314 CPPUNIT_ASSERT(!dof_map.local_index(dependency_dof));
315
316 // Verify that the nonlocal constrained DOF is supported by this
317 // processor's local element, making it a constrained ghost DOF.
318 std::vector<dof_id_type> local_dofs;
319 dof_map.dof_indices(&local_elem, local_dofs);
320 CPPUNIT_ASSERT(std::find(local_dofs.begin(), local_dofs.end(), ghost_constrained_dof) !=
321 local_dofs.end());
322
323 const auto & send_list = dof_map.get_send_list();
324 // The constrained DOF is always ghosted because the local element
325 // uses it; its distant dependency is ghosted only when requested.
326 CPPUNIT_ASSERT(std::binary_search(send_list.begin(), send_list.end(),
327 ghost_constrained_dof));
328 // This assertion is false for the control route and true for the
329 // route with ghost constraint dependency expansion enabled.
330 CPPUNIT_ASSERT_EQUAL(dependency_is_ghosted,
331 std::binary_search(send_list.begin(), send_list.end(),
332 dependency_dof));
333 };
334
335 verify_send_list(default_sys, false);
336 verify_send_list(ghost_sys, true);
337 }
338 }
339#endif
340
342 {
345 auto & sys = es.add_system<System>("SimpleSystem");
346 const auto last_var = sys.add_variable_array({"u0", "u1"}, fe_type);
347 MeshTools::Generation::build_square (mesh,1,1,-1., 1.,-1., 1., QUAD9);
348 es.init();
349 auto & dof_map = sys.get_dof_map();
350 const auto * const elem = mesh.query_elem_ptr(0);
351 if (elem)
352 {
353 std::vector<dof_id_type> array_dofs, work_dofs, dofs;
354 dof_map.array_dof_indices(elem, array_dofs, last_var);
355 // Make sure there are no duplicates
356 std::sort(array_dofs.begin(), array_dofs.end());
357 auto it = std::unique(array_dofs.begin(), array_dofs.end());
358 array_dofs.erase(it, array_dofs.end());
359 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(dof_map.n_dofs()), array_dofs.size());
360 dof_map.dof_indices(elem, work_dofs, last_var);
361 dofs = work_dofs;
362 dof_map.dof_indices(elem, work_dofs, last_var - 1);
363 dofs.insert(dofs.end(), work_dofs.begin(), work_dofs.end());
364 std::sort(dofs.begin(), dofs.end());
365 CPPUNIT_ASSERT(array_dofs == dofs);
366 }
367 }
368
370 {
371 LOG_UNIT_TEST;
372 for (int i = 1; i < 3; ++i)
373 {
378 }
379 }
380
381};
382
CPPUNIT_TEST(testDofOwnerOnQuad9)
void testConstraintLoopDetection()
void testGhostConstraintSendList()
CPPUNIT_TEST(testBadElemFECombo)
CPPUNIT_TEST(testConstraintLoopDetection)
CPPUNIT_TEST(testGhostConstraintSendList)
CPPUNIT_TEST(testDofOwnerOnEdge3)
LIBMESH_CPPUNIT_TEST_SUITE(DofMapTest)
CPPUNIT_TEST_SUITE_END()
CPPUNIT_TEST(testDofOwnerOnHex27)
void testBadElemFECombo()
void testDofOwnerOnTri6()
void testArrayDofIndicesWithType(const FEType &fe_type)
CPPUNIT_TEST(testDofOwnerOnTri6)
void testDofOwner(const ElemType elem_type)
CPPUNIT_TEST(testArrayDofIndices)
void testArrayDofIndices()
void tearDown()
void testDofOwnerOnEdge3()
void testDofOwnerOnHex27()
void testDofOwnerOnQuad9()
virtual bool partitions_from_scratch() const override
void _do_partition(MeshBase &mesh, const unsigned int n) override
This is the actual partitioning method which must be overridden in derived classes.
std::unique_ptr< Partitioner > clone() const override
GhostDofConstraint(System &sys)
void constrain() override
System & _sys
MyConstraint(System &sys)
virtual ~MyConstraint()
void constrain()
void max(const T &r, T &o, Request &req) const
void min(const T &r, T &o, Request &req) const
dof_id_type first_dof(const processor_id_type proc) const
dof_id_type end_dof(const processor_id_type proc) const
This class handles the numbering of degrees of freedom on a mesh.
Definition dof_map.h:181
processor_id_type dof_owner(const dof_id_type dof) const
Definition dof_map.h:822
const std::vector< dof_id_type > & get_send_list() const
Definition dof_map.h:540
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
Definition dof_map.C:2203
bool local_index(dof_id_type dof_index) const
Definition dof_map.h:974
void ghost_constraints_needed()
Sets _need_ghost_constraints to true regardless of the requirements by static condensation.
Definition dof_map.h:2573
dof_id_type n_dofs(const unsigned int vn) const
Definition dof_map.h:783
void add_constraint_row(const dof_id_type dof_number, const DofConstraintRow &constraint_row, const Number constraint_rhs, const bool forbid_constraint_overwrite)
Adds a copy of the user-defined row to the constraint matrix, using an inhomogeneous right-hand-side ...
void set_error_on_constraint_loop(bool error_on_constraint_loop)
Definition dof_map.C:235
dof_id_type dof_number(const unsigned int s, const unsigned int var, const unsigned int comp) const
processor_id_type processor_id() const
Definition dof_object.h:895
This is the base class from which all geometric element types are derived.
Definition elem.h:96
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition elem.C:382
This is the EquationSystems class.
virtual void init()
Initialize all the systems.
virtual System & add_system(std::string_view system_type, std::string_view name)
Add the system of type system_type named name to the systems array.
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition fe_type.h:197
A class to represent the internal "this should never happen" errors, to be thrown by "libmesh_error()...
This is the MeshBase class.
Definition mesh_base.h:81
virtual const Node & node_ref(const dof_id_type i) const
Definition mesh_base.h:742
virtual std::unique_ptr< Partitioner > & partitioner()
A partitioner to use at each partitioning.
Definition mesh_base.h:165
dof_id_type n_local_elem() const
Definition mesh_base.h:694
virtual const Elem * query_elem_ptr(const dof_id_type i) const =0
virtual const Elem & elem_ref(const dof_id_type i) const
Definition mesh_base.h:785
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition mesh.h:51
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
The Partitioner class provides a uniform interface for partitioning algorithms.
Definition partitioner.h:52
The ReplicatedMesh class is derived from the MeshBase class, and is used to store identical copies of...
Abstract base class to be used for system constraints.
Definition system.h:182
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition system.h:100
unsigned int add_variable(std::string_view var, const FEType &type, const std::set< subdomain_id_type > *const active_subdomains=nullptr)
Adds the variable var to the list of variables for this system.
Definition system.C:1344
unsigned int add_variable_array(const std::vector< std::string > &vars, const FEType &type, const std::set< subdomain_id_type > *const active_subdomains=nullptr)
Adds variables vars to the list of variables for this system.
Definition system.C:1386
void attach_constraint_object(Constraint &constrain)
Register a user object for imposing constraints.
Definition system.C:2007
const DofMap & get_dof_map() const
Definition system.h:2417
unsigned int number() const
Definition system.h:2393
const MeshBase & get_mesh() const
Definition system.h:2401
Communicator * TestCommWorld
CPPUNIT_TEST_SUITE_REGISTRATION(DofMapTest)
MeshBase & mesh
void build_square(UnstructuredMesh &mesh, const unsigned int nx, const unsigned int ny, const Real xmin=0., const Real xmax=1., const Real ymin=0., const Real ymax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
A specialized build_cube() for 2D meshes.
void build_line(UnstructuredMesh &mesh, const unsigned int nx, const Real xmin=0., const Real xmax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
A specialized build_cube() for 1D meshes.
void build_cube(UnstructuredMesh &mesh, const unsigned int nx=0, const unsigned int ny=0, const unsigned int nz=0, const Real xmin=0., const Real xmax=1., const Real ymin=0., const Real ymax=1., const Real zmin=0., const Real zmax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
Builds a (elements) cube.
The libMesh namespace provides an interface to certain functionality in the library.
ElemType
Defines an enum for geometric element types.
std::map< dof_id_type, Real, std::less< dof_id_type >, Threads::scalable_allocator< std::pair< const dof_id_type, Real > > > DofConstraintRow
A row of the Dof constraint matrix.
Definition dof_map.h:100
uint8_t dof_id_type
Definition id_types.h:67
uint8_t processor_id_type
Definition id_types.h:104