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