PSCF v1.4.0
cpc/fts/Simulator.tpp
1#ifndef CPC_SIMULATOR_TPP
2#define CPC_SIMULATOR_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// Contains declarations needed in some other templates
12#include <pscf/cpu/complex.h>
13
14#include "Simulator.h"
15#include <cpc/system/System.h>
16#include <cpc/solvers/Mixture.h>
17#include <cpc/solvers/Polymer.h>
18#include <cpc/solvers/Solvent.h>
19#include <cpc/field/Domain.h>
20#include <cpc/fts/step/Step.h>
21#include <cpc/fts/step/StepFactory.h>
22#if 0
23#include <cpc/fts/analyzer/AnalyzerFactory.h>
24#include <cpc/fts/trajectory/TrajectoryReader.h>
25#include <cpc/fts/trajectory/TrajectoryReaderFactory.h>
26#endif
27#include <pscf/interaction/Interaction.h>
28#include <util/random/Random.h>
29#include <util/misc/Timer.h>
30#include <util/global.h>
31
32// Gnu scientific library
33#include <gsl/gsl_eigen.h>
34
35#include <complex>
36
37namespace Pscf {
38namespace Cpc {
39
40 using namespace Util;
41
42 /*
43 * Constructor.
44 */
45 template <int D>
46 Simulator<D>::Simulator(System<D>& system)
47 : //analyzerManager_(*this, system),
48 random_(),
49 wc_(),
50 cc_(),
51 dc_(),
52 u_(),
53 evecs_(),
54 evals_(),
55 isPositiveEval_(),
56 hamiltonian_(0.0),
57 idealHamiltonian_(0.0),
58 fieldHamiltonian_(0.0),
59 systemPtr_(&system),
60 stepPtr_(nullptr),
61 stepFactoryPtr_(nullptr),
62 //trajectoryReaderFactoryPtr_(nullptr)
63 iStep_(0),
64 iTotalStep_(0),
65 seed_(0),
66 hasHamiltonian_(false),
67 hasWc_(false),
68 hasCc_(false),
69 hasDc_(false),
70 isAllocated_(false)
71 {
72 ParamComposite::setClassName("Simulator");
73 stepFactoryPtr_ = new StepFactory<D>(*this);
74 // trajectoryReaderFactoryPtr_
75 // = new TrajectoryReaderFactory<D>(system);
76 }
77
78 /*
79 * Destructor.
80 */
81 template <int D>
83 {
84 if (stepPtr_) {
85 delete stepPtr_;
86 }
87 if (stepFactoryPtr_) {
88 delete stepFactoryPtr_;
89 }
90 #if 0
91 if (trajectoryReaderFactoryPtr_) {
92 delete trajectoryReaderFactoryPtr_;
93 }
94 #endif
95 }
96
97 /*
98 * Allocate required memory.
99 */
100 template <int D>
102 {
103 UTIL_CHECK(!isAllocated_);
104
105 const int nMonomer = system().mixture().nMonomer();
106
107 // Allocate interaction matrix u_ and associated arrays
108 u_.allocate(nMonomer, nMonomer);
109 evecs_.allocate(nMonomer, nMonomer);
110 evals_.allocate(nMonomer);
111 isPositiveEval_.allocate(nMonomer);
112
113 // Allocate memory for eignevector components of w and c fields
114 wc_.allocate(nMonomer);
115 cc_.allocate(nMonomer);
116 dc_.allocate(nMonomer);
117 const IntVec<D> dimensions = system().domain().mesh().dimensions();
118 for (int i = 0; i < nMonomer; ++i) {
119 wc_[i].allocate(dimensions);
120 cc_[i].allocate(dimensions);
121 dc_[i].allocate(dimensions);
122 }
123
124 isAllocated_ = true;
125 }
126
127 /*
128 * Read parameter file block for a BD simulator.
129 */
130 template <int D>
131 void Simulator<D>::readParameters(std::istream &in)
132 {
133 // Optionally read a random seed value
134 readRandomSeed(in);
135
136 // Optionally read a Step block
137 bool isEnd = false;
138 std::string className;
139 stepPtr_ =
140 stepFactoryPtr_->readObjectOptional(in, *this, className,
141 isEnd);
142 if (!hasStep() && ParamComponent::echo()) {
143 Log::file() << indent() << " Step{ [absent] }\n";
144 }
145
146 #if 0
147 // Optionally read an AnalyzerManager
148 Analyzer<D>::baseInterval = 0; // default value
149 readParamCompositeOptional(in, analyzerManager_);
150 #endif
151
152 }
153
154 /*
155 * Setup before main loop of a BD simulation.
156 */
157 template <int D>
158 void Simulator<D>::setup(int nStep)
159 {
160 UTIL_CHECK(system().w().hasData());
161
162 // Eigenanalysis of the interaction matrix
163 analyzeInteraction();
164
165 // Solve MDE and compute c-fields for the intial state
166 system().compute();
167
168 // Compute field components and Hamiltonian for initial state.
169 computeWc();
170 computeCc();
171 computeDc();
172 computeHamiltonian();
173
174 step().setup();
175 #if 0
176 if (analyzerManager_.size() > 0){
177 analyzerManager_.setup();
178 }
179 #endif
180
181 }
182
183 /*
184 * Perform a field theoretic simulation (unimplemented by base class).
185 */
186 template <int D>
188 { UTIL_THROW("Error: Unimplemented function Simulator<D>::simulate"); }
189
190 #if 0
191 /*
192 * Perform a field theoretic MC simulation of nStep steps.
193 */
194 template <int D>
195 void Simulator<D>::simulate(int nStep)
196 {
197 UTIL_CHECK(hasStep());
198 UTIL_CHECK(system().w().hasData());
199
200 // Initial setup
201 setup(nStep);
202
203 // Main simulation loop
204 Timer timer;
205 Timer analyzerTimer;
206 timer.start();
207 iStep_ = 0;
208
209 #if 0
210 // Analysis for initial state (if any)
211 analyzerTimer.start();
212 if (analyzerManager_.size() > 0){
213 analyzerManager_.sample(iStep_);
214 }
215 analyzerTimer.stop();
216 #endif
217
218 for (iTotalStep_ = 0; iTotalStep_ < nStep; ++iTotalStep_) {
219
220 // Take a step (modifies W fields)
221 step().step();
222 iStep_++;
223
224 #if 0
225 // Analysis (if any)
226 analyzerTimer.start();
227 if (Analyzer<D>::baseInterval != 0) {
228 if (analyzerManager_.size() > 0) {
229 if (iStep_ % Analyzer<D>::baseInterval == 0) {
230 analyzerManager_.sample(iStep_);
231 }
232 }
233 }
234 analyzerTimer.stop();
235 #endif
236
237 }
238
239 timer.stop();
240 double time = timer.time();
241 double analyzerTime = analyzerTimer.time();
242
243 #if 0
244 // Output results analyzers to files
245 if (Analyzer<D>::baseInterval > 0){
246 analyzerManager_.output();
247 }
248 #endif
249
250 // Output times for the simulation run
251 Log::file() << std::endl;
252 Log::file() << "nStep " << nStep << std::endl;
253 Log::file() << "Total run time " << time
254 << " sec" << std::endl;
255 double rStep = double(nStep);
256 Log::file() << "time / nStep " << time / rStep
257 << " sec" << std::endl;
258 Log::file() << "Analyzer run time " << analyzerTime
259 << " sec" << std::endl;
260 Log::file() << std::endl;
261
262 }
263 #endif
264
265 #if 0
266 /*
267 * Open, read and analyze a trajectory file (unimplemented by base class).
268 */
269 template <int D>
270 void Simulator<D>::analyze(int min, int max,
271 std::string classname,
272 std::string filename)
273 { UTIL_THROW("Error: Unimplemented function Simulator<D>::analyze"); }
274 #endif
275
276 #if 0
277 /*
278 * Open, read and analyze a trajectory file
279 */
280 template <int D>
281 void Simulator<D>::analyze(int min, int max,
282 std::string classname,
283 std::string filename)
284 {
285 // Preconditions
286 UTIL_CHECK(min >= 0);
287 UTIL_CHECK(max >= min);
288 UTIL_CHECK(Analyzer<D>::baseInterval > 0);
289 UTIL_CHECK(analyzerManager_.size() > 0);
290
291 // Construct TrajectoryReader
292 TrajectoryReader<D>* trajectoryReaderPtr;
293 trajectoryReaderPtr = trajectoryReaderFactory().factory(classname);
294 if (!trajectoryReaderPtr) {
295 std::string message;
296 message = "Invalid TrajectoryReader class name " + classname;
297 UTIL_THROW(message.c_str());
298 }
299
300 // Open trajectory file
301 trajectoryReaderPtr->open(filename);
302 trajectoryReaderPtr->readHeader();
303
304 // Main loop over trajectory frames
305 Timer timer;
306 bool hasFrame;
307 timer.start();
308 hasFrame = trajectoryReaderPtr->readFrame();
309
310 for (iStep_ = 0; iStep_ <= max && hasFrame; ++iStep_) {
311 if (hasFrame) {
312 clearData();
313
314 // Initialize analyzers
315 if (iStep_ == min) {
316 //analyzerManager_.setup();
317 setup(iStep_);
318 }
319
320 // Sample property values only for iStep >= min
321 if (iStep_ >= min) {
322 analyzerManager_.sample(iStep_);
323 }
324 }
325
326 hasFrame = trajectoryReaderPtr->readFrame();
327 }
328 timer.stop();
329 Log::file() << "end main loop" << std::endl;
330 int nFrames = iStep_ - min;
331 trajectoryReaderPtr->close();
332 delete trajectoryReaderPtr;
333
334 // Output results of all analyzers to output files
335 analyzerManager_.output();
336
337 // Output number of frames and times
338 Log::file() << std::endl;
339 Log::file() << "# of frames " << nFrames << std::endl;
340 Log::file() << "run time " << timer.time()
341 << " sec" << std::endl;
342 Log::file() << "time / frame " << timer.time()/double(nFrames)
343 << " sec" << std::endl;
344 Log::file() << std::endl;
345
346 }
347 #endif
348
349 /*
350 * Clear all state data (Hamiltonian, eigen-components of w, c, and d)
351 */
352 template <int D>
354 {
355 hasHamiltonian_ = false;
356 hasWc_ = false;
357 hasCc_ = false;
358 hasDc_ = false;
359 }
360
361 /*
362 * Compute field theoretic Hamiltonian H[W].
363 */
364 template <int D>
366 {
367 UTIL_CHECK(isAllocated_);
368 UTIL_CHECK(system().w().hasData());
369 UTIL_CHECK(system().c().hasData());
370 UTIL_CHECK(hasWc_);
371 hasHamiltonian_ = false;
372
373 Mixture<D> const & mixture = system().mixture();
374 Domain<D> const & domain = system().domain();
375
376 const int nMonomer = mixture.nMonomer();
377 const int meshSize = domain.mesh().size();
378
379 const int np = mixture.nPolymer();
380 const int ns = mixture.nSolvent();
381
382 // Compute number of monomers in the system (nMonomerSystem)
383 const double vSystem = domain.unitCell().volume();
384 const double vMonomer = mixture.vMonomer();
385 const double nMonomerSystem = vSystem / vMonomer;
386
387 // Compute polymer ideal gas contributions to lnQ
388 std::complex<double> phi, mu, lnQ;
389 lnQ = std::complex<double>(0.0, 0.0);
390 if (np > 0) {
391 Polymer<D> const * polymerPtr;
392 double length;
393 for (int i = 0; i < np; ++i) {
394 polymerPtr = &mixture.polymer(i);
395 phi = polymerPtr->phi();
396 if (std::abs(phi) > 1.0E-08) {
397 mu = polymerPtr->mu();
399 length = polymerPtr->length();
400 } else {
401 length = (double)polymerPtr->nBead();
402 }
403 lnQ += phi*( -mu + 1.0 )/length;
404 // Recall: mu = ln(phi/q)
405 }
406 }
407 }
408
409 // Compute solvent ideal gas contributions to lnQ
410 if (ns > 0) {
411 Solvent<D> const * solventPtr;
412 double size;
413 for (int i = 0; i < ns; ++i) {
414 solventPtr = &mixture.solvent(i);
415 phi = solventPtr->phi();
416 if (std::abs(phi) > 1.0E-8) {
417 mu = solventPtr->mu();
418 size = solventPtr->size();
419 lnQ += phi*( -mu + 1.0 )/size;
420 // Recall: mu = ln(phi/q)
421 }
422 }
423 }
424 idealHamiltonian_ = -1.0 * nMonomerSystem * lnQ;
425
426 // Compute quadratic field contribution to fieldHamiltonian_
427 fieldHamiltonian_ = std::complex<double>(0.0, 0.0);
428 std::complex<double> w;
429 double prefactor;
430 int i, j;
431 for (j = 0; j < nMonomer; ++j) {
432 CField<D> const & Wc = wc_[j];
433 prefactor = -0.5*double(nMonomer)/evals_[j];
434 for (i = 0; i < meshSize; ++i) {
435 assign(w, Wc[i]);
436 fieldHamiltonian_ += prefactor * w * w;
437 }
438 }
439 fieldHamiltonian_ /= double(meshSize);
440 fieldHamiltonian_ *= nMonomerSystem;
441
442 hamiltonian_ = idealHamiltonian_ + fieldHamiltonian_;
443
444 hasHamiltonian_ = true;
445 }
446
447 /*
448 * Construct and diagonalize the interaction matrix, U.
449 */
450 template <int D>
452 {
453 UTIL_CHECK(isAllocated_);
454
455 const int nMonomer = system().mixture().nMonomer();
456 DMatrix<double> const & chi = system().interaction().chi();
457 double const & zeta = system().interaction().zeta();
458 int i, j;
459
460 // Compute u_
461 for (i = 0; i < nMonomer; ++i) {
462 for (j = 0; j < nMonomer; ++j) {
463 u_(i, j) = chi(i,j) + zeta;
464 }
465 }
466
467 // Eigenvalue calculations use data structures and
468 // functions from the Gnu Scientific Library (GSL)
469
470 // Allocate GSL matrix A that will hold a copy of U matrix
471 gsl_matrix* A = gsl_matrix_alloc(nMonomer, nMonomer);
472
473 // Copy elements of DMatrix<double> u_ to gsl_matrix A
474 for (i = 0; i < nMonomer; ++i) {
475 for (j = 0; j < nMonomer; ++j) {
476 gsl_matrix_set(A, i, j, u_(i, j));
477 }
478 }
479
480 // Compute eigenvalues and eigenvectors of u_ (or A)
481 gsl_eigen_symmv_workspace* work = gsl_eigen_symmv_alloc(nMonomer);
482 gsl_vector* Avals = gsl_vector_alloc(nMonomer);
483 gsl_matrix* Avecs = gsl_matrix_alloc(nMonomer, nMonomer);
484 int error;
485 error = gsl_eigen_symmv(A, Avals, Avecs, work);
486 UTIL_CHECK(error == 0);
487
488 // Copy eigenvalues and eigenvectors
489 double val;
490 for (i = 0; i < nMonomer; ++i) {
491 val = gsl_vector_get(Avals, i);
492 evals_[i] = val;
493 if (val > 0.0) {
494 isPositiveEval_[i] = true;
495 } else {
496 isPositiveEval_[i] = false;
497 }
498 for (j = 0; j < nMonomer; ++j) {
499 evecs_(i, j) = gsl_matrix_get(Avecs, j, i);
500 }
501 if (evecs_(i, 0) < 0.0) {
502 for (j = 0; j < nMonomer; ++j) {
503 evecs_(i, j) = -evecs_(i, j);
504 }
505 }
506 }
507
508 // Normalize all eigenvectors so that the sum of squares = nMonomer
509 double vec, norm, prefactor;
510 for (i = 0; i < nMonomer; ++i) {
511 norm = 0.0;
512 for (j = 0; j < nMonomer; ++j) {
513 vec = evecs_(i, j);
514 norm += vec*vec;
515 }
516 prefactor = sqrt( double(nMonomer)/norm );
517 for (j = 0; j < nMonomer; ++j) {
518 evecs_(i, j) *= prefactor;
519 }
520 }
521
522 #if 0
523 // Debugging output
524 for (i = 0; i < nMonomer; ++i) {
525 Log::file() << "Eigenpair " << i << "\n";
526 Log::file() << "value = " << evals_[i] << "\n";
527 Log::file() << "vector = [ ";
528 for (j = 0; j < nMonomer; ++j) {
529 Log::file() << evecs_(i, j) << " ";
530 }
531 Log::file() << "]\n";
532 Log::file() << " sc[i] = " << sc_[i] << std::endl;
533 }
534 #endif
535
536 }
537
538 /*
539 * Compute the eigenvector components of the w fields, using the
540 * eigenvectors evecs_ of the interaction matrix as a basis.
541 */
542 template <int D>
544 {
545 UTIL_CHECK(isAllocated_);
546
547 const int nMonomer = system().mixture().nMonomer();
548 const int meshSize = system().domain().mesh().size();
549
550 fftw_complex product;
551 double vec;
552 int i, j, k;
553
554 // Loop over eigenvectors (j is an eigenvector index)
555 for (j = 0; j < nMonomer; ++j) {
556
557 // Loop over grid points to zero out field wc_[j]
558 CField<D>& Wc = wc_[j];
559 for (i = 0; i < meshSize; ++i) {
560 assign(Wc[i], 0.0);
561 }
562
563 // Loop over monomer types (k is a monomer index)
564 for (k = 0; k < nMonomer; ++k) {
565 vec = evecs_(j, k)/double(nMonomer);
566
567 // Loop over grid points
568 CField<D> const & Wr = system().w().field(k);
569 for (i = 0; i < meshSize; ++i) {
570 mul(product, Wr[i], vec);
571 addEq(Wc[i], product);
572 // Wc[i] += vec*Wr[i];
573 }
574
575 }
576 }
577
578 hasWc_ = true;
579 }
580
581 /*
582 * Compute the eigenvector components of the c-fields, using the
583 * eigenvectors evecs_ of the interaction matrix as a basis.
584 */
585 template <int D>
587 {
588 // Preconditions
589 UTIL_CHECK(isAllocated_);
590 UTIL_CHECK(system().w().hasData());
591 UTIL_CHECK(system().c().hasData());
592
593 const int nMonomer = system().mixture().nMonomer();
594 const int meshSize = system().domain().mesh().size();
595
596 // Loop over eigenvectors (i is an eigenvector index)
597 fftw_complex product;
598 double vec;
599 int i, j, k;
600 for (i = 0; i < nMonomer; ++i) {
601
602 // Initialize field cc_[i] to zero
603 CField<D>& Cc = cc_[i];
604 for (k = 0; k < meshSize; ++k) {
605 assign(Cc[k], 0.0);
606 }
607
608 // Loop over monomer types
609 for (j = 0; j < nMonomer; ++j) {
610 CField<D> const & Cr = system().c().field(j);
611 vec = evecs_(i, j);
612
613 // Loop over grid points
614 for (k = 0; k < meshSize; ++k) {
615 //Cc[k] += Cr[k]*vec;
616 mul(product, Cr[k], vec);
617 addEq(Cc[k], product);
618 }
619
620 }
621 }
622
623 hasCc_ = true;
624 }
625
626 /*
627 * Compute d fields, i.e., functional derivatives of H[W].
628 */
629 template <int D>
631 {
632 // Preconditions
633 UTIL_CHECK(isAllocated_);
634 if (!hasWc_) computeWc();
635 if (!hasCc_) computeCc();
636
637 // Local constants and variables
638 const int meshSize = system().domain().mesh().size();
639 const int nMonomer = system().mixture().nMonomer();
640 const double vMonomer = system().mixture().vMonomer();
641 const double a = 1.0/vMonomer;
642 fftw_complex z;
643 double b;
644 int i, k;
645
646 // Compute derivatives for standard Hamiltonian
647 // Loop over composition eigenvectors (exclude the last)
648 for (i = 0; i < nMonomer - 1; ++i) {
649 CField<D>& Dc = dc_[i];
650 CField<D> const & Wc = wc_[i];
651 CField<D> const & Cc = cc_[i];
652 b = -1.0*double(nMonomer)/evals_[i];
653 // Loop over grid points
654 for (k = 0; k < meshSize; ++k) {
655 //Dc[k] = a*( b*Wc[k] + Cc[k] );
656 mul(z, Wc[k], b);
657 addEq(z, Cc[k]);
658 mul(Dc[k], z, a);
659 }
660 }
661
662 hasDc_ = true;
663 }
664
665 // Private Functions
666
667 /*
668 * Optionally read a random number generator seed.
669 */
670 template<int D>
671 void Simulator<D>::readRandomSeed(std::istream& in)
672 {
673 // Optionally read a random number generator seed
674 seed_ = 0;
675 readOptional(in, "seed", seed_);
676
677 // Set random number generator seed
678 // Default value seed_ = 0 uses the clock time.
679 random().setSeed(seed_);
680 }
681
682} // Cpc
683} // Pscf
684#endif
int nPolymer() const
Get number of polymer species.
int nSolvent() const
Get number of solvent (point particle) species.
PolymerT & polymer(int id)
Get a polymer solver object by non-const reference.
SolventT & solvent(int id)
Get a solvent solver object.
double vMonomer() const
Get monomer reference volume (set to 1.0 by default).
int nMonomer() const
Get number of monomer types.
Spatial domain for a periodic structure with real fields, on a CPU.
Mesh< D > & mesh()
Get the Mesh by non-const reference.
UnitCell< D > & unitCell()
Get the UnitCell by non-const reference.
Solver and descriptor for a mixture of polymers and solvents.
Descriptor and solver for one polymer species.
std::complex< double > mu() const
Get the chemical potential for this species (units kT=1).
double length() const
Sum of the lengths of all blocks in the polymer (thread model).
int nBead() const
Total number of beads in the polymer (bead model).
std::complex< double > phi() const
Get the overall volume fraction for this species.
Simulator for complex Langevin field theoretic simulation.
void allocate()
Allocate required memory.
virtual void simulate(int nStep)
Perform a complex Langevin field theoretic simulation.
void computeHamiltonian()
Compute the Hamiltonian used in PS-FTS.
System< D > & system()
Get parent system by reference.
void computeCc()
Compute eigenvector components of the current c fields.
void clearData()
Clear field eigen-components and hamiltonian components.
void computeWc()
Compute eigenvector components of the current w fields.
virtual void readParameters(std::istream &in)
Read parameters for a simulation.
void analyzeInteraction()
Perform eigenvalue analysis of the interaction matrix U.
bool hasStep() const
Does this Simulator have a Step object?
void computeDc()
Compute functional derivatives of the Hamiltonian.
Solver and descriptor for a solvent species.
double size() const
Get the size (number of monomers) in this solvent.
Factory for subclasses of Step.
Definition StepFactory.h:30
Main class for CL-FTS, representing a complete physical system.
An IntVec<D, T> is a D-component vector of elements of integer type T.
Definition IntVec.h:27
Field of complex double precision values on an FFT mesh.
Definition cpu/CField.h:29
WT mu() const
Get the chemical potential for this species (units kT=1).
Definition Species.h:166
WT phi() const
Get the overall volume fraction for this species.
Definition Species.h:159
Dynamically allocated Matrix.
Definition DMatrix.h:25
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.
void setClassName(const char *className)
Set class name string.
std::string className() const
Get class name string.
void readParamCompositeOptional(std::istream &in, ParamComposite &child, bool next=true)
Add and attempt to read an optional child ParamComposite.
Wall clock timer.
Definition Timer.h:35
void start(TimePoint begin)
Start timing from an externally supplied time.
Definition Timer.cpp:49
double time() const
Return the accumulated time, in seconds.
Definition Timer.cpp:37
void stop(TimePoint end)
Stop the clock at an externally supplied time.
Definition Timer.cpp:73
File containing preprocessor macros for error handling.
#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
void addEq(fftw_complex &a, fftw_complex const &b)
In-place addition of fftw_complex numbers, a += b.
void assign(fftw_complex &z, double const &a, double const &b)
Create an fftw_complex from real and imaginary parts, z = a + ib.
void mul(fftw_complex &z, fftw_complex const &a, fftw_complex const &b)
Multiplication of fftw_complex numbers, z = a * b.
double min(Array< double > const &in)
Get minimum of array elements .
Definition Reduce.cpp:198
double max(Array< double > const &in)
Get maximum of array elements (real).
Definition Reduce.cpp:151
Complex periodic fields, CL-FTS (CPU).
Definition cpc.mod:6
bool isThread()
Is the thread model in use ?
PSCF package top-level namespace.
float product(float a, float b)
Product for float Data.
Definition product.h:22