PSCF v1.1
PolymerTmpl.h
1#ifndef PSCF_POLYMER_TMPL_H
2#define PSCF_POLYMER_TMPL_H
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 <pscf/chem/Species.h> // base class
12#include <util/param/ParamComposite.h> // base class
13
14#include <pscf/chem/Monomer.h> // member template argument
15#include <pscf/chem/Vertex.h> // member template argument
16#include <pscf/chem/PolymerType.h> // member
17#include <util/containers/Pair.h> // member template
18#include <util/containers/DArray.h> // member template
19#include <util/containers/DMatrix.h>
20
21#include <cmath>
22
23namespace Pscf
24{
25
26 class Block;
27 using namespace Util;
28
40 template <class Block>
41 class PolymerTmpl : public Species, public ParamComposite
42 {
43
44 public:
45
46 // Modified diffusion equation solver for one block.
47 typedef typename Block::Propagator Propagator;
48
49 // Monomer concentration field.
50 typedef typename Propagator::CField CField;
51
52 // Chemical potential field.
53 typedef typename Propagator::WField WField;
54
59
64
70 virtual void readParameters(std::istream& in);
71
98 virtual void solve(double phiTot = 1.0);
99
102
108 Block& block(int id);
109
115 Block const& block(int id) const ;
116
124 const Vertex& vertex(int id) const;
125
132 Propagator& propagator(int blockId, int directionId);
133
140 Propagator const & propagator(int blockId, int directionId) const;
141
149 Propagator& propagator(int id);
150
160 const Pair<int>& propagatorId(int id) const;
161
165
169 int nBlock() const;
170
177 int nVertex() const;
178
182 int nPropagator() const; //
183
187 double length() const;
188
190
191 protected:
192
203 virtual void makePlan();
204
205 private:
206
208 DArray<Block> blocks_;
209
211 DArray<Vertex> vertices_;
212
214 DArray< Pair<int> > propagatorIds_;
215
217 int nBlock_;
218
220 int nVertex_;
221
223 int nPropagator_;
224
226 PolymerType::Enum type_;
227
228 };
229
230 /*
231 * Number of vertices (ends and/or junctions)
232 */
233 template <class Block>
235 { return nVertex_; }
236
237 /*
238 * Number of blocks.
239 */
240 template <class Block>
241 inline int PolymerTmpl<Block>::nBlock() const
242 { return nBlock_; }
243
244 /*
245 * Number of propagators.
246 */
247 template <class Block>
249 { return nPropagator_; }
250
251 /*
252 * Total length of all blocks = volume / reference volume
253 */
254 template <class Block>
255 inline double PolymerTmpl<Block>::length() const
256 {
257 double value = 0.0;
258 for (int blockId = 0; blockId < nBlock_; ++blockId) {
259 value += blocks_[blockId].length();
260 }
261 return value;
262 }
263
264 /*
265 * Get a specified Vertex.
266 */
267 template <class Block>
268 inline
270 { return vertices_[id]; }
271
272 /*
273 * Get a specified Block.
274 */
275 template <class Block>
276 inline Block& PolymerTmpl<Block>::block(int id)
277 { return blocks_[id]; }
278
279 /*
280 * Get a specified Block by const reference.
281 */
282 template <class Block>
283 inline Block const & PolymerTmpl<Block>::block(int id) const
284 { return blocks_[id]; }
285
286 /*
287 * Get a propagator id, indexed in order of computation.
288 */
289 template <class Block>
290 inline
292 {
293 UTIL_CHECK(id >= 0);
294 UTIL_CHECK(id < nPropagator_);
295 return propagatorIds_[id];
296 }
297
298 /*
299 * Get a propagator indexed by block and direction.
300 */
301 template <class Block>
302 inline
303 typename Block::Propagator&
304 PolymerTmpl<Block>::propagator(int blockId, int directionId)
305 { return block(blockId).propagator(directionId); }
306
307 /*
308 * Get a const propagator indexed by block and direction.
309 */
310 template <class Block>
311 inline
312 typename Block::Propagator const &
313 PolymerTmpl<Block>::propagator(int blockId, int directionId) const
314 { return block(blockId).propagator(directionId); }
315
316 /*
317 * Get a propagator indexed in order of computation.
318 */
319 template <class Block>
320 inline
321 typename Block::Propagator& PolymerTmpl<Block>::propagator(int id)
322 {
323 Pair<int> propId = propagatorId(id);
324 return propagator(propId[0], propId[1]);
325 }
326
327 // Non-inline functions
328
329 /*
330 * Constructor.
331 */
332 template <class Block>
334 : Species(),
335 blocks_(),
336 vertices_(),
337 propagatorIds_(),
338 nBlock_(0),
339 nVertex_(0),
340 nPropagator_(0)
341 { setClassName("PolymerTmpl"); }
342
343 /*
344 * Destructor.
345 */
346 template <class Block>
348 {}
349
350 template <class Block>
352 {
353 // Read polymer type (linear by default)
354 type_ = PolymerType::Linear;
355 readOptional<PolymerType::Enum>(in, "type", type_);
356
357 read<int>(in, "nBlock", nBlock_);
358
359 // Note: For any acyclic graph, nVertex = nBlock + 1
360 nVertex_ = nBlock_ + 1;
361
362 // Allocate all arrays (blocks_, vertices_ and propagatorIds_)
363 blocks_.allocate(nBlock_);
364 vertices_.allocate(nVertex_);
365 propagatorIds_.allocate(2*nBlock_);
366
367 // Set block id and polymerType for all blocks
368 for (int blockId = 0; blockId < nBlock_; ++blockId) {
369 blocks_[blockId].setId(blockId);
370 blocks_[blockId].setPolymerType(type_);
371 }
372
373 // Set all vertex ids
374 for (int vertexId = 0; vertexId < nVertex_; ++vertexId) {
375 vertices_[vertexId].setId(vertexId);
376 }
377
378 // If polymer is linear polymer, set all block vertex Ids:
379 // In a linear chain, block i connects vertex i and vertex i+1.
380 if (type_ == PolymerType::Linear) {
381 for (int blockId = 0; blockId < nBlock_; ++blockId) {
382 blocks_[blockId].setVertexIds(blockId, blockId + 1);
383 }
384 }
385
386 // Read all other required block data
387 readDArray<Block>(in, "blocks", blocks_, nBlock_);
388
389 /*
390 * The parameter file format for each block in the array blocks_
391 * is different for branched and linear polymer. These formats
392 * are defined in >> and << stream io operators for a Pscf::Block.
393 * The choice of format is controlled by the Block::polymerType.
394 *
395 * For a branched polymer, the text format for each block is:
396 *
397 * monomerId length vertexId(0) vertexId(1)
398 *
399 * where monomerId is the index of the block monomer type, length
400 * is the block length, and vertexId(0) and vertexId(1) are the
401 * indices of the two vertices to which the block is attached.
402 *
403 * For a linear polymer, blocks must be entered sequentially, in
404 * their order along the chain, and block vertex id values are
405 * set automatically. In this case, the format for one block is:
406 *
407 * monomerId length
408 *
409 * with no need for explicit vertex ids.
410 */
411
412 // Add blocks to attached vertices
413 int vertexId0, vertexId1;
414 Block* blockPtr;
415 for (int blockId = 0; blockId < nBlock_; ++blockId) {
416 blockPtr = &(blocks_[blockId]);
417 vertexId0 = blockPtr->vertexId(0);
418 vertexId1 = blockPtr->vertexId(1);
419 vertices_[vertexId0].addBlock(*blockPtr);
420 vertices_[vertexId1].addBlock(*blockPtr);
421 }
422
423 // Polymer topology is now fully specified.
424
425 // Construct a plan for the order in which block propagators
426 // should be computed when solving the MDE.
427 makePlan();
428
429 // Read phi or mu (but not both)
430 bool hasPhi = readOptional(in, "phi", phi_).isActive();
431 if (hasPhi) {
432 ensemble_ = Species::Closed;
433 } else {
434 ensemble_ = Species::Open;
435 read(in, "mu", mu_);
436 }
437
438 // Set sources for all propagators
439 Vertex const * vertexPtr = 0;
440 Propagator const * sourcePtr = 0;
441 Propagator * propagatorPtr = 0;
442 Pair<int> propagatorId;
443 int blockId, directionId, vertexId, i;
444 for (blockId = 0; blockId < nBlock(); ++blockId) {
445 // Add sources
446 for (directionId = 0; directionId < 2; ++directionId) {
447 vertexId = block(blockId).vertexId(directionId);
448 vertexPtr = &vertex(vertexId);
449 propagatorPtr = &block(blockId).propagator(directionId);
450 for (i = 0; i < vertexPtr->size(); ++i) {
451 propagatorId = vertexPtr->inPropagatorId(i);
452 if (propagatorId[0] == blockId) {
453 UTIL_CHECK(propagatorId[1] != directionId);
454 } else {
455 sourcePtr =
456 &block(propagatorId[0]).propagator(propagatorId[1]);
457 propagatorPtr->addSource(*sourcePtr);
458 }
459 }
460 }
461
462 }
463
464 }
465
466 /*
467 * Make a plan for the order of computation of block propagators.
468 */
469 template <class Block>
471 {
472 if (nPropagator_ != 0) {
473 UTIL_THROW("nPropagator !=0 on entry");
474 }
475
476 // Allocate and initialize isFinished matrix
477 DMatrix<bool> isFinished;
478 isFinished.allocate(nBlock_, 2);
479 for (int iBlock = 0; iBlock < nBlock_; ++iBlock) {
480 for (int iDirection = 0; iDirection < 2; ++iDirection) {
481 isFinished(iBlock, iDirection) = false;
482 }
483 }
484
485 Pair<int> propagatorId;
486 Vertex* inVertexPtr = 0;
487 int inVertexId = -1;
488 bool isReady;
489 while (nPropagator_ < nBlock_*2) {
490 for (int iBlock = 0; iBlock < nBlock_; ++iBlock) {
491 for (int iDirection = 0; iDirection < 2; ++iDirection) {
492 if (isFinished(iBlock, iDirection) == false) {
493 inVertexId = blocks_[iBlock].vertexId(iDirection);
494 inVertexPtr = &vertices_[inVertexId];
495 isReady = true;
496 for (int j = 0; j < inVertexPtr->size(); ++j) {
497 propagatorId = inVertexPtr->inPropagatorId(j);
498 if (propagatorId[0] != iBlock) {
499 if (!isFinished(propagatorId[0], propagatorId[1])) {
500 isReady = false;
501 break;
502 }
503 }
504 }
505 if (isReady) {
506 propagatorIds_[nPropagator_][0] = iBlock;
507 propagatorIds_[nPropagator_][1] = iDirection;
508 isFinished(iBlock, iDirection) = true;
509 ++nPropagator_;
510 }
511 }
512 }
513 }
514 }
515
516 }
517
518 /*
519 * Compute a solution to the MDE and block concentrations.
520 */
521 template <class Block>
522 void PolymerTmpl<Block>::solve(double phiTot)
523 {
524
525 // Clear all propagators
526 for (int j = 0; j < nPropagator(); ++j) {
527 propagator(j).setIsSolved(false);
528 }
529
530 // Solve modified diffusion equation for all propagators in
531 // the order specified by function makePlan.
532 for (int j = 0; j < nPropagator(); ++j) {
533 UTIL_CHECK(propagator(j).isReady());
534 propagator(j).solve();
535 }
536
537 // Compute molecular partition function q_
538 q_ = block(0).propagator(0).computeQ();
539
540 // The Propagator::computeQ function returns a spatial average.
541 // Correct for partial occupation of the unit cell.k
542 q_ = q_/phiTot;
543
544 // Compute mu_ or phi_, depending on ensemble
545 if (ensemble() == Species::Closed) {
546 mu_ = log(phi_/q_);
547 } else
548 if (ensemble() == Species::Open) {
549 phi_ = exp(mu_)*q_;
550 }
551
552 // Compute block concentration fields
553 double prefactor = phi_ / ( q_ * length() );
554 for (int i = 0; i < nBlock(); ++i) {
555 block(i).computeConcentration(prefactor);
556 }
557
558 }
559
560}
561#endif
Descriptor and MDE solver for an acyclic block polymer.
Definition: PolymerTmpl.h:42
Block const & block(int id) const
Get a specified Block by const reference.
Definition: PolymerTmpl.h:283
virtual void readParameters(std::istream &in)
Read and initialize.
Definition: PolymerTmpl.h:351
virtual void makePlan()
Make a plan for order in which propagators should be computed.
Definition: PolymerTmpl.h:470
Block & block(int id)
Get a specified Block.
Definition: PolymerTmpl.h:276
PolymerTmpl()
Constructor.
Definition: PolymerTmpl.h:333
const Pair< int > & propagatorId(int id) const
Get propagator identifier, indexed by order of computation.
Definition: PolymerTmpl.h:291
int nBlock() const
Number of blocks.
Definition: PolymerTmpl.h:241
Propagator & propagator(int blockId, int directionId)
Get propagator for a specific block and direction.
Definition: PolymerTmpl.h:304
int nPropagator() const
Number of propagators (nBlock*2).
Definition: PolymerTmpl.h:248
Propagator const & propagator(int blockId, int directionId) const
Get a const propagator for a specific block and direction.
Definition: PolymerTmpl.h:313
~PolymerTmpl()
Destructor.
Definition: PolymerTmpl.h:347
Propagator & propagator(int id)
Get propagator indexed in order of computation.
Definition: PolymerTmpl.h:321
int nVertex() const
Number of vertices (junctions and chain ends).
Definition: PolymerTmpl.h:234
virtual void solve(double phiTot=1.0)
Solve modified diffusion equation.
Definition: PolymerTmpl.h:522
double length() const
Sum of the lengths of all blocks in the polymer.
Definition: PolymerTmpl.h:255
const Vertex & vertex(int id) const
Get a specified Vertex by const reference.
Definition: PolymerTmpl.h:269
Base class for a molecular species (polymer or solvent).
Definition: Species.h:30
A junction or chain end in a block polymer.
Definition: Vertex.h:26
int size() const
Get the number of attached blocks.
Definition: Vertex.h:105
Pair< int > const & inPropagatorId(int i) const
Get the block and direction of an incoming propagator.
Definition: Vertex.h:109
Dynamically allocatable contiguous array template.
Definition: DArray.h:32
Dynamically allocated Matrix.
Definition: DMatrix.h:25
void allocate(int capacity1, int capacity2)
Allocate memory for a matrix.
Definition: DMatrix.h:170
An array of exactly 2 objects.
Definition: Pair.h:24
An object that can read multiple parameters from file.
void setClassName(const char *className)
Set class name string.
#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
C++ namespace for polymer self-consistent field theory (PSCF).
Utility classes for scientific computation.
Definition: accumulators.mod:1