PSCF v1.4.0
PolymerSpecies.tpp
1/*
2* PSCF - Polymer Self-Consistent Field
3*
4* Copyright 2015 - 2025, The Regents of the University of Minnesota
5* Distributed under the terms of the GNU General Public License.
6*/
7
8#include "PolymerSpecies.h"
9
10#include <pscf/chem/Edge.h>
11#include <util/containers/GArray.h>
12#include <util/containers/FArray.h>
13#include <util/containers/DMatrix.h>
14
15#include <cmath>
16
17namespace Pscf
18{
19
20 /*
21 * Constructor.
22 */
23 template <typename WT>
25 : Species<WT>(),
26 vertices_(),
27 propagatorIds_(),
28 paths_(),
29 nBlock_(0),
30 nVertex_(0),
31 nPropagator_(0),
32 type_(PolymerType::Linear)
33 { ParamComposite::setClassName("PolymerSpecies"); }
34
35 /*
36 * Read parameter file block.
37 */
38 template <typename WT>
40 {
41 // Read polymer type (linear by default)
42 type_ = PolymerType::Linear;
44
45 ParamComposite::read<int>(in, "nBlock", nBlock_);
46
47 // Note: For any acyclic graph, nVertex = nBlock + 1
48 nVertex_ = nBlock_ + 1;
49
50 // Allocate all arrays
52 vertices_.allocate(nVertex_);
53 propagatorIds_.allocate(2*nBlock_);
54
55 // Set block id and polymerType for all blocks
56 for (int edgeId = 0; edgeId < nBlock_; ++edgeId) {
57 edge(edgeId).setId(edgeId);
58 edge(edgeId).setPolymerType(type_);
59 }
60
61 // Set sequential ids for all vertices
62 for (int vertexId = 0; vertexId < nVertex_; ++vertexId) {
63 vertices_[vertexId].setId(vertexId);
64 }
65
66 // If this is a linear polymer, set all block vertex Ids:
67 if (type_ == PolymerType::Linear) {
68 // In a linear chain, block i connects vertices i and i+1.
69 for (int blockId = 0; blockId < nBlock_; ++blockId) {
70 edge(blockId).setVertexIds(blockId, blockId + 1);
71 }
72 }
73
74 // Read array of block data from parameter file
75 readBlocks(in);
76
77 // Read phi or mu (but not both) and set ensemble accordingly
80 // Reading of parameter file is now complete
81
82 // Add edges to attached vertices
83 int vertexId0, vertexId1;
84 Edge* edgePtr;
85 for (int edgeId = 0; edgeId < nBlock_; ++edgeId) {
86 edgePtr = &(edge(edgeId));
87 vertexId0 = edgePtr->vertexId(0);
88 vertexId1 = edgePtr->vertexId(1);
89 vertices_[vertexId0].addEdge(*edgePtr);
90 vertices_[vertexId1].addEdge(*edgePtr);
91 }
92
93 // Polymer graph topology is now fully specified.
94
95 // Construct a plan for the order in which block propagators
96 // should be computed when solving the MDE.
97 makePlan();
98
99 // Construct vertex-to-vertex path signposts
100 makePaths();
101
102 // Check internal consistency of edge and vertex data
103 isValid();
104
105 }
106
107 /*
108 * Make a plan for the order of computation of block propagators.
109 */
110 template <typename WT>
112 {
113 if (nPropagator_ != 0) {
114 UTIL_THROW("nPropagator !=0 on entry");
115 }
116
117 // Allocate and initialize isFinished matrix
118 DMatrix<bool> isFinished;
119 isFinished.allocate(nBlock_, 2);
120 for (int iBlock = 0; iBlock < nBlock_; ++iBlock) {
121 for (int iDirection = 0; iDirection < 2; ++iDirection) {
122 isFinished(iBlock, iDirection) = false;
123 }
124 }
125
126 Pair<int> propId;
127 Vertex* inVertexPtr = 0;
128 int inVertexId = -1;
129 bool isReady;
130 while (nPropagator_ < nBlock_*2) {
131 for (int iBlock = 0; iBlock < nBlock_; ++iBlock) {
132 for (int iDirection = 0; iDirection < 2; ++iDirection) {
133 if (isFinished(iBlock, iDirection) == false) {
134 inVertexId = edge(iBlock).vertexId(iDirection);
135 inVertexPtr = &vertices_[inVertexId];
136 isReady = true;
137 for (int j = 0; j < inVertexPtr->size(); ++j) {
138 propId = inVertexPtr->inPropagatorId(j);
139 if (propId[0] != iBlock) {
140 if (!isFinished(propId[0], propId[1])){
141 isReady = false;
142 break;
143 }
144 }
145 }
146 if (isReady) {
147 propagatorIds_[nPropagator_][0] = iBlock;
148 propagatorIds_[nPropagator_][1] = iDirection;
149 isFinished(iBlock, iDirection) = true;
150 ++nPropagator_;
151 }
152 }
153 }
155 }
156
157 }
158
159 /*
160 * Identify paths between vertices.
161 */
162 template <typename WT>
164 {
165 UTIL_CHECK(nVertex_ > 0);
166
167 // Local variables
168 DArray< GArray< FArray<int, 3> > > sendPaths;
169 DArray< GArray< FArray<int, 3> > > recvPaths;
172 Pair<int> pId;
173 int is; // sender vertex id
174 int ir; // receiver vertex id
175 int ib; // bond id
176 int io; // outgoing (source) direction id
177 int ii; // incoming (receiver) direction id
178 int j, k, n;
179
180 // Allocate and clear path containers
181 sendPaths.allocate(nVertex_);
182 recvPaths.allocate(nVertex_);
183 oldPaths.allocate(nVertex_);
184 for (is = 0; is < nVertex_; ++is) {
185 sendPaths[is].clear();
186 recvPaths[is].clear();
187 oldPaths[is].clear();
188 }
189
190 // Initialize sendPaths with path-to-self entries
191 for (is = 0; is < nVertex_; ++is) {
192 path[0] = is;
193 path[1] = -1;
194 path[2] = -1;
195 sendPaths[is].append(path);
196 }
197
198 // While loop to completion
199 bool done = false;
200 int iter = 0;
201 while (!done) {
202 UTIL_CHECK(iter < nBlock_ + 2);
203
204 // Check that recvPaths container is empty
205 for (ir = 0; is < nVertex_; ++is) {
206 UTIL_CHECK(recvPaths[ir].size() == 0);
207 }
208
209 // Loop over source vertices for sending
210 done = true;
211 for (is = 0; is < nVertex_; ++is) {
212
213 // Send any sendPaths data to neighbors
214 int n = sendPaths[is].size();
215 if (n > 0) {
216 done = false;
217 Vertex const & sender = vertex(is);
218
219 // Send sendPaths data to all new neighbors
220 for (j = 0; j < sender.size(); ++j) {
221 pId = sender.outPropagatorId(j);
222 ib = pId[0]; // block identifier
223 io = pId[1]; // outgoing direction identifier
224 if (io == 0) {
225 UTIL_CHECK(edge(ib).vertexId(0) == is);
226 ii = 1;
227 } else {
228 UTIL_CHECK(edge(ib).vertexId(1) == is);
229 ii = 0;
230 }
231 ir = edge(ib).vertexId(ii);
232 for (k = 0; k < n; ++k) {
233 path = sendPaths[is][k];
234 // Send unless just received along same bond
235 if (ib != path[1]) {
236 path[1] = ib;
237 path[2] = ii;
238 recvPaths[ir].append(path);
239 }
240 }
241 }
242
243 } // if (n > 0)
244 } // Loop over source vertices
245
246 // Loop over vertices to transfer data structures
247 for (is = 0; is < nVertex_; ++is) {
248
249 // Move sendPaths to oldPaths, clear sendPaths
250 n = sendPaths[is].size();
251 if (n > 0) {
252 for (k = 0; k < n; ++k) {
253 path = sendPaths[is][k];
254 oldPaths[is].append(path);
255 }
256 }
257 sendPaths[is].clear();
258
259 // Move recvPaths to sendPaths, clear recvPaths
260 n = recvPaths[is].size();
261 if (n > 0) {
262 for (k = 0; k < n; ++k) {
263 sendPaths[is].append(recvPaths[is][k]);
264 }
265 }
266 recvPaths[is].clear();
267
268 }
269
270 ++iter;
271 } // while not done
272
273 // Allocate and initialize member variable paths_
274 paths_.allocate(nVertex_);
275 for (is = 0; is < nVertex_; ++is) {
276 paths_[is].allocate(nVertex_);
277 for (ir = 0; ir < nVertex_; ++ir) {
278 paths_[is][ir][0] = -1;
279 paths_[is][ir][1] = -1;
280 }
281 }
282
283 // Assign values to all elements of paths_ container
284 for (is = 0; is < nVertex_; ++is) {
285 n = oldPaths[is].size();
286 UTIL_CHECK(n == nVertex_);
287 for (k = 0; k < n; ++k) {
288 path = oldPaths[is][k];
289 ir = path[0]; // id of target vertex
290 UTIL_CHECK(ir >= 0);
291 UTIL_CHECK(ir < nVertex_);
292 // Check that element was not set previously
293 UTIL_CHECK(paths_[is][ir][0] == -1);
294 UTIL_CHECK(paths_[is][ir][1] == -1);
295 //std::cout << std::endl << is << " " << ir << " "
296 // << path[1] << " " << path[2];
297 if (ir == is) {
298 UTIL_CHECK(path[1] == -1);
299 UTIL_CHECK(path[2] == -1);
300 } else {
301 UTIL_CHECK(path[1] >= 0);
302 UTIL_CHECK(path[1] < nBlock_);
303 UTIL_CHECK(path[2] >= 0);
304 UTIL_CHECK(path[2] < 2);
305 }
306 paths_[is][ir][0] = path[1];
307 paths_[is][ir][1] = path[2];
308 }
309 }
310
311 #if 0
312 std::cout << std::endl << "Paths:" ;
313 for (is = 0; is < nVertex_; ++is) {
314 for (ir = 0; ir < nVertex_; ++ir) {
315 pId = paths_[is][ir];
316 std::cout << std::endl;
317 std::cout << is << ir << pId[0] << pId[1];
318 }
319 }
320 std::cout << std::endl;
321 #endif
322
323 }
324
325 /*
326 * Check consistency of internal data structures.
327 */
328 template <typename WT>
329 void PolymerSpecies<WT>::isValid()
330 {
331 Pair<int> pair;
332 int ib, iv, ip, id, iv0, iv1, n;
333
334 // Check validity of ids owned by edges
335 for (ib = 0; ib < nBlock_; ++ib) {
336 UTIL_CHECK(edge(ib).id() == ib);
337 iv0 = edge(ib).vertexId(0);
338 iv1 = edge(ib).vertexId(1);
339 UTIL_CHECK(iv0 != iv1);
340 UTIL_CHECK(iv0 >= 0);
341 UTIL_CHECK(iv0 < nVertex_);
342 UTIL_CHECK(iv1 >= 0);
343 UTIL_CHECK(iv1 < nVertex_);
344 }
345
346 // Check consistency of vertex::outPropagatorId
347 for (iv = 0; iv < nVertex_; ++iv) {
348 UTIL_CHECK(vertex(iv).id() == iv);
349 n = vertex(iv).size();
350 for (ip = 0; ip < n; ++ip) {
351 pair = vertex(iv).outPropagatorId(ip);
352 ib = pair[0];
353 id = pair[1];
354 UTIL_CHECK(ib >= 0);
355 UTIL_CHECK(ib < nBlock_);
356 UTIL_CHECK(id >= 0);
357 UTIL_CHECK(id < 2);
358 UTIL_CHECK(edge(ib).vertexId(id) == iv);
359 }
360 }
361
362 // Check consistency of vertex::inPropagatorId
363 for (iv = 0; iv < nVertex_; ++iv) {
364 UTIL_CHECK(vertex(iv).id() == iv);
365 n = vertex(iv).size();
366 for (ip = 0; ip < n; ++ip) {
367 pair = vertex(iv).inPropagatorId(ip);
368 ib = pair[0];
369 id = pair[1];
370 UTIL_CHECK(ib >= 0);
371 UTIL_CHECK(ib < nBlock_);
372 UTIL_CHECK(id >= 0);
373 UTIL_CHECK(id < 2);
374 if (id == 0) {
375 UTIL_CHECK(edge(ib).vertexId(1) == iv);
376 } else {
377 UTIL_CHECK(edge(ib).vertexId(0) == iv);
378 }
379 }
380 }
381
382 // Check consistency of vertex ids in paths
383 for (iv0 = 0; iv0 < nVertex_; ++iv0) {
384 for (iv1 = 0; iv1 < nVertex_; ++iv1) {
385 pair = path(iv0, iv1);
386 ib = pair[0];
387 id = pair[1];
388 if (iv0 == iv1) {
389 UTIL_CHECK(ib == -1);
390 UTIL_CHECK(id == -1);
391 } else {
392 UTIL_CHECK(edge(ib).vertexId(id) == iv0);
393 }
394 }
395 }
396
397 }
398
399 /*
400 * Total length of all blocks = volume / reference volume
401 */
402 template <typename WT>
404 {
406 double value = 0.0;
407 for (int blockId = 0; blockId < nBlock_; ++blockId) {
408 value += edge(blockId).length();
409 }
410 return value;
411 }
412
413 /*
414 * Total number of beads in all blocks.
415 */
416 template <typename WT>
418 {
420 int value = 0.0;
421 for (int blockId = 0; blockId < nBlock_; ++blockId) {
422 value += edge(blockId).nBead();
423 }
424 return value;
425 }
426
427}
Descriptor for a block within a block polymer.
Definition Edge.h:59
int vertexId(int i) const
Get the id of one associated vertex.
Definition Edge.h:296
virtual Edge & edge(int id)=0
Get a specified Edge (block descriptor) by non-const reference.
int nBead() const
Total number of beads in the polymer (bead model).
double length() const
Sum of the lengths of all blocks in the polymer (thread model).
Pair< int > const & path(int is, int it) const
Get an id for a propagator from one vertex towards a target.
PolymerSpecies()
Constructor.
virtual void readParameters(std::istream &in)
Read parameters and initialize.
virtual void readBlocks(std::istream &in)=0
Read array of blocks from parameter file.
const Vertex & vertex(int id) const
Get a specified Vertex by const reference.
virtual void allocateBlocks()=0
Allocate array of blocks.
virtual void readParameters(std::istream &in)
Read phi or mu (but not both) and set ensemble accordingly.
Definition Species.tpp:34
A junction or chain end in a block polymer.
Definition Vertex.h:31
int size() const
Get the number of attached blocks.
Definition Vertex.h:110
Pair< int > const & inPropagatorId(int i) const
Get the block and direction of an incoming propagator.
Definition Vertex.h:114
Pair< int > const & outPropagatorId(int i) const
Get the block and direction of an outgoing propagator.
Definition Vertex.h:118
Dynamically allocatable contiguous array template.
Definition DArray.h:32
void allocate(int capacity)
Allocate the underlying C array.
Definition DArray.h:269
Dynamically allocated Matrix.
Definition DMatrix.h:25
void allocate(int capacity1, int capacity2)
Allocate memory for a matrix.
Definition DMatrix.h:170
A fixed size (static) contiguous array template.
Definition FArray.h:47
An array of exactly 2 objects.
Definition Pair.h:24
ScalarParam< Type > & read(std::istream &in, const char *label, Type &value)
Add and read a new required ScalarParam < Type > object.
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.
#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
bool isThread()
Is the thread model in use ?
bool isBead()
Is the bead model in use ?
PSCF package top-level namespace.
Struct containing an enumeration of polymer structure types.
Definition PolymerType.h:27