libMesh
Loading...
Searching...
No Matches
overlapping_coupling_test.C
Go to the documentation of this file.
1#include <libmesh/equation_systems.h>
2#include <libmesh/replicated_mesh.h>
3#include <libmesh/numeric_vector.h>
4#include <libmesh/dof_map.h>
5#include <libmesh/elem.h>
6#include <libmesh/face_quad4.h>
7#include <libmesh/ghosting_functor.h>
8#include <libmesh/coupling_matrix.h>
9#include <libmesh/quadrature_gauss.h>
10#include <libmesh/linear_implicit_system.h>
11#include <libmesh/mesh_refinement.h>
12#include <libmesh/mesh_modification.h>
13#include <libmesh/partitioner.h>
14#include <libmesh/sparse_matrix.h>
15
16#include "test_comm.h"
17#include "libmesh_cppunit.h"
18
19#include <memory>
20
21using namespace libMesh;
22
23
24// This coupling functor is attempting to mimic in a basic way
25// a particular use case. The idea is that the mesh has overlapping domains
26// (we assume only two in this test) and that the elements are not
27// topologically connected between the overlapping domains, but when the user
28// solves their problem, some dofs on are coupled between the overlapping
29// elements. So we want to exercise the GhostingFunctor for this scenario,
30// in particular both algebraic ghosting (dof ghosting) and coupling
31// ghosting (sparsity pattern). In the use case, the coupling happens at
32// quadrature points, so we look for the overlap based on the quadrature points.
33//
34// Additionally, the use case also has an issue that the coupling functor
35// cannot be added until after the System is initialized because the overlap
36// depends on the solution. So we will exercise that case as well.
38{
39 public:
40
43 _system(system),
44 _mesh(system.get_mesh()),
45 _point_locator(system.get_mesh().sub_point_locator()),
47 {
48 // This is a highly specalized coupling functor where we are assuming
49 // only two subdomains that are overlapping so make sure there's only two.
50 CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(_mesh.n_subdomains()));
51
52 // We're assuming a specific set of subdomain ids
53 std::set<subdomain_id_type> ids;
55 CPPUNIT_ASSERT( ids.find(1) != ids.end() );
56 CPPUNIT_ASSERT( ids.find(2) != ids.end() );
57
58 _point_locator->enable_out_of_mesh_mode();
59 }
60
62
63 virtual std::unique_ptr<GhostingFunctor> clone () const override
64 {
65 auto clone_functor =
66 std::make_unique<OverlappingCouplingFunctor>(_system);
67
68 auto coupling_matrix =
69 std::make_unique<CouplingMatrix>(*_coupling_matrix);
70 clone_functor->set_coupling_matrix(coupling_matrix);
71 return clone_functor;
72 }
73
74 void set_coupling_matrix (std::unique_ptr<CouplingMatrix> & coupling_matrix)
75 { _coupling_matrix = std::move(coupling_matrix); }
76
77 virtual void operator()
78 (const MeshBase::const_element_iterator & range_begin,
79 const MeshBase::const_element_iterator & range_end,
81 map_type & coupled_elements) override
82 {
83 std::unique_ptr<PointLocatorBase> sub_point_locator = _mesh.sub_point_locator();
84
85 for( const auto & elem : as_range(range_begin,range_end) )
86 {
87 std::set<subdomain_id_type> allowed_subdomains;
88 unsigned int var = 0;
89 if( elem->subdomain_id() == 1 )
90 {
91 var = _system.variable_number("V");
92 allowed_subdomains.insert(2);
93 }
94 else if( elem->subdomain_id() == 2 )
95 {
96 var = _system.variable_number("U");
97 allowed_subdomains.insert(1);
98 }
99 else
100 CPPUNIT_FAIL("Something bad happpend");
101
102 unsigned int dim = 2;
103 FEType fe_type = _system.variable_type(var);
104 std::unique_ptr<FEBase> fe (FEBase::build(dim, fe_type));
105 QGauss qrule (dim, fe_type.default_quadrature_order());
106 fe->attach_quadrature_rule (&qrule);
107
108 const std::vector<libMesh::Point> & qpoints = fe->get_xyz();
109
110 fe->reinit(elem);
111
112 for ( const auto & qp : qpoints )
113 {
114 const Elem * overlapping_elem = (*sub_point_locator)( qp, &allowed_subdomains );
115
116 if( overlapping_elem && overlapping_elem->processor_id() != p )
117 coupled_elements.emplace(overlapping_elem, _coupling_matrix.get());
118 }
119 }
120 }
121
122private:
123
125
127
128 std::unique_ptr<PointLocatorBase> _point_locator;
129
130 std::unique_ptr<CouplingMatrix> _coupling_matrix;
131};
132
133
134// We use a custom partioner for this test to help ensure we're
135// testing the cases we want. In particular, we're going to
136// ensure that elements in subdomain one are on different processors
137// from elements subdomain two so we know ghosting will be required
138// for algebraic and coupling ghosting between the two subdomains.
140{
141public:
142
148 virtual ~OverlappingTestPartitioner() = default;
149
153 virtual std::unique_ptr<Partitioner> clone () const override
154 {
155 return std::make_unique<OverlappingTestPartitioner>(*this);
156 }
157
158 virtual bool partitions_from_scratch () const override { return true; }
159
160protected:
161
165 virtual void _do_partition (MeshBase & mesh,
166 const unsigned int n) override
167 {
168 // If we're on one partition, then everyone gets to be on that partition
169 if (n == 1)
170 this->single_partition_range (mesh.active_elements_begin(), mesh.active_elements_end());
171 else
172 {
173 libmesh_assert_greater (n, 0);
174
175 // If there are more partitions than elements, then just
176 // assign one element to each partition
177 if (mesh.n_active_elem() <= n)
178 {
179 processor_id_type e = 0;
180 for (auto & elem : mesh.active_element_ptr_range() )
181 {
182 elem->processor_id() = e;
183 e++;
184 }
185 }
186 // Otherwise, we'll split up the partitions into two groups
187 // and then assign the elements of each subdomain into each of
188 // the two groups
189 else
190 {
191 unsigned int n_sub_two_elems = std::distance( mesh.active_subdomain_elements_begin(2),
192 mesh.active_subdomain_elements_end(2) );
193
194 unsigned int n_parts_sub_two = 0;
195 if( n_sub_two_elems < n/2 )
196 n_parts_sub_two = n_sub_two_elems;
197 else
198 n_parts_sub_two = n/2;
199
200 const unsigned int n_parts_sub_one = n - n_parts_sub_two;
201
202 const dof_id_type sub_two_blk_size = cast_int<dof_id_type>
203 (std::distance( mesh.active_subdomain_elements_begin(2),
204 mesh.active_subdomain_elements_end(2) )/n_parts_sub_two );
205
206 this->assign_proc_id_subdomain( mesh, 2, sub_two_blk_size, n_parts_sub_two, 0 );
207
208
209 const dof_id_type sub_one_blk_size = cast_int<dof_id_type>
210 (std::distance( mesh.active_subdomain_elements_begin(1),
211 mesh.active_subdomain_elements_end(1) )/n_parts_sub_one );
212
213 this->assign_proc_id_subdomain( mesh, 1, sub_one_blk_size, n_parts_sub_one, n_parts_sub_two );
214 }
215 }
216 }
217
219 const subdomain_id_type sid,
220 const dof_id_type blksize,
221 const unsigned int n_parts,
222 const processor_id_type offset )
223 {
224 dof_id_type e = 0;
225 for (auto & elem : mesh.active_subdomain_elements_ptr_range(sid))
226 {
227 if ((e/blksize) < n_parts)
228 elem->processor_id() = offset + cast_int<processor_id_type>(e/blksize);
229 else
230 elem->processor_id() = offset;
231
232 e++;
233 }
234 }
235
236};
237
238
239// Common functionality for all the different tests we'll be running
240// We're creating two elements that will live in subdomain 1 and one
241// element that is lives in subdomain 2 that overlaps both elements in
242// subdomain 1, but is not topologically connected.
244{
245protected:
246
247 std::unique_ptr<MeshBase> _mesh;
248
249 std::unique_ptr<EquationSystems> _es;
250
251 void build_quad_mesh(unsigned int n_refinements = 0)
252 {
253 // We are making assumptions in various places about the presence
254 // of the elements on the current processor so we're restricting to
255 // ReplicatedMesh for now.
256 _mesh = std::make_unique<ReplicatedMesh>(*TestCommWorld);
257
258 _mesh->set_mesh_dimension(2);
259
260 _mesh->add_point( Point(0.0,0.0),0 );
261 _mesh->add_point( Point(1.0,0.0),1 );
262 _mesh->add_point( Point(1.0,1.0),2 );
263 _mesh->add_point( Point(0.0,1.0),3 );
264
265 {
266 Elem * elem = _mesh->add_elem(Elem::build_with_id(QUAD4, 0));
267 elem->subdomain_id() = 1;
268
269 for (unsigned int n=0; n<4; n++)
270 elem->set_node(n, _mesh->node_ptr(n));
271 }
272
273 _mesh->add_point( Point(1.0,2.0),4 );
274 _mesh->add_point( Point(0.0,2.0),5 );
275
276 {
277 Elem * elem = _mesh->add_elem(Elem::build_with_id(QUAD4, 1));
278 elem->subdomain_id() = 1;
279
280 elem->set_node(0, _mesh->node_ptr(3));
281 elem->set_node(1, _mesh->node_ptr(2));
282 elem->set_node(2, _mesh->node_ptr(4));
283 elem->set_node(3, _mesh->node_ptr(5));
284 }
285
286 _mesh->add_point( Point(0.0,0.0),6 );
287 _mesh->add_point( Point(1.0,0.0),7 );
288 _mesh->add_point( Point(1.0,2.0),8 );
289 _mesh->add_point( Point(0.0,2.0),9 );
290
291 {
292 Elem* elem = _mesh->add_elem(Elem::build_with_id(QUAD4, 2));
293 elem->subdomain_id() = 2;
294
295 elem->set_node(0, _mesh->node_ptr(6));
296 elem->set_node(1, _mesh->node_ptr(7));
297 elem->set_node(2, _mesh->node_ptr(8));
298 elem->set_node(3, _mesh->node_ptr(9));
299 }
300
301 _mesh->partitioner() = std::make_unique<OverlappingTestPartitioner>();
302
303 _mesh->prepare_for_use();
304
305#ifdef LIBMESH_ENABLE_AMR
306 if (n_refinements > 0)
307 {
308 MeshRefinement refine(*_mesh);
309 refine.uniformly_refine(n_refinements);
310 }
311#else
312 CPPUNIT_ASSERT_EQUAL(n_refinements, 0u);
313#endif // LIBMESH_ENABLE_AMR
314 }
315
316 void init()
317 {
318 _es = std::make_unique<EquationSystems>(*_mesh);
319 LinearImplicitSystem & sys = _es->add_system<LinearImplicitSystem> ("SimpleSystem");
320
321 std::set<subdomain_id_type> sub_one;
322 sub_one.insert(1);
323
324 std::set<subdomain_id_type> sub_two;
325 sub_two.insert(2);
326
327 sys.add_variable("U", FIRST, LAGRANGE, &sub_two);
328 sys.add_variable("L", FIRST, LAGRANGE, &sub_two);
329
330 sys.add_variable("V", FIRST, LAGRANGE, &sub_one);
331 sys.add_variable("p", FIRST, LAGRANGE, &sub_one);
332
333 _es->init();
334 }
335
336 void clear()
337 {
338 _es.reset();
339 _mesh.reset();
340 }
341
342 void setup_coupling_matrix( std::unique_ptr<CouplingMatrix> & coupling )
343 {
344 System & system = _es->get_system("SimpleSystem");
345
346 coupling = std::make_unique<CouplingMatrix>(system.n_vars());
347
348 const unsigned int u_var = system.variable_number("U");
349 const unsigned int l_var = system.variable_number("L");
350 const unsigned int v_var = system.variable_number("V");
351 const unsigned int p_var = system.variable_number("p");
352
353 // Only adding the overlapping couplings since the primary ones should
354 // be there by default.
355 (*coupling)(u_var,v_var) = true;
356 (*coupling)(l_var,v_var) = true;
357 (*coupling)(l_var,p_var) = true;
358 (*coupling)(v_var,u_var) = true;
359 (*coupling)(v_var,l_var) = true;
360 }
361
362};
363
364// Mainly just to sanity check the mesh construction and
365// the assumptions made in the OverlappingCouplingFunctor
366// as well as the custom Partitioner.
367class OverlappingFunctorTest : public CppUnit::TestCase,
369{
370public:
371
373
374#ifdef LIBMESH_HAVE_SOLVER
378# ifdef LIBMESH_ENABLE_AMR
382# endif
383#endif
384
386
387public:
388
389 public:
390 void setUp()
391 {
392 this->build_quad_mesh();
393 this->init();
394 }
395
396 void tearDown()
397 { this->clear(); }
398
401
404
411
418
420 {
421 this->run_partitioner_test(0);
422 }
423
428
429private:
430
431 // This is basically to sanity check the coupling functor
432 // with the supplied mesh to make sure all the assumptions
433 // are kosher. This test requires AMR and some kind of a
434 // linear solver, so let's only run it if we have PETSc.
435 void run_coupling_functor_test(unsigned int n_refinements)
436 {
437#if defined(LIBMESH_ENABLE_AMR) && defined(LIBMESH_HAVE_SOLVER)
438 if( n_refinements > 0 )
439 {
440 MeshRefinement refine(*_mesh);
441 refine.uniformly_refine(n_refinements);
442 _es->reinit();
443 }
444#else
445 CPPUNIT_ASSERT_EQUAL(n_refinements, 0u);
446#endif
447
448 System & system = _es->get_system("SimpleSystem");
449
450 OverlappingCouplingFunctor coupling_functor(system);
451
452 GhostingFunctor::map_type subdomain_one_couplings;
453 GhostingFunctor::map_type subdomain_two_couplings;
454
455 coupling_functor( _mesh->active_subdomain_elements_begin(1),
456 _mesh->active_subdomain_elements_end(1),
458 subdomain_one_couplings );
459
460 coupling_functor( _mesh->active_subdomain_elements_begin(2),
461 _mesh->active_subdomain_elements_end(2),
463 subdomain_two_couplings );
464
465 dof_id_type n_elems_subdomain_two = std::distance( _mesh->active_subdomain_elements_begin(2),
466 _mesh->active_subdomain_elements_end(2) );
467
468 CPPUNIT_ASSERT_EQUAL(n_elems_subdomain_two, static_cast<dof_id_type>(subdomain_one_couplings.size()));
469 CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(2*n_elems_subdomain_two), static_cast<dof_id_type>(subdomain_two_couplings.size()));
470 }
471
472 void run_partitioner_test(unsigned int n_refinements)
473 {
474#ifdef LIBMESH_ENABLE_AMR
475 if( n_refinements > 0 )
476 {
477 MeshRefinement refine(*_mesh);
478 refine.uniformly_refine(n_refinements);
479 _es->reinit();
480 }
481#else
482 CPPUNIT_ASSERT_EQUAL(n_refinements, 0u);
483#endif // LIBMESH_ENABLE_AMR
484
485 System & system = _es->get_system("SimpleSystem");
486
487 std::set<processor_id_type> sub_one_proc_ids, sub_two_proc_ids;
488 for (auto & elem : _mesh->active_subdomain_elements_ptr_range(1))
489 sub_one_proc_ids.insert(elem->processor_id());
490
491 for (auto & elem : _mesh->active_subdomain_elements_ptr_range(2))
492 sub_two_proc_ids.insert(elem->processor_id());
493
494
495 // Everyone should be on the same processor if only 1 processor
496 if( system.n_processors() == 1 )
497 {
498 CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(sub_one_proc_ids.size()));
499 CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(sub_two_proc_ids.size()));
500 CPPUNIT_ASSERT( sub_one_proc_ids == sub_two_proc_ids );
501 }
502 // Otherwise these sets should be disjoint
503 else
504 {
505 // Make sure no subdomain one ids in the subdomain two ids
506 for (auto & id : sub_one_proc_ids )
507 CPPUNIT_ASSERT( sub_two_proc_ids.find(id) == sub_two_proc_ids.end() );
508
509 // Vice-versa
510 for (auto & id : sub_two_proc_ids )
511 CPPUNIT_ASSERT( sub_one_proc_ids.find(id) == sub_one_proc_ids.end() );
512 }
513 }
514
515};
516
517// In this testing, we're relying on the presence of PETSc
518#ifdef LIBMESH_HAVE_PETSC
519
520
521// This testing class the algebraic ghosting of the
522// OverlappingCouplingFunctor.
523class OverlappingAlgebraicGhostingTest : public CppUnit::TestCase,
525{
526public:
528
531#ifdef LIBMESH_ENABLE_AMR
533#endif
534
536
537public:
538
539 void setUp()
540 {}
541
542 void tearDown()
543 { this->clear(); }
544
546 {
547 LOG_UNIT_TEST;
548 this->run_ghosting_test(0, true);
549 }
550
552 {
553 LOG_UNIT_TEST;
554 this->run_ghosting_test(0, false);
555 }
556
558 {
559 LOG_UNIT_TEST;
560 std::unique_ptr<CouplingMatrix> coupling_matrix;
561
562 this->run_ghosting_test(2, false);
563 }
564
565private:
566
567 void run_ghosting_test(const unsigned int n_refinements, bool build_coupling_matrix)
568 {
569 this->build_quad_mesh(n_refinements);
570 this->init();
571
572 std::unique_ptr<CouplingMatrix> coupling_matrix;
573 if (build_coupling_matrix)
574 this->setup_coupling_matrix(coupling_matrix);
575
576 LinearImplicitSystem & system = _es->get_system<LinearImplicitSystem>("SimpleSystem");
577
578 // If we don't add this coupling functor and properly recompute the
579 // sparsity pattern, then PETSc will throw a malloc error when we
580 // try to assemble into the global matrix
581 OverlappingCouplingFunctor functor(system);
582 functor.set_coupling_matrix(coupling_matrix);
583
584 DofMap & dof_map = system.get_dof_map();
585 dof_map.add_algebraic_ghosting_functor(functor);
586 dof_map.reinit_send_list(system.get_mesh());
587
588 // Update current local solution
590
591 system.current_local_solution->init(system.n_dofs(), system.n_local_dofs(),
592 dof_map.get_send_list(), false,
594
595 system.solution->localize(*(system.current_local_solution),dof_map.get_send_list());
596
597 std::unique_ptr<PointLocatorBase> point_locator = _mesh->sub_point_locator();
598
599 const unsigned int u_var = system.variable_number("U");
600
602
603 FEMContext subdomain_one_context(system);
604 FEMContext subdomain_two_context(system);
605
606 // The use case on which this test is based, we only add terms to the residual
607 // corresponding to the dofs in the second subdomain, but that have couplings
608 // to dofs in the first subdomain.
609 for (const auto & elem : _mesh->active_local_subdomain_elements_ptr_range(2))
610 {
611 // A little extra unit testing on the range iterator
612 CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(elem->subdomain_id()));
613
614 subdomain_one_context.get_element_fe(u_var)->get_nothing(); // for this unit test
615 const std::vector<libMesh::Point> & qpoints = subdomain_two_context.get_element_fe(u_var)->get_xyz();
616
617 // Setup the context for the current element
618 subdomain_two_context.pre_fe_reinit(system,elem);
619 subdomain_two_context.elem_fe_reinit();
620
621 std::set<subdomain_id_type> allowed_subdomains;
622 allowed_subdomains.insert(1);
623
624 // Now loop over the quadrature points and find the subdomain-one element that overlaps
625 // with the current subdomain-two element and then initialize the FEMContext for the
626 // subdomain-one element. If the algebraic ghosting has not been done properly,
627 // this will error.
628 for ( const auto & qp : qpoints )
629 {
630 const Elem * overlapping_elem = (*point_locator)( qp, &allowed_subdomains );
631 CPPUNIT_ASSERT(overlapping_elem);
632
633 // Setup the context for the overlapping element
634 subdomain_one_context.pre_fe_reinit(system,overlapping_elem);
635 subdomain_one_context.elem_fe_reinit();
636 }
637 }
638 }
639
640};
641
642
643// This testing class now exercises testing the sparsity
644// pattern augmented by the OverlappingCouplingFunctor
645class OverlappingCouplingGhostingTest : public CppUnit::TestCase,
647{
648public:
650
653#ifdef LIBMESH_ENABLE_AMR
655#endif
656
658
659public:
660
661 void setUp()
662 {}
663
664 void tearDown()
665 { this->clear(); }
666
668 {
669 LOG_UNIT_TEST;
670 this->run_sparsity_pattern_test(0, true);
671 }
672
674 {
675 LOG_UNIT_TEST;
676 this->run_sparsity_pattern_test(0, false);
677 }
678
680 {
681 LOG_UNIT_TEST;
682 this->run_sparsity_pattern_test(1, false);
683 }
684
685private:
686
687 void run_sparsity_pattern_test(const unsigned int n_refinements, bool build_coupling_matrix)
688 {
689 this->build_quad_mesh(n_refinements);
690 this->init();
691
692 std::unique_ptr<CouplingMatrix> coupling_matrix;
693 if (build_coupling_matrix)
694 this->setup_coupling_matrix(coupling_matrix);
695
696 LinearImplicitSystem & system = _es->get_system<LinearImplicitSystem>("SimpleSystem");
697
698 // If we don't add this coupling functor and properly recompute the
699 // sparsity pattern, then PETSc will throw a malloc error when we
700 // try to assemble into the global matrix
701 OverlappingCouplingFunctor coupling_functor(system);
702 coupling_functor.set_coupling_matrix(coupling_matrix);
703
704 DofMap & dof_map = system.get_dof_map();
705 dof_map.add_coupling_functor(coupling_functor);
706 dof_map.reinit_send_list(system.get_mesh());
707
708 // Update current local solution
710
711 system.current_local_solution->init(system.n_dofs(), system.n_local_dofs(),
712 dof_map.get_send_list(), false,
714
715 system.solution->localize(*(system.current_local_solution),dof_map.get_send_list());
716
717 // Now that we've added the coupling functor, we need
718 // to recompute the sparsity
719 dof_map.clear_sparsity();
720 dof_map.compute_sparsity(system.get_mesh());
721
722 // Now that we've recomputed the sparsity pattern, we need
723 // to reinitialize the system matrix.
724 SparseMatrix<Number> & matrix = system.get_system_matrix();
725 libmesh_assert(dof_map.is_attached(matrix));
726 matrix.init();
727
728 std::unique_ptr<PointLocatorBase> point_locator = _mesh->sub_point_locator();
729
730 const unsigned int u_var = system.variable_number("U");
731 const unsigned int v_var = system.variable_number("V");
732
733 DenseMatrix<Number> K12, K21;
734
735 FEMContext subdomain_one_context(system);
736 subdomain_one_context.get_element_fe(u_var)->get_nothing();
737 subdomain_one_context.get_element_fe(v_var)->get_nothing();
738
739 FEMContext subdomain_two_context(system);
740 subdomain_two_context.get_element_fe(u_var)->get_xyz();
741 subdomain_two_context.get_element_fe(v_var)->get_nothing();
742
743 // Add normally coupled parts of the matrix
744 for (const auto & elem : _mesh->active_local_subdomain_elements_ptr_range(1))
745 {
746 subdomain_one_context.pre_fe_reinit(system,elem);
747 subdomain_one_context.elem_fe_reinit();
748
749 std::vector<dof_id_type> & rows = subdomain_one_context.get_dof_indices();
750
751 // Fill with ones in case PETSc ignores the zeros at some point
752 std::fill( subdomain_one_context.get_elem_jacobian().get_values().begin(),
753 subdomain_one_context.get_elem_jacobian().get_values().end(),
754 1);
755
756 // Insert the Jacobian for the dofs for this element
757 matrix.add_matrix( subdomain_one_context.get_elem_jacobian(), rows );
758 }
759
760 for (const auto & elem : _mesh->active_local_subdomain_elements_ptr_range(2))
761 {
762 // A little extra unit testing on the range iterator
763 CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(elem->subdomain_id()));
764
765 const std::vector<libMesh::Point> & qpoints = subdomain_two_context.get_element_fe(u_var)->get_xyz();
766
767 // Setup the context for the current element
768 subdomain_two_context.pre_fe_reinit(system,elem);
769 subdomain_two_context.elem_fe_reinit();
770
771 // We're only assembling rows for the dofs on subdomain 2 (U,L), so
772 // the current element will have all those dof_indices.
773 std::vector<dof_id_type> & rows = subdomain_two_context.get_dof_indices();
774
775 std::fill( subdomain_two_context.get_elem_jacobian().get_values().begin(),
776 subdomain_two_context.get_elem_jacobian().get_values().end(),
777 1);
778
779 // Insert the Jacobian for the normally coupled dofs for this element
780 matrix.add_matrix( subdomain_two_context.get_elem_jacobian(), rows );
781
782 std::set<subdomain_id_type> allowed_subdomains;
783 allowed_subdomains.insert(1);
784
785 // Now loop over the quadrature points and find the subdomain-one element that overlaps
786 // with the current subdomain-two element and then add a local element matrix with
787 // the coupling to the global matrix to try and trip any issues with sparsity pattern
788 // construction
789 for ( const auto & qp : qpoints )
790 {
791 const Elem * overlapping_elem = (*point_locator)( qp, &allowed_subdomains );
792 CPPUNIT_ASSERT(overlapping_elem);
793
794 // Setup the context for the overlapping element
795 subdomain_one_context.pre_fe_reinit(system,overlapping_elem);
796 subdomain_one_context.elem_fe_reinit();
797
798 // We're only coupling to the "V" variable so only need those dof indices
799 std::vector<dof_id_type> & v_indices = subdomain_one_context.get_dof_indices(v_var);
800 std::vector<dof_id_type> columns(rows);
801 columns.insert( columns.end(), v_indices.begin(), v_indices.end() );
802
803 // This will also zero the matrix so we can just insert zeros for this test
804 K21.resize( rows.size(), columns.size() );
805
806 std::fill(K21.get_values().begin(), K21.get_values().end(), 1);
807
808 // Now adding this local matrix to the global would trip a PETSc
809 // malloc error if the sparsity pattern hasn't been correctly
810 // built to include the overlapping coupling.
811 matrix.add_matrix (K21, rows, columns);
812
813 // Now add the other part of the overlapping coupling
814 K12.resize(v_indices.size(), rows.size());
815 std::fill(K12.get_values().begin(), K12.get_values().end(), 1);
816 matrix.add_matrix(K12,v_indices,rows);
817 }
818 } // end element loop
819
820 // We need to make sure to close the matrix for this test. There could still
821 // be PETSc malloc errors tripped here if we didn't allocate the off-processor
822 // part of the sparsity pattern correctly.
823 matrix.close();
824 }
825
826};
827#endif // LIBMESH_HAVE_PETSC
828
829
831
832#ifdef LIBMESH_HAVE_PETSC
835#endif
unsigned int dim
CPPUNIT_TEST(testGhostingNullCouplingMatrixUnifRef)
LIBMESH_CPPUNIT_TEST_SUITE(OverlappingAlgebraicGhostingTest)
CPPUNIT_TEST(testGhostingCouplingMatrix)
CPPUNIT_TEST(testGhostingNullCouplingMatrix)
void run_ghosting_test(const unsigned int n_refinements, bool build_coupling_matrix)
virtual std::unique_ptr< GhostingFunctor > clone() const override
A clone() is needed because GhostingFunctor can not be shared between different meshes.
std::unique_ptr< PointLocatorBase > _point_locator
std::unique_ptr< CouplingMatrix > _coupling_matrix
void set_coupling_matrix(std::unique_ptr< CouplingMatrix > &coupling_matrix)
CPPUNIT_TEST(testSparsityNullCouplingMatrix)
CPPUNIT_TEST(testSparsityCouplingMatrix)
CPPUNIT_TEST(testSparsityNullCouplingMatrixUnifRef)
void run_sparsity_pattern_test(const unsigned int n_refinements, bool build_coupling_matrix)
LIBMESH_CPPUNIT_TEST_SUITE(OverlappingCouplingGhostingTest)
CPPUNIT_TEST(checkCouplingFunctorTri)
CPPUNIT_TEST(checkCouplingFunctorQuadUnifRef)
void run_coupling_functor_test(unsigned int n_refinements)
CPPUNIT_TEST(checkCouplingFunctorTriUnifRef)
CPPUNIT_TEST(checkOverlappingPartitionerUnifRef)
CPPUNIT_TEST(checkOverlappingPartitioner)
LIBMESH_CPPUNIT_TEST_SUITE(OverlappingFunctorTest)
void run_partitioner_test(unsigned int n_refinements)
CPPUNIT_TEST(checkCouplingFunctorQuad)
std::unique_ptr< MeshBase > _mesh
std::unique_ptr< EquationSystems > _es
void setup_coupling_matrix(std::unique_ptr< CouplingMatrix > &coupling)
void build_quad_mesh(unsigned int n_refinements=0)
virtual std::unique_ptr< Partitioner > clone() const override
OverlappingTestPartitioner()=default
void assign_proc_id_subdomain(MeshBase &mesh, const subdomain_id_type sid, const dof_id_type blksize, const unsigned int n_parts, const processor_id_type offset)
virtual ~OverlappingTestPartitioner()=default
OverlappingTestPartitioner & operator=(const OverlappingTestPartitioner &)=default
OverlappingTestPartitioner(const OverlappingTestPartitioner &)=default
OverlappingTestPartitioner(OverlappingTestPartitioner &&)=default
virtual bool partitions_from_scratch() const override
virtual void _do_partition(MeshBase &mesh, const unsigned int n) override
Partition the MeshBase into n subdomains.
Defines a dense matrix for use in Finite Element-type computations.
std::vector< T > & get_values()
void resize(const unsigned int new_m, const unsigned int new_n)
Resizes the matrix to the specified size and calls zero().
const std::vector< dof_id_type > & get_dof_indices() const
Accessor for element dof indices.
const DenseMatrix< Number > & get_elem_jacobian() const
Const accessor for element Jacobian.
This class handles the numbering of degrees of freedom on a mesh.
Definition dof_map.h:181
void reinit_send_list(MeshBase &mesh)
Clears the _send_list vector and then rebuilds it.
Definition dof_map.C:1879
const std::vector< dof_id_type > & get_send_list() const
Definition dof_map.h:540
bool is_attached(SparseMatrix< Number > &matrix)
Matrices should not be attached more than once.
Definition dof_map.C:296
void compute_sparsity(const MeshBase &)
Computes the sparsity pattern for the matrices corresponding to proc_id and sends that data to Linear...
Definition dof_map.C:1962
void clear_sparsity()
Clears the sparsity pattern.
Definition dof_map.C:1983
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:2064
void add_coupling_functor(GhostingFunctor &coupling_functor, bool to_mesh=true)
Adds a functor which can specify coupling requirements for creation of sparse matrices.
Definition dof_map.C:2007
processor_id_type processor_id() const
Definition dof_object.h:895
static constexpr processor_id_type invalid_processor_id
An invalid processor_id to distinguish DoFs that have not been assigned to a processor.
Definition dof_object.h:484
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:2688
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:496
subdomain_id_type subdomain_id() const
Definition elem.h:2712
static std::unique_ptr< FEGenericBase > build(const unsigned int dim, const FEType &type)
Builds a specific finite element type.
This class provides all data required for a physics package (e.g.
Definition fem_context.h:64
virtual void pre_fe_reinit(const System &, const Elem *e)
Reinitializes local data vectors/matrices on the current geometric element.
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.
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition fe_type.h:197
Order default_quadrature_order() const
Definition fe_type.h:415
This abstract base class defines the interface by which library code and user code can report associa...
std::map< const Elem *, const CouplingMatrix *, CompareDofObjectsByPIDAndThenID > map_type
What elements do we care about and what variables do we care about on each element?
const MeshBase * get_mesh() const
Return the mesh associated with ghosting functor.
const SparseMatrix< Number > & get_system_matrix() const
Manages consistently variables, degrees of freedom, coefficient vectors, matrices and linear solvers ...
This is the MeshBase class.
Definition mesh_base.h:81
void subdomain_ids(std::set< subdomain_id_type > &ids, const bool global=true) const
Constructs a list of all subdomain identifiers in the local mesh if global == false,...
Definition mesh_base.C:1127
unsigned int level ElemType type std::set< subdomain_id_type > ss processor_id_type pid unsigned int level std::set< subdomain_id_type > virtual ss SimpleRange< element_iterator > active_subdomain_elements_ptr_range(subdomain_id_type sid)=0
std::unique_ptr< PointLocatorBase > sub_point_locator() const
Definition mesh_base.C:1848
virtual dof_id_type n_active_elem() const =0
subdomain_id_type n_subdomains() const
Definition mesh_base.C:1191
Implements (adaptive) mesh refinement algorithms for a MeshBase.
void uniformly_refine(unsigned int n=1)
Uniformly refines the mesh n times.
static std::unique_ptr< NumericVector< T > > build(const Parallel::Communicator &comm, SolverPackage solver_package=libMesh::default_solver_package(), ParallelType parallel_type=AUTOMATIC)
Builds a NumericVector on the processors in communicator comm using the linear solver package specifi...
const Parallel::Communicator & comm() const
processor_id_type n_processors() const
The Partitioner class provides a uniform interface for partitioning algorithms.
Definition partitioner.h:52
bool single_partition_range(MeshBase::element_iterator it, MeshBase::element_iterator end)
Slightly generalized version of single_partition which acts on a range of elements defined by the pai...
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:41
This class implements specific orders of Gauss quadrature.
Generic sparse matrix.
virtual void init(const numeric_index_type m, const numeric_index_type n, const numeric_index_type m_l, const numeric_index_type n_l, const numeric_index_type nnz=30, const numeric_index_type noz=10, const numeric_index_type blocksize=1)=0
Initialize SparseMatrix with the specified sizes.
virtual void close()=0
Calls the SparseMatrix's internal assembly routines, ensuring that the values are consistent across p...
virtual void add_matrix(const DenseMatrix< T > &dm, const std::vector< numeric_index_type > &rows, const std::vector< numeric_index_type > &cols)=0
Add the full matrix dm to the SparseMatrix.
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition system.h:100
std::unique_ptr< NumericVector< Number > > current_local_solution
All the values I need to compute my contribution to the simulation at hand.
Definition system.h:1667
dof_id_type n_dofs() const
Definition system.C:118
const FEType & variable_type(const unsigned int i) const
Definition system.C:2721
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
dof_id_type n_local_dofs() const
Definition system.C:155
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition system.h:1655
unsigned int variable_number(std::string_view var) const
Definition system.C:1398
unsigned int n_vars() const
Definition system.C:2674
const DofMap & get_dof_map() const
Definition system.h:2417
const MeshBase & get_mesh() const
Definition system.h:2401
Communicator * TestCommWorld
MeshBase & mesh
void all_tri(MeshBase &mesh)
Subdivides any non-simplex elements in a Mesh to produce simplex (triangular in 2D,...
The libMesh namespace provides an interface to certain functionality in the library.
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
libmesh_assert(ctx)
uint8_t dof_id_type
Definition id_types.h:67
uint8_t processor_id_type
Definition id_types.h:104
CPPUNIT_TEST_SUITE_REGISTRATION(OverlappingFunctorTest)
The definition of the const_element_iterator struct.
Definition mesh_base.h:2575