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