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_comm, mpi_world
30 use sort_oct_m
31
32 implicit none
33 private
34
35 public :: centroids_t
36
62 type centroids_t
63 private
64 integer :: ndim
65 real(real64), allocatable :: positions(:, :)
66 integer :: n_centroids_global
67 integer :: n_centroids
68 integer(int64), allocatable :: icg_to_ipg(:)
69 ! Kept private, but one could add a getter if required
70 integer, allocatable :: ic_to_icg(:)
71 integer, allocatable :: ic_to_ip(:)
72
73 contains
74 procedure :: init => centroids_init
75 procedure :: npoints => centroids_get_n_centroids
76 procedure :: npoints_global => centroids_get_n_centroids_global
77 procedure :: get_local_position => centroids_get_local_position
78 procedure :: get_all_positions => centroids_get_all_positions
79 ! ic -> ip
80 procedure :: to_local_mesh_index => centroids_get_local_mesh_index
81 procedure :: local_mesh_indices => centroids_get_local_mesh_indices
82 ! ic -> icg
83 procedure :: to_global_index => centroids_local_index_to_global_index
84 procedure :: global_indices => centroids_local_index_to_global_indices
85 ! ic -> ipg
86 procedure :: to_global_mesh_index => centroids_global_centroid_to_global_mesh_index
87 procedure :: global_mesh_indices => centroids_global_centroid_to_global_mesh_indices
88 ! IO
89 procedure :: output_all_indices => centroids_output_all_indices
90 final :: centroids_finalize
91 end type centroids_t
92
93contains
94
98 subroutine centroids_init(this, mesh, positions, check_duplicates)
99 class(centroids_t), intent(out) :: this
100 class(mesh_t), intent(in ) :: mesh
101 real(real64), intent(in ) :: positions(:, :)
102 logical, optional, intent(in ) :: check_duplicates
103 ! following discretisation of cartesian coordinates
104
105 push_sub(centroids_init)
106
107 this%ndim = size(positions, 1)
108 this%n_centroids_global = size(positions, 2)
109 safe_allocate_source(this%positions(1:this%ndim, 1:this%n_centroids_global), positions)
110 call exact_centroids_init_index_maps(this, mesh, optional_default(check_duplicates, .false.))
111
112 pop_sub(centroids_init)
114 end subroutine centroids_init
115
116
118 pure function centroids_get_n_centroids(this) result(n_centroids)
119 class(centroids_t), intent(in) :: this
120 integer :: n_centroids
121
122 n_centroids = this%n_centroids
123
124 end function centroids_get_n_centroids
125
126
128 pure function centroids_get_n_centroids_global(this) result(n_centroids_global)
129 class(centroids_t), intent(in) :: this
130 integer :: n_centroids_global
131
132 n_centroids_global = this%n_centroids_global
133
135
136
147 pure function centroids_global_centroid_to_global_mesh_index(this, ic) result(ipg)
148 class(centroids_t), intent(in) :: this
149 integer, intent(in) :: ic
150 integer(int64) :: ipg
151 integer :: icg
152
153 icg = this%ic_to_icg(ic)
154 ipg = this%icg_to_ipg(icg)
160 pure function centroids_global_centroid_to_global_mesh_indices(this) result(global_mesh_indices)
161 class(centroids_t), intent(in) :: this
162 integer(int64), allocatable :: global_mesh_indices(:)
164 integer :: ic, icg
165
166 allocate(global_mesh_indices(this%n_centroids))
167 do ic = 1, this%n_centroids
168 icg = this%ic_to_icg(ic)
169 global_mesh_indices(ic) = this%icg_to_ipg(icg)
170 enddo
176 pure function centroids_get_local_mesh_index(this, ic) result(ip)
177 class(centroids_t), intent(in) :: this
178 integer, intent(in) :: ic
179 integer :: ip
181 ip = this%ic_to_ip(ic)
184
185
187 pure function centroids_get_local_mesh_indices(this) result(mesh_indices)
188 class(centroids_t), intent(in) :: this
189 integer, allocatable :: mesh_indices(:)
190
191 allocate(mesh_indices(this%n_centroids), source=this%ic_to_ip)
192
194
195
197 pure function centroids_local_index_to_global_index(this, ic) result(icg)
198 class(centroids_t), intent(in) :: this
199 integer, intent(in) :: ic
200 integer :: icg
201
202 icg = this%ic_to_icg(ic)
203
205
206
208 pure function centroids_local_index_to_global_indices(this) result(global_centroid_indices)
209 class(centroids_t), intent(in) :: this
210 integer, allocatable :: global_centroid_indices(:)
212 allocate(global_centroid_indices(this%n_centroids), source=this%ic_to_icg)
213
215
216
218 pure function centroids_get_local_position(this, mesh, ic) result(pos)
219 class(centroids_t), intent(in) :: this
220 class(mesh_t), intent(in) :: mesh
221 integer, intent(in) :: ic
222
223 real(real64) :: pos(this%ndim)
224
225 pos(:) = mesh%x(this%ic_to_ip(ic), :)
226
228
229
231 pure function centroids_get_all_positions(this) result(positions)
232 class(centroids_t), intent(in) :: this
233 real(real64), allocatable :: positions(:, :)
234
235 allocate(positions(this%ndim, this%n_centroids_global), source=this%positions)
236
237 end function centroids_get_all_positions
238
239
240 ! TODO(Alex) Would be useful to output a domain index
242 subroutine centroids_output_all_indices(this, fname)
243 class(centroids_t), intent(in) :: this
244 character(len=*), intent(in) :: fname
245
246 integer :: unit, icg
247
249
250 unit = io_open(trim(adjustl(fname)), action='write')
251
252 write(unit, *) 'N centroids', this%n_centroids_global
253 do icg = 1, this%n_centroids_global
254 write(unit, *) this%icg_to_ipg(icg)
255 enddo
256
257 call io_close(unit)
258
260
261 end subroutine centroids_output_all_indices
262
263
265 subroutine centroids_finalize(this)
266 type(centroids_t), intent(inout) :: this
267
268 safe_deallocate_a(this%positions)
269 safe_deallocate_a(this%icg_to_ipg)
270 safe_deallocate_a(this%ic_to_icg)
271
272 end subroutine centroids_finalize
273
274 ! Helper function for initialisation, hence not type-bound or exposed
275
286 subroutine exact_centroids_init_index_maps(this, mesh, check_duplicates)
287 type(centroids_t), intent(inout) :: this
288 class(mesh_t), intent(in ) :: mesh
289 logical, intent(in ) :: check_duplicates
290 ! following discretisation of cartesian coordinates
291
292 real(real64) :: chi(this%ndim)
293 integer :: ix(this%ndim)
294 integer :: icg, ip
295 integer(int64) :: ipg
296 integer, allocatable :: tmp_ic_to_icg(:), tmp_ic_to_ip(:)
297#if !defined(NDEBUG)
298 integer :: n_centroids_global
299#endif
300
302
303 safe_allocate(this%icg_to_ipg(1:this%n_centroids_global))
304
305 ! Local to this process
306 this%n_centroids = 0
307 ! Allocated with an upper bound
308 safe_allocate(tmp_ic_to_icg(1:this%n_centroids_global))
309 safe_allocate(tmp_ic_to_ip(1:this%n_centroids_global))
310
311 ! Convert coordinate to global grid index
312 do icg = 1, this%n_centroids_global
313 chi = mesh%coord_system%from_cartesian(this%positions(:, icg))
314 ! See the definition of mesh_x_global, for example
315 ix = nint(chi / mesh%spacing)
316 ipg = mesh_global_index_from_coords(mesh, ix)
317 this%icg_to_ipg(icg) = ipg
318 enddo
319
320 ! Having discretised the coordinates to grid indices, check for and remove any duplicate points
321 ! TODO(Alex) Annoyance with this is that it changes the order of the centroid indices
322 ! compared to not running check_duplicates, due to use of sorting.
323 ! Additionally, this may not be the desired behaviour. I.e. it might be better to select
324 ! the next-best discrete point, or stop Octopus
325 if (check_duplicates) then
326 call remove_duplicates_int64(this%icg_to_ipg)
327 this%n_centroids_global = size(this%icg_to_ipg)
328 endif
329
330 ! Assign grid indices local to this domain
331 do icg = 1, this%n_centroids_global
332 ipg = this%icg_to_ipg(icg)
333 ip = mesh_global2local(mesh, ipg)
334 if (ip > 0 .and. ip <= mesh%np) then
335 this%n_centroids = this%n_centroids + 1
336 tmp_ic_to_icg(this%n_centroids) = icg
337 tmp_ic_to_ip(this%n_centroids) = ip
338 endif
339 enddo
340
341#if !defined(NDEBUG)
342 ! Total number of centroids must be conserved over all domains
343 n_centroids_global = this%n_centroids
344 call comm_allreduce(mesh%mpi_grp, n_centroids_global)
345 assert(n_centroids_global == this%n_centroids_global)
346#endif
347
348 ! Reallocate with correct size
349 safe_allocate_source(this%ic_to_icg(1:this%n_centroids), tmp_ic_to_icg(1:this%n_centroids))
350 safe_deallocate_a(tmp_ic_to_icg)
351
352 safe_allocate_source(this%ic_to_ip(1:this%n_centroids), tmp_ic_to_ip(1:this%n_centroids))
353 safe_deallocate_a(tmp_ic_to_ip)
354
356
360 subroutine remove_duplicates_int64(indices)
361 integer(int64), intent(inout), allocatable :: indices(:)
362 ! Out: Ordered set of indices, with any duplicates removed
363
364 integer :: n_duplicates, cnt, n, i
365 integer(int64), allocatable :: unique_indices(:)
366
368
369 n = size(indices)
370 call sort(indices)
371
372 allocate(unique_indices(n))
373
374 n_duplicates = 0
375 cnt = 1
376 unique_indices(1) = indices(1)
377
378 do i = 1, n - 1
379 if (indices(i) == indices(i+1)) then
380 n_duplicates = n_duplicates + 1
381 cycle
382 endif
383 cnt = cnt + 1
384 unique_indices(cnt) = indices(i+1)
385 enddo
386
387 safe_deallocate_a(indices)
388 safe_allocate_source(indices(1:cnt), unique_indices(1:cnt))
389 safe_deallocate_a(unique_indices)
390
391 if (debug%info .and. n_duplicates > 0) then
392 write(message(1), '(I0)') n_duplicates
393 message(1) = trim(message(1)) // " duplicates found after discretisation of centroid positions"
394 write(message(2), '(I0)') cnt
395 message(2) = " Removed the duplicate/s and resized 'indices' array to " // trim(message(2))
396 call messages_warning(2)
397 endif
398
400
401 end subroutine remove_duplicates_int64
402
403end module centroids_oct_m
404
405!! Local Variables:
406!! mode: f90
407!! coding: utf-8
408!! End:
This is the common interface to a sorting routine. It performs the shell algorithm,...
Definition: sort.F90:149
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:241
subroutine remove_duplicates_int64(indices)
Remove duplicates from an array of integers.
Definition: centroids.F90:454
pure integer function, dimension(:), allocatable centroids_get_local_mesh_indices(this)
Return mesh indices ip for all centroids in this domain.
Definition: centroids.F90:281
subroutine centroids_finalize(this)
Finalize a centroids instance.
Definition: centroids.F90:359
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:302
pure integer function centroids_local_index_to_global_index(this, ic)
Get the global centroid index, given centroid ic this domain.
Definition: centroids.F90:291
subroutine centroids_init(this, mesh, positions, check_duplicates)
Initialise a centroids_t instance.
Definition: centroids.F90:192
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:254
subroutine centroids_output_all_indices(this, fname)
Output all centroid indices, across all domains.
Definition: centroids.F90:336
pure real(real64) function, dimension(this%ndim) centroids_get_local_position(this, mesh, ic)
Get the position of the centroid ic.
Definition: centroids.F90:312
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:380
pure integer function centroids_get_local_mesh_index(this, ic)
Get the mesh index ip of centroid ic in this domain.
Definition: centroids.F90:270
pure integer function centroids_get_n_centroids(this)
Number of centroids in local domain.
Definition: centroids.F90:212
pure real(real64) function, dimension(:, :), allocatable centroids_get_all_positions(this)
Return a copy of all centroid positions.
Definition: centroids.F90:325
pure integer function centroids_get_n_centroids_global(this)
Total number of centroids in system.
Definition: centroids.F90:222
type(debug_t), save, public debug
Definition: debug.F90:156
Definition: io.F90:114
subroutine, public io_close(iunit, grp)
Definition: io.F90:418
integer function, public io_open(file, namespace, action, status, form, position, die, recl, grp)
Definition: io.F90:352
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:118
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:924
integer function, public mesh_global2local(mesh, ipg)
This function returns the local mesh index for a given global index.
Definition: mesh.F90:977
subroutine, public messages_warning(no_lines, all_nodes, namespace)
Definition: messages.F90:537
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:160
type(mpi_grp_t), public mpi_world
Definition: mpi.F90:270
This module is intended to contain "only mathematical" functions and procedures.
Definition: sort.F90:117
Encapsulate centroid points and their indexing across a domain-decomposed mesh.
Definition: centroids.F90:155
Describes mesh distribution to nodes.
Definition: mesh.F90:186