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