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