Octopus
index.F90
Go to the documentation of this file.
1!! Copyright (C) 2002-2014 M. Marques, A. Castro, A. Rubio, G. Bertsch, M. Oliveira
2!! Copyright (C) 2021 S. Ohlmann
3!!
4!! This program is free software; you can redistribute it and/or modify
5!! it under the terms of the GNU General Public License as published by
6!! the Free Software Foundation; either version 2, or (at your option)
7!! any later version.
8!!
9!! This program is distributed in the hope that it will be useful,
10!! but WITHOUT ANY WARRANTY; without even the implied warranty of
11!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12!! GNU General Public License for more details.
13!!
14!! You should have received a copy of the GNU General Public License
15!! along with this program; if not, write to the Free Software
16!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17!! 02110-1301, USA.
18!!
19
20#include "global.h"
21
22
28!
29module index_oct_m
30 use debug_oct_m
31 use global_oct_m
32 use iihash_oct_m
33 use io_oct_m
36 use mpi_oct_m
39
40 implicit none
41
42 private
43
44 public :: &
45 index_t, &
46 index_init, &
47 index_end, &
53 index_dump, &
54 index_load, &
56
57 type index_t
58 ! Components are public by default
59 integer :: dim
60 integer, allocatable :: nr(:,:)
61 integer, allocatable :: ll(:)
62 integer, allocatable :: enlarge(:)
63 integer(int64) :: checksum
64 integer(int64), pointer :: grid_to_spatial_global(:)
65 integer(int64), pointer :: spatial_to_grid_global(:)
66 integer :: type
67 integer :: bits
68 integer, allocatable :: offset(:)
69 integer, allocatable :: stride(:)
70 type(MPI_Win) :: window_grid_to_spatial
71 type(MPI_Win) :: window_spatial_to_grid
72 end type index_t
73
74 integer, parameter, public :: &
75 IDX_CUBIC = 1, &
76 idx_hilbert = 2
77
78
79 interface
80 subroutine hilbert_index_to_point(dim, nbits, index, point)
81 use, intrinsic :: iso_fortran_env
82 implicit none
83
84 integer, intent(in) :: dim
85 integer, intent(in) :: nbits
86 integer(int64), intent(in) :: index
87 integer, intent(out) :: point(*)
88 end subroutine hilbert_index_to_point
89
90 subroutine hilbert_point_to_index(dim, nbits, index, point)
91 use, intrinsic :: iso_fortran_env
92 implicit none
93
94 integer, intent(in) :: dim
95 integer, intent(in) :: nbits
96 integer(int64), intent(out) :: index
97 integer, intent(in) :: point(*)
98 end subroutine hilbert_point_to_index
99
100 subroutine hilbert_index_to_point_int(dim, nbits, index, point)
101 use, intrinsic :: iso_fortran_env
102 implicit none
103
104 integer, intent(in) :: dim
105 integer, intent(in) :: nbits
106 integer(int32), intent(in) :: index
107 integer, intent(out) :: point(*)
108 end subroutine hilbert_index_to_point_int
109
110 subroutine hilbert_point_to_index_int(dim, nbits, index, point)
111 use, intrinsic :: iso_fortran_env
112 implicit none
113
114 integer, intent(in) :: dim
115 integer, intent(in) :: nbits
116 integer(int32), intent(out) :: index
117 integer, intent(in) :: point(*)
118 end subroutine hilbert_point_to_index_int
119 end interface
120
121contains
124 subroutine index_init(idx, dim)
125 type(index_t), intent(inout) :: idx
126 integer, intent(in) :: dim
127
128 push_sub(index_init)
129
130 safe_allocate(idx%nr(1:2, 1:dim))
131 safe_allocate(idx%ll(1:dim))
132 safe_allocate(idx%enlarge(1:dim))
133 safe_allocate(idx%offset(1:dim))
134 safe_allocate(idx%stride(1:dim+1))
135
136 idx%dim = dim
137 idx%nr = 0
138
139 pop_sub(index_init)
140 end subroutine index_init
141
143 subroutine index_end(idx)
144 type(index_t), intent(inout) :: idx
145
146 push_sub(index_end)
147
148 safe_deallocate_a(idx%nr)
149 safe_deallocate_a(idx%ll)
150 safe_deallocate_a(idx%enlarge)
151 safe_deallocate_a(idx%offset)
152 safe_deallocate_a(idx%stride)
154 idx%dim = 0
156 pop_sub(index_end)
157 end subroutine index_end
162 integer(int64) function index_from_coords(idx, ix) result(index)
163 type(index_t), intent(in) :: idx
164 integer, intent(in) :: ix(1:idx%dim)
165
166 integer(int64) :: ispatial
168 ! No PUSH SUB, called too often
169 call index_point_to_spatial(idx, idx%dim, ispatial, ix)
170 if (ispatial < 0 .or. ispatial > ubound(idx%spatial_to_grid_global, dim=1, kind=int64)) then
171 index = 0
172 else
173 index = idx%spatial_to_grid_global(ispatial)
174 end if
175 end function index_from_coords
176
177 ! -----------------------------------------------------------------
178
179 subroutine index_from_coords_vec(idx, npoints, ix, index)
180 type(index_t), intent(in) :: idx
181 integer, intent(in) :: npoints
182 integer, intent(in) :: ix(1:idx%dim, 1:npoints)
183 integer(int64), intent(out) :: index(1:npoints)
184
185 integer :: ip
186 integer(int64) :: ispatial
187
188 ! No PUSH SUB, called too often
189 do ip = 1, npoints
190 call index_point_to_spatial(idx, idx%dim, ispatial, ix(1:idx%dim, ip))
191 if (ispatial < 0 .or. ispatial > ubound(idx%spatial_to_grid_global, dim=1, kind=int64)) then
192 index(ip) = 0
193 else
194 index(ip) = idx%spatial_to_grid_global(ispatial)
195 end if
196 end do
197
198 end subroutine index_from_coords_vec
199
200
203 subroutine index_to_coords(idx, ip, ix)
204 type(index_t), intent(in) :: idx
205 integer(int64), intent(in) :: ip
206 integer, intent(out) :: ix(:)
207
208 ! We set all ix to zero first (otherwise the non-existent dimensions would be
209 ! undefined on exit).
210 ix = 0
211 call index_spatial_to_point(idx, idx%dim, idx%grid_to_spatial_global(ip), ix)
212 end subroutine index_to_coords
213
214 ! --------------------------------------------------------------
215 subroutine index_dump(idx, np, dir, mpi_grp, namespace, ierr)
216 type(index_t), intent(in) :: idx
217 integer(int64), intent(in) :: np
218 character(len=*), intent(in) :: dir
219 type(mpi_grp_t), intent(in) :: mpi_grp
220 type(namespace_t),intent(in) :: namespace
221 integer, intent(out) :: ierr
222
223 integer :: err
224
225 push_sub(index_dump)
226
227 ierr = 0
228
229 if (mpi_grp_is_root(mpi_grp)) then
230 ! the index array is a global function and only root will write
231 assert(associated(idx%grid_to_spatial_global))
232 call io_binary_write(trim(io_workpath(dir, namespace))//"/indices.obf", np, &
233 idx%grid_to_spatial_global, err)
234 if (err /= 0) then
235 ierr = ierr + 1
236 message(1) = "Unable to write index function to '"//trim(dir)//"/indices.obf'."
237 call messages_warning(1, namespace=namespace)
238 end if
239 end if
240
241 call mpi_grp%bcast(ierr, 1, mpi_integer, 0)
242
243 pop_sub(index_dump)
244 end subroutine index_dump
245
246
247 ! --------------------------------------------------------------
249 subroutine index_load(idx, np, dir, mpi_grp, namespace, ierr)
250 type(index_t), intent(inout) :: idx
251 integer(int64), intent(in) :: np
252 character(len=*), intent(in) :: dir
253 type(mpi_grp_t), intent(in) :: mpi_grp
254 type(namespace_t), intent(in) :: namespace
255 integer, intent(out) :: ierr
256
257 integer :: err
258 integer(int64) :: ip, global_size
259 logical :: exists
260
261 push_sub(index_load)
262
263 ierr = 0
264
265 assert(associated(idx%grid_to_spatial_global))
266
267 if (mpi_grp_is_root(mpi_grp)) then
268 ! check for existence of lxyz.obf, print error message if found
269 inquire(file=trim(trim(io_workpath(dir, namespace))//"/lxyz.obf"), exist=exists)
270 if (exists) then
271 message(1) = "Found lxyz.obf file. This means you created the restart files with an old version of the code."
272 message(2) = "Please generate the restart files again with the current version of the code"
273 message(3) = "because the internal format has changed."
274 call messages_fatal(3)
275 end if
276 ! the index array is a global function and only root will write
277 call io_binary_read(trim(io_workpath(dir, namespace))//"/indices.obf", np, &
278 idx%grid_to_spatial_global, err)
279 if (err /= 0) then
280 ierr = ierr + 1
281 message(1) = "Unable to read index function from '"//trim(dir)//"/indices.obf'."
282 call messages_warning(1, namespace=namespace)
283 end if
284 end if
285
286 ! Broadcast the results and synchronize
287 call mpi_grp%bcast(ierr, 1, mpi_integer, 0)
288 if (ierr == 0) then
289 call mpi_grp%bcast(idx%grid_to_spatial_global(1), np, mpi_integer8, 0)
290 end if
291
292 ! fill global hash map
293 global_size = product(idx%nr(2, :) - idx%nr(1, :) + 1)
294 safe_allocate(idx%spatial_to_grid_global(0:global_size))
295 ! TODO: remove this restriction
296 assert(np <= huge(0_int32))
297 do ip = 1, np
298 idx%spatial_to_grid_global(idx%grid_to_spatial_global(ip)) = i8_to_i4(ip)
299 end do
300
301 pop_sub(index_load)
302 end subroutine index_load
303
304 subroutine index_spatial_to_point(idx, dim, ispatial, point)
305 type(index_t), intent(in) :: idx
306 integer, intent(in) :: dim
307 integer(int64), intent(in) :: ispatial
308 integer, intent(out) :: point(1:dim)
309
310 ! no push_sub/pop_sub, called too often
311 select case (idx%type)
312 case (idx_cubic)
313 call index_cubic_to_point(idx, dim, ispatial, point)
314 case (idx_hilbert)
315 call index_hilbert_to_point(idx, dim, ispatial, point)
316 case default
317 message(1) = "Unknown index type."
318 call messages_fatal(1)
319 end select
320 end subroutine index_spatial_to_point
321
322 subroutine index_point_to_spatial(idx, dim, ispatial, point)
323 type(index_t), intent(in) :: idx
324 integer, intent(in) :: dim
325 integer(int64), intent(out) :: ispatial
326 integer, intent(in) :: point(1:dim)
327
328 ! no push_sub/pop_sub, called too often
329 select case (idx%type)
330 case (idx_cubic)
331 call index_point_to_cubic(idx, dim, ispatial, point)
332 case (idx_hilbert)
333 call index_point_to_hilbert(idx, dim, ispatial, point)
334 case default
335 message(1) = "Unknown index type."
336 call messages_fatal(1)
337 end select
338 end subroutine index_point_to_spatial
339
340 pure subroutine index_cubic_to_point(idx, dim, icubic, point)
341 type(index_t), intent(in) :: idx
342 integer, intent(in) :: dim
343 integer(int64),intent(in) :: icubic
344 integer, intent(out) :: point(1:dim)
345
346 integer :: ii
347 integer(int64) :: tmp
348
349 ! no push_sub/pop_sub, called too often
350 tmp = icubic
351 do ii = dim, 2, -1
352 point(ii) = int(tmp/idx%stride(ii) - idx%offset(ii), int32)
353 tmp = mod(tmp, idx%stride(ii))
354 end do
355 point(1) = int(tmp - idx%offset(1), int32)
356 end subroutine index_cubic_to_point
357
358 pure subroutine index_point_to_cubic(idx, dim, icubic, point)
359 type(index_t), intent(in) :: idx
360 integer, intent(in) :: dim
361 integer(int64),intent(out) :: icubic
362 integer, intent(in) :: point(1:dim)
363
364 integer :: ii
365
366 ! no push_sub/pop_sub, called too often
367 icubic = 0
368 do ii = 1, dim
369 if (point(ii) < idx%nr(1, ii) .or. point(ii) > idx%nr(2, ii)) then
370 icubic = -1
371 return
372 end if
373 icubic = icubic + int(point(ii)+idx%offset(ii), int64) * idx%stride(ii)
374 end do
375 end subroutine index_point_to_cubic
376
377 subroutine index_hilbert_to_point(idx, dim, ihilbert, point)
378 type(index_t), intent(in) :: idx
379 integer, intent(in) :: dim
380 integer(int64), intent(in) :: ihilbert
381 integer, intent(out) :: point(:)
382
383 ! no push_sub/pop_sub, called too often
384 call hilbert_index_to_point(dim, idx%bits, ihilbert, point)
385 point(1:dim) = point(1:dim) - idx%offset(1:dim)
386 end subroutine index_hilbert_to_point
387
388 subroutine index_point_to_hilbert(idx, dim, ihilbert, point)
389 type(index_t), intent(in) :: idx
390 integer, intent(in) :: dim
391 integer(int64), intent(out) :: ihilbert
392 integer, intent(in) :: point(1:dim)
393
394 integer :: point_copy(1:dim)
395
396 ! no push_sub/pop_sub, called too often
397 point_copy(1:dim) = point(1:dim) + idx%offset(1:dim)
398 call hilbert_point_to_index(dim, idx%bits, ihilbert, point_copy)
399 end subroutine index_point_to_hilbert
400
401 ! get index along a curve that follows small parallelepipeds
402 ! this corresponds to blocked loops over n-dimensional space
403 integer(int64) function get_blocked_index(dim, point, bsize, number_of_blocks, increase_with_dimensions)
404 integer, intent(in) :: dim
405 integer, intent(in) :: point(1:dim)
406 integer, intent(in) :: bsize(1:dim)
407 integer, intent(in) :: number_of_blocks(1:dim)
408 logical, optional, intent(in) :: increase_with_dimensions
409 integer(int64) :: idim, j_local, j_block, stride_local, stride_block
410 integer :: start, stop, step
411
412 ! no push_sub/pop_sub, called too often
413 if (optional_default(increase_with_dimensions, .true.)) then
414 ! fastest increasing index is in first dimension
415 start = 1
416 stop = dim
417 step = 1
418 else
419 ! fastest increasing index is in last dimension
420 start = dim
421 stop = 1
422 step = -1
423 end if
424
425 ! j_local: index in local block
426 ! j_block: block index
427 j_local = 0
428 j_block = 0
429 stride_local = 1
430 stride_block = 1
431 do idim = start, stop, step
432 j_local = j_local + mod(point(idim), bsize(idim)) * stride_local
433 stride_local = stride_local * bsize(idim)
434 j_block = j_block + point(idim) / bsize(idim) * stride_block
435 stride_block = stride_block * number_of_blocks(idim)
436 end do
437 ! total index along the curve
438 get_blocked_index = j_local + j_block * product(int(bsize(1:dim), int64))
439 end function get_blocked_index
440
441end module index_oct_m
442
443!! Local Variables:
444!! mode: f90
445!! coding: utf-8
446!! End:
This module implements a simple hash table for non-negative integer keys and integer values.
Definition: iihash.F90:125
This module implements the index, used for the mesh points.
Definition: index.F90:122
subroutine, public index_load(idx, np, dir, mpi_grp, namespace, ierr)
Load the index arrays from a file.
Definition: index.F90:343
subroutine index_hilbert_to_point(idx, dim, ihilbert, point)
Definition: index.F90:471
subroutine, public index_end(idx)
This subroutine deallocates all memory.
Definition: index.F90:237
pure subroutine index_cubic_to_point(idx, dim, icubic, point)
Definition: index.F90:434
subroutine, public index_from_coords_vec(idx, npoints, ix, index)
Definition: index.F90:273
subroutine, public index_init(idx, dim)
This subroutine allocates memory and initializes some components.
Definition: index.F90:218
integer(int64) function, public get_blocked_index(dim, point, bsize, number_of_blocks, increase_with_dimensions)
Definition: index.F90:497
subroutine, public index_spatial_to_point(idx, dim, ispatial, point)
Definition: index.F90:398
subroutine, public index_to_coords(idx, ip, ix)
Given a global point index, this function returns the set of integer coordinates of the point.
Definition: index.F90:297
integer, parameter, public idx_hilbert
Definition: index.F90:167
subroutine, public index_dump(idx, np, dir, mpi_grp, namespace, ierr)
Definition: index.F90:309
subroutine, public index_point_to_spatial(idx, dim, ispatial, point)
Definition: index.F90:416
subroutine index_point_to_hilbert(idx, dim, ihilbert, point)
Definition: index.F90:482
integer(int64) function, public index_from_coords(idx, ix)
This function takes care of the boundary conditions for a given vector of integer coordinates it retu...
Definition: index.F90:256
pure subroutine index_point_to_cubic(idx, dim, icubic, point)
Definition: index.F90:452
Definition: io.F90:114
character(len=max_path_len) function, public io_workpath(path, namespace)
Definition: io.F90:313
subroutine, public messages_warning(no_lines, all_nodes, namespace)
Definition: messages.F90:543
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
logical function mpi_grp_is_root(grp)
Is the current MPI process of grpcomm, root.
Definition: mpi.F90:430
This is defined even when running serial.
Definition: mpi.F90:142
int true(void)