PSCF v1.1
pspc/System.tpp
1#ifndef PSPC_SYSTEM_TPP
2#define PSPC_SYSTEM_TPP
3
4/*
5* PSCF - Polymer Self-Consistent Field Theory
6*
7* Copyright 2016 - 2022, The Regents of the University of Minnesota
8* Distributed under the terms of the GNU General Public License.
9*/
10
11#include "System.h"
12
13#include <pspc/sweep/Sweep.h>
14#include <pspc/sweep/SweepFactory.h>
15#include <pspc/iterator/Iterator.h>
16#include <pspc/iterator/IteratorFactory.h>
17#include <pspc/solvers/Polymer.h>
18#include <pspc/solvers/Solvent.h>
19#include <pspc/field/BFieldComparison.h>
20#include <pspc/field/RFieldComparison.h>
21
22#include <pscf/inter/Interaction.h>
23#include <pscf/math/IntVec.h>
24#include <pscf/homogeneous/Clump.h>
25
26#include <util/param/BracketPolicy.h>
27#include <util/format/Str.h>
28#include <util/format/Int.h>
29#include <util/format/Dbl.h>
30#include <util/misc/ioUtil.h>
31
32#include <string>
33#include <unistd.h>
34
35namespace Pscf {
36namespace Pspc
37{
38
39 using namespace Util;
40
41 /*
42 * Constructor.
43 */
44 template <int D>
46 : mixture_(),
47 domain_(),
48 fileMaster_(),
49 homogeneous_(),
50 interactionPtr_(0),
51 iteratorPtr_(0),
52 iteratorFactoryPtr_(0),
53 sweepPtr_(0),
54 sweepFactoryPtr_(0),
55 w_(),
56 c_(),
57 h_(),
58 mask_(),
59 fHelmholtz_(0.0),
60 fIdeal_(0.0),
61 fInter_(0.0),
62 fExt_(0.0),
63 pressure_(0.0),
64 hasMixture_(false),
65 isAllocatedRGrid_(false),
66 isAllocatedBasis_(false),
67 hasCFields_(false),
68 hasFreeEnergy_(false)
69 {
70 setClassName("System");
71 domain_.setFileMaster(fileMaster_);
72 w_.setFieldIo(domain_.fieldIo());
73 h_.setFieldIo(domain_.fieldIo());
74 mask_.setFieldIo(domain_.fieldIo());
75 interactionPtr_ = new Interaction();
76 iteratorFactoryPtr_ = new IteratorFactory<D>(*this);
77 sweepFactoryPtr_ = new SweepFactory<D>(*this);
78 BracketPolicy::set(BracketPolicy::Optional);
79 }
80
81 /*
82 * Destructor.
83 */
84 template <int D>
86 {
87 if (interactionPtr_) {
88 delete interactionPtr_;
89 }
90 if (iteratorPtr_) {
91 delete iteratorPtr_;
92 }
93 if (iteratorFactoryPtr_) {
94 delete iteratorFactoryPtr_;
95 }
96 if (sweepPtr_) {
97 delete sweepPtr_;
98 }
99 if (sweepFactoryPtr_) {
100 delete sweepFactoryPtr_;
101 }
102 }
103
104 /*
105 * Process command line options.
106 */
107 template <int D>
108 void System<D>::setOptions(int argc, char **argv)
109 {
110 bool eflag = false; // echo
111 bool pFlag = false; // param file
112 bool cFlag = false; // command file
113 bool iFlag = false; // input prefix
114 bool oFlag = false; // output prefix
115 char* pArg = 0;
116 char* cArg = 0;
117 char* iArg = 0;
118 char* oArg = 0;
119
120 // Read program arguments
121 int c;
122 opterr = 0;
123 while ((c = getopt(argc, argv, "er:p:c:i:o:f")) != -1) {
124 switch (c) {
125 case 'e':
126 eflag = true;
127 break;
128 case 'p': // parameter file
129 pFlag = true;
130 pArg = optarg;
131 break;
132 case 'c': // command file
133 cFlag = true;
134 cArg = optarg;
135 break;
136 case 'i': // input prefix
137 iFlag = true;
138 iArg = optarg;
139 break;
140 case 'o': // output prefix
141 oFlag = true;
142 oArg = optarg;
143 break;
144 case '?':
145 Log::file() << "Unknown option -" << optopt << std::endl;
146 UTIL_THROW("Invalid command line option");
147 }
148 }
149
150 // Set flag to echo parameters as they are read.
151 if (eflag) {
153 }
154
155 // If option -p, set parameter file name
156 if (pFlag) {
157 fileMaster_.setParamFileName(std::string(pArg));
158 }
159
160 // If option -c, set command file name
161 if (cFlag) {
162 fileMaster_.setCommandFileName(std::string(cArg));
163 }
164
165 // If option -i, set path prefix for input files
166 if (iFlag) {
167 fileMaster_.setInputPrefix(std::string(iArg));
168 }
169
170 // If option -o, set path prefix for output files
171 if (oFlag) {
172 fileMaster_.setOutputPrefix(std::string(oArg));
173 }
174
175 }
176
177 /*
178 * Read parameters and initialize.
179 */
180 template <int D>
181 void System<D>::readParameters(std::istream& in)
182 {
183 // Read the Mixture{ ... } block
184 readParamComposite(in, mixture_);
185 hasMixture_ = true;
186
187 int nm = mixture_.nMonomer();
188 int np = mixture_.nPolymer();
189 int ns = mixture_.nSolvent();
190 UTIL_CHECK(nm > 0);
191 UTIL_CHECK(np >= 0);
192 UTIL_CHECK(ns >= 0);
193 UTIL_CHECK(np + ns > 0);
194
195 // Read the Interaction{ ... } block
196 interaction().setNMonomer(nm);
197 readParamComposite(in, interaction());
198
199 // Read the Domain{ ... } block
200 readParamComposite(in, domain_);
201
202 mixture_.setMesh(domain_.mesh());
203 mixture_.setupUnitCell(unitCell());
204
205 // Allocate field array members of System
206 allocateFieldsGrid();
207 if (domain_.basis().isInitialized()) {
208 allocateFieldsBasis();
209 }
210
211 // Optionally instantiate an Iterator object
212 std::string className;
213 bool isEnd;
214 iteratorPtr_ =
215 iteratorFactoryPtr_->readObjectOptional(in, *this, className,
216 isEnd);
217 if (!iteratorPtr_) {
218 Log::file() << "Notification: No iterator was constructed\n";
219 }
220
221 // Optionally instantiate a Sweep object
222 if (iteratorPtr_) {
223 sweepPtr_ =
224 sweepFactoryPtr_->readObjectOptional(in, *this, className,
225 isEnd);
226 }
227
228 // Initialize homogeneous object
229 // NOTE: THIS OBJECT IS NOT USED AT ALL.
230 homogeneous_.setNMolecule(np+ns);
231 homogeneous_.setNMonomer(nm);
232 initHomogeneous();
233
234 }
235
236 /*
237 * Read default parameter file.
238 */
239 template <int D>
240 void System<D>::readParam(std::istream& in)
241 {
242 readBegin(in, className().c_str());
243 readParameters(in);
244 readEnd(in);
245 }
246
247 /*
248 * Read default parameter file.
249 */
250 template <int D>
252 { readParam(fileMaster_.paramFile()); }
253
254 /*
255 * Read and execute commands from a specified command file.
256 */
257 template <int D>
258 void System<D>::readCommands(std::istream &in)
259 {
260 UTIL_CHECK(isAllocatedRGrid_);
261 std::string command, filename, inFileName, outFileName;
262
263 bool readNext = true;
264 while (readNext) {
265
266 in >> command;
267
268 if (in.eof()) {
269 break;
270 } else {
271 Log::file() << command << std::endl;
272 }
273
274 if (command == "FINISH") {
275 Log::file() << std::endl;
276 readNext = false;
277 } else
278 if (command == "READ_W_BASIS") {
279 readEcho(in, filename);
280 readWBasis(filename);
281 } else
282 if (command == "READ_W_RGRID") {
283 readEcho(in, filename);
284 readWRGrid(filename);
285 } else
286 if (command == "ESTIMATE_W_FROM_C") {
287 readEcho(in, inFileName);
288 estimateWfromC(inFileName);
289 } else
290 if (command == "SET_UNIT_CELL") {
291 UnitCell<D> unitCell;
292 in >> unitCell;
293 Log::file() << " " << unitCell << std::endl;
294 setUnitCell(unitCell);
295 } else
296 if (command == "COMPUTE") {
297 // Solve the modified diffusion equation, without iteration
298 compute();
299 } else
300 if (command == "ITERATE") {
301 // Attempt to iteratively solve a single SCFT problem
302 bool isContinuation = false;
303 int fail = iterate(isContinuation);
304 if (fail) {
305 readNext = false;
306 }
307 } else
308 if (command == "SWEEP") {
309 // Attempt to solve a sequence of SCFT problems along a path
310 // through parameter space
311 sweep();
312 } else
313 if (command == "WRITE_PARAM") {
314 readEcho(in, filename);
315 std::ofstream file;
316 fileMaster().openOutputFile(filename, file);
317 writeParamNoSweep(file);
318 file.close();
319 } else
320 if (command == "WRITE_THERMO") {
321 readEcho(in, filename);
322 std::ofstream file;
323 fileMaster().openOutputFile(filename, file,
324 std::ios_base::app);
325 writeThermo(file);
326 file.close();
327 } else
328 if (command == "WRITE_W_BASIS") {
329 readEcho(in, filename);
330 writeWBasis(filename);
331 } else
332 if (command == "WRITE_W_RGRID") {
333 readEcho(in, filename);
334 writeWRGrid(filename);
335 } else
336 if (command == "WRITE_C_BASIS") {
337 readEcho(in, filename);
338 writeCBasis(filename);
339 } else
340 if (command == "WRITE_C_RGRID") {
341 readEcho(in, filename);
342 writeCRGrid(filename);
343 } else
344 if (command == "WRITE_BLOCK_C_RGRID") {
345 readEcho(in, filename);
346 writeBlockCRGrid(filename);
347 } else
348 if (command == "WRITE_Q_SLICE") {
349 int polymerId, blockId, directionId, segmentId;
350 readEcho(in, filename);
351 in >> polymerId;
352 in >> blockId;
353 in >> directionId;
354 in >> segmentId;
355 Log::file() << Str("polymer ID ", 21) << polymerId << "\n"
356 << Str("block ID ", 21) << blockId << "\n"
357 << Str("direction ID ", 21) << directionId << "\n"
358 << Str("segment ID ", 21) << segmentId << std::endl;
359 writeQSlice(filename, polymerId, blockId, directionId,
360 segmentId);
361 } else
362 if (command == "WRITE_Q_TAIL") {
363 readEcho(in, filename);
364 int polymerId, blockId, directionId;
365 in >> polymerId;
366 in >> blockId;
367 in >> directionId;
368 Log::file() << Str("polymer ID ", 21) << polymerId << "\n"
369 << Str("block ID ", 21) << blockId << "\n"
370 << Str("direction ID ", 21) << directionId << "\n";
371 writeQTail(filename, polymerId, blockId, directionId);
372 } else
373 if (command == "WRITE_Q") {
374 readEcho(in, filename);
375 int polymerId, blockId, directionId;
376 in >> polymerId;
377 in >> blockId;
378 in >> directionId;
379 Log::file() << Str("polymer ID ", 21) << polymerId << "\n"
380 << Str("block ID ", 21) << blockId << "\n"
381 << Str("direction ID ", 21) << directionId << "\n";
382 writeQ(filename, polymerId, blockId, directionId);
383 } else
384 if (command == "WRITE_Q_ALL") {
385 readEcho(in, filename);
386 writeQAll(filename);
387 } else
388 if (command == "WRITE_STARS") {
389 readEcho(in, filename);
390 writeStars(filename);
391 } else
392 if (command == "WRITE_WAVES") {
393 readEcho(in, filename);
394 writeWaves(filename);
395 } else
396 if (command == "WRITE_GROUP") {
397 readEcho(in, filename);
398 writeGroup(filename);
399 } else
400 if (command == "BASIS_TO_RGRID") {
401 readEcho(in, inFileName);
402 readEcho(in, outFileName);
403 basisToRGrid(inFileName, outFileName);
404 } else
405 if (command == "RGRID_TO_BASIS") {
406 readEcho(in, inFileName);
407 readEcho(in, outFileName);
408 rGridToBasis(inFileName, outFileName);
409 } else
410 if (command == "KGRID_TO_RGRID") {
411 readEcho(in, inFileName);
412 readEcho(in, outFileName);
413 kGridToRGrid(inFileName, outFileName);
414 } else
415 if (command == "RGRID_TO_KGRID") {
416 readEcho(in, inFileName);
417 readEcho(in, outFileName);
418 rGridToKGrid(inFileName, outFileName);
419 } else
420 if (command == "BASIS_TO_KGRID") {
421 readEcho(in, inFileName);
422 readEcho(in, outFileName);
423 basisToKGrid(inFileName, outFileName);
424 } else
425 if (command == "KGRID_TO_BASIS") {
426 readEcho(in, inFileName);
427 readEcho(in, outFileName);
428 kGridToBasis(inFileName, outFileName);
429 } else
430 if (command == "CHECK_RGRID_SYMMETRY") {
431 double epsilon;
432 readEcho(in, inFileName);
433 readEcho(in, epsilon);
434 bool hasSymmetry;
435 hasSymmetry = checkRGridFieldSymmetry(inFileName, epsilon);
436 if (hasSymmetry) {
437 Log::file() << std::endl
438 << "Symmetry of r-grid file matches this space group."
439 << std::endl << std::endl;
440 } else {
441 Log::file() << std::endl
442 << "Symmetry of r-grid file does not match this space group"
443 << std::endl
444 << "to within error threshold of "
445 << Dbl(epsilon) << "."
446 << std::endl << std::endl;
447 }
448 } else
449 if (command == "COMPARE_BASIS") {
450
451 // Get two filenames for comparison
452 std::string filecompare1, filecompare2;
453 readEcho(in, filecompare1);
454 readEcho(in, filecompare2);
455
456 DArray< DArray<double> > Bfield1, Bfield2;
457 domain_.fieldIo().readFieldsBasis(filecompare1, Bfield1,
458 domain_.unitCell());
459 domain_.fieldIo().readFieldsBasis(filecompare2, Bfield2,
460 domain_.unitCell());
461 // Note: Bfield1 & Bfield2 are allocated by readFieldsBasis
462
463 // Compare and output report
464 compare(Bfield1, Bfield2);
465
466 } else
467 if (command == "COMPARE_RGRID") {
468 // Get two filenames for comparison
469 std::string filecompare1, filecompare2;
470 readEcho(in, filecompare1);
471 readEcho(in, filecompare2);
472
473 DArray< RField<D> > Rfield1, Rfield2;
474 domain_.fieldIo().readFieldsRGrid(filecompare1, Rfield1,
475 domain_.unitCell());
476 domain_.fieldIo().readFieldsRGrid(filecompare2, Rfield2,
477 domain_.unitCell());
478 // Note: Rfield1, Rfield2 will be allocated by readFieldsRGrid
479
480 // Compare and output report
481 compare(Rfield1, Rfield2);
482
483 } else
484 if (command == "READ_H_BASIS") {
485 readEcho(in, filename);
486 if (!h_.isAllocatedBasis()) {
487 h_.allocateBasis(basis().nBasis());
488 }
489 if (!h_.isAllocatedRGrid()) {
490 h_.allocateRGrid(mesh().dimensions());
491 }
492 h_.readBasis(filename, domain_.unitCell());
493 } else
494 if (command == "READ_H_RGRID") {
495 readEcho(in, filename);
496 if (!h_.isAllocatedRGrid()) {
497 h_.allocateRGrid(mesh().dimensions());
498 }
499 h_.readRGrid(filename, domain_.unitCell());
500 } else
501 if (command == "WRITE_H_BASIS") {
502 readEcho(in, filename);
503 UTIL_CHECK(h_.hasData());
504 UTIL_CHECK(h_.isSymmetric());
505 fieldIo().writeFieldsBasis(filename, h_.basis(), unitCell());
506 } else
507 if (command == "WRITE_H_RGRID") {
508 readEcho(in, filename);
509 UTIL_CHECK(h_.hasData());
510 fieldIo().writeFieldsRGrid(filename, h_.rgrid(), unitCell());
511 } else
512 if (command == "READ_MASK_BASIS") {
513 UTIL_CHECK(domain_.basis().isInitialized());
514 readEcho(in, filename);
515 if (!mask_.isAllocated()) {
516 mask_.allocate(basis().nBasis(), mesh().dimensions());
517 }
518 mask_.readBasis(filename, domain_.unitCell());
519 } else
520 if (command == "READ_MASK_RGRID") {
521 readEcho(in, filename);
522 if (!mask_.isAllocated()) {
523 mask_.allocate(basis().nBasis(), mesh().dimensions());
524 }
525 mask_.readBasis(filename, domain_.unitCell());
526 } else
527 if (command == "WRITE_MASK_BASIS") {
528 readEcho(in, filename);
529 UTIL_CHECK(mask_.hasData());
530 UTIL_CHECK(mask_.isSymmetric());
531 fieldIo().writeFieldBasis(filename, mask_.basis(), unitCell());
532 } else
533 if (command == "WRITE_MASK_RGRID") {
534 readEcho(in, filename);
535 UTIL_CHECK(mask_.hasData());
536 fieldIo().writeFieldRGrid(filename, mask_.rgrid(), unitCell());
537 } else {
538 Log::file() << "Error: Unknown command "
539 << command << std::endl;
540 readNext = false;
541 }
542 }
543 }
544
545 /*
546 * Read and execute commands from the default command file.
547 */
548 template <int D>
550 {
551 if (fileMaster_.commandFileName().empty()) {
552 UTIL_THROW("Empty command file name");
553 }
554 readCommands(fileMaster_.commandFile());
555 }
556
557 // W Field Modifier Functions
558
559 /*
560 * Read w-field in symmetry adapted basis format.
561 */
562 template <int D>
563 void System<D>::readWBasis(const std::string & filename)
564 {
565 // If basis fields are not allocated, peek at field file header to
566 // get unit cell parameters, initialize basis and allocate fields.
567 if (!isAllocatedBasis_) {
568 readFieldHeader(filename);
569 allocateFieldsBasis();
570 }
571
572 // Read w fields
573 w_.readBasis(filename, domain_.unitCell());
574 mixture_.setupUnitCell(domain_.unitCell());
575 hasCFields_ = false;
576 hasFreeEnergy_ = false;
577 }
578
579 /*
580 * Read w-fields in real-space grid (r-grid) format.
581 */
582 template <int D>
583 void System<D>::readWRGrid(const std::string & filename)
584 {
585
586 // If basis fields are not allocated, peek at field file header to
587 // get unit cell parameters, initialize basis and allocate fields.
588 if (!isAllocatedBasis_) {
589 readFieldHeader(filename);
590 allocateFieldsBasis();
591 }
592
593 // Read w fields
594 w_.readRGrid(filename, domain_.unitCell());
595 mixture_.setupUnitCell(domain_.unitCell());
596 hasCFields_ = false;
597 hasFreeEnergy_ = false;
598 }
599
600 /*
601 * Set new w-field values.
602 */
603 template <int D>
605 {
606 UTIL_CHECK(domain_.basis().isInitialized());
607 UTIL_CHECK(isAllocatedBasis_);
608 w_.setBasis(fields);
609 hasCFields_ = false;
610 hasFreeEnergy_ = false;
611 }
612
613 /*
614 * Set new w-field values, using r-grid fields as inputs.
615 */
616 template <int D>
617 void System<D>::setWRGrid(DArray< RField<D> > const & fields)
618 {
619 UTIL_CHECK(isAllocatedRGrid_);
620 w_.setRGrid(fields);
621 hasCFields_ = false;
622 hasFreeEnergy_ = false;
623 }
624
625 /*
626 * Construct estimate for w fields from c fields, by setting xi=0.
627 *
628 * Modifies wFields and wFieldsRGrid.
629 */
630 template <int D>
631 void System<D>::estimateWfromC(std::string const & filename)
632 {
633 UTIL_CHECK(hasMixture_);
634 const int nm = mixture_.nMonomer();
635 UTIL_CHECK(nm > 0);
636
637 // If basis fields are not allocated, peek at field file header to
638 // get unit cell parameters, initialize basis and allocate fields.
639 if (!isAllocatedBasis_) {
640 readFieldHeader(filename);
641 allocateFieldsBasis();
642 }
643 const int nb = domain_.basis().nBasis();
644 UTIL_CHECK(nb > 0);
645
646 // Read c fields and set unit cell
647 domain_.fieldIo().readFieldsBasis(filename, tmpFieldsBasis_,
648 domain_.unitCell());
649
650 DArray<double> wtmp;
651 wtmp.allocate(nm);
652
653 // Compute estimated w fields from c fields
654 int i, j, k;
655 for (i = 0; i < nb; ++i) {
656 for (j = 0; j < nm; ++j) {
657 wtmp[j] = 0.0;
658 for (k = 0; k < nm; ++k) {
659 wtmp[j] += interaction().chi(j,k)*tmpFieldsBasis_[k][i];
660 }
661 }
662 for (j = 0; j < nm; ++j) {
663 tmpFieldsBasis_[j][i] = wtmp[j];
664 }
665 }
666
667 // Store initial guess for w fields
668 w_.setBasis(tmpFieldsBasis_);
669
670 hasCFields_ = false;
671 hasFreeEnergy_ = false;
672 }
673
674 // Unit Cell Modifier / Setter
675
676 /*
677 * Set parameters of the system unit cell.
678 */
679 template <int D>
680 void System<D>::setUnitCell(UnitCell<D> const & unitCell)
681 {
682 domain_.setUnitCell(unitCell);
683 mixture_.setupUnitCell(domain_.unitCell());
684 if (!isAllocatedBasis_) {
685 allocateFieldsBasis();
686 }
687 }
688
689 /*
690 * Set state of the system unit cell.
691 */
692 template <int D>
693 void
695 FSArray<double, 6> const & parameters)
696 {
697 domain_.setUnitCell(lattice, parameters);
698 mixture_.setupUnitCell(domain_.unitCell());
699 if (!isAllocatedBasis_) {
700 allocateFieldsBasis();
701 }
702 }
703
704 /*
705 * Set parameters of the system unit cell.
706 */
707 template <int D>
709 {
710 domain_.setUnitCell(parameters);
711 mixture_.setupUnitCell(domain_.unitCell());
712 if (!isAllocatedBasis_) {
713 allocateFieldsBasis();
714 }
715 }
716
717 // Primary SCFT Computations
718
719 /*
720 * Solve MDE for current w-fields, without iteration.
721 */
722 template <int D>
723 void System<D>::compute(bool needStress)
724 {
725 UTIL_CHECK(w_.isAllocatedRGrid());
726 UTIL_CHECK(c_.isAllocatedRGrid());
727 UTIL_CHECK(w_.hasData());
728
729 // Solve the modified diffusion equation (without iteration)
730 mixture_.compute(w_.rgrid(), c_.rgrid(), mask_.phiTot());
731 hasCFields_ = true;
732 hasFreeEnergy_ = false;
733
734 // Compute stress if requested
735 if (needStress) {
736 mixture_.computeStress();
737 }
738
739 // If w fields are symmetric, compute basis components for c-fields
740 if (w_.isSymmetric()) {
741 UTIL_CHECK(c_.isAllocatedBasis());
742 domain_.fieldIo().convertRGridToBasis(c_.rgrid(), c_.basis(), false);
743 }
744
745 }
746
747 /*
748 * Iteratively solve a SCFT problem for specified parameters.
749 */
750 template <int D>
751 int System<D>::iterate(bool isContinuation)
752 {
753 UTIL_CHECK(iteratorPtr_);
754 UTIL_CHECK(w_.hasData());
755 UTIL_CHECK(w_.isSymmetric());
756 hasCFields_ = false;
757 hasFreeEnergy_ = false;
758
759 Log::file() << std::endl;
760 Log::file() << std::endl;
761
762 // Call iterator (return 0 for convergence, 1 for failure)
763 int error = iterator().solve(isContinuation);
764 hasCFields_ = true;
765
766 // If converged, compute related properties
767 if (!error) {
768 if (!iterator().isFlexible()) {
769 mixture().computeStress();
770 }
771 writeThermo(Log::file());
772 }
773 return error;
774 }
775
776 /*
777 * Perform sweep along a line in parameter space.
778 */
779 template <int D>
781 {
782 UTIL_CHECK(w_.hasData());
783 UTIL_CHECK(w_.isSymmetric());
784 UTIL_CHECK(hasSweep());
785 Log::file() << std::endl;
786 Log::file() << std::endl;
787
788 // Perform sweep
789 sweepPtr_->sweep();
790 }
791
792 // Thermodynamic Properties
793
794 /*
795 * Compute Helmoltz free energy and pressure
796 */
797 template <int D>
799 {
800 UTIL_CHECK(domain_.basis().isInitialized());
801 UTIL_CHECK(w_.hasData());
802 UTIL_CHECK(w_.isSymmetric());
803 UTIL_CHECK(hasCFields_);
804 UTIL_CHECK(!hasFreeEnergy_);
805
806 // Initialize to zero
807 fHelmholtz_ = 0.0;
808 fIdeal_ = 0.0;
809 fInter_ = 0.0;
810
811 double phi, mu;
812 int np = mixture_.nPolymer();
813 int ns = mixture_.nSolvent();
814
815 // Compute polymer ideal gas contributions to fHelhmoltz_
816 if (np > 0) {
817 Polymer<D>* polymerPtr;
818 double length;
819 for (int i = 0; i < np; ++i) {
820 polymerPtr = &mixture_.polymer(i);
821 phi = polymerPtr->phi();
822 mu = polymerPtr->mu();
823 length = polymerPtr->length();
824 // Recall: mu = ln(phi/q)
825 if (phi > 1.0E-08) {
826 fIdeal_ += phi*( mu - 1.0 )/length;
827 }
828 }
829 }
830
831 // Compute solvent ideal gas contributions to fHelhmoltz_
832 if (ns > 0) {
833 Solvent<D>* solventPtr;
834 double size;
835 for (int i = 0; i < ns; ++i) {
836 solventPtr = &mixture_.solvent(i);
837 phi = solventPtr->phi();
838 mu = solventPtr->mu();
839 size = solventPtr->size();
840 if (phi > 1.0E-08) {
841 fIdeal_ += phi*( mu - 1.0 )/size;
842 }
843 }
844 }
845
846 int nm = mixture_.nMonomer();
847 int nBasis = domain_.basis().nBasis();
848
849 double temp(0.0);
850 // Compute Legendre transform subtraction
851 // Use expansion in symmetry-adapted orthonormal basis
852 for (int i = 0; i < nm; ++i) {
853 for (int k = 0; k < nBasis; ++k) {
854 temp -= w_.basis(i)[k] * c_.basis(i)[k];
855 }
856 }
857
858 // If the system has a mask, then the volume that should be used
859 // in calculating free energy/pressure is the volume available to
860 // the polymers, not the total unit cell volume. We thus divide
861 // all terms that involve integrating over the unit cell volume by
862 // mask().phiTot(), the volume fraction of the unit cell that is
863 // occupied by the polymers. This properly scales them to the
864 // correct value. fExt_, fInter_, and the Legendre transform
865 // component of fIdeal_ all require this scaling. If no mask is
866 // present, mask.phiTot() = 1 and no scaling occurs.
867 temp /= mask().phiTot();
868 fIdeal_ += temp;
869 fHelmholtz_ += fIdeal_;
870
871 // Compute contribution from external fields, if fields exist
872 if (hasExternalFields()) {
873 fExt_ = 0.0;
874 for (int i = 0; i < nm; ++i) {
875 for (int k = 0; k < nBasis; ++k) {
876 fExt_ += h_.basis(i)[k] * c_.basis(i)[k];
877 }
878 }
879 fExt_ /= mask().phiTot();
880 fHelmholtz_ += fExt_;
881 }
882
883 // Compute excess interaction free energy [ phi^{T}*chi*phi ]
884 double chi;
885 for (int i = 0; i < nm; ++i) {
886 for (int j = i + 1; j < nm; ++j) {
887 chi = interaction().chi(i,j);
888 for (int k = 0; k < nBasis; ++k) {
889 fInter_ += chi * c_.basis(i)[k] * c_.basis(j)[k];
890 }
891 }
892 }
893 fInter_ /= mask().phiTot();
894 fHelmholtz_ += fInter_;
895
896 // Initialize pressure
897 pressure_ = -fHelmholtz_;
898
899 // Polymer corrections to pressure
900 if (np > 0) {
901 Polymer<D>* polymerPtr;
902 double length;
903 for (int i = 0; i < np; ++i) {
904 polymerPtr = &mixture_.polymer(i);
905 phi = polymerPtr->phi();
906 mu = polymerPtr->mu();
907 length = polymerPtr->length();
908 if (phi > 1E-08) {
909 pressure_ += mu * phi /length;
910 }
911 }
912 }
913
914 // Solvent corrections to pressure
915 if (ns > 0) {
916 Solvent<D>* solventPtr;
917 double size;
918 for (int i = 0; i < ns; ++i) {
919 solventPtr = &mixture_.solvent(i);
920 phi = solventPtr->phi();
921 mu = solventPtr->mu();
922 size = solventPtr->size();
923 if (phi > 1E-08) {
924 pressure_ += mu * phi /size;
925 }
926 }
927 }
928
929 hasFreeEnergy_ = true;
930 }
931
932 // Output Operations
933
934 /*
935 * Write parameter file, omitting any sweep block.
936 */
937 template <int D>
938 void System<D>::writeParamNoSweep(std::ostream& out) const
939 {
940 out << "System{" << std::endl;
941 mixture().writeParam(out);
942 interaction().writeParam(out);
943 domain_.writeParam(out);
944 if (iteratorPtr_) {
945 iterator().writeParam(out);
946 }
947 out << "}" << std::endl;
948 }
949
950 /*
951 * Write thermodynamic properties to file.
952 */
953 template <int D>
954 void System<D>::writeThermo(std::ostream& out)
955 {
956 if (!hasFreeEnergy_) {
957 computeFreeEnergy();
958 }
959
960 out << std::endl;
961 out << "fHelmholtz " << Dbl(fHelmholtz(), 18, 11) << std::endl;
962 out << "pressure " << Dbl(pressure(), 18, 11) << std::endl;
963 out << std::endl;
964 out << "fIdeal " << Dbl(fIdeal_, 18, 11) << std::endl;
965 out << "fInter " << Dbl(fInter_, 18, 11) << std::endl;
966 if (hasExternalFields()) {
967 out << "fExt " << Dbl(fExt_, 18, 11) << std::endl;
968 }
969 out << std::endl;
970
971 int np = mixture_.nPolymer();
972 int ns = mixture_.nSolvent();
973
974 if (np > 0) {
975 out << "polymers:" << std::endl;
976 out << " "
977 << " phi "
978 << " mu "
979 << std::endl;
980 for (int i = 0; i < np; ++i) {
981 out << Int(i, 5)
982 << " " << Dbl(mixture_.polymer(i).phi(),18, 11)
983 << " " << Dbl(mixture_.polymer(i).mu(), 18, 11)
984 << std::endl;
985 }
986 out << std::endl;
987 }
988
989 if (ns > 0) {
990 out << "solvents:" << std::endl;
991 out << " "
992 << " phi "
993 << " mu "
994 << std::endl;
995 for (int i = 0; i < ns; ++i) {
996 out << Int(i, 5)
997 << " " << Dbl(mixture_.solvent(i).phi(),18, 11)
998 << " " << Dbl(mixture_.solvent(i).mu(), 18, 11)
999 << std::endl;
1000 }
1001 out << std::endl;
1002 }
1003
1004 out << "cellParams:" << std::endl;
1005 for (int i = 0; i < unitCell().nParameter(); ++i) {
1006 out << Int(i, 5)
1007 << " "
1008 << Dbl(unitCell().parameter(i), 18, 11)
1009 << std::endl;
1010 }
1011 out << std::endl;
1012 }
1013
1014 /*
1015 * Write w-fields in symmetry-adapted basis format.
1016 */
1017 template <int D>
1018 void System<D>::writeWBasis(std::string const & filename) const
1019 {
1020 UTIL_CHECK(domain_.basis().isInitialized());
1021 UTIL_CHECK(isAllocatedBasis_);
1022 UTIL_CHECK(w_.hasData());
1023 UTIL_CHECK(w_.isSymmetric());
1024 domain_.fieldIo().writeFieldsBasis(filename, w_.basis(),
1025 domain_.unitCell());
1026 }
1027
1028 /*
1029 * Write w-fields in real space grid file format.
1030 */
1031 template <int D>
1032 void System<D>::writeWRGrid(const std::string & filename) const
1033 {
1034 UTIL_CHECK(isAllocatedRGrid_);
1035 UTIL_CHECK(w_.hasData());
1036 domain_.fieldIo().writeFieldsRGrid(filename, w_.rgrid(),
1037 domain_.unitCell());
1038 }
1039
1040 /*
1041 * Write all concentration fields in symmetry-adapted basis format.
1042 */
1043 template <int D>
1044 void System<D>::writeCBasis(const std::string & filename) const
1045 {
1046 UTIL_CHECK(domain_.basis().isInitialized());
1047 UTIL_CHECK(isAllocatedBasis_);
1048 UTIL_CHECK(hasCFields_);
1049 UTIL_CHECK(w_.isSymmetric());
1050 domain_.fieldIo().writeFieldsBasis(filename, c_.basis(),
1051 domain_.unitCell());
1052 }
1053
1054 /*
1055 * Write all concentration fields in real space (r-grid) format.
1056 */
1057 template <int D>
1058 void System<D>::writeCRGrid(const std::string & filename) const
1059 {
1060 UTIL_CHECK(isAllocatedRGrid_);
1061 UTIL_CHECK(hasCFields_);
1062 domain_.fieldIo().writeFieldsRGrid(filename, c_.rgrid(),
1063 domain_.unitCell());
1064 }
1065
1066 /*
1067 * Write all concentration fields in real space (r-grid) format, for
1068 * each block (or solvent) individually rather than for each species.
1069 */
1070 template <int D>
1071 void System<D>::writeBlockCRGrid(const std::string & filename) const
1072 {
1073 UTIL_CHECK(isAllocatedRGrid_);
1074 UTIL_CHECK(hasCFields_);
1075
1076 // Create and allocate the DArray of fields to be written
1077 DArray< RField<D> > blockCFields;
1078 blockCFields.allocate(mixture_.nSolvent() + mixture_.nBlock());
1079 int n = blockCFields.capacity();
1080 for (int i = 0; i < n; i++) {
1081 blockCFields[i].allocate(domain_.mesh().dimensions());
1082 }
1083
1084 // Get data from Mixture and write to file
1085 mixture_.createBlockCRGrid(blockCFields);
1086 domain_.fieldIo().writeFieldsRGrid(filename, blockCFields,
1087 domain_.unitCell());
1088 }
1089
1090 /*
1091 * Write the last time slice of the propagator in r-grid format.
1092 */
1093 template <int D>
1094 void System<D>::writeQSlice(const std::string & filename,
1095 int polymerId, int blockId,
1096 int directionId, int segmentId)
1097 const
1098 {
1099 UTIL_CHECK(polymerId >= 0);
1100 UTIL_CHECK(polymerId < mixture_.nPolymer());
1101 Polymer<D> const& polymer = mixture_.polymer(polymerId);
1102 UTIL_CHECK(blockId >= 0);
1103 UTIL_CHECK(blockId < polymer.nBlock());
1104 UTIL_CHECK(directionId >= 0);
1105 UTIL_CHECK(directionId <= 1);
1106 Propagator<D> const &
1107 propagator = polymer.propagator(blockId, directionId);
1108 RField<D> const& field = propagator.q(segmentId);
1109 domain_.fieldIo().writeFieldRGrid(filename, field,
1110 domain_.unitCell());
1111 }
1112
1113 /*
1114 * Write the last time slice of the propagator in r-grid format.
1115 */
1116 template <int D>
1117 void System<D>::writeQTail(const std::string & filename,
1118 int polymerId, int blockId, int directionId)
1119 const
1120 {
1121 UTIL_CHECK(polymerId >= 0);
1122 UTIL_CHECK(polymerId < mixture_.nPolymer());
1123 Polymer<D> const& polymer = mixture_.polymer(polymerId);
1124 UTIL_CHECK(blockId >= 0);
1125 UTIL_CHECK(blockId < polymer.nBlock());
1126 UTIL_CHECK(directionId >= 0);
1127 UTIL_CHECK(directionId <= 1);
1128 RField<D> const&
1129 field = polymer.propagator(blockId, directionId).tail();
1130 domain_.fieldIo().writeFieldRGrid(filename, field,
1131 domain_.unitCell());
1132 }
1133
1134 /*
1135 * Write the propagator for a block and direction.
1136 */
1137 template <int D>
1138 void System<D>::writeQ(const std::string & filename,
1139 int polymerId, int blockId, int directionId)
1140 const
1141 {
1142 UTIL_CHECK(polymerId >= 0);
1143 UTIL_CHECK(polymerId < mixture_.nPolymer());
1144 Polymer<D> const& polymer = mixture_.polymer(polymerId);
1145 UTIL_CHECK(blockId >= 0);
1146 UTIL_CHECK(blockId < polymer.nBlock());
1147 UTIL_CHECK(directionId >= 0);
1148 UTIL_CHECK(directionId <= 1);
1149 Propagator<D> const& propagator
1150 = polymer.propagator(blockId, directionId);
1151 int ns = propagator.ns();
1152
1153 // Open file
1154 std::ofstream file;
1155 fileMaster_.openOutputFile(filename, file);
1156
1157 // Write header
1158 fieldIo().writeFieldHeader(file, 1, domain_.unitCell());
1159 file << "mesh " << std::endl
1160 << " " << domain_.mesh().dimensions() << std::endl
1161 << "nslice" << std::endl
1162 << " " << ns << std::endl;
1163
1164 // Write data
1165 bool hasHeader = false;
1166 for (int i = 0; i < ns; ++i) {
1167 file << "slice " << i << std::endl;
1168 fieldIo().writeFieldRGrid(file, propagator.q(i),
1169 domain_.unitCell(), hasHeader);
1170 }
1171 file.close();
1172 }
1173
1174 /*
1175 * Write propagators for all blocks of all polymers to files.
1176 */
1177 template <int D>
1178 void System<D>::writeQAll(std::string const & basename)
1179 {
1180 std::string filename;
1181 int np, nb, ip, ib, id;
1182 np = mixture_.nPolymer();
1183 for (ip = 0; ip < np; ++ip) {
1184 nb = mixture_.polymer(ip).nBlock();
1185 for (ib = 0; ib < nb; ++ib) {
1186 for (id = 0; id < 2; ++id) {
1187 filename = basename;
1188 filename += "_";
1189 filename += toString(ip);
1190 filename += "_";
1191 filename += toString(ib);
1192 filename += "_";
1193 filename += toString(id);
1194 filename += ".rq";
1195 writeQ(filename, ip, ib, id);
1196 }
1197 }
1198 }
1199 }
1200
1201 /*
1202 * Write description of symmetry-adapted stars and basis to file.
1203 */
1204 template <int D>
1205 void System<D>::writeStars(std::string const & filename) const
1206 {
1207 UTIL_CHECK(domain_.basis().isInitialized());
1208 std::ofstream file;
1209 fileMaster_.openOutputFile(filename, file);
1210 fieldIo().writeFieldHeader(file, mixture_.nMonomer(),
1211 domain_.unitCell());
1212 domain_.basis().outputStars(file);
1213 file.close();
1214 }
1215
1216 /*
1217 * Write a list of waves and associated stars to file.
1218 */
1219 template <int D>
1220 void System<D>::writeWaves(std::string const & filename) const
1221 {
1222 UTIL_CHECK(domain_.basis().isInitialized());
1223 std::ofstream file;
1224 fileMaster_.openOutputFile(filename, file);
1225 fieldIo().writeFieldHeader(file, mixture_.nMonomer(),
1226 domain_.unitCell());
1227 domain_.basis().outputWaves(file);
1228 file.close();
1229 }
1230
1231 /*
1232 * Write all elements of the space group to a file.
1233 */
1234 template <int D>
1235 void System<D>::writeGroup(const std::string & filename) const
1236 {
1237 Pscf::writeGroup(filename, domain_.group());
1238 }
1239
1240 // Field format conversion functions
1241
1242 /*
1243 * Convert fields from symmetry-adapted basis to real-space grid format.
1244 */
1245 template <int D>
1246 void System<D>::basisToRGrid(const std::string & inFileName,
1247 const std::string & outFileName)
1248 {
1249 // If basis fields are not allocated, peek at field file header to
1250 // get unit cell parameters, initialize basis and allocate fields.
1251 if (!isAllocatedBasis_) {
1252 readFieldHeader(inFileName);
1253 allocateFieldsBasis();
1254 }
1255
1256 // Read, convert, and write fields
1257 UnitCell<D> tmpUnitCell;
1258 fieldIo().readFieldsBasis(inFileName, tmpFieldsBasis_, tmpUnitCell);
1259 fieldIo().convertBasisToRGrid(tmpFieldsBasis_, tmpFieldsRGrid_);
1260 fieldIo().writeFieldsRGrid(outFileName, tmpFieldsRGrid_,
1261 tmpUnitCell);
1262 }
1263
1264 /*
1265 * Convert fields from real-space grid to symmetry-adapted basis format.
1266 */
1267 template <int D>
1268 void System<D>::rGridToBasis(const std::string & inFileName,
1269 const std::string & outFileName)
1270 {
1271 // If basis fields are not allocated, peek at field file header to
1272 // get unit cell parameters, initialize basis and allocate fields.
1273 if (!isAllocatedBasis_) {
1274 readFieldHeader(inFileName);
1275 allocateFieldsBasis();
1276 }
1277
1278 // Read, convert and write fields
1279 UnitCell<D> tmpUnitCell;
1280 fieldIo().readFieldsRGrid(inFileName, tmpFieldsRGrid_, tmpUnitCell);
1281 fieldIo().convertRGridToBasis(tmpFieldsRGrid_, tmpFieldsBasis_);
1282 fieldIo().writeFieldsBasis(outFileName, tmpFieldsBasis_,
1283 tmpUnitCell);
1284 }
1285
1286 /*
1287 * Convert fields from Fourier (k-grid) to real-space (r-grid) format.
1288 */
1289 template <int D>
1290 void System<D>::kGridToRGrid(const std::string & inFileName,
1291 const std::string& outFileName)
1292 {
1293 // If basis fields are not allocated, peek at field file header to
1294 // get unit cell parameters, initialize basis and allocate fields.
1295 if (!isAllocatedBasis_) {
1296 readFieldHeader(inFileName);
1297 allocateFieldsBasis();
1298 }
1299
1300 // Read, convert and write fields
1301 UnitCell<D> tmpUnitCell;
1302 fieldIo().readFieldsKGrid(inFileName, tmpFieldsKGrid_, tmpUnitCell);
1303 for (int i = 0; i < mixture_.nMonomer(); ++i) {
1304 domain_.fft().inverseTransform(tmpFieldsKGrid_[i],
1305 tmpFieldsRGrid_[i]);
1306 }
1307 fieldIo().writeFieldsRGrid(outFileName, tmpFieldsRGrid_,
1308 tmpUnitCell);
1309 }
1310
1311 /*
1312 * Convert fields from real-space (r-grid) to Fourier (k-grid) format.
1313 */
1314 template <int D>
1315 void System<D>::rGridToKGrid(const std::string & inFileName,
1316 const std::string & outFileName)
1317 {
1318 // If basis fields are not allocated, peek at field file header to
1319 // get unit cell parameters, initialize basis and allocate fields.
1320 if (!isAllocatedBasis_) {
1321 readFieldHeader(inFileName);
1322 allocateFieldsBasis();
1323 }
1324
1325 // Read, convert and write fields
1326 UnitCell<D> tmpUnitCell;
1327 fieldIo().readFieldsRGrid(inFileName, tmpFieldsRGrid_,
1328 tmpUnitCell);
1329 for (int i = 0; i < mixture_.nMonomer(); ++i) {
1330 domain_.fft().forwardTransform(tmpFieldsRGrid_[i],
1331 tmpFieldsKGrid_[i]);
1332 }
1333 domain_.fieldIo().writeFieldsKGrid(outFileName, tmpFieldsKGrid_,
1334 tmpUnitCell);
1335 }
1336
1337 /*
1338 * Convert fields from Fourier (k-grid) to symmetry-adapted basis format.
1339 */
1340 template <int D>
1341 void System<D>::kGridToBasis(const std::string & inFileName,
1342 const std::string& outFileName)
1343 {
1344 // If basis fields are not allocated, peek at field file header to
1345 // get unit cell parameters, initialize basis and allocate fields.
1346 if (!isAllocatedBasis_) {
1347 readFieldHeader(inFileName);
1348 allocateFieldsBasis();
1349 }
1350
1351 // Read, convert and write fields
1352 UnitCell<D> tmpUnitCell;
1353 domain_.fieldIo().readFieldsKGrid(inFileName, tmpFieldsKGrid_,
1354 tmpUnitCell);
1355 domain_.fieldIo().convertKGridToBasis(tmpFieldsKGrid_,
1356 tmpFieldsBasis_);
1357 domain_.fieldIo().writeFieldsBasis(outFileName,
1358 tmpFieldsBasis_, tmpUnitCell);
1359 }
1360
1361 /*
1362 * Convert fields from symmetry-adapted basis to Fourier (k-grid) format.
1363 */
1364 template <int D>
1365 void System<D>::basisToKGrid(const std::string & inFileName,
1366 const std::string & outFileName)
1367 {
1368 // If basis fields are not allocated, peek at field file header to
1369 // get unit cell parameters, initialize basis and allocate fields.
1370 if (!isAllocatedBasis_) {
1371 readFieldHeader(inFileName);
1372 allocateFieldsBasis();
1373 }
1374
1375 // Read, convert and write fields
1376 UnitCell<D> tmpUnitCell;
1377 domain_.fieldIo().readFieldsBasis(inFileName,
1378 tmpFieldsBasis_, tmpUnitCell);
1379 domain_.fieldIo().convertBasisToKGrid(tmpFieldsBasis_,
1380 tmpFieldsKGrid_);
1381 domain_.fieldIo().writeFieldsKGrid(outFileName,
1382 tmpFieldsKGrid_, tmpUnitCell);
1383 }
1384
1385 /*
1386 * Convert fields from real-space grid to symmetry-adapted basis format.
1387 */
1388 template <int D>
1389 bool System<D>::checkRGridFieldSymmetry(const std::string & inFileName,
1390 double epsilon)
1391 {
1392 // If basis fields are not allocated, peek at field file header to
1393 // get unit cell parameters, initialize basis and allocate fields.
1394 if (!isAllocatedBasis_) {
1395 readFieldHeader(inFileName);
1396 allocateFieldsBasis();
1397 }
1398
1399 // Read fields
1400 UnitCell<D> tmpUnitCell;
1401 domain_.fieldIo().readFieldsRGrid(inFileName,
1402 tmpFieldsRGrid_, tmpUnitCell);
1403
1404 // Check symmetry for all fields
1405 for (int i = 0; i < mixture_.nMonomer(); ++i) {
1406 bool symmetric;
1407 symmetric = domain_.fieldIo().hasSymmetry(tmpFieldsRGrid_[i],
1408 epsilon);
1409 if (!symmetric) {
1410 return false;
1411 }
1412 }
1413 return true;
1414
1415 }
1416
1417 /*
1418 * Compare two fields in basis format.
1419 */
1420 template <int D>
1422 const DArray< DArray<double> > field2)
1423 {
1424 BFieldComparison comparison(1);
1425 comparison.compare(field1,field2);
1426
1427 Log::file() << "\n Basis expansion field comparison results"
1428 << std::endl;
1429 Log::file() << " Maximum Absolute Difference: "
1430 << comparison.maxDiff() << std::endl;
1431 Log::file() << " Root-Mean-Square Difference: "
1432 << comparison.rmsDiff() << "\n" << std::endl;
1433 }
1434
1435 /*
1436 * Compare two fields in coordinate grid format.
1437 */
1438 template <int D>
1439 void System<D>::compare(const DArray< RField<D> > field1,
1440 const DArray< RField<D> > field2)
1441 {
1442 RFieldComparison<D> comparison;
1443 comparison.compare(field1, field2);
1444
1445 Log::file() << "\n Real-space field comparison results"
1446 << std::endl;
1447 Log::file() << " Maximum Absolute Difference: "
1448 << comparison.maxDiff() << std::endl;
1449 Log::file() << " Root-Mean-Square Difference: "
1450 << comparison.rmsDiff() << "\n" << std::endl;
1451 }
1452
1453 // Private member functions
1454
1455 /*
1456 * Allocate memory for fields.
1457 */
1458 template <int D>
1460 {
1461 // Preconditions
1462 UTIL_CHECK(hasMixture_);
1463 int nMonomer = mixture_.nMonomer();
1464 UTIL_CHECK(nMonomer > 0);
1465 UTIL_CHECK(domain_.mesh().size() > 0);
1466 UTIL_CHECK(!isAllocatedRGrid_);
1467
1468 // Alias for mesh dimensions
1469 IntVec<D> const & dimensions = domain_.mesh().dimensions();
1470
1471 // Allocate w (chemical potential) fields
1472 w_.setNMonomer(nMonomer);
1473 w_.allocateRGrid(dimensions);
1474
1475 // Allocate c (monomer concentration) fields
1476 c_.setNMonomer(nMonomer);
1477 c_.allocateRGrid(dimensions);
1478
1479 h_.setNMonomer(nMonomer);
1480
1481 // Allocate work space field arrays
1482 tmpFieldsRGrid_.allocate(nMonomer);
1483 tmpFieldsKGrid_.allocate(nMonomer);
1484 for (int i = 0; i < nMonomer; ++i) {
1485 tmpFieldsRGrid_[i].allocate(dimensions);
1486 tmpFieldsKGrid_[i].allocate(dimensions);
1487 }
1488 isAllocatedRGrid_ = true;
1489 }
1490
1491 /*
1492 * Allocate memory for fields.
1493 */
1494 template <int D>
1495 void System<D>::allocateFieldsBasis()
1496 {
1497 // Preconditions and constants
1498 UTIL_CHECK(hasMixture_);
1499 const int nMonomer = mixture_.nMonomer();
1500 UTIL_CHECK(nMonomer > 0);
1501 UTIL_CHECK(isAllocatedRGrid_);
1502 UTIL_CHECK(!isAllocatedBasis_);
1503 UTIL_CHECK(domain_.basis().isInitialized());
1504 const int nBasis = domain_.basis().nBasis();
1505 UTIL_CHECK(nBasis > 0);
1506
1507 w_.allocateBasis(nBasis);
1508 c_.allocateBasis(nBasis);
1509
1510 // Temporary work fields
1511 tmpFieldsBasis_.allocate(nMonomer);
1512 for (int i = 0; i < nMonomer; ++i) {
1513 tmpFieldsBasis_[i].allocate(nBasis);
1514 }
1515 isAllocatedBasis_ = true;
1516 }
1517
1518 /*
1519 * Peek at field file header, initialize unit cell parameters and basis.
1520 */
1521 template <int D>
1522 void System<D>::readFieldHeader(std::string filename)
1523 {
1524 UTIL_CHECK(hasMixture_);
1525 UTIL_CHECK(mixture_.nMonomer() > 0);
1526
1527 // Open field file
1528 std::ifstream file;
1529 fileMaster_.openInputFile(filename, file);
1530
1531 // Read field file header, and initialize basis if needed
1532 int nMonomer;
1533 domain_.fieldIo().readFieldHeader(file, nMonomer,
1534 domain_.unitCell());
1535 // FieldIo::readFieldHeader initializes a basis if needed
1536 file.close();
1537
1538 // Postconditions
1539 UTIL_CHECK(mixture_.nMonomer() == nMonomer);
1540 UTIL_CHECK(domain_.unitCell().nParameter() > 0);
1541 UTIL_CHECK(domain_.unitCell().lattice() != UnitCell<D>::Null);
1542 UTIL_CHECK(domain_.unitCell().isInitialized());
1543 UTIL_CHECK(domain_.basis().isInitialized());
1544 UTIL_CHECK(domain_.basis().nBasis() > 0);
1545 }
1546
1547 /*
1548 * Read a filename string and echo to log file (used in readCommands).
1549 */
1550 template <int D>
1551 void System<D>::readEcho(std::istream& in, std::string& string) const
1552 {
1553 in >> string;
1554 if (in.fail()) {
1555 UTIL_THROW("Unable to read string parameter.");
1556 }
1557 Log::file() << " " << Str(string, 20) << std::endl;
1558 }
1559
1560 /*
1561 * Read floating point number, echo to log file (used in readCommands).
1562 */
1563 template <int D>
1564 void System<D>::readEcho(std::istream& in, double& value) const
1565 {
1566 in >> value;
1567 if (in.fail()) {
1568 UTIL_THROW("Unable to read floating point parameter.");
1569 }
1570 Log::file() << " " << Dbl(value, 20) << std::endl;
1571 }
1572
1573 /*
1574 * Initialize the homogeneous_ member object, which describes
1575 * thermodynamics of a homogeneous reference system.
1576 */
1577 template <int D>
1578 void System<D>::initHomogeneous()
1579 {
1580 UTIL_CHECK(hasMixture_);
1581
1582 // Set number of molecular species and monomers
1583 int nm = mixture_.nMonomer();
1584 int np = mixture_.nPolymer();
1585 int ns = mixture_.nSolvent();
1586 UTIL_CHECK(homogeneous_.nMolecule() == np + ns);
1587 UTIL_CHECK(homogeneous_.nMonomer() == nm);
1588
1589 int i; // molecule index
1590 int j; // monomer index
1591
1592 // Loop over polymer molecule species
1593 if (np > 0) {
1594
1595 // Allocate array of clump sizes
1597 s.allocate(nm);
1598
1599 int k; // block or clump index
1600 int nb; // number of blocks
1601 int nc; // number of clumps
1602
1603 // Loop over polymer species
1604 for (i = 0; i < np; ++i) {
1605
1606 // Initial array of clump sizes for this polymer
1607 for (j = 0; j < nm; ++j) {
1608 s[j] = 0.0;
1609 }
1610
1611 // Compute clump sizes for all monomer types.
1612 nb = mixture_.polymer(i).nBlock();
1613 for (k = 0; k < nb; ++k) {
1614 Block<D>& block = mixture_.polymer(i).block(k);
1615 j = block.monomerId();
1616 s[j] += block.length();
1617 }
1618
1619 // Count the number of clumps of nonzero size
1620 nc = 0;
1621 for (j = 0; j < nm; ++j) {
1622 if (s[j] > 1.0E-8) {
1623 ++nc;
1624 }
1625 }
1626 homogeneous_.molecule(i).setNClump(nc);
1627
1628 // Set clump properties for this Homogeneous::Molecule
1629 k = 0; // Clump index
1630 for (j = 0; j < nm; ++j) {
1631 if (s[j] > 1.0E-8) {
1632 homogeneous_.molecule(i).clump(k).setMonomerId(j);
1633 homogeneous_.molecule(i).clump(k).setSize(s[j]);
1634 ++k;
1635 }
1636 }
1637 homogeneous_.molecule(i).computeSize();
1638
1639 }
1640 }
1641
1642 // Add solvent contributions
1643 if (ns > 0) {
1644 double size;
1645 int monomerId;
1646 for (int is = 0; is < ns; ++is) {
1647 i = is + np;
1648 monomerId = mixture_.solvent(is).monomerId();
1649 size = mixture_.solvent(is).size();
1650 homogeneous_.molecule(i).setNClump(1);
1651 homogeneous_.molecule(i).clump(0).setMonomerId(monomerId);
1652 homogeneous_.molecule(i).clump(0).setSize(size);
1653 homogeneous_.molecule(i).computeSize();
1654 }
1655 }
1656
1657 }
1658
1659} // namespace Pspc
1660} // namespace Pscf
1661#endif
Comparator for fields in symmetry-adapted basis format.
double rmsDiff() const
Return the precomputed root-mean-squared difference.
double compare(FT const &a, FT const &b)
Compare individual fields.
double maxDiff() const
Return the precomputed maximum element-by-element difference.
An IntVec<D, T> is a D-component vector of elements of integer type T.
Definition: IntVec.h:27
Flory-Huggins excess free energy model.
Definition: Interaction.h:26
int nBlock() const
Number of blocks.
Definition: PolymerTmpl.h:241
Propagator & propagator(int blockId, int directionId)
Get propagator for a specific block and direction.
Definition: PolymerTmpl.h:304
double length() const
Sum of the lengths of all blocks in the polymer.
Definition: PolymerTmpl.h:255
Factory for subclasses of Iterator.
Descriptor and solver for one polymer species.
MDE solver for one direction of one block.
int ns() const
Number of values of s (or slices), including head and tail.
const QField & q(int i) const
Return q-field at specified step.
const QField & tail() const
Return q-field at the end of the block.
Comparator for fields in real-space (r-grid) format.
Field of real double precision values on an FFT mesh.
Definition: RField.h:29
Solver and descriptor for a solvent species.
Default Factory for subclasses of Sweep.
Main class for SCFT simulation of one system.
Definition: pspc/System.h:76
void setWBasis(DArray< DArray< double > > const &fields)
Set chemical potential fields, in symmetry-adapted basis format.
int iterate(bool isContinuation=false)
Iteratively solve a SCFT problem.
void setOptions(int argc, char **argv)
Process command line options.
void writeCRGrid(const std::string &filename) const
Write concentration fields in real space grid (r-grid) format.
void writeGroup(std::string const &filename) const
Output all elements of the space group.
void readParam()
Read input parameters from default param file.
void kGridToBasis(const std::string &inFileName, const std::string &outFileName)
Convert fields from Fourier (k-grid) to symmetrized basis format.
void writeWRGrid(const std::string &filename) const
Write chemical potential fields in real space grid (r-grid) format.
void setUnitCell(UnitCell< D > const &unitCell)
Set parameters of the associated unit cell.
System()
Constructor.
Definition: pspc/System.tpp:45
void compare(const DArray< DArray< double > > field1, const DArray< DArray< double > > field2)
Compare two field files in symmetrized basis format.
void writeBlockCRGrid(const std::string &filename) const
Write c-fields for all blocks and solvents in r-grid format.
void compute(bool needStress=false)
Solve the modified diffusion equation once, without iteration.
void writeParamNoSweep(std::ostream &out) const
Write parameter file to an ostream, omitting any sweep block.
void basisToRGrid(const std::string &inFileName, const std::string &outFileName)
Convert a field from symmetrized basis format to r-grid format.
virtual void readParameters(std::istream &in)
Read body of parameter block (without opening and closing lines).
void sweep()
Sweep in parameter space, solving an SCF problem at each point.
void readWRGrid(const std::string &filename)
Read chemical potential fields in real space grid (r-grid) format.
~System()
Destructor.
Definition: pspc/System.tpp:85
void writeThermo(std::ostream &out)
Write thermodynamic properties to a file.
void writeCBasis(const std::string &filename) const
Write concentration fields in symmetrized basis format.
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 writeQAll(std::string const &basename)
Write all propagators of all blocks, each to a separate file.
void estimateWfromC(const std::string &filename)
Construct trial w-fields from c-fields.
void writeStars(std::string const &filename) const
Output information about stars and symmetrized basis functions.
void kGridToRGrid(const std::string &inFileName, const std::string &outFileName)
Convert fields from Fourier (k-grid) to real-space (r-grid) format.
void writeWBasis(const std::string &filename) const
Write chemical potential fields in symmetrized basis format.
void readCommands()
Read commands from default command file.
void rGridToBasis(const std::string &inFileName, const std::string &outFileName)
Convert a field from real-space grid to symmetrized basis format.
void readWBasis(const std::string &filename)
Read chemical potential fields in symmetry adapted basis format.
void writeQSlice(std::string const &filename, int polymerId, int blockId, int directionId, int segmentId) const
Write slice of a propagator at fixed s in r-grid format.
bool checkRGridFieldSymmetry(const std::string &inFileName, double epsilon=1.0E-8)
Check if r-grid fields have the declared space group symmetry.
void rGridToKGrid(const std::string &inFileName, const std::string &outFileName)
Convert fields from real-space (r-grid) to Fourier (k-grid) format.
void writeWaves(std::string const &filename) const
Output information about waves.
void computeFreeEnergy()
Compute free energy density and pressure for current fields.
void writeQ(std::string const &filename, int polymerId, int blockId, int directionId) const
Write one propagator for one block, in r-grid format.
void basisToKGrid(const std::string &inFileName, const std::string &outFileName)
Convert fields from symmetrized basis to Fourier (k-grid) format.
void setWRGrid(DArray< RField< D > > const &fields)
Set new w fields, in real-space (r-grid) format.
double size() const
Get the size (number of monomers) in this solvent.
double phi() const
Get the overall volume fraction for this species.
Definition: Species.h:90
double mu() const
Get the chemical potential for this species (units kT=1).
Definition: Species.h:96
Base template for UnitCell<D> classes, D=1, 2 or 3.
Definition: UnitCell.h:44
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
Wrapper for a double precision number, for formatted ostream output.
Definition: Dbl.h:40
A fixed capacity (static) contiguous array with a variable logical size.
Definition: FSArray.h:38
Wrapper for an int, for formatted ostream output.
Definition: Int.h:37
static std::ostream & file()
Get log ostream by reference.
Definition: Log.cpp:57
static void setEcho(bool echo=true)
Enable or disable echoing for all subclasses of ParamComponent.
void setClassName(const char *className)
Set class name string.
Wrapper for a std::string, for formatted ostream output.
Definition: Str.h:37
#define UTIL_CHECK(condition)
Assertion macro suitable for serial or parallel production code.
Definition: global.h:68
#define UTIL_THROW(msg)
Macro for throwing an Exception, reporting function, file and line number.
Definition: global.h:51
std::string toString(int n)
Return string representation of an integer.
Definition: ioUtil.cpp:52
C++ namespace for polymer self-consistent field theory (PSCF).
void set(BracketPolicy::Type policy)
Set policy regarding use of bracket delimiters on arrays.
Utility classes for scientific computation.
Definition: accumulators.mod:1