cuwave 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,225 @@
1
+ // Prepended by wave.compile_kernels: stencils.preamble, then common.cuh.
2
+ // Compile-time configuration this file responds to:
3
+ // NDIM = 1 | 2 | 3
4
+ // USE_DAMPING
5
+
6
+ #if NDIM == 1
7
+ #define AXIS_PARAMS const real_t f0, const int N0
8
+ #define INTERIOR_OR_RETURN \
9
+ const int a0 = blockIdx.x * blockDim.x + threadIdx.x; \
10
+ if (!(a0 > 0 && a0 < N0 - 1)) \
11
+ return; \
12
+ const int idx = a0
13
+ #define AXIS_GEOM const int A[1] = {a0}, S[1] = {1}, NN[1] = {N0}
14
+ #define AXIS_FACTORS const real_t F[1] = {f0}
15
+ #elif NDIM == 2
16
+ #define AXIS_PARAMS \
17
+ const real_t f0, const int N0, const real_t f1, const int N1, const int s0
18
+ #define INTERIOR_OR_RETURN \
19
+ const int a1 = blockIdx.x * blockDim.x + threadIdx.x; \
20
+ const int a0 = blockIdx.y * blockDim.y + threadIdx.y; \
21
+ if (!(a0 > 0 && a0 < N0 - 1 && a1 > 0 && a1 < N1 - 1)) \
22
+ return; \
23
+ const int idx = a0 * s0 + a1
24
+ #define AXIS_GEOM \
25
+ const int A[2] = {a0, a1}, S[2] = {s0, 1}, NN[2] = {N0, N1}
26
+ #define AXIS_FACTORS const real_t F[2] = {f0, f1}
27
+ #elif NDIM == 3
28
+ #define AXIS_PARAMS \
29
+ const real_t f0, const int N0, const real_t f1, const int N1, const int s0, \
30
+ const real_t f2, const int N2, const int s1
31
+ #define INTERIOR_OR_RETURN \
32
+ const int a2 = blockIdx.x * blockDim.x + threadIdx.x; \
33
+ const int a1 = blockIdx.y * blockDim.y + threadIdx.y; \
34
+ const int a0 = blockIdx.z * blockDim.z + threadIdx.z; \
35
+ if (!(a0 > 0 && a0 < N0 - 1 && a1 > 0 && a1 < N1 - 1 && a2 > 0 && \
36
+ a2 < N2 - 1)) \
37
+ return; \
38
+ const int idx = a0 * s0 + a1 * s1 + a2
39
+ #define AXIS_GEOM \
40
+ const int A[3] = {a0, a1, a2}, S[3] = {s0, s1, 1}, NN[3] = {N0, N1, N2}
41
+ #define AXIS_FACTORS const real_t F[3] = {f0, f1, f2}
42
+ #endif
43
+
44
+ // the node's normal strains, one per axis: a wall grades to a zero row under
45
+ // traction (returned as a set bit) and to the antisymmetric fold when clamped
46
+ __device__ __forceinline__ int
47
+ normal_strains(const real_t *__restrict__ u, const int idx, const int cs,
48
+ const int *A, const int *S, const int *NN, const real_t *F,
49
+ const int clamped, real_t *eps) {
50
+ int zeroed = 0;
51
+ #pragma unroll
52
+ for (int d = 0; d < NDIM; ++d) {
53
+ const int r = rad_node(A[d], NN[d]);
54
+ const real_t *__restrict__ ud = u + d * cs;
55
+ if (r > 0) {
56
+ real_t acc = (real_t)0;
57
+ #pragma unroll
58
+ for (int k = 1; k <= STENCIL_RADIUS; ++k)
59
+ if (k <= r)
60
+ acc += SG_W(r, k) * (ud[idx + (k - 1) * S[d]] - ud[idx - k * S[d]]);
61
+ eps[d] = F[d] * acc;
62
+ } else if (A[d] == 1 && clamped_face(clamped, d, 0)) {
63
+ eps[d] = F[d] * 2.f * ud[idx]; // the wall holds u = 0 half a node down
64
+ } else if (A[d] == NN[d] - 2 && clamped_face(clamped, d, 1)) {
65
+ eps[d] = -F[d] * 2.f * ud[idx - S[d]];
66
+ } else {
67
+ eps[d] = (real_t)0;
68
+ zeroed |= 1 << d;
69
+ }
70
+ }
71
+ return zeroed;
72
+ }
73
+
74
+ // a traction wall condenses its axis out of the coupling: the plane stress
75
+ // reduction of lam, applied once per zeroed axis
76
+ __device__ __forceinline__ real_t condensed_lame(const real_t lam,
77
+ const real_t mu,
78
+ const int zeroed) {
79
+ real_t lam_eff = lam;
80
+ #pragma unroll
81
+ for (int d = 0; d < NDIM; ++d)
82
+ if ((zeroed >> d) & 1)
83
+ lam_eff = 2.f * lam_eff * mu / (lam_eff + 2.f * mu);
84
+ return lam_eff;
85
+ }
86
+
87
+ #if NDIM >= 2
88
+ // the engineering shear strain of the (k, l) pair at its own staggered point
89
+ __device__ __forceinline__ real_t
90
+ shear_strain(const real_t *__restrict__ u, const int idx, const int cs,
91
+ const int k, const int l, const int *A, const int *S,
92
+ const int *NN, const real_t *F) {
93
+ const int r1 = rad_half(A[l], NN[l]); // d u_k / d x_l
94
+ const int r2 = rad_half(A[k], NN[k]); // d u_l / d x_k
95
+ real_t e = (real_t)0;
96
+ #pragma unroll
97
+ for (int j = 1; j <= STENCIL_RADIUS; ++j) {
98
+ if (j <= r1)
99
+ e += F[l] * SG_W(r1, j) *
100
+ (u[k * cs + idx + j * S[l]] - u[k * cs + idx - (j - 1) * S[l]]);
101
+ if (j <= r2)
102
+ e += F[k] * SG_W(r2, j) *
103
+ (u[l * cs + idx + j * S[k]] - u[l * cs + idx - (j - 1) * S[k]]);
104
+ }
105
+ return e;
106
+ }
107
+ #endif
108
+
109
+ // -------------------------------------- kernels
110
+ extern "C" {
111
+
112
+ // ------------------------------------------------------------------------------------
113
+ __global__ void
114
+ stress_kernel(const real_t *__restrict__ u1, real_t *__restrict__ sigma,
115
+ const real_t *__restrict__ gnode,
116
+ #if NDIM >= 2
117
+ const real_t *__restrict__ gshear,
118
+ #endif
119
+ const real_t lam, const real_t mu, const int clamped,
120
+ const int cs, AXIS_PARAMS) {
121
+ INTERIOR_OR_RETURN;
122
+ AXIS_GEOM;
123
+ AXIS_FACTORS;
124
+
125
+ real_t eps[NDIM];
126
+ const int zeroed = normal_strains(u1, idx, cs, A, S, NN, F, clamped, eps);
127
+ const real_t lam_eff = condensed_lame(lam, mu, zeroed);
128
+ real_t tr = (real_t)0;
129
+ #pragma unroll
130
+ for (int d = 0; d < NDIM; ++d)
131
+ tr += eps[d];
132
+ const real_t gn = gnode[idx];
133
+ #pragma unroll
134
+ for (int d = 0; d < NDIM; ++d)
135
+ sigma[d * cs + idx] = ((zeroed >> d) & 1)
136
+ ? (real_t)0
137
+ : gn * (2.f * mu * eps[d] + lam_eff * tr);
138
+
139
+ #if NDIM >= 2
140
+ #pragma unroll
141
+ for (int k = 0; k < NDIM - 1; ++k)
142
+ #pragma unroll
143
+ for (int l = k + 1; l < NDIM; ++l) {
144
+ if (A[k] > NN[k] - 3 || A[l] > NN[l] - 3)
145
+ continue;
146
+ const int v = PAIR_ROW(k, l);
147
+ sigma[v * cs + idx] = gshear[(v - NDIM) * cs + idx] * mu *
148
+ shear_strain(u1, idx, cs, k, l, A, S, NN, F);
149
+ }
150
+ #endif
151
+ }
152
+
153
+ // ------------------------------------------------------------------------------------
154
+ __global__ void
155
+ fd_kernel(const real_t *__restrict__ u0, const real_t *__restrict__ u1,
156
+ real_t *__restrict__ u2, const real_t *__restrict__ sigma,
157
+ const real_t *__restrict__ minv,
158
+ #ifdef USE_DAMPING
159
+ const real_t *__restrict__ damping, const real_t dt,
160
+ #endif
161
+ const int clamped, const int cs, AXIS_PARAMS) {
162
+ INTERIOR_OR_RETURN;
163
+ AXIS_GEOM;
164
+ AXIS_FACTORS;
165
+
166
+ #pragma unroll
167
+ for (int c = 0; c < NDIM; ++c) {
168
+ if (A[c] > NN[c] - 3)
169
+ continue; // no unknown on the staggered ghost of its own axis
170
+ real_t force = (real_t)0;
171
+ { // own normal stress along axis c, every tap keyed off the stress node
172
+ const real_t *__restrict__ sc = sigma + c * cs;
173
+ real_t acc = (real_t)0;
174
+ #pragma unroll
175
+ for (int k = 1; k <= STENCIL_RADIUS; ++k) {
176
+ const int ap = A[c] + k;
177
+ const int rp = rad_node(ap, NN[c]);
178
+ if (k <= rp)
179
+ acc += SG_W(rp, k) * sc[idx + k * S[c]];
180
+ else if (k == 1 && ap == NN[c] - 2 && clamped_face(clamped, c, 1))
181
+ acc += 2.f * sc[idx + S[c]]; // the fold of the clamped wall strain
182
+ const int am = A[c] - k + 1;
183
+ const int rm = (am >= 1) ? rad_node(am, NN[c]) : 0;
184
+ if (k <= rm)
185
+ acc -= SG_W(rm, k) * sc[idx - (k - 1) * S[c]];
186
+ else if (k == 1 && am == 1 && clamped_face(clamped, c, 0))
187
+ acc -= 2.f * sc[idx];
188
+ }
189
+ force += F[c] * acc;
190
+ }
191
+ #if NDIM >= 2
192
+ #pragma unroll
193
+ for (int l = 0; l < NDIM; ++l) { // shear stresses along the other axes
194
+ if (l == c)
195
+ continue;
196
+ const int v = PAIR_ROW(min(c, l), max(c, l));
197
+ const real_t *__restrict__ sv = sigma + v * cs;
198
+ real_t acc = (real_t)0;
199
+ #pragma unroll
200
+ for (int j = 1; j <= STENCIL_RADIUS; ++j) {
201
+ const int ap = A[l] + j - 1;
202
+ const int rp = (ap <= NN[l] - 3) ? rad_half(ap, NN[l]) : 0;
203
+ if (j <= rp)
204
+ acc += SG_W(rp, j) * sv[idx + (j - 1) * S[l]];
205
+ const int am = A[l] - j;
206
+ const int rm = (am >= 1) ? rad_half(am, NN[l]) : 0;
207
+ if (j <= rm)
208
+ acc -= SG_W(rm, j) * sv[idx - j * S[l]];
209
+ }
210
+ force += F[l] * acc;
211
+ }
212
+ #endif
213
+ const int n = c * cs + idx;
214
+ const real_t mi = minv[n];
215
+ #ifdef USE_DAMPING
216
+ const real_t beta = 0.5f * mi * damping[idx] * dt;
217
+ u2[n] = (2.f * u1[n] - u0[n] * (1.f - beta) + mi * force) / (1.f + beta);
218
+ #else
219
+ u2[n] = -u0[n] + 2.f * u1[n] + mi * force;
220
+ #endif
221
+ }
222
+ }
223
+
224
+
225
+ } // extern "C"
@@ -0,0 +1,217 @@
1
+ // Prepended by wave.compile_kernels: stencils.preamble, then common.cuh.
2
+ // Compile-time configuration this file responds to:
3
+ // NDIM = 1 | 2 | 3
4
+
5
+ #if NDIM == 1
6
+ #define AXIS_PARAMS const real_t f0, const int N0
7
+ #define INTERIOR_OR_RETURN \
8
+ const int a0 = blockIdx.x * blockDim.x + threadIdx.x; \
9
+ if (!(a0 > 0 && a0 < N0 - 1)) \
10
+ return; \
11
+ const int idx = a0
12
+ #define AXIS_GEOM const int A[1] = {a0}, S[1] = {1}, NN[1] = {N0}
13
+ #define AXIS_FACTORS const real_t F[1] = {f0}
14
+ #elif NDIM == 2
15
+ #define AXIS_PARAMS \
16
+ const real_t f0, const int N0, const real_t f1, const int N1, const int s0
17
+ #define INTERIOR_OR_RETURN \
18
+ const int a1 = blockIdx.x * blockDim.x + threadIdx.x; \
19
+ const int a0 = blockIdx.y * blockDim.y + threadIdx.y; \
20
+ if (!(a0 > 0 && a0 < N0 - 1 && a1 > 0 && a1 < N1 - 1)) \
21
+ return; \
22
+ const int idx = a0 * s0 + a1
23
+ #define AXIS_GEOM \
24
+ const int A[2] = {a0, a1}, S[2] = {s0, 1}, NN[2] = {N0, N1}
25
+ #define AXIS_FACTORS const real_t F[2] = {f0, f1}
26
+ #elif NDIM == 3
27
+ #define AXIS_PARAMS \
28
+ const real_t f0, const int N0, const real_t f1, const int N1, const int s0, \
29
+ const real_t f2, const int N2, const int s1
30
+ #define INTERIOR_OR_RETURN \
31
+ const int a2 = blockIdx.x * blockDim.x + threadIdx.x; \
32
+ const int a1 = blockIdx.y * blockDim.y + threadIdx.y; \
33
+ const int a0 = blockIdx.z * blockDim.z + threadIdx.z; \
34
+ if (!(a0 > 0 && a0 < N0 - 1 && a1 > 0 && a1 < N1 - 1 && a2 > 0 && \
35
+ a2 < N2 - 1)) \
36
+ return; \
37
+ const int idx = a0 * s0 + a1 * s1 + a2
38
+ #define AXIS_GEOM \
39
+ const int A[3] = {a0, a1, a2}, S[3] = {s0, s1, 1}, NN[3] = {N0, N1, N2}
40
+ #define AXIS_FACTORS const real_t F[3] = {f0, f1, f2}
41
+ #endif
42
+
43
+ // the node's normal strains, one per axis: a wall grades to a zero row under
44
+ // traction (returned as a set bit) and to the antisymmetric fold when clamped
45
+ __device__ __forceinline__ int
46
+ normal_strains(const real_t *__restrict__ u, const int idx, const int cs,
47
+ const int *A, const int *S, const int *NN, const real_t *F,
48
+ const int clamped, real_t *eps) {
49
+ int zeroed = 0;
50
+ #pragma unroll
51
+ for (int d = 0; d < NDIM; ++d) {
52
+ const int r = rad_node(A[d], NN[d]);
53
+ const real_t *__restrict__ ud = u + d * cs;
54
+ if (r > 0) {
55
+ real_t acc = (real_t)0;
56
+ #pragma unroll
57
+ for (int k = 1; k <= STENCIL_RADIUS; ++k)
58
+ if (k <= r)
59
+ acc += SG_W(r, k) * (ud[idx + (k - 1) * S[d]] - ud[idx - k * S[d]]);
60
+ eps[d] = F[d] * acc;
61
+ } else if (A[d] == 1 && clamped_face(clamped, d, 0)) {
62
+ eps[d] = F[d] * 2.f * ud[idx]; // the wall holds u = 0 half a node down
63
+ } else if (A[d] == NN[d] - 2 && clamped_face(clamped, d, 1)) {
64
+ eps[d] = -F[d] * 2.f * ud[idx - S[d]];
65
+ } else {
66
+ eps[d] = (real_t)0;
67
+ zeroed |= 1 << d;
68
+ }
69
+ }
70
+ return zeroed;
71
+ }
72
+
73
+ // a traction wall condenses its axis out of the coupling: the plane stress
74
+ // reduction of lam, applied once per zeroed axis
75
+ __device__ __forceinline__ real_t condensed_lame(const real_t lam,
76
+ const real_t mu,
77
+ const int zeroed) {
78
+ real_t lam_eff = lam;
79
+ #pragma unroll
80
+ for (int d = 0; d < NDIM; ++d)
81
+ if ((zeroed >> d) & 1)
82
+ lam_eff = 2.f * lam_eff * mu / (lam_eff + 2.f * mu);
83
+ return lam_eff;
84
+ }
85
+
86
+ #if NDIM >= 2
87
+ // the engineering shear strain of the (k, l) pair at its own staggered point
88
+ __device__ __forceinline__ real_t
89
+ shear_strain(const real_t *__restrict__ u, const int idx, const int cs,
90
+ const int k, const int l, const int *A, const int *S,
91
+ const int *NN, const real_t *F) {
92
+ const int r1 = rad_half(A[l], NN[l]); // d u_k / d x_l
93
+ const int r2 = rad_half(A[k], NN[k]); // d u_l / d x_k
94
+ real_t e = (real_t)0;
95
+ #pragma unroll
96
+ for (int j = 1; j <= STENCIL_RADIUS; ++j) {
97
+ if (j <= r1)
98
+ e += F[l] * SG_W(r1, j) *
99
+ (u[k * cs + idx + j * S[l]] - u[k * cs + idx - (j - 1) * S[l]]);
100
+ if (j <= r2)
101
+ e += F[k] * SG_W(r2, j) *
102
+ (u[l * cs + idx + j * S[k]] - u[l * cs + idx - (j - 1) * S[k]]);
103
+ }
104
+ return e;
105
+ }
106
+ #endif
107
+
108
+ // the normal bilinear form of two strain sets under the condensed coupling
109
+ __device__ __forceinline__ real_t normal_form(const real_t *ea,
110
+ const real_t *eb,
111
+ const real_t lam,
112
+ const real_t mu,
113
+ const int zeroed) {
114
+ const real_t lam_eff = condensed_lame(lam, mu, zeroed);
115
+ real_t tra = (real_t)0, trb = (real_t)0, dot = (real_t)0;
116
+ #pragma unroll
117
+ for (int d = 0; d < NDIM; ++d) {
118
+ tra += ea[d];
119
+ trb += eb[d];
120
+ dot += ea[d] * eb[d];
121
+ }
122
+ return 2.f * mu * dot + lam_eff * tra * trb;
123
+ }
124
+
125
+ // -------------------------------------- kernels
126
+ extern "C" {
127
+
128
+ // ------------------------------------------------------------------------------------
129
+ __global__ void gradient_kernel(real_t *__restrict__ g_mass,
130
+ real_t *__restrict__ g_normal,
131
+ #if NDIM >= 2
132
+ real_t *__restrict__ g_shear,
133
+ #endif
134
+ const real_t *__restrict__ u0,
135
+ const real_t *__restrict__ u1,
136
+ const real_t *__restrict__ u2,
137
+ const real_t *__restrict__ l1, const real_t lam,
138
+ const real_t mu, const real_t mf,
139
+ const int clamped, const int cs, AXIS_PARAMS) {
140
+ INTERIOR_OR_RETURN;
141
+ AXIS_GEOM;
142
+ AXIS_FACTORS;
143
+
144
+ // dJ/dmass on the component points: no neighbour and no material load
145
+ #pragma unroll
146
+ for (int c = 0; c < NDIM; ++c) {
147
+ if (A[c] > NN[c] - 3)
148
+ continue;
149
+ const int n = c * cs + idx;
150
+ g_mass[n] -= mf * l1[n] * (u2[n] - 2.f * u1[n] + u0[n]);
151
+ }
152
+
153
+ // dJ/dgamma on the node: forward against adjoint under the normal coupling
154
+ real_t eu[NDIM], el[NDIM];
155
+ const int zeroed = normal_strains(u1, idx, cs, A, S, NN, F, clamped, eu);
156
+ normal_strains(l1, idx, cs, A, S, NN, F, clamped, el);
157
+ g_normal[idx] -= normal_form(el, eu, lam, mu, zeroed);
158
+
159
+ #if NDIM >= 2
160
+ #pragma unroll
161
+ for (int k = 0; k < NDIM - 1; ++k) // and on each of its shear points
162
+ #pragma unroll
163
+ for (int l = k + 1; l < NDIM; ++l) {
164
+ if (A[k] > NN[k] - 3 || A[l] > NN[l] - 3)
165
+ continue;
166
+ const int v = PAIR_ROW(k, l);
167
+ g_shear[(v - NDIM) * cs + idx] -=
168
+ mu * shear_strain(l1, idx, cs, k, l, A, S, NN, F) *
169
+ shear_strain(u1, idx, cs, k, l, A, S, NN, F);
170
+ }
171
+ #endif
172
+ }
173
+
174
+ // ------------------------------------------------------------------------------------
175
+ __global__ void frechet_kernel(real_t *__restrict__ acc_mass,
176
+ real_t *__restrict__ acc_normal,
177
+ #if NDIM >= 2
178
+ real_t *__restrict__ acc_shear,
179
+ #endif
180
+ const real_t *__restrict__ u0,
181
+ const real_t *__restrict__ u1,
182
+ const real_t *__restrict__ u2, const real_t lam,
183
+ const real_t mu, const real_t ft,
184
+ const real_t fs, const int clamped,
185
+ const int cs, AXIS_PARAMS) {
186
+ INTERIOR_OR_RETURN;
187
+ AXIS_GEOM;
188
+ AXIS_FACTORS;
189
+
190
+ #pragma unroll
191
+ for (int c = 0; c < NDIM; ++c) {
192
+ if (A[c] > NN[c] - 3)
193
+ continue;
194
+ const int n = c * cs + idx;
195
+ const real_t dudt = u2[n] - u0[n];
196
+ acc_mass[n] += ft * dudt * dudt;
197
+ }
198
+
199
+ real_t eu[NDIM];
200
+ const int zeroed = normal_strains(u1, idx, cs, A, S, NN, F, clamped, eu);
201
+ acc_normal[idx] += fs * normal_form(eu, eu, lam, mu, zeroed);
202
+
203
+ #if NDIM >= 2
204
+ #pragma unroll
205
+ for (int k = 0; k < NDIM - 1; ++k)
206
+ #pragma unroll
207
+ for (int l = k + 1; l < NDIM; ++l) {
208
+ if (A[k] > NN[k] - 3 || A[l] > NN[l] - 3)
209
+ continue;
210
+ const int v = PAIR_ROW(k, l);
211
+ const real_t e = shear_strain(u1, idx, cs, k, l, A, S, NN, F);
212
+ acc_shear[(v - NDIM) * cs + idx] += fs * mu * e * e;
213
+ }
214
+ #endif
215
+ }
216
+
217
+ } // extern "C"
@@ -0,0 +1,154 @@
1
+ // Prepended by wave.compile_kernels: stencils.preamble, then common.cuh.
2
+ // Compile-time configuration this file responds to:
3
+ // NDIM = 2 | 3
4
+ // USE_DAMPING
5
+ // USE_MAGNETIC
6
+
7
+ #if NDIM == 1
8
+ #error "a single in-plane component has no curl; use maxwell.ElectricWave"
9
+ #endif
10
+
11
+ #if NDIM == 2
12
+ #define AXIS_PARAMS \
13
+ const real_t f0, const int N0, const real_t f1, const int N1, const int s0
14
+ #define INTERIOR_OR_RETURN \
15
+ const int a1 = blockIdx.x * blockDim.x + threadIdx.x; \
16
+ const int a0 = blockIdx.y * blockDim.y + threadIdx.y; \
17
+ if (!(a0 > 0 && a0 < N0 - 1 && a1 > 0 && a1 < N1 - 1)) \
18
+ return; \
19
+ const int idx = a0 * s0 + a1
20
+ #define AXIS_GEOM \
21
+ const int A[2] = {a0, a1}, S[2] = {s0, 1}, NN[2] = {N0, N1}
22
+ #define AXIS_FACTORS const real_t F[2] = {f0, f1}
23
+ #elif NDIM == 3
24
+ #define AXIS_PARAMS \
25
+ const real_t f0, const int N0, const real_t f1, const int N1, const int s0, \
26
+ const real_t f2, const int N2, const int s1
27
+ #define INTERIOR_OR_RETURN \
28
+ const int a2 = blockIdx.x * blockDim.x + threadIdx.x; \
29
+ const int a1 = blockIdx.y * blockDim.y + threadIdx.y; \
30
+ const int a0 = blockIdx.z * blockDim.z + threadIdx.z; \
31
+ if (!(a0 > 0 && a0 < N0 - 1 && a1 > 0 && a1 < N1 - 1 && a2 > 0 && \
32
+ a2 < N2 - 1)) \
33
+ return; \
34
+ const int idx = a0 * s0 + a1 * s1 + a2
35
+ #define AXIS_GEOM \
36
+ const int A[3] = {a0, a1, a2}, S[3] = {s0, s1, 1}, NN[3] = {N0, N1, N2}
37
+ #define AXIS_FACTORS const real_t F[3] = {f0, f1, f2}
38
+ #endif
39
+
40
+ // ------------------------------------ curl helpers
41
+ // the (k, l) curl component at its own staggered point, the elastic shear
42
+ // strain with its first term negated: both taps are half-point differences
43
+ __device__ __forceinline__ real_t
44
+ curl_component(const real_t *__restrict__ u, const int idx, const int cs,
45
+ const int k, const int l, const int *A, const int *S,
46
+ const int *NN, const real_t *F) {
47
+ const int r1 = rad_half(A[l], NN[l]); // d u_k / d x_l
48
+ const int r2 = rad_half(A[k], NN[k]); // d u_l / d x_k
49
+ real_t b = (real_t)0;
50
+ #pragma unroll
51
+ for (int j = 1; j <= STENCIL_RADIUS; ++j) {
52
+ if (j <= r1)
53
+ b -= F[l] * SG_W(r1, j) *
54
+ (u[k * cs + idx + j * S[l]] - u[k * cs + idx - (j - 1) * S[l]]);
55
+ if (j <= r2)
56
+ b += F[k] * SG_W(r2, j) *
57
+ (u[l * cs + idx + j * S[k]] - u[l * cs + idx - (j - 1) * S[k]]);
58
+ }
59
+ return b;
60
+ }
61
+
62
+ // cell weight of a pair point, halved on the walls of the axes it does not span
63
+ __device__ __forceinline__ real_t pair_weight(const int k, const int l,
64
+ const int *A, const int *NN) {
65
+ real_t w = (real_t)1;
66
+ #pragma unroll
67
+ for (int d = 0; d < NDIM; ++d)
68
+ if (d != k && d != l && (A[d] == 1 || A[d] == NN[d] - 2))
69
+ w *= (real_t)0.5;
70
+ return w;
71
+ }
72
+
73
+ // -------------------------------------- kernels
74
+ extern "C" {
75
+
76
+ // ------------------------------------------------------------------------------------
77
+ __global__ void curl_kernel(const real_t *__restrict__ u1,
78
+ real_t *__restrict__ h,
79
+ #ifdef USE_MAGNETIC
80
+ const real_t *__restrict__ nu_pair,
81
+ #endif
82
+ const real_t nu, const int cs, AXIS_PARAMS) {
83
+ INTERIOR_OR_RETURN;
84
+ AXIS_GEOM;
85
+ AXIS_FACTORS;
86
+
87
+ #pragma unroll
88
+ for (int k = 0; k < NDIM - 1; ++k)
89
+ #pragma unroll
90
+ for (int l = k + 1; l < NDIM; ++l) {
91
+ if (A[k] > NN[k] - 3 || A[l] > NN[l] - 3)
92
+ continue; // no pair point on the staggered ghost of either axis
93
+ const int p = PAIR_ROW(k, l) - NDIM;
94
+ const real_t b = curl_component(u1, idx, cs, k, l, A, S, NN, F);
95
+ #ifdef USE_MAGNETIC
96
+ h[p * cs + idx] = nu_pair[p * cs + idx] * b; // the weight is folded in
97
+ #else
98
+ h[p * cs + idx] = nu * pair_weight(k, l, A, NN) * b;
99
+ #endif
100
+ }
101
+ }
102
+
103
+ // ------------------------------------------------------------------------------------
104
+ __global__ void
105
+ fd_kernel(const real_t *__restrict__ u0, const real_t *__restrict__ u1,
106
+ real_t *__restrict__ u2, const real_t *__restrict__ h,
107
+ const real_t *__restrict__ minv,
108
+ #ifdef USE_DAMPING
109
+ const real_t *__restrict__ damping, const real_t dt,
110
+ #endif
111
+ const int cs, AXIS_PARAMS) {
112
+ INTERIOR_OR_RETURN;
113
+ AXIS_GEOM;
114
+ AXIS_FACTORS;
115
+
116
+ #pragma unroll
117
+ for (int c = 0; c < NDIM; ++c) {
118
+ if (A[c] > NN[c] - 3)
119
+ continue; // no unknown on the staggered ghost of its own axis
120
+ real_t force = (real_t)0;
121
+ #pragma unroll
122
+ for (int m = 0; m < NDIM; ++m) { // the pairs this component belongs to
123
+ if (m == c)
124
+ continue;
125
+ const int p = PAIR_ROW(min(c, m), max(c, m)) - NDIM;
126
+ const real_t *__restrict__ hp = h + p * cs;
127
+ // E_c enters its pair as +d_m E_c for m < c and as -d_m E_c for m > c
128
+ const real_t sgn = (m < c) ? (real_t)1 : (real_t)-1;
129
+ real_t acc = (real_t)0;
130
+ #pragma unroll
131
+ for (int j = 1; j <= STENCIL_RADIUS; ++j) {
132
+ const int ap = A[m] + j - 1;
133
+ const int rp = (ap <= NN[m] - 3) ? rad_half(ap, NN[m]) : 0;
134
+ if (j <= rp)
135
+ acc += SG_W(rp, j) * hp[idx + (j - 1) * S[m]];
136
+ const int am = A[m] - j;
137
+ const int rm = (am >= 1) ? rad_half(am, NN[m]) : 0;
138
+ if (j <= rm)
139
+ acc -= SG_W(rm, j) * hp[idx - j * S[m]];
140
+ }
141
+ force += sgn * F[m] * acc;
142
+ }
143
+ const int n = c * cs + idx;
144
+ const real_t mi = minv[n];
145
+ #ifdef USE_DAMPING
146
+ const real_t beta = 0.5f * mi * damping[idx] * dt;
147
+ u2[n] = (2.f * u1[n] - u0[n] * (1.f - beta) + mi * force) / (1.f + beta);
148
+ #else
149
+ u2[n] = -u0[n] + 2.f * u1[n] + mi * force;
150
+ #endif
151
+ }
152
+ }
153
+
154
+ } // extern "C"