Octopus
double_grid.F90
Go to the documentation of this file.
1!! Copyright (C) 2002-2006 X. Andrade
2!! Copyright (C) 2026 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
44 use debug_oct_m
45 use global_oct_m
46 use iso_c_binding
47 use, intrinsic :: iso_fortran_env
48 use math_oct_m
49 use mesh_oct_m
52!$ use omp_lib
53 use parser_oct_m
57
58 implicit none
59
60 private
61
62 public :: &
68
69 type double_grid_t
70 ! Components are public by default
71 logical :: enabled = .false.
72 integer :: order = 0
73 integer :: divisor = 0
74 integer :: nn = 0
75 integer :: interp_min = 0
76 integer :: interp_max = 0
81 real(real64), allocatable :: co(:, :)
82 end type double_grid_t
83
84contains
85
86 ! ---------------------------------------------------------
87 subroutine double_grid_init(this, namespace, dim)
88 type(double_grid_t), intent(out) :: this
89 type(namespace_t), intent(in) :: namespace
90 integer, intent(in) :: dim
91
92 integer :: aa, ll
93 real(real64), allocatable :: points(:)
94
95 push_sub(double_grid_init)
96
97 !%Variable DoubleGrid
98 !%Type logical
99 !%Default no
100 !%Section Mesh
101 !%Description
102 !% Enables the Ono-Hirose double-grid technique [T. Ono and K. Hirose,
103 !% <i>Phys. Rev. Lett.</i> <b>82</b>, 5016 (1999)]: localized functions of the
104 !% pseudopotentials (Kleinman-Bylander projectors, ultrasoft augmentation
105 !% charges) are evaluated on a fine grid and folded back onto the simulation
106 !% grid with the transpose of a high-order interpolation, at construction time.
107 !% This increases the accuracy of ultrasoft pseudopotentials on coarse grids
108 !% and reduces the egg-box effect. Only available in 3D.
109 !%End
110 if (dim == 3) then
111 call parse_variable(namespace, 'DoubleGrid', .false., this%enabled)
112 else
113 this%enabled = .false.
114 end if
115
116 if (.not. this%enabled) then
117 pop_sub(double_grid_init)
118 return
119 end if
120
121 call messages_experimental('DoubleGrid', namespace=namespace)
122
123 !%Variable DoubleGridOrder
124 !%Type integer
125 !%Default 9
126 !%Section Mesh
127 !%Description
128 !% Order of the Lagrange interpolation whose transpose is used by the
129 !% double-grid technique. Must be an odd number; the integration error
130 !% decreases as O(h^(order+1)). Low-order interpolation is not recommended.
131 !%End
132 call parse_variable(namespace, 'DoubleGridOrder', 9, this%order)
133 if (mod(this%order, 2) == 0 .or. this%order < 3) then
134 call messages_input_error(namespace, 'DoubleGridOrder', 'must be an odd number >= 3')
135 end if
136
137 !%Variable DoubleGridDivisor
138 !%Type integer
139 !%Default 3
140 !%Section Mesh
141 !%Description
142 !% Number of fine-grid points per coarse-grid spacing and axis used by the
143 !% double-grid technique. Must be an odd number.
144 !%End
145 call parse_variable(namespace, 'DoubleGridDivisor', 3, this%divisor)
146 if (mod(this%divisor, 2) == 0 .or. this%divisor < 3) then
147 call messages_input_error(namespace, 'DoubleGridDivisor', 'must be an odd number >= 3')
148 end if
149
150 this%nn = (this%divisor - 1)/2
151 this%interp_min = -this%order/2
152 this%interp_max = this%order/2 + 1
154 ! Lagrange weights of the coarse points at l*h, l = interp_min:interp_max,
155 ! for interpolation at the fine offset a*h/divisor, a = 1:nn.
156 ! In units of h/divisor the coarse points sit at divisor*l - a relative to
157 ! the fine point, so we evaluate the interpolant at 0.
158 safe_allocate(this%co(this%interp_min:this%interp_max, 1:this%nn))
159 safe_allocate(points(1:this%order + 1))
160 do aa = 1, this%nn
161 do ll = this%interp_min, this%interp_max
162 points(ll - this%interp_min + 1) = real(this%divisor*ll - aa, real64)
163 end do
164 call interpolation_coefficients(this%order + 1, points, m_zero, this%co(:, aa))
165 end do
166 safe_deallocate_a(points)
167
168 pop_sub(double_grid_init)
169 end subroutine double_grid_init
170
171 ! ---------------------------------------------------------
172 subroutine double_grid_end(this)
173 type(double_grid_t), intent(inout) :: this
174
175 push_sub(double_grid_end)
176
177 safe_deallocate_a(this%co)
178 this%enabled = .false.
179
180 pop_sub(double_grid_end)
181 end subroutine double_grid_end
182
183 ! ---------------------------------------------------------
188 real(real64) function double_grid_margin(this, mesh) result(margin)
189 type(double_grid_t), intent(in) :: this
190 class(mesh_t), intent(in) :: mesh
191
192 integer :: i1, i2, i3
193
194 margin = m_zero
195 if (.not. this%enabled) return
196
197 do i1 = -1, 1, 2
198 do i2 = -1, 1, 2
199 do i3 = -1, 1, 2
200 margin = max(margin, norm2(mesh%coord_system%to_cartesian(mesh%spacing(1:3)*[i1, i2, i3])))
201 end do
202 end do
203 end do
204 margin = this%interp_max*margin
205
206 end function double_grid_margin
207
208 ! ---------------------------------------------------------
218 subroutine double_grid_transform(this, sm, nf, spl, ll, mm, rcut, feff, cart_dir)
219 type(double_grid_t), intent(in) :: this
220 type(submesh_t), intent(in) :: sm
221 integer, intent(in) :: nf
222 type(spline_t), intent(in) :: spl(:)
223 integer, intent(in) :: ll(:)
224 integer, intent(in) :: mm(:)
225 real(real64), intent(in) :: rcut(:)
226 real(real64), intent(out) :: feff(:, :)
227 integer, optional, intent(in) :: cart_dir(:)
228
229 integer :: ip, jf
230 real(real64) :: ylm
231 real(real64), allocatable :: rcut_eff(:)
232 integer, allocatable :: cdir(:)
233
234 push_sub(double_grid_transform)
235
236 if (sm%np == 0) then
237 pop_sub(double_grid_transform)
238 return
239 end if
240
241 assert(sm%np > 0)
242 assert(size(feff, 1) >= sm%np)
243 assert(size(spl) >= nf .and. size(ll) >= nf .and. size(mm) >= nf .and. size(rcut) >= nf)
245 ! Never evaluate a spline beyond its radial range.
246 safe_allocate(rcut_eff(1:nf))
247 do jf = 1, nf
248 rcut_eff(jf) = min(rcut(jf), spline_range_max(spl(jf)))
249 end do
250
251 safe_allocate(cdir(1:nf))
252 cdir = 0
253 if (present(cart_dir)) cdir(1:nf) = cart_dir(1:nf)
254
255 if (.not. this%enabled) then
256
257 do jf = 1, nf
258 do ip = 1, sm%np
259 if (sm%r(ip) <= rcut_eff(jf)) then
260 feff(ip, jf) = spline_eval(spl(jf), sm%r(ip))
261 if (ll(jf) >= 0) then
262 call ylmr_real(sm%rel_x(1:3, ip), ll(jf), mm(jf), ylm)
263 feff(ip, jf) = feff(ip, jf)*ylm
264 end if
265 if (cdir(jf) > 0) feff(ip, jf) = feff(ip, jf)*sm%rel_x(cdir(jf), ip)
266 else
267 feff(ip, jf) = m_zero
268 end if
269 end do
270 end do
271
272 else
273
274 call double_grid_transform_kernel(this, sm, nf, spl, ll, mm, rcut_eff, cdir, feff)
275
276 end if
277
278 safe_deallocate_a(rcut_eff)
279 safe_deallocate_a(cdir)
280
281 pop_sub(double_grid_transform)
282 end subroutine double_grid_transform
283
284 ! ---------------------------------------------------------
296 subroutine double_grid_transform_kernel(this, sm, nf, spl, ll, mm, rcut, cdir, feff)
297 type(double_grid_t), intent(in) :: this
298 type(submesh_t), intent(in) :: sm
299 integer, intent(in) :: nf
300 type(spline_t), intent(in) :: spl(:)
301 integer, intent(in) :: ll(:)
302 integer, intent(in) :: mm(:)
303 real(real64), intent(in) :: rcut(:)
304 integer, intent(in) :: cdir(:)
305 real(real64), intent(out) :: feff(:, :)
306
307 integer :: ip, jf, idir, io, io1, io2, io3, il1
308 integer :: ia1, ia2, ia3, noff, ngrp, igrp, nthr, ithr
309 integer :: kmin(1:3), kmax(1:3), key(1:3)
310 integer, allocatable :: isub(:, :, :), offs(:, :), gfirst(:)
311 real(real64) :: hh(1:3), chi(1:3), rmax, anchor_reach, rel_ref(1:3)
312 real(real64), allocatable :: xstep(:, :), xoff(:, :), wgt(:, :), fbuf(:, :, :)
313 integer, allocatable :: tgt(:, :), ntgt(:)
314 type(c_ptr) :: acc1
315 type(c_ptr), allocatable :: acc(:)
316
317 call profiling_in("DOUBLE_GRID")
319
320 assert(.not. sm%mesh%use_curvilinear)
321 assert(sm%mesh%box%dim == 3)
322
323 hh(1:3) = sm%mesh%spacing(1:3)
324 rel_ref(1:3) = sm%rel_x(1:3, 1)
325 rmax = maxval(rcut(1:nf))
326
327 ! Integer keys of the local submesh points, relative to point 1, and their
328 ! bounding box. Coordinate differences are mapped to the primitive space,
329 ! where mesh points (and their periodic replicas) are exact multiples of
330 ! the spacing.
331 kmin = 0
332 kmax = 0
333 do ip = 1, sm%np
334 chi = sm%mesh%coord_system%from_cartesian(sm%rel_x(1:3, ip) - rel_ref(1:3))
335 key = nint(chi/hh)
336 assert(all(abs(chi/hh - key) < 1e-6_real64))
337 kmin = min(kmin, key)
338 kmax = max(kmax, key)
339 end do
340
341 safe_allocate(isub(kmin(1):kmax(1), kmin(2):kmax(2), kmin(3):kmax(3)))
342 isub = 0
343 do ip = 1, sm%np
344 chi = sm%mesh%coord_system%from_cartesian(sm%rel_x(1:3, ip) - rel_ref(1:3))
345 key = nint(chi/hh)
346 assert(isub(key(1), key(2), key(3)) == 0)
347 isub(key(1), key(2), key(3)) = ip
348 end do
349
350 ! Cartesian steps of one grid cell per axis, and of the fine offsets.
351 safe_allocate(xstep(1:3, 1:3))
352 do idir = 1, 3
353 chi = m_zero
354 chi(idir) = hh(idir)
355 xstep(1:3, idir) = sm%mesh%coord_system%to_cartesian(chi)
356 end do
357
358 noff = this%divisor**3
359 safe_allocate(offs(1:3, 1:noff))
360 safe_allocate(xoff(1:3, 1:noff))
361 io = 0
362 do io3 = -this%nn, this%nn
363 do io2 = -this%nn, this%nn
364 do io1 = -this%nn, this%nn
365 io = io + 1
366 offs(1:3, io) = [io1, io2, io3]
367 xoff(1:3, io) = (xstep(1:3, 1)*io1 + xstep(1:3, 2)*io2 + xstep(1:3, 3)*io3)/this%divisor
368 end do
369 end do
370 end do
371
372 ! A whole anchor site can be skipped when even its nearest fine point lies
373 ! beyond every support radius.
374 anchor_reach = rmax + 1e-12_real64
375 do io = 1, noff
376 anchor_reach = max(anchor_reach, rmax + norm2(xoff(1:3, io)) + 1e-12_real64)
377 end do
378
379 ! Per-axis scatter targets and weights for each fine offset o: the sites
380 ! l*sign(o) of the mirrored stencil with weights co(l, |o|); for o = 0 the
381 ! interpolation is exact at the site itself (single target, weight 1).
382 safe_allocate(tgt(1:this%order + 1, -this%nn:this%nn))
383 safe_allocate(wgt(1:this%order + 1, -this%nn:this%nn))
384 safe_allocate(ntgt(-this%nn:this%nn))
385 do io1 = -this%nn, this%nn
386 if (io1 == 0) then
387 ntgt(io1) = 1
388 tgt(1, io1) = 0
389 wgt(1, io1) = m_one
390 else
391 ntgt(io1) = this%order + 1
392 do il1 = this%interp_min, this%interp_max
393 tgt(il1 - this%interp_min + 1, io1) = il1*sign(1, io1)
394 wgt(il1 - this%interp_min + 1, io1) = this%co(il1, abs(io1))
395 end do
396 end if
397 end do
398
399 ! Consecutive functions sharing a radial spline and cutoff form a group, whose
400 ! spline is evaluated once per fine point.
401 safe_allocate(gfirst(1:nf + 1))
402 ngrp = 1
403 gfirst(1) = 1
404 do jf = 2, nf
405 if (spline_same(spl(jf), spl(jf - 1)) .and. abs(rcut(jf) - rcut(jf - 1)) < m_epsilon) cycle
406 ngrp = ngrp + 1
407 gfirst(ngrp) = jf
408 end do
409 gfirst(ngrp + 1) = nf + 1
410
411 nthr = 1
412!$ nthr = omp_get_max_threads()
413
414 ! Each thread owns a GSL accelerator per spline, as the shared ones are
415 ! modified on evaluation. The work split keeps the result deterministic
416 if (ngrp >= nthr) then
417
418 feff(1:sm%np, 1:nf) = m_zero
419 !$omp parallel do schedule(dynamic) private(ia1, ia2, ia3, acc1)
420 do igrp = 1, ngrp
421 call spline_accel_init(acc1)
422 do ia3 = kmin(3) - this%interp_max, kmax(3) + this%interp_max
423 do ia2 = kmin(2) - this%interp_max, kmax(2) + this%interp_max
424 do ia1 = kmin(1) - this%interp_max, kmax(1) + this%interp_max
425 call scatter_anchor(ia1, ia2, ia3, igrp, igrp, [acc1], feff)
426 end do
427 end do
428 end do
429 call spline_accel_end(acc1)
430 end do
431 !$omp end parallel do
432
433 else
434
435 safe_allocate(fbuf(1:sm%np, 1:nf, 0:nthr - 1))
436 !$omp parallel private(ithr, igrp, ia1, ia2, ia3, acc)
437 ithr = 0
438!$ ithr = omp_get_thread_num()
439 fbuf(1:sm%np, 1:nf, ithr) = m_zero
440 safe_allocate(acc(1:ngrp))
441 do igrp = 1, ngrp
442 call spline_accel_init(acc(igrp))
443 end do
444 !$omp do schedule(static) collapse(2)
445 do ia3 = kmin(3) - this%interp_max, kmax(3) + this%interp_max
446 do ia2 = kmin(2) - this%interp_max, kmax(2) + this%interp_max
447 do ia1 = kmin(1) - this%interp_max, kmax(1) + this%interp_max
448 call scatter_anchor(ia1, ia2, ia3, 1, ngrp, acc, fbuf(:, :, ithr))
449 end do
450 end do
451 end do
452 !$omp end do
453 do igrp = 1, ngrp
454 call spline_accel_end(acc(igrp))
455 end do
456 safe_deallocate_a(acc)
457 !$omp end parallel
458
459 feff(1:sm%np, 1:nf) = fbuf(1:sm%np, 1:nf, 0)
460 do ithr = 1, nthr - 1
461 feff(1:sm%np, 1:nf) = feff(1:sm%np, 1:nf) + fbuf(1:sm%np, 1:nf, ithr)
462 end do
463 safe_deallocate_a(fbuf)
464
465 end if
466
467 feff(1:sm%np, 1:nf) = feff(1:sm%np, 1:nf)/real(noff, real64)
468
469 safe_deallocate_a(isub)
470 safe_deallocate_a(offs)
471 safe_deallocate_a(xoff)
472 safe_deallocate_a(xstep)
473 safe_deallocate_a(tgt)
474 safe_deallocate_a(wgt)
475 safe_deallocate_a(ntgt)
476 safe_deallocate_a(gfirst)
477
479 call profiling_out("DOUBLE_GRID")
480
481 contains
482
485 subroutine scatter_anchor(ia1, ia2, ia3, ig1, ig2, accg, fout)
486 integer, intent(in) :: ia1, ia2, ia3, ig1, ig2
487 type(c_ptr), intent(in) :: accg(:)
488 real(real64), intent(inout) :: fout(:, :)
489
490 integer :: io, ig, jf, il1, il2, il3, it1, it2, it3
491 real(real64) :: xanchor(1:3), xfine(1:3), rr, rad, ylm, val, ww3, ww23
492
493 xanchor(1:3) = rel_ref(1:3) + xstep(1:3, 1)*ia1 + xstep(1:3, 2)*ia2 + xstep(1:3, 3)*ia3
494 if (norm2(xanchor) > anchor_reach) return
495
496 do io = 1, noff
497 xfine(1:3) = xanchor(1:3) + xoff(1:3, io)
498 rr = norm2(xfine)
499 if (rr > rmax) cycle
500
501 do ig = ig1, ig2
502 if (rr > rcut(gfirst(ig))) cycle
503 rad = spline_eval_accel(spl(gfirst(ig)), rr, accg(ig - ig1 + 1))
504
505 do jf = gfirst(ig), gfirst(ig + 1) - 1
506 val = rad
507 if (ll(jf) >= 0) then
508 call ylmr_real(xfine, ll(jf), mm(jf), ylm)
509 val = val*ylm
510 end if
511 if (cdir(jf) > 0) val = val*xfine(cdir(jf))
512 if (abs(val) <= m_epsilon) cycle
513
514 ! Scatter to the tensor-product stencil around the anchor.
515 do il3 = 1, ntgt(offs(3, io))
516 it3 = ia3 + tgt(il3, offs(3, io))
517 if (it3 < kmin(3) .or. it3 > kmax(3)) cycle
518 ww3 = wgt(il3, offs(3, io))*val
519 do il2 = 1, ntgt(offs(2, io))
520 it2 = ia2 + tgt(il2, offs(2, io))
521 if (it2 < kmin(2) .or. it2 > kmax(2)) cycle
522 ww23 = wgt(il2, offs(2, io))*ww3
523 do il1 = 1, ntgt(offs(1, io))
524 it1 = ia1 + tgt(il1, offs(1, io))
525 if (it1 < kmin(1) .or. it1 > kmax(1)) cycle
526 if (isub(it1, it2, it3) > 0) then
527 fout(isub(it1, it2, it3), jf) = fout(isub(it1, it2, it3), jf) &
528 + wgt(il1, offs(1, io))*ww23
529 end if
530 end do
531 end do
532 end do
533 end do
534 end do
535
536 end do
537 end subroutine scatter_anchor
538 end subroutine double_grid_transform_kernel
539
540end module double_grid_oct_m
541
542!! Local Variables:
543!! mode: f90
544!! coding: utf-8
545!! End:
subroutine scatter_anchor(ia1, ia2, ia3, ig1, ig2, accg, fout)
Scatter the fine points around the anchor site (ia1, ia2, ia3) of the function groups ig1:ig2 (accele...
The Ono-Hirose double-grid technique [T. Ono and K. Hirose, Phys. Rev. Lett. 82, 5016 (1999); Phys....
subroutine, public double_grid_transform(this, sm, nf, spl, ll, mm, rcut, feff, cart_dir)
Evaluate a batch of localized functions f_jf(x) = spl_jf(r) * Y_{ll,mm}(x) (or a purely radial spl_jf...
real(real64) function, public double_grid_margin(this, mesh)
Submesh radius enlargement needed to contain the effective support of a transformed function: the ten...
subroutine, public double_grid_end(this)
subroutine, public double_grid_init(this, namespace, dim)
subroutine double_grid_transform_kernel(this, sm, nf, spl, ll, mm, rcut, cdir, feff)
The transpose-interpolation scatter. All arithmetic is done with integer grid keys relative to the fi...
real(real64), parameter, public m_zero
Definition: global.F90:173
This module is intended to contain "only mathematical" functions and procedures.
Definition: math.F90:94
subroutine, public interpolation_coefficients(nn, xa, xx, cc)
Definition: math.F90:694
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:97
subroutine, public messages_input_error(namespace, var, details, row, column)
Definition: messages.F90:671
subroutine, public messages_experimental(name, namespace)
Definition: messages.F90:1020
/* To use the profiling module, call profiling_in and profiling_out with the label of the region as a...
Definition: profiling.F90:122
Describes mesh distribution to nodes.
Definition: mesh.F90:165