1module regions_mod
2 ! MODULE regions_mod (prefix='reg' category='7. Low-level data objects')
3 !
4 !:Purpose: This is a subset of the EnKF module that deals with
5 ! operations involving the generation of a set of regions.
6 ! Each region consists of a small number of observations
7 ! which is to be assimilated simultaneously in the
8 ! sequential algorithm.
9 !
10 use mathPhysConstants_mod
11 use midasMpi_mod
12 implicit none
13
14 private
15 public :: struct_reg
16 public :: reg_getlatitude, reg_getblock, reg_locatestn, reg_init_struct
17
18 ! REG_MXOBS : maximum number of observations in a region
19 ! REG_MXOBS must be at least as large as the number of observations
20 ! in an individual radiosonde (121 currently)
21 integer, parameter :: REG_MXOBS = 900
22
23 type :: struct_reg
24 ! horizontal radius in km of the area in which observations are selected
25 real(8) :: r0_km
26 ! horizontal radius in km of the area in which an observation can have an impact
27 real(8) :: r1_km
28 ! the vertical extend in log(P) of the area in which an observation can have an
29 ! impact
30 real(8) :: rz_logp
31 ! as r0km but in radians and adjusted considering that we want an integer
32 ! number of latitude bands
33 real(8) :: r0_rad
34 real(8) :: r1_rad ! equivalent of r1 in radians
35 ! the horizontal length scale for the hadamard product (0.5 r1)
36 real(8) :: scale_hor
37 ! the vertical length scale for the hadamard product (in log (p))
38 real(8) :: scale_ver
39 ! number of latitude bands
40 integer :: nlatband
41 ! estimated maximum number of regions that could possibly be formed
42 integer :: mxreg
43 end type struct_reg
44
45contains
46
47 subroutine reg_getblock(nlatband, r0, latmin, latmax, nlonblock)
48 !
49 !:Purpose: We have nlatband latitude bands (bounded by latitudes
50 ! latmin and latmax). For each band determine how many
51 ! longitidunal blocks of at most r0*root(2) radians it
52 ! contains. Output is in nlonblock.
53 !
54 implicit none
55
56 ! Arguments:
57 integer, intent(in) :: nlatband
58 real(8), intent(in) :: latmin(nlatband)
59 real(8), intent(in) :: latmax(nlatband)
60 real(8), intent(in) :: r0
61 integer, intent(out) :: nlonblock(nlatband)
62
63 ! Locals:
64 integer :: i
65 real(8) :: bar, dpie, lat
66
67 dpie=MPC_PI_R8
68
69 nlonblock(1)=1
70 nlonblock(nlatband)=1
71
72 do i = 2, nlatband-1
73 if (latmin(i)*latmax(i).gt.0) then
74 lat = min(abs(latmin(i)),abs(latmax(i)))
75 else
76 ! at the equator
77 lat = 0
78 end if
79 bar = (2**0.5)*r0/cos(lat)
80 nlonblock(i) = int((2.0D0*dpie)/bar)+1
81 end do
82
83 end subroutine reg_getblock
84
85
86 subroutine reg_getlatitude(r0, nlatband, latmin, latcenter, latmax)
87 !
88 !:Purpose: the circle is covered with nlatband latitude bands.
89 ! the polar caps (with radius r0 radians) form the first and
90 ! last band. Intermediate bands are of width r0*(2**0.5)
91 ! For reach band we have the southern most latitude latmin,
92 ! the central latitude latcenter (at the poles for the extreme
93 ! bands) and the northern most latitude latcenter
94 !
95 implicit none
96
97 ! Arguments:
98 integer, intent(in) :: nlatband
99 real(8), intent(in) :: r0
100 real(8), intent(out) :: latmin(nlatband)
101 real(8), intent(out) :: latcenter(nlatband)
102 real(8), intent(out) :: latmax(nlatband)
103
104 ! Locals:
105 integer :: i
106 real(8) :: dpie
107
108 dpie = MPC_PI_R8
109
110 latmin(1) = -0.5D0*dpie
111 latcenter(1) = -0.5D0*dpie
112 latmax(1) = -0.5D0*dpie+r0
113
114 if (mmpi_myid == 0) write(*,*) 'get latitude r0 and nlatband: ', r0, nlatband
115 if (mmpi_myid == 0) write(*,*) 'at ', 1, latmin(1), latcenter(1), latmax(1)
116
117 latmin(nlatband) = 0.5D0*dpie-r0
118 latcenter(nlatband) = 0.5D0*dpie
119 latmax(nlatband) = 0.5D0*dpie
120
121 if (mmpi_myid == 0) write(*,*) 'at ', nlatband, latmin(nlatband), latcenter(nlatband), latmax(nlatband)
122 do i = 2, nlatband-1
123 latmin(i) = latmax(i-1)
124 latmax(i) = latmin(i)+r0*(2**0.5)
125 latcenter(i) = latmin(i)+0.5D0*r0*(2**0.5)
126 if (mmpi_myid == 0) write(*,*) 'at ', i, latmin(i), latcenter(i), latmax(i)
127 end do
128
129 end subroutine reg_getlatitude
130
131
132 subroutine getr0(r0km, r0, nlatband)
133 !
134 !:Purpose: The user specifies a radius of r0km (in km) within which
135 ! stations can be taken together for simultaneous analysis
136 ! in a single batch. For simplicity of the search procedures
137 ! we - instead - use somewhat smaller squares with sides of
138 ! at most r0km*root(2). We do have circles at the two poles.
139 ! Here we compute the radius r0 in radians such that we arrive
140 ! exactly at nlatband latitude bands. The equator separates
141 ! two latitude bands.
142 !
143 implicit none
144
145 ! Arguments:
146 real(8), intent(in) :: r0km
147 real(8), intent(out) :: r0
148 integer, intent(out) :: nlatband
149
150 ! Locals:
151 real(8) :: dpie, eps, difference, r0new, dminpole, nbandr
152 integer :: nbandi
153
154 dpie = MPC_PI_R8
155
156 ! figure out if r0km*root(2) is a divisor of 10000-r0km
157
158 eps = 0.01
159 ! we put a circle with radius r0km around each pole.
160 dminpole = 10000.-r0km
161 ! potential fractional number of latitude bands.
162 nbandr = dminpole/(r0km*(2**0.5))
163 nbandi = nint(nbandr)
164 difference = abs(dble(nbandi)-nbandr)
165 if (difference.gt.eps) then
166 ! increasing number of bands to nbandi
167 nbandi = nbandi+1
168 end if
169 ! decrease r0km to r0new
170 r0new = 10000./(dble(nbandi)*2**0.5 + 1.)
171 ! compute the corresponding r0 in radians
172 r0 = (r0new/20000.)*dpie
173 ! two hemispheres with a pole
174 nlatband = 2*(nbandi+1)
175
176 end subroutine getr0
177
178 subroutine reg_init_struct(lsc, r0_km, r1_km, rz_logp)
179 !
180 !:Purpose: store and derive parameters related to the localization
181 ! in a structure.
182 !
183 implicit none
184
185 ! Arguments:
186 type(struct_reg), intent(out) :: lsc
187 real(8), intent(in) :: r0_km
188 real(8), intent(in) :: r1_km
189 real(8), intent(in) :: rz_logp
190
191 ! Locals:
192 real(8) :: dpie
193 integer :: ione
194
195 lsc%r0_km = r0_km
196 lsc%r1_km = r1_km
197 lsc%rz_logp = rz_logp
198
199 call getr0(lsc%r0_km, lsc%r0_rad, lsc%nlatband)
200 lsc%r1_rad = lsc%r1_km/6370.
201 lsc%scale_hor = 0.5*lsc%r1_rad
202 lsc%scale_ver = 0.5*lsc%rz_logp
203
204 dpie = MPC_PI_R8
205 lsc%mxreg = floor(dpie/(lsc%r1_rad+(2**0.5)*lsc%r0_rad)**2)
206
207 ione = 1
208 lsc%mxreg = max(lsc%mxreg, ione)
209
210 end subroutine reg_init_struct
211
212
213 subroutine reg_locatestn(r0, lat, lon, nlatband, nlonblock, &
214 nblockoffset, iblock)
215 !
216 !:Purpose: locate the lat-lon block in which position (lat,lon) falls
217 !
218 !:Arguments:
219 ! r0: radius of a region (in radians)
220 ! (lat,lon): latitude and longitude in radians
221 ! nlatband: number of latitude bands
222 ! nlonblock: number of longitudinal blocks at each latitude band
223 ! nblockoffset: offset (the number of blocks south of each
224 ! latitude band)
225 ! iblock: block number
226 !
227 implicit none
228
229 ! Arguments:
230 integer, intent(out) :: iblock
231 integer, intent(in) :: nlatband
232 integer, intent(in) :: nlonblock(nlatband)
233 integer, intent(in) :: nblockoffset(nlatband)
234 real(4), intent(inout) :: lat
235 real(4), intent(in) :: lon
236 real(8), intent(in) :: r0
237
238 ! Locals:
239 integer :: ilat, ilon
240 real(8) :: dpie
241
242 dpie = MPC_PI_R8
243
244 if (lat.le.(-0.5D0*dpie+r0)) then
245 ilat = 1
246 else if (lat.gt.(0.5D0*dpie-r0)) then
247 ilat = nlatband
248 else
249 lat = lat+0.5D0*dpie-r0
250 ilat = 1+ceiling(lat/(r0*(2**0.5)))
251 end if
252 ilat = min(max(ilat,1),nlatband)
253 ilon = ceiling((lon/(2.0D0*dpie))*nlonblock(ilat))
254 ! added for points at the Greenwich meridian
255 ilon = min(max(ilon,1),nlonblock(ilat))
256 iblock = nblockoffset(ilat)+ilon
257
258 end subroutine reg_locatestn
259
260end module regions_mod