coupling_matrix.h
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2018 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 unsigned int n=0);
63 
67  bool operator() (const unsigned int i,
68  const unsigned int j) const;
69 
74  CouplingAccessor operator() (const unsigned int i,
75  const unsigned int j);
76 
81  unsigned int size() const;
82 
87  void resize(const unsigned int 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  unsigned int _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  const std::size_t max_size = std::numeric_limits<std::size_t>::max();
146 
147  // Find the range that might contain i,j
148  // lower_bound isn't *quite* what we want
149  CouplingMatrix::rc_type::const_iterator lb = std::upper_bound
150  (_mat._ranges.begin(), _mat._ranges.end(),
151  std::make_pair(_location, max_size));
152  if (lb!=_mat._ranges.begin())
153  --lb;
154  else
155  lb=_mat._ranges.end();
156 
157  // If no range might contain i,j then it's 0
158  if (lb == _mat._ranges.end())
159  return false;
160 
161  const std::size_t lastloc = lb->second;
162 
163 #ifdef DEBUG
164  const std::size_t firstloc = lb->first;
165  libmesh_assert_less_equal(firstloc, lastloc);
166  libmesh_assert_less_equal(firstloc, _location);
167 
168  CouplingMatrix::rc_type::const_iterator next = lb;
169  next++;
170  if (next != _mat._ranges.end())
171  {
172  // Ranges should be sorted and should not touch
173  libmesh_assert_greater(next->first, lastloc+1);
174  }
175 #endif
176 
177  return (lastloc >= _location);
178  }
179 
180 protected:
181 
182  std::size_t _location;
184 };
185 
186 
191 {
192 public:
193  CouplingAccessor(std::size_t loc_in,
194  CouplingMatrix & mat_in) :
195  ConstCouplingAccessor(loc_in, mat_in), _my_mat(mat_in) {}
196 
197  template <typename T>
199  {
200  // For backwards compatibility we take integer arguments,
201  // but coupling matrix entries are really all zero or one.
202  const bool as_bool = new_value;
203  libmesh_assert_equal_to(new_value, as_bool);
204 
205  *this = as_bool;
206  return *this;
207  }
208 
209  CouplingAccessor & operator = (bool new_value)
210  {
211  const std::size_t max_size = std::numeric_limits<std::size_t>::max();
212 
213  // Find the range that might contain i,j
214  // lower_bound isn't *quite* what we want
215  CouplingMatrix::rc_type::iterator lb =
216  std::upper_bound (_my_mat._ranges.begin(), _my_mat._ranges.end(),
217  std::make_pair(_location, max_size));
218  if (lb!=_my_mat._ranges.begin())
219  --lb;
220  else
221  lb=_my_mat._ranges.end();
222 
223  // If no range might contain i,j then we might need to make a new
224  // one.
225  if (lb == _my_mat._ranges.end())
226  {
227  if (new_value == true)
228  _my_mat._ranges.insert(_my_mat._ranges.begin(),
229  std::make_pair(_location, _location));
230  }
231  else
232  {
233  const std::size_t firstloc = lb->first;
234  const std::size_t lastloc = lb->second;
235  libmesh_assert_less_equal(firstloc, lastloc);
236  libmesh_assert_less_equal(firstloc, _location);
237 
238 #ifdef DEBUG
239  {
240  CouplingMatrix::rc_type::const_iterator next = lb;
241  next++;
242  if (next != _my_mat._ranges.end())
243  {
244  // Ranges should be sorted and should not touch
245  libmesh_assert_greater(next->first, lastloc+1);
246  }
247  }
248 #endif
249 
250  // If we're in this range, we might need to shorten or remove
251  // or split it
252  if (new_value == false)
253  {
254  if (_location == firstloc)
255  {
256  if (_location == lastloc)
257  {
258  _my_mat._ranges.erase(lb);
259  }
260  else
261  {
262  libmesh_assert_less (lb->first, lastloc);
263  lb->first++;
264  }
265  }
266  else if (_location == lastloc)
267  {
268  libmesh_assert_less (firstloc, lb->second);
269 
270  lb->second--;
271  }
272  else if (_location < lastloc)
273  {
274  libmesh_assert_less_equal(_location+1, lastloc);
275 
276  lb->first = _location+1;
277 
278  libmesh_assert_less_equal(firstloc, _location-1);
279 
280  _my_mat._ranges.insert
281  (lb, std::make_pair(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()) ?
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
316  _my_mat._ranges.insert
317  (next, std::make_pair(_location, _location));
318  }
319  }
320  }
321  }
322 
323  return *this;
324  }
325 
326 private:
327 
329 };
330 
331 
332 
338 {
339 public:
340  ConstCouplingRow(unsigned int row_in,
341  const CouplingMatrix & mat_in) :
342  _row_i(row_in), _mat(mat_in),
343  _end_location(_row_i*_mat.size()+_mat.size()-1)
344  {
345  libmesh_assert_less(_row_i, _mat.size());
346 
347  // Location for i,N, where we'll start looking for our beginning
348  // range
350 
351  const std::size_t max_size = std::numeric_limits<std::size_t>::max();
352 
353  // Find the range that might contain i,N
354  // lower_bound isn't *quite* what we want
355  _begin_it = std::upper_bound
356  (_mat._ranges.begin(), _mat._ranges.end(),
357  std::make_pair(_begin_location, max_size));
358  if (_begin_it !=_mat._ranges.begin())
359  --_begin_it;
360  else
361  _begin_it=_mat._ranges.end();
362 
363  // If that range doesn't exist then we're an empty row
364  if (_begin_it == _mat._ranges.end())
365  _begin_location = max_size;
366  else
367  {
368  const std::size_t lastloc = _begin_it->second;
369 #ifdef DEBUG
370  const std::size_t firstloc = _begin_it->first;
371  libmesh_assert_less_equal(firstloc, lastloc);
372 #endif
373 
374  // If that range ends before i,0 then we're an empty row
375  std::size_t zero_location = _row_i*_mat.size();
376  if (zero_location > lastloc)
377  {
378  _begin_location = max_size;
379  _begin_it = _mat._ranges.end();
380  }
381  else
382  // We have *some* entry(s) in this row, we just need to find
383  // the earliest
384  {
385  while (_begin_it != _mat._ranges.begin())
386  {
387  CouplingMatrix::rc_type::const_iterator prev =
388  _begin_it;
389  --prev;
390 
391  if (prev->second < zero_location)
392  break;
393 
394  _begin_it = prev;
395  }
396  if (_begin_it->first < zero_location)
397  _begin_location = zero_location;
398  else
399  _begin_location = _begin_it->first;
400  }
401  }
402  }
403 
404  /*
405  * A forward iterator type for looping over indices in this row
406  */
408 
409  /*
410  * An iterator to the first index in this row, or to end() for an
411  * empty row
412  */
413  const_iterator begin() const;
414 
415  /*
416  * An iterator representing past-the-end of this row
417  */
418  const_iterator end() const;
419 
420  bool operator== (const ConstCouplingRow & other) const
421  {
422  // Thinking that rows from different matrix objects are equal is
423  // not even wrong
424  libmesh_assert(&_mat == &other._mat);
425 
426  return ((_begin_location == other._begin_location) &&
427  (_begin_it == other._begin_it));
428  }
429 
430  bool operator!= (const ConstCouplingRow & other) const
431  {
432  return !(*this == other);
433  }
434 protected:
435 
437 
438  unsigned int _row_i;
440 
441  // The location (i*size+j) corresponding to the first entry in this
442  // row, or numeric_limits<size_t>::max() for an empty row.
443  std::size_t _begin_location;
444 
445  // The location (i*size+j) corresponding to the end of this row
446  // (regardless of whether that end falls within a range). Cached
447  // because every ++iterator call needs to use it.
448  const std::size_t _end_location;
449 
450  // Iterator to the range containing the first row element, or
451  // _row._mat._ranges.end() if no CouplingMatrix values are true for
452  // this row
453  CouplingMatrix::rc_type::const_iterator _begin_it;
454 };
455 
456 
457 
459 {
460 public:
462  std::size_t loc_in,
463  CouplingMatrix::rc_type::const_iterator it_in) :
464  _location(loc_in),
465  _row(row_in),
466  _it(it_in)
467  {
468 #ifndef NDEBUG
469  if (_it != _row._mat._ranges.end())
470  {
471  libmesh_assert_less_equal(_it->first, _location);
472  libmesh_assert_less_equal(_location, _it->second);
473  }
474  else
475  {
476  libmesh_assert_equal_to
478  }
479 #endif
480  }
481 
482  unsigned int operator* ()
483  {
484  libmesh_assert_not_equal_to
486  return cast_int<unsigned int>(_location % _row._mat.size());
487  }
488 
490  {
491  libmesh_assert_not_equal_to
493  libmesh_assert
494  (_it != _row._mat._ranges.end());
495 
496  if (_location >= _it->second)
497  {
498  ++_it;
499 
500  // Are we past the end of the matrix?
501  if (_it == _row._mat._ranges.end())
503  else
504  {
505  _location = _it->first;
506  // Is the new range past the end of the row?
509  }
510  }
511  // Would incrementing put us past the end of the row?
512  else if (_location >= _row._end_location)
514  else
515  ++_location;
516 
517  return *this;
518  }
519 
520  bool operator== (const ConstCouplingRowConstIterator & other) const
521  {
522  // Thinking that iterators from different row objects are equal
523  // is not even wrong
524  libmesh_assert(_row == other._row);
525 
526  return (_location == other._location);
527  // Testing for equality of _it is either redundant (for a
528  // dereferenceable iterator, where _it is defined to be the range
529  // which includes _location) or wrong (for an end iterator, where
530  // we don't always bother to set _it)
531  // && (_it == other._it);
532  }
533 
534  bool operator!= (const ConstCouplingRowConstIterator & other) const
535  {
536  return !(*this == other);
537  }
538 
539 private:
540  // The location (i*size+j) corresponding to this iterator, or
541  // numeric_limits<size_t>::max() to signify end()
542  std::size_t _location;
544  // The range containing this iterator location, or
545  // _row._mat._ranges.end() if no range contains this location
546  CouplingMatrix::rc_type::const_iterator _it;
547 };
548 
549 
550 
551 //--------------------------------------------------
552 // ConstCouplingRow inline methods
553 inline
555  return const_iterator (*this, _begin_location, _begin_it);
556 }
557 
558 inline
560  return const_iterator
562  _mat._ranges.end());
563 }
564 
565 
566 
567 //--------------------------------------------------
568 // CouplingMatrix inline methods
569 inline
570 CouplingMatrix::CouplingMatrix (const unsigned int n) :
571  _ranges(), _size(n)
572 {
573  this->resize(n);
574 }
575 
576 
577 
578 inline
579 bool CouplingMatrix::operator() (const unsigned int i,
580  const unsigned int j) const
581 {
582  libmesh_assert_less (i, _size);
583  libmesh_assert_less (j, _size);
584 
585  const std::size_t location = std::size_t(i)*_size + j;
586 
587  return bool(ConstCouplingAccessor(location, *this));
588 }
589 
590 
591 
592 
593 inline
595  const unsigned int j)
596 {
597  const std::size_t location = std::size_t(i)*_size + j;
598 
599  return CouplingAccessor(location, *this);
600 }
601 
602 
603 
604 inline
605 unsigned int CouplingMatrix::size() const
606 {
607  return _size;
608 }
609 
610 
611 
612 inline
613 void CouplingMatrix::resize(const unsigned int n)
614 {
615  _size = n;
616 
617  _ranges.clear();
618 }
619 
620 
621 
622 inline
624 {
625  this->resize(0);
626 }
627 
628 
629 
630 inline
632 {
633  return (_size == 0);
634 }
635 
636 
637 } // namespace libMesh
638 
639 
640 #endif // LIBMESH_COUPLING_MATRIX_H
ConstCouplingAccessor(std::size_t loc_in, const CouplingMatrix &mat_in)
CouplingMatrix & operator &=(const CouplingMatrix &other)
static const unsigned int prev[3]
bool operator!=(const ConstCouplingRowConstIterator &other) const
CouplingMatrix(const unsigned int n=0)
const std::size_t _end_location
long double max(long double a, double b)
bool operator==(const ConstCouplingRow &other) const
ConstCouplingRowConstIterator & operator++()
ConstCouplingRowConstIterator(const ConstCouplingRow &row_in, std::size_t loc_in, CouplingMatrix::rc_type::const_iterator it_in)
friend class ConstCouplingAccessor
const_iterator end() const
static const unsigned int next[3]
void resize(const unsigned int n)
std::pair< std::size_t, std::size_t > range_type
std::vector< range_type > rc_type
CouplingMatrix::rc_type::const_iterator _it
ConstCouplingRow(unsigned int row_in, const CouplingMatrix &mat_in)
ConstCouplingRowConstIterator const_iterator
const_iterator begin() const
unsigned int size() const
bool operator()(const unsigned int i, const unsigned int j) const
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
Defines the coupling between variables of a System.