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))
164 diff = idir * (xlon_u(i,j) - proj_stack(current_nest_number)%stdlon)
165 if (diff > 180.) then
167 else if (diff < -180.) then
171 ! Calculate the rotation angle, alpha, in radians
172 if (proj_stack(current_nest_number)%code == PROJ_LC) then
173 alpha = diff * proj_stack(current_nest_number)%cone * rad_per_deg * proj_stack(current_nest_number)%hemi
174 else if ( (proj_stack(SOURCE_PROJ)%code == PROJ_CASSINI) .and. ( idir == 1 ) ) then
175 call latlon_to_ij ( proj_stack(SOURCE_PROJ) , &
176 xlat_u(i,j), xlon_u(i,j), x_u, y_u )
177 call ij_to_latlon ( proj_stack(SOURCE_PROJ) , &
178 x_u, y_u + 0.1 , xlat_u_p1 , xlon_u_p1 )
179 call ij_to_latlon ( proj_stack(SOURCE_PROJ) , &
180 x_u, y_u - 0.1 , xlat_u_m1 , xlon_u_m1 )
181 diff_lon = xlon_u_p1-xlon_u_m1
182 if (diff_lon > 180.) then
183 diff_lon = diff_lon - 360.
184 else if (diff_lon < -180.) then
185 diff_lon = diff_lon + 360.
187 diff_lat = xlat_u_p1-xlat_u_m1
188 alpha =-atan2( (-cos(xlat_u(i,j)*rad_per_deg) * diff_lon*rad_per_deg), &
189 diff_lat*rad_per_deg &
191 else if ( (proj_stack(current_nest_number)%code == PROJ_CASSINI) .and. ( idir == -1 ) ) then
193 diff = xlon_u(i,j)-xlon_u(i,j-1)
194 if (diff > 180.) then
196 else if (diff < -180.) then
199 alpha = atan2( (-cos(xlat_u(i,j)*rad_per_deg) * diff*rad_per_deg), &
200 (xlat_u(i,j)-xlat_u(i,j-1))*rad_per_deg &
202 else if (j == us2) then
203 diff = xlon_u(i,j+1)-xlon_u(i,j)
204 if (diff > 180.) then
206 else if (diff < -180.) then
209 alpha = atan2( (-cos(xlat_u(i,j)*rad_per_deg) * diff*rad_per_deg), &
210 (xlat_u(i,j+1)-xlat_u(i,j))*rad_per_deg &
213 diff = xlon_u(i,j+1)-xlon_u(i,j-1)
214 if (diff > 180.) then
216 else if (diff < -180.) then
219 alpha = atan2( (-cos(xlat_u(i,j)*rad_per_deg) * diff*rad_per_deg), &
220 (xlat_u(i,j+1)-xlat_u(i,j-1))*rad_per_deg &
224 alpha = diff * rad_per_deg * proj_stack(current_nest_number)%hemi
229 ! On C grid, take U_ij, and get V value at the same lat/lon
230 ! by averaging the four surrounding V points
231 if (bitarray_test(u_mask, i-us1+1, j-us2+1)) then
234 if (j == ue2 .and. do_last_row_u) then
235 v_weight = v_mult(i,j)
236 v_map = v(i,j)*v_mult(i,j)
238 v_weight = v_mult(i,j) + v_mult(i,j+1)
239 v_map = v(i,j)*v_mult(i,j) + v(i,j+1)*v_mult(i,j+1)
241 else if (i == ue1 .and. do_last_col_u) then
242 if (j == ue2 .and. do_last_row_u) then
243 v_weight = v_mult(i-1,j)
246 v_weight = v_mult(i-1,j) + v_mult(i-1,j+1)
247 v_map = v(i-1,j)*v_mult(i-1,j) + v(i-1,j+1)*v_mult(i-1,j+1)
249 else if (j == ue2 .and. do_last_row_u) then
250 v_weight = v_mult(i-1,j-1) + v_mult(i,j-1)
251 v_map = v(i-1,j-1)*v_mult(i-1,j-1) + v(i,j-1)*v_mult(i,j-1)
253 v_weight = v_mult(i-1,j) + v_mult(i-1,j+1) + v_mult(i,j) + v_mult(i,j+1)
254 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)
256 if (v_weight > 0.) then
257 u_new(i,j) = cos(alpha)*u_map + sin(alpha)*v_map/v_weight
272 diff = idir * (xlon_v(i,j) - proj_stack(current_nest_number)%stdlon)
273 if (diff > 180.) then
275 else if (diff < -180.) then
279 if (proj_stack(current_nest_number)%code == PROJ_LC) then
280 alpha = diff * proj_stack(current_nest_number)%cone * rad_per_deg * proj_stack(current_nest_number)%hemi
281 else if ( (proj_stack(SOURCE_PROJ)%code == PROJ_CASSINI) .and. ( idir == 1 ) ) then
282 call latlon_to_ij ( proj_stack(SOURCE_PROJ) , &
283 xlat_v(i,j), xlon_v(i,j), x_v, y_v )
284 call ij_to_latlon ( proj_stack(SOURCE_PROJ) , &
285 x_v, y_v + 0.1 , xlat_v_p1 , xlon_v_p1 )
286 call ij_to_latlon ( proj_stack(SOURCE_PROJ) , &
287 x_v, y_v - 0.1 , xlat_v_m1 , xlon_v_m1 )
288 diff_lon = xlon_v_p1-xlon_v_m1
289 if (diff_lon > 180.) then
290 diff_lon = diff_lon - 360.
291 else if (diff_lon < -180.) then
292 diff_lon = diff_lon + 360.
294 diff_lat = xlat_v_p1-xlat_v_m1
295 alpha =-atan2( (-cos(xlat_v(i,j)*rad_per_deg) * diff_lon*rad_per_deg), &
296 diff_lat*rad_per_deg &
298 else if ( (proj_stack(current_nest_number)%code == PROJ_CASSINI) .and. ( idir == -1 ) ) then
300 diff = xlon_v(i,j)-xlon_v(i,j-1)
301 if (diff > 180.) then
303 else if (diff < -180.) then
306 alpha = atan2( (-cos(xlat_v(i,j)*rad_per_deg) * diff*rad_per_deg), &
307 (xlat_v(i,j)-xlat_v(i,j-1))*rad_per_deg &
309 else if (j == vs2) then
310 diff = xlon_v(i,j+1)-xlon_v(i,j)
311 if (diff > 180.) then
313 else if (diff < -180.) then
316 alpha = atan2( (-cos(xlat_v(i,j)*rad_per_deg) * diff*rad_per_deg), &
317 (xlat_v(i,j+1)-xlat_v(i,j))*rad_per_deg &
320 diff = xlon_v(i,j+1)-xlon_v(i,j-1)
321 if (diff > 180.) then
323 else if (diff < -180.) then
326 alpha = atan2( (-cos(xlat_v(i,j)*rad_per_deg) * diff*rad_per_deg), &
327 (xlat_v(i,j+1)-xlat_v(i,j-1))*rad_per_deg &
331 alpha = diff * rad_per_deg * proj_stack(current_nest_number)%hemi
336 if (bitarray_test(v_mask, i-vs1+1, j-vs2+1)) then
339 if (i == ve1 .and. do_last_col_v) then
340 u_weight = u_mult(i,j)
341 u_map = u(i,j)*u_mult(i,j)
343 u_weight = u_mult(i,j) + u_mult(i+1,j)
344 u_map = u(i,j)*u_mult(i,j) + u(i+1,j)*u_mult(i+1,j)
346 else if (j == ve2 .and. do_last_row_v) then
347 if (i == ve1 .and. do_last_col_v) then
348 u_weight = u_mult(i,j-1)
349 u_map = u(i,j-1)*u_mult(i,j-1)
351 u_weight = u_mult(i,j-1) + u_mult(i+1,j-1)
352 u_map = u(i,j-1)*u_mult(i,j-1) + u(i+1,j-1)*u_mult(i+1,j-1)
354 else if (i == ve1 .and. do_last_col_v) then
355 u_weight = u_mult(i,j) + u_mult(i,j-1)
356 u_map = u(i,j)*u_mult(i,j) + u(i,j-1)*u_mult(i,j-1)
358 u_weight = u_mult(i,j-1) + u_mult(i,j) + u_mult(i+1,j-1) + u_mult(i+1,j)
359 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)
361 if (u_weight > 0.) then
362 v_new(i,j) = -sin(alpha)*u_map/u_weight + cos(alpha)*v_map
373 ! Copy rotated winds back into argument arrays
384 call mprintf(.true.,ERROR,'In metmap_xform(), uninitialized proj_info structure.')
387 end subroutine metmap_xform
391 ! NMM Wind Rotation Code
394 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
395 ! Name: map_to_met_nmm !
397 ! Purpose: Rotate grid-relative winds to Earth-relative winds !
398 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
399 subroutine map_to_met_nmm(u, u_mask, v, v_mask, &
400 vs1, vs2, ve1, ve2, &
406 integer, intent(in) :: vs1, vs2, ve1, ve2
407 real, pointer, dimension(:,:) :: u, v, xlat_v, xlon_v
408 type (bitarray), intent(in) :: u_mask, v_mask
410 orig_selected_projection = iget_selected_domain()
411 call select_domain(SOURCE_PROJ)
412 call metmap_xform_nmm(u, u_mask, v, v_mask, &
413 vs1, vs2, ve1, ve2, &
415 call select_domain(orig_selected_projection)
417 end subroutine map_to_met_nmm
420 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
421 ! Name: met_to_map_nmm !
423 ! Purpose: Rotate Earth-relative winds to grid-relative winds !
424 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
425 subroutine met_to_map_nmm(u, u_mask, v, v_mask, &
426 vs1, vs2, ve1, ve2, &
432 integer, intent(in) :: vs1, vs2, ve1, ve2
433 real, pointer, dimension(:,:) :: u, v, xlat_v, xlon_v
434 type (bitarray), intent(in) :: u_mask, v_mask
436 orig_selected_projection = iget_selected_domain()
437 call select_domain(1)
438 call metmap_xform_nmm(u, u_mask, v, v_mask, &
439 vs1, vs2, ve1, ve2, &
441 call select_domain(orig_selected_projection)
443 end subroutine met_to_map_nmm
446 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
447 ! Name: metmap_xform_nmm !
449 ! Purpose: Do the actual work of rotating winds for E grid. !
450 ! If idir= 1, rotate grid-relative winds to Earth-relative winds !
451 ! If idir=-1, rotate Earth-relative winds to grid-relative winds !
453 ! ASSUMPTIONS: 1) MEMORY ORDER IS XY. !
454 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
455 subroutine metmap_xform_nmm(u, u_mask, v, v_mask, &
456 vs1, vs2, ve1, ve2, &
457 xlat_v, xlon_v, idir)
462 integer, intent(in) :: vs1, vs2, ve1, ve2, idir
463 real, pointer, dimension(:,:) :: u, v, xlat_v, xlon_v
464 type (bitarray), intent(in) :: u_mask, v_mask
468 real :: u_map, v_map, diff, alpha
469 real :: phi0, lmbd0, big_denominator, relm, rlat_v,rlon_v, clontemp
470 real :: sin_phi0, cos_phi0, cos_alpha, sin_alpha
471 real, pointer, dimension(:,:) :: u_new, v_new
474 ! If the proj_info structure has not been initialized, we don't have
475 ! information about the projection and standard longitude.
476 if (proj_stack(current_nest_number)%init) then
478 if (proj_stack(current_nest_number)%code == PROJ_ROTLL) then
480 call mprintf((idir == 1),LOGFILE,'Rotating map winds to earth winds.')
481 call mprintf((idir == -1),LOGFILE,'Rotating earth winds to grid winds')
483 ! Create arrays to hold rotated winds
484 allocate(u_new(vs1:ve1, vs2:ve2))
485 allocate(v_new(vs1:ve1, vs2:ve2))
487 phi0 = proj_stack(current_nest_number)%lat1 * rad_per_deg
489 clontemp= proj_stack(current_nest_number)%lon1
491 if (clontemp .lt. 0.) then
492 lmbd0 = (clontemp + 360.) * rad_per_deg
494 lmbd0 = (clontemp) * rad_per_deg
503 ! Calculate the sine and cosine of rotation angle
504 rlat_v = xlat_v(i,j) * rad_per_deg
505 rlon_v = xlon_v(i,j) * rad_per_deg
506 relm = rlon_v - lmbd0
507 big_denominator = cos(asin( &
508 cos_phi0 * sin(rlat_v) - &
509 sin_phi0 * cos(rlat_v) * cos(relm) &
512 sin_alpha = sin_phi0 * sin(relm) / &
515 cos_alpha = (cos_phi0 * cos(rlat_v) + &
516 sin_phi0 * sin(rlat_v) * cos(relm)) / &
520 if (bitarray_test(u_mask, i-vs1+1, j-vs2+1)) then
522 if (bitarray_test(v_mask, i-vs1+1, j-vs2+1)) then
528 u_new(i,j) = cos_alpha*u_map + idir*sin_alpha*v_map
534 if (bitarray_test(v_mask, i-vs1+1, j-vs2+1)) then
536 if (bitarray_test(u_mask, i-vs1+1, j-vs2+1)) then
542 v_new(i,j) = -idir*sin_alpha*u_map + cos_alpha*v_map
550 ! Copy rotated winds back into argument arrays
557 ! Only rotate winds for Lambert conformal, polar stereographic, or Cassini
558 else if ((proj_stack(current_nest_number)%code == PROJ_LC) .or. &
559 (proj_stack(current_nest_number)%code == PROJ_PS) .or. &
560 (proj_stack(current_nest_number)%code == PROJ_CASSINI)) then
562 call mprintf((idir == 1),LOGFILE,'Rotating map winds to earth winds.')
563 call mprintf((idir == -1),LOGFILE,'Rotating earth winds to grid winds')
565 ! Create arrays to hold rotated winds
566 allocate(u_new(vs1:ve1, vs2:ve2))
567 allocate(v_new(vs1:ve1, vs2:ve2))
572 diff = idir * (xlon_v(i,j) - proj_stack(current_nest_number)%stdlon)
573 if (diff > 180.) then
575 else if (diff < -180.) then
579 ! Calculate the rotation angle, alpha, in radians
580 if (proj_stack(current_nest_number)%code == PROJ_LC) then
581 alpha = diff * proj_stack(current_nest_number)%cone * &
582 rad_per_deg * proj_stack(current_nest_number)%hemi
584 alpha = diff * rad_per_deg * proj_stack(current_nest_number)%hemi
588 if (bitarray_test(u_mask, i-vs1+1, j-vs2+1)) then
590 if (bitarray_test(v_mask, i-vs1+1, j-vs2+1)) then
596 u_new(i,j) = cos(alpha)*u_map + idir*sin(alpha)*v_map
602 if (bitarray_test(v_mask, i-vs1+1, j-vs2+1)) then
604 if (bitarray_test(u_mask, i-vs1+1, j-vs2+1)) then
610 v_new(i,j) = -idir*sin(alpha)*u_map + cos(alpha)*v_map
618 ! Copy rotated winds back into argument arrays
628 call mprintf(.true.,ERROR,'In metmap_xform_nmm(), uninitialized proj_info structure.')
631 end subroutine metmap_xform_nmm
633 end module rotate_winds_module