libMesh
coupling_matrix.h
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2024 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 #ifndef LIBMESH_COUPLING_MATRIX_H
21 #define LIBMESH_COUPLING_MATRIX_H
22 
23 // Local Includes
24 #include "libmesh/libmesh_common.h"
25 
26 // C++ includes
27 #include <algorithm>
28 #include <limits>
29 #include <utility> // std::pair
30 #include <vector>
31 
32 namespace libMesh
33 {
34 
35 // Forward declarations
36 class ConstCouplingAccessor;
37 class CouplingAccessor;
38 
39 class ConstCouplingRow;
40 
41 class ConstCouplingRowConstIterator;
42 
55 {
56 public:
57 
61  explicit
62  CouplingMatrix (const std::size_t n=0);
63 
67  bool operator() (const std::size_t i,
68  const std::size_t j) const;
69 
74  CouplingAccessor operator() (const std::size_t i,
75  const std::size_t j);
76 
81  std::size_t size() const;
82 
87  void resize(const std::size_t n);
88 
92  void clear();
93 
97  bool empty() const;
98 
99  CouplingMatrix & operator&= (const CouplingMatrix & other);
100 
101 private:
102 
103  friend class ConstCouplingAccessor;
104  friend class CouplingAccessor;
105  friend class ConstCouplingRow;
107 
119  typedef std::pair<std::size_t, std::size_t> range_type;
120  typedef std::vector<range_type> rc_type;
122 
126  std::size_t _size;
127 };
128 
129 
130 
135 {
136 public:
137  ConstCouplingAccessor(std::size_t loc_in,
138  const CouplingMatrix & mat_in) :
139  _location(loc_in), _mat(mat_in)
140  {
141  libmesh_assert_less(_location, _mat.size() * _mat.size());
142  }
143 
144  operator bool() const
145  {
146  const std::size_t max_size = std::numeric_limits<std::size_t>::max();
147 
148  // Find the range that might contain i,j
149  // lower_bound isn't *quite* what we want
150  CouplingMatrix::rc_type::const_iterator lb = std::upper_bound
151  (_mat._ranges.begin(), _mat._ranges.end(),
152  std::make_pair(_location, max_size));
153  if (lb!=_mat._ranges.begin())
154  --lb;
155  else
156  lb=_mat._ranges.end();
157 
158  // If no range might contain i,j then it's 0
159  if (lb == _mat._ranges.end())
160  return false;
161 
162  const std::size_t lastloc = lb->second;
163 
164 #ifdef DEBUG
165  const std::size_t firstloc = lb->first;
166  libmesh_assert_less_equal(firstloc, lastloc);
167  libmesh_assert_less_equal(firstloc, _location);
168 
169  CouplingMatrix::rc_type::const_iterator next = lb;
170  next++;
171  if (next != _mat._ranges.end())
172  {
173  // Ranges should be sorted and should not touch
174  libmesh_assert_greater(next->first, lastloc+1);
175  }
176 #endif
177 
178  return (lastloc >= _location);
179  }
180 
181 protected:
182 
183  std::size_t _location;
185 };
186 
187 
192 {
193 public:
194  CouplingAccessor(std::size_t loc_in,
195  CouplingMatrix & mat_in) :
196  ConstCouplingAccessor(loc_in, mat_in), _my_mat(mat_in) {}
197 
198  template <typename T>
200  {
201  // For backwards compatibility we take integer arguments,
202  // but coupling matrix entries are really all zero or one.
203  const bool as_bool = new_value;
204  libmesh_assert_equal_to(new_value, as_bool);
205 
206  *this = as_bool;
207  return *this;
208  }
209 
210  CouplingAccessor & operator = (bool new_value)
211  {
212  const std::size_t max_size = std::numeric_limits<std::size_t>::max();
213 
214  // Find the range that might contain i,j
215  // lower_bound isn't *quite* what we want
216  CouplingMatrix::rc_type::iterator lb =
217  std::upper_bound (_my_mat._ranges.begin(), _my_mat._ranges.end(),
218  std::make_pair(_location, max_size));
219  if (lb!=_my_mat._ranges.begin())
220  --lb;
221  else
222  lb=_my_mat._ranges.end();
223 
224  // If no range might contain i,j then we might need to make a new
225  // one.
226  if (lb == _my_mat._ranges.end())
227  {
228  if (new_value == true)
229  _my_mat._ranges.emplace
230  (_my_mat._ranges.begin(), _location, _location);
231  }
232  else
233  {
234  const std::size_t firstloc = lb->first;
235  const std::size_t lastloc = lb->second;
236  libmesh_assert_less_equal(firstloc, lastloc);
237  libmesh_assert_less_equal(firstloc, _location);
238 
239 #ifdef DEBUG
240  {
241  CouplingMatrix::rc_type::const_iterator next = lb;
242  next++;
243  if (next != _my_mat._ranges.end())
244  {
245  // Ranges should be sorted and should not touch
246  libmesh_assert_greater(next->first, lastloc+1);
247  }
248  }
249 #endif
250 
251  // If we're in this range, we might need to shorten or remove
252  // or split it
253  if (new_value == false)
254  {
255  if (_location == firstloc)
256  {
257  if (_location == lastloc)
258  {
259  _my_mat._ranges.erase(lb);
260  }
261  else
262  {
263  libmesh_assert_less (lb->first, lastloc);
264  lb->first++;
265  }
266  }
267  else if (_location == lastloc)
268  {
269  libmesh_assert_less (firstloc, lb->second);
270 
271  lb->second--;
272  }
273  else if (_location < lastloc)
274  {
275  libmesh_assert_less_equal(_location+1, lastloc);
276 
277  lb->first = _location+1;
278 
279  libmesh_assert_less_equal(firstloc, _location-1);
280 
281  _my_mat._ranges.emplace(lb, firstloc, _location-1);
282  }
283  }
284 
285  // If we're not in this range, we might need to extend it or
286  // join it with its neighbor or add a new one.
287  else // new_value == true
288  {
289  CouplingMatrix::rc_type::iterator next = lb;
290  next++;
291  const std::size_t nextloc =
292  (next == _my_mat._ranges.end()) ?
293  std::numeric_limits<std::size_t>::max() :
294  next->first;
295 
296  // Ranges should be sorted and should not touch
297  libmesh_assert_greater(nextloc, lastloc+1);
298 
299  if (_location > lastloc)
300  {
301  if (_location == lastloc + 1)
302  {
303  if (_location == nextloc - 1)
304  {
305  next->first = firstloc;
306  _my_mat._ranges.erase(lb);
307  }
308  else
309  lb->second++;
310  }
311  else
312  {
313  if (_location == nextloc - 1)
314  next->first--;
315  else
317  }
318  }
319  }
320  }
321 
322  return *this;
323  }
324 
325 private:
326 
328 };
329 
330 
331 
337 {
338 public:
339  ConstCouplingRow(std::size_t row_in,
340  const CouplingMatrix & mat_in) :
341  _row_i(row_in), _mat(mat_in),
342  _end_location(_row_i*_mat.size()+_mat.size()-1)
343  {
344  libmesh_assert_less(_row_i, _mat.size());
345 
346  // Location for i,N, where we'll start looking for our beginning
347  // range
349 
350  const std::size_t max_size = std::numeric_limits<std::size_t>::max();
351 
352  // Find the range that might contain i,N
353  // lower_bound isn't *quite* what we want
354  _begin_it = std::upper_bound
355  (_mat._ranges.begin(), _mat._ranges.end(),
356  std::make_pair(_begin_location, max_size));
357  if (_begin_it !=_mat._ranges.begin())
358  --_begin_it;
359  else
360  _begin_it=_mat._ranges.end();
361 
362  // If that range doesn't exist then we're an empty row
363  if (_begin_it == _mat._ranges.end())
364  _begin_location = max_size;
365  else
366  {
367  const std::size_t lastloc = _begin_it->second;
368 #ifdef DEBUG
369  const std::size_t firstloc = _begin_it->first;
370  libmesh_assert_less_equal(firstloc, lastloc);
371 #endif
372 
373  // If that range ends before i,0 then we're an empty row
374  std::size_t zero_location = _row_i*_mat.size();
375  if (zero_location > lastloc)
376  {
377  _begin_location = max_size;
378  _begin_it = _mat._ranges.end();
379  }
380  else
381  // We have *some* entry(s) in this row, we just need to find
382  // the earliest
383  {
384  while (_begin_it != _mat._ranges.begin())
385  {
386  CouplingMatrix::rc_type::const_iterator prev =
387  _begin_it;
388  --prev;
389 
390  if (prev->second < zero_location)
391  break;
392 
393  _begin_it = prev;
394  }
395  if (_begin_it->first < zero_location)
396  _begin_location = zero_location;
397  else
398  _begin_location = _begin_it->first;
399  }
400  }
401  }
402 
403  /*
404  * A forward iterator type for looping over indices in this row
405  */
407 
408  /*
409  * An iterator to the first index in this row, or to end() for an
410  * empty row
411  */
412  const_iterator begin() const;
413 
414  /*
415  * An iterator representing past-the-end of this row
416  */
417  const_iterator end() const;
418 
419  bool operator== (const ConstCouplingRow & other) const
420  {
421  // Thinking that rows from different matrix objects are equal is
422  // not even wrong
423  libmesh_assert(&_mat == &other._mat);
424 
425  return ((_begin_location == other._begin_location) &&
426  (_begin_it == other._begin_it));
427  }
428 
429  bool operator!= (const ConstCouplingRow & other) const
430  {
431  return !(*this == other);
432  }
433 protected:
434 
436 
437  std::size_t _row_i;
439 
440  // The location (i*size+j) corresponding to the first entry in this
441  // row, or numeric_limits<size_t>::max() for an empty row.
442  std::size_t _begin_location;
443 
444  // The location (i*size+j) corresponding to the end of this row
445  // (regardless of whether that end falls within a range). Cached
446  // because every ++iterator call needs to use it.
447  const std::size_t _end_location;
448 
449  // Iterator to the range containing the first row element, or
450  // _row._mat._ranges.end() if no CouplingMatrix values are true for
451  // this row
452  CouplingMatrix::rc_type::const_iterator _begin_it;
453 };
454 
455 
456 
458 {
459 public:
461  std::size_t loc_in,
462  CouplingMatrix::rc_type::const_iterator it_in) :
463  _location(loc_in),
464  _row(row_in),
465  _it(it_in)
466  {
467 #ifndef NDEBUG
468  if (_it != _row._mat._ranges.end())
469  {
470  libmesh_assert_less_equal(_it->first, _location);
471  libmesh_assert_less_equal(_location, _it->second);
472  }
473  else
474  {
475  libmesh_assert_equal_to
476  (_location, std::numeric_limits<size_t>::max());
477  }
478 #endif
479  }
480 
481  std::size_t operator* ()
482  {
483  libmesh_assert_not_equal_to
484  (_location, std::numeric_limits<std::size_t>::max());
485  return _location % _row._mat.size();
486  }
487 
489  {
490  libmesh_assert_not_equal_to
491  (_location, std::numeric_limits<std::size_t>::max());
493  (_it != _row._mat._ranges.end());
494 
495  if (_location >= _it->second)
496  {
497  ++_it;
498 
499  // Are we past the end of the matrix?
500  if (_it == _row._mat._ranges.end())
501  _location = std::numeric_limits<std::size_t>::max();
502  else
503  {
504  _location = _it->first;
505  // Is the new range past the end of the row?
507  _location = std::numeric_limits<std::size_t>::max();
508  }
509  }
510  // Would incrementing put us past the end of the row?
511  else if (_location >= _row._end_location)
512  _location = std::numeric_limits<std::size_t>::max();
513  else
514  ++_location;
515 
516  return *this;
517  }
518 
519  bool operator== (const ConstCouplingRowConstIterator & other) const
520  {
521  // Thinking that iterators from different row objects are equal
522  // is not even wrong
523  libmesh_assert(_row == other._row);
524 
525  return (_location == other._location);
526  // Testing for equality of _it is either redundant (for a
527  // dereferenceable iterator, where _it is defined to be the range
528  // which includes _location) or wrong (for an end iterator, where
529  // we don't always bother to set _it)
530  // && (_it == other._it);
531  }
532 
533  bool operator!= (const ConstCouplingRowConstIterator & other) const
534  {
535  return !(*this == other);
536  }
537 
538 private:
539  // The location (i*size+j) corresponding to this iterator, or
540  // numeric_limits<size_t>::max() to signify end()
541  std::size_t _location;
543  // The range containing this iterator location, or
544  // _row._mat._ranges.end() if no range contains this location
545  CouplingMatrix::rc_type::const_iterator _it;
546 };
547 
548 
549 
550 //--------------------------------------------------
551 // ConstCouplingRow inline methods
552 inline
554 {
555  return const_iterator (*this, _begin_location, _begin_it);
556 }
557 
558 inline
560 {
561  return const_iterator
562  (*this, std::numeric_limits<std::size_t>::max(),
563  _mat._ranges.end());
564 }
565 
566 
567 
568 //--------------------------------------------------
569 // CouplingMatrix inline methods
570 inline
571 CouplingMatrix::CouplingMatrix (const std::size_t n) :
572  _ranges(), _size(n)
573 {
574  this->resize(n);
575 }
576 
577 
578 
579 inline
580 bool CouplingMatrix::operator() (const std::size_t i,
581  const std::size_t j) const
582 {
583  libmesh_assert_less (i, _size);
584  libmesh_assert_less (j, _size);
585 
586  const std::size_t location = std::size_t(i)*_size + j;
587 
588  return bool(ConstCouplingAccessor(location, *this));
589 }
590 
591 
592 
593 
594 inline
596  const std::size_t j)
597 {
598  const std::size_t location = std::size_t(i)*_size + j;
599 
600  return CouplingAccessor(location, *this);
601 }
602 
603 
604 
605 inline
606 std::size_t CouplingMatrix::size() const
607 {
608  return _size;
609 }
610 
611 
612 
613 inline
614 void CouplingMatrix::resize(const std::size_t n)
615 {
616  _size = n;
617 
618  _ranges.clear();
619 }
620 
621 
622 
623 inline
625 {
626  this->resize(0);
627 }
628 
629 
630 
631 inline
633 {
634  return (_size == 0);
635 }
636 
637 
638 } // namespace libMesh
639 
640 
641 #endif // LIBMESH_COUPLING_MATRIX_H
void clear()
Clears the matrix.
ConstCouplingAccessor(std::size_t loc_in, const CouplingMatrix &mat_in)
CouplingMatrix & operator &=(const CouplingMatrix &other)
static const unsigned int prev[3]
A lookup table for the decrement modulo 3 operation, for iterating through the three nodes per elemen...
bool operator!=(const ConstCouplingRowConstIterator &other) const
void resize(const std::size_t n)
Resizes the matrix and initializes all entries to be 0.
This proxy class acts like a container of indices from a single coupling row.
The libMesh namespace provides an interface to certain functionality in the library.
const std::size_t _end_location
std::size_t size() const
bool operator==(const ConstCouplingRow &other) const
This accessor class allows simple setting of CouplingMatrix values.
ConstCouplingRowConstIterator & operator++()
libmesh_assert(ctx)
ConstCouplingRowConstIterator(const ConstCouplingRow &row_in, std::size_t loc_in, CouplingMatrix::rc_type::const_iterator it_in)
friend class ConstCouplingAccessor
bool operator()(const std::size_t i, const std::size_t j) const
const_iterator end() const
static const unsigned int next[3]
A lookup table for the increment modulo 3 operation, for iterating through the three nodes per elemen...
std::size_t _size
The size of the matrix.
std::pair< std::size_t, std::size_t > range_type
Coupling matrices are typically either full or very sparse, and all values are only zero or one...
std::vector< range_type > rc_type
CouplingMatrix(const std::size_t n=0)
Constructor.
CouplingMatrix::rc_type::const_iterator _it
This accessor class allows simple access to CouplingMatrix values.
ConstCouplingRowConstIterator const_iterator
const_iterator begin() const
ConstCouplingRow(std::size_t row_in, const CouplingMatrix &mat_in)
const CouplingMatrix & _mat
const CouplingMatrix & _mat
bool operator!=(const ConstCouplingRow &other) const
CouplingMatrix::rc_type::const_iterator _begin_it
CouplingAccessor & operator=(T new_value)
CouplingAccessor(std::size_t loc_in, CouplingMatrix &mat_in)
bool operator==(const ConstCouplingRowConstIterator &other) const
This class defines a coupling matrix.