PSCF v1.3
rpc/fts/compressor/LrAmCompressor.tpp
1#ifndef RPC_LR_POST_AM_COMPRESSOR_TPP
2#define RPC_LR_POST_AM_COMPRESSOR_TPP
3
4/*
5* PSCF - Polymer Self-Consistent Field
6*
7* Copyright 2015 - 2025, The Regents of the University of Minnesota
8* Distributed under the terms of the GNU General Public License.
9*/
10
11#include "LrAmCompressor.h"
12#include <rpc/system/System.h>
13#include <pscf/mesh/MeshIterator.h>
14#include <util/global.h>
15
16namespace Pscf {
17namespace Rpc{
18
19 using namespace Util;
20
21 /*
22 * Constructor.
23 */
24 template <int D>
26 : Compressor<D>(system),
27 intra_(system),
28 isIntraCalculated_(false),
29 isAllocated_(false)
30 { setClassName("LrAmCompressor"); }
31
32 /*
33 * Destructor.
34 */
35 template <int D>
38
39 /*
40 * Read parameters from file.
41 */
42 template <int D>
43 void LrAmCompressor<D>::readParameters(std::istream& in)
44 {
45 // Call parent class readParameters
48
49 }
50
51 /*
52 * Initialize just before entry to iterative loop.
53 */
54 template <int D>
55 void LrAmCompressor<D>::setup(bool isContinuation)
56 {
57
58 // Allocate memory required by AM algorithm if not done earlier.
60
61 const int nMonomer = system().mixture().nMonomer();
62 const int meshSize = system().domain().mesh().size();
63 IntVec<D> const & dimensions = system().domain().mesh().dimensions();
64
65 // Compute Fourier space kMeshDimensions_
66 for (int i = 0; i < D; ++i) {
67 if (i < D - 1) {
68 kMeshDimensions_[i] = dimensions[i];
69 } else {
70 kMeshDimensions_[i] = dimensions[i]/2 + 1;
71 }
72 }
73
74 // Allocate memory required by compressor, if not done previously
75 if (!isAllocated_){
76 newBasis_.allocate(meshSize);
77 w0_.allocate(nMonomer);
78 wFieldTmp_.allocate(nMonomer);
79 resid_.allocate(dimensions);
80 residK_.allocate(dimensions);
81 intraCorrelationK_.allocate(kMeshDimensions_);
82 for (int i = 0; i < nMonomer; ++i) {
83 w0_[i].allocate(dimensions);
84 wFieldTmp_[i].allocate(dimensions);
85 }
86
87 isAllocated_ = true;
88 }
89
90 // Compute intraCorrelation
91 if (!isIntraCalculated_){
92 intra_.computeIntraCorrelations(intraCorrelationK_);
93 isIntraCalculated_ = true;
94 }
95
96 // Store initial values of monomer chemical potential fields in w0_
97 for (int i = 0; i < nMonomer; ++i) {
98 for (int j = 0; j< meshSize; ++j){
99 w0_[i][j] = system().w().rgrid(i)[j];
100 }
101 }
102
103 }
104
105 /*
106 * Apply the Anderson-Mixing algorithm (main function).
107 */
108 template <int D>
114
115 /*
116 * Assign one array to another.
117 */
118 template <int D>
119 void LrAmCompressor<D>::setEqual(DArray<double>& a,
120 DArray<double> const & b)
121 { a = b; }
122
123 /*
124 * Compute and return inner product of two vectors.
125 */
126 template <int D>
127 double
128 LrAmCompressor<D>::dotProduct(DArray<double> const & a,
129 DArray<double> const & b)
130 {
131 const int n = a.capacity();
132 UTIL_CHECK(b.capacity() == n);
133 double product = 0.0;
134 for (int i = 0; i < n; i++) {
135 // if either value is NaN, throw NanException
136 if (std::isnan(a[i]) || std::isnan(b[i])) {
137 throw NanException("LrAmCompressor::dotProduct",
138 __FILE__,__LINE__,0);
139 }
140 product += a[i] * b[i];
141 }
142 return product;
143 }
144
145 /*
146 * Compute and return the maximum element of a vector.
147 */
148 template <int D>
149 double LrAmCompressor<D>::maxAbs(DArray<double> const & a)
150 {
151 const int n = a.capacity();
152 double max = 0.0;
153 double value;
154 for (int i = 0; i < n; i++) {
155 value = a[i];
156 if (std::isnan(value)) { // if value is NaN, throw NanException
157 throw NanException("LrAmCompressor::dotProduct",
158 __FILE__, __LINE__, 0);
159 }
160 if (fabs(value) > max) {
161 max = fabs(value);
162 }
163 }
164 return max;
165 }
166
167 /*
168 * Update basis by adding one new basis vector.
169 */
170 template <int D>
171 void
172 LrAmCompressor<D>::updateBasis(
173 RingBuffer< DArray<double> > & basis,
174 RingBuffer< DArray<double> > const & hists)
175 {
176 // Make sure at least two histories are stored
177 UTIL_CHECK(hists.size() >= 2);
178
179 const int n = hists[0].capacity();
180
181 // New basis vector is difference between two most recent states
182 for (int i = 0; i < n; i++) {
183 newBasis_[i] = hists[0][i] - hists[1][i];
184 }
185
186 basis.append(newBasis_);
187 }
188
189 template <int D>
190 void
191 LrAmCompressor<D>::addHistories(DArray<double>& trial,
192 RingBuffer<DArray<double> > const & basis,
193 DArray<double> coeffs,
194 int nHist)
195 {
196 int n = trial.capacity();
197 for (int i = 0; i < nHist; i++) {
198 for (int j = 0; j < n; j++) {
199 // Not clear on the origin of the -1 factor
200 trial[j] += coeffs[i] * -1 * basis[i][j];
201 }
202 }
203 }
204
205 /*
206 * Returns mixing value parameter lambda = 1.0
207 */
208 template<int D>
209 double LrAmCompressor<D>::computeLambda(double r)
210 { return 1.0; }
211
212 /*
213 * Correction step (second step of Anderson mixing)
214 *
215 * This LrAM algorithm uses a quasi-Newton correction step with an
216 * approximate Jacobian given by the Jacobian in a homogeneous state.
217 */
218 template <int D>
219 void
220 LrAmCompressor<D>::addPredictedError(DArray<double>& fieldTrial,
221 DArray<double> const & resTrial,
222 double lambda)
223 {
224 // Local constants
225 const int n = fieldTrial.capacity();
226 const double vMonomer = system().mixture().vMonomer();
227
228 // Copy DArray<double> resTrial into RField<D> resid_
229 // Allows use of FFT functions that take an RField container
230 for (int i = 0 ; i < n; ++i) {
231 resid_[i] = resTrial[i];
232 }
233
234 // Fourier transform r-grid residual resid_ to k-grid form residK_
235 system().domain().fft().forwardTransform(resid_, residK_);
236
237 // Compute update on a k-grid, using quasi-Newton algorithm
238 double factor;
239 MeshIterator<D> iter;
240 iter.setDimensions(kMeshDimensions_);
241 for (iter.begin(); !iter.atEnd(); ++iter) {
242 factor = 1.0 / (vMonomer * intraCorrelationK_[iter.rank()]);
243 residK_[iter.rank()][0] *= factor;
244 residK_[iter.rank()][1] *= factor;
245 }
246 // Field residK_ now stores k-grid update rather than residual
247
248 // Convert field update back to r-grid (real space) form
249 // On return, resid_ contains r-grid field update, residK_ is destroyed
250 system().domain().fft().inverseTransformUnsafe(residK_, resid_);
251
252 // Add update to obtain new fieldTrial
253 for (int i = 0; i < n; i++) {
254 fieldTrial[i] += lambda * resid_[i];
255 }
256 }
257
258 /*
259 * Does the associated system have initialized w fields?
260 */
261 template <int D>
262 bool LrAmCompressor<D>::hasInitialGuess()
263 { return system().w().hasData(); }
264
265 /*
266 * Compute and return the number of elements in a field vector.
267 */
268 template <int D>
269 int LrAmCompressor<D>::nElements()
270 { return system().domain().mesh().size(); }
271
272 /*
273 * Get the current field variable from the system.
274 *
275 * The field variable is the change in the Lagrange multiplier field.
276 * relative to that used in initial array of monomer fields, w0_.
277 */
278 template <int D>
279 void LrAmCompressor<D>::getCurrent(DArray<double>& curr)
280 {
281 // Straighten out fields into linear arrays
282 const int meshSize = system().domain().mesh().size();
283 const DArray< RField<D> > * currSys = &system().w().rgrid();
284
285 /*
286 * The field that we are adjusting is the Langrange multiplier field
287 * with number of grid pts components. The current value is the
288 * difference between w and w0_ for the first monomer (any monomer
289 * should give the same answer)
290 */
291 for (int i = 0; i < meshSize; i++){
292 curr[i] = (*currSys)[0][i] - w0_[0][i];
293 }
294
295 }
296
297 /*
298 * Perform the main system computation (solve the MDE).
299 */
300 template <int D>
301 void LrAmCompressor<D>::evaluate()
302 {
303 system().compute();
304 ++mdeCounter_;
305 }
306
307 /*
308 * Compute the residual vector for the current system state
309 */
310 template <int D>
311 void LrAmCompressor<D>::getResidual(DArray<double>& resid)
312 {
313 const int n = nElements();
314 const int nMonomer = system().mixture().nMonomer();
315 const int meshSize = system().domain().mesh().size();
316
317 // Initialize residuals
318 for (int i = 0 ; i < n; ++i) {
319 resid[i] = -1.0;
320 }
321
322 // Compute SCF residual vector elements
323 for (int j = 0; j < nMonomer; ++j) {
324 for (int k = 0; k < meshSize; ++k) {
325 resid[k] += system().c().rgrid(j)[k];
326 }
327 }
328
329 }
330
331 /*
332 * Update the w field values stored in the system
333 */
334 template <int D>
335 void LrAmCompressor<D>::update(DArray<double>& newGuess)
336 {
337 // Local constant values
338 const int nMonomer = system().mixture().nMonomer();
339 const int meshSize = system().domain().mesh().size();
340
341 // Locally update monomer w fields in array wFieldTmp_
342 // Set wFieldTmp = w0_ + newGuess
343 for (int i = 0; i < nMonomer; i++){
344 for (int k = 0; k < meshSize; k++){
345 wFieldTmp_[i][k] = w0_[i][k] + newGuess[k];
346 }
347 }
348
349 // Update w fields in the system WContainer
350 system().w().setRGrid(wFieldTmp_);
351 }
352
353 /*
354 * Output results to log file (do-nothing implementation).
355 */
356 template<int D>
357 void LrAmCompressor<D>::outputToLog()
358 {}
359
360 /*
361 * Output timing results to an output stream (file)
362 */
363 template<int D>
364 void LrAmCompressor<D>::outputTimers(std::ostream& out) const
365 {
366 // Output timing results, if requested.
367 out << "\n";
368 out << "LrAmCompressor time contributions:\n";
370 }
371
372 /*
373 * Clear all timers and the MDE counter
374 */
375 template<int D>
381
382}
383}
384#endif
An IntVec<D, T> is a D-component vector of elements of integer type T.
Definition IntVec.h:27
Iterator over points in a Mesh<D>.
void setDimensions(const IntVec< D > &dimensions)
Set the grid dimensions in all directions.
Exception thrown when not-a-number (NaN) is encountered.
Base class for iterators that impose incompressibility.
void readParameters(std::istream &in)
Read all parameters and initialize.
LrAmCompressor(System< D > &system)
Constructor.
void outputTimers(std::ostream &out) const
Return compressor times contributions.
void clearTimers()
Clear all timers (reset accumulated time to zero).
void setup(bool isContinuation)
Initialize just before entry to iterative loop.
int compress()
Compress to obtain partial saddle point w+.
Main class, representing one complete system.
int capacity() const
Return allocated size.
Definition Array.h:159
Dynamically allocatable contiguous array template.
Definition DArray.h:32
Class for storing history of previous values in an array.
Definition RingBuffer.h:27
File containing preprocessor macros for error handling.
#define UTIL_CHECK(condition)
Assertion macro suitable for serial or parallel production code.
Definition global.h:68
Real periodic fields, SCFT and PS-FTS (CPU).
Definition param_pc.dox:2
PSCF package top-level namespace.
Definition param_pc.dox:1
float product(float a, float b)
Product for float Data.
Definition product.h:22