Octopus
regridding.F90
Go to the documentation of this file.
1!! Copyright (C) 2022 F. Bonafé, S. Ohlmann
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#include "global.h"
19
20
70 use debug_oct_m
71 use global_oct_m
72 use grid_oct_m
73 use iihash_oct_m
74 use mesh_oct_m
78 use parser_oct_m
82 use space_oct_m
85 use utils_oct_m
86
87 implicit none
88 public :: regridding_t
89
92 type :: regridding_t
93 private
94 class(mesh_t), pointer :: mesh_in, mesh_out
95 type(partition_transfer_t) :: partition_transfer
96 integer :: nsend, nrecv, dim
97 integer, allocatable :: order_in(:), order_out(:)
98 integer(int64), allocatable :: order_in_global(:), order_out_global(:)
99 logical, allocatable :: overlap_map(:)
100 logical :: do_restriction, do_prolongation
101 type(stencil_t) :: transfer_stencil
102 real(real64), allocatable :: weights(:)
103 integer, allocatable :: eta(:)
104 integer :: interpolation_level
105 integer :: scale_norms
106 contains
107 procedure :: dregridding_do_transfer_1, zregridding_do_transfer_1
108 procedure :: dregridding_do_transfer_2, zregridding_do_transfer_2
109 generic :: do_transfer => dregridding_do_transfer_1, zregridding_do_transfer_1
110 generic :: do_transfer => dregridding_do_transfer_2, zregridding_do_transfer_2
111 final :: regridding_finalize
112 end type regridding_t
113
114 interface regridding_t
115 procedure regridding_init
116 end interface regridding_t
117
118 integer, parameter :: &
119 LINEAR = 0, &
120 nearest_neighbor = 1, &
121 scale_none = 0, &
122 scale_norm2 = 1
123
124contains
125
126 ! ---------------------------------------------------------
128 function regridding_init(mesh_out, mesh_in, space_in, namespace) result(this)
129 class(mesh_t), target, intent(in) :: mesh_out
130 class(mesh_t), target, intent(in) :: mesh_in
131 class(space_t), intent(in) :: space_in
132 type(namespace_t), intent(in) :: namespace
133 class(regridding_t), pointer :: this
134
135 integer :: ip_in, ip_out, index(1:space_in%dim), idim, ii, is, size_array
136 integer(int64), allocatable :: global_indices_in(:), order_in(:), order_out(:)
137 integer(int64), allocatable :: global_indices_in_tmp(:)
138 integer, allocatable :: partition_in(:)
139 integer(int64) :: ipg_out, ipg_in
140 real(real64) :: spacing_ratio(1:space_in%dim)
141 integer :: index_stencil(1:space_in%dim)
142 logical :: same_eta, on_coarse_grid, found
143 type(stencil_t) :: cube_stencil
144 type(lihash_t) :: global_indices
145
146 push_sub_with_profile(regridding_init)
147
148 safe_allocate(this)
149
150 !%Variable RegriddingInterpolationLevel
151 !%Type integer
152 !%Default regridding_linear
153 !%Section Mesh
154 !%Description
155 !% Choose the interpolation level for the regridding.
156 !%Option regridding_linear 0
157 !% Use a trilinear interpolation. This is implemented similar to restriction and prolongation
158 !% operations in multigrid methods. This ensures that both directions are adjoint to each other.
159 !%Option regridding_nearest_neighbor 1
160 !% Use the nearest neighbor for the regridding. This is faster than the linear interpolation.
161 !%End
162 call parse_variable(namespace, "RegriddingInterpolationLevel", linear, this%interpolation_level)
163
164 !%Variable RegriddingRescale
165 !%Type integer
166 !%Default scale_norm2
167 !%Section Mesh
168 !%Description
169 !% Rescale the regridded quantities. Not using a rescaling can lead to bad results if the
170 !% ratio of the grid spacings is large.
171 !%Option scale_none 0
172 !% Do not rescale the regridded quantities.
173 !%Option scale_norm2 1
174 !% Scale the regridded quantities by the 2-norm of the quantity on the overlapping
175 !% region of the grid.
176 !%End
177 call parse_variable(namespace, "RegriddingRescale", scale_norm2, this%scale_norms)
178
179 ! check some conditions which are not yet supported
180 if (mesh_out%coord_system%local_basis .or. &
181 mesh_in%coord_system%local_basis .or. &
182 .not. mesh_out%coord_system%orthogonal .or. &
183 .not. mesh_in%coord_system%orthogonal .or. &
184 .not. (mesh_out%coord_system%dim == space_in%dim) .or. &
185 space_in%is_periodic()) then
186 message(1) = "Currently, regridding is only possible for orthogonal, non-periodic systems."
187 call messages_fatal(1, namespace=namespace)
188 end if
189 this%dim = space_in%dim
190 spacing_ratio(:) = mesh_out%spacing(1:space_in%dim)/mesh_in%spacing(1:space_in%dim)
192 this%do_restriction = all(spacing_ratio > m_one)
193 this%do_prolongation = all(spacing_ratio < m_one)
194 ! invert spacing ratio for prolongations
195 if (this%do_prolongation) spacing_ratio = m_one/spacing_ratio
196 ! get the integer ratio of the spacings
197 safe_allocate(this%eta(1:space_in%dim))
198 do idim = 1, space_in%dim
199 this%eta(idim) = nint(spacing_ratio(idim))
200 end do
201 if (any(abs((spacing_ratio - this%eta)/spacing_ratio) > m_epsilon)) then
202 message(1) = 'Only commensurate grids allowed for regridding.'
203 call messages_fatal(1, namespace=namespace)
204 end if
205 same_eta = .true.
206 do idim = 2, space_in%dim
207 same_eta = same_eta .and. this%eta(idim) == this%eta(idim-1)
208 end do
209 if (.not. same_eta) then
210 message(1) = 'Commensurate grids need to have same ratio in all dimensions for regridding.'
211 call messages_fatal(1, namespace=namespace)
212 end if
213
214 this%mesh_in => mesh_in
215 this%mesh_out => mesh_out
216
217 ! collect all locally available points in mesh_in that are also in mesh_out
218 call lihash_init(global_indices)
219 if (.not. this%do_prolongation) then
220 ! same spacing or restriction
221 size_array = mesh_in%np
222 safe_allocate(global_indices_in_tmp(size_array))
223
224 ii = 0
225 do ip_in = 1, mesh_in%np
226 call mesh_local_index_to_coords(mesh_in, ip_in, index)
227 if (this%do_restriction) then
228 on_coarse_grid = .true.
229 do idim = 1, space_in%dim
230 on_coarse_grid = on_coarse_grid .and. mod(index(idim), this%eta(idim)) == 0
231 end do
232 if (.not. on_coarse_grid) cycle
233 ! translate between fine and coarse grid
234 index(:) = index(:) / this%eta(:)
235 end if
236 ipg_out = mesh_global_index_from_coords(mesh_out, index)
237 if (ipg_out > 0 .and. ipg_out <= mesh_out%np_global) then
238 ii = ii + 1
239 ! store global indices of mesh_out here
240 global_indices_in_tmp(ii) = ipg_out
241 call lihash_insert(global_indices, ipg_out, ii)
242 end if
243 end do
244 if (this%do_restriction) then
245 if (this%interpolation_level == linear) then
246 call stencil_cube_get_lapl(cube_stencil, space_in%dim, order=1)
247
248 ! now get all coarse points that are reachable by a cube stencil of order 1
249 ! these are needed for the restriction
250 do ip_out = 1, ii
251 call mesh_global_index_to_coords(mesh_out, global_indices_in_tmp(ip_out), index)
252 do is = 1, cube_stencil%size
253 if (cube_stencil%center == is) cycle
254 index_stencil(:) = index(:) + cube_stencil%points(1:space_in%dim, is)
255 ipg_out = mesh_global_index_from_coords(mesh_out, index_stencil)
256 if (ipg_out > 0 .and. ipg_out <= mesh_out%np_global) then
257 ip_in = lihash_lookup(global_indices, ipg_out, found)
258 if (found) cycle
259 ii = ii + 1
260 ! enlarge array if necessary
261 if (ii >= size_array) then
262 size_array = size_array * 2
263 call make_array_larger(global_indices_in_tmp, size_array)
264 end if
265 global_indices_in_tmp(ii) = ipg_out
266 call lihash_insert(global_indices, ipg_out, ii)
267 end if
268 end do
269 end do
270 call stencil_end(cube_stencil)
271 end if
272 end if
273 call lihash_end(global_indices)
274 else
275 ! collect points for prolongation
276 ! here the logic is the other way round: we need to get all points
277 ! we need for the prolongation to the finer output mesh
278 size_array = mesh_out%np
279 safe_allocate(global_indices_in_tmp(size_array))
280 ii = 0
281 do ip_out = 1, mesh_out%np
282 call mesh_local_index_to_coords(mesh_out, ip_out, index)
283 ! translate between fine and coarse grid
284 on_coarse_grid = .true.
285 do idim = 1, space_in%dim
286 on_coarse_grid = on_coarse_grid .and. mod(index(idim), this%eta(idim)) == 0
287 end do
288 if (.not. on_coarse_grid) cycle
289 ! translate between fine and coarse grid
290 index(:) = index(:) / this%eta(:)
291 ipg_in = mesh_global_index_from_coords(mesh_in, index)
292 if (ipg_in > 0 .and. ipg_in <= mesh_in%np_global) then
293 ii = ii + 1
294 ! store global indices of mesh_in here
295 global_indices_in_tmp(ii) = ipg_in
296 call lihash_insert(global_indices, ipg_in, ii)
297 end if
298 end do
299 call stencil_cube_get_lapl(cube_stencil, space_in%dim, order=1)
300
301 ! now get all coarse points that are reachable by a cube stencil of order 1 on mesh_in
302 ! these are needed for the prolongation
303 do ip_in = 1, ii
304 call mesh_global_index_to_coords(mesh_in, global_indices_in_tmp(ip_in), index)
305 do is = 1, cube_stencil%size
306 if (cube_stencil%center == is) cycle
307 index_stencil(:) = index(:) + cube_stencil%points(1:space_in%dim, is)
308 ipg_in = mesh_global_index_from_coords(mesh_in, index_stencil)
309 if (ipg_in > 0 .and. ipg_in <= mesh_in%np_global) then
310 ip_out = lihash_lookup(global_indices, ipg_in, found)
311 if (found) cycle
312 ii = ii + 1
313 ! enlarge array if necessary
314 if (ii >= size_array) then
315 size_array = size_array * 2
316 call make_array_larger(global_indices_in_tmp, size_array)
317 end if
318 global_indices_in_tmp(ii) = ipg_in
319 call lihash_insert(global_indices, ipg_in, ii)
320 end if
321 end do
322 end do
323 call stencil_end(cube_stencil)
324 call lihash_end(global_indices)
325 end if
326
327 safe_allocate(global_indices_in(ii))
328 safe_allocate(partition_in(ii))
329 global_indices_in(1:ii) = global_indices_in_tmp(1:ii)
330 safe_deallocate_a(global_indices_in_tmp)
331
332 if (.not. this%do_prolongation) then
333 if (mesh_out%parallel_in_domains) then
334 ! determine where the points of mesh_out are stored
335 call partition_get_partition_number(mesh_out%partition, ii, global_indices_in, partition_in)
336 else
337 partition_in = 1
338 end if
339 else
340 if (mesh_in%parallel_in_domains) then
341 ! determine where the points of mesh_in are stored
342 call partition_get_partition_number(mesh_in%partition, ii, global_indices_in, partition_in)
343 else
344 partition_in = 1
345 end if
346 end if
347
348 ! Init partition transfer
349 ! need inverse direction for prolongations
350 call partition_transfer_init(this%partition_transfer, ii, global_indices_in, &
351 mesh_in%mpi_grp, mesh_out%mpi_grp, partition_in, &
352 this%nsend, this%nrecv, order_in, order_out, inverse=this%do_prolongation)
353 ! convert from global to local indices
354 safe_allocate(this%order_in(1:this%nsend))
355 safe_allocate(this%order_in_global(1:this%nsend))
356 safe_allocate(this%order_out(1:this%nrecv))
357 safe_allocate(this%order_out_global(1:this%nrecv))
358
359 ! get the mapping for mesh_in in the order given by the global indices of mesh_out
360 do ip_in = 1, this%nsend
361 if (this%do_restriction) then
362 ! in this case, we have an index on mesh_out
363 this%order_in_global(ip_in) = order_in(ip_in)
364 this%order_in(ip_in) = 0
365 else if (this%do_prolongation) then
366 ! in this case, we have an index on mesh_in
367 this%order_in_global(ip_in) = order_in(ip_in)
368 this%order_in(ip_in) = mesh_global2local(mesh_in, this%order_in_global(ip_in))
369 else
370 ! convert back from the global index of mesh_out to a local index of mesh_in
371 call mesh_global_index_to_coords(mesh_out, order_in(ip_in), index)
372 this%order_in_global(ip_in) = mesh_global_index_from_coords(mesh_in, index)
373 this%order_in(ip_in) = mesh_global2local(mesh_in, this%order_in_global(ip_in))
374 end if
375 if (.not. this%do_restriction) then
376 if (this%order_in(ip_in) == 0 .or. this%order_in(ip_in) > mesh_in%np) then
377 write(message(1),'(a,i10,a,i10)') "Error in regridding part 1: mesh point ", &
378 this%order_in(ip_in), " is not stored in partition ", mesh_in%pv%partno
379 call messages_fatal(1, namespace=namespace)
380 end if
381 end if
382 end do
383
384 ! for mapping back to the global grid after the transfer, convert to local indices of mesh_out
385 do ip_out = 1, this%nrecv
386 if (.not. this%do_prolongation) then
387 this%order_out_global(ip_out) = order_out(ip_out)
388 this%order_out(ip_out) = mesh_global2local(mesh_out, order_out(ip_out))
389 else
390 ! store the global index of mesh_in
391 this%order_out_global(ip_out) = order_out(ip_out)
392 this%order_out(ip_out) = 0
393 end if
394 if (.not. this%do_prolongation) then
395 if (this%order_out(ip_out) == 0 .or. this%order_out(ip_out) > mesh_out%np) then
396 write(message(1),'(a,i10,a,i10)') "Error in regridding part 2: mesh point ", &
397 this%order_out(ip_out), " is not stored in partition ", mesh_out%pv%partno
398 call messages_fatal(1, namespace=namespace)
399 end if
400 end if
401 end do
402
403 if (this%interpolation_level == linear) then
404 ! create transfer stencil
405 if (this%do_restriction .or. this%do_prolongation) then
406 call stencil_cube_get_lapl(this%transfer_stencil, space_in%dim, order=this%eta(1)-1)
407 safe_allocate(this%weights(1:this%transfer_stencil%size))
408 this%weights(:) = m_one
409 do ii = 1, this%transfer_stencil%size
410 do idim = 1, space_in%dim
411 ! the weights correspond to linear interpolation in d dimensions
412 this%weights(ii) = this%weights(ii) * &
413 (m_one - abs(this%transfer_stencil%points(idim, ii))/real(this%eta(idim), real64))
414 ! for the restriction, we need to divide by eta for each dimension
415 ! like this, restriction is adjoint to prolongation
416 if (this%do_restriction) then
417 this%weights(ii) = this%weights(ii) / real(this%eta(idim), real64)
418 end if
419 end do
420 end do
421 end if
422 end if
423
424 select case (this%scale_norms)
425 case(scale_none)
426 ! do nothing
427 case(scale_norm2)
428 ! create overlap map
429 safe_allocate(this%overlap_map(1:this%mesh_in%np))
430 this%overlap_map = this%mesh_out%box%contains_points(this%mesh_in%np, this%mesh_in%x)
431 end select
432
433 safe_deallocate_a(partition_in)
434 safe_deallocate_a(global_indices_in)
435 safe_deallocate_a(order_in)
436 safe_deallocate_a(order_out)
437
438 pop_sub_with_profile(regridding_init)
439 end function regridding_init
440
441 subroutine regridding_finalize(this)
442 type(regridding_t), intent(inout) :: this
443
444 push_sub(regridding_finalize)
445
446 call partition_transfer_end(this%partition_transfer)
447 safe_deallocate_a(this%eta)
448 safe_deallocate_a(this%order_in)
449 safe_deallocate_a(this%order_in_global)
450 safe_deallocate_a(this%order_out)
451 safe_deallocate_a(this%order_out_global)
452 safe_deallocate_a(this%weights)
453 select case (this%scale_norms)
454 case(scale_none)
455 ! do nothing
456 case(scale_norm2)
457 safe_deallocate_a(this%overlap_map)
458 end select
459
460 pop_sub(regridding_finalize)
461 end subroutine regridding_finalize
462
463#include "real.F90"
464#include "regridding_inc.F90"
465#include "undef.F90"
466
467#include "complex.F90"
468#include "regridding_inc.F90"
469#include "undef.F90"
470
471end module regridding_oct_m
real(real64), parameter, public m_epsilon
Definition: global.F90:203
real(real64), parameter, public m_one
Definition: global.F90:188
This module implements the underlying real-space grid.
Definition: grid.F90:117
This module implements a simple hash table for non-negative integer keys and integer values.
Definition: iihash.F90:125
integer function, public lihash_lookup(h, key, found)
Look up a value in the hash table h. If found is present, it indicates if key could be found in the t...
Definition: iihash.F90:334
subroutine, public lihash_insert(h, key, val)
Insert a (key, val) pair into the hash table h.
Definition: iihash.F90:308
subroutine, public lihash_end(h)
Free a hash table.
Definition: iihash.F90:286
subroutine, public lihash_init(h)
Initialize a hash table h.
Definition: iihash.F90:263
This module defines various routines, operating on mesh functions.
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:118
subroutine, public mesh_global_index_to_coords(mesh, ipg, ix)
Given a global point index, this function returns the set of integer coordinates of the point.
Definition: mesh.F90:873
integer(int64) function, public mesh_global_index_from_coords(mesh, ix)
This function returns the true global index of the point for a given vector of integer coordinates.
Definition: mesh.F90:864
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:895
integer function, public mesh_global2local(mesh, ipg)
This function returns the local mesh index for a given global index.
Definition: mesh.F90:915
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:160
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
Definition: messages.F90:420
subroutine, public partition_transfer_init(this, np, global_index, mpi_grp_in, mpi_grp_out, part_out, nsend, nrec, order_in, order_out, inverse)
initialize the partition transfer object
subroutine, public partition_transfer_end(this)
Implementation details for regridding.
Definition: regridding.F90:162
integer, parameter nearest_neighbor
Definition: regridding.F90:211
class(regridding_t) function, pointer regridding_init(mesh_out, mesh_in, space_in, namespace)
Generate a re-mapping of points from mesh_in to mesh_out.
Definition: regridding.F90:222
subroutine regridding_finalize(this)
Definition: regridding.F90:535
integer, parameter scale_norm2
Definition: regridding.F90:211
integer, parameter scale_none
Definition: regridding.F90:211
This module defines routines, generating operators for a cubic stencil.
subroutine, public stencil_cube_get_lapl(this, dim, order)
This module defines stencils used in Octopus.
Definition: stencil.F90:135
subroutine, public stencil_end(this)
Definition: stencil.F90:214
This module is intended to contain simple general-purpose utility functions and procedures.
Definition: utils.F90:118
subroutine, public make_array_larger(array, new_size)
Definition: utils.F90:581
contains the information of the meshes and provides the transfer functions
Definition: regridding.F90:185
int true(void)