FMS 2025.01-dev
Flexible Modeling System
Loading...
Searching...
No Matches
horiz_interp_bilinear.inc
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!> @addtogroup horiz_interp_bilinear_mod
20!> @{
21 subroutine horiz_interp_bilinear_new_1d_ ( Interp, lon_in, lat_in, lon_out, lat_out, &
22 verbose, src_modulo )
23
24 !-----------------------------------------------------------------------
25 type(horiz_interp_type), intent(inout) :: Interp
26 real(FMS_HI_KIND_), intent(in), dimension(:) :: lon_in , lat_in
27 real(FMS_HI_KIND_), intent(in), dimension(:,:) :: lon_out, lat_out
28 integer, intent(in), optional :: verbose
29 logical, intent(in), optional :: src_modulo
30
31 logical :: src_is_modulo
32 integer :: nlon_in, nlat_in, nlon_out, nlat_out, n, m
33 integer :: ie, is, je, js, ln_err, lt_err, warns, iunit
34 real(FMS_HI_KIND_) :: wtw, wte, wts, wtn, lon, lat, tpi, hpi
35 real(FMS_HI_KIND_) :: glt_min, glt_max, gln_min, gln_max, min_lon, max_lon
36 integer,parameter :: kindl = fms_hi_kind_
37 logical :: decreasing_lat !< .True. if latitude is monotically decreasing
38 logical :: decreasing_lon !< .True. if longitude is monotically decreasing
39
40 warns = 0
41 if(present(verbose)) warns = verbose
42 src_is_modulo = .true.
43 if (present(src_modulo)) src_is_modulo = src_modulo
44
45 decreasing_lat = .false.
46 if (lat_in(1) > lat_in(2)) decreasing_lat = .true.
47
48 decreasing_lon = .false.
49 if (lon_in(1) > lon_in(2)) decreasing_lon = .true.
50
51 hpi = 0.5_kindl * real(pi, fms_hi_kind_)
52 tpi = 4.0_kindl * hpi
53 glt_min = hpi
54 glt_max = -hpi
55 gln_min = tpi
56 gln_max = -tpi
57 min_lon = 0.0_kindl
58 max_lon = tpi
59 ln_err = 0
60 lt_err = 0
61 !-----------------------------------------------------------------------
62
63 allocate ( interp % HI_KIND_TYPE_ % wti (size(lon_out,1),size(lon_out,2),2), &
64 interp % HI_KIND_TYPE_ % wtj (size(lon_out,1),size(lon_out,2),2), &
65 interp % i_lon (size(lon_out,1),size(lon_out,2),2), &
66 interp % j_lat (size(lon_out,1),size(lon_out,2),2))
67 !-----------------------------------------------------------------------
68
69 nlon_in = size(lon_in(:)) ; nlat_in = size(lat_in(:))
70 nlon_out = size(lon_out, 1); nlat_out = size(lon_out, 2)
71 interp%nlon_src = nlon_in; interp%nlat_src = nlat_in
72 interp%nlon_dst = nlon_out; interp%nlat_dst = nlat_out
73
74 if(src_is_modulo) then
75 if(lon_in(nlon_in) - lon_in(1) .gt. tpi + real(epsln, fms_hi_kind_)) &
76 call mpp_error(fatal,'horiz_interp_bilinear_mod: '// &
77 'The range of source grid longitude should be no larger than tpi')
78
79 if(lon_in(1) .lt. 0.0_kindl .OR. lon_in(nlon_in) > tpi ) then
80 min_lon = lon_in(1)
81 max_lon = lon_in(nlon_in)
82 endif
83 endif
84
85 do n = 1, nlat_out
86 do m = 1, nlon_out
87 lon = lon_out(m,n)
88 lat = lat_out(m,n)
89
90 if(src_is_modulo) then
91 if(lon .lt. min_lon) then
92 lon = lon + tpi
93 else if(lon .gt. max_lon) then
94 lon = lon - tpi
95 endif
96 else ! when the input grid is in not cyclic, the output grid should located inside
97 ! the input grid
98 if((lon .lt. lon_in(1)) .or. (lon .gt. lon_in(nlon_in))) &
99 call mpp_error(fatal,'horiz_interp_bilinear_mod: ' //&
100 'when input grid is not modulo, output grid should locate inside input grid')
101 endif
102
103 glt_min = min(lat,glt_min); glt_max = max(lat,glt_max)
104 gln_min = min(lon,gln_min); gln_max = max(lon,gln_max)
105
106 is = nearest_index(lon, lon_in )
107 if (decreasing_lon) then
108 ! Lon_in is increasing
109 ! This is so that is is the lower bound.
110 ! For example, if the array is [50 40 30 20 10] and lon is 11, `is` is going to be 5 from `nearest_index`
111 ! but it needs to be 4 so that it can use the data at lon_in(4) and lon_in(5)
112 if( lon_in(is) .lt. lon ) is = max(is-1,1)
113 else
114 ! Lon_in is increasing
115 ! This is so that is is the lower bound.
116 ! For example, if the array is [10 20 30 40 50] and lon is 49, `is` is going to be 5 from `nearest_index`
117 ! but it needs to be 4 so that it can use the data at lon_in(4) and lon_in(5)
118 if( lon_in(is) .gt. lon ) is = max(is-1,1)
119 endif
120 if( lon_in(is) .eq. lon .and. is .eq. nlon_in) is = max(is - 1,1)
121 ie = min(is+1,nlon_in)
122 if(lon_in(is) .ne. lon_in(ie) .and. (decreasing_lon .or. lon_in(is) .le. lon)) then
123 wtw = ( lon_in(ie) - lon) / (lon_in(ie) - lon_in(is) )
124 else
125 ! east or west of the last data value. this could be because a
126 ! cyclic condition is needed or the dataset is too small.
127 ln_err = 1
128 ie = 1
129 is = nlon_in
130 if (lon_in(ie) .ge. lon ) then
131 wtw = (lon_in(ie) -lon)/(lon_in(ie)-lon_in(is)+tpi+real(epsln,fms_hi_kind_))
132 else
133 wtw = (lon_in(ie)-lon+tpi+real(epsln,fms_hi_kind_))/(lon_in(ie)-lon_in(is)+tpi+real(epsln,fms_hi_kind_))
134 endif
135 endif
136 wte = 1.0_kindl - wtw
137
138 js = nearest_index(lat, lat_in )
139 if (decreasing_lat) then
140 ! Lat_in is decreasing
141 if( lat_in(js) .lt. lat ) js = max(js - 1, 1)
142 else
143 ! Lat_in is increasing
144 if( lat_in(js) .gt. lat ) js = max(js - 1, 1)
145 endif
146 if( lat_in(js) .eq. lat .and. js .eq. nlat_in) js = max(js - 1, 1)
147 je = min(js + 1, nlat_in)
148
149 if ( lat_in(js) .ne. lat_in(je) .and. (decreasing_lat .or. lat_in(js) .le. lat)) then
150 wts = ( lat_in(je) - lat )/(lat_in(je)-lat_in(js))
151 else
152 ! north or south of the last data value. this could be because a
153 ! pole is not included in the data set or the dataset is too small.
154 ! in either case extrapolate north or south
155 lt_err = 1
156 wts = 1.0_kindl
157 endif
158
159 wtn = 1.0_kindl - wts
160
161 interp % i_lon (m,n,1) = is; interp % i_lon (m,n,2) = ie
162 interp % j_lat (m,n,1) = js; interp % j_lat (m,n,2) = je
163 interp % HI_KIND_TYPE_ % wti (m,n,1) = wtw
164 interp % HI_KIND_TYPE_ % wti (m,n,2) = wte
165 interp % HI_KIND_TYPE_ % wtj (m,n,1) = wts
166 interp % HI_KIND_TYPE_ % wtj (m,n,2) = wtn
167
168 enddo
169 enddo
170
171 iunit = stdout()
172
173 if (ln_err .eq. 1 .and. warns > 0) then
174 write (iunit,'(/,(1x,a))') &
175 '==> Warning: the geographic data set does not extend far ', &
176 ' enough east or west - a cyclic boundary ', &
177 ' condition was applied. check if appropriate '
178 write (iunit,'(/,(1x,a,2f8.4))') &
179 ' data required between longitudes:', gln_min, gln_max, &
180 ' data set is between longitudes:', lon_in(1), lon_in(nlon_in)
181 warns = warns - 1
182 endif
183
184 if (lt_err .eq. 1 .and. warns > 0) then
185 write (iunit,'(/,(1x,a))') &
186 '==> Warning: the geographic data set does not extend far ',&
187 ' enough north or south - extrapolation from ',&
188 ' the nearest data was applied. this may create ',&
189 ' artificial gradients near a geographic pole '
190 write (iunit,'(/,(1x,a,2f8.4))') &
191 ' data required between latitudes:', glt_min, glt_max, &
192 ' data set is between latitudes:', lat_in(1), lat_in(nlat_in)
193 endif
194 interp% HI_KIND_TYPE_ % is_allocated = .true.
195 interp% interp_method = bilinear
196
197 return
198
199 end subroutine horiz_interp_bilinear_new_1d_
200
201 !#######################################################################
202
203 !> Initialization routine.
204 !!
205 !> Allocates space and initializes a derived-type variable
206 !! that contains pre-computed interpolation indices and weights.
207 subroutine horiz_interp_bilinear_new_2d_ ( Interp, lon_in, lat_in, lon_out, lat_out, &
208 verbose, src_modulo, new_search, no_crash_when_not_found )
209
210 !-----------------------------------------------------------------------
211 type(horiz_interp_type), intent(inout) :: Interp !< A derived type variable containing indices
212 !! and weights for subsequent interpolations. To
213 !! reinitialize for different grid-to-grid interpolation
214 !! @ref horiz_interp_del must be used first.
215 real(FMS_HI_KIND_), intent(in), dimension(:,:) :: lon_in !< Latitude (radians) for source data grid
216 real(FMS_HI_KIND_), intent(in), dimension(:,:) :: lat_in !< Longitude (radians) for source data grid
217 real(FMS_HI_KIND_), intent(in), dimension(:,:) :: lon_out !< Longitude (radians) for output data grid
218 real(FMS_HI_KIND_), intent(in), dimension(:,:) :: lat_out !< Latitude (radians) for output data grid
219 integer, intent(in), optional :: verbose !< flag for amount of print output
220 logical, intent(in), optional :: src_modulo !< indicates if the boundary condition
221 !! along zonal boundary is cyclic or not. Cyclic when true
222 logical, intent(in), optional :: new_search
223 logical, intent(in), optional :: no_crash_when_not_found
224 integer :: warns
225 logical :: src_is_modulo
226 integer :: nlon_in, nlat_in, nlon_out, nlat_out
227 integer :: m, n, is, ie, js, je, num_solution
228 real(FMS_HI_KIND_) :: lon, lat, quadra, x, y, y1, y2
229 real(FMS_HI_KIND_) :: a1, b1, c1, d1, a2, b2, c2, d2, a, b, c
230 real(FMS_HI_KIND_) :: lon1, lat1, lon2, lat2, lon3, lat3, lon4, lat4
231 real(FMS_HI_KIND_) :: tpi, lon_min, lon_max
232 real(FMS_HI_KIND_) :: epsln2
233 logical :: use_new_search, no_crash
234
235 integer, parameter :: kindl=fms_hi_kind_
236
237 tpi = 2.0_kindl * real(pi, fms_hi_kind_)
238
239 warns = 0
240 if(present(verbose)) warns = verbose
241 src_is_modulo = .true.
242 if (present(src_modulo)) src_is_modulo = src_modulo
243 use_new_search = .false.
244 if (present(new_search)) use_new_search = new_search
245 no_crash = .false.
246 if(present(no_crash_when_not_found)) no_crash = no_crash_when_not_found
247
248 ! make sure lon and lat has the same dimension
249 if(size(lon_out,1) /= size(lat_out,1) .or. size(lon_out,2) /= size(lat_out,2) ) &
250 call mpp_error(fatal,'horiz_interp_bilinear_mod: when using bilinear ' // &
251 'interplation, the output grids should be geographical grids')
252
253 if(size(lon_in,1) /= size(lat_in,1) .or. size(lon_in,2) /= size(lat_in,2) ) &
254 call mpp_error(fatal,'horiz_interp_bilinear_mod: when using bilinear '// &
255 'interplation, the input grids should be geographical grids')
256
257 !--- get the grid size
258 nlon_in = size(lon_in,1) ; nlat_in = size(lat_in,2)
259 nlon_out = size(lon_out,1); nlat_out = size(lon_out,2)
260 interp%nlon_src = nlon_in; interp%nlat_src = nlat_in
261 interp%nlon_dst = nlon_out; interp%nlat_dst = nlat_out
262
263 allocate ( interp % HI_KIND_TYPE_ % wti (size(lon_out,1),size(lon_out,2),2), &
264 interp % HI_KIND_TYPE_ % wtj (size(lon_out,1),size(lon_out,2),2), &
265 interp % i_lon (size(lon_out,1),size(lon_out,2),2), &
266 interp % j_lat (size(lon_out,1),size(lon_out,2),2))
267
268 !--- first fine the neighbor points for the destination points.
269 if(use_new_search) then
270 epsln2 = real(epsln,fms_hi_kind_)* 1.0e5_kindl
271 call find_neighbor_new_(interp, lon_in, lat_in, lon_out, lat_out, src_is_modulo, no_crash)
272 else
273 epsln2 = real(epsln,fms_hi_kind_)
274 call find_neighbor_(interp, lon_in, lat_in, lon_out, lat_out, src_is_modulo)
275 endif
276
277 !***************************************************************************
278 ! Algorithm explanation (from disscussion with Steve Garner ) *
279 ! *
280 ! lon(x,y) = a1*x + b1*y + c1*x*y + d1 (1) *
281 ! lat(x,y) = a2*x + b2*y + c2*x*y + d2 (2) *
282 ! f (x,y) = a3*x + b3*y + c3*x*y + d3 (3) *
283 ! with x and y is between 0 and 1. *
284 ! lon1 = lon(0,0) = d1, lat1 = lat(0,0) = d2 *
285 ! lon2 = lon(1,0) = a1+d1, lat2 = lat(1,0) = a2+d2 *
286 ! lon3 = lon(1,1) = a1+b1+c1+d1, lat3 = lat(1,1) = a2+b2+c2+d2 *
287 ! lon4 = lon(0,1) = b1+d1, lat4 = lat(0,1) = b2+d2 *
288 ! where (lon1,lat1),(lon2,lat2),(lon3,lat3),(lon4,lat4) represents *
289 ! the four corners starting from the left lower corner of grid box *
290 ! that encloses a destination grid ( the rotation direction is *
291 ! counterclockwise ). With these conditions, we get *
292 ! a1 = lon2-lon1, a2 = lat2-lat1 *
293 ! b1 = lon4-lon1, b2 = lat4-lat1 *
294 ! c1 = lon3-lon2-lon4+lon1, c2 = lat3-lat2-lat4+lat1 *
295 ! d1 = lon1 d2 = lat1 *
296 ! So given any point (lon,lat), from equation (1) and (2) we can *
297 ! solve (x,y). *
298 ! From equation (3) *
299 ! f1 = f(0,0) = d3, f2 = f(1,0) = a3+d3 *
300 ! f3 = f(1,1) = a3+b3+c3+d3, f4 = f(0,1) = b3+d3 *
301 ! we obtain *
302 ! a3 = f2-f1, b3 = f4-f1 *
303 ! c3 = f3-f2-f4+f1, d3 = f1 *
304 ! at point (lon,lat) ---> (x,y) *
305 ! f(x,y) = (f2-f1)x + (f4-f1)y + (f3-f2-f4+f1)xy + f1 *
306 ! = f1*(1-x)*(1-y) + f2*x*(1-y) + f3*x*y + f4*y*(1-x) *
307 ! wtw=1-x; wte=x; wts=1-y; xtn=y *
308 ! *
309 !***************************************************************************
310
311 lon_min = minval(lon_in);
312 lon_max = maxval(lon_in);
313 !--- calculate the weight
314 do n = 1, nlat_out
315 do m = 1, nlon_out
316 lon = lon_out(m,n)
317 lat = lat_out(m,n)
318 if(lon .lt. lon_min) then
319 lon = lon + tpi
320 else if(lon .gt. lon_max) then
321 lon = lon - tpi
322 endif
323 is = interp%i_lon(m,n,1); ie = interp%i_lon(m,n,2)
324 js = interp%j_lat(m,n,1); je = interp%j_lat(m,n,2)
325 if( is == dummy) cycle
326 lon1 = lon_in(is,js); lat1 = lat_in(is,js);
327 lon2 = lon_in(ie,js); lat2 = lat_in(ie,js);
328 lon3 = lon_in(ie,je); lat3 = lat_in(ie,je);
329 lon4 = lon_in(is,je); lat4 = lat_in(is,je);
330 if(lon .lt. lon_min) then
331 lon1 = lon1 -tpi; lon4 = lon4 - tpi
332 else if(lon .gt. lon_max) then
333 lon2 = lon2 +tpi; lon3 = lon3 + tpi
334 endif
335 a1 = lon2-lon1
336 b1 = lon4-lon1
337 c1 = lon1+lon3-lon4-lon2
338 d1 = lon1
339 a2 = lat2-lat1
340 b2 = lat4-lat1
341 c2 = lat1+lat3-lat4-lat2
342 d2 = lat1
343 !--- the coefficient of the quadratic equation
344 a = b2*c1-b1*c2
345 b = a1*b2-a2*b1+c1*d2-c2*d1+c2*lon-c1*lat
346 c = a2*lon-a1*lat+a1*d2-a2*d1
347 quadra = b*b-4._kindl*a*c
348 if(abs(quadra) < real(epsln, fms_hi_kind_)) quadra = 0.0_kindl
349 if(quadra < 0.0_kindl) call mpp_error(fatal, &
350 "horiz_interp_bilinear_mod: No solution existed for this quadratic equation")
351 if ( abs(a) .lt. epsln2) then ! a = 0 is a linear equation
352 if( abs(b) .lt. real(epsln,fms_hi_kind_)) call mpp_error(fatal, &
353 "horiz_interp_bilinear_mod: no unique solution existed for this linear equation")
354 y = -c/b
355 else
356 y1 = 0.5_kindl*(-b+sqrt(quadra))/a
357 y2 = 0.5_kindl*(-b-sqrt(quadra))/a
358 if(abs(y1) < epsln2) y1 = 0.0_kindl
359 if(abs(y2) < epsln2) y2 = 0.0_kindl
360 if(abs(1.0_kindl-y1) < epsln2) y1 = 1.0_kindl
361 if(abs(1.0_kindl-y2) < epsln2) y2 = 1.0_kindl
362 num_solution = 0
363 if(y1 >= 0.0_kindl .and. y1 <= 1.0_kindl) then
364 y = y1
365 num_solution = num_solution +1
366 endif
367 if(y2 >= 0.0_kindl .and. y2 <= 1.0_kindl) then
368 y = y2
369 num_solution = num_solution + 1
370 endif
371 if(num_solution == 0) then
372 call mpp_error(fatal, "horiz_interp_bilinear_mod: No solution found")
373 else if(num_solution == 2) then
374 call mpp_error(fatal, "horiz_interp_bilinear_mod: Two solutions found")
375 endif
376 endif
377 if(abs(a1+c1*y) < real(epsln,fms_hi_kind_)) call mpp_error(fatal, &
378 "horiz_interp_bilinear_mod: the denomenator is 0")
379 if(abs(y) < epsln2) y = 0.0_kindl
380 if(abs(1.0_kindl-y) < epsln2) y = 1.0_kindl
381 x = (lon-b1*y-d1)/(a1+c1*y)
382 if(abs(x) < epsln2) x = 0.0_kindl
383 if(abs(1.0_kindl-x) < epsln2) x = 1.0_kindl
384 ! x and y should be between 0 and 1.
385 !! Added for ECDA
386 if(use_new_search) then
387 if (x < 0.0_kindl) x = 0.0_kindl ! snz
388 if (y < 0.0_kindl) y = 0.0_kindl ! snz
389 if (x > 1.0_kindl) x = 1.0_kindl
390 if (y > 1.0_kindl) y = 1.0_kindl
391 endif
392 if( x>1.0_kindl .or. x<0.0_kindl .or. y>1.0_kindl .or. y < 0.0_kindl) &
393 call mpp_error(fatal, "horiz_interp_bilinear_mod: weight should be between 0 and 1")
394 interp % HI_KIND_TYPE_ % wti(m,n,1)=1.0_kindl-x
395 interp % HI_KIND_TYPE_ % wti(m,n,2)=x
396 interp % HI_KIND_TYPE_ % wtj(m,n,1)=1.0_kindl-y
397 interp % HI_KIND_TYPE_ % wtj(m,n,2)=y
398 enddo
399 enddo
400
401 interp% HI_KIND_TYPE_ % is_allocated = .true.
402 interp% interp_method = bilinear
403 end subroutine
404
405 !#######################################################################
406 !> this routine will search the source grid to fine the grid box that encloses
407 !! each destination grid.
408 subroutine find_neighbor_ ( Interp, lon_in, lat_in, lon_out, lat_out, src_modulo )
409 type(horiz_interp_type), intent(inout) :: Interp
410 real(FMS_HI_KIND_), intent(in), dimension(:,:) :: lon_in , lat_in
411 real(FMS_HI_KIND_), intent(in), dimension(:,:) :: lon_out, lat_out
412 logical, intent(in) :: src_modulo
413 integer :: nlon_in, nlat_in, nlon_out, nlat_out
414 integer :: max_step, n, m, l, i, j, ip1, jp1, step
415 integer :: is, js, jstart, jend, istart, iend, npts
416 integer, allocatable, dimension(:) :: ilon, jlat
417 real(FMS_HI_KIND_) :: lon_min, lon_max, lon, lat, tpi
418 logical :: found
419 real(FMS_HI_KIND_) :: lon1, lat1, lon2, lat2, lon3, lat3, lon4, lat4
420
421 integer, parameter :: kindl=fms_hi_kind_
422
423 tpi = 2.0_kindl*real(pi, fms_hi_kind_)
424 nlon_in = size(lon_in,1) ; nlat_in = size(lat_in,2)
425 nlon_out = size(lon_out,1); nlat_out = size(lon_out,2)
426
427 lon_min = minval(lon_in);
428 lon_max = maxval(lon_in);
429
430 max_step = max(nlon_in,nlat_in) ! can be adjusted if needed
431 allocate(ilon(5*max_step), jlat(5*max_step) )
432
433 do n = 1, nlat_out
434 do m = 1, nlon_out
435 found = .false.
436 lon = lon_out(m,n)
437 lat = lat_out(m,n)
438
439 if(src_modulo) then
440 if(lon .lt. lon_min) then
441 lon = lon + tpi
442 else if(lon .gt. lon_max) then
443 lon = lon - tpi
444 endif
445 else
446 if(lon .lt. lon_min .or. lon .gt. lon_max ) &
447 call mpp_error(fatal,'horiz_interp_bilinear_mod: ' //&
448 'when input grid is not modulo, output grid should locate inside input grid')
449 endif
450 !--- search for the surrounding four points locatioon.
451 if(m==1 .and. n==1) then
452 j_loop: do j = 1, nlat_in-1
453 do i = 1, nlon_in
454 ip1 = i+1
455 jp1 = j+1
456 if(i==nlon_in) then
457 if(src_modulo)then
458 ip1 = 1
459 else
460 cycle
461 endif
462 endif
463 lon1 = lon_in(i, j); lat1 = lat_in(i,j)
464 lon2 = lon_in(ip1,j); lat2 = lat_in(ip1,j)
465 lon3 = lon_in(ip1,jp1); lat3 = lat_in(ip1,jp1)
466 lon4 = lon_in(i, jp1); lat4 = lat_in(i, jp1)
467
468 if(lon .lt. lon_min .or. lon .gt. lon_max) then
469 if(i .ne. nlon_in) then
470 cycle
471 else
472 if(lon .lt. lon_min) then
473 lon1 = lon1 -tpi; lon4 = lon4 - tpi
474 else if(lon .gt. lon_max) then
475 lon2 = lon2 +tpi; lon3 = lon3 + tpi
476 endif
477 endif
478 endif
479
480 if(lat .ge. intersect(lon1,lat1,lon2,lat2,lon))then ! south
481 if(lon .le. intersect(lat2,lon2,lat3,lon3,lat))then ! east
482 if(lat .le. intersect(lon3,lat3,lon4,lat4,lon))then ! north
483 if(lon .ge. intersect(lat4,lon4,lat1,lon1,lat))then ! west
484 found = .true.
485 interp % i_lon (m,n,1) = i; interp % i_lon (m,n,2) = ip1
486 interp % j_lat (m,n,1) = j; interp % j_lat (m,n,2) = jp1
487 exit j_loop
488 endif
489 endif
490 endif
491 endif
492 enddo
493 enddo j_loop
494 else
495 step = 0
496 do while ( .not. found .and. step .lt. max_step )
497 !--- take the adajcent point as the starting point
498 if(m == 1) then
499 is = interp % i_lon (m,n-1,1)
500 js = interp % j_lat (m,n-1,1)
501 else
502 is = interp % i_lon (m-1,n,1)
503 js = interp % j_lat (m-1,n,1)
504 endif
505 if(step==0) then
506 npts = 1
507 ilon(1) = is
508 jlat(1) = js
509 else
510 npts = 0
511 !--- bottom boundary
512 jstart = max(js-step,1)
513 jend = min(js+step,nlat_in)
514
515 do l = -step, step
516 i = is+l
517 if(src_modulo)then
518 if( i < 1) then
519 i = i + nlon_in
520 else if (i > nlon_in) then
521 i = i - nlon_in
522 endif
523 if( i < 1 .or. i > nlon_in) call mpp_error(fatal, &
524 'horiz_interp_bilinear_mod: max_step is too big, decrease max_step' )
525 else
526 if( i < 1 .or. i > nlon_in) cycle
527 endif
528
529 npts = npts + 1
530 ilon(npts) = i
531 jlat(npts) = jstart
532 enddo
533
534 !--- right and left boundary -----------------------------------------------
535 istart = is - step
536 iend = is + step
537 if(src_modulo) then
538 if( istart < 1) istart = istart + nlon_in
539 if( iend > nlon_in) iend = iend - nlon_in
540 else
541 istart = max(istart,1)
542 iend = min(iend, nlon_in)
543 endif
544 do l = -step, step
545 j = js+l
546 if( j < 1 .or. j > nlat_in .or. j==jstart .or. j==jend) cycle
547 npts = npts+1
548 ilon(npts) = istart
549 jlat(npts) = j
550 npts = npts+1
551 ilon(npts) = iend
552 jlat(npts) = j
553 end do
554
555 !--- top boundary
556
557 do l = -step, step
558 i = is+l
559 if(src_modulo)then
560 if( i < 1) then
561 i = i + nlon_in
562 else if (i > nlon_in) then
563 i = i - nlon_in
564 endif
565 if( i < 1 .or. i > nlon_in) call mpp_error(fatal, &
566 'horiz_interp_bilinear_mod: max_step is too big, decrease max_step' )
567 else
568 if( i < 1 .or. i > nlon_in) cycle
569 endif
570
571 npts = npts + 1
572 ilon(npts) = i
573 jlat(npts) = jend
574 enddo
575
576
577 end if
578
579 !--- find the surrouding points
580 do l = 1, npts
581 i = ilon(l)
582 j = jlat(l)
583 ip1 = i+1
584 if(ip1>nlon_in) then
585 if(src_modulo) then
586 ip1 = 1
587 else
588 cycle
589 endif
590 endif
591 jp1 = j+1
592 if(jp1>nlat_in) cycle
593 lon1 = lon_in(i, j); lat1 = lat_in(i,j)
594 lon2 = lon_in(ip1,j); lat2 = lat_in(ip1,j)
595 lon3 = lon_in(ip1,jp1); lat3 = lat_in(ip1,jp1)
596 lon4 = lon_in(i, jp1); lat4 = lat_in(i, jp1)
597
598 if(lon .lt. lon_min .or. lon .gt. lon_max) then
599 if(i .ne. nlon_in) then
600 cycle
601 else
602 if(lon .lt. lon_min) then
603 lon1 = lon1 -tpi; lon4 = lon4 - tpi
604 else if(lon .gt. lon_max) then
605 lon2 = lon2 +tpi; lon3 = lon3 + tpi
606 endif
607 endif
608 endif
609
610 if(lat .ge. intersect(lon1,lat1,lon2,lat2,lon))then ! south
611 if(lon .le. intersect(lat2,lon2,lat3,lon3,lat))then ! east
612 if(lat .le. intersect(lon3,lat3,lon4,lat4,lon))then !north
613 if(lon .ge. intersect(lat4,lon4,lat1,lon1,lat))then ! west
614 found = .true.
615 is=i; js=j
616 interp % i_lon (m,n,1) = i; interp % i_lon (m,n,2) = ip1
617 interp % j_lat (m,n,1) = j; interp % j_lat (m,n,2) = jp1
618 exit
619 endif
620 endif
621 endif
622 endif
623 enddo
624 step = step + 1
625 enddo
626 endif
627 if(.not.found) then
628 print *,'lon,lat=',lon*180.0_kindl/real(pi,fms_hi_kind_),lat*180.0_kindl/real(pi,fms_hi_kind_)
629 print *,'npts=',npts
630 print *,'is,ie= ',istart,iend
631 print *,'js,je= ',jstart,jend
632 print *,'lon_in(is,js)=',lon_in(istart,jstart)*180.0_kindl/real(pi,fms_hi_kind_)
633 print *,'lon_in(ie,js)=',lon_in(iend,jstart)*180.0_kindl/real(pi,fms_hi_kind_)
634 print *,'lat_in(is,js)=',lat_in(istart,jstart)*180.0_kindl/real(pi,fms_hi_kind_)
635 print *,'lat_in(ie,js)=',lat_in(iend,jstart)*180.0_kindl/real(pi,fms_hi_kind_)
636 print *,'lon_in(is,je)=',lon_in(istart,jend)*180.0_kindl/real(pi,fms_hi_kind_)
637 print *,'lon_in(ie,je)=',lon_in(iend,jend)*180.0_kindl/real(pi,fms_hi_kind_)
638 print *,'lat_in(is,je)=',lat_in(istart,jend)*180.0_kindl/real(pi,fms_hi_kind_)
639 print *,'lat_in(ie,je)=',lat_in(iend,jend)*180.0_kindl/real(pi,fms_hi_kind_)
640
641 call mpp_error(fatal, &
642 'FIND_NEIGHBOR_: the destination point is not inside the source grid' )
643 endif
644 enddo
645 enddo
646
647 end subroutine
648
649 !#######################################################################
650
651 !> The function will return true if the point x,y is inside a polygon, or
652 !! false if it is not. If the point is exactly on the edge of a polygon,
653 !! the function will return .true.
654 function inside_polygon_(polyx, polyy, x, y)
655 real(fms_hi_kind_), dimension(:), intent(in) :: polyx !< longitude coordinates of corners
656 real(fms_hi_kind_), dimension(:), intent(in) :: polyy !< latitude coordinates of corners
657 real(fms_hi_kind_), intent(in) :: x !< x coordinate of point to be tested
658 real(fms_hi_kind_), intent(in) :: y !< y coordinate of point to be tested
659 logical :: inside_polygon_
660 integer :: i, j, nedges
661 real(fms_hi_kind_) :: xx
662
663 inside_polygon_ = .false.
664 nedges = size(polyx(:))
665 j = nedges
666 do i = 1, nedges
667 if( (polyy(i) < y .AND. polyy(j) >= y) .OR. (polyy(j) < y .AND. polyy(i) >= y) ) then
668 xx = polyx(i)+(y-polyy(i))/(polyy(j)-polyy(i))*(polyx(j)-polyx(i))
669 if( xx == x ) then
670 inside_polygon_ = .true.
671 return
672 else if( xx < x ) then
674 endif
675 endif
676 j = i
677 enddo
678
679 return
680
681 end function
682
683 !#######################################################################
684 !> this routine will search the source grid to fine the grid box that encloses
685 !! each destination grid.
686 subroutine find_neighbor_new_( Interp, lon_in, lat_in, lon_out, lat_out, src_modulo, no_crash )
687 type(horiz_interp_type), intent(inout) :: Interp
688 real(FMS_HI_KIND_), intent(in), dimension(:,:) :: lon_in , lat_in
689 real(FMS_HI_KIND_), intent(in), dimension(:,:) :: lon_out, lat_out
690 logical, intent(in) :: src_modulo, no_crash
691 integer :: nlon_in, nlat_in, nlon_out, nlat_out
692 integer :: max_step, n, m, l, i, j, ip1, jp1, step
693 integer :: is, js, jstart, jend, istart, iend, npts
694 integer, allocatable, dimension(:) :: ilon, jlat
695 real(FMS_HI_KIND_) :: lon_min, lon_max, lon, lat, tpi
696 logical :: found
697 real(FMS_HI_KIND_) :: polyx(4), polyy(4)
698 real(FMS_HI_KIND_) :: min_lon, min_lat, max_lon, max_lat
699
700 integer, parameter :: step_div=8, kindl = fms_hi_kind_
701
702 tpi = 2.0_kindl * real(pi, fms_hi_kind_)
703 nlon_in = size(lon_in,1) ; nlat_in = size(lat_in,2)
704 nlon_out = size(lon_out,1); nlat_out = size(lon_out,2)
705
706 lon_min = minval(lon_in);
707 lon_max = maxval(lon_in);
708
709 max_step = min(nlon_in,nlat_in)/step_div ! can be adjusted if needed
710 allocate(ilon(step_div*max_step), jlat(step_div*max_step) )
711
712 do n = 1, nlat_out
713 do m = 1, nlon_out
714 found = .false.
715 lon = lon_out(m,n)
716 lat = lat_out(m,n)
717
718 if(src_modulo) then
719 if(lon .lt. lon_min) then
720 lon = lon + tpi
721 else if(lon .gt. lon_max) then
722 lon = lon - tpi
723 endif
724 else
725 if(lon .lt. lon_min .or. lon .gt. lon_max ) &
726 call mpp_error(fatal,'horiz_interp_bilinear_mod: ' //&
727 'when input grid is not modulo, output grid should locate inside input grid')
728 endif
729 !--- search for the surrounding four points locatioon.
730 if(m==1 .and. n==1) then
731 j_loop: do j = 1, nlat_in-1
732 do i = 1, nlon_in
733 ip1 = i+1
734 jp1 = j+1
735 if(i==nlon_in) then
736 if(src_modulo)then
737 ip1 = 1
738 else
739 cycle
740 endif
741 endif
742
743 polyx(1) = lon_in(i, j); polyy(1) = lat_in(i,j)
744 polyx(2) = lon_in(ip1,j); polyy(2) = lat_in(ip1,j)
745 polyx(3) = lon_in(ip1,jp1); polyy(3) = lat_in(ip1,jp1)
746 polyx(4) = lon_in(i, jp1); polyy(4) = lat_in(i, jp1)
747 if(lon .lt. lon_min .or. lon .gt. lon_max) then
748 if(i .ne. nlon_in) then
749 cycle
750 else
751 if(lon .lt. lon_min) then
752 polyx(1) = polyx(1) -tpi; polyx(4) = polyx(4) - tpi
753 else if(lon .gt. lon_max) then
754 polyx(2) = polyx(2) +tpi; polyx(3) = polyx(3) + tpi
755 endif
756 endif
757 endif
758
759 min_lon = minval(polyx)
760 max_lon = maxval(polyx)
761 min_lat = minval(polyy)
762 max_lat = maxval(polyy)
763! if( lon .GE. min_lon .AND. lon .LE. max_lon .AND. &
764! lat .GE. min_lat .AND. lat .LE. max_lat ) then
765! print*, 'i =', i, 'j = ', j
766! print '(5f15.11)', lon, polyx
767! print '(5f15.11)', lat, polyy
768! endif
769
770 if(inside_polygon_(polyx, polyy, lon, lat)) then
771 found = .true.
772! print*, " found ", i, j
773 interp % i_lon (m,n,1) = i; interp % i_lon (m,n,2) = ip1
774 interp % j_lat (m,n,1) = j; interp % j_lat (m,n,2) = jp1
775 exit j_loop
776 endif
777 enddo
778 enddo j_loop
779 else
780 step = 0
781 do while ( .not. found .and. step .lt. max_step )
782 !--- take the adajcent point as the starting point
783 if(m == 1) then
784 is = interp % i_lon (m,n-1,1)
785 js = interp % j_lat (m,n-1,1)
786 else
787 is = interp % i_lon (m-1,n,1)
788 js = interp % j_lat (m-1,n,1)
789 endif
790 if(step==0) then
791 npts = 1
792 ilon(1) = is
793 jlat(1) = js
794 else
795 npts = 0
796 !--- bottom and top boundary
797 jstart = max(js-step,1)
798 jend = min(js+step,nlat_in)
799
800 do l = -step, step
801 i = is+l
802 if(src_modulo)then
803 if( i < 1) then
804 i = i + nlon_in
805 else if (i > nlon_in) then
806 i = i - nlon_in
807 endif
808 if( i < 1 .or. i > nlon_in) call mpp_error(fatal, &
809 'horiz_interp_bilinear_mod: max_step is too big, decrease max_step' )
810 else
811 if( i < 1 .or. i > nlon_in) cycle
812 endif
813
814 npts = npts + 1
815 ilon(npts) = i
816 jlat(npts) = jstart
817 npts = npts + 1
818 ilon(npts) = i
819 jlat(npts) = jend
820 enddo
821
822 !--- right and left boundary -----------------------------------------------
823 istart = is - step
824 iend = is + step
825 if(src_modulo) then
826 if( istart < 1) istart = istart + nlon_in
827 if( iend > nlon_in) iend = iend - nlon_in
828 else
829 istart = max(istart,1)
830 iend = min(iend, nlon_in)
831 endif
832 do l = -step, step
833 j = js+l
834 if( j < 1 .or. j > nlat_in) cycle
835 npts = npts+1
836 ilon(npts) = istart
837 jlat(npts) = j
838 npts = npts+1
839 ilon(npts) = iend
840 jlat(npts) = j
841 end do
842 end if
843
844 !--- find the surrouding points
845 do l = 1, npts
846 i = ilon(l)
847 j = jlat(l)
848 ip1 = i+1
849 if(ip1>nlon_in) then
850 if(src_modulo) then
851 ip1 = 1
852 else
853 cycle
854 endif
855 endif
856 jp1 = j+1
857 if(jp1>nlat_in) cycle
858 polyx(1) = lon_in(i, j); polyy(1) = lat_in(i,j)
859 polyx(2) = lon_in(ip1,j); polyy(2) = lat_in(ip1,j)
860 polyx(3) = lon_in(ip1,jp1); polyy(3) = lat_in(ip1,jp1)
861 polyx(4) = lon_in(i, jp1); polyy(4) = lat_in(i, jp1)
862 if(inside_polygon_(polyx, polyy, lon, lat)) then
863 found = .true.
864 interp % i_lon (m,n,1) = i; interp % i_lon (m,n,2) = ip1
865 interp % j_lat (m,n,1) = j; interp % j_lat (m,n,2) = jp1
866 exit
867 endif
868 enddo
869 step = step + 1
870 enddo
871 endif
872 if(.not.found) then
873 if(no_crash) then
874 interp % i_lon (m,n,1:2) = dummy
875 interp % j_lat (m,n,1:2) = dummy
876 print*,'lon,lat=',lon,lat ! snz
877 else
878 call mpp_error(fatal, &
879 'horiz_interp_bilinear_mod: the destination point is not inside the source grid' )
880 endif
881 endif
882 enddo
883 enddo
884
885 end subroutine
886
887 !#######################################################################
888 function intersect_(x1, y1, x2, y2, x)
889 real(FMS_HI_KIND_), intent(in) :: x1, y1, x2, y2, x
890 real(FMS_HI_KIND_) :: INTERSECT_
891
892 intersect_ = (y2-y1)*(x-x1)/(x2-x1) + y1
893
894 return
895
896 end function intersect_
897
898 !#######################################################################
899
900 !> Subroutine for performing the horizontal interpolation between two grids
901 !!
902 !! @ref horiz_interp_bilinear_new must be called before calling this routine.
903 subroutine horiz_interp_bilinear_ ( Interp, data_in, data_out, verbose, mask_in,mask_out, &
904 missing_value, missing_permit, new_handle_missing )
905 !-----------------------------------------------------------------------
906 type (horiz_interp_type), intent(in) :: Interp !< Derived type variable containing
907 !! interpolation indices and weights. Returned by a
908 !! previous call to horiz_interp_bilinear_new
909 real(FMS_HI_KIND_), intent(in), dimension(:,:) :: data_in !< input data on source grid
910 real(FMS_HI_KIND_), intent(out), dimension(:,:) :: data_out !< output data on source grid
911 integer, intent(in), optional :: verbose !< 0 = no output; 1 = min,max,means; 2 =
912 !! all output
913 real(FMS_HI_KIND_), intent(in), dimension(:,:), optional :: mask_in !< Input mask, must be the same size as
914 !! the input data. The real(FMS_HI_KIND_) value of mask_in must be in the
915 !! range (0.,1.). Set mask_in=0.0 for data points
916 !! that should not be used or have missing data
917 real(FMS_HI_KIND_), intent(out), dimension(:,:), optional :: mask_out !< output mask that specifies whether
918 !! data was computed
919 real(FMS_HI_KIND_), intent(in), optional :: missing_value
920 integer, intent(in), optional :: missing_permit
921 logical, intent(in), optional :: new_handle_missing
922 !-----------------------------------------------------------------------
923 integer :: nlon_in, nlat_in, nlon_out, nlat_out, n, m, &
924 is, ie, js, je, iverbose, max_missing, num_missing, &
925 miss_in, miss_out, iunit
926 real(FMS_HI_KIND_) :: dwtsum, wtsum, min_in, max_in, avg_in, &
927 min_out, max_out, avg_out, wtw, wte, wts, wtn
928 real(FMS_HI_KIND_) :: mask(size(data_in,1), size(data_in,2) )
929 logical :: set_to_missing, is_missing(4), new_handler
930 real(FMS_HI_KIND_) :: f1, f2, f3, f4, middle, w, s
931 integer, parameter :: kindl = fms_hi_kind_
932
933 num_missing = 0
934
935 nlon_in = interp%nlon_src; nlat_in = interp%nlat_src
936 nlon_out = interp%nlon_dst; nlat_out = interp%nlat_dst
937
938 if(present(mask_in)) then
939 mask = mask_in
940 else
941 mask = 1.0_kindl
942 endif
943
944 if (present(verbose)) then
945 iverbose = verbose
946 else
947 iverbose = 0
948 endif
949
950 if(present(missing_permit)) then
951 max_missing = missing_permit
952 else
953 max_missing = 0
954 endif
955
956 if(present(new_handle_missing)) then
957 new_handler = new_handle_missing
958 else
959 new_handler = .false.
960 endif
961
962 if(max_missing .gt. 3 .or. max_missing .lt. 0) call mpp_error(fatal, &
963 'horiz_interp_bilinear_mod: missing_permit should be between 0 and 3')
964
965 if (size(data_in,1) /= nlon_in .or. size(data_in,2) /= nlat_in) &
966 call mpp_error(fatal,'horiz_interp_bilinear_mod: size of input array incorrect')
967
968 if (size(data_out,1) /= nlon_out .or. size(data_out,2) /= nlat_out) &
969 call mpp_error(fatal,'horiz_interp_bilinear_mod: size of output array incorrect')
970
971 if(new_handler) then
972 if( .not. present(missing_value) ) call mpp_error(fatal, &
973 "horiz_interp_bilinear_mod: misisng_value must be present when new_handle_missing is .true.")
974 if( present(mask_in) ) call mpp_error(fatal, &
975 "horiz_interp_bilinear_mod: mask_in should not be present when new_handle_missing is .true.")
976 do n = 1, nlat_out
977 do m = 1, nlon_out
978 is = interp % i_lon (m,n,1); ie = interp % i_lon (m,n,2)
979 js = interp % j_lat (m,n,1); je = interp % j_lat (m,n,2)
980 wtw = interp % HI_KIND_TYPE_ % wti (m,n,1)
981 wte = interp % HI_KIND_TYPE_ % wti (m,n,2)
982 wts = interp % HI_KIND_TYPE_ % wtj (m,n,1)
983 wtn = interp % HI_KIND_TYPE_ % wtj (m,n,2)
984
985 is_missing = .false.
986 num_missing = 0
987 set_to_missing = .false.
988 if(data_in(is,js) == missing_value) then
989 num_missing = num_missing+1
990 is_missing(1) = .true.
991 if(wtw .GE. 0.5_kindl .AND. wts .GE. 0.5_kindl) set_to_missing = .true.
992 endif
993
994 if(data_in(ie,js) == missing_value) then
995 num_missing = num_missing+1
996 is_missing(2) = .true.
997 if(wte .GE. 0.5_kindl .AND. wts .GE. 0.5_kindl ) set_to_missing = .true.
998 endif
999 if(data_in(ie,je) == missing_value) then
1000 num_missing = num_missing+1
1001 is_missing(3) = .true.
1002 if(wte .GE. 0.5_kindl .AND. wtn .GE. 0.5_kindl ) set_to_missing = .true.
1003 endif
1004 if(data_in(is,je) == missing_value) then
1005 num_missing = num_missing+1
1006 is_missing(4) = .true.
1007 if(wtw .GE. 0.5_kindl .AND. wtn .GE. 0.5_kindl) set_to_missing = .true.
1008 endif
1009
1010 if( num_missing == 4 .OR. set_to_missing ) then
1011 data_out(m,n) = missing_value
1012 if(present(mask_out)) mask_out(m,n) = 0.0_kindl
1013 cycle
1014 else if(num_missing == 0) then
1015 f1 = data_in(is,js)
1016 f2 = data_in(ie,js)
1017 f3 = data_in(ie,je)
1018 f4 = data_in(is,je)
1019 w = wtw
1020 s = wts
1021 else if(num_missing == 3) then !--- three missing value
1022 if(.not. is_missing(1) ) then
1023 data_out(m,n) = data_in(is,js)
1024 else if(.not. is_missing(2) ) then
1025 data_out(m,n) = data_in(ie,js)
1026 else if(.not. is_missing(3) ) then
1027 data_out(m,n) = data_in(ie,je)
1028 else if(.not. is_missing(4) ) then
1029 data_out(m,n) = data_in(is,je)
1030 endif
1031 if(present(mask_out) ) mask_out(m,n) = 1.0_kindl
1032 cycle
1033 else !--- one or two missing value
1034 if( num_missing == 1) then
1035 if( is_missing(1) .OR. is_missing(3) ) then
1036 middle = 0.5_kindl *(data_in(ie,js)+data_in(is,je))
1037 else
1038 middle = 0.5_kindl *(data_in(is,js)+data_in(ie,je))
1039 endif
1040 else ! num_missing = 2
1041 if( is_missing(1) .AND. is_missing(2) ) then
1042 middle = 0.5_kindl *(data_in(ie,je)+data_in(is,je))
1043 else if( is_missing(1) .AND. is_missing(3) ) then
1044 middle = 0.5_kindl *(data_in(ie,js)+data_in(is,je))
1045 else if( is_missing(1) .AND. is_missing(4) ) then
1046 middle = 0.5_kindl *(data_in(ie,js)+data_in(ie,je))
1047 else if( is_missing(2) .AND. is_missing(3) ) then
1048 middle = 0.5_kindl *(data_in(is,js)+data_in(is,je))
1049 else if( is_missing(2) .AND. is_missing(4) ) then
1050 middle = 0.5_kindl*(data_in(is,js)+data_in(ie,je))
1051 else if( is_missing(3) .AND. is_missing(4) ) then
1052 middle = 0.5_kindl*(data_in(is,js)+data_in(ie,js))
1053 endif
1054 endif
1055
1056 if( wtw .GE. 0.5_kindl .AND. wts .GE. 0.5_kindl ) then ! zone 1
1057 w = 2.0_kindl*(wtw-0.5_kindl)
1058 s = 2.0_kindl*(wts-0.5_kindl)
1059 f1 = data_in(is,js)
1060 if(is_missing(2)) then
1061 f2 = f1
1062 else
1063 f2 = 0.5_kindl*(data_in(is,js)+data_in(ie,js))
1064 endif
1065 f3 = middle
1066 if(is_missing(4)) then
1067 f4 = f1
1068 else
1069 f4 = 0.5_kindl*(data_in(is,js)+data_in(is,je))
1070 endif
1071 else if( wte .GE. 0.5_kindl .AND. wts .GE. 0.5_kindl ) then ! zone 2
1072 w = 2.0_kindl*(1.0_kindl-wte)
1073 s = 2.0_kindl*(wts-0.5_kindl)
1074 f2 = data_in(ie,js)
1075 if(is_missing(1)) then
1076 f1 = f2
1077 else
1078 f1 = 0.5_kindl*(data_in(is,js)+data_in(ie,js))
1079 endif
1080 f4 = middle
1081 if(is_missing(3)) then
1082 f3 = f2
1083 else
1084 f3 = 0.5_kindl*(data_in(ie,js)+data_in(ie,je))
1085 endif
1086 else if( wte .GE. 0.5_kindl .AND. wtn .GE. 0.5_kindl ) then ! zone 3
1087 w = 2.0_kindl*(1.0_kindl-wte)
1088 s = 2.0_kindl*(1.0_kindl-wtn)
1089 f3 = data_in(ie,je)
1090 if(is_missing(2)) then
1091 f2 = f3
1092 else
1093 f2 = 0.5_kindl*(data_in(ie,js)+data_in(ie,je))
1094 endif
1095 f1 = middle
1096 if(is_missing(4)) then
1097 f4 = f3
1098 else
1099 f4 = 0.5_kindl*(data_in(ie,je)+data_in(is,je))
1100 endif
1101 else if( wtw .GE. 0.5_kindl .AND. wtn .GE. 0.5_kindl ) then ! zone 4
1102 w = 2.0_kindl*(wtw-0.5_kindl)
1103 s = 2.0_kindl*(1.0_kindl-wtn)
1104 f4 = data_in(is,je)
1105 if(is_missing(1)) then
1106 f1 = f4
1107 else
1108 f1 = 0.5_kindl*(data_in(is,js)+data_in(is,je))
1109 endif
1110 f2 = middle
1111 if(is_missing(3)) then
1112 f3 = f4
1113 else
1114 f3 = 0.5_kindl*(data_in(ie,je)+data_in(is,je))
1115 endif
1116 else
1117 call mpp_error(fatal, &
1118 "horiz_interp_bilinear_mod: the point should be in one of the four zone")
1119 endif
1120 endif
1121
1122 data_out(m,n) = f3 + (f4-f3)*w + (f2-f3)*s + ((f1-f2)+(f3-f4))*w*s
1123 if(present(mask_out)) mask_out(m,n) = 1.0_kindl
1124 enddo
1125 enddo
1126 else
1127 do n = 1, nlat_out
1128 do m = 1, nlon_out
1129 is = interp % i_lon (m,n,1); ie = interp % i_lon (m,n,2)
1130 js = interp % j_lat (m,n,1); je = interp % j_lat (m,n,2)
1131 wtw = interp % HI_KIND_TYPE_ % wti (m,n,1)
1132 wte = interp % HI_KIND_TYPE_ % wti (m,n,2)
1133 wts = interp % HI_KIND_TYPE_ % wtj (m,n,1)
1134 wtn = interp % HI_KIND_TYPE_ % wtj (m,n,2)
1135
1136 if(present(missing_value) ) then
1137 num_missing = 0
1138 if(data_in(is,js) == missing_value) then
1139 num_missing = num_missing+1
1140 mask(is,js) = 0.0_kindl
1141 endif
1142 if(data_in(ie,js) == missing_value) then
1143 num_missing = num_missing+1
1144 mask(ie,js) = 0.0_kindl
1145 endif
1146 if(data_in(ie,je) == missing_value) then
1147 num_missing = num_missing+1
1148 mask(ie,je) = 0.0_kindl
1149 endif
1150 if(data_in(is,je) == missing_value) then
1151 num_missing = num_missing+1
1152 mask(is,je) = 0.0_kindl
1153 endif
1154 endif
1155
1156 dwtsum = data_in(is,js)*mask(is,js)*wtw*wts &
1157 + data_in(ie,js)*mask(ie,js)*wte*wts &
1158 + data_in(ie,je)*mask(ie,je)*wte*wtn &
1159 + data_in(is,je)*mask(is,je)*wtw*wtn
1160 wtsum = mask(is,js)*wtw*wts + mask(ie,js)*wte*wts &
1161 + mask(ie,je)*wte*wtn + mask(is,je)*wtw*wtn
1162
1163 if(.not. present(mask_in) .and. .not. present(missing_value)) wtsum = 1.0_kindl
1164
1165 if(num_missing .gt. max_missing ) then
1166 data_out(m,n) = missing_value
1167 if(present(mask_out)) mask_out(m,n) = 0.0_kindl
1168 else if(wtsum .lt. real(epsln, fms_hi_kind_)) then
1169 if(present(missing_value)) then
1170 data_out(m,n) = missing_value
1171 else
1172 data_out(m,n) = 0.0_kindl
1173 endif
1174 if(present(mask_out)) mask_out(m,n) = 0.0_kindl
1175 else
1176 data_out(m,n) = dwtsum/wtsum
1177 if(present(mask_out)) mask_out(m,n) = wtsum
1178 endif
1179 enddo
1180 enddo
1181 endif
1182 !***********************************************************************
1183 ! compute statistics: minimum, maximum, and mean
1184 !-----------------------------------------------------------------------
1185 if (iverbose > 0) then
1186
1187 ! compute statistics of input data
1188
1189 call stats (data_in, min_in, max_in, avg_in, miss_in, missing_value, mask_in)
1190
1191 ! compute statistics of output data
1192 call stats (data_out, min_out, max_out, avg_out, miss_out, missing_value, mask_out)
1193
1194 !---- output statistics ----
1195 iunit = stdout()
1196 write (iunit,900)
1197 write (iunit,901) min_in ,max_in, avg_in
1198 if (present(mask_in)) write (iunit,903) miss_in
1199 write (iunit,902) min_out,max_out,avg_out
1200 if (present(mask_out)) write (iunit,903) miss_out
1201
1202900 format (/,1x,10('-'),' output from horiz_interp ',10('-'))
1203901 format (' input: min=',f16.9,' max=',f16.9,' avg=',f22.15)
1204902 format (' output: min=',f16.9,' max=',f16.9,' avg=',f22.15)
1205903 format (' number of missing points = ',i6)
1206
1207 endif
1208
1209 return
1210
1211 end subroutine
1212
1213 !> Subroutine for reading a weight file and use it to fill in the horiz interp type
1214 !! for the bilinear interpolation method.
1215 subroutine horiz_interp_read_weights_bilinear_(Interp, weight_filename, lon_out, lat_out, lon_in, lat_in, &
1216 weight_file_source, interp_method, isw, iew, jsw, jew, nglon, nglat)
1217 type(horiz_interp_type), intent(inout) :: Interp !< Horiz interp time to fill
1218 character(len=*), intent(in) :: weight_filename !< Name of the weight file
1219 real(FMS_HI_KIND_), target, intent(in) :: lat_out(:,:) !< Output (model) latitude
1220 real(FMS_HI_KIND_), target, intent(in) :: lon_out(:,:) !< Output (model) longitude
1221 real(FMS_HI_KIND_), intent(in) :: lat_in(:) !< Input (data) latitude
1222 real(FMS_HI_KIND_), intent(in) :: lon_in(:) !< Input (data) longitude
1223 character(len=*), intent(in) :: weight_file_source !< Source of the weight file
1224 character(len=*), intent(in) :: interp_method !< The interp method to use
1225 integer, intent(in) :: isw, iew, jsw, jew !< Starting and ending indices of the compute domain
1226 integer, intent(in) :: nglon !< Number of longitudes in the global domain
1227 integer, intent(in) :: nglat !< Number of latitudes in the globl domain
1228
1229
1230 real(FMS_HI_KIND_), allocatable :: var(:,:,:) !< Dummy variable to read the indices and weight into
1231 type(fmsnetcdffile_t) :: weight_fileobj !< FMS2io fileob for the weight file
1232 integer :: nlon !< Number of longitudes in the model grid as read
1233 !! from the weight file
1234 integer :: nlat !< Number of latitude in the model grid as read
1235 !! from the weight file
1236
1237 if (.not. open_file(weight_fileobj, weight_filename, "read" )) &
1238 call mpp_error(fatal, "Error opening the weight file:"//&
1239 &trim(weight_filename))
1240
1241 !< Check that weight file has the correct dimensions
1242 select case (trim(weight_file_source))
1243 case ("fregrid")
1244 call get_dimension_size(weight_fileobj, "nlon", nlon)
1245 if (nlon .ne. nglon) &
1246 call mpp_error(fatal, "The nlon from the weight file is not the same as in the input grid."//&
1247 &" From weight file:"//string(nlon)//" from input grid:"//string(size(lon_out,1)))
1248 call get_dimension_size(weight_fileobj, "nlat", nlat)
1249 if (nlat .ne. nglat) &
1250 call mpp_error(fatal, "The nlat from the weight file is not the same as in the input grid."//&
1251 &" From weight file:"//string(nlat)//" from input grid:"//string(size(lon_out,2)))
1252 case default
1253 call mpp_error(fatal, trim(weight_file_source)//&
1254 &" is not a supported weight file source. fregrid is the only supported weight file source." )
1255 end select
1256
1257 interp%nlon_src = size(lon_in(:)) ; interp%nlat_src = size(lat_in(:))
1258 interp%nlon_dst = size(lon_out,1); interp%nlat_dst = size(lon_out,2)
1259
1260 allocate ( interp % HI_KIND_TYPE_ % wti (interp%nlon_dst,interp%nlat_dst,2), &
1261 interp % HI_KIND_TYPE_ % wtj (interp%nlon_dst,interp%nlat_dst,2), &
1262 interp % i_lon (interp%nlon_dst,interp%nlat_dst,2), &
1263 interp % j_lat (interp%nlon_dst,interp%nlat_dst,2))
1264
1265
1266 !! Three is for lon, lat, tile
1267 !! Currently, interpolation is only supported from lat,lon input data
1268 allocate(var(interp%nlon_dst,interp%nlat_dst, 3))
1269 call read_data(weight_fileobj, "index", var, corner=(/isw, jsw, 1/), edge_lengths=(/iew-isw+1, jew-jsw+1, 3/))
1270
1271 !! Each point has a lon (i), and lat(j) index
1272 !! From there the four corners are (i,j), (i,j+1) (i+1) (i+1,j+1)
1273 interp % i_lon (:,:,1) = var(:,:,1)
1274 interp % i_lon (:,:,2) = interp % i_lon (:,:,1) + 1
1275 where (interp % i_lon (:,:,2) > size(lon_in(:))) interp % i_lon (:,:,2) = 1
1276
1277 interp % j_lat (:,:,1) = var(:,:,2)
1278 interp % j_lat (:,:,2) = interp % j_lat (:,:,1) + 1
1279 where (interp % j_lat (:,:,2) > size(lat_in(:))) interp % j_lat (:,:,2) = 1
1280
1281 deallocate(var)
1282
1283 allocate(var(interp%nlon_dst,interp%nlat_dst, 4))
1284 call read_data(weight_fileobj, "weight", var, corner=(/isw, jsw, 1/), edge_lengths=(/iew-isw+1, jew-jsw+1, 4/))
1285
1286 !! The weights for the four corners
1287 !! var(:,:,1) -> (i,j)
1288 !! var(:,:,2) -> (i,j+1)
1289 !! var(:,:,3) -> (i+1,j)
1290 !! var(:,:,4) -> (i+1,j+1)
1291 interp % HI_KIND_TYPE_ % wti = var(:,:,1:2)
1292 interp % HI_KIND_TYPE_ % wtj = var(:,:,3:4)
1293 deallocate(var)
1294
1295 interp% HI_KIND_TYPE_ % is_allocated = .true.
1296 interp% interp_method = bilinear
1297 interp% I_am_initialized = .true.
1298 call close_file(weight_fileobj)
1300
1301!> @}
subroutine horiz_interp_bilinear_(interp, data_in, data_out, verbose, mask_in, mask_out, missing_value, missing_permit, new_handle_missing)
Subroutine for performing the horizontal interpolation between two grids.
subroutine find_neighbor_(interp, lon_in, lat_in, lon_out, lat_out, src_modulo)
this routine will search the source grid to fine the grid box that encloses each destination grid.
subroutine horiz_interp_bilinear_new_2d_(interp, lon_in, lat_in, lon_out, lat_out, verbose, src_modulo, new_search, no_crash_when_not_found)
Initialization routine.
subroutine horiz_interp_read_weights_bilinear_(interp, weight_filename, lon_out, lat_out, lon_in, lat_in, weight_file_source, interp_method, isw, iew, jsw, jew, nglon, nglat)
Subroutine for reading a weight file and use it to fill in the horiz interp type for the bilinear int...
subroutine find_neighbor_new_(interp, lon_in, lat_in, lon_out, lat_out, src_modulo, no_crash)
this routine will search the source grid to fine the grid box that encloses each destination grid.
logical function inside_polygon_(polyx, polyy, x, y)
The function will return true if the point x,y is inside a polygon, or false if it is not....