Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
mesh.f90
Go to the documentation of this file.
1! Copyright (c) 2018-2026, The Neko Authors
2! All rights reserved.
3!
4! Redistribution and use in source and binary forms, with or without
5! modification, are permitted provided that the following conditions
6! are met:
7!
8! * Redistributions of source code must retain the above copyright
9! notice, this list of conditions and the following disclaimer.
10!
11! * Redistributions in binary form must reproduce the above
12! copyright notice, this list of conditions and the following
13! disclaimer in the documentation and/or other materials provided
14! with the distribution.
15!
16! * Neither the name of the authors nor the names of its
17! contributors may be used to endorse or promote products derived
18! from this software without specific prior written permission.
19!
20! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21! "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22! LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23! FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24! COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25! INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26! BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27! LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29! LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30! ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31! POSSIBILITY OF SUCH DAMAGE.
32!
34module mesh
35 use num_types, only : rp, dp, i8
36 use point, only : point_t
37 use element, only : element_t
38 use hex, only : hex_t, neko_hex_neds, neko_hex_nfcs, &
42 use mask, only : mask_t
43 use stack, only : stack_i4_t, stack_i8_t
44 use tuple, only : tuple_i4_t, tuple4_i4_t
47 use datadist, only : linear_dist_t
48 use distdata, only : distdata_t
49 use comm, only : pe_size, pe_rank, neko_comm
51 use math, only : abscmp, sort
53 use mpi_f08, only : mpi_integer, mpi_max, mpi_sum, mpi_in_place, &
54 mpi_allreduce, mpi_exscan, mpi_request, mpi_status, mpi_wait, &
55 mpi_issend, mpi_irecv, mpi_status_ignore, mpi_integer8, &
56 mpi_get_count
57 use uset, only : uset_i8_t
58 use curve, only : curve_t
59 use logger, only : log_size
60 use, intrinsic :: iso_fortran_env, only : error_unit
61 implicit none
62 private
63
65 integer, public, parameter :: neko_msh_max_zlbls = 20
67 integer, public, parameter :: neko_msh_max_zlbl_len = 40
68
69 type, private :: mesh_element_t
70 class(element_t), allocatable :: e
71 end type mesh_element_t
72
73 type, public :: mesh_t
74 integer :: nelv
75 integer :: npts
76 integer :: gdim
77 integer :: mpts
78 integer :: mfcs
79 integer :: meds
80
81 integer :: glb_nelv
82 integer :: glb_mpts
83 integer :: glb_mfcs
84 integer :: glb_meds
85
86 integer :: offset_el
87 integer :: max_pts_id
88
89 type(point_t), allocatable :: points(:)
90 type(mesh_element_t), allocatable :: elements(:)
91 logical, allocatable :: dfrmd_el(:)
92
99 type(htable_i4_t), private, allocatable :: htp
100 type(htable_i4_t) :: htel
101
105 integer, allocatable :: pt_lid(:,:)
106
108 integer, allocatable :: edge_lid(:,:)
109
113 integer, allocatable :: face_lid(:,:)
114
118 integer, private, allocatable :: edge_pts(:,:)
119
123 integer, private, allocatable :: face_pts(:,:)
124
125
126 integer, allocatable :: facet_neigh(:,:)
127
131 class(htable_t), allocatable :: facet_map
132 type(stack_i4_t), allocatable :: point_neigh(:)
133
134 type(distdata_t) :: ddata
135 logical, allocatable :: neigh(:)
136 integer, allocatable :: neigh_order(:)
137
138 integer(2), allocatable :: facet_type(:,:)
139
140 type(facet_zone_t), allocatable :: labeled_zones(:)
141 type(facet_zone_periodic_t) :: periodic
142 type(curve_t) :: curve
143
144 logical :: lconn = .false.
145 logical :: ldist = .false.
146 logical :: lnumr = .false.
147 logical :: lgenc = .true.
148
149 logical :: is_submesh = .false.
150
153 procedure(mesh_deform), pass(msh), pointer :: apply_deform => null()
154 contains
155 procedure, private, pass(this) :: init_nelv => mesh_init_nelv
156 procedure, private, pass(this) :: init_dist => mesh_init_dist
157 procedure, private, pass(this) :: add_quad => mesh_add_quad
158 procedure, private, pass(this) :: add_hex => mesh_add_hex
159 procedure, private, pass(this) :: add_point => mesh_add_point
160 procedure, pass(this) :: get_global_edge => mesh_get_global_edge
161 procedure, pass(this) :: get_global_facet => mesh_get_global_facet
162 procedure, pass(this) :: is_shared_point => mesh_is_shared_point
163 procedure, pass(this) :: is_shared_edge => mesh_is_shared_edge
164 procedure, pass(this) :: is_shared_facet => mesh_is_shared_facet
165 procedure, pass(this) :: free => mesh_free
166 procedure, pass(this) :: finalize => mesh_finalize
167 procedure, pass(this) :: mark_periodic_facet => mesh_mark_periodic_facet
168 procedure, pass(this) :: mark_labeled_facet => mesh_mark_labeled_facet
169 procedure, pass(this) :: mark_curve_element => mesh_mark_curve_element
170 procedure, pass(this) :: apply_periodic_facet => mesh_apply_periodic_facet
171 procedure, pass(this) :: all_deformed => mesh_all_deformed
172 procedure, pass(this) :: get_facet_ids => mesh_get_facet_ids
173 procedure, pass(this) :: reset_periodic_ids => mesh_reset_periodic_ids
174 procedure, pass(this) :: create_periodic_ids => mesh_create_periodic_ids
175 procedure, pass(this) :: generate_conn => mesh_generate_conn
176 procedure, pass(this) :: have_point_glb_idx => mesh_have_point_glb_idx
177 procedure, pass(this) :: subset_by_mask => mesh_subset_by_mask
178
180 procedure, pass(this) :: check_right_handedness => &
183 generic :: init => init_nelv, init_dist
185 generic :: add_element => add_quad, add_hex
186 end type mesh_t
187
188 abstract interface
189 subroutine mesh_deform(msh, x, y, z, lx, ly, lz)
190 import mesh_t
191 import rp
192 class(mesh_t) :: msh
193 integer, intent(in) :: lx, ly, lz
194 real(kind=rp), intent(inout) :: x(lx, ly, lz, msh%nelv)
195 real(kind=rp), intent(inout) :: y(lx, ly, lz, msh%nelv)
196 real(kind=rp), intent(inout) :: z(lx, ly, lz, msh%nelv)
197 end subroutine mesh_deform
198 end interface
199
201
202
203contains
204
206 subroutine mesh_init_nelv(this, gdim, nelv)
207 class(mesh_t), intent(inout) :: this
208 integer, intent(in) :: gdim
209 integer, intent(in) :: nelv
210 integer :: ierr
211 logical :: lgenc
212 character(len=LOG_SIZE) :: log_buf
213
214 ! Preserve the caller's connectivity-generation choice across free(), which
215 ! resets lgenc to its .true. default (see mesh_free). Without this, setting
216 ! `msh%lgenc = .false.` before init has no effect, and connectivity is always
217 ! generated.
218 lgenc = this%lgenc
219 call this%free()
220 this%lgenc = lgenc
221
222 this%nelv = nelv
223 this%gdim = gdim
224
225 if (this%nelv < 1) then
226 write(log_buf, '(A,I0,A)') 'MPI rank ', pe_rank, ' has zero elements'
227 call neko_warning(log_buf)
228 end if
229
230 call mpi_allreduce(this%nelv, this%glb_nelv, 1, &
231 mpi_integer, mpi_sum, neko_comm, ierr)
232
233 this%offset_el = 0
234 call mpi_exscan(this%nelv, this%offset_el, 1, &
235 mpi_integer, mpi_sum, neko_comm, ierr)
236
237 call mesh_init_common(this)
238
239 end subroutine mesh_init_nelv
240
242 subroutine mesh_init_dist(this, gdim, dist)
243 class(mesh_t), intent(inout) :: this
244 integer, intent(in) :: gdim
245 type(linear_dist_t), intent(in) :: dist
246 logical :: lgenc
247 character(len=LOG_SIZE) :: log_buf
248
249 ! Preserve the caller's connectivity-generation choice across free()
250 ! (see mesh_init_nelv / mesh_free).
251 lgenc = this%lgenc
252 call this%free()
253 this%lgenc = lgenc
254
255 this%nelv = dist%num_local()
256 if (this%nelv < 1) then
257 write(log_buf, '(A,I0,A)') 'MPI rank ', pe_rank, ' has zero elements'
258 call neko_warning(log_buf)
259 end if
260 this%glb_nelv = dist%num_global()
261 this%offset_el = dist%start_idx()
262 this%gdim = gdim
263
264 call mesh_init_common(this)
265
266 end subroutine mesh_init_dist
267
268 subroutine mesh_init_common(this)
269 type(mesh_t), intent(inout) :: this
270 integer :: i
271 type(tuple_i4_t) :: facet_data
272
273 this%max_pts_id = 0
274
275 allocate(this%elements(this%nelv))
276 allocate(this%dfrmd_el(this%nelv))
277 if (this%gdim .eq. 3) then
278 do i = 1, this%nelv
279 allocate(hex_t::this%elements(i)%e)
280 end do
281 this%npts = neko_hex_npts
283 if (this%lgenc) then
284 allocate(htable_i4t4_t::this%facet_map)
285 select type (fmp => this%facet_map)
286 type is (htable_i4t4_t)
287 call fmp%init(this%nelv, facet_data)
288 end select
289
290 allocate(this%facet_neigh(neko_hex_nfcs, this%nelv))
291
292 allocate(this%edge_lid(neko_hex_neds, this%nelv))
293 allocate(this%face_lid(neko_hex_nfcs, this%nelv))
294 end if
295 else if (this%gdim .eq. 2) then
296 do i = 1, this%nelv
297 allocate(quad_t::this%elements(i)%e)
298 end do
299 this%npts = neko_quad_npts
300 if (this%lgenc) then
301 allocate(htable_i4t2_t::this%facet_map)
302 select type (fmp => this%facet_map)
303 type is (htable_i4t2_t)
304 call fmp%init(this%nelv, facet_data)
305 end select
306
307 allocate(this%facet_neigh(neko_quad_neds, this%nelv))
308
309 allocate(this%edge_lid(neko_quad_neds, this%nelv))
310 end if
311 else
312 call neko_error("Invalid dimension")
313 end if
314
316 allocate(this%points(this%npts*this%nelv))
317
318 ! Only init if we generate connectivity; point_neigh is sized and
319 ! allocated by generate_conn, once the number of unique points is known
320 if (this%lgenc) then
321 allocate(this%pt_lid(this%npts, this%nelv))
322 end if
323
324 allocate(this%facet_type(2 * this%gdim, this%nelv))
325 this%facet_type = 0
326
327 allocate(this%htp)
328 call this%htp%init(this%npts*this%nelv, i)
329 call this%htel%init(this%nelv, i)
330
331 call this%periodic%init(this%nelv)
332
333 allocate(this%labeled_zones(neko_msh_max_zlbls))
334 do i = 1, neko_msh_max_zlbls
335 call this%labeled_zones(i)%init(this%nelv)
336 end do
337
338 call this%curve%init(this%nelv)
339
340 call this%ddata%init()
341
342 allocate(this%neigh(0:pe_size-1))
343 this%neigh = .false.
344
345 this%mpts = 0
346 this%mfcs = 0
347 this%meds = 0
348
349 end subroutine mesh_init_common
350
352 subroutine mesh_free(this)
353 class(mesh_t), intent(inout) :: this
354 integer :: i
355
356 if (allocated(this%htp)) then
357 call this%htp%free()
358 deallocate(this%htp)
359 end if
360 call this%htel%free()
361 call this%ddata%free()
362 call this%curve%free()
363
364 if (allocated(this%pt_lid)) then
365 deallocate(this%pt_lid)
366 end if
367
368 if (allocated(this%edge_lid)) then
369 deallocate(this%edge_lid)
370 end if
371
372 if (allocated(this%face_lid)) then
373 deallocate(this%face_lid)
374 end if
375
376 if (allocated(this%edge_pts)) then
377 deallocate(this%edge_pts)
378 end if
379
380 if (allocated(this%face_pts)) then
381 deallocate(this%face_pts)
382 end if
383
384 if (allocated(this%dfrmd_el)) then
385 deallocate(this%dfrmd_el)
386 end if
387
388 if (allocated(this%elements)) then
389 do i = 1, this%nelv
390 call this%elements(i)%e%free()
391 deallocate(this%elements(i)%e)
392 end do
393 deallocate(this%elements)
394 end if
395
396 if (allocated(this%facet_map)) then
397 select type (fmp => this%facet_map)
398 type is (htable_i4t2_t)
399 call fmp%free()
400 type is (htable_i4t4_t)
401 call fmp%free()
402 end select
403 deallocate(this%facet_map)
404 end if
405
406 if (allocated(this%facet_neigh)) then
407 deallocate(this%facet_neigh)
408 end if
409
410 if (allocated(this%point_neigh)) then
411 do i = 1, size(this%point_neigh)
412 call this%point_neigh(i)%free()
413 end do
414 ! This causes Cray Fortran to take a long vacation
415 !deallocate(this%point_neigh)
416 end if
417
418 if (allocated(this%facet_type)) then
419 deallocate(this%facet_type)
420 end if
421 if (allocated(this%labeled_zones)) then
422 do i = 1, neko_msh_max_zlbls
423 call this%labeled_zones(i)%free()
424 end do
425 deallocate(this%labeled_zones)
426 end if
427
428 if (allocated(this%neigh)) then
429 deallocate(this%neigh)
430 end if
431
432 if (allocated(this%neigh_order)) then
433 deallocate(this%neigh_order)
434 end if
435
436 if (allocated(this%points)) then
437 deallocate(this%points)
438 end if
439
440 call this%periodic%free()
441 this%lconn = .false.
442 this%lnumr = .false.
443 this%ldist = .false.
444 this%lgenc = .true.
445
446 end subroutine mesh_free
447
448 subroutine mesh_finalize(this)
449 class(mesh_t), target, intent(inout) :: this
450 integer :: i
451
452 call mesh_generate_flags(this)
453 call mesh_generate_conn(this)
454
455 call this%periodic%finalize()
456 do i = 1, neko_msh_max_zlbls
457 call this%labeled_zones(i)%finalize()
458 end do
459 call this%curve%finalize()
460
461 ! Due to a bug, right handedness check disabled for the time being.
462 !call this%check_right_handedness()
463
464 end subroutine mesh_finalize
465
466 subroutine mesh_generate_flags(this)
467 type(mesh_t), intent(inout) :: this
468 real(kind=dp) :: u(3), v(3), w(3), temp
469 integer :: e
470
471 do e = 1, this%nelv
472 if (this%gdim .eq. 2) then
473 this%dfrmd_el(e) = .false.
474 u = this%elements(e)%e%pts(2)%p%x - this%elements(e)%e%pts(1)%p%x
475 v = this%elements(e)%e%pts(3)%p%x - this%elements(e)%e%pts(1)%p%x
476 temp = u(1)*v(1) + u(2)*v(2)
477 if(.not. abscmp(temp, 0d0)) this%dfrmd_el(e) = .true.
478 else
479 this%dfrmd_el(e) = .false.
480 u = this%elements(e)%e%pts(2)%p%x - this%elements(e)%e%pts(1)%p%x
481 v = this%elements(e)%e%pts(3)%p%x - this%elements(e)%e%pts(1)%p%x
482 w = this%elements(e)%e%pts(5)%p%x - this%elements(e)%e%pts(1)%p%x
483 temp = u(1)*v(1) + u(2)*v(2) + u(3)*v(3)
484 if(.not. abscmp(temp, 0d0)) this%dfrmd_el(e) = .true.
485 temp = u(1)*w(1) + u(2)*w(2) + u(3)*w(3)
486 if(.not. abscmp(temp, 0d0)) this%dfrmd_el(e) = .true.
487 u = this%elements(e)%e%pts(7)%p%x - this%elements(e)%e%pts(8)%p%x
488 v = this%elements(e)%e%pts(6)%p%x - this%elements(e)%e%pts(8)%p%x
489 w = this%elements(e)%e%pts(4)%p%x - this%elements(e)%e%pts(8)%p%x
490 temp = u(1)*v(1) + u(2)*v(2) + u(3)*v(3)
491 if(.not. abscmp(temp, 0d0)) this%dfrmd_el(e) = .true.
492 temp = u(1)*w(1) + u(2)*w(2) + u(3)*w(3)
493 if(.not. abscmp(temp, 0d0)) this%dfrmd_el(e) = .true.
494 end if
495 end do
496 end subroutine mesh_generate_flags
497
499 subroutine mesh_all_deformed(this)
500 class(mesh_t), intent(inout) :: this
501 this%dfrmd_el = .true.
502 end subroutine mesh_all_deformed
503
505 subroutine mesh_generate_conn(this)
506 class(mesh_t), target, intent(inout) :: this
507 type(tuple_i4_t) :: edge
508 type(tuple4_i4_t) :: face, face_comp
509 type(tuple_i4_t) :: facet_data
510 type(stack_i4_t) :: neigh_order
511 class(element_t), pointer :: ep
512 integer :: p_local_idx
513 integer :: el, id
514 integer :: i, j, k, ierr, el_glb_idx, n_sides, n_nodes, src, dst
515
516 if (this%lconn) return
517
518 if (.not. this%lgenc) return
519
520 !If we generate connectivity, we do that here.
521 !
522 ! Register every point first; add_point hands back the local id directly,
523 ! and once all of them are in, mpts is the exact size of point_neigh
524 do el = 1, this%nelv
525 ep => this%elements(el)%e
526 do i = 1, this%npts
527 call this%add_point(ep%pts(i)%p, p_local_idx)
528 this%pt_lid(i, el) = p_local_idx
529 end do
530 end do
531
532 ! Temporary workaround to avoid long vacations with Cray Fortran
533 if (allocated(this%point_neigh)) then
534 deallocate(this%point_neigh)
535 end if
536 allocate(this%point_neigh(this%mpts))
537 do i = 1, this%mpts
538 call this%point_neigh(i)%init(size = 4)
539 end do
540
541 do el = 1, this%nelv
542 !should stack have inout on what we push? would be neat with in
543 id = this%elements(el)%e%id()
544 do i = 1, this%npts
545 call this%point_neigh(this%pt_lid(i, el))%push(id)
546 end do
547 end do
548
549 !
550 ! Enumerate the unique edges and faces of the local mesh
551 ! (Note it needs to be called after all points have been added)
552 !
553 call mesh_generate_edge_lid(this)
554
555 if (this%gdim .eq. 3) then
556 call mesh_generate_face_lid(this)
557 end if
558
559
560 if (this%gdim .eq. 2) then
561 n_sides = 4
562 n_nodes = 2
563 else
564 n_sides = 6
565 n_nodes = 4
566 end if
567
568 ! Compute global number of unique points
569 call mpi_allreduce(this%max_pts_id, this%glb_mpts, 1, &
570 mpi_integer, mpi_max, neko_comm, ierr)
571
572 !
573 ! Find all (local) boundaries
574 !
575
579 select type (fmp => this%facet_map)
580 type is(htable_i4t2_t)
581 do k = 1, 2
582 do i = 1, this%nelv
583 el_glb_idx = i + this%offset_el
584 do j = 1, n_sides
585 call this%elements(i)%e%facet_id(edge, j)
586
587 ! Assume that all facets are on the exterior
588 facet_data%x = [0, 0]
589
590 !check it this face has shown up earlier
591 if (fmp%get(edge, facet_data) .eq. 0) then
592 !if element is already recognized on face
593 if (facet_data%x(1) .eq. el_glb_idx ) then
594 this%facet_neigh(j, i) = facet_data%x(2)
595 else if( facet_data%x(2) .eq. el_glb_idx) then
596 this%facet_neigh(j, i) = facet_data%x(1)
597 !if this is the second element, arrange so low id is first
598 else if(facet_data%x(1) .gt. el_glb_idx) then
599 facet_data%x(2) = facet_data%x(1)
600 facet_data%x(1) = el_glb_idx
601 this%facet_neigh(j, i) = facet_data%x(2)
602 call fmp%set(edge, facet_data)
603 else if(facet_data%x(1) .lt. el_glb_idx) then
604 facet_data%x(2) = el_glb_idx
605 this%facet_neigh(j, i) = facet_data%x(1)
606 call fmp%set(edge, facet_data)
607 end if
608 else
609 facet_data%x(1) = el_glb_idx
610 this%facet_neigh(j, i) = facet_data%x(2)
611 call fmp%set(edge, facet_data)
612 end if
613 end do
614 end do
615 end do
616 type is(htable_i4t4_t)
617
618 do k = 1, 2
619 do i = 1, this%nelv
620 el_glb_idx = i + this%offset_el
621 do j = 1, n_sides
622 call this%elements(i)%e%facet_id(face, j)
623
624 facet_data%x = (/ 0, 0/)
625
626 !check it this face has shown up earlier
627 if (fmp%get(face, facet_data) .eq. 0) then
628 !if element is already recognized on face
629 if (facet_data%x(1) .eq. el_glb_idx ) then
630 this%facet_neigh(j, i) = facet_data%x(2)
631 call this%elements(i)%e%facet_id(face_comp, &
632 j + (2*mod(j, 2) - 1))
633 if (face_comp .eq. face) then
634 facet_data%x(2) = el_glb_idx
635 this%facet_neigh(j, i) = facet_data%x(1)
636 call fmp%set(face, facet_data)
637 end if
638 else if( facet_data%x(2) .eq. el_glb_idx) then
639 this%facet_neigh(j, i) = facet_data%x(1)
640 !if this is the second element, arrange so low id is first
641 else if(facet_data%x(1) .gt. el_glb_idx) then
642 facet_data%x(2) = facet_data%x(1)
643 facet_data%x(1) = el_glb_idx
644 this%facet_neigh(j, i) = facet_data%x(2)
645 call fmp%set(face, facet_data)
646 else if(facet_data%x(1) .lt. el_glb_idx) then
647 facet_data%x(2) = el_glb_idx
648 this%facet_neigh(j, i) = facet_data%x(1)
649 call fmp%set(face, facet_data)
650 end if
651 else
652 facet_data%x(1) = el_glb_idx
653 this%facet_neigh(j, i) = 0
654 call fmp%set(face, facet_data)
655 end if
656 end do
657 end do
658 end do
659 class default
660 call neko_error('Invalid facet map')
661 end select
662
663
664 !
665 ! Find all external (between PEs) boundaries
666 !
667 if (pe_size .gt. 1) then
668
670
671 !
672 ! Generate neighbour exchange order
673 !
674 call neigh_order%init(pe_size)
675
676 do i = 1, pe_size - 1
677 src = modulo(pe_rank - i + pe_size, pe_size)
678 dst = modulo(pe_rank + i, pe_size)
679 if (this%neigh(src) .or. this%neigh(dst)) then
680 j = i ! adhere to standards...
681 call neigh_order%push(j)
682 end if
683 end do
684
685 allocate(this%neigh_order(neigh_order%size()))
686 select type(order => neigh_order%data)
687 type is (integer)
688 do i = 1, neigh_order%size()
689 this%neigh_order(i) = order(i)
690 end do
691 end select
692 call neigh_order%free()
693
695 else
696 allocate(this%neigh_order(1))
697 this%neigh_order = 1
698 end if
699
700 !
701 ! Find all internal/extenral edge connections
702 ! (Note it needs to be called after external point connections has
703 ! been established)
704 !
705 if (this%gdim .eq. 3) then
706 call mesh_generate_edge_conn(this)
707 end if
708
709
711
712 ! The endpoints are only needed while the numbering is generated, from
713 ! here on an edge or a face is addressed by its element and local number
714 deallocate(this%edge_pts)
715 if (allocated(this%face_pts)) then
716 deallocate(this%face_pts)
717 end if
718
719 ! The global->local point table has served its purpose; every local id
720 ! needed from here on is in pt_lid, edge_lid and face_lid
721 call this%htp%free()
722 deallocate(this%htp)
723
724 this%lconn = .true.
725
726 end subroutine mesh_generate_conn
727
737 subroutine mesh_generate_edge_lid(this)
738 type(mesh_t), target, intent(inout) :: this
739 type(tuple_i4_t) :: e
740 class(element_t), pointer :: ep
741 integer, allocatable :: chain(:), head(:)
742 integer :: el, i, id, n_eds
743
744 if (this%gdim .eq. 3) then
745 n_eds = neko_hex_neds
746 else
747 n_eds = neko_quad_neds
748 end if
749
750 allocate(this%edge_pts(2, n_eds * this%nelv))
751 allocate(chain(n_eds * this%nelv))
752
753 ! Chains are indexed by local point id, bounded by the number of points
754 allocate(head(this%npts * this%nelv))
755 head = 0
756
757 this%meds = 0
758 do el = 1, this%nelv
759 ep => this%elements(el)%e
760 select type (ep)
761 type is (hex_t)
762 do i = 1, neko_hex_neds
763 call ep%edge_id(e, i)
764 call mesh_add_edge(this, e, head, chain, id)
765 this%edge_lid(i, el) = id
766 end do
767 type is (quad_t)
768 do i = 1, neko_quad_neds
769 call ep%facet_id(e, i)
770 call mesh_add_edge(this, e, head, chain, id)
771 this%edge_lid(i, el) = id
772 end do
773 end select
774 end do
775
776 deallocate(chain)
777 deallocate(head)
778
779 end subroutine mesh_generate_edge_lid
780
789 subroutine mesh_generate_face_lid(this)
790 type(mesh_t), target, intent(inout) :: this
791 type(tuple4_i4_t) :: f
792 class(element_t), pointer :: ep
793 integer, allocatable :: chain(:), head(:)
794 integer :: el, i, id
795
796 allocate(this%face_pts(4, neko_hex_nfcs * this%nelv))
797 allocate(chain(neko_hex_nfcs * this%nelv))
798
799 ! Chains are indexed by local point id, bounded by the number of points
800 allocate(head(this%npts * this%nelv))
801 head = 0
802
803 this%mfcs = 0
804 do el = 1, this%nelv
805 ep => this%elements(el)%e
806 select type (ep)
807 type is (hex_t)
808 do i = 1, neko_hex_nfcs
809 call ep%facet_id(f, i)
810 call mesh_add_face(this, f, head, chain, id)
811 this%face_lid(i, el) = id
812 end do
813 end select
814 end do
815
816 deallocate(chain)
817 deallocate(head)
818
819 end subroutine mesh_generate_face_lid
820
823 type(mesh_t), intent(inout) :: this
824 type(tuple_i4_t) :: edge, edge2
825 type(tuple4_i4_t) :: face, face2
826 type(tuple_i4_t) :: facet_data
827 type(stack_i4_t) :: buffer
828 type(mpi_status) :: status
829 type(mpi_request) :: send_req, recv_req
830 integer, allocatable :: recv_buffer(:)
831 integer :: i, j, k, el_glb_idx, n_sides, n_nodes, facet, element, l
832 integer :: max_recv, ierr, src, dst, n_recv, recv_side, neigh_el
833
834
835 if (this%gdim .eq. 2) then
836 n_sides = 4
837 n_nodes = 2
838 else
839 n_sides = 6
840 n_nodes = 4
841 end if
842
843 call buffer%init()
844
845 ! Build send buffers containing
846 ! [el_glb_idx, side number, facet_id (global ids of points)]
847 do i = 1, this%nelv
848 el_glb_idx = i + this%offset_el
849 do j = 1, n_sides
850 facet = j ! Adhere to standards...
851 if (this%facet_neigh(j, i) .eq. 0) then
852 if (n_nodes .eq. 2) then
853 call this%elements(i)%e%facet_id(edge, j)
854 call buffer%push(el_glb_idx)
855 call buffer%push(facet)
856 do k = 1, n_nodes
857 call buffer%push(edge%x(k))
858 end do
859 else
860 call this%elements(i)%e%facet_id(face, j)
861 call buffer%push(el_glb_idx)
862 call buffer%push(facet)
863 do k = 1, n_nodes
864 call buffer%push(face%x(k))
865 end do
866 end if
867 end if
868 end do
869 end do
870
871
872 call mpi_allreduce(buffer%size(), max_recv, 1, &
873 mpi_integer, mpi_max, neko_comm, ierr)
874
875 allocate(recv_buffer(max_recv))
876
877 do i = 1, size(this%neigh_order)
878 src = modulo(pe_rank - this%neigh_order(i) + pe_size, pe_size)
879 dst = modulo(pe_rank + this%neigh_order(i), pe_size)
880
881 if (this%neigh(src)) then
882 call mpi_irecv(recv_buffer, max_recv, mpi_integer, &
883 src, 0, neko_comm, recv_req, ierr)
884 end if
885
886 if (this%neigh(dst)) then
887 ! Synchronous send so the buffer is never eager-buffered as an
888 ! unexpected message, which exhausts the MPI internal buffer pool
889 ! (SIGBUS) under flat MPI at high rank counts. Deadlock-safe: the
890 ! matching recv is pre-posted at the top of the peer's iteration.
891 ! The buffer is passed as the stack's own storage, not as the
892 ! result of %array(): a compiler may pass a function result via a
893 ! temporary copy, which is freed while the send is still pending.
894 select type (sbarray => buffer%data)
895 type is (integer)
896 call mpi_issend(sbarray, buffer%size(), mpi_integer, &
897 dst, 0, neko_comm, send_req, ierr)
898 end select
899 end if
900
901 if (this%neigh(src)) then
902 call mpi_wait(recv_req, status, ierr)
903 call mpi_get_count(status, mpi_integer, n_recv, ierr)
904
905 select type (fmp => this%facet_map)
906 type is(htable_i4t2_t)
907 do j = 1, n_recv, n_nodes + 2
908 neigh_el = recv_buffer(j)
909 recv_side = recv_buffer(j+1)
910
911 edge = (/ recv_buffer(j+2), recv_buffer(j+3) /)
912
913 facet_data = (/ 0, 0 /)
914 !Check if the face is present on this PE
915 if (fmp%get(edge, facet_data) .eq. 0) then
916 element = facet_data%x(1) - this%offset_el
917 !Check which side is connected
918 do l = 1, n_sides
919 call this%elements(element)%e%facet_id(edge2, l)
920 if(edge2 .eq. edge) then
921 facet = l
922 exit
923 end if
924 end do
925 this%facet_neigh(facet, element) = -neigh_el
926 facet_data%x(2) = -neigh_el
927
928 ! Update facet map
929 call fmp%set(edge, facet_data)
930
931 call this%ddata%set_shared_el_facet(element, facet)
932
933 call this%ddata%set_shared_facet( &
934 this%edge_lid(facet, element))
935
936 end if
937
938 end do
939 type is(htable_i4t4_t)
940 do j = 1, n_recv, n_nodes + 2
941 neigh_el = recv_buffer(j)
942 recv_side = recv_buffer(j+1)
943
944 face%x = (/ recv_buffer(j+2), recv_buffer(j+3), &
945 recv_buffer(j+4), recv_buffer(j+5) /)
946
947
948 facet_data%x = (/ 0, 0 /)
949
950 !Check if the face is present on this PE
951 if (fmp%get(face, facet_data) .eq. 0) then
952 ! Determine opposite side and update neighbor
953 element = facet_data%x(1) - this%offset_el
954 do l = 1, 6
955 call this%elements(element)%e%facet_id(face2, l)
956 if(face2 .eq. face) then
957 facet = l
958 exit
959 end if
960 end do
961 this%facet_neigh(facet, element) = -neigh_el
962 facet_data%x(2) = -neigh_el
963
964 ! Update facet map
965 call fmp%set(face, facet_data)
966
967 call this%ddata%set_shared_el_facet(element, facet)
968
969 call this%ddata%set_shared_facet( &
970 this%face_lid(facet, element))
971
972 end if
973
974 end do
975 end select
976 end if
977
978 if (this%neigh(dst)) then
979 call mpi_wait(send_req, mpi_status_ignore, ierr)
980 end if
981
982 end do
983
984
985 deallocate(recv_buffer)
986
987 call buffer%free()
988
990
1000 type(mesh_t), intent(inout) :: this
1001 type(stack_i8_t) :: cr_buf
1002 integer(i8), allocatable :: buf(:), body(:)
1003 integer(i8), pointer :: cr_data(:)
1004 integer, allocatable :: gkey(:), gperm(:), rpos(:)
1005 integer, contiguous, pointer :: neighs(:)
1006 integer :: i, j, k, n, p, owner, num_neigh, nrec, rlen
1007 integer :: pt_glb_idx, pt_loc_idx, src_rank, neigh_el, rk, rp
1008
1009 !
1010 ! Phase 1: route every local point's element list to its canonical owner.
1011 ! record payload = [glb_idx, origin, elems...]
1012 !
1013 call cr_buf%init(this%mpts * 4)
1014 allocate(body(8))
1015 do i = 1, this%mpts
1016 pt_glb_idx = this%points(i)%id() ! Adhere to standards...
1017 num_neigh = this%point_neigh(i)%size()
1018 if (2 + num_neigh .gt. size(body)) then
1019 deallocate(body)
1020 allocate(body(2 + num_neigh))
1021 end if
1022 body(1) = int(pt_glb_idx, i8) ! glb_idx
1023 body(2) = int(pe_rank, i8) ! origin
1024 neighs => this%point_neigh(i)%array()
1025 do j = 1, num_neigh
1026 body(2 + j) = int(neighs(j), i8) ! element ids
1027 end do
1028 owner = modulo(pt_glb_idx, pe_size)
1029 call crystal_router_pack(cr_buf, owner, body(1:2 + num_neigh))
1030 end do
1031 deallocate(body)
1032
1033 n = cr_buf%size()
1034 allocate(buf(max(n, 1)))
1035 if (n .gt. 0) then
1036 cr_data => cr_buf%array()
1037 buf(1:n) = cr_data(1:n)
1038 end if
1039 call cr_buf%free()
1040
1041 call crystal_router_transfer(buf, n)
1042
1043 !
1044 ! Phase 2: at the owner, group received records by glb_idx and reflect,
1045 ! to each holder, the element lists of every *other* holder.
1046 ! reply = [dest=holder, len=2+num_neigh, glb_idx, src_rank, elems...]
1047 !
1048 ! Index the received records and sort their keys (glb_idx) so equal keys
1049 ! form contiguous runs; per-point holder counts are small, so the
1050 ! all-pairs reflection within a run is cheap.
1051 nrec = 0
1052 p = 1
1053 do while (p .le. n)
1054 nrec = nrec + 1
1055 p = p + 2 + int(buf(p + 1))
1056 end do
1057
1058 allocate(gkey(max(nrec, 1)), gperm(max(nrec, 1)), rpos(max(nrec, 1)))
1059 nrec = 0
1060 p = 1
1061 do while (p .le. n)
1062 nrec = nrec + 1
1063 rpos(nrec) = p ! record start offset in buf
1064 gkey(nrec) = int(buf(p + 2)) ! glb_idx
1065 p = p + 2 + int(buf(p + 1))
1066 end do
1067 if (nrec .gt. 0) call sort(gkey, gperm, nrec)
1068
1069 call cr_buf%init(max(n, 1))
1070 i = 1
1071 do while (i .le. nrec)
1072 ! [i, j) is the run of records sharing the same glb_idx
1073 j = i
1074 do while (j .le. nrec)
1075 if (gkey(j) .ne. gkey(i)) exit
1076 j = j + 1
1077 end do
1078 ! All-pairs reflection within the run (skip singletons = unshared):
1079 ! send source holder p's record body (glb_idx, origin, elems) to
1080 ! recipient holder k, addressed to k's origin rank.
1081 if (j - i .gt. 1) then
1082 do k = i, j - 1 ! recipient holder
1083 rk = rpos(gperm(k))
1084 do p = i, j - 1 ! source holder
1085 if (p .eq. k) cycle
1086 rp = rpos(gperm(p))
1087 call crystal_router_pack(cr_buf, int(buf(rk + 3)), &
1088 buf(rp + 2 : rp + 1 + int(buf(rp + 1))))
1089 end do
1090 end do
1091 end if
1092 i = j
1093 end do
1094 deallocate(gkey, gperm, rpos)
1095
1096 n = cr_buf%size()
1097 if (allocated(buf)) deallocate(buf)
1098 allocate(buf(max(n, 1)))
1099 if (n .gt. 0) then
1100 cr_data => cr_buf%array()
1101 buf(1:n) = cr_data(1:n)
1102 end if
1103 call cr_buf%free()
1104
1105 call crystal_router_transfer(buf, n)
1106
1107 !
1108 ! Phase 3: finalise locally. Each reply names a remote holder of one of our
1109 ! points; mark it as a neighbour and absorb its (remote) element list.
1110 !
1111 p = 1
1112 do while (p .le. n)
1113 rlen = int(buf(p + 1))
1114 pt_glb_idx = int(buf(p + 2))
1115 src_rank = int(buf(p + 3))
1116 pt_loc_idx = this%have_point_glb_idx(pt_glb_idx)
1117 if (pt_loc_idx .gt. 0) then
1118 this%neigh(src_rank) = .true.
1119 call this%ddata%set_shared_point(pt_loc_idx)
1120 do k = 1, rlen - 2
1121 neigh_el = -int(buf(p + 3 + k))
1122 call this%point_neigh(pt_loc_idx)%push(neigh_el)
1123 end do
1124 end if
1125 p = p + 2 + rlen
1126 end do
1127
1128 if (allocated(buf)) deallocate(buf)
1129
1131
1136 type(mesh_t), target, intent(inout) :: this
1137 integer, allocatable :: edge_lp(:,:)
1138 logical, allocatable :: shared_edges(:)
1139 type(uset_i8_t), target :: edge_idx, ghost, owner
1140 type(stack_i8_t), target :: send_buff
1141 type(htable_i8_t) :: glb_to_loc
1142 type(mpi_status) :: status
1143 type(mpi_request) :: send_req, recv_req
1144 integer, contiguous, pointer :: p1(:), p2(:), ns_id(:)
1145 integer :: i, j, id, lid, ierr, num_edge_glb, edge_offset, num_edge_loc
1146 integer :: k, l , shared_offset, glb_nshared, n_glb_id
1147 integer(kind=i8) :: C, glb_max, glb_id
1148 integer(kind=i8), pointer :: glb_ptr
1149 integer(kind=i8), allocatable :: recv_buff(:)
1150 type(stack_i4_t), target :: non_shared_edges
1151 integer :: max_recv, src, dst, n_recv
1152
1153
1155 allocate(this%ddata%local_to_global_edge(this%meds))
1156
1157 call edge_idx%init(this%meds)
1158 call send_buff%init(this%meds)
1159 call owner%init(this%meds)
1160
1161 call glb_to_loc%init(32, i)
1162
1163 !
1164 ! Determine/ constants used to generate unique global edge numbers
1165 ! for shared edges
1166 !
1167 c = int(this%glb_nelv, i8) * int(neko_hex_neds, i8)
1168
1169 num_edge_glb = 2* this%meds
1170 call mpi_allreduce(mpi_in_place, num_edge_glb, 1, &
1171 mpi_integer, mpi_sum, neko_comm, ierr)
1172
1173 glb_max = int(num_edge_glb, i8)
1174
1175 call non_shared_edges%init(this%meds)
1176
1177 ! Resolve both endpoints of every edge to a local point id up front, so
1178 ! that the neighbour search below reads nothing but plain arrays
1179 allocate(edge_lp(2, this%meds))
1180 do lid = 1, this%meds
1181 id = this%edge_pts(1, lid)
1182 edge_lp(1, lid) = this%have_point_glb_idx(id)
1183 id = this%edge_pts(2, lid)
1184 edge_lp(2, lid) = this%have_point_glb_idx(id)
1185 end do
1186
1187 !
1188 ! An edge is shared when both of its endpoints see the same remote
1189 ! element. Every iteration writes only its own flag, so this search,
1190 ! the expensive part of the numbering, is the part that threads
1191 !
1192 allocate(shared_edges(this%meds))
1193 !$omp parallel do private(lid, k, l, p1, p2, i, j)
1194 do lid = 1, this%meds
1195 k = edge_lp(1, lid)
1196 l = edge_lp(2, lid)
1197 p1 => this%point_neigh(k)%array()
1198 p2 => this%point_neigh(l)%array()
1199
1200 shared_edges(lid) = .false.
1201
1202 ! Find edge neighbor from point neighbors
1203 do i = 1, this%point_neigh(k)%size()
1204 do j = 1, this%point_neigh(l)%size()
1205 if ((p1(i) .eq. p2(j)) .and. &
1206 (p1(i) .lt. 0) .and. (p2(j) .lt. 0)) then
1207 shared_edges(lid) = .true.
1208 end if
1209 end do
1210 end do
1211 end do
1212 !$omp end parallel do
1213 deallocate(edge_lp)
1214
1215 ! The bookkeeping stays ordered; the order the ids are pushed in is what
1216 ! the global numbering below is built from
1217 do lid = 1, this%meds
1218 id = lid
1219
1220 ! Generate a unique id for the shared edge as,
1221 ! ((e1 * C) + e2 )) + glb_max if e1 > e2
1222 ! ((e2 * C) + e1 )) + glb_max if e2 > e1
1223 if (shared_edges(id)) then
1224 call this%ddata%set_shared_edge(id)
1225 glb_id = ((int(this%edge_pts(1, id), i8)) + &
1226 int(this%edge_pts(2, id), i8)*c) + glb_max
1227 call glb_to_loc%set(glb_id, id)
1228 call edge_idx%add(glb_id)
1229 call owner%add(glb_id) ! Always assume the PE is the owner
1230 call send_buff%push(glb_id)
1231 else
1232 call non_shared_edges%push(id)
1233 end if
1234 end do
1235 deallocate(shared_edges)
1236
1237 ! Determine start offset for global numbering of locally owned edges
1238 edge_offset = 0
1239 num_edge_loc = non_shared_edges%size()
1240 call mpi_exscan(num_edge_loc, edge_offset, 1, &
1241 mpi_integer, mpi_sum, neko_comm, ierr)
1242 edge_offset = edge_offset + 1
1243
1244 ! Construct global numbering of locally owned edges
1245 ns_id => non_shared_edges%array()
1246 do i = 1, non_shared_edges%size()
1247 call this%ddata%set_local_to_global_edge(ns_id(i), edge_offset)
1248 edge_offset = edge_offset + 1
1249 end do
1250 nullify(ns_id)
1251
1252 !
1253 ! Renumber shared edges into integer range
1254 !
1255
1256 call mpi_allreduce(send_buff%size(), max_recv, 1, &
1257 mpi_integer, mpi_max, neko_comm, ierr)
1258
1259 call ghost%init(send_buff%size())
1260
1261 allocate(recv_buff(max_recv))
1262
1263 do i = 1, size(this%neigh_order)
1264 src = modulo(pe_rank - this%neigh_order(i) + pe_size, pe_size)
1265 dst = modulo(pe_rank + this%neigh_order(i), pe_size)
1266
1267 if (this%neigh(src)) then
1268 call mpi_irecv(recv_buff, max_recv, mpi_integer8, &
1269 src, 0, neko_comm, recv_req, ierr)
1270 end if
1271
1272 if (this%neigh(dst)) then
1273 ! We should use the %array() procedure, which works great for
1274 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1275 ! certain data types
1276 select type(sbarray=>send_buff%data)
1277 type is (integer(i8))
1278 ! Synchronous send to avoid eager-buffering the key list as an
1279 ! unexpected message (FJMPI buffer-pool exhaustion / SIGBUS under
1280 ! flat MPI); matching recv is pre-posted by the peer.
1281 call mpi_issend(sbarray, send_buff%size(), mpi_integer8, &
1282 dst, 0, neko_comm, send_req, ierr)
1283 end select
1284 end if
1285
1286 if (this%neigh(src)) then
1287 call mpi_wait(recv_req, status, ierr)
1288 call mpi_get_count(status, mpi_integer8, n_recv, ierr)
1289
1290 do j = 1, n_recv
1291 if ((edge_idx%element(recv_buff(j))) .and. (src .lt. pe_rank)) then
1292 call ghost%add(recv_buff(j))
1293 call owner%remove(recv_buff(j))
1294 end if
1295 end do
1296 end if
1297
1298 if (this%neigh(dst)) then
1299 call mpi_wait(send_req, mpi_status_ignore, ierr)
1300 end if
1301 end do
1302
1303
1304 ! Determine start offset for global numbering of shared edges
1305 glb_nshared = num_edge_loc
1306 call mpi_allreduce(mpi_in_place, glb_nshared, 1, &
1307 mpi_integer, mpi_sum, neko_comm, ierr)
1308
1309 shared_offset = 0
1310 call mpi_exscan(owner%size(), shared_offset, 1, &
1311 mpi_integer, mpi_sum, neko_comm, ierr)
1312 shared_offset = shared_offset + glb_nshared + 1
1313
1314 ! Renumber locally owned set of shared edges
1315 call send_buff%clear()
1316 call owner%iter_init()
1317 do while (owner%iter_next())
1318 glb_ptr => owner%iter_value()
1319 if (glb_to_loc%get(glb_ptr, id) .eq. 0) then
1320 call this%ddata%set_local_to_global_edge(id, shared_offset)
1321
1322 ! Add new number to send data as [old_glb_id new_glb_id] for each edge
1323 call send_buff%push(glb_ptr) ! Old glb_id integer*8
1324 glb_id = int(shared_offset, i8) ! Waste some space here...
1325 call send_buff%push(glb_id) ! New glb_id integer*4
1326
1327 shared_offset = shared_offset + 1
1328 else
1329 call neko_error('Invalid edge id')
1330 end if
1331 end do
1332 nullify(glb_ptr)
1333
1334 ! Determine total number of unique edges in the mesh
1335 ! (This can probably be done in a clever way...)
1336 this%glb_meds = shared_offset -1
1337 call mpi_allreduce(mpi_in_place, this%glb_meds, 1, &
1338 mpi_integer, mpi_max, neko_comm, ierr)
1339
1340 !
1341 ! Update ghosted edges with new global id
1342 !
1343
1344 call mpi_allreduce(send_buff%size(), max_recv, 1, &
1345 mpi_integer, mpi_max, neko_comm, ierr)
1346
1347 deallocate(recv_buff)
1348 allocate(recv_buff(max_recv))
1349
1350
1351 do i = 1, size(this%neigh_order)
1352 src = modulo(pe_rank - this%neigh_order(i) + pe_size, pe_size)
1353 dst = modulo(pe_rank + this%neigh_order(i), pe_size)
1354
1355 if (this%neigh(src)) then
1356 call mpi_irecv(recv_buff, max_recv, mpi_integer8, &
1357 src, 0, neko_comm, recv_req, ierr)
1358 end if
1359
1360 if (this%neigh(dst)) then
1361 ! We should use the %array() procedure, which works great for
1362 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1363 ! certain data types
1364 select type(sbarray=>send_buff%data)
1365 type is (integer(i8))
1366 ! Synchronous send to avoid eager-buffering the key list as an
1367 ! unexpected message (FJMPI buffer-pool exhaustion / SIGBUS under
1368 ! flat MPI); matching recv is pre-posted by the peer.
1369 call mpi_issend(sbarray, send_buff%size(), mpi_integer8, &
1370 dst, 0, neko_comm, send_req, ierr)
1371 end select
1372 end if
1373
1374 if (this%neigh(src)) then
1375 call mpi_wait(recv_req, status, ierr)
1376 call mpi_get_count(status, mpi_integer8, n_recv, ierr)
1377
1378 do j = 1, n_recv, 2
1379 if (ghost%element(recv_buff(j))) then
1380 if (glb_to_loc%get(recv_buff(j), id) .eq. 0) then
1381 n_glb_id = int(recv_buff(j + 1 ), 4)
1382 call this%ddata%set_local_to_global_edge(id, n_glb_id)
1383 else
1384 call neko_error('Invalid edge id')
1385 end if
1386 end if
1387 end do
1388 end if
1389
1390 if (this%neigh(dst)) then
1391 call mpi_wait(send_req, mpi_status_ignore, ierr)
1392 end if
1393 end do
1394
1395 deallocate(recv_buff)
1396 call glb_to_loc%free()
1397 call send_buff%free()
1398 call edge_idx%free()
1399 call non_shared_edges%free()
1400 call ghost%free()
1401 call owner%free()
1402
1403 end subroutine mesh_generate_edge_conn
1404
1407 type(mesh_t), target, intent(inout) :: this
1408 integer, contiguous, pointer :: fd(:), ed(:)
1409 type(tuple4_i4_t) :: face
1410 type(tuple_i4_t) :: edge
1411 type(tuple_i4_t) :: facet_data
1412 type(tuple4_i4_t) :: recv_face
1413 type(tuple_i4_t) :: recv_edge
1414 type(stack_i4_t) :: face_owner
1415 type(htable_i4t4_t) :: face_ghost
1416 type(stack_i4_t) :: edge_owner
1417 type(htable_i4t2_t) :: edge_ghost
1418 type(stack_i4_t) :: send_buff
1419 type(mpi_status) :: status
1420 type(mpi_request) :: send_req, recv_req
1421 integer, allocatable :: recv_buff(:)
1422 integer :: non_shared_facets, shared_facets, facet_offset
1423 integer :: id, lid, glb_nshared, shared_offset, owned_facets
1424 integer :: i, j, ierr, max_recv, src, dst, n_recv
1425
1426 shared_facets = this%ddata%shared_facet%size()
1427
1429 if (this%gdim .eq. 2) then
1430 allocate(this%ddata%local_to_global_facet(this%meds))
1431 call edge_owner%init(this%meds)
1432 call edge_ghost%init(64, i)
1433 non_shared_facets = this%meds - shared_facets
1434 else
1435 allocate(this%ddata%local_to_global_facet(this%mfcs))
1436 call face_owner%init(this%mfcs)
1437 call face_ghost%init(64, i)
1438 non_shared_facets = this%mfcs - shared_facets
1439 end if
1440
1442
1443 facet_offset = 0
1444 call mpi_exscan(non_shared_facets, facet_offset, 1, &
1445 mpi_integer, mpi_sum, neko_comm, ierr)
1446 facet_offset = facet_offset + 1
1447
1448 ! Determine ownership of shared facets
1449 if (this%gdim .eq. 2) then
1450 do lid = 1, this%meds
1451 id = lid
1452 if (.not. this%ddata%shared_facet%element(id)) then
1453 call this%ddata%set_local_to_global_facet(id, facet_offset)
1454 facet_offset = facet_offset + 1
1455 else
1456 edge%x = this%edge_pts(:, id)
1457 select type(fmp => this%facet_map)
1458 type is(htable_i4t2_t)
1459 if (fmp%get(edge, facet_data) .eq. 0) then
1460 if (facet_data%x(2) .lt. 0) then
1461 if (abs(facet_data%x(2)) .lt. (this%offset_el + 1)) then
1462 call edge_ghost%set(edge, id)
1463 else
1464 call edge_owner%push(id)
1465 end if
1466 else
1467 call neko_error("Invalid edge neigh.")
1468 end if
1469 end if
1470 end select
1471 end if
1472 end do
1473 owned_facets = edge_owner%size()
1474 else
1475 do lid = 1, this%mfcs
1476 id = lid
1477 if (.not. this%ddata%shared_facet%element(id)) then
1478 call this%ddata%set_local_to_global_facet(id, facet_offset)
1479 facet_offset = facet_offset + 1
1480 else
1481 face%x = this%face_pts(:, id)
1482 select type(fmp => this%facet_map)
1483 type is(htable_i4t4_t)
1484 if (fmp%get(face, facet_data) .eq. 0) then
1485 if (facet_data%x(2) .lt. 0) then
1486 if (abs(facet_data%x(2)) .lt. (this%offset_el + 1)) then
1487 call face_ghost%set(face, id)
1488 else
1489 call face_owner%push(id)
1490 end if
1491 else
1492 call neko_error("Invalid face neigh.")
1493 end if
1494 end if
1495 end select
1496 end if
1497 end do
1498 owned_facets = face_owner%size()
1499 end if
1500
1501 ! Determine start offset for global numbering of shared facets
1502 glb_nshared = non_shared_facets
1503 call mpi_allreduce(mpi_in_place, glb_nshared, 1, &
1504 mpi_integer, mpi_sum, neko_comm, ierr)
1505
1506 shared_offset = 0
1507 call mpi_exscan(owned_facets, shared_offset, 1, &
1508 mpi_integer, mpi_sum, neko_comm, ierr)
1509 shared_offset = shared_offset + glb_nshared + 1
1510
1511 if (this%gdim .eq. 2) then
1512
1513 if (owned_facets .gt. 32) then
1514 call send_buff%init(owned_facets)
1515 else
1516 call send_buff%init()
1517 end if
1518
1519 ed => edge_owner%array()
1520 do i = 1, edge_owner%size()
1521 id = ed(i)
1522 call this%ddata%set_local_to_global_facet(id, shared_offset)
1523
1524 ! Add new number to send buffer
1525 ! [edge id1 ... edge idn new_glb_id]
1526 do j = 1, 2
1527 call send_buff%push(this%edge_pts(j, id))
1528 end do
1529 call send_buff%push(shared_offset)
1530
1531 shared_offset = shared_offset + 1
1532 end do
1533 nullify(ed)
1534
1535 else
1536
1537 if (owned_facets .gt. 32) then
1538 call send_buff%init(owned_facets)
1539 else
1540 call send_buff%init()
1541 end if
1542
1543 fd => face_owner%array()
1544 do i = 1, face_owner%size()
1545 id = fd(i)
1546 call this%ddata%set_local_to_global_facet(id, shared_offset)
1547
1548 ! Add new number to send buffer
1549 ! [face id1 ... face idn new_glb_id]
1550 do j = 1, 4
1551 call send_buff%push(this%face_pts(j, id))
1552 end do
1553 call send_buff%push(shared_offset)
1554
1555 shared_offset = shared_offset + 1
1556 end do
1557 nullify(fd)
1558
1559 end if
1560
1561 ! Determine total number of unique facets in the mesh
1562 ! (This can probably be done in a clever way...)
1563 this%glb_mfcs = shared_offset - 1
1564 call mpi_allreduce(mpi_in_place, this%glb_mfcs, 1, &
1565 mpi_integer, mpi_max, neko_comm, ierr)
1566
1567 !
1568 ! Update ghosted facets with new global id
1569 !
1570
1571 call mpi_allreduce(send_buff%size(), max_recv, 1, &
1572 mpi_integer, mpi_max, neko_comm, ierr)
1573
1574 allocate(recv_buff(max_recv))
1575
1577 do i = 1, size(this%neigh_order)
1578 src = modulo(pe_rank - this%neigh_order(i) + pe_size, pe_size)
1579 dst = modulo(pe_rank + this%neigh_order(i), pe_size)
1580
1581 if (this%neigh(src)) then
1582 call mpi_irecv(recv_buff, max_recv, mpi_integer, &
1583 src, 0, neko_comm, recv_req, ierr)
1584 end if
1585
1586 if (this%neigh(dst)) then
1587 ! Synchronous send to avoid eager-buffered unexpected messages
1588 ! (FJMPI buffer-pool exhaustion / SIGBUS under flat MPI); the
1589 ! matching recv is pre-posted at the top of the peer's iteration.
1590 ! Send from the stack's own storage, not from the result of
1591 ! %array() (see mesh_generate_external_facet_conn).
1592 select type (sbarray => send_buff%data)
1593 type is (integer)
1594 call mpi_issend(sbarray, send_buff%size(), mpi_integer, &
1595 dst, 0, neko_comm, send_req, ierr)
1596 end select
1597 end if
1598
1599 if (this%neigh(src)) then
1600 call mpi_wait(recv_req, status, ierr)
1601 call mpi_get_count(status, mpi_integer, n_recv, ierr)
1602
1603 if (this%gdim .eq. 2) then
1604 do j = 1, n_recv, 3
1605
1606 recv_edge = (/recv_buff(j), recv_buff(j+1)/)
1607
1608 ! Check if the PE has the shared edge
1609 if (edge_ghost%get(recv_edge, id) .eq. 0) then
1610 call this%ddata%set_local_to_global_facet(id, recv_buff(j+2))
1611 end if
1612 end do
1613 else
1614 do j = 1, n_recv, 5
1615
1616 recv_face = (/recv_buff(j), recv_buff(j+1), &
1617 recv_buff(j+2), recv_buff(j+3) /)
1618
1619 ! Check if the PE has the shared face
1620 if (face_ghost%get(recv_face, id) .eq. 0) then
1621 call this%ddata%set_local_to_global_facet(id, recv_buff(j+4))
1622 end if
1623 end do
1624 end if
1625 end if
1626
1627 if (this%neigh(dst)) then
1628 call mpi_wait(send_req, mpi_status_ignore, ierr)
1629 end if
1630
1631 end do
1632
1633 if (this%gdim .eq. 2) then
1634 call edge_owner%free()
1635 call edge_ghost%free()
1636 else
1637 call face_owner%free()
1638 call face_ghost%free()
1639 end if
1640
1641 call send_buff%free()
1642 deallocate(recv_buff)
1643
1644 end subroutine mesh_generate_facet_numbering
1645
1646
1648 subroutine mesh_add_quad(this, el, el_glb, p1, p2, p3, p4)
1649 class(mesh_t), target, intent(inout) :: this
1650 integer, value :: el, el_glb
1651 type(point_t), target, intent(inout) :: p1, p2, p3, p4
1652 integer :: p(4)
1653 type(tuple_i4_t) :: e
1654
1655 ! Connectivity invalidated if a new element is added
1656 this%lconn = .false.
1657
1658 ! Numbering invalidated if a new element is added
1659 this%lnumr = .false.
1660
1661 call this%add_point(p1, p(1))
1662 call this%add_point(p2, p(2))
1663 call this%add_point(p3, p(3))
1664 call this%add_point(p4, p(4))
1665
1666 select type (ep => this%elements(el)%e)
1667 type is (quad_t)
1668 call ep%init(el_glb, &
1669 this%points(p(1)), this%points(p(2)), &
1670 this%points(p(3)), this%points(p(4)))
1671
1672
1673 class default
1674 call neko_error('Invalid element type')
1675 end select
1676
1677 end subroutine mesh_add_quad
1678
1680 subroutine mesh_add_hex(this, el, el_glb, p1, p2, p3, p4, p5, p6, p7, p8)
1681 class(mesh_t), target, intent(inout) :: this
1682 integer, value :: el, el_glb
1683 type(point_t), target, intent(inout) :: p1, p2, p3, p4, p5, p6, p7, p8
1684 integer :: p(8)
1685 type(tuple4_i4_t) :: f
1686 type(tuple_i4_t) :: e
1687
1688 ! Connectivity invalidated if a new element is added
1689 this%lconn = .false.
1690
1691 ! Numbering invalidated if a new element is added
1692 this%lnumr = .false.
1693
1694 call this%add_point(p1, p(1))
1695 call this%add_point(p2, p(2))
1696 call this%add_point(p3, p(3))
1697 call this%add_point(p4, p(4))
1698 call this%add_point(p5, p(5))
1699 call this%add_point(p6, p(6))
1700 call this%add_point(p7, p(7))
1701 call this%add_point(p8, p(8))
1702
1703 ! Global to local mapping
1704 call this%htel%set(el_glb, el)
1705
1706 select type (ep => this%elements(el)%e)
1707 type is (hex_t)
1708 call ep%init(el_glb, &
1709 this%points(p(1)), this%points(p(2)), &
1710 this%points(p(3)), this%points(p(4)), &
1711 this%points(p(5)), this%points(p(6)), &
1712 this%points(p(7)), this%points(p(8)))
1713 class default
1714 call neko_error('Invalid element type')
1715 end select
1716
1717 end subroutine mesh_add_hex
1718
1720 subroutine mesh_add_point(this, p, idx)
1721 class(mesh_t), intent(inout) :: this
1722 type(point_t), intent(inout) :: p
1723 integer, intent(inout) :: idx
1724 integer :: tmp
1725
1726 tmp = p%id()
1727
1728 this%max_pts_id = max(this%max_pts_id, tmp)
1729
1730 if (tmp .le. 0) then
1731 call neko_error("Invalid point id")
1732 end if
1733
1734 if (this%htp%get(tmp, idx) .gt. 0) then
1735 this%mpts = this%mpts + 1
1736 call this%htp%set(tmp, this%mpts)
1737 this%points(this%mpts) = p
1738 idx = this%mpts
1739 end if
1740
1741 end subroutine mesh_add_point
1742
1747 subroutine mesh_add_face(this, f, head, chain, idx)
1748 type(mesh_t), intent(inout) :: this
1749 type(tuple4_i4_t), intent(inout) :: f
1750 integer, intent(inout) :: head(:)
1751 integer, intent(inout) :: chain(:)
1752 integer, intent(out) :: idx
1753 integer :: lp
1754
1755 ! The tuple is ordered, so chaining on the first point leaves any two
1756 ! faces in the same chain differing in their remaining points
1757 lp = this%have_point_glb_idx(f%x(1))
1758 if (lp .lt. 1) then
1759 call neko_error('Invalid face point')
1760 end if
1761
1762 idx = head(lp)
1763 do while (idx .gt. 0)
1764 if ((this%face_pts(2, idx) .eq. f%x(2)) .and. &
1765 (this%face_pts(3, idx) .eq. f%x(3)) .and. &
1766 (this%face_pts(4, idx) .eq. f%x(4))) return
1767 idx = chain(idx)
1768 end do
1769
1770 this%mfcs = this%mfcs + 1
1771 idx = this%mfcs
1772 this%face_pts(1, idx) = f%x(1)
1773 this%face_pts(2, idx) = f%x(2)
1774 this%face_pts(3, idx) = f%x(3)
1775 this%face_pts(4, idx) = f%x(4)
1776 chain(idx) = head(lp)
1777 head(lp) = idx
1778
1779 end subroutine mesh_add_face
1780
1785 subroutine mesh_add_edge(this, e, head, chain, idx)
1786 type(mesh_t), intent(inout) :: this
1787 type(tuple_i4_t), intent(inout) :: e
1788 integer, intent(inout) :: head(:)
1789 integer, intent(inout) :: chain(:)
1790 integer, intent(out) :: idx
1791 integer :: lp
1792
1793 ! The tuple is ordered, so chaining on the first point leaves any two
1794 ! edges in the same chain differing in their second point
1795 lp = this%have_point_glb_idx(e%x(1))
1796 if (lp .lt. 1) then
1797 call neko_error('Invalid edge endpoint')
1798 end if
1799
1800 idx = head(lp)
1801 do while (idx .gt. 0)
1802 if (this%edge_pts(2, idx) .eq. e%x(2)) return
1803 idx = chain(idx)
1804 end do
1805
1806 this%meds = this%meds + 1
1807 idx = this%meds
1808 this%edge_pts(1, idx) = e%x(1)
1809 this%edge_pts(2, idx) = e%x(2)
1810 chain(idx) = head(lp)
1811 head(lp) = idx
1812
1813 end subroutine mesh_add_edge
1814
1816 subroutine mesh_mark_curve_element(this, e, curve_data, curve_type)
1817 class(mesh_t), intent(inout) :: this
1818 integer, intent(in) :: e
1819 real(kind=dp), dimension(5,12), intent(in) :: curve_data
1820 integer, dimension(12), intent(in) :: curve_type
1821
1822 if (e .gt. this%nelv) then
1823 call neko_error('Invalid element index')
1824 end if
1825 if ((this%gdim .eq. 2 .and. sum(curve_type(5:8)) .gt. 0) ) then
1826 call neko_error('Invalid curve element')
1827 end if
1828 call this%curve%add_element(e, curve_data, curve_type)
1829
1830 end subroutine mesh_mark_curve_element
1831
1833 subroutine mesh_mark_labeled_facet(this, f, e, label)
1834 class(mesh_t), intent(inout) :: this
1835 integer, intent(in) :: f
1836 integer, intent(in) :: e
1837 integer, intent(in) :: label
1838
1839 if (e .gt. this%nelv) then
1840 call neko_error('Invalid element index')
1841 end if
1842
1843 if ((this%gdim .eq. 2 .and. f .gt. 4) .or. &
1844 (this%gdim .eq. 3 .and. f .gt. 6)) then
1845 call neko_error('Invalid facet index')
1846 end if
1847 call this%labeled_zones(label)%add_facet(f, e)
1848 this%facet_type(f,e) = -label
1849
1850 end subroutine mesh_mark_labeled_facet
1851
1853 subroutine mesh_mark_periodic_facet(this, f, e, pf, pe, pids)
1854 class(mesh_t), intent(inout) :: this
1855 integer, intent(in) :: f
1856 integer, intent(in) :: e
1857 integer, intent(in) :: pf
1858 integer, intent(in) :: pe
1859 integer, intent(inout) :: pids(4)
1860 integer, dimension(4) :: org_ids
1861
1862 call this%get_facet_ids(f, e, org_ids)
1863 call this%periodic%add_periodic_facet(f, e, pf, pe, pids, org_ids)
1864 end subroutine mesh_mark_periodic_facet
1865
1867 subroutine mesh_get_facet_ids(this, f, e, pids)
1868 class(mesh_t), intent(inout) :: this
1869 integer, intent(in) :: f
1870 integer, intent(in) :: e
1871 integer, intent(inout) :: pids(4)
1872 type(point_t), pointer :: pi
1873 type(tuple4_i4_t) :: t
1874 type(tuple_i4_t) :: t2
1875
1876 select type(ele => this%elements(e)%e)
1877 type is(hex_t)
1878 call ele%facet_order(t,f)
1879 pids = t%x
1880 type is(quad_t)
1881 call ele%facet_order(t2,f)
1882 pids(1) = t2%x(1)
1883 pids(2) = t2%x(2)
1884 pids(3) = 0
1885 pids(4) = 0
1886 end select
1887 end subroutine mesh_get_facet_ids
1888
1891 class(mesh_t), intent(inout) :: this
1892 integer :: i,j
1893 integer :: f
1894 integer :: e
1895 integer :: pf
1896 integer :: pe
1897 integer :: org_ids(4), pids(4)
1898 type(point_t), pointer :: pi
1899 integer, dimension(4, 6) :: face_nodes = reshape([ &
1900 1,5,7,3, &
1901 2,6,8,4, &
1902 1,2,6,5, &
1903 3,4,8,7, &
1904 1,2,4,3, &
1905 5,6,8,7],&
1906 [4,6])
1907 integer, dimension(2, 4) :: edge_nodes = reshape([ &
1908 1,3, &
1909 2,4, &
1910 1,2, &
1911 3,4],&
1912 [2,4])
1913
1914 do i = 1, this%periodic%size
1915 e = this%periodic%facet_el(i)%x(2)
1916 f = this%periodic%facet_el(i)%x(1)
1917 pe = this%periodic%p_facet_el(i)%x(2)
1918 pf = this%periodic%p_facet_el(i)%x(1)
1919 pids = this%periodic%p_ids(i)%x
1920 call this%get_facet_ids(f, e, pids)
1921 this%periodic%p_ids(i)%x = pids
1922 end do
1923 do i = 1, this%periodic%size
1924 e = this%periodic%facet_el(i)%x(2)
1925 f = this%periodic%facet_el(i)%x(1)
1926 org_ids = this%periodic%org_ids(i)%x
1927 select type(ele => this%elements(e)%e)
1928 type is(hex_t)
1929 do j = 1, 4
1930 pi => ele%pts(face_nodes(j,f))%p
1931 call pi%set_id(org_ids(j))
1932 end do
1933 type is(quad_t)
1934 do j = 1, 2
1935 pi => ele%pts(edge_nodes(j,f))%p
1936 call pi%set_id(org_ids(j))
1937 end do
1938 end select
1939 end do
1940 end subroutine mesh_reset_periodic_ids
1941
1943 subroutine mesh_create_periodic_ids(this, f, e, pf, pe)
1944 class(mesh_t), intent(inout) :: this
1945 integer, intent(in) :: f
1946 integer, intent(in) :: e
1947 integer, intent(in) :: pf
1948 integer, intent(in) :: pe
1949 type(point_t), pointer :: pi, pj
1950 real(kind=dp) :: l(3)
1951 integer :: i, j, id, match
1952 type(tuple4_i4_t) :: ft
1953 type(tuple_i4_t) :: et
1954 integer :: envvar_len
1955 character(len=255) :: tol_str
1956 real(kind=dp) :: tol
1957 integer, dimension(4, 6) :: face_nodes = reshape([&
1958 1,5,7,3,&
1959 2,6,8,4,&
1960 1,2,6,5,&
1961 3,4,8,7,&
1962 1,2,4,3,&
1963 5,6,8,7],&
1964 [4,6])
1965 integer, dimension(2, 4) :: edge_nodes = reshape([&
1966 1,3,&
1967 2,4,&
1968 1,2,&
1969 3,4 ],&
1970 [2,4])
1971
1972 call get_environment_variable("NEKO_PERIODIC_TOL", tol_str, envvar_len)
1973 if (envvar_len .gt. 0) then
1974 read(tol_str(1:envvar_len), *) tol
1975 else
1976 tol = 1d-7
1977 end if
1978
1979 select type(ele => this%elements(e)%e)
1980 type is(hex_t)
1981 select type(elp => this%elements(pe)%e)
1982 type is(hex_t)
1983 l = 0d0
1984 do i = 1, 4
1985 l = l + ele%pts(face_nodes(i,f))%p%x(1:3) - &
1986 elp%pts(face_nodes(i,pf))%p%x(1:3)
1987 end do
1988 l = l/4
1989 do i = 1, 4
1990 pi => ele%pts(face_nodes(i,f))%p
1991 match = 0
1992 do j = 1, 4
1993 pj => elp%pts(face_nodes(j,pf))%p
1994 if (norm2(pi%x(1:3) - pj%x(1:3) - l) .lt. tol) then
1995 id = min(pi%id(), pj%id())
1996 call pi%set_id(id)
1997 call pj%set_id(id)
1998 match = match + 1
1999 end if
2000 end do
2001 if ( match .gt. 1) then
2002 call neko_error('Multiple matches when creating periodic ids')
2003 else if (match .eq. 0) then
2004 call neko_error('Cannot find matching periodic point')
2005 end if
2006 end do
2007 end select
2008 type is(quad_t)
2009 select type(elp => this%elements(pe)%e)
2010 type is(quad_t)
2011 l = 0d0
2012 do i = 1, 2
2013 l = l + ele%pts(edge_nodes(i,f))%p%x(1:3) - &
2014 elp%pts(edge_nodes(i,pf))%p%x(1:3)
2015 end do
2016 l = l/2
2017 do i = 1, 2
2018 pi => ele%pts(edge_nodes(i,f))%p
2019 do j = 1, 2
2020 pj => elp%pts(edge_nodes(j,pf))%p
2021 !whatabout thie tolerance?
2022 if (norm2(pi%x(1:3) - pj%x(1:3) - l) .lt. tol) then
2023 id = min(pi%id(), pj%id())
2024 call pi%set_id(id)
2025 call pj%set_id(id)
2026 end if
2027 end do
2028 end do
2029 end select
2030 end select
2031 end subroutine mesh_create_periodic_ids
2032
2035 subroutine mesh_apply_periodic_facet(this, f, e, pf, pe, pids)
2036 class(mesh_t), intent(inout) :: this
2037 integer, intent(in) :: f
2038 integer, intent(in) :: e
2039 integer, intent(in) :: pf
2040 integer, intent(in) :: pe
2041 integer, intent(inout) :: pids(4)
2042 type(point_t), pointer :: pi
2043 integer :: i, id
2044 type(tuple4_i4_t) :: ft
2045 type(tuple_i4_t) :: et
2046 integer, dimension(4, 6) :: face_nodes = reshape([&
2047 1,5,7,3,&
2048 2,6,8,4,&
2049 1,2,6,5,&
2050 3,4,8,7,&
2051 1,2,4,3,&
2052 5,6,8,7],&
2053 [4,6])
2054 select type(ele => this%elements(e)%e)
2055 type is(hex_t)
2056 do i = 1, 4
2057 pi => ele%pts(face_nodes(i,f))%p
2058 call pi%set_id(pids(i))
2059 ! Only register the point while the mesh is being built; once the
2060 ! connectivity is generated the periodic ids are already known and
2061 ! the global->local table has been released
2062 if (allocated(this%htp)) then
2063 call this%add_point(pi, id)
2064 end if
2065 end do
2066 end select
2067
2068 end subroutine mesh_apply_periodic_facet
2069
2071 function mesh_get_global_edge(this, el, e) result(global_id)
2072 class(mesh_t), intent(in) :: this
2073 integer, intent(in) :: el
2074 integer, intent(in) :: e
2075 integer :: global_id
2076
2077 global_id = this%edge_lid(e, el)
2078
2079 if (this%gdim .eq. 2) then
2080 if (pe_size .gt. 1) then
2081 global_id = this%ddata%local_to_global_facet(global_id)
2082 end if
2083 else
2084 if (pe_size .gt. 1) then
2085 global_id = this%ddata%local_to_global_edge(global_id)
2086 end if
2087 end if
2088
2089 end function mesh_get_global_edge
2090
2094 function mesh_get_global_facet(this, el, f) result(global_id)
2095 class(mesh_t), intent(in) :: this
2096 integer, intent(in) :: el
2097 integer, intent(in) :: f
2098 integer :: global_id
2099
2100 global_id = this%face_lid(f, el)
2101
2102 if (pe_size .gt. 1) then
2103 global_id = this%ddata%local_to_global_facet(global_id)
2104 end if
2105
2106 end function mesh_get_global_facet
2107
2108
2117 function mesh_have_point_glb_idx(this, index) result(local_id)
2118 class(mesh_t), intent(inout) :: this
2119 integer, intent(inout) :: index
2120 integer :: local_id
2121
2122 if (.not. allocated(this%htp)) then
2123 call neko_error('have_point_glb_idx is only valid before generate_conn')
2124 end if
2125
2126 if (this%htp%get(index, local_id) .eq. 1) then
2127 local_id = -1
2128 end if
2129
2130 end function mesh_have_point_glb_idx
2131
2132
2134 function mesh_is_shared_point(this, el, p) result(shared)
2135 class(mesh_t), intent(inout) :: this
2136 integer, intent(in) :: el
2137 integer, intent(in) :: p
2138 integer :: local_index
2139 logical shared
2140
2141 local_index = this%pt_lid(p, el)
2142 shared = this%ddata%shared_point%element(local_index)
2143
2144 end function mesh_is_shared_point
2145
2146
2148 function mesh_is_shared_edge(this, el, e) result(shared)
2149 class(mesh_t), intent(inout) :: this
2150 integer, intent(in) :: el
2151 integer, intent(in) :: e
2152 integer :: local_index
2153 logical shared
2154 local_index = this%edge_lid(e, el)
2155 if (this%gdim .eq. 2) then
2156 shared = this%ddata%shared_facet%element(local_index)
2157 else
2158 shared = this%ddata%shared_edge%element(local_index)
2159 end if
2160 end function mesh_is_shared_edge
2161
2165 function mesh_is_shared_facet(this, el, f) result(shared)
2166 class(mesh_t), intent(inout) :: this
2167 integer, intent(in) :: el
2168 integer, intent(in) :: f
2169 integer :: local_index
2170 logical shared
2171
2172 local_index = this%face_lid(f, el)
2173 shared = this%ddata%shared_facet%element(local_index)
2174
2175 end function mesh_is_shared_facet
2176
2180 class(mesh_t), intent(inout) :: this
2181 integer :: i
2182 real(kind=rp) :: v(8)
2183 type(point_t) :: centroid
2184 logical :: fail
2185
2186 fail = .false.
2187
2188 if (this%gdim .eq. 3) then
2189 do i = 1, this%nelv
2191 this%elements(i)%e%pts(2)%p%x, &
2192 this%elements(i)%e%pts(3)%p%x, &
2193 this%elements(i)%e%pts(5)%p%x, &
2194 this%elements(i)%e%pts(1)%p%x &
2195 )
2196
2198 this%elements(i)%e%pts(4)%p%x, &
2199 this%elements(i)%e%pts(1)%p%x, &
2200 this%elements(i)%e%pts(6)%p%x, &
2201 this%elements(i)%e%pts(2)%p%x &
2202 )
2203
2205 this%elements(i)%e%pts(1)%p%x, &
2206 this%elements(i)%e%pts(4)%p%x, &
2207 this%elements(i)%e%pts(7)%p%x, &
2208 this%elements(i)%e%pts(3)%p%x &
2209 )
2210
2212 this%elements(i)%e%pts(3)%p%x, &
2213 this%elements(i)%e%pts(2)%p%x, &
2214 this%elements(i)%e%pts(8)%p%x, &
2215 this%elements(i)%e%pts(4)%p%x &
2216 )
2217
2219 this%elements(i)%e%pts(6)%p%x, &
2220 this%elements(i)%e%pts(7)%p%x, &
2221 this%elements(i)%e%pts(1)%p%x, &
2222 this%elements(i)%e%pts(5)%p%x &
2223 )
2224
2226 this%elements(i)%e%pts(8)%p%x, &
2227 this%elements(i)%e%pts(5)%p%x, &
2228 this%elements(i)%e%pts(2)%p%x, &
2229 this%elements(i)%e%pts(6)%p%x &
2230 )
2231
2233 this%elements(i)%e%pts(5)%p%x, &
2234 this%elements(i)%e%pts(8)%p%x, &
2235 this%elements(i)%e%pts(3)%p%x, &
2236 this%elements(i)%e%pts(7)%p%x &
2237 )
2238
2240 this%elements(i)%e%pts(7)%p%x, &
2241 this%elements(i)%e%pts(6)%p%x, &
2242 this%elements(i)%e%pts(4)%p%x, &
2243 this%elements(i)%e%pts(8)%p%x &
2244 )
2245
2246 if (v(1) .le. 0.0_rp .or. &
2247 v(2) .le. 0.0_rp .or. &
2248 v(3) .le. 0.0_rp .or. &
2249 v(4) .le. 0.0_rp .or. &
2250 v(5) .le. 0.0_rp .or. &
2251 v(6) .le. 0.0_rp .or. &
2252 v(7) .le. 0.0_rp .or. &
2253 v(8) .le. 0.0_rp ) then
2254
2255 centroid = this%elements(i)%e%centroid()
2256
2257 write(error_unit, '(A, A, I0, A, 3G12.5)') "*** ERROR ***: ", &
2258 "Wrong orientation of mesh element ", i, &
2259 " with centroid ", centroid%x
2260
2261 fail = .true.
2262 end if
2263 end do
2264 end if
2265
2266 if (fail) then
2267 call neko_error("Some mesh elements are not right-handed")
2268 end if
2269 end subroutine mesh_check_right_handedness
2270
2279 function parallelepiped_signed_volume(p1, p2, p3, origin) result(v)
2280 real(kind=dp), dimension(3), intent(in) :: p1, p2, p3, origin
2281 real(kind=dp) :: v
2282 real(kind=dp) :: vp1(3), vp2(3), vp3(3), cross(3)
2283
2284 vp1 = p1 - origin
2285 vp2 = p2 - origin
2286 vp3 = p3 - origin
2287
2288 cross(1) = vp1(2)*vp2(3) - vp2(3)*vp1(2)
2289 cross(2) = vp1(3)*vp2(1) - vp1(1)*vp2(3)
2290 cross(3) = vp1(1)*vp2(2) - vp1(2)*vp2(1)
2291
2292 v = cross(1)*vp3(1) + cross(2)*vp3(2) + cross(3)*vp3(3)
2293
2294 end function parallelepiped_signed_volume
2295
2305 subroutine mesh_subset_by_mask(this, other, mask, lx, ly, lz)
2306 class(mesh_t), intent(in) :: this
2307 class(mesh_t), intent(inout) :: other
2308 type(mask_t), intent(in) :: mask
2309 integer, intent(in) :: lx, ly, lz
2310 integer :: i, j, k, nelv, lxyz, gdim, e_m, nidx(4), nelv_c, el_c, el, i_m
2311 type(point_t) :: p(8)
2312 integer :: p_id = 1
2313
2314 call other%free()
2315 lxyz = lx * ly * lz
2316
2317 ! Initialize
2318 nelv = mask%size()/lxyz
2319 call other%init(this%gdim, nelv)
2320
2321 ! Assign the elements
2322 if (other%gdim .eq. 2) then
2323 call neko_error("Subset mesh not implemented for 2d")
2324 else if (other%gdim .eq. 3) then
2325 do el = 1, nelv
2326 i_m = 1 + lxyz * (el - 1)
2327 nidx = nonlinear_index(mask%get(i_m), lx, ly, lz)
2328 e_m = nidx(4) ! Actual element from the original mesh
2329 ! Retrieve the points form the other mesh.
2330 ! No need to shift points, since original
2331 ! mesh has done it.
2332 ! Had to use a new point id to avoid issues at
2333 ! periodic boundaries
2334 ! But this means that all points might be incorrectly
2335 ! marked as unique.
2336 do j = 1, 8
2337 call p(j)%init(this%elements(e_m)%e%pts(j)%p%x, p_id)
2338 p_id = p_id + 1
2339 end do
2340
2341 call other%add_element(el, el + other%offset_el, &
2342 p(1), p(2), p(3), p(4), &
2343 p(5), p(6), p(7), p(8))
2344 end do
2345 else
2346 if (pe_rank .eq. 0) call neko_error('Invalid dimension of mesh')
2347 end if
2348
2349 ! Skip searching for boundaries.
2350
2351 ! Update the curvature
2352 nelv_c = this%curve%size
2353 if (nelv_c .gt. 0) then
2354 el_c = 1
2355 el = 1
2356 ! 2 pointer scan
2357 do while (el .le. nelv .and. el_c .le. nelv_c)
2358
2359 i_m = 1 + lxyz * (el - 1)
2360 nidx = nonlinear_index(mask%get(i_m), lx, ly, lz)
2361 e_m = nidx(4)
2362
2363 if (e_m .lt. this%curve%curve_el(el_c)%el_idx) then
2364 el = el + 1
2365
2366 else if (e_m .gt. this%curve%curve_el(el_c)%el_idx) then
2367 el_c = el_c + 1
2368
2369 else
2370 call other%mark_curve_element(el, &
2371 this%curve%curve_el(el_c)%curve_data, &
2372 this%curve%curve_el(el_c)%curve_type)
2373 el = el + 1
2374 el_c = el_c + 1
2375 end if
2376
2377 end do
2378 end if
2379
2380 ! Finalize
2381 call other%finalize()
2382
2383 other%is_submesh = .true.
2384
2385 end subroutine mesh_subset_by_mask
2386
2387end module mesh
__inline__ __device__ void nonlinear_index(const int idx, const int lx, int *index)
Definition bc_utils.h:44
Generic buffer that is extended with buffers of varying rank.
Definition buffer.F90:34
Definition comm.F90:1
integer, public pe_size
MPI size of communicator.
Definition comm.F90:62
integer, public pe_rank
MPI rank.
Definition comm.F90:59
type(mpi_comm), public neko_comm
MPI communicator.
Definition comm.F90:46
Crystal router: scalable all-to-some personalized exchange.
subroutine, public crystal_router_transfer(buf, n)
Route packed records to their destination ranks.
subroutine, public crystal_router_pack(out, dest, body)
Append one record to a packed crystal-router buffer.
Defines a domain as a subset of facets in a mesh.
Definition curve.f90:2
Defines practical data distributions.
Definition datadist.f90:34
Distributed mesh data.
Definition distdata.f90:34
Defines a zone as a subset of facets in a mesh.
Defines a hexahedron element.
Definition hex.f90:34
integer, parameter, public neko_hex_npts
Number of points.
Definition hex.f90:42
integer, parameter, public neko_hex_nfcs
Number of faces.
Definition hex.f90:43
integer, parameter, public neko_hex_neds
Number of edges.
Definition hex.f90:44
Implements a hash table ADT.
Definition htable.f90:52
Logging routines.
Definition log.f90:34
integer, parameter, public log_size
Definition log.f90:46
Object for handling masks in Neko.
Definition mask.f90:34
Definition math.F90:60
Defines a mesh.
Definition mesh.f90:34
subroutine mesh_generate_flags(this)
Definition mesh.f90:467
subroutine mesh_add_edge(this, e, head, chain, idx)
Add a unique edge represented as a 2-tuple to the mesh.
Definition mesh.f90:1786
subroutine mesh_generate_facet_numbering(this)
Generate a unique facet numbering.
Definition mesh.f90:1407
subroutine mesh_init_dist(this, gdim, dist)
Initialise a mesh this based on a distribution dist.
Definition mesh.f90:243
integer, parameter, public neko_msh_max_zlbls
Max num. zone labels.
Definition mesh.f90:65
subroutine mesh_generate_edge_conn(this)
Generate element-element connectivity via edges both between internal and between PEs.
Definition mesh.f90:1136
subroutine mesh_add_point(this, p, idx)
Add a unique point to the mesh.
Definition mesh.f90:1721
subroutine mesh_free(this)
Deallocate a mesh this.
Definition mesh.f90:353
subroutine mesh_mark_labeled_facet(this, f, e, label)
Mark facet f in element e with label.
Definition mesh.f90:1834
subroutine mesh_add_quad(this, el, el_glb, p1, p2, p3, p4)
Add a quadrilateral element to the mesh this.
Definition mesh.f90:1649
real(kind=dp) function, public parallelepiped_signed_volume(p1, p2, p3, origin)
Compute a signed volume of a parallelepiped formed by three vectors, in turn defined via three points...
Definition mesh.f90:2280
integer, parameter, public neko_msh_max_zlbl_len
Max length of a zone label.
Definition mesh.f90:67
subroutine mesh_mark_periodic_facet(this, f, e, pf, pe, pids)
Mark facet f in element e as periodic with (pf, pe)
Definition mesh.f90:1854
subroutine mesh_all_deformed(this)
Set all elements as if they are deformed.
Definition mesh.f90:500
subroutine mesh_generate_edge_lid(this)
Enumerate the unique edges of the local mesh.
Definition mesh.f90:738
subroutine mesh_init_common(this)
Definition mesh.f90:269
subroutine mesh_get_facet_ids(this, f, e, pids)
Get original ids of periodic points.
Definition mesh.f90:1868
subroutine mesh_create_periodic_ids(this, f, e, pf, pe)
Creates common ids for matching periodic points.
Definition mesh.f90:1944
subroutine mesh_reset_periodic_ids(this)
Reset ids of periodic points to their original ids.
Definition mesh.f90:1891
subroutine mesh_add_face(this, f, head, chain, idx)
Add a unique face represented as a 4-tuple to the mesh.
Definition mesh.f90:1748
subroutine mesh_init_nelv(this, gdim, nelv)
Initialise a mesh this with nelv elements.
Definition mesh.f90:207
integer function mesh_get_global_edge(this, el, e)
Return the global id of edge e in element el.
Definition mesh.f90:2072
integer function mesh_have_point_glb_idx(this, index)
Check if the mesh has a point given its global index.
Definition mesh.f90:2118
subroutine mesh_generate_external_point_conn(this)
Generate element-element connectivity via points between PEs.
Definition mesh.f90:1000
logical function mesh_is_shared_facet(this, el, f)
Check if facet f in element el is shared.
Definition mesh.f90:2166
subroutine mesh_apply_periodic_facet(this, f, e, pf, pe, pids)
Replaces the periodic point's id with a common id for matching periodic points.
Definition mesh.f90:2036
subroutine mesh_generate_face_lid(this)
Enumerate the unique faces of the local mesh.
Definition mesh.f90:790
subroutine mesh_finalize(this)
Definition mesh.f90:449
subroutine mesh_generate_external_facet_conn(this)
Generate element-element connectivity via facets between PEs.
Definition mesh.f90:823
subroutine mesh_mark_curve_element(this, e, curve_data, curve_type)
Mark element e as a curve element.
Definition mesh.f90:1817
integer function mesh_get_global_facet(this, el, f)
Return the global id of facet f in element el.
Definition mesh.f90:2095
subroutine mesh_check_right_handedness(this)
Check the correct orientation of the rst coordindates.
Definition mesh.f90:2180
subroutine mesh_generate_conn(this)
Generate element-to-element connectivity.
Definition mesh.f90:506
logical function mesh_is_shared_edge(this, el, e)
Check if edge e in element el is shared.
Definition mesh.f90:2149
subroutine mesh_add_hex(this, el, el_glb, p1, p2, p3, p4, p5, p6, p7, p8)
Add a hexahedral element to the mesh this.
Definition mesh.f90:1681
subroutine mesh_subset_by_mask(this, other, mask, lx, ly, lz)
Create a subset of the mesh this in other based on the provided mask.
Definition mesh.f90:2306
logical function mesh_is_shared_point(this, el, p)
Check if point p in element el is shared.
Definition mesh.f90:2135
integer, parameter, public i8
Definition num_types.f90:7
integer, parameter, public dp
Definition num_types.f90:10
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:14
Implements a point.
Definition point.f90:35
Defines a quadrilateral element.
Definition quad.f90:34
integer, parameter, public neko_quad_neds
Number of edges.
Definition quad.f90:43
integer, parameter, public neko_quad_npts
Number of points.
Definition quad.f90:42
Implements a dynamic stack ADT.
Definition stack.f90:49
Implements a n-tuple.
Definition tuple.f90:41
Implements an unordered set ADT.
Definition uset.f90:39
Utilities.
Definition utils.f90:35
subroutine, public neko_warning(warning_msg)
Reports a warning to standard output.
Definition utils.f90:454
Load-balanced linear distribution .
Definition datadist.f90:50
Base type for an element.
Definition element.f90:44
Hexahedron element.
Definition hex.f90:63
Integer based hash table.
Definition htable.f90:102
Integer 2-tuple based hash table.
Definition htable.f90:142
Integer 4-tuple based hash table.
Definition htable.f90:152
Integer*8 based hash table.
Definition htable.f90:112
Base type for a hash table.
Definition htable.f90:73
Type for consistently handling masks in Neko. This type encapsulates the mask array and its associate...
Definition mask.f90:51
A point in with coordinates .
Definition point.f90:43
Quadrilateral element.
Definition quad.f90:58
Integer based stack.
Definition stack.f90:77
Integer*8 based stack.
Definition stack.f90:84
Integer based 4-tuple.
Definition tuple.f90:76
Integer based 2-tuple.
Definition tuple.f90:58
Integer*8 based unordered set.
Definition uset.f90:78
#define max(a, b)
Definition tensor.cu:40