PSCF v1.4.0
cp/system/System.tpp
1#ifndef PRDC_CL_SYSTEM_TPP
2#define PRDC_CL_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/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/Dbl.h>
25#include <util/misc/ioUtil.h>
26#include <util/misc/FileMaster.h>
27
28#include <string>
29#include <unistd.h>
30
31namespace Pscf {
32namespace Cp {
33
34 using namespace Util;
35 using namespace Prdc;
36
37 /*
38 * Constructor.
39 */
40 template <int D, class T>
41 System<D,T>::System(typename T::System& system)
42 : w_(),
43 c_(),
44 systemPtr_(&system),
45 mixturePtr_(nullptr),
46 //mixtureModifierPtr_(nullptr),
47 domainPtr_(nullptr),
48 interactionPtr_(nullptr),
49 //simulatorPtr_(nullptr),
50 //simulatorFactoryPtr_(nullptr),
51 fileMasterPtr_(nullptr),
52 polymerModel_(PolymerModel::Thread),
53 isAllocated_(false),
54 hasMixture_(false)
55 {
56 ParamComposite::setClassName("System"); // label in parameter file
57 BracketPolicy::set(BracketPolicy::Optional);
58
59 // Create dynamically allocated objects owned by this System
60 mixturePtr_ = new typename T::Mixture();
61 // mixtureModifierPtr_ = new typename T::MixtureModifier();
62 interactionPtr_ = new typename T::Interaction();
63 domainPtr_ = new typename T::Domain();
64 //simulatorFactoryPtr_ = new typename T::SimulatorFactory(*systemPtr_);
65 fileMasterPtr_ = new FileMaster();
66
67 // Create associations among class members
68 //mixtureModifier().associate(mixture_());
69 domain_().setFileMaster(*fileMasterPtr_);
70 w_.setFieldIo(domain_().fieldIo());
71 w_.setReadUnitCell(domain_().unitCell());
72 w_.setWriteUnitCell(domain_().unitCell());
73 c_.setFieldIo(domain_().fieldIo());
74 c_.setWriteUnitCell(domain_().unitCell());
75
76 // Use of "signals" to maintain data relationships:
77 // Signals are instances of Unit::Signal<void> that are used to
78 // notify "observer" objects of modification of data owned by a
79 // related "notifier" object. Each signal is owned by a notifier
80 // object that maintains data that may be modified. Each signal
81 // maintains a list of observers objects that should be notified
82 // whenever the data owned by the notifier object changes. Each
83 // observer is added by the Signal<void>::addObserver function
84 // template, which takes two arguments: a reference to an observer
85 // object (i.e., an instance of a class) and a pointer to a member
86 // function of that class which will be invoked on that object when
87 // the signal is triggered by modification of associated data.
88
89 // Addition of observers to signals
90
91 // Signal triggered by unit cell modification
92 Signal<void>& cellSignal = domain_().unitCell().signal();
94
95 // Signal triggered by w-field modification
96 w_.signal().addObserver(*this, &System<D,T>::clearCFields);
97
98 }
99
100 /*
101 * Destructor.
102 */
103 template <int D, class T>
105 {
106 delete mixturePtr_;
107 //delete mixtureModifierPtr_;
108 delete interactionPtr_;
109 delete domainPtr_;
110 //delete simulatorFactoryPtr_;
111 //if (simulatorPtr_) {
112 // delete simulatorPtr_;
113 //}
114 delete fileMasterPtr_;
115 }
116
117 // Lifetime (called in main program)
118
119 /*
120 * Process command line options.
121 */
122 template <int D, class T>
123 void System<D,T>::setOptions(int argc, char **argv)
124 {
125 bool eFlag = false; // echo
126 bool dFlag = false; // spatial dimension (1, 2, or 3)
127 bool pFlag = false; // param file
128 bool cFlag = false; // command file
129 bool iFlag = false; // input prefix
130 bool oFlag = false; // output prefix
131 bool tFlag = false; // thread count
132 char* pArg = 0;
133 char* cArg = 0;
134 char* iArg = 0;
135 char* oArg = 0;
136 int dArg = 0;
137 int tArg = 0;
138
139 // Read program arguments
140 int c;
141 opterr = 0;
142 while ((c = getopt(argc, argv, "ed:p:c:i:o:t:")) != -1) {
143 switch (c) {
144 case 'e':
145 eFlag = true;
146 break;
147 case 'd':
148 dFlag = true;
149 dArg = atoi(optarg);
150 break;
151 case 'p': // parameter file
152 pFlag = true;
153 pArg = optarg;
154 break;
155 case 'c': // command file
156 cFlag = true;
157 cArg = optarg;
158 break;
159 case 'i': // input prefix
160 iFlag = true;
161 iArg = optarg;
162 break;
163 case 'o': // output prefix
164 oFlag = true;
165 oArg = optarg;
166 break;
167 case 't': // thread count, if set by user
168 tFlag = true;
169 tArg = atoi(optarg);
170 break;
171 case '?':
172 Log::file() << "Unknown option -" << optopt << std::endl;
173 UTIL_THROW("Invalid command line option");
174 }
175 }
176
177 // Process program arguments
178
179 // Check required option -d
180 if (dFlag) {
181 UTIL_CHECK(D == dArg);
182 } else {
183 UTIL_THROW("Missing required -d option");
184 }
185
186 // Check required option -p, set parameter file name
187 if (pFlag) {
188 fileMaster().setParamFileName(std::string(pArg));
189 } else {
190 UTIL_THROW("Missing required -p option - no parameter file");
191 }
192
193 // Check required option -c, set command file name
194 if (cFlag) {
195 fileMaster().setCommandFileName(std::string(cArg));
196 } else {
197 UTIL_THROW("Missing required -c option - no command file");
198 }
200 // If option -e, set to echo parameters as they are read
201 if (eFlag) {
203 }
204
205 // If option -i, set path prefix for input files
206 if (iFlag) {
207 fileMaster().setInputPrefix(std::string(iArg));
208 }
209
210 // If option -o, set path prefix for output files
211 if (oFlag) {
212 fileMaster().setOutputPrefix(std::string(oArg));
213 }
214
215 // If option -t, process the thread count
216 if (tFlag) {
217 if (tArg <= 0) {
218 UTIL_THROW("Error: Non-positive thread count -t option");
219 }
220 setThreadCount(tArg); // Default implementation does nothing
221 }
223
224 /*
225 * Read parameters and initialize.
226 */
227 template <int D, class T>
228 void System<D,T>::readParameters(std::istream& in)
229 {
230 // Optionally read polymerModel_ enum value, set the global value
231 if (!PolymerModel::isLocked()) {
232 polymerModel_ = PolymerModel::Thread;
233 readOptional(in, "polymerModel", polymerModel_);
234
235 // Set the global enumeration value
236 PolymerModel::setModel(polymerModel_);
237 }
238
239 // Check number of times the global polymer model has been set.
240 // Retain this value so we can check at the end of this function
241 // that it was not reset within the remainder of this function.
242
243 int nSetPolymerModel = PolymerModel::nSet();
244
245 // Read the Mixture{ ... } block
246 readParamComposite(in, mixture_());
247 hasMixture_ = true;
248
249 int nm = mixture_().nMonomer(); // number of monomer types
250 int np = mixture_().nPolymer(); // number of polymer species
251 int ns = mixture_().nSolvent(); // number of solvent species
252 UTIL_CHECK(nm > 0);
253 UTIL_CHECK(np > 0);
254 UTIL_CHECK(ns >= 0);
255 UTIL_CHECK(np + ns > 0);
256
257 // Read the Interaction{ ... } block
258 interaction().setNMonomer(nm);
260
261 // Read the Domain{ ... } block
262 readParamComposite(in, domain_());
263 UTIL_CHECK(domain_().mesh().size() > 0);
264 UTIL_CHECK(domain_().unitCell().nParameter() > 0);
265 UTIL_CHECK(domain_().unitCell().lattice() != UnitCell<D>::Null);
266 domain_().fieldIo().setNMonomer(nm);
267
268 // Complete setup and allocation of the Mixture
269 mixture_().associate(domain_().mesh(), domain_().fft(),
270 domain_().unitCell(), domain_().waveList());
271 mixture_().setFieldIo(domain_().fieldIo());
272 mixture_().allocate();
273
274 // Allocate memory for w- and c-field containers
276
277 #if 0
278 // Optionally read and construct a Simulator
279 if (!isEnd) {
280 simulatorPtr_ =
281 simulatorFactoryPtr_->readObjectOptional(in, *this,
282 className, isEnd);
283 if (!simulatorPtr_ && ParamComponent::echo()) {
284 Log::file() << indent() << " Simulator{ [absent] }\n";
285 }
286 }
287 #endif
288
289 // Check that the polymer model was not modified after initialization
290 UTIL_CHECK(PolymerModel::nSet() == nSetPolymerModel);
291
292 // Note: The main pscf_cpc or pscf_cpg program can irreversibly lock the
293 // global polymer model after reading the parameter file, to prohibit
294 // later changes during processing of the command file. The polymer model
295 // enumeration is not locked here so that this function can be called
296 // repeatedly during unit testing.
297 }
298
299 /*
300 * Read parameter file (including open and closing brackets).
301 */
302 template <int D, class T>
303 void System<D,T>::readParam(std::istream& in)
304 {
305 readBegin(in, className().c_str());
306 readParameters(in);
307 readEnd(in);
308 }
309
310 /*
311 * Read default parameter file.
312 */
313 template <int D, class T>
315 { readParam(fileMaster().paramFile()); }
316
317 /*
318 * Read and execute commands from a specified command file.
319 */
320 template <int D, class T>
321 void System<D,T>::readCommands(std::istream &in)
322 {
323 UTIL_CHECK(isAllocated_);
324 std::string command, filename, inFileName, outFileName;
325 //typename T::FieldIo const & fieldIo = domain_().fieldIo();
326
327 bool readNext = true;
328 while (readNext) {
329
330 in >> command;
331
332 if (in.eof()) {
333 break;
334 } else {
335 Log::file() << command << std::endl;
336 }
337
338 if (command == "FINISH") {
339 Log::file() << std::endl;
340 readNext = false;
341 } else
342 if (command == "READ_W") {
343 readEcho(in, filename);
344 w().readFields(filename);
345 UTIL_CHECK(domain_().unitCell().isInitialized());
346 UTIL_CHECK(!domain_().waveList().hasKSq());
347 UTIL_CHECK(!c_.hasData());
348 } else
349 if (command == "SET_UNIT_CELL") {
350 UnitCell<D> unitCell;
351 in >> unitCell;
352 Log::file() << " " << unitCell << std::endl;
353 setUnitCell(unitCell);
354 } else
355 if (command == "COMPUTE") {
356 // Solve the modified diffusion equation for the mixture
357 compute();
358 } else
359
360 #if 0
361 if (command == "SIMULATE") {
362 // Perform a field theoretic simulation of nStep steps
363 int nStep;
364 in >> nStep;
365 Log::file() << " " << nStep << "\n";
366 simulate(nStep);
367 } else
368 if (command == "ANALYZE" || command == "ANALYZE_TRAJECTORY") {
369 // Read and analyze a field trajectory file
370 int min, max;
371 in >> min;
372 in >> max;
373 Log::file() << " " << min ;
374 Log::file() << " " << max << "\n";
375 std::string classname;
376 readEcho(in, classname);
377 readEcho(in, filename);
378 simulator().analyze(min, max, classname, filename);
379 } else
380 #endif
381
382 if (command == "WRITE_PARAM") {
383 readEcho(in, filename);
384 std::ofstream file;
385 fileMaster().openOutputFile(filename, file);
386 writeParam(file);
387 file.close();
388 } else
389 if (command == "WRITE_W") {
390 readEcho(in, filename);
391 w().writeFields(filename);
392 } else
393 if (command == "WRITE_C") {
394 readEcho(in, filename);
395 c().writeFields(filename);
396 } else
397
398 #if 0
399 if (command == "WRITE_BLOCK_C_RGRID") {
400 readEcho(in, filename);
401 mixture_().writeBlockCRGrid(filename);
402 } else
403 if (command == "WRITE_Q_SLICE") {
404 int polymerId, blockId, directionId, segmentId;
405 readEcho(in, filename);
406 in >> polymerId;
407 in >> blockId;
408 in >> directionId;
409 in >> segmentId;
410 Log::file() << Str("polymer ID ", 21) << polymerId << "\n"
411 << Str("block ID ", 21) << blockId << "\n"
412 << Str("direction ID ", 21) << directionId
413 << "\n"
414 << Str("segment ID ", 21) << segmentId
415 << std::endl;
416 mixture_().writeQSlice(filename, polymerId, blockId,
417 directionId, segmentId);
418 } else
419 if (command == "WRITE_Q_TAIL") {
420 readEcho(in, filename);
421 int polymerId, blockId, directionId;
422 in >> polymerId;
423 in >> blockId;
424 in >> directionId;
425 Log::file() << Str("polymer ID ", 21) << polymerId << "\n"
426 << Str("block ID ", 21) << blockId << "\n"
427 << Str("direction ID ", 21) << directionId
428 << "\n";
429 mixture_().writeQTail(filename, polymerId, blockId, directionId);
430 } else
431 if (command == "WRITE_Q") {
432 readEcho(in, filename);
433 int polymerId, blockId, directionId;
434 in >> polymerId;
435 in >> blockId;
436 in >> directionId;
437 Log::file() << Str("polymer ID ", 21) << polymerId << "\n"
438 << Str("block ID ", 21) << blockId << "\n"
439 << Str("direction ID ", 21) << directionId
440 << "\n";
441 mixture_().writeQ(filename, polymerId, blockId, directionId);
442 } else
443 if (command == "WRITE_Q_ALL") {
444 readEcho(in, filename);
445 mixture_().writeQAll(filename);
446 } else
447 #endif
448
449 #if 0
450 if (command == "WRITE_TIMERS") {
451 readEcho(in, filename);
452 std::ofstream file;
453 fileMaster().openOutputFile(filename, file);
454 writeTimers(file);
455 file.close();
456 } else
457 if (command == "CLEAR_TIMERS") {
458 clearTimers();
459 } else
460 #endif
461
462 {
463 Log::file() << "Error: Unknown command "
464 << command << std::endl;
465 readNext = false;
466 }
467 }
468 }
469
470 /*
471 * Read and execute commands from the default command file.
472 */
473 template <int D, class T>
475 {
476 if (fileMaster().commandFileName().empty()) {
477 UTIL_THROW("Empty command file name");
478 }
479 readCommands(fileMaster().commandFile());
480 }
481
482 // Primary Field Theory Computations
483
484 /*
485 * Solve MDE for current w fields, without iteration.
486 */
487 template <int D, class T>
488 void System<D,T>::compute(bool needStress)
489 {
490 // Preconditions
491 UTIL_CHECK(isAllocated_);
492 UTIL_CHECK(w_.isAllocated());
493 UTIL_CHECK(c_.isAllocated());
494 UTIL_CHECK(w_.hasData());
495 clearCFields();
496
497 // Solve the modified diffusion equation (without iteration)
498 mixture_().compute(w_.fields(), c_.fields());
499 c_.setHasData(true);
500
501 }
502
503 #if 0
504 /*
505 * Perform a field theoretic simulation of nStep steps.
506 */
507 template <int D, class T>
508 void System<D,T>::simulate(int nStep)
509 {
510 UTIL_CHECK(nStep > 0);
511 UTIL_CHECK(hasSimulator());
512 clearCFields();
513
514 simulator().simulate(nStep);
515 }
516 #endif
517
518 /*
519 * Mark c-fields and free energy as outdated/invalid.
520 */
521 template <int D, class T>
523 { c_.setHasData(false); }
524
525 // Unit Cell Modifiers
526
527 /*
528 * Set the system unit cell.
529 */
530 template <int D, class T>
532 {
533 // Preconditions
534 UTIL_CHECK(domain_().lattice() != UnitCell<D>::Null);
535 UTIL_CHECK(domain_().lattice() == unitCell.lattice());
536
537 // Set system unit cell, using UnitCell<D> assignment operator
538 domain_().unitCell() = unitCell;
539
540 // Postconditions
541 UTIL_CHECK(domain_().unitCell().isInitialized());
542 UTIL_CHECK(domain_().unitCell().lattice() == domain_().lattice());
543 UTIL_CHECK(!domain_().waveList().hasKSq());
544 }
545
546 /*
547 * Set parameters of the system unit cell.
548 */
549 template <int D, class T>
551 {
552 // Precondition
553 UTIL_CHECK(domain_().lattice() != UnitCell<D>::Null);
554
555 // Set system unit cell
556 if (domain_().unitCell().lattice() == UnitCell<D>::Null) {
557 domain_().unitCell().set(domain_().lattice(), parameters);
558 } else {
559 domain_().unitCell().setParameters(parameters);
560 }
561
562 // Postconditions
563 UTIL_CHECK(domain_().unitCell().isInitialized());
564 UTIL_CHECK(domain_().unitCell().lattice() == domain_().lattice());
565 UTIL_CHECK(!domain_().waveList().hasKSq());
566 }
567
568 /*
569 * Notify System members that unit cell parameters have been modified.
570 */
571 template <int D, class T>
573 {
574 clearCFields();
575 mixture_().clearUnitCellData();
576 domain_().waveList().clearUnitCellData();
577 }
578
579 // Timer Operations
580
581 #if 0
582 /*
583 * Write timer values to output stream (computational cost).
584 */
585 template <int D, class T>
586 void System<D,T>::writeTimers(std::ostream& out) const
587 {
588 if (hasSimulator()){
589 simulator().outputTimers(Log::file());
590 simulator().outputTimers(out);
591 }
592 }
593
594 /*
595 * Clear state of all timers.
596 */
597 template <int D, class T>
598 void System<D,T>::clearTimers()
599 {
600 if (hasSimulator()){
601 simulator().clearTimers();
602 }
603 }
604 #endif
605
606 // Private member functions
607
608 /*
609 * Allocate memory for fields in grid format.
610 */
611 template <int D, class T>
612 void System<D,T>::allocateFields()
613 {
614 // Preconditions
615 UTIL_CHECK(hasMixture_);
616 int nMonomer = mixture_().nMonomer();
617 UTIL_CHECK(nMonomer > 0);
618 UTIL_CHECK(domain_().mesh().size() > 0);
619 UTIL_CHECK(!isAllocated_);
620
621 // Alias for mesh dimensions
622 IntVec<D> const & dimensions = domain_().mesh().dimensions();
623
624 // Allocate w (chemical potential) fields
625 //w_.setNMonomer(nMonomer);
626 w_.allocate(nMonomer, dimensions);
627
628 // Allocate c (monomer concentration) fields
629 //c_.setNMonomer(nMonomer);
630 c_.allocate(nMonomer, dimensions);
631
632 isAllocated_ = true;
633 }
634
635 /*
636 * Read a filename string and echo to log file (used in readCommands).
637 */
638 template <int D, class T>
639 void System<D,T>::readEcho(std::istream& in, std::string& string) const
640 {
641 in >> string;
642 if (in.fail()) {
643 UTIL_THROW("Unable to read a string parameter.");
644 }
645 Log::file() << " " << Str(string, 20) << std::endl;
646 }
647
648 /*
649 * Read floating point number, echo to log file (used in readCommands).
650 */
651 template <int D, class T>
652 void System<D,T>::readEcho(std::istream& in, double& value) const
653 {
654 in >> value;
655 if (in.fail()) {
656 UTIL_THROW("Unable to read floating point parameter.");
657 }
658 Log::file() << " " << Dbl(value, 20) << std::endl;
659 }
660
661} // namespace Cp
662} // namespace Pscf
663#endif
Base class template for a complex CL-FTS system.
void readCommands()
Read and process commands from the default command file.
T::WFields & w()
Get the chemical potential (w) fields (non-const).
void readParam()
Read input parameters from default param file.
FileMaster & fileMaster()
Get the FileMaster (non-const).
virtual void readParameters(std::istream &in)
Read body of parameter block (without opening and closing lines).
void clearUnitCellData()
Notify System members that unit cell parameters have been modified.
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.
Types< D >::CFields const & c() const
void compute(bool needStress=false)
Solve the modified diffusion equation once, without iteration.
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.
void setOptions(int argc, char **argv)
Process command line options.
Types< D >::Interaction & interaction()
System(typename T::System &system)
Constructor.
void simulate(int nStep)
Perform a field theoretic simulation (PS-FTS).
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
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.
virtual void writeParam(std::ostream &out) const
Write all parameters to an output stream.
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
Complex-valued periodic fields (class templates).
Definition cp.mod:6
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
void allocateFields(DArray< FT > &fields, int n, IntVec< D > const &dimension)
Allocate a DArray of fields.
PSCF package top-level namespace.
void set(BracketPolicy::Type policy)
Set policy regarding use of bracket delimiters on arrays.