Xyst test code coverage report
Current view: top level - LinearSolver - ConjugateGradients.cpp (source / functions) Hit Total Coverage
Commit: d790e211db0f2cf155e72db869cf1a5a372c10f5 Lines: 320 323 99.1 %
Date: 2025-02-17 15:39:21 Functions: 25 26 96.2 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 261 362 72.1 %

           Branch data     Line data    Source code
       1                 :            : // *****************************************************************************
       2                 :            : /*!
       3                 :            :   \file      src/LinearSolver/ConjugateGradients.cpp
       4                 :            :   \copyright 2012-2015 J. Bakosi,
       5                 :            :              2016-2018 Los Alamos National Security, LLC.,
       6                 :            :              2019-2021 Triad National Security, LLC.,
       7                 :            :              2022-2025 J. Bakosi
       8                 :            :              All rights reserved. See the LICENSE file for details.
       9                 :            :   \brief     Charm++ chare array for distributed conjugate gradients.
      10                 :            :   \details   Charm++ chare array for asynchronous distributed
      11                 :            :              conjugate gradients linear solver for the symmetric linear system
      12                 :            :              A * x = b, where A is in compressed sparse row storage, containing
      13                 :            :              only the upper triangular part of A.
      14                 :            :   \see Y. Saad, Iterative Methods for Sparse Linear Systems: 2nd Edition,
      15                 :            :     ISBN 9780898718003, 2003.
      16                 :            : 
      17                 :            :     Algorithm 'Preconditioned Conjugate Gradient':
      18                 :            :     ```
      19                 :            :     Compute r0 := b - A x0, z0 := M^{-1} r0, p0 := z0
      20                 :            :     For j=0,1,..., until convergence, do
      21                 :            :       alpha_j := (r_j,z_j) / (A p_j,p_j)
      22                 :            :       x_{j+1} := x_j + alpha_j p_j
      23                 :            :       r_{j+1} := r_j - alpha_j A p_j
      24                 :            :       z_{j+1} := M^{-1} r_{j+1}
      25                 :            :       beta_j  := (r_{j+1},z_{j+1}) / (r_j,z_j)
      26                 :            :       p_{j+1} := z_{j+1} + beta_j p_j
      27                 :            :     end
      28                 :            :     ```
      29                 :            : */
      30                 :            : // *****************************************************************************
      31                 :            : 
      32                 :            : #include <numeric>
      33                 :            : 
      34                 :            : #include "Exception.hpp"
      35                 :            : #include "ConjugateGradients.hpp"
      36                 :            : #include "Vector.hpp"
      37                 :            : #include "ContainerUtil.hpp"
      38                 :            : #include "Reorder.hpp"
      39                 :            : #include "Print.hpp"
      40                 :            : 
      41                 :            : using tk::ConjugateGradients;
      42                 :            : 
      43                 :        808 : ConjugateGradients::ConjugateGradients(
      44                 :            :   const CSR& A,
      45                 :            :   const std::vector< tk::real >& x,
      46                 :            :   const std::vector< tk::real >& b,
      47                 :            :   const std::vector< std::size_t >& gid,
      48                 :            :   const std::unordered_map< std::size_t, std::size_t >& lid,
      49                 :            :   const std::unordered_map< int,
      50                 :        808 :           std::unordered_set< std::size_t > >& nodecommmap ) :
      51         [ +  - ]:        808 :   m_A( A ),
      52         [ +  - ]:        808 :   m_An( A ),
      53         [ +  - ]:        808 :   m_x( x ),
      54         [ +  - ]:        808 :   m_b( b ),
      55 [ +  - ][ -  - ]:        808 :   m_pc( "none" ),
      56         [ +  - ]:        808 :   m_gid( gid ),
      57                 :            :   m_lid( lid ),
      58                 :            :   m_nodeCommMap( nodecommmap ),
      59 [ +  - ][ +  - ]:        808 :   m_r( m_A.rsize(), 0.0 ),
      60         [ +  - ]:        808 :   m_z( m_A.rsize(), 0.0 ),
      61 [ +  - ][ -  - ]:        808 :   m_d( m_A.rsize(), 0.0 ),
      62                 :        808 :   m_nr( 0 ),
      63         [ +  - ]:        808 :   m_na( 0 ),
      64                 :        808 :   m_nb( 0 ),
      65         [ +  - ]:        808 :   m_p( m_A.rsize(), 0.0 ),
      66 [ +  - ][ +  + ]:        808 :   m_q( m_A.rsize(), 0.0 ),
                 [ -  - ]
      67                 :        808 :   m_nq( 0 ),
      68                 :        808 :   m_nd( 0 ),
      69         [ +  + ]:        808 :   m_initres(),
      70                 :        808 :   m_solved(),
      71                 :        808 :   m_normb( 0.0 ),
      72                 :        808 :   m_it( 0 ),
      73                 :        808 :   m_maxit( 0 ),
      74                 :        808 :   m_finished( false ),
      75                 :        808 :   m_verbose( 0 ),
      76                 :        808 :   m_rho( 0.0 ),
      77                 :        808 :   m_rho0( 0.0 ),
      78                 :        808 :   m_alpha( 0.0 ),
      79                 :        808 :   m_converged( false ),
      80                 :        808 :   m_nx( 0 ),
      81 [ +  - ][ +  + ]:       1616 :   m_apply( false )
      82                 :            : // *****************************************************************************
      83                 :            : //  Constructor
      84                 :            : //! \param[in] A Left hand side matrix of the linear system to solve in Ax=b
      85                 :            : //! \param[in] x Solution (initial guess) of the linear system to solve in Ax=b
      86                 :            : //! \param[in] b Right hand side of the linear system to solve in Ax=b
      87                 :            : //! \param[in] gid Global node ids
      88                 :            : //! \param[in] lid Local node ids associated to global ones
      89                 :            : //! \param[in] nodecommmap Global mesh node IDs shared with other chares
      90                 :            : //!   associated to their chare IDs
      91                 :            : // *****************************************************************************
      92                 :            : {
      93                 :            :   // Fill in gid and lid for serial solve
      94 [ +  + ][ +  - ]:        808 :   if (gid.empty() || lid.empty() || nodecommmap.empty()) {
                 [ +  + ]
      95         [ +  - ]:         25 :     m_gid.resize( m_A.rsize()/m_A.Ncomp() );
      96                 :            :     std::iota( begin(m_gid), end(m_gid), 0 );
      97 [ +  - ][ +  + ]:      18543 :     for (auto g : m_gid) m_lid[g] = g;
      98                 :            :   }
      99                 :            : 
     100                 :            :   Assert( m_A.rsize() == m_gid.size()*A.Ncomp(), "Size mismatch" );
     101                 :            :   Assert( m_x.size() == m_gid.size()*A.Ncomp(), "Size mismatch" );
     102                 :            :   Assert( m_b.size() == m_gid.size()*A.Ncomp(), "Size mismatch" );
     103                 :        808 : }
     104                 :            : 
     105                 :            : void
     106                 :       6244 : ConjugateGradients::setup( CkCallback c )
     107                 :            : // *****************************************************************************
     108                 :            : //  Setup solver
     109                 :            : //! \param[in] c Call to continue with
     110                 :            : //! \details This function initiates computing the residual (r=b-A*x), its dot
     111                 :            : //!   product, and the rhs norm.
     112                 :            : // *****************************************************************************
     113                 :            : {
     114                 :       6244 :   m_initres = c;
     115                 :       6244 :   m_converged = false;
     116                 :       6244 :   m_finished = false;
     117                 :            : 
     118                 :            :   // initiate computing A * x (for the initial residual)
     119         [ +  - ]:       6244 :   thisProxy[ thisIndex ].wait4res();
     120                 :       6244 :   residual();
     121                 :       6244 :   pc();
     122                 :            : 
     123                 :            :   // initiate computing norm of right hand side
     124         [ +  - ]:       6244 :   dot( m_b, m_b,
     125                 :       6244 :        CkCallback( CkReductionTarget(ConjugateGradients,normb), thisProxy ) );
     126                 :       6244 : }
     127                 :            : 
     128                 :            : void
     129                 :     709403 : ConjugateGradients::dot( const std::vector< tk::real >& a,
     130                 :            :                          const std::vector< tk::real >& b,
     131                 :            :                          CkCallback c )
     132                 :            : // *****************************************************************************
     133                 :            : //  Initiate computation of dot product of two vectors
     134                 :            : //! \param[in] a 1st vector of dot product
     135                 :            : //! \param[in] b 2nd vector of dot product
     136                 :            : //! \param[in] c Callback to target with the final result
     137                 :            : // *****************************************************************************
     138                 :            : {
     139                 :            :   Assert( a.size() == b.size(), "Size mismatch" );
     140                 :            : 
     141                 :     709403 :   tk::real D = 0.0;
     142                 :            :   auto ncomp = m_A.Ncomp();
     143         [ +  + ]:  152957964 :   for (std::size_t i=0; i<a.size()/ncomp; ++i) {
     144                 :  152248561 :     auto incomp = i*ncomp;
     145         [ +  + ]:  152248561 :     if (not slave(m_nodeCommMap,m_gid[i],thisIndex))
     146         [ +  + ]:  284016756 :       for (std::size_t d=0; d<ncomp; ++d)
     147                 :  142417382 :         D += a[incomp+d] * b[incomp+d];
     148                 :            :   }
     149                 :            : 
     150                 :     709403 :   contribute( sizeof(tk::real), &D, CkReduction::sum_double, c );
     151                 :     709403 : }
     152                 :            : 
     153                 :            : void
     154                 :       6244 : ConjugateGradients::normb( tk::real n )
     155                 :            : // *****************************************************************************
     156                 :            : // Compute the norm of the right hand side
     157                 :            : //! \param[in] n Norm of right hand side (aggregated across all chares)
     158                 :            : // *****************************************************************************
     159                 :            : {
     160                 :       6244 :   m_normb = std::sqrt(n);
     161                 :       6244 :   normb_complete();
     162                 :       6244 : }
     163                 :            : 
     164                 :            : void
     165                 :       6244 : ConjugateGradients::residual()
     166                 :            : // *****************************************************************************
     167                 :            : //  Initiate A * x for computing the initial residual, r = b - A * x
     168                 :            : // *****************************************************************************
     169                 :            : {
     170                 :            :   // Compute own contribution to r = A * x
     171                 :       6244 :   m_A.mult( m_x, m_r );
     172                 :            : 
     173                 :            :   // Send partial product on chare-boundary nodes to fellow chares
     174         [ +  + ]:       6244 :   if (m_nodeCommMap.empty()) {
     175                 :        257 :     comres_complete();
     176                 :            :   } else {
     177                 :            :     auto ncomp = m_A.Ncomp();
     178         [ +  + ]:      62353 :     for (const auto& [c,n] : m_nodeCommMap) {
     179                 :      56366 :       std::vector< std::vector< tk::real > > rc( n.size() );
     180                 :            :       std::size_t j = 0;
     181         [ +  + ]:     317924 :       for (auto g : n) {
     182         [ +  - ]:     261558 :         std::vector< tk::real > nr( ncomp );
     183                 :     261558 :         auto i = tk::cref_find( m_lid, g );
     184         [ +  + ]:     524112 :         for (std::size_t d=0; d<ncomp; ++d) nr[d] = m_r[ i*ncomp+d ];
     185                 :     261558 :         rc[j++] = std::move(nr);
     186                 :            :       }
     187 [ +  - ][ +  - ]:     112732 :       thisProxy[c].comres( std::vector<std::size_t>(begin(n),end(n)), rc );
         [ +  - ][ -  + ]
                 [ -  - ]
     188                 :      56366 :     }
     189                 :            :   }
     190                 :            : 
     191                 :       6244 :   ownres_complete();
     192                 :       6244 : }
     193                 :            : 
     194                 :            : void
     195                 :      56366 : ConjugateGradients::comres( const std::vector< std::size_t >& gid,
     196                 :            :                             const std::vector< std::vector< tk::real > >& rc )
     197                 :            : // *****************************************************************************
     198                 :            : //  Receive contributions to A * x on chare-boundaries
     199                 :            : //! \param[in] gid Global mesh node IDs at which we receive contributions
     200                 :            : //! \param[in] rc Partial contributions at chare-boundary nodes
     201                 :            : // *****************************************************************************
     202                 :            : {
     203                 :            :   Assert( rc.size() == gid.size(), "Size mismatch" );
     204                 :            : 
     205         [ +  + ]:     317924 :   for (std::size_t i=0; i<gid.size(); ++i) m_rc[ gid[i] ] += rc[i];
     206                 :            : 
     207         [ +  + ]:      56366 :   if (++m_nr == m_nodeCommMap.size()) {
     208                 :       5987 :     m_nr = 0;
     209                 :       5987 :     comres_complete();
     210                 :            :   }
     211                 :      56366 : }
     212                 :            : 
     213                 :            : void
     214                 :       6244 : ConjugateGradients::pc()
     215                 :            : // *****************************************************************************
     216                 :            : //  Setup preconditioner
     217                 :            : // *****************************************************************************
     218                 :            : {
     219                 :            :   auto ncomp = m_A.Ncomp();
     220                 :            : 
     221         [ +  + ]:       6244 :   if (m_pc == "none") {
     222                 :            : 
     223         [ +  + ]:     113795 :     for (std::size_t i=0; i<m_q.size()/ncomp; ++i) {
     224                 :     109673 :       auto c = tk::count( m_nodeCommMap, m_gid[i] );
     225         [ +  + ]:     219420 :       for (std::size_t d=0; d<ncomp; ++d) {
     226                 :     109747 :         m_q[i*ncomp+d] = 1.0 / c;
     227                 :            :       }
     228                 :            :     }
     229                 :            : 
     230         [ +  - ]:       2122 :   } else if (m_pc == "jacobi") {
     231                 :            : 
     232                 :            :     // Extract Jacobi preconditioner from matrix
     233         [ +  + ]:     489094 :     for (std::size_t i=0; i<m_q.size()/ncomp; ++i) {
     234         [ +  + ]:    1022904 :       for (std::size_t d=0; d<ncomp; ++d) {
     235                 :     535932 :         m_q[i*ncomp+d] = m_A(i,i,d);
     236                 :            :       }
     237                 :            :     }
     238                 :            : 
     239                 :            :   }
     240                 :            : 
     241                 :            :   // Send partial preconditioner on chare-boundary nodes to fellow chares
     242         [ +  + ]:       6244 :   if (m_nodeCommMap.empty()) {
     243                 :        257 :     comd_complete();
     244                 :            :   } else {
     245         [ +  + ]:      62353 :     for (const auto& [c,n] : m_nodeCommMap) {
     246                 :      56366 :       std::vector< std::vector< tk::real > > qc( n.size() );
     247                 :            :       std::size_t j = 0;
     248         [ +  + ]:     317924 :       for (auto g : n) {
     249         [ +  - ]:     261558 :         std::vector< tk::real > nq( ncomp );
     250                 :     261558 :         auto i = tk::cref_find( m_lid, g );
     251         [ +  + ]:     524112 :         for (std::size_t d=0; d<ncomp; ++d) nq[d] = m_q[ i*ncomp+d ];
     252                 :     261558 :         qc[j++] = std::move(nq);
     253                 :            :       }
     254 [ +  - ][ +  - ]:     112732 :       thisProxy[c].comd( std::vector<std::size_t>(begin(n),end(n)), qc );
         [ +  - ][ -  + ]
                 [ -  - ]
     255                 :      56366 :     }
     256                 :            :   }
     257                 :            : 
     258                 :       6244 :   ownd_complete();
     259                 :       6244 : }
     260                 :            : 
     261                 :            : void
     262                 :      56366 : ConjugateGradients::comd( const std::vector< std::size_t >& gid,
     263                 :            :                           const std::vector< std::vector< tk::real > >& qc )
     264                 :            : // *****************************************************************************
     265                 :            : //  Receive contributions to preconditioner on chare-boundaries
     266                 :            : //! \param[in] gid Global mesh node IDs at which we receive contributions
     267                 :            : //! \param[in] qc Partial contributions at chare-boundary nodes
     268                 :            : // *****************************************************************************
     269                 :            : {
     270                 :            :   Assert( qc.size() == gid.size(), "Size mismatch" );
     271                 :            : 
     272         [ +  + ]:     317924 :   for (std::size_t i=0; i<gid.size(); ++i) m_qc[ gid[i] ] += qc[i];
     273                 :            : 
     274         [ +  + ]:      56366 :   if (++m_nd == m_nodeCommMap.size()) {
     275                 :       5987 :     m_nd = 0;
     276                 :       5987 :     comd_complete();
     277                 :            :   }
     278                 :      56366 : }
     279                 :            : 
     280                 :            : void
     281                 :       6244 : ConjugateGradients::initres()
     282                 :            : // *****************************************************************************
     283                 :            : // Finish computing the initial residual, r = b - A * x
     284                 :            : // *****************************************************************************
     285                 :            : {
     286                 :            :   auto ncomp = m_A.Ncomp();
     287                 :            : 
     288                 :            :   // Combine own and communicated contributions to r = A * x
     289         [ +  + ]:     138391 :   for (const auto& [gid,r] : m_rc) {
     290                 :     132147 :     auto i = tk::cref_find( m_lid, gid );
     291         [ +  + ]:     265290 :     for (std::size_t c=0; c<ncomp; ++c) m_r[i*ncomp+c] += r[c];
     292                 :            :   }
     293                 :       6244 :   tk::destroy( m_rc );
     294                 :            : 
     295                 :            :   // Finish computing the initial residual, r = b - A * x
     296         [ +  + ]:     651923 :   for (auto& r : m_r) r *= -1.0;
     297                 :       6244 :   m_r += m_b;
     298                 :            : 
     299                 :            :   // Initialize p
     300                 :       6244 :   m_p = m_r;
     301                 :            : 
     302                 :            :   // Combine own and communicated contributions to q = A * p
     303         [ +  + ]:     144633 :   for (const auto& [gid,q] : m_qc) {
     304                 :     138389 :     auto i = tk::cref_find( m_lid, gid );
     305         [ +  + ]:     277774 :     for (std::size_t c=0; c<ncomp; ++c)
     306                 :     139385 :       m_q[i*ncomp+c] += q[c];
     307                 :            :   }
     308                 :       6244 :   tk::destroy( m_qc );
     309                 :            : 
     310                 :            :   // Set preconditioner
     311                 :       6244 :   m_d = m_q;
     312                 :            : 
     313                 :            :   // initialize preconditioned solve: compute z = M^{-1} * r
     314         [ +  + ]:     651923 :   for (std::size_t i=0; i<m_z.size(); ++i) m_z[i] = m_r[i] / m_d[i];
     315                 :            : 
     316                 :            :   // initiate computing the dot product of the initial residual, rho = (r,z)
     317         [ +  - ]:       6244 :   dot( m_r, m_z,
     318                 :       6244 :        CkCallback( CkReductionTarget(ConjugateGradients,rho), thisProxy ) );
     319                 :       6244 : }
     320                 :            : 
     321                 :            : void
     322                 :       6244 : ConjugateGradients::rho( tk::real r )
     323                 :            : // *****************************************************************************
     324                 :            : // Compute rho = (r,z)
     325                 :            : //! \param[in] r Dot product, rho = (r,z) (aggregated across all chares)
     326                 :            : // *****************************************************************************
     327                 :            : {
     328                 :            :   // store dot product of residual
     329                 :       6244 :   m_rho = r;
     330                 :            : 
     331                 :            :   // send back rhs norm to caller
     332                 :       6244 :   m_initres.send( CkDataMsg::buildNew( sizeof(tk::real), &m_normb ) );
     333                 :       6244 : }
     334                 :            : 
     335                 :            : void
     336                 :       6238 : ConjugateGradients::init(
     337                 :            :   const std::vector< tk::real >& x,
     338                 :            :   const std::vector< tk::real >& b,
     339                 :            :   const std::vector< tk::real >& neubc,
     340                 :            :   const std::unordered_map< std::size_t,
     341                 :            :           std::vector< std::pair< int, tk::real > > >& dirbc,
     342                 :            :   bool apply,
     343                 :            :   const std::string& pc,
     344                 :            :   CkCallback cb )
     345                 :            : // *****************************************************************************
     346                 :            : //  Initialize linear solve: set initial guess and boundary conditions
     347                 :            : //! \param[in] x Initial guess
     348                 :            : //! \param[in] b Right hand side vector
     349                 :            : //! \param[in] neubc Right hand side vector with Neumann BCs partially applied
     350                 :            : //! \param[in] dirbc Local node ids and associated Dirichlet BCs
     351                 :            : //! \param[in] apply True to apply boundary conditions
     352                 :            : //! \param[in] pc Preconditioner to use
     353                 :            : //! \param[in] cb Call to continue with when initialized and ready for a solve
     354                 :            : //! \details This function allows setting the initial guess and boundary
     355                 :            : //!   conditions, followed by computing the initial residual and the rhs norm.
     356                 :            : //!   It also performs communication of BC data.
     357                 :            : // *****************************************************************************
     358                 :            : {
     359                 :            :   // Configure preconditioner
     360                 :       6238 :   m_pc = pc;
     361                 :            : 
     362                 :            :   // Optionally set initial guess
     363         [ -  + ]:       6238 :   if (!x.empty()) m_x = x; //else std::fill( begin(m_x), end(m_x), 0.0 );
     364                 :            : 
     365                 :            :   // Optionally set rhs, assumed complete in parallel
     366         [ +  - ]:       6238 :   if (!b.empty()) m_b = b;
     367                 :            : 
     368                 :            :   // Save if applied BCs
     369                 :       6238 :   m_apply = apply;
     370         [ -  + ]:       6238 :   if (not m_apply) {
     371                 :            : 
     372                 :            :     // Recompute initial residual (r=b-A*x), its dot product, and the rhs norm
     373         [ -  - ]:          0 :     setup( cb );
     374                 :            : 
     375                 :            :   }
     376                 :            :   else {
     377                 :            : 
     378                 :            :     // Save matrix state
     379                 :       6238 :     m_An = m_A;
     380                 :            : 
     381                 :            :     // Set Neumann BCs, partial in parallel, communication below
     382         [ +  + ]:       6238 :     if (!neubc.empty()) m_q = neubc; else std::fill(begin(m_q), end(m_q), 0.0);
     383                 :            : 
     384                 :            :     // Store incoming Dirichlet BCs, partial in parallel, communication below
     385                 :       6238 :     tk::destroy(m_dirbc);
     386         [ +  + ]:      39739 :     for (auto&& [i,bcval] : dirbc) m_dirbc[i] = std::move(bcval);
     387                 :            : 
     388                 :            :     // Get ready to communicate boundary conditions. This is necessary because
     389                 :            :     // there can be nodes a chare contributes to but does not apply BCs on.
     390                 :            :     // This happens if a node is in the node communication map but not on the
     391                 :            :     // list of incoming BCs on this chare. To have all chares share the same
     392                 :            :     // view on all BC nodes, we send the global node ids together with the
     393                 :            :     // Dirichlet BCs at which BCs are set to those fellow chares that also
     394                 :            :     // contribute to those BC nodes. Only after this communication step will we
     395                 :            :     // apply the BCs, which then will correctly setup the BC rows of the matrix
     396                 :            :     // and on the rhs that (1) may contain a nonzero value, (2) may have partial
     397                 :            :     // contributions due to Neumann BCs, and (3) will be modified to apply
     398                 :            :     // Dirichlet BCs.
     399         [ +  - ]:       6238 :     thisProxy[ thisIndex ].wait4bc();
     400 [ +  - ][ +  + ]:      12476 :     thisProxy[ thisIndex ].wait4r();
     401                 :            : 
     402                 :            :     // Send boundary conditions to those who contribute to those rows
     403         [ +  + ]:       6238 :     if (m_nodeCommMap.empty()) {
     404                 :        255 :       combc_complete();
     405                 :            :     } else {
     406                 :            :       auto ncomp = m_A.Ncomp();
     407         [ +  + ]:      62345 :       for (const auto& [c,n] : m_nodeCommMap) {
     408                 :            :         decltype(m_dirbc) dbc;
     409         [ +  - ]:      56362 :         std::vector< std::vector< tk::real > > qc( n.size() );
     410                 :            :         std::size_t k = 0;
     411         [ +  + ]:     317884 :         for (auto g : n) {
     412                 :     261522 :           auto i = tk::cref_find( m_lid, g );
     413                 :            :           auto j = m_dirbc.find(i);
     414 [ +  + ][ +  - ]:     261522 :           if (j != end(m_dirbc)) dbc[g] = j->second;
                 [ +  - ]
     415         [ +  - ]:     261522 :           std::vector< tk::real > nq( ncomp );
     416         [ +  + ]:     524004 :           for (std::size_t d=0; d<ncomp; ++d) nq[d] = m_q[ i*ncomp+d ];
     417                 :     261522 :           qc[k++] = std::move(nq);
     418                 :            :         }
     419         [ +  - ]:      56362 :         std::vector< std::size_t > gid( begin(n), end(n) );
     420 [ +  - ][ +  - ]:     112724 :         thisProxy[c].combc( dbc, gid, qc );
         [ +  - ][ -  - ]
     421                 :      56362 :       }
     422                 :            :     }
     423                 :            : 
     424         [ +  - ]:      12476 :     ownbc_complete( cb );
     425                 :            : 
     426                 :            :   }
     427                 :       6238 : }
     428                 :            : 
     429                 :            : void
     430                 :      56362 : ConjugateGradients::combc(
     431                 :            :   const std::map< std::size_t, std::vector< std::pair< int, tk::real > > >& dbc,
     432                 :            :   const std::vector< std::size_t >& gid,
     433                 :            :   const std::vector< std::vector< tk::real > >& qc )
     434                 :            : // *****************************************************************************
     435                 :            : //  Receive contributions to boundary conditions and rhs on chare-boundaries
     436                 :            : //! \param[in] dbc Contributions to Dirichlet boundary conditions
     437                 :            : //! \param[in] gid Global mesh node IDs at which we receive contributions
     438                 :            : //! \param[in] qc Contributions to Neumann boundary conditions
     439                 :            : // *****************************************************************************
     440                 :            : {
     441         [ +  + ]:      57632 :   for (const auto& [g,dirbc] : dbc) m_dirbcc[g] = dirbc;
     442         [ +  + ]:     317884 :   for (std::size_t i=0; i<gid.size(); ++i) m_qc[ gid[i] ] += qc[i];
     443                 :            : 
     444         [ +  + ]:      56362 :   if (++m_nb == m_nodeCommMap.size()) {
     445                 :       5983 :     m_nb = 0;
     446                 :       5983 :     combc_complete();
     447                 :            :   }
     448                 :      56362 : }
     449                 :            : 
     450                 :            : void
     451                 :       6238 : ConjugateGradients::apply( CkCallback cb )
     452                 :            : // *****************************************************************************
     453                 :            : //  Apply boundary conditions
     454                 :            : //! \param[in] cb Call to continue with after applying the BCs is complete
     455                 :            : // *****************************************************************************
     456                 :            : {
     457                 :            :   auto ncomp = m_A.Ncomp();
     458                 :            : 
     459                 :            :   // Merge own and received contributions to Dirichlet boundary conditions
     460         [ +  + ]:       7325 :   for (const auto& [g,dirbc] : m_dirbcc) {
     461                 :       1087 :     m_dirbc[ tk::cref_find(m_lid,g) ] = dirbc;
     462                 :            :   }
     463                 :       6238 :   tk::destroy( m_dirbcc );
     464                 :            : 
     465                 :            :   // Merge own and received contributions to Neumann boundary conditions
     466         [ +  + ]:     144591 :   for (const auto& [gid,q] : m_qc) {
     467                 :     138353 :     auto i = tk::cref_find( m_lid, gid );
     468         [ +  + ]:     277666 :     for (std::size_t c=0; c<ncomp; ++c) m_q[i*ncomp+c] += q[c];
     469                 :            :   }
     470                 :       6238 :   tk::destroy( m_qc );
     471                 :            : 
     472                 :            :   // Apply Neumann BCs on rhs
     473                 :       6238 :   m_b += m_q;
     474                 :            : 
     475                 :            :   // Apply Dirichlet BCs on matrix and rhs (with decreasing local id)
     476                 :            :   std::fill( begin(m_r), end(m_r), 0.0 );
     477         [ +  + ]:      39739 :   for (auto bi = m_dirbc.rbegin(); bi != m_dirbc.rend(); ++bi) {
     478                 :      33501 :     auto i = bi->first;
     479                 :            :     const auto& dirbc = bi->second;
     480         [ +  + ]:     115962 :     for (std::size_t j=0; j<ncomp; ++j) {
     481         [ +  + ]:      82461 :       if (dirbc[j].first) {
     482                 :      66141 :         m_A.dirichlet( i, dirbc[j].second, m_r, m_gid, m_nodeCommMap, j );
     483                 :            :       }
     484                 :            :     }
     485                 :            :   }
     486                 :            : 
     487                 :            :   // Send rhs with Dirichlet BCs partially applied to fellow chares
     488         [ +  + ]:       6238 :   if (m_nodeCommMap.empty()) {
     489                 :        255 :     comr_complete();
     490                 :            :   } else {
     491         [ +  + ]:      62345 :     for (const auto& [c,n] : m_nodeCommMap) {
     492                 :      56362 :       std::vector< std::vector< tk::real > > rc( n.size() );
     493                 :            :       std::size_t j = 0;
     494         [ +  + ]:     317884 :       for (auto g : n) {
     495         [ +  - ]:     261522 :         std::vector< tk::real > nr( ncomp );
     496                 :     261522 :         auto i = tk::cref_find( m_lid, g );
     497         [ +  + ]:     524004 :         for (std::size_t d=0; d<ncomp; ++d) nr[d] = m_r[ i*ncomp+d ];
     498                 :     261522 :         rc[j++] = std::move(nr);
     499                 :            :       }
     500 [ +  - ][ +  - ]:     112724 :       thisProxy[c].comr( std::vector<std::size_t>(begin(n),end(n)), rc );
         [ +  - ][ -  + ]
                 [ -  - ]
     501                 :      56362 :     }
     502                 :            :   }
     503                 :            : 
     504         [ +  - ]:       6238 :   ownr_complete( cb );
     505                 :       6238 : }
     506                 :            : 
     507                 :            : void
     508                 :      56362 : ConjugateGradients::comr( const std::vector< std::size_t >& gid,
     509                 :            :                           const std::vector< std::vector< tk::real > >& rc )
     510                 :            : // *****************************************************************************
     511                 :            : //  Receive contributions to rhs with Dirichlet BCs applied on chare-boundaries
     512                 :            : //! \param[in] gid Global mesh node IDs at which we receive contributions
     513                 :            : //! \param[in] rc Partial contributions at chare-boundary nodes
     514                 :            : // *****************************************************************************
     515                 :            : {
     516                 :            :   Assert( rc.size() == gid.size(), "Size mismatch" );
     517                 :            : 
     518         [ +  + ]:     317884 :   for (std::size_t i=0; i<gid.size(); ++i) m_rc[ gid[i] ] += rc[i];
     519                 :            : 
     520         [ +  + ]:      56362 :   if (++m_na == m_nodeCommMap.size()) {
     521                 :       5983 :     m_na = 0;
     522                 :       5983 :     comr_complete();
     523                 :            :   }
     524                 :      56362 : }
     525                 :            : 
     526                 :            : void
     527                 :       6238 : ConjugateGradients::r( CkCallback cb )
     528                 :            : // *****************************************************************************
     529                 :            : //  Finish computing rhs with BCs applied
     530                 :            : //! \param[in] cb Call to continue with after applying the BCs is complete
     531                 :            : // *****************************************************************************
     532                 :            : {
     533                 :            :   auto ncomp = m_A.Ncomp();
     534                 :            : 
     535                 :            :   // Combine own and communicated contributions to Dirichlet BCs applied to rhs
     536         [ +  + ]:     144591 :   for (const auto& [gid,r] : m_rc) {
     537                 :     138353 :     auto i = tk::cref_find( m_lid, gid );
     538         [ +  + ]:     277666 :     for (std::size_t c=0; c<ncomp; ++c) m_r[i*ncomp+c] += r[c];
     539                 :            :   }
     540                 :       6238 :   tk::destroy( m_rc );
     541                 :            : 
     542                 :            :   // Subtract matrix columns * BC values from rhs at Dirichlet BC nodes
     543                 :       6238 :   m_b -= m_r;
     544                 :            : 
     545                 :            :   // Apply Dirichlet BCs on rhs
     546         [ +  + ]:      39739 :   for (const auto& [i,dirbc] : m_dirbc) {
     547         [ +  + ]:     115962 :     for (std::size_t j=0; j<ncomp; ++j) {
     548         [ +  + ]:      82461 :       if (dirbc[j].first) {
     549                 :      66141 :         m_b[i*ncomp+j] = dirbc[j].second;
     550                 :            :       }
     551                 :            :     }
     552                 :            :   }
     553                 :            : 
     554                 :            :   // Recompute initial residual (r=b-A*x), its dot product, and the rhs norm
     555         [ +  - ]:       6238 :   setup( cb );
     556                 :       6238 : }
     557                 :            : 
     558                 :            : void
     559                 :       6244 : ConjugateGradients::solve( std::size_t maxit,
     560                 :            :                            tk::real tol,
     561                 :            :                            int pe,
     562                 :            :                            uint64_t verbose,
     563                 :            :                            CkCallback c )
     564                 :            : // *****************************************************************************
     565                 :            : //  Solve linear system
     566                 :            : //! \param[in] maxit Max iteration count
     567                 :            : //! \param[in] tol Stop tolerance
     568                 :            : //! \param[in] pe Processing element
     569                 :            : //! \param[in] verbose Verbose output
     570                 :            : //! \param[in] c Call to continue with after solve is complete
     571                 :            : // *****************************************************************************
     572                 :            : {
     573                 :       6244 :   m_maxit = maxit;
     574                 :       6244 :   m_tol = tol;
     575                 :       6244 :   m_solved = c;
     576                 :       6244 :   m_it = 0;
     577         [ +  + ]:       6244 :   m_verbose = pe == 0 ? verbose : 0;
     578                 :            : 
     579         [ +  + ]:       6244 :   if (m_verbose > 1) tk::Print() << "Xyst> Conjugate gradients start\n";
     580                 :            : 
     581         [ -  + ]:       6244 :   if (m_converged) x(); else next();
     582                 :       6244 : }
     583                 :            : 
     584                 :            : void
     585                 :     232305 : ConjugateGradients::next()
     586                 :            : // *****************************************************************************
     587                 :            : //  Start next linear solver iteration
     588                 :            : // *****************************************************************************
     589                 :            : {
     590         [ +  + ]:     232305 :   if (m_it == 0) m_alpha = 0.0; else m_alpha = m_rho/m_rho0;
     591                 :     232305 :   m_rho0 = m_rho;
     592                 :            : 
     593                 :            :   // compute p = z + alpha * p
     594         [ +  + ]:   50826858 :   for (std::size_t i=0; i<m_p.size(); ++i) m_p[i] = m_z[i] + m_alpha * m_p[i];
     595                 :            : 
     596                 :            :   // initiate computing q = A * p
     597         [ +  - ]:     232305 :   thisProxy[ thisIndex ].wait4q();
     598                 :     232305 :   qAp();
     599                 :     232305 : }
     600                 :            : 
     601                 :            : 
     602                 :            : void
     603                 :     232305 : ConjugateGradients::qAp()
     604                 :            : // *****************************************************************************
     605                 :            : //  Initiate computing q = A * p
     606                 :            : // *****************************************************************************
     607                 :            : {
     608                 :            :   // Compute own contribution to q = A * p
     609                 :     232305 :   m_A.mult( m_p, m_q );
     610                 :            : 
     611                 :            :   // Send partial product on chare-boundary nodes to fellow chares
     612         [ +  + ]:     232305 :   if (m_nodeCommMap.empty()) {
     613                 :      20961 :     comq_complete();
     614                 :            :   } else {
     615                 :            :     auto ncomp = m_A.Ncomp();
     616         [ +  + ]:    1058960 :     for (const auto& [c,n] : m_nodeCommMap) {
     617                 :     847616 :       std::vector< std::vector< tk::real > > qc( n.size() );
     618                 :            :       std::size_t j = 0;
     619         [ +  + ]:    8859200 :       for (auto g : n) {
     620         [ +  - ]:    8011584 :         std::vector< tk::real > nq( ncomp );
     621                 :    8011584 :         auto i = tk::cref_find( m_lid, g );
     622         [ +  + ]:   16028136 :         for (std::size_t d=0; d<ncomp; ++d) nq[d] = m_q[ i*ncomp+d ];
     623                 :    8011584 :         qc[j++] = std::move(nq);
     624                 :            :       }
     625 [ +  - ][ +  - ]:    1695232 :       thisProxy[c].comq( std::vector<std::size_t>(begin(n),end(n)), qc );
         [ +  - ][ -  + ]
                 [ -  - ]
     626                 :     847616 :     }
     627                 :            :   }
     628                 :            : 
     629                 :     232305 :   ownq_complete();
     630                 :     232305 : }
     631                 :            : 
     632                 :            : void
     633                 :     847616 : ConjugateGradients::comq( const std::vector< std::size_t >& gid,
     634                 :            :                           const std::vector< std::vector< tk::real > >& qc )
     635                 :            : // *****************************************************************************
     636                 :            : //  Receive contributions to q = A * p on chare-boundaries
     637                 :            : //! \param[in] gid Global mesh node IDs at which we receive contributions
     638                 :            : //! \param[in] qc Partial contributions at chare-boundary nodes
     639                 :            : // *****************************************************************************
     640                 :            : {
     641                 :            :   Assert( qc.size() == gid.size(), "Size mismatch" );
     642                 :            : 
     643         [ +  + ]:    8859200 :   for (std::size_t i=0; i<gid.size(); ++i) m_qc[ gid[i] ] += qc[i];
     644                 :            : 
     645         [ +  + ]:     847616 :   if (++m_nq == m_nodeCommMap.size()) {
     646                 :     211344 :     m_nq = 0;
     647                 :     211344 :     comq_complete();
     648                 :            :   }
     649                 :     847616 : }
     650                 :            : 
     651                 :            : void
     652                 :     232305 : ConjugateGradients::q()
     653                 :            : // *****************************************************************************
     654                 :            : // Finish computing q = A * p
     655                 :            : // *****************************************************************************
     656                 :            : {
     657                 :            :   // Combine own and communicated contributions to q = A * p
     658                 :            :   auto ncomp = m_A.Ncomp();
     659         [ +  + ]:    6801845 :   for (const auto& [gid,q] : m_qc) {
     660                 :    6569540 :     auto i = tk::cref_find( m_lid, gid );
     661         [ +  + ]:   13144048 :     for (std::size_t c=0; c<ncomp; ++c)
     662                 :    6574508 :       m_q[i*ncomp+c] += q[c];
     663                 :            :   }
     664                 :     232305 :   tk::destroy( m_qc );
     665                 :            : 
     666                 :            :   // initiate computing (p,q)
     667         [ +  - ]:     232305 :   dot( m_p, m_q,
     668                 :     232305 :        CkCallback( CkReductionTarget(ConjugateGradients,pq), thisProxy ) );
     669                 :     232305 : }
     670                 :            : 
     671                 :            : void
     672         [ +  + ]:     232305 : ConjugateGradients::pq( tk::real d )
     673                 :            : // *****************************************************************************
     674                 :            : // Compute the dot product (p,q)
     675                 :            : //! \param[in] d Dot product of (p,q) (aggregated across all chares)
     676                 :            : // *****************************************************************************
     677                 :            : {
     678                 :            :   // If (p,q)=0, then p and q are orthogonal and the system either has a trivial
     679                 :            :   // solution, x=x0, or the BCs are incomplete or wrong, in either case the
     680                 :            :   // solve cannot continue.
     681                 :            :   const auto eps = std::numeric_limits< tk::real >::epsilon();
     682         [ +  + ]:     232305 :   if (std::abs(d) < eps) {
     683         [ -  + ]:       4188 :     if (m_verbose > 1) {
     684                 :            :       tk::Print() << "Xyst> Conjugate gradients it " << m_it << ", (p,q) = 0\n";
     685                 :            :     }
     686                 :       4188 :     m_finished = true;
     687                 :       4188 :     m_alpha = 0.0;
     688                 :            :   } else {
     689                 :     228117 :     m_alpha = m_rho / d;
     690                 :            :   }
     691                 :            : 
     692                 :            :   // compute r = r - alpha * q
     693         [ +  + ]:   50826858 :   for (std::size_t i=0; i<m_r.size(); ++i) m_r[i] -= m_alpha * m_q[i];
     694                 :            : 
     695                 :            :   // apply preconditioner: compute z = M^{-1} * r
     696         [ +  + ]:   50826858 :   for (std::size_t i=0; i<m_z.size(); ++i) m_z[i] = m_r[i] / m_d[i];
     697                 :            : 
     698                 :            :   // initiate computing (r,z)
     699         [ +  - ]:     232305 :   dot( m_r, m_z,
     700                 :     232305 :        CkCallback( CkReductionTarget(ConjugateGradients,rz), thisProxy ) );
     701                 :            :   // initiate computing norm of residual: (r,r)
     702         [ +  - ]:     232305 :   dot( m_r, m_r,
     703                 :     232305 :        CkCallback( CkReductionTarget(ConjugateGradients,normres), thisProxy ) );
     704                 :     232305 : }
     705                 :            : 
     706                 :            : void
     707                 :     232305 : ConjugateGradients::normres( tk::real r )
     708                 :            : // *****************************************************************************
     709                 :            : // Compute norm of residual: (r,r)
     710                 :            : //! \param[in] r Dot product, (r,r) (aggregated across all chares)
     711                 :            : // *****************************************************************************
     712                 :            : {
     713                 :     232305 :   m_normr = r;
     714                 :     232305 :   normr_complete();
     715                 :     232305 : }
     716                 :            : 
     717                 :            : void
     718                 :     232305 : ConjugateGradients::rz( tk::real rz )
     719                 :            : // *****************************************************************************
     720                 :            : // Compute (r,z)
     721                 :            : //! \param[in] rz Dot product, (r,z) (aggregated across all chares)
     722                 :            : // *****************************************************************************
     723                 :            : {
     724                 :     232305 :   m_rho = rz;
     725                 :            : 
     726                 :            :   // Advance solution: x = x + alpha * p
     727         [ +  + ]:   50826858 :   for (std::size_t i=0; i<m_x.size(); ++i) m_x[i] += m_alpha * m_p[i];
     728                 :            : 
     729                 :            :   // Communicate solution
     730 [ +  - ][ +  + ]:     464610 :   thisProxy[ thisIndex ].wait4x();
     731                 :            : 
     732                 :            :   // Send solution on chare-boundary nodes to fellow chares
     733         [ +  + ]:     232305 :   if (m_nodeCommMap.empty()) {
     734                 :      20961 :     comx_complete();
     735                 :            :   } else {
     736                 :            :     auto ncomp = m_A.Ncomp();
     737         [ +  + ]:    1058960 :     for (const auto& [c,n] : m_nodeCommMap) {
     738                 :     847616 :       std::vector< std::vector< tk::real > > xc( n.size() );
     739                 :            :       std::size_t j = 0;
     740         [ +  + ]:    8859200 :       for (auto g : n) {
     741         [ +  - ]:    8011584 :         std::vector< tk::real > nx( ncomp );
     742                 :    8011584 :         auto i = tk::cref_find( m_lid, g );
     743         [ +  + ]:   16028136 :         for (std::size_t d=0; d<ncomp; ++d) nx[d] = m_x[ i*ncomp+d ];
     744                 :    8011584 :         xc[j++] = std::move(nx);
     745                 :            :       }
     746 [ +  - ][ +  - ]:    1695232 :       thisProxy[c].comx( std::vector<std::size_t>(begin(n),end(n)), xc );
         [ +  - ][ -  + ]
                 [ -  - ]
     747                 :     847616 :     }
     748                 :            :   }
     749                 :            : 
     750                 :     232305 :   ownx_complete();
     751                 :     232305 : }
     752                 :            : 
     753                 :            : void
     754                 :     847616 : ConjugateGradients::comx( const std::vector< std::size_t >& gid,
     755                 :            :                           const std::vector< std::vector< tk::real > >& xc )
     756                 :            : // *****************************************************************************
     757                 :            : //  Receive contributions to final solution on chare-boundaries
     758                 :            : //! \param[in] gid Global mesh node IDs at which we receive contributions
     759                 :            : //! \param[in] xc Partial contributions at chare-boundary nodes
     760                 :            : // *****************************************************************************
     761                 :            : {
     762                 :            :   Assert( xc.size() == gid.size(), "Size mismatch" );
     763                 :            : 
     764         [ +  + ]:    8859200 :   for (std::size_t i=0; i<gid.size(); ++i) m_xc[ gid[i] ] += xc[i];
     765                 :            : 
     766         [ +  + ]:     847616 :   if (++m_nx == m_nodeCommMap.size()) {
     767                 :     211344 :     m_nx = 0;
     768                 :     211344 :     comx_complete();
     769                 :            :   }
     770                 :     847616 : }
     771                 :            : 
     772                 :            : void
     773                 :     232305 : ConjugateGradients::x()
     774                 :            : // *****************************************************************************
     775                 :            : //  Assemble solution on chare boundaries and decide what's next
     776                 :            : // *****************************************************************************
     777                 :            : {
     778                 :            :   // Assemble solution on chare boundaries by averaging
     779                 :            :   auto ncomp = m_A.Ncomp();
     780         [ +  + ]:    6801845 :   for (const auto& [g,x] : m_xc) {
     781                 :    6569540 :     auto i = tk::cref_find(m_lid,g);
     782         [ +  + ]:   13144048 :     for (std::size_t d=0; d<ncomp; ++d) m_x[i*ncomp+d] += x[d];
     783                 :    6569540 :     auto c = tk::count(m_nodeCommMap,g);
     784         [ +  + ]:   13144048 :     for (std::size_t d=0; d<ncomp; ++d) m_x[i*ncomp+d] /= c;
     785                 :            :   }
     786                 :     232305 :   tk::destroy( m_xc );
     787                 :            : 
     788                 :     232305 :   ++m_it;
     789         [ +  + ]:     232305 :   auto normb = m_normb > 1.0e-14 ? m_normb : 1.0;
     790                 :     232305 :   auto normr = std::sqrt( m_normr );
     791                 :            : 
     792 [ +  + ][ +  + ]:     232305 :   if ( m_finished || normr < m_tol*normb || m_it >= m_maxit ) {
                 [ -  + ]
     793                 :            : 
     794                 :       6244 :     m_converged = normr > m_tol*normb ? false : true;
     795                 :            : 
     796         [ +  + ]:       6244 :     if (m_verbose) {
     797                 :         51 :       std::stringstream c;
     798         [ +  - ]:         51 :       if (m_converged) {
     799 [ +  - ][ +  - ]:         51 :         c << " < " << m_tol << ", converged";
     800                 :            :       } else {
     801         [ -  - ]:          0 :         c << " > " << m_tol << ", not converged, ||r||/||b|| = "
     802 [ -  - ][ -  - ]:          0 :           << normr << '/' << normb;
     803                 :            :       }
     804                 :         51 :       tk::Print() << "Xyst> Conjugate gradients it " << m_it
     805 [ +  - ][ +  - ]:        102 :                   << ", norm = " << normr/normb << c.str() << '\n';
     806                 :         51 :     }
     807                 :            : 
     808                 :            :     // Restore matrix to state before init()
     809         [ +  + ]:       6244 :     if (m_apply) m_A = m_An;
     810                 :            : 
     811                 :       6244 :     m_solved.send( CkDataMsg::buildNew( sizeof(tk::real), &normr ) );
     812                 :            : 
     813                 :            :   } else {
     814                 :            : 
     815         [ +  + ]:     226061 :     if (m_verbose > 1) {
     816                 :            :       tk::Print() << "Xyst> Conjugate gradients it "
     817                 :        114 :                   << m_it << ", norm = " << normr/normb << '\n';
     818                 :            :     }
     819                 :            : 
     820                 :     226061 :     next();
     821                 :            : 
     822                 :            :   }
     823                 :     232305 : }
     824                 :            : 
     825                 :            : #include "NoWarning/conjugategradients.def.h"

Generated by: LCOV version 1.16