PSCF v1.4.0
LrCompressor.tpp
1#ifndef RP_LR_COMPRESSOR_TPP
2#define RP_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 <prdc/crystal/shiftToMinimum.h>
13#include <pscf/mesh/MeshIterator.h>
14#include <pscf/iterator/NanException.h>
15#include <util/format/Dbl.h>
16#include <util/global.h>
17
18namespace Pscf {
19namespace Rp {
20
21 using namespace Util;
22
23 /*
24 * Constructor.
25 */
26 template <int D, class T>
27 LrCompressor<D,T>::LrCompressor(typename T::System& system)
28 : CompressorT(system),
29 intra_(system),
30 errorType_("rms"),
31 epsilon_(0.0),
32 itr_(0),
33 maxItr_(100),
34 totalItr_(0),
35 verbose_(0),
36 isAllocated_(false),
37 isIntraCalculated_(false)
38 { ParamComposite::setClassName("LrCompressor"); }
39
40 /*
41 * Read parameters from file.
42 */
43 template <int D, class T>
44 void LrCompressor<D,T>::readParameters(std::istream& in)
45 {
46 // Default values
47 maxItr_ = 100;
48 verbose_ = 0;
49 errorType_ = "rms";
50
51 // Read parameters
52 ParamComposite::read(in, "epsilon", epsilon_);
53 ParamComposite::readOptional(in, "maxItr", maxItr_);
54 ParamComposite::readOptional(in, "verbose", verbose_);
55 ParamComposite::readOptional(in, "errorType", errorType_);
56 }
57
58 /*
59 * Initialize just before entry to iterative loop.
60 */
61 template <int D, class T>
63 {
64 IntVec<D> const & dimensions = system().domain().mesh().dimensions();
65 int kSize;
66 FFTT::computeKMesh(dimensions, kMeshDimensions_, kSize);
67
68 // Allocate memory required by AM algorithm if not done earlier.
69 if (!isAllocated_) {
70 resid_.allocate(dimensions);
71 residK_.allocate(dimensions);
72 const int nMonomer = system().mixture().nMonomer();
73 wFieldTmp_.allocate(nMonomer);
74 intraCorrelationK_.allocate(kMeshDimensions_);
75 for (int i = 0; i < nMonomer; ++i) {
76 wFieldTmp_[i].allocate(dimensions);
77 }
78 isAllocated_ = true;
79 }
81 // Compute intraCorrelation
82 if (!isIntraCalculated_){
83 intra_.computeOmegaTotal(intraCorrelationK_);
84 isIntraCalculated_ = true;
85 }
86 }
87
88 /*
89 * Adjust pressure field to find partial saddle point.
90 */
91 template <int D, class T>
93 {
94 // Initialization and allocate operations on entry to loop.
95 setup();
96 UTIL_CHECK(isAllocated_);
97
98 // Start overall timer
99 timerTotal_.start();
100
101 // Solve MDE
102 timerMDE_.start();
103 system().compute();
104 ++CompressorT::mdeCounter_;
105 timerMDE_.stop();
106
107 // Iterative loop
108 for (itr_ = 0; itr_ < maxItr_; ++itr_) {
109
110 if (verbose_ > 2) {
111 Log::file() << "------------------------------- \n";
112 }
113
114 if (verbose_ > 0){
115 Log::file() << " Iteration " << Int(itr_,5);
116 }
117
118 // Compute residual vector
119 computeResidual();
120 double error;
121 try {
122 error = computeError(verbose_);
123 } catch (const NanException&) {
124 Log::file() << ", error = NaN" << std::endl;
125 break; // Exit loop if a NanException is caught
126 }
127 if (verbose_ > 0) {
128 Log::file() << ", error = " << Dbl(error, 15) << std::endl;
129 }
130
131 // Check for convergence
132 if (error < epsilon_) {
133
134 // Successful completion (i.e., converged within tolerance)
135 timerTotal_.stop();
136
137 if (verbose_ > 2) {
138 Log::file() << "-------------------------------\n";
139 }
140 if (verbose_ > 0) {
141 Log::file() << " Converged\n";
142 }
143 if (verbose_ == 2) {
144 Log::file() << "\n";
145 computeError(2);
146 }
147 //mdeCounter_ += itr_;
148 totalItr_ += itr_;
149
150 return 0; // Success
151
152 } else{
153
154 // Not yet converged.
155 updateWFields();
156 timerMDE_.start();
157 system().compute();
158 ++CompressorT::mdeCounter_;
159 timerMDE_.stop();
160
161 }
162
163 }
164
165 // Failure: iteration counter itr reached maxItr without converging
166 timerTotal_.stop();
167 Log::file() << "Iterator failed to converge.\n";
168 return 1;
169
170 }
171
172 /*
173 * Compute and store the residual for the current system state.
174 */
175 template <int D, class T>
176 void LrCompressor<D,T>::computeResidual()
177 {
178 // Initialize resid_ to -1
179 VecOp::eqS(resid_, -1.0);
180
181 // Add all c fields
182 const int nMonomer = system().mixture().nMonomer();
183 for (int i = 0; i < nMonomer; ++i) {
184 VecOp::addEqV(resid_, system().c().rgrid(i));
185 }
186 }
187
188 /*
189 * Update system w fields using linear response approximation.
190 */
191 template <int D, class T>
192 void LrCompressor<D,T>::updateWFields()
193 {
194 const int nMonomer = system().mixture().nMonomer();
195 const double vMonomer = system().mixture().vMonomer();
196
197 // Convert residual to Fourier Space
198 system().domain().fft().forwardTransform(resid_, residK_);
199
200 // Compute change in fields using estimated Jacobian
201 VecOp::divEqVc(residK_, intraCorrelationK_, vMonomer);
202
203 // Convert back to real space (destroys residK_)
204 system().domain().fft().inverseTransformUnsafe(residK_, resid_);
205
206 // Update new fields
207 for (int i = 0; i < nMonomer; i++) {
208 VecOp::addVV(wFieldTmp_[i], system().w().rgrid(i), resid_);
209 }
210
211 // Set system w fields
212 system().w().setRGrid(wFieldTmp_);
213 }
214
215 /*
216 * Output information to a log file (do-nothing implementation).
217 */
218 template <int D, class T>
219 void LrCompressor<D,T>::outputToLog()
220 {}
221
222 /*
223 * Output timing information to evaluate performance.
224 */
225 template <int D, class T>
226 void LrCompressor<D,T>::outputTimers(std::ostream& out) const
227 {
228 // Output timing results, if requested.
229 double total = timerTotal_.time();
230 out << "\n";
231 out << "LrCompressor time contributions:\n";
232 out << " ";
233 out << "Total" << std::setw(22)<< "Per Iteration"
234 << std::setw(9) << "Fraction" << "\n";
235 out << "MDE solution: "
236 << Dbl(timerMDE_.time(), 9, 3) << " s, "
237 << Dbl(timerMDE_.time()/totalItr_, 9, 3) << " s, "
238 << Dbl(timerMDE_.time()/total, 9, 3) << "\n";
239 out << "\n";
240 }
241
242 /*
243 * Clear all timers.
244 */
245 template <int D, class T>
247 {
248 timerTotal_.clear();
249 timerMDE_.clear();
250 CompressorT::mdeCounter_ = 0;
251 totalItr_ = 0;
252 }
253
254 /*
255 * Compute and return the scalar error.
256 */
257 template <int D, class T>
258 double LrCompressor<D,T>::computeError(int verbose)
259 {
260 const int meshSize = system().domain().mesh().size();
261 double error = 0.0;
262
263 // Find max residual vector element
264 double maxRes = Reduce::maxAbs(resid_);
265 // Find norm of residual vector
266 double normRes = sqrt(Reduce::sumSq(resid_));
267 // Find root-mean-squared residual element value
268 double rmsRes = normRes/sqrt(meshSize);
269 if (errorType_ == "max") {
270 error = maxRes;
271 } else if (errorType_ == "norm") {
272 error = normRes;
273 } else if (errorType_ == "rms") {
274 error = rmsRes;
275 } else {
276 UTIL_THROW("Invalid iterator error type in parameter file.");
277 }
278
279 if (verbose > 1) {
280 Log::file() << "\n";
281 Log::file() << "Max Residual = " << Dbl(maxRes,15) << "\n";
282 Log::file() << "Residual Norm = " << Dbl(normRes,15) << "\n";
283 Log::file() << "RMS Residual = " << Dbl(rmsRes,15) << "\n";
284
285 // Check if calculation has diverged (normRes will be NaN)
286 UTIL_CHECK(!std::isnan(normRes));
287 }
288 return error;
289 }
290
291}
292}
293#endif
An IntVec<D, T> is a D-component vector of elements of integer type T.
Definition IntVec.h:27
Exception thrown when not-a-number (NaN) is encountered.
int compress()
Iterate pressure field to obtain partial saddle point.
void outputTimers(std::ostream &out) const
Return compressor times contributions.
LrCompressor(typename T::System &system)
Constructor.
void setup()
Initialize just before entry to iterative loop.
void readParameters(std::istream &in)
Read all parameters and initialize.
void clearTimers()
Clear all timers.
System< D > const & system() const
Return parent system by const reference.
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
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.
ScalarParam< Type > & readOptional(std::istream &in, const char *label, Type &value)
Add and read a new optional ScalarParam < Type > object.
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:182
double sumSq(Array< double > const &in)
Compute sum of of squares of array elements (real).
Definition Reduce.cpp:82
void addEqV(Array< double > &a, Array< double > const &b)
Vector-vector in-place addition, a[i] += b[i] (real).
Definition VecOp.cpp:198
void eqS(Array< double > &a, double b)
Vector assignment, a[i] = b (real).
Definition VecOp.cpp:50
void addVV(Array< double > &a, Array< double > const &b, Array< double > const &c)
Vector-vector addition, a[i] = b[i] + c[i] (real)
Definition VecOp.cpp:64
void divEqVc(Array< fftw_complex > &a, Array< double > const &b, double c)
Vector division in-place w/ coeff., a[i] /= (b[i] * c).
Definition VecOpCx.cpp:730
Class templates for real-valued periodic fields.
PSCF package top-level namespace.