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