Octopus
nl_operator.F90
Go to the documentation of this file.
1!! Copyright (C) 2002-2006 M. Marques, A. Castro, A. Rubio, G. Bertsch
2!!
3!! This program is free software; you can redistribute it and/or modify
4!! it under the terms of the GNU General Public License as published by
5!! the Free Software Foundation; either version 2, or (at your option)
6!! any later version.
7!!
8!! This program is distributed in the hope that it will be useful,
9!! but WITHOUT ANY WARRANTY; without even the implied warranty of
10!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11!! GNU General Public License for more details.
12!!
13!! You should have received a copy of the GNU General Public License
14!! along with this program; if not, write to the Free Software
15!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16!! 02110-1301, USA.
17!!
18
19#include "global.h"
20
24 use accel_oct_m
25 use batch_oct_m
27 use debug_oct_m
28 use global_oct_m
29 use index_oct_m
30 use iso_c_binding
31 use math_oct_m
32 use mesh_oct_m
34 use mpi_oct_m
39 use parser_oct_m
41 use space_oct_m
42 use sort_oct_m
44 use types_oct_m
46
47 implicit none
48
49 private
50 public :: &
71
77 private
78 integer :: nri = 0
79 integer, allocatable :: imin(:)
80 integer, allocatable :: imax(:)
81 integer, allocatable :: ri(:, :)
82 integer, allocatable :: ri_pos(:,:,:)
83 integer, allocatable :: ri_neg(:,:,:)
84 end type nl_operator_index_t
85
86 integer, public, parameter :: &
87 OP_GENERAL = 1, &
88 op_symmetric = 2, &
90
92 type nl_operator_t
93 private
94 type(stencil_t), public :: stencil
95 type(mesh_t), pointer :: mesh => null()
96 integer, allocatable :: nn(:)
97 integer, public :: np = 0
98 ! When running in parallel mode, the next three arrays are unique on each node.
99 real(real64), allocatable, public :: w(:,:)
100
101 logical, public :: const_w = .true.
102
103 type(accel_mem_t), public :: buff_weights
104 type(accel_mem_t), public :: buff_half_weights
105
106 integer :: symmetry = op_general
107
108 character(len=40) :: label
109
111 integer, public :: nri = 0
112 integer, allocatable, public :: ri(:,:)
113 integer, allocatable, public :: rimap(:)
114 integer, allocatable, public :: rimap_inv(:)
115
117 integer :: npairs = 0
118 real(real64),allocatable :: wpair(:)
119 real(real64) :: wcenter
120 integer, allocatable :: ri_pos(:,:,:)
121 integer, allocatable :: ri_neg(:,:,:)
122 integer :: max_allocated_ri_pair = 0
123
124 integer :: ninner = 0
125 integer :: nouter = 0
126
127 type(nl_operator_index_t) :: inner
128 type(nl_operator_index_t) :: outer
129
130 type(accel_kernel_t) :: kernel
131 type(accel_mem_t) :: buff_imin
132 type(accel_mem_t) :: buff_imax
133 type(accel_mem_t) :: buff_ri
134 type(accel_mem_t) :: buff_map
135 type(accel_mem_t) :: buff_all
136 type(accel_mem_t) :: buff_inner
137 type(accel_mem_t) :: buff_outer
138 type(accel_mem_t) :: buff_stencil
139 type(accel_mem_t) :: buff_ip_to_xyz
140 type(accel_mem_t) :: buff_xyz_to_ip
141
143 type(accel_mem_t), public :: buff_wpair
144 type(accel_mem_t), public :: buff_half_wpair
145 type(accel_mem_t), public :: buff_ri_pos
146 type(accel_mem_t), public :: buff_ri_neg
147 type(accel_mem_t), public :: buff_map_sym
148 integer :: max_allocated_ri_pair_gpu = 0
149
150 ! For multigrid solvers
151 type(nl_operator_t), public, pointer :: coarser => null()
152
153 end type nl_operator_t
154
155 integer, parameter :: &
156 OP_FORTRAN = 0, &
157 op_vec = 1, &
158 op_min = op_fortran, &
159 op_max = op_vec
160
161 integer, parameter :: &
162 OP_INVMAP = 1, &
163 op_map = 2, &
164 op_nomap = 3
165
166 integer, public, parameter :: OP_ALL = 3, op_inner = 1, op_outer = 2
167
168 interface
169 integer function op_is_available(opid, type)
170 implicit none
171 integer, intent(in) :: opid, type
172 end function op_is_available
173 end interface
175 integer :: dfunction_global = -1
176 integer :: zfunction_global = -1
177 integer :: function_accel
178 logical :: use_symmetries = .false.
179
180contains
182 ! ---------------------------------------------------------
185 subroutine nl_operator_global_init(namespace)
186 type(namespace_t), intent(in) :: namespace
188 integer :: default
192 !%Variable OperateDouble
193 !%Type integer
194 !%Section Execution::Optimization
195 !%Default optimized
196 !%Description
197 !% This variable selects the subroutine used to apply non-local
198 !% operators over the grid for real functions.
199 !%Option fortran 0
200 !% The standard Fortran function.
201 !%Option optimized 1
202 !% This version is optimized using vector primitives (if available).
203 !%End
204
205 !%Variable OperateComplex
206 !%Type integer
207 !%Section Execution::Optimization
208 !%Default optimized
209 !%Description
210 !% This variable selects the subroutine used to apply non-local
211 !% operators over the grid for complex functions.
212 !%Option fortran 0
213 !% The standard Fortran function.
214 !%Option optimized 1
215 !% This version is optimized using vector primitives (if available).
216 !%End
218 default = op_vec
220 call parse_variable(namespace, 'OperateDouble', default, dfunction_global)
221 if (.not. varinfo_valid_option('OperateDouble', dfunction_global)) call messages_input_error(namespace, 'OperateDouble')
223 call parse_variable(namespace, 'OperateComplex', default, zfunction_global)
224 if (.not. varinfo_valid_option('OperateComplex', zfunction_global)) call messages_input_error(namespace, 'OperateComplex')
226 !%Variable OperateUseSymmetries
227 !%Type logical
228 !%Section Execution::Optimization
229 !%Default yes
230 !%Description
231 !% This variable selects if the operators are built using symmetries or not.
232 !% Ignored for curvilinear meshes.
233 !%End
234 call parse_variable(namespace, 'OperateUseSymmetries', .true., use_symmetries)
236 if (accel_is_enabled()) then
237
238 !%Variable OperateAccel
239 !%Type integer
240 !%Default map
241 !%Section Execution::Optimization
242 !%Description
243 !% This variable selects the subroutine used to apply non-local
244 !% operators over the grid when an accelerator device is used.
245 !%Option invmap 1
246 !% The standard implementation ported to GPUs.
247 !%Option map 2
248 !% A different version, more suitable for GPUs.
249 !%End
250 call parse_variable(namespace, 'OperateAccel', op_map, function_accel)
251
252 call messages_obsolete_variable(namespace, 'OperateOpenCL', 'OperateAccel')
253
254 end if
255
257 end subroutine nl_operator_global_init
258
259 ! ---------------------------------------------------------
260
262 push_sub(nl_operator_global_end)
263
265 end subroutine nl_operator_global_end
266
267 ! ---------------------------------------------------------
271 subroutine nl_operator_init(op, label, symm)
272 type(nl_operator_t), intent(inout) :: op
273 character(len=*), intent(in) :: label
274 integer, optional, intent(in) :: symm
275
276 push_sub(nl_operator_init)
277
278 op%label = label
279 op%symmetry = op_general
281 op%symmetry = optional_default(symm, op_general)
282 end if
283
284 pop_sub(nl_operator_init)
285 end subroutine nl_operator_init
286
287 ! ---------------------------------------------------------
297 subroutine nl_operator_build(space, mesh, op, np, const_w, regenerate)
298 class(space_t), intent(in) :: space
299 type(mesh_t), target, intent(in) :: mesh
300 type(nl_operator_t), intent(inout) :: op
301 integer, intent(in) :: np
302 logical, optional, intent(in) :: const_w
303 logical, optional, intent(in) :: regenerate
304
305 integer :: ii, jj, p1(space%dim), time, current
306 integer, allocatable :: st1(:), st2(:), st1r(:)
307 integer :: ir, maxp, iinner, iouter
308 logical :: change, force_change
309 character(len=200) :: flags
310 integer, allocatable :: inner_points(:), outer_points(:), all_points(:)
311
312 push_sub(nl_operator_build)
313
314 op%const_w = optional_default(const_w, .false.)
315 if (mesh%parallel_in_domains .and. .not. op%const_w) then
316 call messages_experimental('Domain parallelization with curvilinear coordinates')
317 end if
318
319 assert(np > 0)
320
321 ! store values in structure
322 op%np = np
323 op%mesh => mesh
324 if (.not. optional_default(regenerate, .false.)) then
325 op%const_w = optional_default(const_w, .false.)
326 end if
327
328 ! grouping points by symmetric pairs requires constant weights
329 if (.not. op%const_w) op%symmetry = op_general
330
331 ! allocate weights op%w
332 if (op%const_w) then
333 safe_allocate(op%w(1:op%stencil%size, 1))
334 message(1) = 'Debug: nl_operator_build: working with constant weights.'
335 call messages_info(1, debug_only=.true.)
336 else
337 safe_allocate(op%w(1:op%stencil%size, 1:op%np))
338 message(1) = 'Debug: nl_operator_build: working with non-constant weights.'
339 call messages_info(1, debug_only=.true.)
340 end if
341
342 ! set initially to zero
343 op%w = m_zero
344
345 ! Build lookup table
346 safe_allocate(st1(1:op%stencil%size))
347 safe_allocate(st1r(1:op%stencil%size))
348 safe_allocate(st2(1:op%stencil%size))
349
350 op%nri = 0
351 do time = 1, 2
352 st2 = 0
353 do ii = 1, np
354 p1 = 0
355 call mesh_local_index_to_coords(mesh, ii, p1)
357 do jj = 1, op%stencil%size
358 ! Get local index of p1 plus current stencil point.
359 st1(jj) = mesh_local_index_from_coords(mesh, p1 + op%stencil%points(:, jj))
360
361 assert(st1(jj) > 0)
362 end do
363
364 st1(1:op%stencil%size) = st1(1:op%stencil%size) - ii
365
366 change = any(st1 /= st2)
367
368 !the next is to detect when we move from a point that does not
369 !have boundary points as neighbours to one that has
370 force_change = any(st1 + ii > mesh%np) .and. all(st2 + ii - 1 <= mesh%np)
371
372 ! if the stencil changes
373 if (change .or. force_change) then
374 !store it
375 st2(:) = st1(:)
376
377 !first time, just count
378 if (time == 1) op%nri = op%nri + 1
379
380 !second time, store
381 if (time == 2) then
382 current = current + 1
383 op%ri(1:op%stencil%size, current) = st1(1:op%stencil%size)
384 end if
385 end if
386
387 if (time == 2) op%rimap(ii) = current
388
389 end do
390
391 !after counting, allocate
392 if (time == 1) then
393 safe_deallocate_a(op%ri)
394 safe_deallocate_a(op%rimap)
395 safe_deallocate_a(op%rimap_inv)
396
397 safe_allocate(op%ri(1:op%stencil%size, 1:op%nri))
398 safe_allocate(op%rimap(1:op%np))
399 safe_allocate(op%rimap_inv(1:op%nri + 1))
400 op%ri = 0
401 op%rimap = 0
402 op%rimap_inv = 0
403 current = 0
404
405 ! the sizes
406 if (mesh%use_curvilinear) then
407 safe_allocate(op%nn(1:op%nri))
408 ! for the moment all the sizes are the same
409 op%nn = op%stencil%size
410 end if
411 end if
412
413 end do
414
415 !the inverse mapping
416 op%rimap_inv(1) = 0
417 do jj = 1, op%np
418 op%rimap_inv(op%rimap(jj) + 1) = jj
419 end do
420 op%rimap_inv(op%nri + 1) = op%np
421
422 safe_deallocate_a(st1)
423 safe_deallocate_a(st1r)
424 safe_deallocate_a(st2)
425
426 if (op%mesh%parallel_in_domains) then
427 !now build the arrays required to apply the nl_operator by parts
428
429 !count points
430 op%inner%nri = 0
431 op%outer%nri = 0
432 do ir = 1, op%nri
433 maxp = op%rimap_inv(ir + 1) + maxval(op%ri(1:op%stencil%size, ir))
434 if (maxp <= np) then
435 !inner point
436 op%inner%nri = op%inner%nri + 1
437 assert(op%inner%nri <= op%nri)
438 else
439 !outer point
440 op%outer%nri = op%outer%nri + 1
441 assert(op%outer%nri <= op%nri)
442 end if
443 end do
444
445 assert(op%inner%nri + op%outer%nri == op%nri)
446
447 if (optional_default(regenerate, .false.)) then
448 safe_deallocate_a(op%inner%imin)
449 safe_deallocate_a(op%inner%imax)
450 safe_deallocate_a(op%inner%ri)
451 safe_deallocate_a(op%outer%imin)
452 safe_deallocate_a(op%outer%imax)
453 safe_deallocate_a(op%outer%ri)
454 end if
455 safe_allocate(op%inner%imin(1:op%inner%nri + 1))
456 safe_allocate(op%inner%imax(1:op%inner%nri))
457 safe_allocate(op%inner%ri(1:op%stencil%size, 1:op%inner%nri))
458
459 safe_allocate(op%outer%imin(1:op%outer%nri + 1))
460 safe_allocate(op%outer%imax(1:op%outer%nri))
461 safe_allocate(op%outer%ri(1:op%stencil%size, 1:op%outer%nri))
462
463 !now populate the arrays
464 iinner = 0
465 iouter = 0
466 do ir = 1, op%nri
467 maxp = op%rimap_inv(ir + 1) + maxval(op%ri(1:op%stencil%size, ir))
468 if (maxp <= np) then
469 !inner point
470 iinner = iinner + 1
471 op%inner%imin(iinner) = op%rimap_inv(ir)
472 op%inner%imax(iinner) = op%rimap_inv(ir + 1)
473 op%inner%ri(1:op%stencil%size, iinner) = op%ri(1:op%stencil%size, ir)
474 else
475 !outer point
476 iouter = iouter + 1
477 op%outer%imin(iouter) = op%rimap_inv(ir)
478 op%outer%imax(iouter) = op%rimap_inv(ir + 1)
479 op%outer%ri(1:op%stencil%size, iouter) = op%ri(1:op%stencil%size, ir)
480 end if
481 end do
482
483 !verify that all points in the inner operator are actually inner
484 do ir = 1, op%inner%nri
485 do ii = op%inner%imin(ir) + 1, op%inner%imax(ir)
486 assert(all(ii + op%inner%ri(1:op%stencil%size, ir) <= mesh%np))
487 end do
488 end do
489
490 end if
491
492 if (accel_is_enabled() .and. op%const_w) then
493
494 write(flags, '(i5)') op%stencil%size
495 flags='-DNDIM=3 -DSTENCIL_SIZE='//trim(adjustl(flags))
496
497 if (op%symmetry /= op_general) then
498 block
499 character(len=16) :: npairs_str
500 write(npairs_str, '(i0)') op%stencil%size/2
501 flags = trim(flags)//' -DNPAIRS='//trim(adjustl(npairs_str))
502 end block
503 end if
504
505 if (op%mesh%parallel_in_domains) flags = '-DINDIRECT '//trim(flags)
506
507 select case (function_accel)
508 case (op_invmap)
509 if (op%symmetry /= op_general) then
510 call messages_not_implemented("OperateUseSymmetries=yes with OperateAccel=invmap")
511 end if
512 call accel_kernel_build(op%kernel, 'operate.cu', 'operate', flags)
513 case (op_map)
514 select case (op%symmetry)
515 case (op_general)
516 call accel_kernel_build(op%kernel, 'operate.cu', 'operate_map', flags)
517 case (op_symmetric)
518 call accel_kernel_build(op%kernel, 'operate.cu', 'operate_map_sym', flags)
519 case (op_antisymmetric)
520 call accel_kernel_build(op%kernel, 'operate.cu', 'operate_map_antisym', flags)
521 end select
522 end select
523
524 ! conversion to i8 needed to avoid integer overflow
525 call accel_create_buffer(op%buff_ri, accel_mem_read_only, type_integer, int(op%nri, int64)*op%stencil%size)
526 call accel_write_buffer(op%buff_ri, op%stencil%size, op%nri, op%ri)
527
528 select case (function_accel)
529 case (op_invmap)
530 call accel_create_buffer(op%buff_imin, accel_mem_read_only, type_integer, op%nri)
531 call accel_write_buffer(op%buff_imin, op%nri, op%rimap_inv(1:))
532 call accel_create_buffer(op%buff_imax, accel_mem_read_only, type_integer, op%nri)
533 call accel_write_buffer(op%buff_imax, op%nri, op%rimap_inv(2:))
534
535 case (op_map)
536
538 call accel_write_buffer(op%buff_map, op%mesh%np, (op%rimap - 1)*op%stencil%size)
539
540 if (op%symmetry /= op_general) then
541 call accel_create_buffer(op%buff_map_sym, accel_mem_read_only, type_integer, pad(op%mesh%np, accel_max_block_size()))
542 call accel_write_buffer(op%buff_map_sym, op%mesh%np, (op%rimap - 1)*(op%stencil%size/2))
543 end if
544
545 if (op%mesh%parallel_in_domains) then
546
547 safe_allocate(inner_points(1:op%mesh%np))
548 safe_allocate(outer_points(1:op%mesh%np))
549 safe_allocate(all_points(1:op%mesh%np))
550
551 op%ninner = 0
552 op%nouter = 0
553
554 do ii = 1, op%mesh%np
555 all_points(ii) = ii - 1
556 maxp = ii + maxval(op%ri(1:op%stencil%size, op%rimap(ii)))
557 if (maxp <= op%mesh%np) then
558 op%ninner = op%ninner + 1
559 inner_points(op%ninner) = ii - 1
560 else
561 op%nouter = op%nouter + 1
562 outer_points(op%nouter) = ii - 1
563 end if
564 end do
565
567 call accel_write_buffer(op%buff_all, op%mesh%np, all_points)
568
570 call accel_write_buffer(op%buff_inner, op%ninner, inner_points)
571
573 call accel_write_buffer(op%buff_outer, op%nouter, outer_points)
574
575 safe_deallocate_a(inner_points)
576 safe_deallocate_a(outer_points)
577 safe_deallocate_a(all_points)
578
579 end if
580 end select
581 end if
582
583 pop_sub(nl_operator_build)
584
585 end subroutine nl_operator_build
586
587 ! ---------------------------------------------------------
588 subroutine nl_operator_output_weights(this)
589 type(nl_operator_t), intent(inout) :: this
590
591 integer :: istencil, idir
592
594
595 write(message(1), '(3a)') 'Debug info: Finite difference weights for ', trim(this%label), '.'
596 write(message(2), '(a)') ' Spacing:'
597 do idir = 1, this%mesh%box%dim
598 write(message(2), '(a,f16.8)') trim(message(2)), this%mesh%spacing(idir)
599 end do
600 call messages_info(2, debug_only=.true.)
601
602 do istencil = 1, this%stencil%size
603 select case(this%mesh%box%dim)
604 case(1)
605 write(message(1), '(a,i3,1i4,f25.10)') ' ', istencil, this%stencil%points(1:1, istencil), this%w(istencil, 1)
606 case(2)
607 write(message(1), '(a,i3,2i4,f25.10)') ' ', istencil, this%stencil%points(1:2, istencil), this%w(istencil, 1)
608 case(3)
609 write(message(1), '(a,i3,3i4,f25.10)') ' ', istencil, this%stencil%points(1:3, istencil), this%w(istencil, 1)
610 end select
611 call messages_info(1, debug_only=.true.)
612 end do
613
615
616 end subroutine nl_operator_output_weights
617
618 ! ---------------------------------------------------------
619 subroutine nl_operator_end(op)
620 type(nl_operator_t), intent(inout) :: op
621
622 push_sub(nl_operator_end)
623
624 if (accel_is_enabled() .and. op%const_w) then
626 end if
627
628 safe_deallocate_a(op%inner%imin)
629 safe_deallocate_a(op%inner%imax)
630 safe_deallocate_a(op%inner%ri)
631 safe_deallocate_a(op%outer%imin)
632 safe_deallocate_a(op%outer%imax)
633 safe_deallocate_a(op%outer%ri)
634
635 safe_deallocate_a(op%w)
636
637 safe_deallocate_a(op%ri)
638 safe_deallocate_a(op%rimap)
639 safe_deallocate_a(op%rimap_inv)
640 safe_deallocate_a(op%nn)
641
642 safe_deallocate_a(op%wpair)
643 safe_deallocate_a(op%ri_pos)
644 safe_deallocate_a(op%ri_neg)
645
646 safe_deallocate_a(op%inner%ri_pos)
647 safe_deallocate_a(op%inner%ri_neg)
648 safe_deallocate_a(op%outer%ri_pos)
649 safe_deallocate_a(op%outer%ri_neg)
650 call stencil_end(op%stencil)
651
652 pop_sub(nl_operator_end)
653 end subroutine nl_operator_end
654
655
656 subroutine nl_operator_clear_gpu_buffers(op)
657 type(nl_operator_t), intent(inout) :: op
658
660
661 call accel_free_buffer(op%buff_ri)
662 select case (function_accel)
663 case (op_invmap)
664 call accel_free_buffer(op%buff_imin)
665 call accel_free_buffer(op%buff_imax)
666
667 case (op_map)
668 call accel_free_buffer(op%buff_map)
669 if (op%symmetry /= op_general) then
670 call accel_free_buffer(op%buff_map_sym)
671 end if
672 if (op%mesh%parallel_in_domains) then
673 call accel_free_buffer(op%buff_all)
674 call accel_free_buffer(op%buff_inner)
675 call accel_free_buffer(op%buff_outer)
676 end if
677
678 case (op_nomap)
679 call accel_free_buffer(op%buff_map)
680 call accel_free_buffer(op%buff_stencil)
681 call accel_free_buffer(op%buff_xyz_to_ip)
682 call accel_free_buffer(op%buff_ip_to_xyz)
683 end select
684
685 call accel_free_buffer(op%buff_weights)
686 call accel_free_buffer(op%buff_half_weights)
687
688 if (op%symmetry /= op_general) then
689 call accel_free_buffer(op%buff_wpair)
690 call accel_free_buffer(op%buff_half_wpair)
691 if (op%max_allocated_ri_pair_gpu > 0) then
692 call accel_free_buffer(op%buff_ri_pos)
693 call accel_free_buffer(op%buff_ri_neg)
694 op%max_allocated_ri_pair_gpu = 0
695 end if
696 end if
697
699 end subroutine nl_operator_clear_gpu_buffers
700
701 ! ---------------------------------------------------------
702 integer pure function nl_operator_get_index(op, is, ip) result(res)
703 type(nl_operator_t), intent(in) :: op
704 integer, intent(in) :: is
705 integer, intent(in) :: ip
706
707 res = ip + op%ri(is, op%rimap(ip))
708 end function nl_operator_get_index
709
710 ! ---------------------------------------------------------
711
713 type(nl_operator_t), intent(inout) :: op
716
717 ! Update the GPU weights
718 if (accel_is_enabled() .and. op%const_w) then
719 call accel_create_buffer(op%buff_weights, accel_mem_read_only, type_float, op%stencil%size)
720 call accel_create_buffer(op%buff_half_weights, accel_mem_read_only, type_float, op%stencil%size)
721 if (op%symmetry /= op_general) then
722 call accel_create_buffer(op%buff_wpair, accel_mem_read_only, type_float, op%stencil%size/2)
723 call accel_create_buffer(op%buff_half_wpair, accel_mem_read_only, type_float, op%stencil%size/2)
724 end if
725 end if
726
729
730
731 ! ---------------------------------------------------------
732
733 subroutine nl_operator_update_gpu_buffers(op)
734 type(nl_operator_t), intent(inout) :: op
735
736 integer(int64) :: buf_size
737
739
740 ! Update the GPU weights
741 if (accel_is_enabled() .and. op%const_w) then
742 call accel_write_buffer(op%buff_weights, op%stencil%size, op%w(:, 1))
743 call accel_write_buffer(op%buff_half_weights, op%stencil%size, -m_half*op%w(:, 1))
744
745 if (op%symmetry /= op_general) then
746 call accel_write_buffer(op%buff_wpair, op%npairs, op%wpair)
747 call accel_write_buffer(op%buff_half_wpair, op%npairs, -m_half*op%wpair)
748
749 ! (Re)allocate the pair-index buffers if max_allocated_ri_pair has grown
750 if (op%max_allocated_ri_pair > op%max_allocated_ri_pair_gpu) then
751 if (op%max_allocated_ri_pair_gpu > 0) then
752 call accel_free_buffer(op%buff_ri_pos)
753 call accel_free_buffer(op%buff_ri_neg)
754 end if
755 buf_size = int(op%npairs, int64)*op%nri*op%max_allocated_ri_pair
756 call accel_create_buffer(op%buff_ri_pos, accel_mem_read_only, type_integer, buf_size)
757 call accel_create_buffer(op%buff_ri_neg, accel_mem_read_only, type_integer, buf_size)
758 op%max_allocated_ri_pair_gpu = op%max_allocated_ri_pair
759 end if
760
761 if (op%max_allocated_ri_pair > 0) then
762 call accel_write_buffer(op%buff_ri_pos, op%npairs, op%nri, op%max_allocated_ri_pair, op%ri_pos)
763 call accel_write_buffer(op%buff_ri_neg, op%npairs, op%nri, op%max_allocated_ri_pair, op%ri_neg)
764 end if
765 end if
766 end if
767
769 end subroutine nl_operator_update_gpu_buffers
770
771 ! ---------------------------------------------------------
772
773 integer pure function nl_operator_np_zero_bc(op) result(np_bc)
774 type(nl_operator_t), intent(in) :: op
775
776 integer :: jj, ii
777
778 np_bc = 0
779 do jj = 1, op%nri
780 ii = op%rimap_inv(jj + 1) + maxval(op%ri(1:op%stencil%size, jj))
781 np_bc = max(np_bc, ii)
782 end do
783
784 end function nl_operator_np_zero_bc
785
786
787 ! ---------------------------------------------------------
789 subroutine nl_operator_remove_zero_weight_points(op, space, mesh)
790 type(nl_operator_t), intent(inout) :: op
791 type(space_t), intent(in) :: space
792 class(mesh_t), intent(in) :: mesh
793
794 integer :: ip, size
795 real(real64), parameter :: tol = 1.0e-14_real64
796 real(real64) :: max_weight, new_w(op%stencil%size)
797 integer :: new_points(space%dim, op%stencil%size)
798
799 if (.not. op%const_w) return
800
802
803 max_weight = maxval(abs(op%w(:, 1)))
804 size = 0
805 do ip = 1, op%stencil%size
806 if (abs(op%w(ip, 1)) > tol * max_weight) then
807 size = size +1
808 new_w(size) = op%w(ip, 1)
809 new_points(:,size) = op%stencil%points(:, ip)
810 end if
811 end do
812
813 ! We regenerate the stencil without the zero-weight points
814 op%stencil%size = size
815 safe_deallocate_a(op%stencil%points)
816 safe_allocate(op%stencil%points(space%dim, op%stencil%size))
817 op%stencil%points(:, :) = new_points(:, 1:size)
818 safe_deallocate_a(op%w)
820 call nl_operator_build(space, mesh, op, mesh%np, const_w=op%const_w, regenerate=.true.)
821 op%w(1:size, 1) = new_w(1:size)
822
823 !Update Stencil%center
824 call stencil_init_center(op%stencil)
825
830 subroutine group_by_pairs_sym(size, ldf, offsets, wre, ri, nri, npairs, wpair, pair_pos, pair_neg, wcenter)
831 integer, intent(in) :: size
832 integer, intent(in) :: ldf
833 integer, intent(in) :: offsets(:, :)
834 real(real64), intent(in) :: wre(:)
835 integer, intent(in) :: ri(:, :)
836 integer, intent(in) :: nri
837 integer, intent(out) :: npairs
838 real(real64), intent(inout) :: wpair(:)
839 integer, intent(inout) :: pair_pos(:,:), pair_neg(:,:)
840 real(real64), intent(out) :: wcenter
841
842 logical, allocatable :: used(:)
843 integer :: i, j, ndim, s
844 logical :: same
845 integer, allocatable :: idx(:)
846
847 real(real64), parameter :: tol = 1.0e-11_real64
848
849 push_sub(group_by_pairs_sym)
850
851 assert(mod(size,2) == 1)
852
853 safe_allocate(used(1:size))
854 used = .false.
855 npairs = 0
856
857 ndim = ubound(offsets, dim=1)
858
859 safe_allocate(idx(1:size))
860 call robust_sort_by_abs(wre, offsets, idx)
861
862 do i = 1, size
863 if (used(i)) cycle
864
865 if (all(offsets(:, idx(i))==0)) then
866 wcenter = wre(idx(i))
867 used(i) = .true.
868 cycle
869 end if
870
871 ! Try to find symmetric partner j
872 do j = i+1, size
873 if (used(j)) cycle
874
875 ! Weight equality
876 same = abs(wre(idx(i)) - wre(idx(j))) <= tol*max(m_one, abs(wre(idx(i))))
877 if (.not. same) cycle
878
879 ! Offsets equal and opposite
880 if (any(offsets(:,idx(j))+offsets(:, idx(i)) /= 0)) cycle
881
882 npairs = npairs + 1
883 do s = 1, nri
884 pair_pos(npairs, s) = ri(idx(i), s) * 2**ldf
885 pair_neg(npairs, s) = ri(idx(j), s) * 2**ldf
886 end do
887 wpair(npairs) = m_half*(wre(idx(i)) + wre(idx(j)))
888
889 used(i) = .true.
890 used(j) = .true.
891 exit
892 end do
893 end do
894
895 assert(npairs == size/2)
896
897 safe_deallocate_a(idx)
898
899 pop_sub(group_by_pairs_sym)
900 end subroutine group_by_pairs_sym
901
903 subroutine group_by_pairs_antisym(size, ldf, offsets, wre, ri, nri, npairs, wpair, pair_pos, pair_neg)
904 integer, intent(in) :: size
905 integer, intent(in) :: ldf
906 integer, intent(in) :: offsets(:, :)
907 real(real64), intent(in) :: wre(:)
908 integer, intent(in) :: ri(:, :)
909 integer, intent(in) :: nri
910 integer, intent(out) :: npairs
911 real(real64), intent(inout) :: wpair(:)
912 integer, intent(inout) :: pair_pos(:,:), pair_neg(:,:)
913
914 logical, allocatable :: used(:)
915 integer :: i, j, ndim, s
916 logical :: same
917 integer, allocatable :: idx(:)
918
919 real(real64), parameter :: tol = 1.0e-11_real64
920
921 push_sub(group_by_pairs_antisym)
922
923 assert(mod(size,2) == 0)
924
925 safe_allocate(used(1:size))
926 used = .false.
927 npairs = 0
928
929 ndim = ubound(offsets, dim=1)
930
931 safe_allocate(idx(1:size))
932 call robust_sort_by_abs(wre, offsets, idx)
933
934 ! Max pairs = n/2
935 do i = 1, size
936 if (used(i)) cycle
937
938 ! Try to find symmetric partner j
939 do j = i+1, size
940 if (used(j)) cycle
941
942 ! Weight equality
943 same = abs(wre(idx(i)) + wre(idx(j))) <= tol*max(m_one, abs(wre(idx(i))))
944 if (.not. same) cycle
945
946 ! Offsets equal and opposite
947 if (any(offsets(:,idx(j))+offsets(:, idx(i)) /= 0)) cycle
948
949 npairs = npairs + 1
950 do s = 1, nri
951 pair_pos(npairs, s) = ri(idx(i), s) * 2**ldf
952 pair_neg(npairs, s) = ri(idx(j), s) * 2**ldf
953 end do
954 wpair(npairs) = m_half*(wre(idx(i)) - wre(idx(j)))
955
956 used(i) = .true.
957 used(j) = .true.
958 exit
959 end do
960 end do
961
962 assert(npairs == size/2)
963
964 safe_deallocate_a(idx)
966 end subroutine group_by_pairs_antisym
967
969 subroutine nl_operator_build_symmetric_weights(op, max_size)
970 type(nl_operator_t), intent(inout) :: op
971 integer, optional, intent(in) :: max_size
972
973 integer :: ldf, start, end, ipair
974
975 if (op%symmetry == op_general) return
976
978
979 assert(op%const_w)
980
981 if(present(max_size)) then
982 start = op%max_allocated_ri_pair + 1
983 end = max_size
984 call reallocate_array(op%ri_pos, op%stencil%size/2, op%nri, op%max_allocated_ri_pair, end)
985 call reallocate_array(op%ri_neg, op%stencil%size/2, op%nri, op%max_allocated_ri_pair, end)
986 if (op%mesh%parallel_in_domains) then
987 call reallocate_array(op%inner%ri_pos, op%stencil%size/2, op%inner%nri, op%max_allocated_ri_pair, end)
988 call reallocate_array(op%inner%ri_neg, op%stencil%size/2, op%inner%nri, op%max_allocated_ri_pair, end)
989 call reallocate_array(op%outer%ri_pos, op%stencil%size/2, op%outer%nri, op%max_allocated_ri_pair, end)
990 call reallocate_array(op%outer%ri_neg, op%stencil%size/2, op%outer%nri, op%max_allocated_ri_pair, end)
991 end if
992 else if (allocated(op%wpair)) then
993 ! The weights op%w have been modified: recompute the pairs for all allocated levels
994 start = 1
995 end = op%max_allocated_ri_pair
996 else
997 ! Max pairs = n/2
998 safe_allocate(op%wpair(1:op%stencil%size/2))
999 start = 1
1000 ! conf%target_states_block_size is only set for electron systems
1001 if (conf%target_states_block_size > 0) then
1002 end = log2(conf%target_states_block_size)+1
1003 else
1004 end = 1
1005 end if
1006
1007 safe_allocate(op%ri_pos(1:op%stencil%size/2, 1:op%nri, 1:end))
1008 safe_allocate(op%ri_neg(1:op%stencil%size/2, 1:op%nri, 1:end))
1009 if (op%mesh%parallel_in_domains) then
1010 safe_allocate(op%inner%ri_pos(1:op%stencil%size/2, 1:op%inner%nri, 1:end))
1011 safe_allocate(op%inner%ri_neg(1:op%stencil%size/2, 1:op%inner%nri, 1:end))
1012 safe_allocate(op%outer%ri_pos(1:op%stencil%size/2, 1:op%outer%nri, 1:end))
1013 safe_allocate(op%outer%ri_neg(1:op%stencil%size/2, 1:op%outer%nri, 1:end))
1014 end if
1015 end if
1016 op%max_allocated_ri_pair = end
1017
1018 do ldf = start-1, end-1
1019 select case(op%symmetry)
1020 case(op_symmetric)
1021 call group_by_pairs_sym(op%stencil%size, ldf, op%stencil%points, op%w(:,1), op%ri, op%nri, &
1022 op%npairs, op%wpair, op%ri_pos(:,:,ldf+1), op%ri_neg(:,:,ldf+1), op%wcenter)
1023 if (op%mesh%parallel_in_domains) then
1024 call group_by_pairs_sym(op%stencil%size, ldf, op%stencil%points, op%w(:,1), op%inner%ri, op%inner%nri, &
1025 op%npairs, op%wpair, op%inner%ri_pos(:,:,ldf+1), op%inner%ri_neg(:,:,ldf+1), op%wcenter)
1026 call group_by_pairs_sym(op%stencil%size, ldf, op%stencil%points, op%w(:,1), op%outer%ri, op%outer%nri, &
1027 op%npairs, op%wpair, op%outer%ri_pos(:,:,ldf+1), op%outer%ri_neg(:,:,ldf+1), op%wcenter)
1028 end if
1029 case(op_antisymmetric)
1030 call group_by_pairs_antisym(op%stencil%size, ldf, op%stencil%points, op%w(:,1), op%ri, op%nri, &
1031 op%npairs, op%wpair, op%ri_pos(:,:,ldf+1), op%ri_neg(:,:,ldf+1))
1032 if (op%mesh%parallel_in_domains) then
1033 call group_by_pairs_antisym(op%stencil%size, ldf, op%stencil%points, op%w(:,1), op%inner%ri, op%inner%nri, &
1034 op%npairs, op%wpair, op%inner%ri_pos(:,:,ldf+1), op%inner%ri_neg(:,:,ldf+1))
1035 call group_by_pairs_antisym(op%stencil%size, ldf, op%stencil%points, op%w(:,1), op%outer%ri, op%outer%nri, &
1036 op%npairs, op%wpair, op%outer%ri_pos(:,:,ldf+1), op%outer%ri_neg(:,:,ldf+1))
1037 end if
1038 end select
1039 end do
1040
1041 if (.not. present(max_size)) then
1042 write(message(1), '(3a)') 'Debug info: Sorted weights for ', trim(op%label), '.'
1043 call messages_info(1, debug_only=.true.)
1044
1045 do ipair = 1, op%npairs
1046 write(message(1), '(a,i3,f25.10,2(1x,i4))') ' ', ipair, op%wpair(ipair), op%ri_pos(ipair,1,1), op%ri_neg(ipair,1,1)
1047 call messages_info(1, debug_only=.true.)
1048 end do
1049 end if
1050
1053
1055 subroutine reallocate_array(ri, stencil_size, nri, old_size, new_size)
1056 integer, allocatable, intent(inout) :: ri(:,:,:)
1057 integer, intent(in) :: stencil_size, nri
1058 integer, intent(in) :: old_size, new_size
1059
1060 integer, allocatable :: tmp(:,:,:)
1061
1062 safe_allocate_source_a(tmp, ri)
1063 safe_deallocate_a(ri)
1064 safe_allocate(ri(1:stencil_size, 1:nri, 1:new_size))
1065 ri(:,:,1:old_size) = tmp
1066 safe_deallocate_a(tmp)
1067 end subroutine reallocate_array
1068
1069#include "undef.F90"
1070#include "real.F90"
1071#include "nl_operator_inc.F90"
1072
1073#include "undef.F90"
1074#include "complex.F90"
1075#include "nl_operator_inc.F90"
1076
1077end module nl_operator_oct_m
1078
1079!! Local Variables:
1080!! mode: f90
1081!! coding: utf-8
1082!! End:
subroutine, public accel_kernel_build(this, file_name, kernel_name, flags)
Compile the program that contains a given kernel.
Definition: accel.F90:1657
pure logical function, public accel_is_enabled()
Definition: accel.F90:403
integer, parameter, public accel_mem_read_only
Definition: accel.F90:187
integer pure function, public accel_max_block_size()
Definition: accel.F90:1204
This module implements batches of mesh functions.
Definition: batch.F90:135
Module implementing boundary conditions in Octopus.
Definition: boundaries.F90:124
real(real64), parameter, public m_zero
Definition: global.F90:200
This module implements the index, used for the mesh points.
Definition: index.F90:124
This module is intended to contain "only mathematical" functions and procedures.
Definition: math.F90:117
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:120
integer function, public mesh_local_index_from_coords(mesh, ix)
This function returns the local index of the point for a given vector of integer coordinates.
Definition: mesh.F90:939
subroutine, public mesh_local_index_to_coords(mesh, ip, ix)
Given a local point index, this function returns the set of integer coordinates of the point.
Definition: mesh.F90:951
subroutine, public messages_not_implemented(feature, namespace)
Definition: messages.F90:1068
subroutine, public messages_obsolete_variable(namespace, name, rep)
Definition: messages.F90:1000
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:162
subroutine, public messages_input_error(namespace, var, details, row, column)
Definition: messages.F90:691
subroutine, public messages_experimental(name, namespace)
Definition: messages.F90:1040
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
Definition: messages.F90:594
This module handles the communicators for the various parallelization strategies.
Definition: multicomm.F90:147
This module defines non-local operators.
subroutine, public dnl_operator_operate_diag(op, fo)
integer, parameter op_map
integer, parameter op_max
integer, parameter op_vec
subroutine, public nl_operator_init(op, label, symm)
initialize an instance of a non-local operator by setting the label
subroutine, public dnl_operator_operate_batch(op, fi, fo, ghost_update, profile, points, factor, async)
subroutine nl_operator_clear_gpu_buffers(op)
subroutine, public nl_operator_build_symmetric_weights(op, max_size)
Builds (or rebuild) the necessary arrays for symmetric and antisymmetric stencils.
integer dfunction_global
subroutine group_by_pairs_sym(size, ldf, offsets, wre, ri, nri, npairs, wpair, pair_pos, pair_neg, wcenter)
Take a list of weights and offsets and build pairs of symmetric points with common weights.
subroutine group_by_pairs_antisym(size, ldf, offsets, wre, ri, nri, npairs, wpair, pair_pos, pair_neg)
Take a list of weights and offsets and build pairs of symmetric points with common weights.
subroutine, public dnl_operator_operate(op, fi, fo, ghost_update, profile, points)
subroutine, public nl_operator_update_gpu_buffers(op)
subroutine, public nl_operator_global_init(namespace)
initialize global settings for non-local operators
subroutine, public nl_operator_output_weights(this)
integer, parameter, public op_general
subroutine, public nl_operator_end(op)
integer, parameter, public op_inner
subroutine, public znl_operator_operate(op, fi, fo, ghost_update, profile, points)
subroutine, public nl_operator_remove_zero_weight_points(op, space, mesh)
Removes the zero-weight points for constant weight stencils.
integer zfunction_global
integer, parameter op_invmap
integer, parameter, public op_symmetric
subroutine, public nl_operator_global_end()
integer, parameter, public op_outer
subroutine, public nl_operator_build(space, mesh, op, np, const_w, regenerate)
Creates the nonlocal operators for the stencils used for finite differences.
subroutine, public znl_operator_operate_batch(op, fi, fo, ghost_update, profile, points, factor, async)
integer, parameter op_nomap
integer pure function, public nl_operator_np_zero_bc(op)
integer, parameter, public op_antisymmetric
subroutine, public znl_operator_operate_diag(op, fo)
subroutine reallocate_array(ri, stencil_size, nri, old_size, new_size)
Reallocate an ri array.
integer pure function, public nl_operator_get_index(op, is, ip)
subroutine, public nl_operator_allocate_gpu_buffers(op)
integer, parameter op_min
This module contains interfaces for routines in operate.c.
Definition: operate_f.F90:119
Some general things and nomenclature:
Definition: par_vec.F90:173
This module is intended to contain "only mathematical" functions and procedures.
Definition: sort.F90:119
This module defines stencils used in Octopus.
Definition: stencil.F90:137
type(type_t), parameter, public type_integer
Definition: types.F90:137
Describes mesh distribution to nodes.
Definition: mesh.F90:187
index type for non-local operators
data type for non local operators
int true(void)