!-------------------------------------- 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 --------------------------------------
!
subroutine oda_LT(statevector,column,columng,lobsSpaceData) 1,30
use MathPhysConstants_mod
use mpivar_mod
use gridStateVector_mod
use obsSpaceData_mod
use columnData_mod
use timeCoord_mod
implicit none
!
! Purpose: Adjoint of Localisation (3D)
! Compute adjoint grided increments from adjoint profiled increments
!
type(struct_columnData) :: column,columng
type(struct_obs) :: lobsSpaceData
type(struct_gsv) :: statevector
integer :: ilev1, ilev2, deltalev, ierr, myLonEndP1, myLatEndP1, status, Vcode_anl
integer :: mythread, numthread, omp_get_num_threads, omp_get_thread_num
type(struct_vco), pointer :: vco_anl
myLonEndP1 = statevector%myLonEnd + 1
myLatEndP1 = min(statevector%myLatEnd + 1, statevector%nj)
!
!- 2. Variable conversions
!
!- 2.2 Rotated wind to Meteorological wind
if ( gsv_varExist
('UU') .and. gsv_varExist
('VV') .and. statevector%hco%Rotated ) then
write(*,*) 'uvrot2uvAdj Active'
call uvrot2uvAdj
('UU', 'VV', col_getNumLev
(column,'MM')) ! IN
end if
!- 2.1 Mass fields (TT,PS,HU) to hydrostatic geopotential
vco_anl => col_getVco
(columng)
status = vgd_get(vco_anl%vgrid,key='ig_1 - vertical coord code',value=Vcode_anl)
call tmg_start(37,'INTERP_ATT2PHI')
if(Vcode_anl .eq. 5001) then
call att2phi
(column,columng)
else
call att2phi_gem4
(column,columng)
endif
call tmg_stop(37)
!
!- 1. Interpolation to obs location
!
if ( mpi_myid == 0 ) THEN
write(*,*) ' ODA_LT - Adjoint of horizontal interpolation GD --> MVO'
endif
!- 1.2 3D fields interpolation
!$OMP PARALLEL PRIVATE(ilev1,ilev2,deltalev,mythread,numthread)
mythread=omp_get_thread_num()+1
numthread=omp_get_num_threads()
deltalev=ceiling(real(col_getNumLev
(column,'MM'))/real(numthread))
ilev1=1+(mythread-1)*deltalev
ilev2=min(col_getNumLev
(column,'MM'),mythread*deltalev)
if(gsv_varExist
('UU')) call gd2mvoad
('UU',ilev1,ilev2)
if(gsv_varExist
('VV')) call gd2mvoad
('VV',ilev1,ilev2)
deltalev=ceiling(real(col_getNumLev
(column,'TH'))/real(numthread))
ilev1=1+(mythread-1)*deltalev
ilev2=min(col_getNumLev
(column,'TH'),mythread*deltalev)
if(gsv_varExist
('HU')) call gd2mvoad
('HU',ilev1,ilev2)
if(gsv_varExist
('TT')) call gd2mvoad
('TT',ilev1,ilev2)
!$OMP END PARALLEL
!- 1.3 2D fields interpolation
ilev1=1
ilev2=1
if(gsv_varExist
('P0')) call gd2mvoad
('P0',ilev1,ilev2)
if(gsv_varExist
('TG')) call gd2mvoad
('TG',ilev1,ilev2)
!- 1.1 Communicate extra latitude needed for interpolation
call tmg_start(38,'INTERP_BARR')
call rpn_comm_barrier("GRID",ierr)
call tmg_stop(38)
call tmg_start(39,'INTERP_COMM')
if (mpi_nprocs /= 1) call gsv_commLatLonAd
(statevector)
call tmg_stop(39)
CONTAINS
!--------------------------------------------------------------------------
! GD2MVOAD
!--------------------------------------------------------------------------
SUBROUTINE GD2MVOAD (varName,klev1,klev2) 6,8
!*
!***s/r GD2MVOAD - Adjoint of the horizontal interpolation of the model variables
!* in grid-point space to observation locations.
!* Simple multiplication by the weights of
!* the horizontal bilinear interpolation.
!* (adapted from BILINAD)
!*
!** Purpose: Update the estimate of GD from the gradient components
!* at the observation points which have been stored in
!* column
!*
!* Arguments
!* VARNAME: identificator of the variable to be treated
!* KLEV : number of levels
implicit none
character(len=*), intent(in) :: varName
integer, intent(in) :: klev1,klev2
INTEGER JLEV, JLAT, JLON, headerIndex, JSTEP, JK
INTEGER ILON, ILAT, ILO
REAL*8 DLDX, DLDY, DLW1, DLW2, DLW3, DLW4, DInterpWeight
real(8) :: xpos, ypos, lat, lon, latrot, lonrot
real*8, pointer :: field_ptr(:,:,:,:)
real*8, pointer :: column_ptr(:,:)
if (col_getNumCol
(column) > 0) column_ptr => col_getAllColumns
(column,varName)
field_ptr => gsv_getField
(statevector,varName)
! Note: We assume here the all the obs between the poles and the last grid points
! (i.e. outside the grid) have been moved within the grid previously
do jstep = 1, statevector%numStep
field_ptr(:,klev1:klev2,:,jstep) = 0.0D0
!
!- 2. LOOP OVER ALL THE OBSERVATIONS
!
DO headerIndex = 1, col_getNumCol
(column)
if ( tim_getTimeInterpWeight
(headerIndex,jstep) > 0.0d0 .and. &
.not.btest(obs_headElem_i
(lobsSpaceData,OBS_ST1,headerIndex),5) ) then
!- 2.1 Find the obs position within the analysis grid
call col_getLatLon
( column, headerIndex, & ! IN
Lat, Lon, ypos, xpos, LatRot, LonRot ) ! OUT
!- Make sure we are within bounds
if ( ypos < real(statevector%myLatBeg,8) .or. &
ypos > real(myLatEndP1 ,8) .or. &
xpos < real(statevector%myLonBeg,8) .or. &
xpos > real(myLonEndP1 ,8) ) then
write(*,*) 'oda_LT: Obs outside local domain for job = ', headerIndex
write(*,*) ' obs x, y position = ', xpos, ypos
write(*,*) ' domain x_start, x_end, y_start, y_end bounds = ', &
statevector%myLonBeg, myLonEndP1, statevector%myLatBeg, myLatEndP1
! if obs above or below latitude band, move it to the edge of this latitude band
if( ypos < real(statevector%myLatBeg,8) ) ypos = real(statevector%myLatBeg,8)
if( ypos > real(myLatEndP1 ,8) ) ypos = real(myLatEndP1 ,8)
! abort if obs is to the left or right of the analysis domain
if( xpos < real(statevector%myLonBeg,8) ) xpos = real(statevector%myLonBeg,8)
if( xpos > real(myLonEndP1 ,8) ) xpos = real(myLonEndP1 ,8)
end if
!- 2.2 Find the lower-left grid point next to the observation
if ( xpos /= real(myLonEndP1,8) ) then
ILON = floor(xpos)
else
ILON = floor(xpos) - 1
end if
if ( ypos /= real(myLatEndP1,8) ) then
ILAT = floor(ypos)
else
ILAT = floor(ypos) - 1
end if
!- 2.3 COMPUTE THE 4 WEIGHTS OF THE BILINEAR INTERPOLATION
DLDX = xpos - real(ILON,8)
DLDY = ypos - real(ILAT,8)
DInterpWeight = tim_getTimeInterpWeight
(headerIndex,jstep)
DLW1 = DInterpWeight * (1.d0-DLDX) * (1.d0-DLDY)
DLW2 = DInterpWeight * DLDX * (1.d0-DLDY)
DLW3 = DInterpWeight * (1.d0-DLDX) * DLDY
DLW4 = DInterpWeight * DLDX * DLDY
!- 2.4 Interpolate the model state to the obs point
do jlev = klev1, klev2
field_ptr(ILON ,jlev,ILAT, jstep) = field_ptr(ILON ,jlev,ILAT, jstep) &
+ DLW1 * column_ptr(jlev,headerIndex)
field_ptr(ILON+1,jlev,ILAT, jstep) = field_ptr(ILON+1,jlev,ILAT, jstep) &
+ DLW2 * column_ptr(jlev,headerIndex)
field_ptr(ILON ,jlev,ILAT+1,jstep) = field_ptr(ILON ,jlev,ILAT+1,jstep) &
+ DLW3 * column_ptr(jlev,headerIndex)
field_ptr(ILON+1,jlev,ILAT+1,jstep) = field_ptr(ILON+1,jlev,ILAT+1,jstep) &
+ DLW4 * column_ptr(jlev,headerIndex)
end do
end if
end do ! Obs
end do ! jstep
END SUBROUTINE GD2MVOAD
!--------------------------------------------------------------------------
! UVROT2UVADJ
!--------------------------------------------------------------------------
subroutine uvrot2uvAdj(UUvarName,VVvarName,klev) 1,6
!
!- uvrot2uv - Transforms tangential (U,V) wind components at observation
! locations on GEM rotated frame to the real sphere.
use WindRotation_mod
implicit none
character(len=*), intent(in) :: UUvarName
character(len=*), intent(in) :: VVvarName
integer, intent(in) :: klev
real(8) :: lat, lon, latrot, lonrot, xpos, ypos
real(8), pointer :: UUcolumn(:), VVcolumn(:)
integer :: headerIndex
!
!- 1. Loop over all the observation locations
!
do headerIndex = 1, col_getNumCol
(column)
!- 1.1 Extract (rotated) wind profiles
UUColumn => col_getColumn
(column,headerIndex,UUvarName)
VVColumn => col_getColumn
(column,headerIndex,VVvarName)
!- 1.2 Find the latitudes and longitudes
call col_getLatLon
( column, headerIndex, & ! IN
Lat, Lon, ypos, xpos, LatRot, LonRot ) ! OUT
!- 1.3 Rotate Winds
call uvr_RotateWindAdj
( UUColumn, VVColumn, & ! INOUT
Lat, Lon, LatRot, LonRot, & ! IN
'ToMetWind', klev ) ! IN
end do
end subroutine uvrot2uvAdj
end subroutine oda_LT