PSCF v1.1
AmIterator.tpp
1#ifndef PSPC_AM_ITERATOR_TPP
2#define PSPC_AM_ITERATOR_TPP
3
4/*
5* PSCF - Polymer Self-Consistent Field Theory
6*
7* Copyright 2016 - 2022, The Regents of the University of Minnesota
8* Distributed under the terms of the GNU General Public License.
9*/
10
11#include "AmIterator.h"
12#include <pspc/System.h>
13#include <pscf/inter/Interaction.h>
14#include <pscf/iterator/NanException.h>
15#include <util/global.h>
16#include <cmath>
17
18namespace Pscf{
19namespace Pspc{
20
21 using namespace Util;
22
23 // Constructor
24 template <int D>
26 : Iterator<D>(system)
27 { setClassName("AmIterator"); }
28
29 // Destructor
30 template <int D>
32 { }
33
34 // Read parameters from file
35 template <int D>
36 void AmIterator<D>::readParameters(std::istream& in)
37 {
38 // Call parent class readParameters
39 AmIteratorTmpl< Iterator<D>, DArray<double> >::readParameters(in);
40 AmIteratorTmpl< Iterator<D>, DArray<double> >::readErrorType(in);
41
42 // Allocate local modified copy of Interaction class
43 interaction_.setNMonomer(system().mixture().nMonomer());
44
45 // Default parameter values
46 isFlexible_ = 1;
47 scaleStress_ = 10.0;
48
49 int np = system().unitCell().nParameter();
50 UTIL_CHECK(np > 0);
51 UTIL_CHECK(np <= 6);
52 UTIL_CHECK(system().unitCell().lattice() != UnitCell<D>::Null);
53
54 // Read optional isFlexible boolean (true by default)
55 readOptional(in, "isFlexible", isFlexible_);
56
57 // Populate flexibleParams_ based on isFlexible_ (all 0s or all 1s),
58 // then optionally overwrite with user input from param file
59 if (isFlexible_) {
60 flexibleParams_.clear();
61 for (int i = 0; i < np; i++) {
62 flexibleParams_.append(true); // Set all values to true
63 }
64 // Read optional flexibleParams_ array to overwrite current array
65 readOptionalFSArray(in, "flexibleParams", flexibleParams_, np);
66 if (nFlexibleParams() == 0) isFlexible_ = false;
67 } else { // isFlexible_ = false
68 flexibleParams_.clear();
69 for (int i = 0; i < np; i++) {
70 flexibleParams_.append(false); // Set all values to false
71 }
72 }
73
74 // Read optional scaleStress value
75 readOptional(in, "scaleStress", scaleStress_);
76 }
77
78 // Protected virtual function
79
80 // Setup before entering iteration loop
81 template <int D>
82 void AmIterator<D>::setup(bool isContinuation)
83 {
84 AmIteratorTmpl<Iterator<D>, DArray<double> >::setup(isContinuation);
85 interaction_.update(system().interaction());
86 }
87
88 // Private virtual functions used to implement AM algorithm
89
90 // Assign one array to another
91 template <int D>
93 { a = b; }
94
95 // Compute and return inner product of two vectors.
96 template <int D>
97 double AmIterator<D>::dotProduct(DArray<double> const & a,
98 DArray<double> const & b)
99 {
100 const int n = a.capacity();
101 UTIL_CHECK(b.capacity() == n);
102 double product = 0.0;
103 for (int i = 0; i < n; i++) {
104 // if either value is NaN, throw NanException
105 if (std::isnan(a[i]) || std::isnan(b[i])) {
106 throw NanException("AmIterator::dotProduct",__FILE__,__LINE__,0);
107 }
108 product += a[i] * b[i];
109 }
110 return product;
111 }
112
113 // Compute and return maximum element of a vector.
114 template <int D>
115 double AmIterator<D>::maxAbs(DArray<double> const & a)
116 {
117 const int n = a.capacity();
118 double max = 0.0;
119 double value;
120 for (int i = 0; i < n; i++) {
121 value = a[i];
122 if (std::isnan(value)) { // if value is NaN, throw NanException
123 throw NanException("AmIterator::dotProduct",__FILE__,__LINE__,0);
124 }
125 if (fabs(value) > max) {
126 max = fabs(value);
127 }
128 }
129 return max;
130 }
131
132 // Update basis
133 template <int D>
134 void
135 AmIterator<D>::updateBasis(RingBuffer< DArray<double> > & basis,
136 RingBuffer< DArray<double> > const & hists)
137 {
138 // Make sure at least two histories are stored
139 UTIL_CHECK(hists.size() >= 2);
140
141 const int n = hists[0].capacity();
142 DArray<double> newbasis;
143 newbasis.allocate(n);
144
145 // New basis vector is difference between two most recent states
146 for (int i = 0; i < n; i++) {
147 newbasis[i] = hists[0][i] - hists[1][i];
148 }
149
150 basis.append(newbasis);
151 }
152
153 template <int D>
154 void
155 AmIterator<D>::addHistories(DArray<double>& trial,
156 RingBuffer<DArray<double> > const & basis,
157 DArray<double> coeffs,
158 int nHist)
159 {
160 int n = trial.capacity();
161 for (int i = 0; i < nHist; i++) {
162 for (int j = 0; j < n; j++) {
163 // Not clear on the origin of the -1 factor
164 trial[j] += coeffs[i] * -1 * basis[i][j];
165 }
166 }
167 }
168
169 template <int D>
170 void AmIterator<D>::addPredictedError(DArray<double>& fieldTrial,
171 DArray<double> const & resTrial,
172 double lambda)
173 {
174 int n = fieldTrial.capacity();
175 for (int i = 0; i < n; i++) {
176 fieldTrial[i] += lambda * resTrial[i];
177 }
178 }
179
180 // Private virtual functions to exchange data with parent system
181
182 // Does the system have an initial field guess?
183 template <int D>
184 bool AmIterator<D>::hasInitialGuess()
185 { return system().w().hasData(); }
186
187 // Compute and return number of elements in a residual vector
188 template <int D>
189 int AmIterator<D>::nElements()
190 {
191 const int nMonomer = system().mixture().nMonomer();
192 const int nBasis = system().basis().nBasis();
193 int nEle = nMonomer*nBasis;
194
195 if (isFlexible()) {
196 nEle += nFlexibleParams();
197 }
198
199 return nEle;
200 }
201
202 // Get the current field from the system
203 template <int D>
204 void AmIterator<D>::getCurrent(DArray<double>& curr)
205 {
206 // Straighten out fields into linear arrays
207
208 const int nMonomer = system().mixture().nMonomer();
209 const int nBasis = system().basis().nBasis();
210 const DArray< DArray<double> > * currSys = &system().w().basis();
211
212 for (int i = 0; i < nMonomer; i++) {
213 for (int k = 0; k < nBasis; k++) {
214 curr[i*nBasis+k] = (*currSys)[i][k];
215 }
216 }
217
218 const int nParam = system().unitCell().nParameter();
219 const FSArray<double,6> currParam = system().unitCell().parameters();
220
221 int counter = 0;
222 for (int i = 0; i < nParam; i++) {
223 if (flexibleParams_[i]) {
224 curr[nMonomer*nBasis + counter] = scaleStress_*currParam[i];
225 counter++;
226 }
227 }
228 UTIL_CHECK(counter == nFlexibleParams());
229
230 return;
231 }
232
233 // Perform the main system computation (solve the MDE)
234 template <int D>
235 void AmIterator<D>::evaluate()
236 {
237 // Solve MDEs for current omega field
238 system().compute();
239
240 // Compute stress if required
241 if (isFlexible()) {
242 system().mixture().computeStress();
243 }
244 }
245
246 // Compute the residual for the current system state
247 template <int D>
248 void AmIterator<D>::getResidual(DArray<double>& resid)
249 {
250 const int n = nElements();
251 const int nMonomer = system().mixture().nMonomer();
252 const int nBasis = system().basis().nBasis();
253
254 // Initialize residual vector to zero
255 for (int i = 0 ; i < n; ++i) {
256 resid[i] = 0.0;
257 }
258
259 // Compute SCF residual vector elements
260 for (int i = 0; i < nMonomer; ++i) {
261 for (int j = 0; j < nMonomer; ++j) {
262 double chi = interaction_.chi(i,j);
263 double p = interaction_.p(i,j);
264 DArray<double> const & c = system().c().basis(j);
265 DArray<double> const & w = system().w().basis(j);
266 for (int k = 0; k < nBasis; ++k) {
267 int idx = i*nBasis + k;
268 resid[idx] += chi*c[k] - p*w[k];
269 }
270 }
271 }
272
273 // If iterator has mask, account for it in residual values
274 if (system().hasMask()) {
275 for (int i = 0; i < nMonomer; ++i) {
276 for (int k = 0; k < nBasis; ++k) {
277 int idx = i*nBasis + k;
278 resid[idx] -= system().mask().basis()[k] /
279 interaction_.sumChiInverse();
280 }
281 }
282 }
283
284 // If iterator has external fields, account for them in the values
285 // of the residuals
286 if (system().hasExternalFields()) {
287 for (int i = 0; i < nMonomer; ++i) {
288 for (int j = 0; j < nMonomer; ++j) {
289 for (int k = 0; k < nBasis; ++k) {
290 int idx = i*nBasis + k;
291 resid[idx] += interaction_.p(i,j) *
292 system().h().basis(j)[k];
293 }
294 }
295 }
296 }
297
298 // If not canonical, account for incompressibility
299 if (!system().mixture().isCanonical()) {
300 if (!system().hasMask()) {
301 for (int i = 0; i < nMonomer; ++i) {
302 resid[i*nBasis] -= 1.0 / interaction_.sumChiInverse();
303 }
304 }
305 } else {
306 // Explicitly set homogeneous residual components
307 for (int i = 0; i < nMonomer; ++i) {
308 resid[i*nBasis] = 0.0;
309 }
310 }
311
312 // If variable unit cell, compute stress residuals
313 if (isFlexible()) {
314 const int nParam = system().unitCell().nParameter();
315
316 // Combined -1 factor and stress scaling here. This is okay:
317 // - residuals only show up as dot products (U, v, norm)
318 // or with their absolute value taken (max), so the
319 // sign on a given residual vector element is not relevant
320 // as long as it is consistent across all vectors
321 // - The scaling is applied here and to the unit cell param
322 // storage, so that updating is done on the same scale,
323 // and then undone right before passing to the unit cell.
324
325 int counter = 0;
326 for (int i = 0; i < nParam ; i++) {
327 if (flexibleParams_[i]) {
328 resid[nMonomer*nBasis + counter] = scaleStress_ * -1
329 * system().mixture().stress(i);
330 counter++;
331 }
332 }
333 UTIL_CHECK(counter == nFlexibleParams());
334 }
335
336 }
337
338 // Update the current system field coordinates
339 template <int D>
340 void AmIterator<D>::update(DArray<double>& newGuess)
341 {
342 // Convert back to field format
343 const int nMonomer = system().mixture().nMonomer();
344 const int nBasis = system().basis().nBasis();
345
346 DArray< DArray<double> > wField;
347 wField.allocate(nMonomer);
348
349 // Restructure in format of monomers, basis functions
350 for (int i = 0; i < nMonomer; i++) {
351 wField[i].allocate(nBasis);
352 for (int k = 0; k < nBasis; k++)
353 {
354 wField[i][k] = newGuess[i*nBasis + k];
355 }
356 }
357 // If canonical, explicitly set homogeneous field components
358 if (system().mixture().isCanonical()) {
359 double chi;
360 for (int i = 0; i < nMonomer; ++i) {
361 wField[i][0] = 0.0; // initialize to 0
362 for (int j = 0; j < nMonomer; ++j) {
363 chi = interaction_.chi(i,j);
364 wField[i][0] += chi * system().c().basis(j)[0];
365 }
366 }
367 // If iterator has external fields, include them in homogeneous field
368 if (system().hasExternalFields()) {
369 for (int i = 0; i < nMonomer; ++i) {
370 wField[i][0] += system().h().basis(i)[0];
371 }
372 }
373 }
374 system().setWBasis(wField);
375
376 if (isFlexible()) {
377 const int nParam = system().unitCell().nParameter();
378 FSArray<double,6> parameters = system().unitCell().parameters();
379 int counter = 0;
380
381 for (int i = 0; i < nParam; i++) {
382 if (flexibleParams_[i]) {
383 parameters[i] = 1.0/scaleStress_ *
384 newGuess[nMonomer*nBasis + counter];
385 counter++;
386 }
387 }
388 UTIL_CHECK(counter == nFlexibleParams());
389
390 system().setUnitCell(parameters);
391 }
392
393 }
394
395 template<int D>
396 void AmIterator<D>::outputToLog()
397 {
398 if (isFlexible() && verbose() > 1) {
399 const int nParam = system().unitCell().nParameter();
400 for (int i = 0; i < nParam; i++) {
401 if (flexibleParams_[i]) {
402 Log::file()
403 << " Cell Param " << i << " = "
404 << Dbl(system().unitCell().parameters()[i], 15)
405 << " , stress = "
406 << Dbl(system().mixture().stress(i), 15)
407 << "\n";
408 }
409 }
410 }
411 }
412
413}
414}
415#endif
Template for Anderson mixing iterator algorithm.
Exception thrown when not-a-number (NaN) is encountered.
Definition: NanException.h:40
Pspc implementation of the Anderson Mixing iterator.
void setClassName(const char *className)
Set class name string.
void setup(bool isContinuation)
Setup iterator just before entering iteration loop.
Definition: AmIterator.tpp:82
AmIterator(System< D > &system)
Constructor.
Definition: AmIterator.tpp:25
void readParameters(std::istream &in)
Read all parameters and initialize.
Definition: AmIterator.tpp:36
~AmIterator()
Destructor.
Definition: AmIterator.tpp:31
Base class for iterative solvers for SCF equations.
Main class for SCFT simulation of one system.
Definition: pspc/System.h:76
Base template for UnitCell<D> classes, D=1, 2 or 3.
Definition: UnitCell.h:44
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:57
Class for storing history of previous values in an array.
Definition: RingBuffer.h:27
File containing preprocessor macros for error handling.
#define UTIL_CHECK(condition)
Assertion macro suitable for serial or parallel production code.
Definition: global.h:68
C++ namespace for polymer self-consistent field theory (PSCF).
Utility classes for scientific computation.
Definition: accumulators.mod:1
float product(float a, float b)
Product for float Data.
Definition: product.h:22