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 call mpi_issend(buffer%array(), buffer%size(), mpi_integer, &
892 dst, 0, neko_comm, send_req, ierr)
893 end if
894
895 if (this%neigh(src)) then
896 call mpi_wait(recv_req, status, ierr)
897 call mpi_get_count(status, mpi_integer, n_recv, ierr)
898
899 select type (fmp => this%facet_map)
900 type is(htable_i4t2_t)
901 do j = 1, n_recv, n_nodes + 2
902 neigh_el = recv_buffer(j)
903 recv_side = recv_buffer(j+1)
904
905 edge = (/ recv_buffer(j+2), recv_buffer(j+3) /)
906
907 facet_data = (/ 0, 0 /)
908 !Check if the face is present on this PE
909 if (fmp%get(edge, facet_data) .eq. 0) then
910 element = facet_data%x(1) - this%offset_el
911 !Check which side is connected
912 do l = 1, n_sides
913 call this%elements(element)%e%facet_id(edge2, l)
914 if(edge2 .eq. edge) then
915 facet = l
916 exit
917 end if
918 end do
919 this%facet_neigh(facet, element) = -neigh_el
920 facet_data%x(2) = -neigh_el
921
922 ! Update facet map
923 call fmp%set(edge, facet_data)
924
925 call this%ddata%set_shared_el_facet(element, facet)
926
927 call this%ddata%set_shared_facet( &
928 this%edge_lid(facet, element))
929
930 end if
931
932 end do
933 type is(htable_i4t4_t)
934 do j = 1, n_recv, n_nodes + 2
935 neigh_el = recv_buffer(j)
936 recv_side = recv_buffer(j+1)
937
938 face%x = (/ recv_buffer(j+2), recv_buffer(j+3), &
939 recv_buffer(j+4), recv_buffer(j+5) /)
940
941
942 facet_data%x = (/ 0, 0 /)
943
944 !Check if the face is present on this PE
945 if (fmp%get(face, facet_data) .eq. 0) then
946 ! Determine opposite side and update neighbor
947 element = facet_data%x(1) - this%offset_el
948 do l = 1, 6
949 call this%elements(element)%e%facet_id(face2, l)
950 if(face2 .eq. face) then
951 facet = l
952 exit
953 end if
954 end do
955 this%facet_neigh(facet, element) = -neigh_el
956 facet_data%x(2) = -neigh_el
957
958 ! Update facet map
959 call fmp%set(face, facet_data)
960
961 call this%ddata%set_shared_el_facet(element, facet)
962
963 call this%ddata%set_shared_facet( &
964 this%face_lid(facet, element))
965
966 end if
967
968 end do
969 end select
970 end if
971
972 if (this%neigh(dst)) then
973 call mpi_wait(send_req, mpi_status_ignore, ierr)
974 end if
975
976 end do
977
978
979 deallocate(recv_buffer)
980
981 call buffer%free()
982
984
994 type(mesh_t), intent(inout) :: this
995 type(stack_i8_t) :: cr_buf
996 integer(i8), allocatable :: buf(:), body(:)
997 integer(i8), pointer :: cr_data(:)
998 integer, allocatable :: gkey(:), gperm(:), rpos(:)
999 integer, contiguous, pointer :: neighs(:)
1000 integer :: i, j, k, n, p, owner, num_neigh, nrec, rlen
1001 integer :: pt_glb_idx, pt_loc_idx, src_rank, neigh_el, rk, rp
1002
1003 !
1004 ! Phase 1: route every local point's element list to its canonical owner.
1005 ! record payload = [glb_idx, origin, elems...]
1006 !
1007 call cr_buf%init(this%mpts * 4)
1008 allocate(body(8))
1009 do i = 1, this%mpts
1010 pt_glb_idx = this%points(i)%id() ! Adhere to standards...
1011 num_neigh = this%point_neigh(i)%size()
1012 if (2 + num_neigh .gt. size(body)) then
1013 deallocate(body)
1014 allocate(body(2 + num_neigh))
1015 end if
1016 body(1) = int(pt_glb_idx, i8) ! glb_idx
1017 body(2) = int(pe_rank, i8) ! origin
1018 neighs => this%point_neigh(i)%array()
1019 do j = 1, num_neigh
1020 body(2 + j) = int(neighs(j), i8) ! element ids
1021 end do
1022 owner = modulo(pt_glb_idx, pe_size)
1023 call crystal_router_pack(cr_buf, owner, body(1:2 + num_neigh))
1024 end do
1025 deallocate(body)
1026
1027 n = cr_buf%size()
1028 allocate(buf(max(n, 1)))
1029 if (n .gt. 0) then
1030 cr_data => cr_buf%array()
1031 buf(1:n) = cr_data(1:n)
1032 end if
1033 call cr_buf%free()
1034
1035 call crystal_router_transfer(buf, n)
1036
1037 !
1038 ! Phase 2: at the owner, group received records by glb_idx and reflect,
1039 ! to each holder, the element lists of every *other* holder.
1040 ! reply = [dest=holder, len=2+num_neigh, glb_idx, src_rank, elems...]
1041 !
1042 ! Index the received records and sort their keys (glb_idx) so equal keys
1043 ! form contiguous runs; per-point holder counts are small, so the
1044 ! all-pairs reflection within a run is cheap.
1045 nrec = 0
1046 p = 1
1047 do while (p .le. n)
1048 nrec = nrec + 1
1049 p = p + 2 + int(buf(p + 1))
1050 end do
1051
1052 allocate(gkey(max(nrec, 1)), gperm(max(nrec, 1)), rpos(max(nrec, 1)))
1053 nrec = 0
1054 p = 1
1055 do while (p .le. n)
1056 nrec = nrec + 1
1057 rpos(nrec) = p ! record start offset in buf
1058 gkey(nrec) = int(buf(p + 2)) ! glb_idx
1059 p = p + 2 + int(buf(p + 1))
1060 end do
1061 if (nrec .gt. 0) call sort(gkey, gperm, nrec)
1062
1063 call cr_buf%init(max(n, 1))
1064 i = 1
1065 do while (i .le. nrec)
1066 ! [i, j) is the run of records sharing the same glb_idx
1067 j = i
1068 do while (j .le. nrec)
1069 if (gkey(j) .ne. gkey(i)) exit
1070 j = j + 1
1071 end do
1072 ! All-pairs reflection within the run (skip singletons = unshared):
1073 ! send source holder p's record body (glb_idx, origin, elems) to
1074 ! recipient holder k, addressed to k's origin rank.
1075 if (j - i .gt. 1) then
1076 do k = i, j - 1 ! recipient holder
1077 rk = rpos(gperm(k))
1078 do p = i, j - 1 ! source holder
1079 if (p .eq. k) cycle
1080 rp = rpos(gperm(p))
1081 call crystal_router_pack(cr_buf, int(buf(rk + 3)), &
1082 buf(rp + 2 : rp + 1 + int(buf(rp + 1))))
1083 end do
1084 end do
1085 end if
1086 i = j
1087 end do
1088 deallocate(gkey, gperm, rpos)
1089
1090 n = cr_buf%size()
1091 if (allocated(buf)) deallocate(buf)
1092 allocate(buf(max(n, 1)))
1093 if (n .gt. 0) then
1094 cr_data => cr_buf%array()
1095 buf(1:n) = cr_data(1:n)
1096 end if
1097 call cr_buf%free()
1098
1099 call crystal_router_transfer(buf, n)
1100
1101 !
1102 ! Phase 3: finalise locally. Each reply names a remote holder of one of our
1103 ! points; mark it as a neighbour and absorb its (remote) element list.
1104 !
1105 p = 1
1106 do while (p .le. n)
1107 rlen = int(buf(p + 1))
1108 pt_glb_idx = int(buf(p + 2))
1109 src_rank = int(buf(p + 3))
1110 pt_loc_idx = this%have_point_glb_idx(pt_glb_idx)
1111 if (pt_loc_idx .gt. 0) then
1112 this%neigh(src_rank) = .true.
1113 call this%ddata%set_shared_point(pt_loc_idx)
1114 do k = 1, rlen - 2
1115 neigh_el = -int(buf(p + 3 + k))
1116 call this%point_neigh(pt_loc_idx)%push(neigh_el)
1117 end do
1118 end if
1119 p = p + 2 + rlen
1120 end do
1121
1122 if (allocated(buf)) deallocate(buf)
1123
1125
1130 type(mesh_t), target, intent(inout) :: this
1131 integer, allocatable :: edge_lp(:,:)
1132 logical, allocatable :: shared_edges(:)
1133 type(uset_i8_t), target :: edge_idx, ghost, owner
1134 type(stack_i8_t), target :: send_buff
1135 type(htable_i8_t) :: glb_to_loc
1136 type(mpi_status) :: status
1137 type(mpi_request) :: send_req, recv_req
1138 integer, contiguous, pointer :: p1(:), p2(:), ns_id(:)
1139 integer :: i, j, id, lid, ierr, num_edge_glb, edge_offset, num_edge_loc
1140 integer :: k, l , shared_offset, glb_nshared, n_glb_id
1141 integer(kind=i8) :: C, glb_max, glb_id
1142 integer(kind=i8), pointer :: glb_ptr
1143 integer(kind=i8), allocatable :: recv_buff(:)
1144 type(stack_i4_t), target :: non_shared_edges
1145 integer :: max_recv, src, dst, n_recv
1146
1147
1149 allocate(this%ddata%local_to_global_edge(this%meds))
1150
1151 call edge_idx%init(this%meds)
1152 call send_buff%init(this%meds)
1153 call owner%init(this%meds)
1154
1155 call glb_to_loc%init(32, i)
1156
1157 !
1158 ! Determine/ constants used to generate unique global edge numbers
1159 ! for shared edges
1160 !
1161 c = int(this%glb_nelv, i8) * int(neko_hex_neds, i8)
1162
1163 num_edge_glb = 2* this%meds
1164 call mpi_allreduce(mpi_in_place, num_edge_glb, 1, &
1165 mpi_integer, mpi_sum, neko_comm, ierr)
1166
1167 glb_max = int(num_edge_glb, i8)
1168
1169 call non_shared_edges%init(this%meds)
1170
1171 ! Resolve both endpoints of every edge to a local point id up front, so
1172 ! that the neighbour search below reads nothing but plain arrays
1173 allocate(edge_lp(2, this%meds))
1174 do lid = 1, this%meds
1175 id = this%edge_pts(1, lid)
1176 edge_lp(1, lid) = this%have_point_glb_idx(id)
1177 id = this%edge_pts(2, lid)
1178 edge_lp(2, lid) = this%have_point_glb_idx(id)
1179 end do
1180
1181 !
1182 ! An edge is shared when both of its endpoints see the same remote
1183 ! element. Every iteration writes only its own flag, so this search,
1184 ! the expensive part of the numbering, is the part that threads
1185 !
1186 allocate(shared_edges(this%meds))
1187 !$omp parallel do private(lid, k, l, p1, p2, i, j)
1188 do lid = 1, this%meds
1189 k = edge_lp(1, lid)
1190 l = edge_lp(2, lid)
1191 p1 => this%point_neigh(k)%array()
1192 p2 => this%point_neigh(l)%array()
1193
1194 shared_edges(lid) = .false.
1195
1196 ! Find edge neighbor from point neighbors
1197 do i = 1, this%point_neigh(k)%size()
1198 do j = 1, this%point_neigh(l)%size()
1199 if ((p1(i) .eq. p2(j)) .and. &
1200 (p1(i) .lt. 0) .and. (p2(j) .lt. 0)) then
1201 shared_edges(lid) = .true.
1202 end if
1203 end do
1204 end do
1205 end do
1206 !$omp end parallel do
1207 deallocate(edge_lp)
1208
1209 ! The bookkeeping stays ordered; the order the ids are pushed in is what
1210 ! the global numbering below is built from
1211 do lid = 1, this%meds
1212 id = lid
1213
1214 ! Generate a unique id for the shared edge as,
1215 ! ((e1 * C) + e2 )) + glb_max if e1 > e2
1216 ! ((e2 * C) + e1 )) + glb_max if e2 > e1
1217 if (shared_edges(id)) then
1218 call this%ddata%set_shared_edge(id)
1219 glb_id = ((int(this%edge_pts(1, id), i8)) + &
1220 int(this%edge_pts(2, id), i8)*c) + glb_max
1221 call glb_to_loc%set(glb_id, id)
1222 call edge_idx%add(glb_id)
1223 call owner%add(glb_id) ! Always assume the PE is the owner
1224 call send_buff%push(glb_id)
1225 else
1226 call non_shared_edges%push(id)
1227 end if
1228 end do
1229 deallocate(shared_edges)
1230
1231 ! Determine start offset for global numbering of locally owned edges
1232 edge_offset = 0
1233 num_edge_loc = non_shared_edges%size()
1234 call mpi_exscan(num_edge_loc, edge_offset, 1, &
1235 mpi_integer, mpi_sum, neko_comm, ierr)
1236 edge_offset = edge_offset + 1
1237
1238 ! Construct global numbering of locally owned edges
1239 ns_id => non_shared_edges%array()
1240 do i = 1, non_shared_edges%size()
1241 call this%ddata%set_local_to_global_edge(ns_id(i), edge_offset)
1242 edge_offset = edge_offset + 1
1243 end do
1244 nullify(ns_id)
1245
1246 !
1247 ! Renumber shared edges into integer range
1248 !
1249
1250 call mpi_allreduce(send_buff%size(), max_recv, 1, &
1251 mpi_integer, mpi_max, neko_comm, ierr)
1252
1253 call ghost%init(send_buff%size())
1254
1255 allocate(recv_buff(max_recv))
1256
1257 do i = 1, size(this%neigh_order)
1258 src = modulo(pe_rank - this%neigh_order(i) + pe_size, pe_size)
1259 dst = modulo(pe_rank + this%neigh_order(i), pe_size)
1260
1261 if (this%neigh(src)) then
1262 call mpi_irecv(recv_buff, max_recv, mpi_integer8, &
1263 src, 0, neko_comm, recv_req, ierr)
1264 end if
1265
1266 if (this%neigh(dst)) then
1267 ! We should use the %array() procedure, which works great for
1268 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1269 ! certain data types
1270 select type(sbarray=>send_buff%data)
1271 type is (integer(i8))
1272 ! Synchronous send to avoid eager-buffering the key list as an
1273 ! unexpected message (FJMPI buffer-pool exhaustion / SIGBUS under
1274 ! flat MPI); matching recv is pre-posted by the peer.
1275 call mpi_issend(sbarray, send_buff%size(), mpi_integer8, &
1276 dst, 0, neko_comm, send_req, ierr)
1277 end select
1278 end if
1279
1280 if (this%neigh(src)) then
1281 call mpi_wait(recv_req, status, ierr)
1282 call mpi_get_count(status, mpi_integer8, n_recv, ierr)
1283
1284 do j = 1, n_recv
1285 if ((edge_idx%element(recv_buff(j))) .and. (src .lt. pe_rank)) then
1286 call ghost%add(recv_buff(j))
1287 call owner%remove(recv_buff(j))
1288 end if
1289 end do
1290 end if
1291
1292 if (this%neigh(dst)) then
1293 call mpi_wait(send_req, mpi_status_ignore, ierr)
1294 end if
1295 end do
1296
1297
1298 ! Determine start offset for global numbering of shared edges
1299 glb_nshared = num_edge_loc
1300 call mpi_allreduce(mpi_in_place, glb_nshared, 1, &
1301 mpi_integer, mpi_sum, neko_comm, ierr)
1302
1303 shared_offset = 0
1304 call mpi_exscan(owner%size(), shared_offset, 1, &
1305 mpi_integer, mpi_sum, neko_comm, ierr)
1306 shared_offset = shared_offset + glb_nshared + 1
1307
1308 ! Renumber locally owned set of shared edges
1309 call send_buff%clear()
1310 call owner%iter_init()
1311 do while (owner%iter_next())
1312 glb_ptr => owner%iter_value()
1313 if (glb_to_loc%get(glb_ptr, id) .eq. 0) then
1314 call this%ddata%set_local_to_global_edge(id, shared_offset)
1315
1316 ! Add new number to send data as [old_glb_id new_glb_id] for each edge
1317 call send_buff%push(glb_ptr) ! Old glb_id integer*8
1318 glb_id = int(shared_offset, i8) ! Waste some space here...
1319 call send_buff%push(glb_id) ! New glb_id integer*4
1320
1321 shared_offset = shared_offset + 1
1322 else
1323 call neko_error('Invalid edge id')
1324 end if
1325 end do
1326 nullify(glb_ptr)
1327
1328 ! Determine total number of unique edges in the mesh
1329 ! (This can probably be done in a clever way...)
1330 this%glb_meds = shared_offset -1
1331 call mpi_allreduce(mpi_in_place, this%glb_meds, 1, &
1332 mpi_integer, mpi_max, neko_comm, ierr)
1333
1334 !
1335 ! Update ghosted edges with new global id
1336 !
1337
1338 call mpi_allreduce(send_buff%size(), max_recv, 1, &
1339 mpi_integer, mpi_max, neko_comm, ierr)
1340
1341 deallocate(recv_buff)
1342 allocate(recv_buff(max_recv))
1343
1344
1345 do i = 1, size(this%neigh_order)
1346 src = modulo(pe_rank - this%neigh_order(i) + pe_size, pe_size)
1347 dst = modulo(pe_rank + this%neigh_order(i), pe_size)
1348
1349 if (this%neigh(src)) then
1350 call mpi_irecv(recv_buff, max_recv, mpi_integer8, &
1351 src, 0, neko_comm, recv_req, ierr)
1352 end if
1353
1354 if (this%neigh(dst)) then
1355 ! We should use the %array() procedure, which works great for
1356 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1357 ! certain data types
1358 select type(sbarray=>send_buff%data)
1359 type is (integer(i8))
1360 ! Synchronous send to avoid eager-buffering the key list as an
1361 ! unexpected message (FJMPI buffer-pool exhaustion / SIGBUS under
1362 ! flat MPI); matching recv is pre-posted by the peer.
1363 call mpi_issend(sbarray, send_buff%size(), mpi_integer8, &
1364 dst, 0, neko_comm, send_req, ierr)
1365 end select
1366 end if
1367
1368 if (this%neigh(src)) then
1369 call mpi_wait(recv_req, status, ierr)
1370 call mpi_get_count(status, mpi_integer8, n_recv, ierr)
1371
1372 do j = 1, n_recv, 2
1373 if (ghost%element(recv_buff(j))) then
1374 if (glb_to_loc%get(recv_buff(j), id) .eq. 0) then
1375 n_glb_id = int(recv_buff(j + 1 ), 4)
1376 call this%ddata%set_local_to_global_edge(id, n_glb_id)
1377 else
1378 call neko_error('Invalid edge id')
1379 end if
1380 end if
1381 end do
1382 end if
1383
1384 if (this%neigh(dst)) then
1385 call mpi_wait(send_req, mpi_status_ignore, ierr)
1386 end if
1387 end do
1388
1389 deallocate(recv_buff)
1390 call glb_to_loc%free()
1391 call send_buff%free()
1392 call edge_idx%free()
1393 call non_shared_edges%free()
1394 call ghost%free()
1395 call owner%free()
1396
1397 end subroutine mesh_generate_edge_conn
1398
1401 type(mesh_t), target, intent(inout) :: this
1402 integer, contiguous, pointer :: fd(:), ed(:)
1403 type(tuple4_i4_t) :: face
1404 type(tuple_i4_t) :: edge
1405 type(tuple_i4_t) :: facet_data
1406 type(tuple4_i4_t) :: recv_face
1407 type(tuple_i4_t) :: recv_edge
1408 type(stack_i4_t) :: face_owner
1409 type(htable_i4t4_t) :: face_ghost
1410 type(stack_i4_t) :: edge_owner
1411 type(htable_i4t2_t) :: edge_ghost
1412 type(stack_i4_t) :: send_buff
1413 type(mpi_status) :: status
1414 type(mpi_request) :: send_req, recv_req
1415 integer, allocatable :: recv_buff(:)
1416 integer :: non_shared_facets, shared_facets, facet_offset
1417 integer :: id, lid, glb_nshared, shared_offset, owned_facets
1418 integer :: i, j, ierr, max_recv, src, dst, n_recv
1419
1420 shared_facets = this%ddata%shared_facet%size()
1421
1423 if (this%gdim .eq. 2) then
1424 allocate(this%ddata%local_to_global_facet(this%meds))
1425 call edge_owner%init(this%meds)
1426 call edge_ghost%init(64, i)
1427 non_shared_facets = this%meds - shared_facets
1428 else
1429 allocate(this%ddata%local_to_global_facet(this%mfcs))
1430 call face_owner%init(this%mfcs)
1431 call face_ghost%init(64, i)
1432 non_shared_facets = this%mfcs - shared_facets
1433 end if
1434
1436
1437 facet_offset = 0
1438 call mpi_exscan(non_shared_facets, facet_offset, 1, &
1439 mpi_integer, mpi_sum, neko_comm, ierr)
1440 facet_offset = facet_offset + 1
1441
1442 ! Determine ownership of shared facets
1443 if (this%gdim .eq. 2) then
1444 do lid = 1, this%meds
1445 id = lid
1446 if (.not. this%ddata%shared_facet%element(id)) then
1447 call this%ddata%set_local_to_global_facet(id, facet_offset)
1448 facet_offset = facet_offset + 1
1449 else
1450 edge%x = this%edge_pts(:, id)
1451 select type(fmp => this%facet_map)
1452 type is(htable_i4t2_t)
1453 if (fmp%get(edge, facet_data) .eq. 0) then
1454 if (facet_data%x(2) .lt. 0) then
1455 if (abs(facet_data%x(2)) .lt. (this%offset_el + 1)) then
1456 call edge_ghost%set(edge, id)
1457 else
1458 call edge_owner%push(id)
1459 end if
1460 else
1461 call neko_error("Invalid edge neigh.")
1462 end if
1463 end if
1464 end select
1465 end if
1466 end do
1467 owned_facets = edge_owner%size()
1468 else
1469 do lid = 1, this%mfcs
1470 id = lid
1471 if (.not. this%ddata%shared_facet%element(id)) then
1472 call this%ddata%set_local_to_global_facet(id, facet_offset)
1473 facet_offset = facet_offset + 1
1474 else
1475 face%x = this%face_pts(:, id)
1476 select type(fmp => this%facet_map)
1477 type is(htable_i4t4_t)
1478 if (fmp%get(face, facet_data) .eq. 0) then
1479 if (facet_data%x(2) .lt. 0) then
1480 if (abs(facet_data%x(2)) .lt. (this%offset_el + 1)) then
1481 call face_ghost%set(face, id)
1482 else
1483 call face_owner%push(id)
1484 end if
1485 else
1486 call neko_error("Invalid face neigh.")
1487 end if
1488 end if
1489 end select
1490 end if
1491 end do
1492 owned_facets = face_owner%size()
1493 end if
1494
1495 ! Determine start offset for global numbering of shared facets
1496 glb_nshared = non_shared_facets
1497 call mpi_allreduce(mpi_in_place, glb_nshared, 1, &
1498 mpi_integer, mpi_sum, neko_comm, ierr)
1499
1500 shared_offset = 0
1501 call mpi_exscan(owned_facets, shared_offset, 1, &
1502 mpi_integer, mpi_sum, neko_comm, ierr)
1503 shared_offset = shared_offset + glb_nshared + 1
1504
1505 if (this%gdim .eq. 2) then
1506
1507 if (owned_facets .gt. 32) then
1508 call send_buff%init(owned_facets)
1509 else
1510 call send_buff%init()
1511 end if
1512
1513 ed => edge_owner%array()
1514 do i = 1, edge_owner%size()
1515 id = ed(i)
1516 call this%ddata%set_local_to_global_facet(id, shared_offset)
1517
1518 ! Add new number to send buffer
1519 ! [edge id1 ... edge idn new_glb_id]
1520 do j = 1, 2
1521 call send_buff%push(this%edge_pts(j, id))
1522 end do
1523 call send_buff%push(shared_offset)
1524
1525 shared_offset = shared_offset + 1
1526 end do
1527 nullify(ed)
1528
1529 else
1530
1531 if (owned_facets .gt. 32) then
1532 call send_buff%init(owned_facets)
1533 else
1534 call send_buff%init()
1535 end if
1536
1537 fd => face_owner%array()
1538 do i = 1, face_owner%size()
1539 id = fd(i)
1540 call this%ddata%set_local_to_global_facet(id, shared_offset)
1541
1542 ! Add new number to send buffer
1543 ! [face id1 ... face idn new_glb_id]
1544 do j = 1, 4
1545 call send_buff%push(this%face_pts(j, id))
1546 end do
1547 call send_buff%push(shared_offset)
1548
1549 shared_offset = shared_offset + 1
1550 end do
1551 nullify(fd)
1552
1553 end if
1554
1555 ! Determine total number of unique facets in the mesh
1556 ! (This can probably be done in a clever way...)
1557 this%glb_mfcs = shared_offset - 1
1558 call mpi_allreduce(mpi_in_place, this%glb_mfcs, 1, &
1559 mpi_integer, mpi_max, neko_comm, ierr)
1560
1561 !
1562 ! Update ghosted facets with new global id
1563 !
1564
1565 call mpi_allreduce(send_buff%size(), max_recv, 1, &
1566 mpi_integer, mpi_max, neko_comm, ierr)
1567
1568 allocate(recv_buff(max_recv))
1569
1571 do i = 1, size(this%neigh_order)
1572 src = modulo(pe_rank - this%neigh_order(i) + pe_size, pe_size)
1573 dst = modulo(pe_rank + this%neigh_order(i), pe_size)
1574
1575 if (this%neigh(src)) then
1576 call mpi_irecv(recv_buff, max_recv, mpi_integer, &
1577 src, 0, neko_comm, recv_req, ierr)
1578 end if
1579
1580 if (this%neigh(dst)) then
1581 ! Synchronous send to avoid eager-buffered unexpected messages
1582 ! (FJMPI buffer-pool exhaustion / SIGBUS under flat MPI); the
1583 ! matching recv is pre-posted at the top of the peer's iteration.
1584 call mpi_issend(send_buff%array(), send_buff%size(), mpi_integer, &
1585 dst, 0, neko_comm, send_req, ierr)
1586 end if
1587
1588 if (this%neigh(src)) then
1589 call mpi_wait(recv_req, status, ierr)
1590 call mpi_get_count(status, mpi_integer, n_recv, ierr)
1591
1592 if (this%gdim .eq. 2) then
1593 do j = 1, n_recv, 3
1594
1595 recv_edge = (/recv_buff(j), recv_buff(j+1)/)
1596
1597 ! Check if the PE has the shared edge
1598 if (edge_ghost%get(recv_edge, id) .eq. 0) then
1599 call this%ddata%set_local_to_global_facet(id, recv_buff(j+2))
1600 end if
1601 end do
1602 else
1603 do j = 1, n_recv, 5
1604
1605 recv_face = (/recv_buff(j), recv_buff(j+1), &
1606 recv_buff(j+2), recv_buff(j+3) /)
1607
1608 ! Check if the PE has the shared face
1609 if (face_ghost%get(recv_face, id) .eq. 0) then
1610 call this%ddata%set_local_to_global_facet(id, recv_buff(j+4))
1611 end if
1612 end do
1613 end if
1614 end if
1615
1616 if (this%neigh(dst)) then
1617 call mpi_wait(send_req, mpi_status_ignore, ierr)
1618 end if
1619
1620 end do
1621
1622 if (this%gdim .eq. 2) then
1623 call edge_owner%free()
1624 call edge_ghost%free()
1625 else
1626 call face_owner%free()
1627 call face_ghost%free()
1628 end if
1629
1630 call send_buff%free()
1631 deallocate(recv_buff)
1632
1633 end subroutine mesh_generate_facet_numbering
1634
1635
1637 subroutine mesh_add_quad(this, el, el_glb, p1, p2, p3, p4)
1638 class(mesh_t), target, intent(inout) :: this
1639 integer, value :: el, el_glb
1640 type(point_t), target, intent(inout) :: p1, p2, p3, p4
1641 integer :: p(4)
1642 type(tuple_i4_t) :: e
1643
1644 ! Connectivity invalidated if a new element is added
1645 this%lconn = .false.
1646
1647 ! Numbering invalidated if a new element is added
1648 this%lnumr = .false.
1649
1650 call this%add_point(p1, p(1))
1651 call this%add_point(p2, p(2))
1652 call this%add_point(p3, p(3))
1653 call this%add_point(p4, p(4))
1654
1655 select type (ep => this%elements(el)%e)
1656 type is (quad_t)
1657 call ep%init(el_glb, &
1658 this%points(p(1)), this%points(p(2)), &
1659 this%points(p(3)), this%points(p(4)))
1660
1661
1662 class default
1663 call neko_error('Invalid element type')
1664 end select
1665
1666 end subroutine mesh_add_quad
1667
1669 subroutine mesh_add_hex(this, el, el_glb, p1, p2, p3, p4, p5, p6, p7, p8)
1670 class(mesh_t), target, intent(inout) :: this
1671 integer, value :: el, el_glb
1672 type(point_t), target, intent(inout) :: p1, p2, p3, p4, p5, p6, p7, p8
1673 integer :: p(8)
1674 type(tuple4_i4_t) :: f
1675 type(tuple_i4_t) :: e
1676
1677 ! Connectivity invalidated if a new element is added
1678 this%lconn = .false.
1679
1680 ! Numbering invalidated if a new element is added
1681 this%lnumr = .false.
1682
1683 call this%add_point(p1, p(1))
1684 call this%add_point(p2, p(2))
1685 call this%add_point(p3, p(3))
1686 call this%add_point(p4, p(4))
1687 call this%add_point(p5, p(5))
1688 call this%add_point(p6, p(6))
1689 call this%add_point(p7, p(7))
1690 call this%add_point(p8, p(8))
1691
1692 ! Global to local mapping
1693 call this%htel%set(el_glb, el)
1694
1695 select type (ep => this%elements(el)%e)
1696 type is (hex_t)
1697 call ep%init(el_glb, &
1698 this%points(p(1)), this%points(p(2)), &
1699 this%points(p(3)), this%points(p(4)), &
1700 this%points(p(5)), this%points(p(6)), &
1701 this%points(p(7)), this%points(p(8)))
1702 class default
1703 call neko_error('Invalid element type')
1704 end select
1705
1706 end subroutine mesh_add_hex
1707
1709 subroutine mesh_add_point(this, p, idx)
1710 class(mesh_t), intent(inout) :: this
1711 type(point_t), intent(inout) :: p
1712 integer, intent(inout) :: idx
1713 integer :: tmp
1714
1715 tmp = p%id()
1716
1717 this%max_pts_id = max(this%max_pts_id, tmp)
1718
1719 if (tmp .le. 0) then
1720 call neko_error("Invalid point id")
1721 end if
1722
1723 if (this%htp%get(tmp, idx) .gt. 0) then
1724 this%mpts = this%mpts + 1
1725 call this%htp%set(tmp, this%mpts)
1726 this%points(this%mpts) = p
1727 idx = this%mpts
1728 end if
1729
1730 end subroutine mesh_add_point
1731
1736 subroutine mesh_add_face(this, f, head, chain, idx)
1737 type(mesh_t), intent(inout) :: this
1738 type(tuple4_i4_t), intent(inout) :: f
1739 integer, intent(inout) :: head(:)
1740 integer, intent(inout) :: chain(:)
1741 integer, intent(out) :: idx
1742 integer :: lp
1743
1744 ! The tuple is ordered, so chaining on the first point leaves any two
1745 ! faces in the same chain differing in their remaining points
1746 lp = this%have_point_glb_idx(f%x(1))
1747 if (lp .lt. 1) then
1748 call neko_error('Invalid face point')
1749 end if
1750
1751 idx = head(lp)
1752 do while (idx .gt. 0)
1753 if ((this%face_pts(2, idx) .eq. f%x(2)) .and. &
1754 (this%face_pts(3, idx) .eq. f%x(3)) .and. &
1755 (this%face_pts(4, idx) .eq. f%x(4))) return
1756 idx = chain(idx)
1757 end do
1758
1759 this%mfcs = this%mfcs + 1
1760 idx = this%mfcs
1761 this%face_pts(1, idx) = f%x(1)
1762 this%face_pts(2, idx) = f%x(2)
1763 this%face_pts(3, idx) = f%x(3)
1764 this%face_pts(4, idx) = f%x(4)
1765 chain(idx) = head(lp)
1766 head(lp) = idx
1767
1768 end subroutine mesh_add_face
1769
1774 subroutine mesh_add_edge(this, e, head, chain, idx)
1775 type(mesh_t), intent(inout) :: this
1776 type(tuple_i4_t), intent(inout) :: e
1777 integer, intent(inout) :: head(:)
1778 integer, intent(inout) :: chain(:)
1779 integer, intent(out) :: idx
1780 integer :: lp
1781
1782 ! The tuple is ordered, so chaining on the first point leaves any two
1783 ! edges in the same chain differing in their second point
1784 lp = this%have_point_glb_idx(e%x(1))
1785 if (lp .lt. 1) then
1786 call neko_error('Invalid edge endpoint')
1787 end if
1788
1789 idx = head(lp)
1790 do while (idx .gt. 0)
1791 if (this%edge_pts(2, idx) .eq. e%x(2)) return
1792 idx = chain(idx)
1793 end do
1794
1795 this%meds = this%meds + 1
1796 idx = this%meds
1797 this%edge_pts(1, idx) = e%x(1)
1798 this%edge_pts(2, idx) = e%x(2)
1799 chain(idx) = head(lp)
1800 head(lp) = idx
1801
1802 end subroutine mesh_add_edge
1803
1805 subroutine mesh_mark_curve_element(this, e, curve_data, curve_type)
1806 class(mesh_t), intent(inout) :: this
1807 integer, intent(in) :: e
1808 real(kind=dp), dimension(5,12), intent(in) :: curve_data
1809 integer, dimension(12), intent(in) :: curve_type
1810
1811 if (e .gt. this%nelv) then
1812 call neko_error('Invalid element index')
1813 end if
1814 if ((this%gdim .eq. 2 .and. sum(curve_type(5:8)) .gt. 0) ) then
1815 call neko_error('Invalid curve element')
1816 end if
1817 call this%curve%add_element(e, curve_data, curve_type)
1818
1819 end subroutine mesh_mark_curve_element
1820
1822 subroutine mesh_mark_labeled_facet(this, f, e, label)
1823 class(mesh_t), intent(inout) :: this
1824 integer, intent(in) :: f
1825 integer, intent(in) :: e
1826 integer, intent(in) :: label
1827
1828 if (e .gt. this%nelv) then
1829 call neko_error('Invalid element index')
1830 end if
1831
1832 if ((this%gdim .eq. 2 .and. f .gt. 4) .or. &
1833 (this%gdim .eq. 3 .and. f .gt. 6)) then
1834 call neko_error('Invalid facet index')
1835 end if
1836 call this%labeled_zones(label)%add_facet(f, e)
1837 this%facet_type(f,e) = -label
1838
1839 end subroutine mesh_mark_labeled_facet
1840
1842 subroutine mesh_mark_periodic_facet(this, f, e, pf, pe, pids)
1843 class(mesh_t), intent(inout) :: this
1844 integer, intent(in) :: f
1845 integer, intent(in) :: e
1846 integer, intent(in) :: pf
1847 integer, intent(in) :: pe
1848 integer, intent(inout) :: pids(4)
1849 integer, dimension(4) :: org_ids
1850
1851 call this%get_facet_ids(f, e, org_ids)
1852 call this%periodic%add_periodic_facet(f, e, pf, pe, pids, org_ids)
1853 end subroutine mesh_mark_periodic_facet
1854
1856 subroutine mesh_get_facet_ids(this, f, e, pids)
1857 class(mesh_t), intent(inout) :: this
1858 integer, intent(in) :: f
1859 integer, intent(in) :: e
1860 integer, intent(inout) :: pids(4)
1861 type(point_t), pointer :: pi
1862 type(tuple4_i4_t) :: t
1863 type(tuple_i4_t) :: t2
1864
1865 select type(ele => this%elements(e)%e)
1866 type is(hex_t)
1867 call ele%facet_order(t,f)
1868 pids = t%x
1869 type is(quad_t)
1870 call ele%facet_order(t2,f)
1871 pids(1) = t2%x(1)
1872 pids(2) = t2%x(2)
1873 pids(3) = 0
1874 pids(4) = 0
1875 end select
1876 end subroutine mesh_get_facet_ids
1877
1880 class(mesh_t), intent(inout) :: this
1881 integer :: i,j
1882 integer :: f
1883 integer :: e
1884 integer :: pf
1885 integer :: pe
1886 integer :: org_ids(4), pids(4)
1887 type(point_t), pointer :: pi
1888 integer, dimension(4, 6) :: face_nodes = reshape([ &
1889 1,5,7,3, &
1890 2,6,8,4, &
1891 1,2,6,5, &
1892 3,4,8,7, &
1893 1,2,4,3, &
1894 5,6,8,7],&
1895 [4,6])
1896 integer, dimension(2, 4) :: edge_nodes = reshape([ &
1897 1,3, &
1898 2,4, &
1899 1,2, &
1900 3,4],&
1901 [2,4])
1902
1903 do i = 1, this%periodic%size
1904 e = this%periodic%facet_el(i)%x(2)
1905 f = this%periodic%facet_el(i)%x(1)
1906 pe = this%periodic%p_facet_el(i)%x(2)
1907 pf = this%periodic%p_facet_el(i)%x(1)
1908 pids = this%periodic%p_ids(i)%x
1909 call this%get_facet_ids(f, e, pids)
1910 this%periodic%p_ids(i)%x = pids
1911 end do
1912 do i = 1, this%periodic%size
1913 e = this%periodic%facet_el(i)%x(2)
1914 f = this%periodic%facet_el(i)%x(1)
1915 org_ids = this%periodic%org_ids(i)%x
1916 select type(ele => this%elements(e)%e)
1917 type is(hex_t)
1918 do j = 1, 4
1919 pi => ele%pts(face_nodes(j,f))%p
1920 call pi%set_id(org_ids(j))
1921 end do
1922 type is(quad_t)
1923 do j = 1, 2
1924 pi => ele%pts(edge_nodes(j,f))%p
1925 call pi%set_id(org_ids(j))
1926 end do
1927 end select
1928 end do
1929 end subroutine mesh_reset_periodic_ids
1930
1932 subroutine mesh_create_periodic_ids(this, f, e, pf, pe)
1933 class(mesh_t), intent(inout) :: this
1934 integer, intent(in) :: f
1935 integer, intent(in) :: e
1936 integer, intent(in) :: pf
1937 integer, intent(in) :: pe
1938 type(point_t), pointer :: pi, pj
1939 real(kind=dp) :: l(3)
1940 integer :: i, j, id, match
1941 type(tuple4_i4_t) :: ft
1942 type(tuple_i4_t) :: et
1943 integer :: envvar_len
1944 character(len=255) :: tol_str
1945 real(kind=dp) :: tol
1946 integer, dimension(4, 6) :: face_nodes = reshape([&
1947 1,5,7,3,&
1948 2,6,8,4,&
1949 1,2,6,5,&
1950 3,4,8,7,&
1951 1,2,4,3,&
1952 5,6,8,7],&
1953 [4,6])
1954 integer, dimension(2, 4) :: edge_nodes = reshape([&
1955 1,3,&
1956 2,4,&
1957 1,2,&
1958 3,4 ],&
1959 [2,4])
1960
1961 call get_environment_variable("NEKO_PERIODIC_TOL", tol_str, envvar_len)
1962 if (envvar_len .gt. 0) then
1963 read(tol_str(1:envvar_len), *) tol
1964 else
1965 tol = 1d-7
1966 end if
1967
1968 select type(ele => this%elements(e)%e)
1969 type is(hex_t)
1970 select type(elp => this%elements(pe)%e)
1971 type is(hex_t)
1972 l = 0d0
1973 do i = 1, 4
1974 l = l + ele%pts(face_nodes(i,f))%p%x(1:3) - &
1975 elp%pts(face_nodes(i,pf))%p%x(1:3)
1976 end do
1977 l = l/4
1978 do i = 1, 4
1979 pi => ele%pts(face_nodes(i,f))%p
1980 match = 0
1981 do j = 1, 4
1982 pj => elp%pts(face_nodes(j,pf))%p
1983 if (norm2(pi%x(1:3) - pj%x(1:3) - l) .lt. tol) then
1984 id = min(pi%id(), pj%id())
1985 call pi%set_id(id)
1986 call pj%set_id(id)
1987 match = match + 1
1988 end if
1989 end do
1990 if ( match .gt. 1) then
1991 call neko_error('Multiple matches when creating periodic ids')
1992 else if (match .eq. 0) then
1993 call neko_error('Cannot find matching periodic point')
1994 end if
1995 end do
1996 end select
1997 type is(quad_t)
1998 select type(elp => this%elements(pe)%e)
1999 type is(quad_t)
2000 l = 0d0
2001 do i = 1, 2
2002 l = l + ele%pts(edge_nodes(i,f))%p%x(1:3) - &
2003 elp%pts(edge_nodes(i,pf))%p%x(1:3)
2004 end do
2005 l = l/2
2006 do i = 1, 2
2007 pi => ele%pts(edge_nodes(i,f))%p
2008 do j = 1, 2
2009 pj => elp%pts(edge_nodes(j,pf))%p
2010 !whatabout thie tolerance?
2011 if (norm2(pi%x(1:3) - pj%x(1:3) - l) .lt. tol) then
2012 id = min(pi%id(), pj%id())
2013 call pi%set_id(id)
2014 call pj%set_id(id)
2015 end if
2016 end do
2017 end do
2018 end select
2019 end select
2020 end subroutine mesh_create_periodic_ids
2021
2024 subroutine mesh_apply_periodic_facet(this, f, e, pf, pe, pids)
2025 class(mesh_t), intent(inout) :: this
2026 integer, intent(in) :: f
2027 integer, intent(in) :: e
2028 integer, intent(in) :: pf
2029 integer, intent(in) :: pe
2030 integer, intent(inout) :: pids(4)
2031 type(point_t), pointer :: pi
2032 integer :: i, id
2033 type(tuple4_i4_t) :: ft
2034 type(tuple_i4_t) :: et
2035 integer, dimension(4, 6) :: face_nodes = reshape([&
2036 1,5,7,3,&
2037 2,6,8,4,&
2038 1,2,6,5,&
2039 3,4,8,7,&
2040 1,2,4,3,&
2041 5,6,8,7],&
2042 [4,6])
2043 select type(ele => this%elements(e)%e)
2044 type is(hex_t)
2045 do i = 1, 4
2046 pi => ele%pts(face_nodes(i,f))%p
2047 call pi%set_id(pids(i))
2048 ! Only register the point while the mesh is being built; once the
2049 ! connectivity is generated the periodic ids are already known and
2050 ! the global->local table has been released
2051 if (allocated(this%htp)) then
2052 call this%add_point(pi, id)
2053 end if
2054 end do
2055 end select
2056
2057 end subroutine mesh_apply_periodic_facet
2058
2060 function mesh_get_global_edge(this, el, e) result(global_id)
2061 class(mesh_t), intent(in) :: this
2062 integer, intent(in) :: el
2063 integer, intent(in) :: e
2064 integer :: global_id
2065
2066 global_id = this%edge_lid(e, el)
2067
2068 if (this%gdim .eq. 2) then
2069 if (pe_size .gt. 1) then
2070 global_id = this%ddata%local_to_global_facet(global_id)
2071 end if
2072 else
2073 if (pe_size .gt. 1) then
2074 global_id = this%ddata%local_to_global_edge(global_id)
2075 end if
2076 end if
2077
2078 end function mesh_get_global_edge
2079
2083 function mesh_get_global_facet(this, el, f) result(global_id)
2084 class(mesh_t), intent(in) :: this
2085 integer, intent(in) :: el
2086 integer, intent(in) :: f
2087 integer :: global_id
2088
2089 global_id = this%face_lid(f, el)
2090
2091 if (pe_size .gt. 1) then
2092 global_id = this%ddata%local_to_global_facet(global_id)
2093 end if
2094
2095 end function mesh_get_global_facet
2096
2097
2106 function mesh_have_point_glb_idx(this, index) result(local_id)
2107 class(mesh_t), intent(inout) :: this
2108 integer, intent(inout) :: index
2109 integer :: local_id
2110
2111 if (.not. allocated(this%htp)) then
2112 call neko_error('have_point_glb_idx is only valid before generate_conn')
2113 end if
2114
2115 if (this%htp%get(index, local_id) .eq. 1) then
2116 local_id = -1
2117 end if
2118
2119 end function mesh_have_point_glb_idx
2120
2121
2123 function mesh_is_shared_point(this, el, p) result(shared)
2124 class(mesh_t), intent(inout) :: this
2125 integer, intent(in) :: el
2126 integer, intent(in) :: p
2127 integer :: local_index
2128 logical shared
2129
2130 local_index = this%pt_lid(p, el)
2131 shared = this%ddata%shared_point%element(local_index)
2132
2133 end function mesh_is_shared_point
2134
2135
2137 function mesh_is_shared_edge(this, el, e) result(shared)
2138 class(mesh_t), intent(inout) :: this
2139 integer, intent(in) :: el
2140 integer, intent(in) :: e
2141 integer :: local_index
2142 logical shared
2143 local_index = this%edge_lid(e, el)
2144 if (this%gdim .eq. 2) then
2145 shared = this%ddata%shared_facet%element(local_index)
2146 else
2147 shared = this%ddata%shared_edge%element(local_index)
2148 end if
2149 end function mesh_is_shared_edge
2150
2154 function mesh_is_shared_facet(this, el, f) result(shared)
2155 class(mesh_t), intent(inout) :: this
2156 integer, intent(in) :: el
2157 integer, intent(in) :: f
2158 integer :: local_index
2159 logical shared
2160
2161 local_index = this%face_lid(f, el)
2162 shared = this%ddata%shared_facet%element(local_index)
2163
2164 end function mesh_is_shared_facet
2165
2169 class(mesh_t), intent(inout) :: this
2170 integer :: i
2171 real(kind=rp) :: v(8)
2172 type(point_t) :: centroid
2173 logical :: fail
2174
2175 fail = .false.
2176
2177 if (this%gdim .eq. 3) then
2178 do i = 1, this%nelv
2180 this%elements(i)%e%pts(2)%p%x, &
2181 this%elements(i)%e%pts(3)%p%x, &
2182 this%elements(i)%e%pts(5)%p%x, &
2183 this%elements(i)%e%pts(1)%p%x &
2184 )
2185
2187 this%elements(i)%e%pts(4)%p%x, &
2188 this%elements(i)%e%pts(1)%p%x, &
2189 this%elements(i)%e%pts(6)%p%x, &
2190 this%elements(i)%e%pts(2)%p%x &
2191 )
2192
2194 this%elements(i)%e%pts(1)%p%x, &
2195 this%elements(i)%e%pts(4)%p%x, &
2196 this%elements(i)%e%pts(7)%p%x, &
2197 this%elements(i)%e%pts(3)%p%x &
2198 )
2199
2201 this%elements(i)%e%pts(3)%p%x, &
2202 this%elements(i)%e%pts(2)%p%x, &
2203 this%elements(i)%e%pts(8)%p%x, &
2204 this%elements(i)%e%pts(4)%p%x &
2205 )
2206
2208 this%elements(i)%e%pts(6)%p%x, &
2209 this%elements(i)%e%pts(7)%p%x, &
2210 this%elements(i)%e%pts(1)%p%x, &
2211 this%elements(i)%e%pts(5)%p%x &
2212 )
2213
2215 this%elements(i)%e%pts(8)%p%x, &
2216 this%elements(i)%e%pts(5)%p%x, &
2217 this%elements(i)%e%pts(2)%p%x, &
2218 this%elements(i)%e%pts(6)%p%x &
2219 )
2220
2222 this%elements(i)%e%pts(5)%p%x, &
2223 this%elements(i)%e%pts(8)%p%x, &
2224 this%elements(i)%e%pts(3)%p%x, &
2225 this%elements(i)%e%pts(7)%p%x &
2226 )
2227
2229 this%elements(i)%e%pts(7)%p%x, &
2230 this%elements(i)%e%pts(6)%p%x, &
2231 this%elements(i)%e%pts(4)%p%x, &
2232 this%elements(i)%e%pts(8)%p%x &
2233 )
2234
2235 if (v(1) .le. 0.0_rp .or. &
2236 v(2) .le. 0.0_rp .or. &
2237 v(3) .le. 0.0_rp .or. &
2238 v(4) .le. 0.0_rp .or. &
2239 v(5) .le. 0.0_rp .or. &
2240 v(6) .le. 0.0_rp .or. &
2241 v(7) .le. 0.0_rp .or. &
2242 v(8) .le. 0.0_rp ) then
2243
2244 centroid = this%elements(i)%e%centroid()
2245
2246 write(error_unit, '(A, A, I0, A, 3G12.5)') "*** ERROR ***: ", &
2247 "Wrong orientation of mesh element ", i, &
2248 " with centroid ", centroid%x
2249
2250 fail = .true.
2251 end if
2252 end do
2253 end if
2254
2255 if (fail) then
2256 call neko_error("Some mesh elements are not right-handed")
2257 end if
2258 end subroutine mesh_check_right_handedness
2259
2268 function parallelepiped_signed_volume(p1, p2, p3, origin) result(v)
2269 real(kind=dp), dimension(3), intent(in) :: p1, p2, p3, origin
2270 real(kind=dp) :: v
2271 real(kind=dp) :: vp1(3), vp2(3), vp3(3), cross(3)
2272
2273 vp1 = p1 - origin
2274 vp2 = p2 - origin
2275 vp3 = p3 - origin
2276
2277 cross(1) = vp1(2)*vp2(3) - vp2(3)*vp1(2)
2278 cross(2) = vp1(3)*vp2(1) - vp1(1)*vp2(3)
2279 cross(3) = vp1(1)*vp2(2) - vp1(2)*vp2(1)
2280
2281 v = cross(1)*vp3(1) + cross(2)*vp3(2) + cross(3)*vp3(3)
2282
2283 end function parallelepiped_signed_volume
2284
2294 subroutine mesh_subset_by_mask(this, other, mask, lx, ly, lz)
2295 class(mesh_t), intent(in) :: this
2296 class(mesh_t), intent(inout) :: other
2297 type(mask_t), intent(in) :: mask
2298 integer, intent(in) :: lx, ly, lz
2299 integer :: i, j, k, nelv, lxyz, gdim, e_m, nidx(4), nelv_c, el_c, el, i_m
2300 type(point_t) :: p(8)
2301 integer :: p_id = 1
2302
2303 call other%free()
2304 lxyz = lx * ly * lz
2305
2306 ! Initialize
2307 nelv = mask%size()/lxyz
2308 call other%init(this%gdim, nelv)
2309
2310 ! Assign the elements
2311 if (other%gdim .eq. 2) then
2312 call neko_error("Subset mesh not implemented for 2d")
2313 else if (other%gdim .eq. 3) then
2314 do el = 1, nelv
2315 i_m = 1 + lxyz * (el - 1)
2316 nidx = nonlinear_index(mask%get(i_m), lx, ly, lz)
2317 e_m = nidx(4) ! Actual element from the original mesh
2318 ! Retrieve the points form the other mesh.
2319 ! No need to shift points, since original
2320 ! mesh has done it.
2321 ! Had to use a new point id to avoid issues at
2322 ! periodic boundaries
2323 ! But this means that all points might be incorrectly
2324 ! marked as unique.
2325 do j = 1, 8
2326 call p(j)%init(this%elements(e_m)%e%pts(j)%p%x, p_id)
2327 p_id = p_id + 1
2328 end do
2329
2330 call other%add_element(el, el + other%offset_el, &
2331 p(1), p(2), p(3), p(4), &
2332 p(5), p(6), p(7), p(8))
2333 end do
2334 else
2335 if (pe_rank .eq. 0) call neko_error('Invalid dimension of mesh')
2336 end if
2337
2338 ! Skip searching for boundaries.
2339
2340 ! Update the curvature
2341 nelv_c = this%curve%size
2342 if (nelv_c .gt. 0) then
2343 el_c = 1
2344 el = 1
2345 ! 2 pointer scan
2346 do while (el .le. nelv .and. el_c .le. nelv_c)
2347
2348 i_m = 1 + lxyz * (el - 1)
2349 nidx = nonlinear_index(mask%get(i_m), lx, ly, lz)
2350 e_m = nidx(4)
2351
2352 if (e_m .lt. this%curve%curve_el(el_c)%el_idx) then
2353 el = el + 1
2354
2355 else if (e_m .gt. this%curve%curve_el(el_c)%el_idx) then
2356 el_c = el_c + 1
2357
2358 else
2359 call other%mark_curve_element(el, &
2360 this%curve%curve_el(el_c)%curve_data, &
2361 this%curve%curve_el(el_c)%curve_type)
2362 el = el + 1
2363 el_c = el_c + 1
2364 end if
2365
2366 end do
2367 end if
2368
2369 ! Finalize
2370 call other%finalize()
2371
2372 other%is_submesh = .true.
2373
2374 end subroutine mesh_subset_by_mask
2375
2376end 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:1775
subroutine mesh_generate_facet_numbering(this)
Generate a unique facet numbering.
Definition mesh.f90:1401
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:1130
subroutine mesh_add_point(this, p, idx)
Add a unique point to the mesh.
Definition mesh.f90:1710
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:1823
subroutine mesh_add_quad(this, el, el_glb, p1, p2, p3, p4)
Add a quadrilateral element to the mesh this.
Definition mesh.f90:1638
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:2269
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:1843
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:1857
subroutine mesh_create_periodic_ids(this, f, e, pf, pe)
Creates common ids for matching periodic points.
Definition mesh.f90:1933
subroutine mesh_reset_periodic_ids(this)
Reset ids of periodic points to their original ids.
Definition mesh.f90:1880
subroutine mesh_add_face(this, f, head, chain, idx)
Add a unique face represented as a 4-tuple to the mesh.
Definition mesh.f90:1737
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:2061
integer function mesh_have_point_glb_idx(this, index)
Check if the mesh has a point given its global index.
Definition mesh.f90:2107
subroutine mesh_generate_external_point_conn(this)
Generate element-element connectivity via points between PEs.
Definition mesh.f90:994
logical function mesh_is_shared_facet(this, el, f)
Check if facet f in element el is shared.
Definition mesh.f90:2155
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:2025
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:1806
integer function mesh_get_global_facet(this, el, f)
Return the global id of facet f in element el.
Definition mesh.f90:2084
subroutine mesh_check_right_handedness(this)
Check the correct orientation of the rst coordindates.
Definition mesh.f90:2169
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:2138
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:1670
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:2295
logical function mesh_is_shared_point(this, el, p)
Check if point p in element el is shared.
Definition mesh.f90:2124
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:398
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