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