Line data Source code
1 : module dyn_grid
2 : !-----------------------------------------------------------------------
3 : !
4 : ! Define dynamics computational grid and decomposition.
5 : !
6 : ! Original code: John Drake and Patrick Worley
7 : !
8 : !-----------------------------------------------------------------------
9 :
10 : use shr_kind_mod, only: r8=>shr_kind_r8
11 : use pmgrid, only: plon, plat, plev, plevp, splon, spmd_on
12 : use commap, only: w, w_staggered, clat, clat_staggered, clon, &
13 : latdeg, londeg, latdeg_st, londeg_st
14 : use constituents, only: pcnst
15 : use physconst, only: pi, rearth, omega, spval
16 : use spmd_utils, only: iam
17 : use spmd_dyn, only: spmdinit_dyn, proc, lonrangexy, latrangexy
18 : use time_manager, only: get_step_size
19 :
20 : use pio, only: file_desc_t
21 : use cam_initfiles, only: initial_file_get_id
22 :
23 : use dynamics_vars, only: t_fvdycore_state, t_fvdycore_grid, grid_vars_init
24 : use dyn_internal_state, only: get_dyn_state
25 :
26 : use cam_abortutils, only: endrun
27 : use cam_logfile, only: iulog
28 :
29 : implicit none
30 : private
31 : save
32 :
33 : public :: &
34 : dyn_grid_init, &
35 : dyn_grid_find_gcols, &! find nearest column for given lat/lon
36 : dyn_grid_get_colndx, &! global lat and lon coordinate and MPI process indices
37 : ! corresponding to a specified global column index
38 : dyn_grid_get_elem_coords, &! coordinates of a specified element (latitude)
39 : ! of the dynamics grid (lat slice of the block)
40 : get_block_bounds_d, &! first and last indices in global block ordering
41 : get_block_gcol_d, &! global column indices for given block
42 : get_block_gcol_cnt_d, &! number of columns in given block
43 : get_block_levels_d, &! vertical levels in column
44 : get_block_lvl_cnt_d, &! number of vertical levels in column
45 : get_block_owner_d, &! process "owning" given block
46 : get_dyn_grid_parm, &
47 : get_dyn_grid_parm_real1d, &
48 : get_gcol_block_d, &! global block indices and local columns
49 : ! index for given global column index
50 : get_gcol_block_cnt_d, &! number of blocks containing data
51 : ! from a given global column index
52 : get_horiz_grid_d, &! horizontal grid coordinates
53 : get_horiz_grid_dim_d, &! horizontal dimensions of dynamics grid
54 : physgrid_copy_attributes_d
55 :
56 : ! The FV dynamics grids
57 : integer, parameter, public :: dyn_decomp = 101
58 : integer, parameter, public :: dyn_stagger_decomp = 102 !Backward compatibility
59 : integer, parameter, public :: dyn_ustag_decomp = 102
60 : integer, parameter, public :: dyn_vstag_decomp = 103
61 : integer, parameter, public :: dyn_zonal_decomp = 104
62 :
63 : integer, parameter, public :: ptimelevels = 2 ! number of time levels in the dycore
64 :
65 : integer :: ngcols_d = 0 ! number of dynamics columns
66 :
67 : type(t_fvdycore_grid), pointer :: grid
68 :
69 : !========================================================================================
70 : contains
71 : !========================================================================================
72 :
73 3072 : subroutine dyn_grid_init()
74 :
75 : ! Initialize FV grid, decomposition, and PILGRIM communications
76 :
77 : use hycoef, only: hycoef_init, hyai, hybi, hypi, hypm, nprlev
78 : use ref_pres, only: ref_pres_init
79 :
80 : ! Local variables
81 : type(t_fvdycore_state), pointer :: state
82 :
83 : type(file_desc_t), pointer :: fh_ini
84 :
85 : integer :: i, k, lat
86 :
87 : real(r8) :: dt
88 : real(r8) :: dp
89 : real(r8) :: sum
90 :
91 : character(len=*), parameter :: sub='dyn_grid_init'
92 : !-----------------------------------------------------------------------
93 :
94 : ! Assign pointer to FV internal state object
95 1536 : state => get_dyn_state()
96 : ! Assign pointer to grid object stored in state object
97 1536 : grid => state%grid
98 :
99 : ! Get file handle for initial file and first consistency check
100 1536 : fh_ini => initial_file_get_id()
101 :
102 : ! Set grid size parameters
103 1536 : grid%im = plon
104 1536 : grid%jm = plat
105 1536 : grid%km = plev
106 1536 : grid%kmax = plev + 1
107 1536 : grid%nq = pcnst
108 1536 : grid%ntotq = pcnst
109 :
110 : ! Initialize hybrid coordinate arrays
111 1536 : call hycoef_init(fh_ini)
112 :
113 : ! Initialize reference pressures
114 1536 : call ref_pres_init(hypi, hypm, nprlev)
115 :
116 : ! Hybrid coordinate info for FV grid object
117 1536 : allocate(grid%ak(plev+1), grid%bk(plev+1))
118 1536 : grid%ks = plev
119 110592 : do k = 1, plev+1
120 109056 : grid%ak(k) = hyai(k) * 1.e5_r8
121 109056 : grid%bk(k) = hybi(k)
122 110592 : if (grid%bk(k) == 0._r8) grid%ks = k-1
123 : end do
124 1536 : grid%ptop = grid%ak(1)
125 1536 : grid%pint = grid%ak(grid%ks+1)
126 :
127 : ! Initialize the grid decomposition and PILGRIM communications
128 1536 : call spmdinit_dyn(state%jord, grid)
129 :
130 : ! Initialize FV specific grid object variables
131 1536 : dt = get_step_size()
132 : call grid_vars_init(pi, rearth, omega, dt, state%fft_flt, &
133 1536 : state%am_geom_crrct, grid)
134 :
135 : ! initialize commap variables
136 :
137 : ! latitudes for cell centers
138 1536 : dp = 180._r8/(plat-1)
139 296448 : do lat = 1, plat
140 294912 : latdeg(lat) = -90._r8 + (lat-1)*dp
141 296448 : clat(lat) = latdeg(lat)*pi/180._r8
142 : end do
143 :
144 : ! latitudes for the staggered grid
145 294912 : do lat = 1, plat-1
146 293376 : clat_staggered(lat) = (clat(lat) + clat(lat+1)) / 2._r8
147 294912 : latdeg_st (lat) = clat_staggered(lat)*180._r8/pi
148 : end do
149 :
150 : ! Weights are defined as cos(phi)*(delta-phi)
151 : ! For a sanity check, the sum of w across all lats should be 2.
152 293376 : do lat = 2, plat-1
153 293376 : w(lat) = sin(clat_staggered(lat)) - sin(clat_staggered(lat-1))
154 : end do
155 1536 : w(1) = sin(clat_staggered(1)) + 1._r8
156 1536 : w(plat) = w(1)
157 :
158 1536 : sum = 0._r8
159 296448 : do lat=1,plat
160 296448 : sum = sum + w(lat)
161 : end do
162 1536 : if (abs(sum - 2._r8) > 1.e-8_r8) then
163 0 : write(iulog,*) sub//': ERROR: weights do not sum to 2. sum=', sum
164 0 : call endrun(sub//': ERROR: weights do not sum to 2.')
165 : end if
166 :
167 294912 : dp = pi / real(plat-1,r8)
168 294912 : do lat = 1, plat-1
169 294912 : w_staggered(lat) = sin(clat(lat+1)) - sin(clat(lat))
170 : end do
171 :
172 1536 : sum = 0._r8
173 294912 : do lat = 1, plat-1
174 294912 : sum = sum + w_staggered(lat)
175 : end do
176 :
177 1536 : if (abs(sum - 2._r8) > 1.e-8_r8) then
178 0 : write(iulog,*) sub//': ERROR: staggered weights do not sum to 2. sum=', sum
179 0 : call endrun(sub//': ERROR: staggered weights do not sum to 2.')
180 : end if
181 :
182 : ! longitudes for cell centers
183 296448 : do lat = 1, plat
184 85231104 : do i = 1, plon
185 84934656 : londeg(i,lat) = (i-1)*360._r8/plon
186 85229568 : clon(i,lat) = (i-1)*2._r8*pi/plon
187 : end do
188 : end do
189 :
190 : ! longitudes for staggered grid
191 296448 : do lat = 1, plat
192 85231104 : do i = 1, splon
193 85229568 : londeg_st(i,lat) = (i-1.5_r8)*360._r8/splon
194 : end do
195 : end do
196 :
197 : ! Define the CAM grids
198 1536 : call define_cam_grids()
199 :
200 1536 : end subroutine dyn_grid_init
201 :
202 : !========================================================================================
203 :
204 3072 : subroutine get_block_bounds_d(block_first, block_last)
205 :
206 : ! Return first and last indices used in global block ordering
207 :
208 : ! Arguments
209 : integer, intent(out) :: block_first ! first (global) index used for blocks
210 : integer, intent(out) :: block_last ! last (global) index used for blocks
211 : !---------------------------------------------------------------------------
212 :
213 3072 : block_first = 1
214 3072 : if (spmd_on .eq. 1) then
215 : ! Assume 1 block per subdomain
216 3072 : block_last = grid%nprxy_x*grid%nprxy_y
217 : else
218 : ! latitude slice block
219 0 : block_last = plat
220 : end if
221 :
222 1536 : end subroutine get_block_bounds_d
223 :
224 : !========================================================================================
225 :
226 1179648 : subroutine get_block_gcol_d(blockid, size, cdex)
227 :
228 : ! Return list of dynamics column indices in given block
229 :
230 : ! Arguments
231 : integer, intent(in) :: blockid ! global block id
232 : integer, intent(in) :: size ! array size
233 :
234 : integer, intent(out):: cdex(size) ! global column indices
235 :
236 : ! Local workspace
237 : integer :: i,j ! block coordinates
238 : integer :: blksiz ! block size
239 : integer :: k,l ! loop indices
240 : integer :: n ! column index
241 : character(len=*), parameter :: sub='get_block_gcol_d'
242 : !---------------------------------------------------------------------------
243 :
244 1179648 : if (spmd_on .eq. 1) then
245 1179648 : j = (blockid-1) / grid%nprxy_x + 1
246 1179648 : i = blockid - (j-1) * grid%nprxy_x
247 : #if ( defined SPMD )
248 1179648 : blksiz = (lonrangexy(2,i)-lonrangexy(1,i)+1) * &
249 2359296 : (latrangexy(2,j)-latrangexy(1,j)+1)
250 1179648 : if (size < blksiz) then
251 0 : write(iulog,*) sub//': ERROR: array not large enough (', &
252 0 : size,' < ',blksiz,' ) '
253 0 : call endrun(sub//': ERROR: array not large enough')
254 : else
255 1179648 : n = 0
256 4718592 : do k=latrangexy(1,j),latrangexy(2,j)
257 89653248 : do l=lonrangexy(1,i),lonrangexy(2,i)
258 84934656 : n = n + 1
259 88473600 : cdex(n) = l + (k-1)*plon
260 : end do
261 : end do
262 : end if
263 : #endif
264 : else
265 0 : if (size < plon) then
266 0 : write(iulog,*)'GET_BLOCK_GCOL_D: array not large enough (', &
267 0 : size,' < ',plon,' ) '
268 0 : call endrun
269 : else
270 0 : n = (blockid-1)*plon
271 0 : do i = 1,plon
272 0 : n = n + 1
273 0 : cdex(i) = n
274 : end do
275 : end if
276 : end if
277 :
278 1179648 : end subroutine get_block_gcol_d
279 :
280 : !========================================================================================
281 :
282 2360832 : integer function get_block_gcol_cnt_d(blockid)
283 :
284 : ! Return number of dynamics columns in indicated block
285 :
286 : ! Arguments
287 : integer, intent(in) :: blockid ! global block id
288 :
289 : ! Local workspace
290 : integer :: i, j
291 : !---------------------------------------------------------------------------
292 :
293 2360832 : if (spmd_on .eq. 1) then
294 2360832 : j = (blockid-1) / grid%nprxy_x + 1
295 2360832 : i = blockid - (j-1) * grid%nprxy_x
296 : #if ( defined SPMD )
297 2360832 : get_block_gcol_cnt_d = (lonrangexy(2,i)-lonrangexy(1,i)+1) * &
298 4721664 : (latrangexy(2,j)-latrangexy(1,j)+1)
299 : #endif
300 : else
301 : get_block_gcol_cnt_d = plon
302 : end if
303 :
304 2360832 : end function get_block_gcol_cnt_d
305 :
306 : !========================================================================================
307 :
308 112128 : integer function get_block_lvl_cnt_d(blockid, bcid)
309 :
310 : ! Return number of levels in indicated column. If column
311 : ! includes surface fields, then it is defined to also
312 : ! include level 0.
313 :
314 : ! Arguments
315 : integer, intent(in) :: blockid ! global block id
316 : integer, intent(in) :: bcid ! column index within block
317 : !-----------------------------------------------------------------------
318 :
319 : ! latitude slice block
320 112128 : get_block_lvl_cnt_d = plev + 1
321 :
322 112128 : end function get_block_lvl_cnt_d
323 :
324 : !========================================================================================
325 :
326 110592 : subroutine get_block_levels_d(blockid, bcid, lvlsiz, levels)
327 :
328 : ! Return level indices in indicated column. If column
329 : ! includes surface fields, then it is defined to also
330 : ! include level 0.
331 :
332 : ! Arguments
333 : integer, intent(in) :: blockid ! global block id
334 : integer, intent(in) :: bcid ! column index within block
335 : integer, intent(in) :: lvlsiz ! dimension of levels array
336 :
337 : integer, intent(out) :: levels(lvlsiz) ! levels indices for block
338 :
339 : ! Local workspace
340 : integer :: k ! loop index
341 : character(len=*), parameter :: sub='get_block_levels_d'
342 : !-----------------------------------------------------------------------
343 :
344 : ! latitude slice block
345 110592 : if (lvlsiz < plev + 1) then
346 0 : write(iulog,*) sub//': ERROR: levels array not large enough (', &
347 0 : lvlsiz,' < ',plev + 1,' ) '
348 0 : call endrun(sub//': ERROR: levels array not large enough')
349 : else
350 7962624 : do k = 0, plev
351 7962624 : levels(k+1) = k
352 : end do
353 110592 : do k = plev+2, lvlsiz
354 110592 : levels(k) = -1
355 : end do
356 : end if
357 :
358 110592 : end subroutine get_block_levels_d
359 :
360 : !========================================================================================
361 :
362 465960960 : subroutine get_gcol_block_d(gcol, cnt, blockid, bcid, localblockid)
363 :
364 : ! Return global block index and local column index
365 : ! for global column index
366 :
367 : ! Arguments
368 : integer, intent(in) :: gcol ! global column index
369 : integer, intent(in) :: cnt ! size of blockid and bcid arrays
370 :
371 : integer, intent(out) :: blockid(cnt) ! block index
372 : integer, intent(out) :: bcid(cnt) ! column index within block
373 : integer, intent(out), optional :: localblockid(cnt)
374 :
375 : ! Local workspace
376 : integer :: i, j, ii, jj ! loop indices
377 : integer :: glon, glat ! global longitude and latitude indices
378 : integer :: ddlon ! number of longitudes in block
379 : character(len=*), parameter :: sub='get_gcol_block_d'
380 : !---------------------------------------------------------------------------
381 :
382 : ! lon/lat block
383 465960960 : if (cnt < 1) then
384 0 : write(iulog,*) sub//': ERROR: arrays not large enough (', cnt,' < ',1,' )'
385 0 : call endrun(sub//': ERROR: arrays not large enough')
386 : else
387 465960960 : if (spmd_on == 1) then
388 : ! Determine global latitude and longitude coordinate indices from
389 : ! global column index
390 465960960 : glat = (gcol-1)/plon + 1
391 465960960 : glon = gcol - ((glat-1)*plon)
392 :
393 : ! Determine block coordinates (ii,jj), where ii ranges from 1 to
394 : ! nprxy_x and jj ranges from 1 to nprxy_y.
395 : #if ( defined SPMD )
396 465960960 : ii = 0
397 6057492480 : do i = 1, grid%nprxy_x
398 6057492480 : if (lonrangexy(1,i) <= glon .and. glon <= lonrangexy(2,i)) ii=i
399 : end do
400 465960960 : jj = 0
401 30287462400 : do j = 1, grid%nprxy_y
402 30287462400 : if (latrangexy(1,j) <= glat .and. glat <= latrangexy(2,j)) jj=j
403 : end do
404 465960960 : if (ii == 0 .or. jj == 0) then
405 0 : write(iulog,*) sub//': ERROR: could not find block indices for (', &
406 0 : glon,',',glat,' ) '
407 0 : call endrun(sub//': ERROR: could not find block indices')
408 : end if
409 :
410 : ! Global block index
411 465960960 : blockid(1) = (jj-1)*grid%nprxy_x+ii
412 :
413 : ! Local coordinates in block
414 465960960 : j = glat-latrangexy(1,jj)+1
415 465960960 : i = glon-lonrangexy(1,ii)+1
416 465960960 : ddlon = lonrangexy(2,ii)-lonrangexy(1,ii)+1
417 :
418 : ! Local column index in block
419 465960960 : bcid(1) = (j-1)*ddlon+i
420 : #endif
421 : else
422 0 : glat = (gcol-1)/plon + 1
423 0 : glon = gcol - ((glat-1)*plon)
424 :
425 0 : blockid(1) = glat
426 0 : bcid(1) = glon
427 : end if
428 :
429 32133611520 : do j=2,cnt
430 31667650560 : blockid(j) = -1
431 32133611520 : bcid(j) = -1
432 : end do
433 :
434 : end if
435 :
436 465960960 : end subroutine get_gcol_block_d
437 :
438 : !========================================================================================
439 :
440 424673280 : integer function get_gcol_block_cnt_d(gcol)
441 :
442 : ! Return number of blocks contain data for the vertical column
443 : ! with the given global column index
444 :
445 : ! Arguments
446 : integer, intent(in) :: gcol ! global column index
447 : !---------------------------------------------------------------------------
448 :
449 : ! lon/lat block
450 424673280 : get_gcol_block_cnt_d = 1
451 :
452 424673280 : end function get_gcol_block_cnt_d
453 :
454 : !========================================================================================
455 :
456 468320256 : integer function get_block_owner_d(blockid)
457 :
458 : ! Return id of processor that "owns" the indicated block
459 :
460 : ! Arguments
461 : integer, intent(in) :: blockid ! global block id
462 : !---------------------------------------------------------------------------
463 :
464 : ! latitude slice block
465 : #if (defined SPMD)
466 468320256 : if (spmd_on .eq. 1) then
467 468320256 : get_block_owner_d = blockid - 1
468 : else
469 0 : get_block_owner_d = proc(blockid)
470 : end if
471 : #else
472 : get_block_owner_d = 0
473 : #endif
474 :
475 468320256 : end function get_block_owner_d
476 :
477 : !========================================================================================
478 :
479 6144 : subroutine get_horiz_grid_dim_d(hdim1_d, hdim2_d)
480 :
481 : ! Returns declared horizontal dimensions of computational grid.
482 : ! Note that global column ordering is assumed to be compatible
483 : ! with the first dimension major ordering of the 2D array.
484 :
485 : ! Arguments
486 : integer, intent(out) :: hdim1_d ! first horizontal dimension
487 : integer, intent(out) :: hdim2_d ! second horizontal dimension
488 : !---------------------------------------------------------------------------
489 :
490 6144 : if (ngcols_d == 0) then
491 1536 : ngcols_d = plat*plon
492 : end if
493 6144 : hdim1_d = plon
494 6144 : hdim2_d = plat
495 :
496 6144 : end subroutine get_horiz_grid_dim_d
497 :
498 : !========================================================================================
499 :
500 6144 : subroutine get_horiz_grid_d(size, clat_d_out, clon_d_out, area_d_out, wght_d_out, &
501 3072 : lat_d_out, lon_d_out)
502 :
503 : ! Return latitude and longitude (in radians), column surface
504 : ! area (in radians squared) and surface integration weights
505 : ! for global column indices that will be passed to/from physics
506 :
507 : ! Arguments
508 : integer, intent(in) :: size ! array sizes
509 :
510 : real(r8), intent(out), optional :: clat_d_out(size) ! column latitudes
511 : real(r8), intent(out), optional :: clon_d_out(size) ! column longitudes
512 :
513 : real(r8), intent(out), optional :: area_d_out(size) ! column surface
514 : ! area
515 : real(r8), intent(out), optional :: wght_d_out(size) ! column integration
516 : ! weight
517 : real(r8), intent(out), optional :: lat_d_out(size) ! column deg latitudes
518 : real(r8), intent(out), optional :: lon_d_out(size) ! column deg longitudes
519 :
520 : ! Local workspace
521 : integer :: i, j ! loop indices
522 : integer :: n ! column index
523 : real(r8) :: ns_vert(2,plon) ! latitude grid vertices
524 : real(r8) :: ew_vert(2,plon) ! longitude grid vertices
525 : real(r8) :: del_theta ! difference in latitude at a grid point
526 : real(r8) :: del_phi ! difference in longitude at a grid point
527 : real(r8), parameter :: degtorad=pi/180.0_r8 ! convert degrees to radians
528 : character(len=128) :: errormsg
529 : character(len=*), parameter :: sub='get_horiz_grid_d'
530 : !----------------------------------------------------------------------------
531 :
532 3072 : if (present(clon_d_out)) then
533 1536 : if (size == ngcols_d) then
534 : n = 0
535 296448 : do j = 1, plat
536 85231104 : do i = 1, plon
537 84934656 : n = n + 1
538 85229568 : clon_d_out(n) = clon(i,j)
539 : end do
540 : end do
541 0 : else if(size == plon) then
542 0 : clon_d_out(:) = clon(:,1)
543 : else
544 0 : write(errormsg, '(a,4(i0,a))')'clon_d_out array size incorrect (', &
545 0 : size, ' /= ', ngcols_d, ' .and. ', size, ' /= ', plon,') '
546 0 : call endrun(sub//': ERROR: '//errormsg)
547 : end if
548 : end if
549 :
550 3072 : if (present(clat_d_out)) then
551 1536 : if (size == ngcols_d) then
552 : n = 0
553 296448 : do j = 1, plat
554 85231104 : do i = 1, plon
555 84934656 : n = n + 1
556 85229568 : clat_d_out(n) = clat(j)
557 : end do
558 : end do
559 0 : else if (size == plat) then
560 0 : clat_d_out(:) = clat(:)
561 : else
562 0 : write(errormsg, '(a,4(i0,a))')'clat_d_out array size incorrect (', &
563 0 : size, ' /= ', ngcols_d, ' .and. ', size, ' /= ', plat,') '
564 0 : call endrun(sub//': ERROR: '//errormsg)
565 : end if
566 : end if
567 :
568 3072 : if (size==plat .and. present(wght_d_out)) then
569 0 : wght_d_out(:) = w(:)
570 0 : return
571 : end if
572 :
573 3072 : if ( ( present(area_d_out) ) .or. ( present(wght_d_out) ) ) then
574 1536 : if ((size < ngcols_d) .and. present(area_d_out)) then
575 0 : write(errormsg, '(a,2(i0,a))')'area_d_out array size incorrect (', &
576 0 : size, ' /= ', ngcols_d, ') '
577 0 : call endrun(sub//': ERROR: '//errormsg)
578 : else if ((size < ngcols_d) .and. present(area_d_out)) then
579 : write(errormsg, '(a,2(i0,a))')'wght_d_out array size incorrect (', &
580 : size, ' /= ', ngcols_d, ') '
581 : call endrun(sub//': ERROR: '//errormsg)
582 : end if
583 :
584 : n = 0
585 296448 : do j = 1, plat
586 :
587 : ! First, determine vertices of each grid point.
588 : ! Verticies are ordered as follows:
589 : ! ns_vert: 1=lower left, 2 = upper left
590 : ! ew_vert: 1=lower left, 2 = lower right
591 :
592 : ! Latitude vertices
593 255098880 : ns_vert(:,:) = spval
594 294912 : if (j .eq. 1) then
595 443904 : ns_vert(1,:plon) = -90._r8 + (latdeg(1) - latdeg(2))*0.5_r8
596 : else
597 84785664 : ns_vert(1,:plon) = (latdeg(j) + latdeg(j-1) )*0.5_r8
598 : end if
599 :
600 294912 : if (j .eq. plat) then
601 443904 : ns_vert(2,:plon) = 90._r8 + (latdeg(plat) - latdeg(plat-1))*0.5_r8
602 : else
603 84785664 : ns_vert(2,:plon) = (latdeg(j) + latdeg(j+1) )*0.5_r8
604 : end if
605 :
606 : ! Longitude vertices
607 255098880 : ew_vert(:,:) = spval
608 294912 : ew_vert(1,1) = (londeg(1,j) - 360._r8 + londeg(plon,j))*0.5_r8
609 84934656 : ew_vert(1,2:plon) = (londeg(1:plon-1,j)+ londeg(2:plon,j))*0.5_r8
610 84934656 : ew_vert(2,:plon-1) = ew_vert(1,2:plon)
611 294912 : ew_vert(2,plon) = (londeg(plon,j) + (360._r8 + londeg(1,j)))*0.5_r8
612 :
613 85231104 : do i = 1,plon
614 84934656 : n = n + 1
615 :
616 84934656 : if (j .eq. 1) then
617 442368 : del_phi = -sin( latdeg(j)*degtorad ) + sin( ns_vert(2,i)*degtorad )
618 84492288 : else if (j .eq. plat) then
619 442368 : del_phi = sin( latdeg(j)*degtorad ) - sin( ns_vert(1,i)*degtorad )
620 : else
621 84049920 : del_phi = sin( ns_vert(2,i)*degtorad ) - sin( ns_vert(1,i)*degtorad )
622 : end if
623 :
624 84934656 : del_theta = ( ew_vert(2,i) - ew_vert(1,i) )*degtorad
625 :
626 84934656 : if (present(area_d_out)) area_d_out(n) = del_theta*del_phi
627 85229568 : if (present(wght_d_out)) wght_d_out(n) = del_theta*del_phi
628 : end do
629 : end do
630 : end if
631 :
632 3072 : if (present(lon_d_out)) then
633 1536 : if (size == ngcols_d) then
634 : n = 0
635 296448 : do j = 1, plat
636 85231104 : do i = 1, plon
637 84934656 : n = n + 1
638 85229568 : lon_d_out(n) = londeg(i,j)
639 : end do
640 : end do
641 0 : else if(size == plon) then
642 0 : lon_d_out(:) = londeg(:,1)
643 : else
644 0 : write(errormsg, '(a,4(i0,a))')'lon_d_out array size incorrect (', &
645 0 : size, ' /= ', ngcols_d, ' .and. ', size, ' /= ', plon,') '
646 0 : call endrun(sub//': ERROR: '//errormsg)
647 : end if
648 : end if
649 :
650 3072 : if (present(lat_d_out)) then
651 1536 : if (size == ngcols_d) then
652 : n = 0
653 296448 : do j = 1, plat
654 85231104 : do i = 1, plon
655 84934656 : n = n + 1
656 85229568 : lat_d_out(n) = latdeg(j)
657 : end do
658 : end do
659 0 : else if (size == plat) then
660 0 : lat_d_out(:) = latdeg(:)
661 : else
662 0 : write(errormsg, '(a,4(i0,a))')'lat_d_out array size incorrect (', &
663 0 : size, ' /= ', ngcols_d, ' .and. ', size, ' /= ', plat,') '
664 0 : call endrun(sub//': ERROR: '//errormsg)
665 : end if
666 : end if
667 :
668 10752 : end subroutine get_horiz_grid_d
669 :
670 : !========================================================================================
671 :
672 : function get_dyn_grid_parm_real2d(name) result(rval)
673 :
674 : character(len=*), intent(in) :: name
675 : real(r8), pointer :: rval(:,:)
676 :
677 : if(name.eq.'clon') then
678 : rval => clon
679 : else if(name.eq.'londeg') then
680 : rval => londeg
681 : else if(name.eq.'londeg_st') then
682 : rval => londeg_st
683 : else
684 : nullify(rval)
685 : end if
686 : end function get_dyn_grid_parm_real2d
687 :
688 : !========================================================================================
689 :
690 0 : function get_dyn_grid_parm_real1d(name) result(rval)
691 :
692 : character(len=*), intent(in) :: name
693 : real(r8), pointer :: rval(:)
694 :
695 0 : if(name.eq.'clat') then
696 0 : rval => clat
697 0 : else if(name.eq.'latdeg') then
698 0 : rval => latdeg
699 0 : else if(name.eq.'latdeg_st') then
700 0 : rval => latdeg_st
701 0 : else if(name.eq.'clatdeg_staggered') then
702 0 : rval => latdeg_st
703 0 : else if(name.eq.'w') then
704 0 : rval => w
705 0 : else if(name.eq.'w_staggered') then
706 0 : rval => w_staggered
707 : else
708 0 : nullify(rval)
709 : end if
710 0 : end function get_dyn_grid_parm_real1d
711 :
712 : !========================================================================================
713 :
714 270336 : integer function get_dyn_grid_parm(name) result(ival)
715 :
716 : character(len=*), intent(in) :: name
717 :
718 270336 : if (name.eq.'splon') then
719 : ival = splon
720 270336 : else if (name.eq.'beglonxy') then
721 4608 : ival = grid%ifirstxy
722 265728 : else if (name.eq.'endlonxy') then
723 4608 : ival = grid%ilastxy
724 261120 : else if (name.eq.'beglatxy') then
725 4608 : ival = grid%jfirstxy
726 256512 : else if (name.eq.'endlatxy') then
727 4608 : ival = grid%jlastxy
728 251904 : else if (name.eq.'plat') then
729 : ival = plat
730 125952 : else if (name.eq.'plon') then
731 : ival = plon
732 0 : else if (name.eq.'plev') then
733 : ival = plev
734 0 : else if (name.eq.'plevp') then
735 : ival = plevp
736 : else
737 0 : ival = -1
738 : end if
739 :
740 270336 : end function get_dyn_grid_parm
741 :
742 : !========================================================================================
743 :
744 0 : subroutine dyn_grid_find_gcols(lat, lon, nclosest, owners, indx, &
745 0 : jndx, rlat, rlon, idyn_dists)
746 :
747 : ! Return the lat/lon information (and corresponding MPI task numbers (owners))
748 : ! of the global model grid columns nearest to the input coordinate (lat,lon)
749 :
750 : ! arguments
751 : real(r8), intent(in) :: lat
752 : real(r8), intent(in) :: lon
753 : integer, intent(in) :: nclosest
754 : integer, intent(out) :: owners(nclosest)
755 : integer, intent(out) :: indx(nclosest)
756 : integer, intent(out) :: jndx(nclosest)
757 :
758 : real(r8),optional, intent(out) :: rlon(nclosest)
759 : real(r8),optional, intent(out) :: rlat(nclosest)
760 : real(r8),optional, intent(out) :: idyn_dists(nclosest)
761 :
762 : ! local variables
763 : real(r8) :: dist ! the distance (in radians**2 from lat, lon)
764 : real(r8) :: latr, lonr ! lat, lon inputs converted to radians
765 : integer :: ngcols
766 : integer :: i, j
767 :
768 : integer :: blockid(1), bcid(1), lclblockid(1)
769 :
770 0 : real(r8), allocatable :: clat_d(:), clon_d(:), distmin(:)
771 0 : integer, allocatable :: igcol(:)
772 : real(r8), parameter :: rad2deg = 180._r8/pi
773 : !----------------------------------------------------------------------------
774 :
775 0 : latr = lat/rad2deg
776 0 : lonr = lon/rad2deg
777 :
778 0 : ngcols = plon*plat
779 0 : allocate( clat_d(1:ngcols) )
780 0 : allocate( clon_d(1:ngcols) )
781 0 : allocate( igcol(nclosest) )
782 0 : allocate( distmin(nclosest) )
783 :
784 0 : call get_horiz_grid_d(ngcols, clat_d_out=clat_d, clon_d_out=clon_d)
785 :
786 0 : igcol(:) = -999
787 0 : distmin(:) = 1.e10_r8
788 :
789 0 : do i = 1, ngcols
790 :
791 : ! Use the Spherical Law of Cosines to find the great-circle distance.
792 0 : dist = acos(sin(latr) * sin(clat_d(i)) + cos(latr) * cos(clat_d(i)) * &
793 0 : cos(clon_d(i) - lonr)) * rearth
794 0 : do j = nclosest, 1, -1
795 0 : if (dist < distmin(j)) then
796 :
797 0 : if (j < nclosest) then
798 0 : distmin(j+1) = distmin(j)
799 0 : igcol(j+1) = igcol(j)
800 : end if
801 :
802 0 : distmin(j) = dist
803 0 : igcol(j) = i
804 : else
805 : exit
806 : end if
807 : end do
808 : end do
809 :
810 0 : do i = 1,nclosest
811 :
812 0 : call get_gcol_block_d( igcol(i), 1, blockid, bcid, lclblockid )
813 0 : owners(i) = get_block_owner_d(blockid(1))
814 :
815 0 : if ( iam==owners(i) ) then
816 : ! get global lat and lon coordinate indices from global column index
817 : ! -- plon is global number of longitude grid points
818 0 : jndx(i) = (igcol(i)-1)/plon + 1
819 0 : indx(i) = igcol(i) - (jndx(i)-1)*plon
820 : else
821 0 : jndx(i) = -1
822 0 : indx(i) = -1
823 : endif
824 :
825 0 : if ( present(rlat) ) rlat(i) = clat_d(igcol(i)) * rad2deg
826 0 : if ( present(rlon) ) rlon(i) = clon_d(igcol(i)) * rad2deg
827 :
828 0 : if (present(idyn_dists)) then
829 0 : idyn_dists(i) = distmin(i)
830 : end if
831 :
832 : end do
833 :
834 0 : deallocate( clat_d )
835 0 : deallocate( clon_d )
836 0 : deallocate( igcol )
837 0 : deallocate( distmin )
838 :
839 0 : end subroutine dyn_grid_find_gcols
840 :
841 : !========================================================================================
842 :
843 0 : subroutine dyn_grid_get_colndx(igcol, nclosest, owners, indx, jndx)
844 :
845 : ! arguments
846 : integer, intent(in) :: nclosest
847 : integer, intent(in) :: igcol(nclosest)
848 : integer, intent(out) :: owners(nclosest)
849 : integer, intent(out) :: indx(nclosest)
850 : integer, intent(out) :: jndx(nclosest)
851 :
852 : integer :: i
853 : integer :: blockid(1), bcid(1), lclblockid(1)
854 :
855 0 : do i = 1,nclosest
856 :
857 0 : call get_gcol_block_d(igcol(i), 1, blockid, bcid, lclblockid)
858 0 : owners(i) = get_block_owner_d(blockid(1))
859 :
860 0 : if ( iam==owners(i) ) then
861 : ! get global lat and lon coordinate indices from global column index
862 : ! -- plon is global number of longitude grid points
863 0 : jndx(i) = (igcol(i)-1)/plon + 1
864 0 : indx(i) = igcol(i) - (jndx(i)-1)*plon
865 : else
866 0 : jndx(i) = -1
867 0 : indx(i) = -1
868 : end if
869 :
870 : end do
871 :
872 0 : end subroutine dyn_grid_get_colndx
873 :
874 : !========================================================================================
875 :
876 0 : subroutine dyn_grid_get_elem_coords( latndx, rlon, rlat, cdex )
877 :
878 : ! return coordinates of a latitude slice of the block corresponding
879 : ! to latitude index latndx
880 :
881 : ! arguments
882 : integer, intent(in) :: latndx ! lat index
883 :
884 : real(r8),optional, intent(out) :: rlon(:) ! longitudes of the columns in the latndx slice
885 : real(r8),optional, intent(out) :: rlat(:) ! latitudes of the columns in the latndx slice
886 : integer, optional, intent(out) :: cdex(:) ! global column index
887 :
888 : integer :: i, ii, j
889 : !----------------------------------------------------------------------------
890 :
891 0 : if (present(cdex)) cdex(:) = -1
892 0 : if (present(rlat)) rlat(:) = -999._r8
893 0 : if (present(rlon)) rlon(:) = -999._r8
894 :
895 0 : j = latndx
896 0 : ii = 0
897 0 : do i = grid%ifirstxy, grid%ilastxy
898 0 : ii = ii+1
899 0 : if (present(cdex)) cdex(ii) = i + (j-1)*plon
900 0 : if (present(rlat)) rlat(ii) = clat(j)
901 0 : if (present(rlon)) rlon(ii) = clon(i,1)
902 : end do
903 :
904 0 : end subroutine dyn_grid_get_elem_coords
905 :
906 : !========================================================================================
907 :
908 1536 : subroutine define_cam_grids()
909 :
910 : use cam_grid_support, only: horiz_coord_t, horiz_coord_create, iMap
911 : use cam_grid_support, only: cam_grid_register, cam_grid_attribute_register
912 : use cam_grid_support, only: cam_grid_attribute_copy
913 :
914 : integer :: i, j, ind
915 : integer :: beglonxy, endlonxy
916 : integer :: beglatxy, endlatxy
917 : type(horiz_coord_t), pointer :: lat_coord
918 : type(horiz_coord_t), pointer :: lon_coord
919 : type(horiz_coord_t), pointer :: slat_coord
920 : type(horiz_coord_t), pointer :: slon_coord
921 : type(horiz_coord_t), pointer :: zlon_coord
922 1536 : integer(iMap), allocatable :: coord_map(:)
923 1536 : integer(iMap), pointer :: grid_map(:,:)
924 : real(r8) :: zlon_bnds(2,1)
925 1536 : real(r8), allocatable :: latvals(:)
926 1536 : real(r8), pointer :: rattval(:)
927 : logical :: is_lon_distributed
928 : !-----------------------------------------------------------------------------
929 :
930 : ! Note: not using get_horiz_grid_dim_d or get_horiz_grid_d since those
931 : ! are deprecated ('cause I said so' -- goldy)
932 :
933 1536 : nullify(lat_coord)
934 1536 : nullify(lon_coord)
935 1536 : nullify(slat_coord)
936 1536 : nullify(slon_coord)
937 1536 : nullify(zlon_coord)
938 1536 : nullify(grid_map)
939 1536 : nullify(rattval)
940 :
941 1536 : beglonxy = grid%ifirstxy
942 1536 : endlonxy = grid%ilastxy
943 1536 : beglatxy = grid%jfirstxy
944 1536 : endlatxy = grid%jlastxy
945 :
946 1536 : if (iam >= grid%npes_xy) then
947 : ! NB: On inactive PEs, beglonxy should be one and endlonxy should be zero
948 0 : if (beglonxy /= 1) then
949 0 : call endrun("DEFINE_CAM_GRIDS: ERROR: Bad beglonxy")
950 : end if
951 0 : if (endlonxy /= 0) then
952 0 : call endrun("DEFINE_CAM_GRIDS: ERROR: Bad endlonxy")
953 : end if
954 : ! NB: On inactive PEs, beglatxy should be one and endlatxy should be zero
955 0 : if (beglatxy /= 1) then
956 0 : call endrun("DEFINE_CAM_GRIDS: ERROR: Bad beglatxy")
957 : end if
958 0 : if (endlatxy /= 0) then
959 0 : call endrun("DEFINE_CAM_GRIDS: ERROR: Bad endlatxy")
960 : end if
961 : end if
962 :
963 : ! Figure out if lon and slon are distributed dimensions
964 1536 : is_lon_distributed = (grid%nprxy_x > 1)
965 :
966 : ! Grid for cell centers
967 : ! Make a map
968 4608 : allocate(grid_map(4, ((endlonxy - beglonxy + 1) * (endlatxy - beglatxy + 1))))
969 1536 : ind = 0
970 6144 : do i = beglatxy, endlatxy
971 116736 : do j = beglonxy, endlonxy
972 110592 : ind = ind + 1
973 110592 : grid_map(1, ind) = j
974 110592 : grid_map(2, ind) = i
975 110592 : grid_map(3, ind) = j
976 115200 : grid_map(4, ind) = i
977 : end do
978 : end do
979 : ! Cell-centered latitude coordinate
980 4608 : allocate(coord_map(endlatxy - beglatxy + 1))
981 1536 : if (endlatxy >= beglatxy) then
982 1536 : if (beglonxy == 1) then
983 1408 : coord_map = (/ (i, i = beglatxy, endlatxy) /)
984 : else
985 5632 : coord_map = 0
986 : end if
987 : end if
988 : lat_coord => horiz_coord_create('lat', '', plat, 'latitude', &
989 0 : 'degrees_north', beglatxy, endlatxy, latdeg(beglatxy:endlatxy), &
990 1536 : map=coord_map)
991 1536 : deallocate(coord_map)
992 :
993 : ! Cell-centered longitude coordinate
994 1536 : if (is_lon_distributed) then
995 4608 : allocate(coord_map(endlonxy - beglonxy + 1))
996 1536 : if (endlonxy >= beglonxy) then
997 1536 : if (beglatxy == 1) then
998 1776 : coord_map = (/ (i, i = beglonxy, endlonxy) /)
999 : else
1000 37800 : coord_map = 0
1001 : end if
1002 : end if
1003 : lon_coord => horiz_coord_create('lon', '', plon, 'longitude', &
1004 0 : 'degrees_east', beglonxy, endlonxy, londeg(beglonxy:endlonxy,1), &
1005 1536 : map=coord_map)
1006 1536 : deallocate(coord_map)
1007 : else
1008 : lon_coord => horiz_coord_create('lon', '', plon, 'longitude', &
1009 0 : 'degrees_east', beglonxy, endlonxy, londeg(beglonxy:endlonxy,1))
1010 : end if
1011 : ! Cell-centered grid
1012 : call cam_grid_register('fv_centers', dyn_decomp, lat_coord, lon_coord, &
1013 1536 : grid_map, unstruct=.false.)
1014 1536 : allocate(rattval(size(w)))
1015 592896 : rattval = w
1016 1536 : call cam_grid_attribute_register('fv_centers', 'gw', 'latitude weights', 'lat', rattval)
1017 1536 : nullify(rattval)
1018 1536 : nullify(grid_map) ! Belongs to the grid
1019 :
1020 : ! Staggered grid for U_S
1021 : ! Make a map
1022 4608 : allocate(grid_map(4, ((endlonxy - beglonxy + 1) * (endlatxy - beglatxy + 1))))
1023 1536 : ind = 0
1024 6144 : do i = beglatxy, endlatxy
1025 116736 : do j = beglonxy, endlonxy
1026 110592 : ind = ind + 1
1027 110592 : grid_map(1, ind) = j
1028 110592 : grid_map(2, ind) = i
1029 110592 : grid_map(3, ind) = j
1030 115200 : if ((i == beglatxy) .and. (beglatxy == 1)) then
1031 576 : grid_map(4, ind) = 0
1032 : else
1033 110016 : grid_map(4, ind) = i - 1
1034 : end if
1035 : end do
1036 : end do
1037 :
1038 : ! Staggered latitudes 'skip' the first one so they are 'off by one'
1039 : ! This means we always must have a coordinate map
1040 4608 : allocate(coord_map(endlatxy - beglatxy + 1))
1041 : ! NB: coord_map(1) == 0 when beglat == 1, that element is not output
1042 6144 : do i = 1, size(coord_map)
1043 6144 : if (beglonxy == 1) then
1044 384 : coord_map(i) = i + beglatxy - 2
1045 : else
1046 4224 : coord_map(i) = 0
1047 : end if
1048 : end do
1049 1536 : if (iam .lt. grid%npes_xy) then
1050 4608 : allocate(latvals(beglatxy:endlatxy))
1051 1536 : if (beglatxy == 1) then
1052 24 : latvals(1) = 0
1053 96 : latvals(2:endlatxy) = latdeg_st(1:endlatxy-1)
1054 : else
1055 1512 : i = beglatxy - 1 ! Stupid NAG 'error'
1056 7560 : latvals(beglatxy:endlatxy) = latdeg_st(i:endlatxy-1)
1057 : end if
1058 : else
1059 0 : allocate(latvals(0))
1060 : end if
1061 : slat_coord => horiz_coord_create('slat', '', (plat - 1), &
1062 : 'staggered latitude', 'degrees_north', beglatxy, endlatxy, latvals, &
1063 1536 : map=coord_map)
1064 1536 : deallocate(coord_map)
1065 1536 : deallocate(latvals)
1066 :
1067 : call cam_grid_register('fv_u_stagger', dyn_ustag_decomp, slat_coord, &
1068 1536 : lon_coord, grid_map, unstruct=.false.)
1069 : call cam_grid_attribute_register('fv_u_stagger', 'w_stag', &
1070 1536 : 'staggered latitude weights', 'slat', w_staggered)
1071 1536 : nullify(grid_map) ! Belongs to the grid
1072 :
1073 : ! Staggered grid for V_S
1074 : ! Make a map (need to do this because lat indices are distributed)
1075 4608 : allocate(grid_map(4, ((endlonxy - beglonxy + 1) * (endlatxy - beglatxy + 1))))
1076 1536 : ind = 0
1077 6144 : do i = beglatxy, endlatxy
1078 116736 : do j = beglonxy, endlonxy
1079 110592 : ind = ind + 1
1080 110592 : grid_map(1, ind) = j
1081 110592 : grid_map(2, ind) = i
1082 110592 : grid_map(3, ind) = j
1083 115200 : grid_map(4, ind) = i
1084 : end do
1085 : end do
1086 : ! Staggered longitude coordinate
1087 1536 : if (is_lon_distributed) then
1088 4608 : allocate(coord_map(endlonxy - beglonxy + 1))
1089 1536 : if (endlonxy >= beglonxy) then
1090 1536 : if (beglatxy == 1) then
1091 1776 : coord_map = (/ (i, i = beglonxy, endlonxy) /)
1092 : else
1093 37800 : coord_map = 0
1094 : end if
1095 : end if
1096 : slon_coord => horiz_coord_create('slon', '', plon, 'staggered longitude', &
1097 0 : 'degrees_east', beglonxy, endlonxy, londeg_st(beglonxy:endlonxy,1), &
1098 1536 : map=coord_map)
1099 1536 : deallocate(coord_map)
1100 : else
1101 : slon_coord => horiz_coord_create('slon', '', plon, 'staggered longitude', &
1102 0 : 'degrees_east', beglonxy, endlonxy, londeg_st(beglonxy:endlonxy,1))
1103 : end if
1104 : call cam_grid_register('fv_v_stagger', dyn_vstag_decomp, lat_coord, &
1105 1536 : slon_coord, grid_map, unstruct=.false.)
1106 1536 : nullify(grid_map) ! Belongs to the grid
1107 :
1108 : ! Zonal mean grid
1109 : ! Make a map
1110 4608 : allocate(grid_map(4, (endlatxy - beglatxy + 1)))
1111 1536 : ind = 0
1112 6144 : do i = beglatxy, endlatxy
1113 4608 : ind = ind + 1
1114 4608 : grid_map(1, ind) = 1
1115 4608 : grid_map(2, ind) = i
1116 6144 : if (beglonxy == 1) then
1117 384 : grid_map(3, ind) = 1
1118 384 : grid_map(4, ind) = i
1119 : else
1120 4224 : grid_map(3, ind) = 0
1121 4224 : grid_map(4, ind) = 0
1122 : end if
1123 : end do
1124 : ! We need a special, size-one "longigude" coordinate
1125 : ! NB: This is never a distributed coordinate so calc even on inactive PEs
1126 85231104 : zlon_bnds(1,1) = minval(londeg)
1127 85231104 : zlon_bnds(2,1) = maxval(londeg)
1128 1536 : allocate(latvals(1)) ! Really for a longitude
1129 1536 : latvals(1) = 0._r8
1130 : zlon_coord => horiz_coord_create('zlon', '', 1, 'longitude', &
1131 1536 : 'degrees_east', 1, 1, latvals(1:1), bnds=zlon_bnds)
1132 1536 : deallocate(latvals)
1133 : ! Zonal mean grid
1134 : call cam_grid_register('fv_centers_zonal', dyn_zonal_decomp, lat_coord, &
1135 1536 : zlon_coord, grid_map, unstruct=.false., zonal_grid=.true.)
1136 : ! Make sure 'gw' attribute shows up even if all variables are zonal mean
1137 1536 : call cam_grid_attribute_copy('fv_centers', 'fv_centers_zonal', 'gw')
1138 1536 : nullify(grid_map) ! Belongs to the grid
1139 :
1140 3072 : end subroutine define_cam_grids
1141 :
1142 : !========================================================================================
1143 :
1144 1536 : subroutine physgrid_copy_attributes_d(gridname, grid_attribute_names)
1145 1536 : use cam_grid_support, only: max_hcoordname_len
1146 :
1147 : ! Dummy arguments
1148 : character(len=max_hcoordname_len), intent(out) :: gridname
1149 : character(len=max_hcoordname_len), pointer, intent(out) :: grid_attribute_names(:)
1150 :
1151 1536 : gridname = 'fv_centers'
1152 1536 : allocate(grid_attribute_names(1))
1153 1536 : grid_attribute_names(1) = 'gw'
1154 :
1155 1536 : end subroutine physgrid_copy_attributes_d
1156 :
1157 : !========================================================================================
1158 :
1159 : end module dyn_grid
|