PSCF v1.3.3
rpg/environment/FilmFieldGenExt.tpp
1#ifndef RPG_EXT_GEN_FILM_TPP
2#define RPG_EXT_GEN_FILM_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 "FilmFieldGenExt.h"
12#include <rpg/scft/iterator/Iterator.h>
13#include <rpg/field/FieldIo.h>
14#include <rpg/solvers/Mixture.h>
15#include <rpg/field/Domain.h>
16#include <prdc/cpu/RField.h>
17#include <prdc/crystal/Basis.h>
18#include <prdc/crystal/UnitCell.h>
19#include <prdc/crystal/paramIdConversions.h>
20#include <prdc/cuda/resources.h>
21#include <cmath>
22
23namespace Pscf {
24namespace Rpg
25{
26
27 using namespace Util;
28 using namespace Pscf::Prdc;
29 using namespace Pscf::Prdc::Cuda;
30
31 // CUDA kernels:
32 // (defined in anonymous namespace, used only in this file)
33
34 namespace {
35
36 /*
37 * Compute the derivative of h fields with respect to film thickness.
38 *
39 * (note: indices are inverted from their RField representation. So,
40 * for a 3D system, x, y, and z in an RField become z, y, and x in the
41 * CUDA thread grid, respectively. In 2D, x, y becomes y, x. This
42 * allows for memory coalescing; see ThreadMesh::setConfig
43 * documentation for more information.)
44 *
45 * \param hDerivs array of fields containing the h derivatives (output)
46 * \param mask field containing the mask
47 * \param chiBottom array of chiBottom values for each species
48 * \param chiTop array of chiTop values for each species
49 * \param interfaceThickness thickness of the polymer/wall interface
50 * \param normalVecId index of the lattice vector normal to the film
51 * \param nMonomer number of monomer species in the system
52 * \param meshDims grid dimensions of the field in real space
53 */
54 template <int D>
55 __global__ void _hFieldDerivatives(cudaReal ** hDerivs,
56 cudaReal const * mask,
57 cudaReal const * chiBottom,
58 cudaReal const * chiTop,
59 cudaReal const interfaceThickness,
60 int const normalVecId,
61 int const nMonomer,
62 dim3 const meshDims)
63 {
64 // Get position in 3-dimensional grid of threads
65 int ix = blockIdx.x * blockDim.x + threadIdx.x;
66 int iy = blockIdx.y * blockDim.y + threadIdx.y;
67 int iz = blockIdx.z * blockDim.z + threadIdx.z;
68
69 // Get thread index in linearized array
70 int id = ix;
71 if (D > 1) id += meshDims.x * iy;
72 if (D > 2) id += meshDims.x * meshDims.y * iz;
73
74 // Get position d along normalVecId in reduced coordinates
75 cudaReal d;
76 if (D == 3) {
77 if (normalVecId == 2) d = (cudaReal)ix / (cudaReal)meshDims.x;
78 if (normalVecId == 1) d = (cudaReal)iy / (cudaReal)meshDims.y;
79 if (normalVecId == 0) d = (cudaReal)iz / (cudaReal)meshDims.z;
80 } else if (D == 2) {
81 if (normalVecId == 1) d = (cudaReal)ix / (cudaReal)meshDims.x;
82 if (normalVecId == 0) d = (cudaReal)iy / (cudaReal)meshDims.y;
83 } else { // D == 1
84 d = (cudaReal)ix / (cudaReal)meshDims.x;
85 }
86
87 if (ix < meshDims.x && iy < meshDims.y && iz < meshDims.z) {
88 // Get mask value at this gridpoint from global memory
89 cudaReal dPhidL = mask[id];
90
91 // Calculate d\phi_m / dL at this gridpoint
92 dPhidL = dPhidL * (dPhidL - 1) * 8.0 * (abs(d - 0.5) - 0.5)
93 / interfaceThickness;
94
95 // Calculate dh_i / dL at this gridpoint for each species i
96 for (int in = 0; in < nMonomer; in++) {
97 if (d < 0.5) {
98 hDerivs[in][id] = -1.0 * dPhidL * chiBottom[in];
99 } else {
100 hDerivs[in][id] = -1.0 * dPhidL * chiTop[in];
101 }
102 }
103 }
104 }
105
106 /*
107 * Generate the h fields.
108 *
109 * \param hFields array of h fields (output)
110 * \param mask field containing the mask
111 * \param chiBottom array of chiBottom values for each species
112 * \param chiTop array of chiTop values for each species
113 * \param normalVecId index of the lattice vector normal to the film
114 * \param nMonomer number of monomer species in the system
115 * \param meshDims grid dimensions of the field in real space
116 */
117 template <int D>
118 __global__ void _generateHFields(cudaReal ** hFields,
119 cudaReal const * mask,
120 cudaReal const * chiBottom,
121 cudaReal const * chiTop,
122 int const normalVecId,
123 int const nMonomer,
124 dim3 const meshDims)
125 {
126 // Get position in 3-dimensional grid of threads
127 int ix = blockIdx.x * blockDim.x + threadIdx.x;
128 int iy = blockIdx.y * blockDim.y + threadIdx.y;
129 int iz = blockIdx.z * blockDim.z + threadIdx.z;
130
131 // Get thread index in linearized array
132 int id = ix;
133 if (D > 1) id += meshDims.x * iy;
134 if (D > 2) id += meshDims.x * meshDims.y * iz;
135
136 // Determine if this grid point is in the "top" half
137 bool topHalf;
138 if (D == 3) {
139 if (normalVecId == 2) topHalf = (ix > meshDims.x / 2);
140 if (normalVecId == 1) topHalf = (iy > meshDims.y / 2);
141 if (normalVecId == 0) topHalf = (iz > meshDims.z / 2);
142 } else if (D == 2) {
143 if (normalVecId == 1) topHalf = (ix > meshDims.x / 2);
144 if (normalVecId == 0) topHalf = (iy > meshDims.y / 2);
145 } else { // D == 1
146 topHalf = (ix > meshDims.x / 2);
147 }
148
149 if (ix < meshDims.x && iy < meshDims.y && iz < meshDims.z) {
150 // Get mask value at this gridpoint from global memory
151 cudaReal maskVal = mask[id];
152
153 // Calculate h field value at this gridpoint for each species i
154 for (int in = 0; in < nMonomer; in++) {
155 if (topHalf) {
156 hFields[in][id] = (1.0 - maskVal) * chiTop[in];
157 } else {
158 hFields[in][id] = (1.0 - maskVal) * chiBottom[in];
159 }
160 }
161 }
162 }
163
164 }
165
166 /*
167 * Default constructor
168 */
169 template <int D>
172 sysPtr_(0)
173 { setClassName("FilmFieldGenExt"); }
174
175 /*
176 * Constructor
177 */
178 template <int D>
181 sysPtr_(&sys)
182 { setClassName("FilmFieldGenExt"); }
183
184 /*
185 * Destructor
186 */
187 template <int D>
190
191 /*
192 * Get contribution to the stress from these external fields
193 */
194 template <int D>
195 double FilmFieldGenExt<D>::stress(int paramId) const
196 {
197 // If walls are athermal then there is no external field, so no
198 // contribution to the stress.
199 if (isAthermal()) return 0.0;
200
201 // If paramId is not normalVecId, there is no stress contribution
202 UTIL_CHECK(normalVecId() >= 0); // normalVecId has been set
204 system().domain().lattice());
205 if (nvParamId != paramId) return 0.0;
206
207 // If this point is reached, calculate the stress contribution
208 // from the external fields.
210 UTIL_CHECK(system().mask().hasData());
211 UTIL_CHECK(system().h().hasData());
212
213 // Setup
214 int nMonomer = system().mixture().nMonomer();
215
216 DArray< RField<D> > hDerivatives;
217 HostDArray<cudaReal*> hDerivPtrs_h(nMonomer);
218 DeviceArray<cudaReal*> hDerivPtrs(nMonomer);
219
220 hDerivatives.allocate(nMonomer);
221 for (int i = 0; i < nMonomer; i++) {
222 hDerivatives[i].allocate(system().domain().mesh().dimensions());
223 hDerivPtrs_h[i] = hDerivatives[i].cArray();
224 }
225 hDerivPtrs = hDerivPtrs_h;
226
227 HostDArray<cudaReal> chiBottom_h(nMonomer), chiTop_h(nMonomer);
228 DeviceArray<cudaReal> chiBottom_d(nMonomer), chiTop_d(nMonomer);
229 for (int i = 0; i < nMonomer; i++) {
230 chiBottom_h[i] = chiBottom(i);
231 chiTop_h[i] = chiTop(i);
232 }
233 chiBottom_d = chiBottom_h;
234 chiTop_d = chiTop_h;
235
236 // Set D-dimensional GPU configuration
237 ThreadMesh::setConfig(system().domain().mesh().dimensions(), true);
238 dim3 gridDims = ThreadMesh::gridDims();
239 dim3 blockDims = ThreadMesh::blockDims();
240
241 // Compute the derivative of h fields with respect to film thickness
242 _hFieldDerivatives<D><<<gridDims, blockDims>>>
243 (hDerivPtrs.cArray(), system().mask().rgrid().cArray(),
244 chiBottom_d.cArray(), chiTop_d.cArray(), interfaceThickness(),
245 normalVecId(), nMonomer, ThreadMesh::meshDims());
246
247 // Integrate resulting fields to get the stress
248 double stress;
249 for (int i = 0; i < nMonomer; i++) {
250 stress += Reduce::innerProduct(hDerivatives[i], system().c().rgrid(i));
251 }
252 stress /= system().mask().phiTot() * hDerivatives[0].capacity();
253 return stress;
254 }
255
259 template <int D>
261 {
262 // Set chiBottomCurrent_, chiTopCurrent_, and parametersCurrent_
266
267 // If walls are athermal then there is no external field needed.
268 // If external fields already exist in System, clear them, then return.
269 if (isAthermal()) {
270 system().h().clear();
271 return;
272 }
273
274 // If this point is reached, external field must be generated
275 UTIL_CHECK(normalVecId() >= 0);
276 UTIL_CHECK(system().domain().unitCell().isInitialized());
277 UTIL_CHECK(system().mask().hasData());
278 if (system().iterator().isSymmetric()) {
279 UTIL_CHECK(system().domain().basis().isInitialized());
280 }
281
282 // Setup
283 int nMonomer = system().mixture().nMonomer();
284
285 DArray< RField<D> > hFields;
286 HostDArray<cudaReal*> hPtrs_h(nMonomer);
287 DeviceArray<cudaReal*> hPtrs(nMonomer);
288
289 hFields.allocate(nMonomer);
290 for (int i = 0; i < nMonomer; i++) {
291 hFields[i].allocate(system().domain().mesh().dimensions());
292 hPtrs_h[i] = hFields[i].cArray();
293 }
294 hPtrs = hPtrs_h;
295
296 HostDArray<cudaReal> chiBottom_h(nMonomer), chiTop_h(nMonomer);
297 DeviceArray<cudaReal> chiBottom_d(nMonomer), chiTop_d(nMonomer);
298 for (int i = 0; i < nMonomer; i++) {
299 chiBottom_h[i] = chiBottom(i);
300 chiTop_h[i] = chiTop(i);
301 }
302 chiBottom_d = chiBottom_h;
303 chiTop_d = chiTop_h;
304
305 // Set D-dimensional GPU configuration
306 ThreadMesh::setConfig(system().domain().mesh().dimensions(), true);
307 dim3 gridDims = ThreadMesh::gridDims();
308 dim3 blockDims = ThreadMesh::blockDims();
309
310 // Generate the h fields
311 _generateHFields<D><<<gridDims, blockDims>>>
312 (hPtrs.cArray(), system().mask().rgrid().cArray(),
313 chiBottom_d.cArray(), chiTop_d.cArray(), normalVecId(),
314 nMonomer, ThreadMesh::meshDims());
315
316 // Pass h into the System
317 system().h().setRGrid(hFields, system().iterator().isSymmetric());
318 }
319
320}
321}
322
323#endif
Dynamic array on the GPU device with aligned data.
Definition DeviceArray.h:43
Data * cArray()
Return pointer to underlying C array.
Template for dynamic array stored in host CPU memory.
Definition HostDArray.h:43
DArray< double > chiTopCurrent_
The chiTop array used to generate the current external fields.
DArray< double > const & chiTop() const
Get const chiTop array by reference.
DArray< double > chiBottomCurrent_
The chiBottom array used to generate the current external fields.
DArray< double > const & chiBottom() const
Get const chiBottom matrix by reference.
bool isAthermal() const
Are the walls athermal?
double interfaceThickness() const
Get value of interfaceThickness.
RealVec< D > normalVecCurrent_
The lattice vector normal to the film used to generate these fields.
int normalVecId() const
Get value of normalVecId.
void setClassName(const char *className)
Set class name string.
void compute()
Compute the fields and store where the System can access.
RealVec< D > systemLatticeVector(int id) const
Get one of the lattice vectors for this system.
double stress(int paramId) const
Get contribution to the stress from the external fields.
System< D > & system()
Get the System associated with this object by reference.
Main class, representing a complete physical system.
Data * cArray()
Return a pointer to the underlying C array.
Definition Array.h:214
int capacity() const
Return allocated size.
Definition Array.h:159
Dynamically allocatable contiguous array template.
Definition DArray.h:32
void allocate(int capacity)
Allocate the underlying C array.
Definition DArray.h:199
#define UTIL_CHECK(condition)
Assertion macro suitable for serial or parallel production code.
Definition global.h:68
int convertFullParamIdToReduced(const int fullId, const typename UnitCell< D >::LatticeSystem lattice)
Convert full lattice parameter index to a reduced index.
void setConfig(IntVec< D > const &meshDims, bool invert, int blockSize)
Given a multidimensional grid of threads, set execution configuration.
Definition ThreadMesh.cu:88
dim3 gridDims()
Get the multidimensional grid of blocks determined by setConfig.
dim3 meshDims()
Return last requested multidimensional grid of threads.
dim3 blockDims()
Get the dimensions of a single block determined by setConfig.
RT abs(CT const &a)
Return absolute magnitude of a complex number.
double innerProduct(Array< double > const &a, Array< double > const &b)
Compute inner product of two real arrays .
Definition Reduce.cpp:94
Fields, FFTs, and utilities for periodic boundary conditions (CUDA)
Definition CField.cu:12
Periodic fields and crystallography.
Definition CField.cpp:11
SCFT and PS-FTS with real periodic fields (GPU)
PSCF package top-level namespace.