libmesh解析
本工作只是尝试解析原libmesh的代码,供学习使用
 全部  命名空间 文件 函数 变量 类型定义 枚举 枚举值 友元 
sparsity_pattern.C
浏览该文件的文档.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2023 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 
19 
20 // Local includes
21 #include "libmesh/sparsity_pattern.h"
22 
23 // libMesh includes
24 #include "libmesh/coupling_matrix.h"
25 #include "libmesh/dof_map.h"
26 #include "libmesh/elem.h"
27 #include "libmesh/ghosting_functor.h"
28 #include "libmesh/hashword.h"
29 #include "libmesh/parallel_algebra.h"
30 #include "libmesh/parallel.h"
31 #include "libmesh/parallel_sync.h"
32 #include "libmesh/utility.h"
33 
34 // TIMPI includes
35 #include "timpi/communicator.h"
36 
37 
38 namespace libMesh
39 {
40 namespace SparsityPattern
41 {
42 
43 //-------------------------------------------------------
44 // we need to implement these constructors here so that
45 // a full DofMap definition is available.
46 Build::Build (const DofMap & dof_map_in,
47  const CouplingMatrix * dof_coupling_in,
48  const std::set<GhostingFunctor *> & coupling_functors_in,
49  const bool implicit_neighbor_dofs_in,
50  const bool need_full_sparsity_pattern_in,
51  const bool calculate_constrained_in) :
52  ParallelObject(dof_map_in),
53  dof_map(dof_map_in),
54  dof_coupling(dof_coupling_in),
55  coupling_functors(coupling_functors_in),
56  implicit_neighbor_dofs(implicit_neighbor_dofs_in),
57  need_full_sparsity_pattern(need_full_sparsity_pattern_in),
58  calculate_constrained(calculate_constrained_in),
59  sparsity_pattern(),
60  nonlocal_pattern(),
61  n_nz(),
62  n_oz()
63 {}
64 
65 
66 
67 Build::Build (Build & other, Threads::split) :
68  ParallelObject(other),
69  dof_map(other.dof_map),
70  dof_coupling(other.dof_coupling),
71  coupling_functors(other.coupling_functors),
72  implicit_neighbor_dofs(other.implicit_neighbor_dofs),
73  need_full_sparsity_pattern(other.need_full_sparsity_pattern),
74  calculate_constrained(other.calculate_constrained),
75  hashed_dof_sets(other.hashed_dof_sets),
76  sparsity_pattern(),
77  nonlocal_pattern(),
78  n_nz(),
79  n_oz()
80 {}
81 
82 
83 
84 #if defined(__GNUC__) && (__GNUC__ < 4) && !defined(__INTEL_COMPILER)
85 
86 void _dummy_function(void) {}
87 
88 #endif
89 
90 
91 
92 void Build::sorted_connected_dofs(const Elem * elem,
93  std::vector<dof_id_type> & dofs_vi,
94  unsigned int vi)
95 {
96  dof_map.dof_indices (elem, dofs_vi, vi);
97 #ifdef LIBMESH_ENABLE_CONSTRAINTS
98  dof_map.find_connected_dofs (dofs_vi);
99 #endif
100  // We can be more efficient if we sort the element DOFs into
101  // increasing order
102  std::sort(dofs_vi.begin(), dofs_vi.end());
103 
104  // Handle cases where duplicate nodes are intentionally assigned to
105  // a single element.
106  dofs_vi.erase(std::unique(dofs_vi.begin(), dofs_vi.end()), dofs_vi.end());
107 }
108 
109 
110 
111 void Build::handle_vi_vj(const std::vector<dof_id_type> & element_dofs_i,
112  const std::vector<dof_id_type> & element_dofs_j)
113 {
114  const unsigned int n_dofs_on_element_i =
115  cast_int<unsigned int>(element_dofs_i.size());
116 
117  const processor_id_type proc_id = dof_map.processor_id();
118  const dof_id_type first_dof_on_proc = dof_map.first_dof(proc_id);
119  const dof_id_type end_dof_on_proc = dof_map.end_dof(proc_id);
120 
121  std::vector<dof_id_type>
122  dofs_to_add;
123 
124  const unsigned int n_dofs_on_element_j =
125  cast_int<unsigned int>(element_dofs_j.size());
126 
127  // It only makes sense to compute hashes and see if we can skip
128  // doing work when there are a "large" amount of DOFs for a given
129  // element. The cutoff for "large" is somewhat arbitrarily chosen
130  // based on a test case with a spider node that resulted in O(10^3)
131  // entries in element_dofs_i for O(10^3) elements. Making this
132  // number larger will disable the hashing optimization in more
133  // cases.
134  bool dofs_seen = false;
135  if (n_dofs_on_element_j > 0 && n_dofs_on_element_i > 256)
136  {
137  auto hash_i = Utility::hashword(element_dofs_i);
138  auto hash_j = Utility::hashword(element_dofs_j);
139  auto final_hash = Utility::hashword2(hash_i, hash_j);
140  auto result = hashed_dof_sets.insert(final_hash);
141  // if insert failed, we have already seen these dofs
142  dofs_seen = !result.second;
143  }
144 
145  // there might be 0 dofs for the other variable on the same element
146  // (when subdomain variables do not overlap) and that's when we do
147  // not do anything
148  if (n_dofs_on_element_j > 0 && !dofs_seen)
149  {
150  for (unsigned int i=0; i<n_dofs_on_element_i; i++)
151  {
152  const dof_id_type ig = element_dofs_i[i];
153 
154  SparsityPattern::Row * row;
155 
156  // We save non-local row components for now so we can
157  // communicate them to other processors later.
158 
159  if ((ig >= first_dof_on_proc) &&
160  (ig < end_dof_on_proc))
161  {
162  // This is what I mean
163  // libmesh_assert_greater_equal ((ig - first_dof_on_proc), 0);
164  // but do the test like this because ig and
165  // first_dof_on_proc are unsigned ints
166  libmesh_assert_greater_equal (ig, first_dof_on_proc);
167  libmesh_assert_less (ig, (sparsity_pattern.size() +
168  first_dof_on_proc));
169 
170  row = &sparsity_pattern[ig - first_dof_on_proc];
171  }
172  else
173  {
174  row = &nonlocal_pattern[ig];
175  }
176 
177  // If the row is empty we will add *all*
178  // the element j DOFs, so just do that.
179  if (row->empty())
180  {
181  row->insert(row->end(),
182  element_dofs_j.begin(),
183  element_dofs_j.end());
184  }
185  else
186  {
187  // Build a list of the DOF indices not found in the
188  // sparsity pattern
189  dofs_to_add.clear();
190 
191  // Cache iterators. Low will move forward, subsequent
192  // searches will be on smaller ranges
193  SparsityPattern::Row::iterator
194  low = std::lower_bound
195  (row->begin(), row->end(), element_dofs_j.front()),
196  high = std::upper_bound
197  (low, row->end(), element_dofs_j.back());
198 
199  for (unsigned int j=0; j<n_dofs_on_element_j; j++)
200  {
201  const dof_id_type jg = element_dofs_j[j];
202 
203  // See if jg is in the sorted range
204  std::pair<SparsityPattern::Row::iterator,
205  SparsityPattern::Row::iterator>
206  pos = std::equal_range (low, high, jg);
207 
208  // Must add jg if it wasn't found
209  if (pos.first == pos.second)
210  dofs_to_add.push_back(jg);
211 
212  // pos.first is now a valid lower bound for any
213  // remaining element j DOFs. (That's why we sorted them.)
214  // Use it for the next search
215  low = pos.first;
216  }
217 
218  // Add to the sparsity pattern
219  if (!dofs_to_add.empty())
220  {
221  const std::size_t old_size = row->size();
222 
223  row->insert (row->end(),
224  dofs_to_add.begin(),
225  dofs_to_add.end());
226 
228  (row->begin(), row->begin()+old_size,
229  row->end());
230  }
231  }
232  } // End dofs-of-var-i loop
233  } // End if-dofs-of-var-j
234 }
235 
236 
237 
238 void Build::operator()(const ConstElemRange & range)
239 {
240  // Compute the sparsity structure of the global matrix. This can be
241  // fed into a PetscMatrix to allocate exactly the number of nonzeros
242  // necessary to store the matrix. This algorithm should be linear
243  // in the (# of elements)*(# nodes per element)
244  const processor_id_type proc_id = dof_map.processor_id();
245  const dof_id_type n_dofs_on_proc = dof_map.n_dofs_on_processor(proc_id);
246  const dof_id_type first_dof_on_proc = dof_map.first_dof(proc_id);
247  const dof_id_type end_dof_on_proc = dof_map.end_dof(proc_id);
248 
249  sparsity_pattern.resize(n_dofs_on_proc);
250 
251  // Handle dof coupling specified by library and user coupling functors
252  {
253  const unsigned int n_var = dof_map.n_variables();
254 
255  std::vector<std::vector<dof_id_type> > element_dofs_i(n_var);
256 
257  std::vector<const Elem *> coupled_neighbors;
258  for (const auto & elem : range)
259  {
260  // Make some fake element iterators defining a range
261  // pointing to only this element.
262  Elem * const * elempp = const_cast<Elem * const *>(&elem);
263  Elem * const * elemend = elempp+1;
264 
265  const MeshBase::const_element_iterator fake_elem_it =
266  MeshBase::const_element_iterator(elempp,
267  elemend,
269 
270  const MeshBase::const_element_iterator fake_elem_end =
271  MeshBase::const_element_iterator(elemend,
272  elemend,
274 
275  GhostingFunctor::map_type elements_to_couple;
276  DofMap::CouplingMatricesSet temporary_coupling_matrices;
277 
278  dof_map.merge_ghost_functor_outputs(elements_to_couple,
279  temporary_coupling_matrices,
282  fake_elem_it,
283  fake_elem_end,
285  for (unsigned int vi=0; vi<n_var; vi++)
286  this->sorted_connected_dofs(elem, element_dofs_i[vi], vi);
287 
288  for (unsigned int vi=0; vi<n_var; vi++)
289  for (const auto & [partner, ghost_coupling] : elements_to_couple)
290  {
291  // Loop over coupling matrix row variables if we have a
292  // coupling matrix, or all variables if not.
293  if (ghost_coupling)
294  {
295  libmesh_assert_equal_to (ghost_coupling->size(), n_var);
296  ConstCouplingRow ccr(vi, *ghost_coupling);
297 
298  for (const auto & idx : ccr)
299  {
300  if (partner == elem)
301  this->handle_vi_vj(element_dofs_i[vi], element_dofs_i[idx]);
302  else
303  {
304  std::vector<dof_id_type> partner_dofs;
305  this->sorted_connected_dofs(partner, partner_dofs, idx);
306  this->handle_vi_vj(element_dofs_i[vi], partner_dofs);
307  }
308  }
309  }
310  else
311  {
312  for (unsigned int vj = 0; vj != n_var; ++vj)
313  {
314  if (partner == elem)
315  this->handle_vi_vj(element_dofs_i[vi], element_dofs_i[vj]);
316  else
317  {
318  std::vector<dof_id_type> partner_dofs;
319  this->sorted_connected_dofs(partner, partner_dofs, vj);
320  this->handle_vi_vj(element_dofs_i[vi], partner_dofs);
321  }
322  }
323  }
324  } // End ghosted element loop
325  } // End range element loop
326  } // End ghosting functor section
327 
328  // Now a new chunk of sparsity structure is built for all of the
329  // DOFs connected to our rows of the matrix.
330 
331  // If we're building a full sparsity pattern, then we've got
332  // complete rows to work with, so we can just count them from
333  // scratch.
335  {
336  n_nz.clear();
337  n_oz.clear();
338  }
339 
340  n_nz.resize (n_dofs_on_proc, 0);
341  n_oz.resize (n_dofs_on_proc, 0);
342 
343  for (dof_id_type i=0; i<n_dofs_on_proc; i++)
344  {
345  // Get the row of the sparsity pattern
347 
348  for (const auto & df : row)
349  if ((df < first_dof_on_proc) || (df >= end_dof_on_proc))
350  n_oz[i]++;
351  else
352  n_nz[i]++;
353 
354  // If we're not building a full sparsity pattern, then we want
355  // to avoid overcounting these entries as much as possible.
357  row.clear();
358  }
359 }
360 
361 
362 
364 {
365  const processor_id_type proc_id = dof_map.processor_id();
366  const dof_id_type n_global_dofs = dof_map.n_dofs();
367  const dof_id_type n_dofs_on_proc = dof_map.n_dofs_on_processor(proc_id);
368  const dof_id_type first_dof_on_proc = dof_map.first_dof(proc_id);
369  const dof_id_type end_dof_on_proc = dof_map.end_dof(proc_id);
370 
371  libmesh_assert_equal_to (sparsity_pattern.size(), other.sparsity_pattern.size());
372  libmesh_assert_equal_to (n_nz.size(), sparsity_pattern.size());
373  libmesh_assert_equal_to (n_oz.size(), sparsity_pattern.size());
374 
375  for (dof_id_type r=0; r<n_dofs_on_proc; r++)
376  {
377  // increment the number of on and off-processor nonzeros in this row
378  // (note this will be an upper bound unless we need the full sparsity pattern)
380  {
382  const SparsityPattern::Row & their_row = other.sparsity_pattern[r];
383 
384  // simple copy if I have no dofs
385  if (my_row.empty())
386  my_row = their_row;
387 
388  // otherwise add their DOFs to mine, resort, and re-unique the row
389  else if (!their_row.empty()) // do nothing for the trivial case where
390  { // their row is empty
391  my_row.insert (my_row.end(),
392  their_row.begin(),
393  their_row.end());
394 
395  // We cannot use SparsityPattern::sort_row() here because it expects
396  // the [begin,middle) [middle,end) to be non-overlapping. This is not
397  // necessarily the case here, so use std::sort()
398  std::sort (my_row.begin(), my_row.end());
399 
400  my_row.erase(std::unique (my_row.begin(), my_row.end()), my_row.end());
401  }
402 
403  // fix the number of on and off-processor nonzeros in this row
404  n_nz[r] = n_oz[r] = 0;
405 
406  for (const auto & df : my_row)
407  if ((df < first_dof_on_proc) || (df >= end_dof_on_proc))
408  n_oz[r]++;
409  else
410  n_nz[r]++;
411  }
412  else
413  {
414  n_nz[r] += other.n_nz[r];
415  n_nz[r] = std::min(n_nz[r], n_dofs_on_proc);
416  n_oz[r] += other.n_oz[r];
417  n_oz[r] =std::min(n_oz[r], static_cast<dof_id_type>(n_global_dofs-n_nz[r]));
418  }
419  }
420 
421  // Move nonlocal row information to ourselves; the other thread
422  // won't need it in the map after that.
423  for (const auto & p : other.nonlocal_pattern)
424  {
425 #ifndef NDEBUG
426  const dof_id_type dof_id = p.first;
427 
428  processor_id_type dbg_proc_id = 0;
429  while (dof_id >= dof_map.end_dof(dbg_proc_id))
430  dbg_proc_id++;
431  libmesh_assert (dbg_proc_id != this->processor_id());
432 #endif
433 
434  const SparsityPattern::Row & their_row = p.second;
435 
436  // We should have no empty values in a map
437  libmesh_assert (!their_row.empty());
438 
439  NonlocalGraph::iterator my_it = nonlocal_pattern.find(p.first);
440  if (my_it == nonlocal_pattern.end())
441  {
442  // nonlocal_pattern[it->first].swap(their_row);
443  nonlocal_pattern[p.first] = their_row;
444  }
445  else
446  {
447  SparsityPattern::Row & my_row = my_it->second;
448 
449  my_row.insert (my_row.end(),
450  their_row.begin(),
451  their_row.end());
452 
453  // We cannot use SparsityPattern::sort_row() here because it expects
454  // the [begin,middle) [middle,end) to be non-overlapping. This is not
455  // necessarily the case here, so use std::sort()
456  std::sort (my_row.begin(), my_row.end());
457 
458  my_row.erase(std::unique (my_row.begin(), my_row.end()), my_row.end());
459  }
460  }
461 
462  // Combine the other thread's hashed_dof_sets with ours.
463  hashed_dof_sets.insert(other.hashed_dof_sets.begin(),
464  other.hashed_dof_sets.end());
465 }
466 
467 
468 
470 {
471  parallel_object_only();
472  libmesh_assert(this->comm().verify(need_full_sparsity_pattern));
473 
474  auto & comm = this->comm();
475  auto my_pid = comm.rank();
476 
477  const auto n_global_dofs = dof_map.n_dofs();
478  const auto n_dofs_on_proc = dof_map.n_dofs_on_processor(my_pid);
479  const auto local_first_dof = dof_map.first_dof();
480  const auto local_end_dof = dof_map.end_dof();
481 
482  // The data to send
483  std::map<processor_id_type, std::vector<dof_id_type>> ids_to_send;
484  std::map<processor_id_type, std::vector<Row>> rows_to_send;
485 
486  // Loop over the nonlocal rows and transform them into the new datastructure
487  NonlocalGraph::iterator it = nonlocal_pattern.begin();
488  while (it != nonlocal_pattern.end())
489  {
490  const auto dof_id = it->first;
491  auto & row = it->second;
492 
493  processor_id_type proc_id = 0;
494  while (dof_id >= dof_map.end_dof(proc_id))
495  proc_id++;
496 
497  ids_to_send[proc_id].push_back(dof_id);
498 
499  // Note this invalidates the data in nonlocal_pattern
500  rows_to_send[proc_id].push_back(std::move(row));
501 
502  // Might as well remove it since it's invalidated anyway
503  it = nonlocal_pattern.erase(it);
504  }
505 
506  std::map<processor_id_type, std::vector<dof_id_type>> received_ids_map;
507 
508  auto ids_action_functor =
509  [& received_ids_map]
510  (processor_id_type pid,
511  const std::vector<dof_id_type> & received_ids)
512  {
513  received_ids_map.emplace(pid, received_ids);
514  };
515 
516  Parallel::push_parallel_vector_data(this->comm(), ids_to_send,
517  ids_action_functor);
518 
519  auto rows_action_functor =
520  [this,
521  & received_ids_map,
522  n_global_dofs,
523  n_dofs_on_proc,
524  local_first_dof,
525  local_end_dof]
526  (processor_id_type pid,
527  const std::vector<Row> & received_rows)
528  {
529  const std::vector<dof_id_type> & received_ids = libmesh_map_find(received_ids_map, pid);
530 
531  std::size_t n_rows = received_rows.size();
532  libmesh_assert_equal_to(n_rows, received_ids.size());
533 
534  for (auto i : IntRange<std::size_t>(0, n_rows))
535  {
536  const auto r = received_ids[i];
537  libmesh_assert(dof_map.local_index(r));
538 
539  const auto my_r = r - local_first_dof;
540 
541  auto & their_row = received_rows[i];
542 
544  {
545  auto & my_row = sparsity_pattern[my_r];
546 
547  // They wouldn't have sent an empty row
548  libmesh_assert(!their_row.empty());
549 
550  // We can end up with an empty row on a dof that touches our
551  // inactive elements but not our active ones
552  if (my_row.empty())
553  {
554  my_row.assign (their_row.begin(), their_row.end());
555  }
556  else
557  {
558  my_row.insert (my_row.end(),
559  their_row.begin(),
560  their_row.end());
561 
562  // We cannot use SparsityPattern::sort_row() here because it expects
563  // the [begin,middle) [middle,end) to be non-overlapping. This is not
564  // necessarily the case here, so use std::sort()
565  std::sort (my_row.begin(), my_row.end());
566 
567  my_row.erase(std::unique (my_row.begin(), my_row.end()), my_row.end());
568  }
569 
570  // fix the number of on and off-processor nonzeros in this row
571  n_nz[my_r] = n_oz[my_r] = 0;
572 
573  for (const auto & df : my_row)
574  if ((df < local_first_dof) || (df >= local_end_dof))
575  n_oz[my_r]++;
576  else
577  n_nz[my_r]++;
578  }
579  else
580  {
581  for (const auto & df : their_row)
582  if ((df < local_first_dof) || (df >= local_end_dof))
583  n_oz[my_r]++;
584  else
585  n_nz[my_r]++;
586 
587  n_nz[my_r] = std::min(n_nz[my_r], n_dofs_on_proc);
588  n_oz[my_r] = std::min(n_oz[my_r],
589  static_cast<dof_id_type>(n_global_dofs-n_nz[my_r]));
590  }
591  }
592  };
593 
594  Parallel::push_parallel_vector_data(this->comm(), rows_to_send,
595  rows_action_functor);
596 
597  // We should have sent everything at this point.
598  libmesh_assert (nonlocal_pattern.empty());
599 }
600 
601 
603 {
605 }
606 
607 
608 } // namespace SparsityPattern
609 } // namespace libMesh
SparsityPattern::Graph sparsity_pattern
This helper class can be called on multiple threads to compute the sparsity pattern (or graph) of the...
std::set< GhostingFunctor * >::const_iterator coupling_functors_begin() const
Beginning of range of coupling functors.
Definition: dof_map.h:344
bool local_index(dof_id_type dof_index) const
Definition: dof_map.h:831
void parallel_sync()
Send sparsity pattern data relevant to other processors to those processors, and receive and incorpor...
dof_id_type n_dofs() const
Definition: dof_map.h:659
std::unordered_set< dof_id_type > hashed_dof_sets
Build(const DofMap &dof_map_in, const CouplingMatrix *dof_coupling_in, const std::set< GhostingFunctor * > &coupling_functors_in, const bool implicit_neighbor_dofs_in, const bool need_full_sparsity_pattern_in, const bool calculate_constrained_in=false)
Used to iterate over non-nullptr entries in a container.
uint8_t processor_id_type
Definition: id_types.h:104
dof_id_type n_dofs_on_processor(const processor_id_type proc) const
Definition: dof_map.h:675
This class handles the numbering of degrees of freedom on a mesh.
Definition: dof_map.h:169
std::vector< dof_id_type, Threads::scalable_allocator< dof_id_type > > Row
dof_id_type first_dof(const processor_id_type proc) const
Definition: dof_map.h:684
dof_id_type end_dof(const processor_id_type proc) const
Definition: dof_map.h:708
void join(const Build &other)
Combine the sparsity pattern in other with this object&#39;s sparsity pattern.
std::set< std::unique_ptr< CouplingMatrix >, Utility::CompareUnderlying > CouplingMatricesSet
Definition: dof_map.h:1742
static const 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:488
void sorted_connected_dofs(const Elem *elem, std::vector< dof_id_type > &dofs_vi, unsigned int vi)
std::vector< dof_id_type > n_nz
void apply_extra_sparsity_object(SparsityPattern::AugmentSparsityPattern &asp)
Let a user-provided AugmentSparsityPattern subclass modify our sparsity structure.
SparsityPattern::NonlocalGraph nonlocal_pattern
unsigned int n_variables() const
Definition: dof_map.h:621
static void merge_ghost_functor_outputs(GhostingFunctor::map_type &elements_to_ghost, CouplingMatricesSet &temporary_coupling_matrices, const std::set< GhostingFunctor * >::iterator &gf_begin, const std::set< GhostingFunctor * >::iterator &gf_end, const MeshBase::const_element_iterator &elems_begin, const MeshBase::const_element_iterator &elems_end, processor_id_type p)
Definition: dof_map.C:1440
void find_connected_dofs(std::vector< dof_id_type > &elem_dofs) const
Finds all the DOFS associated with the element DOFs elem_dofs.
Definition: dof_map.C:2852
void handle_vi_vj(const std::vector< dof_id_type > &element_dofs_i, const std::vector< dof_id_type > &element_dofs_j)
void operator()(const ConstElemRange &range)
Add entries from a range of elements to this object&#39;s sparsity pattern.
std::set< GhostingFunctor * >::const_iterator coupling_functors_end() const
End of range of coupling functors.
Definition: dof_map.h:350
virtual void augment_sparsity_pattern(SparsityPattern::Graph &sparsity, std::vector< dof_id_type > &n_nz, std::vector< dof_id_type > &n_oz)=0
User-defined function to augment the sparsity pattern.
Abstract base class to be used to add user-defined implicit degree of freedom couplings.
uint8_t dof_id_type
Definition: id_types.h:67
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
Fills the vector di with the global degree of freedom indices for the element.
Definition: dof_map.C:1992
static void sort_row(const BidirectionalIterator begin, BidirectionalIterator middle, const BidirectionalIterator end)
Splices the two sorted ranges [begin,middle) and [middle,end) into one sorted range [begin...
std::vector< dof_id_type > n_oz