!--------------------------------------- LICENCE BEGIN -----------------------------------
!Environment Canada - Atmospheric Science and Technology License/Disclaimer,
! version 3; Last Modified: May 7, 2008.
!This is free but copyrighted software; you can use/redistribute/modify it under the terms
!of the Environment Canada - Atmospheric Science and Technology License/Disclaimer
!version 3 or (at your option) any later version that should be found at:
!http://collaboration.cmc.ec.gc.ca/science/rpn.comm/license.html
!
!This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
!without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
!See the above mentioned License/Disclaimer for more details.
!You should have received a copy of the License/Disclaimer along with this software;
!if not, you can write to: EC-RPN COMM Group, 2121 TransCanada, suite 500, Dorval (Quebec),
!CANADA, H9P 1J3; or send e-mail to service.rpn@ec.gc.ca
!-------------------------------------- LICENCE END --------------------------------------
!--------------------------------------------------------------------------
! > MODULE WindRotation (prefix="uvr")
!
! Purpose: To transform winds FROM the rotated spherical
! coordinate system TO the non-rotated spherical coordinate system.
!
! - Subroutines
! uvr_Setup (public)
!
! - Dependencies
! horizontalcoord_mod
!--------------------------------------------------------------------------
module WindRotation_mod 4,2
use MathPhysConstants_mod
, only: MPC_RADIANS_PER_DEGREE_R8,MPC_DEGREES_PER_RADIAN_R8, MPC_PI_R8
use HorizontalCoord_mod
implicit none
save
private
! Public Subroutines
public :: uvr_Setup, uvr_RotateWind, uvr_RotateLatLon
public :: uvr_RotateWindAdj
integer, parameter :: msize = 3
real(8) :: grd_rot_8 (msize,msize) ! Real Earth to Rotated Earth
real(8) :: grd_rotinv_8(msize,msize) ! Rotated Earth to Real Earth
logical :: initialized = .false.
contains
!--------------------------------------------------------------------------
! uvr_Setup
!--------------------------------------------------------------------------
subroutine uvr_Setup(hco_in) 1,1
implicit none
type(struct_hco), intent(in) :: hco_in
!
!- Compute the rotation matrices (grd_rot_8 and grd_rotinv_8)
!
write(*,*)
write(*,*) 'uvr_Setup: Starting...'
print*,hco_in % xlon1
print*,hco_in % xlat1
print*,hco_in % xlon2
print*,hco_in % xlat2
call sugrdpar
(hco_in % xlon1, hco_in % xlat1, hco_in % xlon2, hco_in % xlat2 )
initialized = .true.
write(*,*)
write(*,*) 'uvr_Setup: Done!'
end subroutine uvr_Setup
!--------------------------------------------------------------------------
! SUGRDPAR
!--------------------------------------------------------------------------
SUBROUTINE sugrdpar(grd_xlon1, grd_xlat1, grd_xlon2, grd_xlat2 ) 1,3
!
!- Compute the rotation matrix (r_8) that allows transformation
! from the non-rotated to the rotated spherical coordinate system.
IMPLICIT NONE
real(8), intent(in) :: grd_xlon1, grd_xlat1, grd_xlon2, grd_xlat2
integer :: ierr, ji, jj, j1, j2
integer :: ii0, ij0, Idum, Imargin
real(8) :: zxlon1_8,zxlat1_8,zxlon2_8,zxlat2_8
real(8) :: a_8, b_8, c_8, d_8, xyz1_8(msize), xyz2_8(msize)
real(8) :: zrot_t(msize,msize), zunit(msize,msize)
zxlon1_8 = grd_xlon1
zxlat1_8 = grd_xlat1
zxlon2_8 = grd_xlon2
zxlat2_8 = grd_xlat2
if ( zxlon1_8 == zxlon2_8 .and. zxlat1_8 == zxlat2_8 ) then
!
!- 1. Non rotated grid (for test case only)
!
write(*,*)
write(*,*) 'uvr_sugrdpar: Warning: This grid is not rotated !!!'
grd_rot_8(1,1) = 1.0d0
grd_rot_8(1,2) = 0.0d0
grd_rot_8(1,3) = 0.0d0
grd_rot_8(2,1) = 0.0d0
grd_rot_8(2,2) = 1.0d0
grd_rot_8(2,3) = 0.0d0
grd_rot_8(3,1) = 0.0d0
grd_rot_8(3,2) = 0.0d0
grd_rot_8(3,3) = 1.0d0
else
!
!- 2. Rotated grid
!
call vllacar
( xyz1_8, zxlon1_8, zxlat1_8 )
call vllacar
( xyz2_8, zxlon2_8, zxlat2_8 )
!- 2.1 Compute a = cos(alpha) & b = sin(alpha)
a_8 = (xyz1_8(1)*xyz2_8(1)) + (xyz1_8(2)*xyz2_8(2)) &
+ (xyz1_8(3)*xyz2_8(3))
b_8 = sqrt (((xyz1_8(2)*xyz2_8(3)) - (xyz2_8(2)*xyz1_8(3)))**2 &
+ ((xyz2_8(1)*xyz1_8(3)) - (xyz1_8(1)*xyz2_8(3)))**2 &
+ ((xyz1_8(1)*xyz2_8(2)) - (xyz2_8(1)*xyz1_8(2)))**2)
!- 2.2 Compute c = norm(-r1) & d = norm(r4)
c_8 = sqrt ( xyz1_8(1)**2 + xyz1_8(2)**2 + xyz1_8(3)**2 )
d_8 = sqrt ( ( ( (a_8*xyz1_8(1)) - xyz2_8(1) ) / b_8 )**2 + &
( ( ( a_8*xyz1_8(2)) - xyz2_8(2) ) / b_8 )**2 + &
( ( ( a_8*xyz1_8(3)) - xyz2_8(3) ) / b_8 )**2 )
!- 2.3 Compute the forward rotation matrix
grd_rot_8(1,1) = -xyz1_8(1) / c_8
grd_rot_8(1,2) = -xyz1_8(2) / c_8
grd_rot_8(1,3) = -xyz1_8(3) / c_8
grd_rot_8(2,1) = ( ((a_8*xyz1_8(1)) - xyz2_8(1)) / b_8 ) / d_8
grd_rot_8(2,2) = ( ((a_8*xyz1_8(2)) - xyz2_8(2)) / b_8 ) / d_8
grd_rot_8(2,3) = ( ((a_8*xyz1_8(3)) - xyz2_8(3)) / b_8 ) / d_8
grd_rot_8(3,1) = ( (xyz1_8(2)*xyz2_8(3)) - (xyz2_8(2)*xyz1_8(3))) / b_8
grd_rot_8(3,2) = ( (xyz2_8(1)*xyz1_8(3)) - (xyz1_8(1)*xyz2_8(3))) / b_8
grd_rot_8(3,3) = ( (xyz1_8(1)*xyz2_8(2)) - (xyz2_8(1)*xyz1_8(2))) / b_8
end if
do j1 = 1, msize
do j2 = 1, msize
write(*,*) 'sugrdpar: grd_rot_8(j1,j2) =',j1,j2,grd_rot_8(j1,j2)
end do
end do
!
!- 3. Compute the inverse matrix (transpose is the inverse)
!
do j1 = 1, msize
do j2 = 1, msize
grd_rotinv_8(j1,j2) = grd_rot_8(j2,j1)
end do
end do
zunit(:,:) = 0.d0
call mxma8x
(zunit,grd_rotinv_8,grd_rot_8,msize,msize,msize)
do j1 = 1, msize
do j2 = 1, msize
write(*,*) 'sugrdpar: unit = ', j1, j2, zunit(j1,j2)
end do
end do
end SUBROUTINE sugrdpar
!--------------------------------------------------------------------------
! VLLACAR
!--------------------------------------------------------------------------
subroutine vllacar(F_xyz_8, F_lon, F_lat) 3
IMPLICIT NONE
real(8), intent(out) :: F_xyz_8(msize)
real(8), intent(in) :: F_lon, F_lat ! In degrees
F_xyz_8(1) = cos(MPC_RADIANS_PER_DEGREE_R8*F_lat) * cos(MPC_RADIANS_PER_DEGREE_R8*F_lon)
F_xyz_8(2) = cos(MPC_RADIANS_PER_DEGREE_R8*F_lat) * sin(MPC_RADIANS_PER_DEGREE_R8*F_lon)
F_xyz_8(3) = sin(MPC_RADIANS_PER_DEGREE_R8*F_lat)
end subroutine vllacar
!--------------------------------------------------------------------------
! MXMA8X
!--------------------------------------------------------------------------
subroutine mxma8x(pmat3,pmat1,pmat2,kdimi1,kdimj1,kdimj2) 1
!
!- Matrix times matrix.
!
IMPLICIT NONE
integer kdimi1,kdimj1,kdimj2
real*8 pmat3(kdimi1,kdimj2)
real*8 pmat1(kdimi1,kdimj1)
real*8 pmat2(kdimj1,kdimj2)
integer ji1,jj2,jj
pmat3(:,:) = 0.d0
do jj2 = 1, kdimj2
do jj = 1, kdimj1
do ji1 = 1, kdimi1
pmat3(ji1,jj2) = pmat3(ji1,jj2) + pmat1(ji1,jj) * pmat2(jj,jj2)
end do
end do
end do
end subroutine mxma8x
!--------------------------------------------------------------------------
! UVR_RotateWind
!--------------------------------------------------------------------------
subroutine uvr_RotateWind( uwind, vwind, Lat, Lon, LatRot, LonRot, mode, & 1,3
nlev )
!
!- Go from tangential wind components from one sphere to another
! (same origin!). Fast version used by Variational analysis.
!
IMPLICIT NONE
integer, intent(in) :: nlev
real(8), intent(in) :: Lat, Lon ! In radians
real(8), intent(in) :: LatRot, LonRot ! In radians
real(8), intent(inout) :: uwind(nlev), vwind(nlev)
character(*), intent(in) :: mode ! ToMetWind or ToRotWind
real(8) :: coslatr, sinlatr, coslonr, sinlonr, coslon, sinlon, rsinlat
real(8), allocatable :: xyz(:,:)
real(8), allocatable :: uvcart(:,:)
if ( .not. initialized ) then
write(*,*)
write(*,*) 'uvr_RotateWind: WindRotation module is not initialize'
stop
endif
allocate(xyz (msize,nlev))
allocate(uvcart(msize,nlev))
coslatr = cos(LatRot)
sinlatr = sin(LatRot)
coslonr = cos(LonRot)
sinlonr = sin(LonRot)
coslon = cos(Lon)
sinlon = sin(Lon)
rsinlat = 1.0d0/sin(Lat)
if ( trim(mode) == 'ToMetWind' ) then
call fuvacart
( xyz, & ! OUT
uwind, vwind, sinlonr, coslonr, & ! IN
sinlatr, coslatr, nlev ) ! IN
call mxm8
( grd_rotinv_8, msize, xyz, msize, & ! IN
uvcart, & ! OUT
nlev ) ! IN
call fcartauv
( uwind, vwind, & ! OUT
uvcart, coslon, sinlon, rsinlat, nlev ) ! IN
else if ( trim(mode) == 'ToRotWind' ) then
write(*,*)
write(*,*) 'uvr_RotateWind: mode ToRotWind is not available yet'
stop
else
write(*,*)
write(*,*) 'uvr_RotateWind: Unknown transform name: ', trim(mode)
write(*,*) ' mode = ToMetWind or ToRotWind'
stop
end if
deallocate(xyz)
deallocate(uvcart)
end subroutine uvr_RotateWind
!--------------------------------------------------------------------------
! UVR_RotateWindAdj
!--------------------------------------------------------------------------
subroutine uvr_RotateWindAdj( uwind, vwind, Lat, Lon, LatRot, LonRot, mode, & 1,3
nlev )
!
!- Adjoint of : Go from tangential wind components from one sphere to another
! (same origin!). Fast version used by Variational analysis.
!
IMPLICIT NONE
integer, intent(in) :: nlev
real(8), intent(in) :: Lat, Lon ! In radians
real(8), intent(in) :: LatRot, LonRot ! In radians
real(8), intent(inout) :: uwind(nlev), vwind(nlev)
character(*), intent(in) :: mode ! ToMetWind or ToRotWind
real(8) :: coslatr, sinlatr, coslonr, sinlonr, coslon, sinlon, rsinlat
real(8), allocatable :: xyz(:,:)
real(8), allocatable :: uvcart(:,:)
if ( .not. initialized ) then
write(*,*)
write(*,*) 'uvr_RotateWindAdj: WindRotation module is not initialize'
stop
endif
allocate(xyz (msize,nlev))
allocate(uvcart(msize,nlev))
coslatr = cos(LatRot)
sinlatr = sin(LatRot)
coslonr = cos(LonRot)
sinlonr = sin(LonRot)
coslon = cos(Lon)
sinlon = sin(Lon)
rsinlat = 1.0d0/sin(Lat)
if ( trim(mode) == 'ToMetWind' ) then
call fcartauvAdj
( uvcart, & ! OUT
uwind, vwind, coslon, sinlon, rsinlat, nlev ) ! IN
call mxm8
( grd_rot_8, msize, uvcart, msize, & ! IN
xyz, & ! OUT
nlev ) ! IN
call fuvacartAdj
( uwind, vwind, & ! OUT
xyz, sinlonr, coslonr, & ! IN
sinlatr, coslatr, nlev ) ! IN
else if ( trim(mode) == 'ToRotWind' ) then
write(*,*)
write(*,*) 'uvr_RotateWindAdj: mode ToRotWind is not available yet'
stop
else
write(*,*)
write(*,*) 'uvr_RotateWindAdj: Unknown transform name: ', trim(mode)
write(*,*) ' mode = ToMetWind or ToRotWind'
stop
end if
deallocate(xyz)
deallocate(uvcart)
end subroutine uvr_RotateWindAdj
!--------------------------------------------------------------------------
! FUVACART
!--------------------------------------------------------------------------
subroutine fuvacart(pcart,pu,pv,psinlon,pcoslon,psinlat,pcoslat,knk) 1
!
!- Compute the winds in the cartesian space from
! the tangential wind vector components.
! Fast version of uvacart special to Var analysis.
!
!arguments
! out pvcart - Cartesian components of the tangential wind vector
! IN pu - Zonal component of the wind on the tangential plane.
! pv - Meridional component of the wind on the tangential plane.
!
IMPLICIT NONE
integer, intent(in) :: knk
real(8), intent(in) :: psinlon,pcoslon,psinlat,pcoslat
real(8), intent(out) :: pcart(msize,knk)
real(8), intent(in) :: pu(knk),pv(knk)
integer :: ji,jj,jk
real(8) :: zfac1,zfac2
zfac1 = pcoslon*psinlat
zfac2 = psinlon*psinlat
do jk = 1, knk
pcart(1,jk) = -(pu(jk)*psinlon) - (pv(jk)*zfac1)
pcart(2,jk) = (pu(jk)*pcoslon) - (pv(jk)*zfac2)
pcart(3,jk) = pv(jk)*pcoslat
end do
end subroutine fuvacart
!--------------------------------------------------------------------------
! FUCACARTAdj
!--------------------------------------------------------------------------
subroutine fuvacartAdj(pu,pv,pcart,psinlonr,pcoslonr,psinlatr,pcoslatr,knk) 1
!
!- Adjoint of fuvacart.
!
IMPLICIT NONE
integer, intent(in) :: knk
real(8), intent(in) :: psinlonr,pcoslonr,psinlatr,pcoslatr
real(8), intent(inout) :: pcart(msize,knk)
real(8), intent(out) :: pu(knk), pv(knk)
integer :: jk
real(8) :: zfac1,zfac2
do jk = 1, knk
pu(jk)=0.0d0 ! dont use sub. zero.ftn here... *4 versus *8 diff...
pv(jk)=0.0d0
end do
zfac1=psinlonr*psinlatr
zfac2=pcoslonr*psinlatr
do jk =1, knk
pu(jk) = pu(jk)+pcart(2,jk)*pcoslonr
pv(jk) = pv(jk)-pcart(2,jk)*zfac1
pcart(2,jk) = 0.0d0
pu(jk) = pu(jk)-pcart(1,jk)*psinlonr
pv(jk) = pv(jk)-pcart(1,jk)*zfac2
pcart(1,jk) = 0.0d0
pv(jk) = pv(jk)+pcart(3,jk)*pcoslatr
pcart(3,jk) = 0.0d0
end do
end subroutine fuvacartAdj
!--------------------------------------------------------------------------
! FCARTAUV
!--------------------------------------------------------------------------
subroutine fcartauv(pu,pv,pcart,pcoslonob,psinlonob,prsinlatob,knk) 1
!
!- Compute the components of the winds in the rotated system of
! coordinates from the winds in the rotated cartesian space
! Fast version of cartauv used by Variational analysis.
!
IMPLICIT NONE
integer, intent(in) :: knk
real(8), intent(in) :: pcart(msize,knk)
real(8), intent(out) :: pu(knk), pv(knk)
real(8), intent(in) :: pcoslonob, psinlonob, prsinlatob
integer :: jk
real(8) :: zfac1, zfac2
zfac1 = -prsinlatob*pcoslonob
zfac2 = -prsinlatob*psinlonob
do jk=1,knk
pu(jk) = -psinlonob*pcart(1,jk) + pcoslonob*pcart(2,jk)
pv(jk) = zfac1*pcart(1,jk) + zfac2*pcart(2,jk)
end do
end subroutine fcartauv
!--------------------------------------------------------------------------
! FCARTAUVAdj
!--------------------------------------------------------------------------
subroutine fcartauvAdj(pcart,pu,pv,pcoslonob,psinlonob,prsinlatob,knk) 1
!
!- Adjoint of cartauv.
!
IMPLICIT NONE
integer, intent(in) :: knk
real(8), intent(in) :: pcoslonob, psinlonob, prsinlatob
real(8), intent(inout) :: pu(knk), pv(knk)
real(8), intent(out) :: pcart(msize,knk)
integer :: jk
real(8) :: zfac1,zfac2
do jk=1,knk
pcart(1,jk)=0.0d0
pcart(2,jk)=0.0d0
pcart(3,jk)=0.0d0
end do
zfac1=prsinlatob*pcoslonob
zfac2=prsinlatob*psinlonob
do jk = 1, knk
pcart(1,jk) = pcart(1,jk)-pv(jk)*zfac1
pcart(2,jk) = pcart(2,jk)-pv(jk)*zfac2
pv(jk) = 0.0d0
pcart(2,jk) = pcart(2,jk)+pu(jk)*pcoslonob
pcart(1,jk) = pcart(1,jk)-pu(jk)*psinlonob
pu(jk) = 0.0d0
end do
end subroutine fcartauvAdj
!--------------------------------------------------------------------------
! MXM8
!--------------------------------------------------------------------------
subroutine mxm8(a,nar,b,nac,c,nbc) 2
implicit none
integer, intent(in) :: nar, nac, nbc
real(8), intent(in) :: a(nar,nac), b(nac,nbc)
real(8), intent(out) :: c(nar,nbc)
integer :: i,j,k
do j = 1, nbc
do i = 1, nar
c(i,j) = 0.0d0
do k = 1, nac
c(i,j) = c(i,j) + a(i,k)*b(k,j)
end do
end do
end do
end subroutine mxm8
!--------------------------------------------------------------------------
! uvr_RotateLatLon
!--------------------------------------------------------------------------
subroutine uvr_RotateLatLon(LatOut,LonOut,LatIn,LonIn,mode) 1,4
!
!- Go from (lat,lon) of one Cartesian frame to (lat,lon) of another
! Cartesian frame given the rotation matrix.
!
IMPLICIT NONE
real(8), intent(in) :: LatIn , LonIn ! In radians
real(8), intent(out) :: LatOut, LonOut ! In radians
character(len=*), intent(in) :: mode
real(8) :: CartIn(msize),CartOut(msize)
real(8) :: rLon,rLat
if ( .not. initialized ) then
write(*,*)
write(*,*) 'uvr_RotateLatLon: WindRotation module is not initialize'
stop
endif
rLon = LonIn * MPC_DEGREES_PER_RADIAN_R8 ! To degress
rLat = LatIn * MPC_DEGREES_PER_RADIAN_R8 ! To degrees
call vllacar
( CartIn, & ! OUT
rLon, rLat ) ! IN
if ( trim(mode) == 'ToLatLonRot' ) then
call mxv
( CartOut, & ! OUT
grd_rot_8, CartIn, & ! IN
msize, msize) ! IN
else if ( trim(mode) == 'ToLatLon' ) then
call mxv
( CartOut, & ! OUT
grd_rotinv_8, CartIn, & ! IN
msize, msize) ! IN
else
write(*,*)
write(*,*) 'uvr_RotateLatLon: Unknown transform name: ', trim(mode)
write(*,*) ' mode = ToLatLonRot or ToLatLon'
stop
end if
call carall
( LonOut, LatOut, & ! OUT
CartOut ) ! IN
LonOut = LonOut * MPC_RADIANS_PER_DEGREE_R8 ! To radians
LatOut = LatOut * MPC_RADIANS_PER_DEGREE_R8 ! To radians
end subroutine uvr_RotateLatLon
!--------------------------------------------------------------------------
! CARALL
!--------------------------------------------------------------------------
subroutine carall(plon,plat,pcart) 1
!
!- Returns (lat,lon) (degrees) of an input Cartesian position vector
! on the unit sphere.
!
IMPLICIT NONE
real(8), intent(out) :: plat, plon
real(8), intent(in) :: pcart(msize)
plat = asin(pcart(3))
plat = plat * MPC_DEGREES_PER_RADIAN_R8
if ( pcart(1) == 0.d0 ) then
if ( pcart(2) == 0.d0 ) then ! point is located at the pole
plon = 0.0d0 ! can be any longitude... set it to zero simply.
else if ( pcart(2) > 0.0d0 ) then
plon = 90.0d0
else if ( pcart(2) < 0.0d0 ) then
plon = 270.0d0
end if
else
plon = atan2(pcart(2),pcart(1))
if (plon < 0.0d0) plon = plon + 2.d0 * MPC_PI_R8
plon = plon * MPC_DEGREES_PER_RADIAN_R8
end if
end subroutine carall
!--------------------------------------------------------------------------
! MXV
!--------------------------------------------------------------------------
subroutine mxv(pvec2,pmat,pvec1,kdimi,kdimj) 2
!
!- Matrix times vector.
!
IMPLICIT NONE
integer :: kdimi, kdimj
real(8) :: pvec2(kdimi), pmat(kdimi,kdimj), pvec1(kdimj)
integer :: ji,jj
pvec2(:) = 0.0d0
do jj = 1,kdimj
do ji = 1,kdimi
pvec2(ji) = pvec2(ji) + pmat(ji,jj) * pvec1(jj)
end do
end do
end subroutine mxv
end module WindRotation_mod