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
140 type(accel_mem_t), public :: buff_wpair
141 type(accel_mem_t), public :: buff_half_wpair
142 type(accel_mem_t), public :: buff_ri_pos
143 type(accel_mem_t), public :: buff_ri_neg
144 type(accel_mem_t), public :: buff_map_sym
145 integer :: max_allocated_ri_pair_gpu = 0
146
147 ! For multigrid solvers
148 type(nl_operator_t), public, pointer :: coarser => null()
149
150 end type nl_operator_t
151
152 integer, parameter :: &
153 OP_FORTRAN = 0, &
154 op_vec = 1, &
155 op_min = op_fortran, &
156 op_max = op_vec
157
158 integer, parameter :: &
159 OP_INVMAP = 1, &
160 op_map = 2
161
162 integer, public, parameter :: OP_ALL = 3, op_inner = 1, op_outer = 2
163
164 interface
165 integer function op_is_available(opid, type)
166 implicit none
167 integer, intent(in) :: opid, type
168 end function op_is_available
169 end interface
170
171 integer :: dfunction_global = -1
172 integer :: zfunction_global = -1
173 integer :: function_accel
174 logical :: use_symmetries = .false.
176contains
178 ! ---------------------------------------------------------
181 subroutine nl_operator_global_init(namespace)
182 type(namespace_t), intent(in) :: namespace
183
184 integer :: default
185
188 !%Variable OperateDouble
189 !%Type integer
190 !%Section Execution::Optimization
191 !%Default optimized
192 !%Description
193 !% This variable selects the subroutine used to apply non-local
194 !% operators over the grid for real functions.
195 !%Option fortran 0
196 !% The standard Fortran function.
197 !%Option optimized 1
198 !% This version is optimized using vector primitives (if available).
199 !%End
200
201 !%Variable OperateComplex
202 !%Type integer
203 !%Section Execution::Optimization
204 !%Default optimized
205 !%Description
206 !% This variable selects the subroutine used to apply non-local
207 !% operators over the grid for complex functions.
208 !%Option fortran 0
209 !% The standard Fortran function.
210 !%Option optimized 1
211 !% This version is optimized using vector primitives (if available).
212 !%End
214 default = op_vec
216 call parse_variable(namespace, 'OperateDouble', default, dfunction_global)
217 if (.not. varinfo_valid_option('OperateDouble', dfunction_global)) call messages_input_error(namespace, 'OperateDouble')
218
219 call parse_variable(namespace, 'OperateComplex', default, zfunction_global)
220 if (.not. varinfo_valid_option('OperateComplex', zfunction_global)) call messages_input_error(namespace, 'OperateComplex')
221
222 !%Variable OperateUseSymmetries
223 !%Type logical
224 !%Section Execution::Optimization
225 !%Default yes
226 !%Description
227 !% This variable selects if the operators are built using symmetries or not.
228 !% Ignored for curvilinear meshes.
229 !%End
230 call parse_variable(namespace, 'OperateUseSymmetries', .true., use_symmetries)
233
234 !%Variable OperateAccel
235 !%Type integer
236 !%Default map
237 !%Section Execution::Optimization
238 !%Description
239 !% This variable selects the subroutine used to apply non-local
240 !% operators over the grid when an accelerator device is used.
241 !%Option invmap 1
242 !% The standard implementation ported to GPUs.
243 !%Option map 2
244 !% A different version, more suitable for GPUs.
245 !%End
246 call parse_variable(namespace, 'OperateAccel', op_map, function_accel)
248 call messages_obsolete_variable(namespace, 'OperateOpenCL', 'OperateAccel')
249
250 end if
251
254
255 ! ---------------------------------------------------------
256
258 push_sub(nl_operator_global_end)
259
261 end subroutine nl_operator_global_end
262
263 ! ---------------------------------------------------------
267 subroutine nl_operator_init(op, label, symm)
268 type(nl_operator_t), intent(inout) :: op
269 character(len=*), intent(in) :: label
270 integer, optional, intent(in) :: symm
271
272 push_sub(nl_operator_init)
273
274 op%label = label
275 op%symmetry = op_general
277 op%symmetry = optional_default(symm, op_general)
278 end if
279
280 pop_sub(nl_operator_init)
281 end subroutine nl_operator_init
282
283 ! ---------------------------------------------------------
293 subroutine nl_operator_build(space, mesh, op, np, const_w, regenerate)
294 class(space_t), intent(in) :: space
295 type(mesh_t), target, intent(in) :: mesh
296 type(nl_operator_t), intent(inout) :: op
297 integer, intent(in) :: np
298 logical, optional, intent(in) :: const_w
299 logical, optional, intent(in) :: regenerate
300
301 integer :: ii, jj, p1(space%dim), time, current
302 integer, allocatable :: st1(:), st2(:), st1r(:)
303 integer :: ir, maxp, iinner, iouter
304 logical :: change, force_change
305 character(len=200) :: flags
306 character(len=32) :: indirect_arg
307 integer, allocatable :: inner_points(:), outer_points(:), all_points(:)
308
309 push_sub(nl_operator_build)
310
311 op%const_w = optional_default(const_w, .false.)
312 if (mesh%parallel_in_domains .and. .not. op%const_w) then
313 call messages_experimental('Domain parallelization with curvilinear coordinates')
314 end if
315
316 assert(np > 0)
317
318 ! store values in structure
319 op%np = np
320 op%mesh => mesh
321 if (.not. optional_default(regenerate, .false.)) then
322 op%const_w = optional_default(const_w, .false.)
323 end if
324
325 ! grouping points by symmetric pairs requires constant weights
326 if (.not. op%const_w) op%symmetry = op_general
327
328 ! allocate weights op%w
329 if (op%const_w) then
330 safe_allocate(op%w(1:op%stencil%size, 1))
331 message(1) = 'Debug: nl_operator_build: working with constant weights.'
332 call messages_info(1, debug_only=.true.)
333 else
334 safe_allocate(op%w(1:op%stencil%size, 1:op%np))
335 message(1) = 'Debug: nl_operator_build: working with non-constant weights.'
336 call messages_info(1, debug_only=.true.)
337 end if
338
339 ! set initially to zero
340 op%w = m_zero
341
342 ! Build lookup table
343 safe_allocate(st1(1:op%stencil%size))
344 safe_allocate(st1r(1:op%stencil%size))
345 safe_allocate(st2(1:op%stencil%size))
346
347 op%nri = 0
348 do time = 1, 2
349 st2 = 0
350 do ii = 1, np
351 p1 = 0
352 call mesh_local_index_to_coords(mesh, ii, p1)
353
354 do jj = 1, op%stencil%size
355 ! Get local index of p1 plus current stencil point.
356 st1(jj) = mesh_local_index_from_coords(mesh, p1 + op%stencil%points(:, jj))
357
358 assert(st1(jj) > 0)
359 end do
360
361 st1(1:op%stencil%size) = st1(1:op%stencil%size) - ii
363 change = any(st1 /= st2)
364
365 !the next is to detect when we move from a point that does not
366 !have boundary points as neighbours to one that has
367 force_change = any(st1 + ii > mesh%np) .and. all(st2 + ii - 1 <= mesh%np)
368
369 ! if the stencil changes
370 if (change .or. force_change) then
371 !store it
372 st2(:) = st1(:)
373
374 !first time, just count
375 if (time == 1) op%nri = op%nri + 1
376
377 !second time, store
378 if (time == 2) then
379 current = current + 1
380 op%ri(1:op%stencil%size, current) = st1(1:op%stencil%size)
381 end if
382 end if
383
384 if (time == 2) op%rimap(ii) = current
385
386 end do
387
388 !after counting, allocate
389 if (time == 1) then
390 safe_deallocate_a(op%ri)
391 safe_deallocate_a(op%rimap)
392 safe_deallocate_a(op%rimap_inv)
393
394 safe_allocate(op%ri(1:op%stencil%size, 1:op%nri))
395 safe_allocate(op%rimap(1:op%np))
396 safe_allocate(op%rimap_inv(1:op%nri + 1))
397 op%ri = 0
398 op%rimap = 0
399 op%rimap_inv = 0
400 current = 0
401
402 ! the sizes
403 if (mesh%use_curvilinear) then
404 safe_allocate(op%nn(1:op%nri))
405 ! for the moment all the sizes are the same
406 op%nn = op%stencil%size
407 end if
408 end if
409
410 end do
411
412 !the inverse mapping
413 op%rimap_inv(1) = 0
414 do jj = 1, op%np
415 op%rimap_inv(op%rimap(jj) + 1) = jj
416 end do
417 op%rimap_inv(op%nri + 1) = op%np
418
419 safe_deallocate_a(st1)
420 safe_deallocate_a(st1r)
421 safe_deallocate_a(st2)
422
423 if (op%mesh%parallel_in_domains) then
424 !now build the arrays required to apply the nl_operator by parts
425
426 !count points
427 op%inner%nri = 0
428 op%outer%nri = 0
429 do ir = 1, op%nri
430 maxp = op%rimap_inv(ir + 1) + maxval(op%ri(1:op%stencil%size, ir))
431 if (maxp <= np) then
432 !inner point
433 op%inner%nri = op%inner%nri + 1
434 assert(op%inner%nri <= op%nri)
435 else
436 !outer point
437 op%outer%nri = op%outer%nri + 1
438 assert(op%outer%nri <= op%nri)
439 end if
440 end do
441
442 assert(op%inner%nri + op%outer%nri == op%nri)
443
444 if (optional_default(regenerate, .false.)) then
445 safe_deallocate_a(op%inner%imin)
446 safe_deallocate_a(op%inner%imax)
447 safe_deallocate_a(op%inner%ri)
448 safe_deallocate_a(op%outer%imin)
449 safe_deallocate_a(op%outer%imax)
450 safe_deallocate_a(op%outer%ri)
451 end if
452 safe_allocate(op%inner%imin(1:op%inner%nri + 1))
453 safe_allocate(op%inner%imax(1:op%inner%nri))
454 safe_allocate(op%inner%ri(1:op%stencil%size, 1:op%inner%nri))
455
456 safe_allocate(op%outer%imin(1:op%outer%nri + 1))
457 safe_allocate(op%outer%imax(1:op%outer%nri))
458 safe_allocate(op%outer%ri(1:op%stencil%size, 1:op%outer%nri))
459
460 !now populate the arrays
461 iinner = 0
462 iouter = 0
463 do ir = 1, op%nri
464 maxp = op%rimap_inv(ir + 1) + maxval(op%ri(1:op%stencil%size, ir))
465 if (maxp <= np) then
466 !inner point
467 iinner = iinner + 1
468 op%inner%imin(iinner) = op%rimap_inv(ir)
469 op%inner%imax(iinner) = op%rimap_inv(ir + 1)
470 op%inner%ri(1:op%stencil%size, iinner) = op%ri(1:op%stencil%size, ir)
471 else
472 !outer point
473 iouter = iouter + 1
474 op%outer%imin(iouter) = op%rimap_inv(ir)
475 op%outer%imax(iouter) = op%rimap_inv(ir + 1)
476 op%outer%ri(1:op%stencil%size, iouter) = op%ri(1:op%stencil%size, ir)
477 end if
478 end do
479
480 !verify that all points in the inner operator are actually inner
481 do ir = 1, op%inner%nri
482 do ii = op%inner%imin(ir) + 1, op%inner%imax(ir)
483 assert(all(ii + op%inner%ri(1:op%stencil%size, ir) <= mesh%np))
484 end do
485 end do
486
487 end if
488
489 if (accel_is_enabled() .and. op%const_w) then
490
491 write(flags, '(i5)') op%stencil%size
492 flags='-DSTENCIL_SIZE='//trim(adjustl(flags))
493
494 select case (function_accel)
495 case (op_invmap)
496 if (op%symmetry /= op_general) then
497 call messages_not_implemented("OperateUseSymmetries=yes with OperateAccel=invmap")
498 end if
499 call accel_kernel_build(op%kernel, 'operate.cu', 'operate<double>', flags)
500 case (op_map)
501 if (op%mesh%parallel_in_domains) then
502 indirect_arg = 'Indexing::Indirect'
503 else
504 indirect_arg = 'Indexing::Direct'
505 end if
506 select case (op%symmetry)
507 case (op_general)
508 call accel_kernel_build(op%kernel, 'operate.cu', &
509 'operate_map<double, '//trim(indirect_arg)//'>', flags)
510 case (op_symmetric)
511 call accel_kernel_build(op%kernel, 'operate.cu', &
512 'operate_map<double, '//trim(indirect_arg)//', Symmetry::Symmetric>', flags)
513 case (op_antisymmetric)
514 call accel_kernel_build(op%kernel, 'operate.cu', &
515 'operate_map<double, '//trim(indirect_arg)//', Symmetry::Antisymmetric>', flags)
516 end select
517 end select
518
519 ! conversion to i8 needed to avoid integer overflow
520 call accel_create_buffer(op%buff_ri, accel_mem_read_only, type_integer, int(op%nri, int64)*op%stencil%size)
521 call accel_write_buffer(op%buff_ri, op%stencil%size, op%nri, op%ri)
522
523 select case (function_accel)
524 case (op_invmap)
525 call accel_create_buffer(op%buff_imin, accel_mem_read_only, type_integer, op%nri)
526 call accel_write_buffer(op%buff_imin, op%nri, op%rimap_inv(1:))
527 call accel_create_buffer(op%buff_imax, accel_mem_read_only, type_integer, op%nri)
528 call accel_write_buffer(op%buff_imax, op%nri, op%rimap_inv(2:))
529
530 case (op_map)
531
533 call accel_write_buffer(op%buff_map, op%mesh%np, (op%rimap - 1)*op%stencil%size)
534
535 if (op%symmetry /= op_general) then
536 call accel_create_buffer(op%buff_map_sym, accel_mem_read_only, type_integer, pad(op%mesh%np, accel_max_block_size()))
537 call accel_write_buffer(op%buff_map_sym, op%mesh%np, (op%rimap - 1)*(op%stencil%size/2))
538 end if
539
540 if (op%mesh%parallel_in_domains) then
541
542 safe_allocate(inner_points(1:op%mesh%np))
543 safe_allocate(outer_points(1:op%mesh%np))
544 safe_allocate(all_points(1:op%mesh%np))
545
546 op%ninner = 0
547 op%nouter = 0
548
549 do ii = 1, op%mesh%np
550 all_points(ii) = ii - 1
551 maxp = ii + maxval(op%ri(1:op%stencil%size, op%rimap(ii)))
552 if (maxp <= op%mesh%np) then
553 op%ninner = op%ninner + 1
554 inner_points(op%ninner) = ii - 1
555 else
556 op%nouter = op%nouter + 1
557 outer_points(op%nouter) = ii - 1
558 end if
559 end do
560
562 call accel_write_buffer(op%buff_all, op%mesh%np, all_points)
563
565 call accel_write_buffer(op%buff_inner, op%ninner, inner_points)
566
568 call accel_write_buffer(op%buff_outer, op%nouter, outer_points)
569
570 safe_deallocate_a(inner_points)
571 safe_deallocate_a(outer_points)
572 safe_deallocate_a(all_points)
573
574 end if
575 end select
576 end if
577
578 pop_sub(nl_operator_build)
579
580 end subroutine nl_operator_build
581
582 ! ---------------------------------------------------------
583 subroutine nl_operator_output_weights(this)
584 type(nl_operator_t), intent(inout) :: this
585
586 integer :: istencil, idir
587
589
590 write(message(1), '(3a)') 'Debug info: Finite difference weights for ', trim(this%label), '.'
591 write(message(2), '(a)') ' Spacing:'
592 do idir = 1, this%mesh%box%dim
593 write(message(2), '(a,f16.8)') trim(message(2)), this%mesh%spacing(idir)
594 end do
595 call messages_info(2, debug_only=.true.)
596
597 do istencil = 1, this%stencil%size
598 select case(this%mesh%box%dim)
599 case(1)
600 write(message(1), '(a,i3,1i4,f25.10)') ' ', istencil, this%stencil%points(1:1, istencil), this%w(istencil, 1)
601 case(2)
602 write(message(1), '(a,i3,2i4,f25.10)') ' ', istencil, this%stencil%points(1:2, istencil), this%w(istencil, 1)
603 case(3)
604 write(message(1), '(a,i3,3i4,f25.10)') ' ', istencil, this%stencil%points(1:3, istencil), this%w(istencil, 1)
605 end select
606 call messages_info(1, debug_only=.true.)
607 end do
608
610
611 end subroutine nl_operator_output_weights
612
613 ! ---------------------------------------------------------
614 subroutine nl_operator_end(op)
615 type(nl_operator_t), intent(inout) :: op
616
617 push_sub(nl_operator_end)
618
619 if (accel_is_enabled() .and. op%const_w) then
621 end if
622
623 safe_deallocate_a(op%inner%imin)
624 safe_deallocate_a(op%inner%imax)
625 safe_deallocate_a(op%inner%ri)
626 safe_deallocate_a(op%outer%imin)
627 safe_deallocate_a(op%outer%imax)
628 safe_deallocate_a(op%outer%ri)
629
630 safe_deallocate_a(op%w)
631
632 safe_deallocate_a(op%ri)
633 safe_deallocate_a(op%rimap)
634 safe_deallocate_a(op%rimap_inv)
635 safe_deallocate_a(op%nn)
636
637 safe_deallocate_a(op%wpair)
638 safe_deallocate_a(op%ri_pos)
639 safe_deallocate_a(op%ri_neg)
640
641 safe_deallocate_a(op%inner%ri_pos)
642 safe_deallocate_a(op%inner%ri_neg)
643 safe_deallocate_a(op%outer%ri_pos)
644 safe_deallocate_a(op%outer%ri_neg)
645 call stencil_end(op%stencil)
646
647 pop_sub(nl_operator_end)
648 end subroutine nl_operator_end
649
650
651 subroutine nl_operator_clear_gpu_buffers(op)
652 type(nl_operator_t), intent(inout) :: op
653
655
656 call accel_free_buffer(op%buff_ri)
657 select case (function_accel)
658 case (op_invmap)
659 call accel_free_buffer(op%buff_imin)
660 call accel_free_buffer(op%buff_imax)
661
662 case (op_map)
663 call accel_free_buffer(op%buff_map)
664 if (op%symmetry /= op_general) then
665 call accel_free_buffer(op%buff_map_sym)
666 end if
667 if (op%mesh%parallel_in_domains) then
668 call accel_free_buffer(op%buff_all)
669 call accel_free_buffer(op%buff_inner)
670 call accel_free_buffer(op%buff_outer)
671 end if
672 end select
673
674 call accel_free_buffer(op%buff_weights)
675 call accel_free_buffer(op%buff_half_weights)
676
677 if (op%symmetry /= op_general) then
678 call accel_free_buffer(op%buff_wpair)
679 call accel_free_buffer(op%buff_half_wpair)
680 if (op%max_allocated_ri_pair_gpu > 0) then
681 call accel_free_buffer(op%buff_ri_pos)
682 call accel_free_buffer(op%buff_ri_neg)
683 op%max_allocated_ri_pair_gpu = 0
684 end if
685 end if
686
688 end subroutine nl_operator_clear_gpu_buffers
689
690 ! ---------------------------------------------------------
691 integer pure function nl_operator_get_index(op, is, ip) result(res)
692 type(nl_operator_t), intent(in) :: op
693 integer, intent(in) :: is
694 integer, intent(in) :: ip
695
696 res = ip + op%ri(is, op%rimap(ip))
697 end function nl_operator_get_index
698
699 ! ---------------------------------------------------------
700
702 type(nl_operator_t), intent(inout) :: op
703
705
706 ! Update the GPU weights
707 if (accel_is_enabled() .and. op%const_w) then
708 call accel_create_buffer(op%buff_weights, accel_mem_read_only, type_float, op%stencil%size)
709 call accel_create_buffer(op%buff_half_weights, accel_mem_read_only, type_float, op%stencil%size)
710 if (op%symmetry /= op_general) then
711 call accel_create_buffer(op%buff_wpair, accel_mem_read_only, type_float, op%stencil%size/2)
712 call accel_create_buffer(op%buff_half_wpair, accel_mem_read_only, type_float, op%stencil%size/2)
713 end if
714 end if
715
718
719
720 ! ---------------------------------------------------------
721
722 subroutine nl_operator_update_gpu_buffers(op)
723 type(nl_operator_t), intent(inout) :: op
724
725 integer(int64) :: buf_size
726
728
729 ! Update the GPU weights
730 if (accel_is_enabled() .and. op%const_w) then
731 call accel_write_buffer(op%buff_weights, op%stencil%size, op%w(:, 1))
732 call accel_write_buffer(op%buff_half_weights, op%stencil%size, -m_half*op%w(:, 1))
733
734 if (op%symmetry /= op_general) then
735 call accel_write_buffer(op%buff_wpair, op%npairs, op%wpair)
736 call accel_write_buffer(op%buff_half_wpair, op%npairs, -m_half*op%wpair)
737
738 ! (Re)allocate the pair-index buffers if max_allocated_ri_pair has grown
739 if (op%max_allocated_ri_pair > op%max_allocated_ri_pair_gpu) then
740 if (op%max_allocated_ri_pair_gpu > 0) then
741 call accel_free_buffer(op%buff_ri_pos)
742 call accel_free_buffer(op%buff_ri_neg)
743 end if
744 buf_size = int(op%npairs, int64)*op%nri*op%max_allocated_ri_pair
745 call accel_create_buffer(op%buff_ri_pos, accel_mem_read_only, type_integer, buf_size)
746 call accel_create_buffer(op%buff_ri_neg, accel_mem_read_only, type_integer, buf_size)
747 op%max_allocated_ri_pair_gpu = op%max_allocated_ri_pair
748 end if
749
750 if (op%max_allocated_ri_pair > 0) then
751 call accel_write_buffer(op%buff_ri_pos, op%npairs, op%nri, op%max_allocated_ri_pair, op%ri_pos)
752 call accel_write_buffer(op%buff_ri_neg, op%npairs, op%nri, op%max_allocated_ri_pair, op%ri_neg)
753 end if
754 end if
755 end if
756
758 end subroutine nl_operator_update_gpu_buffers
759
760 ! ---------------------------------------------------------
761
762 integer pure function nl_operator_np_zero_bc(op) result(np_bc)
763 type(nl_operator_t), intent(in) :: op
764
765 integer :: jj, ii
766
767 np_bc = 0
768 do jj = 1, op%nri
769 ii = op%rimap_inv(jj + 1) + maxval(op%ri(1:op%stencil%size, jj))
770 np_bc = max(np_bc, ii)
771 end do
772
773 end function nl_operator_np_zero_bc
774
775
776 ! ---------------------------------------------------------
778 subroutine nl_operator_remove_zero_weight_points(op, space, mesh)
779 type(nl_operator_t), intent(inout) :: op
780 type(space_t), intent(in) :: space
781 class(mesh_t), intent(in) :: mesh
782
783 integer :: ip, size
784 real(real64), parameter :: tol = 1.0e-14_real64
785 real(real64) :: max_weight, new_w(op%stencil%size)
786 integer :: new_points(space%dim, op%stencil%size)
787
788 if (.not. op%const_w) return
789
791
792 max_weight = maxval(abs(op%w(:, 1)))
793 size = 0
794 do ip = 1, op%stencil%size
795 if (abs(op%w(ip, 1)) > tol * max_weight) then
796 size = size +1
797 new_w(size) = op%w(ip, 1)
798 new_points(:,size) = op%stencil%points(:, ip)
799 end if
800 end do
801
802 ! We regenerate the stencil without the zero-weight points
803 op%stencil%size = size
804 safe_deallocate_a(op%stencil%points)
805 safe_allocate(op%stencil%points(space%dim, op%stencil%size))
806 op%stencil%points(:, :) = new_points(:, 1:size)
807 safe_deallocate_a(op%w)
809 call nl_operator_build(space, mesh, op, mesh%np, const_w=op%const_w, regenerate=.true.)
810 op%w(1:size, 1) = new_w(1:size)
811
812 !Update Stencil%center
813 call stencil_init_center(op%stencil)
814
819 subroutine group_by_pairs_sym(size, ldf, offsets, wre, ri, nri, npairs, wpair, pair_pos, pair_neg, wcenter)
820 integer, intent(in) :: size
821 integer, intent(in) :: ldf
822 integer, intent(in) :: offsets(:, :)
823 real(real64), intent(in) :: wre(:)
824 integer, intent(in) :: ri(:, :)
825 integer, intent(in) :: nri
826 integer, intent(out) :: npairs
827 real(real64), intent(inout) :: wpair(:)
828 integer, intent(inout) :: pair_pos(:,:), pair_neg(:,:)
829 real(real64), intent(out) :: wcenter
830
831 logical, allocatable :: used(:)
832 integer :: i, j, ndim, s
833 logical :: same
834 integer, allocatable :: idx(:)
835
836 real(real64), parameter :: tol = 1.0e-11_real64
837
838 push_sub(group_by_pairs_sym)
839
840 assert(mod(size,2) == 1)
841
842 safe_allocate(used(1:size))
843 used = .false.
844 npairs = 0
845
846 ndim = ubound(offsets, dim=1)
847
848 safe_allocate(idx(1:size))
849 call robust_sort_by_abs(wre, offsets, idx)
850
851 do i = 1, size
852 if (used(i)) cycle
853
854 if (all(offsets(:, idx(i))==0)) then
855 wcenter = wre(idx(i))
856 used(i) = .true.
857 cycle
858 end if
859
860 ! Try to find symmetric partner j
861 do j = i+1, size
862 if (used(j)) cycle
863
864 ! Weight equality
865 same = abs(wre(idx(i)) - wre(idx(j))) <= tol*max(m_one, abs(wre(idx(i))))
866 if (.not. same) cycle
867
868 ! Offsets equal and opposite
869 if (any(offsets(:,idx(j))+offsets(:, idx(i)) /= 0)) cycle
870
871 npairs = npairs + 1
872 do s = 1, nri
873 pair_pos(npairs, s) = ri(idx(i), s) * 2**ldf
874 pair_neg(npairs, s) = ri(idx(j), s) * 2**ldf
875 end do
876 wpair(npairs) = m_half*(wre(idx(i)) + wre(idx(j)))
877
878 used(i) = .true.
879 used(j) = .true.
880 exit
881 end do
882 end do
883
884 assert(npairs == size/2)
885
886 safe_deallocate_a(idx)
887
888 pop_sub(group_by_pairs_sym)
889 end subroutine group_by_pairs_sym
890
892 subroutine group_by_pairs_antisym(size, ldf, offsets, wre, ri, nri, npairs, wpair, pair_pos, pair_neg)
893 integer, intent(in) :: size
894 integer, intent(in) :: ldf
895 integer, intent(in) :: offsets(:, :)
896 real(real64), intent(in) :: wre(:)
897 integer, intent(in) :: ri(:, :)
898 integer, intent(in) :: nri
899 integer, intent(out) :: npairs
900 real(real64), intent(inout) :: wpair(:)
901 integer, intent(inout) :: pair_pos(:,:), pair_neg(:,:)
902
903 logical, allocatable :: used(:)
904 integer :: i, j, ndim, s
905 logical :: same
906 integer, allocatable :: idx(:)
907
908 real(real64), parameter :: tol = 1.0e-11_real64
909
910 push_sub(group_by_pairs_antisym)
911
912 assert(mod(size,2) == 0)
913
914 safe_allocate(used(1:size))
915 used = .false.
916 npairs = 0
917
918 ndim = ubound(offsets, dim=1)
919
920 safe_allocate(idx(1:size))
921 call robust_sort_by_abs(wre, offsets, idx)
922
923 ! Max pairs = n/2
924 do i = 1, size
925 if (used(i)) cycle
926
927 ! Try to find symmetric partner j
928 do j = i+1, size
929 if (used(j)) cycle
930
931 ! Weight equality
932 same = abs(wre(idx(i)) + wre(idx(j))) <= tol*max(m_one, abs(wre(idx(i))))
933 if (.not. same) cycle
934
935 ! Offsets equal and opposite
936 if (any(offsets(:,idx(j))+offsets(:, idx(i)) /= 0)) cycle
937
938 npairs = npairs + 1
939 do s = 1, nri
940 pair_pos(npairs, s) = ri(idx(i), s) * 2**ldf
941 pair_neg(npairs, s) = ri(idx(j), s) * 2**ldf
942 end do
943 wpair(npairs) = m_half*(wre(idx(i)) - wre(idx(j)))
944
945 used(i) = .true.
946 used(j) = .true.
947 exit
948 end do
949 end do
950
951 assert(npairs == size/2)
952
953 safe_deallocate_a(idx)
955 end subroutine group_by_pairs_antisym
956
958 subroutine nl_operator_build_symmetric_weights(op, max_size)
959 type(nl_operator_t), intent(inout) :: op
960 integer, optional, intent(in) :: max_size
961
962 integer :: ldf, start, end, ipair
963
964 if (op%symmetry == op_general) return
965
967
968 assert(op%const_w)
969
970 if(present(max_size)) then
971 start = op%max_allocated_ri_pair + 1
972 end = max_size
973 call reallocate_array(op%ri_pos, op%stencil%size/2, op%nri, op%max_allocated_ri_pair, end)
974 call reallocate_array(op%ri_neg, op%stencil%size/2, op%nri, op%max_allocated_ri_pair, end)
975 if (op%mesh%parallel_in_domains) then
976 call reallocate_array(op%inner%ri_pos, op%stencil%size/2, op%inner%nri, op%max_allocated_ri_pair, end)
977 call reallocate_array(op%inner%ri_neg, op%stencil%size/2, op%inner%nri, op%max_allocated_ri_pair, end)
978 call reallocate_array(op%outer%ri_pos, op%stencil%size/2, op%outer%nri, op%max_allocated_ri_pair, end)
979 call reallocate_array(op%outer%ri_neg, op%stencil%size/2, op%outer%nri, op%max_allocated_ri_pair, end)
980 end if
981 else if (allocated(op%wpair)) then
982 ! The weights op%w have been modified: recompute the pairs for all allocated levels
983 start = 1
984 end = op%max_allocated_ri_pair
985 else
986 ! Max pairs = n/2
987 safe_allocate(op%wpair(1:op%stencil%size/2))
988 start = 1
989 ! conf%target_states_block_size is only set for electron systems
990 if (conf%target_states_block_size > 0) then
991 end = log2(conf%target_states_block_size)+1
992 else
993 end = 1
994 end if
995
996 safe_allocate(op%ri_pos(1:op%stencil%size/2, 1:op%nri, 1:end))
997 safe_allocate(op%ri_neg(1:op%stencil%size/2, 1:op%nri, 1:end))
998 if (op%mesh%parallel_in_domains) then
999 safe_allocate(op%inner%ri_pos(1:op%stencil%size/2, 1:op%inner%nri, 1:end))
1000 safe_allocate(op%inner%ri_neg(1:op%stencil%size/2, 1:op%inner%nri, 1:end))
1001 safe_allocate(op%outer%ri_pos(1:op%stencil%size/2, 1:op%outer%nri, 1:end))
1002 safe_allocate(op%outer%ri_neg(1:op%stencil%size/2, 1:op%outer%nri, 1:end))
1003 end if
1004 end if
1005 op%max_allocated_ri_pair = end
1006
1007 do ldf = start-1, end-1
1008 select case(op%symmetry)
1009 case(op_symmetric)
1010 call group_by_pairs_sym(op%stencil%size, ldf, op%stencil%points, op%w(:,1), op%ri, op%nri, &
1011 op%npairs, op%wpair, op%ri_pos(:,:,ldf+1), op%ri_neg(:,:,ldf+1), op%wcenter)
1012 if (op%mesh%parallel_in_domains) then
1013 call group_by_pairs_sym(op%stencil%size, ldf, op%stencil%points, op%w(:,1), op%inner%ri, op%inner%nri, &
1014 op%npairs, op%wpair, op%inner%ri_pos(:,:,ldf+1), op%inner%ri_neg(:,:,ldf+1), op%wcenter)
1015 call group_by_pairs_sym(op%stencil%size, ldf, op%stencil%points, op%w(:,1), op%outer%ri, op%outer%nri, &
1016 op%npairs, op%wpair, op%outer%ri_pos(:,:,ldf+1), op%outer%ri_neg(:,:,ldf+1), op%wcenter)
1017 end if
1018 case(op_antisymmetric)
1019 call group_by_pairs_antisym(op%stencil%size, ldf, op%stencil%points, op%w(:,1), op%ri, op%nri, &
1020 op%npairs, op%wpair, op%ri_pos(:,:,ldf+1), op%ri_neg(:,:,ldf+1))
1021 if (op%mesh%parallel_in_domains) then
1022 call group_by_pairs_antisym(op%stencil%size, ldf, op%stencil%points, op%w(:,1), op%inner%ri, op%inner%nri, &
1023 op%npairs, op%wpair, op%inner%ri_pos(:,:,ldf+1), op%inner%ri_neg(:,:,ldf+1))
1024 call group_by_pairs_antisym(op%stencil%size, ldf, op%stencil%points, op%w(:,1), op%outer%ri, op%outer%nri, &
1025 op%npairs, op%wpair, op%outer%ri_pos(:,:,ldf+1), op%outer%ri_neg(:,:,ldf+1))
1026 end if
1027 end select
1028 end do
1029
1030 if (.not. present(max_size)) then
1031 write(message(1), '(3a)') 'Debug info: Sorted weights for ', trim(op%label), '.'
1032 call messages_info(1, debug_only=.true.)
1033
1034 do ipair = 1, op%npairs
1035 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)
1036 call messages_info(1, debug_only=.true.)
1037 end do
1038 end if
1039
1042
1044 subroutine reallocate_array(ri, stencil_size, nri, old_size, new_size)
1045 integer, allocatable, intent(inout) :: ri(:,:,:)
1046 integer, intent(in) :: stencil_size, nri
1047 integer, intent(in) :: old_size, new_size
1048
1049 integer, allocatable :: tmp(:,:,:)
1050
1051 safe_allocate_source_a(tmp, ri)
1052 safe_deallocate_a(ri)
1053 safe_allocate(ri(1:stencil_size, 1:nri, 1:new_size))
1054 ri(:,:,1:old_size) = tmp
1055 safe_deallocate_a(tmp)
1056 end subroutine reallocate_array
1057
1058#include "undef.F90"
1059#include "real.F90"
1060#include "nl_operator_inc.F90"
1061
1062#include "undef.F90"
1063#include "complex.F90"
1064#include "nl_operator_inc.F90"
1065
1066end module nl_operator_oct_m
1067
1068!! Local Variables:
1069!! mode: f90
1070!! coding: utf-8
1071!! End:
subroutine, public accel_free_buffer(this, async)
Definition: accel.F90:986
subroutine, public accel_kernel_build(this, file_name, kernel_name, flags)
Compile the program that contains a given kernel.
Definition: accel.F90:1707
pure logical function, public accel_is_enabled()
Definition: accel.F90:395
integer, parameter, public accel_mem_read_only
Definition: accel.F90:187
integer pure function, public accel_max_block_size()
Definition: accel.F90:1188
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.
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, 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 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
subroutine, public stencil_end(this)
Definition: stencil.F90:217
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)