ADflow  v1.0
ADflow is a finite volume RANS solver tailored for gradient-based aerodynamic design optimization.
zipperMesh.F90
Go to the documentation of this file.
1 module zippermesh
2 contains
3 
4  !
5  ! createZipperMesh zips multiple overlapping surface meshes.
6  ! First, it eliminates overlapping quads and then stiches the
7  ! non-overlapping surface mesh boundaries with triangular
8  ! surface grids.
9  ! In an overset framework the overlapping surface grids give
10  ! wrong estimate of airloads. Zipper mesh overcomes this by
11  ! creating a more accurate representation of the surface in the
12  ! mesh overlap regions.
13  ! ref: "Enhancements to the Hybrid Mesh Approach to Surface Loads
14  ! Integration on Overset Structured Grids", William M. Chan
15  ! http://people.nas.nasa.gov/~wchan/publications/AIAA-2009-3990.pdf
16  !
17 
18  subroutine createzippermesh(zipperFamList, nZipFam)
19 
20  use constants
23  use blockpointers, only: ndom, bcdata, nbocos, bctype, il, jl, kl
27  use walldistancedata, only: xvolumevec, is1, is2
28  use utils, only: setpointers, echk
29  use adjointvars, only: nnodeslocal
30  use sorting, only: faminlist
38  use stringops
39  use gapboundaries
40  use wallsearches, only: wallsearch
41 
42 #include <petsc/finclude/petsc.h>
43  use petsc
44  implicit none
45 
46  ! Input Parameters
47  integer(kind=intType), intent(in) :: nZipFam
48  integer(kind=intType), intent(in), dimension(nZipFam) :: zipperFamList
49 
50  ! Local Variables
51  integer(kind=intType) :: i, j, k, ii, jj, kk, iStart, iSize, sps, level, iStr
52  integer(kind=intType) :: iDom, jDom, nn, mm, n, ierr, iProc, iWork, nodeFam(3)
53  integer(kind=intType) :: nNodeTotal, nTriTotal, offset, iBCGroup, nFam
54  integer(kind=intType), dimension(:), allocatable :: famList
55 
56  integer(kind=intType), dimension(:, :), allocatable :: tmpInt2D, work
57  logical, dimension(:), allocatable :: oSurfReady
58  type(oversetwall), dimension(:), allocatable :: oSurfs
59  integer(kind=intType), dimension(:), allocatable :: intRecvBuf
60  type(oversetstring), target :: master, pocketMaster
61  type(oversetstring), pointer :: str
62  integer(kind=intType), dimension(:), allocatable :: nodeIndices
63 
64  integer(kind=intType) :: nFullStrings, nUnique
65  integer(kind=intType) :: nOSurfRecv, nOSurfSend
66  integer(kind=intType), dimension(:, :), allocatable :: oSurfRecvList, oSurfSendList
67  ! MPI/Communication related
68  integer mpiStatus(MPI_STATUS_SIZE)
69  integer(kind=intType), dimension(:, :), allocatable :: bufSizes
70  integer(kind=intType), dimension(:, :), allocatable :: recvInfo
71  integer(kind=intType) :: sendCount, recvCount, index
72  type(csrmatrix), pointer :: overlap
73  type(csrmatrix) :: overlapTranspose
74  type(zippermesh), pointer :: zipper
75  ! Wall search related
76  integer(kind=intType) :: ncells
77  type(oversetwall), dimension(:), allocatable, target :: walls
78  type(oversetwall), target :: fullWall
79 
80  if (.not. oversetpresent .or. (.not. usezippermesh)) then
81  ! Not overset so we don't can't have a zipper.
82  return
83  end if
84 
85  ! Zipper is only implemented for single grid and 1 spectral
86  ! instance (ie not time spectral).
87  level = 1
88  sps = 1
89 
90  call initbcdataiblank(level, sps)
91 
92  ! We build the zipper meshes *independently* for each BCType.
93  bcgrouploop: do ibcgroup = 1, nfamexchange
94 
95  ! Set a pointer to the zipper we are working on to make code
96  ! easier to read
97  zipper => zippermeshes(ibcgroup)
98 
99  ! Deallocate if already exists
100  if (zipper%allocated) then
101  call vecscatterdestroy(zipper%scatter, ierr)
102  call echk(ierr, __file__, __line__)
103  call vecdestroy(zipper%localVal, ierr)
104  call echk(ierr, __file__, __line__)
105  zipper%allocated = .false.
106  end if
107 
108  ! Note that the zipper%conn could be allocated with size of
109  ! zero, but the vecScatter and Vec are not petsc-allocated.
110  if (allocated(zipper%conn)) then
111  deallocate (zipper%conn, zipper%fam, zipper%indices)
112  end if
113 
114  ! Before we can proceed with the zipper, we need to generate
115  ! intersection of the zipperFamList() with the families on this
116  ! BCGroup. This would be so much easier in Python...
117 
118  if (allocated(famlist)) then
119  deallocate (famlist)
120  end if
121 
122  nfam = 0
123  do i = 1, size(bcfamgroups(ibcgroup)%famList)
124  do j = 1, size(zipperfamlist)
125  if (bcfamgroups(ibcgroup)%famList(i) == zipperfamlist(j)) then
126  nfam = nfam + 1
127  end if
128  end do
129  end do
130 
131  ! If nFam is zero, no need ot do anything for this zipper. Just
132  ! allocated zero-sized arrays so we know the size is 0.
133  if (nfam == 0) then
134  allocate (zipper%conn(3, 0), zipper%fam(0), zipper%indices(0))
135 
136  cycle ! to the next BCGroup
137  else
138  ! Do a second pass and fill up the famList
139  allocate (famlist(nfam))
140  nfam = 0
141  do i = 1, size(bcfamgroups(ibcgroup)%famList)
142  do j = 1, size(zipperfamlist)
143  if (bcfamgroups(ibcgroup)%famList(i) == zipperfamlist(j)) then
144  nfam = nfam + 1
145  famlist(nfam) = zipperfamlist(j)
146  end if
147  end do
148  end do
149  end if
150 
151  if (debugzipper .and. myid == 0) then
152  write (*, "(a)", advance="no") '-> Creating zipper for families : '
153  do i = 1, size(famlist)
154  write (*, "(a,1x)", advance="no") trim(famnames(famlist(i)))
155  end do
156  print "(1x)"
157  end if
158 
159  overlap => overlapmatrix(level, sps)
160  call transposeoverlap(overlap, overlaptranspose)
161  ! -------------------------------------------------------------------
162  ! Step 1: Eliminate any gap overlaps between meshes
163  ! -------------------------------------------------------------------
164 
165  ! Determine the average area of surfaces on each cluster. This
166  ! will be independent of any block splitting distribution.
167  call determineclusterareas(famlist)
168 
169  ! Set the boundary condition blank values
170  call slitelimination(famlist, level, sps)
171 
172  ! Create the surface deltas
173  call surfacedeviation(famlist, level, sps)
174 
175  ! ================ WE NORMALLY GOT THIS FROM OVERSETCOMM =============
176  ! Get the OSurf buffer sizes becuase we need that for getOSurfCommPattern
177  allocate (bufsizes(ndomtotal, 6), tmpint2d(ndomtotal, 6))
178  tmpint2d = 0
179  do nn = 1, ndom
180  call setpointers(nn, level, sps)
181  idom = cumdomproc(myid) + nn
182  call getosurfbuffersizes(famlist, il, jl, kl, tmpint2d(idom, 5), &
183  tmpint2d(idom, 6), .true.)
184  end do
185 
186  call mpi_allreduce(tmpint2d, bufsizes, 6 * ndomtotal, adflow_integer, mpi_sum, &
187  adflow_comm_world, ierr)
188  call echk(ierr, __file__, __line__)
189 
190  ! Get the basic surface comm pattern.
191  ! For sending, the worse case is sending all my blocks/fringes/walls to
192  ! everyone but myself:
193  ii = ndom * (nproc - 1)
194  allocate (osurfsendlist(2, ii))
195 
196  ! For receiving, the worse receive is all the blocks/fringes/wall I
197  ! don't already have:
198  ii = ndomtotal - ndom
199  allocate (osurfrecvlist(2, ii))
200 
201  call getosurfcommpattern(overlap, overlaptranspose, &
202  osurfsendlist, nosurfsend, osurfrecvlist, nosurfrecv, bufsizes(:, 6))
203  deallocate (tmpint2d, bufsizes)
204  ! ========================================================================
205 
206  ! Alloc data for the OSurfs
207  nn = max(nproc, 2 * nosurfsend, 2 * nosurfrecv)
208  allocate (tmpint2d(ndomtotal, 2), bufsizes(ndomtotal, 2), &
209  osurfready(ndomtotal), recvinfo(2, nn))
210 
211  tmpint2d = 0
212  ! Need to get the differt sizes for the OSurfs since they are now
213  ! based on the primal mesh as opposed to do the dual mesh as
214  ! previously done
215  do nn = 1, ndom
216  call setpointers(nn, level, sps)
217  idom = cumdomproc(myid) + nn
218  call getosurfbuffersizes(famlist, il, jl, kl, tmpint2d(idom, 1), &
219  tmpint2d(idom, 2), .false.)
220  end do
221 
222  ! Make sure everyone has the sizes
223  call mpi_allreduce(tmpint2d, bufsizes, 2 * ndomtotal, adflow_integer, mpi_sum, &
224  adflow_comm_world, ierr)
225  call echk(ierr, __file__, __line__)
226  deallocate (tmpint2d)
227 
228  ! allocate oSurfs for the primal mesh
229  allocate (osurfs(ndomtotal))
230  do idom = 1, ndomtotal
231  if (bufsizes(idom, 1) == 0) then
232  osurfready(idom) = .true.
233  else
234  osurfready(idom) = .false.
235  end if
236  end do
237 
238  ! Initialize the primal walls
239  do nn = 1, ndom
240  call setpointers(nn, level, sps)
241  idom = cumdomproc(myid) + nn
242  call initializeosurf(famlist, osurfs(idom), .false., clusters(idom))
243  call packosurf(famlist, osurfs(idom), .false.)
244  osurfready(idom) = .true.
245  end do
246 
247  ! Post all the oSurf iSends
248  sendcount = 0
249  do jj = 1, nosurfsend
250  iproc = osurfsendlist(1, jj)
251  idom = osurfsendlist(2, jj)
252  call sendosurf(osurfs(idom), idom, iproc, 0, sendcount)
253  end do
254 
255  recvcount = 0
256  do jj = 1, nosurfrecv
257  iproc = osurfrecvlist(1, jj)
258  idom = osurfrecvlist(2, jj)
259  call recvosurf(osurfs(idom), idom, iproc, 0, &
260  bufsizes(idom, 1), bufsizes(idom, 2), recvcount, recvinfo)
261  end do
262 
263  ! Complete all the recives and sends
264  do i = 1, recvcount
265  call mpi_waitany(recvcount, recvrequests, index, mpistatus, ierr)
266  call echk(ierr, __file__, __line__)
267  end do
268 
269  do i = 1, sendcount
270  call mpi_waitany(sendcount, sendrequests, index, mpistatus, ierr)
271  call echk(ierr, __file__, __line__)
272  end do
273 
274  ! Unpack any blocks we received if necessary:
275  do i = 1, recvcount
276 
277  ! Global domain index of the recv that finished
278  idom = recvinfo(1, i)
279  if (.not. osurfs(idom)%allocated) then
280  call unpackosurf(osurfs(idom))
281  end if
282  end do
283 
284  ! Determine the size of the buffer we need locally for the
285  ! receives. We do this outside the main iteration loop since it
286  ! always the same size.
287  ii = 0
288  do jj = 1, nosurfsend
289  ! These blocks are by definition local.
290  idom = osurfsendlist(2, jj)
291  ii = ii + osurfs(idom)%maxCells
292  end do
293  allocate (intrecvbuf(max(1, ii)))
294 
295  ! ------------------------ Performing Searches ----------------
296  call getworkarray(overlap, work)
297 
298  do iwork = 1, size(work, 2)
299  idom = work(1, iwork)
300  jdom = work(2, iwork)
301  call wallsearch(osurfs(idom), osurfs(jdom))
302  end do
303 
304  ! ------------------------ Receiving iBlank back ---------------
305 
306  sendcount = 0
307  do jj = 1, nosurfrecv
308  iproc = osurfrecvlist(1, jj)
309  idom = osurfrecvlist(2, jj)
310  sendcount = sendcount + 1
311  call mpi_isend(osurfs(idom)%iBlank, osurfs(idom)%maxCells, adflow_integer, &
312  iproc, idom, adflow_comm_world, sendrequests(sendcount), ierr)
313  call echk(ierr, __file__, __line__)
314  end do
315 
316  recvcount = 0
317  istart = 1
318  do jj = 1, nosurfsend
319  iproc = osurfsendlist(1, jj)
320  idom = osurfsendlist(2, jj)
321  isize = osurfs(idom)%maxCells
322  recvcount = recvcount + 1
323 
324  call mpi_irecv(intrecvbuf(istart), isize, adflow_integer, &
325  iproc, idom, adflow_comm_world, recvrequests(recvcount), ierr)
326  call echk(ierr, __file__, __line__)
327  istart = istart + isize
328  end do
329 
330  ! Now wait for the sends and receives to finish
331  do i = 1, sendcount
332  call mpi_waitany(sendcount, sendrequests, index, mpistatus, ierr)
333  call echk(ierr, __file__, __line__)
334  end do
335 
336  do i = 1, recvcount
337  call mpi_waitany(recvcount, recvrequests, index, mpistatus, ierr)
338  call echk(ierr, __file__, __line__)
339  end do
340 
341  ! Process the oSurfs we own locally
342  do nn = 1, ndom
343  call setpointers(nn, level, sps)
344  idom = cumdomproc(myid) + nn
345  ii = 0
346  do mm = 1, nbocos
347  if (faminlist(bcdata(mm)%famID, famlist)) then
348  do j = bcdata(mm)%jnBeg + 1, bcdata(mm)%jnEnd
349  do i = bcdata(mm)%inBeg + 1, bcdata(mm)%inEnd
350  ii = ii + 1
351  if (osurfs(idom)%iBlank(ii) == -2) then
352  bcdata(mm)%iblank(i, j) = -2
353  end if
354  end do
355  end do
356  end if
357  end do
358  end do
359 
360  ! And update based on the data we received from other processors
361  ii = 0
362  do kk = 1, nosurfsend
363 
364  idom = osurfsendlist(2, kk)
365  nn = idom - cumdomproc(myid)
366 
367  ! Set the block pointers for the local block we are dealing
368  !with:
369  call setpointers(nn, level, sps)
370  do mm = 1, nbocos
371  if (faminlist(bcdata(mm)%famID, famlist)) then
372  do j = bcdata(mm)%jnBeg + 1, bcdata(mm)%jnEnd
373  do i = bcdata(mm)%inBeg + 1, bcdata(mm)%inEnd
374  ii = ii + 1
375  if (intrecvbuf(ii) == -2) then
376  bcdata(mm)%iBlank(i, j) = -2
377  end if
378  end do
379  end do
380  end if
381  end do
382  end do
383  ! Release some unnecessary memory
384  deallocate (bufsizes, osurfsendlist, osurfrecvlist, osurfready, recvinfo, work)
385 
386  ! Ditch our oSurfs
387  call deallocateosurfs(osurfs, ndomtotal)
388  deallocate (osurfs, intrecvbuf)
389 
390  ! Before we continue, we do a little more
391  ! processing. bowTieElimination tries to eliminate cells
392  call bowtieandisolationelimination(famlist, level, sps)
393 
394  ! -------------------------------------------------------------------
395  ! Step 2: Identify gap boundary strings and split the strings to
396  ! sub-strings.
397  ! -------------------------------------------------------------------
398 
399  if (debugzipper) then
400  call writewalls(famlist)
401  end if
402 
403  call makegapboundarystrings(famlist, level, sps, master)
404 
405  rootproc: if (myid == 0) then
406  if (debugzipper) then
407  call writezipperdebug(master)
408  end if
409 
410  ! Run the common core zipper routines
411  call zippercore(master, pocketmaster, debugzipper)
412 
413  ! Before we create the final data structures for the zipper
414  ! mesh; we will combine the master and pocketMaster
415  ! strings, and unique-ify the indices to help keep amount of data
416  ! transfer to a minimum. We also determine at this point which
417  ! triangle belongs to which family.
418 
419  call setstringpointers(master)
420  ntritotal = master%ntris + pocketmaster%ntris
421  nnodetotal = master%nNodes + pocketmaster%nNodes
422  allocate (zipper%conn(3, ntritotal), zipper%fam(ntritotal), zipper%indices(nnodetotal))
423 
424  ii = 0
425  jj = 0
426  outerloop: do istr = 1, 2
427  ! Select which of the two we are dealing with
428  if (istr == 1) then
429  str => master
430  offset = 0
431  else
432  str => pocketmaster
433  offset = master%nNodes
434  end if
435 
436  ! Loop over the number of triangles.
437  do i = 1, str%nTris
438 
439  ! Extract the family from the nodes
440  do j = 1, 3
441  nodefam(j) = str%family(str%tris(j, i))
442  end do
443 
444  ! Increment the running counter for all triangles.
445  ii = ii + 1
446 
447  ! Set the family
448  zipper%Fam(ii) = selectnodefamily(nodefam)
449 
450  ! Set the connectivity.
451  zipper%conn(:, ii) = str%tris(:, i) + offset
452  end do
453 
454  ! Loop over the nodal indices and add
455  do j = 1, str%nNodes
456  ! And set the global indices that the zipper needs. Note
457  ! that we are doing zipperIndices as a scalar and that
458  ! the indices refer to the global nodes so are already in
459  ! 0-based ordering.
460  jj = jj + 1
461  zipper%indices(jj) = str%ind(j)
462  end do
463  end do outerloop
464 
465  call deallocatestring(master)
466  call deallocatestring(pocketmaster)
467  else
468  ! Other procs don't have any triangles *sniffle* :-(
469  allocate (zipper%conn(3, 0), zipper%fam(0), zipper%indices(0))
470  end if rootproc
471 
472  call veccreatempi(adflow_comm_world, size(zipper%indices), petsc_determine, &
473  zipper%localVal, ierr)
474  call echk(ierr, __file__, __line__)
475 
476  ! Now create the general scatter that goes from the
477  ! globalNodalVector to the local vectors.
478  call iscreategeneral(adflow_comm_world, size(zipper%indices), &
479  zipper%indices - 1, petsc_copy_values, is1, ierr)
480  call echk(ierr, __file__, __line__)
481 
482 #if PETSC_VERSION_GE(3,8,0)
483  call vecscattercreate(bcfamexchange(ibcgroup, sps)%nodeValLocal, is1, &
484  zipper%localVal, petsc_null_is, zipper%scatter, ierr)
485 #else
486  call vecscattercreate(bcfamexchange(ibcgroup, sps)%nodeValLocal, is1, &
487  zipper%localVal, petsc_null_object, zipper%scatter, ierr)
488 #endif
489  call echk(ierr, __file__, __line__)
490 
491  call isdestroy(is1, ierr)
492  call echk(ierr, __file__, __line__)
493 
494  ! Flag to keep track of allocated PETSc object.
495  zipper%allocated = .true.
496 
497  end do bcgrouploop
498 
499  contains
500  function selectnodefamily(nodeFam)
501  implicit none
502  integer(kind=intType), dimension(3) :: nodefam
503  integer(kind=intType) :: selectnodefamily
504 
505  ! We have a few cases to check:
506 
507  if (nodefam(1) == nodefam(2) .and. nodefam(1) == nodefam(3)) then
508  ! Case 1: All of the nodes have the same family. This is what
509  ! *should* happen all the time:
510 
511  selectnodefamily = nodefam(1)
512 
513  else if (nodefam(1) == nodefam(2)) then
514 
515  ! Case 2a: First two nodes are the same. Take the family from 1.
516 
517  selectnodefamily = nodefam(1)
518 
519  else if (nodefam(2) == nodefam(3)) then
520 
521  ! Case 2b: Last two nodes are the same. Take the family from 2.
522 
523  selectnodefamily = nodefam(2)
524 
525  else if (nodefam(1) == nodefam(3)) then
526 
527  ! Case 2b: First and last nodes are the same. Take the family from 1.
528 
529  selectnodefamily = nodefam(1)
530 
531  else
532 
533  ! All nodes are different. We arbitrarily take the first and
534  ! print a warning becuase this should not happen.
535 
536  selectnodefamily = nodefam(1)
537  print *, 'Family for triangle could not be uniquely determined. Nodes are from 3 different families!'
538  end if
539 
540  end function selectnodefamily
541  end subroutine createzippermesh
542 
543  subroutine checkzipper(fileName)
544 
545  ! Special routine for checking zipper mesh loaded from debug file
546  use constants
547  use stringops
548  implicit none
549 
550  ! Input/Output
551  character(*), intent(in) :: fileName
552 
553  ! Working
554  type(oversetstring) :: master, pocketMaster
555 
556  call loadzipperdebug(filename, master)
557  call zippercore(master, pocketmaster, .true.)
558 
559  print *, 'Zipper successfully completed'
560  end subroutine checkzipper
561 
562  subroutine zippercore(master, pocketMaster, debugZipper)
563 
564  ! Common routine for creating the zipper from a given master
565  use constants
566  use stringops
567  use kdtree2_module
568  implicit none
569 
570  ! Input/Output
571  type(oversetstring), intent(inout) :: master, pocketMaster
572  logical, intent(in) :: debugZipper
573 
574  ! Local
575  type(oversetstring), dimension(:), allocatable, target :: strings
576  type(oversetstring), pointer :: str
577  integer(kind=intType) :: nStrings, i, j, nTriSelf
578 
579  if (debugzipper) then
580  open (unit=101, file="master_beforeStrings.dat", form='formatted')
581  write (101, *) 'TITLE = "Master Data" '
582  write (101, *) 'Variables = "X" "Y" "Z"'
583  call writeoversetmaster(master, 101)
584  close (101)
585  end if
586 
587  call createorderedstrings(master, strings, nstrings)
588 
589  master%myID = 99
590 
591  ! Allocate space for the maximum number of directed edges. This
592  ! is equal to the initial number of edges (nElems) plus 3 times
593  ! the number of triangles we will add, which is also nElems. Now,
594  ! we will probably not actualy have that many since we save a
595  ! triangle and 3 edges for every self zip that is
596  ! applied. Therefore we know this will always be enough
597  allocate (master%edges(4 * master%nElems))
598 
599  master%nEdges = 0
600 
601  do i = 1, nstrings
602  str => strings(i)
603  do j = 1, str%nElems
604  master%nEdges = master%nEdges + 1
605  master%edges(master%nEdges)%n1 = str%p%conn(1, str%pElems(j))
606  master%edges(master%nEdges)%n2 = str%p%conn(2, str%pElems(j))
607  end do
608  end do
609 
610  ! Allocate space for the triangles. Again, this can be at most,
611  ! nElems, but the total number of elements will most likely be
612  ! smaller due to self zipping. If someone puts
613  allocate (master%tris(3, master%nElems))
614  master%nTris = 0
615 
616  ! Build the master tree
617  master%tree => kdtree2_create(master%x, sort=.true.)
618 
619  ! Perform the string association:
620  call stringmatch(strings, nstrings, debugzipper)
621 
622  if (debugzipper) then
623  open (unit=101, file="strings_beforeSelfZip.dat", form='formatted')
624  write (101, *) 'TITLE = "Gap Strings Data" '
625  write (101, *) 'Variables = "X" "Y" "Z" "Nx" "Ny" "Nz" "Vx" "Vy" "Vz" "ind" &
626  &"gapID" "gapIndex" "otherID" "otherIndex" "ratio"'
627  do i = 1, nstrings
628  call writeoversetstring(strings(i), strings, nstrings, 101)
629  end do
630  close (101)
631  end if
632 
633  call performselfzip(master, strings, nstrings, debugzipper)
634 
635  ! Write out any self-zipped triangles
636  ntriself = master%nTris
637 
638  if (debugzipper) then
639  call writeoversettriangles(master, "selfzipTriangulation.dat", 1, master%nTris)
640  end if
641 
642  ! Write out the gaps AFTER the self zip
643  if (debugzipper) then
644  open (unit=101, file="strings_afterSelfZip.dat", form='formatted')
645  write (101, *) 'TITLE = "Gap Strings Data" '
646  write (101, *) 'Variables = "X" "Y" "Z" "Nx" "Ny" "Nz" "Vx" "Vy" "Vz" "ind" &
647  &"gapID" "gapIndex" "otherID" "otherIndex" "ratio"'
648  do i = 1, nstrings
649  call writeoversetstring(strings(i), strings, nstrings, 101)
650  end do
651  close (101)
652  end if
653 
654  ! Now do the cross zip
655  call makecrosszip(master, strings, nstrings, debugzipper)
656 
657  ! And write out the triangle from the cross zip
658  if (debugzipper) then
659  call writeoversettriangles(master, "crossZipTriangulation.dat", ntriself + 1, master%nTris)
660  end if
661 
662  ! ---------------------------------------------------------------
663  ! Sort through zipped triangle edges and the edges which have not
664  ! been used twice (orphan edges) will be ultimately gathered to
665  ! form polygon pockets to be zipped.
666  if (debugzipper) then
667  print *, 'Doing pocket zip'
668  end if
669  call makepocketzip(master, strings, nstrings, pocketmaster, debugzipper)
670 
671  if (debugzipper) then
672  call writeoversettriangles(pocketmaster, "pocketTriangulation.dat", 1, pocketmaster%nTris)
673  end if
674 
675  ! Clean up the reminder of the sting memory on the root proc
676  do i = 1, nstrings
677  call deallocatestring(strings(i))
678  end do
679  deallocate (strings)
680 
681  end subroutine zippercore
682 
683  !
684  ! determineClusterArea determine the average cell surface area
685  ! for all blocks in a particular cluster. This is used for
686  ! determine blanking preference for overlapping surface cells.
687 
688  subroutine determineclusterareas(famList)
689 
690  use constants
691  use blockpointers, only: ndom, bcdata, nbocos, bctype
695  use bcpointers, only: xx
696  use sorting, only: faminlist
697  implicit none
698 
699  ! Input Parameters
700  integer(kind=intType), intent(in), dimension(:) :: famList
701 
702  ! Working
703  integer(kind=intType) :: i, j, mm, nn, clusterID, ierr, nPts, nCells
704  integer(kind=intType) :: iBeg, iEnd, jBeg, jEnd
705  real(kind=realtype), dimension(:), allocatable :: localareas
706  integer(kind=intType), dimension(:), allocatable :: localCount, globalCount
707  real(kind=realtype), dimension(:, :), allocatable :: pts
708  real(kind=realtype) :: fact, v1(3), v2(3), sss(3), da
709 
710  if (allocated(clusterareas)) then
711  ! We only ever do this once!
712  deallocate (clusterareas)
713  end if
714 
715  allocate (clusterareas(nclusters), localareas(nclusters), &
716  localcount(nclusters), globalcount(nclusters))
717 
718  localareas = zero
719  localcount = 0
720 
721  domains: do nn = 1, ndom
722  call setpointers(nn, 1_inttype, 1_inttype)
723 
724  clusterid = clusters(cumdomproc(myid) + nn)
725 
726  ! Loop over the number of boundary subfaces of this block.
727  bocos: do mm = 1, nbocos
728  faminclude: if (faminlist(bcdata(mm)%famID, famlist)) then
729  ! Store the cell range of the subfaces a bit easier.
730  ! As only owned faces must be considered the nodal range
731  ! in BCData must be used to obtain this data.
732 
733  jbeg = bcdata(mm)%jnBeg + 1; jend = bcdata(mm)%jnEnd
734  ibeg = bcdata(mm)%inBeg + 1; iend = bcdata(mm)%inEnd
735 
736  call setbcpointers(mm, .true.)
737 
738  ! Compute the dual area at each node. Just store in first dof
739  do j = jbeg, jend ! This is a face loop
740  do i = ibeg, iend ! This is a face loop
741 
742  v1(:) = xx(i + 1, j + 1, :) - xx(i, j, :)
743  v2(:) = xx(i, j + 1, :) - xx(i + 1, j, :)
744 
745  ! Cross Product
746  call cross_prod(v1, v2, sss)
747  da = half * sqrt(sss(1)**2 + sss(2)**2 + sss(3)**2)
748  localareas(clusterid) = localareas(clusterid) + da
749  localcount(clusterid) = localcount(clusterid) + 1
750  end do
751  end do
752  end if faminclude
753  end do bocos
754  end do domains
755 
756  ! All reduce sum for the localAreas to get clusterAreas and
757  ! localCount to get globalCount
758 
759  call mpi_allreduce(localareas, clusterareas, nclusters, adflow_real, &
760  mpi_sum, adflow_comm_world, ierr)
761  call echk(ierr, __file__, __line__)
762 
763  call mpi_allreduce(localcount, globalcount, nclusters, adflow_integer, &
764  mpi_sum, adflow_comm_world, ierr)
765  call echk(ierr, __file__, __line__)
766 
767  ! Final get the average global area
768  do i = 1, nclusters
769  clusterareas(i) = clusterareas(i) / max(globalcount(i), 1)
770  end do
771 
772  deallocate (localareas, localcount, globalcount)
773  end subroutine determineclusterareas
774 
775  subroutine initbcdataiblank(level, sps)
776 
777  use constants
778  use blockpointers
779  use communication
780  use utils, only: setpointers
782  implicit none
783 
784  ! Input Parameters
785  integer(kind=intType), intent(in) :: level, sps
786 
787  ! Local variables
788  integer(kind=intType) :: mm, nn, i, j, k, iBeg, iEnd, jBeg, jEnd
789  logical :: side(4)
790 
791  integer(kind=intType), dimension(:, :), pointer :: ibp, gcp, frx
792  integer(kind=intType), dimension(:, :), allocatable :: toFlip
793 
794  ! This routine initializes the surface cell iblank based on the
795  ! volume iblank.
796 
797  domainloop: do nn = 1, ndom
798  call setpointers(nn, level, sps)
799 
800  ! Setting the surface IBlank array is done for *all* bocos.
801  bocoloop: do mm = 1, nbocos
802  select case (bcfaceid(mm))
803  case (imin)
804  ibp => iblank(2, :, :)
805  case (imax)
806  ibp => iblank(il, :, :)
807  case (jmin)
808  ibp => iblank(:, 2, :)
809  case (jmax)
810  ibp => iblank(:, jl, :)
811  case (kmin)
812  ibp => iblank(:, :, 2)
813  case (kmax)
814  ibp => iblank(:, :, kl)
815  end select
816 
817  ! -------------------------------------------------
818  ! Step 1: Set the (haloed) cell iBlanks directly from
819  ! the volume iBlanks
820  ! -------------------------------------------------
821  jbeg = bcdata(mm)%jnBeg + 1; jend = bcdata(mm)%jnEnd
822  ibeg = bcdata(mm)%inBeg + 1; iend = bcdata(mm)%inEnd
823 
824  ! Just set the cell iblank directly from the cell iblank
825  ! above it. Remember the +1 in ibp is for the pointer
826  ! offset. These ranges *ALWAYS* give 1 level of halos
827  do j = jbeg - 1, jend + 1
828  do i = ibeg - 1, iend + 1
829  bcdata(mm)%iBlank(i, j) = ibp(i + 1, j + 1)
830  end do
831  end do
832  end do bocoloop
833  end do domainloop
834  end subroutine initbcdataiblank
835 
836  subroutine slitelimination(famList, level, sps)
837 
838  use constants
839  use blockpointers
840  use communication
841  use utils, only: setpointers
843  use sorting, only: faminlist
844  implicit none
845 
846  ! Input Parameters
847  integer(kind=intType), intent(in), dimension(:) :: famList
848  integer(kind=intType), intent(in) :: level, sps
849 
850  ! Local variables
851  integer(kind=intType) :: mm, nn, i, j, k, iBeg, iEnd, jBeg, jEnd
852  logical :: side(4)
853 
854  integer(kind=intType), dimension(:, :), pointer :: ibp, gcp, frx
855  integer(kind=intType), dimension(:, :), allocatable :: toFlip
856 
857  ! This routine initializes the surface cell iblank based on the
858  ! volume iblank. It is not a straight copy since we a little
859  ! preprocessing
860 
861  ! This is a little trickier than it seems. The reason is that we
862  ! will allow a limited number of interpolated cells to be used
863  ! directly in the integration provided the meet certain criteria.
864 
865  ! Consider the following
866  ! +------+--------+-------+
867  ! |ib=1 | ib=1 | ib= 1 |
868  ! | | | |
869  ! | | | |
870  ! +------+========+-------+
871  ! |ib=1 || ib=-1 || ib=1 |
872  ! | || || |
873  ! | || || |
874  !==+======+--------+=======+==
875  ! |ib=-1 | ib=-1 | ib=-1 |
876  ! | | | |
877  ! | | | |
878  ! +------+--------+-------+
879  !
880  ! The boundary between real/interpolated cells is marked by double
881  ! lines. For zipper mesh purposes, it is generally going to be
882  ! better to treat the center cell, as a regular force integration
883  ! cell (ie surface iblank=1). The criteria for selection of these
884  ! cells is:
885 
886  ! 1. The cell must not have been a forced receiver (ie at overset outer
887  ! bound)
888  ! 2. Any pair *opposite* sides of the cell must be compute cells.
889 
890  ! This criterial allows one-cell wide 'slits' to be pre-eliminated.
891 
892  call flagforcedrecv()
893  domainloop: do nn = 1, ndom
894  call setpointers(nn, level, sps)
895 
896  ! Setting the surface IBlank array is done for *all* bocos.
897  bocoloop: do mm = 1, nbocos
898  select case (bcfaceid(mm))
899  case (imin)
900  ibp => iblank(2, :, :)
901  gcp => globalcell(2, :, :)
902  frx => forcedrecv(2, :, :)
903  case (imax)
904  ibp => iblank(il, :, :)
905  gcp => globalcell(il, :, :)
906  frx => forcedrecv(il, :, :)
907  case (jmin)
908  ibp => iblank(:, 2, :)
909  gcp => globalcell(:, 2, :)
910  frx => forcedrecv(:, 2, :)
911  case (jmax)
912  ibp => iblank(:, jl, :)
913  gcp => globalcell(:, jl, :)
914  frx => forcedrecv(:, jl, :)
915  case (kmin)
916  ibp => iblank(:, :, 2)
917  gcp => globalcell(:, :, 2)
918  frx => forcedrecv(:, :, 2)
919  case (kmax)
920  ibp => iblank(:, :, kl)
921  gcp => globalcell(:, :, kl)
922  frx => forcedrecv(:, :, kl)
923  end select
924 
925  ! -------------------------------------------------
926  ! Step 1: Set the (haloed) cell iBlanks directly from
927  ! the volume iBlanks
928  ! -------------------------------------------------
929  jbeg = bcdata(mm)%jnBeg + 1; jend = bcdata(mm)%jnEnd
930  ibeg = bcdata(mm)%inBeg + 1; iend = bcdata(mm)%inEnd
931 
932  ! Only do the slit elimination if we actually care about
933  ! this surface for the zipper
934  faminclude: if (faminlist(bcdata(mm)%famID, famlist)) then
935 
936  ! -------------------------------------------------
937  ! Step 2: Slit elimination
938  ! -------------------------------------------------
939 
940  ! Now we loop back through the cells again. For
941  ! interpolated cells with iblank=-1 we see if it satifies
942  ! the criteria above. If so we flag it wil "toFlip" =
943  ! 1. Note that we can't set a particular iblank directly
944  ! since that could cause an "avalance" effect with the
945  ! later iterations using the updated iblank from a previous
946  ! iteration.
947  allocate (toflip(ibeg:iend, jbeg:jend))
948  toflip = 0
949  do j = jbeg, jend
950  do i = ibeg, iend
951 
952  ! We *might* add it if the interpolated cell is
953  ! touching two real cell on opposite sides.
954 
955  if (bcdata(mm)%iBlank(i, j) == -1 .and. validcell(i, j)) then
956 
957  ! Reset the side flag
958  side = .false.
959 
960  if (validcell(i - 1, j) .and. bcdata(mm)%iBlank(i - 1, j) == 1) then
961  side(1) = .true.
962  end if
963 
964  if (validcell(i + 1, j) .and. bcdata(mm)%iBlank(i + 1, j) == 1) then
965  side(2) = .true.
966  end if
967 
968  if (validcell(i, j - 1) .and. bcdata(mm)%iBlank(i, j - 1) == 1) then
969  side(3) = .true.
970  end if
971 
972  if (validcell(i, j + 1) .and. bcdata(mm)%iBlank(i, j + 1) == 1) then
973  side(4) = .true.
974  end if
975 
976  if ((side(1) .and. side(2)) .or. (side(3) .and. side(4))) then
977  toflip(i, j) = 1
978  end if
979  end if
980  end do
981  end do
982 
983  ! Now just set the cell surface iblank to 1 if we
984  ! determined above we need to flip the cell
985 
986  do j = jbeg, jend
987  do i = ibeg, iend
988  if (toflip(i, j) == 1) then
989  bcdata(mm)%iBlank(i, j) = 1
990  end if
991  end do
992  end do
993 
994  deallocate (toflip)
995  end if faminclude
996  end do bocoloop
997  end do domainloop
998 
999  contains
1000  function validcell(i, j)
1001  implicit none
1002  integer(kind=intType), intent(in) :: i, j
1003  logical :: validcell
1004 
1005  ! for our purposes here, a valid cell is one that:
1006  ! 1. Is not a boundary halo. ie has globalCell >= 0
1007  ! 2. It is not a force receiver.
1008 
1009  validcell = .false.
1010  if (gcp(i + 1, j + 1) >= 0 .and. frx(i + 1, j + 1) == 0) then
1011  validcell = .true.
1012  end if
1013  end function validcell
1014  end subroutine slitelimination
1015 
1016  !
1017  ! surfaceDeviation computes an approximation of the maximum
1018  ! deviation a surface could be as compared to an underlying "exact"
1019  ! surface. The purpose is to compute an adaptive "near wall distance"
1020  ! value that can be used to determine if a point is "close" to a
1021  ! * wall.
1022  !
1023 
1024  subroutine surfacedeviation(famList, level, sps)
1025 
1026  use constants
1027  use blockpointers, only: bcdata, x, nbocos, ndom, bctype, il, jl, kl, bcfaceid
1028  use utils, only: setpointers, mynorm2, setbcpointers
1029  use sorting, only: faminlist
1030  use bcpointers, only: xx
1031  implicit none
1032 
1033  ! Input Parameters
1034  integer(kind=intType), intent(in), dimension(:) :: famList
1035  integer(kind=intType), intent(in) :: level, sps
1036 
1037  ! Local Variables
1038  integer(kind=intType) :: i, j, k, ii, jj, kk, nn, iBeg, iEnd, jBeg, jEnd, mm
1039  real(kind=realtype) :: deviation
1040 
1041  ! Loop over blocks
1042  do nn = 1, ndom
1043  call setpointers(nn, level, sps)
1044 
1045  bocoloop: do mm = 1, nbocos
1046  faminclude: if (faminlist(bcdata(mm)%famID, famlist)) then
1047 
1048  call setbcpointers(mm, .true.)
1049  jbeg = bcdata(mm)%jnBeg; jend = bcdata(mm)%jnEnd
1050  ibeg = bcdata(mm)%inBeg; iend = bcdata(mm)%inEnd
1051 
1052  ! The procedure goes in 2 passes. The first pass checks all
1053  ! the i-direction edges, and the second all the j-direction
1054  ! edges. For every edge, we estimate the max deviation
1055  ! along that edge and then the surface will use the maximum
1056  ! deviation from each of the 4 edges. Ie we scatter the
1057  ! edge deviation to the two cells next to it. We only do
1058  ! the real cells here. Boundary halos get -one set (below)
1059  ! and then actual compute halos are set with an exchange.
1060 
1061  bcdata(mm)%delta = -one
1062 
1063  ! ------------------
1064  ! Check the i-edges
1065  ! ------------------
1066  do j = jbeg, jend ! <------- Node loop
1067  do i = ibeg + 1, iend ! <------- Face Loop
1068 
1069  ! We will creating a local cubic approximation of the
1070  ! local edge. This will use node i-2, i-1, i, and
1071  ! i+1. However, due to the pointer offset, these are
1072  ! all shifted by 1 to get: i-1, i, i+1, i+2
1073 
1074  deviation = checkdeviation(xx(i - 1, j, :), xx(i, j, :), xx(i + 1, j, :), &
1075  xx(i + 2, j, :))
1076 
1077  ! Cell to the bottom:
1078  if (j - 1 >= jbeg + 1) then
1079  bcdata(mm)%delta(i, j - 1) = max(bcdata(mm)%delta(i, j - 1), deviation)
1080  end if
1081 
1082  ! Cell to the top:
1083  if (j + 1 <= jend) then
1084  bcdata(mm)%delta(i, j + 1) = max(bcdata(mm)%delta(i, j + 1), deviation)
1085  end if
1086  end do
1087  end do
1088 
1089  ! -----------------
1090  ! Check the j-edges
1091  ! -----------------
1092  do j = jbeg + 1, jend ! <------- Face loop
1093  do i = ibeg, iend ! <------- Node Loop
1094 
1095  ! We will creating a local cubic approximation of the
1096  ! local edge. This will use node j-2, j-1, j, and
1097  ! j+1. However, due to the pointer offset, these are
1098  ! all shifted by 1 to get: j-1, j, j+1, j+2
1099 
1100  deviation = checkdeviation(xx(i, j - 1, :), xx(i, j, :), xx(i, j + 1, :), &
1101  xx(i, j + 2, :))
1102 
1103  ! Cell to the left:
1104  if (i - 1 >= ibeg + 1) then
1105  bcdata(mm)%delta(i - 1, j) = max(bcdata(mm)%delta(i - 1, j), deviation)
1106  end if
1107 
1108  ! Cell to the right:
1109  if (i + 1 <= iend) then
1110  bcdata(mm)%delta(i + 1, j) = max(bcdata(mm)%delta(i + 1, j), deviation)
1111  end if
1112 
1113  end do
1114  end do
1115  end if faminclude
1116  end do bocoloop
1117  end do
1118 
1119  ! Exchange so that halos get correct values set as well. THIS IS BROKEN FIX IT!
1120  !call exchangeSurfaceDelta(level, sps, commPatternCell_1st, internalCell_1st)
1121 
1122  ! Now make one pass back and compute a delta for the nodes. Of
1123  ! course, this technically makes no sense: The nodes should
1124  ! exactly
1125  ! using this as a surrogate for what is near a surface, it make a
1126  ! little sense. Essentially we go through the nodes, and take the
1127  ! max deviation from the cells surrpounding it.
1128 
1129  ! Loop over blocks
1130  do nn = 1, ndom
1131  call setpointers(nn, level, sps)
1132 
1133  bocoloop2: do mm = 1, nbocos
1134  faminclude2: if (faminlist(bcdata(mm)%famID, famlist)) then
1135 
1136  jbeg = bcdata(mm)%jnBeg; jend = bcdata(mm)%jnEnd
1137  ibeg = bcdata(mm)%inBeg; iend = bcdata(mm)%inEnd
1138 
1139  do j = jbeg, jend
1140  do i = ibeg, iend
1141 
1142  ! Since we are taking the max and the boundary halos
1143  ! have a value of -one it's ok to blindy just take
1144  ! the max from each of the 4 cells surrounding each node.
1145 
1146  bcdata(mm)%deltaNode(i, j) = max( &
1147  bcdata(mm)%delta(i, j), &
1148  bcdata(mm)%delta(i + 1, j), &
1149  bcdata(mm)%delta(i, j + 1), &
1150  bcdata(mm)%delta(i + 1, j + 1))
1151  end do
1152  end do
1153  end if faminclude2
1154  end do bocoloop2
1155  end do
1156 
1157  end subroutine surfacedeviation
1158 
1159  function checkdeviation(P0, P1, P2, P3)
1160 
1161  ! Find the maximum deviation between a local cubic approximation
1162  ! formed by nodes P0, P1, P2 and P3, with the linear approximation
1163  ! formed by nodes P1 and P2.
1164 
1165  ! See this article for the implementation.
1166  ! https://en.wikipedia.org/wiki/Centripetal_Catmull-Rom_spline
1167 
1168  use constants
1169  use utils, only: mynorm2
1170  implicit none
1171 
1172  ! Input Parameters
1173  real(kind=realtype), intent(in), dimension(3) :: p0, p1, p2, p3
1174 
1175  ! Function value
1176  real(kind=realtype) :: checkdeviation
1177 
1178  ! Working Parameters
1179  real(kind=realtype) :: t0, t1, t2, t3
1180  real(kind=realtype), dimension(3) :: a1, a2, a3, b1, b2
1181  real(kind=realtype), parameter :: alpha = half
1182  integer(kind=intType), parameter :: n = 20
1183  integer(kind=intType) :: i
1184  real(kind=realtype) :: t, p(3), q(3), s
1185 
1186  t0 = zero
1187  t1 = t0 + mynorm2(p1 - p0)**alpha
1188  t2 = t1 + mynorm2(p2 - p1)**alpha
1189  t3 = t2 + mynorm2(p3 - p2)**alpha
1190 
1191  ! Normalize
1192  t1 = t1 / t3
1193  t2 = t2 / t3
1194  t3 = one
1195 
1196  ! Loop over the number of points to check. We need to go between t2
1197  ! and t3. No need to check the first and last since the devaition
1198  ! there is zero by construction.
1200 
1201  do i = 1, n
1202  s = (i - one) / (n - one)
1203  t = (one - s) * t1 + s * t2
1204 
1205  ! Spline pt
1206  a3 = (t3 - t) / (t3 - t2) * p2 + (t - t2) / (t3 - t2) * p3
1207  a2 = (t2 - t) / (t2 - t1) * p1 + (t - t1) / (t2 - t1) * p2
1208  a1 = (t1 - t) / (t1 - t0) * p0 + (t - t0) / (t1 - t0) * p1
1209 
1210  b2 = (t3 - t) / (t3 - t1) * a2 + (t - t1) / (t3 - t1) * a3
1211  b1 = (t2 - t) / (t2 - t0) * a1 + (t - t0) / (t2 - t0) * a2
1212 
1213  p = (t2 - t) / (t2 - t1) * b1 + (t - t1) / (t2 - t1) * b2
1214 
1215  ! Now project the cubic point onto the line to get point Q
1216 
1217  q = p1 + dot_product(p - p1, p2 - p1) / dot_product(p2 - p1, p2 - p1) * (p2 - p1)
1218 
1219  ! Just get the distance between the two points.
1220  checkdeviation = max(checkdeviation, mynorm2(q - p))
1221 
1222  end do
1223 
1224  end function checkdeviation
1225 
1226  subroutine writewalls(famList)
1227 
1228  !use oversetData
1229  use constants
1230  use blockpointers
1231  use utils, only: setpointers, setbcpointers
1232  use bcpointers, only: xx
1233  use sorting, only: faminlist
1235  use utils, only: echk
1236  use commonformats, only: sci12
1237  implicit none
1238  integer(kind=intType), intent(in), dimension(:) :: famList
1239  character(80) :: fileName, zoneName
1240  integer(kind=intType) :: i, j, nn, iDom, iBeg, iEnd, jBeg, jEnd, mm, iDim
1241  integer(kind=intType) :: nNode, nCell, tag, ierr, ii, iProc, nLocalBoco, tmp(5)
1242  real(kind=realtype), dimension(:), allocatable :: xbuffer
1243  integer(kind=intType), dimension(:), allocatable :: iblankBuffer, bocosPerProc
1244  integer(kind=intType), dimension(:, :, :), allocatable :: faceInfo
1245  integer, dimension(mpi_status_size) :: mpiStatus
1246 
1247  ! Write a gathered surface tecplot file.
1248  if (myid == 0) then
1249  write (filename, "(a)") "zipper_wall.dat"
1250 
1251  open (unit=101, file=trim(filename), form='formatted')
1252  write (101, *) 'TITLE = "zipper walls"'
1253  write (101, *) 'Variables = "X", "Y", "Z", "CellIBlank"'
1254  end if
1255 
1256  ! Before we start, the root processor needs to know how many
1257  ! receives we can expect from each processor.
1258 
1259  nlocalboco = 0
1260  do nn = 1, ndom
1261  call setpointers(nn, 1, 1)
1262  do mm = 1, nbocos
1263  faminclude: if (faminlist(bcdata(mm)%famID, famlist)) then
1264  nlocalboco = nlocalboco + 1
1265  end if faminclude
1266  end do
1267  end do
1268 
1269  if (myid == 0) then
1270  allocate (bocosperproc(0:nproc - 1))
1271  end if
1272 
1273  call mpi_gather(nlocalboco, 1, adflow_integer, bocosperproc, 1, &
1274  adflow_integer, 0, adflow_comm_world, ierr)
1275  call echk(ierr, __file__, __line__)
1276  ! Now setup the info array on the root proc:
1277  if (myid == 0) then
1278  allocate (faceinfo(5, maxval(bocosperproc), 0:nproc - 1))
1279  end if
1280 
1281  if (myid /= 0) then
1282  tag = 0
1283  do nn = 1, ndom
1284  call setpointers(nn, 1, 1)
1285  do mm = 1, nbocos
1286  jbeg = bcdata(mm)%jnBeg; jend = bcdata(mm)%jnEnd
1287  ibeg = bcdata(mm)%inBeg; iend = bcdata(mm)%inEnd
1288  faminclude2: if (faminlist(bcdata(mm)%famID, famlist)) then
1289  tag = tag + 1
1290  tmp = (/ibeg, iend, jbeg, jend, nbkglobal/)
1291  call mpi_send(tmp, 5, &
1292  adflow_integer, 0, tag, adflow_comm_world, ierr)
1293  call echk(ierr, __file__, __line__)
1294  end if faminclude2
1295  end do
1296  end do
1297  else
1298  ! Receive the size info:
1299  do iproc = 1, nproc - 1
1300  do tag = 1, bocosperproc(iproc)
1301  call mpi_recv(tmp, 5, adflow_integer, iproc, tag, &
1302  adflow_comm_world, mpistatus, ierr)
1303  call echk(ierr, __file__, __line__)
1304  faceinfo(:, tag, iproc) = tmp
1305  end do
1306  end do
1307  end if
1308 
1309  ! Need a barrier between the two sets of the comms just in case.
1310  call mpi_barrier(adflow_comm_world, ierr)
1311  call echk(ierr, __file__, __line__)
1312 
1313  tag = 0
1314  do nn = 1, ndom
1315  call setpointers(nn, 1, 1)
1316  do mm = 1, nbocos
1317  jbeg = bcdata(mm)%jnBeg; jend = bcdata(mm)%jnEnd
1318  ibeg = bcdata(mm)%inBeg; iend = bcdata(mm)%inEnd
1319  faminclude3: if (faminlist(bcdata(mm)%famID, famlist)) then
1320  call setbcpointers(mm, .true.)
1321 
1322  nnode = (iend - ibeg + 1) * (jend - jbeg + 1)
1323  ncell = (iend - ibeg + 1) * (jend - jbeg + 1)
1324  allocate (iblankbuffer(ncell), xbuffer(3 * nnode))
1325  ii = 0
1326  do j = jbeg + 1, jend
1327  do i = ibeg + 1, iend
1328  ii = ii + 1
1329  iblankbuffer(ii) = bcdata(mm)%iBlank(i, j)
1330  end do
1331  end do
1332 
1333  ii = 0
1334  do idim = 1, 3
1335  do j = jbeg, jend
1336  do i = ibeg, iend
1337  ii = ii + 1
1338  xbuffer(ii) = xx(i + 1, j + 1, idim)
1339  end do
1340  end do
1341  end do
1342 
1343  if (myid == 0) then
1344  ! We can write it directly:
1345  call writezone(ibeg, iend, jbeg, jend, nbkglobal, xbuffer, iblankbuffer)
1346  else
1347 
1348  tag = tag + 1
1349  call mpi_send(iblankbuffer, ncell, adflow_integer, 0, tag, &
1350  adflow_comm_world, ierr)
1351  call echk(ierr, __file__, __line__)
1352 
1353  tag = tag + 1
1354  call mpi_send(xbuffer, nnode * 3, adflow_real, 0, tag, &
1355  adflow_comm_world, ierr)
1356  call echk(ierr, __file__, __line__)
1357 
1358  end if
1359  deallocate (iblankbuffer, xbuffer)
1360  end if faminclude3
1361  end do
1362  end do
1363 
1364  ! Complete the receives and writes on the root proc:
1365  if (myid == 0) then
1366  ! Receive the nodes and iblank info
1367  do iproc = 1, nproc - 1
1368  do tag = 1, bocosperproc(iproc)
1369 
1370  ibeg = faceinfo(1, tag, iproc)
1371  iend = faceinfo(2, tag, iproc)
1372 
1373  jbeg = faceinfo(3, tag, iproc)
1374  jend = faceinfo(4, tag, iproc)
1375 
1376  nbkglobal = faceinfo(5, tag, iproc)
1377 
1378  nnode = (iend - ibeg + 1) * (jend - jbeg + 1)
1379  ncell = (iend - ibeg + 1) * (jend - jbeg + 1)
1380 
1381  allocate (iblankbuffer(ncell), xbuffer(3 * nnode))
1382 
1383  call mpi_recv(iblankbuffer, ncell, adflow_integer, iproc, (2 * tag - 1), &
1384  adflow_comm_world, mpistatus, ierr)
1385  call echk(ierr, __file__, __line__)
1386 
1387  call mpi_recv(xbuffer, nnode * 3, adflow_real, iproc, 2 * tag, &
1388  adflow_comm_world, mpistatus, ierr)
1389  call echk(ierr, __file__, __line__)
1390 
1391  call writezone(ibeg, iend, jbeg, jend, nbkglobal, xbuffer, iblankbuffer)
1392 
1393  deallocate (iblankbuffer, xbuffer)
1394 
1395  end do
1396  end do
1397  deallocate (faceinfo, bocosperproc)
1398  close (101)
1399  end if
1400 
1401  contains
1402 
1403  subroutine writezone(iBeg, iEnd, jBeg, jEnd, nBkGlobal, xx, iblank)
1404 
1405  implicit none
1406  ! Input
1407  integer(kind=intType), intent(in) :: iBeg, iEnd, jBeg, jEnd, nBkGlobal
1408  real(kind=realtype), intent(in), dimension(:) :: xx
1409  integer(kind=intType), intent(in), dimension(:) :: iblank
1410 
1411  character(80) :: zoneName
1412  integer(kind=intType) :: iDim
1413  character(len=maxStringLen) :: zoneFormat
1414 
1415  write (zonename, "(a,I5.5)") "Zone_", nbkglobal
1416  zoneformat = "(3(A), I5, A, I5)"
1417  write (101, zoneformat) 'ZONE T=', trim(zonename), " I=", iend - ibeg + 1, " J=", jend - jbeg + 1
1418  write (101, *) "DATAPACKING=BLOCK, VARLOCATION=([1,2,3]=NODAL, [4]=CELLCENTERED)"
1419 
1420  ! The 3 is for the three coordinate directions
1421  nnode = (iend - ibeg + 1) * (jend - jbeg + 1)
1422  ncell = (iend - ibeg) * (jend - jbeg)
1423 
1424  do i = 1, 3 * nnode
1425  write (101, sci12) xx(i)
1426  end do
1427 
1428  do i = 1, ncell
1429  write (101, *) iblank(i)
1430  end do
1431  end subroutine writezone
1432  end subroutine writewalls
1433 
1434  subroutine bowtieandisolationelimination(famList, level, sps)
1435 
1436  use constants
1437  use blockpointers
1438  use communication
1439  use utils, only: setpointers
1441  use sorting, only: faminlist
1442  implicit none
1443 
1444  ! Input Parameters
1445  integer(kind=intType), intent(in), dimension(:) :: famList
1446  integer(kind=intType), intent(in) :: level, sps
1447 
1448  ! Local variables
1449  integer(kind=intType) :: mm, nn, i, j, k, e, iBeg, iEnd, jBeg, jEnd
1450  logical :: side(4)
1451 
1452  integer(kind=intType), dimension(:, :), pointer :: ibp, gcp
1453  integer(kind=intType), dimension(:, :), allocatable :: toFlip, nE, nC
1454 
1455  ! This routine initializes the surface cell iblank based on the
1456  ! volume iblank. It is not a straight copy since we a little
1457  ! preprocessing to eliminate a few particularly nasty cases.
1458  ! Three analysis are performed:
1459  ! 1. Bow-tie elimination
1460  ! 2. Single cell elmination
1461 
1462  bowtieloop: do e = 0, 2
1463  domainloop1: do nn = 1, ndom
1464  call setpointers(nn, level, sps)
1465 
1466  bocoloop1: do mm = 1, nbocos
1467  faminclude1: if (faminlist(bcdata(mm)%famID, famlist)) then
1468 
1469  select case (bcfaceid(mm))
1470  case (imin)
1471  ibp => iblank(2, :, :)
1472  gcp => globalcell(2, :, :)
1473  case (imax)
1474  ibp => iblank(il, :, :)
1475  gcp => globalcell(il, :, :)
1476  case (jmin)
1477  ibp => iblank(:, 2, :)
1478  gcp => globalcell(:, 2, :)
1479  case (jmax)
1480  ibp => iblank(:, jl, :)
1481  gcp => globalcell(:, jl, :)
1482  case (kmin)
1483  ibp => iblank(:, :, 2)
1484  gcp => globalcell(:, :, 2)
1485  case (kmax)
1486  ibp => iblank(:, :, kl)
1487  gcp => globalcell(:, :, kl)
1488  end select
1489 
1490  ! -------------------------------------------------
1491  ! Step 2: Bow-tie elimination: Elimiate cells
1492  ! that touch only at a corner.
1493  ! -------------------------------------------------
1494 
1495  ! Make bounds a little easier to read. Owned cells only
1496  ! from now on.
1497  jbeg = bcdata(mm)%jnBeg + 1; jend = bcdata(mm)%jnEnd
1498  ibeg = bcdata(mm)%inBeg + 1; iend = bcdata(mm)%inEnd
1499 
1500  ! Allocate two tmporary auxilary arrays 'eN'->
1501  ! edgeNeighbours and 'cN'-> cornerNeighbous. For every
1502  ! comupte cell determine the number of compute
1503  ! neighbours connected along edges and at corners
1504  allocate (ne(ibeg:iend, jbeg:jend), nc(ibeg:iend, jbeg:jend))!, &
1505 
1506  call findbowties()
1507 
1508  do j = jbeg, jend
1509  do i = ibeg, iend
1510  if (bcdata(mm)%iBlank(i, j) > 0 .and. nc(i, j) >= 1 .and. ne(i, j) <= e) then
1511  bcdata(mm)%iBlank(i, j) = 0
1512  end if
1513  end do
1514  end do
1515 
1516  deallocate (nc, ne)
1517  end if faminclude1
1518  end do bocoloop1
1519  end do domainloop1
1520 
1521  ! Since we potentially changed iBlanks, we need to updated by
1522  ! performing an exchange.
1523  call exchangesurfaceiblanks(famlist, level, sps, commpatterncell_2nd, internalcell_2nd)
1524 
1525  end do bowtieloop
1526 
1527  domainloop2: do nn = 1, ndom
1528  call setpointers(nn, level, sps)
1529 
1530  bocoloop2: do mm = 1, nbocos
1531  faminclude2: if (faminlist(bcdata(mm)%famID, famlist)) then
1532 
1533  select case (bcfaceid(mm))
1534  case (imin)
1535  gcp => globalcell(2, :, :)
1536  case (imax)
1537  gcp => globalcell(il, :, :)
1538  case (jmin)
1539  gcp => globalcell(:, 2, :)
1540  case (jmax)
1541  gcp => globalcell(:, jl, :)
1542  case (kmin)
1543  gcp => globalcell(:, :, 2)
1544  case (kmax)
1545  gcp => globalcell(:, :, kl)
1546  end select
1547 
1548  ! Make bounds a little easier to read. Owned cells only
1549  ! from now on.
1550  jbeg = bcdata(mm)%jnBeg + 1; jend = bcdata(mm)%jnEnd
1551  ibeg = bcdata(mm)%inBeg + 1; iend = bcdata(mm)%inEnd
1552 
1553  ! -------------------------------------------------
1554  ! Step 3: Single-cell elimination: Elimiate cells
1555  ! that do not touch any other cells.
1556  ! -------------------------------------------------
1557 
1558  allocate (ne(ibeg:iend, jbeg:jend), nc(ibeg:iend, jbeg:jend))
1559 
1560  call setneighbourcounts()
1561 
1562  ! This is easy, if a compute cell is stil around with no
1563  ! neighbours, kill it
1564  do j = jbeg, jend
1565  do i = ibeg, iend
1566  if (bcdata(mm)%iBlank(i, j) == 1 .and. ne(i, j) == 0 .and. nc(i, j) == 0) then
1567  bcdata(mm)%iBlank(i, j) = 0
1568  end if
1569  end do
1570  end do
1571 
1572  deallocate (ne, nc)
1573 
1574  end if faminclude2
1575  end do bocoloop2
1576  end do domainloop2
1577 
1578  ! Again, since we potentially changed iBlanks, we need to updated by
1579  ! performing an exchange.
1580  call exchangesurfaceiblanks(famlist, level, sps, commpatterncell_2nd, internalcell_2nd)
1581  contains
1582  subroutine findbowties
1583 
1584  implicit none
1585  ! For every compute determine the number of compute neighbours
1586  ! connected along edges (nE) and the number of bow-ties (in nC)
1587 
1588  integer(kind=intType) :: i, j, e(4)
1589 
1590  ne = 0
1591  nc = 0
1592 
1593  do j = jbeg, jend
1594  do i = ibeg, iend
1595  if (bcdata(mm)%iBlank(i, j) >= 0) then
1596  e = 0
1597 
1598  ! | e3 |
1599  ! --c4-----c3-
1600  ! | |
1601  ! e4 | x | e2
1602  ! | |
1603  ! -- c1-----c2-
1604  ! | e1 |
1605 
1606  ! Set the status of each of the 4 edges:
1607 
1608  if (bcdata(mm)%iBlank(i, j - 1) == 1) &
1609  e(1) = 1
1610 
1611  if (bcdata(mm)%iBlank(i + 1, j) == 1) &
1612  e(2) = 1
1613 
1614  if (bcdata(mm)%iBlank(i, j + 1) == 1) &
1615  e(3) = 1
1616 
1617  if (bcdata(mm)%iBlank(i - 1, j) == 1) &
1618  e(4) = 1
1619 
1620  ! Check the 4 corner neighbours for bow-tie status
1621  if (bcdata(mm)%iBlank(i - 1, j - 1) == 1 .and. e(4) == 0 .and. e(1) == 0) &
1622  nc(i, j) = nc(i, j) + 1
1623 
1624  if (bcdata(mm)%iBlank(i + 1, j - 1) == 1 .and. e(1) == 0 .and. e(2) == 0) &
1625  nc(i, j) = nc(i, j) + 1
1626 
1627  if (bcdata(mm)%iBlank(i + 1, j + 1) == 1 .and. e(2) == 0 .and. e(3) == 0) &
1628  nc(i, j) = nc(i, j) + 1
1629 
1630  if (bcdata(mm)%iBlank(i - 1, j + 1) == 1 .and. e(3) == 0 .and. e(4) == 0) &
1631  nc(i, j) = nc(i, j) + 1
1632 
1633  ne(i, j) = sum(e)
1634  end if
1635  end do
1636  end do
1637  end subroutine findbowties
1638 
1640 
1641  implicit none
1642  ! For every comute determine the number of compute neighbours
1643  ! connected along edges and at corners
1644 
1645  integer(kind=intType) :: i, j
1646 
1647  ne = 0
1648  nc = 0
1649 
1650  do j = jbeg, jend
1651  do i = ibeg, iend
1652  if (bcdata(mm)%iBlank(i, j) == 1) then
1653 
1654  ! | e3 |
1655  ! --c4-----c3-
1656  ! | |
1657  ! e4 | x | e2
1658  ! | |
1659  ! -- c1-----c2-
1660  ! | e1 |
1661 
1662  ! Set the status of each of the 4 edges:
1663 
1664  if (bcdata(mm)%iBlank(i, j - 1) == 1) &
1665  ne(i, j) = ne(i, j) + 1
1666 
1667  if (bcdata(mm)%iBlank(i + 1, j) == 1) &
1668  ne(i, j) = ne(i, j) + 1
1669 
1670  if (bcdata(mm)%iBlank(i, j + 1) == 1) &
1671  ne(i, j) = ne(i, j) + 1
1672 
1673  if (bcdata(mm)%iBlank(i - 1, j) == 1) &
1674  ne(i, j) = ne(i, j) + 1
1675 
1676  ! Check the 4 corner neighbours for compute neighbour
1677  if (bcdata(mm)%iBlank(i - 1, j - 1) == 1) &
1678  nc(i, j) = nc(i, j) + 1
1679 
1680  if (bcdata(mm)%iBlank(i + 1, j - 1) == 1) &
1681  nc(i, j) = nc(i, j) + 1
1682 
1683  if (bcdata(mm)%iBlank(i + 1, j + 1) == 1) &
1684  nc(i, j) = nc(i, j) + 1
1685 
1686  if (bcdata(mm)%iBlank(i - 1, j + 1) == 1) &
1687  nc(i, j) = nc(i, j) + 1
1688  end if
1689  end do
1690  end do
1691  end subroutine setneighbourcounts
1692  end subroutine bowtieandisolationelimination
1693 end module zippermesh
integer(kind=inttype), dimension(maxlevels) nnodeslocal
Definition: ADjointVars.F90:39
Definition: BCData.F90:1
real(kind=realtype), dimension(:, :, :), pointer xx
Definition: BCPointers.F90:16
integer(kind=inttype) jl
integer(kind=inttype), dimension(:, :, :), pointer iblank
integer(kind=inttype), dimension(:, :, :), pointer globalcell
integer(kind=inttype) nbkglobal
integer(kind=inttype), dimension(:), pointer bcfaceid
integer(kind=inttype) nbocos
integer(kind=inttype), dimension(:), pointer bctype
integer(kind=inttype), dimension(:, :, :), pointer forcedrecv
real(kind=realtype), dimension(:, :, :, :), pointer x
integer(kind=inttype) kl
integer(kind=inttype) il
character(len=maxstringlen) sci12
integer, dimension(:), allocatable recvrequests
type(internalcommtype), dimension(:), allocatable internalcell_2nd
integer, dimension(:), allocatable sendrequests
type(commtype), dimension(:), allocatable commpatterncell_2nd
integer adflow_comm_world
real(kind=realtype), parameter zero
Definition: constants.F90:71
integer(kind=inttype), parameter imax
Definition: constants.F90:293
integer(kind=inttype), parameter kmin
Definition: constants.F90:296
integer(kind=inttype), parameter jmax
Definition: constants.F90:295
integer(kind=inttype), parameter nfamexchange
Definition: constants.F90:317
real(kind=realtype), parameter one
Definition: constants.F90:72
real(kind=realtype), parameter half
Definition: constants.F90:80
integer(kind=inttype), parameter imin
Definition: constants.F90:292
integer(kind=inttype), parameter kmax
Definition: constants.F90:297
integer(kind=inttype), parameter jmin
Definition: constants.F90:294
subroutine makegapboundarystrings(zipperFamList, level, sps, master)
logical debugzipper
Definition: inputParam.F90:886
logical usezippermesh
Definition: inputParam.F90:892
type(kdtree2) function, pointer, public kdtree2_create(input_data, dim, sort, rearrange)
Definition: kd_tree.f90:588
subroutine getosurfcommpattern(oMat, oMatT, sendList, nSend, recvList, nRecv, rBufSize)
subroutine recvosurf(oWall, iDom, iProc, tagOffset, iSize, rSize, recvCount, recvInfo)
subroutine sendosurf(oWall, iDom, iProc, tagOffset, sendCount)
subroutine exchangesurfaceiblanks(zipperFamList, level, sps, commPattern, internal)
real(kind=realtype), dimension(:), allocatable clusterareas
Definition: overset.F90:368
integer(kind=inttype) nclusters
Definition: overset.F90:366
type(zippermesh), dimension(nfamexchange), target zippermeshes
Definition: overset.F90:376
integer(kind=inttype) ndomtotal
Definition: overset.F90:365
integer(kind=inttype), dimension(:), allocatable cumdomproc
Definition: overset.F90:364
integer(kind=inttype), dimension(:), allocatable clusters
Definition: overset.F90:367
logical oversetpresent
Definition: overset.F90:373
type(csrmatrix), dimension(:, :), allocatable, target overlapmatrix
Definition: overset.F90:361
subroutine initializeosurf(famList, oSurf, dualMesh, cluster)
subroutine getosurfbuffersizes(famList, il, jl, kl, iSize, rSize, dualMesh)
subroutine packosurf(famList, oSurf, dualMesh)
subroutine deallocateosurfs(oSurfs, n)
subroutine getworkarray(overlap, work)
subroutine transposeoverlap(A, B)
logical function faminlist(famID, famList)
Definition: sorting.F90:7
subroutine loadzipperdebug(fileName, str)
Definition: stringOps.F90:3008
subroutine writeoversettriangles(string, fileName, startTri, endTri)
Definition: stringOps.F90:2938
subroutine stringmatch(strings, nStrings, debugZipper)
Definition: stringOps.F90:2510
subroutine makepocketzip(p, strings, nStrings, pocketMaster, debugZipper)
Definition: stringOps.F90:1839
subroutine writeoversetstring(str, strings, n, fileID)
Definition: stringOps.F90:2798
subroutine writezipperdebug(str)
Definition: stringOps.F90:2974
subroutine makecrosszip(p, strings, nStrings, debugZipper)
Definition: stringOps.F90:1374
subroutine performselfzip(master, strings, nStrings, debugZipper)
Definition: stringOps.F90:206
subroutine writeoversetmaster(str, fileID)
Definition: stringOps.F90:2904
subroutine deallocatestring(string)
Definition: stringOps.F90:38
subroutine setstringpointers(string)
Definition: stringOps.F90:89
subroutine createorderedstrings(master, strings, nString)
Definition: stringOps.F90:105
type(bcgrouptype), dimension(nfamexchange) bcfamgroups
character(len=maxcgnsnamelen), dimension(:), allocatable famnames
type(familyexchange), dimension(:, :), allocatable, target bcfamexchange
Definition: utils.F90:1
subroutine cross_prod(a, b, c)
Definition: utils.F90:1721
subroutine setbcpointers(nn, spatialPointers)
Definition: utils.F90:882
real(kind=realtype) function mynorm2(x)
Definition: utils.F90:1697
subroutine echk(errorcode, file, line)
Definition: utils.F90:5722
subroutine setpointers(nn, mm, ll)
Definition: utils.F90:3237
subroutine wallsearch(aSurf, bSurf)
Definition: wallSearch.F90:4
real(kind=realtype) function checkdeviation(P0, P1, P2, P3)
subroutine checkzipper(fileName)
Definition: zipperMesh.F90:544
subroutine createzippermesh(zipperFamList, nZipFam)
Definition: zipperMesh.F90:19
subroutine writewalls(famList)
subroutine slitelimination(famList, level, sps)
Definition: zipperMesh.F90:837
subroutine surfacedeviation(famList, level, sps)
subroutine determineclusterareas(famList)
Definition: zipperMesh.F90:689
subroutine initbcdataiblank(level, sps)
Definition: zipperMesh.F90:776
subroutine zippercore(master, pocketMaster, debugZipper)
Definition: zipperMesh.F90:563
subroutine bowtieandisolationelimination(famList, level, sps)
subroutine findbowties
subroutine setneighbourcounts
integer(kind=inttype) function selectnodefamily(nodeFam)
Definition: zipperMesh.F90:501
subroutine writezone(iBeg, iEnd, jBeg, jEnd, nBkGlobal, xx, iblank)
logical function validcell(i, j)