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 : : All rights reserved. See the LICENSE file for details.
8 : : \brief Charm++ chare array for distributed conjugate gradients.
9 : : \details Charm++ chare array for asynchronous distributed
10 : : conjugate gradients linear solver.
11 : : \see Y. Saad, Iterative Methods for Sparse Linear Systems: Second Edition,
12 : : ISBN 9780898718003, 2003, Algorithm 6.18, conjugate gradients to solve the
13 : : linear system A * x = b, reproduced here:
14 : :
15 : : Compute r0:=b-A*x0, p0:=r0
16 : : For j=0,1,..., until convergence, do
17 : : alpha_j := (r_j,r_j) / (Ap_j,p_j)
18 : : x_{j+1} := x_j + alpha_j p_j
19 : : r_{j+1} := r_j - alpha_j A p_j
20 : : beta_j := (r_{j+1},r_{j+1}) / (r_j,r_j)
21 : : p_{j+1} := r_{j+1} + beta_j p_j
22 : : end
23 : : */
24 : : // *****************************************************************************
25 : :
26 : : #include <numeric>
27 : : #include <iostream>
28 : :
29 : : #include "Exception.hpp"
30 : : #include "ConjugateGradients.hpp"
31 : : #include "Vector.hpp"
32 : :
33 : : using tk::ConjugateGradients;
34 : :
35 : 27 : ConjugateGradients::ConjugateGradients(
36 : : const CSR& A,
37 : : const std::vector< tk::real >& x,
38 : : const std::vector< tk::real >& b,
39 : : const std::vector< std::size_t >& gid,
40 : : const std::unordered_map< std::size_t, std::size_t >& lid,
41 : 27 : const NodeCommMap& nodecommmap ) :
42 : : m_A( A ),
43 : : m_x( x ),
44 : : m_b( b ),
45 : : m_gid( gid ),
46 : : m_lid( lid ),
47 : : m_nodeCommMap( nodecommmap ),
48 : 54 : m_r( m_A.rsize(), 0.0 ),
49 : : m_rc(),
50 : : m_nr( 0 ),
51 : : m_bc(),
52 : : m_bcc(),
53 [ + - ]: 27 : m_bcmask( m_A.rsize(), 1.0 ),
54 : : m_nb( 0 ),
55 : 54 : m_p( m_A.rsize(), 0.0 ),
56 : 54 : m_q( m_A.rsize(), 0.0 ),
57 : : m_qc(),
58 : : m_nq( 0 ),
59 : : m_initres(),
60 : : m_solved(),
61 : : m_normb( 0.0 ),
62 : : m_it( 0 ),
63 : : m_maxit( 0 ),
64 : : m_rho( 0.0 ),
65 : : m_rho0( 0.0 ),
66 : : m_alpha( 0.0 ),
67 : : m_converged( false ),
68 : : m_xc(),
69 [ + - ][ + - ]: 54 : m_nx( 0 )
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + + ][ - - ]
[ - - ]
70 : : // *****************************************************************************
71 : : // Constructor
72 : : //! \param[in] A Left hand side matrix of the linear system to solve in Ax=b
73 : : //! \param[in] x Solution (initial guess) of the linear system to solve in Ax=b
74 : : //! \param[in] b Right hand side of the linear system to solve in Ax=b
75 : : //! \param[in] gid Global node ids
76 : : //! \param[in] lid Local node ids associated to global ones
77 : : //! \param[in] nodecommmap Global mesh node IDs shared with other chares
78 : : //! associated to their chare IDs
79 : : // *****************************************************************************
80 : : {
81 : : // Fill in gid and lid for serial solve
82 [ + + ][ + - ]: 27 : if (gid.empty() || lid.empty() || nodecommmap.empty()) {
[ + + ]
83 [ + - ]: 3 : m_gid.resize( m_A.rsize()/m_A.Ncomp() );
84 : : std::iota( begin(m_gid), end(m_gid), 0 );
85 [ + + ][ + - ]: 7325 : for (auto g : m_gid) m_lid[g] = g;
86 : : }
87 : :
88 : : Assert( m_A.rsize() == m_gid.size()*A.Ncomp(), "Size mismatch" );
89 : : Assert( m_x.size() == m_gid.size()*A.Ncomp(), "Size mismatch" );
90 : : Assert( m_b.size() == m_gid.size()*A.Ncomp(), "Size mismatch" );
91 : 27 : }
92 : :
93 : : void
94 : 807 : ConjugateGradients::setup( CkCallback c )
95 : : // *****************************************************************************
96 : : // Setup solver
97 : : //! \param[in] c Call to continue with after initialization is complete
98 : : //! \details This function initiates computing the residual (r=b-A*x), its dot
99 : : //! product, and the rhs norm.
100 : : // *****************************************************************************
101 : : {
102 : 807 : m_initres = c;
103 : :
104 : : // initiate computing A * x (for the initial residual)
105 [ + - ]: 807 : thisProxy[ thisIndex ].wait4res();
106 : 807 : residual();
107 : :
108 : : // initiate computing norm of right hand side
109 [ + - ]: 807 : dot( m_b, m_b,
110 : 807 : CkCallback( CkReductionTarget(ConjugateGradients,normb), thisProxy ) );
111 : 807 : }
112 : :
113 : : void
114 : 8488 : ConjugateGradients::dot( const std::vector< tk::real >& a,
115 : : const std::vector< tk::real >& b,
116 : : CkCallback c )
117 : : // *****************************************************************************
118 : : // Initiate computation of dot product of two vectors
119 : : //! \param[in] a 1st vector of dot product
120 : : //! \param[in] b 2nd vector of dot product
121 : : //! \param[in] c Callback to target with the final result
122 : : // *****************************************************************************
123 : : {
124 : : Assert( a.size() == b.size(), "Size mismatch" );
125 : :
126 : 8488 : tk::real D = 0.0;
127 : 8488 : auto ncomp = m_A.Ncomp();
128 [ + + ]: 10498482 : for (std::size_t i=0; i<a.size()/ncomp; ++i) {
129 : 10489994 : auto incomp = i*ncomp;
130 [ + + ]: 10489994 : if (not slave(m_nodeCommMap,m_gid[i],thisIndex))
131 [ + + ]: 36000828 : for (std::size_t d=0; d<ncomp; ++d)
132 : 26960034 : D += a[incomp+d] * b[incomp+d];
133 : : }
134 : :
135 : 8488 : contribute( sizeof(tk::real), &D, CkReduction::sum_double, c );
136 : 8488 : }
137 : :
138 : : void
139 : 807 : ConjugateGradients::normb( tk::real n )
140 : : // *****************************************************************************
141 : : // Compute the norm of the right hand side
142 : : //! \param[in] n Norm of right hand side (aggregated across all chares)
143 : : // *****************************************************************************
144 : : {
145 : 807 : m_normb = std::sqrt(n);
146 : 807 : normb_complete();
147 : 807 : }
148 : :
149 : : void
150 : 807 : ConjugateGradients::residual()
151 : : // *****************************************************************************
152 : : // Initiate A * x for computing the initial residual, r = b - A * x
153 : : // *****************************************************************************
154 : : {
155 : : // Compute own contribution to r = A * x
156 : 807 : m_A.mult( m_x, m_r, m_bcmask );
157 : :
158 : : // Send partial product on chare-boundary nodes to fellow chares
159 [ + + ]: 807 : if (m_nodeCommMap.empty()) {
160 : 63 : comres_complete();
161 : : } else {
162 : 744 : auto ncomp = m_A.Ncomp();
163 [ + + ]: 2416 : for (const auto& [c,n] : m_nodeCommMap) {
164 [ + - ]: 1672 : std::vector< std::vector< tk::real > > rc( n.size() );
165 : : std::size_t j = 0;
166 [ + + ]: 271548 : for (auto g : n) {
167 [ + - ]: 269876 : std::vector< tk::real > nr( ncomp );
168 : 269876 : auto i = tk::cref_find( m_lid, g );
169 [ + + ]: 1067440 : for (std::size_t d=0; d<ncomp; ++d) nr[d] = m_r[ i*ncomp+d ];
170 [ - + ]: 269876 : rc[j++] = std::move(nr);
171 : : }
172 [ + - ][ + - ]: 3344 : thisProxy[c].comres( std::vector<std::size_t>(begin(n),end(n)), rc );
[ + - ][ - + ]
173 : : }
174 : : }
175 : :
176 : 807 : ownres_complete();
177 : 807 : }
178 : :
179 : : void
180 : 1672 : ConjugateGradients::comres( const std::vector< std::size_t >& gid,
181 : : const std::vector< std::vector< tk::real > >& rc )
182 : : // *****************************************************************************
183 : : // Receive contributions to A * x on chare-boundaries
184 : : //! \param[in] gid Global mesh node IDs at which we receive contributions
185 : : //! \param[in] rc Partial contributions at chare-boundary nodes
186 : : // *****************************************************************************
187 : : {
188 : : Assert( rc.size() == gid.size(), "Size mismatch" );
189 : :
190 : : using tk::operator+=;
191 : :
192 [ + + ]: 271548 : for (std::size_t i=0; i<gid.size(); ++i)
193 : 269876 : m_rc[ gid[i] ] += rc[i];
194 : :
195 [ + + ]: 1672 : if (++m_nr == m_nodeCommMap.size()) {
196 : 744 : m_nr = 0;
197 : 744 : comres_complete();
198 : : }
199 : 1672 : }
200 : :
201 : : void
202 : 807 : ConjugateGradients::initres()
203 : : // *****************************************************************************
204 : : // Finish computing the initial residual, r = b - A * x
205 : : // *****************************************************************************
206 : : {
207 : : // Combine own and communicated contributions to r = A * x
208 : 807 : auto ncomp = m_A.Ncomp();
209 [ + + ]: 240861 : for (const auto& [gid,r] : m_rc) {
210 : 240054 : auto i = tk::cref_find( m_lid, gid );
211 [ + + ]: 951004 : for (std::size_t c=0; c<ncomp; ++c) m_r[i*ncomp+c] += r[c];
212 : : }
213 : 807 : tk::destroy( m_rc );
214 : :
215 : : // Finish computing the initial residual, r = b - A * x
216 [ + + ]: 3763550 : for (auto& r : m_r) r *= -1.0;
217 : 807 : m_r += m_b;
218 : :
219 : : // Initialize p
220 : 807 : m_p = m_r;
221 : :
222 : : // initiate computing the dot product of the initial residual, rho = (r,r)
223 [ + - ]: 807 : dot( m_r, m_r,
224 : 807 : CkCallback( CkReductionTarget(ConjugateGradients,rho), thisProxy ) );
225 : 807 : }
226 : :
227 : : void
228 : 807 : ConjugateGradients::rho( tk::real r )
229 : : // *****************************************************************************
230 : : // Compute rho = (r,r)
231 : : //! \param[in] r Dot product, rho = (r,r) (aggregated across all chares)
232 : : // *****************************************************************************
233 : : {
234 : : // store dot product of residual
235 : 807 : m_rho = r;
236 : :
237 : : // send back rhs norm to caller
238 : 807 : m_initres.send( CkDataMsg::buildNew( sizeof(tk::real), &m_normb ) );
239 : 807 : }
240 : :
241 : : void
242 [ + + ]: 801 : ConjugateGradients::init(
243 : : const std::vector< tk::real >& x,
244 : : const std::vector< tk::real >& b,
245 : : const std::unordered_map< std::size_t,
246 : : std::vector< std::pair< bool, tk::real > > >& bc,
247 : : std::size_t ignorebc,
248 : : CkCallback cb )
249 : : // *****************************************************************************
250 : : // Initialize linear solve: set initial guess and boundary conditions
251 : : //! \param[in] x Initial guess
252 : : //! \param[in] b Right hand side vector
253 : : //! \param[in] bc Local node ids and associated Dirichlet BCs
254 : : //! \param[in] ignorebc True if applyin BCs should be skipped
255 : : //! \param[in] cb Call to continue with when initialized and ready for a solve
256 : : //! \details This function allows setting the initial guess and boundary
257 : : //! conditions, followed by computing the initial residual and the rhs norm.
258 : : // *****************************************************************************
259 : : {
260 : : // Optionally set initial guess
261 [ + + ]: 801 : if (not x.empty()) m_x = x;
262 : :
263 : : // Optionally update rhs
264 [ + + ]: 801 : if (not b.empty()) m_b = b;
265 : :
266 [ + + ]: 801 : if (ignorebc) {
267 : :
268 [ + - ]: 216 : setup( cb );
269 : :
270 : : } else {
271 : :
272 : : // Store incoming BCs
273 : : m_bc = bc;
274 : :
275 : : // Get ready to communicate boundary conditions. This is necessary because
276 : : // there can be nodes a chare contributes to but does not apply BCs on. This
277 : : // happens if a node is in the node communication map but not on the list of
278 : : // incoming BCs on this chare. To have all chares share the same view on all
279 : : // BC nodes, we send the global node ids together with the Dirichlet BCs at
280 : : // which BCs are set to those fellow chares that also contribute to those BC
281 : : // nodes. Only after this communication step we apply the BCs on the matrix,
282 : : // which then will correctly setup the BC rows that exist on multiple chares
283 : : // (which now will be the same as the results of making the BCs consistent
284 : : // across all chares that contribute.
285 [ + - ][ + + ]: 1386 : thisProxy[ thisIndex ].wait4bc();
286 : :
287 : : // Send boundary conditions to those who contribute to those rows
288 [ + + ]: 693 : if (m_nodeCommMap.empty()) {
289 : 61 : combc_complete();
290 : : } else {
291 [ + + ]: 2138 : for (const auto& [c,n] : m_nodeCommMap) {
292 : : std::unordered_map< std::size_t,
293 : : std::vector< std::pair< bool, tk::real > > > expbc;
294 [ + + ]: 263462 : for (auto g : n) {
295 : 261956 : auto i = tk::cref_find( m_lid, g );
296 : : auto j = bc.find(i);
297 [ + + ][ + - ]: 261956 : if (j != end(bc)) expbc[g] = j->second;
[ + - ]
298 : : }
299 [ + - ][ + - ]: 3012 : thisProxy[c].combc( expbc );
300 : : }
301 : : }
302 : :
303 [ + - ]: 1386 : ownbc_complete( cb );
304 : :
305 : : }
306 : 801 : }
307 : :
308 : : void
309 : 1506 : ConjugateGradients::combc(
310 : : const std::unordered_map< std::size_t,
311 : : std::vector< std::pair< bool, tk::real > > >& bc )
312 : : // *****************************************************************************
313 : : // Receive contributions to boundary conditions on chare-boundaries
314 : : //! \param[in] bc Contributions to boundary conditions
315 : : // *****************************************************************************
316 : : {
317 [ + + ]: 21294 : for (const auto& [g,dirbc] : bc) m_bcc[ tk::cref_find(m_lid,g) ] = dirbc;
318 : :
319 [ + + ]: 1506 : if (++m_nb == m_nodeCommMap.size()) {
320 : 632 : m_nb = 0;
321 : 632 : combc_complete();
322 : : }
323 : 1506 : }
324 : :
325 : : void
326 : 693 : ConjugateGradients::apply( CkCallback cb )
327 : : // *****************************************************************************
328 : : // Apply boundary conditions
329 : : //! \param[in] cb Call to continue with after applying the BCs is complete
330 : : // *****************************************************************************
331 : : {
332 : : // Merge own and received contributions to boundary conditions
333 [ + + ]: 18435 : for (const auto& [i,dirbc] : m_bcc) m_bc[i] = dirbc;
334 : 693 : tk::destroy( m_bcc );
335 : :
336 : 693 : auto ncomp = m_A.Ncomp();
337 : :
338 : : // Setup Dirichlet BC map as contiguous mask
339 [ + + ]: 47893 : for (const auto& [i,bc] : m_bc)
340 [ + + ]: 172928 : for (std::size_t j=0; j<ncomp; ++j)
341 : 125728 : m_bcmask[i*ncomp+j] = 0.0;
342 : :
343 : : // Apply Dirichlet BCs on matrix and rhs
344 [ + + ]: 47893 : for (const auto& [i,dirbc] : m_bc) {
345 [ + + ]: 172928 : for (std::size_t j=0; j<ncomp; ++j) {
346 [ + + ]: 125728 : if (dirbc[j].first) {
347 : 125332 : m_A.dirichlet( i, m_gid, m_nodeCommMap, j );
348 : 125332 : m_b[i*ncomp+j] = dirbc[j].second;
349 : : }
350 : : }
351 : : }
352 : :
353 : : // Recompute initial residual (r=b-A*x), its dot product, and the rhs norm
354 [ + - ]: 693 : setup( cb );
355 : 693 : }
356 : :
357 : : void
358 : 807 : ConjugateGradients::solve( std::size_t maxit, tk::real tol, CkCallback c )
359 : : // *****************************************************************************
360 : : // Solve linear system
361 : : //! \param[in] maxit Max iteration count
362 : : //! \param[in] tol Stop tolerance
363 : : //! \param[in] c Call to continue with after solve is complete
364 : : // *****************************************************************************
365 : : {
366 : 807 : m_maxit = maxit;
367 : 807 : m_tol = tol;
368 : 807 : m_solved = c;
369 : 807 : m_it = 0;
370 : :
371 : 807 : next();
372 : 807 : }
373 : :
374 : : void
375 : 3437 : ConjugateGradients::next()
376 : : // *****************************************************************************
377 : : // Start next linear solver iteration
378 : : // *****************************************************************************
379 : : {
380 [ + + ]: 3437 : if (m_it == 0) m_alpha = 0.0; else m_alpha = m_rho/m_rho0;
381 : 3437 : m_rho0 = m_rho;
382 : :
383 : : // compute p = r + alpha * p
384 [ + + ]: 11867345 : for (std::size_t i=0; i<m_p.size(); ++i) m_p[i] = m_r[i] + m_alpha * m_p[i];
385 : :
386 : : // initiate computing q = A * p
387 [ + - ]: 3437 : thisProxy[ thisIndex ].wait4q();
388 : 3437 : qAp();
389 : 3437 : }
390 : :
391 : :
392 : : void
393 : 3437 : ConjugateGradients::qAp()
394 : : // *****************************************************************************
395 : : // Initiate computing q = A * p
396 : : // *****************************************************************************
397 : : {
398 : : // Compute own contribution to q = A * p
399 : 3437 : m_A.mult( m_p, m_q, m_bcmask );
400 : :
401 : : // Send partial product on chare-boundary nodes to fellow chares
402 [ + + ]: 3437 : if (m_nodeCommMap.empty()) {
403 : 141 : comq_complete();
404 : : } else {
405 : 3296 : auto ncomp = m_A.Ncomp();
406 [ + + ]: 10916 : for (const auto& [c,n] : m_nodeCommMap) {
407 [ + - ]: 7620 : std::vector< std::vector< tk::real > > qc( n.size() );
408 : : std::size_t j = 0;
409 [ + + ]: 1316194 : for (auto g : n) {
410 [ + - ]: 1308574 : std::vector< tk::real > nq( ncomp );
411 : 1308574 : auto i = tk::cref_find( m_lid, g );
412 [ + + ]: 5180536 : for (std::size_t d=0; d<ncomp; ++d) nq[d] = m_q[ i*ncomp+d ];
413 [ - + ]: 1308574 : qc[j++] = std::move(nq);
414 : : }
415 [ + - ][ + - ]: 15240 : thisProxy[c].comq( std::vector<std::size_t>(begin(n),end(n)), qc );
[ + - ][ - + ]
416 : : }
417 : : }
418 : :
419 : 3437 : ownq_complete();
420 : 3437 : }
421 : :
422 : : void
423 : 7620 : ConjugateGradients::comq( const std::vector< std::size_t >& gid,
424 : : const std::vector< std::vector< tk::real > >& qc )
425 : : // *****************************************************************************
426 : : // Receive contributions to q = A * p on chare-boundaries
427 : : //! \param[in] gid Global mesh node IDs at which we receive contributions
428 : : //! \param[in] qc Partial contributions at chare-boundary nodes
429 : : // *****************************************************************************
430 : : {
431 : : Assert( qc.size() == gid.size(), "Size mismatch" );
432 : :
433 : : using tk::operator+=;
434 : :
435 [ + + ]: 1316194 : for (std::size_t i=0; i<gid.size(); ++i)
436 : 1308574 : m_qc[ gid[i] ] += qc[i];
437 : :
438 [ + + ]: 7620 : if (++m_nq == m_nodeCommMap.size()) {
439 : 3296 : m_nq = 0;
440 : 3296 : comq_complete();
441 : : }
442 : 7620 : }
443 : :
444 : : void
445 : 3437 : ConjugateGradients::q()
446 : : // *****************************************************************************
447 : : // Finish computing q = A * p
448 : : // *****************************************************************************
449 : : {
450 : : // Combine own and communicated contributions to q = A * p
451 : 3437 : auto ncomp = m_A.Ncomp();
452 [ + + ]: 1163683 : for (const auto& [gid,q] : m_qc) {
453 : 1160246 : auto i = tk::cref_find( m_lid, gid );
454 [ + + ]: 4599920 : for (std::size_t c=0; c<ncomp; ++c)
455 : 3439674 : m_q[i*ncomp+c] += q[c];
456 : : }
457 : 3437 : tk::destroy( m_qc );
458 : :
459 : : // initiate computing (p,q)
460 [ + - ]: 3437 : dot( m_p, m_q,
461 : 3437 : CkCallback( CkReductionTarget(ConjugateGradients,pq), thisProxy ) );
462 : 3437 : }
463 : :
464 : : void
465 [ + + ]: 3437 : ConjugateGradients::pq( tk::real d )
466 : : // *****************************************************************************
467 : : // Compute the dot product (p,q)
468 : : //! \param[in] d Dot product of (p,q) (aggregated across all chares)
469 : : // *****************************************************************************
470 : : {
471 : : // If (p,q)=0, then p and q are orthogonal and the system either has a trivial
472 : : // solution, x=x0, or the BCs are incomplete or wrong, in either case the
473 : : // solve cannot continue.
474 : : const auto eps = std::numeric_limits< tk::real >::epsilon();
475 [ + + ]: 3437 : if (std::abs(d) < eps) {
476 : 9 : m_it = m_maxit;
477 : 9 : m_alpha = 0.0;
478 : : } else {
479 : 3428 : m_alpha = m_rho / d;
480 : : }
481 : :
482 : : // compute r = r - alpha * q
483 [ + + ]: 11867345 : for (std::size_t i=0; i<m_r.size(); ++i) m_r[i] -= m_alpha * m_q[i];
484 : :
485 : : // initiate computing norm of residual: (r,r)
486 [ + - ]: 3437 : dot( m_r, m_r,
487 : 3437 : CkCallback( CkReductionTarget(ConjugateGradients,normres), thisProxy ) );
488 : 3437 : }
489 : :
490 : : void
491 : 3437 : ConjugateGradients::normres( tk::real r )
492 : : // *****************************************************************************
493 : : // Compute norm of residual: (r,r)
494 : : //! \param[in] r Dot product, (r,r) (aggregated across all chares)
495 : : // *****************************************************************************
496 : : {
497 : 3437 : m_rho = r;
498 : :
499 : : // Advance solution: x = x + alpha * p
500 [ + + ]: 11867345 : for (std::size_t i=0; i<m_x.size(); ++i) m_x[i] += m_alpha * m_p[i];
501 : :
502 : : // Communicate solution
503 [ + - ][ + + ]: 6874 : thisProxy[ thisIndex ].wait4x();
504 : :
505 : : // Send solution on chare-boundary nodes to fellow chares
506 [ + + ]: 3437 : if (m_nodeCommMap.empty()) {
507 : 141 : comx_complete();
508 : : } else {
509 : 3296 : auto ncomp = m_A.Ncomp();
510 [ + + ]: 10916 : for (const auto& [c,n] : m_nodeCommMap) {
511 [ + - ]: 7620 : std::vector< std::vector< tk::real > > xc( n.size() );
512 : : std::size_t j = 0;
513 [ + + ]: 1316194 : for (auto g : n) {
514 [ + - ]: 1308574 : std::vector< tk::real > nx( ncomp );
515 : 1308574 : auto i = tk::cref_find( m_lid, g );
516 [ + + ]: 5180536 : for (std::size_t d=0; d<ncomp; ++d) nx[d] = m_x[ i*ncomp+d ];
517 [ - + ]: 1308574 : xc[j++] = std::move(nx);
518 : : }
519 [ + - ][ + - ]: 15240 : thisProxy[c].comx( std::vector<std::size_t>(begin(n),end(n)), xc );
[ + - ][ - + ]
520 : : }
521 : : }
522 : :
523 : 3437 : ownx_complete();
524 : 3437 : }
525 : :
526 : : void
527 : 7620 : ConjugateGradients::comx( const std::vector< std::size_t >& gid,
528 : : const std::vector< std::vector< tk::real > >& xc )
529 : : // *****************************************************************************
530 : : // Receive contributions to final solution on chare-boundaries
531 : : //! \param[in] gid Global mesh node IDs at which we receive contributions
532 : : //! \param[in] xc Partial contributions at chare-boundary nodes
533 : : // *****************************************************************************
534 : : {
535 : : Assert( xc.size() == gid.size(), "Size mismatch" );
536 : :
537 [ + + ]: 1316194 : for (std::size_t i=0; i<gid.size(); ++i) m_xc[ gid[i] ] += xc[i];
538 : :
539 [ + + ]: 7620 : if (++m_nx == m_nodeCommMap.size()) {
540 : 3296 : m_nx = 0;
541 : 3296 : comx_complete();
542 : : }
543 : 7620 : }
544 : :
545 : : void
546 : 3437 : ConjugateGradients::x()
547 : : // *****************************************************************************
548 : : // Assemble solution on chare boundaries
549 : : // *****************************************************************************
550 : : {
551 : : // Assemble solution on chare boundaries by averaging
552 : 3437 : auto ncomp = m_A.Ncomp();
553 [ + + ]: 1163683 : for (const auto& [g,x] : m_xc) {
554 : 1160246 : auto i = tk::cref_find(m_lid,g);
555 [ + + ]: 4599920 : for (std::size_t d=0; d<ncomp; ++d) m_x[i*ncomp+d] += x[d];
556 : 1160246 : auto c = tk::count(m_nodeCommMap,g);
557 [ + + ]: 4599920 : for (std::size_t d=0; d<ncomp; ++d) m_x[i*ncomp+d] /= c;
558 : : }
559 : 3437 : tk::destroy( m_xc );
560 : :
561 : 3437 : ++m_it;
562 [ + + ]: 3437 : auto normb = m_normb > 1.0e-14 ? m_normb : 1.0;
563 : 3437 : auto normr = std::sqrt( m_rho );
564 : :
565 [ + + ][ + + ]: 3437 : if ( m_it < m_maxit && normr > m_tol*normb ) {
566 : :
567 : 2630 : next();
568 : :
569 : : } else {
570 : :
571 [ + + ][ + - ]: 807 : m_converged = m_it == m_maxit && normr > m_tol*normb ? false : true;
572 : 807 : m_solved.send( CkDataMsg::buildNew( sizeof(tk::real), &normr ) );
573 : :
574 : : }
575 : 3437 : }
576 : :
577 : : #include "NoWarning/conjugategradients.def.h"
|