libMesh
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 
21 using 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 
42  : GhostingFunctor(),
43  _system(system),
44  _mesh(system.get_mesh()),
45  _point_locator(system.get_mesh().sub_point_locator()),
46  _coupling_matrix(nullptr)
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;
54  _mesh.subdomain_ids(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 
122 private:
123 
125 
126  const MeshBase & _mesh;
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 {
141 public:
142 
143  OverlappingTestPartitioner () = default;
146  OverlappingTestPartitioner & operator= (const OverlappingTestPartitioner &) = default;
147  OverlappingTestPartitioner & operator= (OverlappingTestPartitioner &&) = default;
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 protected:
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 {
243 protected:
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.
365 class OverlappingFunctorTest : public CppUnit::TestCase,
366  public OverlappingTestBase
367 {
368 public:
369 
370  LIBMESH_CPPUNIT_TEST_SUITE( OverlappingFunctorTest );
371 
372 #ifdef LIBMESH_HAVE_SOLVER
373  CPPUNIT_TEST( checkCouplingFunctorQuad );
374  CPPUNIT_TEST( checkCouplingFunctorTri );
375  CPPUNIT_TEST( checkOverlappingPartitioner );
376 # ifdef LIBMESH_ENABLE_AMR
377  CPPUNIT_TEST( checkCouplingFunctorQuadUnifRef );
378  CPPUNIT_TEST( checkCouplingFunctorTriUnifRef );
379  CPPUNIT_TEST( checkOverlappingPartitionerUnifRef );
380 # endif
381 #endif
382 
383  CPPUNIT_TEST_SUITE_END();
384 
385 public:
386 
387  public:
388  void setUp()
389  {
390  this->build_quad_mesh();
391  this->init();
392  }
393 
394  void tearDown()
395  { this->clear(); }
396 
398  { this->run_coupling_functor_test(0); }
399 
401  { this->run_coupling_functor_test(1); }
402 
404  {
406  _es->reinit();
407  this->run_coupling_functor_test(0);
408  }
409 
411  {
413  _es->reinit();
414  this->run_coupling_functor_test(1);
415  }
416 
418  {
419  this->run_partitioner_test(0);
420  }
421 
423  {
424  this->run_partitioner_test(1);
425  }
426 
427 private:
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.
521 class OverlappingAlgebraicGhostingTest : public CppUnit::TestCase,
522  public OverlappingTestBase
523 {
524 public:
525  LIBMESH_CPPUNIT_TEST_SUITE( OverlappingAlgebraicGhostingTest );
526 
527  CPPUNIT_TEST( testGhostingCouplingMatrix );
528  CPPUNIT_TEST( testGhostingNullCouplingMatrix );
529 #ifdef LIBMESH_ENABLE_AMR
530  CPPUNIT_TEST( testGhostingNullCouplingMatrixUnifRef );
531 #endif
532 
533  CPPUNIT_TEST_SUITE_END();
534 
535 public:
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 
563 private:
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
643 class OverlappingCouplingGhostingTest : public CppUnit::TestCase,
644  public OverlappingTestBase
645 {
646 public:
647  LIBMESH_CPPUNIT_TEST_SUITE( OverlappingCouplingGhostingTest );
648 
649  CPPUNIT_TEST( testSparsityCouplingMatrix );
650  CPPUNIT_TEST( testSparsityNullCouplingMatrix );
651 #ifdef LIBMESH_ENABLE_AMR
652  CPPUNIT_TEST( testSparsityNullCouplingMatrixUnifRef );
653 #endif
654 
655  CPPUNIT_TEST_SUITE_END();
656 
657 public:
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 
683 private:
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
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition: fe_type.h:196
void run_coupling_functor_test(unsigned int n_refinements)
const DenseMatrix< Number > & get_elem_jacobian() const
Const accessor for element Jacobian.
Definition: diff_context.h:274
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
virtual Node *& set_node(const unsigned int i)
Definition: elem.h:2564
virtual dof_id_type n_active_elem() const =0
This abstract base class defines the interface by which library code and user code can report associa...
virtual void pre_fe_reinit(const System &, const Elem *e)
Reinitializes local data vectors/matrices on the current geometric element.
Definition: fem_context.C:1683
void setup_coupling_matrix(std::unique_ptr< CouplingMatrix > &coupling)
The definition of the const_element_iterator struct.
Definition: mesh_base.h:2521
unsigned int dim
Manages consistently variables, degrees of freedom, coefficient vectors, matrices and linear solvers ...
virtual std::unique_ptr< Partitioner > clone() const override
bool is_attached(SparseMatrix< Number > &matrix)
Matrices should not be attached more than once.
Definition: dof_map.C:295
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
MeshBase & mesh
const Parallel::Communicator & comm() const
Order default_quadrature_order() const
Definition: fe_type.h:415
std::unique_ptr< CouplingMatrix > _coupling_matrix
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?
The libMesh namespace provides an interface to certain functionality in the library.
dof_id_type n_local_dofs() const
Definition: system.C:155
void run_partitioner_test(unsigned int n_refinements)
const MeshBase & get_mesh() const
Definition: system.h:2401
Real distance(const Point &p)
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
dof_id_type n_dofs() const
Definition: system.C:118
This is the MeshBase class.
Definition: mesh_base.h:80
virtual void elem_fe_reinit(const std::vector< Point > *const pts=nullptr)
Reinitializes interior FE objects on the current geometric element.
Definition: fem_context.C:1476
The Partitioner class provides a uniform interface for partitioning algorithms.
Definition: partitioner.h:51
unsigned int variable_number(std::string_view var) const
Definition: system.C:1398
std::unique_ptr< MeshBase > _mesh
std::unique_ptr< EquationSystems > _es
virtual void _do_partition(MeshBase &mesh, const unsigned int n) override
Partition the MeshBase into n subdomains.
std::unique_ptr< PointLocatorBase > _point_locator
virtual std::unique_ptr< GhostingFunctor > clone() const override
A clone() is needed because GhostingFunctor can not be shared between different meshes.
This class handles the numbering of degrees of freedom on a mesh.
Definition: dof_map.h:179
void build_quad_mesh(unsigned int n_refinements=0)
uint8_t processor_id_type
void all_tri(MeshBase &mesh)
Subdivides any non-simplex elements in a Mesh to produce simplex (triangular in 2D, tetrahedral in 3D) elements.
void reinit_send_list(MeshBase &mesh)
Clears the _send_list vector and then rebuilds it.
Definition: dof_map.C:1877
processor_id_type n_processors() const
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.
Implements (adaptive) mesh refinement algorithms for a MeshBase.
CPPUNIT_TEST_SUITE_REGISTRATION(OverlappingFunctorTest)
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)
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition: system.h:98
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
Definition: simple_range.h:57
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition: system.h:1655
void init(triangulateio &t)
Initializes the fields of t to nullptr/0 as necessary.
static std::unique_ptr< FEGenericBase > build(const unsigned int dim, const FEType &type)
Builds a specific finite element type.
libmesh_assert(ctx)
This class provides all data required for a physics package (e.g.
Definition: fem_context.h:62
const std::vector< dof_id_type > & get_dof_indices() const
Accessor for element dof indices.
Definition: diff_context.h:363
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
void run_ghosting_test(const unsigned int n_refinements, bool build_coupling_matrix)
std::vector< T > & get_values()
Definition: dense_matrix.h:382
virtual void close()=0
Calls the SparseMatrix&#39;s internal assembly routines, ensuring that the values are consistent across p...
void set_coupling_matrix(std::unique_ptr< CouplingMatrix > &coupling_matrix)
subdomain_id_type subdomain_id() const
Definition: elem.h:2588
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&#39;s ...
Definition: elem.C:556
void resize(const unsigned int new_m, const unsigned int new_n)
Resizes the matrix to the specified size and calls zero().
Definition: dense_matrix.h:895
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...
void clear_sparsity()
Clears the sparsity pattern.
Definition: dof_map.C:1981
This class implements specific orders of Gauss quadrature.
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< NumericVector< Number > > current_local_solution
All the values I need to compute my contribution to the simulation at hand.
Definition: system.h:1667
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...
Definition: fem_context.h:277
void run_sparsity_pattern_test(const unsigned int n_refinements, bool build_coupling_matrix)
unsigned int n_vars() const
Definition: system.C:2674
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 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
const DofMap & get_dof_map() const
Definition: system.h:2417
processor_id_type processor_id() const
Definition: dof_object.h:881
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
const SparseMatrix< Number > & get_system_matrix() const
const std::vector< dof_id_type > & get_send_list() const
Definition: dof_map.h:533
uint8_t dof_id_type
Definition: id_types.h:67
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.
void uniformly_refine(unsigned int n=1)
Uniformly refines the mesh n times.