PSCF v1.4.0
cp/solvers/Mixture.tpp
1#ifndef PRDC_CL_MIXTURE_TPP
2#define PRDC_CL_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 Cp {
23
24 // Construction, destruction, and initialization
25
26 /*
27 * Constructor
28 */
29 template <int D, class PT, class ST, class TT>
31 : ds_(-1.0),
32 meshPtr_(nullptr),
33 unitCellPtr_(nullptr),
34 fieldIoPtr_(nullptr)
35 { setClassName("Mixture"); }
36
37 /*
38 * Destructor
39 */
40 template <int D, class PT, class ST, class TT>
43
44 /*
45 * Read all parameters and initialize.
46 */
47 template <int D, class PT, class ST, class TT>
49 {
50 // Read standard data for a mixture
52 UTIL_CHECK(nMonomer() > 0);
54
55 // Set ds_ parameter (only used in thread model)
57 // Thread model (ds_ is a required parameter)
58 read(in, "ds", ds_);
59 UTIL_CHECK(ds_ > 0);
60 } else {
61 // Bead model (set to 1.0, but unused)
62 ds_ = 1.0;
63 }
64
65 }
66
67 /*
68 * Create associations with Mesh, FFT, UnitCell, and WaveList objects.
69 */
70 template <int D, class PT, class ST, class TT>
72 FFTT const & fft,
73 UnitCell<D> const & cell,
74 WaveListT & waveList)
75 {
76 UTIL_CHECK(nMonomer() > 0);
77 UTIL_CHECK(nPolymer() + nSolvent() > 0);
78 UTIL_CHECK(mesh.size() > 0);
79 UTIL_CHECK(fft.isSetup());
80 UTIL_CHECK(mesh.dimensions() == fft.meshDimensions());
81 UTIL_CHECK(cell.nParameter() > 0);
82
83 // Assign member variables
84 meshPtr_ = &mesh;
85 unitCellPtr_ = &cell;
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 for (j = 0; j < polymer(i).nBlock(); ++j) {
92 polymer(i).block(j).associate(mesh, fft, cell, waveList);
93 }
94 }
95 }
97 // Create associations for all solvents (if any)
98 if (nSolvent() > 0) {
99 for (int i = 0; i < nSolvent(); ++i) {
100 solvent(i).associate(mesh);
102 }
103
104 }
105
106 /*
107 * Create an association with a FieldIo object, for file output.
108 */
109 template <int D, class PT, class ST, class TT>
111 { fieldIoPtr_ = &fieldIo; }
113 /*
114 * Allocate internal data containers in all solvers.
115 */
116 template <int D, class PT, class ST, class TT>
118 {
119 UTIL_CHECK(nMonomer() > 0);
120 UTIL_CHECK(nPolymer()+ nSolvent() > 0);
121 UTIL_CHECK(meshPtr_);
122 UTIL_CHECK(meshPtr_->size() > 0);
123 UTIL_CHECK(ds_ > 0);
124
125 // Allocate memory for all Block objects
126 if (nPolymer() > 0) {
127 allocateBlocks();
128 }
129
130 // Allocate memory for all Solvent objects
131 if (nSolvent() > 0) {
132 for (int i = 0; i < nSolvent(); ++i) {
133 solvent(i).allocate();
135 }
136
137 clearUnitCellData();
138 }
139
140 // Primary Computations
141
142 /*
143 * Compute concentrations (but not total free energy).
144 */
145 template <int D, class PT, class ST, class TT>
147 DArray<FieldT> & cFields)
148 {
149 UTIL_CHECK(meshPtr_);
150 UTIL_CHECK(mesh().size() > 0);
151 UTIL_CHECK(nMonomer() > 0);
152 UTIL_CHECK(nPolymer() + nSolvent() > 0);
153 UTIL_CHECK(wFields.capacity() == nMonomer());
154 UTIL_CHECK(cFields.capacity() == nMonomer());
156 int nm = nMonomer();
157 int monomerId;
158 int i, j;
159
160 // Clear all monomer concentration fields, check capacities
161 for (i = 0; i < nm; ++i) {
162 eqS(cFields[i], 0.0);
163 }
164
165 // Process polymer species
166 if (nPolymer() > 0) {
167
168 // Solve MDE for all polymers
169 for (i = 0; i < nPolymer(); ++i) {
170 polymer(i).compute(wFields);
171 }
172
173 // Add block contributions to monomer concentrations
174 for (i = 0; i < nPolymer(); ++i) {
175 for (j = 0; j < polymer(i).nBlock(); ++j) {
176 monomerId = polymer(i).block(j).monomerId();
177 UTIL_CHECK(monomerId >= 0);
178 UTIL_CHECK(monomerId < nm);
179 FieldT& monomerField = cFields[monomerId];
180 FieldT const & blockField = polymer(i).block(j).cField();
181 addEqV(monomerField, blockField);
183 }
184
185 }
186
187 // Process solvent species
188 if (nSolvent() > 0) {
189
190 // Compute solvent concentrations
191 for (i = 0; i < nSolvent(); ++i) {
192 monomerId = solvent(i).monomerId();
193 solvent(i).compute(wFields[monomerId]);
194 }
195
196 // Add solvent contributions to monomer concentrations
197 for (i = 0; i < nSolvent(); ++i) {
198 monomerId = solvent(i).monomerId();
199 UTIL_CHECK(monomerId >= 0);
200 UTIL_CHECK(monomerId < nm);
201 FieldT& monomerField = cFields[monomerId];
202 FieldT const & solventField = solvent(i).cField();
203 addEqV(monomerField, solventField);
204 }
205
206 }
207 }
208
210 // Parameter Modification
211
212 /*
213 * Reset statistical segment length for one monomer type.
214 */
215 template <int D, class PT, class ST, class TT>
216 void Mixture<D,PT,ST,TT>::setKuhn(int monomerId, double kuhn)
217 {
218 // Set new Kuhn length for relevant Monomer object
219 monomer(monomerId).setKuhn(kuhn);
220
221 // Update kuhn length for all blocks of this monomer type
222 for (int i = 0; i < nPolymer(); ++i) {
223 for (int j = 0; j < polymer(i).nBlock(); ++j) {
224 BlockT& block = polymer(i).block(j);
225 if (monomerId == block.monomerId()) {
226 block.setKuhn(kuhn);
227 }
228 }
229 }
230 }
231
232 /*
233 * Clear all data that depends on the unit cell parameters.
234 */
235 template <int D, class PT, class ST, class TT>
237 {
238 if (nPolymer() > 0) {
239 for (int i = 0; i < nPolymer(); ++i) {
240 polymer(i).clearUnitCellData();
241 }
242 }
243 }
244
245 #if 0
246 // Concentration Field Output
247
248 /*
249 * Combine cFields for all blocks and solvents into one DArray
250 */
251 template <int D, class PT, class ST, class TT> void
253 const
254 {
255 int np = nSolvent() + nBlock();
256 UTIL_CHECK(np > 0);
257 UTIL_CHECK(nMonomer() > 0);
258 int nx = mesh().size();
259 UTIL_CHECK(nx > 0);
260 int i, j;
261
262 // Check allocation of blockCFields, allocate if necessary
263 // Initialize all concentration values to zero
264 if (!blockCFields.isAllocated()) {
265 blockCFields.allocate(np);
266 }
267 UTIL_CHECK(blockCFields.capacity() == np);
268 for (i = 0; i < np; ++i) {
269 if (!blockCFields[i].isAllocated()) {
270 blockCFields[i].allocate(mesh().dimensions());
271 }
272 eqS(blockCFields[i], 0.0);
273 }
274
275 // Initialize section (block or solvent) index
276 int sectionId = 0;
277
278 // Process polymer species
279 if (nPolymer() > 0) {
280 for (i = 0; i < nPolymer(); ++i) {
281 for (j = 0; j < polymer(i).nBlock(); ++j) {
282 UTIL_CHECK(sectionId >= 0);
283 UTIL_CHECK(sectionId < np);
284 // Copy block r-grid c-field to blockCFields
285 blockCFields[sectionId] = polymer(i).block(j).cField();
286 sectionId++;
287 }
288 }
289 }
290 UTIL_CHECK(sectionId == nBlock());
291
292 // Process solvent species
293 if (nSolvent() > 0) {
294 for (i = 0; i < nSolvent(); ++i) {
295 UTIL_CHECK(sectionId >= 0);
296 UTIL_CHECK(sectionId < np);
297 // Copy solvent r-grid c-field to blockCFields
298 blockCFields[sectionId] = solvent(i).cField();
299 sectionId++;
300 }
301 }
302 UTIL_CHECK(sectionId == np);
303
304 }
305
306 /*
307 * Output all concentration fields in real space (r-grid) format for
308 * each block and solvent species to specified file.
309 */
310 template <int D, class PT, class ST, class TT>
311 void
312 Mixture<D,PT,ST,TT>::writeBlockCRGrid(std::string const & filename)
313 const
314 {
315 UTIL_CHECK(fieldIoPtr_);
316
317 // Create and allocate array to hold c field data
318 DArray<FieldT> blockCFields;
319 int np = nSolvent() + nBlock();
320 blockCFields.allocate(np);
321 for (int i = 0; i < np; i++) {
322 blockCFields[i].allocate(mesh().dimensions());
323 }
324
325 // Get c field data from the Mixture
326 createBlockCRGrid(blockCFields);
327
328 // Write block and solvent c field data to file
329 fieldIo().writeCFields(filename, blockCFields, unitCell());
330 }
331 #endif
332
333 #if 0
334 // Propagator field output
335
336 /*
337 * Write a specified slice of the propagator in r-grid format.
338 */
339 template <int D, class PT, class ST, class TT>
341 std::string const & filename,
342 int polymerId,
343 int blockId,
344 int directionId,
345 int sliceId) const
346 {
347 UTIL_CHECK(fieldIoPtr_);
348 UTIL_CHECK(polymerId >= 0);
349 UTIL_CHECK(polymerId < nPolymer());
350 PolymerT const& polymerRef = polymer(polymerId);
351 UTIL_CHECK(blockId >= 0);
352 UTIL_CHECK(blockId < polymerRef.nBlock());
353 UTIL_CHECK(directionId >= 0);
354 UTIL_CHECK(directionId <= 1);
355 UTIL_CHECK(unitCell().isInitialized());
356 PropagatorT const &
357 propagator = polymerRef.propagator(blockId, directionId);
358 FieldT const & field = propagator.q(sliceId);
359 fieldIo().writeCField(filename, field, unitCell());
360 }
361
362 /*
363 * Write the last (tail) slice of the propagator in r-grid format.
364 */
365 template <int D, class PT, class ST, class TT>
367 std::string const & filename,
368 int polymerId,
369 int blockId,
370 int directionId) const
371 {
372 UTIL_CHECK(fieldIoPtr_);
373 UTIL_CHECK(polymerId >= 0);
374 UTIL_CHECK(polymerId < nPolymer());
375 PolymerT const& polymerRef = polymer(polymerId);
376 UTIL_CHECK(blockId >= 0);
377 UTIL_CHECK(blockId < polymerRef.nBlock());
378 UTIL_CHECK(directionId >= 0);
379 UTIL_CHECK(directionId <= 1);
380 UTIL_CHECK(unitCell().isInitialized());
381 FieldT const &
382 field = polymerRef.propagator(blockId, directionId).tail();
383 fieldIo().writeCField(filename, field, unitCell());
384 }
385
386 /*
387 * Write the entire propagator for a specified block and direction.
388 */
389 template <int D, class PT, class ST, class TT>
391 std::string const & filename,
392 int polymerId,
393 int blockId,
394 int directionId) const
395 {
396 UTIL_CHECK(fieldIoPtr_);
397 UTIL_CHECK(polymerId >= 0);
398 UTIL_CHECK(polymerId < nPolymer());
399 PolymerT const& polymerRef = polymer(polymerId);
400 UTIL_CHECK(blockId >= 0);
401 UTIL_CHECK(blockId < polymerRef.nBlock());
402 UTIL_CHECK(directionId >= 0);
403 UTIL_CHECK(directionId <= 1);
404 UTIL_CHECK(unitCell().isInitialized());
405 PropagatorT const&
406 propagator = polymerRef.propagator(blockId, directionId);
407 int ns = propagator.ns();
408
409 // Open file
410 std::ofstream file;
411 fieldIo().fileMaster().openOutputFile(filename, file);
412
413 // Write header
414 fieldIo().writeFieldHeader(file, 1, unitCell());
415 file << "mesh" << std::endl
416 << " " << mesh().dimensions() << std::endl
417 << "nslice" << std::endl
418 << " " << ns << std::endl;
419
420 // Write data
421 bool hasHeader = false;
422 for (int i = 0; i < ns; ++i) {
423 file << "slice " << i << std::endl;
424 fieldIo().writeCField(file, propagator.q(i), unitCell(),
425 hasHeader);
426 }
427 file.close();
428 }
429
430 /*
431 * Write propagators for all blocks of all polymers to files.
432 */
433 template <int D, class PT, class ST, class TT>
434 void Mixture<D,PT,ST,TT>::writeQAll(std::string const & basename)
435 {
436 UTIL_CHECK(fieldIoPtr_);
437 std::string filename;
438 int np, nb, ip, ib, id;
439 np = nPolymer();
440 for (ip = 0; ip < np; ++ip) {
441 nb = polymer(ip).nBlock();
442 for (ib = 0; ib < nb; ++ib) {
443 for (id = 0; id < 2; ++id) {
444 filename = basename;
445 filename += "_";
446 filename += toString(ip);
447 filename += "_";
448 filename += toString(ib);
449 filename += "_";
450 filename += toString(id);
451 filename += ".rq";
452 writeQ(filename, ip, ib, id);
453 }
454 }
455 }
456 }
457 #endif
458
459} // namespace Cp
460} // namespace Pscf
461#endif
Solver and descriptor for a mixture of polymers and solvents.
typename TT::FFT FFTT
WaveList type.
int nPolymer() const
Get number of polymer species.
ScalarParam< Type > & read(std::istream &in, const char *label, Type &value)
Add and read a new required ScalarParam < Type > object.
virtual void readParameters(std::istream &in)
Read all parameters and initialize.
Monomer const & monomer(int id) const
Get a Monomer type descriptor by const reference.
typename TT::FieldIo FieldIoT
FieldIo type.
void setClassName(const char *className)
Set class name string.
void clearUnitCellData()
Clear all data that depends on the unit cell parameters.
typename TT::WaveList WaveListT
WaveList type.
int nSolvent() const
Get number of solvent (point particle) species.
void setKuhn(int monomerId, double kuhn)
Reset statistical segment length for one monomer type.
PolymerT & polymer(int id)
Get a polymer solver object by non-const reference.
void compute(DArray< FieldT > const &wFields, DArray< FieldT > &cFields)
Compute partition functions and concentrations.
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 allocate()
Allocate required internal memory for all solvers.
Mesh< D > const & mesh() const
Return associated Mesh<D> by const reference.
FieldIoT const & fieldIo() const
Return associated FieldIoT by const reference.
typename TT::Block BlockT
Block type, for a block in a block polymer.
void setFieldIo(FieldIoT const &fieldIo)
Create an association with a FieldIoT object.
int nMonomer() const
Get number of monomer types.
Description of a regular grid of points in a periodic domain.
Definition Mesh.h:61
IntVec< D > dimensions() const
Get an IntVec<D> of the grid dimensions.
Definition Mesh.h:217
int size() const
Get total number of grid points.
Definition Mesh.h:229
void readParameters(std::istream &in) override
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: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
#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
Complex-valued periodic fields (class templates).
Definition cp.mod:6
bool isThread()
Is the thread model in use ?
PSCF package top-level namespace.