getGridPosition_mod sourceΒΆ

  1module getGridPosition_mod
  2  ! MODULE getGridPosition_mod (prefix='gpos' category='8. Low-level utilities and constants')
  3  !
  4  !:Purpose:  A place to collect numerous interpolation related routines.
  5  !           The main task of the module is to compute the grid XY position from a lat-lon.
  6  !           This simply calls the ezsint routine gdxyfll for simple grids. For
  7  !           Yin-Yan grids it calls the function gpos_xyfll_yinYangGrid
  8  !           (see below in this module). There is also support for
  9  !           RPN Y grids, in which case it calls the subroutine gpos_xyfll_unstructGrid.
 10  !
 11  use kdTree2_mod
 12  use mathPhysConstants_mod
 13  use physicsFunctions_mod
 14  use utilities_mod
 15
 16  implicit none
 17  save
 18  private
 19
 20  ! public procedures
 21  public :: gpos_getPositionXY, gpos_gridIsOrca
 22
 23  integer, parameter :: maxNumLocalGridPointsSearch = 350
 24  integer, external  :: get_max_rss
 25
 26  type(kdtree2), pointer :: tree => null()
 27
 28contains
 29
 30  !---------------------------------------------------------
 31  ! gpos_getPositionXY
 32  !---------------------------------------------------------
 33  function gpos_getPositionXY( gdid, xpos_r4, ypos_r4, xpos2_r4, ypos2_r4,  &
 34                          lat_deg_r4, lon_deg_r4, subGridIndex ) result(ierr)
 35    !
 36    !:Purpose: Compute the grid XY position from a lat-lon. This
 37    !           simply calls the ezsint routine gdxyfll for simple grids. For
 38    !           Yin-Yan grids it calls the function gpos_xyfll_yinYangGrid
 39    !           (see below in this module). There is also support for
 40    !           RPN Y grids, in which case it calls the subroutine gpos_xyfll_unstructGrid.
 41    !
 42    implicit none
 43
 44    ! Arguments:
 45    integer, intent(in)  :: gdid
 46    integer, intent(out) :: subGridIndex
 47    real(4), intent(out) :: xpos_r4
 48    real(4), intent(out) :: ypos_r4
 49    real(4), intent(out) :: xpos2_r4
 50    real(4), intent(out) :: ypos2_r4
 51    real(4), intent(in)  :: lat_deg_r4
 52    real(4), intent(in)  :: lon_deg_r4
 53    ! Result:
 54    integer :: ierr  ! returned value of function
 55
 56    ! Locals:
 57    integer :: numSubGrids
 58    integer :: ezget_nsubGrids, gdxyfll, ezgprm
 59    character(len=1) :: grtyp
 60    integer :: ni, nj, ig1, ig2, ig3, ig4
 61
 62    numSubGrids = ezget_nsubGrids(gdid)
 63    xpos2_r4 = -999.0
 64    ypos2_r4 = -999.0
 65
 66    if ( numSubGrids == 1 ) then
 67
 68      ierr = ezgprm(gdid, grtyp, ni, nj, ig1, ig2, ig3, ig4)
 69
 70      if (grtyp == 'Y') then
 71
 72        !$omp critical
 73        ierr = gpos_xyfll_unstructGrid(gdid, xpos_r4, ypos_r4, lat_deg_r4, lon_deg_r4)
 74        !$omp end critical
 75
 76      else
 77
 78        ! Not an unstructured grid, nor a Yin-Yang grid, call the standard ezscint routine
 79        ierr = gdxyfll(gdid, xpos_r4, ypos_r4, lat_deg_r4, lon_deg_r4, 1)
 80
 81      end if
 82
 83      subGridIndex = 1
 84
 85    else
 86
 87      ! This is a Yin-Yang grid, do something different
 88      !$omp critical
 89      ierr = gpos_xyfll_yinYangGrid(gdid, xpos_r4, ypos_r4, xpos2_r4, ypos2_r4, &
 90                                    lat_deg_r4, lon_deg_r4, subGridIndex)
 91      !$omp end critical
 92
 93    end if    
 94
 95    if ( subGridIndex /= 3 ) then
 96      ! when only returning 1 position, copy values to pos2
 97      xpos2_r4 = xpos_r4
 98      ypos2_r4 = ypos_r4
 99    end if
