Octopus
multigrid_solver.F90
Go to the documentation of this file.
1!! Copyright (C) 2005-2006 M. Marques, A. Castro, A. Rubio, G. Bertsch, X. Andrade
2!! Copyright (C) 2024 N. Tancogne-Dejean
3!!
4!! This program is free software; you can redistribute it and/or modify
5!! it under the terms of the GNU General Public License as published by
6!! the Free Software Foundation; either version 2, or (at your option)
7!! any later version.
8!!
9!! This program is distributed in the hope that it will be useful,
10!! but WITHOUT ANY WARRANTY; without even the implied warranty of
11!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12!! GNU General Public License for more details.
13!!
14!! You should have received a copy of the GNU General Public License
15!! along with this program; if not, write to the Free Software
16!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17!! 02110-1301, USA.
18!!
19
20#include "global.h"
21
25 use debug_oct_m
27 use global_oct_m
28 use, intrinsic :: iso_fortran_env
30 use mesh_oct_m
37 use parser_oct_m
40 use space_oct_m
42
43 implicit none
44
45 integer, parameter :: &
46 GAUSS_SEIDEL = 1, &
48
49 private
50 public :: &
57
58 type mg_solver_t
59 private
60
61 real(real64), public :: threshold
62 real(real64) :: relax_factor
63
64 integer, public :: maxcycles = 0
65 integer :: presteps
66 integer :: poststeps
67 integer :: restriction_method
68 integer :: relaxation_method
69 end type mg_solver_t
70
71 integer, parameter, public :: &
72 MG_V_SHAPE = 1, &
73 mg_w_shape = 2, &
74 mg_fmg = 3
75
76contains
77
78 ! ---------------------------------------------------------
79 subroutine multigrid_solver_init(this, namespace, space, mesh, thr)
80 type(mg_solver_t), intent(out) :: this
81 type(namespace_t), intent(in) :: namespace
82 class(space_t), intent(in) :: space
83 type(mesh_t), intent(inout) :: mesh
84 real(real64), intent(in) :: thr
85
86 push_sub(multigrid_solver_init)
87
88 this%threshold = thr
89
90 !%Variable MultigridPresmoothingSteps
91 !%Type integer
92 !%Default 1
93 !%Section Hamiltonian::Poisson::Multigrid
94 !%Description
95 !% Number of Gauss-Seidel smoothing steps before coarse-level
96 !% correction in the multigrid solver.
97 !%End
98 call parse_variable(namespace, 'MultigridPresmoothingSteps', 1, this%presteps)
99
100 !%Variable MultigridPostsmoothingSteps
101 !%Type integer
102 !%Default 4
103 !%Section Hamiltonian::Poisson::Multigrid
104 !%Description
105 !% Number of Gauss-Seidel smoothing steps after coarse-level
106 !% correction in the multigrid solver.
107 !%End
108 call parse_variable(namespace, 'MultigridPostsmoothingSteps', 4, this%poststeps)
109
110 !%Variable MultigridMaxCycles
111 !%Type integer
112 !%Default 50
113 !%Section Hamiltonian::Poisson::Multigrid
114 !%Description
115 !% Maximum number of multigrid cycles that are performed if
116 !% convergence is not achieved.
117 !%End
118 call parse_variable(namespace, 'MultigridMaxCycles', 50, this%maxcycles)
119
120 !%Variable MultigridRestrictionMethod
121 !%Type integer
122 !%Default fullweight
123 !%Section Hamiltonian::Poisson::Multigrid
124 !%Description
125 !% Method used from fine-to-coarse grid transfer.
126 !%Option injection 1
127 !% Injection
128 !%Option fullweight 2
129 !% Fullweight restriction
130 !%End
131 call parse_variable(namespace, 'MultigridRestrictionMethod', 2, this%restriction_method)
132 if (.not. varinfo_valid_option('MultigridRestrictionMethod', this%restriction_method)) then
133 call messages_input_error(namespace, 'MultigridRestrictionMethod')
134 end if
135 call messages_print_var_option("MultigridRestrictionMethod", this%restriction_method, namespace=namespace)
136
137 !%Variable MultigridRelaxationMethod
138 !%Type integer
139 !%Section Hamiltonian::Poisson::Multigrid
140 !%Description
141 !% Method used to solve the linear system approximately in each grid for the
142 !% multigrid procedure that solves a linear equation like the Poisson equation. Default is <tt>gauss_seidel</tt>,
143 !% unless curvilinear coordinates are used, in which case the default is <tt>gauss_jacobi</tt>.
144 !%Option gauss_seidel 1
145 !% Gauss-Seidel.
146 !%Option weighted_jacobi 2
147 !% Jacobi relaxation with a weight. The weight is determined by by MultigridRelaxationFactor.
148 !%End
149 if (mesh%use_curvilinear) then
150 call parse_variable(namespace, 'MultigridRelaxationMethod', weighted_jacobi, this%relaxation_method)
151 else
152 call parse_variable(namespace, 'MultigridRelaxationMethod', gauss_seidel, this%relaxation_method)
153 end if
155 if (.not. varinfo_valid_option('MultigridRelaxationMethod', this%relaxation_method)) then
156 call messages_input_error(namespace, 'MultigridRelaxationMethod')
157 end if
158 call messages_print_var_option("MultigridRelaxationMethod", this%relaxation_method, namespace=namespace)
160 if (this%relaxation_method == weighted_jacobi) then
161 !%Variable MultigridRelaxationFactor
162 !%Type float
163 !%Section Hamiltonian::Poisson::Multigrid
164 !%Description
165 !% Relaxation factor of the relaxation operator used for the
166 !% multigrid method. Only used for the <tt>gauss_jacobi</tt> method.
167 !% The default is 0.6666 for the <tt>gauss_jacobi</tt> method.
168 !%End
169 call parse_variable(namespace, 'MultigridRelaxationFactor', 0.6666_real64, this%relax_factor)
170 end if
171
173 end subroutine multigrid_solver_init
174
175
176 ! ---------------------------------------------------------
180 recursive subroutine multigrid_solver_v_cycle(this, der, op, sol, rhs)
181 type(mg_solver_t), intent(in) :: this
182 type(derivatives_t), intent(in) :: der
183 type(nl_operator_t), intent(in) :: op
184 real(real64), contiguous, intent(inout) :: sol(:)
185 real(real64), contiguous, intent(in) :: rhs(:)
186
187 real(real64), allocatable :: residue(:), coarse_residue(:), correction(:), coarse_correction(:)
188
190
191 safe_allocate(residue(1:der%mesh%np_part))
192
193 if (associated(der%coarser)) then
194 assert(associated(op%coarser))
195
196 safe_allocate(correction(1:der%mesh%np_part))
197 safe_allocate(coarse_residue(1:der%coarser%mesh%np_part))
198 safe_allocate(coarse_correction(1:der%coarser%mesh%np_part))
199
200 ! Pre-Smoothing
201 call multigrid_relax(this, der%mesh, der, op, sol, rhs, this%presteps)
202
203 ! Compute the residual error
204 call get_residual(op, der, sol, rhs, residue)
205
206 ! Restriction of the residual is the next r.h.s
207 if (debug%info) then
208 message(1) = "Multigrid restriction"
209 call messages_info(1)
210 end if
211 call dmultigrid_fine2coarse(der%to_coarser, der, der%coarser%mesh, residue, coarse_residue, this%restriction_method)
212
213 ! Recursive call for the coarse-grid correction
214 coarse_correction = m_zero
215 call multigrid_solver_v_cycle(this, der%coarser, op%coarser, coarse_correction, coarse_residue)
216
217 !Prolongation
218 if (debug%info) then
219 message(1) = "Multigrid prolongation"
220 call messages_info(1)
221 end if
222 correction = m_zero
223 call dmultigrid_coarse2fine(der%to_coarser, der%coarser, der%mesh, coarse_correction, correction)
224
225 ! Correction
226 call lalg_axpy(der%mesh%np, m_one, correction, sol)
227
228 safe_deallocate_a(correction)
229 safe_deallocate_a(coarse_residue)
230 safe_deallocate_a(coarse_correction)
231
232 ! Post-Smoothing
233 call multigrid_relax(this, der%mesh, der, op, sol, rhs, this%poststeps)
234
235 else ! Coarsest grid
236
237 call multigrid_solver_solve_coarsest(this, der, op, sol, rhs, residue)
238
239 end if
240
241 safe_deallocate_a(residue)
242
244 end subroutine multigrid_solver_v_cycle
245
246 ! ---------------------------------------------------------
250 recursive subroutine multigrid_solver_w_cycle(this, der, op, sol, rhs)
251 type(mg_solver_t), intent(in) :: this
252 type(derivatives_t), intent(in) :: der
253 type(nl_operator_t), intent(in) :: op
254 real(real64), contiguous, intent(inout) :: sol(:)
255 real(real64), contiguous, intent(in) :: rhs(:)
256
257 real(real64), allocatable :: residue(:), coarse_residue(:), correction(:), coarse_correction(:)
258
260
261 safe_allocate(residue(1:der%mesh%np_part))
262
263 if (associated(der%coarser)) then
264 assert(associated(op%coarser))
265
266 safe_allocate(correction(1:der%mesh%np_part))
267 safe_allocate(coarse_residue(1:der%coarser%mesh%np_part))
268 safe_allocate(coarse_correction(1:der%coarser%mesh%np_part))
269
270 ! Pre-Smoothing
271 call multigrid_relax(this, der%mesh, der, op, sol, rhs, this%presteps)
272
273 ! Compute the residual error
274 call get_residual(op, der, sol, rhs, residue)
275
276 ! Restriction of the residual is the next r.h.s
277 if (debug%info) then
278 message(1) = "Multigrid restriction"
279 call messages_info(1)
280 end if
281 call dmultigrid_fine2coarse(der%to_coarser, der, der%coarser%mesh, residue, coarse_residue, this%restriction_method)
282
283 ! Recursive call for the coarse-grid correction
284 coarse_correction = m_zero
285 call multigrid_solver_w_cycle(this, der%coarser, op%coarser, coarse_correction, coarse_residue)
286
287 !Prolongation
288 if (debug%info) then
289 message(1) = "Multigrid prolongation"
290 call messages_info(1)
291 end if
292 correction = m_zero
293 call dmultigrid_coarse2fine(der%to_coarser, der%coarser, der%mesh, coarse_correction, correction)
294
295 ! Correction
296 call lalg_axpy(der%mesh%np, m_one, correction, sol)
297
298 ! Re-Smoothing
299 call multigrid_relax(this, der%mesh, der, op, sol, rhs, this%presteps)
300
301 ! Compute the residual error
302 call get_residual(op, der, sol, rhs, residue)
303
304 ! Restriction of the residual is the next r.h.s
305 if (debug%info) then
306 message(1) = "Multigrid restriction"
307 call messages_info(1)
308 end if
309 call dmultigrid_fine2coarse(der%to_coarser, der, der%coarser%mesh, residue, coarse_residue, this%restriction_method)
310
311 ! Recursive call for the coarse-grid correction
312 coarse_correction = m_zero
313 call multigrid_solver_w_cycle(this, der%coarser, op%coarser, coarse_correction, coarse_residue)
314
315 !Prolongation
316 if (debug%info) then
317 message(1) = "Multigrid prolongation"
318 call messages_info(1)
319 end if
320 correction = m_zero
321 call dmultigrid_coarse2fine(der%to_coarser, der%coarser, der%mesh, coarse_correction, correction)
322
323 ! Correction
324 call lalg_axpy(der%mesh%np, m_one, correction, sol)
325
326 safe_deallocate_a(correction)
327 safe_deallocate_a(coarse_residue)
328 safe_deallocate_a(coarse_correction)
329
330 ! Post-Smoothing
331 call multigrid_relax(this, der%mesh, der, op, sol, rhs, this%poststeps)
332
333 else ! Coarsest grid
334
335 call multigrid_solver_solve_coarsest(this, der, op, sol, rhs, residue)
336
337 end if
338
339 safe_deallocate_a(residue)
340
342
344
345 ! ---------------------------------------------------------
349 subroutine multigrid_iterative_solver(this, namespace, der, op, sol, rhs, multigrid_shape)
350 type(mg_solver_t), intent(in) :: this
351 type(namespace_t), intent(in) :: namespace
352 type(derivatives_t), intent(in) :: der
353 type(nl_operator_t), intent(in) :: op
354 real(real64), contiguous, intent(inout) :: sol(:)
355 real(real64), contiguous, intent(inout) :: rhs(:)
356 integer, intent(in) :: multigrid_shape
357
358 integer :: iter
359 real(real64) :: resnorm
360 real(real64), allocatable :: err(:)
361
363
364 safe_allocate(err(1:der%mesh%np))
365
366 do iter = 1, this%maxcycles
367
368 select case (multigrid_shape)
369 case(mg_v_shape)
370 call multigrid_solver_v_cycle(this, der, op, sol, rhs)
371 case(mg_w_shape)
372 call multigrid_solver_w_cycle(this, der, op, sol, rhs)
373 case default
374 assert(.false.)
375 end select
376 ! Compute the residual
377 call dderivatives_lapl(der, sol, err)
378 call lalg_axpy(der%mesh%np, -m_one, rhs, err)
379 resnorm = dmf_nrm2(der%mesh, err)
380
381 if (resnorm < this%threshold) exit
382
383 if (debug%info) then
384 write(message(1), '(a,i5,a,e13.6)') "Multigrid: base level: iter ", iter, " res ", resnorm
385 call messages_info(1, namespace=namespace)
386 end if
387
388 end do
389
390 if (resnorm >= this%threshold) then
391 message(1) = 'Multigrid Poisson solver did not converge.'
392 write(message(2), '(a,e14.6)') ' Abs. norm of the residue = ', resnorm
393 call messages_warning(2, namespace=namespace)
394 else
395 if (debug%info) then
396 write(message(1), '(a,i4,a)') "Multigrid Poisson solver converged in ", iter, " iterations."
397 write(message(2), '(a,e14.6)') ' Abs. norm of the residue = ', resnorm
398 call messages_info(2, namespace=namespace)
399 end if
400 end if
401
402 safe_deallocate_a(err)
403
405 end subroutine multigrid_iterative_solver
406
407 ! ---------------------------------------------------------
412 recursive subroutine multigrid_fmg_solver(this, namespace, der, op, sol, rhs)
413 type(mg_solver_t), intent(in) :: this
414 type(namespace_t), intent(in) :: namespace
415 type(derivatives_t), intent(in) :: der
416 type(nl_operator_t), intent(in) :: op
417 real(real64), contiguous, intent(inout) :: sol(:)
418 real(real64), contiguous, intent(inout) :: rhs(:)
419
420 real(real64), allocatable :: coarse_solution(:), coarse_rhs(:), residue(:)
421
422 push_sub(multigrid_fmg_solver)
423
424 if (associated(der%coarser)) then
425 assert(associated(op%coarser))
426
427 safe_allocate(coarse_rhs(1:der%coarser%mesh%np_part))
428 safe_allocate(coarse_solution(1:der%coarser%mesh%np_part))
429
430 ! Restriction of the r.h.s
431 if (debug%info) then
432 message(1) = "Full Multigrid restriction"
433 call messages_info(1)
434 end if
435 call dmultigrid_fine2coarse(der%to_coarser, der, der%coarser%mesh, rhs, coarse_rhs, this%restriction_method)
436
437 ! Recursive call for the coarse-grid correction
438 coarse_solution = m_zero
439 call multigrid_fmg_solver(this, namespace, der%coarser, op%coarser, coarse_solution, coarse_rhs)
440
441 !Prolongation
442 if (debug%info) then
443 message(1) = "Full Multigrid prolongation"
444 call messages_info(1)
445 end if
446 sol = m_zero
447 call dmultigrid_coarse2fine(der%to_coarser, der%coarser, der%mesh, coarse_solution, sol)
448
449 ! Perform N times a V cycle, up to convergence at each step
450 call multigrid_iterative_solver(this, namespace, der, op, sol, rhs, mg_v_shape)
451
452 safe_deallocate_a(coarse_solution)
453 safe_deallocate_a(coarse_rhs)
454
455 else ! Coarsest grid - solve the problem
456
457 safe_allocate(residue(1:der%mesh%np))
458 call multigrid_solver_solve_coarsest(this, der, op, sol, rhs, residue)
459 safe_deallocate_a(residue)
460 end if
461
462 pop_sub(multigrid_fmg_solver)
463 end subroutine multigrid_fmg_solver
464
465
466 ! ---------------------------------------------------------
468 subroutine multigrid_solver_solve_coarsest(this, der, op, sol, rhs, residue)
469 type(mg_solver_t), intent(in) :: this
470 type(derivatives_t), intent(in) :: der
471 type(nl_operator_t), intent(in) :: op
472 real(real64), contiguous, intent(inout) :: sol(:)
473 real(real64), contiguous, intent(in) :: rhs(:)
474 real(real64), contiguous, intent(inout) :: residue(:)
475
476 integer :: iter
477 real(real64) :: resnorm
478
480
481 ! Solution of the problem, i.e., multiple call to multigrid_relax up to convergence
482 do iter = 1, this%maxcycles
483
484 call multigrid_relax(this, der%mesh, der, op, sol, rhs, 1)
485
486 call get_residual(op, der, sol, rhs, residue)
487 resnorm = dmf_nrm2(der%mesh, residue)
488 if (resnorm < this%threshold) exit
489
490 end do
491
492 if (debug%info) then
493 write(message(1), '(a,i4,a)') "Multigrid coarsest grid solver converged in ", iter, " iterations."
494 write(message(2), '(a,es18.6)') "Residue norm is ", resnorm
495 call messages_info(2)
496 end if
497
499 end subroutine
500
501 ! ---------------------------------------------------------
503 subroutine get_residual(op, der, sol, rhs, residue)
504 type(nl_operator_t), intent(in) :: op
505 type(derivatives_t), intent(in) :: der
506 real(real64), contiguous, intent(inout) :: sol(:)
507 real(real64), contiguous, intent(in) :: rhs(:)
508 real(real64), contiguous, intent(inout) :: residue(:)
509
510 integer :: ip
511
512 ! Compute the residue
513 call dderivatives_perform(op, der, sol, residue)
514 !$omp parallel do
515 do ip = 1, der%mesh%np
516 residue(ip) = rhs(ip) - residue(ip)
517 end do
518 end subroutine get_residual
519
520
521 ! ---------------------------------------------------------
525 subroutine multigrid_relax(this, mesh, der, op, sol, rhs, steps)
526 type(mg_solver_t), intent(in) :: this
527 type(mesh_t), intent(in) :: mesh
528 type(derivatives_t), intent(in) :: der
529 type(nl_operator_t), intent(in) :: op
530 real(real64), contiguous, intent(inout) :: sol(:)
531 real(real64), contiguous, intent(in) :: rhs(:)
532 integer, intent(in) :: steps
533
534 integer :: istep, index
535 integer :: ip, nn, is
536 real(real64) :: point, factor
537 real(real64), allocatable :: op_sol(:), diag(:)
538
539 push_sub(multigrid_relax)
540 call profiling_in("MG_GAUSS_SEIDEL")
541
542 select case (this%relaxation_method)
543
544 case (gauss_seidel)
545
546 do istep = 1, steps
547
548 call boundaries_set(der%boundaries, der%mesh, sol)
549
550 if (mesh%parallel_in_domains) then
551 call dpar_vec_ghost_update(mesh%pv, sol)
552 end if
553
554 nn = op%stencil%size
555
556 if (op%const_w) then
557 factor = -m_one/op%w(op%stencil%center, 1)
558 call dgauss_seidel(op%stencil%size, op%w(1, 1), op%nri, &
559 op%ri(1, 1), op%rimap_inv(1), op%rimap_inv(2), factor, sol(1), rhs(1))
560 else
561 !$omp parallel do private(point, index)
562 do ip = 1, mesh%np
563 point = m_zero
564 do is = 1, nn
565 index = nl_operator_get_index(op, is, ip)
566 point = point + op%w(is, ip)*sol(index)
567 end do
568 sol(ip) = sol(ip) - (point-rhs(ip))/op%w(op%stencil%center, ip)
569 end do
570 end if
571
572 end do
573 call profiling_count_operations(mesh%np*(steps + 1)*(2*nn + 3))
574
575 case (weighted_jacobi)
576
577 safe_allocate(op_sol(1:mesh%np))
578 safe_allocate(diag(1:mesh%np))
579
580 call dnl_operator_operate_diag(op, diag)
581 !$omp parallel do
582 do ip = 1, mesh%np
583 diag(ip) = this%relax_factor/diag(ip)
584 end do
585
586 do istep = 1, steps
587 call dderivatives_perform(op, der, sol, op_sol)
588 !$omp parallel do
589 do ip = 1, mesh%np
590 sol(ip) = sol(ip) - diag(ip)*(op_sol(ip) - rhs(ip))
591 end do
592 end do
593
594 safe_deallocate_a(diag)
595 safe_deallocate_a(op_sol)
597 end select
598
599 call profiling_out("MG_GAUSS_SEIDEL")
600 pop_sub(multigrid_relax)
601
602 end subroutine multigrid_relax
603
604end module multigrid_solver_oct_m
605
606!! Local Variables:
607!! mode: f90
608!! coding: utf-8
609!! End:
constant times a vector plus a vector
Definition: lalg_basic.F90:170
Module implementing boundary conditions in Octopus.
Definition: boundaries.F90:122
subroutine, public dpar_vec_ghost_update(pv, v_local)
Updates ghost points of every node.
type(debug_t), save, public debug
Definition: debug.F90:154
This module calculates the derivatives (gradients, Laplacians, etc.) of a function.
subroutine, public dderivatives_perform(op, der, ff, op_ff, ghost_update, set_bc, factor)
apply a nl_operator to a mesh function
subroutine, public dderivatives_lapl(der, ff, op_ff, ghost_update, set_bc, factor)
apply the Laplacian to a mesh function
real(real64), parameter, public m_zero
Definition: global.F90:187
real(real64), parameter, public m_one
Definition: global.F90:188
This module defines various routines, operating on mesh functions.
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:118
subroutine, public messages_warning(no_lines, all_nodes, namespace)
Definition: messages.F90:543
subroutine, public messages_info(no_lines, iunit, verbose_limit, stress, all_nodes, namespace)
Definition: messages.F90:624
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:160
subroutine, public messages_input_error(namespace, var, details, row, column)
Definition: messages.F90:723
subroutine, public dmultigrid_coarse2fine(tt, coarse_der, fine_mesh, f_coarse, f_fine, set_bc)
Definition: multigrid.F90:674
subroutine, public dmultigrid_fine2coarse(tt, fine_der, coarse_mesh, f_fine, f_coarse, method_p)
Definition: multigrid.F90:753
This modules provides the routines for solving Ax=b using the V-shaped multigrid method.
recursive subroutine, public multigrid_fmg_solver(this, namespace, der, op, sol, rhs)
Full multigrid (FMG) solver.
recursive subroutine, public multigrid_solver_v_cycle(this, der, op, sol, rhs)
Performs one cycle of a V-shaped multigrid solver.
recursive subroutine, public multigrid_solver_w_cycle(this, der, op, sol, rhs)
Performs one cycle of a W-shaped multigrid solver.
subroutine multigrid_solver_solve_coarsest(this, der, op, sol, rhs, residue)
Computes the solution on the coarsest grid.
subroutine, public multigrid_iterative_solver(this, namespace, der, op, sol, rhs, multigrid_shape)
An iterative multigrid solver.
integer, parameter weighted_jacobi
subroutine, public multigrid_solver_init(this, namespace, space, mesh, thr)
integer, parameter, public mg_fmg
subroutine get_residual(op, der, sol, rhs, residue)
Computes the residual.
subroutine multigrid_relax(this, mesh, der, op, sol, rhs, steps)
Given a nonlocal operator op, perform the relaxation operator.
integer, parameter, public mg_w_shape
This module defines non-local operators.
subroutine, public dnl_operator_operate_diag(op, fo)
integer pure function, public nl_operator_get_index(op, is, ip)
This module contains interfaces for routines in operate.c.
Definition: operate_f.F90:117
Some general things and nomenclature:
Definition: par_vec.F90:171
subroutine, public profiling_out(label)
Increment out counter and sum up difference between entry and exit time.
Definition: profiling.F90:623
subroutine, public profiling_in(label, exclude)
Increment in counter and save entry time.
Definition: profiling.F90:552
class representing derivatives
Describes mesh distribution to nodes.
Definition: mesh.F90:186
data type for non local operators