PSCF v1.4.0
rp/system/System.tpp
1#ifndef RP_SYSTEM_TPP
2#define RP_SYSTEM_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 "System.h"
12
13#include <prdc/crystal/Basis.h>
14#include <prdc/crystal/UnitCell.h>
15
16#include <pscf/interaction/Interaction.h>
17#include <pscf/mesh/Mesh.h>
18#include <pscf/math/IntVec.h>
19
20#include <util/containers/DArray.h>
21#include <util/containers/FSArray.h>
22#include <util/param/BracketPolicy.h>
23#include <util/param/ParamComponent.h>
24#include <util/signal/Signal.h>
25#include <util/format/Str.h>
26#include <util/format/Int.h>
27#include <util/format/Dbl.h>
28#include <util/misc/ioUtil.h>
29#include <util/misc/FileMaster.h>
30
31#include <string>
32#include <unistd.h>
33
34namespace Pscf {
35namespace Rp {
36
37 using namespace Util;
38 using namespace Prdc;
39
40 /*
41 * Constructor.
42 */
43 template <int D, class T>
44 System<D,T>::System(typename T::System& system)
45 : systemPtr_(&system),
46 cPtr_(nullptr),
47 wPtr_(nullptr),
48 hPtr_(nullptr),
49 maskPtr_(nullptr),
50 mixturePtr_(nullptr),
51 mixtureModifierPtr_(nullptr),
52 domainPtr_(nullptr),
53 interactionPtr_(nullptr),
54 scftPtr_(nullptr),
55 environmentPtr_(nullptr),
56 environmentFactoryPtr_(nullptr),
57 iteratorPtr_(nullptr),
58 iteratorFactoryPtr_(nullptr),
59 sweepPtr_(nullptr),
60 sweepFactoryPtr_(nullptr),
61 simulatorPtr_(nullptr),
62 simulatorFactoryPtr_(nullptr),
63 fileMasterPtr_(nullptr),
64 tmpUnitCellPtr_(nullptr),
65 polymerModel_(PolymerModel::Thread),
66 isAllocatedGrid_(false),
67 isAllocatedBasis_(false),
68 hasMixture_(false)
69 {
70 setClassName("System"); // Set block label in parameter file
71 BracketPolicy::set(BracketPolicy::Optional);
72
73 // Create field containers
74 cPtr_ = new typename T::CFields();
75 wPtr_ = new typename T::WFields();
76 hPtr_ = new typename T::WFields();
77 maskPtr_ = new typename T::Mask();
78
79 // Create non-optional components
80 mixturePtr_ = new typename T::Mixture();
81 mixtureModifierPtr_ = new typename T::MixtureModifier();
82 interactionPtr_ = new typename T::Interaction();
83 domainPtr_ = new typename T::Domain();
84 scftPtr_ = new typename T::ScftThermo(*systemPtr_);
85 fileMasterPtr_ = new FileMaster();
86 tmpUnitCellPtr_ = new UnitCell<D>();
87
88 // Create Factories for optional components
89 environmentFactoryPtr_
90 = new typename T::EnvironmentFactory(*systemPtr_);
91 iteratorFactoryPtr_ = new typename T::IteratorFactory(*systemPtr_);
92 sweepFactoryPtr_ = new typename T::SweepFactory(*systemPtr_);
93 simulatorFactoryPtr_ = new typename T::SimulatorFactory(*systemPtr_);
94
95 // Create associations among class members
96 mixtureModifier().associate(mixture_());
97 domain_().setFileMaster(*fileMasterPtr_);
98 w().setFieldIo(domain_().fieldIo());
99 w().setReadUnitCell(domain_().unitCell());
100 w().setWriteUnitCell(domain_().unitCell());
101 h().setFieldIo(domain_().fieldIo());
102 h().setReadUnitCell(*tmpUnitCellPtr_);
103 h().setWriteUnitCell(domain_().unitCell());
104 mask().setFieldIo(domain_().fieldIo());
105 mask().setReadUnitCell(*tmpUnitCellPtr_);
106 mask().setWriteUnitCell(domain_().unitCell());
107 c_().setFieldIo(domain_().fieldIo());
108 c_().setWriteUnitCell(domain_().unitCell());
109
110 // Note the arguments of setReadUnitCell functions used above:
111 // When w is read from a file in basis or r-grid format, the
112 // parameters of the system unit cell, domain_().unitCell(), are
113 // reset to those in the field file header. When imposed fields h()
114 // and mask() are read from a file, however, unit cell parameters
115 // from the field file header are read into a mutable workspace
116 // object, *tmpUnitCellPtr_, and ultimately discarded.
117
118 // Use of "signals" to maintain data relationships:
119 // Signals are instances of Unit::Signal<void> that are used to
120 // notify "observer" objects of modification of data owned by a
121 // related "notifier" object. Each signal is owned by a notifier
122 // object that maintains data that may be modified. Each signal
123 // maintains a list of observers objects that should be notified
124 // whenever the data owned by the notifier object changes. Each
125 // observer is added by the Signal<void>::addObserver function
126 // template, which takes two arguments: a reference to an observer
127 // object (i.e., an instance of a class) and a pointer to a member
128 // function of that class which will be invoked on that object when
129 // the signal is triggered by modification of associated data.
130
131 // Addition of observers to signals
132
133 // Signal triggered by unit cell modification
134 Signal<void>& cellSignal = domain_().unitCell().signal();
136
137 // Signal triggered by basis construction
138 Signal<void>& basisSignal = domain_().basis().signal();
139 basisSignal.addObserver(*this, &System<D,T>::allocateFieldsBasis);
141 // Signal triggered by w-field modification
142 w().signal().addObserver(*this, &System<D,T>::clearCFields);
143
144 // Signal triggered by h-field modification
145 h().signal().addObserver(*this, &System<D,T>::clearCFields);
146
147 // Signal triggered by mask modification
148 mask().signal().addObserver(*this, &System<D,T>::clearCFields);
149 }
150
151 /*
152 * Destructor.
153 */
154 template <int D, class T>
156 {
157 // Delete optional elements
158 if (environmentPtr_) {
159 delete environmentPtr_;
160 }
161 if (iteratorPtr_) {
162 delete iteratorPtr_;
163 }
164 if (sweepPtr_) {
165 delete sweepPtr_;
166 }
167 if (simulatorPtr_) {
168 delete simulatorPtr_;
170
171 // Delete factories
172 delete environmentFactoryPtr_;
173 delete iteratorFactoryPtr_;
174 delete sweepFactoryPtr_;
175 delete simulatorFactoryPtr_;
176
177 // Delete other non-optional components
178 delete mixturePtr_;
179 delete mixtureModifierPtr_;
180 delete interactionPtr_;
181 delete domainPtr_;
182 delete scftPtr_;
183 delete fileMasterPtr_;
184 delete tmpUnitCellPtr_;
185
186 // Delete field containers
187 delete maskPtr_;
188 delete hPtr_;
189 delete wPtr_;
190 delete cPtr_;
191 }
192
193 // Lifetime (called in main program)
194
195 /*
196 * Process command line options.
197 */
198 template <int D, class T>
199 void System<D,T>::setOptions(int argc, char **argv)
200 {
201 bool eFlag = false; // echo
202 bool dFlag = false; // spatial dimension (1, 2, or 3)
203 bool pFlag = false; // param file
204 bool cFlag = false; // command file
205 bool iFlag = false; // input prefix
206 bool oFlag = false; // output prefix
207 bool tFlag = false; // thread count
208 char* pArg = 0;
209 char* cArg = 0;
210 char* iArg = 0;
211 char* oArg = 0;
212 int dArg = 0;
213 int tArg = 0;
214
215 // Read program arguments
216 int c;
217 opterr = 0;
218 while ((c = getopt(argc, argv, "ed:p:c:i:o:t:")) != -1) {
219 switch (c) {
220 case 'e':
221 eFlag = true;
222 break;
223 case 'd':
224 dFlag = true;
225 dArg = atoi(optarg);
226 break;
227 case 'p': // parameter file
228 pFlag = true;
229 pArg = optarg;
230 break;
231 case 'c': // command file
232 cFlag = true;
233 cArg = optarg;
234 break;
235 case 'i': // input prefix
236 iFlag = true;
237 iArg = optarg;
238 break;
239 case 'o': // output prefix
240 oFlag = true;
241 oArg = optarg;
242 break;
243 case 't': // thread count, if set by user
244 tFlag = true;
245 tArg = atoi(optarg);
246 break;
247 case '?':
248 Log::file() << "Unknown option -" << optopt << std::endl;
249 UTIL_THROW("Invalid command line option");
250 }
251 }
252
253 // Process program arguments
254
255 // Check required option -d
256 if (dFlag) {
257 UTIL_CHECK(D == dArg);
258 } else {
259 UTIL_THROW("Missing required -d option");
260 }
261
262 // Check required option -p, set parameter file name
263 if (pFlag) {
264 fileMaster().setParamFileName(std::string(pArg));
265 } else {
266 UTIL_THROW("Missing required -p option - no parameter file");
267 }
268
269 // Check required option -c, set command file name
270 if (cFlag) {
271 fileMaster().setCommandFileName(std::string(cArg));
272 } else {
273 UTIL_THROW("Missing required -c option - no command file");
274 }
275
276 // If option -e, set to echo parameters as they are read
277 if (eFlag) {
279 }
280
281 // If option -i, set path prefix for input files
282 if (iFlag) {
283 fileMaster().setInputPrefix(std::string(iArg));
284 }
286 // If option -o, set path prefix for output files
287 if (oFlag) {
288 fileMaster().setOutputPrefix(std::string(oArg));
289 }
290
291 // If option -t, process the thread count
292 if (tFlag) {
293 if (tArg <= 0) {
294 UTIL_THROW("Error: Non-positive thread count -t option");
295 }
296 setThreadCount(tArg);
297 }
298 }
299
300 /*
301 * Read parameters and initialize.
302 */
303 template <int D, class T>
304 void System<D,T>::readParameters(std::istream& in)
305 {
306 // Optionally read polymerModel_ enum value, set the global value
307 if (!PolymerModel::isLocked()) {
308 polymerModel_ = PolymerModel::Thread;
309 readOptional(in, "polymerModel", polymerModel_);
310
311 // Set the global enumeration value
312 PolymerModel::setModel(polymerModel_);
313 }
314
315 // Check number of times the global polymer model has been set.
316 // Retain this value so we can check at the end of this function
317 // that it was not set again within the function.
318
319 int nSetPolymerModel = PolymerModel::nSet();
320
321 // Read the Mixture{ ... } block
322 readParamComposite(in, mixture_());
323 hasMixture_ = true;
324
325 int nm = mixture_().nMonomer();
326 int np = mixture_().nPolymer();
327 int ns = mixture_().nSolvent();
328 UTIL_CHECK(nm > 0);
329 UTIL_CHECK(np >= 0);
330 UTIL_CHECK(ns >= 0);
331 UTIL_CHECK(np + ns > 0);
332
333 // Read the Interaction{ ... } block
334 interaction().setNMonomer(nm);
336
337 // Read the Domain{ ... } block
338 readParamComposite(in, domain_());
339 UTIL_CHECK(domain_().mesh().size() > 0);
340 UTIL_CHECK(domain_().unitCell().nParameter() > 0);
341 UTIL_CHECK(domain_().unitCell().lattice() != UnitCell<D>::Null);
342 domain_().fieldIo().setNMonomer(nm);
343
344 // Setup the mixture
345 mixture_().associate(domain_().mesh(), domain_().fft(),
346 domain_().unitCell(), domain_().waveList());
347 mixture_().setFieldIo(domain_().fieldIo());
348 mixture_().allocate();
349
350 // Allocate memory for field containers in r-grid form
351 allocateFieldsGrid();
352
353 // Optionally read and construct an Environment
354 std::string className;
355 bool isEnd;
356 environmentPtr_ =
357 environmentFactoryPtr_->readObjectOptional(in, *this,
358 className, isEnd);
359 if (!environmentPtr_ && ParamComponent::echo()) {
360 Log::file() << indent() << " Environment{ [absent] }\n";
361 }
362 if (environmentPtr_) {
363 environment().setNParams(domain_().unitCell().nParameter());
364 }
365
366 // Optionally read and construct an Iterator
367 if (!isEnd) {
368 iteratorPtr_ =
369 iteratorFactoryPtr_->readObjectOptional(in, *this,
370 className, isEnd);
371 if (iteratorPtr_) {
372 if (iteratorPtr_->isSymmetric()) {
373 UTIL_CHECK(domain_().hasGroup());
374 }
375 } else {
376 if (ParamComponent::echo()) {
377 Log::file() << indent() << " Iterator{ [absent] }\n";
378 }
379 }
380 }
381
382 // Optionally read and construct a Sweep (if an iterator exists)
383 if (hasIterator() && !isEnd) {
384 sweepPtr_ =
385 sweepFactoryPtr_->readObjectOptional(in, *this,
386 className, isEnd);
387 if (!sweepPtr_ && ParamComponent::echo()) {
388 Log::file() << indent() << " Sweep{ [absent] }\n";
389 }
390 }
391
392 // Optionally read and construct a Simulator
393 if (!isEnd) {
394 simulatorPtr_ =
395 simulatorFactoryPtr_->readObjectOptional(in, *this,
396 className, isEnd);
397 if (!simulatorPtr_ && ParamComponent::echo()) {
398 Log::file() << indent() << " Simulator{ [absent] }\n";
399 }
400 }
401
402 // Check that the polymer model was not modified after initialization
403 UTIL_CHECK(PolymerModel::nSet() == nSetPolymerModel);
404
405 // Note: The main pscf_rpc or pscf_rpg program can irreversibly lock the
406 // global polymer model after reading the parameter file, to also prohibit
407 // later changes. The model is not locked here to allow repeated calls to
408 // this function during unit testing.
409 }
410
411 /*
412 * Read parameter file (including open and closing brackets).
413 */
414 template <int D, class T>
415 void System<D,T>::readParam(std::istream& in)
416 {
417 readBegin(in, className().c_str());
418 readParameters(in);
419 readEnd(in);
420 }
421
422 /*
423 * Read default parameter file.
424 */
425 template <int D, class T>
427 { readParam(fileMaster().paramFile()); }
428
429 /*
430 * Read and execute commands from a specified command file.
431 */
432 template <int D, class T>
433 void System<D,T>::readCommands(std::istream &in)
434 {
435 UTIL_CHECK(isAllocatedGrid_);
436 std::string command, filename, inFileName, outFileName;
437 typename T::FieldIo const & fieldIo = domain_().fieldIo();
438
439 bool readNext = true;
440 while (readNext) {
441
442 in >> command;
443
444 if (in.eof()) {
445 break;
446 } else {
447 Log::file() << command << std::endl;
448 }
449
450 if (command == "FINISH") {
451 Log::file() << std::endl;
452 readNext = false;
453 } else
454 if (command == "READ_W_BASIS") {
455 readEcho(in, filename);
456 w().readBasis(filename);
457 UTIL_CHECK(domain_().unitCell().isInitialized());
458 UTIL_CHECK(domain_().basis().isInitialized());
459 UTIL_CHECK(!domain_().waveList().hasKSq());
460 UTIL_CHECK(isAllocatedBasis_);
461 UTIL_CHECK(!c().hasData());
462 UTIL_CHECK(!scft().hasData());
463 } else
464 if (command == "READ_W_RGRID") {
465 readEcho(in, filename);
466 w().readRGrid(filename);
467 UTIL_CHECK(domain_().unitCell().isInitialized());
468 UTIL_CHECK(!domain_().waveList().hasKSq());
469 UTIL_CHECK(!c().hasData());
470 UTIL_CHECK(!scft().hasData());
471 } else
472 if (command == "SET_UNIT_CELL") {
473 UnitCell<D> unitCell;
474 in >> unitCell;
475 Log::file() << " " << unitCell << std::endl;
476 setUnitCell(unitCell);
477 UTIL_CHECK(domain_().unitCell().isInitialized());
478 UTIL_CHECK(!domain_().waveList().hasKSq());
479 UTIL_CHECK(!c().hasData());
480 UTIL_CHECK(!scft().hasData());
481 } else
482 if (command == "COMPUTE") {
483 // Solve the modified diffusion equation, without iteration
484 compute();
485 } else
486 if (command == "ITERATE") {
487 // Attempt to iteratively solve a single SCFT problem
488 bool isContinuation = false;
489 int fail = iterate(isContinuation);
490 if (fail) {
491 readNext = false;
492 }
493 } else
494 if (command == "SWEEP") {
495 // Attempt to solve a sequence of SCFT problems along a path
496 // through parameter space
497 sweep();
498 } else
499 if (command == "COMPRESS") {
500 // Impose incompressibility
501 UTIL_CHECK(hasSimulator());
502 simulator().compressor().compress();
503 } else
504 if (command == "SIMULATE") {
505 // Perform a field theoretic simulation
506 int nStep;
507 in >> nStep;
508 Log::file() << " " << nStep << "\n";
509 simulate(nStep);
510 } else
511 if (command == "ANALYZE" || command == "ANALYZE_TRAJECTORY") {
512 // Read and analyze a field trajectory file
513 int min, max;
514 in >> min;
515 in >> max;
516 Log::file() << " " << min ;
517 Log::file() << " " << max << "\n";
518 std::string classname;
519 readEcho(in, classname);
520 readEcho(in, filename);
521 simulator().analyze(min, max, classname, filename);
522 } else
523 if (command == "WRITE_PARAM") {
524 readEcho(in, filename);
525 std::ofstream file;
526 fileMaster().openOutputFile(filename, file);
527 writeParamNoSweep(file);
528 file.close();
529 } else
530 if (command == "WRITE_THERMO") {
531 readEcho(in, filename);
532 std::ofstream file;
533 fileMaster().openOutputFile(filename, file,
534 std::ios_base::app);
535 scft().write(file);
536 file.close();
537 } else
538 if (command == "WRITE_STRESS") {
539 readEcho(in, filename);
540 std::ofstream file;
541 if (!mixture_().hasStress()) {
542 computeStress();
543 }
544 fileMaster().openOutputFile(filename, file,
545 std::ios_base::app);
546 mixture_().writeStress(file);
547 if (hasEnvironment()) {
548 environment().writeStress(file);
549 }
550 file.close();
551 } else
552 if (command == "WRITE_W_BASIS") {
553 readEcho(in, filename);
554 w().writeBasis(filename);
555 } else
556 if (command == "WRITE_W_RGRID") {
557 readEcho(in, filename);
558 w().writeRGrid(filename);
559 } else
560 if (command == "WRITE_C_BASIS") {
561 readEcho(in, filename);
562 c().writeBasis(filename);
563 } else
564 if (command == "WRITE_C_RGRID") {
565 readEcho(in, filename);
566 c().writeRGrid(filename);
567 } else
568 if (command == "WRITE_BLOCK_C_RGRID") {
569 readEcho(in, filename);
570 mixture_().writeBlockCRGrid(filename);
571 } else
572 if (command == "WRITE_Q_SLICE") {
573 int polymerId, blockId, directionId, segmentId;
574 readEcho(in, filename);
575 in >> polymerId;
576 in >> blockId;
577 in >> directionId;
578 in >> segmentId;
579 Log::file() << Str("polymer ID ", 21) << polymerId << "\n"
580 << Str("block ID ", 21) << blockId << "\n"
581 << Str("direction ID ", 21) << directionId
582 << "\n"
583 << Str("segment ID ", 21) << segmentId
584 << std::endl;
585 mixture_().writeQSlice(filename, polymerId, blockId,
586 directionId, segmentId);
587 } else
588 if (command == "WRITE_Q_TAIL") {
589 readEcho(in, filename);
590 int polymerId, blockId, directionId;
591 in >> polymerId;
592 in >> blockId;
593 in >> directionId;
594 Log::file() << Str("polymer ID ", 21) << polymerId << "\n"
595 << Str("block ID ", 21) << blockId << "\n"
596 << Str("direction ID ", 21) << directionId
597 << "\n";
598 mixture_().writeQTail(filename, polymerId, blockId, directionId);
599 } else
600 if (command == "WRITE_Q") {
601 readEcho(in, filename);
602 int polymerId, blockId, directionId;
603 in >> polymerId;
604 in >> blockId;
605 in >> directionId;
606 Log::file() << Str("polymer ID ", 21) << polymerId << "\n"
607 << Str("block ID ", 21) << blockId << "\n"
608 << Str("direction ID ", 21) << directionId
609 << "\n";
610 mixture_().writeQ(filename, polymerId, blockId, directionId);
611 } else
612 if (command == "WRITE_Q_ALL") {
613 readEcho(in, filename);
614 mixture_().writeQAll(filename);
615 } else
616 if (command == "WRITE_STARS") {
617 readEcho(in, filename);
618 domain_().writeStars(filename);
619 } else
620 if (command == "WRITE_WAVES") {
621 readEcho(in, filename);
622 domain_().writeWaves(filename);
623 } else
624 if (command == "WRITE_GROUP") {
625 readEcho(in, filename);
626 domain_().writeGroup(filename);
627 } else
628 if (command == "BASIS_TO_RGRID") {
629 readEcho(in, inFileName);
630 readEcho(in, outFileName);
631 fieldIo.convertBasisToRGrid(inFileName, outFileName);
632 } else
633 if (command == "RGRID_TO_BASIS") {
634 readEcho(in, inFileName);
635 readEcho(in, outFileName);
636 fieldIo.convertRGridToBasis(inFileName, outFileName);
637 } else
638 if (command == "KGRID_TO_RGRID") {
639 readEcho(in, inFileName);
640 readEcho(in, outFileName);
641 fieldIo.convertKGridToRGrid(inFileName, outFileName);
642 } else
643 if (command == "RGRID_TO_KGRID") {
644 readEcho(in, inFileName);
645 readEcho(in, outFileName);
646 fieldIo.convertRGridToKGrid(inFileName, outFileName);
647 } else
648 if (command == "BASIS_TO_KGRID") {
649 readEcho(in, inFileName);
650 readEcho(in, outFileName);
651 fieldIo.convertBasisToKGrid(inFileName, outFileName);
652 } else
653 if (command == "KGRID_TO_BASIS") {
654 readEcho(in, inFileName);
655 readEcho(in, outFileName);
656 fieldIo.convertKGridToBasis(inFileName, outFileName);
657 } else
658 if (command == "CHECK_RGRID_SYMMETRY") {
659 double epsilon;
660 readEcho(in, inFileName);
661 readEcho(in, epsilon);
662 bool hasSymmetry;
663 hasSymmetry = fieldIo.hasSymmetry(inFileName, epsilon);
664 if (hasSymmetry) {
665 Log::file() << std::endl
666 << "Symmetry of r-grid file matches this space group"
667 << std::endl << std::endl;
668 } else {
669 Log::file() << std::endl
670 << "Symmetry of r-grid file does not match this\n"
671 << "space group to within error threshold of "
672 << Dbl(epsilon) << " ." << std::endl << std::endl;
673 }
674 } else
675 if (command == "COMPARE_BASIS") {
676 std::string filecompare1, filecompare2;
677 readEcho(in, filecompare1);
678 readEcho(in, filecompare2);
679 fieldIo.compareFieldsBasis(filecompare1, filecompare2);
680 } else
681 if (command == "COMPARE_RGRID") {
682 std::string filecompare1, filecompare2;
683 readEcho(in, filecompare1);
684 readEcho(in, filecompare2);
685 fieldIo.compareFieldsRGrid(filecompare1, filecompare2);
686 } else
687 if (command == "SCALE_BASIS") {
688 double factor;
689 readEcho(in, inFileName);
690 readEcho(in, outFileName);
691 readEcho(in, factor);
692 fieldIo.scaleFieldsBasis(inFileName, outFileName, factor);
693 } else
694 if (command == "SCALE_RGRID") {
695 double factor;
696 readEcho(in, inFileName);
697 readEcho(in, outFileName);
698 readEcho(in, factor);
699 fieldIo.scaleFieldsRGrid(inFileName, outFileName, factor);
700 } else
701 if (command == "EXPAND_RGRID_DIMENSION") {
702 readEcho(in, inFileName);
703 readEcho(in, outFileName);
704
705 // Read expanded spatial dimension d
706 int d;
707 in >> d;
708 UTIL_CHECK(d > D);
709 Log::file() << Str("Expand dimension to: ") << d << "\n";
710
711 // Read numbers of grid points along added dimensions
712 DArray<int> newGridDimensions;
713 newGridDimensions.allocate(d-D);
714 for (int i = 0; i < d-D; i++) {
715 in >> newGridDimensions[i];
716 }
717 Log::file()
718 << Str("Numbers of grid points in added dimensions : ");
719 for (int i = 0; i < d-D; i++) {
720 Log::file() << newGridDimensions[i] << " ";
721 }
722 Log::file() << "\n";
723
724 fieldIo.expandRGridDimension(inFileName, outFileName,
725 d, newGridDimensions);
726
727 } else
728 if (command == "REPLICATE_UNIT_CELL") {
729 readEcho(in, inFileName);
730 readEcho(in, outFileName);
731
732 // Read numbers of replicas along each direction
733 IntVec<D> replicas;
734 for (int i = 0; i < D; i++){
735 in >> replicas[i];
736 }
737 for (int i = 0; i < D; i++){
738 Log::file() << "Replicate unit cell in direction "
739 << i << " : ";
740 Log::file() << replicas[i] << " times ";
741 Log::file() << "\n";
742 }
743 fieldIo.replicateUnitCell(inFileName, outFileName, replicas);
744
745 } else
746 if (command == "ESTIMATE_W_BASIS") {
747 readEcho(in, inFileName);
748 readEcho(in, outFileName);
749 fieldIo.estimateWBasis(inFileName, outFileName,
750 interaction().chi());
751 } else
752 if (command == "READ_H_BASIS") {
753 readEcho(in, filename);
754 h().readBasis(filename);
755 UTIL_CHECK(!c().hasData());
756 } else
757 if (command == "READ_H_RGRID") {
758 readEcho(in, filename);
759 h().readRGrid(filename);
760 UTIL_CHECK(!c().hasData());
761 } else
762 if (command == "WRITE_H_BASIS") {
763 readEcho(in, filename);
764 h().writeBasis(filename);
765 } else
766 if (command == "WRITE_H_RGRID") {
767 readEcho(in, filename);
768 h().writeRGrid(filename);
769 } else
770 if (command == "READ_MASK_BASIS") {
771 readEcho(in, filename);
772 mask().readBasis(filename);
773 } else
774 if (command == "READ_MASK_RGRID") {
775 readEcho(in, filename);
776 mask().readRGrid(filename);
777 } else
778 if (command == "WRITE_MASK_BASIS") {
779 readEcho(in, filename);
780 mask().writeBasis(filename);
781 } else
782 if (command == "WRITE_MASK_RGRID") {
783 readEcho(in, filename);
784 mask().writeRGrid(filename);
785 } else
786 if (command == "WRITE_TIMERS") {
787 readEcho(in, filename);
788 std::ofstream file;
789 fileMaster().openOutputFile(filename, file);
790 writeTimers(file);
791 file.close();
792 } else
793 if (command == "CLEAR_TIMERS") {
794 clearTimers();
795 } else {
796 Log::file() << "Error: Unknown command "
797 << command << std::endl;
798 readNext = false;
799 }
800 }
801 }
802
803 /*
804 * Read and execute commands from the default command file.
805 */
806 template <int D, class T>
808 {
809 if (fileMaster().commandFileName().empty()) {
810 UTIL_THROW("Empty command file name");
811 }
812 readCommands(fileMaster().commandFile());
813 }
814
815 // Primary Field Theory Computations
816
817 /*
818 * Solve MDE for current w fields, without iteration.
819 */
820 template <int D, class T>
821 void System<D,T>::compute(bool needStress)
822 {
823 // Preconditions
824 UTIL_CHECK(isAllocatedGrid_);
825 UTIL_CHECK(w().isAllocatedRGrid());
826 UTIL_CHECK(c().isAllocatedRGrid());
827 UTIL_CHECK(w().hasData());
828 clearCFields();
829
830 // Make sure Environment is up-to-date
831 if (hasEnvironment()) {
832 if (environment().needsUpdate()) {
833 environment().generate();
834 }
835 }
836
837 // Solve the modified diffusion equation (without iteration)
838 mixture_().compute(w().rgrid(), c_().rgrid(), mask().phiTot());
839 mixture_().setIsSymmetric(w().isSymmetric());
840 c_().setHasData(true);
841 scft().clear();
842
843 // If w fields are symmetric, compute basis components for c fields
844 if (w().isSymmetric()) {
845 UTIL_CHECK(c().isAllocatedBasis());
846 domain_().fieldIo().convertRGridToBasis(c().rgrid(), c_().basis(),
847 false);
848 c_().setIsSymmetric(true);
849 }
850
851 // Compute stress if needed
852 if (needStress) {
854 }
855 }
856
857 /*
858 * Compute SCFT stress for current fields.
859 */
860 template <int D, class T>
862 {
863 // Compute and store standard Mixture stress
864 if (!mixture_().hasStress()) {
865 mixture_().computeStress(mask().phiTot());
866 }
867
868 // If necessary, compute and store Environment stress
869 if (hasEnvironment()) {
870 if (!environment().hasStress()) {
871 environment().computeStress(mixture_(),
872 iterator().flexibleParams());
873 }
874 }
875 }
876
877 /*
878 * Iteratively solve a SCFT problem.
879 */
880 template <int D, class T>
881 int System<D,T>::iterate(bool isContinuation)
882 {
883 // Preconditions
885 UTIL_CHECK(w().hasData());
886 if (iterator().isSymmetric()) {
887 UTIL_CHECK(isAllocatedBasis_);
888 UTIL_CHECK(w().isSymmetric());
889 }
890 clearCFields();
891
892 Log::file() << std::endl;
893 Log::file() << std::endl;
894
895 // Make sure Environment is up-to-date
896 if (hasEnvironment()) {
897 if (environment().needsUpdate()) {
898 environment().generate();
899 }
900 }
901
902 // Call iterator (return 0 for convergence, 1 for failure)
903 int error = iterator().solve(isContinuation);
904 UTIL_CHECK(c().hasData());
905
906 // If converged, compute related thermodynamic properties
907 if (!error) {
908 scft().compute();
909 scft().write(Log::file());
910 if (!iterator().isFlexible()) {
911 if (!mixture_().hasStress()) {
913 }
914 mixture_().writeStress(Log::file());
915 if (hasEnvironment()) {
916 environment().writeStress(Log::file());
917 }
918 }
919 }
920
921 return error;
922 }
923
924 /*
925 * Perform an SCFT sweep along a path in parameter space.
926 */
927 template <int D, class T>
929 {
930 // Preconditions
933 UTIL_CHECK(w().hasData());
934 if (iterator().isSymmetric()) {
935 UTIL_CHECK(isAllocatedBasis_);
936 UTIL_CHECK(w().isSymmetric());
937 }
938
939 Log::file() << std::endl;
940 Log::file() << std::endl;
941
942 // Perform sweep
943 sweepPtr_->sweep();
944 }
945
946 /*
947 * Perform a field theoretic simulation of nStep steps.
948 */
949 template <int D, class T>
950 void System<D,T>::simulate(int nStep)
951 {
952 UTIL_CHECK(nStep > 0);
954 clearCFields();
955
956 simulator().simulate(nStep);
957 }
958
959 /*
960 * Mark c-fields and free energy as outdated/invalid.
961 */
962 template <int D, class T>
964 {
965 c_().setHasData(false);
966 scft().clear();
967 }
968
969 // Unit Cell Modifiers
970
971 /*
972 * Set the system unit cell.
973 */
974 template <int D, class T>
976 {
977 // Preconditions
978 UTIL_CHECK(domain_().lattice() != UnitCell<D>::Null);
979 UTIL_CHECK(domain_().lattice() == unitCell.lattice());
980
981 // Set system unit cell, using UnitCell<D> assignment operator
982 domain_().unitCell() = unitCell;
983
984 // If necessary, make basis
985 if (domain_().hasGroup() && !domain_().basis().isInitialized()) {
986 domain_().makeBasis();
987 }
988
989 // Postconditions
990 UTIL_CHECK(domain_().unitCell().isInitialized());
991 UTIL_CHECK(domain_().unitCell().lattice() == domain_().lattice());
992 if (domain_().hasGroup()) {
993 UTIL_CHECK(domain_().basis().isInitialized());
994 UTIL_CHECK(isAllocatedBasis_);
995 }
996 UTIL_CHECK(!domain_().waveList().hasKSq());
997 }
998
999 /*
1000 * Set parameters of the system unit cell.
1001 */
1002 template <int D, class T>
1004 {
1005 // Precondition
1006 UTIL_CHECK(domain_().lattice() != UnitCell<D>::Null);
1007
1008 // Set system unit cell
1009 if (domain_().unitCell().lattice() == UnitCell<D>::Null) {
1010 domain_().unitCell().set(domain_().lattice(), parameters);
1011 } else {
1012 domain_().unitCell().setParameters(parameters);
1013 }
1014
1015 // If necessary, make basis
1016 if (domain_().hasGroup() && !domain_().basis().isInitialized()) {
1017 domain_().makeBasis();
1018 }
1019
1020 // Postconditions
1021 UTIL_CHECK(domain_().unitCell().isInitialized());
1022 UTIL_CHECK(domain_().unitCell().lattice() == domain_().lattice());
1023 if (domain_().hasGroup()) {
1024 UTIL_CHECK(domain_().basis().isInitialized());
1025 UTIL_CHECK(isAllocatedBasis_);
1026 }
1027 UTIL_CHECK(!domain_().waveList().hasKSq());
1028 }
1029
1030 /*
1031 * Notify System members that unit cell parameters have been modified.
1032 */
1033 template <int D, class T>
1035 {
1036 clearCFields();
1037 mixture_().clearUnitCellData();
1038 domain_().waveList().clearUnitCellData();
1039 if (hasEnvironment()) {
1040 environment().reset();
1041 }
1042 }
1043
1044 // Property Output
1045
1046 /*
1047 * Write parameter file for SCFT, omitting any sweep block.
1048 */
1049 template <int D, class T>
1050 void System<D,T>::writeParamNoSweep(std::ostream& out) const
1051 {
1052 out << "System{" << std::endl;
1053 mixture().writeParam(out);
1054 interaction().writeParam(out);
1055 domain().writeParam(out);
1056 if (hasEnvironment()) {
1057 environment().writeParam(out);
1058 }
1059 if (hasIterator()) {
1060 iterator().writeParam(out);
1061 }
1062 out << "}" << std::endl;
1063 }
1064
1065 // Timer Operations
1066
1067 /*
1068 * Write timer values to output stream (computational cost).
1069 */
1070 template <int D, class T>
1071 void System<D,T>::writeTimers(std::ostream& out) const
1072 {
1073 if (hasIterator()) {
1074 iterator().outputTimers(Log::file());
1075 iterator().outputTimers(out);
1076 }
1077 if (hasSimulator()){
1078 simulator().outputTimers(Log::file());
1079 simulator().outputTimers(out);
1080 }
1081 }
1082
1083 /*
1084 * Clear state of all timers.
1085 */
1086 template <int D, class T>
1088 {
1089 if (hasIterator()) {
1090 iterator().clearTimers();
1091 }
1092 if (hasSimulator()){
1093 simulator().clearTimers();
1094 }
1095 }
1096
1097 // Private member functions
1098
1099 /*
1100 * Allocate memory for fields in grid format.
1101 */
1102 template <int D, class T>
1103 void System<D,T>::allocateFieldsGrid()
1104 {
1105 // Preconditions
1106 UTIL_CHECK(hasMixture_);
1107 int nMonomer = mixture_().nMonomer();
1108 UTIL_CHECK(nMonomer > 0);
1109 UTIL_CHECK(domain_().mesh().size() > 0);
1110 UTIL_CHECK(!isAllocatedGrid_);
1111
1112 // Alias for mesh dimensions
1113 IntVec<D> const & dimensions = domain_().mesh().dimensions();
1114
1115 // Allocate w (chemical potential) fields
1116 w().setNMonomer(nMonomer);
1117 w().allocateRGrid(dimensions);
1118
1119 // Allocate c (monomer concentration) fields
1120 c_().setNMonomer(nMonomer);
1121 c_().allocateRGrid(dimensions);
1122
1123 // Set nMonomer for external field container
1124 h().setNMonomer(nMonomer);
1125
1126 // If hasEnvironment(), allocate mask and h fields
1127 if (hasEnvironment()) {
1128 if (environment().generatesMask()) {
1129 mask().allocateRGrid(dimensions);
1130 }
1131 if (environment().generatesExternalFields()) {
1132 h().allocateRGrid(dimensions);
1133 }
1134 }
1135
1136 isAllocatedGrid_ = true;
1137 }
1138
1139 /*
1140 * Allocate memory for fields in basis format.
1141 */
1142 template <int D, class T>
1143 void System<D,T>::allocateFieldsBasis()
1144 {
1145 // Preconditions and constants
1146 UTIL_CHECK(hasMixture_);
1147 const int nMonomer = mixture_().nMonomer();
1148 UTIL_CHECK(nMonomer > 0);
1149 UTIL_CHECK(domain_().mesh().size() > 0);
1150 UTIL_CHECK(domain_().hasGroup());
1151 UTIL_CHECK(domain_().basis().isInitialized());
1152 const int nBasis = domain_().basis().nBasis();
1153 UTIL_CHECK(nBasis > 0);
1154 UTIL_CHECK(isAllocatedGrid_);
1155 UTIL_CHECK(!isAllocatedBasis_);
1156
1157 // Allocate basis fields in w and c field containers
1158 w().allocateBasis(nBasis);
1159 c_().allocateBasis(nBasis);
1160
1161 // If hasEnvironment(), allocate mask and h fields
1162 if (hasEnvironment()) {
1163 if (environment().generatesMask()) {
1164 mask().allocateBasis(nBasis);
1165 }
1166 if (environment().generatesExternalFields()) {
1167 h().allocateBasis(nBasis);
1168 }
1169 }
1170
1171 isAllocatedBasis_ = true;
1172 }
1173
1174 /*
1175 * Read a filename string and echo to log file (used in readCommands).
1176 */
1177 template <int D, class T>
1178 void System<D,T>::readEcho(std::istream& in, std::string& string) const
1179 {
1180 in >> string;
1181 if (in.fail()) {
1182 UTIL_THROW("Unable to read a string parameter.");
1183 }
1184 Log::file() << " " << Str(string, 20) << std::endl;
1185 }
1186
1187 /*
1188 * Read floating point number, echo to log file (used in readCommands).
1189 */
1190 template <int D, class T>
1191 void System<D,T>::readEcho(std::istream& in, double& value) const
1192 {
1193 in >> value;
1194 if (in.fail()) {
1195 UTIL_THROW("Unable to read floating point parameter.");
1196 }
1197 Log::file() << " " << Dbl(value, 20) << std::endl;
1198 }
1199
1200} // namespace Rp
1201} // namespace Pscf
1202#endif
An IntVec<D, T> is a D-component vector of elements of integer type T.
Definition IntVec.h:27
Base template for UnitCell<D> classes, D=1, 2 or 3.
Definition UnitCell.h:56
T::Simulator const & simulator() const
T::Mixture const & mixture() const
Get the Mixture (const).
T::WFields const & w() const
Get the chemical potential (w) fields (const).
void sweep()
Sweep in parameter space, solving an SCF problem at each point.
T::Interaction const & interaction() const
T::Domain const & domain() const
Get the Domain (const).
bool hasSimulator() const
Does this system have a Simulator?
void readCommands(std::istream &in)
Read and process commands from an input stream.
void computeStress()
Compute SCFT stress.
T::CFields const & c() const
FileMaster const & fileMaster() const
bool hasSweep() const
Does this system have a Sweep?
void readCommands()
Read and process commands from the default command file.
T::ScftThermo const & scft() const
Get the ScftThermo object (const).
T::Iterator const & iterator() const
Get the Iterator (const).
void clearUnitCellData()
Notify System members that unit cell parameters have been modified.
void clearCFields()
Mark c-fields and free energy as outdated or invalid.
virtual void readParam(std::istream &in)
Read input parameters (with opening and closing lines).
void setUnitCell(UnitCell< D > const &unitCell)
Set parameters of the associated unit cell.
virtual void readParameters(std::istream &in)
Read body of parameter block (without opening and closing lines).
void compute(bool needStress=false)
Solve the modified diffusion equation once, without iteration.
T::Environment const & environment() const
int iterate(bool isContinuation=false)
Iteratively solve a SCFT problem.
void readParam()
Read input parameters from default param file.
void clearTimers()
Clear all timers.
T::Mask const & mask() const
Get the mask (const).
T::MixtureModifier & mixtureModifier()
Get the MixtureModifier (non-const).
bool hasIterator() const
Does this system have an Iterator?
void writeParamNoSweep(std::ostream &out) const
Write partial parameter file to an ostream.
void writeTimers(std::ostream &out) const
Write timer information to an output stream.
T::WFields const & h() const
Get the external potential (h) fields (const).
void setOptions(int argc, char **argv)
Process command line options.
Dynamically allocatable contiguous array template.
Definition DArray.h:32
void allocate(int capacity)
Allocate the underlying C array.
Definition DArray.h:269
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
A FileMaster manages input and output files for a simulation.
Definition FileMaster.h:143
static std::ostream & file()
Get log ostream by reference.
Definition Log.cpp:59
std::string indent() const
Return indent string for this object (string of spaces).
static bool echo()
Get echo parameter.
static void setEcho(bool echo=true)
Enable or disable echoing for all subclasses of ParamComponent.
Begin & readBegin(std::istream &in, const char *label, bool isRequired=true)
Add and read a class label and opening bracket.
void setClassName(const char *className)
Set class name string.
ScalarParam< Type > & readOptional(std::istream &in, const char *label, Type &value)
Add and read a new optional ScalarParam < Type > object.
std::string className() const
Get class name string.
void readParamComposite(std::istream &in, ParamComposite &child, bool next=true)
Add and read a required child ParamComposite.
End & readEnd(std::istream &in)
Add and read the closing bracket.
Notifier (or subject) in the Observer design pattern.
Definition Signal.h:39
void addObserver(Observer &observer, void(Observer::*methodPtr)(const T &))
Register an observer.
Definition Signal.h:111
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:49
bool hasSymmetry(AT const &in, Basis< D > const &basis, IntVec< D > const &dftDimensions, double epsilon=1.0e-8, bool verbose=true)
Check if a k-grid field has the declared space group symmetry.
Enumeration and functions to specify a model for polymer chains.
int nSet()
How many times has setModel been called?
void setModel(Type model)
Set the global polymer model enumeration value.
bool isLocked()
Has the model type been locked (i.e., made immutable) ?
Periodic fields and crystallography.
Definition complex.cpp:11
Class templates for real-valued periodic fields.
PSCF package top-level namespace.
void set(BracketPolicy::Type policy)
Set policy regarding use of bracket delimiters on arrays.