Octopus
centroids.F90
Go to the documentation of this file.
1!! Copyright (C) 2024 A. Buccheri
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
20module centroids_oct_m
21 use, intrinsic :: iso_fortran_env
22 use comm_oct_m, only: comm_allreduce
23 use debug_oct_m
24 use global_oct_m
25 use io_oct_m
26 use mesh_oct_m
28 use mpi_oct_m, only: mpi_grp_t, mpi_integer, mpi_integer8
31 use sort_oct_m
32
33 implicit none
34 private
35
36 public :: centroids_t, &
38
64 type centroids_t
65 private
66 integer :: ndim
67 real(real64), allocatable :: positions(:, :)
68 integer :: n_centroids_global
69 integer :: n_centroids
70 integer(int64), allocatable :: icg_to_ipg(:)
71 ! Defined in full on every process.
72 ! Kept private, but one could add a getter if required
73 integer, allocatable :: ic_to_icg(:)
74 integer, allocatable :: ic_to_ip(:)
75
76 contains
77 ! Initialize
78 procedure :: init => centroids_init
79 procedure :: init_from_file => centroids_init_from_file
80 ! Getters
81 procedure :: npoints => centroids_get_n_centroids
82 procedure :: npoints_global => centroids_get_n_centroids_global
83 procedure :: get_local_position => centroids_get_local_position
84 procedure :: get_all_positions => centroids_get_all_positions
85 ! ic -> ip
86 procedure :: to_local_mesh_index => centroids_get_local_mesh_index
87 procedure :: local_mesh_indices => centroids_get_local_mesh_indices
88 ! ic -> icg
89 procedure :: to_global_index => centroids_local_index_to_global_index
90 procedure :: global_indices => centroids_local_index_to_global_indices
91 ! ic -> ipg
92 procedure :: to_global_mesh_index => centroids_global_centroid_to_global_mesh_index
93 procedure :: global_mesh_indices => centroids_global_centroid_to_global_mesh_indices
94 ! IO
95 procedure :: output_all_indices => centroids_output_all_indices
96 ! Finalize
97 procedure :: end => centroids_end
98 final :: centroids_finalize
99 end type centroids_t
100
101contains
102
106 subroutine centroids_init(this, mesh, positions, check_duplicates)
107 class(centroids_t), intent(out) :: this
108 class(mesh_t), intent(in ) :: mesh
109 real(real64), intent(in ) :: positions(:, :)
110 logical, optional, intent(in ) :: check_duplicates
111 ! following discretisation of cartesian coordinates
112
113 push_sub(centroids_init)
114
115 this%ndim = size(positions, 1)
116 this%n_centroids_global = size(positions, 2)
117 safe_allocate_source(this%positions(1:this%ndim, 1:this%n_centroids_global), positions)
119 call exact_centroids_init_index_maps(this, mesh, optional_default(check_duplicates, .false.))
120
121 pop_sub(centroids_init)
122
123 end subroutine centroids_init
124
125
127 subroutine centroids_init_from_file(this, mesh, fname, check_duplicates)
128 class(centroids_t), intent(out) :: this
129 class(mesh_t), intent(in ) :: mesh
130 character(len=*), intent(in ) :: fname
131 logical, optional, intent(in ) :: check_duplicates
132
134
135 this%ndim = mesh%box%dim
136 call centroids_read_all_indices(mesh%mpi_grp, fname, this%n_centroids_global, this%icg_to_ipg)
137 ! This step might not strictly be necessary, but it populates this%positions
139 call exact_centroids_init_index_maps(this, mesh, optional_default(check_duplicates, .false.))
140
142
143 end subroutine centroids_init_from_file
144
145
147 pure function centroids_get_n_centroids(this) result(n_centroids)
148 class(centroids_t), intent(in) :: this
149 integer :: n_centroids
150
151 n_centroids = this%n_centroids
152
153 end function centroids_get_n_centroids
154
155
157 pure function centroids_get_n_centroids_global(this) result(n_centroids_global)
158 class(centroids_t), intent(in) :: this
159 integer :: n_centroids_global
160
161 n_centroids_global = this%n_centroids_global
176 pure function centroids_global_centroid_to_global_mesh_index(this, ic) result(ipg)
177 class(centroids_t), intent(in) :: this
178 integer, intent(in) :: ic
179 integer(int64) :: ipg
180 integer :: icg
182 icg = this%ic_to_icg(ic)
183 ipg = this%icg_to_ipg(icg)
186
189 pure function centroids_global_centroid_to_global_mesh_indices(this) result(global_mesh_indices)
190 class(centroids_t), intent(in) :: this
191 integer(int64), allocatable :: global_mesh_indices(:)
193 integer :: ic, icg
194
195 allocate(global_mesh_indices(this%n_centroids))
196 do ic = 1, this%n_centroids
197 icg = this%ic_to_icg(ic)
198 global_mesh_indices(ic) = this%icg_to_ipg(icg)
199 enddo
200
202
203
205 pure function centroids_get_local_mesh_index(this, ic) result(ip)
206 class(centroids_t), intent(in) :: this
207 integer, intent(in) :: ic
208 integer :: ip
209
210 ip = this%ic_to_ip(ic)
211
213
214
216 pure function centroids_get_local_mesh_indices(this) result(mesh_indices)
217 class(centroids_t), intent(in) :: this
218 integer, allocatable :: mesh_indices(:)
219
220 allocate(mesh_indices(this%n_centroids), source=this%ic_to_ip)
221
223
224
226 pure function centroids_local_index_to_global_index(this, ic) result(icg)
227 class(centroids_t), intent(in) :: this
228 integer, intent(in) :: ic
229 integer :: icg
230
231 icg = this%ic_to_icg(ic)
232
234
235
237 pure function centroids_local_index_to_global_indices(this) result(global_centroid_indices)
238 class(centroids_t), intent(in) :: this
239 integer, allocatable :: global_centroid_indices(:)
240
241 allocate(global_centroid_indices(this%n_centroids), source=this%ic_to_icg)
244
245
247 pure function centroids_get_local_position(this, mesh, ic) result(pos)
248 class(centroids_t), intent(in) :: this
249 class(mesh_t), intent(in) :: mesh
250 integer, intent(in) :: ic
251
252 real(real64) :: pos(this%ndim)
253
254 pos(:) = mesh%x(this%ic_to_ip(ic), :)
255
257
258
260 pure function centroids_get_all_positions(this) result(positions)
261 class(centroids_t), intent(in) :: this
262 real(real64), allocatable :: positions(:, :)
263
264 allocate(positions(this%ndim, this%n_centroids_global), source=this%positions)
265
266 end function centroids_get_all_positions
267
268
270 subroutine centroids_output_all_indices(this, namespace, grp, fname)
271 class(centroids_t), intent(in) :: this
272 type(namespace_t), intent(in) :: namespace
273 type(mpi_grp_t), intent(in) :: grp
274 character(len=*), intent(in) :: fname
275
276 integer :: unit, icg
277
279
280 write(message(1),'(a)') 'Info: Writing interpolation points to file: ' // trim(adjustl(fname))
281 call messages_info(1, namespace=namespace)
282
283 unit = io_open(trim(adjustl(fname)), action='write', grp=grp)
285 if (grp%is_root()) then
286 write(unit, *) 'N centroids', this%n_centroids_global
287 do icg = 1, this%n_centroids_global
288 write(unit, *) this%icg_to_ipg(icg)
289 enddo
290 endif
291
292 call io_close(unit, grp=grp)
293
295
296 end subroutine centroids_output_all_indices
297
298
300 subroutine centroids_read_all_indices(grp, fname, n_centroids_global, icg_to_ipg)
301 type(mpi_grp_t), intent(in ) :: grp
302 character(len=*), intent(in ) :: fname
303 integer, intent(out) :: n_centroids_global
304 ! over the whole mesh/system
305 integer(int64), allocatable, intent(out) :: icg_to_ipg(:)
306 ! to global mesh indices, defined for all processed
307
308 integer :: icg, iunit
309 character(len=11) :: dummy
310
312
313 iunit = io_open(trim(adjustl(fname)), action='read', grp=grp)
314
315 if (grp%is_root()) then
316 read(iunit, *) dummy, n_centroids_global
317 safe_allocate(icg_to_ipg(1:n_centroids_global))
318 do icg = 1, n_centroids_global
319 read(iunit, *) icg_to_ipg(icg)
320 enddo
321 endif
322
323 call io_close(iunit, grp=grp)
324
325 call grp%bcast(n_centroids_global, 1, mpi_integer, 0)
326 if (.not. grp%is_root()) safe_allocate(icg_to_ipg(1:n_centroids_global))
327 call grp%bcast(icg_to_ipg, n_centroids_global, mpi_integer8, 0)
328
330
331 end subroutine centroids_read_all_indices
333
335 subroutine centroids_end(this)
336 class(centroids_t), intent(inout) :: this
337
338 push_sub(centroids_end)
339
340 safe_deallocate_a(this%positions)
341 safe_deallocate_a(this%icg_to_ipg)
342 safe_deallocate_a(this%ic_to_icg)
343 safe_deallocate_a(this%ic_to_ip)
344
345 pop_sub(centroids_end)
346
347 end subroutine centroids_end
348
349
351 subroutine centroids_finalize(this)
352 type(centroids_t), intent(inout) :: this
353
354 push_sub(centroids_finalize)
355 call this%end()
356 pop_sub(centroids_finalize)
357
358 end subroutine centroids_finalize
359
360
361 ! Helper functions for initialisation, hence not type-bound or exposed
362
364 subroutine convert_coordinates_to_global_indices(this, mesh)
365 type(centroids_t), intent(inout) :: this
366 class(mesh_t), intent(in ) :: mesh
367
368 real(real64) :: chi(this%ndim)
369 integer :: ix(this%ndim)
370 integer :: icg
371 integer(int64) :: ipg
372
374
375 ! Convert coordinate to global grid index
376 safe_allocate(this%icg_to_ipg(1:this%n_centroids_global))
377 do icg = 1, this%n_centroids_global
378 chi = mesh%coord_system%from_cartesian(this%positions(:, icg))
379 ! See the definition of mesh_x_global, for example
380 ix = nint(chi / mesh%spacing)
381 ipg = mesh_global_index_from_coords(mesh, ix)
382 this%icg_to_ipg(icg) = ipg
383 enddo
384
386
388
389
391 subroutine convert_global_indices_to_coordinates(this, mesh)
392 type(centroids_t), intent(inout) :: this
393 class(mesh_t), intent(in ) :: mesh
394
395 integer :: icg
396 integer(int64) :: ipg
397
399
400 safe_allocate(this%positions(1:this%ndim, 1:this%n_centroids_global))
401
402 do icg = 1, this%n_centroids_global
403 ipg = this%icg_to_ipg(icg)
404 this%positions(1:this%ndim, icg) = mesh_x_global(mesh, ipg)
405 enddo
406
408
410
411
419 subroutine exact_centroids_init_index_maps(this, mesh, check_duplicates)
420 type(centroids_t), intent(inout) :: this
421 class(mesh_t), intent(in ) :: mesh
422 logical, intent(in ) :: check_duplicates
423 ! following discretisation of cartesian coordinates
424
425 integer :: icg, ip
426 integer(int64) :: ipg
427 integer, allocatable :: tmp_ic_to_icg(:), tmp_ic_to_ip(:)
428#if !defined(NDEBUG)
429 integer :: n_centroids_global
430#endif
431
433
434 ! Having discretised the coordinates to grid indices, check for and remove any duplicate points
435 ! TODO(Alex) Annoyance with this is that it changes the order of the centroid indices
436 ! compared to not running check_duplicates, due to use of sorting.
437 ! Additionally, this may not be the desired behaviour. I.e. it might be better to select
438 ! the next-best discrete point, or stop Octopus
439 if (check_duplicates) then
440 call remove_duplicates_int64(this%icg_to_ipg)
441 this%n_centroids_global = size(this%icg_to_ipg)
442 endif
443
444 ! Local to this process
445 this%n_centroids = 0
446 ! Allocated with an upper bound
447 safe_allocate(tmp_ic_to_icg(1:this%n_centroids_global))
448 safe_allocate(tmp_ic_to_ip(1:this%n_centroids_global))
449
450 ! Assign grid indices local to this domain
451 do icg = 1, this%n_centroids_global
452 ipg = this%icg_to_ipg(icg)
453 ip = mesh_global2local(mesh, ipg)
454 if (ip > 0 .and. ip <= mesh%np) then
455 this%n_centroids = this%n_centroids + 1
456 tmp_ic_to_icg(this%n_centroids) = icg
457 tmp_ic_to_ip(this%n_centroids) = ip
458 endif
459 enddo
460
461#if !defined(NDEBUG)
462 ! Total number of centroids must be conserved over all domains
463 n_centroids_global = this%n_centroids
464 call comm_allreduce(mesh%mpi_grp, n_centroids_global)
465 assert(n_centroids_global == this%n_centroids_global)
466#endif
467
468 ! Reallocate with correct size
469 safe_allocate_source(this%ic_to_icg(1:this%n_centroids), tmp_ic_to_icg(1:this%n_centroids))
470 safe_deallocate_a(tmp_ic_to_icg)
471
472 safe_allocate_source(this%ic_to_ip(1:this%n_centroids), tmp_ic_to_ip(1:this%n_centroids))
473 safe_deallocate_a(tmp_ic_to_ip)
474
476
478
480 subroutine remove_duplicates_int64(indices)
481 integer(int64), intent(inout), allocatable :: indices(:)
482 ! Out: Ordered set of indices, with any duplicates removed
483
484 integer :: n_duplicates, cnt, n, i
485 integer(int64), allocatable :: unique_indices(:)
488
489 n = size(indices)
490 call sort(indices)
491
492 allocate(unique_indices(n))
493
494 n_duplicates = 0
495 cnt = 1
496 unique_indices(1) = indices(1)
497
498 do i = 1, n - 1
499 if (indices(i) == indices(i+1)) then
500 n_duplicates = n_duplicates + 1
501 cycle
502 endif
503 cnt = cnt + 1
504 unique_indices(cnt) = indices(i+1)
505 enddo
506
507 safe_deallocate_a(indices)
508 safe_allocate_source(indices(1:cnt), unique_indices(1:cnt))
509 safe_deallocate_a(unique_indices)
510
511 if (debug%info .and. n_duplicates > 0) then
512 write(message(1), '(I0)') n_duplicates
513 message(1) = trim(message(1)) // " duplicates found after discretisation of centroid positions"
514 write(message(2), '(I0)') cnt
515 message(2) = " Removed the duplicate/s and resized 'indices' array to " // trim(message(2))
516 call messages_warning(2)
517 endif
518
520
521 end subroutine remove_duplicates_int64
522
523end module centroids_oct_m
524
525!! Local Variables:
526!! mode: f90
527!! coding: utf-8
528!! End:
This is the common interface to a sorting routine. It performs the shell algorithm,...
Definition: sort.F90:151
subroutine centroids_init_from_file(this, mesh, fname, check_duplicates)
Initialise a centroids_t instance from grid points in file.
Definition: centroids.F90:223
subroutine centroids_end(this)
Type-bound routine to manually deallocate a centroids instance.
Definition: centroids.F90:431
pure integer(int64) function centroids_global_centroid_to_global_mesh_index(this, ic)
Given centroid index ic, return the global mesh index ipg.
Definition: centroids.F90:272
subroutine, public centroids_read_all_indices(grp, fname, n_centroids_global, icg_to_ipg)
Read all centroid indices from a file written using centroids_output_all_indices.
Definition: centroids.F90:396
subroutine remove_duplicates_int64(indices)
Remove duplicates from an array of integers.
Definition: centroids.F90:576
pure integer function, dimension(:), allocatable centroids_get_local_mesh_indices(this)
Return mesh indices ip for all centroids in this domain.
Definition: centroids.F90:312
subroutine centroids_finalize(this)
Finalize a centroids instance.
Definition: centroids.F90:447
subroutine convert_coordinates_to_global_indices(this, mesh)
Convert coordinates to the mesh global index.
Definition: centroids.F90:460
pure integer function, dimension(:), allocatable centroids_local_index_to_global_indices(this)
Return global centroid indices for all centroids in this domain.
Definition: centroids.F90:333
pure integer function centroids_local_index_to_global_index(this, ic)
Get the global centroid index, given centroid ic this domain.
Definition: centroids.F90:322
subroutine centroids_init(this, mesh, positions, check_duplicates)
Initialise a centroids_t instance.
Definition: centroids.F90:202
pure integer(int64) function, dimension(:), allocatable centroids_global_centroid_to_global_mesh_indices(this)
Return global mesh indices for all centroids in this domain.
Definition: centroids.F90:285
pure real(real64) function, dimension(this%ndim) centroids_get_local_position(this, mesh, ic)
Get the position of the centroid ic.
Definition: centroids.F90:343
subroutine centroids_output_all_indices(this, namespace, grp, fname)
Output all centroid indices, across all domains.
Definition: centroids.F90:366
subroutine exact_centroids_init_index_maps(this, mesh, check_duplicates)
Initialise index maps with centroid points that are commensurate with the grid.
Definition: centroids.F90:515
subroutine convert_global_indices_to_coordinates(this, mesh)
Convert mesh global index to coordinates.
Definition: centroids.F90:487
pure integer function centroids_get_local_mesh_index(this, ic)
Get the mesh index ip of centroid ic in this domain.
Definition: centroids.F90:301
pure integer function centroids_get_n_centroids(this)
Number of centroids in local domain.
Definition: centroids.F90:243
pure real(real64) function, dimension(:, :), allocatable centroids_get_all_positions(this)
Return a copy of all centroid positions.
Definition: centroids.F90:356
pure integer function centroids_get_n_centroids_global(this)
Total number of centroids in system.
Definition: centroids.F90:253
type(debug_t), save, public debug
Definition: debug.F90:158
Definition: io.F90:116
subroutine, public io_close(iunit, grp)
Definition: io.F90:466
integer function, public io_open(file, namespace, action, status, form, position, die, recl, grp)
Definition: io.F90:401
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:120
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:916
integer function, public mesh_global2local(mesh, ipg)
This function returns the local mesh index for a given global index.
Definition: mesh.F90:969
real(real64) function, dimension(1:mesh%box%dim), public mesh_x_global(mesh, ipg)
Given a global point index, this function returns the coordinates of the point.
Definition: mesh.F90:814
subroutine, public messages_warning(no_lines, all_nodes, namespace)
Definition: messages.F90:531
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:162
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
Definition: messages.F90:600
This module is intended to contain "only mathematical" functions and procedures.
Definition: sort.F90:119
Encapsulate centroid points and their indexing across a domain-decomposed mesh.
Definition: centroids.F90:159
Describes mesh distribution to nodes.
Definition: mesh.F90:187
This is defined even when running serial.
Definition: mpi.F90:144