libMesh
sparsity_pattern.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 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 #include "libmesh/static_condensation_dof_map.h"
34 
35 // TIMPI includes
36 #include "timpi/communicator.h"
37 
38 
39 namespace libMesh
40 {
41 namespace SparsityPattern
42 {
43 
44 //-------------------------------------------------------
45 // we need to implement these constructors here so that
46 // a full DofMap definition is available.
47 Build::Build (const DofMap & dof_map_in,
48  const CouplingMatrix * dof_coupling_in,
49  const std::set<GhostingFunctor *> & coupling_functors_in,
50  const bool implicit_neighbor_dofs_in,
51  const bool need_full_sparsity_pattern_in,
52  const bool calculate_constrained_in,
53  const StaticCondensationDofMap * const sc_in) :
54  ParallelObject(dof_map_in),
55  dof_map(dof_map_in),
56  dof_coupling(dof_coupling_in),
57  coupling_functors(coupling_functors_in),
58  implicit_neighbor_dofs(implicit_neighbor_dofs_in),
59  need_full_sparsity_pattern(need_full_sparsity_pattern_in),
60  calculate_constrained(calculate_constrained_in),
61  sc(sc_in),
62  sparsity_pattern(),
63  nonlocal_pattern(),
64  n_nz(),
65  n_oz()
66 {}
67 
68 
69 
71  ParallelObject(other),
72  dof_map(other.dof_map),
73  dof_coupling(other.dof_coupling),
74  coupling_functors(other.coupling_functors),
75  implicit_neighbor_dofs(other.implicit_neighbor_dofs),
76  need_full_sparsity_pattern(other.need_full_sparsity_pattern),
77  calculate_constrained(other.calculate_constrained),
78  sc(other.sc),
79  hashed_dof_sets(other.hashed_dof_sets),
80  sparsity_pattern(),
81  nonlocal_pattern(),
82  n_nz(),
83  n_oz()
84 {}
85 
86 
87 
88 #if defined(__GNUC__) && (__GNUC__ < 4) && !defined(__INTEL_COMPILER)
89 
90 void _dummy_function(void) {}
91 
92 #endif
93 
94 
95 
97  std::vector<dof_id_type> & dofs_vi,
98  unsigned int vi)
99 {
100  if (this->sc)
101  {
102  // We build a sparsity pattern that will match the size of the condensed system. This is so that
103  // we have the data necessary to init the reduced system matrix
104  dofs_vi.clear();
105 
106  auto total_and_uncondensed_from_scalar_dofs_functor =
107  [&dofs_vi](const Elem & /*elem*/,
108  std::vector<dof_id_type> & dof_indices,
109  const std::vector<dof_id_type> & scalar_dof_indices)
110  {
111  dof_indices.insert(dof_indices.end(), scalar_dof_indices.begin(), scalar_dof_indices.end());
112  dofs_vi.insert(dofs_vi.end(), scalar_dof_indices.begin(), scalar_dof_indices.end());
113  };
114 
115  auto total_and_uncondensed_from_field_dofs_functor =
116  [&dofs_vi, this](const Elem & functor_elem,
117  const unsigned int node_num,
118  const unsigned int var_num,
119  std::vector<dof_id_type> & dof_indices,
120  const dof_id_type field_dof)
121  {
122  dof_indices.push_back(field_dof);
123  if (this->sc->uncondensed_vars().count(var_num) ||
124  (node_num != invalid_uint && !functor_elem.is_internal(node_num)))
125  dofs_vi.push_back(field_dof);
126  };
127 
128  dof_map.dof_indices(elem,
129  dummy_vec,
130  vi,
131  total_and_uncondensed_from_scalar_dofs_functor,
132  total_and_uncondensed_from_field_dofs_functor,
133  elem->p_level());
134  }
135  else
136  dof_map.dof_indices (elem, dofs_vi, vi);
137 
138 #ifdef LIBMESH_ENABLE_CONSTRAINTS
139  dof_map.find_connected_dofs (dofs_vi);
140 #endif
141  // We can be more efficient if we sort the element DOFs into
142  // increasing order
143  std::sort(dofs_vi.begin(), dofs_vi.end());
144 
145  // Handle cases where duplicate nodes are intentionally assigned to
146  // a single element.
147  dofs_vi.erase(std::unique(dofs_vi.begin(), dofs_vi.end()), dofs_vi.end());
148 }
149 
150 
151 
152 void Build::handle_vi_vj(const std::vector<dof_id_type> & element_dofs_i,
153  const std::vector<dof_id_type> & element_dofs_j)
154 {
155  const unsigned int n_dofs_on_element_i =
156  cast_int<unsigned int>(element_dofs_i.size());
157 
158  const processor_id_type proc_id = dof_map.processor_id();
159  const dof_id_type first_dof_on_proc = dof_map.first_dof(proc_id);
160  const dof_id_type end_dof_on_proc = dof_map.end_dof(proc_id);
161 
162  std::vector<dof_id_type>
163  dofs_to_add;
164 
165  const unsigned int n_dofs_on_element_j =
166  cast_int<unsigned int>(element_dofs_j.size());
167 
168  // It only makes sense to compute hashes and see if we can skip
169  // doing work when there are a "large" amount of DOFs for a given
170  // element. The cutoff for "large" is somewhat arbitrarily chosen
171  // based on a test case with a spider node that resulted in O(10^3)
172  // entries in element_dofs_i for O(10^3) elements. Making this
173  // number larger will disable the hashing optimization in more
174  // cases.
175  bool dofs_seen = false;
176  if (n_dofs_on_element_j > 0 && n_dofs_on_element_i > 256)
177  {
178  auto hash_i = Utility::hashword(element_dofs_i);
179  auto hash_j = Utility::hashword(element_dofs_j);
180  auto final_hash = Utility::hashword2(hash_i, hash_j);
181  auto result = hashed_dof_sets.insert(final_hash);
182  // if insert failed, we have already seen these dofs
183  dofs_seen = !result.second;
184  }
185 
186  // there might be 0 dofs for the other variable on the same element
187  // (when subdomain variables do not overlap) and that's when we do
188  // not do anything
189  if (n_dofs_on_element_j > 0 && !dofs_seen)
190  {
191  for (unsigned int i=0; i<n_dofs_on_element_i; i++)
192  {
193  const dof_id_type ig = element_dofs_i[i];
194 
195  SparsityPattern::Row * row;
196 
197  // We save non-local row components for now so we can
198  // communicate them to other processors later.
199 
200  if ((ig >= first_dof_on_proc) &&
201  (ig < end_dof_on_proc))
202  {
203  // This is what I mean
204  // libmesh_assert_greater_equal ((ig - first_dof_on_proc), 0);
205  // but do the test like this because ig and
206  // first_dof_on_proc are unsigned ints
207  libmesh_assert_greater_equal (ig, first_dof_on_proc);
208  libmesh_assert_less (ig, (sparsity_pattern.size() +
209  first_dof_on_proc));
210 
211  row = &sparsity_pattern[ig - first_dof_on_proc];
212  }
213  else
214  {
215  row = &nonlocal_pattern[ig];
216  }
217 
218  // If the row is empty we will add *all*
219  // the element j DOFs, so just do that.
220  if (row->empty())
221  {
222  row->insert(row->end(),
223  element_dofs_j.begin(),
224  element_dofs_j.end());
225  }
226  else
227  {
228  // Build a list of the DOF indices not found in the
229  // sparsity pattern
230  dofs_to_add.clear();
231 
232  // Cache iterators. Low will move forward, subsequent
233  // searches will be on smaller ranges
234  SparsityPattern::Row::iterator
235  low = std::lower_bound
236  (row->begin(), row->end(), element_dofs_j.front()),
237  high = std::upper_bound
238  (low, row->end(), element_dofs_j.back());
239 
240  for (unsigned int j=0; j<n_dofs_on_element_j; j++)
241  {
242  const dof_id_type jg = element_dofs_j[j];
243 
244  // See if jg is in the sorted range
245  std::pair<SparsityPattern::Row::iterator,
246  SparsityPattern::Row::iterator>
247  pos = std::equal_range (low, high, jg);
248 
249  // Must add jg if it wasn't found
250  if (pos.first == pos.second)
251  dofs_to_add.push_back(jg);
252 
253  // pos.first is now a valid lower bound for any
254  // remaining element j DOFs. (That's why we sorted them.)
255  // Use it for the next search
256  low = pos.first;
257  }
258 
259  // Add to the sparsity pattern
260  if (!dofs_to_add.empty())
261  {
262  const std::size_t old_size = row->size();
263 
264  row->insert (row->end(),
265  dofs_to_add.begin(),
266  dofs_to_add.end());
267 
269  (row->begin(), row->begin()+old_size,
270  row->end());
271  }
272  }
273  } // End dofs-of-var-i loop
274  } // End if-dofs-of-var-j
275 }
276 
277 
278 
280 {
281  // Compute the sparsity structure of the global matrix. This can be
282  // fed into a PetscMatrixBase to allocate exactly the number of nonzeros
283  // necessary to store the matrix. This algorithm should be linear
284  // in the (# of elements)*(# nodes per element)
286 
287  // Handle dof coupling specified by library and user coupling functors
288  {
289  const unsigned int n_var = dof_map.n_variables();
290 
291  std::vector<std::vector<dof_id_type> > element_dofs_i(n_var);
292 
293  std::vector<const Elem *> coupled_neighbors;
294  for (const auto & elem : range)
295  {
296  // Make some fake element iterators defining a range
297  // pointing to only this element.
298  Elem * const * elempp = const_cast<Elem * const *>(&elem);
299  Elem * const * elemend = elempp+1;
300 
301  const MeshBase::const_element_iterator fake_elem_it =
303  elemend,
305 
306  const MeshBase::const_element_iterator fake_elem_end =
308  elemend,
310 
311  GhostingFunctor::map_type elements_to_couple;
312  DofMap::CouplingMatricesSet temporary_coupling_matrices;
313 
314  dof_map.merge_ghost_functor_outputs(elements_to_couple,
315  temporary_coupling_matrices,
318  fake_elem_it,
319  fake_elem_end,
321  for (unsigned int vi=0; vi<n_var; vi++)
322  this->sorted_connected_dofs(elem, element_dofs_i[vi], vi);
323 
324  for (unsigned int vi=0; vi<n_var; vi++)
325  for (const auto & [partner, ghost_coupling] : elements_to_couple)
326  {
327  // Loop over coupling matrix row variables if we have a
328  // coupling matrix, or all variables if not.
329  if (ghost_coupling)
330  {
331  libmesh_assert_equal_to (ghost_coupling->size(), n_var);
332  ConstCouplingRow ccr(vi, *ghost_coupling);
333 
334  for (const auto & idx : ccr)
335  {
336  if (partner == elem)
337  this->handle_vi_vj(element_dofs_i[vi], element_dofs_i[idx]);
338  else
339  {
340  std::vector<dof_id_type> partner_dofs;
341  this->sorted_connected_dofs(partner, partner_dofs, idx);
342  this->handle_vi_vj(element_dofs_i[vi], partner_dofs);
343  }
344  }
345  }
346  else
347  {
348  for (unsigned int vj = 0; vj != n_var; ++vj)
349  {
350  if (partner == elem)
351  this->handle_vi_vj(element_dofs_i[vi], element_dofs_i[vj]);
352  else
353  {
354  std::vector<dof_id_type> partner_dofs;
355  this->sorted_connected_dofs(partner, partner_dofs, vj);
356  this->handle_vi_vj(element_dofs_i[vi], partner_dofs);
357  }
358  }
359  }
360  } // End ghosted element loop
361  } // End range element loop
362  } // End ghosting functor section
363 }
364 
365 
366 
368 {
369  libmesh_assert_equal_to (sparsity_pattern.size(), other.sparsity_pattern.size());
370 
371  for (dof_id_type r=0; r<dof_map.n_local_dofs(); r++)
372  {
373  // increment the number of on and off-processor nonzeros in this row
374  // (note this will be an upper bound unless we need the full sparsity pattern)
376  const SparsityPattern::Row & their_row = other.sparsity_pattern[r];
377 
378  // simple copy if I have no dofs
379  if (my_row.empty())
380  my_row = their_row;
381 
382  // otherwise add their DOFs to mine, resort, and re-unique the row
383  else if (!their_row.empty()) // do nothing for the trivial case where
384  { // their row is empty
385  my_row.insert (my_row.end(),
386  their_row.begin(),
387  their_row.end());
388 
389  // We cannot use SparsityPattern::sort_row() here because it expects
390  // the [begin,middle) [middle,end) to be non-overlapping. This is not
391  // necessarily the case here, so use std::sort()
392  std::sort (my_row.begin(), my_row.end());
393 
394  my_row.erase(std::unique (my_row.begin(), my_row.end()), my_row.end());
395  }
396  }
397 
398  // Move nonlocal row information to ourselves; the other thread
399  // won't need it in the map after that.
400  for (const auto & p : other.nonlocal_pattern)
401  {
402 #ifndef NDEBUG
403  const dof_id_type dof_id = p.first;
404 
405  processor_id_type dbg_proc_id = 0;
406  while (dof_id >= dof_map.end_dof(dbg_proc_id))
407  dbg_proc_id++;
408  libmesh_assert (dbg_proc_id != this->processor_id());
409 #endif
410 
411  const SparsityPattern::Row & their_row = p.second;
412 
413  // We should have no empty values in a map
414  libmesh_assert (!their_row.empty());
415 
416  if (auto my_it = nonlocal_pattern.find(p.first);
417  my_it == nonlocal_pattern.end())
418  {
419  // nonlocal_pattern[it->first].swap(their_row);
420  nonlocal_pattern[p.first] = their_row;
421  }
422  else
423  {
424  SparsityPattern::Row & my_row = my_it->second;
425 
426  my_row.insert (my_row.end(),
427  their_row.begin(),
428  their_row.end());
429 
430  // We cannot use SparsityPattern::sort_row() here because it expects
431  // the [begin,middle) [middle,end) to be non-overlapping. This is not
432  // necessarily the case here, so use std::sort()
433  std::sort (my_row.begin(), my_row.end());
434 
435  my_row.erase(std::unique (my_row.begin(), my_row.end()), my_row.end());
436  }
437  }
438 
439  // Combine the other thread's hashed_dof_sets with ours.
440  hashed_dof_sets.insert(other.hashed_dof_sets.begin(),
441  other.hashed_dof_sets.end());
442 }
443 
444 
445 
447 {
448  parallel_object_only();
450 
451  const auto n_dofs_on_proc = dof_map.n_local_dofs();
452  const auto local_first_dof = dof_map.first_dof();
453 
454  // The data to send
455  std::map<processor_id_type, std::vector<dof_id_type>> ids_to_send;
456  std::map<processor_id_type, std::vector<Row>> rows_to_send;
457 
458  // Loop over the nonlocal rows and transform them into the new datastructure
459  NonlocalGraph::iterator it = nonlocal_pattern.begin();
460  while (it != nonlocal_pattern.end())
461  {
462  const auto dof_id = it->first;
463  auto & row = it->second;
464 
465  processor_id_type proc_id = 0;
466  while (dof_id >= dof_map.end_dof(proc_id))
467  proc_id++;
468 
469  ids_to_send[proc_id].push_back(dof_id);
470 
471  // Note this invalidates the data in nonlocal_pattern
472  rows_to_send[proc_id].push_back(std::move(row));
473 
474  // Might as well remove it since it's invalidated anyway
475  it = nonlocal_pattern.erase(it);
476  }
477 
478  std::map<processor_id_type, std::vector<dof_id_type>> received_ids_map;
479 
480  auto ids_action_functor =
481  [& received_ids_map]
482  (processor_id_type pid,
483  const std::vector<dof_id_type> & received_ids)
484  {
485  received_ids_map.emplace(pid, received_ids);
486  };
487 
488  Parallel::push_parallel_vector_data(this->comm(), ids_to_send,
489  ids_action_functor);
490 
491  auto rows_action_functor =
492  [this,
493  & received_ids_map,
494  local_first_dof]
495  (processor_id_type pid,
496  const std::vector<Row> & received_rows)
497  {
498  const std::vector<dof_id_type> & received_ids = libmesh_map_find(received_ids_map, pid);
499 
500  std::size_t n_rows = received_rows.size();
501  libmesh_assert_equal_to(n_rows, received_ids.size());
502 
503  for (auto i : IntRange<std::size_t>(0, n_rows))
504  {
505  const auto r = received_ids[i];
507 
508  const auto my_r = r - local_first_dof;
509 
510  auto & their_row = received_rows[i];
511 
512  auto & my_row = sparsity_pattern[my_r];
513 
514  // They wouldn't have sent an empty row
515  libmesh_assert(!their_row.empty());
516 
517  // We can end up with an empty row on a dof that touches our
518  // inactive elements but not our active ones
519  if (my_row.empty())
520  {
521  my_row.assign (their_row.begin(), their_row.end());
522  }
523  else
524  {
525  my_row.insert (my_row.end(),
526  their_row.begin(),
527  their_row.end());
528 
529  // We cannot use SparsityPattern::sort_row() here because it expects
530  // the [begin,middle) [middle,end) to be non-overlapping. This is not
531  // necessarily the case here, so use std::sort()
532  std::sort (my_row.begin(), my_row.end());
533 
534  my_row.erase(std::unique (my_row.begin(), my_row.end()), my_row.end());
535  }
536 
537  }
538  };
539 
540  Parallel::push_parallel_vector_data(this->comm(), rows_to_send,
541  rows_action_functor);
542 
543  // We should have sent everything at this point.
545 
546  // assert these are empty because std::vector::resize will only append the specified element value
547  // if the new size is greater than the current size. Elements whose indices are less than the
548  // current size are untouched
549  libmesh_assert(n_nz.empty());
550  libmesh_assert(n_oz.empty());
551  n_nz.resize (n_dofs_on_proc, 0);
552  n_oz.resize (n_dofs_on_proc, 0);
553 
554  const dof_id_type first_dof_on_proc = dof_map.first_dof();
555  const dof_id_type end_dof_on_proc = dof_map.end_dof();
556 
557  for (dof_id_type i=0; i<n_dofs_on_proc; i++)
558  {
559  // Get the row of the sparsity pattern
561 
562  for (const auto & df : row)
563  if ((df < first_dof_on_proc) || (df >= end_dof_on_proc))
564  n_oz[i]++;
565  else
566  n_nz[i]++;
567 
568  libmesh_assert(n_nz[i] <= n_dofs_on_proc);
569 
570  // If we're not building a full sparsity pattern, then we want
571  // to avoid overcounting these entries as much as possible.
573  row.clear();
574  }
575 }
576 
577 
579 {
581 }
582 
583 
584 std::size_t Build::n_nonzeros() const
585 {
586  // At some point I'll remember that "C++17" compilers don't always
587  // come with complete C++17 standard libraries.
588  // std::size_t total_nonzeros = std::reduce(n_nz.begin(), n_nz.end(), std::size_t(0));
589  // total_nonzeros += std::reduce(n_oz.begin(), n_oz.end(), std::size_t(0));
590 
591  std::size_t total_nonzeros = 0;
592  for (auto nnzi : n_nz)
593  total_nonzeros += nnzi;
594  for (auto nozi : n_oz)
595  total_nonzeros += nozi;
596 
597  this->comm().sum(total_nonzeros);
598  return total_nonzeros;
599 }
600 
601 
602 } // namespace SparsityPattern
603 } // namespace libMesh
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:2855
SparsityPattern::Graph sparsity_pattern
A class holding degree of freedom information pertinent to static condensation.
dof_id_type end_dof(const processor_id_type proc) const
Definition: dof_map_base.h:191
std::set< GhostingFunctor * >::const_iterator coupling_functors_begin() const
Beginning of range of coupling functors.
Definition: dof_map.h:359
This helper class can be called on multiple threads to compute the sparsity pattern (or graph) of the...
const unsigned int invalid_uint
A number which is used quite often to represent an invalid or uninitialized value for an unsigned int...
Definition: libmesh.h:310
The IntRange templated class is intended to make it easy to loop over integers which are indices of a...
Definition: int_range.h:53
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
Definition: dof_map.C:2164
The definition of the const_element_iterator struct.
Definition: mesh_base.h:2216
Dummy "splitting object" used to distinguish splitting constructors from copy constructors.
Definition: threads_none.h:63
void sum(T &r) const
std::size_t n_nonzeros() const
The total number of nonzeros in the global matrix.
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
const Parallel::Communicator & comm() const
This proxy class acts like a container of indices from a single coupling row.
unsigned int p_level() const
Definition: elem.h:3108
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 StoredRange class defines a contiguous, divisible set of objects.
Definition: stored_range.h:54
The libMesh namespace provides an interface to certain functionality in the library.
dof_id_type n_local_dofs(const unsigned int vn) const
Definition: dof_map.h:686
void parallel_sync()
Send sparsity pattern data relevant to other processors to those processors, and receive and incorpor...
std::unordered_set< dof_id_type > hashed_dof_sets
If there are "spider" nodes in the mesh (i.e.
Used to iterate over non-nullptr entries in a container.
uint8_t processor_id_type
Definition: id_types.h:104
This class handles the numbering of degrees of freedom on a mesh.
Definition: dof_map.h:179
std::vector< dof_id_type > dummy_vec
A dummy work vector to avoid repeated memory allocations.
std::vector< dof_id_type, Threads::scalable_allocator< dof_id_type > > Row
uint32_t hashword(const uint32_t *k, size_t length, uint32_t initval=0)
The hashword function takes an array of uint32_t&#39;s of length &#39;length&#39; and computes a single key from ...
Definition: hashword.h:158
void join(const Build &other)
Combine the sparsity pattern in other with this object&#39;s sparsity pattern.
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:1614
const std::unordered_set< unsigned int > & uncondensed_vars() const
std::set< std::unique_ptr< CouplingMatrix >, Utility::CompareUnderlying > CouplingMatricesSet
Definition: dof_map.h:1848
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:493
unsigned int n_variables() const override
Definition: dof_map.h:628
void sorted_connected_dofs(const Elem *elem, std::vector< dof_id_type > &dofs_vi, unsigned int vi)
libmesh_assert(ctx)
std::vector< dof_id_type > n_nz
An object whose state is distributed along a set of processors.
void apply_extra_sparsity_object(SparsityPattern::AugmentSparsityPattern &asp)
Let a user-provided AugmentSparsityPattern subclass modify our sparsity structure.
SparsityPattern::NonlocalGraph nonlocal_pattern
const StaticCondensationDofMap *const sc
uint32_t hashword2(const uint32_t &first, const uint32_t &second, uint32_t initval=0)
This is a hard-coded version of hashword for hashing exactly 2 numbers.
Definition: hashword.h:215
std::set< GhostingFunctor * >::const_iterator coupling_functors_end() const
End of range of coupling functors.
Definition: dof_map.h:365
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.
dof_id_type first_dof(const processor_id_type proc) const
Definition: dof_map_base.h:185
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.
processor_id_type processor_id() const
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, const StaticCondensationDofMap *sc=nullptr)
Abstract base class to be used to add user-defined implicit degree of freedom couplings.
unsigned int idx(const ElemType type, const unsigned int nx, const unsigned int i, const unsigned int j)
A useful inline function which replaces the macros used previously.
uint8_t dof_id_type
Definition: id_types.h:67
bool local_index(dof_id_type dof_index) const
Definition: dof_map.h:839
This class defines a coupling matrix.
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