1 module rotate_winds_module
7 use misc_definitions_module
10 integer :: orig_selected_projection
15 ! ARW Wind Rotation Code
18 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
21 ! Purpose: Rotate grid-relative winds to Earth-relative winds !
22 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
23 subroutine map_to_met(u, u_mask, v, v_mask, &
26 xlon_u, xlon_v, xlat_u, xlat_v)
31 integer, intent(in) :: us1, us2, ue1, ue2, vs1, vs2, ve1, ve2
32 real, pointer, dimension(:,:) :: u, v, xlon_u, xlon_v, xlat_u, xlat_v
33 type (bitarray), intent(in) :: u_mask, v_mask
35 orig_selected_projection = iget_selected_domain()
36 call select_domain(SOURCE_PROJ)
37 call metmap_xform(u, u_mask, v, v_mask, &
40 xlon_u, xlon_v, xlat_u, xlat_v, 1)
41 call select_domain(orig_selected_projection)
43 end subroutine map_to_met
46 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
49 ! Purpose: Rotate Earth-relative winds to grid-relative winds !
50 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
51 subroutine met_to_map(u, u_mask, v, v_mask, &
54 xlon_u, xlon_v, xlat_u, xlat_v)
59 integer, intent(in) :: us1, us2, ue1, ue2, vs1, vs2, ve1, ve2
60 real, pointer, dimension(:,:) :: u, v, xlon_u, xlon_v, xlat_u, xlat_v
61 type (bitarray), intent(in) :: u_mask, v_mask
63 orig_selected_projection = iget_selected_domain()
65 call metmap_xform(u, u_mask, v, v_mask, &
68 xlon_u, xlon_v, xlat_u, xlat_v, -1)
69 call select_domain(orig_selected_projection)
71 end subroutine met_to_map
74 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
75 ! Name: metmap_xform !
77 ! Purpose: Do the actual work of rotating winds for C grid. !
78 ! If idir= 1, rotate grid-relative winds to Earth-relative winds !
79 ! If idir=-1, rotate Earth-relative winds to grid-relative winds !
81 ! ASSUMPTIONS: 1) MEMORY ORDER IS XY. !
82 ! 2) U ARRAY HAS ONE MORE COLUMN THAN THE V ARRAY, AND V ARRAY !
83 ! HAS ONE MORE ROW THAN U ARRAY. !
84 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
85 subroutine metmap_xform(u, u_mask, v, v_mask, &
86 us1, us2, ue1, ue2, vs1, vs2, ve1, ve2, &
87 xlon_u, xlon_v, xlat_u, xlat_v, idir)
92 integer, intent(in) :: us1, us2, ue1, ue2, vs1, vs2, ve1, ve2, idir
93 real, pointer, dimension(:,:) :: u, v, xlon_u, xlon_v, xlat_u, xlat_v
94 type (bitarray), intent(in) :: u_mask, v_mask
98 real :: u_weight, v_weight
99 real :: u_map, v_map, alpha, diff
100 real, pointer, dimension(:,:) :: u_new, v_new, u_mult, v_mult
101 logical :: do_last_col_u, do_last_row_u, do_last_col_v, do_last_row_v
102 real :: x_u, y_u, x_v, y_v
103 real :: xlat_u_p1 , xlon_u_p1, xlat_v_p1 , xlon_v_p1
104 real :: xlat_u_m1 , xlon_u_m1, xlat_v_m1 , xlon_v_m1
105 real :: diff_lon, diff_lat
107 ! If the proj_info structure has not been initialized, we don't have
108 ! information about the projection and standard longitude.
109 if (proj_stack(current_nest_number)%init) then
111 ! Only rotate winds for Lambert conformal, polar stereographic, or Cassini
112 if ((proj_stack(current_nest_number)%code == PROJ_LC) .or. &
113 (proj_stack(current_nest_number)%code == PROJ_PS) .or. &
114 (proj_stack(current_nest_number)%code == PROJ_CASSINI)) then
115 call mprintf((idir == 1),LOGFILE,'Rotating map winds to earth winds.')
116 call mprintf((idir == -1),LOGFILE,'Rotating earth winds to grid winds')
118 allocate(u_mult(us1:ue1,us2:ue2))
119 allocate(v_mult(vs1:ve1,vs2:ve2))
123 if (bitarray_test(u_mask, i-us1+1, j-us2+1)) then
133 if (bitarray_test(v_mask, i-vs1+1, j-vs2+1)) then
141 if (ue1-us1 == ve1-vs1) then
142 do_last_col_u = .false.
143 do_last_col_v = .true.
145 do_last_col_u = .true.
146 do_last_col_v = .false.
149 if (ue2-us2 == ve2-vs2) then
150 do_last_row_u = .true.
151 do_last_row_v = .false.
153 do_last_row_u = .false.
154 do_last_row_v = .true.
157 ! Create arrays to hold rotated winds
158 allocate(u_new(us1:ue1, us2:ue2))
159 allocate(v_new(vs1:ve1, vs2:ve2))
165 diff = idir * (xlon_u(i,j) - proj_stack(current_nest_number)%stdlon)
166 if (diff > 180.) then
168 else if (diff < -180.) then
172 ! Calculate the rotation angle, alpha, in radians
173 if (proj_stack(current_nest_number)%code == PROJ_LC) then
174 alpha = diff * proj_stack(current_nest_number)%cone * rad_per_deg * proj_stack(current_nest_number)%hemi
175 else if (proj_stack(current_nest_number)%code == PROJ_CASSINI) then
176 call latlon_to_ij ( proj_stack(SOURCE_PROJ) , &
177 xlat_u(i,j), xlon_u(i,j), x_u, y_u )
178 call ij_to_latlon ( proj_stack(SOURCE_PROJ) , &
179 x_u, y_u + 0.1 , xlat_u_p1 , xlon_u_p1 )
180 call ij_to_latlon ( proj_stack(SOURCE_PROJ) , &
181 x_u, y_u - 0.1 , xlat_u_m1 , xlon_u_m1 )
182 diff_lon = xlon_u_p1-xlon_u_m1
183 if (diff_lon > 180.) then
184 diff_lon = diff_lon - 360.
185 else if (diff_lon < -180.) then
186 diff_lon = diff_lon + 360.
188 diff_lat = xlat_u_p1-xlat_u_m1
189 alpha = atan2( (-cos(xlat_u(i,j)*rad_per_deg) * diff_lon*rad_per_deg), &
190 diff_lat*rad_per_deg &
193 alpha = diff * rad_per_deg * proj_stack(current_nest_number)%hemi
198 ! On C grid, take U_ij, and get V value at the same lat/lon
199 ! by averaging the four surrounding V points
200 if (bitarray_test(u_mask, i-us1+1, j-us2+1)) then
203 if (j == ue2 .and. do_last_row_u) then
204 v_weight = v_mult(i,j)
205 v_map = v(i,j)*v_mult(i,j)
207 v_weight = v_mult(i,j) + v_mult(i,j+1)
208 v_map = v(i,j)*v_mult(i,j) + v(i,j+1)*v_mult(i,j+1)
210 else if (i == ue1 .and. do_last_col_u) then
211 if (j == ue2 .and. do_last_row_u) then
212 v_weight = v_mult(i-1,j)
215 v_weight = v_mult(i-1,j) + v_mult(i-1,j+1)
216 v_map = v(i-1,j)*v_mult(i-1,j) + v(i-1,j+1)*v_mult(i-1,j+1)
218 else if (j == ue2 .and. do_last_row_u) then
219 v_weight = v_mult(i-1,j-1) + v_mult(i,j-1)
220 v_map = v(i-1,j-1)*v_mult(i-1,j-1) + v(i,j-1)*v_mult(i,j-1)
222 v_weight = v_mult(i-1,j) + v_mult(i-1,j+1) + v_mult(i,j) + v_mult(i,j+1)
223 v_map = v(i-1,j)*v_mult(i-1,j) + v(i-1,j+1)*v_mult(i-1,j+1) + v(i,j)*v_mult(i,j) + v(i,j+1)*v_mult(i,j+1)
225 if (v_weight > 0.) then
226 u_new(i,j) = cos(alpha)*u_map + sin(alpha)*v_map/v_weight
241 diff = idir * (xlon_v(i,j) - proj_stack(current_nest_number)%stdlon)
242 if (diff > 180.) then
244 else if (diff < -180.) then
248 if (proj_stack(current_nest_number)%code == PROJ_LC) then
249 alpha = diff * proj_stack(current_nest_number)%cone * rad_per_deg * proj_stack(current_nest_number)%hemi
250 else if (proj_stack(current_nest_number)%code == PROJ_CASSINI) then
251 call latlon_to_ij ( proj_stack(SOURCE_PROJ) , &
252 xlat_v(i,j), xlon_v(i,j), x_v, y_v )
253 call ij_to_latlon ( proj_stack(SOURCE_PROJ) , &
254 x_v, y_v + 0.1 , xlat_v_p1 , xlon_v_p1 )
255 call ij_to_latlon ( proj_stack(SOURCE_PROJ) , &
256 x_v, y_v - 0.1 , xlat_v_m1 , xlon_v_m1 )
257 diff_lon = xlon_v_p1-xlon_v_m1
258 if (diff_lon > 180.) then
259 diff_lon = diff_lon - 360.
260 else if (diff_lon < -180.) then
261 diff_lon = diff_lon + 360.
263 diff_lat = xlat_v_p1-xlat_v_m1
264 alpha = atan2( (-cos(xlat_v(i,j)*rad_per_deg) * diff_lon*rad_per_deg), &
265 diff_lat*rad_per_deg &
268 alpha = diff * rad_per_deg * proj_stack(current_nest_number)%hemi
273 if (bitarray_test(v_mask, i-vs1+1, j-vs2+1)) then
276 if (i == ve1 .and. do_last_col_v) then
277 u_weight = u_mult(i,j)
278 u_map = u(i,j)*u_mult(i,j)
280 u_weight = u_mult(i,j) + u_mult(i+1,j)
281 u_map = u(i,j)*u_mult(i,j) + u(i+1,j)*u_mult(i+1,j)
283 else if (j == ve2 .and. do_last_row_v) then
284 if (i == ve1 .and. do_last_col_v) then
285 u_weight = u_mult(i,j-1)
286 u_map = u(i,j-1)*u_mult(i,j-1)
288 u_weight = u_mult(i,j-1) + u_mult(i+1,j-1)
289 u_map = u(i,j-1)*u_mult(i,j-1) + u(i+1,j-1)*u_mult(i+1,j-1)
291 else if (i == ve1 .and. do_last_col_v) then
292 u_weight = u_mult(i,j) + u_mult(i,j-1)
293 u_map = u(i,j)*u_mult(i,j) + u(i,j-1)*u_mult(i,j-1)
295 u_weight = u_mult(i,j-1) + u_mult(i,j) + u_mult(i+1,j-1) + u_mult(i+1,j)
296 u_map = u(i,j-1)*u_mult(i,j-1) + u(i,j)*u_mult(i,j) + u(i+1,j-1)*u_mult(i+1,j-1) + u(i+1,j)*u_mult(i+1,j)
298 if (u_weight > 0.) then
299 v_new(i,j) = -sin(alpha)*u_map/u_weight + cos(alpha)*v_map
310 ! Copy rotated winds back into argument arrays
321 call mprintf(.true.,ERROR,'In metmap_xform(), uninitialized proj_info structure.')
324 end subroutine metmap_xform
328 ! NMM Wind Rotation Code
331 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
332 ! Name: map_to_met_nmm !
334 ! Purpose: Rotate grid-relative winds to Earth-relative winds !
335 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
336 subroutine map_to_met_nmm(u, u_mask, v, v_mask, &
337 vs1, vs2, ve1, ve2, &
343 integer, intent(in) :: vs1, vs2, ve1, ve2
344 real, pointer, dimension(:,:) :: u, v, xlat_v, xlon_v
345 type (bitarray), intent(in) :: u_mask, v_mask
347 orig_selected_projection = iget_selected_domain()
348 call select_domain(SOURCE_PROJ)
349 call metmap_xform_nmm(u, u_mask, v, v_mask, &
350 vs1, vs2, ve1, ve2, &
352 call select_domain(orig_selected_projection)
354 end subroutine map_to_met_nmm
357 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
358 ! Name: met_to_map_nmm !
360 ! Purpose: Rotate Earth-relative winds to grid-relative winds !
361 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
362 subroutine met_to_map_nmm(u, u_mask, v, v_mask, &
363 vs1, vs2, ve1, ve2, &
369 integer, intent(in) :: vs1, vs2, ve1, ve2
370 real, pointer, dimension(:,:) :: u, v, xlat_v, xlon_v
371 type (bitarray), intent(in) :: u_mask, v_mask
373 orig_selected_projection = iget_selected_domain()
374 call select_domain(1)
375 call metmap_xform_nmm(u, u_mask, v, v_mask, &
376 vs1, vs2, ve1, ve2, &
378 call select_domain(orig_selected_projection)
380 end subroutine met_to_map_nmm
383 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
384 ! Name: metmap_xform_nmm !
386 ! Purpose: Do the actual work of rotating winds for E grid. !
387 ! If idir= 1, rotate grid-relative winds to Earth-relative winds !
388 ! If idir=-1, rotate Earth-relative winds to grid-relative winds !
390 ! ASSUMPTIONS: 1) MEMORY ORDER IS XY. !
391 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
392 subroutine metmap_xform_nmm(u, u_mask, v, v_mask, &
393 vs1, vs2, ve1, ve2, &
394 xlat_v, xlon_v, idir)
399 integer, intent(in) :: vs1, vs2, ve1, ve2, idir
400 real, pointer, dimension(:,:) :: u, v, xlat_v, xlon_v
401 type (bitarray), intent(in) :: u_mask, v_mask
405 real :: u_map, v_map, diff, alpha
406 real :: phi0, lmbd0, big_denominator, relm, rlat_v,rlon_v, clontemp
407 real :: sin_phi0, cos_phi0, cos_alpha, sin_alpha
408 real, pointer, dimension(:,:) :: u_new, v_new
411 ! If the proj_info structure has not been initialized, we don't have
412 ! information about the projection and standard longitude.
413 if (proj_stack(current_nest_number)%init) then
415 if (proj_stack(current_nest_number)%code == PROJ_ROTLL) then
417 call mprintf((idir == 1),LOGFILE,'Rotating map winds to earth winds.')
418 call mprintf((idir == -1),LOGFILE,'Rotating earth winds to grid winds')
420 ! Create arrays to hold rotated winds
421 allocate(u_new(vs1:ve1, vs2:ve2))
422 allocate(v_new(vs1:ve1, vs2:ve2))
424 phi0 = proj_stack(current_nest_number)%lat1 * rad_per_deg
426 clontemp= proj_stack(current_nest_number)%lon1
428 if (clontemp .lt. 0.) then
429 lmbd0 = (clontemp + 360.) * rad_per_deg
431 lmbd0 = (clontemp) * rad_per_deg
440 ! Calculate the sine and cosine of rotation angle
441 rlat_v = xlat_v(i,j) * rad_per_deg
442 rlon_v = xlon_v(i,j) * rad_per_deg
443 relm = rlon_v - lmbd0
444 big_denominator = cos(asin( &
445 cos_phi0 * sin(rlat_v) - &
446 sin_phi0 * cos(rlat_v) * cos(relm) &
449 sin_alpha = sin_phi0 * sin(relm) / &
452 cos_alpha = (cos_phi0 * cos(rlat_v) + &
453 sin_phi0 * sin(rlat_v) * cos(relm)) / &
457 if (bitarray_test(u_mask, i-vs1+1, j-vs2+1)) then
459 if (bitarray_test(v_mask, i-vs1+1, j-vs2+1)) then
465 u_new(i,j) = cos_alpha*u_map + idir*sin_alpha*v_map
471 if (bitarray_test(v_mask, i-vs1+1, j-vs2+1)) then
473 if (bitarray_test(u_mask, i-vs1+1, j-vs2+1)) then
479 v_new(i,j) = -idir*sin_alpha*u_map + cos_alpha*v_map
487 ! Copy rotated winds back into argument arrays
494 ! Only rotate winds for Lambert conformal, polar stereographic, or Cassini
495 else if ((proj_stack(current_nest_number)%code == PROJ_LC) .or. &
496 (proj_stack(current_nest_number)%code == PROJ_PS) .or. &
497 (proj_stack(current_nest_number)%code == PROJ_CASSINI)) then
499 call mprintf((idir == 1),LOGFILE,'Rotating map winds to earth winds.')
500 call mprintf((idir == -1),LOGFILE,'Rotating earth winds to grid winds')
502 ! Create arrays to hold rotated winds
503 allocate(u_new(vs1:ve1, vs2:ve2))
504 allocate(v_new(vs1:ve1, vs2:ve2))
509 diff = idir * (xlon_v(i,j) - proj_stack(current_nest_number)%stdlon)
510 if (diff > 180.) then
512 else if (diff < -180.) then
516 ! Calculate the rotation angle, alpha, in radians
517 if (proj_stack(current_nest_number)%code == PROJ_LC) then
518 alpha = diff * proj_stack(current_nest_number)%cone * &
519 rad_per_deg * proj_stack(current_nest_number)%hemi
521 alpha = diff * rad_per_deg * proj_stack(current_nest_number)%hemi
525 if (bitarray_test(u_mask, i-vs1+1, j-vs2+1)) then
527 if (bitarray_test(v_mask, i-vs1+1, j-vs2+1)) then
533 u_new(i,j) = cos(alpha)*u_map + idir*sin(alpha)*v_map
539 if (bitarray_test(v_mask, i-vs1+1, j-vs2+1)) then
541 if (bitarray_test(u_mask, i-vs1+1, j-vs2+1)) then
547 v_new(i,j) = -idir*sin(alpha)*u_map + cos(alpha)*v_map
555 ! Copy rotated winds back into argument arrays
565 call mprintf(.true.,ERROR,'In metmap_xform_nmm(), uninitialized proj_info structure.')
568 end subroutine metmap_xform_nmm
570 end module rotate_winds_module