LCOV - code coverage report
Current view: top level - include/numerics - coupling_matrix.h (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 41 145 28.3 %
Date: 2025-08-19 19:27:09 Functions: 2 17 11.8 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : #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             : 
      43             : /**
      44             :  * This class defines a coupling matrix.  A coupling
      45             :  * matrix is simply a matrix of ones and zeros describing
      46             :  * how different components in a system couple with each
      47             :  * other.  A coupling matrix is necessarily square but not
      48             :  * necessarily symmetric.
      49             :  *
      50             :  * \author Benjamin S. Kirk
      51             :  * \date 2002
      52             :  * \brief Defines the coupling between variables of a System.
      53             :  */
      54           0 : class CouplingMatrix
      55             : {
      56             : public:
      57             : 
      58             :   /**
      59             :    * Constructor.
      60             :    */
      61             :   explicit
      62             :   CouplingMatrix (const std::size_t n=0);
      63             : 
      64             :   /**
      65             :    * \returns The (i,j) entry of the matrix.
      66             :    */
      67             :   bool operator() (const std::size_t i,
      68             :                    const std::size_t j) const;
      69             : 
      70             :   /**
      71             :    * \returns The (i,j) entry of the matrix as
      72             :    * a smart-reference.
      73             :    */
      74             :   CouplingAccessor operator() (const std::size_t i,
      75             :                                const std::size_t j);
      76             : 
      77             :   /**
      78             :    * \returns The size of the matrix, i.e. N for an
      79             :    * NxN matrix.
      80             :    */
      81             :   std::size_t size() const;
      82             : 
      83             :   /**
      84             :    * Resizes the matrix and initializes
      85             :    * all entries to be 0.
      86             :    */
      87             :   void resize(const std::size_t n);
      88             : 
      89             :   /**
      90             :    * Clears the matrix.
      91             :    */
      92             :   void clear();
      93             : 
      94             :   /**
      95             :    * \returns \p true if the matrix is empty.
      96             :    */
      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;
     106             :   friend class ConstCouplingRowConstIterator;
     107             : 
     108             :   /**
     109             :    * Coupling matrices are typically either full or very sparse, and
     110             :    * all values are only zero or one.
     111             :    *
     112             :    * We store non-zeros as ranges: the first entry of each range pair
     113             :    * is the location of the first non-zero, and the second is the
     114             :    * location of the last subsequent non-zero (*not* the next
     115             :    * subsequent zero; we drop empty ranges).
     116             :    *
     117             :    * We store locations (i,j) as long integers i*_size+j
     118             :    */
     119             :   typedef std::pair<std::size_t, std::size_t> range_type;
     120             :   typedef std::vector<range_type> rc_type;
     121             :   rc_type _ranges;
     122             : 
     123             :   /**
     124             :    * The size of the matrix.
     125             :    */
     126             :   std::size_t _size;
     127             : };
     128             : 
     129             : 
     130             : 
     131             : /**
     132             :  * This accessor class allows simple access to CouplingMatrix values.
     133             :  */
     134             : class ConstCouplingAccessor
     135             : {
     136             : public:
     137           0 :   ConstCouplingAccessor(std::size_t loc_in,
     138           0 :                         const CouplingMatrix & mat_in) :
     139           0 :     _location(loc_in), _mat(mat_in)
     140             :   {
     141           0 :     libmesh_assert_less(_location, _mat.size() * _mat.size());
     142           0 :   }
     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;
     184             :   const CouplingMatrix & _mat;
     185             : };
     186             : 
     187             : 
     188             : /**
     189             :  * This accessor class allows simple setting of CouplingMatrix values.
     190             :  */
     191             : class CouplingAccessor : public ConstCouplingAccessor
     192             : {
     193             : public:
     194           0 :   CouplingAccessor(std::size_t loc_in,
     195           0 :                    CouplingMatrix & mat_in) :
     196           0 :     ConstCouplingAccessor(loc_in, mat_in), _my_mat(mat_in) {}
     197             : 
     198             :   template <typename T>
     199           0 :   CouplingAccessor & operator = (T new_value)
     200             :   {
     201             :     // For backwards compatibility we take integer arguments,
     202             :     // but coupling matrix entries are really all zero or one.
     203           0 :     const bool as_bool = new_value;
     204           0 :     libmesh_assert_equal_to(new_value, as_bool);
     205             : 
     206           0 :     *this = as_bool;
     207           0 :     return *this;
     208             :   }
     209             : 
     210           0 :   CouplingAccessor & operator = (bool new_value)
     211             :   {
     212           0 :     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           0 :       std::upper_bound (_my_mat._ranges.begin(), _my_mat._ranges.end(),
     218           0 :                         std::make_pair(_location, max_size));
     219           0 :     if (lb!=_my_mat._ranges.begin())
     220           0 :       --lb;
     221             :     else
     222           0 :       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           0 :     if (lb == _my_mat._ranges.end())
     227             :       {
     228           0 :         if (new_value == true)
     229           0 :           _my_mat._ranges.emplace
     230           0 :             (_my_mat._ranges.begin(), _location, _location);
     231             :       }
     232             :     else
     233             :       {
     234           0 :         const std::size_t firstloc = lb->first;
     235           0 :         const std::size_t lastloc  = lb->second;
     236           0 :         libmesh_assert_less_equal(firstloc, lastloc);
     237           0 :         libmesh_assert_less_equal(firstloc, _location);
     238             : 
     239             : #ifdef DEBUG
     240             :         {
     241           0 :           CouplingMatrix::rc_type::const_iterator next = lb;
     242           0 :           next++;
     243           0 :           if (next != _my_mat._ranges.end())
     244             :             {
     245             :               // Ranges should be sorted and should not touch
     246           0 :               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           0 :         if (new_value == false)
     254             :           {
     255           0 :             if (_location == firstloc)
     256             :               {
     257           0 :                 if (_location == lastloc)
     258             :                   {
     259           0 :                     _my_mat._ranges.erase(lb);
     260             :                   }
     261             :                 else
     262             :                   {
     263           0 :                     libmesh_assert_less (lb->first, lastloc);
     264           0 :                     lb->first++;
     265             :                   }
     266             :               }
     267           0 :             else if (_location == lastloc)
     268             :               {
     269           0 :                 libmesh_assert_less (firstloc, lb->second);
     270             : 
     271           0 :                 lb->second--;
     272             :               }
     273           0 :             else if (_location < lastloc)
     274             :               {
     275           0 :                 libmesh_assert_less_equal(_location+1, lastloc);
     276             : 
     277           0 :                 lb->first = _location+1;
     278             : 
     279           0 :                 libmesh_assert_less_equal(firstloc, _location-1);
     280             : 
     281           0 :                 _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           0 :             CouplingMatrix::rc_type::iterator next = lb;
     290           0 :             next++;
     291             :             const std::size_t nextloc =
     292           0 :               (next == _my_mat._ranges.end()) ?
     293           0 :               std::numeric_limits<std::size_t>::max() :
     294           0 :               next->first;
     295             : 
     296             :             // Ranges should be sorted and should not touch
     297           0 :             libmesh_assert_greater(nextloc, lastloc+1);
     298             : 
     299           0 :             if (_location > lastloc)
     300             :               {
     301           0 :                 if (_location == lastloc + 1)
     302             :                   {
     303           0 :                     if (_location == nextloc - 1)
     304             :                       {
     305           0 :                         next->first = firstloc;
     306           0 :                         _my_mat._ranges.erase(lb);
     307             :                       }
     308             :                     else
     309           0 :                       lb->second++;
     310             :                   }
     311             :                 else
     312             :                   {
     313           0 :                     if (_location == nextloc - 1)
     314           0 :                       next->first--;
     315             :                     else
     316           0 :                       _my_mat._ranges.emplace(next, _location, _location);
     317             :                   }
     318             :               }
     319             :           }
     320             :       }
     321             : 
     322           0 :     return *this;
     323             :   }
     324             : 
     325             : private:
     326             : 
     327             :   CouplingMatrix & _my_mat;
     328             : };
     329             : 
     330             : 
     331             : 
     332             : /**
     333             :  * This proxy class acts like a container of indices from a single
     334             :  * coupling row
     335             :  */
     336             : class ConstCouplingRow
     337             : {
     338             : public:
     339         360 :   ConstCouplingRow(std::size_t row_in,
     340         360 :                    const CouplingMatrix & mat_in) :
     341         328 :     _row_i(row_in), _mat(mat_in),
     342         360 :     _end_location(_row_i*_mat.size()+_mat.size()-1)
     343             :   {
     344           0 :     libmesh_assert_less(_row_i, _mat.size());
     345             : 
     346             :     // Location for i,N, where we'll start looking for our beginning
     347             :     // range
     348         360 :     _begin_location = _end_location;
     349             : 
     350           0 :     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         328 :       (_mat._ranges.begin(), _mat._ranges.end(),
     356         360 :        std::make_pair(_begin_location, max_size));
     357         360 :     if (_begin_it !=_mat._ranges.begin())
     358           0 :       --_begin_it;
     359             :     else
     360           0 :       _begin_it=_mat._ranges.end();
     361             : 
     362             :     // If that range doesn't exist then we're an empty row
     363         392 :     if (_begin_it == _mat._ranges.end())
     364           0 :       _begin_location = max_size;
     365             :     else
     366             :       {
     367         360 :         const std::size_t lastloc  = _begin_it->second;
     368             : #ifdef DEBUG
     369           0 :         const std::size_t firstloc = _begin_it->first;
     370           0 :         libmesh_assert_less_equal(firstloc, lastloc);
     371             : #endif
     372             : 
     373             :         // If that range ends before i,0 then we're an empty row
     374          32 :         std::size_t zero_location = _row_i*_mat.size();
     375         360 :         if (zero_location > lastloc)
     376             :           {
     377          90 :             _begin_location = max_size;
     378          90 :             _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         294 :             while (_begin_it != _mat._ranges.begin())
     385             :               {
     386           0 :                 CouplingMatrix::rc_type::const_iterator prev =
     387             :                   _begin_it;
     388           0 :                 --prev;
     389             : 
     390         180 :                 if (prev->second < zero_location)
     391           0 :                   break;
     392             : 
     393           0 :                 _begin_it = prev;
     394             :               }
     395         270 :             if (_begin_it->first < zero_location)
     396          90 :               _begin_location = zero_location;
     397             :             else
     398         180 :               _begin_location = _begin_it->first;
     399             :           }
     400             :       }
     401         360 :   }
     402             : 
     403             :   /*
     404             :    * A forward iterator type for looping over indices in this row
     405             :    */
     406             :   typedef ConstCouplingRowConstIterator const_iterator;
     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           0 :   bool operator== (const ConstCouplingRow & other) const
     420             :   {
     421             :     // Thinking that rows from different matrix objects are equal is
     422             :     // not even wrong
     423           0 :     libmesh_assert(&_mat == &other._mat);
     424             : 
     425           0 :     return ((_begin_location == other._begin_location) &&
     426           0 :             (_begin_it == other._begin_it));
     427             :   }
     428             : 
     429             :   bool operator!= (const ConstCouplingRow & other) const
     430             :   {
     431             :     return !(*this == other);
     432             :   }
     433             : protected:
     434             : 
     435             :   friend class ConstCouplingRowConstIterator;
     436             : 
     437             :   std::size_t _row_i;
     438             :   const CouplingMatrix & _mat;
     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             : 
     457             : class ConstCouplingRowConstIterator
     458             : {
     459             : public:
     460           0 :   ConstCouplingRowConstIterator (const ConstCouplingRow & row_in,
     461             :                                  std::size_t loc_in,
     462         360 :                                  CouplingMatrix::rc_type::const_iterator it_in) :
     463         328 :     _location(loc_in),
     464         328 :     _row(row_in),
     465         360 :     _it(it_in)
     466             :   {
     467             : #ifndef NDEBUG
     468           0 :     if (_it != _row._mat._ranges.end())
     469             :       {
     470           0 :         libmesh_assert_less_equal(_it->first, _location);
     471           0 :         libmesh_assert_less_equal(_location, _it->second);
     472             :       }
     473             :     else
     474             :       {
     475           0 :         libmesh_assert_equal_to
     476             :           (_location, std::numeric_limits<size_t>::max());
     477             :       }
     478             : #endif
     479           0 :   }
     480             : 
     481           0 :   std::size_t operator* ()
     482             :   {
     483           0 :     libmesh_assert_not_equal_to
     484             :       (_location, std::numeric_limits<std::size_t>::max());
     485         450 :     return _location % _row._mat.size();
     486             :   }
     487             : 
     488         450 :   ConstCouplingRowConstIterator & operator++ ()
     489             :   {
     490           0 :     libmesh_assert_not_equal_to
     491             :       (_location, std::numeric_limits<std::size_t>::max());
     492           0 :     libmesh_assert
     493             :       (_it != _row._mat._ranges.end());
     494             : 
     495         450 :     if (_location >= _it->second)
     496             :       {
     497           0 :         ++_it;
     498             : 
     499             :         // Are we past the end of the matrix?
     500         180 :         if (_it == _row._mat._ranges.end())
     501          90 :           _location = std::numeric_limits<std::size_t>::max();
     502             :         else
     503             :           {
     504          90 :             _location = _it->first;
     505             :             // Is the new range past the end of the row?
     506          90 :             if (_location > _row._end_location)
     507          90 :               _location = std::numeric_limits<std::size_t>::max();
     508             :           }
     509             :       }
     510             :     // Would incrementing put us past the end of the row?
     511         270 :     else if (_location >= _row._end_location)
     512          90 :       _location = std::numeric_limits<std::size_t>::max();
     513             :     else
     514         180 :       ++_location;
     515             : 
     516         450 :     return *this;
     517             :   }
     518             : 
     519           0 :   bool operator== (const ConstCouplingRowConstIterator & other) const
     520             :   {
     521             :     // Thinking that iterators from different row objects are equal
     522             :     // is not even wrong
     523           0 :     libmesh_assert(_row == other._row);
     524             : 
     525         738 :     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           0 :   bool operator!= (const ConstCouplingRowConstIterator & other) const
     534             :   {
     535          72 :     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;
     542             :   const ConstCouplingRow & _row;
     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
     553           0 : ConstCouplingRow::const_iterator ConstCouplingRow::begin() const
     554             : {
     555         360 :   return const_iterator (*this, _begin_location, _begin_it);
     556             : }
     557             : 
     558             : inline
     559           0 : ConstCouplingRow::const_iterator ConstCouplingRow::end() const
     560             : {
     561             :   return const_iterator
     562             :     (*this, std::numeric_limits<std::size_t>::max(),
     563         328 :      _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
     595           0 : CouplingAccessor CouplingMatrix::operator() (const std::size_t i,
     596             :                                              const std::size_t j)
     597             : {
     598           0 :   const std::size_t location = std::size_t(i)*_size + j;
     599             : 
     600           0 :   return CouplingAccessor(location, *this);
     601             : }
     602             : 
     603             : 
     604             : 
     605             : inline
     606           0 : std::size_t CouplingMatrix::size() const
     607             : {
     608         738 :   return _size;
     609             : }
     610             : 
     611             : 
     612             : 
     613             : inline
     614           0 : void CouplingMatrix::resize(const std::size_t n)
     615             : {
     616           0 :   _size = n;
     617             : 
     618           0 :   _ranges.clear();
     619           0 : }
     620             : 
     621             : 
     622             : 
     623             : inline
     624             : void CouplingMatrix::clear()
     625             : {
     626             :   this->resize(0);
     627             : }
     628             : 
     629             : 
     630             : 
     631             : inline
     632           0 : bool CouplingMatrix::empty() const
     633             : {
     634           0 :   return (_size == 0);
     635             : }
     636             : 
     637             : 
     638             : } // namespace libMesh
     639             : 
     640             : 
     641             : #endif // LIBMESH_COUPLING_MATRIX_H

Generated by: LCOV version 1.14