PSCF v1.3.2
rpc/fts/compressor/LrCompressor.tpp
1#ifndef RPC_LR_COMPRESSOR_TPP
2#define RPC_LR_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 "LrCompressor.h"
12#include <rpc/system/System.h>
13#include <prdc/crystal/shiftToMinimum.h>
14#include <pscf/mesh/MeshIterator.h>
15#include <pscf/iterator/NanException.h>
16#include <util/global.h>
17#include <util/format/Dbl.h>
18
19
20namespace Pscf {
21namespace Rpc{
22
23 using namespace Util;
24
25 // Constructor
26 template <int D>
28 : Compressor<D>(system),
29 epsilon_(0.0),
30 itr_(0),
31 maxItr_(100),
32 totalItr_(0),
33 errorType_("rms"),
34 verbose_(0),
35 intra_(system),
36 isIntraCalculated_(false),
37 isAllocated_(false)
38 { setClassName("LrCompressor"); }
39
40 // Destructor
41 template <int D>
44
45 // Read parameters from file
46 template <int D>
47 void LrCompressor<D>::readParameters(std::istream& in)
48 {
49 // Default values
50 maxItr_ = 100;
51 errorType_ = "rms";
52
53 read(in, "epsilon", epsilon_);
54 readOptional(in, "maxItr", maxItr_);
55 readOptional(in, "verbose", verbose_);
56 readOptional(in, "errorType", errorType_);
57 }
58
59 // Initialize just before entry to iterative loop.
60 template <int D>
62 {
63 const int nMonomer = system().mixture().nMonomer();
64 IntVec<D> const & dimensions = system().domain().mesh().dimensions();
65 for (int i = 0; i < D; ++i) {
66 if (i < D - 1) {
67 kMeshDimensions_[i] = dimensions[i];
68 } else {
69 kMeshDimensions_[i] = dimensions[i]/2 + 1;
70 }
71 }
72
73 // Allocate memory required by AM algorithm if not done earlier.
74 if (!isAllocated_){
75 resid_.allocate(dimensions);
76 residK_.allocate(dimensions);
77 wFieldTmp_.allocate(nMonomer);
78 intraCorrelationK_.allocate(kMeshDimensions_);
79 for (int i = 0; i < nMonomer; ++i) {
80 wFieldTmp_[i].allocate(dimensions);
81 }
82 isAllocated_ = true;
83 }
84
85 // Compute intraCorrelation
86 if (!isIntraCalculated_){
87 intra_.computeIntraCorrelations(intraCorrelationK_);
88 isIntraCalculated_ = true;
89 }
90 }
91
92 /*
93 * Adjust pressure field find partial saddle point.
94 */
95 template <int D>
97 {
98 // Initialization and allocate operations on entry to loop.
99 setup();
100 UTIL_CHECK(isAllocated_);
101
102 // Start overall timer
103 timerTotal_.start();
104
105 // Solve MDE
106 timerMDE_.start();
107 system().compute();
108 ++mdeCounter_;
109 timerMDE_.stop();
110
111 // Iterative loop
112 for (itr_ = 0; itr_ < maxItr_; ++itr_) {
113
114 if (verbose_ > 2) {
115 Log::file() << "------------------------------- \n";
116 }
117
118 if (verbose_ > 0){
119 Log::file() << " Iteration " << Int(itr_,5);
120 }
121
122 // Compute residual vector
123 getResidual();
124 double error;
125 try {
126 error = computeError(verbose_);
127 } catch (const NanException&) {
128 Log::file() << ", error = NaN" << std::endl;
129 break; // Exit loop if a NanException is caught
130 }
131 if (verbose_ > 0) {
132 Log::file() << ", error = " << Dbl(error, 15) << std::endl;
133 }
134
135 // Check for convergence
136 if (error < epsilon_) {
137
138 // Successful completion (i.e., converged within tolerance)
139 timerTotal_.stop();
140
141 if (verbose_ > 2) {
142 Log::file() << "-------------------------------\n";
143 }
144 if (verbose_ > 0) {
145 Log::file() << " Converged\n";
146 }
147 if (verbose_ == 2) {
148 Log::file() << "\n";
149 computeError(2);
150 }
151 //mdeCounter_ += itr_;
152 totalItr_ += itr_;
153
154 return 0; // Success
155
156 } else{
157
158 // Not yet converged.
159 updateWFields();
160 timerMDE_.start();
161 system().compute();
162 ++mdeCounter_;
163 timerMDE_.stop();
164
165 }
166
167 }
168
169 // Failure: iteration counter itr reached maxItr without converging
170 timerTotal_.stop();
171
172 Log::file() << "Iterator failed to converge.\n";
173 return 1;
174
175 }
176
177 /*
178 * Compute the residual for the current system state
179 */
180 template <int D>
181 void LrCompressor<D>::getResidual()
182 {
183 const int nMonomer = system().mixture().nMonomer();
184 const int meshSize = system().domain().mesh().size();
185
186 // Initialize residuals
187 for (int i = 0 ; i < meshSize; ++i) {
188 resid_[i] = -1.0;
189 }
190
191 // Compute SCF residual vector elements
192 for (int j = 0; j < nMonomer; ++j) {
193 for (int k = 0; k < meshSize; ++k) {
194 resid_[k] += system().c().rgrid(j)[k];
195 }
196 }
197 }
198
199 // update system w field using linear response approximation
200 template <int D>
201 void LrCompressor<D>::updateWFields()
202 {
203 const int nMonomer = system().mixture().nMonomer();
204 const int meshSize = system().domain().mesh().size();
205 const double vMonomer = system().mixture().vMonomer();
206
207 // Convert residual to Fourier Space
208 system().domain().fft().forwardTransform(resid_, residK_);
209 MeshIterator<D> iter;
210 iter.setDimensions(residK_.dftDimensions());
211 for (iter.begin(); !iter.atEnd(); ++iter) {
212 residK_[iter.rank()][0] *= 1.0 / (vMonomer * intraCorrelationK_[iter.rank()]);
213 residK_[iter.rank()][1] *= 1.0 / (vMonomer * intraCorrelationK_[iter.rank()]);
214 }
215
216 // Convert back to real Space (destroys residK_)
217 system().domain().fft().inverseTransformUnsafe(residK_, resid_);
218
219 for (int i = 0; i < nMonomer; i++){
220 for (int k = 0; k < meshSize; k++){
221 wFieldTmp_[i][k] = system().w().rgrid(i)[k] + resid_[k];
222 }
223 }
224 system().w().setRGrid(wFieldTmp_);
225 }
226
227 template<int D>
228 void LrCompressor<D>::outputToLog()
229 {}
230
231 template<int D>
232 void LrCompressor<D>::outputTimers(std::ostream& out) const
233 {
234 // Output timing results, if requested.
235 double total = timerTotal_.time();
236 out << "\n";
237 out << "LrCompressor time contributions:\n";
238 out << " ";
239 out << "Total" << std::setw(22)<< "Per Iteration"
240 << std::setw(9) << "Fraction" << "\n";
241 out << "MDE solution: "
242 << Dbl(timerMDE_.time(), 9, 3) << " s, "
243 << Dbl(timerMDE_.time()/totalItr_, 9, 3) << " s, "
244 << Dbl(timerMDE_.time()/total, 9, 3) << "\n";
245 out << "\n";
246 }
247
248 template<int D>
250 {
251 timerTotal_.clear();
252 timerMDE_.clear();
253 mdeCounter_ = 0;
254 totalItr_ = 0;
255 }
256
257 template <int D>
258 double LrCompressor<D>::maxAbs(RField<D> const & a)
259 {
260 const int n = a.capacity();
261 double max = 0.0;
262 double value;
263 for (int i = 0; i < n; i++) {
264 value = a[i];
265 if (std::isnan(value)) { // if value is NaN, throw NanException
266 throw NanException("LrCompressor::dotProduct",__FILE__,__LINE__,0);
267 }
268 if (fabs(value) > max) {
269 max = fabs(value);
270 }
271 }
272 return max;
273 }
274
275 // Compute and return inner product of two vectors.
276 template <int D>
277 double LrCompressor<D>::dotProduct(RField<D> const & a,
278 RField<D> const & b)
279 {
280 const int n = a.capacity();
281 UTIL_CHECK(b.capacity() == n);
282 double product = 0.0;
283 for (int i = 0; i < n; i++) {
284 // if either value is NaN, throw NanException
285 if (std::isnan(a[i]) || std::isnan(b[i])) {
286 throw NanException("AmCompressor::dotProduct",__FILE__,__LINE__,0);
287 }
288 product += a[i] * b[i];
289 }
290 return product;
291 }
292
293 // Compute L2 norm of an RField
294 template <int D>
295 double LrCompressor<D>::norm(RField<D> const & a)
296 {
297 double normSq = dotProduct(a, a);
298 return sqrt(normSq);
299 }
300
301 // Compute and return the scalar error
302 template <int D>
303 double LrCompressor<D>::computeError(int verbose)
304 {
305 const int meshSize = system().domain().mesh().size();
306 double error = 0.0;
307
308 // Find max residual vector element
309 double maxRes = maxAbs(resid_);
310 // Find norm of residual vector
311 double normRes = norm(resid_);
312 // Find root-mean-squared residual element value
313 double rmsRes = normRes/sqrt(meshSize);
314 if (errorType_ == "max") {
315 error = maxRes;
316 } else if (errorType_ == "norm") {
317 error = normRes;
318 } else if (errorType_ == "rms") {
319 error = rmsRes;
320 } else {
321 UTIL_THROW("Invalid iterator error type in parameter file.");
322 }
323
324 if (verbose > 1) {
325 Log::file() << "\n";
326 Log::file() << "Max Residual = " << Dbl(maxRes,15) << "\n";
327 Log::file() << "Residual Norm = " << Dbl(normRes,15) << "\n";
328 Log::file() << "RMS Residual = " << Dbl(rmsRes,15) << "\n";
329
330 // Check if calculation has diverged (normRes will be NaN)
331 UTIL_CHECK(!std::isnan(normRes));
332 }
333 return error;
334 }
335
336}
337}
338#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.
Field of real double precision values on an FFT mesh.
Definition cpu/RField.h:29
System< D > const & system() const
Return const reference to parent system.
int mdeCounter_
Count how many times MDE has been solved.
ScalarParam< Type > & read(std::istream &in, const char *label, Type &value)
Add and read a new required ScalarParam < Type > object.
void setClassName(const char *className)
Set class name string.
LrCompressor(System< D > &system)
Constructor.
ScalarParam< Type > & readOptional(std::istream &in, const char *label, Type &value)
Add and read a new optional ScalarParam < Type > object.
int compress()
Iterate pressure field to obtain partial saddle point.
void readParameters(std::istream &in)
Read all parameters and initialize.
void setup()
Initialize just before entry to iterative loop.
void outputTimers(std::ostream &out) const
Return compressor times contributions.
Main class, representing a complete physical system.
int capacity() const
Return allocated size.
Definition Array.h:159
Wrapper for a double precision number, for formatted ostream output.
Definition Dbl.h:40
Wrapper for an int, for formatted ostream output.
Definition Int.h:37
static std::ostream & file()
Get log ostream by reference.
Definition Log.cpp:59
File containing preprocessor macros for error handling.
#define UTIL_CHECK(condition)
Assertion macro suitable for serial or parallel production code.
Definition global.h:68
#define UTIL_THROW(msg)
Macro for throwing an Exception, reporting function, file and line number.
Definition global.h:49
double maxAbs(Array< double > const &in)
Get maximum absolute magnitude of array elements .
Definition Reduce.cpp:34
double max(Array< double > const &in)
Get maximum of array elements .
Definition Reduce.cpp:20
Real periodic fields, SCFT and PS-FTS (CPU).
Definition param_pc.dox:2
PSCF package top-level namespace.
float product(float a, float b)
Product for float Data.
Definition product.h:22