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