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