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
158protected:
159
163 virtual void _do_partition (MeshBase & mesh,
164 const unsigned int n) override
165 {
166 // If we're on one partition, then everyone gets to be on that partition
167 if (n == 1)
168 this->single_partition_range (mesh.active_elements_begin(), mesh.active_elements_end());
169 else
170 {
171 libmesh_assert_greater (n, 0);
172
173 // If there are more partitions than elements, then just
174 // assign one element to each partition
175 if (mesh.n_active_elem() <= n)
176 {
177 processor_id_type e = 0;
178 for (auto & elem : mesh.active_element_ptr_range() )
179 {
180 elem->processor_id() = e;
181 e++;
182 }
183 }
184 // Otherwise, we'll split up the partitions into two groups
185 // and then assign the elements of each subdomain into each of
186 // the two groups
187 else
188 {
189 unsigned int n_sub_two_elems = std::distance( mesh.active_subdomain_elements_begin(2),
190 mesh.active_subdomain_elements_end(2) );
191
192 unsigned int n_parts_sub_two = 0;
193 if( n_sub_two_elems < n/2 )
194 n_parts_sub_two = n_sub_two_elems;
195 else
196 n_parts_sub_two = n/2;
197
198 const unsigned int n_parts_sub_one = n - n_parts_sub_two;
199
200 const dof_id_type sub_two_blk_size = cast_int<dof_id_type>
201 (std::distance( mesh.active_subdomain_elements_begin(2),
202 mesh.active_subdomain_elements_end(2) )/n_parts_sub_two );
203
204 this->assign_proc_id_subdomain( mesh, 2, sub_two_blk_size, n_parts_sub_two, 0 );
205
206
207 const dof_id_type sub_one_blk_size = cast_int<dof_id_type>
208 (std::distance( mesh.active_subdomain_elements_begin(1),
209 mesh.active_subdomain_elements_end(1) )/n_parts_sub_one );
210
211 this->assign_proc_id_subdomain( mesh, 1, sub_one_blk_size, n_parts_sub_one, n_parts_sub_two );
212 }
213 }
214 }
215
217 const subdomain_id_type sid,
218 const dof_id_type blksize,
219 const unsigned int n_parts,
220 const processor_id_type offset )
221 {
222 dof_id_type e = 0;
223 for (auto & elem : mesh.active_subdomain_elements_ptr_range(sid))
224 {
225 if ((e/blksize) < n_parts)
226 elem->processor_id() = offset + cast_int<processor_id_type>(e/blksize);
227 else
228 elem->processor_id() = offset;
229
230 e++;
231 }
232 }
233
234};
235
236
237// Common functionality for all the different tests we'll be running
238// We're creating two elements that will live in subdomain 1 and one
239// element that is lives in subdomain 2 that overlaps both elements in
240// subdomain 1, but is not topologically connected.
242{
243protected:
244
245 std::unique_ptr<MeshBase> _mesh;
246
247 std::unique_ptr<EquationSystems> _es;
248
249 void build_quad_mesh(unsigned int n_refinements = 0)
250 {
251 // We are making assumptions in various places about the presence
252 // of the elements on the current processor so we're restricting to
253 // ReplicatedMesh for now.
254 _mesh = std::make_unique<ReplicatedMesh>(*TestCommWorld);
255
256 _mesh->set_mesh_dimension(2);
257
258 _mesh->add_point( Point(0.0,0.0),0 );
259 _mesh->add_point( Point(1.0,0.0),1 );
260 _mesh->add_point( Point(1.0,1.0),2 );
261 _mesh->add_point( Point(0.0,1.0),3 );
262
263 {
264 Elem * elem = _mesh->add_elem(Elem::build_with_id(QUAD4, 0));
265 elem->subdomain_id() = 1;
266
267 for (unsigned int n=0; n<4; n++)
268 elem->set_node(n, _mesh->node_ptr(n));
269 }
270
271 _mesh->add_point( Point(1.0,2.0),4 );
272 _mesh->add_point( Point(0.0,2.0),5 );
273
274 {
275 Elem * elem = _mesh->add_elem(Elem::build_with_id(QUAD4, 1));
276 elem->subdomain_id() = 1;
277
278 elem->set_node(0, _mesh->node_ptr(3));
279 elem->set_node(1, _mesh->node_ptr(2));
280 elem->set_node(2, _mesh->node_ptr(4));
281 elem->set_node(3, _mesh->node_ptr(5));
282 }
283
284 _mesh->add_point( Point(0.0,0.0),6 );
285 _mesh->add_point( Point(1.0,0.0),7 );
286 _mesh->add_point( Point(1.0,2.0),8 );
287 _mesh->add_point( Point(0.0,2.0),9 );
288
289 {
290 Elem* elem = _mesh->add_elem(Elem::build_with_id(QUAD4, 2));
291 elem->subdomain_id() = 2;
292
293 elem->set_node(0, _mesh->node_ptr(6));
294 elem->set_node(1, _mesh->node_ptr(7));
295 elem->set_node(2, _mesh->node_ptr(8));
296 elem->set_node(3, _mesh->node_ptr(9));
297 }
298
299 _mesh->partitioner() = std::make_unique<OverlappingTestPartitioner>();
300
301 _mesh->prepare_for_use();
302
303#ifdef LIBMESH_ENABLE_AMR
304 if (n_refinements > 0)
305 {
306 MeshRefinement refine(*_mesh);
307 refine.uniformly_refine(n_refinements);
308 }
309#else
310 CPPUNIT_ASSERT_EQUAL(n_refinements, 0u);
311#endif // LIBMESH_ENABLE_AMR
312 }
313
314 void init()
315 {
316 _es = std::make_unique<EquationSystems>(*_mesh);
317 LinearImplicitSystem & sys = _es->add_system<LinearImplicitSystem> ("SimpleSystem");
318
319 std::set<subdomain_id_type> sub_one;
320 sub_one.insert(1);
321
322 std::set<subdomain_id_type> sub_two;
323 sub_two.insert(2);
324
325 sys.add_variable("U", FIRST, LAGRANGE, &sub_two);
326 sys.add_variable("L", FIRST, LAGRANGE, &sub_two);
327
328 sys.add_variable("V", FIRST, LAGRANGE, &sub_one);
329 sys.add_variable("p", FIRST, LAGRANGE, &sub_one);
330
331 _es->init();
332 }
333
334 void clear()
335 {
336 _es.reset();
337 _mesh.reset();
338 }
339
340 void setup_coupling_matrix( std::unique_ptr<CouplingMatrix> & coupling )
341 {
342 System & system = _es->get_system("SimpleSystem");
343
344 coupling = std::make_unique<CouplingMatrix>(system.n_vars());
345
346 const unsigned int u_var = system.variable_number("U");
347 const unsigned int l_var = system.variable_number("L");
348 const unsigned int v_var = system.variable_number("V");
349 const unsigned int p_var = system.variable_number("p");
350
351 // Only adding the overlapping couplings since the primary ones should
352 // be there by default.
353 (*coupling)(u_var,v_var) = true;
354 (*coupling)(l_var,v_var) = true;
355 (*coupling)(l_var,p_var) = true;
356 (*coupling)(v_var,u_var) = true;
357 (*coupling)(v_var,l_var) = true;
358 }
359
360};
361
362// Mainly just to sanity check the mesh construction and
363// the assumptions made in the OverlappingCouplingFunctor
364// as well as the custom Partitioner.
365class OverlappingFunctorTest : public CppUnit::TestCase,
367{
368public:
369
371
372#ifdef LIBMESH_HAVE_SOLVER
376# ifdef LIBMESH_ENABLE_AMR
380# endif
381#endif
382
384
385public:
386
387 public:
388 void setUp()
389 {
390 this->build_quad_mesh();
391 this->init();
392 }
393
394 void tearDown()
395 { this->clear(); }
396
399
402
409
416
418 {
419 this->run_partitioner_test(0);
420 }
421
426
427private:
428
429 // This is basically to sanity check the coupling functor
430 // with the supplied mesh to make sure all the assumptions
431 // are kosher. This test requires AMR and some kind of a
432 // linear solver, so let's only run it if we have PETSc.
433 void run_coupling_functor_test(unsigned int n_refinements)
434 {
435#if defined(LIBMESH_ENABLE_AMR) && defined(LIBMESH_HAVE_SOLVER)
436 if( n_refinements > 0 )
437 {
438 MeshRefinement refine(*_mesh);
439 refine.uniformly_refine(n_refinements);
440 _es->reinit();
441 }
442#else
443 CPPUNIT_ASSERT_EQUAL(n_refinements, 0u);
444#endif
445
446 System & system = _es->get_system("SimpleSystem");
447
448 OverlappingCouplingFunctor coupling_functor(system);
449
450 GhostingFunctor::map_type subdomain_one_couplings;
451 GhostingFunctor::map_type subdomain_two_couplings;
452
453 coupling_functor( _mesh->active_subdomain_elements_begin(1),
454 _mesh->active_subdomain_elements_end(1),
456 subdomain_one_couplings );
457
458 coupling_functor( _mesh->active_subdomain_elements_begin(2),
459 _mesh->active_subdomain_elements_end(2),
461 subdomain_two_couplings );
462
463 dof_id_type n_elems_subdomain_two = std::distance( _mesh->active_subdomain_elements_begin(2),
464 _mesh->active_subdomain_elements_end(2) );
465
466 CPPUNIT_ASSERT_EQUAL(n_elems_subdomain_two, static_cast<dof_id_type>(subdomain_one_couplings.size()));
467 CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(2*n_elems_subdomain_two), static_cast<dof_id_type>(subdomain_two_couplings.size()));
468 }
469
470 void run_partitioner_test(unsigned int n_refinements)
471 {
472#ifdef LIBMESH_ENABLE_AMR
473 if( n_refinements > 0 )
474 {
475 MeshRefinement refine(*_mesh);
476 refine.uniformly_refine(n_refinements);
477 _es->reinit();
478 }
479#else
480 CPPUNIT_ASSERT_EQUAL(n_refinements, 0u);
481#endif // LIBMESH_ENABLE_AMR
482
483 System & system = _es->get_system("SimpleSystem");
484
485 std::set<processor_id_type> sub_one_proc_ids, sub_two_proc_ids;
486 for (auto & elem : _mesh->active_subdomain_elements_ptr_range(1))
487 sub_one_proc_ids.insert(elem->processor_id());
488
489 for (auto & elem : _mesh->active_subdomain_elements_ptr_range(2))
490 sub_two_proc_ids.insert(elem->processor_id());
491
492
493 // Everyone should be on the same processor if only 1 processor
494 if( system.n_processors() == 1 )
495 {
496 CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(sub_one_proc_ids.size()));
497 CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(sub_two_proc_ids.size()));
498 CPPUNIT_ASSERT( sub_one_proc_ids == sub_two_proc_ids );
499 }
500 // Otherwise these sets should be disjoint
501 else
502 {
503 // Make sure no subdomain one ids in the subdomain two ids
504 for (auto & id : sub_one_proc_ids )
505 CPPUNIT_ASSERT( sub_two_proc_ids.find(id) == sub_two_proc_ids.end() );
506
507 // Vice-versa
508 for (auto & id : sub_two_proc_ids )
509 CPPUNIT_ASSERT( sub_one_proc_ids.find(id) == sub_one_proc_ids.end() );
510 }
511 }
512
513};
514
515// In this testing, we're relying on the presence of PETSc
516#ifdef LIBMESH_HAVE_PETSC
517
518
519// This testing class the algebraic ghosting of the
520// OverlappingCouplingFunctor.
521class OverlappingAlgebraicGhostingTest : public CppUnit::TestCase,
523{
524public:
526
529#ifdef LIBMESH_ENABLE_AMR
531#endif
532
534
535public:
536
537 void setUp()
538 {}
539
540 void tearDown()
541 { this->clear(); }
542
544 {
545 LOG_UNIT_TEST;
546 this->run_ghosting_test(0, true);
547 }
548
550 {
551 LOG_UNIT_TEST;
552 this->run_ghosting_test(0, false);
553 }
554
556 {
557 LOG_UNIT_TEST;
558 std::unique_ptr<CouplingMatrix> coupling_matrix;
559
560 this->run_ghosting_test(2, false);
561 }
562
563private:
564
565 void run_ghosting_test(const unsigned int n_refinements, bool build_coupling_matrix)
566 {
567 this->build_quad_mesh(n_refinements);
568 this->init();
569
570 std::unique_ptr<CouplingMatrix> coupling_matrix;
571 if (build_coupling_matrix)
572 this->setup_coupling_matrix(coupling_matrix);
573
574 LinearImplicitSystem & system = _es->get_system<LinearImplicitSystem>("SimpleSystem");
575
576 // If we don't add this coupling functor and properly recompute the
577 // sparsity pattern, then PETSc will throw a malloc error when we
578 // try to assemble into the global matrix
579 OverlappingCouplingFunctor functor(system);
580 functor.set_coupling_matrix(coupling_matrix);
581
582 DofMap & dof_map = system.get_dof_map();
583 dof_map.add_algebraic_ghosting_functor(functor);
584 dof_map.reinit_send_list(system.get_mesh());
585
586 // Update current local solution
588
589 system.current_local_solution->init(system.n_dofs(), system.n_local_dofs(),
590 dof_map.get_send_list(), false,
592
593 system.solution->localize(*(system.current_local_solution),dof_map.get_send_list());
594
595 std::unique_ptr<PointLocatorBase> point_locator = _mesh->sub_point_locator();
596
597 const unsigned int u_var = system.variable_number("U");
598
600
601 FEMContext subdomain_one_context(system);
602 FEMContext subdomain_two_context(system);
603
604 // The use case on which this test is based, we only add terms to the residual
605 // corresponding to the dofs in the second subdomain, but that have couplings
606 // to dofs in the first subdomain.
607 for (const auto & elem : _mesh->active_local_subdomain_elements_ptr_range(2))
608 {
609 // A little extra unit testing on the range iterator
610 CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(elem->subdomain_id()));
611
612 subdomain_one_context.get_element_fe(u_var)->get_nothing(); // for this unit test
613 const std::vector<libMesh::Point> & qpoints = subdomain_two_context.get_element_fe(u_var)->get_xyz();
614
615 // Setup the context for the current element
616 subdomain_two_context.pre_fe_reinit(system,elem);
617 subdomain_two_context.elem_fe_reinit();
618
619 std::set<subdomain_id_type> allowed_subdomains;
620 allowed_subdomains.insert(1);
621
622 // Now loop over the quadrature points and find the subdomain-one element that overlaps
623 // with the current subdomain-two element and then initialize the FEMContext for the
624 // subdomain-one element. If the algebraic ghosting has not been done properly,
625 // this will error.
626 for ( const auto & qp : qpoints )
627 {
628 const Elem * overlapping_elem = (*point_locator)( qp, &allowed_subdomains );
629 CPPUNIT_ASSERT(overlapping_elem);
630
631 // Setup the context for the overlapping element
632 subdomain_one_context.pre_fe_reinit(system,overlapping_elem);
633 subdomain_one_context.elem_fe_reinit();
634 }
635 }
636 }
637
638};
639
640
641// This testing class now exercises testing the sparsity
642// pattern augmented by the OverlappingCouplingFunctor
643class OverlappingCouplingGhostingTest : public CppUnit::TestCase,
645{
646public:
648
651#ifdef LIBMESH_ENABLE_AMR
653#endif
654
656
657public:
658
659 void setUp()
660 {}
661
662 void tearDown()
663 { this->clear(); }
664
666 {
667 LOG_UNIT_TEST;
668 this->run_sparsity_pattern_test(0, true);
669 }
670
672 {
673 LOG_UNIT_TEST;
674 this->run_sparsity_pattern_test(0, false);
675 }
676
678 {
679 LOG_UNIT_TEST;
680 this->run_sparsity_pattern_test(1, false);
681 }
682
683private:
684
685 void run_sparsity_pattern_test(const unsigned int n_refinements, bool build_coupling_matrix)
686 {
687 this->build_quad_mesh(n_refinements);
688 this->init();
689
690 std::unique_ptr<CouplingMatrix> coupling_matrix;
691 if (build_coupling_matrix)
692 this->setup_coupling_matrix(coupling_matrix);
693
694 LinearImplicitSystem & system = _es->get_system<LinearImplicitSystem>("SimpleSystem");
695
696 // If we don't add this coupling functor and properly recompute the
697 // sparsity pattern, then PETSc will throw a malloc error when we
698 // try to assemble into the global matrix
699 OverlappingCouplingFunctor coupling_functor(system);
700 coupling_functor.set_coupling_matrix(coupling_matrix);
701
702 DofMap & dof_map = system.get_dof_map();
703 dof_map.add_coupling_functor(coupling_functor);
704 dof_map.reinit_send_list(system.get_mesh());
705
706 // Update current local solution
708
709 system.current_local_solution->init(system.n_dofs(), system.n_local_dofs(),
710 dof_map.get_send_list(), false,
712
713 system.solution->localize(*(system.current_local_solution),dof_map.get_send_list());
714
715 // Now that we've added the coupling functor, we need
716 // to recompute the sparsity
717 dof_map.clear_sparsity();
718 dof_map.compute_sparsity(system.get_mesh());
719
720 // Now that we've recomputed the sparsity pattern, we need
721 // to reinitialize the system matrix.
722 SparseMatrix<Number> & matrix = system.get_system_matrix();
723 libmesh_assert(dof_map.is_attached(matrix));
724 matrix.init();
725
726 std::unique_ptr<PointLocatorBase> point_locator = _mesh->sub_point_locator();
727
728 const unsigned int u_var = system.variable_number("U");
729 const unsigned int v_var = system.variable_number("V");
730
731 DenseMatrix<Number> K12, K21;
732
733 FEMContext subdomain_one_context(system);
734 subdomain_one_context.get_element_fe(u_var)->get_nothing();
735 subdomain_one_context.get_element_fe(v_var)->get_nothing();
736
737 FEMContext subdomain_two_context(system);
738 subdomain_two_context.get_element_fe(u_var)->get_xyz();
739 subdomain_two_context.get_element_fe(v_var)->get_nothing();
740
741 // Add normally coupled parts of the matrix
742 for (const auto & elem : _mesh->active_local_subdomain_elements_ptr_range(1))
743 {
744 subdomain_one_context.pre_fe_reinit(system,elem);
745 subdomain_one_context.elem_fe_reinit();
746
747 std::vector<dof_id_type> & rows = subdomain_one_context.get_dof_indices();
748
749 // Fill with ones in case PETSc ignores the zeros at some point
750 std::fill( subdomain_one_context.get_elem_jacobian().get_values().begin(),
751 subdomain_one_context.get_elem_jacobian().get_values().end(),
752 1);
753
754 // Insert the Jacobian for the dofs for this element
755 matrix.add_matrix( subdomain_one_context.get_elem_jacobian(), rows );
756 }
757
758 for (const auto & elem : _mesh->active_local_subdomain_elements_ptr_range(2))
759 {
760 // A little extra unit testing on the range iterator
761 CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(elem->subdomain_id()));
762
763 const std::vector<libMesh::Point> & qpoints = subdomain_two_context.get_element_fe(u_var)->get_xyz();
764
765 // Setup the context for the current element
766 subdomain_two_context.pre_fe_reinit(system,elem);
767 subdomain_two_context.elem_fe_reinit();
768
769 // We're only assembling rows for the dofs on subdomain 2 (U,L), so
770 // the current element will have all those dof_indices.
771 std::vector<dof_id_type> & rows = subdomain_two_context.get_dof_indices();
772
773 std::fill( subdomain_two_context.get_elem_jacobian().get_values().begin(),
774 subdomain_two_context.get_elem_jacobian().get_values().end(),
775 1);
776
777 // Insert the Jacobian for the normally coupled dofs for this element
778 matrix.add_matrix( subdomain_two_context.get_elem_jacobian(), rows );
779
780 std::set<subdomain_id_type> allowed_subdomains;
781 allowed_subdomains.insert(1);
782
783 // Now loop over the quadrature points and find the subdomain-one element that overlaps
784 // with the current subdomain-two element and then add a local element matrix with
785 // the coupling to the global matrix to try and trip any issues with sparsity pattern
786 // construction
787 for ( const auto & qp : qpoints )
788 {
789 const Elem * overlapping_elem = (*point_locator)( qp, &allowed_subdomains );
790 CPPUNIT_ASSERT(overlapping_elem);
791
792 // Setup the context for the overlapping element
793 subdomain_one_context.pre_fe_reinit(system,overlapping_elem);
794 subdomain_one_context.elem_fe_reinit();
795
796 // We're only coupling to the "V" variable so only need those dof indices
797 std::vector<dof_id_type> & v_indices = subdomain_one_context.get_dof_indices(v_var);
798 std::vector<dof_id_type> columns(rows);
799 columns.insert( columns.end(), v_indices.begin(), v_indices.end() );
800
801 // This will also zero the matrix so we can just insert zeros for this test
802 K21.resize( rows.size(), columns.size() );
803
804 std::fill(K21.get_values().begin(), K21.get_values().end(), 1);
805
806 // Now adding this local matrix to the global would trip a PETSc
807 // malloc error if the sparsity pattern hasn't been correctly
808 // built to include the overlapping coupling.
809 matrix.add_matrix (K21, rows, columns);
810
811 // Now add the other part of the overlapping coupling
812 K12.resize(v_indices.size(), rows.size());
813 std::fill(K12.get_values().begin(), K12.get_values().end(), 1);
814 matrix.add_matrix(K12,v_indices,rows);
815 }
816 } // end element loop
817
818 // We need to make sure to close the matrix for this test. There could still
819 // be PETSc malloc errors tripped here if we didn't allocate the off-processor
820 // part of the sparsity pattern correctly.
821 matrix.close();
822 }
823
824};
825#endif // LIBMESH_HAVE_PETSC
826
827
829
830#ifdef LIBMESH_HAVE_PETSC
833#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 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:1877
const std::vector< dof_id_type > & get_send_list() const
Definition dof_map.h:533
bool is_attached(SparseMatrix< Number > &matrix)
Matrices should not be attached more than once.
Definition dof_map.C:295
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:1960
void clear_sparsity()
Clears the sparsity pattern.
Definition dof_map.C:1981
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
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:2005
processor_id_type processor_id() const
Definition dof_object.h:881
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:2567
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
subdomain_id_type subdomain_id() const
Definition elem.h:2591
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:63
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:1126
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:1833
virtual dof_id_type n_active_elem() const =0
subdomain_id_type n_subdomains() const
Definition mesh_base.C:1190
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:40
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:2556