NCEPLIBS-ip 5.3.0
All Data Structures Namespaces Files Functions Variables Pages
ip_equid_cylind_grid_mod.F90
Go to the documentation of this file.
1!> @file
2!! @brief Equidistant cylindrical grib decoder and grid coordinate
3!! transformations.
4!!
5!! @author Mark Iredell, George Gayno, Kyle Gerheiser
6!! @date July 2021
7
8!> @brief Equidistant cylindrical grib decoder and grid coordinate
9!! transformations.
10!!
11!! Octet numbers refer to [GRIB2 - GRID DEFINITION TEMPLATE 3.0
12!! Latitude/Longitude or equidistant cylindrical, or Plate
13!! Carree](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp3-0.shtml).
14!!
15!! @author George Gayno, Mark Iredell, Kyle Gerheiser
16!! @date July 2021
19 use ip_grid_mod
21 implicit none
22
23 private
24 public :: ip_equid_cylind_grid
25
26 type, extends(ip_grid) :: ip_equid_cylind_grid
27 real :: hi !< Scan mode in the 'i' direction. GRIB2, Section 3, octet 72.
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 :: rlat2 !< Latitude of last grid point. GRIB2, Section 3, octets 56-59.
31 real :: rlon2 !< Longitude of last grid point. GRIB2, Section 3, octets 60-63.
32 real :: dlat !< Di — i direction increment. GRIB2, Section 3, octets 64-67.
33 real :: dlon !< Dj — j direction increment. GRIB2, Section 3, octets 68-71.
34 contains
35 !> @return N/A @copydoc ip_equid_cylind_grid_mod::init_grib1
36 procedure :: init_grib1 !< Init GRIB1.
37 !> @return N/A @copydoc ip_equid_cylind_grid_mod::init_grib2
38 procedure :: init_grib2 !< Init GRIB2.
39 !> @return N/A @copydoc ip_equid_cylind_grid_mod::gdswzd_equid_cylind
42
43 REAL :: dlat !< Grid resolution in degrees n/s direction.
44 REAL :: dlon !< Grid resolution in degrees e/w direction.
45 REAL :: rerth !< Radius of the Earth.
46
47contains
48
49 !> Initializes an equidistant cylindrical grid given a grib1_descriptor object.
50 !!
51 !! @param[inout] self The grid to initialize
52 !! @param[in] g1_desc A grib1_descriptor
53 !!
54 !! @author Kyle Gerheiser
55 !! @date July 2021
56 subroutine init_grib1(self, g1_desc)
57 class(ip_equid_cylind_grid), intent(inout) :: self
58 type(grib1_descriptor), intent(in) :: g1_desc
59
60 integer :: iscan
61
62 associate(kgds => g1_desc%gds)
63 self%IM=kgds(2)
64 self%JM=kgds(3)
65 self%RLAT1=kgds(4)*1.e-3
66 self%RLON1=kgds(5)*1.e-3
67 self%RLAT2=kgds(7)*1.e-3
68 self%RLON2=kgds(8)*1.e-3
69 iscan=mod(kgds(11)/128,2)
70 self%HI=(-1.)**iscan
71 self%DLON=self%HI*(mod(self%HI*(self%RLON2-self%RLON1)-1+3600,360.)+1)/(self%IM-1)
72 self%DLAT=(self%RLAT2-self%RLAT1)/(self%JM-1)
73
74 ! defaults
75 self%iwrap = 0
76 self%jwrap1 = 0
77 self%jwrap2 = 0
78 self%nscan = mod(kgds(11) / 32, 2)
79 self%nscan_field_pos = self%nscan
80 self%kscan = 0
81
82 self%iwrap = nint(360/abs(self%dlon))
83
84 if(self%im < self%iwrap) self%iwrap=0
85 self%jwrap1 = 0
86 self%jwrap2 = 0
87 if(self%iwrap > 0 .and. mod(self%iwrap,2) == 0) then
88 if(abs(self%rlat1) > 90-0.25*self%dlat) then
89 self%jwrap1 = 2
90 elseif(abs(self%rlat1) > 90-0.75*self%dlat) then
91 self%jwrap1 = 1
92 endif
93 if(abs(self%rlat2) > 90-0.25*self%dlat) then
94 self%jwrap2 = 2 * self%jm
95 elseif(abs(self%rlat2) > 90-0.75*self%dlat) then
96 self%jwrap2 = 2 * self%jm+1
97 endif
98 endif
99
100 self%rerth = 6.3712e6
101 self%eccen_squared = 0.0
102 end associate
103
104 end subroutine init_grib1
105
106 !> Initializes an equidistant cylindrical 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_equid_cylind_grid), intent(inout) :: self
114 type(grib2_descriptor), intent(in) :: g2_desc
115
116 integer :: iscale, iscan
117
118 associate(igdtmpl => g2_desc%gdt_tmpl, igdtlen => g2_desc%gdt_len)
119 self%IM=igdtmpl(8)
120 self%JM=igdtmpl(9)
121 iscale=igdtmpl(10)*igdtmpl(11)
122 IF(iscale==0) iscale=10**6
123 self%RLAT1=float(igdtmpl(12))/float(iscale)
124 self%RLON1=float(igdtmpl(13))/float(iscale)
125 self%RLAT2=float(igdtmpl(15))/float(iscale)
126 self%RLON2=float(igdtmpl(16))/float(iscale)
127 iscan=mod(igdtmpl(19)/128,2)
128 self%HI=(-1.)**iscan
129 self%DLON=self%HI*(mod(self%HI*(self%RLON2-self%RLON1)-1+3600,360.)+1)/(self%IM-1)
130 self%DLAT=(self%RLAT2-self%RLAT1)/(self%JM-1)
131
132 self%nscan = mod(igdtmpl(19)/32,2)
133 self%nscan_field_pos = self%nscan
134 self%kscan = 0
135 self%iwrap = nint(360/abs(self%DLON))
136
137 if(self%im.lt.self%iwrap) self%iwrap=0
138 self%jwrap1=0
139 self%jwrap2=0
140
141 if(self%im < self%iwrap) self%iwrap=0
142 self%jwrap1 = 0
143 self%jwrap2 = 0
144 if(self%iwrap > 0 .and. mod(self%iwrap,2) == 0) then
145 if(abs(self%rlat1) > 90-0.25*self%dlat) then
146 self%jwrap1 = 2
147 elseif(abs(self%rlat1) > 90-0.75*self%dlat) then
148 self%jwrap1 = 1
149 endif
150 if(abs(self%rlat2) > 90-0.25*self%dlat) then
151 self%jwrap2 = 2 * self%jm
152 elseif(abs(self%rlat2) > 90-0.75*self%dlat) then
153 self%jwrap2 = 2 * self%jm+1
154 endif
155 endif
156
157 call earth_radius(igdtmpl, igdtlen, self%rerth, self%eccen_squared)
158
159 end associate
160 end subroutine init_grib2
161
162 !> Calculates Earth coordinates (iopt = 1) or grid coorindates (iopt = -1)
163 !! for equidistant cylindrical grids.
164 !!
165 !! If the selected coordinates are more than one gridpoint
166 !! beyond the the edges of the grid domain, then the relevant
167 !! output elements are set to fill values.
168 !!
169 !! The actual number of valid points computed is returned too.
170 !! Optionally, the vector rotations, the map jacobians and
171 !! the grid box areas may be returned as well.
172 !!
173 !! To compute the vector rotations, the optional arguments 'srot' and 'crot'
174 !! must be present.
175 !!
176 !! To compute the map jacobians, the optional arguments
177 !! 'xlon', 'xlat', 'ylon', 'ylat' must be present.
178 !!
179 !! To compute the grid box areas, the optional argument
180 !! 'area' must be present.
181 !!
182 !! @param[in] self The grid object gdswzd was called on.
183 !! @param[in] iopt option flag
184 !! - +1 to compute earth coords of selected grid coords.
185 !! - -1 o compute grid coords of selected earth coords.
186 !! @param[in] npts Maximum number of coordinates.
187 !! @param[in] fill Fill value to set invalid output data.
188 !! Must be impossible value; suggested value: -9999.
189 !! @param[inout] xpts Grid x point coordinates if iopt>0.
190 !! @param[inout] ypts Grid y point coordinates if iopt>0.
191 !! @param[inout] rlon Earth longitudes in degrees e if iopt<0
192 !! (Acceptable range: -360. to 360.)
193 !! @param[inout] rlat Earth latitudes in degrees n if iopt<0
194 !! (Acceptable range: -90. to 90.)
195 !! @param[out] nret Number of valid points computed.
196 !! @param[out] crot Optional clockwise vector rotation cosines.
197 !! @param[out] srot Optional clockwise vector rotation sines.
198 !! @param[out] xlon Optional dx/dlon in 1/degrees.
199 !! @param[out] xlat Optional dx/dlat in 1/degrees.
200 !! @param[out] ylon Optional dy/dlon in 1/degrees.
201 !! @param[out] ylat Optional dy/dlat in 1/degrees.
202 !! @param[out] area Optional area weights in m**2.
203 !!
204 !! @author Mark Iredell, George Gayno, Kyle Gerheiser
205 !! @date July 2021
206 SUBROUTINE gdswzd_equid_cylind(self,IOPT,NPTS,FILL, &
207 XPTS,YPTS,RLON,RLAT,NRET, &
208 CROT,SROT,XLON,XLAT,YLON,YLAT,AREA)
209 IMPLICIT NONE
210 !
211 class(ip_equid_cylind_grid), intent(in) :: self
212 INTEGER, INTENT(IN ) :: IOPT, NPTS
213 INTEGER, INTENT( OUT) :: NRET
214 !
215 REAL, INTENT(IN ) :: FILL
216 REAL, INTENT(INOUT) :: RLON(NPTS),RLAT(NPTS)
217 REAL, INTENT(INOUT) :: XPTS(NPTS),YPTS(NPTS)
218 REAL, OPTIONAL, INTENT( OUT) :: CROT(NPTS),SROT(NPTS)
219 REAL, OPTIONAL, INTENT( OUT) :: XLON(NPTS),XLAT(NPTS)
220 REAL, OPTIONAL, INTENT( OUT) :: YLON(NPTS),YLAT(NPTS),AREA(NPTS)
221 !
222 INTEGER :: IM, JM, N
223 !
224 LOGICAL :: LROT, LMAP, LAREA
225 !
226 REAL :: HI, RLAT1, RLON1, RLAT2, RLON2
227 REAL :: XMAX, XMIN, YMAX, YMIN
228 ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
229 IF(PRESENT(crot)) crot=fill
230 IF(PRESENT(srot)) srot=fill
231 IF(PRESENT(xlon)) xlon=fill
232 IF(PRESENT(xlat)) xlat=fill
233 IF(PRESENT(ylon)) ylon=fill
234 IF(PRESENT(ylat)) ylat=fill
235 IF(PRESENT(area)) area=fill
236
237 im=self%im
238 jm=self%jm
239
240 rlat1=self%rlat1
241 rlon1=self%rlon1
242 rlat2=self%rlat2
243 rlon2=self%rlon2
244
245 hi=self%hi
246
247 rerth = self%rerth
248 dlat = self%dlat
249 dlon = self%dlon
250
251 xmin=0
252 xmax=im+1
253 IF(im.EQ.nint(360/abs(dlon))) xmax=im+2
254 ymin=0
255 ymax=jm+1
256 nret=0
257 IF(PRESENT(crot).AND.PRESENT(srot))THEN
258 lrot=.true.
259 ELSE
260 lrot=.false.
261 ENDIF
262 IF(PRESENT(xlon).AND.PRESENT(xlat).AND.PRESENT(ylon).AND.PRESENT(ylat))THEN
263 lmap=.true.
264 ELSE
265 lmap=.false.
266 ENDIF
267 IF(PRESENT(area))THEN
268 larea=.true.
269 ELSE
270 larea=.false.
271 ENDIF
272 ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
273 ! TRANSLATE GRID COORDINATES TO EARTH COORDINATES
274 IF(iopt.EQ.0.OR.iopt.EQ.1) THEN
275 !$OMP PARALLEL DO PRIVATE(N) REDUCTION(+:NRET) SCHEDULE(STATIC)
276 DO n=1,npts
277 IF(xpts(n).GE.xmin.AND.xpts(n).LE.xmax.AND. &
278 ypts(n).GE.ymin.AND.ypts(n).LE.ymax) THEN
279 rlon(n)=mod(rlon1+dlon*(xpts(n)-1)+3600,360.)
280 rlat(n)=min(max(rlat1+dlat*(ypts(n)-1),-90.),90.)
281 nret=nret+1
282 IF(lrot) CALL equid_cylind_vect_rot(crot(n),srot(n))
283 IF(lmap) CALL equid_cylind_map_jacob(xlon(n),xlat(n),ylon(n),ylat(n))
284 IF(larea) CALL equid_cylind_grid_area(rlat(n),area(n))
285 ELSE
286 rlon(n)=fill
287 rlat(n)=fill
288 ENDIF
289 ENDDO
290 !$OMP END PARALLEL DO
291 ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
292 ! TRANSLATE EARTH COORDINATES TO GRID COORDINATES
293 ELSEIF(iopt.EQ.-1) THEN
294 !$OMP PARALLEL DO PRIVATE(N) REDUCTION(+:NRET) SCHEDULE(STATIC)
295 DO n=1,npts
296 IF(abs(rlon(n)).LE.360.AND.abs(rlat(n)).LE.90) THEN
297 xpts(n)=1+hi*mod(hi*(rlon(n)-rlon1)+3600,360.)/dlon
298 ypts(n)=1+(rlat(n)-rlat1)/dlat
299 IF(xpts(n).GE.xmin.AND.xpts(n).LE.xmax.AND. &
300 ypts(n).GE.ymin.AND.ypts(n).LE.ymax) THEN
301 nret=nret+1
302 IF(lrot) CALL equid_cylind_vect_rot(crot(n),srot(n))
303 IF(lmap) CALL equid_cylind_map_jacob(xlon(n),xlat(n),ylon(n),ylat(n))
304 IF(larea) CALL equid_cylind_grid_area(rlat(n),area(n))
305 ELSE
306 xpts(n)=fill
307 ypts(n)=fill
308 ENDIF
309 ELSE
310 xpts(n)=fill
311 ypts(n)=fill
312 ENDIF
313 ENDDO
314 !$OMP END PARALLEL DO
315 ENDIF
316 END SUBROUTINE gdswzd_equid_cylind
317
318 !> Computes the vector rotation sines and
319 !! cosines for a equidistant cylindrical grid.
320 !!
321 !! @param[out] crot Clockwise vector rotation cosines.
322 !! @param[out] srot Clockwise vector rotation sines.
323 !!
324 !! @note
325 !! - ugrid=crot*uearth-srot*vearth;
326 !! - vgrid=srot*uearth+crot*vearth
327 !!
328 !! @author George Gayno
329 !! @date July 2021
330 SUBROUTINE equid_cylind_vect_rot(CROT,SROT)
331 IMPLICIT NONE
332
333 REAL, INTENT( OUT) :: CROT, SROT
334
335 crot=1.0
336 srot=0.0
337
338 END SUBROUTINE equid_cylind_vect_rot
339
340 !> Computes the map jacobians for a equidistant cylindrical grid.
341 !!
342 !! @param[out] xlon dx/dlon in 1/degrees.
343 !! @param[out] xlat dx/dlat in 1/degrees.
344 !! @param[out] ylon dy/dlon in 1/degrees.
345 !! @param[out] ylat dy/dlat in 1/degrees.
346 !!
347 !! @author George Gayno
348 !! @date July 2021
349 SUBROUTINE equid_cylind_map_jacob(XLON,XLAT,YLON,YLAT)
350 REAL, INTENT( OUT) :: XLON,XLAT,YLON,YLAT
351
352 xlon=1.0/dlon
353 xlat=0.
354 ylon=0.
355 ylat=1.0/dlat
356
357 END SUBROUTINE equid_cylind_map_jacob
358
359 !> Computes the grid box area for a equidistant cylindrical grid.
360 !!
361 !! @param[in] rlat Latitude of grid point in degrees.
362 !! @param[out] area Area weights in m^2.
363 !!
364 !! @author Mark Iredell, George Gayno
365 !! @date July 2021
366 SUBROUTINE equid_cylind_grid_area(RLAT,AREA)
367 IMPLICIT NONE
368
369 REAL, INTENT(IN ) :: RLAT
370 REAL, INTENT( OUT) :: AREA
371
372 REAL, PARAMETER :: PI=3.14159265358979
373 REAL, PARAMETER :: DPR=180./pi
374
375 REAL :: DSLAT, RLATU, RLATD
376
377 rlatu=min(max(rlat+dlat/2,-90.),90.)
378 rlatd=min(max(rlat-dlat/2,-90.),90.)
379 dslat=sin(rlatu/dpr)-sin(rlatd/dpr)
380 area=rerth**2*abs(dslat*dlon)/dpr
381
382 END SUBROUTINE equid_cylind_grid_area
383
385
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.
Equidistant cylindrical grib decoder and grid coordinate transformations.
real dlat
Grid resolution in degrees n/s direction.
subroutine init_grib2(self, g2_desc)
Initializes an equidistant cylindrical grid given a grib2_descriptor object.
subroutine init_grib1(self, g1_desc)
Initializes an equidistant cylindrical grid given a grib1_descriptor object.
real dlon
Grid resolution in degrees e/w direction.
subroutine equid_cylind_map_jacob(xlon, xlat, ylon, ylat)
Computes the map jacobians for a equidistant cylindrical grid.
subroutine equid_cylind_grid_area(rlat, area)
Computes the grid box area for a equidistant cylindrical grid.
subroutine gdswzd_equid_cylind(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 equidistant cylindrical g...
subroutine equid_cylind_vect_rot(crot, srot)
Computes the vector rotation sines and cosines for a equidistant cylindrical grid.
Users derived type grid descriptor objects to abstract away the raw GRIB1 and GRIB2 grid definitions.
Abstract ip_grid type.
Descriptor representing a grib1 grib descriptor section (GDS) with an integer array.
Abstract grid that holds fields and methods common to all grids.