PSCF v1.1
pspg/solvers/Mixture.tpp
1#ifndef PSPG_MIXTURE_TPP
2#define PSPG_MIXTURE_TPP
3
4/*
5* PSCF - Polymer Self-Consistent Field Theory
6*
7* Copyright 2016 - 2022, The Regents of the University of Minnesota
8* Distributed under the terms of the GNU General Public License.
9*/
10
11#include "Mixture.h"
12#include <pspg/math/GpuResources.h>
13
14#include <cmath>
15
16namespace Pscf {
17namespace Pspg
18{
19
20 template <int D>
22 : ds_(-1.0),
23 nUnitCellParams_(0),
24 meshPtr_(0),
25 hasStress_(false)
26 { setClassName("Mixture"); }
27
28 template <int D>
30 {}
31
32 template <int D>
33 void Mixture<D>::readParameters(std::istream& in)
34 {
35 MixtureTmpl< Polymer<D>, Solvent<D> >::readParameters(in);
36 read(in, "ds", ds_);
37
38 UTIL_CHECK(nMonomer() > 0);
39 UTIL_CHECK(nPolymer()+ nSolvent() > 0);
40 UTIL_CHECK(ds_ > 0);
41 }
42
43 template <int D>
44 void Mixture<D>::setMesh(Mesh<D> const& mesh)
45 {
46 UTIL_CHECK(nMonomer() > 0);
47 UTIL_CHECK(nPolymer()+ nSolvent() > 0);
48 UTIL_CHECK(ds_ > 0);
49
50 meshPtr_ = &mesh;
51
52 // Set discretization for all blocks
53 int i, j;
54 for (i = 0; i < nPolymer(); ++i) {
55 for (j = 0; j < polymer(i).nBlock(); ++j) {
56 polymer(i).block(j).setDiscretization(ds_, mesh);
57 }
58 }
59
60 // Set spatial discretization for solvents
61 if (nSolvent() > 0) {
62 for (int i = 0; i < nSolvent(); ++i) {
63 solvent(i).setDiscretization(mesh);
64 }
65 }
66 }
67
68 template <int D>
69 void Mixture<D>::setupUnitCell(UnitCell<D> const & unitCell,
70 WaveList<D> const& wavelist)
71 {
72 nUnitCellParams_ = unitCell.nParameter();
73 for (int i = 0; i < nPolymer(); ++i) {
74 polymer(i).setupUnitCell(unitCell, wavelist);
75 }
76 hasStress_ = false;
77 }
78
79 /*
80 * Reset statistical segment length for one monomer type.
81 */
82 template <int D>
83 void Mixture<D>::setKuhn(int monomerId, double kuhn)
84 {
85 // Set new Kuhn length for relevant Monomer object
86 monomer(monomerId).setKuhn(kuhn);
87
88 // Update kuhn length for all blocks of this monomer type
89 for (int i = 0; i < nPolymer(); ++i) {
90 for (int j = 0; j < polymer(i).nBlock(); ++j) {
91 if (monomerId == polymer(i).block(j).monomerId()) {
92 polymer(i).block(j).setKuhn(kuhn);
93 }
94 }
95 }
96 hasStress_ = false;
97 }
98
99
100 /*
101 * Compute concentrations (but not total free energy).
102 */
103 template <int D>
104 void Mixture<D>::compute(DArray< RDField<D> > const & wFields,
105 DArray< RDField<D> > & cFields)
106 {
107 UTIL_CHECK(meshPtr_);
108 UTIL_CHECK(mesh().size() > 0);
109 UTIL_CHECK(nMonomer() > 0);
110 UTIL_CHECK(nPolymer() + nSolvent() > 0);
111 UTIL_CHECK(wFields.capacity() == nMonomer());
112 UTIL_CHECK(cFields.capacity() == nMonomer());
113
114 int nMesh = mesh().size();
115 int nm = nMonomer();
116 int i, j;
117
118 // GPU resources
119 int nBlocks, nThreads;
120 ThreadGrid::setThreadsLogical(nMesh, nBlocks, nThreads);
121
122 // Clear all monomer concentration fields
123 for (i = 0; i < nm; ++i) {
124 UTIL_CHECK(cFields[i].capacity() == nMesh);
125 UTIL_CHECK(wFields[i].capacity() == nMesh);
126 assignUniformReal<<<nBlocks, nThreads>>>(cFields[i].cDField(),
127 0.0, nMesh);
128 }
129
130 // Solve MDE for all polymers
131 for (i = 0; i < nPolymer(); ++i) {
132 polymer(i).compute(wFields);
133 }
134
135 // Accumulate monomer concentration fields
136 int monomerId;
137 for (i = 0; i < nPolymer(); ++i) {
138 for (j = 0; j < polymer(i).nBlock(); ++j) {
139 monomerId = polymer(i).block(j).monomerId();
140 UTIL_CHECK(monomerId >= 0);
141 UTIL_CHECK(monomerId < nm);
142 RDField<D>& monomerField = cFields[monomerId];
143 RDField<D>& blockField = polymer(i).block(j).cField();
144 UTIL_CHECK(blockField.capacity()==nMesh);
145 pointWiseAdd<<<nBlocks, nThreads>>>(monomerField.cDField(),
146 blockField.cDField(), nMesh);
147 }
148 }
149
150 // Process solvent species
151 for (i = 0; i < nSolvent(); i++) {
152 monomerId = solvent(i).monomerId();
153 UTIL_CHECK(monomerId >= 0);
154 UTIL_CHECK(monomerId < nm);
155
156 // Compute solvent concentration
157 solvent(i).compute(wFields[monomerId]);
158
159 // Add solvent contribution to relevant monomer concentration
160 RDField<D>& monomerField = cFields[monomerId];
161 RDField<D> const & solventField = solvent(i).concField();
162 UTIL_CHECK(solventField.capacity() == nMesh);
163 pointWiseAdd<<<nBlocks, nThreads>>>(monomerField.cDField(),
164 solventField.cDField(),
165 nMesh);
166
167
168 }
169
170 hasStress_ = false;
171 }
172
173 /*
174 * Compute Total Stress.
175 */
176 template <int D>
178 {
179 int i, j;
180
181 // Compute stress for each polymer.
182 for (i = 0; i < nPolymer(); ++i) {
183 polymer(i).computeStress(wavelist);
184 }
185
186 // Accumulate total stress
187 for (i = 0; i < nUnitCellParams_; ++i) {
188 stress_[i] = 0.0;
189 for (j = 0; j < nPolymer(); ++j) {
190 stress_[i] += polymer(j).stress(i);
191 }
192 }
193 // Note: Solvent does not contribute to derivatives of f_Helmholtz
194 // with respect to unit cell parameters at fixed volume fractions.
195
196 hasStress_ = true;
197 }
198
199 template <int D>
201 {
202 // Check ensemble of all polymers
203 for (int i = 0; i < nPolymer(); ++i) {
204 if (polymer(i).ensemble() == Species::Open) {
205 return false;
206 }
207 }
208 // Check ensemble of all solvents
209 for (int i = 0; i < nSolvent(); ++i) {
210 if (solvent(i).ensemble() == Species::Open) {
211 return false;
212 }
213 }
214 // Returns true if false was never returned
215 return true;
216 }
217
218 /*
219 * Combine cFields for each block (and solvent) into one DArray
220 */
221 template <int D>
223 const
224 {
225 UTIL_CHECK(nMonomer() > 0);
226 UTIL_CHECK(nBlock() + nSolvent() > 0);
227
228 int np = nSolvent() + nBlock();
229 int nx = mesh().size();
230 int i, j;
231
232 UTIL_CHECK(blockCFields.capacity() == nBlock() + nSolvent());
233
234 // GPU resources
235 int nBlocks, nThreads;
236 ThreadGrid::setThreadsLogical(nx, nBlocks, nThreads);
237
238 // Clear all monomer concentration fields, check capacities
239 for (i = 0; i < np; ++i) {
240 UTIL_CHECK(blockCFields[i].capacity() == nx);
241 assignUniformReal<<<nBlocks, nThreads>>>(blockCFields[i].cDField(), 0.0, nx);
242 }
243
244 // Process polymer species
245 int sectionId = -1;
246
247 if (nPolymer() > 0) {
248
249 // Write each block's r-grid data to blockCFields
250 for (i = 0; i < nPolymer(); ++i) {
251 for (j = 0; j < polymer(i).nBlock(); ++j) {
252 sectionId++;
253
254 UTIL_CHECK(sectionId >= 0);
255 UTIL_CHECK(sectionId < np);
256 UTIL_CHECK(blockCFields[sectionId].capacity() == nx);
257
258 const cudaReal* blockField = polymer(i).block(j).cField().cDField();
259 cudaMemcpy(blockCFields[sectionId].cDField(), blockField,
260 mesh().size() * sizeof(cudaReal), cudaMemcpyDeviceToDevice);
261 }
262 }
263 }
264
265 // Process solvent species
266 if (nSolvent() > 0) {
267
268 // Write each solvent's r-grid data to blockCFields
269 for (i = 0; i < nSolvent(); ++i) {
270 sectionId++;
271
272 UTIL_CHECK(sectionId >= 0);
273 UTIL_CHECK(sectionId < np);
274 UTIL_CHECK(blockCFields[sectionId].capacity() == nx);
275
276 const cudaReal* solventField = solvent(i).concField().cDField();
277 cudaMemcpy(blockCFields[sectionId].cDField(), solventField,
278 mesh().size() * sizeof(cudaReal), cudaMemcpyDeviceToDevice);
279 }
280 }
281 }
282
283} // namespace Pspg
284} // namespace Pscf
285#endif
Description of a regular grid of points in a periodic domain.
Definition: Mesh.h:61
A mixture of polymer and solvent species.
Definition: MixtureTmpl.h:27
Solvent< D > Solvent
Solvent species solver typename.
Definition: MixtureTmpl.h:40
Data * cDField()
Return pointer to underlying C array.
Definition: DField.h:119
int capacity() const
Return allocated size.
Definition: DField.h:112
void setKuhn(int monomerId, double kuhn)
Reset statistical segment length for one monomer type.
bool isCanonical()
Is the ensemble canonical (i.e, closed for all species)?
void readParameters(std::istream &in)
Read all parameters and initialize.
void setupUnitCell(UnitCell< D > const &unitCell, WaveList< D > const &waveList)
Set unit cell parameters used in solver.
void computeStress(WaveList< D > const &waveList)
Get monomer reference volume.
void compute(DArray< RDField< D > > const &wFields, DArray< RDField< D > > &cFields)
Compute concentrations.
void setMesh(Mesh< D > const &mesh)
Create an association with the mesh and allocate memory.
void createBlockCRGrid(DArray< RDField< D > > &blockCFields) const
Combine cFields for each block/solvent into one DArray.
Field of real single precision values on an FFT mesh on a device.
Definition: RDField.h:34
Container for wavevector data.
Definition: WaveList.h:31
int nParameter() const
Get the number of parameters in the unit cell.
Definition: UnitCellBase.h:243
Base template for UnitCell<D> classes, D=1, 2 or 3.
Definition: UnitCell.h:44
Dynamically allocatable contiguous array template.
Definition: DArray.h:32
void setClassName(const char *className)
Set class name string.
#define UTIL_CHECK(condition)
Assertion macro suitable for serial or parallel production code.
Definition: global.h:68
void setThreadsLogical(int nThreadsLogical)
Set the total number of threads required for execution.
Definition: ThreadGrid.cu:80
C++ namespace for polymer self-consistent field theory (PSCF).