ADflow  v1.0
ADflow is a finite volume RANS solver tailored for gradient-based aerodynamic design optimization.
wallDistance.F90
Go to the documentation of this file.
2 
3  use constants, only: inttype, realtype
5  implicit none
6  save
7 
8 #ifndef USE_TAPENADE
9  ! nquadVisc: Number of local quads on the viscous
10  ! bodies.
11  ! nNodeVisc: Number of local nodes on the viscous
12  ! bodies.
13  ! nquadViscGlob: Global number of viscous quads.
14  ! connVisc(4,nquadVisc): Connectivity of the local viscous
15  ! quads.
16  ! coorVisc(3,nNodeVisc): The coordinates of the local nodes.
17 
18  integer(kind=intType) :: nquadvisc, nnodevisc
19  integer(kind=intType) :: nquadviscglob
20 
21  integer(kind=intType), dimension(:, :), allocatable :: connvisc
22  real(kind=realtype), dimension(:, :), allocatable :: coorvisc
23 
24  ! rotMatrixSections(nSections,3,3): Rotation matrices needed
25  ! for the alignment of the
26  ! sections. The rotation
27  ! matrix is a**n, where a is
28  ! periodic transformation
29  ! matrix; n is an integer.
30 
31  real(kind=realtype), dimension(:, :, :), allocatable :: rotmatrixsections
32 #endif
33 
34 contains
35 
36  subroutine updatewalldistancesquickly(nn, level, sps)
37 
38  ! This is the actual update routine that uses xSurf. It is done on
39  ! block-level-sps basis. This is the used to update the wall
40  ! distance. Most importantly, this routine is included in the
41  ! reverse mode AD routines, but NOT the forward mode. Since it is
42  ! done on a per-block basis, it is assumed that the required block
43  ! pointers are already set.
44 
45  use constants
46  use blockpointers, only: nx, ny, nz, il, jl, kl, x, flowdoms, d2wall
47  implicit none
48 
49  ! Subroutine arguments
50  integer(kind=intType) :: nn, level, sps
51 
52  ! Local Variables
53  integer(kind=intType) :: i, j, k, ii, ind(4)
54  real(kind=realtype) :: xp(3), xc(3), u, v
55 
56 #ifdef TAPENADE_REVERSE
57  !$AD II-LOOP
58  do ii = 0, nx * ny * nz - 1
59  i = mod(ii, nx) + 2
60  j = mod(ii / nx, ny) + 2
61  k = ii / (nx * ny) + 2
62 #else
63  do k = 2, kl
64  do j = 2, jl
65  do i = 2, il
66 #endif
67 
68  if (flowdoms(nn, level, sps)%surfNodeIndices(1, i, j, k) == 0) then
69  ! This node is too far away and has no
70  ! association. Set the distance to a large constant.
71  d2wall(i, j, k) = large
72  cycle
73  end if
74 
75  ! Extract elemID and u-v position for the association of
76  ! this cell:
77 
78  ind = flowdoms(nn, level, sps)%surfNodeIndices(:, i, j, k)
79  u = flowdoms(nn, level, sps)%uv(1, i, j, k)
80  v = flowdoms(nn, level, sps)%uv(2, i, j, k)
81 
82  ! Now we have the 4 corners, use bi-linear shape
83  ! functions o to get target: (CCW ordering remember!)
84 
85  xp(:) = &
86  (one - u) * (one - v) * xsurf(3 * (ind(1) - 1) + 1:3 * ind(1)) + &
87  (u) * (one - v) * xsurf(3 * (ind(2) - 1) + 1:3 * ind(2)) + &
88  (u) * (v) * xsurf(3 * (ind(3) - 1) + 1:3 * ind(3)) + &
89  (one - u) * (v) * xsurf(3 * (ind(4) - 1) + 1:3 * ind(4))
90 
91  ! Get the cell center
92  xc(1) = eighth * (x(i - 1, j - 1, k - 1, 1) + x(i, j - 1, k - 1, 1) &
93  + x(i - 1, j, k - 1, 1) + x(i, j, k - 1, 1) &
94  + x(i - 1, j - 1, k, 1) + x(i, j - 1, k, 1) &
95  + x(i - 1, j, k, 1) + x(i, j, k, 1))
96 
97  xc(2) = eighth * (x(i - 1, j - 1, k - 1, 2) + x(i, j - 1, k - 1, 2) &
98  + x(i - 1, j, k - 1, 2) + x(i, j, k - 1, 2) &
99  + x(i - 1, j - 1, k, 2) + x(i, j - 1, k, 2) &
100  + x(i - 1, j, k, 2) + x(i, j, k, 2))
101 
102  xc(3) = eighth * (x(i - 1, j - 1, k - 1, 3) + x(i, j - 1, k - 1, 3) &
103  + x(i - 1, j, k - 1, 3) + x(i, j, k - 1, 3) &
104  + x(i - 1, j - 1, k, 3) + x(i, j - 1, k, 3) &
105  + x(i - 1, j, k, 3) + x(i, j, k, 3))
106 
107  ! Now we have the two points...just take the norm of the
108  ! distance between them
109 
110  d2wall(i, j, k) = sqrt( &
111  (xc(1) - xp(1))**2 + (xc(2) - xp(2))**2 + (xc(3) - xp(3))**2)
112 #ifdef TAPENADE_REVERSE
113  end do
114 #else
115  end do
116  end do
117  end do
118 #endif
119 
120  end subroutine updatewalldistancesquickly
121 
122  ! ----------------------------------------------------------------------
123  ! |
124  ! No Tapenade Routine below this line |
125  ! |
126  ! ----------------------------------------------------------------------
127 
128 #ifndef USE_TAPENADE
129  subroutine computewalldistance(level, allocMem)
130  !
131  ! wallDistance computes the distances of the cell centers to
132  ! the nearest viscous wall. An adt type of method is used, which
133  ! guarantees to find the minimum distance to the wall. Possible
134  ! periodic transformations are taken into account, such that
135  ! also in case of a periodic problem the correct distance is
136  ! computed; the nearest wall point may lie in a periodic domain.
137  use constants
138  use blockpointers, only: ndom
144  use oversetdata, only: oversetpresent
145  use utils, only: setpointers, echk, terminate, &
147  implicit none
148  !
149  ! Subroutine arguments.
150  !
151  integer(kind=intType), intent(in) :: level
152  logical, intent(in) :: allocMem
153  !
154  ! Local variables.
155  !
156  integer :: ierr, i, j, k, nn, ii, l
157 
158  integer(kind=intType) :: sps, sps2, ll, nLevels
159  logical :: tempLogical
160  real(kind=alwaysrealtype) :: t0
161  character(len=3) :: integerString
162 
163  ! Check if the RANS equations are solved. If not, the wall
164  ! distance is not needed and a return can be made.
165 
166  if (equations /= ransequations) return
167 
168  ! If the turbulence model is wall distance free just compute the
169  ! normal spacing of the first cell and store this in the wall
170  ! distance. It may be needed for the boundary conditions and
171  ! the monitoring of the y+. Return afterwards.
172 
173  if (.not. walldistanceneeded) then
174 
175  ! Loop over the number of spectral solutions, initialize the
176  ! distance and compute the initial normal spacing.
177 
178  do sps = 1, ntimeintervalsspectral
179  call initwalldistance(level, sps, allocmem)
180  call computenormalspacing(level, sps)
181  end do
182 
183  ! And return.
184 
185  return
186  end if
187 
188  ! Write a message to stdout to indicate that the wall distance
189  ! computation starts for the given level.
190 
191  write (integerstring, "(i2)") level
192  integerstring = adjustl(integerstring)
193 
194  ! Store the start time.
195 
196  t0 = mpi_wtime()
197 
198  ! Release temporarily some memory such that the overall memory
199  ! requirement is not dictated by this routine. What memory is
200  ! released depends on allocMem. If this is .True. It means that
201  ! this is the first time the wall distances are computed, i.e. in
202  ! the preprocessing phase. Then only send and receive buffers are
203  ! released. If allocMem is .False., this means that the wall
204  ! distances are computed in a moving mesh computation and
205  ! consequently some more memory can be released.
206 
207  if (allocmem) then
208  deallocate (sendbuffer, recvbuffer, stat=ierr)
209  if (ierr /= 0) &
210  call terminate("wallDistance", &
211  "Deallocation error for communication buffers")
212  else
213  call deallocatetempmemory(.true.)
214  end if
215 
216  ! There are two different searches we can do: the original code
217  ! always works and it capable to dealing with rotating/periodic
218  ! geometries. It uses constant memory and is slow. The
219  ! alternative method uses memory that scales with the size of
220  ! the surface grid per processor and only works for
221  ! steady/unsteady simulations without periodic/rotating
222  ! components. But it is fast. It is designed to be used for
223  ! updating the wall distances between iterations of
224  ! aerostructural solutions.
225 
226  ! Normal, original wall distance calc. Cannot be used when
227  ! overset is present due to possibility of overlapping walls.
228  if ((.not. useapproxwalldistance) .and. (.not. oversetpresent)) then
229  ! Loop over the number of spectral solutions.
230  spectralloop: do sps = 1, ntimeintervalsspectral
231 
232  ! Initialize the wall distances.
233 
234  call initwalldistance(level, sps, allocmem)
235 
236  ! Build the viscous surface mesh.
237 
238  call viscoussurfacemesh(level, sps)
239 
240  ! If there are no viscous faces, processor 0 prints a warning
241  ! and the wall distances are not computed.
242 
243  if (nquadviscglob == 0) then
244 
245  if (myid == 0) then
246  print "(a)", "#"
247  print "(a)", "# Warning!!!!"
248  print "(a)", "# No viscous boundary found. Wall &
249  &distances are set to infinity"
250  print "(a)", "#"
251  end if
252 
253  else
254  ! Determine the wall distances for the owned cells.
255  call determinedistance(level, sps)
256  end if
257  end do spectralloop
258  else ! The user wants to use approx wall distance calcs OR we
259  ! have overset mesh. :
260 
261  if (updatelevelwallassociation(level)) then
262 
263  ! Initialize the wall distance
264  spectralloop2: do sps = 1, ntimeintervalsspectral
265  call initwalldistance(level, sps, allocmem)
266  end do spectralloop2
267 
268  ! Destroy the PETSc wall distance data if necessary
269  call destroywalldistancedatalevel(level)
270 
271  ! Do the associtaion. This allocates the data destroyed
272  ! in the destroyWallDistanceData call
273 
274  do sps = 1, ntimeintervalsspectral
275  call determinewallassociation(level, sps)
276  end do
277 
278  if (.not. updatewallassociations) then
279  ! don't re-compute the wall associations after the first call
280  updatelevelwallassociation(level) = .false.
281  end if
282  end if
283 
284  ! Update the xsurf vector from X
285  call updatexsurf(level)
286 
287  ! Call the actual update routine, on each of the sps instances and blocks
288  do sps = 1, ntimeintervalsspectral
289 
290  ! Now extract the vector of the surface data we need
291  call vecgetarrayf90(xsurfvec(level, sps), xsurf, ierr)
292  call echk(ierr, __file__, __line__)
293 
294  do nn = 1, ndom
295  call setpointers(nn, level, sps)
296  call updatewalldistancesquickly(nn, level, sps)
297  end do
298 
299  call vecrestorearrayf90(xsurfvec(level, sps), xsurf, ierr)
300  call echk(ierr, __file__, __line__)
301 
302  end do
303  end if
304 
305  ! Allocate the temporarily released memory again. For more info
306  ! see the comments at the beginning of this routine.
307 
308  if (allocmem) then
309  allocate (sendbuffer(sendbuffersize), &
310  recvbuffer(recvbuffersize), stat=ierr)
311  if (ierr /= 0) &
312  call terminate("wallDistance", &
313  "Memory allocation failure for comm buffers")
314  else
315  call allocatetempmemory(.true.)
316  end if
317 
318  ! Synchronize the processors.
319 
320  call mpi_barrier(adflow_comm_world, ierr)
321 
322  ! Write a message to stdout with the amount of time it
323  ! took to compute the distances.
324 
325  ! if(myID == 0) then
326  ! print "(*(A))", "# End wall distances level ", trim(integerString)
327  ! print "(*(A, ES12.5))", "# Wall clock time:", mpi_wtime() - t0," sec."
328  ! print "(a)", "#"
329  ! endif
330 
331  end subroutine computewalldistance
332 
333  subroutine computenormalspacing(level, sps)
334  !
335  ! computeNormalSpacing computes the normal spacing of the first
336  ! cell center from the viscous wall for the given multigrid
337  ! level and spectral solution. This routine is called for
338  ! turbulence models, which do not need the wall distance.
339  ! However, they do need info of the first normal spacing for the
340  ! monitoring of y+ and possibly for the boundary conditions.
341  ! This is computed in this routine.
342  !
343  use constants
344  use blockpointers, only: x, d2wall, nviscbocos, bcfaceid, bcdata, &
345  nx, ny, nz, il, jl, kl, ndom
346  use utils, only: setpointers
347  implicit none
348  !
349  ! Subroutine arguments.
350  !
351  integer(kind=intType), intent(in) :: level, sps
352  !
353  ! Local variables.
354  !
355  integer(kind=intType) :: nn, mm, i, j
356  integer(kind=intType) :: iBeg, iEnd, jBeg, jEnd
357 
358  real(kind=realtype) :: nnx, nny, nnz, vecx, vecy, vecz, dot
359 
360  real(kind=realtype), dimension(:, :, :), pointer :: xface, xint
361  real(kind=realtype), dimension(:, :), pointer :: dd2wall
362 
363  ! Loop over the domains.
364 
365  domain: do nn = 1, ndom
366 
367  ! Set the pointers for this block.
368 
369  call setpointers(nn, level, sps)
370 
371  ! Loop over the viscous subfaces of this block. Note that
372  ! these are numbered first.
373 
374  bocos: do mm = 1, nviscbocos
375 
376  ! Set the pointers for the plane on the surface, one plane
377  ! into the computational domain and the wall distance.
378  ! This depends on the block face on which the subface is
379  ! located. Note that the starting index of d2Wall is 2 and
380  ! therefore a pointer offset will be needed later on.
381 
382  select case (bcfaceid(mm))
383 
384  case (imin)
385  xface => x(1, 1:, 1:, :); xint => x(2, 1:, 1:, :)
386  dd2wall => d2wall(2, :, :)
387 
388  case (imax)
389  xface => x(il, 1:, 1:, :); xint => x(nx, 1:, 1:, :)
390  dd2wall => d2wall(il, :, :)
391 
392  case (jmin)
393  xface => x(1:, 1, 1:, :); xint => x(1:, 2, 1:, :)
394  dd2wall => d2wall(:, 2, :)
395 
396  case (jmax)
397  xface => x(1:, jl, 1:, :); xint => x(1:, ny, 1:, :)
398  dd2wall => d2wall(:, jl, :)
399 
400  case (kmin)
401  xface => x(1:, 1:, 1, :); xint => x(1:, 1:, 2, :)
402  dd2wall => d2wall(:, :, 2)
403 
404  case (kmax)
405  xface => x(1:, 1:, kl, :); xint => x(1:, 1:, nz, :)
406  dd2wall => d2wall(:, :, kl)
407 
408  end select
409 
410  ! Store the face range of this subface a bit easier.
411 
412  jbeg = bcdata(mm)%jnBeg + 1; jend = bcdata(mm)%jnEnd
413  ibeg = bcdata(mm)%inBeg + 1; iend = bcdata(mm)%inEnd
414 
415  ! Loop over the faces of the subfaces.
416 
417  do j = jbeg, jend
418  do i = ibeg, iend
419 
420  ! Store the three components of the unit normal a
421  ! bit easier.
422 
423  nnx = bcdata(mm)%norm(i, j, 1)
424  nny = bcdata(mm)%norm(i, j, 2)
425  nnz = bcdata(mm)%norm(i, j, 3)
426 
427  ! Compute the vector from centroid of the adjacent cell
428  ! to the centroid of the face.
429 
430  vecx = eighth * (xface(i - 1, j - 1, 1) + xface(i - 1, j, 1) &
431  + xface(i, j - 1, 1) + xface(i, j, 1) &
432  - xint(i - 1, j - 1, 1) - xint(i - 1, j, 1) &
433  - xint(i, j - 1, 1) - xint(i, j, 1))
434 
435  vecy = eighth * (xface(i - 1, j - 1, 2) + xface(i - 1, j, 2) &
436  + xface(i, j - 1, 2) + xface(i, j, 2) &
437  - xint(i - 1, j - 1, 2) - xint(i - 1, j, 2) &
438  - xint(i, j - 1, 2) - xint(i, j, 2))
439 
440  vecz = eighth * (xface(i - 1, j - 1, 3) + xface(i - 1, j, 3) &
441  + xface(i, j - 1, 3) + xface(i, j, 3) &
442  - xint(i - 1, j - 1, 3) - xint(i - 1, j, 3) &
443  - xint(i, j - 1, 3) - xint(i, j, 3))
444 
445  ! Compute the projection of this vector onto the normal
446  ! vector of the face. For a decent mesh there will not be
447  ! much of a difference between the projection and the
448  ! original mesh, but it does not hurt to do it.
449 
450  dot = nnx * vecx + nny * vecy + nnz * vecz
451 
452  ! As (nnx,nny,nnz) is a unit vector the distance to the
453  ! wall of the first cell center is given by the absolute
454  ! value of dot. Due to the use of pointers and the fact
455  ! that the original d2Wall array starts at 2 and offset
456  ! of -1 must be used to store the data at the correct
457  ! location.
458 
459  dd2wall(i - 1, j - 1) = abs(dot)
460 
461  end do
462  end do
463 
464  end do bocos
465 
466  end do domain
467 
468  end subroutine computenormalspacing
469 
470  subroutine initwalldistance(level, sps, allocMem)
471  !
472  ! initWallDistance allocates the memory for the wall distance,
473  ! if needed, and initializes the wall distance to a large value.
474  !
475  use constants
476  use blockpointers, only: ndom, flowdoms
477  use utils, only: terminate
478  implicit none
479  !
480  ! Subroutine arguments.
481  !
482  integer(kind=intType), intent(in) :: level, sps
483  logical, intent(in) :: allocMem
484  !
485  ! Local variables.
486  !
487  integer :: ierr
488 
489  integer(kind=intType) :: nn, il, jl, kl
490 
491  ! Loop over the domains.
492 
493  domain: do nn = 1, ndom
494 
495  ! Allocate the memory for d2Wall, if desired.
496 
497  if (allocmem) then
498 
499  il = flowdoms(nn, level, sps)%il
500  jl = flowdoms(nn, level, sps)%jl
501  kl = flowdoms(nn, level, sps)%kl
502 
503  allocate (flowdoms(nn, level, sps)%d2Wall(2:il, 2:jl, 2:kl), &
504  stat=ierr)
505  if (ierr /= 0) &
506  call terminate("initWallDistance", &
507  "Memory allocation failure for d2Wall")
508  end if
509 
510  ! Initialize the wall distances to a large value.
511 
512  flowdoms(nn, level, sps)%d2Wall = large
513 
514  end do domain
515 
516  end subroutine initwalldistance
517 
518  subroutine determinedistance(level, sps)
519  !
520  ! determineDistance determines the distance from the center
521  ! of the cell to the nearest viscous wall for owned cells.
522  !
523  use constants
525  use blockpointers, only: x, flowdoms, kl, jl, il, ndom, nx, ny, nz, &
528  use section, only: sections
529  use inputphysics, only: walloffset
530  use utils, only: setpointers, terminate
531  implicit none
532  !
533  ! Subroutine arguments
534  !
535  integer(kind=intType), intent(in) :: level, sps
536  !
537  ! Local parameter, which defines the name of the adt to be create
538  ! and used here. Only needed because of the api of the adt library
539  !
540  character(len=10), parameter :: viscAdt = "ViscousADT"
541  !
542  ! Local variables.
543  !
544  integer :: ierr
545 
546  integer, dimension(:), allocatable :: procID
547 
548  integer(kind=intType) :: nCell, nCellPer, nTria
549 
550  integer(kind=intType), dimension(1, 1) :: connTria
551  real(kind=realtype), dimension(3, 2) :: dummy
552 
553  integer(kind=intType), dimension(:), allocatable :: elementID
554 
555  real(kind=realtype), dimension(:), allocatable :: dist2
556  real(kind=realtype), dimension(:), allocatable :: dist2per
557  real(kind=realtype), dimension(:, :), allocatable :: coor, uvw
558  real(kind=realtype), dimension(:, :), allocatable :: coorper
559 
560  integer(kind=adtElementType), dimension(:), allocatable :: &
561  elementType
562 
563  integer(kind=intType) :: nn, mm, ll, ii, jj, i, j, k
564 
565  real(kind=realtype), dimension(3) :: xc
566 
567  ! Build the adt of the surface grid. As the api requires to
568  ! specify both the quadrilateral and the triangular connectivity,
569  ! some dummy variables must be passed.
570 
571  ntria = 0
572  conntria = 0
573  dummy = zero
574 
575  call adtbuildsurfaceadt(ntria, nquadvisc, nnodevisc, &
576  coorvisc, conntria, connvisc, &
577  dummy, .false., adflow_comm_world, &
578  viscadt)
579 
580  ! Determine the number of cell centers for which the distance
581  ! must be computed. Also determine how many of them are periodic.
582 
583  ncell = 0
584  ncellper = 0
585 
586  do nn = 1, ndom
587  ll = flowdoms(nn, level, sps)%nx * flowdoms(nn, level, sps)%ny &
588  * flowdoms(nn, level, sps)%nz
589  ncell = ncell + ll
590 
591  mm = flowdoms(nn, level, sps)%sectionID
592  if (sections(mm)%periodic) ncellper = ncellper + ll
593  end do
594 
595  ! Allocate the memory for the arrays needed by the ADT.
596 
597  allocate (coor(3, ncell), procid(ncell), elementtype(ncell), &
598  elementid(ncell), uvw(3, ncell), dist2(ncell), &
599  coorper(3, ncellper), dist2per(ncellper), stat=ierr)
600  if (ierr /= 0) &
601  call terminate("determineDistance", &
602  "Memory allocation failure for the variables &
603  &needed by the adt.")
604  !
605  ! Step 1: The search of the original coordinates; possibly a
606  ! rotational periodic transformation is applied to align
607  ! the sections.
608  !
609  ! Loop over the domains to store the coordinates of the cell
610  ! centers of the owned cells. Apply the transformation such that
611  ! the sections are aligned.
612 
613  mm = 0
614  domains: do nn = 1, ndom
615 
616  ! Set the pointers for this block, store the section id a bit
617  ! easier in ll and loop over the cell centers.
618 
619  call setpointers(nn, level, sps)
620  ll = sectionid
621 
622  do k = 2, kl
623  do j = 2, jl
624  do i = 2, il
625 
626  ! Compute the coordinates of the cell center relative
627  ! to the rotation center of this section.
628 
629  xc(1) = eighth * (x(i - 1, j - 1, k - 1, 1) + x(i, j - 1, k - 1, 1) &
630  + x(i - 1, j, k - 1, 1) + x(i, j, k - 1, 1) &
631  + x(i - 1, j - 1, k, 1) + x(i, j - 1, k, 1) &
632  + x(i - 1, j, k, 1) + x(i, j, k, 1)) &
633  - sections(ll)%rotCenter(1)
634 
635  xc(2) = eighth * (x(i - 1, j - 1, k - 1, 2) + x(i, j - 1, k - 1, 2) &
636  + x(i - 1, j, k - 1, 2) + x(i, j, k - 1, 2) &
637  + x(i - 1, j - 1, k, 2) + x(i, j - 1, k, 2) &
638  + x(i - 1, j, k, 2) + x(i, j, k, 2)) &
639  - sections(ll)%rotCenter(2)
640 
641  xc(3) = eighth * (x(i - 1, j - 1, k - 1, 3) + x(i, j - 1, k - 1, 3) &
642  + x(i - 1, j, k - 1, 3) + x(i, j, k - 1, 3) &
643  + x(i - 1, j - 1, k, 3) + x(i, j - 1, k, 3) &
644  + x(i - 1, j, k, 3) + x(i, j, k, 3)) &
645  - sections(ll)%rotCenter(3)
646 
647  ! Apply the periodic transformation for this section to
648  ! align it with other sections and store this coordinate
649  ! in the appropriate place in coor.
650 
651  mm = mm + 1
652  coor(1, mm) = rotmatrixsections(ll, 1, 1) * xc(1) &
653  + rotmatrixsections(ll, 1, 2) * xc(2) &
654  + rotmatrixsections(ll, 1, 3) * xc(3) &
655  + sections(ll)%rotCenter(1)
656 
657  coor(2, mm) = rotmatrixsections(ll, 2, 1) * xc(1) &
658  + rotmatrixsections(ll, 2, 2) * xc(2) &
659  + rotmatrixsections(ll, 2, 3) * xc(3) &
660  + sections(ll)%rotCenter(2)
661 
662  coor(3, mm) = rotmatrixsections(ll, 3, 1) * xc(1) &
663  + rotmatrixsections(ll, 3, 2) * xc(2) &
664  + rotmatrixsections(ll, 3, 3) * xc(3) &
665  + sections(ll)%rotCenter(3)
666 
667  ! Initialize the distance squared, because this is an
668  ! inout argument in the call to adtMinDistanceSearch.
669 
670  dist2(mm) = d2wall(i, j, k)
671 
672  end do
673  end do
674  end do
675  end do domains
676 
677  ! Perform the search. As no no interpolations are required,
678  ! some dummies are passed.
679 
680  call adtmindistancesearch(ncell, coor, viscadt, &
681  procid, elementtype, elementid, &
682  uvw, dist2, 0_inttype, &
683  dummy, dummy)
684  ! if (myid == 0) then
685  ! print *,'procID = '
686  ! print *,procID
687  ! print *,'elemID:',elementID
688  ! end if
689 
690  !
691  ! Step 2: For periodic sections the nearest wall may be in the
692  ! periodic part of the grid that is not stored.
693  ! Therefore apply the periodic transformation to the
694  ! node and compute the minimum distance for this
695  ! coordinate.
696  !
697  ! Initialize the counters mm and ii. Mm is the counter for coor;
698  ! ii is the counter for coorPer.
699 
700  mm = 0
701  ii = 0
702 
703  ! Loop over the domains and find the periodic ones.
704 
705  domainsper1: do nn = 1, ndom
706  jj = flowdoms(nn, level, sps)%nx * flowdoms(nn, level, sps)%ny &
707  * flowdoms(nn, level, sps)%nz
708 
709  ll = flowdoms(nn, level, sps)%sectionID
710 
711  ! Check if the section is periodic.
712 
713  if (sections(ll)%periodic) then
714 
715  ! Loop over the corresponding entries in coor of this block
716  ! and apply the periodic transformation. The transformed
717  ! coordinates are stored in coorPer. Also initialize
718  ! the wall distance squared to the value just computed.
719 
720  do i = 1, jj
721  mm = mm + 1
722  ii = ii + 1
723 
724  xc(1) = coor(1, mm) - sections(ll)%rotCenter(1)
725  xc(2) = coor(2, mm) - sections(ll)%rotCenter(2)
726  xc(3) = coor(3, mm) - sections(ll)%rotCenter(3)
727 
728  coorper(1, ii) = sections(ll)%rotMatrix(1, 1) * xc(1) &
729  + sections(ll)%rotMatrix(1, 2) * xc(2) &
730  + sections(ll)%rotMatrix(1, 3) * xc(3) &
731  + sections(ll)%rotCenter(1) &
732  + sections(ll)%translation(1)
733 
734  coorper(2, ii) = sections(ll)%rotMatrix(2, 1) * xc(1) &
735  + sections(ll)%rotMatrix(2, 2) * xc(2) &
736  + sections(ll)%rotMatrix(2, 3) * xc(3) &
737  + sections(ll)%rotCenter(2) &
738  + sections(ll)%translation(2)
739 
740  coorper(3, ii) = sections(ll)%rotMatrix(3, 1) * xc(1) &
741  + sections(ll)%rotMatrix(3, 2) * xc(2) &
742  + sections(ll)%rotMatrix(3, 3) * xc(3) &
743  + sections(ll)%rotCenter(3) &
744  + sections(ll)%translation(3)
745 
746  dist2per(ii) = dist2(mm)
747  end do
748 
749  else
750  ! Section is not periodic. Update the counter mm.
751 
752  mm = mm + jj
753  end if
754 
755  end do domainsper1
756 
757  ! Perform the adt search of this set of periodic coordinates.
758  ! As no no interpolations are required, some dummies are passed.
759 
760  call adtmindistancesearch(ncellper, coorper, viscadt, &
761  procid, elementtype, elementid, &
762  uvw, dist2per, 0_inttype, &
763  dummy, dummy)
764  !
765  ! Step 3: Also apply the inverse periodic transformation.
766  !
767  ! Initialize the counters mm and ii. Mm is the counter for coor;
768  ! ii is the counter for coorPer.
769 
770  mm = 0
771  ii = 0
772 
773  ! Loop over the domains and find the periodic ones.
774 
775  domainsper2: do nn = 1, ndom
776  jj = flowdoms(nn, level, sps)%nx * flowdoms(nn, level, sps)%ny &
777  * flowdoms(nn, level, sps)%nz
778 
779  ll = flowdoms(nn, level, sps)%sectionID
780 
781  ! Check if the section is periodic.
782 
783  if (sections(ll)%periodic) then
784 
785  ! Loop over the corresponding entries in coor of this block
786  ! and apply the inverse periodic transformation. Again the
787  ! transformed coordinates are stored in coorPer. Note that
788  ! the inverse of the rotation matrix is the transpose and
789  ! that the translation vector should be multiplied by the
790  ! inverse of the rotation matrix. Note that the wall distance
791  ! has already been initialized in the previous periodic
792  ! search.
793 
794  do i = 1, jj
795  mm = mm + 1
796  ii = ii + 1
797 
798  xc(1) = coor(1, mm) - sections(ll)%rotCenter(1) &
799  - sections(ll)%translation(1)
800  xc(2) = coor(2, mm) - sections(ll)%rotCenter(2) &
801  - sections(ll)%translation(2)
802  xc(3) = coor(3, mm) - sections(ll)%rotCenter(3) &
803  - sections(ll)%translation(3)
804 
805  coorper(1, ii) = sections(ll)%rotMatrix(1, 1) * xc(1) &
806  + sections(ll)%rotMatrix(2, 1) * xc(2) &
807  + sections(ll)%rotMatrix(3, 1) * xc(3) &
808  + sections(ll)%rotCenter(1)
809 
810  coorper(2, ii) = sections(ll)%rotMatrix(1, 2) * xc(1) &
811  + sections(ll)%rotMatrix(2, 2) * xc(2) &
812  + sections(ll)%rotMatrix(3, 2) * xc(3) &
813  + sections(ll)%rotCenter(2)
814 
815  coorper(3, ii) = sections(ll)%rotMatrix(1, 3) * xc(1) &
816  + sections(ll)%rotMatrix(2, 3) * xc(2) &
817  + sections(ll)%rotMatrix(3, 3) * xc(3) &
818  + sections(ll)%rotCenter(3)
819  end do
820 
821  else
822  ! Section is not periodic. Update the counter mm.
823 
824  mm = mm + jj
825  end if
826 
827  end do domainsper2
828 
829  ! Perform the adt search of this set of periodic coordinates.
830  ! As no no interpolations are required, some dummies are passed.
831 
832  call adtmindistancesearch(ncellper, coorper, viscadt, &
833  procid, elementtype, elementid, &
834  uvw, dist2per, 0_inttype, &
835  dummy, dummy)
836  !
837  ! Step 4: Store the minimum distance in the block type.
838  !
839  mm = 0
840  ii = 0
841 
842  domainsstore: do nn = 1, ndom
843 
844  ! Set the pointers for this block and store the section id a
845  ! bit easier in ll.
846 
847  call setpointers(nn, level, sps)
848  ll = sectionid
849 
850  ! Check if the section is periodic. If so the distance is set
851  ! to the minimum of the value computed in step 1 and the
852  ! periodic values. Note that mm should not be updated in this
853  ! loop, because it is updated in the loop over the cell centers
854  ! of this block. Instead the counter j is used.
855 
856  if (sections(ll)%periodic) then
857 
858  jj = nx * ny * nz
859 
860  j = mm
861  do i = 1, jj
862  j = j + 1
863  ii = ii + 1
864 
865  dist2(j) = min(dist2(j), dist2per(ii))
866  end do
867  end if
868 
869  ! Loop over the cell centers of the block to store the wall
870  ! distance. Note that dist2 stores the distance squared and
871  ! thus a square root must be taken.
872  ! Add a possible offset for the debugging of wall functions.
873 
874  do k = 2, kl
875  do j = 2, jl
876  do i = 2, il
877  mm = mm + 1
878  d2wall(i, j, k) = sqrt(dist2(mm)) + walloffset
879  end do
880  end do
881  end do
882 
883  end do domainsstore
884 
885  ! Release the memory of the ADT and the arrays of the module
886  ! viscSurface.
887 
888  call adtdeallocateadts(viscadt)
889 
890  deallocate (connvisc, coorvisc, rotmatrixsections, stat=ierr)
891  if (ierr /= 0) &
892  call terminate("determineDistance", &
893  "Deallocation error for the arrays &
894  &of viscSurface")
895 
896  ! Release the variables needed by the ADT.
897 
898  deallocate (coor, procid, elementtype, elementid, uvw, dist2, &
899  coorper, dist2per, stat=ierr)
900  if (ierr /= 0) &
901  call terminate("determineDistance", &
902  "Deallocation failure for the variables &
903  &needed by the adt.")
904 
905  end subroutine determinedistance
906 
907  subroutine localviscoussurfacemesh(multSections, level, sps)
908  !
909  ! localViscousSurfaceMesh stores the local viscous surface
910  ! mesh (with possible periodic extensions in conn and coor.
911  !
912  use constants
913  use blockpointers, only: bcdata, x, il, jl, kl, bcfaceid, sectionid, &
914  flowdoms, nbocos, ndom, bctype
916  use section, only: sections, nsections
917  use utils, only: setpointers, terminate
918  implicit none
919  !
920  ! Subroutine arguments.
921  !
922  integer(kind=intType), intent(in) :: level, sps
923  integer(kind=intType), dimension(*), intent(in) :: multSections
924  !
925  ! Local variables.
926  !
927  integer :: size, ierr
928 
929  integer(kind=intType) :: nn, mm, i, j, k
930  integer(kind=intType) :: np, nq, npOld, np1, nqOld, mp, mq
931  integer(kind=intType) :: nq1, nq2, nq3, nq4, sec, row, col
932  integer(kind=intType) :: iBeg, jBeg, iEnd, jEnd
933 
934  integer(kind=intType), dimension(3) :: ind
935 
936  real(kind=realtype) :: length, dot, xx, yy, zz, r1, r2, aaa, bbb
937  real(kind=realtype) :: theta, costheta, sintheta
938 
939  real(kind=realtype), dimension(3, 3) :: a
940  real(kind=realtype), dimension(nSections) :: thetanmin, &
941  thetanmax, &
942  thetapmin, &
943  thetapmax, tmp
944  real(kind=realtype), dimension(nSections, 3) :: rad1, rad2, axis
945 
946  real(kind=realtype), dimension(:, :, :), pointer :: xface
947 
948  ! Determine the unit vectors of the local coordinate system
949  ! aligned with the rotation axis of the possible rotational
950  ! periodic section.
951  !
952  do nn = 1, nsections
953  if (sections(nn)%nSlices == 1) cycle
954 
955  ! Section is rotational periodic. First determine the rotation
956  ! axis. This is the eigenvector which corresponds to the
957  ! eigenvalue 1 of the transformation matrix.
958 
959  ! Store rot - i in a and initialize ind.
960 
961  ind(1) = 1; ind(2) = 2; ind(3) = 3
962 
963  a(1, 1) = sections(nn)%rotMatrix(1, 1) - one
964  a(1, 2) = sections(nn)%rotMatrix(1, 2)
965  a(1, 3) = sections(nn)%rotMatrix(1, 3)
966 
967  a(2, 1) = sections(nn)%rotMatrix(2, 1)
968  a(2, 2) = sections(nn)%rotMatrix(2, 2) - one
969  a(2, 3) = sections(nn)%rotMatrix(2, 3)
970 
971  a(3, 1) = sections(nn)%rotMatrix(3, 1)
972  a(3, 2) = sections(nn)%rotMatrix(3, 2)
973  a(3, 3) = sections(nn)%rotMatrix(3, 3) - one
974 
975  ! Loop over the two times that Gaussian elimination must be
976  ! applied.
977 
978  loopgauss: do k = 1, 2
979 
980  ! Find the largest value in the sub-matrix.
981 
982  aaa = abs(a(k, k)); row = k; col = k
983  do j = k, 3
984  do i = k, 3
985  bbb = abs(a(i, j))
986  if (bbb > aaa) then
987  aaa = bbb
988  row = i
989  col = j
990  end if
991  end do
992  end do
993 
994  ! Swap the rows k and row.
995 
996  do j = 1, 3
997  aaa = a(k, j)
998  a(k, j) = a(row, j)
999  a(row, j) = aaa
1000  end do
1001 
1002  ! Swap the colums k and col; also swap ind(k) and ind(col).
1003 
1004  i = ind(k)
1005  ind(k) = ind(col)
1006  ind(col) = i
1007  do i = 1, 3
1008  aaa = a(i, k)
1009  a(i, k) = a(i, col)
1010  a(i, col) = aaa
1011  end do
1012 
1013  ! Perform gaussian eliMination, because now it's sure that
1014  ! the element (k,k) is non-zero.
1015 
1016  aaa = one / a(k, k)
1017  do i = (k + 1), 3
1018  bbb = a(i, k) * aaa
1019  do j = k, 3
1020  a(i, j) = a(i, j) - bbb * a(k, j)
1021  end do
1022  end do
1023 
1024  end do loopgauss
1025 
1026  ! Due to the full pivoting it is now guaranteed that the elements
1027  ! a(ind(1),ind(1)) and a(ind(2),ind(2)) are nonzero and
1028  ! a(ind(3),ind(3)) == zero. Remember that the rotation matrix
1029  ! only has 1 eigenvalue of one. Set axis(ind(3)) to one and
1030  ! determine the other two elements of the eigen vector.
1031 
1032  axis(nn, ind(3)) = one
1033  axis(nn, ind(2)) = -(a(2, 3) * axis(nn, ind(3))) / a(2, 2)
1034  axis(nn, ind(1)) = -(a(1, 3) * axis(nn, ind(3)) &
1035  + a(1, 2) * axis(nn, ind(2))) / a(1, 1)
1036 
1037  ! Create a unit vector.
1038 
1039  length = one / sqrt(axis(nn, 1)**2 + axis(nn, 2)**2 + axis(nn, 3)**2)
1040  axis(nn, 1) = axis(nn, 1) * length
1041  axis(nn, 2) = axis(nn, 2) * length
1042  axis(nn, 3) = axis(nn, 3) * length
1043 
1044  ! Make sure that the largest component of this vector is
1045  ! positive, such that a unique definition of the rotation
1046  ! axis is obtained. Use dot and length as a temporary
1047  ! storage.
1048 
1049  dot = axis(nn, 1); length = abs(dot)
1050  if (abs(axis(nn, 2)) > length) then
1051  dot = axis(nn, 2); length = abs(dot)
1052  end if
1053  if (abs(axis(nn, 3)) > length) then
1054  dot = axis(nn, 3); length = abs(dot)
1055  end if
1056 
1057  if (dot < zero) then
1058  axis(nn, 1) = -axis(nn, 1)
1059  axis(nn, 2) = -axis(nn, 2)
1060  axis(nn, 3) = -axis(nn, 3)
1061  end if
1062 
1063  ! Determine the two vectors which determine the plane normal
1064  ! to the axis of rotation.
1065 
1066  ! Initial guess of rad1. First try the y-axis. If not good
1067  ! enough try the z-axis.
1068 
1069  if (abs(axis(nn, 2)) < 0.707107_realtype) then
1070  rad1(nn, 1) = zero
1071  rad1(nn, 2) = one
1072  rad1(nn, 3) = zero
1073  else
1074  rad1(nn, 1) = zero
1075  rad1(nn, 2) = zero
1076  rad1(nn, 3) = one
1077  end if
1078 
1079  ! Make sure that rad1 is normal to axis. Create a unit
1080  ! vector again.
1081 
1082  dot = rad1(nn, 1) * axis(nn, 1) + rad1(nn, 2) * axis(nn, 2) &
1083  + rad1(nn, 3) * axis(nn, 3)
1084  rad1(nn, 1) = rad1(nn, 1) - dot * axis(nn, 1)
1085  rad1(nn, 2) = rad1(nn, 2) - dot * axis(nn, 2)
1086  rad1(nn, 3) = rad1(nn, 3) - dot * axis(nn, 3)
1087 
1088  length = one / (rad1(nn, 1)**2 + rad1(nn, 2)**2 + rad1(nn, 3)**2)
1089  rad1(nn, 1) = rad1(nn, 1) * length
1090  rad1(nn, 2) = rad1(nn, 2) * length
1091  rad1(nn, 3) = rad1(nn, 3) * length
1092 
1093  ! Create the second vector which spans the radIal plane. This
1094  ! must be normal to both axis and rad1, i.e. the cross-product.
1095 
1096  rad2(nn, 1) = axis(nn, 2) * rad1(nn, 3) - axis(nn, 3) * rad1(nn, 2)
1097  rad2(nn, 2) = axis(nn, 3) * rad1(nn, 1) - axis(nn, 1) * rad1(nn, 3)
1098  rad2(nn, 3) = axis(nn, 1) * rad1(nn, 2) - axis(nn, 2) * rad1(nn, 1)
1099 
1100  end do
1101 
1102  ! Initialize the values of thetaNMin, etc.
1103 
1104  thetanmin = zero
1105  thetanmax = -pi
1106  thetapmin = pi
1107  thetapmax = zero
1108  !
1109  ! Determine the local values of thetaNMin, etc. for the
1110  ! different sections.
1111  !
1112  do nn = 1, ndom
1113 
1114  ! Store the section id of the block a bit easier. Continue with
1115  ! the next block if this section consist of only one slice.
1116 
1117  sec = flowdoms(nn, level, sps)%sectionID
1118  if (sections(sec)%nSlices == 1) cycle
1119 
1120  ! Set the pointers for this block.
1121 
1122  call setpointers(nn, level, sps)
1123 
1124  ! Initialize nq1, nq2, nq3 and nq4 to 0. These integers store
1125  ! the number of nodes in the first, second, third and fourth
1126  ! quadrant respectivily.
1127 
1128  nq1 = 0; nq2 = 0; nq3 = 0; nq4 = 0
1129 
1130  ! Loop over the nodes of this block.
1131 
1132  do k = 1, kl
1133  do j = 1, jl
1134  do i = 1, il
1135 
1136  ! Determine the coordinates relative to the
1137  ! center of rotation.
1138 
1139  xx = x(i, j, k, 1) - sections(sec)%rotCenter(1)
1140  yy = x(i, j, k, 2) - sections(sec)%rotCenter(2)
1141  zz = x(i, j, k, 3) - sections(sec)%rotCenter(3)
1142 
1143  ! Determine the radIal components in the local
1144  ! cylindrical coordinate system of the section.
1145 
1146  r1 = xx * rad1(sec, 1) + yy * rad1(sec, 2) + zz * rad1(sec, 3)
1147  r2 = xx * rad2(sec, 1) + yy * rad2(sec, 2) + zz * rad2(sec, 3)
1148 
1149  ! Determine the angle if r1 or r2 is nonzero.
1150 
1151  if ((abs(r1) >= eps) .or. (abs(r2) >= eps)) then
1152 
1153  theta = atan2(r2, r1)
1154 
1155  ! Update the minimum and maximum angle for this
1156  ! section, depending on the sign of theta.
1157 
1158  if (theta >= zero) then
1159  thetapmin(sec) = min(thetapmin(sec), theta)
1160  thetapmax(sec) = max(thetapmax(sec), theta)
1161  end if
1162 
1163  if (theta <= zero) then
1164  thetanmin(sec) = min(thetanmin(sec), theta)
1165  thetanmax(sec) = max(thetanmax(sec), theta)
1166  end if
1167 
1168  ! Determine the quadrant in which this node is located
1169  ! and update the corresponding counter.
1170 
1171  if (theta <= -half * pi) then
1172  nq3 = nq3 + 1
1173  else if (theta <= zero) then
1174  nq4 = nq4 + 1
1175  else if (theta <= half * pi) then
1176  nq1 = nq1 + 1
1177  else
1178  nq2 = nq2 + 1
1179  end if
1180 
1181  end if
1182 
1183  end do
1184  end do
1185  end do
1186 
1187  ! Modify the minimum and maximum angles if nodes are present
1188  ! in multiple quadrants.
1189 
1190  if (nq1 > 0 .and. nq4 > 0) then
1191 
1192  ! Nodes in both the 1st and 4th quadrant. Update the
1193  ! corresponding minimum and maximum angle.
1194 
1195  thetanmax(sec) = zero
1196  thetapmin(sec) = zero
1197 
1198  end if
1199 
1200  if (nq2 > 0 .and. nq3 > 0) then
1201 
1202  ! Nodes in both the 2nd and 3rd quadrant. Update the
1203  ! corresponding minimum and maximum angle.
1204 
1205  thetanmin(sec) = -pi
1206  thetapmax(sec) = pi
1207 
1208  end if
1209 
1210  end do
1211 
1212  ! Determine the minimum of the minimum angles and the maximum of
1213  ! the maximum angles for all sections.
1214 
1215  size = nsections
1216  call mpi_allreduce(thetanmax, tmp, size, adflow_real, mpi_max, &
1217  adflow_comm_world, ierr)
1218  thetanmax = tmp
1219 
1220  call mpi_allreduce(thetapmax, tmp, size, adflow_real, mpi_max, &
1221  adflow_comm_world, ierr)
1222  thetapmax = tmp
1223 
1224  call mpi_allreduce(thetanmin, tmp, size, adflow_real, mpi_min, &
1225  adflow_comm_world, ierr)
1226  thetanmin = tmp
1227 
1228  call mpi_allreduce(thetapmin, tmp, size, adflow_real, mpi_min, &
1229  adflow_comm_world, ierr)
1230  thetapmin = tmp
1231 
1232  ! Allocate the memory for rotMatrixSections, the rotation
1233  ! matrices of the sections needed for the alignment.
1234 
1235  allocate (rotmatrixsections(nsections, 3, 3), stat=ierr)
1236  if (ierr /= 0) &
1237  call terminate("localViscousSurfaceMesh", &
1238  "Memory allocation failure for &
1239  &rotMatrixSections")
1240  !
1241  ! Determine the rotation matrix for each section, which aligns
1242  ! the rotational periodic sections with other sections.
1243  !
1244  do nn = 1, nsections
1245 
1246  ! Test if a rotation is actually needed.
1247 
1248  testrot: if (sections(nn)%nSlices == 1 .or. &
1249  thetapmin(nn) == zero) then
1250 
1251  ! Section consist out of 1 slice or the slice crosses the
1252  ! line theta == 0. For both cases the rotation matrix is
1253  ! the identity matrix.
1254 
1255  rotmatrixsections(nn, 1, 1) = one
1256  rotmatrixsections(nn, 1, 2) = zero
1257  rotmatrixsections(nn, 1, 3) = zero
1258 
1259  rotmatrixsections(nn, 2, 1) = zero
1260  rotmatrixsections(nn, 2, 2) = one
1261  rotmatrixsections(nn, 2, 3) = zero
1262 
1263  rotmatrixsections(nn, 3, 1) = zero
1264  rotmatrixsections(nn, 3, 2) = zero
1265  rotmatrixsections(nn, 3, 3) = one
1266 
1267  else testrot
1268 
1269  ! Section consist out of multiple slices and the current slice
1270  ! does not cross the line theta == 0. The rotation matrix
1271  ! for alignment must be computed.
1272 
1273  theta = two * pi / sections(nn)%nSlices
1274 
1275  ! Determine the number of rotations needed to align the mesh.
1276 
1277  if (thetanmin(nn) < zero) then
1278 
1279  ! The section lies (at least partially) in the third and
1280  ! fourth quadrant. Determine the number of rotations for
1281  ! alignment; this is a positive number.
1282 
1283  mm = -thetanmax(nn) / theta + 1
1284 
1285  else
1286 
1287  ! The section lies (completely) in the first and second
1288  ! quadrant. The number of rotations will be a negative
1289  ! number now.
1290 
1291  mm = -thetapmin(nn) / theta - 1
1292 
1293  end if
1294 
1295  ! Compute the rotation angle in the local cylindrical frame
1296  ! and its sine and cosine.
1297 
1298  theta = mm * theta
1299  costheta = cos(theta)
1300  sintheta = sin(theta)
1301 
1302  ! Apply the transformation to obtain the matrix in the
1303  ! original cartesian frame.
1304 
1305  rotmatrixsections(nn, 1, 1) = axis(nn, 1) * axis(nn, 1) &
1306  + costheta * (rad1(nn, 1) * rad1(nn, 1) + rad2(nn, 1) * rad2(nn, 1))
1307  rotmatrixsections(nn, 1, 2) = axis(nn, 1) * axis(nn, 2) &
1308  + costheta * (rad1(nn, 1) * rad1(nn, 2) + rad2(nn, 1) * rad2(nn, 2)) &
1309  + sintheta * (rad1(nn, 2) * rad2(nn, 1) - rad1(nn, 1) * rad2(nn, 2))
1310  rotmatrixsections(nn, 1, 3) = axis(nn, 1) * axis(nn, 3) &
1311  + costheta * (rad1(nn, 1) * rad1(nn, 3) + rad2(nn, 1) * rad2(nn, 3)) &
1312  + sintheta * (rad1(nn, 3) * rad2(nn, 1) - rad1(nn, 1) * rad2(nn, 3))
1313 
1314  rotmatrixsections(nn, 2, 1) = axis(nn, 1) * axis(nn, 2) &
1315  + costheta * (rad1(nn, 1) * rad1(nn, 2) + rad2(nn, 1) * rad2(nn, 2)) &
1316  - sintheta * (rad1(nn, 2) * rad2(nn, 1) - rad1(nn, 1) * rad2(nn, 2))
1317  rotmatrixsections(nn, 2, 2) = axis(nn, 2) * axis(nn, 2) &
1318  + costheta * (rad1(nn, 2) * rad1(nn, 2) + rad2(nn, 2) * rad2(nn, 2))
1319  rotmatrixsections(nn, 2, 3) = axis(nn, 2) * axis(nn, 3) &
1320  + costheta * (rad1(nn, 2) * rad1(nn, 3) + rad2(nn, 2) * rad2(nn, 3)) &
1321  + sintheta * (rad1(nn, 3) * rad2(nn, 2) - rad1(nn, 2) * rad2(nn, 3))
1322 
1323  rotmatrixsections(nn, 3, 1) = axis(nn, 1) * axis(nn, 3) &
1324  + costheta * (rad1(nn, 1) * rad1(nn, 3) + rad2(nn, 1) * rad2(nn, 3)) &
1325  - sintheta * (rad1(nn, 3) * rad2(nn, 1) - rad1(nn, 1) * rad2(nn, 3))
1326  rotmatrixsections(nn, 3, 2) = axis(nn, 2) * axis(nn, 3) &
1327  + costheta * (rad1(nn, 2) * rad1(nn, 3) + rad2(nn, 2) * rad2(nn, 3)) &
1328  - sintheta * (rad1(nn, 3) * rad2(nn, 2) - rad1(nn, 2) * rad2(nn, 3))
1329  rotmatrixsections(nn, 3, 3) = axis(nn, 3) * axis(nn, 3) &
1330  + costheta * (rad1(nn, 3) * rad1(nn, 3) + rad2(nn, 3) * rad2(nn, 3))
1331 
1332  end if testrot
1333 
1334  end do
1335  !
1336  ! Determine the local viscous surface grid.
1337  !
1338  np = 0
1339  nq = 0
1340 
1341  loopdomains: do nn = 1, ndom
1342 
1343  ! Set the pointers for this block and store the section id
1344  ! a bit easier.
1345 
1346  call setpointers(nn, level, sps)
1347  sec = sectionid
1348 
1349  ! Loop over the subfaces of this block and test if this is
1350  ! a viscous subface.
1351 
1352  loopbocos: do mm = 1, nbocos
1353  testviscous: if (bctype(mm) == nswalladiabatic .or. &
1354  bctype(mm) == nswallisothermal) then
1355 
1356  ! Viscous subface. Set the pointer for the coordinates of
1357  ! the face.
1358 
1359  select case (bcfaceid(mm))
1360 
1361  case (imin)
1362  xface => x(1, 1:, 1:, :)
1363 
1364  case (imax)
1365  xface => x(il, 1:, 1:, :)
1366 
1367  case (jmin)
1368  xface => x(1:, 1, 1:, :)
1369 
1370  case (jmax)
1371  xface => x(1:, jl, 1:, :)
1372 
1373  case (kmin)
1374  xface => x(1:, 1:, 1, :)
1375 
1376  case (kmax)
1377  xface => x(1:, 1:, kl, :)
1378 
1379  end select
1380 
1381  ! Store the nodal range of this subface a bit easier.
1382 
1383  jbeg = bcdata(mm)%jnBeg; jend = bcdata(mm)%jnEnd
1384  ibeg = bcdata(mm)%inBeg; iend = bcdata(mm)%inEnd
1385 
1386  ! Store the old value of the number of points stored and
1387  ! determine the new coordinates.
1388 
1389  npold = np
1390  do j = jbeg, jend
1391  do i = ibeg, iend
1392 
1393  ! Determine the coordinates relative to the rotation
1394  ! center of this section.
1395 
1396  xx = xface(i, j, 1) - sections(sec)%rotCenter(1)
1397  yy = xface(i, j, 2) - sections(sec)%rotCenter(2)
1398  zz = xface(i, j, 3) - sections(sec)%rotCenter(3)
1399 
1400  ! Update the counter and determine the surface mesh
1401  ! coordinates.
1402 
1403  np = np + 1
1404  coorvisc(1, np) = rotmatrixsections(sec, 1, 1) * xx &
1405  + rotmatrixsections(sec, 1, 2) * yy &
1406  + rotmatrixsections(sec, 1, 3) * zz &
1407  + sections(sec)%rotCenter(1)
1408 
1409  coorvisc(2, np) = rotmatrixsections(sec, 2, 1) * xx &
1410  + rotmatrixsections(sec, 2, 2) * yy &
1411  + rotmatrixsections(sec, 2, 3) * zz &
1412  + sections(sec)%rotCenter(2)
1413 
1414  coorvisc(3, np) = rotmatrixsections(sec, 3, 1) * xx &
1415  + rotmatrixsections(sec, 3, 2) * yy &
1416  + rotmatrixsections(sec, 3, 3) * zz &
1417  + sections(sec)%rotCenter(3)
1418  end do
1419  end do
1420 
1421  ! Determine and store the connectivity of this subface.
1422 
1423  np1 = iend - ibeg + 1
1424  nqold = nq
1425 
1426  do j = (jbeg + 1), jend
1427  do i = (ibeg + 1), iend
1428 
1429  ! Update the counter nq and determine the 4 indices
1430  ! of the surface quad.
1431 
1432  nq = nq + 1
1433 
1434  connvisc(1, nq) = npold + (j - jbeg - 1) * np1 + i - ibeg
1435  connvisc(2, nq) = connvisc(1, nq) + 1
1436  connvisc(3, nq) = connvisc(2, nq) + np1
1437  connvisc(4, nq) = connvisc(3, nq) - 1
1438 
1439  end do
1440  end do
1441 
1442  ! Loop over the number of times the subface must be stored.
1443  ! This happens when the rotational periodicity differs from
1444  ! section to section. Note that this loop starts at k == 2.
1445 
1446  loopmultiplicity: do k = 2, multsections(sec)
1447 
1448  ! Store the current number of nodes and quads
1449  ! in mp and mq respectivily.
1450 
1451  mp = np
1452  mq = nq
1453 
1454  ! Loop over the of points on this subface.
1455 
1456  do i = (npold + 1), mp
1457 
1458  ! Determine the coordinates relative to the center
1459  ! of rotation.
1460 
1461  np = np + 1
1462 
1463  xx = coorvisc(1, i) - sections(sec)%rotCenter(1)
1464  yy = coorvisc(2, i) - sections(sec)%rotCenter(2)
1465  zz = coorvisc(3, i) - sections(sec)%rotCenter(3)
1466 
1467  ! Update the counter np and determine the new
1468  ! coordinates after the transformation.
1469 
1470  coorvisc(1, np) = sections(sec)%rotMatrix(1, 1) * xx &
1471  + sections(sec)%rotMatrix(1, 2) * yy &
1472  + sections(sec)%rotMatrix(1, 3) * zz &
1473  + sections(sec)%rotCenter(1) &
1474  + sections(sec)%translation(1)
1475 
1476  coorvisc(2, np) = sections(sec)%rotMatrix(2, 1) * xx &
1477  + sections(sec)%rotMatrix(2, 2) * yy &
1478  + sections(sec)%rotMatrix(2, 3) * zz &
1479  + sections(sec)%rotCenter(2) &
1480  + sections(sec)%translation(2)
1481 
1482  coorvisc(3, np) = sections(sec)%rotMatrix(3, 1) * xx &
1483  + sections(sec)%rotMatrix(3, 2) * yy &
1484  + sections(sec)%rotMatrix(3, 3) * zz &
1485  + sections(sec)%rotCenter(3) &
1486  + sections(sec)%translation(3)
1487  end do
1488 
1489  ! Store the number of nodes in this subface in j
1490  ! and determine the connectivity of this rotated part.
1491 
1492  j = np - mp
1493  do i = (nqold + 1), mq
1494 
1495  ! Update the counter nq and set the new connectivity,
1496  ! which is the old connectivity plus an offset.
1497 
1498  nq = nq + 1
1499  connvisc(1, nq) = connvisc(1, i) + j
1500  connvisc(2, nq) = connvisc(2, i) + j
1501  connvisc(3, nq) = connvisc(3, i) + j
1502  connvisc(4, nq) = connvisc(4, i) + j
1503 
1504  end do
1505 
1506  ! Copy the values of mp and mq into npOld and nqOld for
1507  ! the next multiple of the slice. Idem for np in mp, etc.
1508 
1509  npold = mp
1510  nqold = mq
1511 
1512  mp = np
1513  mq = nq
1514 
1515  end do loopmultiplicity
1516 
1517  end if testviscous
1518  end do loopbocos
1519  end do loopdomains
1520 
1521  end subroutine localviscoussurfacemesh
1522 
1524  !
1525  ! updateWallDistanceAllLevels updates the wall distances for
1526  ! the cell centers on all grid levels. This routine is typically
1527  ! called when grid parts have been moved, either due to a
1528  ! physical motion of some parts or due to deformation.
1529  !
1530  use constants
1531  use block, only: flowdoms
1532  use inputphysics, only: equations
1533  use iteration, only: groundlevel
1534  implicit none
1535  !
1536  ! Local variables.
1537  !
1538  integer(kind=intType) :: nLevels, nn
1539 
1540  ! Return immediately if the rans equations are not solved.
1541 
1542  if (equations /= ransequations) return
1543 
1544  ! Loop over the grid levels and call wallDistance.
1545 
1546  nlevels = ubound(flowdoms, 2)
1547  do nn = groundlevel, nlevels
1548  call computewalldistance(nn, .false.)
1549  end do
1550 
1551  end subroutine updatewalldistancealllevels
1552 
1553  subroutine viscoussurfacemesh(level, sps)
1554  !
1555  ! viscousSurfaceMesh determines and stores the entire viscous
1556  ! surface possibly extended by periodic parts.
1557  !
1558  use constants
1559  use block, only: flowdoms, ndom
1561  use section, only: nsections, sections
1562  use utils, only: terminate
1563  implicit none
1564  !
1565  ! Subroutine arguments.
1566  !
1567  integer(kind=intType), intent(in) :: level, sps
1568  !
1569  ! Local variables.
1570  !
1571  integer :: ierr
1572 
1573  integer(kind=intType) :: nn, mm, ii
1574  integer(kind=intType) :: ni, nj, nk
1575 
1576  integer(kind=intType), dimension(nSections) :: multSections
1577 
1578  ! Determine the minimum number of slices present in a certain
1579  ! section. For time accurate computations the number of slices
1580  ! is identical for all sections, but for steady flow using the
1581  ! mixing plane assumption this is not necessarily the case.
1582 
1583  mm = sections(1)%nSlices
1584  do nn = 2, nsections
1585  mm = min(mm, sections(nn)%nSlices)
1586  end do
1587 
1588  ! Determine the multiplicity of every section needed in the
1589  ! surface mesh, such that every part covers an angle which is
1590  ! at least equal to the angle of the largest section. Again note
1591  ! that this multiplicity is 1 for all sections if a time
1592  ! accurate computation is performed.
1593 
1594  do nn = 1, nsections
1595  multsections(nn) = sections(nn)%nSlices / mm
1596  if (sections(nn)%nSlices > mm * multsections(nn)) &
1597  multsections(nn) = multsections(nn) + 1
1598  end do
1599 
1600  ! Determine the local number of viscous nodes and quads.
1601  ! Note that these numbers are identical for all spectral
1602  ! solutions and thus it is okay to take the 1st one.
1603 
1604  nnodevisc = 0
1605  nquadvisc = 0
1606 
1607  do nn = 1, ndom
1608  do mm = 1, flowdoms(nn, level, 1)%nBocos
1609  if (flowdoms(nn, level, 1)%BCType(mm) == nswalladiabatic .or. &
1610  flowdoms(nn, level, 1)%BCType(mm) == nswallisothermal) then
1611 
1612  ! Determine the number of nodes of the subface in the
1613  ! three directions.
1614 
1615  ni = flowdoms(nn, level, 1)%inEnd(mm) &
1616  - flowdoms(nn, level, 1)%inBeg(mm)
1617  nj = flowdoms(nn, level, 1)%jnEnd(mm) &
1618  - flowdoms(nn, level, 1)%jnBeg(mm)
1619  nk = flowdoms(nn, level, 1)%knEnd(mm) &
1620  - flowdoms(nn, level, 1)%knBeg(mm)
1621 
1622  ! Determine the multiplication factor, because of the
1623  ! possible multiple sections.
1624 
1625  ii = flowdoms(nn, level, 1)%sectionId
1626  ii = multsections(ii)
1627 
1628  ! Update the number of nodes and quads. Take the
1629  ! multiplicity into account.
1630 
1631  nnodevisc = nnodevisc + ii * (ni + 1) * (nj + 1) * (nk + 1)
1632  nquadvisc = nquadvisc + ii * max(ni, 1_inttype) &
1633  * max(nj, 1_inttype) &
1634  * max(nk, 1_inttype)
1635  end if
1636  end do
1637  end do
1638 
1639  ! Determine the global number of elements on the viscous
1640  ! surfaces. Return if there are no viscous quads present.
1641 
1642  call mpi_allreduce(nquadvisc, nquadviscglob, 1, adflow_integer, &
1643  mpi_sum, adflow_comm_world, ierr)
1644 
1645  if (nquadviscglob == 0) return
1646 
1647  ! Allocate the memory for the local connectivity and coordinates.
1648 
1649  allocate (connvisc(4, nquadvisc), coorvisc(3, nnodevisc), &
1650  stat=ierr)
1651  if (ierr /= 0) &
1652  call terminate("viscousSurfaceMesh", &
1653  "Memory allocation failure for connVisc &
1654  &and coorVisc.")
1655 
1656  ! Determine the local viscous surface mesh, possibly rotated
1657  ! to align the other sections.
1658 
1659  call localviscoussurfacemesh(multsections, level, sps)
1660 
1661  end subroutine viscoussurfacemesh
1662 
1663  subroutine determinewallassociation(level, sps)
1664 
1665  ! This routine will determine the closest surface point for every
1666  ! field cell. Special treatment is required for overlapping surfaces.
1667 
1668  use constants
1669  use adtapi, only: mindistancesearch
1670  use adtdata, only: adtbboxtargettype
1672  use adtutils, only: stack
1674  use blockpointers
1675  use communication
1676  use inputphysics
1677  use inputtimespectral
1679  use inputoverset
1680  use adjointvars
1681  use surfacefamilies, only: bcfamgroups
1682  use utils, only: setpointers, echk
1683  use sorting, only: unique
1684  implicit none
1685 
1686  ! Input Variables
1687  integer(kind=intType), intent(in) :: level, sps
1688 
1689  ! Local Variables
1690  integer(kind=intType) :: i, j, k, l, ii, jj, kk, nn, mm, iNode, iCell, c
1691  integer(kind=intType) :: iBeg, iEnd, jBeg, jEnd, ni, nj, nUnique, cellID, cellID2
1692  integer(kind=intType) :: ierr, iDim
1693 
1694  ! Data for local surface
1695  integer(kind=intType) :: nNodes, nCells
1696  logical :: gridHasOverset
1697 
1698  ! Overset Walls for storing the surface ADT's
1699  type(oversetwall), dimension(:), allocatable, target :: walls
1700  type(oversetwall), target :: fullWall
1701  integer(kind=intType), dimension(:), allocatable :: link, indicesToGet
1702 
1703  ! Data for the ADT
1704  integer(kind=intType) :: intInfo(3), intInfo2(3)
1705  real(kind=realtype) :: coor(4), uvw(5), uvw2(5)
1706  real(kind=realtype), dimension(3, 2) :: dummy
1707  real(kind=realtype), parameter :: tol = 1e-12
1708  integer(kind=intType), dimension(:), pointer :: frontLeaves, frontLeavesNew, &
1709  BBint, wallFamList
1710  type(adtbboxtargettype), dimension(:), pointer :: BB
1711  real(kind=realtype), dimension(3) :: xp
1712 
1713  ! The first thing we do is gather all the surface nodes to
1714  ! each processor such that every processor can make it's own copy of
1715  ! the complete surface mesh to use to search. Note that this
1716  ! procedure *DOES NOT SCALE IN MEMORY*...ie eventually the surface
1717  ! mesh will become too large to store on a single processor,
1718  ! although this will probably not happen until the sizes get up in
1719  ! the hundreds of millions of cells.
1720 
1721  allocate (walls(nclusters))
1722  wallfamlist => bcfamgroups(ibcgroupwalls)%famList
1723  call buildclusterwalls(level, sps, .false., walls, wallfamlist, size(wallfamlist))
1724 
1725  if (oversetpresent) then
1726  ! Finally build up a "full wall" that is made up of all the cluster
1727  ! walls.
1728 
1729  nnodes = 0
1730  ncells = 0
1731  do i = 1, nclusters
1732  nnodes = nnodes + walls(i)%nNodes
1733  ncells = ncells + walls(i)%nCells
1734  end do
1735 
1736  allocate (fullwall%x(3, nnodes))
1737  allocate (fullwall%conn(4, ncells))
1738  allocate (fullwall%ind(nnodes))
1739 
1740  nnodes = 0
1741  ncells = 0
1742  ii = 0
1743  do i = 1, nclusters
1744 
1745  ! Add in the nodes/elements from this cluster
1746 
1747  do j = 1, walls(i)%nNodes
1748  nnodes = nnodes + 1
1749  fullwall%x(:, nnodes) = walls(i)%x(:, j)
1750  fullwall%ind(nnodes) = walls(i)%ind(j)
1751  end do
1752 
1753  do j = 1, walls(i)%nCells
1754  ncells = ncells + 1
1755  fullwall%conn(:, ncells) = walls(i)%conn(:, j) + ii
1756  end do
1757 
1758  ! Increment the node offset
1759  ii = ii + walls(i)%nNodes
1760  end do
1761 
1762  ! Finish the setup of the full wall.
1763  fullwall%nCells = ncells
1764  fullwall%nNodes = nnodes
1765  call buildserialquad(ncells, nnodes, fullwall%x, fullwall%conn, fullwall%ADT)
1766  end if
1767 
1768  ! Allocate the (pointer) memory that may be resized as necessary for
1769  ! the singlePoint search routine.
1770  allocate (stack(100), bb(20), bbint(20), frontleaves(25), frontleavesnew(25))
1771 
1772  ! We need to store the 4 global node indices defining the quad that
1773  ! each point has the closest point wrt. We also ned to store the uv
1774  ! values. This allows us to recompute the exact surface point, after
1775  ! the rquired nodes are fetched from (a possibly) remote proc.
1776 
1777  do nn = 1, ndom
1778  call setpointers(nn, level, sps)
1779 
1780  ! Check if elemID and uv are allocated yet.
1781  if (.not. associated(flowdoms(nn, level, sps)%surfNodeIndices)) then
1782  allocate (flowdoms(nn, level, sps)%surfNodeIndices(4, 2:il, 2:jl, 2:kl))
1783  allocate (flowdoms(nn, level, sps)%uv(2, 2:il, 2:jl, 2:kl))
1784  end if
1785 
1786  ! Set the cluster for this block
1787  c = clusters(cumdomproc(myid) + nn)
1788 
1789  do k = 2, kl
1790  do j = 2, jl
1791  do i = 2, il
1792 
1793  ! Compute the coordinates of the cell center
1794  coor(1) = eighth * (x(i - 1, j - 1, k - 1, 1) + x(i, j - 1, k - 1, 1) &
1795  + x(i - 1, j, k - 1, 1) + x(i, j, k - 1, 1) &
1796  + x(i - 1, j - 1, k, 1) + x(i, j - 1, k, 1) &
1797  + x(i - 1, j, k, 1) + x(i, j, k, 1))
1798 
1799  coor(2) = eighth * (x(i - 1, j - 1, k - 1, 2) + x(i, j - 1, k - 1, 2) &
1800  + x(i - 1, j, k - 1, 2) + x(i, j, k - 1, 2) &
1801  + x(i - 1, j - 1, k, 2) + x(i, j - 1, k, 2) &
1802  + x(i - 1, j, k, 2) + x(i, j, k, 2))
1803 
1804  coor(3) = eighth * (x(i - 1, j - 1, k - 1, 3) + x(i, j - 1, k - 1, 3) &
1805  + x(i - 1, j, k - 1, 3) + x(i, j, k - 1, 3) &
1806  + x(i - 1, j - 1, k, 3) + x(i, j - 1, k, 3) &
1807  + x(i - 1, j, k, 3) + x(i, j, k, 3))
1808 
1809  if (.not. oversetpresent) then
1810  ! No overset present. Simply search our own wall,
1811  ! walls(c), (the only one we have) up to the wall
1812  ! cutoff.
1813  coor(4) = walldistcutoff**2
1814  intinfo(3) = 0 ! Must be initialized since the search
1815  ! may not find closer point.
1816  call mindistancetreesearchsinglepoint(walls(c)%ADT, coor, intinfo, &
1817  uvw, dummy, 0, bb, frontleaves, frontleavesnew)
1818 
1819  cellid = intinfo(3)
1820  if (cellid > 0) then
1821  do kk = 1, 4
1822  flowdoms(nn, level, sps)%surfNodeIndices(kk, i, j, k) = &
1823  walls(c)%ind(walls(c)%conn(kk, cellid))
1824  end do
1825  flowdoms(nn, level, sps)%uv(:, i, j, k) = uvw(1:2)
1826  else
1827  ! Just set dummy values. These will never be used.
1828  flowdoms(nn, level, sps)%surfNodeIndices(:, i, j, k) = 0
1829  flowdoms(nn, level, sps)%uv(:, i, j, k) = 0
1830  end if
1831 
1832  ! We are done with this point.
1833  cycle
1834  end if
1835 
1836  ! This is now the overset (possibly) overlapping surface
1837  ! mesh case. It is somewhat more complex since we use
1838  ! the same searches to flag cells that are inside the
1839  ! body.
1840 
1841  coor(4) = walldistcutoff**2
1842  intinfo(3) = 0
1843  call mindistancetreesearchsinglepoint(fullwall%ADT, coor, &
1844  intinfo, uvw, dummy, 0, bb, frontleaves, frontleavesnew)
1845  cellid = intinfo(3)
1846 
1847  if (cellid > 0) then
1848  ! We found the cell:
1849 
1850  ! If the cell is outside of near-wall distance or our
1851  ! cluster doesn't have any owned cells. Just accept it.
1852  if (uvw(4) > nearwalldist**2 .or. walls(c)%nCells == 0) then
1853 
1854  do kk = 1, 4
1855  flowdoms(nn, level, sps)%surfNodeIndices(kk, i, j, k) = &
1856  fullwall%ind(fullwall%conn(kk, cellid))
1857  end do
1858  flowdoms(nn, level, sps)%uv(:, i, j, k) = uvw(1:2)
1859 
1860  else
1861 
1862  ! This point is *closer* than the nearWallDist AND
1863  ! it has a wall. Search on our own wall.
1864 
1865  coor(4) = large
1866  call mindistancetreesearchsinglepoint(walls(c)%ADT, coor, &
1867  intinfo2, uvw2, dummy, 0, bb, &
1868  frontleaves, frontleavesnew)
1869  cellid2 = intinfo2(3)
1870 
1871  if (uvw2(4) < nearwalldist**2) then
1872  ! Both are close to the wall. Accept the one
1873  ! from our own wall unconditionally.
1874  do kk = 1, 4
1875  flowdoms(nn, level, sps)%surfNodeIndices(kk, i, j, k) = &
1876  walls(c)%ind(walls(c)%conn(kk, cellid2))
1877  end do
1878  flowdoms(nn, level, sps)%uv(:, i, j, k) = uvw2(1:2)
1879  else
1880  ! The full wall distance is better. Take that.
1881 
1882  do kk = 1, 4
1883  flowdoms(nn, level, sps)%surfNodeIndices(kk, i, j, k) = &
1884  fullwall%ind(fullwall%conn(kk, cellid))
1885  end do
1886  flowdoms(nn, level, sps)%uv(:, i, j, k) = uvw(1:2)
1887 
1888  end if
1889  end if
1890  else
1891 
1892  ! What happend here is a cell is outside the
1893  ! wallDistCutoff. We don't care about wall distance
1894  ! info here so just set dummy info.
1895 
1896  flowdoms(nn, level, sps)%surfNodeIndices(:, i, j, k) = 0
1897  flowdoms(nn, level, sps)%uv(:, i, j, k) = 0
1898 
1899  end if
1900  end do
1901  end do
1902  end do
1903  end do
1904 
1905  ! Now determine all the node indices this processor needs to get.
1906  mm = 0
1907  allocate (indicestoget(ncellslocal(level) * 4), link(ncellslocal(level) * 4))
1908  do nn = 1, ndom
1909  call setpointers(nn, level, sps)
1910  do k = 2, kl
1911  do j = 2, jl
1912  do i = 2, il
1913  do kk = 1, 4
1914  mm = mm + 1
1915  indicestoget(mm) = flowdoms(nn, level, sps)%surfNodeIndices(kk, i, j, k)
1916  end do
1917  end do
1918  end do
1919  end do
1920  end do
1921 
1922  ! This unique-ifies the indices.
1923  call unique(indicestoget, 4 * ncellslocal(level), nunique, link)
1924 
1925  ! we need to update the stored indices to use the ordering of the nodes we will receive.
1926  mm = 0
1927  do nn = 1, ndom
1928  call setpointers(nn, level, sps)
1929  do k = 2, kl
1930  do j = 2, jl
1931  do i = 2, il
1932  do kk = 1, 4
1933  mm = mm + 1
1934  flowdoms(nn, level, sps)%surfNodeIndices(kk, i, j, k) = link(mm)
1935  end do
1936  end do
1937  end do
1938  end do
1939  end do
1940  deallocate (link)
1941 
1942  ! Now create the index set for the nodes we need to get. We have to
1943  ! expand "indices to get" to include the DOF. Use link for this
1944  ! temporary array operation.
1945 
1946  allocate (link(nunique * 3))
1947  do i = 1, nunique
1948  link((i - 1) * 3 + 1) = indicestoget(i) * 3
1949  link((i - 1) * 3 + 2) = indicestoget(i) * 3 + 1
1950  link((i - 1) * 3 + 3) = indicestoget(i) * 3 + 2
1951  end do
1952 
1953  call iscreategeneral(adflow_comm_world, nunique * 3, link, petsc_copy_values, is1, ierr)
1954  call echk(ierr, __file__, __line__)
1955  deallocate (link)
1956 
1957  ! Create the volume vector the nodes will be scatter from. Note that
1958  ! this vector contains all the spectal instances. It is therefore
1959  ! only allocated on the first call with sps=1
1960  if (sps == 1) then
1961  call veccreatempi(adflow_comm_world, 3 * nnodeslocal(level) * ntimeintervalsspectral, &
1962  petsc_determine, xvolumevec(level), ierr)
1963  call echk(ierr, __file__, __line__)
1964  end if
1965 
1966  ! This is the vector we will scatter the nodes into.
1967  call veccreatempi(adflow_comm_world, 3 * nunique, petsc_determine, &
1968  xsurfvec(level, sps), ierr)
1969  call echk(ierr, __file__, __line__)
1970 
1971  call vecgetownershiprange(xsurfvec(level, sps), i, j, ierr)
1972  call echk(ierr, __file__, __line__)
1973 
1974  call iscreatestride(adflow_comm_world, j - i, i, 1, is2, ierr)
1975  call echk(ierr, __file__, __line__)
1976 
1977  ! Create the actual final scatter context.
1978  call vecscattercreate(xvolumevec(level), is1, xsurfvec(level, sps), is2, &
1979  wallscatter(level, sps), ierr)
1980  call echk(ierr, __file__, __line__)
1981 
1982  call isdestroy(is1, ierr)
1983  call echk(ierr, __file__, __line__)
1984 
1985  call isdestroy(is2, ierr)
1986  call echk(ierr, __file__, __line__)
1987 
1988  ! Deallocate all the remaining temporary data
1989  deallocate (stack, bb, frontleaves, frontleavesnew, bbint)
1990 
1991  do i = 1, nclusters
1992  deallocate (walls(i)%x, walls(i)%conn, walls(i)%ind)
1993  call destroyserialquad(walls(i)%ADT)
1994  end do
1995  deallocate (walls)
1996 
1997  if (oversetpresent) then
1998  deallocate (fullwall%x, fullwall%conn, fullwall%ind)
1999  call destroyserialquad(fullwall%ADT)
2000  end if
2001 
2002  end subroutine determinewallassociation
2003 
2004  subroutine updatexsurf(level)
2005 
2006  use blockpointers
2007  use inputtimespectral
2008  use utils, only: echk, setpointers
2009  implicit none
2010 
2011  ! Input Parameters
2012  integer(kind=intType), intent(in) :: level
2013 
2014  ! Working Parameters
2015  integer(kind=intType) :: ii, i, j, k, l, nn, sps, ierr
2016 
2017  ! Fill up xVolumeVec
2018  call vecgetarrayf90(xvolumevec(level), xvolume, ierr)
2019  call echk(ierr, __file__, __line__)
2020 
2021  ii = 0
2022  do nn = 1, ndom
2023  do sps = 1, ntimeintervalsspectral
2024  call setpointers(nn, level, sps)
2025  do k = 1, kl
2026  do j = 1, jl
2027  do i = 1, il
2028  do l = 1, 3
2029  ii = ii + 1
2030  xvolume(ii) = x(i, j, k, l)
2031  end do
2032  end do
2033  end do
2034  end do
2035  end do
2036  end do
2037  call vecrestorearrayf90(xvolumevec(level), xvolume, ierr)
2038  call echk(ierr, __file__, __line__)
2039 
2040  ! Perform the scatter from the global x vector to xSurf. SPS loop since the xSurfVec is done by SPS instance.
2041  do sps = 1, ntimeintervalsspectral
2042  call vecscatterbegin(wallscatter(level, sps), xvolumevec(level), &
2043  xsurfvec(level, sps), insert_values, scatter_forward, ierr)
2044  call echk(ierr, __file__, __line__)
2045 
2046  call vecscatterend(wallscatter(level, sps), xvolumevec(level), &
2047  xsurfvec(level, sps), insert_values, scatter_forward, ierr)
2048  call echk(ierr, __file__, __line__)
2049  end do
2050 
2051  end subroutine updatexsurf
2052 
2054 
2055  use constants
2056  use block, only: flowdoms
2057  implicit none
2058 
2059  ! Working
2060  integer(kind=intType) :: level, nLevels, l
2061 
2062  nlevels = ubound(flowdoms, 2)
2063  do l = 1, nlevels
2065  end do
2066 
2067  deallocate (xsurfvec, xvolumevec, wallscatter)
2068 
2069  end subroutine destroywalldistancedata
2070 
2072  use constants
2074  use utils, onlY: echk
2075  use block, only: flowdoms
2076 
2077  implicit none
2078 
2079  ! Input Parameters
2080  integer(kind=intType), intent(in) :: level
2081 
2082  ! Working
2083  integer(kind=intType) :: ierr, sps
2084 
2085  ! Determine if we need to deallocate the PETSc data for
2086  ! this level
2087  if (walldistancedataallocated(level)) then
2088  call vecdestroy(xvolumevec(level), ierr)
2089  call echk(ierr, __file__, __line__)
2090 
2091  do sps = 1, ntimeintervalsspectral
2092  call vecdestroy(xsurfvec(level, sps), ierr)
2093  call echk(ierr, __file__, __line__)
2094 
2095  call vecscatterdestroy(wallscatter(level, sps), ierr)
2096  call echk(ierr, __file__, __line__)
2097  end do
2098 
2099  walldistancedataallocated(level) = .false.
2100  end if
2101  end subroutine destroywalldistancedatalevel
2102 
2103 #endif
2104 end module walldistance
subroutine buildclusterwalls(level, sps, useDual, walls, famList, nFamList)
integer(kind=inttype), dimension(maxlevels) ncellslocal
Definition: ADjointVars.F90:40
integer(kind=inttype), dimension(maxlevels) nnodeslocal
Definition: ADjointVars.F90:39
Definition: adtAPI.F90:1
subroutine adtbuildsurfaceadt(nTria, nQuads, nNodes, coor, triaConn, quadsConn, BBox, useBBox, comm, adtID)
Definition: adtAPI.F90:24
subroutine adtdeallocateadts(adtID)
Definition: adtAPI.F90:212
subroutine adtmindistancesearch(nCoor, coor, adtID, procID, elementType, elementID, uvw, dist2, nInterpol, arrDonor, arrInterpol)
Definition: adtAPI.F90:319
subroutine destroyserialquad(ADT)
Definition: adtBuild.F90:1404
subroutine buildserialquad(nQuad, nNodes, coor, quadsConn, ADT)
Definition: adtBuild.F90:1278
subroutine mindistancetreesearchsinglepoint(ADT, coor, intInfo, uvw, arrDonor, nInterpol, BB, frontLeaves, frontLeavesNew)
integer(kind=inttype), dimension(:), pointer stack
Definition: adtUtils.F90:20
Definition: BCData.F90:1
Definition: block.F90:1
integer(kind=inttype) ndom
Definition: block.F90:761
type(blocktype), dimension(:, :, :), allocatable, target flowdoms
Definition: block.F90:771
integer(kind=inttype) jl
integer(kind=inttype) nviscbocos
integer(kind=inttype) nx
integer(kind=inttype) ny
real(kind=realtype), dimension(:, :, :), pointer d2wall
integer(kind=inttype), dimension(:), pointer bcfaceid
integer(kind=inttype) nbocos
integer(kind=inttype) sectionid
integer(kind=inttype), dimension(:), pointer bctype
integer(kind=inttype) nz
real(kind=realtype), dimension(:, :, :, :), pointer x
integer(kind=inttype) kl
integer(kind=inttype) il
integer(kind=inttype) sendbuffersize
real(kind=realtype), dimension(:), allocatable recvbuffer
real(kind=realtype), dimension(:), allocatable sendbuffer
integer(kind=inttype) recvbuffersize
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
real(kind=realtype), parameter pi
Definition: constants.F90:22
integer(kind=inttype), parameter jmax
Definition: constants.F90:295
real(kind=realtype), parameter eps
Definition: constants.F90:23
real(kind=realtype), parameter eighth
Definition: constants.F90:84
integer(kind=inttype), parameter nswalladiabatic
Definition: constants.F90:260
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
real(kind=realtype), parameter two
Definition: constants.F90:73
integer(kind=inttype), parameter nswallisothermal
Definition: constants.F90:261
integer(kind=inttype), parameter ibcgroupwalls
Definition: constants.F90:309
real(kind=realtype), parameter large
Definition: constants.F90:24
integer(kind=inttype), parameter kmax
Definition: constants.F90:297
integer(kind=inttype), parameter ransequations
Definition: constants.F90:110
integer(kind=inttype), parameter jmin
Definition: constants.F90:294
logical updatewallassociations
Definition: inputParam.F90:95
logical useapproxwalldistance
Definition: inputParam.F90:94
real(kind=realtype) nearwalldist
Definition: inputParam.F90:883
integer(kind=inttype) equations
Definition: inputParam.F90:583
logical walldistanceneeded
Definition: inputParam.F90:589
real(kind=realtype) walldistcutoff
Definition: inputParam.F90:596
real(kind=realtype) walloffset
Definition: inputParam.F90:596
integer(kind=inttype) ntimeintervalsspectral
Definition: inputParam.F90:645
integer(kind=inttype) groundlevel
Definition: iteration.f90:18
integer(kind=inttype) nclusters
Definition: overset.F90:366
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
integer(kind=inttype) nsections
Definition: section.f90:44
type(sectiontype), dimension(:), allocatable sections
Definition: section.f90:46
subroutine unique(arr, nn, n_unique, inverse)
Definition: sorting.F90:899
type(bcgrouptype), dimension(nfamexchange) bcfamgroups
Definition: utils.F90:1
subroutine deallocatetempmemory(resNeeded)
Definition: utils.F90:3834
subroutine allocatetempmemory(resNeeded)
Definition: utils.F90:3908
subroutine echk(errorcode, file, line)
Definition: utils.F90:5722
subroutine setpointers(nn, mm, ll)
Definition: utils.F90:3237
subroutine terminate(routineName, errorMessage)
Definition: utils.F90:502
subroutine localviscoussurfacemesh(multSections, level, sps)
real(kind=realtype), dimension(:, :, :), allocatable rotmatrixsections
subroutine viscoussurfacemesh(level, sps)
subroutine initwalldistance(level, sps, allocMem)
subroutine computenormalspacing(level, sps)
integer(kind=inttype) nquadviscglob
integer(kind=inttype) nquadvisc
integer(kind=inttype) nnodevisc
subroutine destroywalldistancedata
subroutine determinedistance(level, sps)
subroutine destroywalldistancedatalevel(level)
subroutine computewalldistance(level, allocMem)
subroutine updatexsurf(level)
subroutine updatewalldistancesquickly(nn, level, sps)
real(kind=realtype), dimension(:, :), allocatable coorvisc
subroutine updatewalldistancealllevels
subroutine determinewallassociation(level, sps)
integer(kind=inttype), dimension(:, :), allocatable connvisc
real(kind=realtype), dimension(:), pointer xvolume
real(kind=realtype), dimension(:), pointer xsurf
logical, dimension(:), allocatable walldistancedataallocated
logical, dimension(:), allocatable updatelevelwallassociation