PSCF v1.1
AmIteratorBasis.tpp
1#ifndef PSPG_AM_ITERATOR_BASIS_TPP
2#define PSPG_AM_ITERATOR_BASIS_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 "AmIteratorBasis.h"
12#include <pspg/System.h>
13#include <pscf/inter/Interaction.h>
14#include <pscf/iterator/NanException.h>
15#include <pspg/field/RDField.h>
16#include <util/global.h>
17
18namespace Pscf {
19namespace Pspg{
20
21 using namespace Util;
22
23 // Constructor
24 template <int D>
26 : Iterator<D>(system)
27 { setClassName("AmIteratorBasis"); }
28
29 // Destructor
30 template <int D>
32 {}
33
34 // Read parameter file block
35 template <int D>
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 // Read in additional parameters
50 readOptional(in, "isFlexible", isFlexible_);
51 readOptional(in, "scaleStress", scaleStress_);
52 }
53
54 // -- Protected virtual function -- //
55
56 // Setup before entering iteration loop
57 template <int D>
58 void AmIteratorBasis<D>::setup(bool isContinuation)
59 {
60 // Setup by AM algorithm
61 AmIteratorTmpl<Iterator<D>, DArray<double> >::setup(isContinuation);
62
63 // Update chi matrix and related properties in member interaction_
64 interaction_.update(system().interaction());
65 }
66
67
68 // -- Private virtual functions used to implement AM algorithm -- //
69
70 template <int D>
72 DArray<double> const & b)
73 { a = b; }
74
75 template <int D>
76 double AmIteratorBasis<D>::dotProduct(DArray<double> const & a,
77 DArray<double> const & b)
78 {
79 const int n = a.capacity();
80 UTIL_CHECK(b.capacity() == n);
81 double product = 0.0;
82 for (int i=0; i < n; ++i) {
83 // if either value is NaN, throw NanException
84 if (std::isnan(a[i]) || std::isnan(b[i])) {
85 throw NanException("AmIteratorBasis::dotProduct", __FILE__,
86 __LINE__, 0);
87 }
88 product += a[i] * b[i];
89 }
90 return product;
91 }
92
93 // Compute and return maximum element of residual vector.
94 template <int D>
95 double AmIteratorBasis<D>::maxAbs(DArray<double> const & a)
96 {
97 const int n = a.capacity();
98 double max = 0.0;
99 double value;
100 for (int i = 0; i < n; i++) {
101 value = a[i];
102 if (std::isnan(value)) { // if value is NaN, throw NanException
103 throw NanException("AmIteratorBasis::dotProduct", __FILE__,
104 __LINE__, 0);
105 }
106 if (fabs(value) > max)
107 max = fabs(value);
108 }
109 return max;
110 }
111
112 #if 0
113 template <int D>
115 {
116 const int n = a.capacity();
117 double data;
118 double normSq = 0.0;
119 for (int i=0; i < n; ++i) {
120 data = a[i];
121 normSq += data*data;
122 }
123 return sqrt(normSq);
124 }
125
126 // Compute one element of U matrix of by computing a dot product
127 template <int D>
128 double
129 AmIteratorBasis<D>::computeUDotProd(RingBuffer<DArray<double> > const & resBasis,
130 int m, int n)
131 {
132 const int length = resBasis[0].capacity();
133
134 double dotprod = 0.0;
135 for(int i = 0; i < length; i++) {
136 dotprod += resBasis[m][i] * resBasis[n][i];
137 }
138
139 return dotprod;
140 }
141
142 // Compute one element of V vector by computing a dot product
143 template <int D>
144 double
145 AmIteratorBasis<D>::computeVDotProd(DArray<double> const & resCurrent,
146 RingBuffer<DArray<double> > const & resBasis,
147 int m)
148 {
149 const int length = resBasis[0].capacity();
150
151 double dotprod = 0.0;
152 for(int i = 0; i < length; i++) {
153 dotprod += resCurrent[i] * resBasis[m][i];
154 }
155
156 return dotprod;
157 }
158
159 // Update entire U matrix
160 template <int D>
161 void AmIteratorBasis<D>::updateU(DMatrix<double> & U,
162 RingBuffer<DArray<double> > const & resBasis,
163 int nHist)
164 {
165 // Update matrix U by shifting elements diagonally
166 int maxHist = U.capacity1();
167 for (int m = maxHist-1; m > 0; --m) {
168 for (int n = maxHist-1; n > 0; --n) {
169 U(m,n) = U(m-1,n-1);
170 }
171 }
172
173 // Compute U matrix's new row 0 and col 0
174 for (int m = 0; m < nHist; ++m) {
175 double dotprod = computeUDotProd(resBasis,0,m);
176 U(m,0) = dotprod;
177 U(0,m) = dotprod;
178 }
179 }
180
181 template <int D>
182 void AmIteratorBasis<D>::updateV(DArray<double> & v,
183 DArray<double> const & resCurrent,
184 RingBuffer<DArray<double> > const & resBasis,
185 int nHist)
186 {
187 // Compute U matrix's new row 0 and col 0
188 // Also, compute each element of v_ vector
189 for (int m = 0; m < nHist; ++m) {
190 v[m] = computeVDotProd(resCurrent,resBasis,m);
191 }
192 }
193 #endif
194
195 // Update basis
196 template <int D>
197 void
198 AmIteratorBasis<D>::updateBasis(RingBuffer<DArray<double> > & basis,
199 RingBuffer<DArray<double> > const& hists)
200 {
201 // Make sure at least two histories are stored
202 UTIL_CHECK(hists.size() >= 2);
203
204 const int n = hists[0].capacity();
205 DArray<double> newbasis;
206 newbasis.allocate(n);
207
208 for (int i = 0; i < n; i++) {
209 // sequential histories basis vectors
210 newbasis[i] = hists[0][i] - hists[1][i];
211 }
212
213 basis.append(newbasis);
214 }
215
216 template <int D>
217 void
218 AmIteratorBasis<D>::addHistories(DArray<double>& trial,
219 RingBuffer<DArray<double> > const & basis,
220 DArray<double> coeffs,
221 int nHist)
222 {
223 int n = trial.capacity();
224 for (int i = 0; i < nHist; i++) {
225 for (int j = 0; j < n; j++) {
226 // Not clear on the origin of the -1 factor
227 trial[j] += coeffs[i] * -1 * basis[i][j];
228 }
229 }
230 }
231
232 template <int D>
233 void AmIteratorBasis<D>::addPredictedError(DArray<double>& fieldTrial,
234 DArray<double> const & resTrial,
235 double lambda)
236 {
237 int n = fieldTrial.capacity();
238 for (int i = 0; i < n; i++) {
239 fieldTrial[i] += lambda * resTrial[i];
240 }
241 }
242
243 // Does the system have an initial field guess?
244 template <int D>
245 bool AmIteratorBasis<D>::hasInitialGuess()
246 {
247 return system().w().hasData();
248 }
249
250 // Compute and return the number of elements in a field vector
251 template <int D>
252 int AmIteratorBasis<D>::nElements()
253 {
254 const int nMonomer = system().mixture().nMonomer();
255 const int nBasis = system().basis().nBasis();
256
257 int nEle = nMonomer*nBasis;
258
259 if (isFlexible_) {
260 nEle += system().unitCell().nParameter();
261 }
262
263 return nEle;
264 }
265
266 // Get the current field from the system
267 template <int D>
268 void AmIteratorBasis<D>::getCurrent(DArray<double>& curr)
269 {
270 // Straighten out fields into linear arrays
271
272 const int nMonomer = system().mixture().nMonomer();
273 const int nBasis = system().basis().nBasis();
274 const DArray< DArray<double> > * currSys = &system().w().basis();
275
276 for (int i = 0; i < nMonomer; i++) {
277 for (int k = 0; k < nBasis; k++) {
278 curr[i*nBasis+k] = (*currSys)[i][k];
279 }
280 }
281
282 if (isFlexible_) {
283 const int begin = nMonomer*nBasis;
284 const int nParam = system().unitCell().nParameter();
285 FSArray<double,6> const & parameters
286 = system().unitCell().parameters();
287 for (int i = 0; i < nParam; i++) {
288 curr[begin + i] = scaleStress_ * parameters[i];
289 }
290 }
291
292 }
293
294 // Perform the main system computation (solve the MDE)
295 template <int D>
296 void AmIteratorBasis<D>::evaluate()
297 {
298 // Solve MDEs for current omega field
299 system().compute();
300
301 // If flexible, compute stress
302 if (isFlexible_) {
303 system().mixture().computeStress(system().domain().waveList());
304 }
305 }
306
307 // Compute the residual for the current system state
308 template <int D>
309 void AmIteratorBasis<D>::getResidual(DArray<double>& resid)
310 {
311 UTIL_CHECK(system().basis().isInitialized());
312 const int nMonomer = system().mixture().nMonomer();
313 const int nBasis = system().basis().nBasis();
314 const int n = nElements();
315
316 // Initialize residuals
317 for (int i = 0 ; i < n; ++i) {
318 resid[i] = 0.0;
319 }
320
321 // Compute SCF residual vector elements
322 for (int i = 0; i < nMonomer; ++i) {
323 for (int j = 0; j < nMonomer; ++j) {
324 for (int k = 0; k < nBasis; ++k) {
325 int idx = i*nBasis + k;
326 resid[idx] +=
327 interaction_.chi(i,j)*system().c().basis(j)[k] -
328 interaction_.p(i,j)*system().w().basis(j)[k];
329 }
330 }
331 }
332
333 // If not canonical, account for incompressibility
334 if (!system().mixture().isCanonical()) {
335 for (int i = 0; i < nMonomer; ++i) {
336 resid[i*nBasis] -= 1.0/interaction_.sumChiInverse();
337 }
338 } else {
339 // Explicitly set homogeneous residual components
340 for (int i = 0; i < nMonomer; ++i) {
341 resid[i*nBasis] = 0.0;
342 }
343 }
344
345 // If variable unit cell, compute stress residuals
346 if (isFlexible_) {
347 const int nParam = system().unitCell().nParameter();
348 for (int i = 0; i < nParam ; i++) {
349 resid[nMonomer*nBasis + i] = -1.0 * scaleStress_
350 * system().mixture().stress(i);
351 }
352
353 // Note:
354 // Combined -1 factor and stress scaling here. This is okay:
355 // - Residuals only show up as dot products (U, v, norm)
356 // or with their absolute value taken (max), so the
357 // sign on a given residual vector element is not relevant
358 // as long as it is consistent across all vectors
359 // - The scaling is applied here and to the unit cell param
360 // storage, so that updating is done on the same scale,
361 // and then undone right before passing to the unit cell.
362 }
363
364 }
365
366 // Update the current system field coordinates
367 template <int D>
368 void AmIteratorBasis<D>::update(DArray<double>& newGuess)
369 {
370 UTIL_CHECK(system().basis().isInitialized());
371 const int nMonomer = system().mixture().nMonomer();
372 const int nBasis = system().basis().nBasis();
373
374 DArray< DArray<double> > wField;
375 wField.allocate(nMonomer);
376
377 // Restructure in format of monomers, basis functions
378 for (int i = 0; i < nMonomer; i++) {
379 wField[i].allocate(nBasis);
380 for (int k = 0; k < nBasis; k++) {
381 wField[i][k] = newGuess[i*nBasis + k];
382 }
383 }
384
385 // If canonical, explicitly set homogeneous field components
386 if (system().mixture().isCanonical()) {
387 for (int i = 0; i < nMonomer; ++i) {
388 wField[i][0] = 0.0; // initialize to 0
389 for (int j = 0; j < nMonomer; ++j) {
390 wField[i][0] +=
391 interaction_.chi(i,j) * system().c().basis(j)[0];
392 }
393 }
394 }
395 system().setWBasis(wField);
396
397 if (isFlexible_) {
398 const int nParam = system().unitCell().nParameter();
399 const int begin = nMonomer*nBasis;
400 FSArray<double,6> parameters;
401 double value;
402 for (int i = 0; i < nParam; i++) {
403 value = newGuess[begin + i]/scaleStress_;
404 parameters.append(value);
405 }
406 system().setUnitCell(parameters);
407 }
408
409 }
410
411 template<int D>
412 void AmIteratorBasis<D>::outputToLog()
413 {
414 if (isFlexible_) {
415 const int nParam = system().unitCell().nParameter();
416 for (int i = 0; i < nParam; i++) {
417 Log::file() << "Parameter " << i << " = "
418 << Dbl(system().unitCell().parameters()[i])
419 << "\n";
420 }
421 }
422 }
423
424}
425}
426#endif
Template for Anderson mixing iterator algorithm.
virtual double norm(DArray< double > const &hist)
Find the L2 norm of a vector.
Exception thrown when not-a-number (NaN) is encountered.
Definition: NanException.h:40
Pspg implementation of the Anderson Mixing iterator.
void setup(bool isContinuation)
Setup iterator just before entering iteration loop.
void readParameters(std::istream &in)
Read all parameters and initialize.
AmIteratorBasis(System< D > &system)
Constructor.
Base class for iterative solvers for SCF equations.
Main class in SCFT simulation of one system.
Definition: pspg/System.h:71
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
Dynamically allocated Matrix.
Definition: DMatrix.h:25
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
void append(Data const &data)
Append data to the end of the array.
Definition: FSArray.h:258
static std::ostream & file()
Get log ostream by reference.
Definition: Log.cpp:57
int capacity1() const
Get number of rows (range of the first array index).
Definition: Matrix.h:136
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