libMesh
Loading...
Searching...
No Matches
slit_mesh_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/edge_edge2.h>
5#include <libmesh/face_quad4.h>
6#include <libmesh/cell_hex8.h>
7#include <libmesh/dof_map.h>
8#include <libmesh/linear_implicit_system.h>
9#include <libmesh/mesh_refinement.h>
10
11#include <libmesh/discontinuity_measure.h>
12#include <libmesh/error_vector.h>
13#include <libmesh/overlap_coupling.h>
14
15#include "test_comm.h"
16#include "libmesh_cppunit.h"
17
18
19using namespace libMesh;
20
21class SlitFunc : public FEMFunctionBase<Number>
22{
23public:
24
26
28
29 virtual void init_context (const FEMContext &) override {}
30
31 virtual std::unique_ptr<FEMFunctionBase<Number>>
32 clone () const override
33 {
34 return std::make_unique<SlitFunc>();
35 }
36
37 virtual Number operator() (const FEMContext & c,
38 const Point & p,
39 const Real /*time*/ = 0.) override
40 {
41 using std::abs;
42
43 const Real & x = p(0);
44 const Real & y = p(1);
45 const Point centroid = c.get_elem().vertex_average();
46 const Real sign = centroid(1)/std::abs(centroid(1));
47
48 // For testing we want something discontinuous on the slit,
49 // continuous everywhere else, and bilinear on all coarse quads
50 return (abs(x) + abs(2-x) - 2*abs(1-x)) * (1-abs(y)) * sign;
51 }
52
53 virtual void operator() (const FEMContext & c,
54 const Point & p,
55 const Real time,
56 DenseVector<Number> & output) override
57 {
58 for (unsigned int i=0; i != output.size(); ++i)
59 output(i) = (*this)(c, p, time);
60 }
61};
62
63
64
65
66
67
68
69class SlitMeshTest : public CppUnit::TestCase {
77public:
79
80#if LIBMESH_DIM > 1
82#endif
83
85
86protected:
87
88 std::unique_ptr<Mesh> _mesh;
89
91 {
92 _mesh = std::make_unique<Mesh>(*TestCommWorld);
93
94 // (-1,1) (0,1) (1,1) (2,1) (3,1)
95 // o----------o----------o----------o----------o
96 // | | | | |
97 // | | | | |
98 // | | | | |
99 // | | | | |
100 // o----------o==========8==========o----------o
101 // (-1,0) (0,0) (1,0) (2,0) (3,0)
102 // | | | | |
103 // | | | | |
104 // | | | | |
105 // o----------o----------o----------o----------o
106 // (-1,-1) (0,-1) (1,-1) (2,-1) (3,-1)
107
108 _mesh->set_mesh_dimension(2);
109
110 _mesh->add_point( Point(0.0, 0.0), 0 );
111 _mesh->add_point( Point(1.0, 0.0), 1 );
112 _mesh->add_point( Point(1.0, 1.0), 2 );
113 _mesh->add_point( Point(0.0, 1.0), 3 );
114 _mesh->add_point( Point(0.0,-1.0), 4 );
115 _mesh->add_point( Point(1.0,-1.0), 5 );
116 _mesh->add_point( Point(1.0, 0.0), 6 ); // Doubled!
117 _mesh->add_point( Point(2.0, 0.0), 7 );
118 _mesh->add_point( Point(2.0, 1.0), 8 );
119 _mesh->add_point( Point(2.0,-1.0), 9 );
120 _mesh->add_point( Point(-1.0,-1.0), 10);
121 _mesh->add_point( Point(-1.0, 0.0), 11);
122 _mesh->add_point( Point(-1.0, 1.0), 12);
123 _mesh->add_point( Point(3.0,-1.0), 13);
124 _mesh->add_point( Point(3.0, 0.0), 14);
125 _mesh->add_point( Point(3.0, 1.0), 15);
126
127 {
128 Elem * elem_top_left = _mesh->add_elem(Elem::build_with_id(QUAD4, 0));
129 elem_top_left->set_node(0, _mesh->node_ptr(0));
130 elem_top_left->set_node(1, _mesh->node_ptr(1));
131 elem_top_left->set_node(2, _mesh->node_ptr(2));
132 elem_top_left->set_node(3, _mesh->node_ptr(3));
133
134 Elem * elem_bottom_left = _mesh->add_elem(Elem::build_with_id(QUAD4, 1));
135 elem_bottom_left->set_node(0, _mesh->node_ptr(4));
136 elem_bottom_left->set_node(1, _mesh->node_ptr(5));
137 elem_bottom_left->set_node(2, _mesh->node_ptr(6));
138 elem_bottom_left->set_node(3, _mesh->node_ptr(0));
139
140 Elem * elem_top_right = _mesh->add_elem(Elem::build_with_id(QUAD4, 2));
141 elem_top_right->set_node(0, _mesh->node_ptr(1));
142 elem_top_right->set_node(1, _mesh->node_ptr(7));
143 elem_top_right->set_node(2, _mesh->node_ptr(8));
144 elem_top_right->set_node(3, _mesh->node_ptr(2));
145
146 Elem * elem_bottom_right = _mesh->add_elem(Elem::build_with_id(QUAD4, 3));
147 elem_bottom_right->set_node(0, _mesh->node_ptr(5));
148 elem_bottom_right->set_node(1, _mesh->node_ptr(9));
149 elem_bottom_right->set_node(2, _mesh->node_ptr(7));
150 elem_bottom_right->set_node(3, _mesh->node_ptr(6));
151
152 Elem * elem_top_leftleft = _mesh->add_elem(Elem::build_with_id(QUAD4, 4));
153 elem_top_leftleft->set_node(0, _mesh->node_ptr(11));
154 elem_top_leftleft->set_node(1, _mesh->node_ptr(0));
155 elem_top_leftleft->set_node(2, _mesh->node_ptr(3));
156 elem_top_leftleft->set_node(3, _mesh->node_ptr(12));
157
158 Elem * elem_bottom_leftleft = _mesh->add_elem(Elem::build_with_id(QUAD4, 5));
159 elem_bottom_leftleft->set_node(0, _mesh->node_ptr(10));
160 elem_bottom_leftleft->set_node(1, _mesh->node_ptr(4));
161 elem_bottom_leftleft->set_node(2, _mesh->node_ptr(0));
162 elem_bottom_leftleft->set_node(3, _mesh->node_ptr(11));
163
164 Elem * elem_top_rightright = _mesh->add_elem(Elem::build_with_id(QUAD4, 6));
165 elem_top_rightright->set_node(0, _mesh->node_ptr(7));
166 elem_top_rightright->set_node(1, _mesh->node_ptr(14));
167 elem_top_rightright->set_node(2, _mesh->node_ptr(15));
168 elem_top_rightright->set_node(3, _mesh->node_ptr(8));
169
170 Elem * elem_bottom_rightright = _mesh->add_elem(Elem::build_with_id(QUAD4, 7));
171 elem_bottom_rightright->set_node(0, _mesh->node_ptr(9));
172 elem_bottom_rightright->set_node(1, _mesh->node_ptr(13));
173 elem_bottom_rightright->set_node(2, _mesh->node_ptr(14));
174 elem_bottom_rightright->set_node(3, _mesh->node_ptr(7));
175 }
176
177 // libMesh shouldn't renumber, or our based-on-initial-id
178 // assertions later may fail.
179 _mesh->allow_renumbering(false);
180
181 _mesh->prepare_for_use();
182 }
183
184public:
185 void setUp()
186 {
187#if LIBMESH_DIM > 1
188 this->build_mesh();
189#endif
190 }
191
192 void tearDown() {}
193
194 void testMesh()
195 {
196 LOG_UNIT_TEST;
197
198 // There'd better be 8 elements
199 CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(8), _mesh->n_elem());
200
201 // There'd better still be a full 16 nodes
202 CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(16), _mesh->n_nodes());
203
204 /* The middle nodes should still be distinct between the top and
205 * bottom elements */
206 if (_mesh->query_elem_ptr(0) && _mesh->query_elem_ptr(1))
207 CPPUNIT_ASSERT( _mesh->elem_ref(0).node_id(1) != _mesh->elem_ref(1).node_id(2) );
208 if (_mesh->query_elem_ptr(2) && _mesh->query_elem_ptr(3))
209 CPPUNIT_ASSERT( _mesh->elem_ref(2).node_id(0) != _mesh->elem_ref(3).node_id(3) );
210
211 /* The middle nodes should still be shared between left and right
212 * elements on top and bottom */
213 if (_mesh->query_elem_ptr(0) && _mesh->query_elem_ptr(2))
214 CPPUNIT_ASSERT_EQUAL( _mesh->elem_ref(0).node_id(1),
215 _mesh->elem_ref(2).node_id(0) );
216 if (_mesh->query_elem_ptr(1) && _mesh->query_elem_ptr(3))
217 CPPUNIT_ASSERT_EQUAL( _mesh->elem_ref(1).node_id(2),
218 _mesh->elem_ref(3).node_id(3) );
219 }
220
221};
222
230public:
232
233#if LIBMESH_DIM > 1
235#endif
236
238
239 // Yes, this is necessary. Somewhere in those macros is a protected/private
240public:
241
242 void setUp()
243 {
244#if LIBMESH_DIM > 1
245 this->build_mesh();
246
247#ifdef LIBMESH_ENABLE_AMR
249#endif
250#endif
251 }
252
253 void testMesh()
254 {
255 LOG_UNIT_TEST;
256
257#ifdef LIBMESH_ENABLE_AMR
258 // We should have 40 total and 32 active elements.
259 CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(40), _mesh->n_elem());
260 CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(32), _mesh->n_active_elem());
261
262 // We should have 48 nodes, not 45 or 46
263 CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(48), _mesh->n_nodes());
264#endif
265 }
266};
267
274public:
276
277#if LIBMESH_DIM > 1
279
281
282#ifdef LIBMESH_ENABLE_UNIQUE_ID
284#endif
285#endif
286
288
289protected:
290
292 std::unique_ptr<EquationSystems> _es;
293
294public:
295
296 void setUp()
297 {
298#if LIBMESH_DIM > 1
299 this->build_mesh();
300
301 // libMesh *should* renumber now, or a DistributedMesh might not
302 // have contiguous ids, which is a requirement to write xda files.
303 _mesh->allow_renumbering(true);
304
305 _es = std::make_unique<EquationSystems>(*_mesh);
306 _sys = &_es->add_system<System> ("SimpleSystem");
307 _sys->add_variable("u", FIRST);
308
309 // We're going to be integrating across the slit in the mesh, so
310 // let's make sure we can *see* elements and data across the slit.
311 _mesh->allgather();
313 (std::make_shared<OverlapCoupling>());
314 _mesh->delete_remote_elements();
315
316 _es->init();
317 SlitFunc slitfunc;
318 _sys->project_solution(&slitfunc);
319
320#ifdef LIBMESH_ENABLE_AMR
322 _es->reinit();
324 _es->reinit();
325#endif
326#endif
327 }
328
329 void tearDown() {}
330
331 void testMesh()
332 {
333 LOG_UNIT_TEST;
334
335#ifdef LIBMESH_ENABLE_AMR
336 // We should have 168 total and 128 active elements.
337 CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(8+32+128), _mesh->n_elem());
338 CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(128), _mesh->n_active_elem());
339
340 // We should have 160 nodes
341 CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(160), _mesh->n_nodes());
342#endif
343 }
344
346 {
347 LOG_UNIT_TEST;
348
349 SlitFunc slitfunc;
350
351 unsigned int dim = 2;
352
353 CPPUNIT_ASSERT_EQUAL( _sys->n_vars(), 1u );
354
355 FEMContext context(*_sys);
356 FEBase * fe = NULL;
357 context.get_element_fe( 0, fe, dim );
358 const std::vector<Point> & xyz = fe->get_xyz();
359 fe->get_phi();
360
361 for (const auto & elem : _mesh->active_local_element_ptr_range())
362 {
363 context.pre_fe_reinit(*_sys, elem);
364 context.elem_fe_reinit();
365
366 const unsigned int n_qp = xyz.size();
367
368 for (unsigned int qp=0; qp != n_qp; ++qp)
369 {
370 const Number exact_val = slitfunc(context, xyz[qp]);
371
372 const Number discrete_val = context.interior_value(0, qp);
373
374 LIBMESH_ASSERT_NUMBERS_EQUAL
375 (exact_val, discrete_val, TOLERANCE*TOLERANCE);
376 }
377 }
378
379 // We should have no discontinuities (beyond floating-point error)
380 // between topologically connected elements
381 DiscontinuityMeasure connected_dm;
382 ErrorVector connected_err;
383 connected_dm.estimate_error(*_sys, connected_err);
384 const Real mean_connected_disc = connected_err.mean();
385 CPPUNIT_ASSERT_LESS(Real(1e-14), mean_connected_disc);
386
387 // We should be able to see the discontinuity along the slit
388 DiscontinuityMeasure slit_dm;
389 slit_dm.integrate_slits = true;
390 ErrorVector slit_disc;
391 slit_dm.estimate_error(*_sys, slit_disc);
392 const Real mean_slit_disc = slit_disc.mean();
393 CPPUNIT_ASSERT_GREATER(Real(1e-3), mean_slit_disc);
394 }
395
397 {
398 LOG_UNIT_TEST;
399
400 SlitFunc slitfunc;
401
402 _mesh->write("slit_mesh.xda");
403 _es->write("slit_solution.xda",
406
407 Mesh mesh2(*TestCommWorld);
408 mesh2.read("slit_mesh.xda");
409 EquationSystems es2(mesh2);
410 es2.read("slit_solution.xda");
411
412 System & sys2 = es2.get_system<System> ("SimpleSystem");
413
414 unsigned int dim = 2;
415
416 CPPUNIT_ASSERT_EQUAL( sys2.n_vars(), 1u );
417
418 FEMContext context(sys2);
419 FEBase * fe = NULL;
420 context.get_element_fe( 0, fe, dim );
421 const std::vector<Point> & xyz = fe->get_xyz();
422 fe->get_phi();
423
424 // While we're in the middle of a unique id based test case, let's
425 // make sure our unique ids were all read in correctly too.
426 std::unique_ptr<PointLocatorBase> locator = _mesh->sub_point_locator();
427
428 if (!_mesh->is_serial())
429 locator->enable_out_of_mesh_mode();
430
431 for (const auto & elem : mesh2.active_local_element_ptr_range())
432 {
433 const Elem * mesh1_elem = (*locator)(elem->vertex_average());
434 if (mesh1_elem)
435 {
436 CPPUNIT_ASSERT_EQUAL( elem->unique_id(),
437 mesh1_elem->unique_id() );
438
439 for (unsigned int n=0; n != elem->n_nodes(); ++n)
440 {
441 const Node & node = elem->node_ref(n);
442 const Node & mesh1_node = mesh1_elem->node_ref(n);
443 CPPUNIT_ASSERT_EQUAL( node.unique_id(),
444 mesh1_node.unique_id() );
445 }
446 }
447
448 context.pre_fe_reinit(sys2, elem);
449 context.elem_fe_reinit();
450
451 const unsigned int n_qp = xyz.size();
452
453 for (unsigned int qp=0; qp != n_qp; ++qp)
454 {
455 const Number exact_val = slitfunc(context, xyz[qp]);
456
457 const Number discrete_val = context.interior_value(0, qp);
458
459 LIBMESH_ASSERT_NUMBERS_EQUAL
460 (exact_val, discrete_val, TOLERANCE*TOLERANCE);
461 }
462 }
463 }
464};
465
unsigned int dim
virtual void init_context(const FEMContext &) override
Prepares a context object for use.
virtual std::unique_ptr< FEMFunctionBase< Number > > clone() const override
virtual Number operator()(const FEMContext &c, const Point &p, const Real=0.) override
LIBMESH_CPPUNIT_TEST_SUITE(SlitMeshRefinedMeshTest)
The goal of this test is the same as the previous, but now we do a uniform refinement and make sure t...
std::unique_ptr< EquationSystems > _es
LIBMESH_CPPUNIT_TEST_SUITE(SlitMeshRefinedSystemTest)
The goal of this test is the same as the previous, but now we create a system and set dof values to m...
CPPUNIT_TEST_SUITE_END()
std::unique_ptr< Mesh > _mesh
LIBMESH_CPPUNIT_TEST_SUITE(SlitMeshTest)
The goal of this test is to ensure that a 2D mesh with nodes overlapping on opposite sides of an inte...
CPPUNIT_TEST(testMesh)
Defines a dense vector for use in Finite Element-type computations.
virtual unsigned int size() const override final
This class measures discontinuities between elements for debugging purposes.
void add_algebraic_ghosting_functor(GhostingFunctor &evaluable_functor, bool to_mesh=true)
Adds a functor which can specify algebraic ghosting requirements for use with distributed vectors.
Definition dof_map.C:2062
unique_id_type unique_id() const
Definition dof_object.h:835
This is the base class from which all geometric element types are derived.
Definition elem.h:96
virtual Node *& set_node(const unsigned int i)
Definition elem.h:2567
const Node & node_ref(const unsigned int i) const
Definition elem.h:2538
static std::unique_ptr< Elem > build_with_id(const ElemType type, dof_id_type id)
Calls the build() method above with a nullptr parent, and additionally sets the newly-created Elem's ...
Definition elem.C:556
Point vertex_average() const
Definition elem.C:669
This is the EquationSystems class.
void read(std::string_view name, const XdrMODE, const unsigned int read_flags=(READ_HEADER|READ_DATA), bool partition_agnostic=true)
Read & initialize the systems from disk using the XDR data format.
const T_sys & get_system(std::string_view name) const
The ErrorVector is a specialization of the StatisticsVector for error data computed on a finite eleme...
virtual Real mean() const override
virtual_for_inffe const std::vector< Point > & get_xyz() const
This class forms the foundation from which generic finite elements may be derived.
Definition fe_base.h:86
const std::vector< std::vector< OutputShape > > & get_phi() const
Definition fe_base.h:207
This class provides all data required for a physics package (e.g.
Definition fem_context.h:63
virtual void pre_fe_reinit(const System &, const Elem *e)
Reinitializes local data vectors/matrices on the current geometric element.
Number interior_value(unsigned int var, unsigned int qp) const
const Elem & get_elem() const
Accessor for current Elem object.
virtual void elem_fe_reinit(const std::vector< Point > *const pts=nullptr)
Reinitializes interior FE objects on the current geometric element.
void get_element_fe(unsigned int var, FEGenericBase< OutputShape > *&fe) const
Accessor for interior finite element object for variable var for the largest dimension in the mesh.
FEMFunctionBase is a base class from which users can derive in order to define "function-like" object...
bool integrate_slits
A boolean flag, by default false, to be set to true if integrations should be performed on "slits" wh...
virtual void estimate_error(const System &system, ErrorVector &error_per_cell, const NumericVector< Number > *solution_vector=nullptr, bool estimate_parent_error=false) override
This function uses the derived class's jump error estimate formula to estimate the error on each cell...
Implements (adaptive) mesh refinement algorithms for a MeshBase.
void uniformly_refine(unsigned int n=1)
Uniformly refines the mesh n times.
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition mesh.h:51
A Node is like a Point, but with more information.
Definition node.h:55
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition system.h:100
void project_solution(FunctionBase< Number > *f, FunctionBase< Gradient > *g=nullptr, std::optional< ConstElemRange > active_local_range=std::nullopt, std::optional< std::vector< unsigned int > > variable_numbers=std::nullopt) const
Projects arbitrary functions onto the current solution.
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 n_vars() const
Definition system.C:2674
const DofMap & get_dof_map() const
Definition system.h:2417
virtual void read(const std::string &name, void *mesh_data=nullptr, bool skip_renumber_nodes_and_elements=false, bool skip_find_neighbors=false, bool skip_detect_interior_parents=false) override
Reads the file specified by name.
Communicator * TestCommWorld
The libMesh namespace provides an interface to certain functionality in the library.
static constexpr Real TOLERANCE
uint8_t dof_id_type
Definition id_types.h:67
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
CPPUNIT_TEST_SUITE_REGISTRATION(SlitMeshTest)