Neko  0.9.0
A portable framework for high-order spectral element flow simulations
global_interpolation.F90
Go to the documentation of this file.
1 ! Copyright (c) 2020-2023, The Neko Authors
2 ! All rights reserved.
3 !
4 ! Redistribution and use in source and binary forms, with or without
5 ! modification, are permitted provided that the following conditions
6 ! are met:
7 !
8 ! * Redistributions of source code must retain the above copyright
9 ! notice, this list of conditions and the following disclaimer.
10 !
11 ! * Redistributions in binary form must reproduce the above
12 ! copyright notice, this list of conditions and the following
13 ! disclaimer in the documentation and/or other materials provided
14 ! with the distribution.
15 !
16 ! * Neither the name of the authors nor the names of its
17 ! contributors may be used to endorse or promote products derived
18 ! from this software without specific prior written permission.
19 !
20 ! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 ! "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 ! LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 ! FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 ! COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ! INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 ! BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 ! LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 ! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 ! LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 ! ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 ! POSSIBILITY OF SUCH DAMAGE.
32 !
39  use num_types, only: rp
40  use space, only: space_t
41  use dofmap, only: dofmap_t
42  use mesh, only: mesh_t
43  use logger, only: neko_log, log_size
44  use utils, only: neko_error, neko_warning
46  use comm
47  use math, only: copy
48  use neko_mpi_types
49  use structs, only: array_ptr_t
50  use, intrinsic :: iso_c_binding
51  implicit none
52  private
54  type, public :: global_interpolation_t
56  type(array_ptr_t) :: x
58  type(array_ptr_t) :: y
60  type(array_ptr_t) :: z
62  integer :: gdim
64  integer :: nelv
66  type(space_t), pointer :: xh
68  type(local_interpolator_t) :: local_interp
70  logical :: all_points_local = .false.
71  !! Gslib handle.
72  !! @note: Remove when we remove gslib.
73  integer :: gs_handle
74  logical :: gs_init = .false.
77  integer :: n_points
80  real(kind=rp), allocatable :: xyz(:,:)
82  integer, allocatable :: proc_owner(:)
84  integer, allocatable :: el_owner(:)
85  type(c_ptr) :: el_owner_d = c_null_ptr
88  real(kind=rp), allocatable :: rst(:,:)
91  real(kind=rp), allocatable :: dist2(:)
93  integer, allocatable :: error_code(:)
95  real(kind=rp) :: tol = 5d-13
96  contains
98  procedure, pass(this) :: init_xyz => global_interpolation_init_xyz
99  procedure, pass(this) :: init_dof => global_interpolation_init_dof
101  procedure, pass(this) :: free => global_interpolation_free
103  procedure, pass(this) :: free_points => global_interpolation_free_points
104  procedure, pass(this) :: find_points_and_redist => &
109  procedure, pass(this) :: find_points_coords => &
111  procedure, pass(this) :: find_points_xyz => global_interpolation_find_xyz
112  generic :: find_points => find_points_xyz, find_points_coords
114  procedure, pass(this) :: evaluate => global_interpolation_evaluate
115 
117  generic :: init => init_dof, init_xyz
118 
119  end type global_interpolation_t
120 
121 contains
122 
126  subroutine global_interpolation_init_dof(this, dof, tol)
127  class(global_interpolation_t), intent(inout) :: this
128  type(dofmap_t), target :: dof
129  real(kind=rp), optional :: tol
130 
131  ! NOTE: Passing dof%x(:,1,1,1), etc in init_xyz passes down the entire
132  ! dof%x array and not a slice. It is done this way for
133  ! this%x%ptr to point to dof%x (see global_interpolation_init_xyz).
134  call this%init_xyz(dof%x(:,1,1,1), dof%y(:,1,1,1), dof%z(:,1,1,1), &
135  dof%msh%gdim, dof%msh%nelv, dof%Xh, tol = tol)
136 
137  end subroutine global_interpolation_init_dof
138 
148  subroutine global_interpolation_init_xyz(this, x, y, z, gdim, nelv, Xh, tol)
149  class(global_interpolation_t), intent(inout) :: this
150  real(kind=rp), intent(in), target :: x(:)
151  real(kind=rp), intent(in), target :: y(:)
152  real(kind=rp), intent(in), target :: z(:)
153  integer, intent(in) :: gdim
154  integer, intent(in) :: nelv
155  type(space_t), intent(in), target :: Xh
156  real(kind=rp), intent(in), optional :: tol
157  integer :: lx, ly, lz, max_pts_per_iter
158 
159 #ifdef HAVE_GSLIB
160 
161  this%x%ptr => x
162  this%y%ptr => y
163  this%z%ptr => z
164  this%gdim = gdim
165  this%nelv = nelv
166  this%Xh => xh
167  if (present(tol)) this%tol = tol
168 
169  ! Number of points to iterate on simultaneosuly
170  max_pts_per_iter = 128
171  lx = xh%lx
172  ly = xh%ly
173  lz = xh%lz
174 
175  call fgslib_findpts_setup(this%gs_handle, &
176  neko_comm, pe_size, &
177  this%gdim, &
178  this%x%ptr, this%y%ptr, this%z%ptr, & ! Physical nodal values
179  lx, ly, lz, nelv, & ! Mesh dimensions
180  2*lx, 2*ly, 2*lz, & ! Mesh size for bounding box computation
181  0.01, & ! relative size to expand bounding boxes by
182  lx*ly*lz*nelv, lx*ly*lz*nelv, & ! local/global hash mesh sizes
183  max_pts_per_iter, this%tol)
184  this%gs_init = .true.
185 #else
186  call neko_error('Neko needs to be built with GSLIB support')
187 #endif
188 
189  end subroutine global_interpolation_init_xyz
190 
191 
193  subroutine global_interpolation_free(this)
194  class(global_interpolation_t), intent(inout) :: this
195 
196  nullify(this%x%ptr)
197  nullify(this%y%ptr)
198  nullify(this%z%ptr)
199  nullify(this%Xh)
200 
201  this%nelv = 0
202  this%gdim = 0
203 
204  call this%free_points()
205 
206 #ifdef HAVE_GSLIB
207  if (this%gs_init) then
208  call fgslib_findpts_free(this%gs_handle)
209  this%gs_init = .false.
210  end if
211 #endif
212 
213  end subroutine global_interpolation_free
214 
217  class(global_interpolation_t), intent(inout) :: this
218 
219  this%n_points = 0
220  this%all_points_local = .false.
221 
222  if (allocated(this%xyz)) deallocate(this%xyz)
223  if (allocated(this%rst)) deallocate(this%rst)
224  if (allocated(this%proc_owner)) deallocate(this%proc_owner)
225  if (allocated(this%el_owner)) deallocate(this%el_owner)
226  if (allocated(this%dist2)) deallocate(this%dist2)
227  if (allocated(this%error_code)) deallocate(this%error_code)
228 
229  if (c_associated(this%el_owner_d)) then
230  call device_free(this%el_owner_d)
231  end if
232 
233  end subroutine global_interpolation_free_points
234 
237  class(global_interpolation_t), intent(inout) :: this
238  !!Perhaps this should be kind dp
239  real(kind=rp) :: xdiff, ydiff, zdiff
240  character(len=8000) :: log_buf
241  real(kind=rp), allocatable :: x_check(:)
242  real(kind=rp), allocatable :: y_check(:)
243  real(kind=rp), allocatable :: z_check(:)
244  logical :: isdiff
245  integer :: i
246 
247 
248 #ifdef HAVE_GSLIB
249 
250  ! gslib find points, which element they belong, to process etc.
251  call fgslib_findpts(this%gs_handle, &
252  this%error_code, 1, &
253  this%proc_owner, 1, &
254  this%el_owner, 1, &
255  this%rst, this%gdim, &
256  this%dist2, 1, &
257  this%xyz(1,1), this%gdim, &
258  this%xyz(2,1), this%gdim, &
259  this%xyz(3,1), this%gdim, this%n_points)
260 
261  do i = 1 , this%n_points
262 
263  !
264  ! Check validity of points
265  !
266  if (this%error_code(i) .eq. 1) then
267  if (this%dist2(i) .gt. this%tol) then
268  write(*,*) 'Point with coords: ',&
269  this%xyz(1,i),&
270  this%xyz(2,i),&
271  this%xyz(3,i),&
272  'Did not converge to tol. Absolute differences squared: ',&
273  this%dist2(i), 'PE rank', pe_rank
274  end if
275  end if
276 
277  if (this%error_code(i) .eq. 2) &
278  write(*,*) 'Point with coords: ',&
279  this%xyz(1,i), this%xyz(2,i), this%xyz(3,i),&
280  'Outside the mesh!',&
281  ' Interpolation on these points will return 0.0. dist2: ', &
282  this%dist2(i),&
283  'el_owner, rst coords, pe: ',&
284  this%el_owner(i), this%rst(1,i), this%rst(2,i), &
285  this%rst(3,i), pe_rank
286 
287  end do
288 
289  allocate(x_check(this%n_points))
290  allocate(y_check(this%n_points))
291  allocate(z_check(this%n_points))
292 
293  call fgslib_findpts_eval(this%gs_handle, x_check, &
294  1, this%error_code, 1, &
295  this%proc_owner, 1, this%el_owner, 1, &
296  this%rst, this%gdim, &
297  this%n_points, this%x%ptr)
298 
299  call fgslib_findpts_eval(this%gs_handle, y_check, &
300  1, this%error_code, 1, &
301  this%proc_owner, 1, this%el_owner, 1, &
302  this%rst, this%gdim, &
303  this%n_points, this%y%ptr)
304 
305  call fgslib_findpts_eval(this%gs_handle, z_check, &
306  1, this%error_code, 1, &
307  this%proc_owner, 1, this%el_owner, 1, &
308  this%rst, this%gdim, &
309  this%n_points, this%z%ptr)
310 
311  do i = 1 , this%n_points
312 
313  !
314  ! Check validity of points
315  !
316  isdiff = .false.
317  xdiff = (x_check(i)-this%xyz(1,i))**2
318  if ( xdiff .gt. this%tol) isdiff = .true.
319  ydiff = (y_check(i)-this%xyz(2,i))**2
320  if ( ydiff .gt. this%tol) isdiff = .true.
321  zdiff = (z_check(i)-this%xyz(3,i))**2
322  if ( zdiff .gt. this%tol) isdiff = .true.
323 
324  if (isdiff) then
325  write(*,*) 'Points with coords: ', &
326  this%xyz(1, i), this%xyz(2, i), this%xyz(3, i), &
327  'Differ from interpolated coords: ', &
328  x_check(i), y_check(i), z_check(i), &
329  'Distance squared: ', &
330  xdiff, ydiff, zdiff
331  end if
332 
333  end do
334 
335  deallocate(x_check)
336  deallocate(y_check)
337  deallocate(z_check)
338 
339  if (neko_bcknd_device .eq. 1) then
340  call device_memcpy(this%el_owner, this%el_owner_d, &
341  this%n_points, host_to_device, sync = .true.)
342  end if
343 #else
344  call neko_error('Neko needs to be built with GSLIB support')
345 #endif
346  end subroutine global_interpolation_find_common
347 
360  subroutine global_interpolation_find_coords(this, x, y, z, n_points)
361  class(global_interpolation_t), intent(inout) :: this
362  integer :: n_points
363  !!Perhaps this should be kind dp
364  !!this is to get around that x,y,z often is 4 dimensional...
365  !!Should maybe add interface for 1d aswell
366  real(kind=rp) :: x(n_points,1,1,1)
367  real(kind=rp) :: y(n_points,1,1,1)
368  real(kind=rp) :: z(n_points,1,1,1)
369  integer :: i
370 
371  call this%free_points()
372 
373  this%n_points = n_points
374 
376 
377  do i = 1, n_points
378  this%xyz(1, i) = x(i,1,1,1)
379  this%xyz(2, i) = y(i,1,1,1)
380  this%xyz(3, i) = z(i,1,1,1)
381  end do
382 
384 
385  end subroutine global_interpolation_find_coords
386 
388  class(global_interpolation_t) :: this
389 
390  allocate(this%xyz(3, this%n_points))
391  allocate(this%rst(3, this%n_points))
392  allocate(this%proc_owner(this%n_points))
393  allocate(this%el_owner(this%n_points))
394  allocate(this%dist2(this%n_points))
395  allocate(this%error_code(this%n_points))
396 
397  if (neko_bcknd_device .eq. 1) &
398  call device_map(this%el_owner, this%el_owner_d, this%n_points)
399 
401 
412  subroutine global_interpolation_find_xyz(this, xyz, n_points)
413  class(global_interpolation_t), intent(inout) :: this
414  integer, intent(in) :: n_points
415  !!Perhaps this should be kind dp
416  real(kind=rp), intent(inout) :: xyz(3, n_points)
417 
418 
419  call this%free_points()
420 
421  this%n_points = n_points
422 
424 
426  call copy(this%xyz, xyz, 3 * n_points)
427 
429 
430  end subroutine global_interpolation_find_xyz
431 
443  subroutine global_interpolation_find_and_redist(this, xyz, n_points)
444  class(global_interpolation_t), intent(inout) :: this
445  integer, intent(inout) :: n_points
446  !!Perhaps this should be kind dp
447  real(kind=rp), allocatable, intent(inout) :: xyz(:,:)
448  integer :: i
449 
450 
451  call this%free_points()
452 
453  this%n_points = n_points
455 
457  call copy(this%xyz, xyz, 3 * n_points)
458 
461  call global_interpolation_redist(this)
463 
464  do i = 1, this%n_points
465  if (this%proc_owner(i) .ne. pe_rank) then
466  write(*,*) 'Redistribution failed on rank: ', pe_rank, &
467  'for point with coord: ', &
468  this%xyz(1, i), this%xyz(2, i), this%xyz(3, i)
469  exit
470  end if
471  end do
472 
473  n_points = this%n_points
474  if (allocated(xyz)) then
475  deallocate(xyz)
476  end if
477  allocate(xyz(3, n_points))
478  call copy(xyz, this%xyz, 3*n_points)
479 
480  call this%local_interp%init(this%Xh, this%rst(1,:),&
481  this%rst(2,:), this%rst(3,:), n_points)
482  this%all_points_local = .true.
483 
484 
486 
488  class(global_interpolation_t), intent(inout) :: this
489  integer, allocatable :: n_points_per_pe(:)
490  integer, allocatable :: n_points_from_pe(:)
491  integer, allocatable :: n_point_offset_from_pe(:)
492  real(kind=rp), allocatable :: xyz_send_to_pe(:,:)
493  real(kind=rp), allocatable :: new_xyz(:,:)
494  integer :: i, j, k, ierr, n_new_points, max_n_points_to_send
495 
496  n_new_points = 0
497 
498  allocate(n_points_per_pe(0:(pe_size-1)))
499  allocate(n_points_from_pe(0:(pe_size-1)))
500  n_points_per_pe = 0
501  n_points_from_pe = 0
503  do i = 1, this%n_points
504  n_points_per_pe(this%proc_owner(i)) = &
505  n_points_per_pe(this%proc_owner(i)) + 1
506  end do
509  do i = 0, (pe_size - 1)
510  call mpi_reduce(n_points_per_pe(i), n_new_points, 1, mpi_integer, &
511  mpi_sum, i, neko_comm, ierr)
512  call mpi_gather(n_points_per_pe(i), 1, mpi_integer,&
513  n_points_from_pe, 1, mpi_integer, i, neko_comm, ierr)
514  end do
515 
516  allocate(n_point_offset_from_pe(0:(pe_size-1)))
517  n_point_offset_from_pe(0) = 0
518  do i = 1, (pe_size - 1)
519  n_point_offset_from_pe(i) = n_points_from_pe(i-1)&
520  + n_point_offset_from_pe(i-1)
521  end do
522 
523  allocate(new_xyz(3, n_new_points))
524  max_n_points_to_send = maxval(n_points_per_pe)
525  allocate(xyz_send_to_pe(3, max_n_points_to_send))
526  do i = 0, (pe_size - 1)
528  k = 0
529  do j = 1, this%n_points
530  if (this%proc_owner(j) .eq. i) then
531  k = k + 1
532  xyz_send_to_pe(:,k) = this%xyz(:,j)
533  end if
534  end do
535  if (k .ne. n_points_per_pe(i)) then
536  write(*,*) 'PE: ', pe_rank, ' has k= ', k, &
537  'points for PE:', i,' but should have: ', &
538  n_points_per_pe(i)
539  call neko_error('Error in redistribution of points')
540  end if
541  call mpi_gatherv(xyz_send_to_pe,3*n_points_per_pe(i), &
542  mpi_double_precision, new_xyz,3*n_points_from_pe, &
543  3*n_point_offset_from_pe, &
544  mpi_double_precision, i, neko_comm, ierr)
545 
546  end do
547 
548  call this%free_points()
549 
550  this%n_points = n_new_points
552  call copy(this%xyz, new_xyz, 3 * n_new_points)
553 
554  deallocate(n_point_offset_from_pe)
555  deallocate(n_points_from_pe)
556  deallocate(n_points_per_pe)
557  deallocate(xyz_send_to_pe)
558 
559  end subroutine global_interpolation_redist
560 
561 
562 
566  subroutine global_interpolation_evaluate(this, interp_values, field)
567  class(global_interpolation_t), intent(inout) :: this
568  real(kind=rp), intent(inout) :: interp_values(this%n_points)
569  real(kind=rp), intent(inout) :: field(this%nelv*this%Xh%lxyz)
570 
571 
572 #ifdef HAVE_GSLIB
573  if (.not. this%all_points_local) then
574  call fgslib_findpts_eval(this%gs_handle, interp_values, &
575  1, this%error_code, 1, &
576  this%proc_owner, 1, this%el_owner, 1, &
577  this%rst, this%gdim, &
578  this%n_points, field)
579  else
580  if (this%n_points .gt. 0) &
581  call this%local_interp%evaluate(interp_values, this%el_owner, &
582  field, this%nelv)
583  end if
584 #else
585  call neko_error('Neko needs to be built with GSLIB support')
586 #endif
587 
588  end subroutine global_interpolation_evaluate
589 
590 end module global_interpolation
Definition: comm.F90:1
integer pe_rank
MPI rank.
Definition: comm.F90:28
type(mpi_comm) neko_comm
MPI communicator.
Definition: comm.F90:16
integer pe_size
MPI size of communicator.
Definition: comm.F90:31
Defines a mapping of the degrees of freedom.
Definition: dofmap.f90:35
Defines a field.
Definition: field.f90:34
Implements global_interpolation given a dofmap.
subroutine global_interpolation_free(this)
Destructor.
subroutine global_interpolation_free_points(this)
Destructor for point arrays.
subroutine global_interpolation_init_point_arrays(this)
subroutine global_interpolation_find_coords(this, x, y, z, n_points)
Finds the corresponding r,s,t coordinates in the correct global element as well as which process that...
subroutine global_interpolation_evaluate(this, interp_values, field)
Evalute the interpolated value in the points given a field on the dofmap.
subroutine global_interpolation_init_dof(this, dof, tol)
Initialize the global interpolation object on a dofmap.
subroutine global_interpolation_find_xyz(this, xyz, n_points)
Finds the corresponding r,s,t coordinates in the correct global element as well as which process that...
subroutine global_interpolation_find_and_redist(this, xyz, n_points)
Finds the corresponding r,s,t coordinates and redistributes the points to the owning rank in the corr...
subroutine global_interpolation_find_common(this)
Common routine for finding the points.
subroutine global_interpolation_init_xyz(this, x, y, z, gdim, nelv, Xh, tol)
Initialize the global interpolation object on a set of coordinates.
subroutine global_interpolation_redist(this)
Routines to obtain interpolated values on a set of points with known rst coordinates in elements loca...
Logging routines.
Definition: log.f90:34
type(log_t), public neko_log
Global log stream.
Definition: log.f90:65
integer, parameter, public log_size
Definition: log.f90:42
Definition: math.f90:60
subroutine, public copy(a, b, n)
Copy a vector .
Definition: math.f90:239
Defines a mesh.
Definition: mesh.f90:34
MPI derived types.
Definition: mpi_types.f90:34
integer, parameter, public rp
Global precision used in computations.
Definition: num_types.f90:12
Defines a function space.
Definition: space.f90:34
Defines structs that are used... Dont know if we should keep it though.
Definition: structs.f90:2
Utilities.
Definition: utils.f90:35
subroutine, public neko_warning(warning_msg)
Reports a warning to standard output.
Definition: utils.f90:245
Implements global interpolation for arbitrary points in the domain.
Interpolation on a set of points with known rst coordinates in elements local to this process....
The function space for the SEM solution fields.
Definition: space.f90:62
Pointer to array.
Definition: structs.f90:14