Neko-TOP
A portable framework for high-order spectral element flow toplogy optimization.
Loading...
Searching...
No Matches
mma_kernel.h
Go to the documentation of this file.
1
37#ifndef MMA_CUDA_KERNEL_H
38#define MMA_CUDA_KERNEL_H
39
40#include <cfloat>
41
42// z term of the Hessian of the dip subsolver, Hess -= 10 * a * a^T. The
43// factor 10 is the inverse of the weight 2 * 0.05 of z^2 in L_z.
44template <typename T>
45__global__ void mma_update_hessian_z_kernel(
47 const T* __restrict__ a,
48 const int m)
49{
50 int tid = blockIdx.x * blockDim.x + threadIdx.x;
51 int total = m * m;
52
53 if (tid >= total) return;
54
55 int i = tid % m;
56 int j = tid / m;
57
58 Hess[tid] -= T(10.0) * a[i] * a[j];
59}
60
61template<typename T>
62__global__ void mma_prepare_aa_matrix_kernel(T* __restrict__ AA,
63 const T* __restrict__ s, const T* __restrict__ lambda,
64 const T* __restrict__ d, const T* __restrict__ mu,
65 const T* __restrict__ y, const T* __restrict__ a,
66 const T zeta, const T z, const int m) {
67 const int tj = blockIdx.x * blockDim.x + threadIdx.x;
68 const int matrix_size = m + 1;
69
70 if (tj >= m) return;
71 AA[tj * matrix_size + tj] += s[tj] / lambda[tj] +
72 (T)1.0 / (d[tj] + mu[tj] / y[tj]);
73 AA[tj * matrix_size + m] = a[tj]; // column m+1
74 AA[m * matrix_size + tj] = a[tj];// row m+1
75
76 // Only first thread updates the bottom-right corner element.
77 if (tj == 0)
78 AA[m * matrix_size + m] = -zeta / z;
79}
80
81
82//Update Hessian diagonal elements (y contributions for dip subsolve)
83template<typename T>
84__global__ void mma_update_hessian_diagonal_kernel(T* __restrict__ Hess,
85 const T* __restrict__ y, const T* __restrict__ mu,
86 const T* __restrict__ lambda, const int m) {
87 int tj = blockIdx.x * blockDim.x + threadIdx.x;
88 if (tj >= m) return;
89
90 T diag = Hess[tj * m + tj];
91 // Contribution from y terms (inactive constraints)
92 if (y[tj] > (T)0.0) {
93 diag -= (T)1.0;
94 }
95
96 // Contribution from -mu/lambda (eq 10)
97 diag -= mu[tj] / lambda[tj];
98 Hess[tj * m + tj] = diag;
99}
100
101// Levenberg-Marquardt algorithm (heuristically)
102// Single-block version for m <= 1024
103template<typename T>
104__global__ void mma_stabilize_hessian_single_kernel(T* __restrict__ Hess, const int m) {
105 const int tid = threadIdx.x;
106
107 // Single thread computes trace and LM factor
108 if (tid == 0) {
109 T trace = (T)0.0;
110 for (int j = 0; j < m; j++) {
111 trace += Hess[j * m + j];
112 }
113 T lm_factor = max((T)(-1.0e-4) * trace / m, (T)1.0e-7);
114
115 // Apply to all diagonal elements
116 for (int j = 0; j < m; j++) {
117 Hess[j * m + j] -= lm_factor;
118 }
119 }
120}
121
122// Levenberg-Marquardt algorithm (heuristically)
123// Multi-block version for m > 1024
124template<typename T>
125__global__ void mma_stabilize_hessian_multi_kernel(T* __restrict__ Hess, const T lm_factor, const int m) {
126 const int i = blockIdx.x * blockDim.x + threadIdx.x;
127 if (i < m) {
128 Hess[i * m + i] -= lm_factor;
129 }
130}
131
132// Small linear solver for MMA (n <= 50)
133template<typename T>
134__global__ void mma_small_lu_kernel(T* __restrict__ A, T* __restrict__ b, const int n) {
135 const int tid = threadIdx.x;
136
137 // Handle 1x1 case
138 if (n == 1) {
139 if (tid == 0 && abs(A[0]) > (T)1e-12) b[0] /= A[0];
140 return;
141 }
142
143 // LU decomposition with partial pivoting
144 for (int k = 0; k < n; k++) {
145 // Pivoting - single thread
146 if (tid == 0) {
147 int max_row = k;
148 T max_val = abs(A[k * n + k]);
149 for (int i = k + 1; i < n; i++) {
150 T val = abs(A[i * n + k]);
151 if (val > max_val) {
152 max_val = val;
153 max_row = i;
154 }
155 }
156 if (max_val > (T)1e-12 && max_row != k) {
157 // Swap rows
158 for (int j = k; j < n; j++) {
159 T temp = A[k * n + j];
160 A[k * n + j] = A[max_row * n + j];
161 A[max_row * n + j] = temp;
162 }
163 // Swap rhs
164 T temp_b = b[k];
165 b[k] = b[max_row];
166 b[max_row] = temp_b;
167 }
168 }
170
171 // Parallel elimination
172 T diag = A[k * n + k];
173 if (abs(diag) > (T)1e-12) {
174 for (int i = tid + k + 1; i < n; i += blockDim.x) {
175 T factor = A[i * n + k] / diag;
176 A[i * n + k] = factor;
177 for (int j = k + 1; j < n; j++) {
178 A[i * n + j] -= factor * A[k * n + j];
179 }
180 }
181 }
183 }
184
185 // Parallel forward substitution
186 for (int i = tid; i < n; i += blockDim.x) {
187 T sum = b[i];
188 for (int j = 0; j < i; j++) {
189 sum -= A[i * n + j] * b[j];
190 }
191 b[i] = sum;
192 }
194
195 // Parallel backward substitution
196 for (int i = n - 1 - tid; i >= 0; i -= blockDim.x) {
197 if (i >= 0) {
198 T sum = b[i];
199 for (int j = i + 1; j < n; j++) {
200 sum -= A[i * n + j] * b[j];
201 }
202 if (abs(A[i * n + i]) > (T)1e-12) {
203 b[i] = sum / A[i * n + i];
204 }
205 }
206 }
208}
209
210template <typename T>
211__global__ void delta_1dbeam_kernel(T* __restrict__ Delta,
212 const T L_total, const T Le, const int offset, const int n) {
213 int k = blockIdx.x * blockDim.x + threadIdx.x;
214 if (k >= n) return;
215
216 // Convert to 1-based indexing for the calculation
217 int idx = k + 1;
218
219 // Calculate first term: (L_total - Le*(offset+idx-1))^3
220 T x1 = L_total - Le * static_cast<T>(offset + idx - 1);
221 T term1 = x1 * x1 * x1;
222
223 // Calculate second term: (L_total - Le*(offset+idx))^3
224 T x2 = L_total - Le * static_cast<T>(offset + idx);
225 T term2 = x2 * x2 * x2;
226
227 // Final result
228 Delta[k] = (term1 - term2) / static_cast<T>(3.0);
229}
230
231template <typename T>
232__global__ void mma_Ljjxinv_kernel(T* __restrict__ Ljjxinv,
234 const T* __restrict__ x, const T* __restrict__ low, const T* __restrict__ upp,
235 const T* __restrict__ alpha, const T* __restrict__ beta,
236 const int n) {
237 int tj = blockIdx.x * blockDim.x + threadIdx.x;
238 if (tj >= n) return;
239
240 // Load inputs into registers
241 const T xt = x[tj];
242 const T low_j = low[tj];
243 const T upp_j = upp[tj];
244 const T pj = pjlambda[tj];
245 const T qj = qjlambda[tj];
246
247 // Precompute reused differences
248 const T diff_u = upp_j - xt;
249 const T diff_l = xt - low_j;
250
251 // Cube once (avoiding pow for speed)
252 const T diff_u3 = diff_u * diff_u * diff_u;
253 const T diff_l3 = diff_l * diff_l * diff_l;
254
255 // Compute inverse value safely
256 T denom = 2.0 * pj / diff_u3 + 2.0 * qj / diff_l3;
257 T val = -1.0 / denom;
258
259 // Mask out active primal constraints, as mma_subsolve_dip_cpu with
260 // NEKO_EPS = epsilon(1.0_rp)
261 const T eps = (sizeof(T) == sizeof(float)) ?
262 static_cast<T>(FLT_EPSILON) :
263 static_cast<T>(DBL_EPSILON);
264 bool active = (xt - alpha[tj] < eps) || (beta[tj] - xt < eps);
265
266 Ljjxinv[tj] = active ? T(0.0) : val;
267}
268
269template <typename T>
270__global__ void mma_dipsolvesub1_kernel(T* __restrict__ x,
272 const T* __restrict__ low, const T* __restrict__ upp,
273 const T* __restrict__ alpha, const T* __restrict__ beta,
274 const int n) {
275
276 int tj = blockIdx.x * blockDim.x + threadIdx.x;
277 if (tj >= n) return;
278
279 // Load inputs into registers
280 const T pj_sqrt = sqrt(pjlambda[tj]);
281 const T qj_sqrt = sqrt(qjlambda[tj]);
282 const T denom = pj_sqrt + qj_sqrt;
283
284 const T low_j = low[tj];
285 const T upp_j = upp[tj];
286 const T alpha_j = alpha[tj];
287 const T beta_j = beta[tj];
288
289 // Weighted average
290 const T val = (pj_sqrt * low_j + qj_sqrt * upp_j) / denom;
291
292 // Clamp x between alpha and beta using branchless min/max
293 x[tj] = fmin(fmax(val, alpha_j), beta_j);
294}
295
296template <typename T>
297__global__ void mattrans_v_mul_kernel(T* __restrict__ output,
298 const T* __restrict__ pij, const T* __restrict__ lambda,
299 const int m, const int n) {
300
301 int tj = blockIdx.x * blockDim.x + threadIdx.x;
302 if (tj >= n) return;
303
304 T acc = 0.0;
305
306 // Use register accumulation
307 for (int i = 0; i < m; ++i) {
308 acc += pij[i + tj * m] * lambda[i];
309 }
310
311 output[tj] = acc;
312}
313
314template <typename T>
315__global__ void mma_sub1_kernel(
318 const T* __restrict__ x,
319 const T* __restrict__ xmin,
320 const T* __restrict__ xmax,
321 const T asyinit,
322 const int n) {
323 int tj = blockIdx.x * blockDim.x + threadIdx.x;
324 if (tj >= n) return;
325
326 // Load values once into registers (global memory is slow)
327 const T xt = x[tj];
328 const T xmin_j = xmin[tj];
329 const T xmax_j = xmax[tj];
330
331 // Reuse xgap calculation
332 const T xgap = xmax_j - xmin_j;
333 const T offset = asyinit * xgap;
334
335 xlow[tj] = xt - offset;
336 xupp[tj] = xt + offset;
337}
338
339
340
341template< typename T >
342__global__ void mma_sub2_kernel(T* __restrict__ low, T* __restrict__ upp,
343 const T* __restrict__ x, const T* __restrict__ xold1,
344 const T* __restrict__ xold2, const T* __restrict__ xdiff,
345 const T asydecr, const T asyincr, const int n) {
346 int tj = blockIdx.x * blockDim.x + threadIdx.x;
347 if (tj >= n) return;
348
349 // Load data into registers for faster accessing compare to global memory
350 // when accessing repeatedly)
351 const T xval = x[tj];
352 const T xold1val = xold1[tj];
353 const T xold2val = xold2[tj];
354 const T lowval = low[tj];
355 const T uppval = upp[tj];
356 const T xdiffval = xdiff[tj];
357
358 // Compute the product
359 const T prod = (xval - xold1val) * (xold1val - xold2val);
360
361 // Compute asy_factor without branching
362 T asy_factor = (prod < T(0)) ? asydecr :
363 (prod > T(0)) ? asyincr : T(1);
364
365 // Update low and upp using fma (fused multiply-add) for numerical stability
368
369 // Apply bounds
370 new_low = max(new_low, xval - T(10.0) * xdiffval);
371 new_low = min(new_low, xval - T(0.01) * xdiffval);
372
373 new_upp = min(new_upp, xval + T(10.0) * xdiffval);
374 new_upp = max(new_upp, xval + T(0.01) * xdiffval);
375
376 // Write results back
377 low[tj] = new_low;
378 upp[tj] = new_upp;
379}
380
381template <typename T>
382__global__ void mma_sub3_kernel( const T* __restrict__ x,
383 const T* __restrict__ df0dx, const T* __restrict__ dfdx,
384 T* __restrict__ low, T* __restrict__ upp, const T* __restrict__ xmin,
385 const T* __restrict__ xmax, T* __restrict__ alpha, T* __restrict__ beta,
386 T* __restrict__ p0j, T* __restrict__ q0j, T* __restrict__ pij,
387 T* __restrict__ qij, const int n, const int m) {
388 int tj = blockIdx.x * blockDim.x + threadIdx.x;
389 if (tj >= n) return;
390
391 // Load into registers once
392 const T xt = x[tj];
393 const T xmin_j = xmin[tj];
394 const T xmax_j = xmax[tj];
395 const T low_j = low[tj];
396 const T upp_j = upp[tj];
397 const T df0 = df0dx[tj];
398 const T xgap = xmax_j - xmin_j;
399
400 // Clamp helpers
401 const T tenth_low_diff = T(0.1) * (xt - low_j);
402 const T tenth_upp_diff = T(0.1) * (upp_j - xt);
403
404 // Compute alpha and beta with fused max/min and fewer calls
407
408 alpha[tj] = alpha_val;
409 beta[tj] = beta_val;
410
411 // Avoid multiple pow calls, compute once
412 const T upp_minus_x = upp_j - xt;
413 const T x_minus_low = xt - low_j;
416
417 // Small epsilon for numerical stability
418 const T eps = T(1e-5);
419 const T inv_xgap = T(1.0) / max(eps, xgap);
420
421 // Compute terms reused multiple times
422 const T max_df0_pos = max(df0, T(0));
423 const T max_df0_neg = max(-df0, T(0));
424
425 p0j[tj] = upp_minus_x_sq * (T(1.001) * max_df0_pos +
426 T(0.001) * max_df0_neg + eps * inv_xgap);
427 q0j[tj] = x_minus_low_sq * (T(0.001) * max_df0_pos +
428 T(1.001) * max_df0_neg + eps * inv_xgap);
429
430 // Loop over m for pij and qij
431 for (int i = 0; i < m; ++i) {
432 int idx = i + tj * m;
433
434 T dfdx_val = dfdx[idx];
435 T max_pos = max(dfdx_val, T(0));
436 T max_neg = max(-dfdx_val, T(0));
437
438 pij[idx] = upp_minus_x_sq * (T(1.001) * max_pos +
439 T(0.001) * max_neg + eps * inv_xgap);
440 qij[idx] = x_minus_low_sq * (T(0.001) * max_pos +
441 T(1.001) * max_neg + eps * inv_xgap);
442 }
443}
444
445// As mma_sub3_kernel, but with the p0j, q0j, pij and qij of the dip
446// subsolver (mma_gensub_cpu): following MMA::GenSub in topopt_in_petsc
447// (constraintModification = false), only the objective is regularised,
448// by 0.5e-6/(upp - low).
449template <typename T>
450__global__ void mma_sub3_dip_kernel( const T* __restrict__ x,
451 const T* __restrict__ df0dx, const T* __restrict__ dfdx,
452 T* __restrict__ low, T* __restrict__ upp, const T* __restrict__ xmin,
453 const T* __restrict__ xmax, T* __restrict__ alpha, T* __restrict__ beta,
454 T* __restrict__ p0j, T* __restrict__ q0j, T* __restrict__ pij,
455 T* __restrict__ qij, const int n, const int m) {
456 int tj = blockIdx.x * blockDim.x + threadIdx.x;
457 if (tj >= n) return;
458
459 // Load into registers once
460 const T xt = x[tj];
461 const T xmin_j = xmin[tj];
462 const T xmax_j = xmax[tj];
463 const T low_j = low[tj];
464 const T upp_j = upp[tj];
465 const T df0 = df0dx[tj];
466
467 // Clamp helpers
468 const T tenth_low_diff = T(0.1) * (xt - low_j);
469 const T tenth_upp_diff = T(0.1) * (upp_j - xt);
470
471 // Compute alpha and beta with fused max/min and fewer calls
474
475 alpha[tj] = alpha_val;
476 beta[tj] = beta_val;
477
478 const T upp_minus_x = upp_j - xt;
479 const T x_minus_low = xt - low_j;
482
483 // Regularisation of the objective, a true division as in mma_gensub_cpu
484 const T reg = T(0.5) * T(1.0e-6) / (upp_j - low_j);
485
486 p0j[tj] = upp_minus_x_sq * (max(df0, T(0)) + T(0.001) * abs(df0) + reg);
487 q0j[tj] = x_minus_low_sq * (max(-df0, T(0)) + T(0.001) * abs(df0) + reg);
488
489 // The constraints are not regularised
490 for (int i = 0; i < m; ++i) {
491 const int idx = i + tj * m;
492 const T dfdx_val = dfdx[idx];
493
494 pij[idx] = upp_minus_x_sq * max(dfdx_val, T(0));
495 qij[idx] = x_minus_low_sq * max(-dfdx_val, T(0));
496 }
497}
498
499template <typename T>
500__global__ void mma_sub4_kernel(
501 const T* __restrict__ x,
502 const T* __restrict__ low,
503 const T* __restrict__ upp,
504 const T* __restrict__ pij,
505 const T* __restrict__ qij,
506 T* __restrict__ temp,
507 const int n,
508 const int m) {
509
510 int tj = blockIdx.x * blockDim.x + threadIdx.x;
511 if (tj >= n) return;
512
513 // Register caching
514 const T xt = x[tj];
515 const T low_j = low[tj];
516 const T upp_j = upp[tj];
517
518 const T denom_upp = upp_j - xt;
519 const T denom_low = xt - low_j;
520
521 const T eps = T(1e-12); // Precision-dependent epsilon
522 const T inv_denom_upp = T(1) / max(denom_upp, eps);
523 const T inv_denom_low = T(1) / max(denom_low, eps);
524
525 const int base_idx = tj * m;
526
527 for (int i = 0; i < m; ++i) {
528 int idx = base_idx + i;
529 temp[idx] = pij[idx] * inv_denom_upp + qij[idx] * inv_denom_low;
530 }
531}
532
533
534template <typename T>
535__global__ void mma_max2_kernel(
536 T* __restrict__ xsi,
537 const T* __restrict__ x,
538 const T* __restrict__ alpha,
539 const int n) {
540
541 int tj = blockIdx.x * blockDim.x + threadIdx.x;
542 if (tj >= n) return;
543
544 const T eps = T(1e-12);
545 T denom = max(x[tj] - alpha[tj], eps);
546 xsi[tj] = max(T(1), T(1) / denom);
547}
548
549
550
551
552template <typename T>
553__global__ void relambda_kernel(
554 T* __restrict__ temp,
555 const T* __restrict__ x,
556 const T* __restrict__ xupp,
557 const T* __restrict__ xlow,
558 const T* __restrict__ pij,
559 const T* __restrict__ qij,
560 const int n,
561 const int m) {
562
563 int tj = blockIdx.x * blockDim.x + threadIdx.x;
564 if (tj >= n) return;
565
566 const T xt = x[tj];
567 const T xup = xupp[tj];
568 const T xlo = xlow[tj];
569
570 // Prevent divide-by-zero using small epsilon
571 const T eps = T(1e-12);
572 const T inv_denom_upp = T(1) / max(xup - xt, eps);
573 const T inv_denom_low = T(1) / max(xt - xlo, eps);
574
575 int base_idx = tj * m;
576 for (int i = 0; i < m; ++i) {
577 int idx = base_idx + i;
578 temp[idx] = pij[idx] * inv_denom_upp + qij[idx] * inv_denom_low;
579 }
580}
581
582
583template <typename T>
584__global__ void sub2cons2_kernel(
585 T* __restrict__ a,
586 const T* __restrict__ b,
587 const T* __restrict__ c,
588 const T* __restrict__ d,
589 const T e,
590 const int n) {
591 const int idx = blockIdx.x * blockDim.x + threadIdx.x;
592 if (idx >= n) return;
593
594 const T bt = b[idx];
595 const T ct = c[idx];
596 const T dt = d[idx];
597
598 a[idx] = bt * (ct - dt) - e;
599}
600
601
602template< typename T>
603__inline__ __device__ T max_reduce_warp(T val) {
604 val = max(val, __shfl_down_sync(0xffffffff, val, 16));
605 val = max(val, __shfl_down_sync(0xffffffff, val, 8));
606 val = max(val, __shfl_down_sync(0xffffffff, val, 4));
607 val = max(val, __shfl_down_sync(0xffffffff, val, 2));
608 val = max(val, __shfl_down_sync(0xffffffff, val, 1));
609 return val;
610}
611
612
613
614template <typename T>
615__global__ void maxval_kernel(const T* __restrict__ a, T* temp, const int n) {
616 const int idx = blockIdx.x * blockDim.x + threadIdx.x;
617 const int stride = blockDim.x * gridDim.x;
618
619 const unsigned int lane = threadIdx.x % warpSize;
620 const unsigned int warp_id = threadIdx.x / warpSize;
621
622 // Shared memory to store warp-level maxima
623 __shared__ T shared[32]; // Assumes max 1024 threads per block
624
625 T local_max = T(0);
626 for (int i = idx; i < n; i += stride) {
627 local_max = max(local_max, abs(a[i]));
628 }
629
630 // Warp-level reduction
632
633 // Write warp-level results to shared memory
634 if (lane == 0) {
636 }
638
639 // Let the first warp reduce shared values
640 local_max = (threadIdx.x < blockDim.x / warpSize) ? shared[lane] : T(0);
641
642 if (warp_id == 0) {
644 }
645
646 if (threadIdx.x == 0) {
647 temp[blockIdx.x] = local_max;
648 }
649}
650
651
652template <typename T>
653__global__ void max_reduce_kernel(T* __restrict__ bufred, const int n) {
654
655 T maxval = T(0);
656 const int idx = blockIdx.x * blockDim.x + threadIdx.x;
657 const int stride = blockDim.x * gridDim.x;
658
659 // Grid-stride loop to cover all elements
660 for (int i = idx; i < n; i += stride) {
661 maxval = max(maxval, bufred[i]);
662 }
663
664 __shared__ T shared[32]; // One slot per warp (max 1024 threads/block)
665
666 unsigned int lane = threadIdx.x % warpSize;
667 unsigned int wid = threadIdx.x / warpSize;
668
669 // Warp-level max reduction
671
672 // Store each warp's max value in shared memory
673 if (lane == 0) {
674 shared[wid] = maxval;
675 }
677
678 // Let the first warp reduce the warp-level maxima
679 maxval = (threadIdx.x < blockDim.x / warpSize) ? shared[lane] : T(0);
680 if (wid == 0) {
682 }
683
684 // Write the block's maximum value back to bufred[blockIdx.x]
685 if (threadIdx.x == 0) {
687 }
688}
689
690template <typename T>
691__global__ void delx_kernel(
693 const T* __restrict__ x,
694 const T* __restrict__ xlow,
695 const T* __restrict__ xupp,
696 const T* __restrict__ pij,
697 const T* __restrict__ qij,
698 const T* __restrict__ p0j,
699 const T* __restrict__ q0j,
700 const T* __restrict__ alpha,
701 const T* __restrict__ beta,
702 const T* __restrict__ lambda,
703 const T epsi,
704 const int n,
705 const int m)
706{
707 int tj = blockIdx.x * blockDim.x + threadIdx.x;
708 if (tj < n) {
709 T xt = x[tj];
710 T xlow_j = xlow[tj];
711 T xupp_j = xupp[tj];
712 T alpha_j = alpha[tj];
713 T beta_j = beta[tj];
714
715 // Precompute denominators squared for better performance
716 T denom_low = xt - xlow_j;
717 T denom_upp = xupp_j - xt;
719 T denom_beta = beta_j - xt;
720
721 const T small_eps = T(1e-12);
726
727 T sum = T(0);
728 for (int i = 0; i < m; i++) {
729 T lambda_i = lambda[i];
730 sum += pij[i + tj * m] * lambda_i / (denom_upp * denom_upp)
731 - qij[i + tj * m] * lambda_i / (denom_low * denom_low);
732 }
733 sum += p0j[tj] / (denom_upp * denom_upp)
734 - q0j[tj] / (denom_low * denom_low)
735 - epsi / denom_alpha
736 + epsi / denom_beta;
737
738 delx[tj] = sum;
739 }
740}
741
742template <typename T>
743__global__ void GG_kernel(
745 const T* __restrict__ x,
746 const T* __restrict__ xlow,
747 const T* __restrict__ xupp,
748 const T* __restrict__ pij,
749 const T* __restrict__ qij,
750 const int n,
751 const int m) {
752
753 int tj = blockIdx.x * blockDim.x + threadIdx.x;
754 if (tj < n) {
755 T xt = x[tj];
756 T xlow_j = xlow[tj];
757 T xupp_j = xupp[tj];
758
759 // Distances from bounds
760 T diff_upper = xupp_j - xt;
761 T diff_lower = xt - xlow_j;
762
763 // Squared distances
766
767 for (int i = 0; i < m; i++) {
768 int idx = i + tj * m;
769 GG[idx] = pij[idx] / diff_upper2 - qij[idx] / diff_lower2;
770 }
771 }
772}
773
774
775template <typename T>
776__global__ void diagx_kernel(T* __restrict__ diagx, const T* __restrict__ x,
777 const T* __restrict__ xsi, const T* __restrict__ xlow,
778 const T* __restrict__ xupp, const T* __restrict__ p0j,
779 const T* __restrict__ q0j, const T* __restrict__ pij,
780 const T* __restrict__ qij, const T* alpha, const T* beta,
781 const T* eta, const T* lambda, const int n, const int m) {
782 int tj = blockIdx.x * blockDim.x + threadIdx.x;
783 if (tj < n) {
784 T sum = 0;
785 T sum1 = 0;
786 for (int i = 0; i < m; i++) {
787 sum = sum + pij[tj *m+ i] * lambda[i];
788 sum1 = sum1 + qij[tj*m + i] * lambda[i];
789 }
790 diagx[tj] = (p0j[tj] + sum) / pow(xupp[tj] - x[tj], 3) +
791 (q0j[tj] + sum1) / pow(x[tj] - xlow[tj], 3);
792 diagx[tj] = 2.0 * diagx[tj] + xsi[tj] / (x[tj] - alpha[tj]) +
793 eta[tj] / (beta[tj] - x[tj]);
794 }
795}
796
797
798template <typename T>
799__inline__ __device__ T reduce_warp(T val) {
800 volatile T v = val;
801 v += __shfl_down_sync(0xffffffff, v, 16);
802 v += __shfl_down_sync(0xffffffff, v, 8);
803 v += __shfl_down_sync(0xffffffff, v, 4);
804 v += __shfl_down_sync(0xffffffff, v, 2);
805 v += __shfl_down_sync(0xffffffff, v, 1);
806 return v;
807}
808
809
810template <typename T>
811__global__ void mmareduce_kernel(T* __restrict__ bufred, const int n) {
812
813 T sum = 0;
814 const int idx = blockIdx.x * blockDim.x + threadIdx.x;
815 const int str = blockDim.x * gridDim.x;
816 for (int i = idx; i < n; i += str)
817 {
818 sum += bufred[i];
819 }
820
821 __shared__ T shared[32];
822 unsigned int lane = threadIdx.x % warpSize;
823 unsigned int wid = threadIdx.x / warpSize;
824
826 if (lane == 0)
827 shared[wid] = sum;
829
830 sum = (threadIdx.x < blockDim.x / warpSize) ? shared[lane] : 0;
831 if (wid == 0)
833
834 if (threadIdx.x == 0)
835 bufred[blockIdx.x] = sum;
836}
837
838
839template< typename T >
840__global__ void mmasum_kernel(const T* __restrict__ a, T* __restrict__ buf_h,
841 const int n, const int m, const int k) {
842
843 const int idx = blockIdx.x * blockDim.x + threadIdx.x;
844 const int str = blockDim.x * gridDim.x;
845
846 const unsigned int lane = threadIdx.x % warpSize;
847 const unsigned int wid = threadIdx.x / warpSize;
848
849 __shared__ T shared[32];
850 T sum = 0;
851 for (int i = idx; i < n; i += str)
852 {
853 sum += a[m * i + k ];
854 }
855
857 if (lane == 0)
858 shared[wid] = sum;
860
861 sum = (threadIdx.x < blockDim.x / warpSize) ? shared[lane] : 0;
862 if (wid == 0)
864
865 if (threadIdx.x == 0)
866 buf_h[blockIdx.x] = sum;
867
868}
869template< typename T >
870__global__ void mmasumbb_kernel(const T* __restrict__ GG,
871 const T* __restrict__ delx, const T* __restrict__ diagx,
872 T* __restrict__ buf_h, const int n, const int m, const int k) {
873
874 const int idx = blockIdx.x * blockDim.x + threadIdx.x;
875 const int str = blockDim.x * gridDim.x;
876
877 const unsigned int lane = threadIdx.x % warpSize;
878 const unsigned int wid = threadIdx.x / warpSize;
879
880 __shared__ T shared[32];
881 T sum = 0;
882 for (int i = idx; i < n; i += str)
883 {
884 sum += GG[ k + i * m] * delx[i] / diagx[i];
885 }
886
888 if (lane == 0)
889 shared[wid] = sum;
891
892 sum = (threadIdx.x < blockDim.x / warpSize) ? shared[lane] : 0;
893 if (wid == 0)
895
896 if (threadIdx.x == 0)
897 buf_h[blockIdx.x] = sum;
898
899}
900
901template <typename T>
902__global__ void mmasumHess_kernel(
903 const T* __restrict__ hijx,
904 const T* __restrict__ Ljjxinv,
906 const int n,
907 const int m,
908 const int k0,
909 const int k1)
910{
911 const int idx = blockIdx.x * blockDim.x + threadIdx.x;
912 const int stride = blockDim.x * gridDim.x;
913
914 // Warp lane and warp id within the block
915 const unsigned int lane = threadIdx.x % warpSize;
916 const unsigned int wid = threadIdx.x / warpSize;
917
918 __shared__ T shared[32];
919
920 T sum = T(0);
921
922 // Grid-stride loop for global reduction
923 for (int i = idx; i < n; i += stride) {
924 // hijx is indexed as hijx[offset + row * m]
925 T val0 = hijx[k0 + i * m];
926 T val1 = hijx[k1 + i * m];
927 sum += val0 * Ljjxinv[i] * val1;
928 }
929
930 // Warp-level reduction using your reduce_warp implementation
932
933 // Write each warp's partial sum to shared memory
934 if (lane == 0) {
935 shared[wid] = sum;
936 }
938
939 // First warp reduces the warp sums in shared memory
940 sum = (threadIdx.x < blockDim.x / warpSize) ? shared[lane] : T(0);
941 if (wid == 0) {
943 }
944
945 // Write final result from first thread of block
946 if (threadIdx.x == 0) {
947 buf_h[blockIdx.x] = sum;
948 }
949}
950
951
952template< typename T >
953__global__ void mmasumAA_kernel(const T* __restrict__ GG,
954 const T* __restrict__ diagx, T* __restrict__ buf_h, const int n,
955 const int m, const int k0, const int k1) {
956
957 const int idx = blockIdx.x * blockDim.x + threadIdx.x;
958 const int str = blockDim.x * gridDim.x;
959
960 const unsigned int lane = threadIdx.x % warpSize;
961 const unsigned int wid = threadIdx.x / warpSize;
962
963 __shared__ T shared[32];
964 T sum = 0;
965 for (int i = idx; i < n; i += str)
966 {
967 sum += GG[ k0 + i * m] /diagx[i] * GG[ k1 + i * m];
968 }
969
971 if (lane == 0)
972 shared[wid] = sum;
974
975 sum = (threadIdx.x < blockDim.x / warpSize) ? shared[lane] : 0;
976 if (wid == 0)
978
979 if (threadIdx.x == 0)
980 buf_h[blockIdx.x] = sum;
981
982}
983
984
985template <typename T>
986__global__ void mma_copy_kernel(T* __restrict__ a, const T* __restrict__ b,
987 const int n, const int m) {
988 int tj = blockIdx.x * blockDim.x + threadIdx.x;
989 if(tj<n)
990 a[tj+m]=b[tj];
991}
992
993
994
995template <typename T>
996__global__ void AA_kernel(T* __restrict__ temp, const T* __restrict__ GG,
997 const T* __restrict__ diagx, const int n, const int m) {
998 int tj = blockIdx.x * blockDim.x + threadIdx.x;
999 if (tj < n) {
1000 for (int i0 = 0; i0 < m; i0++) {
1001 for (int i1 = 0; i1 < m; i1++) {
1002 temp[tj + i0 * (n + 1) + i1 * (m + 1) * (n + 1)] = GG[i0 * n + tj] *
1003 (1.0 / diagx[tj]) * GG[i1 * n + tj];
1004 }
1005 }
1006 }
1007}
1008
1009
1010template <typename T>
1011__global__ void dx_kernel(T* __restrict__ dx, const T* __restrict__ delx,
1012 const T* __restrict__ diagx, const T* __restrict__ GG,
1013 const T* __restrict__ dlambda, const int n, const int m) {
1014 int tj = blockIdx.x * blockDim.x + threadIdx.x;
1015 if (tj < n) {
1016 dx[tj] = -delx[tj]/diagx[tj];
1017 for(int i=0;i<m;i++){
1018 dx[tj] =dx[tj] - GG[tj*m+i]*dlambda[i]/diagx[tj];
1019 }
1020 }
1021}
1022
1023
1024
1025template <typename T>
1026__global__ void dxsi_kernel(T* __restrict__ dxsi, const T* __restrict__ xsi,
1027 const T* __restrict__ dx, const T* __restrict__ x,
1028 const T* __restrict__ alpha, const T epsi, const int n) {
1029 int tj = blockIdx.x * blockDim.x + threadIdx.x;
1030 if (tj < n) {
1031 dxsi[tj]= -xsi[tj] + (epsi-dx[tj]*xsi[tj])/(x[tj] - alpha[tj]);
1032 }
1033}
1034
1035
1036template <typename T>
1037__global__ void deta_kernel(T* __restrict__ deta, const T* __restrict__ eta,
1038 const T* __restrict__ dx, const T* __restrict__ x,
1039 const T* __restrict__ beta, const T epsi, const int n) {
1040 int tj = blockIdx.x * blockDim.x + threadIdx.x;
1041 if (tj < n) {
1042 deta[tj] = -eta[tj] + (epsi + dx[tj] * eta[tj]) / (beta[tj] - x[tj]);
1043 }
1044}
1045
1046
1047template <typename T>
1048__global__ void RexCalculation_kernel(
1050 const T* __restrict__ x,
1051 const T* __restrict__ xlow,
1052 const T* __restrict__ xupp,
1053 const T* __restrict__ pij,
1054 const T* __restrict__ p0j,
1055 const T* __restrict__ qij,
1056 const T* __restrict__ q0j,
1057 const T* __restrict__ lambda,
1058 const T* __restrict__ xsi,
1059 const T* __restrict__ eta,
1060 const int n,
1061 const int m)
1062{
1063 int tj = blockIdx.x * blockDim.x + threadIdx.x;
1064 if (tj < n) {
1065 T upp_diff = xupp[tj] - x[tj];
1066 T low_diff = x[tj] - xlow[tj];
1069
1070 T sum = 0.0;
1071 for (int i = 0; i < m; ++i) {
1072 sum += pij[i + tj * m] * lambda[i] / upp_diff_sq
1073 - qij[i + tj * m] * lambda[i] / low_diff_sq;
1074 }
1075
1076 rex[tj] = sum
1077 + p0j[tj] / upp_diff_sq
1078 - q0j[tj] / low_diff_sq
1079 - xsi[tj] + eta[tj];
1080 }
1081}
1082
1083
1084template <typename T>
1085__global__ void rey_calculation_kernel(T* __restrict__ rey,
1086 const T* __restrict__ c, const T* __restrict__ d, const T* __restrict__ y,
1087 const T* __restrict__ lambda, const T* __restrict__ mu, const int n) {
1088 int tj = blockIdx.x * blockDim.x + threadIdx.x;
1089 if (tj < n) {
1090 rey[tj] = c[tj] + d[tj] * y[tj] - lambda[tj] - mu[tj];
1091 }
1092}
1093
1094
1095template <typename T>
1096__global__ void norm_kernel(const T* __restrict__ a,
1097 T* __restrict__ buf_h, const int n) {
1098 const int idx = blockIdx.x * blockDim.x + threadIdx.x;
1099 const int str = blockDim.x * gridDim.x;
1100
1101 const unsigned int lane = threadIdx.x % warpSize;
1102 const unsigned int wid = threadIdx.x / warpSize;
1103
1104 __shared__ T shared[32];
1105 T sum = T(0);
1106
1107 for (int i = idx; i < n; i += str) {
1108 T val = a[i];
1109 sum += val * val; // faster than pow(val, 2)
1110 }
1111
1112 sum = reduce_warp<T>(sum); // your warp-level sum reduction function
1113 if (lane == 0)
1114 shared[wid] = sum;
1115 __syncthreads();
1116
1117 sum = (threadIdx.x < blockDim.x / warpSize) ? shared[lane] : T(0);
1118 if (wid == 0)
1120
1121 if (threadIdx.x == 0)
1122 buf_h[blockIdx.x] = sum;
1123}
1124
1125
1126template <typename T>
1127__global__ void sub2cons_kernel(T* __restrict__ a, const T* __restrict__ b,
1128 const T* __restrict__ c,
1129 const T d, const int n) {
1130 int tj = blockIdx.x * blockDim.x + threadIdx.x;
1131 if (tj < n) {
1132 a[tj] = b[tj]*c[tj]-d;
1133 }
1134}
1135
1136
1137template <typename T>
1138__global__ void dely_kernel(T* __restrict__ dely, const T* __restrict__ c,
1139 const T* __restrict__ d, const T* __restrict__ y,
1140 const T* __restrict__ lambda, const T epsi, const int n) {
1141 int tj = blockIdx.x * blockDim.x + threadIdx.x;
1142 if (tj < n) {
1143 dely[tj] = c[tj] + d[tj]*y[tj] - lambda[tj] - epsi/y[tj];
1144 }
1145}
1146
1147
1148
1149template< typename T >
1150__global__ void maxval2_kernel(const T* __restrict__ a, const T* __restrict__ b,
1151 T* __restrict__ temp, const T cons, const int n) {
1152
1153 const int idx = blockIdx.x * blockDim.x + threadIdx.x;
1154 const int str = blockDim.x * gridDim.x;
1155
1156 const unsigned int lane = threadIdx.x % warpSize;
1157 const unsigned int wid = threadIdx.x / warpSize;
1158
1159 __shared__ T shared[32];
1160 T maxval = cons * a[0] / b[0];
1161 for (int i = idx; i < n; i += str)
1162 {
1163 maxval = max(maxval, cons * a[i] / b[i]);
1164 }
1165
1167 if (lane == 0)
1168 shared[wid] = maxval;
1169 __syncthreads();
1170
1171 maxval = (threadIdx.x < blockDim.x / warpSize) ? shared[lane] : 0.0;
1172 if (wid == 0)
1174
1175 if (threadIdx.x == 0)
1176 temp[blockIdx.x] = maxval;
1177
1178}
1179
1180
1181template< typename T >
1182__global__ void maxval3_kernel(const T* __restrict__ a, const T* __restrict__ b,
1183 const T* __restrict__ c, T* __restrict__ temp, const T cons, const int n) {
1184
1185 const int idx = blockIdx.x * blockDim.x + threadIdx.x;
1186 const int str = blockDim.x * gridDim.x;
1187
1188 const unsigned int lane = threadIdx.x % warpSize;
1189 const unsigned int wid = threadIdx.x / warpSize;
1190
1191 __shared__ T shared[32];
1192 T maxval = cons * a[0] / b[0];
1193 for (int i = idx; i < n; i += str)
1194 {
1195 maxval = max(maxval, cons * a[i] / (b[i] - c[i]));
1196 }
1197
1199 if (lane == 0)
1200 shared[wid] = maxval;
1201 __syncthreads();
1202
1203 maxval = (threadIdx.x < blockDim.x / warpSize) ? shared[lane] : 0;
1204 if (wid == 0)
1206
1207 if (threadIdx.x == 0)
1208 temp[blockIdx.x] = maxval;
1209
1210}
1211
1212
1213template <typename T>
1214__global__ void kkt_rex_kernel(T* __restrict__ rex, const T* __restrict__ df0dx,
1215 const T* __restrict__ dfdx, const T* __restrict__ xsi,
1216 const T* __restrict__ eta, const T* __restrict__ lambda, const int n,
1217 const int m) {
1218 int tj = blockIdx.x * blockDim.x + threadIdx.x;
1219 if (tj < n) {
1220 rex[tj] = 0.0;
1221 for (int i = 0; i < m; i++) {
1222 rex[tj] = rex[tj] + dfdx[i + tj*m] * lambda[i];
1223 }
1224 rex[tj] += df0dx[tj] - xsi[tj] + eta[tj];
1225 }
1226}
1227
1228// KKT residual of the original problem for the dip subsolver, as
1229// mma_dip_KKT_cpu (MMA::KKTresidual in topopt_in_petsc), for each local
1230// design variable j: the stationarity in res[j] and the complementarity
1231// of the lower and upper bounds in res[n + j] and res[2n + j]. The bound
1232// multipliers are estimated where x_j is within 1e-5 of the true bounds.
1233template <typename T>
1234__global__ void mma_dip_kkt_kernel(T* __restrict__ res,
1235 const T* __restrict__ x, const T* __restrict__ df0dx,
1236 const T* __restrict__ dfdx, const T* __restrict__ xmin,
1237 const T* __restrict__ xmax, const T* __restrict__ lambda,
1238 const int n, const int m) {
1239 const int tj = blockIdx.x * blockDim.x + threadIdx.x;
1240 if (tj >= n) return;
1241
1242 const T xt = x[tj];
1243 const T xmin_j = xmin[tj];
1244 const T xmax_j = xmax[tj];
1245
1246 // Gradient of the Lagrangian w.r.t. x_j
1247 T rex = df0dx[tj];
1248 for (int i = 0; i < m; i++) {
1249 rex = rex + lambda[i] * dfdx[i + tj * m];
1250 }
1251
1252 // Estimate the bound multipliers where x_j is at a bound
1253 T xsi = T(0.0);
1254 if (xt < xmin_j + T(1.0e-5) && rex > T(0.0)) xsi = rex;
1255 T eta = T(0.0);
1256 if (xt > xmax_j - T(1.0e-5) && rex < T(0.0)) eta = -rex;
1257 rex = rex + (-xsi + eta);
1258
1259 res[tj] = rex;
1260 res[n + tj] = xsi * (xt - xmin_j);
1261 res[2 * n + tj] = eta * (xmax_j - xt);
1262}
1263
1264
1265template <typename T>
1266__global__ void maxcons_kernel(T* __restrict__ a, const T b,
1267 const T c, const T* __restrict__ d, const int n) {
1268 int tj = blockIdx.x * blockDim.x + threadIdx.x;
1269 if (tj < n) {
1270 a[tj] = max(b, c * d[tj]);
1271 }
1272}
1273
1276 template< typename T >
1277 __global__ void glsum_kernel(const T * a, T * buf_h, const int n) {
1278 const int idx = blockIdx.x * blockDim.x + threadIdx.x;
1279 const int str = blockDim.x * gridDim.x;
1280
1281 const unsigned int lane = threadIdx.x % warpSize;
1282 const unsigned int wid = threadIdx.x / warpSize;
1283
1284 __shared__ T shared[32];
1285 T sum = 0;
1286 for (int i = idx; i<n ; i += str)
1287 {
1288 sum += a[i];
1289 }
1290
1292 if (lane == 0)
1293 shared[wid] = sum;
1294 __syncthreads();
1295
1296 sum = (threadIdx.x < blockDim.x / warpSize) ? shared[lane] : 0;
1297 if (wid == 0)
1299
1300 if (threadIdx.x == 0)
1301 buf_h[blockIdx.x] = sum;
1302
1303 }
1304 template< typename T >
1305__global__ void glsc2_kernel(const T * a,
1306 const T * b,
1307 T * buf_h,
1308 const int n) {
1309
1310 const int idx = blockIdx.x * blockDim.x + threadIdx.x;
1311 const int str = blockDim.x * gridDim.x;
1312
1313 const unsigned int lane = threadIdx.x % warpSize;
1314 const unsigned int wid = threadIdx.x / warpSize;
1315
1316 __shared__ T shared[32];
1317 T sum = 0.0;
1318 for (int i = idx; i < n; i+= str) {
1319 sum += a[i] * b[i];
1320 }
1321
1323 if (lane == 0)
1324 shared[wid] = sum;
1325 __syncthreads();
1326
1327 sum = (threadIdx.x < blockDim.x / warpSize) ? shared[lane] : 0;
1328 if (wid == 0)
1330
1331 if (threadIdx.x == 0)
1332 buf_h[blockIdx.x] = sum;
1333
1334 }
1335
1336
1337
1338
1339
1340template <typename T>
1341__global__ void add2inv2_kernel(T* __restrict__ a, const T* __restrict__ b,
1342 const T c, const int n) {
1343 int tj = blockIdx.x * blockDim.x + threadIdx.x;
1344 if (tj < n) {
1345 a[tj] = a[tj]+c/b[tj];
1346 }
1347}
1348
1349template <typename T>
1350__global__ void max2_kernel(T* __restrict__ a, const T b,
1351 const T* __restrict__ c, const T d, const int n) {
1352 int tj = blockIdx.x * blockDim.x + threadIdx.x;
1353 if (tj < n) {
1354 a[tj]=max(b, d*c[tj]);
1355 }
1356}
1357
1358template <typename T>
1359__global__ void updatebb_kernel(T* __restrict__ bb,
1360 const T* __restrict__ dellambda, const T* __restrict__ dely,
1361 const T* __restrict__ d, const T* __restrict__ mu,
1362 const T* __restrict__ y, const T delz, const int m) {
1363 int tj = blockIdx.x * blockDim.x + threadIdx.x;
1364 if(tj<m)
1365 bb[tj]=dellambda[tj] + dely[tj]/(d[tj] + mu[tj]/y[tj]) - bb[tj];
1366 else if(tj<m+1)
1367 bb[tj]=delz;
1368}
1369
1370
1371
1372template <typename T>
1373__global__ void updateAA_kernel(T* __restrict__ AA,
1374 const T* __restrict__ globaltmp_mm, const T* __restrict__ s,
1375 const T* __restrict__ lambda, const T* __restrict__ d,
1376 const T* __restrict__ mu, const T* __restrict__ y, const T* __restrict__ a,
1377 const T zeta, const T z, const int m) {
1378 int tj = blockIdx.x * blockDim.x + threadIdx.x;
1379 if(tj<m)
1380 {
1381 AA[tj+tj*(m+1)]=globaltmp_mm[tj+tj*m] + (s[tj] / lambda[tj] +
1382 1.0/ (d[tj] + mu[tj] / y[tj]));
1383 AA[tj+m*(m+1)]=a[tj];
1384 AA[m+tj*(m+1)]=a[tj];
1385 }
1386 else if(tj<m+1)
1387 AA[tj+tj*(m+1)]= -zeta/z;
1388}
1389
1390template <typename T>
1391__global__ void dy_kernel(T* __restrict__ dy, const T* __restrict__ dely,
1392 const T* __restrict__ dlambda, const T* __restrict__ d,
1393 const T* __restrict__ mu, const T* __restrict__ y, const int n) {
1394 int tj = blockIdx.x * blockDim.x + threadIdx.x;
1395 if(tj<n)
1396 dy[tj] = (-dely[tj]+dlambda[tj])/(d[tj] + mu[tj]/y[tj]);
1397}
1398
1399#endif
1400
__global__ void Borrvall_Petersson_mapping_apply_kernel(const T f_min, const T f_max, const T q, T *__restrict__ X_out_d, T *__restrict__ X_in_d, const int n)