PSCF v1.3.3
MixturePrdc.tpp
1#ifndef PRDC_MIXTURE_PRDC_TPP
2#define PRDC_MIXTURE_PRDC_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 "MixturePrdc.h"
12#include <prdc/crystal/UnitCell.h>
13#include <pscf/solvers/MixtureTmpl.tpp>
14#include <pscf/mesh/Mesh.h>
15#include <pscf/chem/Monomer.h>
16#include <pscf/chem/PolymerModel.h>
17#include <util/containers/DArray.h>
18#include <util/misc/ioUtil.h>
19#include <util/misc/FileMaster.h>
20
21namespace Pscf {
22namespace Prdc {
23
24 // Construction, destruction, and initialization
25
26 /*
27 * Constructor
28 */
29 template <int D, class PT, class ST, class TT>
31 : stress_(),
32 ds_(-1.0),
33 meshPtr_(nullptr),
34 unitCellPtr_(nullptr),
35 fieldIoPtr_(nullptr),
36 nParam_(0),
37 hasStress_(false),
38 isSymmetric_(false)
39 { setClassName("Mixture"); }
40
41 /*
42 * Destructor
43 */
44 template <int D, class PT, class ST, class TT>
47
48 /*
49 * Read all parameters and initialize.
50 */
51 template <int D, class PT, class ST, class TT>
53 {
54 // Read standard data for a mixture
56 UTIL_CHECK(nMonomer() > 0);
58
59 // Set ds_ parameter (only used in thread model)
61 // Thread model (ds_ is a required parameter)
62 read(in, "ds", ds_);
63 UTIL_CHECK(ds_ > 0);
64 } else {
65 // Bead model (set to 1.0, but unused)
66 ds_ = 1.0;
67 }
68
69 }
70
71 /*
72 * Create associations with Mesh, FFT, UnitCell, and WaveList objects.
73 */
74 template <int D, class PT, class ST, class TT>
76 FFTT const & fft,
77 UnitCell<D> const & cell,
78 WaveListT & waveList)
79 {
80 UTIL_CHECK(nMonomer() > 0);
81 UTIL_CHECK(nPolymer() + nSolvent() > 0);
82 UTIL_CHECK(mesh.size() > 0);
83 UTIL_CHECK(fft.isSetup());
84 UTIL_CHECK(mesh.dimensions() == fft.meshDimensions());
85 UTIL_CHECK(cell.nParameter() > 0);
86
87 // Assign member variables
88 meshPtr_ = &mesh;
89 unitCellPtr_ = &cell;
90 nParam_ = cell.nParameter();
91
92 // Create associations for all blocks, set nParams in Polymer objects
93 if (nPolymer() > 0) {
94 int i, j;
95 for (i = 0; i < nPolymer(); ++i) {
96 polymer(i).setNParams(nParam_);
97 for (j = 0; j < polymer(i).nBlock(); ++j) {
98 polymer(i).block(j).associate(mesh, fft, cell, waveList);
99 }
101 }
102
103 // Create associations for all solvents (if any)
104 if (nSolvent() > 0) {
105 for (int i = 0; i < nSolvent(); ++i) {
106 solvent(i).associate(mesh);
107 }
108 }
109
110 }
111
112 /*
113 * Create an association with a FieldIo object, for file output.
114 */
115 template <int D, class PT, class ST, class TT>
117 { fieldIoPtr_ = &fieldIo; }
118
119 /*
120 * Allocate internal data containers in all solvers.
121 */
122 template <int D, class PT, class ST, class TT>
124 {
125 UTIL_CHECK(nMonomer() > 0);
126 UTIL_CHECK(nPolymer()+ nSolvent() > 0);
127 UTIL_CHECK(meshPtr_);
128 UTIL_CHECK(meshPtr_->size() > 0);
129 UTIL_CHECK(ds_ > 0);
130
131 // Allocate memory for all Block objects
132 if (nPolymer() > 0) {
133 allocateBlocks();
134 }
135
136 // Allocate memory for all Solvent objects
137 if (nSolvent() > 0) {
138 for (int i = 0; i < nSolvent(); ++i) {
139 solvent(i).allocate();
140 }
141 }
142
143 clearUnitCellData();
144 }
145
146 // Primary Computations
147
148 /*
149 * Compute concentrations (but not total free energy).
150 */
151 template <int D, class PT, class ST, class TT>
153 DArray<FieldT> & cFields,
154 double phiTot)
155 {
156 UTIL_CHECK(meshPtr_);
157 UTIL_CHECK(mesh().size() > 0);
158 UTIL_CHECK(nMonomer() > 0);
160 UTIL_CHECK(wFields.capacity() == nMonomer());
161 UTIL_CHECK(cFields.capacity() == nMonomer());
162
163 int nm = nMonomer();
164 int monomerId;
165 int i, j;
166
167 // Clear all monomer concentration fields, check capacities
168 for (i = 0; i < nm; ++i) {
169 eqS(cFields[i], 0.0);
170 }
171
172 // Process polymer species
173 if (nPolymer() > 0) {
174
175 // Solve MDE for all polymers
176 for (i = 0; i < nPolymer(); ++i) {
177 polymer(i).compute(wFields, phiTot);
178 }
179
180 // Add block contributions to monomer concentrations
181 for (i = 0; i < nPolymer(); ++i) {
182 for (j = 0; j < polymer(i).nBlock(); ++j) {
183 monomerId = polymer(i).block(j).monomerId();
184 UTIL_CHECK(monomerId >= 0);
185 UTIL_CHECK(monomerId < nm);
186 FieldT& monomerField = cFields[monomerId];
187 FieldT const & blockField = polymer(i).block(j).cField();
188 addEqV(monomerField, blockField);
189 }
190 }
191
192 }
193
194 // Process solvent species
195 if (nSolvent() > 0) {
196
197 // Compute solvent concentrations
198 for (i = 0; i < nSolvent(); ++i) {
199 monomerId = solvent(i).monomerId();
200 solvent(i).compute(wFields[monomerId], phiTot);
201 }
202
203 // Add solvent contributions to monomer concentrations
204 for (i = 0; i < nSolvent(); ++i) {
205 monomerId = solvent(i).monomerId();
206 UTIL_CHECK(monomerId >= 0);
207 UTIL_CHECK(monomerId < nm);
208 FieldT& monomerField = cFields[monomerId];
209 FieldT const & solventField = solvent(i).cField();
210 addEqV(monomerField, solventField);
212
213 }
214
215 isSymmetric_ = false;
216 hasStress_ = false;
217 }
218
219 /*
220 * Set the isSymmetric boolean variable true or false.
221 */
222 template <int D, class PT, class ST, class TT>
224 { isSymmetric_ = isSymmetric; }
225
226 /*
227 * Compute total SCFT stress for this mixture.
228 */
229 template <int D, class PT, class ST, class TT>
231 {
232 UTIL_CHECK(nParam_ > 0);
233
234 int i, j;
235
236 // Initialize all stress components to zero
237 stress_.clear();
238 for (i = 0; i < nParam_; ++i) {
239 stress_.append(0.0);
240 }
241
242 if (nPolymer() > 0) {
243
244 // Compute stress for each polymer
245 for (i = 0; i < nPolymer(); ++i) {
246 polymer(i).computeStress();
247 }
248
249 // Accumulate total stress
250 for (i = 0; i < nParam_; ++i) {
251 for (j = 0; j < nPolymer(); ++j) {
252 stress_[i] += polymer(j).stress(i);
253 }
254 }
256 }
257
258 // Correct for possible partial occupation of the unit cell.
259 // Used in problems that contain a Mask, e.g., thin films.
260 for (i = 0; i < nParam_; ++i) {
261 stress_[i] /= phiTot;
262 }
263
264 // Note: Solvent does not contribute to derivatives of f_Helmholtz
265 // with respect to unit cell parameters at fixed volume fractions.
266
267 hasStress_ = true;
268 }
269
270 // Parameter Modification
271
272 /*
273 * Reset statistical segment length for one monomer type.
274 */
275 template <int D, class PT, class ST, class TT>
276 void MixturePrdc<D,PT,ST,TT>::setKuhn(int monomerId, double kuhn)
277 {
278 // Set new Kuhn length for relevant Monomer object
279 monomer(monomerId).setKuhn(kuhn);
280
281 // Update kuhn length for all blocks of this monomer type
282 for (int i = 0; i < nPolymer(); ++i) {
283 for (int j = 0; j < polymer(i).nBlock(); ++j) {
284 BlockT& block = polymer(i).block(j);
285 if (monomerId == block.monomerId()) {
286 block.setKuhn(kuhn);
287 }
288 }
289 }
290 hasStress_ = false;
292
293 /*
294 * Clear all data that depends on the unit cell parameters.
295 */
296 template <int D, class PT, class ST, class TT>
298 {
299 if (nPolymer() > 0) {
300 for (int i = 0; i < nPolymer(); ++i) {
301 polymer(i).clearUnitCellData();
302 }
303 }
304 stress_.clear();
305 hasStress_ = false;
307
308 // Concentration Field Output
309
310 /*
311 * Combine cFields for all blocks and solvents into one DArray
312 */
313 template <int D, class PT, class ST, class TT> void
315 const
316 {
317 int np = nSolvent() + nBlock();
318 UTIL_CHECK(np > 0);
319 UTIL_CHECK(nMonomer() > 0);
320 int nx = mesh().size();
321 UTIL_CHECK(nx > 0);
322 int i, j;
323
324 // Check allocation of blockCFields, allocate if necessary
325 // Initialize all concentration values to zero
326 if (!blockCFields.isAllocated()) {
327 blockCFields.allocate(np);
328 }
329 UTIL_CHECK(blockCFields.capacity() == np);
330 for (i = 0; i < np; ++i) {
331 if (!blockCFields[i].isAllocated()) {
332 blockCFields[i].allocate(mesh().dimensions());
334 eqS(blockCFields[i], 0.0);
335 }
336
337 // Initialize section (block or solvent) index
338 int sectionId = 0;
339
340 // Process polymer species
341 if (nPolymer() > 0) {
342 for (i = 0; i < nPolymer(); ++i) {
343 for (j = 0; j < polymer(i).nBlock(); ++j) {
344 UTIL_CHECK(sectionId >= 0);
345 UTIL_CHECK(sectionId < np);
346 // Copy block r-grid c-field to blockCFields
347 blockCFields[sectionId] = polymer(i).block(j).cField();
348 sectionId++;
349 }
350 }
351 }
352 UTIL_CHECK(sectionId == nBlock());
353
354 // Process solvent species
355 if (nSolvent() > 0) {
356 for (i = 0; i < nSolvent(); ++i) {
357 UTIL_CHECK(sectionId >= 0);
358 UTIL_CHECK(sectionId < np);
359 // Copy solvent r-grid c-field to blockCFields
360 blockCFields[sectionId] = solvent(i).cField();
361 sectionId++;
362 }
363 }
364 UTIL_CHECK(sectionId == np);
366 }
367
368 /*
369 * Output all concentration fields in real space (r-grid) format for
370 * each block and solvent species to specified file.
371 */
372 template <int D, class PT, class ST, class TT>
373 void
374 MixturePrdc<D,PT,ST,TT>::writeBlockCRGrid(std::string const & filename)
375 const
377 UTIL_CHECK(fieldIoPtr_);
378
379 // Create and allocate array to hold c field data
380 DArray<FieldT> blockCFields;
381 int np = nSolvent() + nBlock();
382 blockCFields.allocate(np);
383 for (int i = 0; i < np; i++) {
384 blockCFields[i].allocate(mesh().dimensions());
385 }
386
387 // Get c field data from the Mixture
388 createBlockCRGrid(blockCFields);
389
390 // Write block and solvent c field data to file
391 fieldIo().writeFieldsRGrid(filename, blockCFields,
392 unitCell(), isSymmetric_);
393 }
394
395 // Propagator field output
396
397 /*
398 * Write a specified slice of the propagator in r-grid format.
399 */
400 template <int D, class PT, class ST, class TT>
402 std::string const & filename,
403 int polymerId,
404 int blockId,
405 int directionId,
406 int sliceId) const
407 {
408 UTIL_CHECK(fieldIoPtr_);
409 UTIL_CHECK(polymerId >= 0);
410 UTIL_CHECK(polymerId < nPolymer());
411 PolymerT const& polymerRef = polymer(polymerId);
412 UTIL_CHECK(blockId >= 0);
413 UTIL_CHECK(blockId < polymerRef.nBlock());
414 UTIL_CHECK(directionId >= 0);
415 UTIL_CHECK(directionId <= 1);
416 UTIL_CHECK(unitCell().isInitialized());
417 PropagatorT const &
418 propagator = polymerRef.propagator(blockId, directionId);
419 FieldT const & field = propagator.q(sliceId);
420 fieldIo().writeFieldRGrid(filename, field, unitCell(), isSymmetric_);
421 }
422
423 /*
424 * Write the last (tail) slice of the propagator in r-grid format.
425 */
426 template <int D, class PT, class ST, class TT>
428 std::string const & filename,
429 int polymerId,
430 int blockId,
431 int directionId) const
432 {
433 UTIL_CHECK(fieldIoPtr_);
434 UTIL_CHECK(polymerId >= 0);
435 UTIL_CHECK(polymerId < nPolymer());
436 PolymerT const& polymerRef = polymer(polymerId);
437 UTIL_CHECK(blockId >= 0);
438 UTIL_CHECK(blockId < polymerRef.nBlock());
439 UTIL_CHECK(directionId >= 0);
440 UTIL_CHECK(directionId <= 1);
441 UTIL_CHECK(unitCell().isInitialized());
442 FieldT const &
443 field = polymerRef.propagator(blockId, directionId).tail();
444 fieldIo().writeFieldRGrid(filename, field, unitCell(), isSymmetric_);
445 }
446
447 /*
448 * Write the entire propagator for a specified block and direction.
449 */
450 template <int D, class PT, class ST, class TT>
452 std::string const & filename,
453 int polymerId,
454 int blockId,
455 int directionId) const
456 {
457 UTIL_CHECK(fieldIoPtr_);
458 UTIL_CHECK(polymerId >= 0);
459 UTIL_CHECK(polymerId < nPolymer());
460 PolymerT const& polymerRef = polymer(polymerId);
461 UTIL_CHECK(blockId >= 0);
462 UTIL_CHECK(blockId < polymerRef.nBlock());
463 UTIL_CHECK(directionId >= 0);
464 UTIL_CHECK(directionId <= 1);
465 UTIL_CHECK(unitCell().isInitialized());
466 PropagatorT const&
467 propagator = polymerRef.propagator(blockId, directionId);
468 int ns = propagator.ns();
469
470 // Open file
471 std::ofstream file;
472 fieldIo().fileMaster().openOutputFile(filename, file);
473
474 // Write header
475 fieldIo().writeFieldHeader(file, 1, unitCell(), isSymmetric_);
476 file << "mesh" << std::endl
477 << " " << mesh().dimensions() << std::endl
478 << "nslice" << std::endl
479 << " " << ns << std::endl;
480
481 // Write data
482 bool hasHeader = false;
483 for (int i = 0; i < ns; ++i) {
484 file << "slice " << i << std::endl;
485 fieldIo().writeFieldRGrid(file, propagator.q(i), unitCell(),
486 hasHeader);
487 }
488 file.close();
489 }
490
491 /*
492 * Write propagators for all blocks of all polymers to files.
493 */
494 template <int D, class PT, class ST, class TT>
495 void MixturePrdc<D,PT,ST,TT>::writeQAll(std::string const & basename)
496 {
497 UTIL_CHECK(fieldIoPtr_);
498 std::string filename;
499 int np, nb, ip, ib, id;
500 np = nPolymer();
501 for (ip = 0; ip < np; ++ip) {
502 nb = polymer(ip).nBlock();
503 for (ib = 0; ib < nb; ++ib) {
504 for (id = 0; id < 2; ++id) {
505 filename = basename;
506 filename += "_";
507 filename += toString(ip);
508 filename += "_";
509 filename += toString(ib);
510 filename += "_";
511 filename += toString(id);
512 filename += ".rq";
513 writeQ(filename, ip, ib, id);
514 }
515 }
516 }
517 }
518
519 // Stress Output
520
521 /*
522 * Write stress to output stream.
523 */
524 template <int D, class PT, class ST, class TT>
525 void MixturePrdc<D,PT,ST,TT>::writeStress(std::ostream& out) const
526 {
527 UTIL_CHECK(hasStress_);
528
529 out << "stress:" << std::endl;
530
531 for (int i = 0; i < nParam_; i++) {
532 out << Int(i, 5)
533 << " "
534 << Dbl(stress_[i], 18, 11)
535 << std::endl;
536 }
537 out << std::endl;
538 }
539
540} // namespace Prdc
541} // namespace Pscf
542#endif
Description of a regular grid of points in a periodic domain.
Definition Mesh.h:61
virtual void readParameters(std::istream &in)
Read parameters from file and initialize.
void createBlockCRGrid(DArray< FieldT > &blockCFields) const
Get c-fields for all blocks and solvents as array of r-grid fields.
MixturePrdc()
Constructor.
void setFieldIo(FieldIoT const &fieldIo)
Create an association with a FieldIoT object.
void allocate()
Allocate required internal memory for all solvers.
virtual void readParameters(std::istream &in)
Read all parameters and initialize.
void computeStress(double phiTot=1.0)
Compute derivatives of free energy w/ respect to cell parameters.
void writeQAll(std::string const &basename)
Write all propagators of all blocks, each to a separate file.
void writeQSlice(std::string const &filename, int polymerId, int blockId, int directionId, int segmentId) const
Write one slice of a propagator at fixed s in r-grid format.
ScalarParam< Type > & read(std::istream &in, const char *label, Type &value)
Add and read a new required ScalarParam < Type > object.
void associate(Mesh< D > const &mesh, FFTT const &fft, UnitCell< D > const &cell, WaveListT &waveList)
Create associations with Mesh, FFT, UnitCell, and WaveList objects.
void setClassName(const char *className)
Set class name string.
void clearUnitCellData()
Clear all data that depends on the unit cell parameters.
int nPolymer() const
Get number of polymer species.
typename TT::Propagator PropagatorT
Propagator type, for one direction within a block.
Definition MixturePrdc.h:78
Monomer const & monomer(int id) const
Get a Monomer type descriptor by const reference.
PT PolymerT
Polymer species solver type.
Definition MixtureTmpl.h:40
typename TT::FFT FFTT
WaveList type.
Definition MixturePrdc.h:84
typename TT::RField FieldT
Field type, for data defined on a real-space grid.
Definition MixturePrdc.h:81
void setIsSymmetric(bool isSymmetric)
Set the isSymmetric flag true or false.
int nMonomer() const
Get number of monomer types.
int nBlock() const
Get total number blocks among all polymer species.
PolymerT & polymer(int id)
Get a polymer solver object by non-const reference.
int nSolvent() const
Get number of solvent (point particle) species.
Mesh< D > const & mesh() const
Return associated Mesh<D> by const reference.
void compute(DArray< FieldT > const &wFields, DArray< FieldT > &cFields, double phiTot=1.0)
Compute partition functions and concentrations.
void writeStress(std::ostream &out) const
Write stress values to output stream.
typename TT::WaveList WaveListT
WaveList type.
Definition MixturePrdc.h:87
typename TT::Block BlockT
Block type, for a block in a block polymer.
Definition MixturePrdc.h:75
void writeQTail(std::string const &filename, int polymerId, int blockId, int directionId) const
Write the final slice of a propagator in r-grid format.
void writeQ(std::string const &filename, int polymerId, int blockId, int directionId) const
Write the complete propagator for one block, in r-grid format.
void setKuhn(int monomerId, double kuhn)
Reset statistical segment length for one monomer type.
void writeBlockCRGrid(std::string const &filename) const
Write c fields for all blocks and solvents in r-grid format.
int nParameter() const
Get the number of parameters in the unit cell.
Base template for UnitCell<D> classes, D=1, 2 or 3.
Definition UnitCell.h:56
int capacity() const
Return allocated size.
Definition Array.h:159
Dynamically allocatable contiguous array template.
Definition DArray.h:32
void allocate(int capacity)
Allocate the underlying C array.
Definition DArray.h:199
bool isAllocated() const
Return true if this DArray has been allocated, false otherwise.
Definition DArray.h:247
Wrapper for a double precision number, for formatted ostream output.
Definition Dbl.h:40
Wrapper for an int, for formatted ostream output.
Definition Int.h:37
#define UTIL_CHECK(condition)
Assertion macro suitable for serial or parallel production code.
Definition global.h:68
std::string toString(int n)
Return string representation of an integer.
Definition ioUtil.cpp:52
bool isThread()
Is the thread model in use ?
Periodic fields and crystallography.
Definition CField.cpp:11
PSCF package top-level namespace.