PSCF v1.3.2
rpc/scft/iterator/AmIteratorGrid.tpp
1#ifndef RPC_AM_ITERATOR_GRID_TPP
2#define RPC_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 <rpc/system/System.h>
13#include <rpc/solvers/Mixture.h>
14#include <rpc/field/Domain.h>
15#include <prdc/crystal/UnitCell.h>
16#include <prdc/cpu/Reduce.h>
17#include <prdc/cpu/VecOp.h>
18#include <pscf/inter/Interaction.h>
19#include <pscf/iterator/NanException.h>
20#include <util/containers/RingBuffer.h>
21#include <util/containers/DArray.h>
22#include <util/global.h>
23#include <cmath>
24
25namespace Pscf {
26namespace Rpc {
27
28 using namespace Util;
29 using namespace Prdc;
30 using namespace Prdc::Cpu;
31
32 // Public member functions
33
34 /*
35 * Constructor.
36 */
37 template <int D>
39 : Iterator<D>(system)
40 {
41 isSymmetric_ = true;
42 ParamComposite::setClassName("AmIteratorGrid");
43 }
44
45 /*
46 * Destructor.
47 */
48 template <int D>
51
52 /*
53 * Read parameters from file.
54 */
55 template <int D>
56 void AmIteratorGrid<D>::readParameters(std::istream& in)
57 {
58 // Use base class methods to read parameters
61
62 UnitCell<D> const & unitCell = system().domain().unitCell();
63 UTIL_CHECK(unitCell.lattice() != UnitCell<D>::Null);
64 int np = unitCell.nParameter();
65 UTIL_CHECK(np > 0);
66 UTIL_CHECK(np <= 6);
67
68 // Read optional isFlexible boolean (true by default)
69 isFlexible_ = 1; // Default
70 readOptional(in, "isFlexible", isFlexible_);
71
72 // Populate flexibleParams_ based on isFlexible_ (all 0s or all 1s),
73 // then optionally overwrite with user input from param file
74 if (isFlexible_) {
75 flexibleParams_.clear();
76 for (int i = 0; i < np; i++) {
77 flexibleParams_.append(true); // Set all values to true
78 }
79 // Read optional flexibleParams_ array to overwrite current array
80 readOptionalFSArray(in, "flexibleParams", flexibleParams_, np);
81 if (nFlexibleParams() == 0) {
82 isFlexible_ = false;
83 }
84 } else { // isFlexible_ = false
85 flexibleParams_.clear();
86 for (int i = 0; i < np; i++) {
87 flexibleParams_.append(false); // Set all values to false
88 }
89 }
90
91 // Read optional scaleStress value
92 scaleStress_ = 10.0; // Default
93 readOptional(in, "scaleStress", scaleStress_);
94
95 // Read option mixing parameters (lambda, useLambdaRamp, and r)
97
98 // Allocate local modified copy of Interaction class
99 const int nMonomer = system().mixture().nMonomer();
100 interaction_.setNMonomer(nMonomer);
101
102 }
103
104 /*
105 * Output timing results to log file.
106 */
107 template<int D>
108 void AmIteratorGrid<D>::outputTimers(std::ostream& out) const
109 {
110 out << "\n";
111 out << "Iterator times contributions:\n";
113 }
114
115 // Protected virtual function
116
117 // Setup before entering iteration loop
118 template <int D>
119 void AmIteratorGrid<D>::setup(bool isContinuation)
120 {
121 AmTmpl::setup(isContinuation);
122 interaction_.update(system().interaction());
123 }
124
125 // Private virtual functions to exchange data with parent system
126
127 /*
128 * Compute the number of elements in the residual vector.
129 */
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 if (isFlexible_) {
138 nEle += nFlexibleParams();
139 }
140 return nEle;
141 }
142
143 /*
144 * Does the system have an initial field guess?
145 */
146 template <int D>
147 bool AmIteratorGrid<D>::hasInitialGuess()
148 { return system().w().hasData(); }
149
150 /*
151 * Get the current field vector (w fields and lattice parameters).
152 */
153 template <int D>
154 void AmIteratorGrid<D>::getCurrent(DArray<double>& state)
155 {
156 const int nMonomer = system().mixture().nMonomer();
157 const int nMesh = system().domain().mesh().size();
158 const int nEle = nElements();
159 UTIL_CHECK(state.capacity() == nEle);
160
161 // Copy w-fields into a linear array
162 int begin;
163 for (int i = 0; i < nMonomer; i++) {
164 const RField<D>& field = system().w().rgrid(i);
165 begin = i*nMesh;
166 for (int k = 0; k < nMesh; k++) {
167 state[begin + k] = field[k];
168 }
169 }
170
171 // Add elements associated with unit cell parameters (if any)
172 if (isFlexible()) {
173 UTIL_CHECK(nFlexibleParams() > 0);
174 UnitCell<D> const & unitCell = system().domain().unitCell();
175 FSArray<double, 6> const & parameters = unitCell.parameters();
176 const int nParam = unitCell.nParameter();
177 begin = nMonomer*nMesh;
178 int counter = 0;
179 for (int i = 0; i < nParam; i++) {
180 if (flexibleParams_[i]) {
181 state[begin + counter] = scaleStress_ * parameters[i];
182 counter++;
183 }
184 }
185 UTIL_CHECK(counter == nFlexibleParams());
186 }
187
188 }
189
190 /*
191 * Perform the main system computation (solve the MDE).
192 */
193 template <int D>
194 void AmIteratorGrid<D>::evaluate()
195 { system().compute(isFlexible_); }
196
197 /*
198 * Compute the residual for the current system state.
199 */
200 template <int D>
201 void AmIteratorGrid<D>::getResidual(DArray<double>& resid)
202 {
203 const int nMonomer = system().mixture().nMonomer();
204 const int nMesh = system().domain().mesh().size();
205 const int n = nElements();
206 UTIL_CHECK(resid.capacity() == n);
207
208 // Local variables
209 double chi, p;
210 int i, j, k, begin;
211
212 // Initialize residual vector to zero
213 for (i = 0 ; i < n; ++i) {
214 resid[i] = 0.0;
215 }
216
217 // Compute SCF residual vector elements
218 for (i = 0; i < nMonomer; ++i) {
219 begin = i*nMesh;
220 for (j = 0; j < nMonomer; ++j) {
221 chi = interaction_.chi(i,j);
222 p = interaction_.p(i,j);
223 RField<D> const & c = system().c().rgrid(j);
224 RField<D> const & w = system().w().rgrid(j);
225 if (system().h().hasData()) {
226 RField<D> const & h = system().h().rgrid(j);
227 for (k = 0; k < nMesh; ++k) {
228 resid[begin + k] += chi*c[k] + p*(h[k] - w[k]) ;
229 }
230 } else {
231 for (k = 0; k < nMesh; ++k) {
232 resid[begin + k] += chi*c[k] - p*w[k];
233 }
234 }
235 }
236 }
237
238 // Add term proportional to sum of all concentrations
239 double shift = -1.0 / interaction_.sumChiInverse();
240 if (system().mask().hasData()) {
241 RField<D> const & mask = system().mask().rgrid();
242 for (i = 0; i < nMonomer; ++i) {
243 begin = i*nMesh;
244 for (k = 0; k < nMesh; ++k) {
245 resid[begin + k] += shift*mask[k];
246 }
247 }
248 } else {
249 for (i = 0; i < nMonomer; ++i) {
250 begin = i*nMesh;
251 for (k = 0; k < nMesh; ++k) {
252 resid[begin + k] += shift;
253 }
254 }
255 }
256
257 // If system is canonical, set all spatial averages to zero
258 if (system().mixture().isCanonical()) {
259 double average;
260 for (i = 0; i < nMonomer; ++i) {
261 begin = i*nMesh;
262 average = 0.0;
263 for (k = 0; k < nMesh; ++k) {
264 average += resid[begin + k];
265 }
266 average /= double(nMesh);
267 for (k = 0; k < nMesh; ++k) {
268 resid[begin + k] -= average;
269 }
270 }
271 }
272
273 // If flexible unit cell, then compute stress residuals
274 if (isFlexible()) {
275
276 // Combined -1 factor and stress scaling here. This is okay:
277 // - residuals only show up as dot products (U, v, norm)
278 // or with their absolute value taken (max), so the
279 // sign on a given residual vector element is not relevant
280 // as long as it is consistent across all vectors
281 // - The scaling is applied here and to the unit cell param
282 // storage, so that updating is done on the same scale,
283 // and then undone right before passing to the unit cell.
284
285 const double coeff = -1.0 * scaleStress_;
286 const int nParam = system().domain().unitCell().nParameter();
287 begin = nMonomer*nMesh;
288 int counter = 0;
289 for (i = 0; i < nParam ; i++) {
290 if (flexibleParams_[i]) {
291 resid[begin + counter] = coeff * stress(i);
292 counter++;
293 }
294 }
295 UTIL_CHECK(counter == nFlexibleParams());
296 }
297
298 }
299
300 /*
301 * Update the current system field vector.
302 */
303 template <int D>
304 void AmIteratorGrid<D>::update(DArray<double>& newState)
305 {
306
307 // References to system components
308 Domain<D> const & domain = system().domain();
309 Mesh<D> const & mesh = domain.mesh();
310 IntVec<D> const & meshDimensions = mesh.dimensions();
311
312 // Constants
313 const int nMonomer = system().mixture().nMonomer();
314 const int nMesh = mesh.size();
315
316 // Allocate wFields container
317 DArray< RField<D> > wFields;
318 wFields.allocate(nMonomer);
319 for (int i = 0; i < nMonomer; i++) {
320 wFields[i].allocate(meshDimensions);
321 UTIL_CHECK(wFields[i].capacity() == nMesh);
322 }
323
324 // Copy new fields from newState vector to wFields container
325 int begin;
326 for (int i = 0; i < nMonomer; i++) {
327 begin = i*nMesh;
328 for (int k = 0; k < nMesh; k++) {
329 wFields[i][k] = newState[begin + k];
330 }
331 }
332
333 // If canonical, explicitly set homogeneous field components
334 if (system().mixture().isCanonical()) {
335
336 // Subtract spatial average from each w field
337 double wAve;
338 for (int i = 0; i < nMonomer; ++i) {
339 wAve = Reduce::sum(wFields[i]);
340 wAve /= double(nMesh);
341 VecOp::subEqS(wFields[i], wAve);
342 }
343
344 // Compute spatial averages of all concentration fields
345 DArray<double> cAve;
346 cAve.allocate(nMonomer);
347 for (int i = 0; i < nMonomer; ++i) {
348 cAve[i] = Reduce::sum(system().c().rgrid(i));
349 cAve[i] /= double(nMesh);
350 }
351
352 // Add average value arising from interactions
353 double chi;
354 for (int i = 0; i < nMonomer; ++i) {
355 wAve = 0.0;
356 for (int j = 0; j < nMonomer; ++j) {
357 chi = interaction_.chi(i,j);
358 wAve += chi * cAve[j];
359 }
360 VecOp::addEqS(wFields[i], wAve);
361 }
362
363 // If external fields exist, add their spatial averages
364 if (system().h().hasData()) {
365 double hAve;
366 for (int i = 0; i < nMonomer; ++i) {
367 hAve = Reduce::sum(system().h().rgrid(i));
368 hAve /= double(nMesh);
369 VecOp::addEqS(wFields[i], hAve);
370 }
371 }
372 }
373
374 // Set fields in system w container
375 system().w().setRGrid(wFields);
376
377 // If flexible, update unit cell parameters
378 if (isFlexible()) {
379
380 // Initialize parameters array with current values
381 FSArray<double, 6> parameters;
382 parameters = domain.unitCell().parameters();
383
384 // Reset any parameters that are flexible
385 const int nParam = domain.unitCell().nParameter();
386 double coeff = 1.0 / scaleStress_;
387 const int begin = nMonomer*nMesh;
388 int counter = 0;
389 for (int i = 0; i < nParam; i++) {
390 if (flexibleParams_[i]) {
391 parameters[i] = coeff * newState[begin + counter];
392 counter++;
393 }
394 }
395 UTIL_CHECK(counter == nFlexibleParams());
396
397 // Set system unit cell parameters
398 system().setUnitCell(parameters);
399 }
400
401 }
402
403 /*
404 * Output relevant system details to the iteration log.
405 */
406 template<int D>
407 void AmIteratorGrid<D>::outputToLog()
408 {
409 if (isFlexible() && verbose() > 1) {
410 double str;
411 UnitCell<D> const & unitCell = system().domain().unitCell();
412 const int nParam = unitCell.nParameter();
413 const int nMonomer = system().mixture().nMonomer();
414 const int nMesh = system().domain().mesh().size();
415 const int begin = nMonomer*nMesh;
416 int counter = 0;
417 for (int i = 0; i < nParam; i++) {
418 if (flexibleParams_[i]) {
419 str = - 1.0 * residual()[begin + counter] / scaleStress_;
420 Log::file()
421 << " Cell Param " << i << " = "
422 << Dbl(unitCell.parameters()[i], 15)
423 << " , stress = "
424 << Dbl(str, 15)
425 << "\n";
426 counter++;
427 }
428 }
429 }
430 }
431
432}
433}
434#endif
void readMixingParameters(std::istream &in, bool useLambdaRamp=true)
An IntVec<D, T> is a D-component vector of elements of integer type T.
Definition IntVec.h:27
Description of a regular grid of points in a periodic domain.
Definition Mesh.h:61
Field of real double precision values on an FFT mesh.
Definition cpu/RField.h:29
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) override
Read all parameters and initialize.
void outputTimers(std::ostream &out) const override
Output timing results to log file.
ScalarParam< Type > & readOptional(std::istream &in, const char *label, Type &value)
Add and read a new optional ScalarParam < Type > object.
void setup(bool isContinuation) override
Setup iterator just before entering iteration loop.
Spatial domain for a periodic structure with real fields, on a CPU.
Base class for iterative solvers for SCF equations.
Main class, representing a complete physical system.
int capacity() const
Return allocated size.
Definition Array.h:159
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
void setClassName(const char *className)
Set class name string.
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
void addEqS(Array< double > &a, double b)
Vector addition in-place, a[i] += b.
Definition VecOp.cpp:212
void subEqS(Array< double > &a, double b)
Vector subtraction in-place, a[i] -= b.
Definition VecOp.cpp:240
Periodic fields and crystallography.
Definition CField.cpp:11
Real periodic fields, SCFT and PS-FTS (CPU).
Definition param_pc.dox:2
PSCF package top-level namespace.