FMS  2024.03
Flexible Modeling System
diag_grid.F90
1 !***********************************************************************
2 !* GNU Lesser General Public License
3 !*
4 !* This file is part of the GFDL Flexible Modeling System (FMS).
5 !*
6 !* FMS is free software: you can redistribute it and/or modify it under
7 !* the terms of the GNU Lesser General Public License as published by
8 !* the Free Software Foundation, either version 3 of the License, or (at
9 !* your option) any later version.
10 !*
11 !* FMS is distributed in the hope that it will be useful, but WITHOUT
12 !* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 !* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 !* for more details.
15 !*
16 !* You should have received a copy of the GNU Lesser General Public
17 !* License along with FMS. If not, see <http://www.gnu.org/licenses/>.
18 !***********************************************************************
19 !> @defgroup diag_grid_mod diag_grid_mod
20 !> @ingroup diag_manager
21 !> @brief diag_grid_mod is a set of procedures to work with the
22 !! model's global grid to allow regional output.
23 !!
24 !> @author Seth Underwood seth.underwood@noaa.gov
25 !!
26 !! <TT>diag_grid_mod</TT> contains useful utilities for dealing
27 !! with, mostly, regional output for grids other than the standard
28 !! lat/lon grid. This module contains three public procedures <TT>
29 !! diag_grid_init</TT>, which is shared globably in the <TT>
30 !! diag_manager_mod</TT>, <TT>diag_grid_end</TT> which will free
31 !! up memory used during the register field calls, and
32 !! <TT>get_local_indexes</TT>. The <TT>send_global_grid</TT>
33 !! procedure is called by the model that creates the global grid.
34 !! <TT>send_global_grid</TT> needs to be called before any fields
35 !! are registered that will output only regions. <TT>get_local_indexes</TT>
36 !! is to be called by the <TT>diag_manager_mod</TT> to discover the
37 !! global indexes defining a subregion on the tile.
38 
39 MODULE diag_grid_mod
40 use platform_mod
41 
42  ! <INFO>
43  ! <FUTURE>
44  ! Multi-tile regional output in the cubed sphere.
45  ! </FUTURE>
46  ! <FUTURE>
47  ! Single grid in the tri-polar grid.
48  ! </FUTURE>
49  ! <FUTURE>
50  ! Multi-tile regional output in the tri-polar grid.
51  ! </FUTURE>
52  ! <FUTURE>
53  ! Regional output using array masking. This should allow
54  ! regional output to work on any current or future grid.
55  ! </FUTURE>
56  ! </INFO>
57 
58  USE constants_mod, ONLY: deg_to_rad, rad_to_deg, radius
59  USE fms_mod, ONLY: write_version_number, error_mesg, warning, fatal,&
60  & mpp_pe
61  USE mpp_mod, ONLY: mpp_root_pe, mpp_npes, mpp_max, mpp_min
62  USE mpp_domains_mod, ONLY: domain2d, mpp_get_tile_id,&
64 
65  IMPLICIT NONE
66 
67  ! Include variable "version" to be written to log file.
68 #include<file_version.h>
69 
70  !> @brief Private type to hold the model's global grid data, and other grid information for use
71  !! in this module.
72  !> @ingroup diag_grid_mod
73  type, private :: diag_global_grid_type
74  REAL, allocatable, DIMENSION(:,:) :: glo_lat !< The latitude values on the global grid.
75  REAL, allocatable, DIMENSION(:,:) :: glo_lon !< The longitude values on the global grid.
76  REAL, allocatable, DIMENSION(:,:) :: aglo_lat !< The latitude values on the global a-grid. Here
77  !! we expect isc-1:iec+1 and
78  !! jsc=1:jec+1 to be passed in.
79  REAL, allocatable, DIMENSION(:,:) :: aglo_lon !< The longitude values on the global a-grid. Here
80  !! we expec isc-1:iec+j and
81  !! jsc-1:jec+1 to be passed in.
82  INTEGER :: myxbegin !< The starting index of the compute domain on the current PE.
83  INTEGER :: myybegin !< The starting index of the compute domain on the current PE.
84  INTEGER :: dimi !< The dimension of the global grid in the 'i' / longitudal direction.
85  INTEGER :: dimj !< The dimension of the global grid in the 'j' / latitudal direction.
86  INTEGER :: adimi !< The dimension of the global a-grid in the 'i' / longitudal direction. Again,
87  !! the expected dimension for diag_grid_mod is isc-1:iec+1.
88  INTEGER :: adimj !< The dimension of the global a-grid in the 'j' / latitudal direction. Again,
89  !! the expected dimension for diag_grid_mod is jsc-1:jec+1.
90  INTEGER :: tile_number !< The tile the <TT>glo_lat</TT> and <TT>glo_lon</TT> define.
91  INTEGER :: ntiles !< The number of tiles.
92  INTEGER :: pestart !< The starting PE number for the current tile.
93  INTEGER :: peend !< The ending PE number for the current tile.
94  CHARACTER(len=128) :: grid_type !< The global grid type.
95  END TYPE diag_global_grid_type
96 
97  !> @brief Private type to hold the corresponding (x,y,z) location for a (lat,lon)
98  !! location.
99  !> @ingroup diag_grid_mod
100  type, private :: point
101  REAL :: x !< The x value of the (x,y,z) coordinates.
102  REAL :: y !< The y value of the (x,y,z) coordinates.
103  REAL :: z !< The z value of the (x,y,z) coordinates.
104  END TYPE point
105 
106 !> @addtogroup diag_grid_mod
107 !> @{
108 
109  TYPE(diag_global_grid_type) :: diag_global_grid !< Variable to hold the global grid data
110 
111  LOGICAL :: diag_grid_initialized = .false. !< Indicates if the diag_grid_mod has been initialized.
112 
113  PRIVATE
116 
117 CONTAINS
118 
119  !> @brief Send the global grid to the <TT>diag_manager_mod</TT> for
120  !! regional output.
121  !!
122  !> In order for the diag_manager to do regional output for grids
123  !! other than the standard lat/lon grid, the <TT>
124  !! diag_manager_mod</TT> needs to know the the latitude and
125  !! longitude values for the entire global grid. This procedure
126  !! is the mechanism the models will use to share their grid with
127  !! the diagnostic manager.
128  !!
129  !! This procedure needs to be called after the grid is created,
130  !! and before the first call to register the fields.
131  SUBROUTINE diag_grid_init(domain, glo_lat, glo_lon, aglo_lat, aglo_lon)
132  TYPE(domain2d), INTENT(in) :: domain !< The domain to which the grid data corresponds.
133  CLASS(*), INTENT(in), DIMENSION(:,:) :: glo_lat !< The latitude information for the grid tile.
134  CLASS(*), INTENT(in), DIMENSION(:,:) :: glo_lon !< The longitude information for the grid tile.
135  CLASS(*), INTENT(in), DIMENSION(:,:) :: aglo_lat !< The latitude information for the a-grid tile.
136  CLASS(*), INTENT(in), DIMENSION(:,:) :: aglo_lon !< The longitude information for the a-grid tile.
137 
138  INTEGER, DIMENSION(1) :: tile
139  INTEGER :: ntiles
140  INTEGER :: stat
141  INTEGER :: i_dim, j_dim
142  INTEGER :: ai_dim, aj_dim
143  INTEGER, DIMENSION(2) :: latdim, londim
144  INTEGER, DIMENSION(2) :: alatdim, alondim
145  INTEGER :: mype, npes, npespertile
146  INTEGER, ALLOCATABLE, DIMENSION(:) :: xbegin, xend, ybegin, yend
147 
148  ! Write the file version to the logfile
149  CALL write_version_number("DIAG_GRID_MOD", version)
150 
151  ! Verify all allocatable / pointers for diag_global_grid hare not
152  ! allocated / associated.
153  IF ( ALLOCATED(xbegin) ) DEALLOCATE(xbegin)
154  IF ( ALLOCATED(ybegin) ) DEALLOCATE(ybegin)
155  IF ( ALLOCATED(xend) ) DEALLOCATE(xend)
156  IF ( ALLOCATED(yend) ) DEALLOCATE(yend)
157 
158  ! What is my PE
159  mype = mpp_pe() -mpp_root_pe() + 1
160 
161  ! Get the domain/pe layout, and allocate the [xy]begin|end arrays/pointers
162  npes = mpp_npes()
163  ALLOCATE(xbegin(npes), &
164  & ybegin(npes), &
165  & xend(npes), &
166  & yend(npes), stat=stat)
167  IF ( stat .NE. 0 ) THEN
168  CALL error_mesg('diag_grid_mod::diag_grid_init',&
169  &'Could not allocate memory for the compute grid indices&
170  &.', fatal)
171  END IF
172 
173  ! Get tile information
174  ntiles = mpp_get_ntile_count(domain)
175  tile = mpp_get_tile_id(domain)
176 
177  ! Number of PEs per tile
178  npespertile = npes / ntiles
179  diag_global_grid%peEnd = npespertile * tile(1)
180  diag_global_grid%peStart = diag_global_grid%peEnd - npespertile + 1
181 
182  ! Get the compute domains
183  CALL mpp_get_compute_domains(domain,&
184  & xbegin=xbegin, xend=xend,&
185  & ybegin=ybegin, yend=yend)
186 
187  ! Module initialized
188  diag_grid_initialized = .true.
189 
190  ! Get the size of the grids
191  latdim = shape(glo_lat)
192  londim = shape(glo_lon)
193  IF ( (latdim(1) == londim(1)) .AND.&
194  &(latdim(2) == londim(2)) ) THEN
195  i_dim = latdim(1)
196  j_dim = latdim(2)
197  ELSE
198  CALL error_mesg('diag_grid_mod::diag_grid_init',&
199  &'glo_lat and glo_lon must be the same shape.', fatal)
200  END IF
201 
202  ! Same thing for the a-grid
203  alatdim = shape(aglo_lat)
204  alondim = shape(aglo_lon)
205  IF ( (alatdim(1) == alondim(1)) .AND. &
206  &(alatdim(2) == alondim(2)) ) THEN
207  IF ( tile(1) == 4 .OR. tile(1) == 5 ) THEN
208  ! These tiles need to be transposed.
209  ai_dim = alatdim(2)
210  aj_dim = alatdim(1)
211  ELSE
212  ai_dim = alatdim(1)
213  aj_dim = alatdim(2)
214  END IF
215  ELSE
216  CALL error_mesg('diag_grid_mod::diag_grid_init',&
217  & "a-grid's glo_lat and glo_lon must be the same shape.", fatal)
218  END IF
219 
220  ! Allocate the grid arrays
221  IF ( allocated(diag_global_grid%glo_lat) .OR.&
222  & allocated(diag_global_grid%glo_lon) ) THEN
223  IF ( mpp_pe() == mpp_root_pe() ) &
224  & CALL error_mesg('diag_grid_mod::diag_grid_init',&
225  &'The global grid has already been initialized', warning)
226  ELSE
227  ALLOCATE(diag_global_grid%glo_lat(i_dim,j_dim),&
228  & diag_global_grid%glo_lon(i_dim,j_dim), stat=stat)
229  IF ( stat .NE. 0 ) THEN
230  CALL error_mesg('diag_grid_mod::diag_grid_init',&
231  &'Could not allocate memory for the global grid.', fatal)
232  END IF
233  END IF
234 
235  ! Same thing for the a-grid
236  IF ( allocated(diag_global_grid%aglo_lat) .OR.&
237  & allocated(diag_global_grid%aglo_lon) ) THEN
238  IF ( mpp_pe() == mpp_root_pe() ) &
239  & CALL error_mesg('diag_grid_mod::diag_grid_init',&
240  &'The global a-grid has already been initialized', warning)
241  ELSE
242  ALLOCATE(diag_global_grid%aglo_lat(0:ai_dim-1,0:aj_dim-1),&
243  & diag_global_grid%aglo_lon(0:ai_dim-1,0:aj_dim-1), stat=stat)
244  IF ( stat .NE. 0 ) THEN
245  CALL error_mesg('diag_global_mod::diag_grid_init',&
246  &'Could not allocate memory for the global a-grid', fatal)
247  END IF
248  END IF
249 
250  ! Set the values for diag_global_grid
251 
252  ! If we are on tile 4 or 5, we need to transpose the grid to get
253  ! this to work.
254  IF ( tile(1) == 4 .OR. tile(1) == 5 ) THEN
255  SELECT TYPE (aglo_lat)
256  TYPE IS (real(kind=r4_kind))
257  diag_global_grid%aglo_lat = transpose(aglo_lat)
258  TYPE IS (real(kind=r8_kind))
259  diag_global_grid%aglo_lat = transpose(real(aglo_lat))
260  CLASS DEFAULT
261  CALL error_mesg('diag_grid_mod::diag_grid_init',&
262  & 'The a-grid latitude data is not one of the supported types of real(kind=4) or real(kind=8)', fatal)
263  END SELECT
264 
265  SELECT TYPE (aglo_lon)
266  TYPE IS (real(kind=r4_kind))
267  diag_global_grid%aglo_lon = transpose(aglo_lon)
268  TYPE IS (real(kind=r8_kind))
269  diag_global_grid%aglo_lon = transpose(real(aglo_lon))
270  CLASS DEFAULT
271  CALL error_mesg('diag_grid_mod::diag_grid_init',&
272  & 'The a-grid longitude data is not one of the supported types of real(kind=4) or real(kind=8)', fatal)
273  END SELECT
274  ELSE
275  SELECT TYPE (aglo_lat)
276  TYPE IS (real(kind=r4_kind))
277  diag_global_grid%aglo_lat = aglo_lat
278  TYPE IS (real(kind=r8_kind))
279  diag_global_grid%aglo_lat = real(aglo_lat)
280  CLASS DEFAULT
281  CALL error_mesg('diag_grid_mod::diag_grid_init',&
282  & 'The a-grid latitude data is not one of the supported types of real(kind=4) or real(kind=8)', fatal)
283  END SELECT
284 
285  SELECT TYPE (aglo_lon)
286  TYPE IS (real(kind=r4_kind))
287  diag_global_grid%aglo_lon = aglo_lon
288  TYPE IS (real(kind=r8_kind))
289  diag_global_grid%aglo_lon = real(aglo_lon)
290  CLASS DEFAULT
291  CALL error_mesg('diag_grid_mod::diag_grid_init',&
292  & 'The a-grid longitude data is not one of the supported types of real(kind=4) or real(kind=8)', fatal)
293  END SELECT
294  END IF
295 
296  SELECT TYPE (glo_lat)
297  TYPE IS (real(kind=r4_kind))
298  diag_global_grid%glo_lat = glo_lat
299  TYPE IS (real(kind=r8_kind))
300  diag_global_grid%glo_lat = real(glo_lat)
301  CLASS DEFAULT
302  CALL error_mesg('diag_grid_mod::diag_grid_init',&
303  & 'The grid latitude data is not one of the supported types of real(kind=4) or real(kind=8)', fatal)
304  END SELECT
305 
306  SELECT TYPE (glo_lon)
307  TYPE IS (real(kind=r4_kind))
308  diag_global_grid%glo_lon = glo_lon
309  TYPE IS (real(kind=r8_kind))
310  diag_global_grid%glo_lon = real(glo_lon)
311  CLASS DEFAULT
312  CALL error_mesg('diag_grid_mod::diag_grid_init',&
313  & 'The grid longitude data is not one of the supported types of real(kind=4) or real(kind=8)', fatal)
314  END SELECT
315 
316  diag_global_grid%dimI = i_dim
317  diag_global_grid%dimJ = j_dim
318  diag_global_grid%adimI = ai_dim
319  diag_global_grid%adimJ = aj_dim
320  !--- For the nested model, the nested region only has 1 tile ( ntiles = 1) but
321  !--- the tile_id is 7 for the nested region. In the routine get_local_indexes,
322  !--- local variables ijMin and ijMax have dimesnion (ntiles) and will access
323  !--- ijMin(diag_global_grid%tile_number,:). For the nested region, ntiles = 1 and
324  !--- diag_global_grid%tile_number = 7 will cause out of bounds. So need to
325  !--- set diag_global_grid%tile_number = 1 when ntiles = 1 for the nested model.
326  if(ntiles == 1) then
327  diag_global_grid%tile_number = 1
328  else
329  diag_global_grid%tile_number = tile(1)
330  endif
331  diag_global_grid%ntiles = ntiles
332  diag_global_grid%myXbegin = xbegin(mype)
333  diag_global_grid%myYbegin = ybegin(mype)
334 
335  ! Unallocate arrays used here
336  DEALLOCATE(xbegin)
337  DEALLOCATE(ybegin)
338  DEALLOCATE(xend)
339  DEALLOCATE(yend)
340  END SUBROUTINE diag_grid_init
341 
342  !> @brief Unallocate the diag_global_grid variable.
343  !!
344  !> The <TT>diag_global_grid</TT> variable is only needed during
345  !! the register field calls, and then only if there are fields
346  !! requestion regional output. Once all the register fields
347  !! calls are complete (before the first <TT>send_data</TT> call
348  !! this procedure can be called to free up memory.
349  SUBROUTINE diag_grid_end()
350 
351  IF ( diag_grid_initialized ) THEN
352  ! De-allocate grid
353  IF ( allocated(diag_global_grid%glo_lat) ) THEN
354  DEALLOCATE(diag_global_grid%glo_lat)
355  ELSE IF ( mpp_pe() == mpp_root_pe() ) THEN
356  CALL error_mesg('diag_grid_mod::diag_grid_end',&
357  &'diag_global_grid%glo_lat was not allocated.', warning)
358  END IF
359 
360  IF ( allocated(diag_global_grid%glo_lon) ) THEN
361  DEALLOCATE(diag_global_grid%glo_lon)
362  ELSE IF ( mpp_pe() == mpp_root_pe() ) THEN
363  CALL error_mesg('diag_grid_mod::diag_grid_end',&
364  &'diag_global_grid%glo_lon was not allocated.', warning)
365  END IF
366  ! De-allocate a-grid
367  IF ( allocated(diag_global_grid%aglo_lat) ) THEN
368  DEALLOCATE(diag_global_grid%aglo_lat)
369  ELSE IF ( mpp_pe() == mpp_root_pe() ) THEN
370  CALL error_mesg('diag_grid_mod::diag_grid_end',&
371  &'diag_global_grid%aglo_lat was not allocated.', warning)
372  END IF
373 
374  IF ( allocated(diag_global_grid%aglo_lon) ) THEN
375  DEALLOCATE(diag_global_grid%aglo_lon)
376  ELSE IF ( mpp_pe() == mpp_root_pe() ) THEN
377  CALL error_mesg('diag_grid_mod::diag_grid_end',&
378  &'diag_global_grid%aglo_lon was not allocated.', warning)
379  END IF
380 
381  diag_grid_initialized = .false.
382  END IF
383  END SUBROUTINE diag_grid_end
384 
385  !> @brief Find the local start and local end indexes on the local PE
386  !! for regional output.
387  !!
388  !> Given a defined region, find the local indexes on the local
389  !! PE surrounding the region.
390  SUBROUTINE get_local_indexes(latStart, latEnd, lonStart, lonEnd,&
391  & istart, iend, jstart, jend)
392  REAL, INTENT(in) :: latstart !< lat start angles
393  REAL, INTENT(in) :: lonstart !< lon start angles
394  REAL, INTENT(in) :: latend !< lat end angles
395  REAL, INTENT(in) :: lonend !< lon end angles
396  INTEGER, INTENT(out) :: istart !< i start indexes
397  INTEGER, INTENT(out) :: jstart !< j start indexes
398  INTEGER, INTENT(out) :: iend !< i end indexes
399  INTEGER, INTENT(out) :: jend !< j end indexes
400 
401  REAL, ALLOCATABLE, DIMENSION(:,:) :: delta_lat, delta_lon, grid_lon
402 
403  REAL, DIMENSION(4) :: dists_lon, dists_lat
404  REAL :: lonendadj, my_lonstart, my_lonend
405 
406  INTEGER, ALLOCATABLE, DIMENSION(:,:) :: ijmin, ijmax
407  INTEGER :: mytile, ntiles, i, j, k, dimi, dimj, istat
408  INTEGER :: count
409 
410  LOGICAL :: onmype
411 
412  !For cfsite potential fix.
413  INTEGER :: mini
414  INTEGER :: minj
415  REAL :: minimum_distance
416  REAL :: global_min_distance
417  INTEGER :: rank_buf
418 
419  IF ( .NOT. diag_grid_initialized )&
420  & CALL error_mesg('diag_grid_mod::get_local_indexes',&
421  &'Module not initialized, first initialze module with a call &
422  &to diag_grid_init', fatal)
423 
424  ! Make adjustment for negative longitude values
425  if ( lonstart < 0. ) then
426  my_lonstart = lonstart + 360.
427  else
428  my_lonstart = lonstart
429  end if
430  if ( lonend < 0. ) then
431  my_lonend = lonend + 360.
432  else
433  my_lonend = lonend
434  end if
435 
436  IF (latstart .EQ. latend .AND. my_lonstart .EQ. my_lonend) THEN
437 
438  !For a single point, use the a-grid longitude and latitude
439  !values.
440 
441  mytile = diag_global_grid%tile_number
442  ntiles = diag_global_grid%ntiles
443 
444  allocate(ijmin(ntiles,2))
445  ijmin = 0
446 
447  !Find the i,j indices of the a-grid point nearest to the
448  !my_lonStart,latStart point.
449  CALL find_nearest_agrid_index(latstart, &
450  my_lonstart, &
451  mini, &
452  minj, &
453  minimum_distance)
454 
455  !Find the minimum distance across all ranks.
456  global_min_distance = minimum_distance
457  CALL mpp_min(global_min_distance)
458 
459  !In the case of a tie (i.e. two ranks with exactly the same
460  !minimum distance), use the i,j values from the larger rank id.
461  IF (global_min_distance .EQ. minimum_distance) THEN
462  rank_buf = mpp_pe()
463  ELSE
464  rank_buf = -1
465  ENDIF
466  CALL mpp_max(rank_buf)
467 
468  !Sanity check.
469  IF (rank_buf .EQ. -1) THEN
470  CALL error_mesg("get_local_indexes", &
471  "No rank has minimum distance.", &
472  fatal)
473  ENDIF
474 
475  IF (rank_buf .EQ. mpp_pe()) THEN
476  ijmin(mytile,1) = mini + diag_global_grid%myXbegin - 1
477  ijmin(mytile,2) = minj + diag_global_grid%myYbegin - 1
478  ENDIF
479 
480  DO i = 1,ntiles
481  CALL mpp_max(ijmin(i,1))
482  CALL mpp_max(ijmin(i,2))
483  ENDDO
484 
485  istart = ijmin(mytile,1)
486  jstart = ijmin(mytile,2)
487  iend = istart
488  jend = jstart
489 
490  DEALLOCATE(ijmin)
491  ELSE
492 
493  mytile = diag_global_grid%tile_number
494  ntiles = diag_global_grid%ntiles
495 
496  ! Arrays to home min/max for each tile
497  ALLOCATE(ijmin(ntiles,2), stat=istat)
498  IF ( istat .NE. 0 )&
499  & CALL error_mesg('diag_grid_mod::get_local_indexes',&
500  &'Cannot allocate ijMin index array', fatal)
501  ALLOCATE(ijmax(ntiles,2), stat=istat)
502  IF ( istat .NE. 0 )&
503  & CALL error_mesg('diag_grid_mod::get_local_indexes',&
504  &'Cannot allocate ijMax index array', fatal)
505  ijmin = 0
506  ijmax = 0
507 
508  ! There will be four points to define a region, find all four.
509  ! Need to call the correct function depending on if the tile is a
510  ! pole tile or not.
511  dimi = diag_global_grid%dimI
512  dimj = diag_global_grid%dimJ
513 
514  ! Build the delta array
515  ALLOCATE(delta_lat(dimi,dimj), stat=istat)
516  IF ( istat .NE. 0 )&
517  & CALL error_mesg('diag_grid_mod::get_local_indexes',&
518  &'Cannot allocate latitude delta array', fatal)
519  ALLOCATE(delta_lon(dimi,dimj), stat=istat)
520  IF ( istat .NE. 0 )&
521  & CALL error_mesg('diag_grid_mod::get_local_indexes',&
522  &'Cannot allocate longitude delta array', fatal)
523  DO j=1, dimj
524  DO i=1, dimi
525  count = 0
526  dists_lon = 0.
527  dists_lat = 0.
528  IF ( i < dimi ) THEN
529  dists_lon(1) = abs(diag_global_grid%glo_lon(i+1,j) - diag_global_grid%glo_lon(i,j))
530  dists_lat(1) = abs(diag_global_grid%glo_lat(i+1,j) - diag_global_grid%glo_lat(i,j))
531  count = count+1
532  END IF
533  IF ( j < dimj ) THEN
534  dists_lon(2) = abs(diag_global_grid%glo_lon(i,j+1) - diag_global_grid%glo_lon(i,j))
535  dists_lat(2) = abs(diag_global_grid%glo_lat(i,j+1) - diag_global_grid%glo_lat(i,j))
536  count = count+1
537  END IF
538  IF ( i > 1 ) THEN
539  dists_lon(3) = abs(diag_global_grid%glo_lon(i,j) - diag_global_grid%glo_lon(i-1,j))
540  dists_lat(3) = abs(diag_global_grid%glo_lat(i,j) - diag_global_grid%glo_lat(i-1,j))
541  count = count+1
542  END IF
543  IF ( j > 1 ) THEN
544  dists_lon(4) = abs(diag_global_grid%glo_lon(i,j) - diag_global_grid%glo_lon(i,j-1))
545  dists_lat(4) = abs(diag_global_grid%glo_lat(i,j) - diag_global_grid%glo_lat(i,j-1))
546  count = count+1
547  END IF
548 
549  ! Fix wrap around problem
550  DO k=1, 4
551  IF ( dists_lon(k) > 180.0 ) THEN
552  dists_lon(k) = 360.0 - dists_lon(k)
553  END IF
554  END DO
555  delta_lon(i,j) = sum(dists_lon)/real(count)
556  delta_lat(i,j) = sum(dists_lat)/real(count)
557  END DO
558  END DO
559 
560  ijmin = huge(1)
561  ijmax = -huge(1)
562 
563  ! Adjusted longitude array
564  ALLOCATE(grid_lon(dimi,dimj), stat=istat)
565  IF ( istat .NE. 0 )&
566  & CALL error_mesg('diag_grid_mod::get_local_indexes',&
567  &'Cannot allocate temporary longitude array', fatal)
568  grid_lon = diag_global_grid%glo_lon
569 
570  ! Make adjustments where required
571  IF ( my_lonstart > my_lonend ) THEN
572  WHERE ( grid_lon < my_lonstart )
573  grid_lon = grid_lon + 360.0
574  END WHERE
575  lonendadj = my_lonend + 360.0
576  ELSE
577  lonendadj = my_lonend
578  END IF
579 
580  DO j=1, dimj-1
581  DO i=1, dimi-1
582  onmype = .false.
583  IF ( latstart-delta_lat(i,j) <= diag_global_grid%glo_lat(i,j) .AND.&
584  & diag_global_grid%glo_lat(i,j) < latend+delta_lat(i,j) ) THEN
585  ! Short-cut for the poles
586  IF ( (abs(latstart)-delta_lat(i,j) <= 90.0 .AND.&
587  & 90.0 <= abs(latend)+delta_lat(i,j)) .AND.&
588  & abs(diag_global_grid%glo_lat(i,j)) == 90.0 ) THEN
589  onmype = .true.
590  ELSE IF ( (my_lonstart-delta_lon(i,j) <= grid_lon(i,j) .AND.&
591  & grid_lon(i,j) < lonendadj+delta_lon(i,j)) ) THEN
592  onmype = .true.
593  ELSE
594  onmype = .false.
595  END IF
596  IF ( onmype ) THEN
597  ijmin(mytile,1) = min(ijmin(mytile,1),i + diag_global_grid%myXbegin - 1)
598  ijmax(mytile,1) = max(ijmax(mytile,1),i + diag_global_grid%myXbegin - 1)
599  ijmin(mytile,2) = min(ijmin(mytile,2),j + diag_global_grid%myYbegin - 1)
600  ijmax(mytile,2) = max(ijmax(mytile,2),j + diag_global_grid%myYbegin - 1)
601  END IF
602  END IF
603  END DO
604  END DO
605  DEALLOCATE(delta_lon)
606  DEALLOCATE(delta_lat)
607  DEALLOCATE(grid_lon)
608 
609  ! Global min/max reduce
610  DO i=1, ntiles
611  CALL mpp_min(ijmin(i,1))
612  CALL mpp_max(ijmax(i,1))
613  CALL mpp_min(ijmin(i,2))
614  CALL mpp_max(ijmax(i,2))
615  END DO
616 
617  IF ( ijmin(mytile,1) == huge(1) .OR. ijmax(mytile,1) == -huge(1) ) THEN
618  ijmin(mytile,1) = 0
619  ijmax(mytile,1) = 0
620  END IF
621  IF ( ijmin(mytile,2) == huge(1) .OR. ijmax(mytile,2) == -huge(1) ) THEN
622  ijmin(mytile,2) = 0
623  ijmax(mytile,2) = 0
624  END IF
625 
626  istart = ijmin(mytile,1)
627  jstart = ijmin(mytile,2)
628  iend = ijmax(mytile,1)
629  jend = ijmax(mytile,2)
630 
631  DEALLOCATE(ijmin)
632  DEALLOCATE(ijmax)
633  END IF
634 
635  END SUBROUTINE get_local_indexes
636 
637  !> @brief Find the indices of the nearest grid point of the a-grid to the
638  !! specified (lon,lat) location on the local PE. if desired point not
639  !! within domain of local PE, return (0,0) as the indices.
640  SUBROUTINE get_local_indexes2(lat, lon, iindex, jindex)
641  REAL, INTENT(in) :: lat !< lat location
642  REAL, INTENT(in) :: lon !< lon location
643  INTEGER, INTENT(out) :: iindex !< i indexes
644  INTEGER, INTENT(out) :: jindex !< j indexes
645 
646  INTEGER :: indexes(2)
647 
648  IF ( .NOT. diag_grid_initialized )&
649  & CALL error_mesg('diag_grid_mod::get_local_indexes2',&
650  &'Module not initialized, first initialze module with a call &
651  &to diag_grid_init', fatal)
652 
653  indexes = 0
654 
655  IF ( mod(diag_global_grid%tile_number,3) == 0 ) THEN
656  IF ( lat > 30.0 .AND. diag_global_grid%tile_number == 3 ) THEN
657  indexes(:) = find_pole_index_agrid(lat,lon)
658  ELSE IF ( lat < -30.0 .AND. diag_global_grid%tile_number == 6 ) THEN
659  indexes(:) = find_pole_index_agrid(lat,lon)
660  ENDIF
661  ELSE
662  indexes(:) = find_equator_index_agrid(lat,lon)
663  END IF
664 
665  iindex = indexes(1)
666  jindex = indexes(2)
667  IF (iindex == diag_global_grid%adimI -1 .OR.&
668  jindex == diag_global_grid%adimJ -1 ) THEN
669  iindex = 0
670  jindex = 0
671  ENDIF
672 
673  END SUBROUTINE get_local_indexes2
674 
675  !> @fn pure elemental real rad2deg(real angle)
676  !> @brief Convert an angle in radian to degrees.
677  !!
678  !> Given a scalar, or an array of angles in radians this
679  !! function will return a scalar or array (of the same
680  !! dimension) of angles in degrees.
681  !! @return Scalar or array (depending on the size of angle) of angles in
682  !! degrees.
683  PURE ELEMENTAL REAL FUNCTION rad2deg(angle)
684  REAL, INTENT(in) :: angle !< Scalar or array of angles in radians.
685 
686  rad2deg = rad_to_deg * angle
687  END FUNCTION rad2deg
688 
689  !> @brief Convert an angle in degrees to radians.
690  !!
691  !> Given a scalar, or an array of angles in degrees this
692  !! function will return a scalar or array (of the same
693  !! dimension) of angles in radians.
694  !! @return Scalar or array (depending on the size of angle) of angles in
695  !! radians.
696  PURE ELEMENTAL REAL FUNCTION deg2rad(angle)
697  REAL, INTENT(in) :: angle !< Scalar or array of angles in degrees.
698 
699  deg2rad = deg_to_rad * angle
700  END FUNCTION deg2rad
701 
702  !> @brief Return the closest index (i,j) to the given (lat,lon) point.
703  !!
704  !> This function searches a pole a-grid tile looking for the grid point
705  !! closest to the give (lat, lon) location, and returns the i
706  !! and j indexes of the point.
707  !! @return The (i, j) location of the closest grid to the given (lat,
708  !! lon) location.
709  PURE FUNCTION find_pole_index_agrid(lat, lon)
710  INTEGER, DIMENSION(2) :: find_pole_index_agrid !< The (i, j) location of the closest grid to the given (lat,
711  !! lon) location.
712  REAL, INTENT(in) :: lat !< Latitude location
713  REAL, INTENT(in) :: lon !< Longitude location
714 
715  INTEGER :: indxi !< Indexes to be returned.
716  INTEGER :: indxj !< Indexes to be returned.
717  INTEGER :: dimi !< Size of the grid dimensions
718  INTEGER :: dimj !< Size of the grid dimensions
719  INTEGER :: i !< Count indexes
720  INTEGER :: j !< Count indexes
721  INTEGER :: nearestcorner !< index of the nearest corner.
722  INTEGER, DIMENSION(4,2) :: ijarray !< indexes of the cornerPts and pntDistances arrays
723  REAL :: llat, llon
724  REAL :: maxctrdist !< maximum distance to center of grid
725  REAL, DIMENSION(4) :: pntdistances !< distance from origPt to corner
726  TYPE(point) :: origpt !< Original point
727  REAL, DIMENSION(4,2) :: cornerpts !< Corner points using (lat,lon)
728  TYPE(point), DIMENSION(9) :: points !< xyz of 8 nearest neighbors
729  REAL, DIMENSION(9) :: distsqrd !< distance between origPt and points(:)
730 
731  ! Set the inital fail values for indxI and indxJ
732  indxi = 0
733  indxj = 0
734 
735  dimi = diag_global_grid%adimI
736  dimj = diag_global_grid%adimJ
737 
738  ! Since the poles have an non-unique longitude value, make a small correction if looking for one of the poles.
739  IF ( lat == 90.0 ) THEN
740  llat = lat - .1
741  ELSE IF ( lat == -90.0 ) THEN
742  llat = lat + .1
743  ELSE
744  llat = lat
745  END IF
746  llon = lon
747 
748  origpt = latlon2xyz(llat,llon)
749 
750  iloop: DO i=0, dimi-2
751  jloop: DO j = 0, dimj-2
752  cornerpts = reshape( (/ diag_global_grid%aglo_lat(i, j), diag_global_grid%aglo_lon(i, j),&
753  & diag_global_grid%aglo_lat(i+1,j+1),diag_global_grid%aglo_lon(i+1,j+1),&
754  & diag_global_grid%aglo_lat(i+1,j), diag_global_grid%aglo_lon(i+1,j),&
755  & diag_global_grid%aglo_lat(i, j+1),diag_global_grid%aglo_lon(i, j+1) /),&
756  & (/ 4, 2 /), order=(/2,1/) )
757  ! Find the maximum half distance of the corner points
758  maxctrdist = max(gcirdistance(cornerpts(1,1),cornerpts(1,2), cornerpts(2,1),cornerpts(2,2)),&
759  & gcirdistance(cornerpts(3,1),cornerpts(3,2), cornerpts(4,1),cornerpts(4,2)))
760 
761  ! Find the distance of the four corner points to the point of interest.
762  pntdistances = gcirdistance(cornerpts(:,1),cornerpts(:,2), llat,llon)
763 
764  IF ( (minval(pntdistances) <= maxctrdist) .AND. (i*j.NE.0) ) THEN
765  ! Set up the i,j index array
766  ijarray = reshape( (/ i, j, i+1, j+1, i+1, j, i, j+1 /), (/ 4, 2 /), order=(/2,1/) )
767 
768  ! the nearest point index
769  nearestcorner = minloc(pntdistances,1)
770 
771  indxi = ijarray(nearestcorner,1)
772  indxj = ijarray(nearestcorner,2)
773 
774  EXIT iloop
775  END IF
776  END DO jloop
777  END DO iloop
778 
779 
780  ! Make sure we have indexes in the correct range
781  valid: IF ( (indxi <= 0 .OR. dimi-1 <= indxi) .OR. &
782  & (indxj <= 0 .OR. dimj-1 <= indxj) ) THEN
783  indxi = 0
784  indxj = 0
785  ELSE ! indxI and indxJ are valid.
786  ! Since we are looking for the closest grid point to the
787  ! (lat,lon) point, we need to check the surrounding
788  ! points. The indexes for the variable points are as follows
789  !
790  ! 1---4---7
791  ! | | |
792  ! 2---5---8
793  ! | | |
794  ! 3---6---9
795 
796  ! Set the 'default' values for points(:) x,y,z to some large
797  ! value.
798  DO i=1, 9
799  points(i)%x = 1.0e20
800  points(i)%y = 1.0e20
801  points(i)%z = 1.0e20
802  END DO
803 
804  ! All the points around the i,j indexes
805  points(1) = latlon2xyz(diag_global_grid%aglo_lat(indxi-1,indxj+1),&
806  & diag_global_grid%aglo_lon(indxi-1,indxj+1))
807  points(2) = latlon2xyz(diag_global_grid%aglo_lat(indxi-1,indxj),&
808  & diag_global_grid%aglo_lon(indxi-1,indxj))
809  points(3) = latlon2xyz(diag_global_grid%aglo_lat(indxi-1,indxj-1),&
810  & diag_global_grid%aglo_lon(indxi-1,indxj-1))
811  points(4) = latlon2xyz(diag_global_grid%aglo_lat(indxi, indxj+1),&
812  & diag_global_grid%aglo_lon(indxi, indxj+1))
813  points(5) = latlon2xyz(diag_global_grid%aglo_lat(indxi, indxj),&
814  & diag_global_grid%aglo_lon(indxi, indxj))
815  points(6) = latlon2xyz(diag_global_grid%aglo_lat(indxi, indxj-1),&
816  & diag_global_grid%aglo_lon(indxi, indxj-1))
817  points(7) = latlon2xyz(diag_global_grid%aglo_lat(indxi+1,indxj+1),&
818  & diag_global_grid%aglo_lon(indxi+1,indxj+1))
819  points(8) = latlon2xyz(diag_global_grid%aglo_lat(indxi+1,indxj),&
820  & diag_global_grid%aglo_lon(indxi+1,indxj))
821  points(9) = latlon2xyz(diag_global_grid%aglo_lat(indxi+1,indxj-1),&
822  & diag_global_grid%aglo_lon(indxi+1,indxj-1))
823 
824 
825  ! Calculate the distance squared between the points(:) and the origPt
826  distsqrd = distancesqrd(origpt, points)
827 
828  SELECT CASE (minloc(distsqrd,1))
829  CASE ( 1 )
830  indxi = indxi-1
831  indxj = indxj+1
832  CASE ( 2 )
833  indxi = indxi-1
834  indxj = indxj
835  CASE ( 3 )
836  indxi = indxi-1
837  indxj = indxj-1
838  CASE ( 4 )
839  indxi = indxi
840  indxj = indxj+1
841  CASE ( 5 )
842  indxi = indxi
843  indxj = indxj
844  CASE ( 6 )
845  indxi = indxi
846  indxj = indxj-1
847  CASE ( 7 )
848  indxi = indxi+1
849  indxj = indxj+1
850  CASE ( 8 )
851  indxi = indxi+1
852  indxj = indxj
853  CASE ( 9 )
854  indxi = indxi+1
855  indxj = indxj-1
856  CASE DEFAULT
857  indxi = 0
858  indxj = 0
859  END SELECT
860  END IF valid
861 
862  ! Set the return value for the funtion
863  find_pole_index_agrid = (/indxi, indxj/)
864  END FUNCTION find_pole_index_agrid
865 
866  !> @brief Return the closest index (i,j) to the given (lat,lon) point.
867  !!
868  !> This function searches a equator grid tile looking for the grid point
869  !! closest to the give (lat, lon) location, and returns the i
870  !! and j indexes of the point.
871  !! @return The (i, j) location of the closest grid to the given (lat,
872  !! lon) location.
873  PURE FUNCTION find_equator_index_agrid(lat, lon)
874  INTEGER, DIMENSION(2) :: find_equator_index_agrid !< The (i, j) location of the closest grid to the given (lat,
875  !! lon) location.
876  REAL, INTENT(in) :: lat !< Latitude location
877  REAL, INTENT(in) :: lon !< Longitude location
878 
879  INTEGER :: indxi, indxj !< Indexes to be returned.
880  INTEGER :: indxi_tmp !< Hold the indxI value if on tile 3 or 4
881  INTEGER :: dimi, dimj !< Size of the grid dimensions
882  INTEGER :: i,j !< Count indexes
883  INTEGER :: jstart, jend, nextj !< j counting variables
884  TYPE(point) :: origpt !< Original point
885  TYPE(point), DIMENSION(4) :: points !< xyz of 8 nearest neighbors
886  REAL, DIMENSION(4) :: distsqrd !< distance between origPt and points(:)
887 
888  ! Set the inital fail values for indxI and indxJ
889  indxi = 0
890  indxj = 0
891 
892  dimi = diag_global_grid%adimI
893  dimj = diag_global_grid%adimJ
894 
895  ! check to see if the 'fix' for the latitude index is needed
896  IF ( diag_global_grid%aglo_lat(1,1) > &
897  &diag_global_grid%aglo_lat(1,2) ) THEN
898  ! reverse the j search
899  jstart = dimj-1
900  jend = 1
901  nextj = -1
902  ELSE
903  jstart = 0
904  jend = dimj-2
905  nextj = 1
906  END IF
907 
908  ! find the I index
909  iloop: DO i=0, dimi-2
910  IF ( diag_global_grid%aglo_lon(i,0) >&
911  & diag_global_grid%aglo_lon(i+1,0) ) THEN
912  ! We are at the 0 longitudal line
913  IF ( (diag_global_grid%aglo_lon(i,0) <= lon .AND. lon <= 360.) .OR.&
914  & (0. <= lon .AND. lon < diag_global_grid%aglo_lon(i+1, 0)) ) THEN
915  indxi = i
916  EXIT iloop
917  END IF
918  ELSEIF ( diag_global_grid%aglo_lon(i,0) <= lon .AND.&
919  & lon <= diag_global_grid%aglo_lon(i+1,0) ) THEN
920  indxi = i
921  EXIT iloop
922  END IF
923  END DO iloop
924 
925  ! Find the J index
926  IF ( indxi > 0 ) THEN
927  jloop: DO j=jstart, jend, nextj
928  IF ( diag_global_grid%aglo_lat(indxi,j) <= lat .AND.&
929  & lat <= diag_global_grid%aglo_lat(indxi,j+nextj) ) THEN
930  indxj = j
931  EXIT jloop
932  END IF
933  END DO jloop
934  END IF
935 
936  ! Make sure we have indexes in the correct range
937  valid: IF ( (indxi <= 0 .OR. dimi-1 < indxi) .OR. &
938  & (indxj <= 0 .OR. dimj-1 < indxj) ) THEN
939  indxi = 0
940  indxj = 0
941  ELSE ! indxI and indxJ are valid.
942  ! Since we are looking for the closest grid point to the
943  ! (lat,lon) point, we need to check the surrounding
944  ! points. The indexes for the variable points are as follows
945  !
946  ! 1---3
947  ! | |
948  ! 2---4
949 
950  ! The original point
951  origpt = latlon2xyz(lat,lon)
952 
953  ! Set the 'default' values for points(:) x,y,z to some large
954  ! value.
955  DO i=1, 4
956  points(i)%x = 1.0e20
957  points(i)%y = 1.0e20
958  points(i)%z = 1.0e20
959  END DO
960 
961  ! The original point
962  origpt = latlon2xyz(lat,lon)
963 
964  points(1) = latlon2xyz(diag_global_grid%aglo_lat(indxi,indxj),&
965  & diag_global_grid%aglo_lon(indxi,indxj))
966  points(2) = latlon2xyz(diag_global_grid%aglo_lat(indxi,indxj+nextj),&
967  & diag_global_grid%aglo_lon(indxi,indxj+nextj))
968  points(3) = latlon2xyz(diag_global_grid%aglo_lat(indxi+1,indxj+nextj),&
969  & diag_global_grid%aglo_lon(indxi+1,indxj+nextj))
970  points(4) = latlon2xyz(diag_global_grid%aglo_lat(indxi+1,indxj),&
971  & diag_global_grid%aglo_lon(indxi+1,indxj))
972 
973  ! Find the distance between the original point and the four
974  ! grid points
975  distsqrd = distancesqrd(origpt, points)
976 
977  SELECT CASE (minloc(distsqrd,1))
978  CASE ( 1 )
979  indxi = indxi;
980  indxj = indxj;
981  CASE ( 2 )
982  indxi = indxi;
983  indxj = indxj+nextj;
984  CASE ( 3 )
985  indxi = indxi+1;
986  indxj = indxj+nextj;
987  CASE ( 4 )
988  indxi = indxi+1;
989  indxj = indxj;
990  CASE DEFAULT
991  indxi = 0;
992  indxj = 0;
993  END SELECT
994 
995  ! If we are on tile 3 or 4, then the indxI and indxJ are
996  ! reversed due to the transposed grids.
997  IF ( diag_global_grid%tile_number == 4 .OR.&
998  & diag_global_grid%tile_number == 5 ) THEN
999  indxi_tmp = indxi
1000  indxi = indxj
1001  indxj = indxi_tmp
1002  END IF
1003  END IF valid
1004 
1005  ! Set the return value for the function
1006  find_equator_index_agrid = (/indxi, indxj/)
1007  END FUNCTION find_equator_index_agrid
1008 
1009  !> @brief Return the (x,y,z) position of a given (lat,lon) point.
1010  !!
1011  !> Given a specific (lat, lon) point on the Earth, return the
1012  !! corresponding (x,y,z) location. The return of latlon2xyz
1013  !! will be either a scalar or an array of the same size as lat
1014  !! and lon.
1015  !! @return The return of latlon2xyz
1016  !! will be either a scalar or an array of the same size as lat
1017  !! and lon.
1018  PURE ELEMENTAL TYPE(point) function latlon2xyz(lat, lon)
1019  REAL, INTENT(in) :: lat !< The latitude of the (x,y,z) location to find. <TT>lat</TT>
1020  !! can be either a scalar or array. <TT>lat</TT> must be of the
1021  !! same rank / size as <TT>lon</TT>. This function assumes
1022  !! <TT>lat</TT> is in the range [-90,90].
1023  REAL, INTENT(in) :: lon !< The longitude of the (x,y,z) location to find. <TT>lon</TT>
1024  !! can be either a scalar or array. <TT>lon</TT> must be of the
1025  !! same rank / size as <TT>lat</TT>. This function assumes
1026  !! <TT>lon</TT> is in the range [0,360].
1027 
1028  ! lat/lon angles in radians
1029  REAL :: theta !< lat angles in radians
1030  REAL :: phi !< lon angles in radians
1031 
1032  ! Convert the lat lon values to radians The lat values passed in
1033  ! are in the range [-90,90], but we need to have a radian range
1034  ! [0,pi], where 0 is at the north pole. This is the reason for
1035  ! the subtraction from 90
1036  theta = deg2rad(90.-lat)
1037  phi = deg2rad(lon)
1038 
1039  ! Calculate the x,y,z point
1040  latlon2xyz%x = radius * sin(theta) * cos(phi)
1041  latlon2xyz%y = radius * sin(theta) * sin(phi)
1042  latlon2xyz%z = radius * cos(theta)
1043  END FUNCTION latlon2xyz
1044 
1045  !> @brief Find the distance between two points in the Cartesian
1046  !! coordinate space.
1047  !!
1048  !> <TT>distanceSqrd</TT> will find the distance squared between
1049  !! two points in the xyz coordinate space. <TT>pt1</TT> and <TT>
1050  !! pt2</TT> can either be both scalars, both arrays of the same
1051  !! size, or one a scalar and one an array. The return value
1052  !! will be a scalar or array of the same size as the input array.
1053  !! @return The return value will be a scalar or array of the same size as the input array.
1054  PURE ELEMENTAL REAL FUNCTION distancesqrd(pt1, pt2)
1055  TYPE(point), INTENT(in) :: pt1, pt2
1056 
1057  distancesqrd = (pt1%x-pt2%x)**2 +&
1058  & (pt1%y-pt2%y)**2 +&
1059  & (pt1%z-pt2%z)**2
1060  END FUNCTION distancesqrd
1061 
1062  !> @brief Find the distance, along the geodesic, between two points.
1063  !!
1064  !> <TT>gCirDistance</TT> will find the distance, along the geodesic, between two points defined
1065  !! by the (lat,lon) position of each point.
1066  !! @return real
1067  PURE ELEMENTAL REAL FUNCTION gcirdistance(lat1, lon1, lat2, lon2)
1068  REAL, INTENT(in) :: lat1, lat2, lon1, lon2
1069 
1070  REAL :: theta1, theta2
1071  REAL :: deltalambda !< Difference in longitude angles, in radians.
1072  REAL :: deltatheta !< Difference in latitude angels, in radians.
1073 
1074  theta1 = deg2rad(lat1)
1075  theta2 = deg2rad(lat2)
1076  deltalambda = deg2rad(lon2-lon1)
1077  deltatheta = deg2rad(lat2-lat1)
1078 
1079  gcirdistance = radius * 2. * asin(sqrt((sin(deltatheta/2.))**2 + cos(theta1)*cos(theta2)*(sin(deltalambda/2.))**2))
1080  END FUNCTION gcirdistance
1081 
1082  !> @brief Find the i,j indices and distance of the a-grid point nearest to
1083  !! the inputted lat,lon point.
1084  SUBROUTINE find_nearest_agrid_index(lat, &
1085  lon, &
1086  minI, &
1087  minJ, &
1088  minimum_distance)
1089 
1090  !Inputs/outputs
1091  REAL,INTENT(IN) :: lat
1092  REAL,INTENT(IN) :: lon
1093  INTEGER,INTENT(OUT) :: minI
1094  INTEGER,INTENT(OUT) :: minJ
1095  REAL,INTENT(OUT) :: minimum_distance
1096 
1097  !Local variables
1098  REAL :: llat
1099  REAL :: llon
1100  INTEGER :: j
1101  INTEGER :: i
1102  REAL :: dist
1103 
1104  !Since the poles have an non-unique longitude value, make a small
1105  !correction if looking for one of the poles.
1106  IF (lat .EQ. 90.0) THEN
1107  llat = lat - .1
1108  ELSEIF (lat .EQ. -90.0) THEN
1109  llat = lat + .1
1110  ELSE
1111  llat = lat
1112  END IF
1113  llon = lon
1114 
1115  !Loop through non-halo points. Calculate the distance
1116  !between each a-grid point and the point that we
1117  !are seeking. Store the minimum distance and its
1118  !corresponding i,j indices.
1119  mini = 0
1120  minj = 0
1121  minimum_distance = 2.0*radius*3.141592653
1122  DO j = 1,diag_global_grid%adimJ-2
1123  DO i = 1,diag_global_grid%adimI-2
1124  dist = gcirdistance(llat, &
1125  llon, &
1126  diag_global_grid%aglo_lat(i,j), &
1127  diag_global_grid%aglo_lon(i,j))
1128  IF (dist .LT. minimum_distance) THEN
1129 
1130  !These number shouldn't be hardcoded, but they have to
1131  !match the ones in diag_grid_init.
1132  if (diag_global_grid%tile_number .eq. 4 .or. &
1133  diag_global_grid%tile_number .eq. 5) then
1134 
1135  !Because of transpose in diag_grid_init.
1136  mini = j
1137  minj = i
1138 
1139  else
1140  mini = i
1141  minj = j
1142  endif
1143  minimum_distance = dist
1144  ENDIF
1145  ENDDO
1146  ENDDO
1147 
1148  !Check that valid i,j indices have been found.
1149  IF (mini .EQ. 0 .OR. minj .EQ. 0) THEN
1150  call error_mesg("find_nearest_agrid_index", &
1151  "A minimum distance was not found.", &
1152  fatal)
1153  ENDIF
1154 
1155  END SUBROUTINE find_nearest_agrid_index
1156 
1157 END MODULE diag_grid_mod
1158 !> @}
1159 ! close documentation grouping
pure elemental real function distancesqrd(pt1, pt2)
Find the distance between two points in the Cartesian coordinate space.
Definition: diag_grid.F90:1055
pure integer function, dimension(2) find_equator_index_agrid(lat, lon)
Return the closest index (i,j) to the given (lat,lon) point.
Definition: diag_grid.F90:874
subroutine, public diag_grid_init(domain, glo_lat, glo_lon, aglo_lat, aglo_lon)
Send the global grid to the diag_manager_mod for regional output.
Definition: diag_grid.F90:132
pure elemental type(point) function latlon2xyz(lat, lon)
Return the (x,y,z) position of a given (lat,lon) point.
Definition: diag_grid.F90:1019
pure elemental real function gcirdistance(lat1, lon1, lat2, lon2)
Find the distance, along the geodesic, between two points.
Definition: diag_grid.F90:1068
type(diag_global_grid_type) diag_global_grid
Variable to hold the global grid data.
Definition: diag_grid.F90:109
logical diag_grid_initialized
Indicates if the diag_grid_mod has been initialized.
Definition: diag_grid.F90:111
subroutine, public diag_grid_end()
Unallocate the diag_global_grid variable.
Definition: diag_grid.F90:350
pure elemental real function deg2rad(angle)
Convert an angle in degrees to radians.
Definition: diag_grid.F90:697
subroutine, public get_local_indexes2(lat, lon, iindex, jindex)
Find the indices of the nearest grid point of the a-grid to the specified (lon,lat) location on the l...
Definition: diag_grid.F90:641
subroutine, public get_local_indexes(latStart, latEnd, lonStart, lonEnd, istart, iend, jstart, jend)
Find the local start and local end indexes on the local PE for regional output.
Definition: diag_grid.F90:392
pure integer function, dimension(2) find_pole_index_agrid(lat, lon)
Return the closest index (i,j) to the given (lat,lon) point.
Definition: diag_grid.F90:710
subroutine find_nearest_agrid_index(lat, lon, minI, minJ, minimum_distance)
Find the i,j indices and distance of the a-grid point nearest to the inputted lat,...
Definition: diag_grid.F90:1089
pure elemental real function rad2deg(angle)
Definition: diag_grid.F90:684
Private type to hold the model's global grid data, and other grid information for use in this module.
Definition: diag_grid.F90:73
Private type to hold the corresponding (x,y,z) location for a (lat,lon) location.
Definition: diag_grid.F90:100
subroutine, public write_version_number(version, tag, unit)
Prints to the log file (or a specified unit) the version id string and tag name.
Definition: fms.F90:758
subroutine, public error_mesg(routine, message, level)
Print notes, warnings and error messages; terminates program for warning and error messages....
Definition: fms.F90:498
integer function, dimension(size(domain%tile_id(:))) mpp_get_tile_id(domain)
Returns the tile_id on current pe.
integer function mpp_get_ntile_count(domain)
Returns number of tiles in mosaic.
Retrieve the entire array of compute domain extents associated with a decomposition.
The domain2D type contains all the necessary information to define the global, compute and data domai...
integer function mpp_npes()
Returns processor count for current pelist.
Definition: mpp_util.inc:421
integer function mpp_pe()
Returns processor ID.
Definition: mpp_util.inc:407
Reduction operations. Find the max of scalar a from the PEs in pelist result is also automatically br...
Definition: mpp.F90:538
Reduction operations. Find the min of scalar a from the PEs in pelist result is also automatically br...
Definition: mpp.F90:560