PSCF v1.3
AmIteratorGrid.tpp
1#ifndef RPG_AM_ITERATOR_GRID_TPP
2#define RPG_AM_ITERATOR_GRID_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 "AmIteratorGrid.h"
12#include <rpg/system/System.h>
13#include <prdc/crystal/UnitCell.h>
14#include <prdc/cuda/resources.h>
15#include <pscf/inter/Interaction.h>
16#include <pscf/iterator/NanException.h>
17
18#include <util/global.h>
19#include <cmath>
20
21namespace Pscf {
22namespace Rpg {
23
24 using namespace Util;
25 using namespace Prdc;
26 using namespace Prdc::Cuda;
27
28 // Constructor
29 template <int D>
31 : Iterator<D>(system)
32 {
33 setClassName("AmIteratorGrid");
34 isSymmetric_ = false;
35 }
36
37 // Destructor
38 template <int D>
41
42 // Read parameter file block
43 template <int D>
44 void AmIteratorGrid<D>::readParameters(std::istream& in)
45 {
46 // Call parent class readParameters
49
50 // Allocate local modified copy of Interaction class
51 interaction_.setNMonomer(system().mixture().nMonomer());
52
53 // Default parameter values
54 isFlexible_ = 1;
55 scaleStress_ = 10.0;
56
57 int np = system().domain().unitCell().nParameter();
58 UTIL_CHECK(np > 0);
59 UTIL_CHECK(np <= 6);
60 UTIL_CHECK(system().domain().unitCell().lattice() != UnitCell<D>::Null);
61
62 // Read in optional isFlexible value
63 readOptional(in, "isFlexible", isFlexible_);
64
65 // Populate flexibleParams_ based on isFlexible_ (all 0s or all 1s),
66 // then optionally overwrite with user input from param file
67 if (isFlexible_) {
68 flexibleParams_.clear();
69 for (int i = 0; i < np; i++) {
70 flexibleParams_.append(true); // Set all values to true
71 }
72 // Read optional flexibleParams_ array to overwrite current array
73 readOptionalFSArray(in, "flexibleParams", flexibleParams_, np);
74 if (nFlexibleParams() == 0) isFlexible_ = false;
75 } else { // isFlexible_ = false
76 flexibleParams_.clear();
77 for (int i = 0; i < np; i++) {
78 flexibleParams_.append(false); // Set all values to false
79 }
80 }
81
82 // Read optional scaleStress value
83 readOptional(in, "scaleStress", scaleStress_);
84 }
85
86 // Output timing results to log file.
87 template<int D>
88 void AmIteratorGrid<D>::outputTimers(std::ostream& out) const
89 {
90 // Output timing results, if requested.
91 out << "\n";
92 out << "Iterator times contributions:\n";
94 }
95
96 // Protected virtual function
97
98 // Setup before entering iteration loop
99 template <int D>
100 void AmIteratorGrid<D>::setup(bool isContinuation)
101 {
102 AmIteratorTmpl<Iterator<D>, FieldCUDA>::setup(isContinuation);
103 interaction_.update(system().interaction());
104 }
105
106 // Private virtual functions used to implement AM algorithm
107
108 // Set vector a equal to vector b (a = b).
109 template <int D>
110 void AmIteratorGrid<D>::setEqual(FieldCUDA& a, FieldCUDA const & b)
111 {
112 UTIL_CHECK(b.capacity() == a.capacity());
113 VecOp::eqV(a, b);
114 }
115
116 // Compute and return inner product of two real fields.
117 template <int D>
118 double AmIteratorGrid<D>::dotProduct(FieldCUDA const & a,
119 FieldCUDA const & b)
120 {
121 UTIL_CHECK(a.capacity() == b.capacity());
122 double val = Reduce::innerProduct(a, b);
123 if (std::isnan(val)) { // if value is NaN, throw NanException
124 throw NanException("AmIteratorGrid::dotProduct", __FILE__,
125 __LINE__, 0);
126 }
127 return val;
128 }
129
130 // Find the maximum magnitude element of a residual vector.
131 template <int D>
132 double AmIteratorGrid<D>::maxAbs(FieldCUDA const & a)
133 {
134 double val = Reduce::maxAbs(a);
135 if (std::isnan(val)) { // if value is NaN, throw NanException
136 throw NanException("AmIteratorGrid::maxAbs", __FILE__, __LINE__, 0);
137 }
138 return val;
139 }
140
141 // Update the series of residual vectors.
142 template <int D>
143 void AmIteratorGrid<D>::updateBasis(RingBuffer<FieldCUDA> & basis,
144 RingBuffer<FieldCUDA> const & hists)
145 {
146 // Make sure at least two histories are stored
147 UTIL_CHECK(hists.size() >= 2);
148
149 // Set up array to store new basis
150 basis.advance();
151 if (basis[0].isAllocated()) {
152 UTIL_CHECK(basis[0].capacity() == hists[0].capacity());
153 } else {
154 basis[0].allocate(hists[0].capacity());
155 }
156
157 // New basis vector is difference between two most recent states
158 VecOp::subVV(basis[0], hists[0], hists[1]);
159 }
160
161 // Compute trial field so as to minimize L2 norm of residual.
162 template <int D>
163 void
164 AmIteratorGrid<D>::addHistories(FieldCUDA& trial,
165 RingBuffer<FieldCUDA> const & basis,
166 DArray<double> coeffs, int nHist)
167 {
168 for (int i = 0; i < nHist; i++) {
169 VecOp::addEqVc(trial, basis[i], -1.0 * coeffs[i]);
170 }
171 }
172
173 // Add predicted error to the trial field.
174 template <int D>
175 void AmIteratorGrid<D>::addPredictedError(FieldCUDA& fieldTrial,
176 FieldCUDA const & resTrial,
177 double lambda)
178 {
179 VecOp::addEqVc(fieldTrial, resTrial, lambda);
180 }
181
182 // Checks if the system has an initial guess
183 template <int D>
184 bool AmIteratorGrid<D>::hasInitialGuess()
185 { return system().w().hasData(); }
186
187 // Compute the number of elements in the residual vector.
188 template <int D>
189 int AmIteratorGrid<D>::nElements()
190 {
191 const int nMonomer = system().mixture().nMonomer();
192 const int nMesh = system().domain().mesh().size();
193
194 int nEle = nMonomer*nMesh;
195
196 if (isFlexible_) {
197 nEle += nFlexibleParams();
198 }
199
200 return nEle;
201 }
202
203 // Get the current w fields and lattice parameters.
204 template <int D>
205 void AmIteratorGrid<D>::getCurrent(FieldCUDA& curr)
206 {
207 const int nMonomer = system().mixture().nMonomer();
208 const int nMesh = system().domain().mesh().size();
209 const int n = nElements();
210 UTIL_CHECK(curr.capacity() == n);
211
212 // Loop to unfold the system fields and store them in one long array
213 for (int i = 0; i < nMonomer; i++) {
214 VecOp::eqV(curr, system().w().rgrid(i), i*nMesh, 0, nMesh);
215 }
216
217 // If flexible unit cell, also store unit cell parameters
218 if (isFlexible_) {
219 const int nParam = system().domain().unitCell().nParameter();
220 FSArray<double,6> const & parameters
221 = system().domain().unitCell().parameters();
222
223 // convert into a cudaReal array
224 HostDArray<cudaReal> tempH(nFlexibleParams());
225 int counter = 0;
226 for (int i = 0; i < nParam; i++) {
227 if (flexibleParams_[i]) {
228 tempH[counter] = scaleStress_ * parameters[i];
229 counter++;
230 }
231 }
232 UTIL_CHECK(counter == tempH.capacity());
233
234 // Copy parameters to the end of the curr array
235 FieldCUDA tempD;
236 tempD.associate(curr, nMonomer*nMesh, tempH.capacity());
237 tempD = tempH; // copy from host to device
238 }
239 }
240
241 // Solve MDE for current state of system.
242 template <int D>
243 void AmIteratorGrid<D>::evaluate()
244 {
245 // Solve MDEs for current omega field
246 system().compute(isFlexible_);
247 }
248
249 // Gets the residual vector from system.
250 template <int D>
251 void AmIteratorGrid<D>::getResidual(FieldCUDA& resid)
252 {
253 const int n = nElements();
254 const int nMonomer = system().mixture().nMonomer();
255 const int nMesh = system().domain().mesh().size();
256
257 // Initialize residuals to zero. Kernel will take care of potential
258 // additional elements (n vs nMesh).
259 VecOp::eqS(resid, 0.0);
260
261 // Array of FieldCUDA arrays associated with slices of resid.
262 // one FieldCUDA array per monomer species, each of size nMesh.
263 DArray<FieldCUDA> residSlices;
264 residSlices.allocate(nMonomer);
265 for (int i = 0; i < nMonomer; i++) {
266 residSlices[i].associate(resid, i*nMesh, nMesh);
267 }
268
269 // Compute SCF residuals
270 for (int i = 0; i < nMonomer; i++) {
271 for (int j = 0; j < nMonomer; j++) {
272 VecOp::addVcVcVc(residSlices[i], residSlices[i], 1.0,
273 system().c().rgrid(j), interaction_.chi(i, j),
274 system().w().rgrid(j), -interaction_.p(i, j));
275 }
276 }
277
278 // If iterator has mask, account for it in residual values
279 if (system().mask().hasData()) {
280 double coeff = -1.0 / interaction_.sumChiInverse();
281 for (int i = 0; i < nMonomer; ++i) {
282 VecOp::addEqVc(residSlices[i], system().mask().rgrid(), coeff);
283 }
284 }
285
286 // If iterator has external fields, account for them in the values
287 // of the residuals
288 if (system().h().hasData()) {
289 for (int i = 0; i < nMonomer; ++i) {
290 for (int j = 0; j < nMonomer; ++j) {
291 double p = interaction_.p(i,j);
292 VecOp::addEqVc(residSlices[i], system().h().rgrid(j), p);
293 }
294 }
295 }
296
297 // If ensemble is not canonical, account for incompressibility.
298 if (!system().mixture().isCanonical()) {
299 cudaReal factor = 1.0 / interaction_.sumChiInverse();
300 VecOp::subEqS(resid, factor, 0, nMonomer*nMesh);
301 } else {
302 for (int i = 0; i < nMonomer; i++) {
303 // Find current average
304 cudaReal average = findAverage(residSlices[i]);
305 // subtract out average to set residual average to zero
306 VecOp::subEqS(residSlices[i], average);
307 }
308 }
309
310 // If variable unit cell, compute stress residuals
311 if (isFlexible_) {
312 const int nParam = system().domain().unitCell().nParameter();
313 HostDArray<cudaReal> stressH(nFlexibleParams());
314
315 int counter = 0;
316 for (int i = 0; i < nParam; i++) {
317 if (flexibleParams_[i]) {
318 double str = stress(i);
319 stressH[counter] = -1 * scaleStress_ * str;
320 counter++;
321 }
322 }
323 UTIL_CHECK(counter == stressH.capacity());
324
325 FieldCUDA stressD;
326 stressD.associate(resid, nMonomer*nMesh, stressH.capacity());
327 stressD = stressH; // copy from host to device
328 }
329 }
330
331 // Update the system with a new trial field vector.
332 template <int D>
333 void AmIteratorGrid<D>::update(FieldCUDA& newGuess)
334 {
335 const int nMonomer = system().mixture().nMonomer();
336 const int nMesh = system().domain().mesh().size();
337
338 // If canonical, explicitly set homogeneous field components
339 if (system().mixture().isCanonical()) {
340 cudaReal average, wAverage, cAverage;
341 for (int i = 0; i < nMonomer; i++) {
342 // Define array associated with a slice of newGuess
343 FieldCUDA ngSlice;
344 ngSlice.associate(newGuess, i*nMesh, nMesh);
345
346 // Find current spatial average
347 average = findAverage(ngSlice);
348
349 // Subtract average from field, setting average to zero
350 VecOp::subEqS(ngSlice, average);
351
352 // Compute the new average omega value
353 wAverage = 0;
354 for (int j = 0; j < nMonomer; j++) {
355 // Find average concentration for j monomers
356 if (system().w().isSymmetric()) { // c().basis() has data
357 UTIL_CHECK(system().c().isAllocatedBasis());
358 cAverage = system().c().basis(j)[0];
359 } else { // average must be calculated
360 cAverage = findAverage(system().c().rgrid(j));
361 }
362 wAverage += interaction_.chi(i,j) * cAverage;
363 }
364
365 // If system has external fields, include them in homogeneous field
366 if (system().h().hasData()) {
367 if (system().h().isSymmetric()) { // h().basis() has data
368 UTIL_CHECK(system().h().isAllocatedBasis());
369 wAverage += system().h().basis(i)[0];
370 } else { // average must be calculated
371 wAverage += findAverage(system().h().rgrid(i));
372 }
373 }
374
375 // Add new average omega value to the field
376 VecOp::addEqS(ngSlice, wAverage);
377 }
378 }
379
380 system().w().setRGrid(newGuess);
381 system().w().symmetrize();
382
383 // If flexible unit cell, update cell parameters
384 if (isFlexible_) {
385 FSArray<double,6> parameters;
386 parameters = system().domain().unitCell().parameters();
387 const int nParam = system().domain().unitCell().nParameter();
388
389 // transfer from device to host
390 HostDArray<cudaReal> tempH(nFlexibleParams());
391 tempH.copySlice(newGuess, nMonomer*nMesh);
392
393 const double coeff = 1.0 / scaleStress_;
394 int counter = 0;
395 for (int i = 0; i < nParam; i++) {
396 if (flexibleParams_[i]) {
397 parameters[i] = coeff * tempH[i];
398 counter++;
399 }
400 }
401 UTIL_CHECK(counter == tempH.capacity());
402
403 system().setUnitCell(parameters);
404 }
405 }
406
407 // Output relevant system details to the iteration log file.
408 template<int D>
409 void AmIteratorGrid<D>::outputToLog()
410 {
411 if (isFlexible_ && verbose() > 1) {
412 const int nParam = system().domain().unitCell().nParameter();
413 const int nMonomer = system().mixture().nMonomer();
414 const int nMesh = system().domain().mesh().size();
415
416 // transfer stress residuals from device to host
417 HostDArray<cudaReal> tempH(nFlexibleParams());
418 tempH.copySlice(residual(), nMonomer*nMesh);
419
420 int counter = 0;
421 for (int i = 0; i < nParam; i++) {
422 if (flexibleParams_[i]) {
423 double str = tempH[counter] / (-1.0 * scaleStress_);
424 Log::file()
425 << " Cell Param " << i << " = "
426 << Dbl(system().domain().unitCell().parameters()[i], 15)
427 << " , stress = "
428 << Dbl(str, 15)
429 << "\n";
430 counter++;
431 }
432 }
433 }
434 }
435
436 // --- Private member functions specific to this implementation ---
437
438 // Calculate the average value of an array.
439 template<int D>
440 cudaReal AmIteratorGrid<D>::findAverage(FieldCUDA const & field)
441 {
442 return Reduce::sum(field) / field.capacity();
443 }
444
445}
446}
447#endif
int capacity() const
Return allocated capacity.
Template for dynamic array stored in host CPU memory.
Definition HostDArray.h:43
Exception thrown when not-a-number (NaN) is encountered.
Base template for UnitCell<D> classes, D=1, 2 or 3.
Definition UnitCell.h:56
FSArrayParam< Type, N > & readOptionalFSArray(std::istream &in, const char *label, FSArray< Type, N > &array, int size)
Add and read an optional FSArray < Type, N > array parameter.
AmIteratorGrid(System< D > &system)
Constructor.
void readParameters(std::istream &in)
Read all parameters and initialize.
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.
void setup(bool isContinuation)
Setup iterator just before entering iteration loop.
void outputTimers(std::ostream &out) const
Output timing results to log file.
Base class for iterative solvers for SCF equations.
Main class, representing one complete system.
Dynamically allocatable contiguous array template.
Definition DArray.h:32
void allocate(int capacity)
Allocate the underlying C array.
Definition DArray.h:199
Wrapper for a double precision number, for formatted ostream output.
Definition Dbl.h:40
A fixed capacity (static) contiguous array with a variable logical size.
Definition FSArray.h:38
static std::ostream & file()
Get log ostream by reference.
Definition Log.cpp:59
Class for storing history of previous values in an array.
Definition RingBuffer.h:27
void allocate(int capacity)
Allocate a new empty buffer.
Definition RingBuffer.h:257
void advance()
Advances the pointer to an added element without assigning a value.
Definition RingBuffer.h:306
int size() const
Return number of values currently in the buffer.
Definition RingBuffer.h:325
File containing preprocessor macros for error handling.
#define UTIL_CHECK(condition)
Assertion macro suitable for serial or parallel production code.
Definition global.h:68
void eqV(DeviceArray< cudaReal > &a, DeviceArray< cudaReal > const &b, const int beginIdA, const int beginIdB, const int n)
Vector assignment, a[i] = b[i], kernel wrapper (cudaReal).
Definition VecOp.cu:1039
void eqS(DeviceArray< cudaReal > &a, const cudaReal b, const int beginIdA, const int n)
Vector assignment, a[i] = b, kernel wrapper (cudaReal).
Definition VecOp.cu:1073
void addEqS(DeviceArray< cudaReal > &a, const cudaReal b, const int beginIdA, const int n)
Vector addition in-place, a[i] += b, kernel wrapper (cudaReal).
Definition VecOp.cu:1724
void subEqS(DeviceArray< cudaReal > &a, const cudaReal b, const int beginIdA, const int n)
Vector subtraction in-place, a[i] -= b, kernel wrapper (cudaReal).
Definition VecOp.cu:1822
void addVcVcVc(DeviceArray< cudaReal > &a, DeviceArray< cudaReal > const &b, cudaReal const c, DeviceArray< cudaReal > const &d, cudaReal const e, DeviceArray< cudaReal > const &f, cudaReal const g)
3-vec addition w coeff, a[i] = (b[i]*c) + (d[i]*e) + (f[i]*g), kernel wrapper.
Definition VecOpMisc.cu:322
void subVV(DeviceArray< cudaReal > &a, DeviceArray< cudaReal > const &b, DeviceArray< cudaReal > const &c, const int beginIdA, const int beginIdB, const int beginIdC, const int n)
Vector subtraction, a[i] = b[i] - c[i], kernel wrapper (cudaReal).
Definition VecOp.cu:1247
void addEqVc(DeviceArray< cudaReal > &a, DeviceArray< cudaReal > const &b, cudaReal const c)
Vector addition in-place w/ coefficient, a[i] += b[i] * c, kernel wrapper.
Definition VecOpMisc.cu:343
Periodic fields and crystallography.
Definition CField.cpp:11
SCFT and PS-FTS with real periodic fields (GPU)
PSCF package top-level namespace.
Definition param_pc.dox:1