Neko-TOP
A portable framework for high-order spectral element flow toplogy optimization.
Loading...
Searching...
No Matches
mma_cpu.f90
1
33
34submodule(mma) mma_cpu
35 use lapack_interfaces, only: dgesv
36 use mpi_f08, only: mpi_in_place, mpi_max, mpi_min
37 use comm, only: neko_comm, pe_rank, mpi_real_precision
38 use math, only: neko_eps
39 use profiler, only: profiler_start_region, profiler_end_region
40
41 implicit none
42
43contains
44
45 module subroutine mma_update_cpu(this, iter, x, df0dx, fval, dfdx)
46 ! ----------------------------------------------------- !
47 ! Update the design variable x by solving the convex !
48 ! approximation of the problem. !
49 ! !
50 ! This subroutine is called in each iteration of the !
51 ! optimization loop !
52 ! ----------------------------------------------------- !
53 class(mma_t), intent(inout) :: this
54 integer, intent(in) :: iter
55 real(kind=rp), dimension(this%n), intent(inout) :: x
56 real(kind=rp), dimension(this%n), intent(in) :: df0dx
57 real(kind=rp), dimension(this%m), intent(in) :: fval
58 real(kind=rp), dimension(this%m, this%n), intent(in) :: dfdx
59
60 if (.not. this%is_initialized) then
61 call neko_error("The MMA object is not initialized.")
62 end if
63
64 call profiler_start_region("MMA update")
65
66 ! generate a convex approximation of the problem
67 call profiler_start_region("MMA gensub")
68 call mma_gensub_cpu(this, iter, x, df0dx, fval, dfdx)
69 call profiler_end_region("MMA gensub")
70
71 !solve the approximation problem using interior point method
72 call profiler_start_region("MMA subsolve")
73 if (this%subsolver .eq. "dip") then
74 call mma_subsolve_dip_cpu(this, x)
75 else if (this%subsolver .eq. "pdip") then
76 call mma_subsolve_pdip_cpu(this, x)
77 else
78 call neko_error("Unrecognized subsolver for MMA in mma_cpu.")
79 end if
80 call profiler_end_region("MMA subsolve")
81
82 call profiler_end_region("MMA update")
83
84 this%is_updated = .true.
85 end subroutine mma_update_cpu
86
88 module subroutine mma_kkt_cpu(this, x, df0dx, fval, dfdx)
89 ! ----------------------------------------------------- !
90 ! Compute the KKT condition right hand side for a given !
91 ! designx x and set the max and norm values of the !
92 ! residue of KKT system to this%residumax and !
93 ! this%residunorm. !
94 ! !
95 ! The left hand sides of the KKT conditions are computed!
96 ! for the following nonlinear programming problem: !
97 ! Minimize f_0(x) + a_0*z + !
98 ! sum(c_i*y_i + 0.5*d_i*(y_i)^2)!
99 ! subject to f_i(x) - a_i*z - y_i <= 0, i = 1,...,m !
100 ! xmin_j <= x_j <= xmax_j, j = 1,...,n !
101 ! z >= 0, y_i >= 0, i = 1,...,m !
102 ! !
103 ! !
104 ! Note that before calling this function, the function !
105 ! values (f0val, fval, dfdx, ...) should be updated !
106 ! using the new x values. !
107 ! ----------------------------------------------------- !
108 class(mma_t), intent(inout) :: this
109 real(kind=rp), dimension(this%n), intent(in) :: x
110 real(kind=rp), dimension(this%n), intent(in) :: df0dx
111 real(kind=rp), dimension(this%m), intent(in) :: fval
112 real(kind=rp), dimension(this%m, this%n), intent(in) :: dfdx
113
114 call profiler_start_region("MMA KKT computation")
115
116 if (this%subsolver .eq. "dip") then
117 call mma_dip_kkt_cpu(this, x, df0dx, fval, dfdx)
118 else if (this%subsolver .eq. "pdip") then
119 call mma_pdip_kkt_cpu(this, x, df0dx, fval, dfdx)
120 else
121 call neko_error("Unrecognized subsolver for MMA in mma_cpu.")
122 end if
123
124 call profiler_end_region("MMA KKT computation")
125 end subroutine mma_kkt_cpu
126
128 ! point method (pdip) subsolve of MMA algorithm.
129 module subroutine mma_pdip_kkt_cpu(this, x, df0dx, fval, dfdx)
130 ! ----------------------------------------------------- !
131 ! Compute the KKT condition right hand side for a given !
132 ! designx x and set the max and norm values of the !
133 ! residue of KKT system to this%residumax and !
134 ! this%residunorm. !
135 ! !
136 ! The left hand sides of the KKT conditions are computed!
137 ! for the following nonlinear programming problem: !
138 ! Minimize f_0(x) + a_0*z + !
139 ! sum( c_i*y_i + 0.5*d_i*(y_i)^2 )!
140 ! subject to f_i(x) - a_i*z - y_i <= 0, i = 1,...,m !
141 ! xmin_j <= x_j <= xmax_j, j = 1,...,n !
142 ! z >= 0, y_i >= 0, i = 1,...,m !
143 ! !
144 ! !
145 ! Note that before calling this function, the function !
146 ! values (f0val, fval, dfdx, ...) should be updated !
147 ! using the new x values. !
148 ! ----------------------------------------------------- !
149 class(mma_t), intent(inout) :: this
150 real(kind=rp), dimension(this%n), intent(in) :: x
151 real(kind=rp), dimension(this%n), intent(in) :: df0dx
152 real(kind=rp), dimension(this%m), intent(in) :: fval
153 real(kind=rp), dimension(this%m, this%n), intent(in) :: dfdx
154
155 real(kind=rp) :: rez, rezeta
156 real(kind=rp), dimension(this%m) :: rey, relambda, remu, res
157 real(kind=rp), dimension(this%n) :: rex, rexsi, reeta
158 real(kind=rp), dimension(3*this%n+4*this%m+2) :: residual
159
160 real(kind=rp), dimension(4*this%m+2) :: residual_small
161 integer :: ierr
162 real(kind=rp) :: re_sq_norm
163
164 rex = df0dx + matmul(transpose(dfdx), this%lambda%x) &
165 - this%xsi%x + this%eta%x
166 rey = this%c%x + this%d%x*this%y%x - this%lambda%x - this%mu%x
167 rez = this%a0 - this%zeta - dot_product(this%lambda%x, this%a%x)
168
169 relambda = fval - this%a%x * this%z - this%y%x + this%s%x
170 rexsi = this%xsi%x * (x - this%xmin%x)
171 reeta = this%eta%x * (this%xmax%x - x)
172 remu = this%mu%x * this%y%x
173 rezeta = this%zeta * this%z
174 res = this%lambda%x * this%s%x
175
176 residual = [rex, rey, rez, relambda, rexsi, reeta, remu, rezeta, res]
177 residual_small = [rey, rez, relambda, remu, rezeta, res]
178
179 this%residumax = maxval(abs(residual))
180 re_sq_norm = norm2(rex)**2 + norm2(rexsi)**2 + norm2(reeta)**2
181
182 call mpi_allreduce(mpi_in_place, this%residumax, 1, &
183 mpi_real_precision, mpi_max, neko_comm, ierr)
184
185 call mpi_allreduce(mpi_in_place, re_sq_norm, 1, &
186 mpi_real_precision, mpi_sum, neko_comm, ierr)
187
188 this%residunorm = sqrt(norm2(residual_small)**2 + re_sq_norm)
189 end subroutine mma_pdip_kkt_cpu
190
192 ! point method (dip) subsolve of MMA algorithm.
193 module subroutine mma_dip_kkt_cpu(this, x, df0dx, fval, dfdx)
194 ! ----------------------------------------------------- !
195 ! Compute the KKT residual of the original problem for !
196 ! the design x and set its max and 2-norm to !
197 ! this%residumax and this%residunorm. !
198 ! !
199 ! This follows MMA::KKTresidual in the Cpp code by !
200 ! Niels (topopt_in_petsc). The multipliers lambda and !
201 ! the artificial variables y and z are those of the !
202 ! last subproblem. The residual consists of: !
203 ! - stationarity w.r.t. x_j: !
204 ! df0dx_j + sum_i lambda_i*dfdx_ij - xsi_j + eta_j!
205 ! where the bound multipliers xsi_j, eta_j are !
206 ! estimated for x_j within 1e-5 of xmin_j, xmax_j, !
207 ! - complementarity of the bounds: !
208 ! xsi_j*(x_j - xmin_j) and eta_j*(xmax_j - x_j), !
209 ! - complementarity of the constraints: !
210 ! sum_i lambda_i*(a_i*z + y_i - f_i(x)). !
211 ! The true bounds xmin, xmax are used, not the move !
212 ! limited ones. !
213 ! !
214 ! Note that before calling this function, the function !
215 ! values (f0val, fval, dfdx, ...) should be updated !
216 ! using the new x values. !
217 ! ----------------------------------------------------- !
218 class(mma_t), intent(inout) :: this
219 real(kind=rp), dimension(this%n), intent(in) :: x
220 real(kind=rp), dimension(this%n), intent(in) :: df0dx
221 real(kind=rp), dimension(this%m), intent(in) :: fval
222 real(kind=rp), dimension(this%m, this%n), intent(in) :: dfdx
223
224 real(kind=rp) :: rex, resi, xsi, eta, relambda
225 real(kind=rp) :: residual_max, residual_sq_norm
226 integer :: i, j, ierr
227
228 residual_max = 0.0_rp
229 residual_sq_norm = 0.0_rp
230
231 associate(xmin => this%xmin%x, xmax => this%xmax%x, &
232 lambda => this%lambda%x)
233
234 do j = 1, this%n
235 ! Gradient of the Lagrangian w.r.t. x_j
236 rex = df0dx(j)
237 do i = 1, this%m
238 rex = rex + lambda(i) * dfdx(i, j)
239 end do
240
241 ! Estimate the bound multipliers where x_j is at a bound
242 xsi = 0.0_rp
243 if (x(j) .lt. xmin(j) + 1.0e-5_rp .and. rex .gt. 0.0_rp) xsi = rex
244 eta = 0.0_rp
245 if (x(j) .gt. xmax(j) - 1.0e-5_rp .and. rex .lt. 0.0_rp) eta = -rex
246 rex = rex + (-xsi + eta)
247
248 residual_sq_norm = residual_sq_norm + rex**2
249 residual_max = max(abs(rex), residual_max)
250 resi = xsi * (x(j) - xmin(j))
251 residual_sq_norm = residual_sq_norm + resi**2
252 residual_max = max(abs(resi), residual_max)
253 resi = eta * (xmax(j) - x(j))
254 residual_sq_norm = residual_sq_norm + resi**2
255 residual_max = max(abs(resi), residual_max)
256 end do
257
258 end associate
259
260 call mpi_allreduce(mpi_in_place, residual_sq_norm, 1, &
261 mpi_real_precision, mpi_sum, neko_comm, ierr)
262 call mpi_allreduce(mpi_in_place, residual_max, 1, &
263 mpi_real_precision, mpi_max, neko_comm, ierr)
264
265 ! Complementarity of the constraints, a global quantity
266 relambda = 0.0_rp
267 do i = 1, this%m
268 relambda = relambda + this%lambda%x(i) * &
269 (this%a%x(i) * this%z + this%y%x(i) - fval(i))
270 end do
271
272 this%residumax = max(abs(relambda), residual_max)
273 this%residunorm = sqrt(residual_sq_norm + relambda**2)
274
275 end subroutine mma_dip_kkt_cpu
276
277 !============================================================================!
278 ! private internal subroutines
279
281 subroutine mma_gensub_cpu(this, iter, x, df0dx, fval, dfdx)
282 ! ----------------------------------------------------- !
283 ! Generate the approximation sub problem by computing !
284 ! the lower and upper asymtotes and the other necessary !
285 ! parameters (alpha, beta, p0j, q0j, pij, qij, ...). !
286 ! ----------------------------------------------------- !
287 class(mma_t), intent(inout) :: this
288 real(kind=rp), dimension(this%n), intent(in) :: x
289 real(kind=rp), dimension(this%n), intent(in) :: df0dx
290 real(kind=rp), dimension(this%m), intent(in) :: fval
291 real(kind=rp), dimension(this%m, this%n), intent(in) :: dfdx
292 integer, intent(in) :: iter
293 integer :: i, j, ierr
294 real(kind=rp), dimension(this%n) :: xmin_eff, xmax_eff
295 real(kind=rp), dimension(this%n) :: x_diff
296 real(kind=rp) :: asy_factor
297
298 xmin_eff = this%xmin%x
299 xmax_eff = this%xmax%x
300 if (this%move_limit .gt. 0.0_rp) then
301 xmin_eff = max(xmin_eff, x - this%move_limit)
302 xmax_eff = min(xmax_eff, x + this%move_limit)
303 end if
304
305 x_diff = max(xmax_eff - xmin_eff, 1.0e-5_rp)
306
307 ! ------------------------------------------------------------------------ !
308 ! Setup the current asymptotes
309 associate(low => this%low%x, upp => this%upp%x, &
310 x_1 => this%xold1%x, x_2 => this%xold2%x)
311
312 if (iter .lt. 3) then
313 ! Initialize the lower and upper asymptotes
314 low = x - this%asyinit * x_diff
315 upp = x + this%asyinit * x_diff
316 else
317 do j = 1, this%n
318 if ((x(j) - x_1(j)) * (x_1(j) - x_2(j)) .lt. 0.0_rp) then
319 asy_factor = this%asydecr
320 else if ((x(j) - x_1(j)) * (x_1(j) - x_2(j)) .gt. 0.0_rp) then
321 asy_factor = this%asyincr
322 else
323 asy_factor = 1.0_rp
324 end if
325
326 low(j) = x(j) - asy_factor * (x_1(j) - low(j))
327 upp(j) = x(j) + asy_factor * (upp(j) - x_1(j))
328 end do
329
330
331 ! Setting a minimum and maximum for the low and upp
332 ! asymptotes (eq3.9)
333 low = max(low, x - 10.0_rp * x_diff)
334 low = min(low, x - 0.01_rp * x_diff)
335
336 upp = min(upp, x + 10.0_rp * x_diff)
337 upp = max(upp, x + 0.01_rp * x_diff)
338 end if
339
340 end associate
341
342
343 ! ------------------------------------------------------------------------ !
344 ! Set the the bounds and coefficients for the approximation
345 ! the move bounds (alpha and beta) are slightly more restrictive
346 ! than low and upp. This is done based on eq(3.6)--eq(3.10).
347 ! also check
348 ! https://comsolyar.com/wp-content/uploads/2020/03/gcmma.pdf
349 ! eq (2.8) and (2.9)
350
351 associate(alpha => this%alpha%x, beta => this%beta%x, &
352 xmin => xmin_eff, xmax => xmax_eff, &
353 low => this%low%x, upp => this%upp%x, x => x)
354
355 alpha = max(xmin, low + 0.1_rp*(x - low))
356 beta = min(xmax, upp - 0.1_rp*(upp - x))
357 end associate
358
359 ! ------------------------------------------------------------------------ !
360 ! Calculate p0j, q0j, pij, qij
361 ! where j = 1,2,...,n and i = 1,2,...,m (eq(2.3)-eq(2.5))
362
363 associate(p0j => this%p0j%x, q0j => this%q0j%x, &
364 pij => this%pij%x, qij => this%qij%x, &
365 low => this%low%x, upp => this%upp%x)
366
367 if (this%subsolver .eq. "dip") then
368 ! Following MMA::GenSub in the Cpp code by Niels (topopt_in_petsc,
369 ! constraintModification = false): only the objective is
370 ! regularised, by 0.5e-6/(upp - low).
371 p0j = (upp - x)**2 * (max(df0dx, 0.0_rp) + 0.001_rp*abs(df0dx) &
372 + 0.5_rp*1.0e-6_rp/(upp - low))
373 q0j = (x - low)**2 * (max(-df0dx, 0.0_rp) + 0.001_rp*abs(df0dx) &
374 + 0.5_rp*1.0e-6_rp/(upp - low))
375 do j = 1, this%n
376 do i = 1, this%m
377 pij(i, j) = (upp(j) - x(j))**2 * max(dfdx(i, j), 0.0_rp)
378 qij(i, j) = (x(j) - low(j))**2 * max(-dfdx(i, j), 0.0_rp)
379 end do
380 end do
381 else
382 ! Following mmasub by Svanberg: the objective and all constraints
383 ! are regularised by 1e-5/max(x_diff, 1e-5).
384 p0j = ( &
385 1.001_rp * max(df0dx, 0.0_rp) &
386 + 0.001_rp * max(-df0dx, 0.0_rp) &
387 + 0.00001_rp / max(x_diff, 0.00001_rp) &
388 ) * (upp - x)**2
389
390 q0j = ( &
391 0.001_rp * max(df0dx, 0.0_rp) &
392 + 1.001_rp * max(-df0dx, 0.0_rp) &
393 + 0.00001_rp / max(x_diff, 0.00001_rp)&
394 ) * (x - low)**2
395
396 do j = 1, this%n
397 do i = 1, this%m
398 pij(i, j) = ( &
399 1.001_rp * max(dfdx(i, j), 0.0_rp) &
400 + 0.001_rp * max(-dfdx(i, j), 0.0_rp) &
401 + 0.00001_rp / max(x_diff(j), 0.00001_rp) &
402 ) * (upp(j) - x(j))**2
403
404 qij(i, j) = ( &
405 0.001_rp * max(dfdx(i, j), 0.0_rp) &
406 + 1.001_rp * max(-dfdx(i, j), 0.0_rp) &
407 + 0.00001_rp / max(x_diff(j), 0.00001_rp) &
408 ) * (x(j) - low(j))**2
409 end do
410 end do
411 end if
412
413 end associate
414
415 ! ------------------------------------------------------------------------ !
416 ! Computing bi as defined in page 5
417
418 associate(bi => this%bi%x, &
419 pij => this%pij%x, qij => this%qij%x, &
420 low => this%low%x, upp => this%upp%x)
421
422 bi = 0.0_rp
423 do i = 1, this%m
424 do j = 1, this%n
425 bi(i) = bi(i) &
426 + pij(i, j) / (upp(j) - x(j)) &
427 + qij(i, j) / (x(j) - low(j))
428 end do
429 end do
430
431 call mpi_allreduce(mpi_in_place, bi, this%m, &
432 mpi_real_precision, mpi_sum, neko_comm, ierr)
433 bi = bi - fval
434
435 end associate
436 end subroutine mma_gensub_cpu
437
440 subroutine mma_subsolve_pdip_cpu(this, designx)
441 ! ------------------------------------------------------- !
442 ! Dual-primal interior point method using Newton's step !
443 ! to solve MMA sub problem. !
444 ! A Backtracking Line Search approach is used to compute !
445 ! the step size; starting with the full Newton's step !
446 ! (delta = 1) and dividing by 2 until we have a step size !
447 ! that leads to a feasible point while ensuring a !
448 ! decrease in the residue. !
449 ! ------------------------------------------------------- !
450
451 class(mma_t), intent(inout) :: this
452 real(kind=rp), dimension(this%n), intent(inout) :: designx
453 ! Note that there is a local dummy "x" in this subroutine, thus, we call
454 ! the current design "designx" instead of just "x"
455 integer :: i, j, k, iter, itto, ierr
456 real(kind=rp) :: epsi, residual_max, residual_norm, &
457 z, zeta, rez, rezeta, &
458 delz, dz, dzeta, &
459 steg, zold, zetaold, new_residual
460 real(kind=rp), dimension(this%m) :: y, lambda, s, mu, &
461 rey, relambda, remu, res, &
462 dely, dellambda, &
463 dy, dlambda, ds, dmu, &
464 yold, lambdaold, sold, muold
465 real(kind=rp), dimension(this%n) :: x, xsi, eta, &
466 rex, rexsi, reeta, &
467 delx, diagx, dx, dxsi, deta, &
468 xold, xsiold, etaold
469 real(kind=rp), dimension(4*this%m + 2) :: residual_small
470 real(kind=rp), dimension(3*this%n + 4*this%m + 2) :: residual
471 real(kind=rp), dimension(2*this%n + 4*this%m + 2) :: xx, dxx
472
473 real(kind=rp), dimension(this%m, this%n) :: gg
474 real(kind=rp), dimension(this%m+1) :: bb
475 real(kind=rp), dimension(this%m+1, this%m+1) :: aa
476 real(kind=rp), dimension(this%m * this%m) :: aa_buffer
477
478 ! using DGESV in lapack to solve
479 ! the linear system which needs the following parameters
480 integer :: info
481 integer, dimension(this%m+1) :: ipiv
482
483 ! Parameters for global communication
484 real(kind=rp) :: re_sq_norm
485 real(kind=rp) :: minimal_epsilon
486
487 ! ------------------------------------------------------------------------ !
488 ! initial value for the parameters in the subsolve based on
489 ! page 15 of "https://people.kth.se/~krille/mmagcmma.pdf"
490
491 epsi = 1.0_rp !100
492 x = 0.5_rp * (this%alpha%x + this%beta%x)
493 y = 1.0_rp
494 z = 1.0_rp
495 zeta = 1.0_rp
496 lambda = 1.0_rp
497 s = 1.0_rp
498 xsi = max(1.0_rp, 1.0_rp / (x - this%alpha%x))
499 eta = max(1.0_rp, 1.0_rp / (this%beta%x - x))
500 mu = max(1.0_rp, 0.5_rp * this%c%x)
501
502 ! ------------------------------------------------------------------------ !
503 ! Computing the minimal epsilon and choose the most conservative one
504
505 minimal_epsilon = max(0.9_rp * this%epsimin, 1.0e-12_rp)
506 call mpi_allreduce(mpi_in_place, minimal_epsilon, 1, &
507 mpi_real_precision, mpi_min, neko_comm, ierr)
508
509 ! ------------------------------------------------------------------------ !
510 ! The main loop of the dual-primal interior point method.
511
512 do while (epsi .gt. minimal_epsilon)
513
514 ! --------------------------------------------------------------------- !
515 ! Calculating residuals based on
516 ! "https://people.kth.se/~krille/mmagcmma.pdf" for the variables
517 ! x, y, z, lambda residuals based on eq(5.9a)-(5.9d), respectively.
518
519 associate(p0j => this%p0j%x, q0j => this%q0j%x, &
520 pij => this%pij%x, qij => this%qij%x, &
521 low => this%low%x, upp => this%upp%x, &
522 alpha => this%alpha%x, beta => this%beta%x, &
523 c => this%c%x, d => this%d%x, &
524 a0 => this%a0, a => this%a%x)
525
526 rex = (p0j + matmul(transpose(pij), lambda)) / (upp - x)**2 &
527 - (q0j + matmul(transpose(qij), lambda)) / (x - low)**2 &
528 - xsi + eta
529
530 rey = c + d * y - lambda - mu
531 rez = a0 - zeta - dot_product(lambda, a)
532
533 relambda = 0.0_rp
534 do i = 1, this%m
535 do j = 1, this%n
536 ! Accumulate sums for relambda (the term gi(x))
537 relambda(i) = relambda(i) &
538 + pij(i, j) / (upp(j) - x(j)) &
539 + qij(i, j) / (x(j) - low(j))
540 end do
541 end do
542
543 end associate
544
545 ! --------------------------------------------------------------------- !
546 ! Computing the norm of the residuals
547
548 ! Complete the computations of lambda residuals
549 call mpi_allreduce(mpi_in_place, relambda, this%m, &
550 mpi_real_precision, mpi_sum, neko_comm, ierr)
551 relambda = relambda - this%a%x*z - y + s - this%bi%x
552
553 rexsi = xsi * (x - this%alpha%x) - epsi
554 reeta = eta * (this%beta%x - x) - epsi
555 remu = mu * y - epsi
556 rezeta = zeta * z - epsi
557 res = lambda * s - epsi
558
559 ! Setup vectors of residuals and their norms
560 residual = [rex, rey, rez, relambda, rexsi, reeta, remu, rezeta, res]
561 residual_small = [rey, rez, relambda, remu, rezeta, res]
562
563 residual_max = maxval(abs(residual))
564 re_sq_norm = norm2(rex)**2 + norm2(rexsi)**2 + norm2(reeta)**2
565
566 call mpi_allreduce(mpi_in_place, residual_max, 1, &
567 mpi_real_precision, mpi_max, neko_comm, ierr)
568
569 call mpi_allreduce(mpi_in_place, re_sq_norm, &
570 1, mpi_real_precision, mpi_sum, neko_comm, ierr)
571
572 residual_norm = sqrt(norm2(residual_small)**2 + re_sq_norm)
573
574 ! --------------------------------------------------------------------- !
575 ! Internal loop
576
577 do iter = 1, this%max_iter
578
579 !Check the condition
580 if (residual_max .lt. epsi) exit
581
582 delx = 0.0_rp
583 do j = 1, this%n
584 do i = 1, this%m
585 delx(j) = delx(j) &
586 + this%pij%x(i,j) * lambda(i) / (this%upp%x(j) - x(j))**2 &
587 - this%qij%x(i,j) * lambda(i) / (x(j) - this%low%x(j))**2
588 end do
589 end do
590
591 delx = delx &
592 + this%p0j%x / (this%upp%x - x)**2 &
593 - this%q0j%x / (x - this%low%x)**2 &
594 - epsi / (x - this%alpha%x) &
595 + epsi / (this%beta%x - x)
596
597 dely = this%c%x + this%d%x * y - lambda - epsi / y
598 delz = this%a0 - dot_product(lambda, this%a%x) - epsi / z
599
600 ! Accumulate sums for dellambda (the term gi(x))
601 dellambda = 0.0_rp
602 do i = 1, this%m
603 do j = 1, this%n
604 dellambda(i) = dellambda(i) &
605 + this%pij%x(i, j) / (this%upp%x(j) - x(j)) &
606 + this%qij%x(i, j) / (x(j) - this%low%x(j))
607 end do
608 end do
609
610 call mpi_allreduce(mpi_in_place, dellambda, this%m, &
611 mpi_real_precision, mpi_sum, neko_comm, ierr)
612
613 dellambda = dellambda - this%a%x*z - y - this%bi%x + epsi / lambda
614
615 do i = 1, this%m
616 gg(i,:) = this%pij%x(i,:) / (this%upp%x - x)**2 &
617 - this%qij%x(i,:) / (x - this%low%x)**2
618 end do
619
620 diagx = &
621 (this%p0j%x + matmul(transpose(this%pij%x), lambda)) &
622 / (this%upp%x - x)**3 &
623 + (this%q0j%x + matmul(transpose(this%qij%x), lambda)) &
624 / (x - this%low%x)**3
625
626 diagx = 2.0_rp * diagx &
627 + xsi / (x - this%alpha%x) &
628 + eta / (this%beta%x - x)
629
630
631 !Here we only consider the case m<n in the matlab code
632 !assembling the right hand side matrix based on eq(5.20)
633 ! bb = [dellambda + dely/(this%d%x + &
634 ! (mu/y)) - matmul(GG,delx/diagx), delz ]
635
636 !--------------------------------------------------------------------!
637 ! for MPI computation of bb
638
639 bb = 0.0_rp
640 do i = 1, this%m
641 do j = 1, this%n
642 bb(i) = bb(i) + gg(i, j) * (delx(j) / diagx(j))
643 end do
644 end do
645
646 call mpi_allreduce(mpi_in_place, bb, this%m, &
647 mpi_real_precision, mpi_sum, neko_comm, ierr)
648
649 bb(1:this%m) = dellambda + dely / (this%d%x + mu / y) - bb(1:this%m)
650 bb(this%m + 1) = delz
651
652 !--------------------------------------------------------------------!
653 ! assembling the coefficients matrix AA based on eq(5.20)
654 ! AA(1:this%m,1:this%m) = &
655 ! matmul(matmul(GG,mma_diag(1/diagx)), transpose(GG))
656 ! !update diag(AA)
657 ! AA(1:this%m,1:this%m) = AA(1:this%m,1:this%m) + &
658 ! mma_diag(s/lambda + 1.0/(this%d%x + (mu/y)))
659
660 aa = 0.0_rp
661 ! Direct computation of the matrix multiplication
662 ! (for better performance)
663 do i = 1, this%m
664 do j = 1, this%m
665 ! Compute the (i, j) element of AA
666 do k = 1, this%n !this n is global
667 aa(i, j) = aa(i, j) &
668 + gg(i, k) * (1.0_rp / diagx(k)) * gg(j, k)
669 end do
670 end do
671 end do
672
673 aa_buffer = reshape(aa(1:this%m, 1:this%m), [this%m * this%m])
674
675 call mpi_allreduce(mpi_in_place, aa_buffer, &
676 this%m*this%m, mpi_real_precision, mpi_sum, neko_comm, ierr)
677
678 aa(1:this%m, 1:this%m) = reshape(aa_buffer, [this%m, this%m])
679
680 do i = 1, this%m
681 ! update the diag AA
682 aa(i, i) = aa(i, i) &
683 + s(i) / lambda(i) &
684 + 1.0_rp / (this%d%x(i) + mu(i) / y(i))
685 end do
686
687 aa(1:this%m, this%m+1) = this%a%x
688 aa(this%m+1, 1:this%m) = this%a%x
689 aa(this%m+1, this%m+1) = - zeta/z
690
691 call dgesv(this%m + 1, 1, aa, this%m + 1, ipiv, bb, this%m + 1, info)
692
693 if (info .ne. 0) then
694 call neko_error("DGESV failed to solve the linear system in " // &
695 "mma_subsolve_pdip.")
696 end if
697
698
699 dlambda = bb(1:this%m)
700 dz = bb(this%m + 1)
701
702 ! based on eq(5.19)
703 dx = - delx / diagx - matmul(transpose(gg), dlambda) / diagx
704 dy = (-dely + dlambda) / (this%d%x + mu / y)
705
706 dxsi = -xsi + (epsi - dx * xsi) / (x - this%alpha%x)
707 deta = -eta + (epsi + dx * eta) / (this%beta%x - x)
708 dmu = -mu + (epsi - mu * dy) / y
709 dzeta = -zeta + (epsi - zeta * dz) / z
710 ds = -s + (epsi - dlambda * s) / lambda
711
712 dxx = [dy, dz, dlambda, dxsi, deta, dmu, dzeta, ds]
713 xx = [y, z, lambda, xsi, eta, mu, zeta, s]
714
715 steg = 1.0_rp / maxval([ &
716 1.0_rp, &
717 -1.01_rp * dxx / xx, &
718 -1.01_rp * dx / (x - this%alpha%x), &
719 1.01_rp * dx / (this%beta%x - x) &
720 ])
721
722 ! Save the old values
723 xold = x
724 yold = y
725 zold = z
726 lambdaold = lambda
727 xsiold = xsi
728 etaold = eta
729 muold = mu
730 zetaold = zeta
731 sold = s
732
733 new_residual = 2.0_rp * residual_norm
734
735 ! Share the new_residual and steg values
736 call mpi_allreduce(mpi_in_place, steg, 1, &
737 mpi_real_precision, mpi_min, neko_comm, ierr)
738 call mpi_allreduce(mpi_in_place, new_residual, 1, &
739 mpi_real_precision, mpi_min, neko_comm, ierr)
740
741 ! The innermost loop to determine the suitable step length
742 ! using the Backtracking Line Search approach
743 itto = 0
744 do while ((new_residual .gt. residual_norm) .and. (itto .lt. 50))
745 itto = itto + 1
746
747 ! update the variables
748 x = xold + steg*dx
749 y = yold + steg*dy
750 z = zold + steg*dz
751
752 lambda = lambdaold + steg*dlambda
753 xsi = xsiold + steg*dxsi
754 eta = etaold + steg*deta
755 mu = muold + steg*dmu
756 zeta = zetaold + steg*dzeta
757 s = sold + steg*ds
758
759 ! Recompute the new_residual to see if this stepsize improves
760 ! the residue
761 rex = (this%p0j%x + matmul(transpose(this%pij%x), lambda)) &
762 / (this%upp%x - x)**2 &
763 - (this%q0j%x + matmul(transpose(this%qij%x), lambda)) &
764 / (x - this%low%x)**2 &
765 - xsi + eta
766
767 rey = this%c%x + this%d%x*y - lambda - mu
768 rez = this%a0 - zeta - dot_product(lambda, this%a%x)
769
770 ! Accumulate sums for relambda (the term gi(x))
771 relambda = 0.0_rp
772 do i = 1, this%m
773 do j = 1, this%n
774 relambda(i) = relambda(i) &
775 + this%pij%x(i, j) / (this%upp%x(j) - x(j)) &
776 + this%qij%x(i, j) / (x(j) - this%low%x(j))
777 end do
778 end do
779
780 call mpi_allreduce(mpi_in_place, relambda, this%m, &
781 mpi_real_precision, mpi_sum, neko_comm, ierr)
782
783 relambda = relambda - this%a%x*z - y + s - this%bi%x
784
785 rexsi = xsi * (x - this%alpha%x) - epsi
786 reeta = eta * (this%beta%x - x) - epsi
787 remu = mu * y - epsi
788 rezeta = zeta * z - epsi
789 res = lambda * s - epsi
790
791 ! Compute squared norms for the residuals
792 re_sq_norm = norm2(rex)**2 + norm2(rexsi)**2 + norm2(reeta)**2
793 call mpi_allreduce(mpi_in_place, re_sq_norm, &
794 1, mpi_real_precision, mpi_sum, neko_comm, ierr)
795
796 residual_small = [rey, rez, relambda, remu, rezeta, res]
797 new_residual = sqrt(norm2(residual_small)**2 + re_sq_norm)
798 call mpi_allreduce(mpi_in_place, new_residual, 1, &
799 mpi_real_precision, mpi_min, neko_comm, ierr)
800
801 steg = steg / 2.0_rp
802 end do
803 steg = 2.0_rp * steg ! Correction for the final division by 2
804
805 residual = [rex, rey, rez, relambda, rexsi, reeta, remu, rezeta, res]
806
807 ! Update the maximum and norm of the residuals
808 residual_norm = new_residual
809 residual_max = maxval(abs(residual))
810 call mpi_allreduce(mpi_in_place, residual_max, 1, &
811 mpi_real_precision, mpi_max, neko_comm, ierr)
812 end do
813
814 epsi = 0.1_rp * epsi
815 end do
816
817 ! Save the new designx
818 this%xold2%x = this%xold1%x
819 this%xold1%x = designx
820 designx = x
821
822 !update the parameters of the MMA object nesessary to compute KKT residual
823 this%y%x = y
824 this%z = z
825 this%lambda%x = lambda
826 this%zeta = zeta
827 this%xsi%x = xsi
828 this%eta%x = eta
829 this%mu%x = mu
830 this%s%x = s
831
832 end subroutine mma_subsolve_pdip_cpu
833
836 subroutine mma_subsolve_dip_cpu(this, designx)
837 ! ------------------------------------------------------------------------ !
838 ! -------------------------------Dual Solver------------------------------ !
839 ! ------------------------------------------------------------------------ !
840 ! This implementation is based on: !
841 ! https://doi.org/10.1007/s00158-012-0869-2 !
842 ! Definition of the Lagrangian function: !
843 ! (Note that the equation is slightly different with d(i)=1 and a quadratic!
844 ! term for z. This is done to ensure that we have quadratic terms for both !
845 ! y and z. The weight 0.05 of z^2 follows the Cpp code by Niels.) !
846 ! !
847 ! L(x, y, z, λ) = !
848 ! sum_{j=1}^{n} [ (p_{0j} + sum_{i=1}^{m} λ_i * p_{ij}) / (u_j - x_j)!
849 ! + (q_{0j} + sum_{i=1}^{m} λ_i * q_{ij}) / (x_j - l_j) ]!
850 ! - sum_{i=1}^{m} λ_i * b_i !
851 ! + sum_{i=1}^{m} [ (c_i - λ_i) * y_i + 0.5 * y_i^2 ] !
852 ! + (a_0 - sum_{i=1}^{m} λ_i * a_i) * z + 0.05 * z^2 !
853 ! !
854 ! Breakdown of terms: !
855 ! - Terms related to x: L_x (the first three lines of L(x, y, z, λ)) !
856 ! - Terms related to y: L_y (the fourth line of L(x, y, z, λ)) !
857 ! - Terms related to z: L_z (the last line of L(x, y, z, λ)) !
858 ! !
859 ! Optimization problem if λ is given: !
860 ! !
861 ! Minimize L(x, y, z, λ) !
862 ! subject to: α_j ≤ x_j ≤ β_j, z ≥ 0, and y_i ≥ 0 for all i, j. !
863 ! !
864 ! Since the problem is separable: !
865 ! Ψ(λ) = !
866 ! sum_{j=1}^{n} min_xj {L_x(x_j, λ) | α_j ≤ x_j ≤ β_j} !
867 ! + min_z {L_z(z, λ) | z ≥ 0} !
868 ! + sum_{i=1}^{m} min_yi {L_y(y_i, λ) | y_i ≥ 0} !
869 ! !
870 ! Maximize Ψ(λ) subject to λ_i ≥ 0 for i = 1, ..., m. !
871 ! !
872 ! ------------------------------------------------------------------------ !
873
874 class(mma_t), intent(inout) :: this
875 real(kind=rp), dimension(this%n), intent(inout) :: designx
876 ! Note that there is a local dummy "x" in this subroutine, thus, we call
877 ! the current design "designx" instead of just "x"
878 integer :: i, j, k, iter, ierr
879 real(kind=rp) :: epsi, residual_max, z, steg
880 real(kind=rp), dimension(this%m) :: y, lambda, mu, &
881 relambda, remu, dlambda, dmu, gradlambda
882 real(kind=rp), dimension(this%n) :: x, pjlambda, qjlambda
883
884 ! To compute the Hessian based on eq(13)
885 ! https://doi.org/10.1007/s00158-012-0869-2
886
887 ! inverse of a diag matrix:
888 real(kind=rp), dimension(this%n) :: ljjxinv ! [∇_x^2 Ljj]−1
889 real(kind=rp), dimension(this%m,this%n) :: hijx ! ∇_x hij
890 real(kind=rp), dimension(this%m,this%m) :: hess
891 real(kind=rp) :: hesstrace
892
893
894 ! using DGESV in lapack to solve
895 ! the linear system which needs the following parameters
896 integer :: info
897 integer, dimension(this%m+1) :: ipiv
898
899 ! Parameters for global communication
900 real(kind=rp) :: minimal_epsilon
901
902 ! ------------------------------------------------------------------------ !
903 ! initial value for the parameters in the subsolve based on
904 ! page 15 of "https://people.kth.se/~krille/mmagcmma.pdf"
905
906 epsi = 1.0_rp !100
907 ! The design is kept if no iteration is performed, as in the Cpp code
908 x = designx
909 y = 1.0_rp
910 z = 0.0_rp
911 lambda = max(1.0_rp, 0.5_rp * this%c%x)
912 mu = 1.0_rp !this parameter is eta in Niel's paper
913 ! note that mu in the paper translates to epsi in the code following the
914 ! same style as the Cpp code by Neils
915
916 ! ------------------------------------------------------------------------ !
917 ! Computing the minimal epsilon and choose the most conservative one
918
919 minimal_epsilon = this%epsimin
920 call mpi_allreduce(mpi_in_place, minimal_epsilon, 1, &
921 mpi_real_precision, mpi_min, neko_comm, ierr)
922
923 ! As in the Cpp code by Niels, the residual starts at 1 and is carried
924 ! over from one epsilon level to the next.
925 residual_max = 1.0_rp
926
927 ! ------------------------------------------------------------------------ !
928 ! The main loop of the dual-primal interior point method.
929
930 do while (epsi .gt. minimal_epsilon)
931
932 ! --------------------------------------------------------------------- !
933 ! Compute x(λ), y(λ), z(λ) for the current λ. The residual is carried
934 ! over from the previous epsilon level (initially 1), as in
935 ! MMA::SolveDIP in the Cpp code by Niels.
936
937 associate(p0j => this%p0j%x, q0j => this%q0j%x, &
938 pij => this%pij%x, qij => this%qij%x, &
939 low => this%low%x, upp => this%upp%x, &
940 alpha => this%alpha%x, beta => this%beta%x, &
941 c => this%c%x, d => this%d%x, &
942 a0 => this%a0, a => this%a%x, &
943 bi => this%bi%x)
944 ! minimize(L_x, L_y, L_z) and compute x(λ), y(λ), z(λ) for
945 ! the current value of λ
946
947 ! Comput the value of y that minimizes L_y for the current λ
948 ! minimize (sum_{i=1}^{m} [ (c_i - λ_i) * y_i + 0.5 * y_i^2 ])
949 ! dL_y/dy =0 => y= (λ_i - c_i), ensure y>=0
950 y = max(0.0_rp, lambda - c)
951
952 ! Comput the value of z that minimizes L_z for the current λ
953 ! minimize ((a_0 - sum_{i=1}^{m} λ_i * a_i) * z + 0.05 * z^2)
954 ! ensure z>=0
955 z = max(0.0_rp, 10.0_rp * (dot_product(lambda, a) - a0))
956
957 ! Comput the value of x that minimizes L_x for the current λ
958 ! minimize( sum_{j=1}^{n} [ (p_{0j} + sum_{i=1}^{m} λ_i *
959 ! p_{ij}) / (u_j - x_j) + (q_{0j} + sum_{i=1}^{m} λ_i * q_{ij}) /
960 ! (x_j - l_j) ] - sum_{i=1}^{m} λ_i * b_i)
961 pjlambda = (p0j + matmul(transpose(pij), lambda))
962 qjlambda = (q0j + matmul(transpose(qij), lambda))
963 x = (sqrt(pjlambda) * low + sqrt(qjlambda) * upp) / &
964 (sqrt(pjlambda) + sqrt(qjlambda))
965
966 ! Ensure that x is feasible (alpha<=x<=beta)
967 x = merge(alpha, x, x .lt. alpha)
968 x = merge(beta, x, x .gt. beta)
969
970 ! ------------------------------------------------------------------- !
971 ! Internal loop, the residual of the previous epsilon level is used
972 ! for the first check, following the Cpp code by Niels.
973 do iter = 1, this%max_iter
974
975 !Check the condition
976 if (.not. (residual_max .gt. 0.9_rp * epsi)) exit
977
978 ! Compute dL(x, y, z, λ)/dλ for the updated x(λ), y(λ), z(λ)
979
980 gradlambda = matmul(pij, 1.0_rp / (upp - x)) + &
981 matmul(qij, 1.0_rp/(x - low))
982
983 ! Global comminucation for gradlambda values
984 call mpi_allreduce(mpi_in_place, gradlambda, this%m, &
985 mpi_real_precision, mpi_sum, neko_comm, ierr)
986 gradlambda = gradlambda - bi - y - a * z
987
988 ! Update gradlambda as the right hand side for Newton's method(eq10)
989 gradlambda = - gradlambda - epsi / lambda
990
991 ! Computing the Hessian as in equation (13) in
992 !! https://doi.org/10.1007/s00158-012-0869-2
993
994 !--------------contributions of x terms to Hess--------------------!
995 ljjxinv= - 1.0_rp / ( (2.0_rp * pjlambda/(upp - x)**3) + &
996 (2.0_rp * qjlambda/(x - low)**3))
997
998
999 ! Remove the sensitivity for the active primal constraints
1000 ljjxinv = merge(0.0_rp, ljjxinv, x - alpha < neko_eps)
1001 ljjxinv = merge(0.0_rp, ljjxinv, beta - x < neko_eps)
1002
1003 do i = 1, this%m
1004 hijx(i,:) = pij(i,:) / (upp - x)**2 &
1005 - qij(i,:) / (x - low)**2
1006 end do
1007
1008 hess = 0.0_rp
1009 ! Direct computation of the matrix multiplication
1010 ! (for better performance)
1011 do i = 1, this%m
1012 do j = 1, this%m
1013 ! Compute the (i, j) element of AA
1014 ! this n is local, the sum over the ranks follows below
1015 do k = 1, this%n
1016 hess(i, j) = hess(i, j) &
1017 + hijx(i, k) * (ljjxinv(k)) * hijx(j, k)
1018 end do
1019 end do
1020 end do
1021
1022 call mpi_allreduce(mpi_in_place, hess, &
1023 this%m * this%m, mpi_real_precision, mpi_sum, neko_comm, ierr)
1024
1025 !---------------contributions of z terms to Hess-------------------!
1026 ! Only for inactive constraint, we consider contributions to Hess
1027 ! based on the cpp code by Niels. The factor 10 is the inverse of
1028 ! the weight 2 * 0.05 of z^2 in L_z.
1029 if (dot_product(lambda, a) .gt. 0.0_rp) then
1030 do i = 1, this%m
1031 do j = 1, this%m
1032 hess(i, j) = hess(i, j) - 10.0_rp * a(i) * a(j)
1033 end do
1034 end do
1035 end if
1036
1037 !---------------contributions of y terms to Hess-------------------!
1038 ! Only for inactive constraint, we consider contributions to Hess.
1039 do i = 1, this%m
1040 if (y(i) .gt. 0.0_rp) then
1041 hess(i, i) = hess(i, i) - 1.0_rp
1042 end if
1043 ! Based on eq(10), note the term (-\Omega \Lambda)
1044 hess(i, i) = hess(i, i) - mu(i) / lambda(i)
1045 end do
1046
1047 ! Improve the robustness by stablizing the Hess using
1048 ! Levenberg-Marquardt algorithm (heuristically)
1049 hesstrace = 0.0_rp
1050 do i=1, this%m
1051 hesstrace = hesstrace + hess(i, i)
1052 end do
1053 do i=1, this%m
1054 hess(i,i) = hess(i, i) - &
1055 max(-1.0e-4_rp*hesstrace/this%m, 1.0e-7_rp)
1056 end do
1057
1058 call dgesv(this%m , 1, hess, this%m , ipiv, &
1059 gradlambda, this%m, info)
1060
1061 if (info .ne. 0) then
1062 call neko_error("DGESV failed to solve the linear system in " // &
1063 "mma_subsolve_dip.")
1064 end if
1065 dlambda = gradlambda
1066
1067 ! based on eq(11) for delta eta
1068 dmu = -mu + epsi / lambda - dlambda * mu / lambda
1069
1070 ! Compute the stepsize and update lambda and mu (eta in the paper)
1071
1072 steg = 1.005_rp
1073 do i = 1, this%m
1074 steg = merge(-1.01_rp * dlambda(i) / lambda(i), steg, &
1075 steg < -1.01_rp * dlambda(i) / lambda(i))
1076 steg = merge(-1.01_rp * dmu(i) / mu(i), &
1077 steg, steg < -1.01_rp * dmu(i) / mu(i))
1078 end do
1079
1080 steg = 1.0_rp / steg
1081
1082 lambda = lambda + steg*dlambda
1083 mu = mu + steg*dmu
1084
1085 ! minimize(L_x, L_y, L_z) and compute x(λ), y(λ), z(λ) for
1086 ! the updated values of λ
1087
1088 ! Comput the value of y that minimizes L_y for the current λ
1089 ! minimize (sum_{i=1}^{m} [ (c_i - λ_i) * y_i + 0.5 * y_i^2 ])
1090 ! dL_y/dy =0 => y= (λ_i - c_i), ensure y>=0
1091 y = max(0.0_rp, lambda - c)
1092
1093
1094 ! Comput the value of z that minimizes L_z for the current λ
1095 ! minimize ((a_0 - sum_{i=1}^{m} λ_i * a_i) * z + 0.05 * z^2)
1096 ! ensure z>=0
1097 z = max(0.0_rp, 10.0_rp * (dot_product(lambda, a) - a0))
1098
1099 ! Comput the value of x that minimizes L_x for the current λ
1100 ! minimize( sum_{j=1}^{n} [ (p_{0j} + sum_{i=1}^{m} λ_i *
1101 ! p_{ij}) / (u_j - x_j) + (q_{0j} + sum_{i=1}^{m} λ_i * q_{ij}) /
1102 ! (x_j - l_j) ] - sum_{i=1}^{m} λ_i * b_i)
1103 pjlambda = (p0j + matmul(transpose(pij), lambda))
1104 qjlambda = (q0j + matmul(transpose(qij), lambda))
1105 x = (sqrt(pjlambda) * low + sqrt(qjlambda) * upp) / &
1106 (sqrt(pjlambda) + sqrt(qjlambda))
1107
1108 ! Ensure that x is feasible (alpha<=x<=beta)
1109 x = merge(alpha, x, x .lt. alpha)
1110 x = merge(beta, x, x .gt. beta)
1111
1112 ! Compute the residual for the lambda and mu using eq(9) and eq(15)
1113 relambda = matmul(pij, 1.0_rp / (upp - x)) + &
1114 matmul(qij, 1.0_rp / (x - low))
1115 ! Global comminucation for relambda values
1116 call mpi_allreduce(mpi_in_place, relambda, this%m, &
1117 mpi_real_precision, mpi_sum, neko_comm, ierr)
1118 relambda = relambda - bi - y - a * z + mu
1119
1120 ! Compute residual for mu (eta in the paper)
1121 remu = mu * lambda - epsi
1122
1123 residual_max = maxval(abs([relambda, remu]))
1124 call mpi_allreduce(mpi_in_place, residual_max, 1, &
1125 mpi_real_precision, mpi_max, neko_comm, ierr)
1126 end do
1127 end associate
1128
1129 epsi = 0.1_rp * epsi
1130 end do
1131
1132 ! Save the new designx
1133 this%xold2%x = this%xold1%x
1134 this%xold1%x = designx
1135 designx = x
1136
1137 !update the parameters of the MMA object nesessary to compute KKT residual
1138
1139 this%y%x = y
1140 this%z = z
1141 this%lambda%x = lambda
1142 this%mu%x = mu
1143 end subroutine mma_subsolve_dip_cpu
1144
1145end submodule mma_cpu
MMA module.
Definition mma.f90:69