PSCF v1.4.0
cpc/solvers/Propagator.tpp
1#ifndef CPC_PROPAGATOR_TPP
2#define CPC_PROPAGATOR_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 "Propagator.h"
12#include "Block.h"
13
14#include <pscf/cpu/complex.h>
15#include <pscf/math/arithmetic.h>
16#include <pscf/mesh/Mesh.h>
17
18namespace Pscf {
19namespace Cpc {
20
21 using namespace Util;
22
23 /*
24 * Constructor.
25 */
26 template <int D>
28 : blockPtr_(nullptr),
29 meshPtr_(nullptr),
30 ns_(0),
31 isAllocated_(false)
32 {}
33
34 /*
35 * Destructor.
36 */
37 template <int D>
40
41 /*
42 * Associate this propagator with a block.
43 */
44 template <int D> inline
46 {
47 UTIL_CHECK(!blockPtr_);
48 blockPtr_ = &block;
49 }
50
51 /*
52 * Allocate memory used by this propagator.
53 */
54 template <int D>
55 void Propagator<D>::allocate(int ns, const Mesh<D>& mesh)
56 {
57 UTIL_CHECK(!isAllocated_);
58 ns_ = ns;
59 meshPtr_ = &mesh;
60
61 qFields_.allocate(ns);
62 for (int i = 0; i < ns; ++i) {
63 qFields_[i].allocate(mesh.dimensions());
64 }
65 isAllocated_ = true;
66 }
67
68 /*
69 * Reallocate memory used by this propagator using a new ns value.
70 */
71 template <int D>
73 {
74 UTIL_CHECK(meshPtr_);
75 UTIL_CHECK(isAllocated_);
76 UTIL_CHECK(ns_ != ns);
77 ns_ = ns;
78
79 // Deallocate all memory previously used by this propagator.
80 qFields_.deallocate();
81
82 // NOTE: The qFields_ container is a DArray<FieldT>, where FieldT
83 // is an alias for CField<D>. The DArray::deallocate() function
84 // calls "delete [] ptr", where ptr is a pointer to the underlying
85 // C array. The C++ delete [] command calls the destructor for each
86 // element of type CField<T> before deleting the outer array itself.
87 // The CField<D> destructor deletes the fftw_complex* array that
88 // stores the field associated with each slice of the propagator.
89
90 // Allocate new memory for qFields_ using the new value of ns
91 qFields_.allocate(ns);
92 for (int i = 0; i < ns; ++i) {
93 qFields_[i].allocate(meshPtr_->dimensions());
94 }
95
96 setIsSolved(false);
97 }
98
99 /*
100 * Compute initial head q-field for the thread model.
101 */
102 template <int D>
103 void Propagator<D>::computeHead()
104 {
105 UTIL_CHECK(meshPtr_);
106 int nx = meshPtr_->size();
107
108 // Reference to head of this propagator
109 FieldT& qh = qFields_[0];
110
111 // Initialize qh field to 1.0 at all grid points
112 for (int ix = 0; ix < nx; ++ix) {
113 assign(qh[ix], 1.0);
114 //qh[ix] = 1.0;
115 }
116 // VecOp::eqS(qh, 1.0);
117
118 if (!isHeadEnd()) {
119 // Pointwise multiply tail q-fields of all sources
120 for (int is = 0; is < nSource(); ++is) {
121 if (!source(is).isSolved()) {
122 UTIL_THROW("Source not solved in computeHead");
123 }
124 FieldT const& qt = source(is).tail();
125 for (int ix = 0; ix < nx; ++ix) {
126 mulEq(qh[ix], qt[ix]);
127 }
128 // VecOp::mulEq(qh, qt);
129 }
130 }
131
132 }
133
134 /*
135 * Assign one field to another.
136 */
137 template <int D>
138 void Propagator<D>::assignField(FieldT& lhs, FieldT const & rhs)
139 {
140 int nx = lhs.capacity();
141 UTIL_CHECK(rhs.capacity() == nx);
142 for (int ix = 0; ix < nx; ++ix) {
143 assign(lhs[ix], rhs[ix]);
144 // lhs[ix] = rhs[ix];
145 }
146 // VecOp::eqV(lhs, rhs);
147 }
148
149 /*
150 * Solve the modified diffusion equation for this block.
151 */
152 template <int D>
154 {
155 UTIL_CHECK(blockPtr_);
157
158 // Initialize head as pointwise product of source propagators
159 computeHead();
160
162
163 // MDE step loop for thread model
164 for (int iStep = 0; iStep < ns_ - 1; ++iStep) {
165 block().stepThread(qFields_[iStep], qFields_[iStep + 1]);
166 }
167
168 } else
169 if (PolymerModel::isBead()) {
170
171 // Half-bond and bead weight for first bead
172 if (isHeadEnd()) {
173 assignField(qFields_[1], qFields_[0]);
174 } else {
175 block().stepHalfBondBead(qFields_[0], qFields_[1]);
176 }
177 block().stepFieldBead(qFields_[1]);
178
179 // MDE step loop for bead model (stop before tail vertex)
180 int iStep;
181 for (iStep = 1; iStep < ns_ - 2; ++iStep) {
182 block().stepBead(qFields_[iStep], qFields_[iStep + 1]);
183 }
184
185 // Half-bond for tail slice
186 if (isTailEnd()) {
187 assignField(qFields_[ns_-1], qFields_[ns_-2]);
188 } else {
189 block().stepHalfBondBead(qFields_[ns_-2], qFields_[ns_-1]);
190 }
191
192 } else {
193 // This should be impossible
194 UTIL_THROW("Unexpected PolymerModel type");
195 }
196
197 setIsSolved(true);
198 }
199
200 /*
201 * Solve the MDE with a specified initial condition at the head.
202 */
203 template <int D>
205 {
206 UTIL_CHECK(blockPtr_);
207 UTIL_CHECK(meshPtr_);
208 UTIL_CHECK(isAllocated_);
209 int nx = meshPtr_->size();
210 UTIL_CHECK(head.capacity() == nx);
211
212 // Initialize initial (head) field
213 FieldT& qh = qFields_[0];
214 for (int i = 0; i < nx; ++i) {
215 assign(qh[i], head[i]);
216 }
217 // VecOp::eqV(qh, head);
218
220
221 // MDE step loop for thread model
222 for (int iStep = 0; iStep < ns_ - 1; ++iStep) {
223 block().stepThread(qFields_[iStep], qFields_[iStep + 1]);
224 }
225
226 } else
227 if (PolymerModel::isBead()) {
228
229 // Half-bond and bead weight for first bead
230 if (isHeadEnd()) {
231 assignField(qFields_[1], qFields_[0]);
232 } else {
233 block().stepHalfBondBead(qFields_[0], qFields_[1]);
234 }
235 block().stepFieldBead(qFields_[1]);
236
237 // MDE step loop for bead model (stop before tail vertex)
238 int iStep;
239 for (iStep = 1; iStep < ns_ - 2; ++iStep) {
240 block().stepBead(qFields_[iStep], qFields_[iStep + 1]);
241 }
242
243 // Half-bond for tail
244 if (isTailEnd()) {
245 assignField(qFields_[ns_-1], qFields_[ns_-2]);
246 } else {
247 block().stepHalfBondBead(qFields_[ns_-2], qFields_[ns_-1]);
248 }
249
250 } else {
251 // This should be impossible
252 UTIL_THROW("Unexpected PolymerModel type");
253 }
254
255 setIsSolved(true);
256 }
257
258 /*
259 * Compute spatial average of product of head and tail of partner.
260 */
261 template <int D>
262 void Propagator<D>::computeQ(std::complex<double> & Q) const
263 {
264 // Preconditions
265 if (!isSolved()) {
266 UTIL_THROW("Propagator is not solved.");
267 }
268 if (!hasPartner()) {
269 UTIL_THROW("Propagator has no partner set.");
270 }
271 if (!partner().isSolved()) {
272 UTIL_THROW("Partner propagator is not solved");
273 }
275 UTIL_CHECK(meshPtr_);
276 int nx = meshPtr_->size();
277
278 fftw_complex sum;
279 assign(sum, 0.0);
280 if (PolymerModel::isBead() && isHeadEnd()) {
281 // Compute average of q for last bead of partner
282 FieldT const& qt = partner().q(ns_-2);
283 for (int ix = 0; ix < nx; ++ix) {
284 addEq(sum, qt[ix]);
285 }
286 } else {
287 // Compute average product of head slice and partner tail slice
288 FieldT const& qh = head();
289 FieldT const& qt = partner().tail();
290 fftw_complex product;
291 for (int ix = 0; ix < nx; ++ix) {
292 mul(product, qh[ix], qt[ix]);
293 addEq(sum, product);
294 // sum += qh[ix]*qt[ix];
295 }
296 }
297 double meshSize = double(nx);
298 divEq(sum, meshSize);
299 assign(Q, sum);
300 }
301
302}
303}
304#endif
Block within a linear or branched block polymer.
Block< D > const & block() const
Get the associated Block<D> object by const reference.
void computeQ(std::complex< double > &Q) const
Compute and return partition function for the polymer.
const Propagator< D > & partner() const
Get partner propagator.
bool isHeadEnd() const
Is the head vertex a chain end?
void solve()
Solve the modified diffusion equation (MDE) for this block.
void setIsSolved(bool isSolved)
Set the isSolved flag to true or false.
bool isSolved() const
Has the modified diffusion equation been solved?
bool isTailEnd() const
Is the tail vertex a chain end?
const FieldT & head() const
Return q-field at beginning of the block (initial condition).
void reallocate(int ns)
Reallocate memory used by this propagator.
int ns() const
Get the number of values of s (or slices), including head and tail.
bool isAllocated() const
Has memory been allocated for this propagator?
CField< D > FieldT
Field type (function of position, defined on a r-space grid).
void allocate(int ns, const Mesh< D > &mesh)
Allocate memory used by this propagator.
bool hasPartner() const
Does this have a partner propagator?
void setBlock(Block< D > &block)
Associate this propagator with a block.
Description of a regular grid of points in a periodic domain.
Definition Mesh.h:61
IntVec< D > dimensions() const
Get an IntVec<D> of the grid dimensions.
Definition Mesh.h:217
#define UTIL_CHECK(condition)
Assertion macro suitable for serial or parallel production code.
Definition global.h:68
#define UTIL_THROW(msg)
Macro for throwing an Exception, reporting function, file and line number.
Definition global.h:49
void mulEq(fftw_complex &a, fftw_complex const &b)
In-place multiplication of two complex number, a *= b.
void addEq(fftw_complex &a, fftw_complex const &b)
In-place addition of fftw_complex numbers, a += b.
void assign(fftw_complex &z, double const &a, double const &b)
Create an fftw_complex from real and imaginary parts, z = a + ib.
void mul(fftw_complex &z, fftw_complex const &a, fftw_complex const &b)
Multiplication of fftw_complex numbers, z = a * b.
void divEq(fftw_complex &a, fftw_complex const &b)
In-place division of fftw_complex numbers, a /= b.
Complex periodic fields, CL-FTS (CPU).
Definition cpc.mod:6
bool isThread()
Is the thread model in use ?
bool isBead()
Is the bead model in use ?
PSCF package top-level namespace.
float product(float a, float b)
Product for float Data.
Definition product.h:22