Octopus
sturm_liouville.F90
Go to the documentation of this file.
1!! Copyright (C) 2002-2006 M. Marques, A. Castro, A. Rubio, G. Bertsch
2!! Copyright (C) 2023-2026 N. Tancogne-Dejean, C. Joens
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
40 use debug_oct_m
42 use global_oct_m
43 use grid_oct_m
44 use iso_c_binding
47 use mesh_oct_m
55 use space_oct_m
56
57 implicit none
58
59 private
60 public :: &
67
68
69 type :: sturm_liouville_t
70 private
71 type(grid_t), pointer, public :: gr => null()
72 real(real64), pointer :: rho(:) => null()
73 real(real64), allocatable :: diag_preconditioner(:)
74 real(real64), allocatable :: lapl_rho(:)
75 real(real64), allocatable :: diag_lapl(:)
76 real(real64), allocatable :: sl_tmp_prod(:)
77 real(real64), allocatable :: sl_tmp_lapl(:)
78 integer :: max_iter = 500
79 real(real64) :: threshold = 1e-7_real64
80 real(real64) :: inverse_tol = m_epsilon
81 logical :: initialized = .false.
82 end type sturm_liouville_t
83
84contains
85
86 ! -------------------------------------------------------------------------------------
91 subroutine sturm_liouville_init(this, namespace, gr, space, max_iter, thr, inverse_tol)
92 type(sturm_liouville_t), intent(inout) :: this
93 type(namespace_t), intent(in) :: namespace
94 type(grid_t), target, intent(in) :: gr
95 type(space_t), intent(in) :: space
96 integer, optional, intent(in) :: max_iter
97 real(real64), optional, intent(in) :: thr
98 real(real64), optional, intent(in) :: inverse_tol
99
100 character(len=80) :: name
101 type(nl_operator_t) :: op(1)
102
103 push_sub(sturm_liouville_init)
104 assert(.not. this%initialized)
105
106 this%gr => gr
107 if (present(max_iter)) this%max_iter = max_iter
108 if (present(thr)) this%threshold = thr
109 if (gr%box%dim == 1) this%inverse_tol = 1e-20_real64
110 if (present(inverse_tol)) this%inverse_tol = inverse_tol
111
112 ! Build the Laplacian stencil to extract the diagonal for the Jacobi preconditioner
113 name = 'SL preconditioner'
114 call derivatives_get_lapl(gr%der, namespace, op, space, name, 1)
115 safe_allocate(this%diag_lapl(1:op(1)%np))
116 call dnl_operator_operate_diag(op(1), this%diag_lapl)
117 call nl_operator_end(op(1))
118
119 safe_allocate(this%lapl_rho(1:this%gr%np))
120 safe_allocate(this%sl_tmp_prod(1:this%gr%np_part))
121 safe_allocate(this%sl_tmp_lapl(1:this%gr%np))
122 safe_allocate(this%diag_preconditioner(1:this%gr%np))
123
124 this%initialized = .true.
125
126 pop_sub(sturm_liouville_init)
127 end subroutine sturm_liouville_init
128
129
130 ! -------------------------------------------------------------------------------------
132 subroutine sturm_liouville_end(this)
133 type(sturm_liouville_t), intent(inout) :: this
135 push_sub(sturm_liouville_end)
136
137 safe_deallocate_a(this%diag_lapl)
138 safe_deallocate_a(this%diag_preconditioner)
139 safe_deallocate_a(this%lapl_rho)
140 safe_deallocate_a(this%sl_tmp_prod)
141 safe_deallocate_a(this%sl_tmp_lapl)
142 nullify(this%gr)
143 nullify(this%rho)
144 this%initialized = .false.
145
146 pop_sub(sturm_liouville_end)
147 end subroutine sturm_liouville_end
148
149 ! -------------------------------------------------------------------------------------
152 subroutine sturm_liouville_solve_poisson(this, psolver, namespace, f, v, with_inverse_density, rho)
153 type(sturm_liouville_t), target, intent(inout) :: this
154 type(poisson_t), intent(in) :: psolver
155 type(namespace_t), intent(in) :: namespace
156 real(real64), intent(in) :: f(:,:)
157 real(real64), intent(inout) :: v(:)
158 logical, optional, intent(in) :: with_inverse_density
159 real(real64), optional, target, intent(in) :: rho(:)
160
161 real(real64), allocatable :: poisson_rhs(:), tmp_f(:, :)
162 real(real64) :: res
163 integer :: iter, ip, idm
166 assert(this%initialized)
169 if (present(rho)) this%rho => rho
171 safe_allocate(tmp_f(1:this%gr%np_part, 1:this%gr%box%dim))
172 ! We allow the force density F and \f$\frac{F}{\rho}\f$ as initial values of f(:, :)
173 if (optional_default(with_inverse_density, .true.)) then
174 assert(associated(this%rho))
175 do ip = 1, this%gr%np
176 do idm = 1, this%gr%box%dim
177 tmp_f(ip, idm) = f(ip, idm) / (safe_tol(this%rho(ip), this%inverse_tol))
178 end do
179 end do
180 else
181 call lalg_copy(this%gr%np, this%gr%box%dim, f, tmp_f)
182 end if
183
184 ! \f$ poisson_rhs = \nabla \frac{\vec(F)}{\rho} \f$
185 safe_allocate(poisson_rhs(1:this%gr%np))
186 call dderivatives_div(this%gr%der, tmp_f(:, :), poisson_rhs(:))
187 safe_deallocate_a(tmp_f)
188
189 !! Solve the Poisson equation for the initial guess
190 !! \f$ v = -\int frac{ \nabla'\cdot\frac{\vec{F} }{\rho}(\vec{r'}){\abs{r-r'}} d^\text{dim}\vec{r'}\f$
191 !! For 3D we use the default Poisson solver, for 1D and 2D we use the conjugate gradient solver.
192 select case(this%gr%box%dim)
193 case(3)
194 call dpoisson_solve(psolver, namespace, v, poisson_rhs, all_nodes=.true.)
195 call lalg_scal(this%gr%np, -m_fourth / m_pi, v)
196 case default
197 ! For 1D and 2D we solve the Poisson equation using the conjugate gradient method
198 ! Importantly, we cannot use the default Poisson solver,
199 ! as we do not want a soft Coulomb here but the Poisson eqaution in 1D.
200 iter = 1500
201 ! Initialize the mesh_aux pointer needed by dmf_dotu_aux / dmf_nrm2_aux
202 call mesh_init_mesh_aux(this%gr)
203 call dconjugate_gradients(this%gr%np, v, poisson_rhs, dlaplacian_op, dmf_dotp_aux, iter, res, &
204 this%threshold, userdata=[c_loc(this%gr%der)])
205 write(message(1),'(a,i6,a)') "Info: CG converged with ", iter, " iterations."
206 write(message(2),'(a,e14.6)') "Info: The residue is ", res
207 call messages_info(2, namespace=namespace)
208 ! The dlaplacian_op applies a negative Laplacian.
209 ! Hence, we need a negative sign here.
210 call lalg_scal(this%gr%np, -m_one, v)
211 end select
212
213 safe_deallocate_a(poisson_rhs)
215 end subroutine sturm_liouville_solve_poisson
216
217 ! -------------------------------------------------------------------------------------
225 subroutine sturm_liouville_solve(this, namespace, rho, rhs, v)
226 type(sturm_liouville_t), target, intent(inout) :: this
227 type(namespace_t), intent(in) :: namespace
228 real(real64), contiguous, target, intent(in) :: rho(:)
229 real(real64), contiguous, intent(in) :: rhs(:)
230 real(real64), contiguous, intent(inout) :: v(:)
231
232 integer :: iter, ip
233 real(real64) :: res
234
235 push_sub(sturm_liouville_solve)
236
237 assert(this%initialized)
238 assert(ubound(rho, dim=1) >= this%gr%np_part)
239 assert(ubound(rhs, dim=1) >= this%gr%np)
240 assert(ubound(v, dim=1) >= this%gr%np)
241
242 ! Set the density pointer, precompute its Laplacian and set diag_preconditioner for the Jacobi preconditioner
243 this%rho => rho
244 call mesh_init_mesh_aux(this%gr)
245 call dderivatives_lapl(this%gr%der, this%rho, this%lapl_rho)
246 !$omp parallel do
247 do ip = 1, this%gr%np
248 this%diag_preconditioner(ip) = 1 / (safe_tol(this%rho(ip)* this%diag_lapl(ip), this%inverse_tol))
249 end do
250
251 iter = this%max_iter
252 call dqmr_sym_gen_dotu(this%gr%np, v, rhs, sl_operator, dmf_dotu_aux, &
253 dmf_nrm2_aux, jacobi_preconditioner, iter, userdata=[c_loc(this)], residue = res, &
254 threshold = this%threshold, showprogress = .false.)
255
256 write(message(1), '(a, i6, a)') "Info: Sturm-Liouville solver converged in ", iter, " iterations."
257 write(message(2), '(a, es14.6)') "Info: The residue is ", res
258 call messages_info(2, namespace=namespace)
259
260 ! Cleanup per-solve data
261 nullify(this%rho)
262
263 pop_sub(sturm_liouville_solve)
264 end subroutine sturm_liouville_solve
265
266
267 ! -------------------------------------------------------------------------------------
278 subroutine sturm_liouville_solve_from_div(this, namespace, rho, f, v, psolver)
279 type(sturm_liouville_t), target, intent(inout) :: this
280 type(namespace_t), intent(in) :: namespace
281 real(real64), target, contiguous, intent(in) :: rho(:)
282 real(real64), contiguous, intent(in) :: f(:,:)
283 real(real64), contiguous, intent(inout) :: v(:)
284 type(poisson_t), optional, intent(in) :: psolver
285
286 real(real64), allocatable :: rhs(:), neg_f(:,:)
287
289 assert(this%initialized)
290 assert(ubound(rho, dim=1) >= this%gr%np_part)
291 assert(ubound(f, dim=1) >= this%gr%np_part)
292 assert(ubound(f, dim=2) >= this%gr%box%dim)
293 assert(ubound(v, dim=1) >= this%gr%np)
294
295 safe_allocate(neg_f(1:this%gr%np_part, 1:this%gr%box%dim))
296 neg_f = -f
297 ! Initialize the guess with the Poisson solution
298 if (present(psolver)) then
299 call sturm_liouville_solve_poisson(this, psolver, namespace, neg_f, v, rho=rho)
300 end if
301
302 safe_allocate(rhs(1:this%gr%np))
303 call dderivatives_div(this%gr%der, neg_f, rhs)
304 call sturm_liouville_solve(this, namespace, rho, rhs, v)
305
306 safe_deallocate_a(rhs)
307 safe_deallocate_a(neg_f)
308
310 end subroutine sturm_liouville_solve_from_div
311
312
313 ! -------------------------------------------------------------------------------------
320 subroutine sl_operator(x, hx, userdata)
321 real(real64), contiguous, intent(in) :: x(:)
322 real(real64), contiguous, intent(out) :: hx(:)
323 type(c_ptr), intent(in) :: userdata(:)
324
325 integer :: ip
326 real(real64), allocatable :: v(:), prod(:), lapl_v(:), lapl_product(:)
327 type(sturm_liouville_t), pointer :: sl
328
329 assert(size(userdata) == 1)
330 assert(c_associated(userdata(1)))
331 call c_f_pointer(userdata(1), sl)
332
333 safe_allocate(v(1:sl%gr%np_part))
334 safe_allocate(lapl_v(1:sl%gr%np))
335 call lalg_copy(sl%gr%np, x, v)
336 call dderivatives_lapl(sl%gr%der, v, lapl_v)
337
338 safe_allocate(prod(1:sl%gr%np_part))
339 safe_allocate(lapl_product(1:sl%gr%np))
340 do ip = 1, sl%gr%np_part
341 prod(ip) = sl%rho(ip) * v(ip)
342 end do
343 call dderivatives_lapl(sl%gr%der, prod, lapl_product, set_bc=.false.)
344
345 do ip = 1, sl%gr%np
346 hx(ip) = m_half * (sl%rho(ip) * lapl_v(ip) - v(ip) * sl%lapl_rho(ip) + lapl_product(ip))
347 end do
348
349 safe_deallocate_a(v)
350 safe_deallocate_a(prod)
351 safe_deallocate_a(lapl_v)
352 safe_deallocate_a(lapl_product)
353
354 end subroutine sl_operator
355
361 subroutine jacobi_preconditioner(x, hx, userdata)
362 real(real64), contiguous, intent(in) :: x(:)
363 real(real64), contiguous, intent(out) :: hx(:)
364 type(c_ptr), intent(in) :: userdata(:)
365
366 integer :: ip
367 type(sturm_liouville_t), pointer :: sl
368
369 assert(size(userdata) == 1)
370 assert(c_associated(userdata(1)))
371 call c_f_pointer(userdata(1), sl)
372
373 !$omp parallel do
374 do ip = 1, sl%gr%np
375 hx(ip) = x(ip) * sl%diag_preconditioner(ip)
376 end do
377
378 end subroutine jacobi_preconditioner
379
380end module sturm_liouville_oct_m
381
382!! Local Variables:
383!! mode: f90
384!! coding: utf-8
385!! End:
Copies a vector x, to a vector y.
Definition: lalg_basic.F90:188
scales a vector by a constant
Definition: lalg_basic.F90:159
This module calculates the derivatives (gradients, Laplacians, etc.) of a function.
subroutine, public derivatives_get_lapl(this, namespace, op, space, name, order)
subroutine, public dderivatives_lapl(der, ff, op_ff, ghost_update, set_bc, factor)
apply the Laplacian to a mesh function
subroutine, public dderivatives_div(der, ff, op_ff, ghost_update, set_bc, to_cartesian)
apply the divergence operator to a vector of mesh functions
real(real64), parameter, public m_zero
Definition: global.F90:200
real(real64), parameter, public m_pi
some mathematical constants
Definition: global.F90:198
real(real64), parameter, public m_fourth
Definition: global.F90:209
real(real64), parameter, public m_epsilon
Definition: global.F90:216
real(real64), parameter, public m_half
Definition: global.F90:206
real(real64), parameter, public m_one
Definition: global.F90:201
This module implements the underlying real-space grid.
Definition: grid.F90:119
Computes and , suitable as an operator callback for iterative solvers (CG, QMR, etc....
subroutine, public dlaplacian_op(x, hx, userdata)
Computes the negative Laplacian operator action: .
This module defines various routines, operating on mesh functions.
real(real64) function, public dmf_dotu_aux(f1, f2)
dot product between two vectors (mesh functions) without conjugation
real(real64) function, public dmf_dotp_aux(f1, f2)
dot product between two vectors (mesh functions)
subroutine, public mesh_init_mesh_aux(mesh)
Initialise a pointer to the grid/mesh, that is globally exposed, such that low level mesh operations ...
real(real64) function, public dmf_nrm2_aux(ff)
calculate norm2 of a vector (mesh function)
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:120
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:162
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
Definition: messages.F90:594
This module defines non-local operators.
subroutine, public dnl_operator_operate_diag(op, fo)
subroutine, public nl_operator_end(op)
subroutine, public dpoisson_solve(this, namespace, pot, rho, all_nodes, kernel, reset)
Calculates the Poisson equation. Given the density returns the corresponding potential.
Definition: poisson.F90:940
This module is intended to contain "only mathematical" functions and procedures.
Definition: solvers.F90:117
subroutine, public dqmr_sym_gen_dotu(np, x, b, op, dotu, nrm2, prec, iter, userdata, residue, threshold, showprogress, converged, use_initial_guess)
for complex symmetric matrices W Chen and B Poirier, J Comput Phys 219, 198-209 (2006)
Definition: solvers.F90:1810
General Sturm-Liouville solver for equations of the form .
subroutine, public sturm_liouville_solve_from_div(this, namespace, rho, f, v, psolver)
Solve the Sturm-Liouville equation from a divergence.
subroutine, public sturm_liouville_solve(this, namespace, rho, rhs, v)
Solve the Sturm-Liouville equation with a scalar right-hand side.
subroutine sl_operator(x, hx, userdata)
Computes .
subroutine, public sturm_liouville_solve_poisson(this, psolver, namespace, f, v, with_inverse_density, rho)
Solves Initial guess for the Sturm-Liouville solver: Poisson solution.
subroutine, public sturm_liouville_end(this)
Finalize the Sturm-Liouville solver.
subroutine, public sturm_liouville_init(this, namespace, gr, space, max_iter, thr, inverse_tol)
Initialize the Sturm-Liouville solver.
subroutine jacobi_preconditioner(x, hx, userdata)
Jacobi preconditioner: approximates .
static double f(double w, void *p)
int true(void)