PSCF v1.4.0
AmIteratorBasis.tpp
1#ifndef RP_AM_ITERATOR_BASIS_TPP
2#define RP_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 <prdc/crystal/Basis.h>
13#include <prdc/crystal/UnitCell.h>
14#include <pscf/interaction/Interaction.h>
15#include <pscf/iterator/NanException.h>
16#include <util/containers/DArray.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 AmIteratorBasis<D,T>::AmIteratorBasis(typename T::System& system)
35 : AmIterTmplT(),
36 interaction_(),
37 scaleStress_(10.0)
38 {
39 IteratorT::setSystem(system);
40 IteratorT::isSymmetric_ = true;
41 ParamComposite::setClassName("AmIteratorBasis");
42 }
43
44 /*
45 * Destructor.
46 */
47 template <int D, class T>
50
51 /*
52 * Read parameters from file.
53 */
54 template <int D, class T>
56 {
57 // Use base class methods to read parameters
60
61 UnitCell<D> const & unitCell = system().domain().unitCell();
62 UTIL_CHECK(unitCell.lattice() != UnitCell<D>::Null);
63 int np = unitCell.nParameter();
64 UTIL_CHECK(np > 0);
65 UTIL_CHECK(np <= 6);
66
67 // Read optional isFlexible boolean (true by default)
68 IteratorT::isFlexible_ = 1; // Default
70 IteratorT::isFlexible_);
71
72 // Populate flexibleParams_ bool array
73 if (IteratorT::isFlexible_) {
74 flexibleParams_.clear();
75 // Set all flexibleParams_ values to true by default
76 for (int i = 0; i < np; i++) {
77 flexibleParams_.append(true);
78 }
79 // Optionally read flexibleParams_ array
80 ParamComposite::readOptionalFSArray(in, "flexibleParams",
81 flexibleParams_, np);
82 if (IteratorT::nFlexibleParams() == 0) {
83 IteratorT::isFlexible_ = false;
84 }
85 } else { // If isFlexible_ == false
86 // Set all flexibleParams_ values to false
87 flexibleParams_.clear();
88 for (int i = 0; i < np; i++) {
89 flexibleParams_.append(false);
90 }
91 }
92
93 // Optionally read scaleStress value
94 scaleStress_ = 10.0; // Default value
95 ParamComposite::readOptional(in, "scaleStress", scaleStress_);
96
97 // Read optional mixing parameters (lambda, useLambdaRamp, and r)
98 AmIterTmplT::readMixingParameters(in);
99
100 // Allocate local AmbdInteraction class
101 const int nMonomer = system().mixture().nMonomer();
102 interaction_.setNMonomer(nMonomer);
103
104 }
105
106 /*
107 * Output timing results to log file.
108 */
109 template <int D, class T>
110 void AmIteratorBasis<D,T>::outputTimers(std::ostream& out) const
111 {
112 out << "\n";
113 out << "Iterator times contributions:\n";
115 }
116
117 // Protected virtual function
118
119 // Setup before entering iteration loop
120 template <int D, class T>
121 void AmIteratorBasis<D,T>::setup(bool isContinuation)
122 {
123 AmIterTmplT::setup(isContinuation);
124 interaction_.update(system().interaction());
125 }
126
127 // Private virtual functions
128
129 /*
130 * Compute and return number of elements in a residual vector.
131 */
132 template <int D, class T>
133 int AmIteratorBasis<D,T>::nElements()
134 {
135 const int nMonomer = system().mixture().nMonomer();
136 const int nBasis = system().domain().basis().nBasis();
137
138 int nEle = nMonomer*nBasis;
139 if (IteratorT::isFlexible_) {
140 nEle += IteratorT::nFlexibleParams();
141 }
142 return nEle;
143 }
144
145 /*
146 * Does the system have an initial field guess?
147 */
148 template <int D, class T>
149 bool AmIteratorBasis<D,T>::hasInitialGuess()
150 { return system().w().hasData(); }
151
152 /*
153 * Get the current state (w fields and lattice parameters).
154 */
155 template <int D, class T>
156 void AmIteratorBasis<D,T>::getCurrent(VectorT& state)
157 {
158 const int nMonomer = system().mixture().nMonomer();
159 const int nBasis = system().domain().basis().nBasis();
160 const int nEle = nElements();
161 UTIL_CHECK(state.capacity() == nEle);
162
163 // Copy all w-fields into a linear array
164 int begin;
165 for (int i = 0; i < nMonomer; i++) {
166 DArray<double> const & field = system().w().basis(i);
167 begin = i*nBasis;
168 for (int k = 0; k < nBasis; k++) {
169 state[begin + k] = field[k];
170 }
171 }
172
173 // Add elements associated with unit cell parameters (if any)
174 if (IteratorT::isFlexible_) {
175 UTIL_CHECK(IteratorT::nFlexibleParams() > 0);
176 UnitCell<D> const & unitCell = system().domain().unitCell();
177 FSArray<double,6> const & parameters = unitCell.parameters();
178 const int nParam = unitCell.nParameter();
179 begin = nMonomer*nBasis;
180 int counter = 0;
181 for (int i = 0; i < nParam; i++) {
182 if (flexibleParams_[i]) {
183 state[begin + counter] = scaleStress_ * parameters[i];
184 counter++;
185 }
186 }
187 UTIL_CHECK(counter == IteratorT::nFlexibleParams());
188 }
189
190 }
191
192 /*
193 * Perform the main system computation (solve the MDE).
194 */
195 template <int D, class T>
196 void AmIteratorBasis<D,T>::evaluate()
197 { system().compute(IteratorT::isFlexible_); }
198
199 /*
200 * Compute the residual for the current system state.
201 */
202 template <int D, class T>
203 void AmIteratorBasis<D,T>::getResidual(VectorT& resid)
204 {
205 const int nMonomer = system().mixture().nMonomer();
206 const int nBasis = system().domain().basis().nBasis();
207 const int n = nElements();
208 UTIL_CHECK(resid.capacity() == n);
209
210 // Local variables
211 double chi, p;
212 int i, j, k, begin;
213
214 // Initialize residual vector to zero
215 for (i = 0 ; i < n; ++i) {
216 resid[i] = 0.0;
217 }
218
219 // Compute SCF residual vector elements
220 for (i = 0; i < nMonomer; ++i) {
221 begin = i*nBasis;
222 for (j = 0; j < nMonomer; ++j) {
223 chi = interaction_.chi(i,j);
224 p = interaction_.p(i,j);
225 DArray<double> const & c = system().c().basis(j);
226 DArray<double> const & w = system().w().basis(j);
227 if (system().h().hasData()) {
228 DArray<double> const & h = system().h().basis(j);
229 for (k = 0; k < nBasis; ++k) {
230 resid[begin + k] += chi*c[k] + p*(h[k] - w[k]);
231 }
232 } else {
233 for (k = 0; k < nBasis; ++k) {
234 resid[begin + k] += chi*c[k] - p*w[k];
235 }
236 }
237 }
238 }
239
240 // Add term proportional to sum of all concentrations
241 double shift = -1.0 / interaction_.sumChiInverse();
242 if (system().mask().hasData()) {
243 DArray<double> const & mask = system().mask().basis();
244 for (i = 0; i < nMonomer; ++i) {
245 begin = i*nBasis;
246 for (k = 0; k < nBasis; ++k) {
247 resid[begin + k] += shift*mask[k];
248 }
249 }
250 } else {
251 for (i = 0; i < nMonomer; ++i) {
252 resid[i*nBasis] += shift;
253 }
254 }
255
256 // If canonical ensemble, zero homogeneous residual components
257 if (system().mixture().isCanonical()) {
258 for (i = 0; i < nMonomer; ++i) {
259 resid[i*nBasis] = 0.0;
260 }
261 }
262
263 // If flexible unit cell, then compute stress residuals
264 if (IteratorT::isFlexible_) {
265
266 // Combined -1 factor and stress scaling here. This is okay:
267 // - residuals only show up as dot products (U, v, norm)
268 // or with their absolute value taken (max), so the
269 // sign on a given residual vector element is not relevant
270 // as long as it is consistent across all vectors
271 // - The scaling is applied here and to the unit cell param
272 // storage, so that updating is done on the same scale,
273 // and then undone right before passing to the unit cell.
274
275 double coeff = -1.0 * scaleStress_;
276 const int nParam = system().domain().unitCell().nParameter();
277 begin = nMonomer*nBasis;
278 int counter = 0;
279 for (i = 0; i < nParam ; i++) {
280 if (flexibleParams_[i]) {
281 resid[begin + counter] = coeff * IteratorT::stress(i);
282 counter++;
283 }
284 }
285 UTIL_CHECK(counter == IteratorT::nFlexibleParams());
286 }
287
288 }
289
290 /*
291 * Update the current system field vector.
292 */
293 template <int D, class T>
294 void AmIteratorBasis<D,T>::update(VectorT& newState)
295 {
296 // Constants
297 const int nMonomer = system().mixture().nMonomer();
298 const int nBasis = system().domain().basis().nBasis();
299
300 // Allocate wFields container
301 DArray< DArray<double> > wFields;
302 wFields.allocate(nMonomer);
303 for (int i = 0; i < nMonomer; i++) {
304 wFields[i].allocate(nBasis);
305 }
306
307 // Copy w fields from newState to wFields container
308 int begin;
309 for (int i = 0; i < nMonomer; i++) {
310 begin = i*nBasis;
311 for (int k = 0; k < nBasis; k++) {
312 wFields[i][k] = newState[begin + k];
313 }
314 }
315
316 // If canonical, explicitly set homogeneous field components
317 if (system().mixture().isCanonical()) {
318
319 // Set homogeneous components of all w fields to zero
320 for (int i = 0; i < nMonomer; ++i) {
321 wFields[i][0] = 0.0;
322 }
323
324 // Add average values arising from interactions
325 double chi, wAve, cAve;
326 for (int i = 0; i < nMonomer; ++i) {
327 wAve = 0.0;
328 for (int j = 0; j < nMonomer; ++j) {
329 chi = interaction_.chi(i,j);
330 cAve = system().c().basis(j)[0];
331 wAve += chi * cAve;
332 }
333 wFields[i][0] = wAve;
334 }
335
336 // If external fields exist, add their spatial averages
337 if (system().h().hasData()) {
338 for (int i = 0; i < nMonomer; ++i) {
339 wFields[i][0] += system().h().basis(i)[0];
340 }
341 }
342 }
343
344 // Set fields in system w container
345 system().w().setBasis(wFields);
346
347 // If flexible, update unit cell parameters
348 if (IteratorT::isFlexible_) {
349
350 // Initialize parameters array with current values
351 FSArray<double, 6> parameters;
352 parameters = system().domain().unitCell().parameters();
353
354 // Reset any parameters that are flexible
355 const int nParam = system().domain().unitCell().nParameter();
356 const int begin = nMonomer*nBasis;
357 const double scale = 1.0 / scaleStress_;
358 int counter = 0;
359 for (int i = 0; i < nParam; i++) {
360 if (flexibleParams_[i]) {
361 parameters[i] = scale * newState[begin + counter];
362 counter++;
363 }
364 }
365 UTIL_CHECK(counter == IteratorT::nFlexibleParams());
366
367 // Set system unit cell parameters
368 system().setUnitCell(parameters);
369 }
370 }
371
372 /*
373 * Output relevant system details to the iteration log file.
374 */
375 template <int D, class T>
376 void AmIteratorBasis<D,T>::outputToLog()
377 {
378 if (IteratorT::isFlexible_ && AmIterTmplT::verbose() > 1) {
379
380 UnitCell<D> const & unitCell = system().domain().unitCell();
381 const int nParam = unitCell.nParameter();
382 const int nMonomer = system().mixture().nMonomer();
383 const int nBasis = system().domain().basis().nBasis();
384 const int begin = nMonomer*nBasis;
385
386 double res, str;
387 int counter = 0;
388 for (int i = 0; i < nParam; i++) {
389 if (flexibleParams_[i]) {
390 res = AmIterTmplT::residual()[begin + counter];
391 str = -1.0 * res / scaleStress_;
392 Log::file()
393 << " Cell Param " << i << " = "
394 << Dbl(unitCell.parameters()[i], 15)
395 << " , stress = "
396 << Dbl(str, 15)
397 << "\n";
398 counter++;
399 }
400 }
401 }
402 }
403
404}
405}
406#endif
virtual void setup(bool isContinuation)
void outputTimers(std::ostream &out) const
virtual void readParameters(std::istream &in)
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
virtual ~AmIteratorBasis()
Destructor.
void readParameters(std::istream &in) override
Read all parameters and initialize.
void setup(bool isContinuation) override
Setup iterator just before entering iteration loop.
void outputTimers(std::ostream &out) const override
Output timing results to log file.
AmIteratorTmpl< IteratorT, VectorT > AmIterTmplT
Alias for base class.
AmIteratorBasis(typename T::System &system)
Constructor.
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
Periodic fields and crystallography.
Definition complex.cpp:11
Class templates for real-valued periodic fields.
PSCF package top-level namespace.