BELFEM 0.9.0
Berkeley Lab Finite Element Framework
Loading...
Searching...
No Matches
fn_FEM_anderson_mixing.hpp
Go to the documentation of this file.
1/*
2 * BELFEM -- The Berkeley Lab Finite Element Framework
3 * Copyright (c) 2026, The Regents of the University of California,
4 * through Lawrence Berkeley National Laboratory (subject to receipt of any required
5 * approvals from the U.S. Dept. of Energy). All rights reserved.
6 *
7 * Developers: Christian Messe, Gregory Giard
8 *
9 * See the top-level LICENSE file for the complete license and disclaimer.
10 */
11#ifndef BELFEM_FN_FEM_ANDERSON_MIXING_HPP
12#define BELFEM_FN_FEM_ANDERSON_MIXING_HPP
13
14#include <cmath>
15
16#include "typedefs.hpp"
17#include "assert.hpp"
18#include "cl_Vector.hpp"
19#include "cl_Matrix.hpp"
20#include "cl_ShiftRegister.hpp"
21#include "fn_gels.hpp"
22
23namespace belfem
24{
25 namespace fem
26 {
32 constexpr real gAndersonGammaMax = 1.0e2 ;
33
34//------------------------------------------------------------------------------
35
64 inline uint
66 const Vector< real > & aX, // x_k
67 const Vector< real > & aR, // r_k = G(x_k) - x_k
68 const ShiftRegister< Vector< real > > & aXHistory, // committed x, (0) = newest
69 const ShiftRegister< Vector< real > > & aRHistory, // committed r, (0) = newest
70 const real aBeta, // mixing = live relaxation
71 Matrix< real > & aDeltaR, // scratch, destroyed by gels
72 Vector< real > & aRhs, // scratch, destroyed by gels
73 Vector< real > & aWork, // gels workspace
74 Vector< real > & aGamma, // scratch, un-scaled solution
75 Vector< real > & aColNorm, // scratch, column norms
76 Vector< real > & aXNew ) // out
77 {
78 const index_t tN = aX.length() ;
79
80 BELFEM_ASSERT( aR.length() == tN, "size mismatch of x and r" );
81 BELFEM_ASSERT( aXHistory.size() == aRHistory.size(),
82 "x and r histories out of sync ( %u vs %u )",
83 ( unsigned int ) aXHistory.size(),
84 ( unsigned int ) aRHistory.size() );
85
86 // the plain relaxed Picard step, also the fallback
87 auto tPlainStep = [ & ]()
88 {
89 for ( index_t k = 0; k < tN; ++k )
90 {
91 aXNew( k ) = aX( k ) + aBeta * aR( k );
92 }
93 };
94
95 // one attempt with the newest tUse difference columns; returns
96 // true if aGamma holds a usable un-scaled solution
97 auto tTrySolve = [ & ]( const uint tUse ) -> bool
98 {
99 // chain point i ( 0 = oldest used ): history( tUse - 1 - i )
100 // for i < tUse, the current pair for i = tUse
101 for ( uint j = 0; j < tUse; ++j )
102 {
103 const Vector< real > & tR1 = ( j + 1 == tUse ) ?
104 aR : aRHistory( tUse - 2 - j );
105 const Vector< real > & tR0 = aRHistory( tUse - 1 - j );
106
107 real tNorm = 0.0 ;
108 for ( index_t k = 0; k < tN; ++k )
109 {
110 const real tValue = tR1( k ) - tR0( k );
111 aDeltaR( k, j ) = tValue ;
112 tNorm += tValue * tValue ;
113 }
114 tNorm = std::sqrt( tNorm );
115
116 // a vanishing difference column can not be normalized;
117 // treat like rank deficiency
118 if ( tNorm < BELFEM_EPSILON )
119 {
120 return false ;
121 }
122 aColNorm( j ) = tNorm ;
123
124 const real tScale = 1.0 / tNorm ;
125 for ( index_t k = 0; k < tN; ++k )
126 {
127 aDeltaR( k, j ) *= tScale ;
128 }
129 }
130
131 // gels destroys the rhs; the solution lands in its head
132 for ( index_t k = 0; k < tN; ++k )
133 {
134 aRhs( k ) = aR( k );
135 }
136
137 // call the typed backend directly with the logical column
138 // count: the scratch matrix keeps its full allocation and the
139 // leading dimension makes the size mismatch legal
140 char tTrans = 'N' ;
141 int_t tM = tN ;
142 int_t tCols = tUse ;
143 int_t tNrhs = 1 ;
144 int_t tLda = lapack::leading_dimension( aDeltaR );
145 int_t tLdb = lapack::leading_dimension( aRhs );
146 int_t tInfo = 0 ;
147
148 int_t tLwork = ( int_t ) aWork.length() ;
149 if ( tLwork < 2 * tCols )
150 {
151 // workspace query, then grow the buffer once
152 int_t tQuery = -1 ;
153 aWork.set_size( 1 );
154 lapack::gels( &tTrans, &tM, &tCols, &tNrhs,
155 aDeltaR.data(), &tLda, aRhs.data(), &tLdb,
156 aWork.data(), &tQuery, &tInfo );
157 BELFEM_ERROR( tInfo == 0,
158 "gels workspace query failed: %i", ( int ) tInfo );
159 aWork.set_size( lapack::work_size( aWork( 0 ) ) );
160 tLwork = ( int_t ) aWork.length() ;
161 }
162
163 lapack::gels( &tTrans, &tM, &tCols, &tNrhs,
164 aDeltaR.data(), &tLda, aRhs.data(), &tLdb,
165 aWork.data(), &tLwork, &tInfo );
166
167 // an illegal argument is a programming error, always fatal
168 BELFEM_ERROR( tInfo >= 0,
169 "gels reports an illegal argument: %i", ( int ) tInfo );
170
171 // rank deficient: caller shrinks the window
172 if ( tInfo > 0 )
173 {
174 return false ;
175 }
176
177 // un-scale and guard the solution
178 real tGammaMax = 0.0 ;
179 for ( uint j = 0; j < tUse; ++j )
180 {
181 const real tValue = aRhs( j ) / aColNorm( j );
182 if ( ! std::isfinite( tValue ) )
183 {
184 return false ;
185 }
186 aGamma( j ) = tValue ;
187 tGammaMax = std::max( tGammaMax, std::abs( tValue ) );
188 }
189 return tGammaMax <= gAndersonGammaMax ;
190 };
191
192 // window: newest tUse columns; drop the oldest and retry once.
193 // A window wider than the system would make the least-squares
194 // problem underdetermined and overrun the rhs buffer ( gels needs
195 // max( m, n ) rows ), so clamp — this must hold in release too
196 uint tUse = aXHistory.size() ;
197 if ( ( index_t ) tUse > tN )
198 {
199 tUse = ( uint ) tN ;
200 }
201
202 bool tOK = tUse > 0 ? tTrySolve( tUse ) : false ;
203 if ( ! tOK && tUse > 1 )
204 {
205 --tUse ;
206 tOK = tTrySolve( tUse );
207 }
208 if ( ! tOK )
209 {
210 tPlainStep();
211 return 0 ;
212 }
213
214 // x_{k+1} = x + beta r - sum_j gamma_j ( dx_j + beta dr_j ),
215 // differences re-formed from the histories ( gels destroyed the
216 // normalized copy )
217 tPlainStep();
218 for ( uint j = 0; j < tUse; ++j )
219 {
220 const bool tHead = ( j + 1 == tUse );
221 const Vector< real > & tX1 = tHead ? aX : aXHistory( tUse - 2 - j );
222 const Vector< real > & tX0 = aXHistory( tUse - 1 - j );
223 const Vector< real > & tR1 = tHead ? aR : aRHistory( tUse - 2 - j );
224 const Vector< real > & tR0 = aRHistory( tUse - 1 - j );
225
226 const real tGamma = aGamma( j );
227 for ( index_t k = 0; k < tN; ++k )
228 {
229 aXNew( k ) -= tGamma * ( ( tX1( k ) - tX0( k ) )
230 + aBeta * ( tR1( k ) - tR0( k ) ) );
231 }
232 }
233 return tUse ;
234 }
235
236//------------------------------------------------------------------------------
237 }
238}
239#endif //BELFEM_FN_FEM_ANDERSON_MIXING_HPP
#define BELFEM_ERROR(aCheck,...)
Definition assert.hpp:264
#define BELFEM_ASSERT(aCheck,...)
Definition assert.hpp:244
T * data()
Definition cl_AR_Matrix.hpp:135
Fixed-capacity FIFO with history, for time-stepping.
Definition cl_ShiftRegister.hpp:74
T * data()
expose the underlying raw pointer ( writable version )
Definition cl_AR_Vector.hpp:182
void set_size(const size_t aNumRows)
change the size of the vector
Definition cl_AR_Vector.hpp:237
size_t length() const
get the length of the vector
Definition cl_AR_Vector.hpp:257
Least-squares or minimum-norm solution of a full-rank system (LAPACK ?gels).
Definition cl_IFB_LINE3.hpp:21
constexpr real gAndersonGammaMax
Definition fn_FEM_anderson_mixing.hpp:32
uint anderson_mixing_step(const Vector< real > &aX, const Vector< real > &aR, const ShiftRegister< Vector< real > > &aXHistory, const ShiftRegister< Vector< real > > &aRHistory, const real aBeta, Matrix< real > &aDeltaR, Vector< real > &aRhs, Vector< real > &aWork, Vector< real > &aGamma, Vector< real > &aColNorm, Vector< real > &aXNew)
one type-II Anderson mixing step (Walker & Ni 2011) for the fixed-point iteration x <- G( x ) with re...
Definition fn_FEM_anderson_mixing.hpp:65
int_t leading_dimension(const Vector< T > &A)
logical length of a vector operand, as passed to LAPACK as LDB; vector storage is contiguous under bo...
Definition lapacktools.hpp:143
int_t work_size(const float &aValue)
lapack reports the optimal work size in the first entry of the work array, which stays real valued fo...
Definition lapacktools.hpp:191
void gels(const char *trans, const int_t *m, const int_t *n, const int_t *nrhs, T *a, const int_t *lda, T *b, const int_t *ldb, T *work, const int_t *lwork, int_t *info)
Definition fn_gels.hpp:117
USER GUIDES:
Definition cl_Capacitor.cpp:16
unsigned int uint
Definition typedefs.hpp:30
constexpr real BELFEM_EPSILON
Definition typedefs.hpp:90
uint32_t index_t
Definition typedefs.hpp:52
double real
Definition typedefs.hpp:36
int32_t int_t
Definition typedefs.hpp:51