PSCF v1.4.0
BinaryRelaxIterator.cpp
1/*
2 * PSCF - Polymer Self-Consistent Field
3 *
4 * Copyright 2015 - 2025, The Regents of the University of Minnesota
5 * Distributed under the terms of the GNU General Public License.
6 */
7
8#include "BinaryRelaxIterator.h"
9#include <r1d/system/System.h>
10#include <r1d/solvers/Mixture.h>
11#include <r1d/solvers/Polymer.h>
12#include <pscf/chem/Ensemble.h>
13#include <pscf/interaction/Interaction.h>
14#include <util/misc/Log.h>
15#include <util/misc/Timer.h>
16#include <math.h>
17
18namespace Pscf {
19namespace R1d
20{
21 using namespace Util;
22
23 // Constructor
26 epsilon_(0.0),
27 lambdaPlus_(0.0),
28 lambdaMinus_(0.0),
29 maxItr_(200),
30 isAllocated_(false),
31 isCanonical_(true)
32 { setClassName("BinaryRelaxIterator"); }
33
34 // Destructor
37
38 // Read parameter file block and allocate memory
40 {
41 UTIL_CHECK(domain().nx() > 0);
42 read(in, "epsilon", epsilon_);
43 read(in, "maxIter", maxItr_);
44 read(in, "lambdaPlus", lambdaPlus_);
45 read(in, "lambdaMinus", lambdaMinus_);
46
47 allocate();
48 }
49
50 // Allocate required memory (called by readParameters)
51 void BinaryRelaxIterator::allocate()
52 {
53 if (isAllocated_) return;
54 int nm = mixture().nMonomer(); // number of monomer types
55 UTIL_CHECK(nm == 2);
56 int nx = domain().nx(); // number of grid points
57 UTIL_CHECK(nx > 0);
58 cArray_.allocate(nm);
59 wArray_.allocate(nm);
60 dW_.allocate(nm);
61 dWNew_.allocate(nm);
62 wFieldsNew_.allocate(nm);
63 cFieldsNew_.allocate(nm);
64 for (int i = 0; i < nm; ++i) {
65 wFieldsNew_[i].allocate(nx);
66 cFieldsNew_[i].allocate(nx);
67 dW_[i].allocate(nx);
68 dWNew_[i].allocate(nx);
69 }
70 isAllocated_ = true;
71 }
72
73 void BinaryRelaxIterator::computeDW(Array<FieldT> const & wOld,
74 Array<FieldT> const & cFields,
75 Array<FieldT> & dW,
76 double & dWNorm)
77
78 {
79 double dWm, dWp, c0, c1, w0, w1, wm;
80 double dWmNorm = 0.0;
81 double dWpNorm = 0.0;
82 double chi = system().interaction().chi(0,1);
83 int nx = domain().nx(); // number of grid points
84 //For AB diblok
85 for (int i = 0; i < nx; ++i) {
86 c0 = cFields[0][i];
87 c1 = cFields[1][i];
88 w0 = wOld[0][i];
89 w1 = wOld[1][i];
90 wm = w1 - w0;
91 dWp = c1 + c0 - 1.0;
92 dWm = 0.5*( c0 - c1 - wm/chi);
93 dWpNorm += dWp*dWp;
94 dWmNorm += dWm*dWm;
95 dWp *= lambdaPlus_;
96 dWm *= lambdaMinus_;
97 dW[0][i] = dWp - dWm;
98 dW[1][i] = dWp + dWm;
99 }
100 dWNorm = (dWpNorm + dWmNorm)/double(nx);
101 dWNorm = sqrt(dWNorm);
102 }
103
104 void BinaryRelaxIterator::updateWFields(Array<FieldT> const & wOld,
105 Array<FieldT> const & dW,
106 Array<FieldT> & wNew)
107 {
108 int nm = mixture().nMonomer(); // number of monomer types
109 UTIL_CHECK(nm == 2);
110 int nx = domain().nx(); // number of grid points
111 int i; // monomer index
112 int j; // grid point index
113 double w0, w1;
114
115 // Add dW
116 for (j = 0; j < nx; j++){
117 w0 = wOld[0][j];
118 w1 = wOld[1][j];
119 wNew[0][j] = w0 + dW[0][j];
120 wNew[1][j] = w1 + dW[1][j];
121 }
122
123 // If canonical, shift such that last element is exactly zero
124 if (isCanonical_) {
125 double shift = wNew[nm-1][nx-1];
126 for (i = 0; i < nm; ++i) {
127 for (j = 0; j < nx; ++j) {
128 wNew[i][j] -= shift;
129 }
130 }
131 }
132
133 }
134
135 int BinaryRelaxIterator::solve(bool isContinuation)
136 {
137 //Declare Timer
138 Timer timerTotal;
139
140 int nm = mixture().nMonomer(); // number of monomer types
141 UTIL_CHECK(nm == 2);
142 int np = mixture().nPolymer(); // number of polymer species
143 int nx = domain().nx(); // number of grid points
144
145 // Start overall timer
146 timerTotal.start();
147
148 // Determine if isCanonical (iff all species ensembles are closed)
149 isCanonical_ = true;
150 Ensemble ensemble;
151 for (int i = 0; i < np; ++i) {
152 ensemble = mixture().polymer(i).ensemble();
153 if (ensemble == Ensemble::Unknown) {
154 UTIL_THROW("Unknown species ensemble");
155 }
156 if (ensemble == Ensemble::Open) {
157 isCanonical_ = false;
158 }
159 }
160
161 // If isCanonical, shift so that last element is zero.
162 // Note: This is one of the residuals in this case.
163 if (isCanonical_) {
164 double shift = wFields()[nm-1][nx-1];
165 int i, j;
166 for (i = 0; i < nm; ++i) {
167 for (j = 0; j < nx; ++j) {
168 wFields()[i][j] -= shift;
169 }
170 }
171 }
172
173 // Compute initial dWNorm.
175 computeDW(system().wFields(), system().cFields(), dW_, dWNorm_);
176
177 // Iterative loop
178 int i, j, k;
179 for (i = 0; i < maxItr_; ++i) {
180 Log::file() << "iteration " << i
181 << " , error = " << dWNorm_
182 << std::endl;
183
184 if (dWNorm_ < epsilon_) {
185 // Stop timers
186 timerTotal.stop();
187
188 Log::file() << "The epsilon is " << epsilon_<< std::endl;
189 Log::file() << "Converged" << std::endl;
191 // Success
192 Log::file() << "\n\n";
193 // Output timing resultsl;
194 Log::file() << "Total time: "
195 << timerTotal.time() << " s " << std::endl;
196 Log::file() << "Average time cost of each iteration: "
197 << timerTotal.time()/i << " s " << std::endl;
198 Log::file() << "\n\n";
199 return 0;
200 }
201
202 // Try full Fd relaxation update
203 updateWFields(system().wFields(), dW_, wFieldsNew_);
204 mixture().compute(wFieldsNew_, cFieldsNew_);
205 computeDW(wFieldsNew_, cFieldsNew_, dWNew_, dWNormNew_);
206
207 // Decrease increment if necessary
208 j = 0;
209 while (dWNormNew_ > dWNorm_ && j < 3) {
210 //double dWNormDecrease_;
211 Log::file() << " error = "
212 << dWNormNew_ << ", decreasing increment" << std::endl;
213 lambdaPlus_ *= 0.5;
214 lambdaMinus_ *= 0.5;
215 //Print lambdaPlus_ and lambdaMinus_
216 Log::file() << " lambdaPlus = "
217 << lambdaPlus_ << std::endl;
218 Log::file() << " lambdaMinus = "
219 << lambdaMinus_<< std::endl;
220 computeDW(system().wFields(),system().cFields(), dWNew_,
221 dWNormNew_);
222 updateWFields(system().wFields(), dWNew_, wFieldsNew_);
223 mixture().compute(wFieldsNew_, cFieldsNew_);
224 ++j;
225 }
226
227 // Accept or reject update
228 if (dWNormNew_ < dWNorm_) {
229 // Update system fields
230 for (j = 0; j < nm; ++j) {
231 for (k = 0; k < nx; ++k) {
232 system().wField(j)[k] = wFieldsNew_[j][k];
233 system().cField(j)[k] = cFieldsNew_[j][k];
234 dW_[j][k] = dWNew_[j][k];
235 }
236 dWNorm_ = dWNormNew_;
237 }
238 } else {
239 Log::file() << "Iteration failed, norm = "
240 << dWNormNew_ << std::endl;
241 break;
242 }
243
244 }
245 // Failure
246 return 1;
247 }
248
249
250} // namespace R1d
251} // namespace Pscf
DMatrix< double > const & chi() const
Return the chi matrix by const reference.
int nPolymer() const
Get number of polymer species.
int nMonomer() const
Get number of monomer types.
PolymerT & polymer(int id)
Get a polymer solver object by non-const reference.
virtual ~BinaryRelaxIterator()
Destructor.
int solve(bool isContinuation=false)
Iterate self-consistent field equations to solution.
void readParameters(std::istream &in)
Read all parameters and initialize.
BinaryRelaxIterator(System &system)
Constructor.
int nx() const
Get number of spatial grid points, including both endpoints.
Iterator()
Default constructor.
void compute(DArray< FieldT > const &wFields, DArray< FieldT > &cFields)
Compute concentrations.
const Domain & domain() const
Get spatial domain (including grid info) by reference.
DArray< System::CField > & cFields()
Get array of all chemical potential fields.
DArray< System::WField > & wFields()
Get array of all chemical potential fields.
const System & system() const
Get parent System by reference.
const Mixture & mixture() const
Get Mixture by reference.
Main class in SCFT simulation of one system.
Interaction & interaction()
Get interaction (i.e., excess free energy) by reference.
void computeFreeEnergy()
Compute free energy density and pressure for current fields.
Field & cField(int monomerId)
Get chemical potential field for a specific monomer type.
WField & wField(int monomerId)
Get chemical potential field for a specific monomer type.
Array container class template.
Definition Array.h:40
void allocate(int capacity)
Allocate the underlying C array.
Definition DArray.h:269
static std::ostream & file()
Get log ostream by reference.
Definition Log.cpp:59
ScalarParam< Type > & read(std::istream &in, const char *label, Type &value)
Add and read a new required ScalarParam < Type > object.
void setClassName(const char *className)
Set class name string.
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
#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
Ensemble
Statistical ensemble type for the number of molecules of one species.
Definition Ensemble.h:23
SCFT with real 1D fields.
PSCF package top-level namespace.