Loading [MathJax]/extensions/TeX/AMSsymbols.js
PSCF v1.2
rpc/solvers/Block.tpp
1#ifndef RPC_BLOCK_TPP
2#define RPC_BLOCK_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 "Block.h"
12#include <prdc/crystal/UnitCell.h>
13#include <prdc/crystal/shiftToMinimum.h>
14#include <pscf/mesh/Mesh.h>
15#include <pscf/mesh/MeshIterator.h>
16#include <pscf/math/IntVec.h>
17#include <util/containers/DMatrix.h>
18#include <util/containers/DArray.h>
19#include <util/containers/FArray.h>
20#include <util/containers/FSArray.h>
21
22namespace Pscf {
23namespace Rpc {
24
25 using namespace Util;
26 using namespace Pscf::Prdc;
27 using namespace Pscf::Prdc::Cpu;
28
29 /*
30 * Constructor.
31 */
32 template <int D>
34 : meshPtr_(0),
35 fftPtr_(0),
36 kMeshDimensions_(0),
37 kSize_(0),
38 ds_(0.0),
39 dsTarget_(0.0),
40 ns_(0),
41 isAllocated_(false),
42 hasExpKsq_(false)
43 {
44 propagator(0).setBlock(*this);
45 propagator(1).setBlock(*this);
46 }
47
48 /*
49 * Destructor.
50 */
51 template <int D>
54
55 /*
56 * Store addresses of mesh, FFT and unit cell.
57 */
58 template <int D>
59 void Block<D>::associate(Mesh<D> const & mesh,
60 FFT<D> const& fft,
61 UnitCell<D> const& cell)
62 {
63 // Preconditions
64 UTIL_CHECK(!isAllocated_);
65
66 // Set pointers to mesh and fft
67 meshPtr_ = &mesh;
68 fftPtr_ = &fft;
69 unitCellPtr_ = &cell;
70
71 hasExpKsq_ = false;
72 }
73
74 /*
75 * Compute number of contour steps and allocate all memory.
76 */
77 template <int D>
78 void Block<D>::allocate(double ds)
79 {
80 UTIL_CHECK(ds > 0.0);
81 UTIL_CHECK(meshPtr_);
82 UTIL_CHECK(fftPtr_);
83 UTIL_CHECK(unitCellPtr_);
84 UTIL_CHECK(mesh().size() > 1);
85 UTIL_CHECK(mesh().dimensions() == fft().meshDimensions());
86 UTIL_CHECK(!isAllocated_);
87 UTIL_CHECK(length() > 0);
88
89 // Set contour length discretization for this block
90 dsTarget_ = ds;
91 int tempNs;
92 tempNs = floor( length()/(2.0 *ds) + 0.5 );
93 if (tempNs == 0) {
94 tempNs = 1;
95 }
96 ns_ = 2*tempNs + 1;
97 ds_ = length()/double(ns_-1);
98
99 // Compute Fourier space kMeshDimensions_ and kSize_
100 kSize_ = 1;
101 for (int i = 0; i < D; ++i) {
102 if (i < D - 1) {
103 kMeshDimensions_[i] = mesh().dimensions()[i];
104 } else {
105 kMeshDimensions_[i] = mesh().dimensions()[i]/2 + 1;
106 }
107 kSize_ *= kMeshDimensions_[i];
108 }
109
110 // Allocate work arrays for MDE solution
111 expKsq_.allocate(kMeshDimensions_);
112 expKsq2_.allocate(kMeshDimensions_);
113 expW_.allocate(mesh().dimensions());
114 expW2_.allocate(mesh().dimensions());
115 qr_.allocate(mesh().dimensions());
116 qk_.allocate(mesh().dimensions());
117 qr2_.allocate(mesh().dimensions());
118 qk2_.allocate(mesh().dimensions());
119
120 // Allocate work array for stress calculation
121 dGsq_.allocate(kSize_, 6);
122
123 // Allocate block concentration field
124 cField().allocate(mesh().dimensions());
125
126 // Allocate memory for solutions to MDE (requires ns_)
127 propagator(0).allocate(ns_, mesh());
128 propagator(1).allocate(ns_, mesh());
129
130 isAllocated_ = true;
131 hasExpKsq_ = false;
132 }
133
134 /*
135 * Set or reset the the block length.
136 */
137 template <int D>
138 void Block<D>::setLength(double newLength)
139 {
141
142 if (isAllocated_) {
143 // Reset contour length discretization
144 UTIL_CHECK(dsTarget_ > 0);
145 int oldNs = ns_;
146 int tempNs;
147 tempNs = floor( length()/(2.0 *dsTarget_) + 0.5 );
148 if (tempNs == 0) {
149 tempNs = 1;
150 }
151 ns_ = 2*tempNs + 1;
152 ds_ = length()/double(ns_-1);
153
154 if (oldNs != ns_) {
155 // If propagators are already allocated and ns_ has changed,
156 // reallocate memory for solutions to MDE
157 propagator(0).reallocate(ns_);
158 propagator(1).reallocate(ns_);
159 }
160 }
161
162 hasExpKsq_ = false;
163 }
164
165 /*
166 * Set or reset the the block length.
167 */
168 template <int D>
169 void Block<D>::setKuhn(double kuhn)
170 {
171 BlockTmpl< Propagator<D> >::setKuhn(kuhn);
172 hasExpKsq_ = false;
173 }
174
175 /*
176 * Mark data that depend on the unit cell parameters as invalid.
177 */
178 template <int D>
180 { hasExpKsq_ = false; }
181
182 /*
183 * Compute all elements of expKsq_ and expKsq2_ arrays
184 */
185 template <int D>
187 {
188 UTIL_CHECK(isAllocated_);
189 UTIL_CHECK(unitCellPtr_);
190 UTIL_CHECK(unitCellPtr_->isInitialized());
191
192 MeshIterator<D> iter;
193 iter.setDimensions(kMeshDimensions_);
194 IntVec<D> G, Gmin;
195 double Gsq;
196 double factor = -1.0*kuhn()*kuhn()*ds_/6.0;
197 int i;
198 for (iter.begin(); !iter.atEnd(); ++iter) {
199 i = iter.rank();
200 G = iter.position();
201 Gmin = shiftToMinimum(G, mesh().dimensions(), unitCell());
202 Gsq = unitCell().ksq(Gmin);
203 expKsq_[i] = exp(Gsq*factor);
204 expKsq2_[i] = exp(Gsq*factor*0.5);
205 }
206
207 hasExpKsq_ = true;
208 }
209
210 /*
211 * Setup the contour length step algorithm.
212 */
213 template <int D>
214 void
216 {
217 // Preconditions
218 int nx = mesh().size();
219 UTIL_CHECK(nx > 0);
220 UTIL_CHECK(isAllocated_);
221
222 // Compute expW arrays
223 for (int i = 0; i < nx; ++i) {
224
225 // First, check that w[i]*ds_ is not unreasonably large:
226 // (if this condition is not met, solution will have large
227 // error, and user should consider using a smaller ds_)
228 //UTIL_CHECK(std::abs(w[i]*ds_) < 1.0);
229
230 // Calculate values
231 expW_[i] = exp(-0.5*w[i]*ds_);
232 expW2_[i] = exp(-0.5*0.5*w[i]*ds_);
233 }
234
235 // Compute expKsq arrays if necessary
236 if (!hasExpKsq_) {
237 computeExpKsq();
238 }
239
240 }
241
242 /*
243 * Integrate to calculate monomer concentration for this block
244 */
245 template <int D>
246 void Block<D>::computeConcentration(double prefactor)
247 {
248 // Preconditions
249 UTIL_CHECK(isAllocated_);
250 int nx = mesh().size();
251 UTIL_CHECK(nx > 0);
252 UTIL_CHECK(ns_ > 0);
253 UTIL_CHECK(ds_ > 0);
254 UTIL_CHECK(propagator(0).isAllocated());
255 UTIL_CHECK(propagator(1).isAllocated());
256 UTIL_CHECK(cField().capacity() == nx);
257
258 // Initialize cField to zero at all points
259 int i;
260 for (i = 0; i < nx; ++i) {
261 cField()[i] = 0.0;
262 }
263
264 Propagator<D> const & p0 = propagator(0);
265 Propagator<D> const & p1 = propagator(1);
266
267 // Evaluate unnormalized integral
268
269 // Endpoint contributions
270 for (i = 0; i < nx; ++i) {
271 cField()[i] += p0.q(0)[i]*p1.q(ns_ - 1)[i];
272 cField()[i] += p0.q(ns_ -1)[i]*p1.q(0)[i];
273 }
274
275 // Odd indices
276 int j;
277 for (j = 1; j < (ns_ -1); j += 2) {
278 for (i = 0; i < nx; ++i) {
279 cField()[i] += p0.q(j)[i] * p1.q(ns_ - 1 - j)[i] * 4.0;
280 }
281 }
282
283 // Even indices
284 for (j = 2; j < (ns_ -2); j += 2) {
285 for (i = 0; i < nx; ++i) {
286 cField()[i] += p0.q(j)[i] * p1.q(ns_ - 1 - j)[i] * 2.0;
287 }
288 }
289
290 // Normalize the integral
291 prefactor *= ds_ / 3.0;
292 for (i = 0; i < nx; ++i) {
293 cField()[i] *= prefactor;
294 }
295
296 }
297
298 /*
299 * Integrate to Stress exerted by the chain for this block
300 */
301 template <int D>
302 void Block<D>::computeStress(double prefactor)
303 {
304 // Preconditions
305 int nx = mesh().size();
306 UTIL_CHECK(nx > 0);
307 UTIL_CHECK(fft().isSetup());
308 UTIL_CHECK(mesh().dimensions() == fft().meshDimensions());
309 UTIL_CHECK(ns_ > 0);
310 UTIL_CHECK(ds_ > 0);
311 UTIL_CHECK(propagator(0).isAllocated());
312 UTIL_CHECK(propagator(1).isAllocated());
313
314 stress_.clear();
315
316 double dels, normal, increment;
317 int nParam, c, m;
318
319 normal = 3.0*6.0;
320
321 nParam = unitCell().nParameter();
322 c = kSize_;
323
325
326 // Initialize work array and stress_ to zero at all points
327 int i;
328 for (i = 0; i < nParam; ++i) {
329 dQ.append(0.0);
330 stress_.append(0.0);
331 }
332
333 computedGsq();
334
335 Propagator<D> const & p0 = propagator(0);
336 Propagator<D> const & p1 = propagator(1);
337
338 // Evaluate unnormalized integral
339 for (int j = 0; j < ns_ ; ++j) {
340
341 qr_ = p0.q(j);
342 fft().forwardTransform(qr_, qk_);
343
344 qr2_ = p1.q(ns_ - 1 - j);
345 fft().forwardTransform(qr2_, qk2_);
346
347 dels = ds_;
348
349 if (j != 0 && j != ns_ - 1) {
350 if (j % 2 == 0) {
351 dels = dels*2.0;
352 } else {
353 dels = dels*4.0;
354 }
355 }
356
357 for (int n = 0; n < nParam ; ++n) {
358 increment = 0.0;
359
360 for (m = 0; m < c ; ++m) {
361 double prod = 0;
362 prod = (qk2_[m][0] * qk_[m][0]) + (qk2_[m][1] * qk_[m][1]);
363 prod *= dGsq_(m,n);
364 increment += prod;
365 }
366 increment = (increment * kuhn() * kuhn() * dels)/normal;
367 dQ[n] = dQ[n] - increment;
368 }
369 }
370
371 // Normalize
372 for (i = 0; i < nParam; ++i) {
373 stress_[i] = stress_[i] - (dQ[i] * prefactor);
374 }
375
376 }
377
378 /*
379 * Compute dGsq_ array (derivatives of Gsq for all wavevectors)
380 */
381 template <int D>
383 {
384 IntVec<D> temp;
385 IntVec<D> vec;
386 IntVec<D> Partner;
387 MeshIterator<D> iter;
388 iter.setDimensions(kMeshDimensions_);
389
390 for (int n = 0; n < unitCell().nParameter() ; ++n) {
391 for (iter.begin(); !iter.atEnd(); ++iter) {
392 temp = iter.position();
393 vec = shiftToMinimum(temp, mesh().dimensions(), unitCell());
394 dGsq_(iter.rank(), n) = unitCell().dksq(vec, n);
395 for (int p = 0; p < D; ++p) {
396 if (temp [p] != 0) {
397 Partner[p] = mesh().dimensions()[p] - temp[p];
398 } else {
399 Partner[p] = 0;
400 }
401 }
402 if (Partner[D-1] > kMeshDimensions_[D-1]) {
403 dGsq_(iter.rank(), n) *= 2;
404 }
405 }
406 }
407 }
408
409 /*
410 * Propagate solution by one step.
411 */
412 template <int D>
413 void Block<D>::step(RField<D> const & q, RField<D>& qNew)
414 {
415 // Prereconditions on mesh and fft
416 int nx = mesh().size();
417 UTIL_CHECK(nx > 0);
418 UTIL_CHECK(fft().isSetup());
419 UTIL_CHECK(mesh().dimensions() == fft().meshDimensions());
420
421 // Internal preconditions
422 UTIL_CHECK(isAllocated_);
423 int nk = qk_.capacity();
424 UTIL_CHECK(nk > 0);
425 UTIL_CHECK(qr_.capacity() == nx);
426 UTIL_CHECK(expW_.capacity() == nx);
427 UTIL_CHECK(expKsq_.capacity() == nk);
428 UTIL_CHECK(hasExpKsq_);
429
430 // Preconditions on parameters
432 UTIL_CHECK(q.capacity() == nx);
433 UTIL_CHECK(qNew.isAllocated());
434 UTIL_CHECK(qNew.capacity() == nx);
435
436 // Apply pseudo-spectral algorithm
437
438 // Full step for ds, half-step for ds/2
439 int i;
440 for (i = 0; i < nx; ++i) {
441 qr_[i] = q[i]*expW_[i];
442 qr2_[i] = q[i]*expW2_[i];
443 }
444 fft().forwardTransform(qr_, qk_);
445 fft().forwardTransform(qr2_, qk2_);
446 for (i = 0; i < nk; ++i) {
447 qk_[i][0] *= expKsq_[i];
448 qk_[i][1] *= expKsq_[i];
449 qk2_[i][0] *= expKsq2_[i];
450 qk2_[i][1] *= expKsq2_[i];
451 }
452 fft().inverseTransformUnsafe(qk_, qr_); // destroys qk_
453 fft().inverseTransformUnsafe(qk2_, qr2_); // destroys qk2_
454 for (i = 0; i < nx; ++i) {
455 qr_[i] = qr_[i]*expW_[i];
456 qr2_[i] = qr2_[i]*expW_[i];
457 }
458
459 // Finish second half-step for ds/2
460 fft().forwardTransform(qr2_, qk2_);
461 for (i = 0; i < nk; ++i) {
462 qk2_[i][0] *= expKsq2_[i];
463 qk2_[i][1] *= expKsq2_[i];
464 }
465 fft().inverseTransformUnsafe(qk2_, qr2_); // destroys qk2_
466 for (i = 0; i < nx; ++i) {
467 qr2_[i] = qr2_[i]*expW2_[i];
468 }
469
470 // Richardson extrapolation
471 for (i = 0; i < nx; ++i) {
472 qNew[i] = (4.0*qr2_[i] - qr_[i])/3.0;
473 }
474 }
475
476}
477}
478#endif
virtual void setLength(double length)
Set the length of this block.
Class template for a block in a block copolymer.
Definition BlockTmpl.h:106
Propagator< D > & propagator(int directionId)
Definition BlockTmpl.h:191
An IntVec<D, T> is a D-component vector of elements of integer type T.
Definition IntVec.h:27
Iterator over points in a Mesh<D>.
int rank() const
Get the rank of current element.
void begin()
Set iterator to the first point in the mesh.
bool atEnd() const
Is this the end (i.e., one past the last point)?
void setDimensions(const IntVec< D > &dimensions)
Set the grid dimensions in all directions.
IntVec< D > position() const
Get current position in the grid, as integer vector.
Description of a regular grid of points in a periodic domain.
Fourier transform wrapper.
bool isAllocated() const
Return true if the FftwDArray has been allocated, false otherwise.
Definition FftwDArray.h:102
Field of real double precision values on an FFT mesh.
Base template for UnitCell<D> classes, D=1, 2 or 3.
Definition rpg/System.h:34
Block within a branched polymer.
void computeStress(double prefactor)
Compute stress contribution for this block.
void setLength(double newLength)
Set or reset block length.
void setKuhn(double kuhn)
Set or reset monomer statistical segment length.
void clearUnitCellData()
Clear all internal data that depends on the unit cell parameters.
void setupSolver(RField< D > const &w)
Set solver for this block.
void allocate(double ds)
Allocate memory and set contour step size.
void associate(Mesh< D > const &mesh, FFT< D > const &fft, UnitCell< D > const &cell)
Initialize discretization and allocate required memory.
void step(RField< D > const &q, RField< D > &qNew)
Compute one step of solution of MDE, from step i to i+1.
void computeConcentration(double prefactor)
Compute concentration (volume fraction) for block by integration.
int capacity() const
Return allocated size.
Definition Array.h:159
A fixed capacity (static) contiguous array with a variable logical size.
Definition rpg/System.h:28
void append(Data const &data)
Append data to the end of the array.
Definition FSArray.h:258
#define UTIL_CHECK(condition)
Assertion macro suitable for serial or parallel production code.
Definition global.h:68
Fields and FFTs for periodic boundary conditions (CPU)
Definition CField.cpp:12
Periodic fields and crystallography.
Definition CField.cpp:11
PSCF package top-level namespace.
Definition param_pc.dox:1
Utility classes for scientific computation.