Octopus
submesh.F90
Go to the documentation of this file.
1!! Copyright (C) 2007 X. Andrade
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
19#include "global.h"
20
21module submesh_oct_m
22 use accel_oct_m
23 use batch_oct_m
25 use box_oct_m
26 use comm_oct_m
27 use debug_oct_m
28 use global_oct_m
29 use index_oct_m
30 use, intrinsic :: iso_fortran_env
34 use sort_oct_m
35 use mesh_oct_m
37 use mpi_oct_m
40 use space_oct_m
41 use types_oct_m
42
43 implicit none
44 private
45
46 public :: &
47 submesh_t, &
60 dsm_nrm2, &
61 zsm_nrm2, &
79
82 type submesh_t
83 ! Components are public by default
84 real(real64), allocatable :: center(:)
85 real(real64) :: radius = m_zero
86 class(box_t), pointer :: box => null()
87 integer :: np = -1
88 integer, allocatable :: map(:)
89 integer :: num_regions
90 integer, allocatable :: regions(:)
91 type(accel_mem_t) :: buff_map
92 real(real64), allocatable :: rel_x(:,:)
93 real(real64), allocatable :: r(:)
94 type(mesh_t), pointer :: mesh => null()
95 logical :: overlap
97 integer :: np_global = -1
98 real(real64), allocatable :: rel_x_global(:,:)
99 integer, allocatable :: part_v(:)
100 integer, allocatable :: global2local(:)
101
102 type(mesh_cube_map_t) :: cube_map
103 end type submesh_t
104
105 interface submesh_add_to_mesh
107 end interface submesh_add_to_mesh
108
109 interface submesh_to_mesh_dotp
111 end interface submesh_to_mesh_dotp
112
113contains
114
115 ! -------------------------------------------------------------
116 ! Multipliers for recursive formulation of n-ellipsoid volume
117 ! simplifying the Gamma function
118 ! f(n) = 2f(n-2)/n, f(0)=1, f(1)=2
119 recursive real(real64) function f_n(dims) result(fn)
120 integer :: dims
121
122 if (dims == 0) then
123 fn = m_one
124 else if (dims == 1) then
125 fn = m_two
126 else
127 fn = m_two * f_n(dims - 2) / dims
128 end if
129
130 end function f_n
131
133 subroutine submesh_init_box(this, space, mesh, box, center)
134 type(submesh_t), intent(inout) :: this
135 class(space_t), intent(in) :: space
136 class(mesh_t), target, intent(in) :: mesh
137 class(box_t), target, intent(in) :: box
138 real(real64), intent(in) :: center(:)
139
140 integer :: original_mesh_index, submesh_index
141 logical :: submesh_points(1:mesh%np)
142 real(real64), allocatable :: xtmp(:, :), rtmp(:)
143
144 push_sub(submesh_init_box)
145
146 assert(.not. space%is_periodic())
147
148 ! Assign the mesh and the box
149 this%mesh => mesh
150 this%box => box
151
152 ! Get the total number of points inside the submesh. First of all get the number of points inside the submesh box
153 submesh_points = box%contains_points(mesh%np, mesh%x_t)
154 this%np = count(submesh_points)
155 ! Verify that the box of the submesh is contained into the box of the mesh
156 assert(this%np <= mesh%np)
157
158 safe_allocate(this%map(1:this%np))
159 safe_allocate(xtmp(1:space%dim, 1:this%np))
160 safe_allocate(rtmp(1:this%np))
161 xtmp = m_zero
162 rtmp = m_zero
163
164 ! Now we generate a map between the points of the submesh and the points of the original mesh
165 submesh_index = 0
166 do original_mesh_index = 1, mesh%np
167 ! check that the point is contained in the new box
168 if (submesh_points(original_mesh_index)) then
169 submesh_index = submesh_index + 1
170 this%map(submesh_index) = original_mesh_index
171 ! Assign the coordinates to this new point
172 xtmp(:, submesh_index) = mesh%x(:, original_mesh_index) - center(:)
173 rtmp(submesh_index) = norm2(xtmp(:, submesh_index))
174 end if
175 end do
176
177 call submesh_reorder_points(this, space, xtmp, rtmp)
178
179 safe_deallocate_a(xtmp)
180 safe_deallocate_a(rtmp)
183 end subroutine submesh_init_box
185 ! -------------------------------------------------------------
186 subroutine submesh_init(this, space, mesh, latt, center, rc)
187 type(submesh_t), intent(inout) :: this
188 class(space_t), intent(in) :: space
189 class(mesh_t), target, intent(in) :: mesh
190 type(lattice_vectors_t), intent(in) :: latt
191 real(real64), intent(in) :: center(1:space%dim)
192 real(real64), intent(in) :: rc
194 real(real64) :: r2, rc2, xx(space%dim), rc_norm_n
195 real(real64), allocatable :: center_copies(:,:), xtmp(:, :), rtmp(:)
196 integer :: icell, is, ip, ix, iy, iz
197 integer(int64) :: max_elements_count
198 type(lattice_iterator_t) :: latt_iter
199 integer, allocatable :: map_inv(:), map_temp(:)
200 integer :: nmax(3), nmin(3)
201 real(real64), parameter :: tol = 1e-13_real64
202
203
204 push_sub(submesh_init)
205 call profiling_in("SUBMESH_INIT")
206
207 assert(space%dim <= 3)
208
209 this%mesh => mesh
210
211 safe_allocate(this%center(1:space%dim))
212 this%center(:) = center(:)
213
214 this%radius = rc
215 ! We add a small number of avoid instabilities due to rounding errors
216 rc2 = rc**2 + tol
217
218 ! The spheres are generated differently for periodic coordinates,
219 ! mainly for performance reasons.
220 if (.not. space%is_periodic()) then
221
222 call profiling_in("SUBMESH_INIT_MAP_INV")
223 safe_allocate(map_inv(0:this%mesh%np))
224 map_inv(0:this%mesh%np) = 0
225
226 nmin = 0
227 nmax = 0
229 ! get a cube of points that contains the sphere
230 nmin(1:space%dim) = int((center(1:space%dim) - abs(rc))/mesh%spacing(1:space%dim)) - 1
231 nmax(1:space%dim) = int((center(1:space%dim) + abs(rc))/mesh%spacing(1:space%dim)) + 1
232
233 ! make sure that the cube is inside the grid
234 ! parts of the cube which would fall outside the simulation box are chopped off.
235 nmin(1:space%dim) = max(mesh%idx%nr(1, 1:space%dim), nmin(1:space%dim))
236 nmax(1:space%dim) = min(mesh%idx%nr(2, 1:space%dim), nmax(1:space%dim))
237
238 ! Get the total number of points inside the sphere
239 is = 0 ! this index counts inner points
240 do iz = nmin(3), nmax(3)
241 do iy = nmin(2), nmax(2)
242 do ix = nmin(1), nmax(1)
243 ip = mesh_local_index_from_coords(mesh, [ix, iy, iz])
244 if (ip == 0 .or. ip > mesh%np) cycle
245 r2 = sum((mesh%x(:, ip) - center)**2)
246 if (r2 <= rc2) then
247 is = is + 1
248 map_inv(ip) = is
249 end if
250 end do
251 end do
252 end do
253 this%np = is
254 call profiling_out("SUBMESH_INIT_MAP_INV")
255
256 call profiling_in("SUBMESH_INIT_RTMP")
257 safe_allocate(this%map(1:this%np))
258 safe_allocate(xtmp(1:space%dim, 1:this%np))
259 safe_allocate(rtmp(1:this%np))
260
261 ! Generate the table and the positions
262 do iz = nmin(3), nmax(3)
263 do iy = nmin(2), nmax(2)
264 do ix = nmin(1), nmax(1)
265 ip = mesh_local_index_from_coords(mesh, [ix, iy, iz])
266 if (ip == 0 .or. ip > mesh%np) cycle
267 is = map_inv(ip)
268 if (is == 0) cycle
269 this%map(is) = ip
270 xtmp(:, is) = mesh%x(:, ip) - center
271 rtmp(is) = norm2(xtmp(:,is))
272 end do
273 end do
274 end do
275
276 safe_deallocate_a(map_inv)
277 call profiling_out("SUBMESH_INIT_RTMP")
278
279 ! This is the case for a periodic system
280 else
282 ! Get the total number of points inside the sphere considering
283 ! replicas along PBCs
284
285 ! this requires some optimization
286
287 latt_iter = lattice_iterator_t(latt, rc)
288
289 safe_allocate(center_copies(1:space%dim, 1:latt_iter%n_cells))
290 do icell = 1, latt_iter%n_cells
291 center_copies(:, icell) = center + latt_iter%get(icell)
292 end do
293
294 !Recursive formulation for the volume of n-ellipsoid
295 !Garry Tee, NZ J. Mathematics Vol. 34 (2005) p. 165 eqs. 53,55
296 rc_norm_n = product(ceiling(rc / mesh%spacing(1:space%dim), int64) + m_one)
297 if (mesh%use_curvilinear) rc_norm_n = rc_norm_n / mesh%coord_system%min_mesh_scaling_product
298 max_elements_count = 3**space%dim * int(m_pi**floor(0.5 * space%dim) * rc_norm_n * f_n(space%dim), int64)
299
300 call profiling_in("SUBMESH_INIT_PERIODIC_R")
301 safe_allocate(map_temp(1:max_elements_count))
302 safe_allocate(xtmp(1:space%dim, 1:max_elements_count))
303 safe_allocate(rtmp(1:max_elements_count))
304
305 is = 0
306 do ip = 1, mesh%np
307 do icell = 1, latt_iter%n_cells
308 xx = mesh%x(:,ip) - center_copies(:, icell)
309 if(any(abs(xx)>rc+tol)) cycle
310 r2 = sum(xx**2)
311 if (r2 > rc2) cycle
312 is = is + 1
313 map_temp(is) = ip
314 rtmp(is) = sqrt(r2)
315 xtmp(:, is) = xx
316 ! Note that xx can be outside the unit cell
317 end do
318 end do
319 assert(is < huge(is))
320 this%np = is
321
322 safe_allocate(this%map(1:this%np))
323 this%map(1:this%np) = map_temp(1:this%np)
324 call profiling_out("SUBMESH_INIT_PERIODIC_R")
325
326 safe_deallocate_a(map_temp)
327 safe_deallocate_a(center_copies)
328
329 end if
330
331 call submesh_reorder_points(this, space, xtmp, rtmp)
332 call profiling_out("SUBMESH_INIT")
333
334 safe_deallocate_a(xtmp)
335 safe_deallocate_a(rtmp)
336
337 pop_sub(submesh_init)
338 end subroutine submesh_init
339
340 subroutine submesh_reorder_points(this, space, xtmp, rtmp)
341 type(submesh_t), intent(inout) :: this
342 class(space_t), intent(in) :: space
343 real(real64), intent(in) :: xtmp(:, :), rtmp(:)
344
345 integer :: ip, i_region, offset
346 integer, allocatable :: order(:), order_new(:)
347 integer, allocatable :: map_new(:)
348 integer, allocatable :: np_region(:), tmp_array(:)
349
350 push_sub(submesh_reorder_points)
351
352 ! now order points for better locality
353
354 call profiling_in("SUBMESH_INIT_ORDER")
355 safe_allocate(order(1:this%np))
356 safe_allocate(this%rel_x(1:space%dim, 1:this%np))
357 safe_allocate(this%r(1:this%np))
358
359 do ip = 1, this%np
360 order(ip) = ip
361 end do
362
363 ! First we just reorder in order to determine overlap:
364 call sort(this%map, order)
365
366 !check whether points overlap (i.e. whether a submesh contains the same point more than once)
367 this%overlap = .false.
368 do ip = 1, this%np - 1
369 if (this%map(ip) == this%map(ip + 1)) then
370 ! this simplified test works, as the points are ordered.
371 this%overlap = .true.
372 exit
373 end if
374 end do
375
376 this%num_regions = 1
377 call profiling_out("SUBMESH_INIT_ORDER")
378
379 if(this%overlap) then
380 call profiling_in("SUBMESH_INIT_OVERLAP")
381 !disentangle the map into injective regions
382
383 safe_allocate(tmp_array(1:this%np))
384 safe_allocate(order_new(1:this%np))
385 safe_allocate(np_region(1:this%np))
386 safe_allocate(map_new( 1:this%np))
387
388 np_region(1) = 1
389 tmp_array(1) = 1
390 i_region = 1
391
392 do ip = 2, this%np
393 if (this%map(ip) == this%map(ip - 1)) then
394 i_region = i_region + 1
395 if (i_region > this%num_regions) then
396 this%num_regions = i_region
397 np_region(i_region) = 0
398 end if
399 else
400 i_region = 1
401 end if
402 tmp_array(ip) = i_region ! which region does ip belong to
403 np_region(i_region) = np_region(i_region) + 1 ! increase number of points in i_region
404 end do
405
406 assert( .not. allocated(this%regions))
407
408 ! construct array of offsets
409 safe_allocate(this%regions(1:this%num_regions+1))
410
411 this%regions(1) = 1
412
413 if(this%num_regions > 1) then
414 do i_region = 1, this%num_regions
415 this%regions(i_region + 1) = this%regions(i_region) + np_region(i_region)
416 end do
417 else
418 this%regions(2) = this%np + 1
419 end if
420
421 np_region(1:this%np) = 0
422 order_new(1:this%np) = -1
423 map_new(1:this%np) = -1
424
425
426 !reassemble regions into global map array
427 do ip = 1, this%np
428 i_region = tmp_array(ip)
429 np_region(i_region) = np_region(i_region) + 1
430 offset = this%regions(i_region) - 1
431 map_new( offset + np_region(i_region) ) = this%map(ip)
432 order_new( offset + np_region(i_region) ) = order(ip)
433 end do
434
435 order(1:this%np) = order_new(1:this%np)
436 this%map(1:this%np) = map_new(1:this%np)
437
438 safe_deallocate_a(tmp_array)
439 safe_deallocate_a(order_new)
440 safe_deallocate_a(np_region)
441 safe_deallocate_a(map_new)
442 call profiling_out("SUBMESH_INIT_OVERLAP")
443
444 else
445 this%num_regions = 1
446 safe_allocate(this%regions(1:2))
447 this%regions(1) = 1
448 this%regions(2) = this%np + 1
449 end if
450
451 ! Lastly, reorder the points according to the new scheme
452 do ip = 1, this%np
453 this%rel_x(:, ip) = xtmp(:, order(ip))
454 this%r(ip) = rtmp(order(ip))
455 end do
456
457 safe_deallocate_a(order)
458
460 end subroutine submesh_reorder_points
461
462
463 ! --------------------------------------------------------------
464 !This routine takes two submeshes and merge them into a bigger submesh
465 !The grid is centered on the first center
466 subroutine submesh_merge(this, space, mesh, sm1, sm2, shift)
467 type(submesh_t), intent(inout) :: this
468 class(space_t), intent(in) :: space
469 class(mesh_t), target, intent(in) :: mesh
470 type(submesh_t), intent(in) :: sm1
471 type(submesh_t), intent(in) :: sm2
472 real(real64), optional, intent(in) :: shift(:)
473
474 real(real64) :: r2
475 integer :: ip, is
476 real(real64) :: xx(space%dim), diff_centers(space%dim)
477
478 push_sub(submesh_merge)
479 call profiling_in("SUBMESH_MERGE")
480
481 this%mesh => mesh
482
483 safe_allocate(this%center(1:space%dim))
484 this%center(:) = sm1%center(:)
485 this%radius = sm1%radius
486
487 ! This is a quick fix to prevent uninitialized variables. To properly check the self-overlap,
488 ! a similar approach as in submesh_init should be taken with respect to the merged map.
489 this%overlap = sm1%overlap .or. sm2%overlap
490
491 diff_centers = sm1%center - sm2%center
492 if (present(shift)) diff_centers = diff_centers - shift
493
494 !As we take the union of the two submeshes, we know that we have all the points from the first one included.
495 !The extra points from the second submesh are those which are not included in the first one
496 is = sm1%np
497 do ip = 1, sm2%np
498 !sm2%x contains points coordinates defined with respect to sm2%center
499 xx = sm2%rel_x(:, ip) - diff_centers
500 !If the point is not in sm1, we add it
501 if (sum(xx**2) > sm1%radius**2) is = is + 1
502 end do
503
504 this%np = is
505
506 safe_allocate(this%map(1:this%np))
507 safe_allocate(this%rel_x(1:space%dim, 1:this%np))
508 safe_allocate(this%r(1:this%np))
509 this%map(1:sm1%np) = sm1%map(1:sm1%np)
510 this%rel_x(:, 1:sm1%np) = sm1%rel_x(:, 1:sm1%np)
511 this%r(1:sm1%np) = sm1%r(1:sm1%np)
512
513 !iterate again to fill the tables
514 is = sm1%np
515 do ip = 1, sm2%np
516 xx = sm2%rel_x(:, ip) - diff_centers
517 r2 = sum(xx**2)
518 if (r2 > sm1%radius**2) then
519 is = is + 1
520 this%map(is) = sm2%map(ip)
521 this%r(is) = sqrt(r2)
522 this%rel_x(:, is) = xx
523 end if
524 end do
525
526 call profiling_out("SUBMESH_MERGE")
527 pop_sub(submesh_merge)
528 end subroutine submesh_merge
529
530 ! --------------------------------------------------------------
531 !This routine shifts the center of a submesh, without changing the grid points
532 subroutine submesh_shift_center(this, space, newcenter)
533 type(submesh_t), intent(inout) :: this
534 class(space_t), intent(in) :: space
535 real(real64), intent(in) :: newcenter(:)
536
537 real(real64) :: xx(space%dim), diff_centers(space%dim), oldcenter(space%dim)
538 integer :: ip
539
540 push_sub(submesh_shift_center)
541 call profiling_in("SUBMESH_SHIFT")
542
543 oldcenter = this%center
544 this%center(:) = newcenter(:)
545
546 diff_centers = newcenter - oldcenter
547
548 do ip = 1, this%np
549 xx = this%rel_x(:, ip) - diff_centers
550 this%r(ip) = norm2(xx)
551 this%rel_x(:, ip) = xx
552 end do
553
554 call profiling_out("SUBMESH_SHIFT")
555 pop_sub(submesh_shift_center)
556 end subroutine submesh_shift_center
557
558 ! --------------------------------------------------------------
559 subroutine submesh_broadcast(this, space, mesh, center, radius, root, mpi_grp)
560 type(submesh_t), intent(inout) :: this
561 class(space_t), intent(in) :: space
562 type(mesh_t), target, intent(in) :: mesh
563 real(real64), intent(in) :: center(1:space%dim)
564 real(real64), intent(in) :: radius
565 integer, intent(in) :: root
566 type(mpi_grp_t), intent(in) :: mpi_grp
567
568 integer :: nparray(1:3)
569
570 push_sub(submesh_broadcast)
571 call profiling_in('SUBMESH_BCAST')
572
573 if (root /= mpi_grp%rank) then
574 this%mesh => mesh
575 safe_allocate(this%center(1:space%dim))
576 this%center(:) = center(:)
577 this%radius = radius
578 end if
579
580 if (mpi_grp%size > 1) then
581
582 if (root == mpi_grp%rank) then
583 nparray(1) = this%np
584 nparray(2) = this%num_regions
585 if (this%overlap) then
586 nparray(3) = 1
587 else
588 nparray(3) = 0
589 end if
590 end if
591
592 call mpi_grp%bcast(nparray, 3, mpi_integer, root)
593 this%np = nparray(1)
594 this%num_regions = nparray(2)
595 this%overlap = (nparray(3) == 1)
596
597 if (root /= mpi_grp%rank) then
598 safe_allocate(this%map(1:this%np))
599 safe_allocate(this%rel_x(1:space%dim, 1:this%np))
600 safe_allocate(this%r(1:this%np))
601 safe_allocate(this%regions(1:this%num_regions+1))
602 end if
603
604 call mpi_grp%bcast(this%regions(1), this%num_regions+1, mpi_integer, root)
605
606 if (this%np > 0) then
607 call mpi_grp%bcast(this%map(1), this%np, mpi_integer, root)
608 call mpi_grp%bcast(this%rel_x(1, 1), this%np*space%dim, mpi_double_precision, root)
609 call mpi_grp%bcast(this%r(1), this%np, mpi_double_precision, root)
610 end if
611
612 end if
613
614 call profiling_out('SUBMESH_BCAST')
615 pop_sub(submesh_broadcast)
616 end subroutine submesh_broadcast
617
618 ! --------------------------------------------------------------
619 logical function submesh_compatible(this, radius, center, dx) result(compatible)
620 type(submesh_t), intent(in) :: this
621 real(real64), intent(in) :: radius
622 real(real64), intent(in) :: center(:)
623 real(real64), intent(in) :: dx
624
625 compatible =.false.
626 if (allocated(this%center)) then
629 if (radius <= this%radius+dx*1e-6_real64 .and. all(abs(this%center - center) < 0.25*dx)) then
630 compatible = .true.
631 end if
632 end if
633
634 end function submesh_compatible
635
636 ! --------------------------------------------------------------
637 subroutine submesh_end(this)
638 type(submesh_t), intent(inout) :: this
639
640 push_sub(submesh_end)
641
642 if (this%np /= -1) then
643 nullify(this%mesh)
644 this%np = -1
645 safe_deallocate_a(this%center)
646 safe_deallocate_a(this%map)
647 safe_deallocate_a(this%rel_x)
648 safe_deallocate_a(this%r)
649 safe_deallocate_a(this%regions)
650 end if
651
652 if (accel_is_enabled()) then
653 call accel_release_buffer(this%buff_map)
654 end if
655
656 pop_sub(submesh_end)
657 end subroutine submesh_end
658
659 ! --------------------------------------------------------------
660
661 subroutine submesh_get_inv(this, map_inv)
662 type(submesh_t), intent(in) :: this
663 integer, intent(out) :: map_inv(:)
664
665 integer :: is
666
667 push_sub(submesh_get_inv)
668
669 map_inv(1:this%mesh%np_part) = 0
670 do is = 1, this%np
671 map_inv(this%map(is)) = is
672 end do
673
674 pop_sub(submesh_get_inv)
675 end subroutine submesh_get_inv
676
677 ! --------------------------------------------------------------
678 logical function submesh_overlap(sm1, sm2, space) result(overlap)
679 type(submesh_t), intent(in) :: sm1
680 type(submesh_t), intent(in) :: sm2
681 class(space_t), intent(in) :: space
682
683 integer :: ii, jj, dd
684 real(real64) :: distance
685
686 !no PUSH_SUB, called too often
687
688 if (.not. space%is_periodic()) then
689 !first check the distance
690 distance = sum((sm1%center - sm2%center)**2)
691 overlap = distance <= (1.5_real64*(sm1%radius + sm2%radius))**2
692
693 ! if they are very far, no need to check in detail
694 if (.not. overlap) return
695 end if
696
697 ! Otherwise check whether they have the some point in common. We
698 ! can make the comparison faster using that the arrays are sorted.
699 overlap = .false.
700 ii = 1
701 jj = 1
702 do while(ii <= sm1%np .and. jj <= sm2%np)
703 dd = sm1%map(ii) - sm2%map(jj)
704 if (dd < 0) then
705 ii = ii + 1
706 else if (dd > 0) then
707 jj = jj + 1
708 else
709 overlap = .true.
710 exit
711 end if
712 end do
713
714 if (sm1%mesh%parallel_in_domains) then
715 call sm1%mesh%mpi_grp%allreduce_inplace(overlap, 1, mpi_logical, mpi_lor)
716 end if
717
718 end function submesh_overlap
719
720 ! -------------------------------------------------------------
721 subroutine submesh_build_global(this, space)
722 type(submesh_t), intent(inout) :: this
723 class(space_t), intent(in) :: space
724
725 integer, allocatable :: part_np(:)
726 integer :: ipart, ind, ip
727
728 push_sub(submesh_build_global)
729
730 if (.not. this%mesh%parallel_in_domains) then
731 this%np_global = this%np
733 return
734 end if
735
736 safe_allocate(part_np(this%mesh%pv%npart))
737 part_np = 0
738 part_np(this%mesh%pv%partno) = this%np
739
740 call this%mesh%allreduce(part_np)
741 this%np_global = sum(part_np)
742
743 safe_allocate(this%rel_x_global(1:space%dim, 1:this%np_global))
744 safe_allocate(this%part_v(1:this%np_global))
745 safe_allocate(this%global2local(1:this%np_global))
746 this%rel_x_global(1:space%dim, 1:this%np_global) = m_zero
747 this%part_v(1:this%np_global) = 0
748 this%global2local(1:this%np_global) = 0
749
750 ind = 0
751 do ipart = 1, this%mesh%pv%npart
752 if (ipart == this%mesh%pv%partno) then
753 do ip = 1, this%np
754 this%rel_x_global(:, ind + ip) = this%rel_x(:, ip)
755 this%part_v(ind + ip) = this%mesh%pv%partno
756 this%global2local(ind + ip) = ip
757 end do
758 end if
759 ind = ind + part_np(ipart)
760 end do
761
762 call this%mesh%allreduce(this%rel_x_global)
763 call this%mesh%allreduce(this%part_v)
764 call this%mesh%allreduce(this%global2local)
765
766 safe_deallocate_a(part_np)
767
768 pop_sub(submesh_build_global)
769 end subroutine submesh_build_global
770
771 ! -----------------------------------------------------------
772 subroutine submesh_end_global(this)
773 type(submesh_t), intent(inout) :: this
774
775 push_sub(submesh_end_global)
776
777 safe_deallocate_a(this%rel_x_global)
778 this%np_global = -1
779 safe_deallocate_a(this%part_v)
780 safe_deallocate_a(this%global2local)
781
782 pop_sub(submesh_end_global)
783 end subroutine submesh_end_global
784
785
786 ! -----------------------------------------------------------
787 subroutine zzsubmesh_add_to_mesh(this, sphi, phi, factor)
788 type(submesh_t), intent(in) :: this
789 complex(real64), intent(in) :: sphi(:)
790 complex(real64), intent(inout) :: phi(:)
791 complex(real64), optional, intent(in) :: factor
792
793 integer :: ip, m
794
795 push_sub(zzdsubmesh_add_to_mesh)
796
797 if (present(factor)) then
798 !Loop unrolling inspired by BLAS axpy routine
799 m = mod(this%np,4)
800 do ip = 1, m
801 phi(this%map(ip)) = phi(this%map(ip)) + factor*sphi(ip)
802 end do
803 if (this%np.ge.4) then
804 do ip = m+1, this%np, 4
805 phi(this%map(ip)) = phi(this%map(ip)) + factor*sphi(ip)
806 phi(this%map(ip+1)) = phi(this%map(ip+1)) + factor*sphi(ip+1)
807 phi(this%map(ip+2)) = phi(this%map(ip+2)) + factor*sphi(ip+2)
808 phi(this%map(ip+3)) = phi(this%map(ip+3)) + factor*sphi(ip+3)
809 end do
810 end if
811 else
812 m = mod(this%np,4)
813 do ip = 1, m
814 phi(this%map(ip)) = phi(this%map(ip)) + sphi(ip)
815 end do
816 if (this%np.ge.4) then
817 do ip = m+1, this%np, 4
818 phi(this%map(ip)) = phi(this%map(ip)) + sphi(ip)
819 phi(this%map(ip+1)) = phi(this%map(ip+1)) + sphi(ip+1)
820 phi(this%map(ip+2)) = phi(this%map(ip+2)) + sphi(ip+2)
821 phi(this%map(ip+3)) = phi(this%map(ip+3)) + sphi(ip+3)
822 end do
823 end if
824 end if
825
826 pop_sub(zzdsubmesh_add_to_mesh)
827 end subroutine zzsubmesh_add_to_mesh
828
829 !------------------------------------------------------------
830 complex(real64) function zzsubmesh_to_mesh_dotp(this, sphi, phi, reduce) result(dotp)
831 type(submesh_t), intent(in) :: this
832 complex(real64), intent(in) :: sphi(:)
833 complex(real64), intent(in) :: phi(:)
834 logical, optional, intent(in) :: reduce
835
836 integer :: is, m, ip
837
838 push_sub(zzsubmesh_to_mesh_dotp)
839
840 dotp = m_z0
841
842 if (this%mesh%use_curvilinear) then
843 do is = 1, this%np
844 dotp = dotp + this%mesh%vol_pp(this%map(is))*phi(this%map(is))*conjg(sphi(is))
845 end do
846 else
847 m = mod(this%np,4)
848 do ip = 1, m
849 dotp = dotp + phi(this%map(ip))*conjg(sphi(ip))
850 end do
851 if (this%np.ge.4) then
852 do ip = m+1, this%np, 4
853 dotp = dotp + phi(this%map(ip))*conjg(sphi(ip)) &
854 + phi(this%map(ip+1))*conjg(sphi(ip+1)) &
855 + phi(this%map(ip+2))*conjg(sphi(ip+2)) &
856 + phi(this%map(ip+3))*conjg(sphi(ip+3))
857 end do
858 end if
859 dotp = dotp*this%mesh%vol_pp(1)
860 end if
861
862 if (optional_default(reduce, .true.)) then
863 call profiling_in("SM_REDUCE_DOTP")
864 call this%mesh%allreduce(dotp)
865 call profiling_out("SM_REDUCE_DOTP")
866 end if
869 end function zzsubmesh_to_mesh_dotp
870
871 !------------------------------------------------------------
873 subroutine submesh_get_cube_dim(sm, space, db)
874 type(submesh_t), target, intent(in) :: sm
875 class(space_t), intent(in) :: space
876 integer, intent(out) :: db(1:space%dim)
877
878 integer :: ip, idir
879 real(real64) :: chi(space%dim), max_chi(space%dim)
880 integer :: db_red(1:space%dim)
881 real(real64), parameter :: tol=1.0e-10_real64
883 push_sub(submesh_get_cube_dim)
884
885 max_chi = abs(sm%mesh%coord_system%from_cartesian(sm%rel_x(:, 1)))/sm%mesh%spacing
886 do ip = 2, sm%np
887 chi = sm%mesh%coord_system%from_cartesian(sm%rel_x(:, ip))
888 do idir = 1, space%dim
889 max_chi(idir) = max(max_chi(idir), abs(chi(idir))/sm%mesh%spacing(idir))
890 end do
891 end do
892
893 do idir = 1, space%dim
894 db(idir) = nint(max_chi(idir)-tol)
895 end do
896
897 if(sm%mesh%parallel_in_domains) then
898 call sm%mesh%mpi_grp%allreduce(db(1), db_red(1), space%dim, mpi_integer, mpi_max)
899 db(1:space%dim) = db_red(1:space%dim)
900 end if
901
902 db = 2 * db + 1
903
904 pop_sub(submesh_get_cube_dim)
905 end subroutine submesh_get_cube_dim
906
907 !------------------------------------------------------------
908 subroutine submesh_init_cube_map(sm, space)
909 type(submesh_t), target, intent(inout) :: sm
910 class(space_t), intent(in) :: space
911
912 integer(int64) :: ip
913 integer ::idir
914 real(real64) :: chi(space%dim), shift(space%dim)
915
916 push_sub(submesh_init_cube_map)
917
918 sm%cube_map%nmap = sm%np
919
920 safe_allocate(sm%cube_map%map(1:space%dim, 1:sm%cube_map%nmap))
921
922 !The center of the submesh does not belong to the mesh
923 !So we first need to find the closest grid point, and center the cube to it
924 chi = sm%mesh%coord_system%from_cartesian(sm%center)
925 do idir = 1, space%dim
926 shift(idir) = nint(chi(idir)/sm%mesh%spacing(idir))*sm%mesh%spacing(idir)
927 end do
928 shift = sm%mesh%coord_system%to_cartesian(shift)
929 shift = shift - sm%center
930
931 do ip = 1, sm%cube_map%nmap
932 chi = sm%mesh%coord_system%from_cartesian(sm%rel_x(:,ip) - shift)
933 do idir = 1, space%dim
934 sm%cube_map%map(idir, ip) = nint(chi(idir)/sm%mesh%spacing(idir))
935 end do
936 end do
937
938 if (accel_is_enabled()) then
939 call accel_create_buffer(sm%cube_map%map_buffer, accel_mem_read_only, type_integer, sm%cube_map%nmap*space%dim)
940 call accel_write_buffer(sm%cube_map%map_buffer, space%dim, sm%cube_map%nmap, sm%cube_map%map)
941 end if
942
943 pop_sub(submesh_init_cube_map)
944 end subroutine submesh_init_cube_map
945
946 !------------------------------------------------------------
947 subroutine submesh_end_cube_map(sm)
948 type(submesh_t), intent(inout) :: sm
949
950 push_sub(submesh_end_cube_map)
951
952 call mesh_cube_map_end(sm%cube_map)
953
954 pop_sub(submesh_end_cube_map)
955 end subroutine submesh_end_cube_map
956
963 subroutine dzsubmesh_batch_add(this, ss, mm)
964 type(submesh_t), intent(in) :: this
965 class(batch_t), intent(in) :: ss
966 class(batch_t), intent(inout) :: mm
967
968 integer :: ist, idim, jdim, is
969
970 push_sub(dzsubmesh_batch_add)
971
972 assert(.not. mm%is_packed())
973 assert(ss%type() == type_float)
974 assert(mm%type() == type_cmplx)
975 assert(ss%nst_linear == mm%nst_linear)
976 assert(ss%status() == mm%status())
977 assert(ss%dim == mm%dim)
978
979 assert(mm%nst == ss%nst)
980
981 !$omp parallel do private(ist, idim, jdim, is) if(.not. this%overlap)
982 do ist = 1, mm%nst
983 do idim = 1, mm%dim
984 jdim = min(idim, ss%dim)
985
986 do is = 1, this%np
987 mm%zff(this%map(is), idim, ist) = &
988 mm%zff(this%map(is), idim, ist) + ss%dff(is, jdim, ist)
989 end do
990
991 end do
992 end do
993 !$omp end parallel do
994
995 pop_sub(dzsubmesh_batch_add)
996 end subroutine dzsubmesh_batch_add
997
998
999#include "undef.F90"
1000#include "real.F90"
1001#include "submesh_inc.F90"
1002
1003#include "undef.F90"
1004#include "complex.F90"
1005#include "submesh_inc.F90"
1006
1007end module submesh_oct_m
1008
1009!! Local Variables:
1010!! mode: f90
1011!! coding: utf-8
1012!! End:
This is the common interface to a sorting routine. It performs the shell algorithm,...
Definition: sort.F90:151
double sqrt(double __x) __attribute__((__nothrow__
double floor(double __x) __attribute__((__nothrow__
double fn(const gsl_vector *v, void *params)
subroutine, public accel_release_buffer(this, async)
Definition: accel.F90:882
pure logical function, public accel_is_enabled()
Definition: accel.F90:395
integer, parameter, public accel_mem_read_only
Definition: accel.F90:183
This module implements batches of mesh functions.
Definition: batch.F90:135
Module implementing boundary conditions in Octopus.
Definition: boundaries.F90:124
real(real64), parameter, public m_two
Definition: global.F90:193
real(real64), parameter, public m_zero
Definition: global.F90:191
real(real64), parameter, public m_pi
some mathematical constants
Definition: global.F90:189
complex(real64), parameter, public m_z0
Definition: global.F90:201
real(real64), parameter, public m_one
Definition: global.F90:192
This module implements the index, used for the mesh points.
Definition: index.F90:124
subroutine, public mesh_cube_map_end(this)
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:120
integer function, public mesh_local_index_from_coords(mesh, ix)
This function returns the local index of the point for a given vector of integer coordinates.
Definition: mesh.F90:938
Some general things and nomenclature:
Definition: par_vec.F90:173
subroutine, public profiling_out(label)
Increment out counter and sum up difference between entry and exit time.
Definition: profiling.F90:631
subroutine, public profiling_in(label, exclude)
Increment in counter and save entry time.
Definition: profiling.F90:554
This module is intended to contain "only mathematical" functions and procedures.
Definition: sort.F90:119
real(real64) function, public dsm_integrate_frommesh(mesh, sm, ff, reduce)
Definition: submesh.F90:1203
subroutine zdsubmesh_add_to_mesh(this, sphi, phi, factor)
Definition: submesh.F90:1773
subroutine, public submesh_init_cube_map(sm, space)
Definition: submesh.F90:1004
logical function, public submesh_overlap(sm1, sm2, space)
Definition: submesh.F90:774
subroutine, public zsubmesh_batch_dotp_matrix(this, mm, ss, dot, reduce)
Definition: submesh.F90:2078
real(real64) function, public dsm_nrm2(sm, ff, reduce)
this function returns the the norm of a vector
Definition: submesh.F90:1314
subroutine zzsubmesh_add_to_mesh(this, sphi, phi, factor)
Definition: submesh.F90:883
subroutine, public submesh_end_global(this)
Definition: submesh.F90:868
subroutine, public zsubmesh_copy_from_mesh(this, phi, sphi, conjugate)
Definition: submesh.F90:1821
subroutine submesh_reorder_points(this, space, xtmp, rtmp)
Definition: submesh.F90:436
complex(real64) function, public zsm_integrate(mesh, sm, ff, reduce)
Definition: submesh.F90:1700
subroutine, public submesh_shift_center(this, space, newcenter)
Definition: submesh.F90:628
subroutine, public submesh_init_box(this, space, mesh, box, center)
This subroutine creates a submesh which has the same box of the one that the user passes.
Definition: submesh.F90:229
complex(real64) function zzsubmesh_to_mesh_dotp(this, sphi, phi, reduce)
Definition: submesh.F90:926
real(real64) function, public dsm_integrate(mesh, sm, ff, reduce)
Definition: submesh.F90:1163
subroutine, public dsubmesh_batch_add_matrix(this, factor, ss, mm)
The following functions takes a batch of functions defined in submesh (ss) and adds all of them to ea...
Definition: submesh.F90:1396
recursive real(real64) function f_n(dims)
Definition: submesh.F90:215
subroutine, public dsubmesh_copy_from_mesh(this, phi, sphi, conjugate)
Definition: submesh.F90:1284
real(real64) function ddsubmesh_to_mesh_dotp(this, sphi, phi, reduce)
Definition: submesh.F90:1351
subroutine, public submesh_merge(this, space, mesh, sm1, sm2, shift)
Definition: submesh.F90:562
subroutine, public dzsubmesh_batch_add(this, ss, mm)
The following function takes a batch of functions defined in submesh (ss) and adds one of them to eac...
Definition: submesh.F90:1059
subroutine, public zsubmesh_batch_add(this, ss, mm)
The following function takes a batch of functions defined in submesh (ss) and adds one of them to eac...
Definition: submesh.F90:2026
real(real64) function, public zsm_nrm2(sm, ff, reduce)
this function returns the the norm of a vector
Definition: submesh.F90:1851
subroutine, public zsubmesh_batch_add_matrix(this, factor, ss, mm)
The following functions takes a batch of functions defined in submesh (ss) and adds all of them to ea...
Definition: submesh.F90:1933
subroutine, public submesh_end_cube_map(sm)
Definition: submesh.F90:1043
subroutine, public submesh_end(this)
Definition: submesh.F90:733
subroutine ddsubmesh_add_to_mesh(this, sphi, phi, factor)
Definition: submesh.F90:1236
subroutine, public submesh_get_cube_dim(sm, space, db)
finds the dimension of a box containing the submesh
Definition: submesh.F90:969
subroutine, public dsubmesh_batch_dotp_matrix(this, mm, ss, dot, reduce)
Definition: submesh.F90:1541
complex(real64) function, public zsm_integrate_frommesh(mesh, sm, ff, reduce)
Definition: submesh.F90:1740
logical function, public submesh_compatible(this, radius, center, dx)
Definition: submesh.F90:715
subroutine, public submesh_build_global(this, space)
Definition: submesh.F90:817
subroutine, public submesh_broadcast(this, space, mesh, center, radius, root, mpi_grp)
Definition: submesh.F90:655
subroutine, public submesh_get_inv(this, map_inv)
Definition: submesh.F90:757
subroutine, public dsubmesh_batch_add(this, ss, mm)
The following function takes a batch of functions defined in submesh (ss) and adds one of them to eac...
Definition: submesh.F90:1489
subroutine, public submesh_init(this, space, mesh, latt, center, rc)
Definition: submesh.F90:282
complex(real64) function zdsubmesh_to_mesh_dotp(this, sphi, phi, reduce)
Definition: submesh.F90:1888
type(type_t), public type_float
Definition: types.F90:135
type(type_t), public type_cmplx
Definition: types.F90:136
type(type_t), public type_integer
Definition: types.F90:137
Class defining batches of mesh functions.
Definition: batch.F90:161
The following class implements a lattice iterator. It allows one to loop over all cells that are with...
Describes mesh distribution to nodes.
Definition: mesh.F90:187
This is defined even when running serial.
Definition: mpi.F90:144
A submesh is a type of mesh, used for the projectors in the pseudopotentials It contains points on a ...
Definition: submesh.F90:177
int true(void)
void distance(const int iatom, const int jatom, const double coordinates[], double *rr, double *rr2, double *rr6, double *rr7)
Definition: vdw_ts_low.c:2059