NCEPLIBS-ip 5.3.0
All Data Structures Namespaces Files Functions Variables Pages
ip_gaussian_grid_mod.F90
Go to the documentation of this file.
1!> @file
2!! @brief Gaussian grid coordinate transformations.
3!! @author Mark Iredell, George Gayno, Kyle Gerheiser
4!! @date July 2021
5
6!> @brief Gaussian grid coordinate transformations.
7!!
8!! Octet numbers refer to [GRIB2 - GRID DEFINITION TEMPLATE 3.40
9!! Gaussian
10!! Latitude/Longitude](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp3-40.shtml).
11!!
12!! @author George Gayno, Mark Iredell, Kyle Gerheiser
13!! @date July 2021
16 use ip_grid_mod
19 use sp_mod
20 implicit none
21
22 private
23 public :: ip_gaussian_grid
24
25 type, extends(ip_grid) :: ip_gaussian_grid
26 integer :: jh !< Scan mode flag in 'j' direction. When '1' points scan from N to S. When "-1" points scan from S to N.
27 real :: dlon !< "i"-direction increment. GRIB2 Section 3, octets 64-67.
28 real :: rlat1 !< Latitude of first grid point. GRIB2 Section 3, octets 47-50.
29 real :: rlon1 !< Longitude of first grid point. GRIB2 Section 3, octets 51-54.
30 real :: rlon2 !< Longitude of last grid point. GRIB2 Section 3, octets 60-63.
31 real :: hi !< Scan mode flag in 'i' direction. When '1' points scan from W to E. When "-1" points scan from E to W.
32 integer :: jg !< Number of parallels between the equator and pole times 2. GRIB2 Section 3, octets 68-71.
33 integer :: jscan !< Scanning mode in the 'j' direction. GRIB2 Section 3, octet 72.
34 contains
35 !> Initializes a gaussian grid given a grib1_descriptor object.
36 !> @return N/A @copydoc ip_gaussian_grid_mod::init_grib1
37 procedure :: init_grib1
38 !> Initializes a gaussian grid given a grib2_descriptor object.
39 !> @return N/A @copydoc ip_gaussian_grid_mod::init_grib2
40 procedure :: init_grib2
41 !> Calculates Earth coordinates (iopt = 1) or grid coorindates (iopt = -1)
42 !> for Gaussian grids.
43 !> @return N/A @copydoc ip_gaussian_grid_mod::gdswzd_gaussian
44 procedure :: gdswzd => gdswzd_gaussian
45 end type ip_gaussian_grid
46
47 INTEGER :: j1 !< 'j' index of first grid point within the global array of latitudes.
48 INTEGER :: jh !< Scan mode flag in 'j' direction. When '1' points scan from N to S. When "-1" points scan from S to N.
49 REAL, ALLOCATABLE :: blat(:) !< Gaussian latitude for each parallel.
50 REAL :: dlon !< "i"-direction increment. GRIB2 Section 3, octets 64-67.
51 REAL :: rerth !< Radius of the earth. GRIB2 Section 3, octets 15-30.
52 REAL, ALLOCATABLE :: ylat_row(:) !< dy/dlat for each row in 1/degrees.
53
54contains
55
56 !> Initializes a gaussian grid given a grib1_descriptor object.
57 !>
58 !> @param[inout] self The grid to initialize
59 !> @param[in] g1_desc A grib1_descriptor
60 !>
61 !> @author Kyle Gerheiser
62 !> @date July 2021
63 subroutine init_grib1(self, g1_desc)
64 class(ip_gaussian_grid), intent(inout) :: self
65 type(grib1_descriptor), intent(in) :: g1_desc
66
67 integer :: iscan, jg
68
69 associate(kgds => g1_desc%gds)
70 self%rerth = 6.3712e6
71 self%eccen_squared = 0.0
72
73 self%IM=kgds(2)
74 self%JM=kgds(3)
75 self%RLAT1=kgds(4)*1.e-3
76 self%RLON1=kgds(5)*1.e-3
77 self%RLON2=kgds(8)*1.e-3
78 self%JG=kgds(10)*2
79 iscan=mod(kgds(11)/128,2)
80 self%JSCAN=mod(kgds(11)/64,2)
81 self%HI=(-1.)**iscan
82 self%JH=(-1)**self%JSCAN
83 self%DLON=self%HI*(mod(self%HI*(self%RLON2-self%RLON1)-1+3600,360.)+1)/(self%IM-1)
84
85 self%iwrap = 0
86 self%jwrap1 = 0
87 self%jwrap2 = 0
88 self%nscan = mod(kgds(11) / 32, 2)
89 self%nscan_field_pos = self%nscan
90 self%kscan = 0
91
92 self%iwrap=nint(360 / abs(self%dlon))
93 if(self%im < self%iwrap) self%iwrap = 0
94
95 if(self%iwrap > 0 .and. mod(self%iwrap, 2) == 0) then
96 jg=kgds(10)*2
97 if(self%jm == self%jg) then
98 self%jwrap1 = 1
99 self%jwrap2 = 2 * self%jm + 1
100 endif
101 endif
102
103 end associate
104 end subroutine init_grib1
105
106 !> Initializes a gaussian grid given a grib2_descriptor object.
107 !> @param[inout] self The grid to initialize
108 !> @param[in] g2_desc A grib2_descriptor
109 !>
110 !> @author Kyle Gerheiser
111 !> @date July 2021
112 subroutine init_grib2(self, g2_desc)
113 class(ip_gaussian_grid), intent(inout) :: self
114 type(grib2_descriptor), intent(in) :: g2_desc
115
116 integer :: iscale, iscan, jg
117
118 associate(igdtmpl => g2_desc%gdt_tmpl, igdtlen => g2_desc%gdt_len)
119 call earth_radius(igdtmpl, igdtlen, self%rerth, self%eccen_squared)
120
121 self%IM=igdtmpl(8)
122 self%JM=igdtmpl(9)
123 iscale=igdtmpl(10)*igdtmpl(11)
124 IF(iscale==0) iscale=10**6
125 self%RLAT1=float(igdtmpl(12))/float(iscale)
126 self%RLON1=float(igdtmpl(13))/float(iscale)
127 self%RLON2=float(igdtmpl(16))/float(iscale)
128 self%JG=igdtmpl(18)*2
129 iscan=mod(igdtmpl(19)/128,2)
130 self%JSCAN=mod(igdtmpl(19)/64,2)
131 self%HI=(-1.)**iscan
132 self%JH=(-1)**self%JSCAN
133 self%DLON=self%HI*(mod(self%HI*(self%RLON2-self%RLON1)-1+3600,360.)+1)/(self%IM-1)
134
135
136 self%iwrap = nint(360 / abs(self%dlon))
137 if(self%im < self%iwrap) self%iwrap = 0
138 self%jwrap1 = 0
139 self%jwrap2 = 0
140 if(self%iwrap > 0 .and. mod(self%iwrap, 2) == 0) then
141 jg = igdtmpl(18) * 2
142 if(self%jm == jg) then
143 self%jwrap1=1
144 self%jwrap2 = 2 * self%jm + 1
145 endif
146 endif
147 self%nscan = mod(igdtmpl(19) / 32, 2)
148 self%nscan_field_pos = self%nscan
149 self%kscan = 0
150 end associate
151
152 end subroutine init_grib2
153
154 !> Calculates Earth coordinates (iopt = 1) or grid coorindates (iopt = -1)
155 !> for Gaussian grids.
156 !>
157 !> If the selected coordinates are more than one gridpoint
158 !> beyond the the edges of the grid domain, then the relevant
159 !> output elements are set to fill values.
160 !>
161 !> The actual number of valid points computed is returned too.
162 !> Optionally, the vector rotations, the map jacobians and
163 !> the grid box areas may be returned as well.
164 !>
165 !> To compute the vector rotations, the optional arguments 'srot' and 'crot'
166 !> must be present.
167 !>
168 !> To compute the map jacobians, the optional arguments
169 !> 'xlon', 'xlat', 'ylon', 'ylat' must be present.
170 !>
171 !> To compute the grid box areas, the optional argument
172 !> 'area' must be present.
173 !>
174 !> @param[in] self The grid object gdswzd was called on.
175 !> @param[in] iopt option flag
176 !> - +1 to compute earth coords of selected grid coords.
177 !> - -1 o compute grid coords of selected earth coords.
178 !> @param[in] npts Maximum number of coordinates.
179 !> @param[in] fill Fill value to set invalid output data.
180 !> Must be impossible value; suggested value: -9999.
181 !> @param[inout] xpts Grid x point coordinates if iopt>0.
182 !> @param[inout] ypts Grid y point coordinates if iopt>0.
183 !> @param[inout] rlon Earth longitudes in degrees e if iopt<0
184 !> (Acceptable range: -360. to 360.)
185 !> @param[inout] rlat Earth latitudes in degrees n if iopt<0
186 !> (Acceptable range: -90. to 90.)
187 !> @param[out] nret Number of valid points computed.
188 !> @param[out] crot Optional clockwise vector rotation cosines.
189 !> @param[out] srot Optional clockwise vector rotation sines.
190 !> @param[out] xlon Optional dx/dlon in 1/degrees.
191 !> @param[out] xlat Optional dx/dlat in 1/degrees.
192 !> @param[out] ylon Optional dy/dlon in 1/degrees.
193 !> @param[out] ylat Optional dy/dlat in 1/degrees.
194 !> @param[out] area Optional area weights in m**2.
195 !>
196 !> @author Mark Iredell, George Gayno, Kyle Gerheiser
197 !> @date July 2021
198 SUBROUTINE gdswzd_gaussian(self,IOPT,NPTS,FILL, &
199 XPTS,YPTS,RLON,RLAT,NRET, &
200 CROT,SROT,XLON,XLAT,YLON,YLAT,AREA)
201 IMPLICIT NONE
202 !
203 class(ip_gaussian_grid), intent(in) :: self
204 INTEGER, INTENT(IN ) :: IOPT, NPTS
205 INTEGER, INTENT( OUT) :: NRET
206 !
207 REAL, INTENT(IN ) :: FILL
208 REAL, INTENT(INOUT) :: RLON(NPTS),RLAT(NPTS)
209 REAL, INTENT(INOUT) :: XPTS(NPTS),YPTS(NPTS)
210 REAL, OPTIONAL, INTENT( OUT) :: CROT(NPTS),SROT(NPTS)
211 REAL, OPTIONAL, INTENT( OUT) :: XLON(NPTS),XLAT(NPTS)
212 REAL, OPTIONAL, INTENT( OUT) :: YLON(NPTS),YLAT(NPTS),AREA(NPTS)
213 !
214 INTEGER :: JSCAN, IM, JM
215 INTEGER :: J, JA, JG
216 INTEGER :: N
217 !
218 LOGICAL :: LROT, LMAP, LAREA
219 !
220 REAL, ALLOCATABLE :: ALAT(:), ALAT_JSCAN(:)
221 REAL, ALLOCATABLE :: ALAT_TEMP(:),BLAT_TEMP(:)
222 REAL :: HI, RLATA, RLATB, RLAT1, RLON1, RLON2
223 REAL :: XMAX, XMIN, YMAX, YMIN, YPTSA, YPTSB
224 REAL :: WB
225
226 IF(PRESENT(crot)) crot=fill
227 IF(PRESENT(srot)) srot=fill
228 IF(PRESENT(xlon)) xlon=fill
229 IF(PRESENT(xlat)) xlat=fill
230 IF(PRESENT(ylon)) ylon=fill
231 IF(PRESENT(ylat)) ylat=fill
232 IF(PRESENT(area)) area=fill
233
234 IF(PRESENT(crot).AND.PRESENT(srot))THEN
235 lrot=.true.
236 ELSE
237 lrot=.false.
238 ENDIF
239 IF(PRESENT(xlon).AND.PRESENT(xlat).AND.PRESENT(ylon).AND.PRESENT(ylat))THEN
240 lmap=.true.
241 ELSE
242 lmap=.false.
243 ENDIF
244 IF(PRESENT(area))THEN
245 larea=.true.
246 ELSE
247 larea=.false.
248 ENDIF
249
250 im=self%im
251 jm=self%jm
252
253 rlat1=self%rlat1
254 rlon1=self%rlon1
255 rlon2=self%rlon2
256
257 jg=self%jg
258 jscan=self%jscan
259 hi=self%hi
260
261 jh=self%jh
262 dlon=self%dlon
263 rerth = self%rerth
264
265 ALLOCATE(alat_temp(jg))
266 ALLOCATE(blat_temp(jg))
267 CALL splat(4,jg,alat_temp,blat_temp)
268 ALLOCATE(alat(0:jg+1))
269 ALLOCATE(blat(0:jg+1))
270 !$OMP PARALLEL DO PRIVATE(JA) SCHEDULE(STATIC)
271 DO ja=1,jg
272 alat(ja)=real(dpr*asin(alat_temp(ja)))
273 blat(ja)=blat_temp(ja)
274 ENDDO
275 !$OMP END PARALLEL DO
276 DEALLOCATE(alat_temp,blat_temp)
277 alat(0)=180.-alat(1)
278 alat(jg+1)=-alat(0)
279 blat(0)=-blat(1)
280 blat(jg+1)=blat(0)
281 j1=1
282 DO WHILE(j1.LT.jg.AND.rlat1.LT.(alat(j1)+alat(j1+1))/2)
283 j1=j1+1
284 ENDDO
285 IF(lmap)THEN
286 ALLOCATE(alat_jscan(jg))
287 DO ja=1,jg
288 alat_jscan(j1+jh*(ja-1))=alat(ja)
289 ENDDO
290 ALLOCATE(ylat_row(0:jg+1))
291 DO ja=2,(jg-1)
292 ylat_row(ja)=2.0/(alat_jscan(ja+1)-alat_jscan(ja-1))
293 ENDDO
294 ylat_row(1)=1.0/(alat_jscan(2)-alat_jscan(1))
295 ylat_row(0)=ylat_row(1)
296 ylat_row(jg)=1.0/(alat_jscan(jg)-alat_jscan(jg-1))
297 ylat_row(jg+1)=ylat_row(jg)
298 DEALLOCATE(alat_jscan)
299 ENDIF
300 xmin=0
301 xmax=im+1
302 IF(im.EQ.nint(360/abs(dlon))) xmax=im+2
303 ymin=0.5
304 ymax=jm+0.5
305 nret=0
306
307 ! TRANSLATE GRID COORDINATES TO EARTH COORDINATES
308 IF(iopt.EQ.0.OR.iopt.EQ.1) THEN
309 !$OMP PARALLEL DO PRIVATE(N,J,WB,RLATA,RLATB) REDUCTION(+:NRET) SCHEDULE(STATIC)
310 DO n=1,npts
311 IF(xpts(n).GE.xmin.AND.xpts(n).LE.xmax.AND. &
312 ypts(n).GE.ymin.AND.ypts(n).LE.ymax) THEN
313 rlon(n)=mod(rlon1+dlon*(xpts(n)-1)+3600,360.)
314 j=int(ypts(n))
315 wb=ypts(n)-j
316 rlata=alat(j1+jh*(j-1))
317 rlatb=alat(j1+jh*j)
318 rlat(n)=rlata+wb*(rlatb-rlata)
319 nret=nret+1
320 IF(lrot) CALL gaussian_vect_rot(crot(n),srot(n))
321 IF(lmap) CALL gaussian_map_jacob(ypts(n),&
322 xlon(n),xlat(n),ylon(n),ylat(n))
323 IF(larea) CALL gaussian_grid_area(ypts(n),area(n))
324 ELSE
325 rlon(n)=fill
326 rlat(n)=fill
327 ENDIF
328 ENDDO
329 !$OMP END PARALLEL DO
330
331 ! TRANSLATE EARTH COORDINATES TO GRID COORDINATES
332 ELSEIF(iopt.EQ.-1) THEN
333 !$OMP PARALLEL DO PRIVATE(N,JA,YPTSA, YPTSB, WB) REDUCTION(+:NRET) SCHEDULE(STATIC)
334 DO n=1,npts
335 xpts(n)=fill
336 ypts(n)=fill
337 IF(abs(rlon(n)).LE.360.AND.abs(rlat(n)).LE.90) THEN
338 xpts(n)=1+hi*mod(hi*(rlon(n)-rlon1)+3600,360.)/dlon
339 ja=min(int((jg+1)/180.*(90-rlat(n))),jg)
340 IF(rlat(n).GT.alat(ja)) ja=max(ja-2,0)
341 IF(rlat(n).LT.alat(ja+1)) ja=min(ja+2,jg)
342 IF(rlat(n).GT.alat(ja)) ja=ja-1
343 IF(rlat(n).LT.alat(ja+1)) ja=ja+1
344 yptsa=1+jh*(ja-j1)
345 yptsb=1+jh*(ja+1-j1)
346 wb=(alat(ja)-rlat(n))/(alat(ja)-alat(ja+1))
347 ypts(n)=yptsa+wb*(yptsb-yptsa)
348 IF(xpts(n).GE.xmin.AND.xpts(n).LE.xmax.AND. &
349 ypts(n).GE.ymin.AND.ypts(n).LE.ymax) THEN
350 nret=nret+1
351 IF(lrot) CALL gaussian_vect_rot(crot(n),srot(n))
352 IF(lmap) CALL gaussian_map_jacob(ypts(n), &
353 xlon(n),xlat(n),ylon(n),ylat(n))
354 IF(larea) CALL gaussian_grid_area(ypts(n),area(n))
355 ELSE
356 xpts(n)=fill
357 ypts(n)=fill
358 ENDIF
359 ENDIF
360 ENDDO
361 !$OMP END PARALLEL DO
362 ENDIF
363 DEALLOCATE(alat, blat)
364 IF (ALLOCATED(ylat_row)) DEALLOCATE(ylat_row)
365
366 END SUBROUTINE gdswzd_gaussian
367
368 !> Computes the vector rotation sines and cosines for a gaussian
369 !> cylindrical grid.
370 !>
371 !> @param[out] crot Clockwise vector rotation cosines.
372 !> @param[out] srot Clockwise vector rotation sines.
373 !>
374 !> @note
375 !> ugrid=crot*uearth-srot*vearth;
376 !> vgrid=srot*uearth+crot*vearth)
377 !>
378 !> @author George Gayno
379 !> @date July 2021
380 SUBROUTINE gaussian_vect_rot(CROT,SROT)
381 IMPLICIT NONE
382
383 REAL, INTENT( OUT) :: CROT, SROT
384
385 crot=1.0
386 srot=0.0
387
388 END SUBROUTINE gaussian_vect_rot
389
390 !> Computes the map jacobians for a gaussian cylindrical grid.
391 !>
392 !> @param[in] ypts y-index of grid point.
393 !> @param[out] xlon dx/dlon in 1/degrees.
394 !> @param[out] xlat dx/dlat in 1/degrees.
395 !> @param[out] ylon dy/dlon in 1/degrees.
396 !> @param[out] ylat dy/dlat in 1/degrees.
397 !>
398 !> @author George Gayno
399 !> @date July 2021
400 SUBROUTINE gaussian_map_jacob(YPTS, XLON, XLAT, YLON, YLAT)
401 IMPLICIT NONE
402
403 REAL, INTENT(IN ) :: YPTS
404 REAL, INTENT( OUT) :: XLON, XLAT, YLON, YLAT
405
406 xlon=1/dlon
407 xlat=0.
408 ylon=0.
409 ylat=ylat_row(nint(ypts))
410
411 END SUBROUTINE gaussian_map_jacob
412
413 !> Computes the grid box area for a gaussian cylindrical grid.
414 !>
415 !> @param[in] ypts y-index of grid point.
416 !> @param[out] area Area weights in m^2
417 !>
418 !> @author Mark Iredell, George Gayno
419 !> @date July 2021
420 SUBROUTINE gaussian_grid_area(YPTS,AREA)
421 IMPLICIT NONE
422
423 REAL, INTENT(IN ) :: YPTS
424 REAL, INTENT( OUT) :: AREA
425
426 INTEGER :: J
427
428 REAL :: WB, WLAT, WLATA, WLATB
429
430 j = int(ypts)
431 wb=ypts-j
432 wlata=blat(j1+jh*(j-1))
433 wlatb=blat(j1+jh*j)
434 wlat=wlata+wb*(wlatb-wlata)
435 area=real(rerth**2*wlat*dlon/dpr)
436
437 END SUBROUTINE gaussian_grid_area
438end module ip_gaussian_grid_mod
439
void gdswzd(int igdtnum, int *igdtmpl, int igdtlen, int iopt, int npts, float fill, float *xpts, float *ypts, float *rlon, float *rlat, int *nret, float *crot, float *srot, float *xlon, float *xlat, float *ylon, float *ylat, float *area)
gdswzd() interface for C for _4 build of library.
Determine earth radius and shape.
Module containing common constants.
Gaussian grid coordinate transformations.
subroutine gdswzd_gaussian(self, iopt, npts, fill, xpts, ypts, rlon, rlat, nret, crot, srot, xlon, xlat, ylon, ylat, area)
Calculates Earth coordinates (iopt = 1) or grid coorindates (iopt = -1) for Gaussian grids.
real, dimension(:), allocatable ylat_row
dy/dlat for each row in 1/degrees.
subroutine gaussian_grid_area(ypts, area)
Computes the grid box area for a gaussian cylindrical grid.
integer jh
Scan mode flag in 'j' direction.
subroutine gaussian_map_jacob(ypts, xlon, xlat, ylon, ylat)
Computes the map jacobians for a gaussian cylindrical grid.
real rerth
Radius of the earth.
real dlon
"i"-direction increment.
subroutine init_grib1(self, g1_desc)
Initializes a gaussian grid given a grib1_descriptor object.
subroutine gaussian_vect_rot(crot, srot)
Computes the vector rotation sines and cosines for a gaussian cylindrical grid.
subroutine init_grib2(self, g2_desc)
Initializes a gaussian grid given a grib2_descriptor object.
real, dimension(:), allocatable blat
Gaussian latitude for each parallel.
integer j1
'j' index of first grid point within the global array of latitudes.
Users derived type grid descriptor objects to abstract away the raw GRIB1 and GRIB2 grid definitions.
Abstract ip_grid type.
subroutine splat(idrt, jmax, slat, wlat)
Computes cosines of colatitude and Gaussian weights for one of the following specific global sets of ...
Definition splat.F:46
Descriptor representing a grib1 grib descriptor section (GDS) with an integer array.
Abstract grid that holds fields and methods common to all grids.