100
101  end function gpos_getPositionXY
102
103  !---------------------------------------------------------
104  ! gpos_xyfll_yinYangGrid
105  !---------------------------------------------------------
106  function gpos_xyfll_yinYangGrid( gdid, xpos_r4, ypos_r4, xpos2_r4, ypos2_r4,  &
107                          lat_deg_r4, lon_deg_r4, subGridIndex ) result(ierr)
108    !
109    !:Purpose: Compute the grid XY position from a lat-lon for a Yin-Yang grid.
110    !           It returns locations from both the Yin and Yang
111    !           subgrids when in the overlap region, depending on the logical 
112    !           variable `useSingleValueOverlap`.
113    !
114    implicit none
115
116    ! Arguments:
117    integer, intent(in)  :: gdid
118    integer, intent(out) :: subGridIndex
119    real(4), intent(in)  :: lat_deg_r4
120    real(4), intent(in)  :: lon_deg_r4
121    real(4), intent(out) :: xpos_r4
122    real(4), intent(out) :: ypos_r4
123    real(4), intent(out) :: xpos2_r4
124    real(4), intent(out) :: ypos2_r4
125    ! Result:
126    integer :: ierr  ! returned value of function
127
128    ! Locals:
129    integer :: ezget_subGridids, gdgaxes, gdxyfll, ezgprm
130    integer :: EZscintIDvec(2)
131    integer, save :: EZscintIDvec1_old = -999
132    integer :: lonIndex, latIndex
133    real :: lonrot, latrot
134    real, allocatable, save :: ax_yin(:), ay_yin(:), ax_yan(:), ay_yan(:)
135    logical :: axesDifferent
136    character(len=1) :: grtyp
137    integer :: ni, nj, ig1, ig2, ig3, ig4
138    ! this controls which approach to use for interpolation within the YIN-YAN overlap
139    logical :: useSingleValueOverlap = .true.  
140
141    ierr = ezget_subGridids(gdid, EZscintIDvec)
142    ! get ni nj of subGrid, assume same for both YIN and YANG
143    ierr = ezgprm(EZscintIDvec(1), grtyp, ni, nj, ig1, ig2, ig3, ig4)
144
145    ! first check YIN
146    ierr = gdxyfll(EZscintIDvec(1), xpos_r4, ypos_r4, lat_deg_r4, lon_deg_r4, 1)
147
148    ! compute rotated lon and lat at obs location
149    axesDifferent = (EZscintIDvec1_old /= EZscintIDvec(1))
150    if (axesDifferent) then
151      write(*,*) 'gpos_getPositionXY: axesDifferent, compute needed parameters'
152      if (allocated(ax_yin)) deallocate(ax_yin,ay_yin)
153      allocate(ax_yin(ni),ay_yin(nj))
154      ierr = gdgaxes(EZscintIDvec(1), ax_yin, ay_yin)
155      EZscintIDvec1_old = EZscintIDvec(1)
156    end if
157    lonIndex = floor(xpos_r4)
158    if ( lonIndex >= 1 .and. (lonIndex+1) <= ni ) then
159      lonrot = ax_yin(lonIndex) + (ax_yin(lonIndex+1) - ax_yin(lonIndex)) *  &
160           (xpos_r4 - lonIndex)
161    else
162      lonrot = -999.0
163    end if
164    latIndex = floor(ypos_r4)
165    if ( latIndex >= 1 .and. (latIndex+1) <= nj ) then
166      latrot = ay_yin(latIndex) + (ay_yin(latIndex+1) - ay_yin(latIndex)) *  &
167           (ypos_r4 - latIndex)
168    else
169      latrot = -999.0
170    end if
171    subGridIndex = 1
172
173    if ( useSingleValueOverlap ) then
174
175      ! this approach is most similar to how ezsint works, preferentially take YIN
176
177      if ( lonrot < 45.0 .or. lonrot > 315.0 .or. latrot < -45.0 .or. latrot > 45.0 ) then
178        ! Outside YIN, therefore use YANG (assume it is inside YANG)
179        ierr = gdxyfll(EZscintIDvec(2), xpos_r4, ypos_r4, lat_deg_r4, lon_deg_r4, 1)
180        ypos_r4 = ypos_r4 + real(nj) ! shift from YANG position to Supergrid position
181        subGridIndex = 2
182      else
183        subGridIndex = 1
184      end if
185
186    else ! not useSingleValueOverlap
187
188      ! this approach returns both the YIN and YAN locations when point is inside both
189
190      if ( lonrot < 45.0 .or. lonrot > 315.0 .or. latrot < -45.0 .or. latrot > 45.0 ) then
191        ! Outside YIN, therefore use YANG (assume it is inside YANG)
192        ierr = gdxyfll(EZscintIDvec(2), xpos_r4, ypos_r4, lat_deg_r4, lon_deg_r4, 1)
193        ypos_r4 = ypos_r4 + real(nj) ! shift from YANG position to Supergrid position
194        subGridIndex = 2
195      else
196        ! inside YIN, check if also inside YANG
197        allocate(ax_yan(ni),ay_yan(nj))
198        ierr = gdgaxes(EZscintIDvec(2), ax_yan, ay_yan)
199        ierr = gdxyfll(EZscintIDvec(2), xpos2_r4, ypos2_r4, lat_deg_r4, lon_deg_r4, 1)
200        if ( lonIndex >= 1 .and. (lonIndex+1) <= ni ) then
201          lonrot = ax_yan(lonIndex) + (ax_yan(lonIndex+1) - ax_yan(lonIndex)) *  &
202               (xpos2_r4 - lonIndex)
203        else
204          lonrot = -999.0
205        end if
206        latIndex = floor(ypos2_r4)
207        if ( latIndex >= 1 .and. (latIndex+1) <= nj ) then
208          latrot = ay_yan(latIndex) + (ay_yan(latIndex+1) - ay_yan(latIndex)) *  &
209               (ypos2_r4 - latIndex)
210        else
211          latrot = -999.0
212        end if
213        deallocate(ax_yan,ay_yan)
214        if ( lonrot < 45.0 .or. lonrot > 315.0 .or. latrot < -45.0 .or. latrot > 45.0 ) then
215          ! outside YANG, only inside YIN
216          xpos2_r4 = -999.0
217          ypos2_r4 = -999.0
218          subGridIndex = 1
219        else
220          ! inside both YIN and YANG
221          ypos2_r4 = ypos2_r4 + real(nj) ! shift from YANG position to Supergrid position
222          subGridIndex = 3
223        end if
224      end if
225
226    end if
227
228  end function gpos_xyfll_yinYangGrid
229
230  !---------------------------------------------------------
231  ! gpos_xyfll_unstructGrid
232  !---------------------------------------------------------
233  function gpos_xyfll_unstructGrid( gdid, xpos_r4, ypos_r4, lat_deg_r4, lon_deg_r4 ) result(ierr)
234
235    !:Purpose: This function is used to interpolate from a RPN grid to a location
236    !           specified by a latitude and longitude.
237    !           It has special treatment for the ORCA12 and ORCA025 tri-polar grids
238    !           used for sea ice and ocean models.
239    !           The kdtree2 module is used to efficiently
240    !           perform this task. The kdtree itself is constructed on the first call.
241    implicit none
242
243    ! Arguments:
244    integer, intent(in)  :: gdid
245    real(4), intent(in)  :: lat_deg_r4
246    real(4), intent(in)  :: lon_deg_r4
247    real(4), intent(out) :: xpos_r4
248    real(4), intent(out) :: ypos_r4
249    ! Result:
250    integer :: ierr  ! returned value of function
251
252    ! Locals:
253    real(8) :: lon_rad_r8, lat_rad_r8, pertGridLonRad, pertGridLatRad
254    real(8), parameter :: deltaGrid = 0.00001d0
255    real(8) :: pertPosition(3)
256    real(8) :: gridSpacing
257    real(8) :: gridSpacingSquared, lowerLeftCornerDistSquared, lowerRightCornerDistSquared, upperLeftCornerDistSquared
258    integer, save :: gdidOld = -999
259    integer :: nx, ny
260    integer, save :: ni, nj
261    real(8), allocatable, save :: grid_lon_rad(:,:), grid_lat_rad(:,:)
262    real(4), save :: maxGridSpacing
263    integer, save :: startXIndex, startYIndex, endXIndex, endYIndex
264    character(len=1) :: grtyp
265    integer :: ig1, ig2, ig3, ig4
266    real(4), allocatable :: grid_lat_deg_r4(:,:), grid_lon_deg_r4(:,:)
267    integer :: closePointsIndex
268    integer :: gridIndex
269    integer :: xIndex, yIndex, xIndexMin, xIndexMax, yIndexMin, yIndexMax
270    integer :: ezgprm, gdll ! Functions
271    integer                   :: numLocalGridPointsFound
272    real(kdkind), allocatable :: positionArray(:,:)
273    type(kdtree2_result)      :: searchResults(maxNumLocalGridPointsSearch)
274    real(kdkind)              :: maxRadiusSquared
275    real(kdkind)              :: refPosition(3)
276
277    if ( gdid /= gdidOld .and. gdidOld > 0) then
278      write(*,*) 'gpos_xyfll_unstructGrid: gdid gdidOld = ',gdid, gdidOld
279      call utl_abort('gpos_xyfll_unstructGrid: only one grid expected. Change code !')
280    end if
281
282    ! create the kdtree on the first call
283    if (.not. associated(tree)) then
284      write(*,*) 'gpos_xyfll_unstructGrid: start creating kdtree'
285      write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
286      ierr = ezgprm(gdid, grtyp, ni, nj, ig1, ig2, ig3, ig4)
287
288      startXIndex = 1
289      startYIndex = 1
290      endXIndex = ni
291      endYIndex = nj
292      maxGridSpacing = 10000.0
293
294      if (allocated(grid_lat_rad)) deallocate(grid_lat_rad, grid_lon_rad)
295      allocate(grid_lat_rad(ni,nj), grid_lon_rad(ni,nj))
296      allocate(grid_lat_deg_r4(ni,nj), grid_lon_deg_r4(ni,nj))
297      ierr = gdll(gdid, grid_lat_deg_r4, grid_lon_deg_r4)
298      grid_lat_rad(:,:) = real(grid_lat_deg_r4(:,:),8)*MPC_RADIANS_PER_DEGREE_R8
299      grid_lon_rad(:,:) = real(grid_lon_deg_r4(:,:),8)*MPC_RADIANS_PER_DEGREE_R8
300      deallocate(grid_lat_deg_r4, grid_lon_deg_r4)
301      gdidOld = gdid
302
303      if( gpos_gridIsOrca(ni, nj, grid_lat_rad, grid_lon_rad) ) then
304        ! The last 2 columns (i=ni-1 and ni) are repetitions of the first 2 columns (i=1 and 2)
305        ! so both ends are removed from the search area.
306        startXIndex = 2
307        endXIndex = ni - 1
308        ! The last line (j=nj) is a repetition in reverse order of the line (j=nj-2)
309        ! and is thus eliminated from the search domain.
310        endYIndex = nj - 1
311      end if
312      if(ni == 1442 .and. nj == 1021) then
313        ! This is orca025
314        maxGridSpacing = 40000.0
315      end if
316
317      nx = endXIndex - startXIndex + 1
318      ny = endYIndex - startYIndex + 1
319
320      allocate(positionArray(3,nx*ny))
321
322      gridIndex = 0
323      do yIndex = startYIndex, endYIndex
324        do xIndex = startXIndex, endXIndex
325          gridIndex = gridIndex + 1
326          positionArray(:,gridIndex) = kdtree2_3dPosition(grid_lon_rad(xIndex,yIndex), grid_lat_rad(xIndex,yIndex))
327        end do
328      end do
329      tree => kdtree2_create(positionArray, sort=.true., rearrange=.true.) 
330      write(*,*) 'gpos_xyfll_unstructGrid: done creating kdtree'
331      write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
332
333    else
334
335      ierr = 0
336      nx = endXIndex - startXIndex + 1
337
338    end if
339
340    ! do the search
341    maxRadiusSquared = maxGridSpacing**2
342    lon_rad_r8 = real(lon_deg_r4,8)*MPC_RADIANS_PER_DEGREE_R8
343    lat_rad_r8 = real(lat_deg_r4,8)*MPC_RADIANS_PER_DEGREE_R8
344    refPosition(:) = kdtree2_3dPosition(lon_rad_r8, lat_rad_r8)
345
346    call kdtree2_r_nearest(tp=tree, qv=refPosition, r2=maxRadiusSquared, nfound=numLocalGridPointsFound,&
347                           nalloc=maxNumLocalGridPointsSearch, results=searchResults)
348    if (numLocalGridPointsFound > maxNumLocalGridPointsSearch) then
349      call utl_abort('gpos_xyfll_unstructGrid: the parameter maxNumLocalGridPointsSearch must be increased')
350    end if
351
352    if (numLocalGridPointsFound < 4) then
353      write(*,*) 'gpos_xyfll_unstructGrid: numLocalGridPointsFound = ',numLocalGridPointsFound
354      write(*,*) 'gpos_xyfll_unstructGrid: search radius           = ',maxGridSpacing,' meters'
355      write(*,*) 'gpos_xyfll_unstructGrid: obs lon, lat = ', lon_deg_r4, lat_deg_r4
356      call utl_abort('gpos_xyfll_unstructGrid: the search did not find 4 close points.')
357    end if
358
359    if (searchResults(1)%dis > maxRadiusSquared) then
360      write(*,*) 'gpos_xyfll_unstructGrid: No grid point found within ',maxGridSpacing,' meters'
361      write(*,*) 'of the reference location lat-lon (degrees): ',lat_deg_r4,lon_deg_r4
362      xpos_r4 = -999.0
363      ypos_r4 = -999.0
364      return
365    end if
366
367    ! We found the closest grid point to the reference point.
368    ! Now we need to determine in which of the 4 quadrants around the grid point the reference point lies.
369
370    closePointsIndex = 1
371
372    gridIndex = searchResults(closePointsIndex)%idx
373    yIndex = (gridIndex-1)/nx + startYIndex
374    xIndex = gridIndex - (yIndex-startYIndex)*nx + startXIndex - 1
375
376    ! Perturbing the closest grid point in the x direction
377
378    if ( xIndex < ni ) then
379
380      if ( grid_lon_rad(xIndex+1,yIndex) - grid_lon_rad(xIndex,yIndex) > 5.0d0 ) then
381        pertGridLonRad = grid_lon_rad(xIndex,yIndex) + (grid_lon_rad(xIndex+1,yIndex) - (grid_lon_rad(xIndex,yIndex) + 2.d0 * MPC_PI_R8))*deltaGrid
382      else if ( grid_lon_rad(xIndex+1,yIndex) - grid_lon_rad(xIndex,yIndex) < -5.0d0 ) then
383        pertGridLonRad = grid_lon_rad(xIndex,yIndex) + ((grid_lon_rad(xIndex+1,yIndex) + 2.d0 * MPC_PI_R8) - grid_lon_rad(xIndex,yIndex))*deltaGrid
384      else
385        pertGridLonRad = grid_lon_rad(xIndex,yIndex) + (grid_lon_rad(xIndex+1,yIndex) - grid_lon_rad(xIndex,yIndex))*deltaGrid
386      end if
387      pertGridLatRad = grid_lat_rad(xIndex,yIndex) + (grid_lat_rad(xIndex+1,yIndex) - grid_lat_rad(xIndex,yIndex))*deltaGrid
388
389      ! Calculate the distance between the reference point and the perturbed grid point.
390      pertPosition(:) = kdtree2_3dPosition(pertGridLonRad, pertGridLatRad)
391
392      gridSpacingSquared = sum( (pertPosition(:) - refPosition(:))**2 )
393
394      if (gridSpacingSquared < searchResults(closePointsIndex)%dis) then
395        xIndexMin = xIndex
396        xIndexMax = xIndex + 1
397      else
398        if ( xIndex > 1 ) then
399          xIndexMin = xIndex - 1
400          xIndexMax = xIndex
401        else
402          write(*,*) 'xIndex yIndex ', xIndex, yIndex
403          write(*,*) 'Closest distance = ',sqrt(searchResults(closePointsIndex)%dis)
404          write(*,*) 'Perturbed distance = ',sqrt(gridSpacingSquared)
405          call utl_abort('gpos_xyfll_unstructGrid: 1. the reference point is outside the grid.')
406        end if
407      end if
408
409    else
410
411      if ( grid_lon_rad(xIndex-1,yIndex) - grid_lon_rad(xIndex,yIndex) > 5.0d0 ) then
412        pertGridLonRad = grid_lon_rad(xIndex,yIndex) + (grid_lon_rad(xIndex-1,yIndex) - (grid_lon_rad(xIndex,yIndex) + 2.d0 * MPC_PI_R8))*deltaGrid
413      else if ( grid_lon_rad(xIndex-1,yIndex) - grid_lon_rad(xIndex,yIndex) < -5.0d0 ) then
414        pertGridLonRad = grid_lon_rad(xIndex,yIndex) + ((grid_lon_rad(xIndex-1,yIndex) + 2.d0 * MPC_PI_R8) - grid_lon_rad(xIndex,yIndex))*deltaGrid
415      else
416        pertGridLonRad = grid_lon_rad(xIndex,yIndex) + (grid_lon_rad(xIndex-1,yIndex) - grid_lon_rad(xIndex,yIndex))*deltaGrid
417      end if
418      pertGridLatRad = grid_lat_rad(xIndex,yIndex) + (grid_lat_rad(xIndex-1,yIndex) - grid_lat_rad(xIndex,yIndex))*deltaGrid
419
420      ! Calculate the distance between the reference point and the perturbed grid point.
421      pertPosition(:) = kdtree2_3dPosition(pertGridLonRad, pertGridLatRad)
422
423      gridSpacingSquared = sum( (pertPosition(:) - refPosition(:))**2 )
424
425      if (gridSpacingSquared < searchResults(closePointsIndex)%dis) then
426        xIndexMin = xIndex - 1
427        xIndexMax = xIndex
428      else
429        write(*,*) 'xIndex yIndex ', xIndex, yIndex
430        write(*,*) 'Closest distance = ',sqrt(searchResults(closePointsIndex)%dis)
431        write(*,*) 'Perturbed distance = ',sqrt(gridSpacingSquared)
432        call utl_abort('gpos_xyfll_unstructGrid: 2. the reference point is outside the grid.')
433      end if
434
435    end if
436
437    ! Perturbing the closest grid point in the y direction
438
439    if ( yIndex < nj ) then
440
441      if ( grid_lon_rad(xIndex,yIndex+1) - grid_lon_rad(xIndex,yIndex) > 5.0d0 ) then
442        pertGridLonRad = grid_lon_rad(xIndex,yIndex) + (grid_lon_rad(xIndex,yIndex+1) - (grid_lon_rad(xIndex,yIndex) + 2.d0 * MPC_PI_R8))*deltaGrid
443      else if ( grid_lon_rad(xIndex,yIndex+1) - grid_lon_rad(xIndex,yIndex) < -5.0d0 ) then
444        pertGridLonRad = grid_lon_rad(xIndex,yIndex) + ((grid_lon_rad(xIndex,yIndex+1) + 2.d0 * MPC_PI_R8) - grid_lon_rad(xIndex,yIndex))*deltaGrid
445      else
446        pertGridLonRad = grid_lon_rad(xIndex,yIndex) + (grid_lon_rad(xIndex,yIndex+1) - grid_lon_rad(xIndex,yIndex))*deltaGrid
447      end if
448      pertGridLatRad = grid_lat_rad(xIndex,yIndex) + (grid_lat_rad(xIndex,yIndex+1) - grid_lat_rad(xIndex,yIndex))*deltaGrid
449
450      ! Calculate the distance between the reference point and the perturbed grid point.
451      pertPosition(:) = kdtree2_3dPosition(pertGridLonRad, pertGridLatRad)
452
453      gridSpacingSquared = sum( (pertPosition(:) - refPosition(:))**2 )
454
455      if (gridSpacingSquared < searchResults(closePointsIndex)%dis) then
456        yIndexMin = yIndex
457        yIndexMax = yIndex + 1
458      else
459        if ( yIndex > 1 ) then
460          yIndexMin = yIndex - 1
461          yIndexMax = yIndex
462        else
463          write(*,*) 'xIndex yIndex ', xIndex, yIndex
464          write(*,*) 'Closest distance = ',sqrt(searchResults(closePointsIndex)%dis)
465          write(*,*) 'Perturbed distance = ',sqrt(gridSpacingSquared)
466          call utl_abort('gpos_xyfll_unstructGrid: 3. the reference point is outside the grid.')
467        end if
468      end if
469
470    else
471
472      if ( grid_lon_rad(xIndex,yIndex-1) - grid_lon_rad(xIndex,yIndex) > 5.0d0 ) then
473        pertGridLonRad = grid_lon_rad(xIndex,yIndex) + (grid_lon_rad(xIndex,yIndex-1) - (grid_lon_rad(xIndex,yIndex) + 2.d0 * MPC_PI_R8))*deltaGrid
474      else if ( grid_lon_rad(xIndex,yIndex-1) - grid_lon_rad(xIndex,yIndex) < -5.0d0 ) then
475        pertGridLonRad = grid_lon_rad(xIndex,yIndex) + ((grid_lon_rad(xIndex,yIndex-1) + 2.d0 * MPC_PI_R8) - grid_lon_rad(xIndex,yIndex))*deltaGrid
476      else
477        pertGridLonRad = grid_lon_rad(xIndex,yIndex) + (grid_lon_rad(xIndex,yIndex-1) - grid_lon_rad(xIndex,yIndex))*deltaGrid
478      end if
479      pertGridLatRad = grid_lat_rad(xIndex,yIndex) + (grid_lat_rad(xIndex,yIndex-1) - grid_lat_rad(xIndex,yIndex))*deltaGrid
480
481      ! Calculate the distance between the reference point and the perturbed grid point.
482      pertPosition(:) = kdtree2_3dPosition(pertGridLonRad, pertGridLatRad)
483
484      gridSpacingSquared = sum( (pertPosition(:) - refPosition(:))**2 )
485
486      if (gridSpacingSquared < searchResults(closePointsIndex)%dis) then
487        yIndexMin = yIndex - 1
488        yIndexMax = yIndex
489      else
490        write(*,*) 'xIndex yIndex ', xIndex, yIndex
491        write(*,*) 'Closest distance = ',sqrt(searchResults(closePointsIndex)%dis)
492        write(*,*) 'Perturbed distance = ',sqrt(gridSpacingSquared)
493        call utl_abort('gpos_xyfll_unstructGrid: 4. the reference point is outside the grid.')
494      end if
495
496    end if
497
498    ! Calculate real x and y position in the grid.
499    ! kdtree returns distance in square meters !
500
501    gridSpacingSquared = phf_calcDistance(grid_lat_rad(xIndexMin,yIndexMin), grid_lon_rad(xIndexMin,yIndexMin), &
502           grid_lat_rad(xIndexMax,yIndexMin), grid_lon_rad(xIndexMax,yIndexMin))**2
503
504    lowerLeftCornerDistSquared = phf_calcDistance(grid_lat_rad(xIndexMin,yIndexMin), grid_lon_rad(xIndexMin,yIndexMin), &
505           lat_rad_r8, lon_rad_r8)**2
506
507    lowerRightCornerDistSquared = phf_calcDistance(grid_lat_rad(xIndexMax,yIndexMin), grid_lon_rad(xIndexMax,yIndexMin), &
508           lat_rad_r8, lon_rad_r8)**2
509
510    xpos_r4 = real(xIndexMin) + (lowerLeftCornerDistSquared + gridSpacingSquared - lowerRightCornerDistSquared)/(2.0*(gridSpacingSquared))
511
512    gridSpacingSquared = phf_calcDistance(grid_lat_rad(xIndexMin,yIndexMin), grid_lon_rad(xIndexMin,yIndexMin), &
513           grid_lat_rad(xIndexMin,yIndexMax), grid_lon_rad(xIndexMin,yIndexMax))**2
514
515    upperLeftCornerDistSquared = phf_calcDistance(grid_lat_rad(xIndexMin,yIndexMax), grid_lon_rad(xIndexMin,yIndexMax), &
516           lat_rad_r8, lon_rad_r8)**2
517
518    ypos_r4 = real(yIndexMin) + (lowerLeftCornerDistSquared + gridSpacingSquared  - upperLeftCornerDistSquared)/(2.0*(gridSpacingSquared))
519
520    if ( abs(ypos_r4 - yIndexMin) > 2.0 .or. abs(xpos_r4 - xIndexMin) > 4.3 ) then
521      write(*,*) 'xpos_r4 = ',xpos_r4
522      write(*,*) 'ypos_r4 = ',ypos_r4
523      write(*,*) 'xIndexMin = ',xIndexMin
524      write(*,*) 'yIndexMin = ',yIndexMin
525      do closePointsIndex = 1,min(numLocalGridPointsFound,5)
526        gridIndex = searchResults(closePointsIndex)%idx
527        yIndex = (gridIndex-1)/nx + startYIndex
528        xIndex = gridIndex - (yIndex-startYIndex)*nx
529        write(*,*) 'closePointsIndex: ', closePointsIndex
530        write(*,*) 'xIndex yIndex ', xIndex, yIndex
531        write(*,*) 'idx: ',searchResults(closePointsIndex)%idx
532        write(*,*) 'distance (meters): ',sqrt(searchResults(closePointsIndex)%dis)
533      end do
534      write(*,*) 'lowerLeftCornerDist = ',sqrt(lowerLeftCornerDistSquared)
535      write(*,*) 'lowerRightCornerDist = ',sqrt(lowerRightCornerDistSquared)
536      write(*,*) 'upperLeftCornerDist = ',sqrt(upperLeftCornerDistSquared)
537      write(*,*) 'y gridSpacing = ',sqrt(gridSpacingSquared)
538      gridSpacing = phf_calcDistance(grid_lat_rad(xIndexMin,yIndexMin), grid_lon_rad(xIndexMin,yIndexMin), &
539           grid_lat_rad(xIndexMax,yIndexMin), grid_lon_rad(xIndexMax,yIndexMin))
540      write(*,*) 'x gridSpacing = ',gridSpacing
541      write(*,*) 'lon-lat (deg):'
542      write(*,*) 'reference point: ',lon_deg_r4, lat_deg_r4
543      write(*,*) xIndexMin,yIndexMin,grid_lon_rad(xIndexMin,yIndexMin)* MPC_DEGREES_PER_RADIAN_R8,grid_lat_rad(xIndexMin,yIndexMin)* MPC_DEGREES_PER_RADIAN_R8
544      write(*,*) xIndexMax,yIndexMin,grid_lon_rad(xIndexMax,yIndexMin)* MPC_DEGREES_PER_RADIAN_R8,grid_lat_rad(xIndexMax,yIndexMin)* MPC_DEGREES_PER_RADIAN_R8
545      write(*,*) xIndexMax+1,yIndexMin,grid_lon_rad(xIndexMax+1,yIndexMin)* MPC_DEGREES_PER_RADIAN_R8,grid_lat_rad(xIndexMax+1,yIndexMin)* MPC_DEGREES_PER_RADIAN_R8
546      write(*,*) xIndexMin,yIndexMax,grid_lon_rad(xIndexMin,yIndexMax)* MPC_DEGREES_PER_RADIAN_R8,grid_lat_rad(xIndexMin,yIndexMax)* MPC_DEGREES_PER_RADIAN_R8
547      write(*,*) xIndexMax,yIndexMax,grid_lon_rad(xIndexMax,yIndexMax)* MPC_DEGREES_PER_RADIAN_R8,grid_lat_rad(xIndexMax,yIndexMax)* MPC_DEGREES_PER_RADIAN_R8
548      write(*,*) xIndexMax+1,yIndexMax,grid_lon_rad(xIndexMax+1,yIndexMax)* MPC_DEGREES_PER_RADIAN_R8,grid_lat_rad(xIndexMax+1,yIndexMax)* MPC_DEGREES_PER_RADIAN_R8
549      write(*,*) xIndexMin,yIndexMax+1,grid_lon_rad(xIndexMin,yIndexMax+1)* MPC_DEGREES_PER_RADIAN_R8,grid_lat_rad(xIndexMin,yIndexMax+1)* MPC_DEGREES_PER_RADIAN_R8
550      write(*,*) xIndexMax,yIndexMax+1,grid_lon_rad(xIndexMax,yIndexMax+1)* MPC_DEGREES_PER_RADIAN_R8,grid_lat_rad(xIndexMax,yIndexMax+1)* MPC_DEGREES_PER_RADIAN_R8
551      write(*,*) xIndexMax+1,yIndexMax+1,grid_lon_rad(xIndexMax+1,yIndexMax+1)* MPC_DEGREES_PER_RADIAN_R8,grid_lat_rad(xIndexMax+1,yIndexMax+1)* MPC_DEGREES_PER_RADIAN_R8
552      call utl_abort('gpos_xyfll_unstructGrid: Check code !')
553    end if
554
555  end function gpos_xyfll_unstructGrid
556
557  !---------------------------------------------------------
558  ! gpos_gridIsOrca
559  !---------------------------------------------------------
560  function gpos_gridIsOrca( ni, nj, latitude, longitude ) result(gridIsOrca)
561
562    !:Purpose: Check if the grid is of the orca tri-polar family
563    implicit none
564
565   ! Arguments:
566    integer, intent(in) :: ni                ! first  dimension of the grid
567    integer, intent(in) :: nj                ! second dimension of the grid
568    real(8), intent(in) :: longitude(ni,nj)  ! longitude (degrees or radians)
569    real(8), intent(in) :: latitude(ni,nj)   ! latitude  (degrees or radians)
570    ! Result:
571    logical :: gridIsOrca  ! returned value of function
572
573    ! Locals:
574    integer :: xIndex, yIndex
575
576    gridIsOrca = .true.
577
578    ! The last 2 columns (ni-1 and ni) are repetitions of the first 2 columns (i=1 and 2)
579    ! Check that is true
580    do yIndex = 1, nj
581      if(latitude(1,yIndex) /= latitude(ni-1,yIndex) .or. &
582         latitude(2,yIndex) /= latitude(ni,  yIndex) .or. &
583         longitude(1,yIndex) /= longitude(ni-1,yIndex) .or. &
584         longitude(2,yIndex) /= longitude(ni,  yIndex)) then
585        gridIsOrca = .false.
586        write(*,*) '1. Not an orca grid',latitude(1,yIndex),latitude(ni-1,yIndex),latitude(2,yIndex),latitude(ni,  yIndex),longitude(1,yIndex),longitude(ni-1,yIndex),longitude(2,yIndex),longitude(ni,  yIndex)
587        return
588      end if
589    end do
590    
591    ! The last line (j=nj) is a repetition in reverse order of the line (j=nj-2)
592    ! Check that is true (does not work at (ni/2 + 1) for orca025 lats as with the letkf ocean unit test)
593    do xIndex = 2, ni
594      if(longitude(xIndex,nj) /= longitude(ni-xIndex+2,nj-2)) then
595        gridIsOrca = .false.
596        write(*,*) '2. Not an orca grid',xIndex,longitude(xIndex,nj),longitude(ni-xIndex+2,nj-2)
597        return
598      end if
599    end do
600    do xIndex = 2, ni/2
601      if(latitude(xIndex,nj) /= latitude(ni-xIndex+2,nj-2)) then
602        gridIsOrca = .false.
603        write(*,*) '3. Not an orca grid',xIndex,latitude(xIndex,nj),latitude(ni-xIndex+2,nj-2)
604        return
605      end if
606    end do
607    do xIndex = ni/2 + 2, ni
608      if(latitude(xIndex,nj) /= latitude(ni-xIndex+2,nj-2)) then
609        gridIsOrca = .false.
610        write(*,*) '3. Not an orca grid',xIndex,latitude(xIndex,nj),latitude(ni-xIndex+2,nj-2)
611        return
612      end if
613    end do
614
615  end function gpos_gridIsOrca
616
617end module getGridPosition_mod