FMS 2025.01-dev
Flexible Modeling System
Loading...
Searching...
No Matches
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
39MODULE diag_grid_mod
40use 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.
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
117CONTAINS
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.
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
1157END MODULE diag_grid_mod
1158!> @}
1159! close documentation grouping
pure elemental real function deg2rad(angle)
Convert an angle in degrees to radians.
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.
pure integer function, dimension(2) find_equator_index_agrid(lat, lon)
Return the closest index (i,j) to the given (lat,lon) point.
pure integer function, dimension(2) find_pole_index_agrid(lat, lon)
Return the closest index (i,j) to the given (lat,lon) point.
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,...
pure elemental real function rad2deg(angle)
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...
type(diag_global_grid_type) diag_global_grid
Variable to hold the global grid data.
subroutine, public diag_grid_end()
Unallocate the diag_global_grid variable.
logical diag_grid_initialized
Indicates if the diag_grid_mod has been initialized.
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.
pure elemental real function gcirdistance(lat1, lon1, lat2, lon2)
Find the distance, along the geodesic, between two points.
pure elemental real function distancesqrd(pt1, pt2)
Find the distance between two points in the Cartesian coordinate space.
pure elemental type(point) function latlon2xyz(lat, lon)
Return the (x,y,z) position of a given (lat,lon) point.
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.
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...
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