1module windRotation_mod
2 ! MODULE windRotation_mod (prefix='uvr' category='4. Data Object transformations')
3 !
4 !:Purpose: To transform winds FROM the rotated spherical coordinate system
5 ! TO the non-rotated spherical coordinate system. Also includes
6 ! tangent-linear and adjoint versions of the transformation.
7 !
8 use mathPhysConstants_mod
9 use horizontalCoord_mod
10 use utilities_mod
11 use midasMpi_mod
12 implicit none
13 save
14 private
15
16 ! Public derived type definition
17 public :: struct_uvr
18
19 ! Public Subroutines
20 public :: uvr_setup, uvr_rotateWind_nl, uvr_rotateWind_tl, uvr_rotateWind_ad, uvr_rotateLatLon
21
22 integer, parameter :: msize = 3
23 integer, parameter :: maxNumSubGrid = 2
24
25 type :: struct_uvr
26 real(8) :: grd_rot_8 (msize,msize,maxNumSubGrid) ! Real Earth to Rotated Earth
27 real(8) :: grd_rotinv_8(msize,msize,maxNumSubGrid) ! Rotated Earth to Real Earth
28 logical :: initialized = .false.
29 end type struct_uvr
30
31 ! Minimum value for latitude used in TL/AD wind rotation routines
32 real(8), parameter :: lat_minValue = 1D-10
33
34 contains
35
36 subroutine uvr_setup( uvr, hco_in )
37 !
38 !:Purpose: Setup the information for wind rotation
39 !
40 implicit none
41
42 ! Arguments:
43 type(struct_uvr), pointer, intent(inout) :: uvr ! Wind rotation object
44 type(struct_hco), intent(in) :: hco_in ! Horizontal grid object
45
46 ! Locals:
47 logical, save :: firstCall = .true.
48
49 if ( associated(uvr) ) then
50 if ( uvr%initialized ) then
51 if ( mmpi_myid == 0 ) write(*,*) 'uvr_setup: already initialized, returning'
52 return
53 else
54 call utl_abort('uvr_setup: the supplied non-null uvr pointer is not initialized!')
55 end if
56 end if
57
58 !
59 !- Compute the rotation matrices (grd_rot_8 and grd_rotinv_8)
60 !
61 if ( mmpi_myid == 0 ) then
62 write(*,*)
63 write(*,*) 'uvr_setup: Starting... for grid type = ', hco_in%grtyp
64 end if
65
66 allocate(uvr)
67
68 if ( mmpi_myid == 0 .and. firstCall ) then
69 write(*,*) hco_in % xlon1
70 write(*,*) hco_in % xlat1
71 write(*,*) hco_in % xlon2
72 write(*,*) hco_in % xlat2
73 end if
74
75 call sugrdpar(uvr, 1, hco_in % xlon1, hco_in % xlat1, hco_in % xlon2, hco_in % xlat2 )
76
77 if ( hco_in%grtyp == 'U' ) then
78
79 if ( mmpi_myid == 0 .and. firstCall ) then
80 write(*,*) 'uvr_setup: doing setup for YAN grid'
81 write(*,*) hco_in % xlon1_yan
82 write(*,*) hco_in % xlat1_yan
83 write(*,*) hco_in % xlon2_yan
84 write(*,*) hco_in % xlat2_yan
85 end if
86
87 call sugrdpar(uvr, 2, hco_in % xlon1_yan, hco_in % xlat1_yan, hco_in % xlon2_yan, hco_in % xlat2_yan )
88
89 end if
90
91 uvr%initialized = .true.
92
93 if ( mmpi_myid == 0 ) then
94 write(*,*)
95 write(*,*) 'uvr_setup: Done!'
96 end if
97
98 firstCall = .false.
99
100 end subroutine uvr_setup
101
102 subroutine sugrdpar( uvr, subGridIndex, grd_xlon1, grd_xlat1, grd_xlon2, grd_xlat2 )
103 !
104 !:Purpose: Compute the rotation matrix (r_8) that allows transformation
105 ! from the non-rotated to the rotated spherical coordinate system.
106 !
107 implicit none
108
109 ! Arguments:
110 type(struct_uvr), pointer, intent(inout) :: uvr ! Wind rotation object
111 integer, intent(in) :: subGridIndex ! Horizontal subGrid index = 2
112 real(8), intent(in) :: grd_xlon1 ! Horizontal grid xlon1_yan
113 real(8), intent(in) :: grd_xlat1 ! Horizontal grid xlat1_yan
114 real(8), intent(in) :: grd_xlon2 ! Horizontal grid xlon2_yan
115 real(8), intent(in) :: grd_xlat2 ! Horizontal grid xlat2_yan
116
117 ! Locals:
118 integer :: j1, j2
119 real(8) :: zxlon1_8,zxlat1_8,zxlon2_8,zxlat2_8
120 real(8) :: a_8, b_8, c_8, d_8, xyz1_8(msize), xyz2_8(msize)
121 real(8) :: zunit(msize,msize)
122 logical, save :: firstCall = .true.
123
124 zxlon1_8 = grd_xlon1
125 zxlat1_8 = grd_xlat1
126 zxlon2_8 = grd_xlon2
127 zxlat2_8 = grd_xlat2
128
129 if ( zxlon1_8 == zxlon2_8 .and. zxlat1_8 == zxlat2_8 ) then
130 !
131 !- 1. Non rotated grid (for test case only)
132 !
133 if ( mmpi_myid == 0 ) then
134 write(*,*)
135 write(*,*) 'uvr_sugrdpar: Warning: This grid is not rotated !!!'
136 end if
137 uvr%grd_rot_8(1,1,subGridIndex) = 1.0d0
138 uvr%grd_rot_8(1,2,subGridIndex) = 0.0d0
139 uvr%grd_rot_8(1,3,subGridIndex) = 0.0d0
140 uvr%grd_rot_8(2,1,subGridIndex) = 0.0d0
141 uvr%grd_rot_8(2,2,subGridIndex) = 1.0d0
142 uvr%grd_rot_8(2,3,subGridIndex) = 0.0d0
143 uvr%grd_rot_8(3,1,subGridIndex) = 0.0d0
144 uvr%grd_rot_8(3,2,subGridIndex) = 0.0d0
145 uvr%grd_rot_8(3,3,subGridIndex) = 1.0d0
146
147 else
148 !
149 !- 2. Rotated grid
150 !
151 call vllacar( xyz1_8, zxlon1_8, zxlat1_8 )
152 call vllacar( xyz2_8, zxlon2_8, zxlat2_8 )
153
154 !- 2.1 Compute a = cos(alpha) & b = sin(alpha)
155 a_8 = (xyz1_8(1)*xyz2_8(1)) + (xyz1_8(2)*xyz2_8(2)) &
156 + (xyz1_8(3)*xyz2_8(3))
157
158 b_8 = sqrt (((xyz1_8(2)*xyz2_8(3)) - (xyz2_8(2)*xyz1_8(3)))**2 &
159 + ((xyz2_8(1)*xyz1_8(3)) - (xyz1_8(1)*xyz2_8(3)))**2 &
160 + ((xyz1_8(1)*xyz2_8(2)) - (xyz2_8(1)*xyz1_8(2)))**2)
161
162 !- 2.2 Compute c = norm(-r1) & d = norm(r4)
163 c_8 = sqrt ( xyz1_8(1)**2 + xyz1_8(2)**2 + xyz1_8(3)**2 )
164
165 d_8 = sqrt ( ( ( (a_8*xyz1_8(1)) - xyz2_8(1) ) / b_8 )**2 + &
166 ( ( ( a_8*xyz1_8(2)) - xyz2_8(2) ) / b_8 )**2 + &
167 ( ( ( a_8*xyz1_8(3)) - xyz2_8(3) ) / b_8 )**2 )
168
169 !- 2.3 Compute the forward rotation matrix
170 uvr%grd_rot_8(1,1,subGridIndex) = -xyz1_8(1) / c_8
171 uvr%grd_rot_8(1,2,subGridIndex) = -xyz1_8(2) / c_8
172 uvr%grd_rot_8(1,3,subGridIndex) = -xyz1_8(3) / c_8
173 uvr%grd_rot_8(2,1,subGridIndex) = ( ((a_8*xyz1_8(1)) - xyz2_8(1)) / b_8 ) / d_8
174 uvr%grd_rot_8(2,2,subGridIndex) = ( ((a_8*xyz1_8(2)) - xyz2_8(2)) / b_8 ) / d_8
175 uvr%grd_rot_8(2,3,subGridIndex) = ( ((a_8*xyz1_8(3)) - xyz2_8(3)) / b_8 ) / d_8
176 uvr%grd_rot_8(3,1,subGridIndex) = ( (xyz1_8(2)*xyz2_8(3)) - (xyz2_8(2)*xyz1_8(3))) / b_8
177 uvr%grd_rot_8(3,2,subGridIndex) = ( (xyz2_8(1)*xyz1_8(3)) - (xyz1_8(1)*xyz2_8(3))) / b_8
178 uvr%grd_rot_8(3,3,subGridIndex) = ( (xyz1_8(1)*xyz2_8(2)) - (xyz2_8(1)*xyz1_8(2))) / b_8
179
180 end if
181
182 do j1 = 1, msize
183 do j2 = 1, msize
184 if ( mmpi_myid == 0 .and. firstCall ) then
185 write(*,*) 'sugrdpar: grd_rot_8(j1,j2) =',j1,j2,uvr%grd_rot_8(j1,j2,subGridIndex)
186 end if
187 end do
188 end do
189
190 !
191 !- 3. Compute the inverse matrix (transpose is the inverse)
192 !
193 do j1 = 1, msize
194 do j2 = 1, msize
195 uvr%grd_rotinv_8(j1,j2,subGridIndex) = uvr%grd_rot_8(j2,j1,subGridIndex)
196 end do
197 end do
198
199 zunit(:,:) = 0.d0
200 call mxma8x(zunit,uvr%grd_rotinv_8(:,:,subGridIndex),uvr%grd_rot_8(:,:,subGridIndex),msize,msize,msize)
201 do j1 = 1, msize
202 do j2 = 1, msize
203 if ( mmpi_myid == 0 .and. firstCall ) then
204 write(*,*) 'sugrdpar: unit = ', j1, j2, zunit(j1,j2)
205 end if
206 end do
207 end do
208
209 firstCall = .false.
210
211 end subroutine sugrdpar
212
213 subroutine vllacar( F_xyz_8, F_lon, F_lat )
214 !
215 !:Purpose: Compute parameters of rotated grid
216 !
217 implicit none
218
219 ! Arguments:
220 real(8), intent(out) :: F_xyz_8(msize) ! output
221 real(8), intent(in) :: F_lon ! Input in degrees
222 real(8), intent(in) :: F_lat ! Input in degrees
223
224 F_xyz_8(1) = cos(MPC_RADIANS_PER_DEGREE_R8*F_lat) * cos(MPC_RADIANS_PER_DEGREE_R8*F_lon)
225 F_xyz_8(2) = cos(MPC_RADIANS_PER_DEGREE_R8*F_lat) * sin(MPC_RADIANS_PER_DEGREE_R8*F_lon)
226 F_xyz_8(3) = sin(MPC_RADIANS_PER_DEGREE_R8*F_lat)
227
228 end subroutine vllacar
229
230 subroutine mxma8x( pmat3, pmat1, pmat2, kdimi1, kdimj1, kdimj2 )
231 !
232 !:Purpose: Compute a product of two matrices.
233 !
234 implicit none
235
236 ! Arguments:
237 real(8), intent(out) :: pmat3(kdimi1,kdimj2) ! output
238 real(8), intent(in) :: pmat1(kdimi1,kdimj1) ! input matrix one
239 real(8), intent(in) :: pmat2(kdimj1,kdimj2) ! input matrix two
240 integer, intent(in) :: kdimi1 ! first dimension of the first matrix
241 integer, intent(in) :: kdimj1 ! second dimension of the first matrix
242 integer, intent(in) :: kdimj2 ! second dimension of the second matrix
243
244 ! Locals:
245 integer :: ji1,jj2,jj
246
247 pmat3(:,:) = 0.d0
248
249 do jj2 = 1, kdimj2
250 do jj = 1, kdimj1
251 do ji1 = 1, kdimi1
252 pmat3(ji1,jj2) = pmat3(ji1,jj2) + pmat1(ji1,jj) * pmat2(jj,jj2)
253 end do
254 end do
255 end do
256
257 end subroutine mxma8x
258
259 subroutine uvr_rotateWind_nl( uvr, subGridIndex, uwind, vwind, Lat, Lon, LatRot, LonRot, mode )
260 !
261 !:Purpose: Go from tangential wind components from one sphere to another
262 ! (same origin!). Original ezsint version used for computing innovation.
263 !
264 implicit none
265
266 ! Arguments:
267 type(struct_uvr), pointer, intent(in) :: uvr ! Wind rotation object
268 integer, intent(in) :: subGridIndex ! Current subgrid index
269 real(8), intent(inout) :: uwind ! interpUU
270 real(8), intent(inout) :: vwind ! interpVV
271 real(8), intent(in) :: Lat ! Latitude in radians
272 real(8), intent(in) :: Lon ! Longitude in radians
273 real(8), intent(in) :: LatRot ! Rotated latitude in radians
274 real(8), intent(in) :: LonRot ! Rotated longitude in radians
275 character(*), intent(in) :: mode ! ToMetWind or ToRotWind
276
277 ! Locals:
278 integer :: index1, index2
279 real(8) :: coslatr, sinlatr, coslonr, sinlonr, coslat, sinlat, coslon, sinlon, ezCoeff_C, ezCoeff_D
280 real(8) :: xyz(msize), uvcart(msize)
281
282 if ( .not. uvr%initialized ) then
283 write(*,*)
284 call utl_abort('uvr_rotateWind_nl: WindRotation module is not initialize')
285 endif
286
287 coslatr = cos(LatRot)
288 sinlatr = sin(LatRot)
289 coslonr = cos(LonRot)
290 sinlonr = sin(LonRot)
291 coslat = cos(Lat)
292 sinlat = sin(Lat)
293 coslon = cos(Lon)
294 sinlon = sin(Lon)
295
296 if ( trim(mode) == 'ToMetWind' ) then
297
298 xyz(1) = -uwind*sinlonr - vwind*coslonr*sinlatr
299 xyz(2) = uwind*coslonr - vwind*sinlonr*sinlatr
300 xyz(3) = vwind*coslatr
301
302 uvcart(:) = 0.0d0
303 do index2 = 1, msize
304 do index1 = 1, msize
305 uvcart(index1) = uvcart(index1) + &
306 uvr%grd_rotinv_8(index1,index2,subGridIndex)*xyz(index2)
307 end do
308 end do
309
310 uwind = uvcart(2)*coslon - uvcart(1)*sinlon
311 ezCoeff_C = uvcart(1)*coslon + uvcart(2)*sinlon
312 ezCoeff_D = sqrt( ezCoeff_C**2 + uvcart(3)**2 )
313 vwind = sign( ezCoeff_D, uvcart(3)*coslat - ezCoeff_C*sinlat )
314
315 else if ( trim(mode) == 'ToRotWind' ) then
316 write(*,*)
317 call utl_abort('uvr_rotateWind_nl: mode ToRotWind is not available yet')
318 else
319 write(*,*)
320 write(*,*) 'uvr_rotateWind_nl: Unknown transform name: ', trim(mode)
321 write(*,*) ' mode = ToMetWind or ToRotWind'
322 call utl_abort('uvr_rotateWind_nl')
323 end if
324
325 end subroutine uvr_rotateWind_nl
326
327 subroutine uvr_rotateWind_tl( uvr, subGridIndex, uwind, vwind, Lat_in, Lon_in, LatRot_in, LonRot_in, mode )
328 !
329 !:Purpose: Go from tangential wind components from one sphere to another
330 ! (same origin!). Fast version used by Variational analysis.
331 !
332 implicit none
333
334 ! Arguments:
335 type(struct_uvr), pointer, intent(in) :: uvr ! Wind rotation object
336 integer, intent(in) :: subGridIndex ! Current subgrid index
337 real(8), intent(inout) :: uwind ! interpUU
338 real(8), intent(inout) :: vwind ! interpVV
339 real(8), intent(in) :: Lat_in ! Latitude in radians
340 real(8), intent(in) :: Lon_in ! Longitude in radians
341 real(8), intent(in) :: LatRot_in ! Rotated latitude in radians
342 real(8), intent(in) :: LonRot_in ! Rotated longitude in radians
343 character(*), intent(in) :: mode ! ToMetWind or ToRotWind
344
345 ! Locals:
346 integer :: index1, index2
347 real(8) :: coslatr, sinlatr, coslonr, sinlonr, coslon, sinlon, rsinlat
348 real(8) :: lat, lon, latRot, lonRot
349 real(8) :: xyz(msize), uvcart(msize)
350
351 if ( .not. uvr%initialized ) then
352 write(*,*)
353 call utl_abort('uvr_rotateWind_tl: WindRotation module is not initialize')
354 endif
355
356 ! Special case for the equator
357 if (abs(lat_in) < lat_minValue) then
358 lat = lat_minValue
359 lon = lon_in
360 call uvr_RotateLatLon( uvr, & ! INOUT
361 subGridIndex, & ! IN
362 latRot, lonRot, & ! OUT (radians)
363 lat, lon, & ! IN (radians)
364 'ToLatLonRot') ! IN
365 else
366 lat = lat_in
367 lon = lon_in
368 latRot = latRot_in
369 lonRot = lonRot_in
370 end if
371
372 coslatr = cos(LatRot)
373 sinlatr = sin(LatRot)
374 coslonr = cos(LonRot)
375 sinlonr = sin(LonRot)
376 coslon = cos(Lon)
377 sinlon = sin(Lon)
378 if ( Lat /= 0.0d0 ) then
379 rsinlat = 1.0d0/sin(Lat)
380 else
381 call utl_abort('uvr_rotateWind_tl: cannot be used for points on the equator')
382 end if
383
384 if ( trim(mode) == 'ToMetWind' ) then
385
386 xyz(1) = -uwind*sinlonr - vwind*coslonr*sinlatr
387 xyz(2) = uwind*coslonr - vwind*sinlonr*sinlatr
388 xyz(3) = vwind*coslatr
389
390 uvcart(:) = 0.0d0
391 do index2 = 1, msize
392 do index1 = 1, msize
393 uvcart(index1) = uvcart(index1) + &
394 uvr%grd_rotinv_8(index1,index2,subGridIndex)*xyz(index2)
395 end do
396 end do
397
398 uwind = -uvcart(1)*sinlon + uvcart(2)*coslon
399 vwind = -uvcart(1)*rsinlat*coslon - uvcart(2)*rsinlat*sinlon
400
401 else if ( trim(mode) == 'ToRotWind' ) then
402 write(*,*)
403 call utl_abort('uvr_rotateWind_tl: mode ToRotWind is not available yet')
404 else
405 write(*,*)
406 write(*,*) 'uvr_rotateWind_tl: Unknown transform name: ', trim(mode)
407 write(*,*) ' mode = ToMetWind or ToRotWind'
408 call utl_abort('uvr_rotateWind_tl')
409 end if
410
411 end subroutine uvr_rotateWind_tl
412
413 subroutine uvr_rotateWind_ad( uvr, subGridIndex, uwind, vwind, Lat_in, Lon_in, LatRot_in, LonRot_in, mode )
414 !
415 !:Purpose: Adjoint of : Go from tangential wind components from one sphere to another
416 ! (same origin!). Fast version used by Variational analysis.
417 !
418 implicit none
419
420 ! Arguments:
421 type(struct_uvr), pointer, intent(in) :: uvr ! Wind rotation object
422 integer, intent(in) :: subGridIndex ! Current subgrid index
423 real(8), intent(inout) :: uwind ! interpUU
424 real(8), intent(inout) :: vwind ! interpVV
425 real(8), intent(in) :: Lat_in ! Latitude in radians
426 real(8), intent(in) :: Lon_in ! Longitude in radians
427 real(8), intent(in) :: LatRot_in ! Rotated latitude in radians
428 real(8), intent(in) :: LonRot_in ! Rotated longitude in radians
429 character(*), intent(in) :: mode ! ToMetWind or ToRotWind
430
431 ! Locals:
432 integer :: index1, index2
433 real(8) :: coslatr, sinlatr, coslonr, sinlonr, coslon, sinlon, rsinlat
434 real(8) :: lat, lon, latRot, lonRot
435 real(8) :: xyz(msize), uvcart(msize)
436
437 if ( .not. uvr%initialized ) then
438 write(*,*)
439 call utl_abort('uvr_rotateWind_ad: WindRotation module is not initialize')
440 endif
441
442 ! Special case for the equator
443 if (abs(lat_in) < lat_minValue) then
444 lat = lat_minValue
445 lon = lon_in
446 call uvr_RotateLatLon( uvr, & ! INOUT
447 subGridIndex, & ! IN
448 latRot, lonRot, & ! OUT (radians)
449 lat, lon, & ! IN (radians)
450 'ToLatLonRot') ! IN
451 else
452 lat = lat_in
453 lon = lon_in
454 latRot = latRot_in
455 lonRot = lonRot_in
456 end if
457
458 coslatr = cos(LatRot)
459 sinlatr = sin(LatRot)
460 coslonr = cos(LonRot)
461 sinlonr = sin(LonRot)
462 coslon = cos(Lon)
463 sinlon = sin(Lon)
464 if ( Lat /= 0.0d0 ) then
465 rsinlat = 1.0d0/sin(Lat)
466 else
467 call utl_abort('uvr_rotateWind_ad: cannot be used for points on the equator')
468 end if
469
470 if ( trim(mode) == 'ToMetWind' ) then
471
472 uvcart(1) = -uwind*sinlon - vwind*rsinlat*coslon
473 uvcart(2) = uwind*coslon - vwind*rsinlat*sinlon
474 uvcart(3) = 0.0d0
475
476 xyz(:) = 0.0d0
477 do index2 = 1, msize
478 do index1 = 1, msize
479 xyz(index1) = xyz(index1) + uvr%grd_rot_8(index1,index2,subGridIndex)*uvcart(index2)
480 end do
481 end do
482
483 uwind = -xyz(1)*sinlonr + xyz(2)*coslonr
484 vwind = -xyz(1)*coslonr*sinlatr - xyz(2)*sinlonr*sinlatr + xyz(3)*coslatr
485
486 else if ( trim(mode) == 'ToRotWind' ) then
487 write(*,*)
488 call utl_abort('uvr_rotateWind_ad: mode ToRotWind is not available yet')
489 else
490 write(*,*)
491 write(*,*) 'uvr_rotateWind_ad: Unknown transform name: ', trim(mode)
492 write(*,*) ' mode = ToMetWind or ToRotWind'
493 call utl_abort('uvr_rotateWind_ad')
494 end if
495
496 end subroutine uvr_rotateWind_ad
497
498 subroutine uvr_rotateLatLon( uvr, subGridIndex, LatOut, LonOut, LatIn, LonIn, mode )
499 !
500 !:Purpose: Go from (lat,lon) of one Cartesian frame to (lat,lon) of another
501 ! Cartesian frame given the rotation matrix.
502 !
503 implicit none
504
505 ! Arguments:
506 type(struct_uvr), pointer, intent(in) :: uvr ! Wind rotation object
507 integer, intent(in) :: subGridIndex ! Current subgrid index
508 real(8), intent(in) :: LatIn ! Input latitude in radians
509 real(8), intent(in) :: LonIn ! Input longitude in radians
510 real(8), intent(out) :: LatOut ! Output latitude in radians
511 real(8), intent(out) :: LonOut ! Output longitude in radians
512 character(*), intent(in) :: mode ! ToLatLonRot or ToLatLon
513
514 ! Locals:
515 real(8) :: CartIn(msize),CartOut(msize)
516 real(8) :: rLon,rLat
517
518 if ( .not. uvr%initialized ) then
519 write(*,*)
520 call utl_abort('uvr_rotateLatLon: WindRotation module is not initialize')
521 endif
522
523 rLon = LonIn * MPC_DEGREES_PER_RADIAN_R8 ! To degress
524 rLat = LatIn * MPC_DEGREES_PER_RADIAN_R8 ! To degrees
525
526 call vllacar( CartIn, & ! OUT
527 rLon, rLat ) ! IN
528
529 if ( trim(mode) == 'ToLatLonRot' ) then
530 call mxv( CartOut, & ! OUT
531 uvr%grd_rot_8(:,:,subGridIndex), CartIn, & ! IN
532 msize, msize) ! IN
533 else if ( trim(mode) == 'ToLatLon' ) then
534 call mxv( CartOut, & ! OUT
535 uvr%grd_rotinv_8(:,:,subGridIndex), CartIn, & ! IN
536 msize, msize) ! IN
537 else
538 write(*,*)
539 write(*,*) 'uvr_rotateLatLon: Unknown transform name: ', trim(mode)
540 write(*,*) ' mode = ToLatLonRot or ToLatLon'
541 call utl_abort('uvr_rotateLatLon')
542 end if
543
544 call carall( LonOut, LatOut, & ! OUT
545 CartOut ) ! IN
546
547 LonOut = LonOut * MPC_RADIANS_PER_DEGREE_R8 ! To radians
548 LatOut = LatOut * MPC_RADIANS_PER_DEGREE_R8 ! To radians
549
550 end subroutine uvr_rotateLatLon
551
552
553 subroutine carall( plon, plat, pcart )
554 !
555 !:Purpose: Returns (lat,lon) (degrees) of an input Cartesian position vector
556 ! on the unit sphere.
557 !
558 implicit none
559
560 ! Arguments:
561 real(8), intent(out) :: plat ! output latitude
562 real(8), intent(out) :: plon ! output longitude
563 real(8), intent(in) :: pcart(msize) ! input Cartesian vector
564
565 plat = asin(pcart(3))
566 plat = plat * MPC_DEGREES_PER_RADIAN_R8
567
568 if ( pcart(1) == 0.d0 ) then
569 if ( pcart(2) == 0.d0 ) then ! point is located at the pole
570 plon = 0.0d0 ! can be any longitude... set it to zero simply.
571 else if ( pcart(2) > 0.0d0 ) then
572 plon = 90.0d0
573 else if ( pcart(2) < 0.0d0 ) then
574 plon = 270.0d0
575 end if
576 else
577 plon = atan2(pcart(2),pcart(1))
578 if (plon < 0.0d0) plon = plon + 2.d0 * MPC_PI_R8
579 plon = plon * MPC_DEGREES_PER_RADIAN_R8
580 end if
581
582 end subroutine carall
583
584 subroutine mxv( pvec2, pmat, pvec1, kdimi, kdimj )
585 !
586 !:Purpose: Compute a product : matrix times vector.
587 !
588 implicit none
589
590 ! Arguments:
591 real(8), intent(out) :: pvec2(kdimi) ! output vector
592 real(8), intent(in) :: pmat(kdimi,kdimj) ! input matrix
593 real(8), intent(in) :: pvec1(kdimj) ! input vector
594 integer, intent(in) :: kdimi ! first dimension
595 integer, intent(in) :: kdimj ! second dimension
596
597 ! Locals:
598 integer :: ji,jj
599
600 pvec2(:) = 0.0d0
601 do jj = 1,kdimj
602 do ji = 1,kdimi
603 pvec2(ji) = pvec2(ji) + pmat(ji,jj) * pvec1(jj)
604 end do
605 end do
606
607 end subroutine mxv
608
609end module windRotation_mod