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