PSCF v1.3.3
rpg/scft/iterator/AmIteratorBasis.tpp
1#ifndef RPG_AM_ITERATOR_BASIS_TPP
2#define RPG_AM_ITERATOR_BASIS_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 "AmIteratorBasis.h"
12#include <rpg/system/System.h>
13#include <rpg/solvers/Mixture.h>
14#include <rpg/field/Domain.h>
15#include <prdc/crystal/Basis.h>
16#include <prdc/crystal/UnitCell.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
28 // Public member functions
29
30 /*
31 * Constructor.
32 */
33 template <int D>
35 : Iterator<D>(system)
36 {
37 isSymmetric_ = true;
38 ParamComposite::setClassName("AmIteratorBasis");
39 }
40
41 /*
42 * Destructor.
43 */
44 template <int D>
47
48 /*
49 * Read parameter file block.
50 */
51 template <int D>
53 {
54 // Call parent class readParameters
57
58 // Read optional isFlexible value
59 isFlexible_ = 1; // default value
60 readOptional(in, "isFlexible", isFlexible_);
61
62 // Get and check the number of unit cell parameters
63 int np = system().domain().unitCell().nParameter();
64 UTIL_CHECK(np > 0);
65 UTIL_CHECK(np <= 6);
66 UTIL_CHECK(system().domain().unitCell().lattice()
68
69 // Populate flexibleParams_ based on isFlexible_ (all 0s or all 1s),
70 // then optionally overwrite with user input from param file
71 if (isFlexible_) {
72 flexibleParams_.clear();
73 for (int i = 0; i < np; i++) {
74 flexibleParams_.append(true); // Set all values to true
75 }
76 // Read optional flexibleParams_ array to overwrite current array
77 readOptionalFSArray(in, "flexibleParams", flexibleParams_, np);
78 if (nFlexibleParams() == 0) isFlexible_ = false;
79 } else { // isFlexible_ = false
80 flexibleParams_.clear();
81 for (int i = 0; i < np; i++) {
82 flexibleParams_.append(false); // Set all values to false
83 }
84 }
85
86 // Read optional scaleStress value
87 scaleStress_ = 10.0; // default value
88 readOptional(in, "scaleStress", scaleStress_);
89
90 // Optionally read mixing parameters (lambda, useLambdaRamp, r)
92
93 // Optionally read mixing parameters (lambda, useLambdaRamp, r)
95
96 // Allocate local modified copy of Interaction class
97 interaction_.setNMonomer(system().mixture().nMonomer());
98 }
99
100 /*
101 * Output timing results to log file.
102 */
103 template<int D>
104 void AmIteratorBasis<D>::outputTimers(std::ostream& out) const
105 {
106 // Output timing results, if requested.
107 out << "\n";
108 out << "Iterator times contributions:\n";
110 }
111
112 // Protected virtual function
113
114 // Setup before entering iteration loop
115 template <int D>
116 void AmIteratorBasis<D>::setup(bool isContinuation)
117 {
118 // Call parent setup method
119 AmTmpl::setup(isContinuation);
120
121 // Update chi matrix and related properties in member interaction_
122 interaction_.update(system().interaction());
123 }
124
125 // Private virtual functions that interact with parent system
126
127 /*
128 * Compute the number of elements in the residual vector.
129 */
130 template <int D>
131 int AmIteratorBasis<D>::nElements()
132 {
133 const int nMonomer = system().mixture().nMonomer();
134 const int nBasis = system().domain().basis().nBasis();
135
136 int nEle = nMonomer*nBasis;
137
138 if (isFlexible_) {
139 nEle += nFlexibleParams();
140 }
141
142 return nEle;
143 }
144
145 /*
146 * Does the system have an initial field guess?
147 */
148 template <int D>
149 bool AmIteratorBasis<D>::hasInitialGuess()
150 { return system().w().hasData(); }
151
152 /*
153 * Get the current w fields and lattice parameters.
154 */
155 template <int D>
156 void AmIteratorBasis<D>::getCurrent(DArray<double>& curr)
157 {
158 // Straighten out fields into linear arrays
159
160 const int nMonomer = system().mixture().nMonomer();
161 const int nBasis = system().domain().basis().nBasis();
162 const DArray< DArray<double> > & currSys = system().w().basis();
163
164 for (int i = 0; i < nMonomer; i++) {
165 for (int k = 0; k < nBasis; k++) {
166 curr[i*nBasis+k] = currSys[i][k];
167 }
168 }
169
170 if (isFlexible_) {
171 const int begin = nMonomer*nBasis;
172 const int nParam = system().domain().unitCell().nParameter();
173 FSArray<double,6> const & parameters
174 = system().domain().unitCell().parameters();
175 int counter = 0;
176 for (int i = 0; i < nParam; i++) {
177 if (flexibleParams_[i]) {
178 curr[begin + counter] = scaleStress_ * parameters[i];
179 counter++;
180 }
181 }
182 UTIL_CHECK(counter == nFlexibleParams());
183 }
184
185 }
186
187 /*
188 * Perform the main system computation (solve the MDE).
189 */
190 template <int D>
191 void AmIteratorBasis<D>::evaluate()
192 {
193 // Solve MDEs for current omega field
194 // (computes stress if isFlexible_ == true)
195 system().compute(isFlexible_);
196 }
197
198 /*
199 * Compute the residual for the current system state.
200 */
201 template <int D>
202 void AmIteratorBasis<D>::getResidual(DArray<double>& resid)
203 {
204 UTIL_CHECK(system().domain().basis().isInitialized());
205 const int nMonomer = system().mixture().nMonomer();
206 const int nBasis = system().domain().basis().nBasis();
207 const int n = nElements();
208
209 // Initialize residuals
210 for (int i = 0 ; i < n; ++i) {
211 resid[i] = 0.0;
212 }
213
214 // Compute SCF residual vector elements
215 for (int i = 0; i < nMonomer; ++i) {
216 for (int j = 0; j < nMonomer; ++j) {
217 double chi = interaction_.chi(i,j);
218 double p = interaction_.p(i,j);
219 DArray<double> const & c = system().c().basis(j);
220 DArray<double> const & w = system().w().basis(j);
221 for (int k = 0; k < nBasis; ++k) {
222 int idx = i*nBasis + k;
223 resid[idx] += chi*c[k] - p*w[k];
224 }
225 }
226 }
227
228 // If iterator has mask, account for it in residual values
229 if (system().mask().hasData()) {
230 DArray<double> const & mask = system().mask().basis();
231 double sumChiInv = interaction_.sumChiInverse();
232 for (int i = 0; i < nMonomer; ++i) {
233 for (int k = 0; k < nBasis; ++k) {
234 int idx = i*nBasis + k;
235 resid[idx] -= mask[k] / sumChiInv;
236 }
237 }
238 }
239
240 // If iterator has external fields, account for them in the values
241 // of the residuals
242 if (system().h().hasData()) {
243 for (int i = 0; i < nMonomer; ++i) {
244 for (int j = 0; j < nMonomer; ++j) {
245 double p = interaction_.p(i,j);
246 DArray<double> const & h = system().h().basis(j);
247 for (int k = 0; k < nBasis; ++k) {
248 int idx = i*nBasis + k;
249 resid[idx] += p * h[k];
250 }
251 }
252 }
253 }
254
255 // If not canonical, account for incompressibility
256 if (!system().mixture().isCanonical()) {
257 for (int i = 0; i < nMonomer; ++i) {
258 resid[i*nBasis] -= 1.0 / interaction_.sumChiInverse();
259 }
260 } else {
261 // Explicitly set homogeneous residual components
262 for (int i = 0; i < nMonomer; ++i) {
263 resid[i*nBasis] = 0.0;
264 }
265 }
266
267 // If variable unit cell, compute stress residuals
268 if (isFlexible_) {
269 const int nParam = system().domain().unitCell().nParameter();
270
271 // Note:
272 // Combined -1 factor and stress scaling here. This is okay:
273 // - Residuals only show up as dot products (U, v, norm)
274 // or with their absolute value taken (max), so the
275 // sign on a given residual vector element is not relevant
276 // as long as it is consistent across all vectors
277 // - The scaling is applied here and to the unit cell param
278 // storage, so that updating is done on the same scale,
279 // and then undone right before passing to the unit cell.
280
281 int counter = 0;
282 for (int i = 0; i < nParam ; i++) {
283 if (flexibleParams_[i]) {
284 double str = stress(i);
285
286 resid[nMonomer*nBasis + counter] = -1 * scaleStress_ * str;
287 counter++;
288 }
289 }
290 UTIL_CHECK(counter == nFlexibleParams());
291 }
292
293 }
294
295 /*
296 * Update the current system field coordinates.
297 */
298 template <int D>
299 void AmIteratorBasis<D>::update(DArray<double>& newGuess)
300 {
301 UTIL_CHECK(system().domain().basis().isInitialized());
302 const int nMonomer = system().mixture().nMonomer();
303 const int nBasis = system().domain().basis().nBasis();
304
305 DArray< DArray<double> > wField;
306 wField.allocate(nMonomer);
307
308 // Restructure in format of monomers, basis functions
309 for (int i = 0; i < nMonomer; i++) {
310 wField[i].allocate(nBasis);
311 for (int k = 0; k < nBasis; k++) {
312 wField[i][k] = newGuess[i*nBasis + k];
313 }
314 }
315
316 // If canonical, explicitly set homogeneous field components
317 if (system().mixture().isCanonical()) {
318 double chi;
319 for (int i = 0; i < nMonomer; ++i) {
320 wField[i][0] = 0.0; // initialize to 0
321 for (int j = 0; j < nMonomer; ++j) {
322 chi = interaction_.chi(i,j);
323 wField[i][0] += chi * system().c().basis(j)[0];
324 }
325 }
326 // If system has external fields, include them in homogeneous part
327 if (system().h().hasData()) {
328 for (int i = 0; i < nMonomer; ++i) {
329 wField[i][0] += system().h().basis(i)[0];
330 }
331 }
332 }
333 system().w().setBasis(wField);
334
335 if (isFlexible_) {
336 const int nParam = system().domain().unitCell().nParameter();
337 const int begin = nMonomer*nBasis;
338
339 FSArray<double,6> parameters;
340 parameters = system().domain().unitCell().parameters();
341
342 const double coeff = 1.0 / scaleStress_;
343 int counter = 0;
344 for (int i = 0; i < nParam; i++) {
345 if (flexibleParams_[i]) {
346 parameters[i] = coeff * newGuess[begin + counter];
347 counter++;
348 }
349 }
350 UTIL_CHECK(counter == nFlexibleParams());
351
352 system().setUnitCell(parameters);
353 }
354 }
355
356 /*
357 * Output relevant system details to the iteration log file.
358 */
359 template<int D>
360 void AmIteratorBasis<D>::outputToLog()
361 {
362 if (isFlexible_ && verbose() > 1) {
363 const int nParam = system().domain().unitCell().nParameter();
364 const int nMonomer = system().mixture().nMonomer();
365 const int nBasis = system().domain().basis().nBasis();
366 int counter = 0;
367 for (int i = 0; i < nParam; i++) {
368 if (flexibleParams_[i]) {
369 double str = residual()[nMonomer*nBasis + counter] /
370 (-1.0 * scaleStress_);
371 Log::file()
372 << " Cell Param " << i << " = "
373 << Dbl(system().domain().unitCell().parameters()[i], 15)
374 << " , stress = "
375 << Dbl(str, 15)
376 << "\n";
377 counter++;
378 }
379 }
380 }
381 }
382
383 #if 0
384 // Private virtual functions for vector math
385
386 /*
387 * Set a vector equal to another (assign a = b).
388 */
389 template <int D>
390 void AmIteratorBasis<D>::setEqual(DArray<double>& a,
391 DArray<double> const & b)
392 { a = b; }
393
394 /*
395 * Compute the inner product of two real vectors.
396 */
397 template <int D>
398 double AmIteratorBasis<D>::dotProduct(DArray<double> const & a,
399 DArray<double> const & b)
400 {
401 const int n = a.capacity();
402 UTIL_CHECK(b.capacity() == n);
403 double product = 0.0;
404 for (int i=0; i < n; ++i) {
405 // if either value is NaN, throw NanException
406 if (std::isnan(a[i]) || std::isnan(b[i])) {
407 throw NanException("AmIteratorBasis::dotProduct", __FILE__,
408 __LINE__, 0);
409 }
410 product += a[i] * b[i];
411 }
412 return product;
413 }
414
415 /*
416 * Compute and return maximum element of residual vector.
417 */
418 template <int D>
419 double AmIteratorBasis<D>::maxAbs(DArray<double> const & a)
420 {
421 const int n = a.capacity();
422 double max = 0.0;
423 double value;
424 for (int i = 0; i < n; i++) {
425 value = a[i];
426 if (std::isnan(value)) { // if value is NaN, throw NanException
427 throw NanException("AmIteratorBasis::dotProduct", __FILE__,
428 __LINE__, 0);
429 }
430 if (fabs(value) > max)
431 max = fabs(value);
432 }
433 return max;
434 }
435
436 /*
437 * Compute the vector difference a = b - c
438 */
439 template <int D>
440 void AmIteratorBasis<D>::subVV(DArray<double>& a,
441 DArray<double> const & b,
442 DArray<double> const & c)
443 {
444 const int n = a.capacity();
445 UTIL_CHECK(n == b.capacity());
446 UTIL_CHECK(n == c.capacity());
447 for (int i = 0; i < n; i++) {
448 a[i] = b[i] - c[i];
449 }
450 }
451
452 /*
453 * Composite a += b*c for vectors a and b, scalar c
454 */
455 template <int D>
456 void AmIteratorBasis<D>::addEqVc(DArray<double>& a,
457 DArray<double> const & b,
458 double c)
459 {
460 const int n = a.capacity();
461 UTIL_CHECK(n == b.capacity());
462 for (int i = 0; i < n; i++) {
463 a[i] += c*b[i];
464 }
465 }
466 #endif
467
468}
469}
470#endif
void readMixingParameters(std::istream &in, bool useLambdaRamp=true)
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.
ScalarParam< Type > & readOptional(std::istream &in, const char *label, Type &value)
Add and read a new optional ScalarParam < Type > object.
AmIteratorBasis(System< D > &system)
Constructor.
void readParameters(std::istream &in)
Read all parameters and initialize.
void outputTimers(std::ostream &out) const
Output timing results to log file.
void setup(bool isContinuation)
Setup iterator just before entering iteration loop.
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 max(Array< double > const &in)
Get maximum of array elements .
Definition Reduce.cpp:20
Periodic fields and crystallography.
Definition CField.cpp:11
SCFT and PS-FTS with real periodic fields (GPU)
PSCF package top-level namespace.
float product(float a, float b)
Product for float Data.
Definition product.h:22