PSCF v1.3.3
rpc/fts/brownian/LMBdStep.tpp
1#ifndef RPC_LM_BD_STEP_TPP
2#define RPC_LM_BD_STEP_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 "LMBdStep.h"
12
13#include <rpc/fts/brownian/BdSimulator.h>
14#include <rpc/fts/compressor/Compressor.h>
15#include <rpc/system/System.h>
16#include <rpc/solvers/Mixture.h>
17#include <rpc/field/Domain.h>
18#include <pscf/math/IntVec.h>
19#include <util/random/Random.h>
20
21namespace Pscf {
22namespace Rpc {
23
24 using namespace Util;
25 using namespace Prdc::Cpu;
26
27 /*
28 * Constructor.
29 */
30 template <int D>
32 : BdStep<D>(simulator),
33 w_(),
34 etaA_(),
35 etaB_(),
36 dwc_(),
37 etaNewPtr_(0),
38 etaOldPtr_(0),
39 mobility_(0.0)
40 {}
41
42 /*
43 * Destructor, empty default implementation.
44 */
45 template <int D>
48
49 /*
50 * ReadParameters, empty default implementation.
51 */
52 template <int D>
53 void LMBdStep<D>::readParameters(std::istream &in)
54 {
55 read(in, "mobility", mobility_);
56
57 int nMonomer = system().mixture().nMonomer();
58 IntVec<D> meshDimensions = system().domain().mesh().dimensions();
59
60 // Allocate memory for private containers
61 w_.allocate(nMonomer);
62 for (int i=0; i < nMonomer; ++i) {
63 w_[i].allocate(meshDimensions);
64 }
65 etaA_.allocate(nMonomer-1);
66 etaB_.allocate(nMonomer-1);
67 for (int i=0; i < nMonomer - 1; ++i) {
68 etaA_[i].allocate(meshDimensions);
69 etaB_[i].allocate(meshDimensions);
70 }
71 dwc_.allocate(meshDimensions);
72 }
73
74 /*
75 * Generate new random displacement values
76 */
77 template <int D>
78 void LMBdStep<D>::generateEtaNew()
79 {
80 const int nMonomer = system().mixture().nMonomer();
81 const int meshSize = system().domain().mesh().size();
82
83 // Prefactor b for displacements
84 const double vSystem = system().domain().unitCell().volume();
85 const double b = sqrt(0.5*mobility_*double(meshSize)/vSystem);
86
87 int j, k;
88 for (j = 0; j < nMonomer - 1; ++j) {
89 RField<D>& eta = etaNew(j);
90 for (k = 0; k < meshSize; ++k) {
91 eta[k] = b*random().gaussian();
92 }
93 }
94 }
95
96 /*
97 * Exchange pointers to old and new random fields.
98 */
99 template <int D>
100 void LMBdStep<D>::exchangeOldNew()
101 {
102 DArray< RField<D> >* temp;
103 temp = etaOldPtr_;
104 etaOldPtr_ = etaNewPtr_;
105 etaNewPtr_ = temp;
106 }
107
108 /*
109 * Initial setup of stepper before main simulation loop.
110 */
111 template <int D>
113 {
114 int nMonomer = system().mixture().nMonomer();
115 int meshSize = system().domain().mesh().size();
116
117 // Check array capacities
118 UTIL_CHECK(etaA_.capacity() == nMonomer-1);
119 UTIL_CHECK(etaB_.capacity() == nMonomer-1);
120 for (int i=0; i < nMonomer - 1; ++i) {
121 UTIL_CHECK(etaA_[i].capacity() == meshSize);
122 UTIL_CHECK(etaB_[i].capacity() == meshSize);
123 }
124 UTIL_CHECK(dwc_.capacity() == meshSize);
125
126 // Initialize pointers
127 etaOldPtr_ = &etaA_;
128 etaNewPtr_ = &etaB_;
129
130 generateEtaNew();
131 exchangeOldNew();
132 }
133
134 /*
135 * One step of Leimkuhler-Matthews BD algorithm.
136 */
137 template <int D>
139 {
140 // Array sizes and indices
141 const int nMonomer = system().mixture().nMonomer();
142 const int meshSize = system().domain().mesh().size();
143 int i, j, k;
144
145 // Save current state
146 simulator().saveState();
147
148 // Copy current fields to w_
149 for (i = 0; i < nMonomer; ++i) {
150 w_[i] = system().w().rgrid(i);
151 }
152
153 // Generate new random displacement values
154 generateEtaNew();
155
156 // Take LM step:
157 const double a = -1.0*mobility_;
158 double evec;
159 // Loop over composition eigenvectors of projected chi matrix
160 for (j = 0; j < nMonomer - 1; ++j) {
161 RField<D> const & etaN = etaNew(j);
162 RField<D> const & etaO = etaOld(j);
163 RField<D> const & dc = simulator().dc(j);
164 for (k = 0; k < meshSize; ++k) {
165 dwc_[k] = a*dc[k] + etaN[k] + etaO[k];
166 }
167 // Loop over monomer types
168 for (i = 0; i < nMonomer; ++i) {
169 RField<D> & w = w_[i];
170 evec = simulator().chiEvecs(j,i);
171 for (k = 0; k < meshSize; ++k) {
172 w[k] += evec*dwc_[k];
173 }
174 }
175 }
176
177 // Set modified fields
178 system().w().setRGrid(w_);
179
180 // Enforce incompressibility (also solves MDE repeatedly)
181 bool isConverged = false;
182 int compress = simulator().compressor().compress();
183 if (compress != 0){
184 simulator().restoreState();
185 } else {
186 isConverged = true;
187 UTIL_CHECK(system().c().hasData());
188
189 // Compute components and derivatives at wp_
190 simulator().clearState();
191 simulator().clearData();
192 simulator().computeWc();
193 simulator().computeCc();
194 simulator().computeDc();
195
196 // Exchange old and new random fields
197 exchangeOldNew();
198
199 }
200
201 return isConverged;
202 }
203
204}
205}
206#endif
An IntVec<D, T> is a D-component vector of elements of integer type T.
Definition IntVec.h:27
Field of real double precision values on an FFT mesh.
Definition cpu/RField.h:29
Brownian dynamics simulator for PS-FTS.
BdSimulator< D > & simulator()
Get parent BdSimulator object.
System< D > & system()
Get parent System object.
BdStep(BdSimulator< D > &simulator)
Constructor.
virtual bool step()
Take a single Brownian dynamics step.
virtual void readParameters(std::istream &in)
Read required parameters from file.
ScalarParam< Type > & read(std::istream &in, const char *label, Type &value)
Add and read a new required ScalarParam < Type > object.
virtual void setup()
Setup before simulation.
LMBdStep(BdSimulator< D > &simulator)
Constructor.
Dynamically allocatable contiguous array template.
Definition DArray.h:32
#define UTIL_CHECK(condition)
Assertion macro suitable for serial or parallel production code.
Definition global.h:68
Real periodic fields, SCFT and PS-FTS (CPU).
Definition param_pc.dox:2
PSCF package top-level namespace.