subroutine general_g2s0(grd,sp,spectral_out,grid_in) !$$$ subprogram documentation block ! . . . . ! subprogram: g2s0 grid to spectral ! prgmmr: kleist org: np23 date: 2006-07-15 ! ! abstract: transform scalar from gaussian grid to spherical harmonic coefficients. ! This works for equally spaced grid also ! ! program history log: ! 2006-07-15 kleist ! 2010-02-18 parrish, copy g2s0 to general_g2s0, and replace 'use specmod' with input structure var sp. ! also replace 'use gridmod' with input structure variable grd ! ! input argument list: ! grid_in - input grid field on gaussian grid ! ! output argument list: ! spectral_out - output spherical harmonic coefficients ! ! attributes: ! language: f90 ! machine: ibm RS/6000 SP ! !$$$ use general_specmod, only: spec_vars use general_sub2grid_mod, only: sub2grid_info use kinds, only: r_kind,i_kind use constants, only: zero implicit none type(spec_vars),intent(in ) :: sp type(sub2grid_info),intent(in ) :: grd real(r_kind) ,intent( out) :: spectral_out(sp%nc) real(r_kind) ,intent(in ) :: grid_in(grd%nlat,grd%nlon) real(r_kind) work(grd%nlon,grd%nlat-2) integer(i_kind) i,j,jj ! Transfer contents of input grid to local work array ! Reverse ordering in j direction from n-->s to s-->n do j=2,grd%nlat-1 jj=grd%nlat-j do i=1,grd%nlon work(i,jj)=grid_in(j,i) end do end do call general_sptez_s(sp,spectral_out,work,-1) do i=1,sp%nc if(sp%factsml(i))spectral_out(i)=zero end do return end subroutine general_g2s0 subroutine general_g2s0_ad(grd,sp,spectral_in,grid_out) !$$$ subprogram documentation block ! . . . . ! subprogram: g2s0_ad adjoint of g2s0 ! prgmmr: kleist org: np23 date: 2006-07-15 ! ! abstract: adjoint of g2s0 ! ! program history log: ! 2006-07-15 kleist ! 2007-05-15 errico - Correct for proper use if grid includes equator ! 2008-04-11 safford - rm unused var ! 2010-02-18 parrish, copy g2s0_ad to general_g2s0_ad--replace 'use specmod' with input structure var sp ! also replace 'use gridmod' with input structure variable grd ! ! input argument list: ! spectral_in - input spherical harmonic coefficients ! ! output argument list: ! grid_out - output grid field on gaussian grid ! ! attributes: ! language: f90 ! machine: ibm RS/6000 SP ! !$$$ use general_specmod, only: spec_vars use general_sub2grid_mod, only: sub2grid_info use kinds, only: r_kind,i_kind use constants, only: zero,half,two implicit none type(spec_vars),intent(in ) :: sp type(sub2grid_info),intent(in ) :: grd real(r_kind),intent(in ) :: spectral_in(sp%nc) real(r_kind),intent( out) :: grid_out(grd%nlat,grd%nlon) real(r_kind) work(grd%nlon,grd%nlat-2),spec_work(sp%nc) integer(i_kind) i,j,jj do i=1,sp%nc spec_work(i)=spectral_in(i)/float(grd%nlon) if(sp%factsml(i))spec_work(i)=zero end do do i=2*sp%jcap+3,sp%nc spec_work(i)=half*spec_work(i) end do call general_sptez_s(sp,spec_work,work,1) ! ! If nlat odd, then j=je is the equator. The factor of 2 is because, ! je is referenced only once, not twice as in the spectral transform ! routines where half of the equator is considered in each hemisphere, ! separately. do j=sp%jb,sp%je-mod(grd%nlat,2) do i=1,grd%nlon work(i,j)=work(i,j)*sp%wlat(j) work(i,grd%nlat-1-j)=work(i,grd%nlat-1-j)*sp%wlat(j) end do end do if (mod(grd%nlat,2) /= 0) then do i=1,grd%nlon work(i,sp%je)=work(i,sp%je)*two*sp%wlat(sp%je) end do endif ! Transfer contents of output grid to local work array ! Reverse ordering in j direction from n-->s to s-->n do j=2,grd%nlat-1 jj=grd%nlat-j do i=1,grd%nlon grid_out(j,i)=work(i,jj) end do end do ! Load zero into pole points do i=1,grd%nlon grid_out(1,i) =zero grid_out(grd%nlat,i)=zero end do return end subroutine general_g2s0_ad subroutine general_s2g0(grd,sp,spectral_in,grid_out) !$$$ subprogram documentation block ! . . . . ! subprogram: s2g0 inverse of g2s0 ! prgmmr: kleist org: np23 date: 2006-07-15 ! ! abstract: inverse of g2s0 ! ! program history log: ! 2006-07-15 kleist ! 2007-05-15 errico - add call to spectra_pole_scalar ! 2008-04-11 safford - rm unused uses ! 2010-02-18 parrish, copy s2g0 to general_s2g0--replace 'use specmod' with input structure var sp ! also replace 'use gridmod' with input structure variable grd ! ! input argument list: ! spectral_in - input spherical harmonic coefficients ! ! output argument list: ! grid_out - output grid field on gaussian grid ! ! attributes: ! language: f90 ! machine: ibm RS/6000 SP ! !$$$ use general_specmod, only: spec_vars use general_sub2grid_mod, only: sub2grid_info use kinds, only: r_kind,i_kind use constants, only: zero implicit none type(spec_vars),intent(in ) :: sp type(sub2grid_info),intent(in ) :: grd real(r_kind),intent(in ) :: spectral_in(sp%nc) real(r_kind),intent( out) :: grid_out(grd%nlat,grd%nlon) real(r_kind) work(grd%nlon,grd%nlat-2),spec_work(sp%nc) integer(i_kind) i,j,jj do i=1,sp%nc spec_work(i)=spectral_in(i) if(sp%factsml(i))spec_work(i)=zero end do ! fill in pole points using spectral coefficients ! (replace earlier algorithm that assumed zero gradient next to pole) call general_spectra_pole_scalar (grd,sp,grid_out,spec_work) call general_sptez_s(sp,spec_work,work,1) ! Reverse ordering in j direction from n-->s to s-->n ! And account for work array excluding pole points do j=2,grd%nlat-1 jj=grd%nlat-j do i=1,grd%nlon grid_out(j,i)=work(i,jj) end do end do return end subroutine general_s2g0 subroutine general_s2g0_ad(grd,sp,spectral_out,grid_in) !$$$ subprogram documentation block ! . . . . ! subprogram: s2g0_ad adjoint of s2g0 ! prgmmr: kleist org: np23 date: 2006-07-15 ! ! abstract: adjoint of s2g0 ! ! program history log: ! 2006-07-15 kleist ! 2007-04-22 errico correction for proper treatment of equator ! also add call to spectra_pole_scalar_ad ! 2010-02-18 parrish, copy s2g0 to general_s2g0--replace 'use specmod' with input structure var sp ! also replace 'use gridmod' with input structure variable grd ! ! input argument list: ! grid_in - input spherical harmonic coefficients ! ! output argument list: ! spectral_out - output grid field on gaussian grid ! ! attributes: ! language: f90 ! machine: ibm RS/6000 SP ! !$$$ use general_specmod, only: spec_vars use general_sub2grid_mod, only: sub2grid_info use kinds, only: r_kind,i_kind use constants, only: zero,two implicit none type(spec_vars),intent(in ) :: sp type(sub2grid_info),intent(in ) :: grd real(r_kind),intent( out) :: spectral_out(sp%nc) real(r_kind),intent(in ) :: grid_in(grd%nlat,grd%nlon) real(r_kind) work(grd%nlon,grd%nlat-2),spec_work(sp%nc) integer(i_kind) i,j,jj ! Reverse ordering in j direction from n-->s to s-->n ! And account for work array excluding pole points do j=2,grd%nlat-1 jj=grd%nlat-j do i=1,grd%nlon work(i,jj)=grid_in(j,i) end do end do do j=sp%jb,sp%je-mod(grd%nlat,2) do i=1,grd%nlon work(i,j)=work(i,j)/sp%wlat(j) work(i,grd%nlat-1-j)=work(i,grd%nlat-1-j)/sp%wlat(j) end do end do if (mod(grd%nlat,2) /= 0) then do i=1,grd%nlon work(i,sp%je)=work(i,sp%je)/(two*sp%wlat(sp%je)) end do endif call general_sptez_s(sp,spec_work,work,-1) do i=1,sp%nc spec_work(i)=spec_work(i)*float(grd%nlon) end do do i=2*sp%jcap+3,sp%nc spec_work(i)=two*spec_work(i) end do call general_spectra_pole_scalar_ad (grd,sp,grid_in,spec_work) do i=1,sp%nc spectral_out(i)=spec_work(i) if(sp%factsml(i))spectral_out(i)=zero end do return end subroutine general_s2g0_ad subroutine sfilter(grd,sp,filter,grid) !$$$ subprogram documentation block ! . . . . ! subprogram: sfilter apply spectral filter ! prgmmr: kleist org: np23 date: 2013-12-03 ! ! abstract: takes subdomain variables and applies spectral filter. Eliminates ! unneaded data motion ! ! program history log: ! 2013-12-03 derber ! ! input argument list: ! grd - input grid specification ! sp - input spectral specification ! filter - spectral filter coefficients ! grid - input subdomain field ! ! output argument list: ! grid - output subdomain field ! ! attributes: ! language: f90 ! machine: ibm RS/6000 SP ! !$$$ use general_specmod, only: spec_vars use general_sub2grid_mod, only: sub2grid_info use kinds, only: r_kind,i_kind use constants, only: zero,two implicit none type(spec_vars),intent(in ) :: sp type(sub2grid_info),intent(in ) :: grd real(r_kind),intent(in ) :: filter(sp%nc) real(r_kind),intent(inout) :: grid(grd%nlat,grd%nlon) real(r_kind) work(grd%nlon,grd%nlat-2),spec_work(sp%nc) real(r_kind) gnlon integer(i_kind) i,j,jj,imod ! Reverse ordering in j direction from n-->s to s-->n ! And account for work array excluding pole points do j=2,grd%nlat-1 jj=grd%nlat-j do i=1,grd%nlon work(i,jj)=grid(j,i) end do end do imod=mod(grd%nlat,2) if (imod /= 0) then do j=sp%jb,sp%je-1 do i=1,grd%nlon work(i,j)=work(i,j)/sp%wlat(j) work(i,grd%nlat-1-j)=work(i,grd%nlat-1-j)/sp%wlat(j) end do end do do i=1,grd%nlon work(i,sp%je)=work(i,sp%je)/(two*sp%wlat(sp%je)) end do else do j=sp%jb,sp%je do i=1,grd%nlon work(i,j)=work(i,j)/sp%wlat(j) work(i,grd%nlat-1-j)=work(i,grd%nlat-1-j)/sp%wlat(j) end do end do endif call general_sptez_s(sp,spec_work,work,-1) gnlon=float(grd%nlon) ! gnlon=real(grd%nlon,r_kind) do i=1,sp%nc spec_work(i)=spec_work(i)*gnlon end do do i=2*sp%jcap+3,sp%nc spec_work(i)=two*spec_work(i) end do call general_spectra_pole_scalar_ad (grd,sp,grid,spec_work) do i=1,sp%nc spec_work(i)=spec_work(i)*filter(i) end do ! fill in pole points using spectral coefficients ! (replace earlier algorithm that assumed zero gradient next to pole) call general_spectra_pole_scalar (grd,sp,grid,spec_work) call general_sptez_s(sp,spec_work,work,1) ! Reverse ordering in j direction from n-->s to s-->n ! And account for work array excluding pole points do j=2,grd%nlat-1 jj=grd%nlat-j do i=1,grd%nlon grid(j,i)=work(i,jj) end do end do return end subroutine sfilter subroutine general_uvg2zds(grd,sp,zsp,dsp,ugrd,vgrd) !$$$ subprogram documentation block ! . . . . ! subprogram: uvg2zds grid u,v to spectral vort, div ! prgmmr: kleist org: np23 date: 2006-07-15 ! ! abstract: transform vector u,v from gaussian grid to spherical harmonic ! coefficients of vorticity and divergence. ! ! program history log: ! 2006-07-15 kleist ! 2010-02-18 parrish, copy uvg2zds to general_uvg2zds--replace 'use specmod' with input structure var sp ! also replace 'use gridmod' with input structure variable grd ! ! input argument list: ! ugrd - input u on gaussian grid ! vgrd - input v on gaussian grid ! ! output argument list: ! zsp - output spherical harmonic coefficients of vorticity ! dsp - output spherical harmonic coefficients of divergence ! ! attributes: ! language: f90 ! machine: ibm RS/6000 SP ! !$$$ use general_specmod, only: spec_vars use general_sub2grid_mod, only: sub2grid_info use kinds, only: r_kind,i_kind use constants, only: zero implicit none ! Passed variables type(spec_vars),intent(in ) :: sp type(sub2grid_info),intent(in ) :: grd real(r_kind),dimension(grd%nlat,grd%nlon),intent(in ) :: ugrd,vgrd real(r_kind),dimension(sp%nc) ,intent( out) :: zsp,dsp ! Local variables real(r_kind),dimension(grd%nlon,grd%nlat-2):: grdwrk1,grdwrk2 real(r_kind),dimension(sp%nc):: spcwrk1,spcwrk2 integer(i_kind) i,j,jj ! Transfer contents of input grid to local work array ! Reverse ordering in j direction from n-->s to s-->n do j=2,grd%nlat-1 jj=grd%nlat-j do i=1,grd%nlon grdwrk1(i,jj)=ugrd(j,i) grdwrk2(i,jj)=vgrd(j,i) end do end do call general_sptez_v(sp,spcwrk1,spcwrk2,grdwrk1,grdwrk2,-1) do i=1,sp%nc zsp(i)=spcwrk2(i) dsp(i)=spcwrk1(i) if(sp%factvml(i))then zsp(i)=zero dsp(i)=zero end if end do return end subroutine general_uvg2zds subroutine general_uvg2zds_ad(grd,sp,zsp,dsp,ugrd,vgrd) !$$$ subprogram documentation block ! . . . . ! subprogram: uvg2zds_ad adjoint of uvg2zds ! prgmmr: kleist org: np23 date: 2006-07-15 ! ! abstract: adjoint of uvg2zds ! ! program history log: ! 2006-07-15 kleist ! 2007-04-22 errico - correction for proper treatment of equator ! 2008-04-11 safford - rm unused uses ! 2010-02-18 parrish, copy uvg2zds_ad to general_uvg2zds_ad--replace 'use specmod' with structure var sp ! also replace 'use gridmod' with input structure variable grd ! ! input argument list: ! ugrd - input u on gaussian grid ! vgrd - input v on gaussian grid ! zsp - input spherical harmonic coefficients of vorticity ! dsp - input spherical harmonic coefficients of divergence ! ! output argument list: ! ugrd - output u on gaussian grid ! vgrd - output v on gaussian grid ! ! attributes: ! language: f90 ! machine: ibm RS/6000 SP ! !$$$ use general_specmod, only: spec_vars use general_sub2grid_mod, only: sub2grid_info use kinds, only: r_kind,i_kind use constants, only: zero,half,two implicit none ! Passed variables type(spec_vars),intent(in ) :: sp type(sub2grid_info),intent(in ) :: grd real(r_kind),dimension(grd%nlat,grd%nlon),intent(inout) :: ugrd,vgrd real(r_kind),dimension(sp%nc) ,intent(in ) :: zsp,dsp ! Local variables real(r_kind),dimension(grd%nlon,grd%nlat-2):: grdwrk1,grdwrk2 real(r_kind),dimension(sp%nc):: spcwrk1,spcwrk2 integer(i_kind) i,j,jj do i=1,sp%nc spcwrk1(i)=dsp(i)/float(grd%nlon) spcwrk2(i)=zsp(i)/float(grd%nlon) if(sp%factvml(i))then spcwrk1(i)=zero spcwrk2(i)=zero end if end do do i=2*sp%jcap+3,sp%nc spcwrk1(i)=half*spcwrk1(i) spcwrk2(i)=half*spcwrk2(i) end do do i=2,sp%ncd2 spcwrk1(2*i-1)=spcwrk1(2*i-1)*sp%enn1(i) spcwrk1(2*i )=spcwrk1(2*i )*sp%enn1(i) spcwrk2(2*i-1)=spcwrk2(2*i-1)*sp%enn1(i) spcwrk2(2*i )=spcwrk2(2*i )*sp%enn1(i) end do call general_sptez_v(sp,spcwrk1,spcwrk2,grdwrk1,grdwrk2,1) do j=sp%jb,sp%je-mod(grd%nlat,2) do i=1,grd%nlon grdwrk1(i,j)=grdwrk1(i,j)*sp%wlat(j) grdwrk1(i,grd%nlat-1-j)=grdwrk1(i,grd%nlat-1-j)*sp%wlat(j) grdwrk2(i,j)=grdwrk2(i,j)*sp%wlat(j) grdwrk2(i,grd%nlat-1-j)=grdwrk2(i,grd%nlat-1-j)*sp%wlat(j) end do end do if (mod(grd%nlat,2) /= 0) then do i=1,grd%nlon grdwrk1(i,sp%je)=grdwrk1(i,sp%je)*two*sp%wlat(sp%je) grdwrk2(i,sp%je)=grdwrk2(i,sp%je)*two*sp%wlat(sp%je) end do endif ! Transfer contents of input grid to local work array ! Reverse ordering in j direction from n-->s to s-->n do j=2,grd%nlat-1 jj=grd%nlat-j do i=1,grd%nlon ugrd(j,i)=grdwrk1(i,jj) vgrd(j,i)=grdwrk2(i,jj) end do end do do i=1,grd%nlon ugrd(1,i) = zero ugrd(grd%nlat,i) = zero vgrd(1,i) = zero vgrd(grd%nlat,i) = zero end do return end subroutine general_uvg2zds_ad subroutine general_zds2pcg(grd,sp,zsp,dsp,pgrd,cgrd) !$$$ subprogram documentation block ! . . . . ! subprogram: zds2pcg transform spec. vort,div to psi,chi on grid ! prgmmr: kleist org: np23 date: 2006-07-15 ! ! abstract: transform spectral vorticity, divergence to psi and chi on grid ! ! program history log: ! 2006-07-15 kleist ! 2010-02-18 parrish, copy zds2pcg to general_zds2pcg--replace 'use specmod' with structure var sp ! also replace 'use gridmod' with input structure variable grd ! ! input argument list: ! zsp - input spherical harmonic coefficients of vorticity ! dsp - input spherical harmonic coefficients of divergence ! ! output argument list: ! pgrd - output psi on gaussian grid ! cgrd - output chi on gaussian grid ! ! attributes: ! language: f90 ! machine: ibm RS/6000 SP ! !$$$ use general_specmod, only: spec_vars use general_sub2grid_mod, only: sub2grid_info use kinds, only: r_kind,i_kind use constants, only: zero implicit none ! Passed variables type(spec_vars),intent(in ) :: sp type(sub2grid_info),intent(in ) :: grd real(r_kind),dimension(sp%nc) ,intent(in ) :: zsp,dsp real(r_kind),dimension(grd%nlat,grd%nlon),intent( out) :: pgrd,cgrd ! Local variables real(r_kind),dimension(sp%nc):: spc1,spc2 integer(i_kind) i ! Inverse laplacian spc1(1)=zero spc1(2)=zero spc2(1)=zero spc2(2)=zero do i=2,sp%ncd2 spc1(2*i-1)=zsp(2*i-1)/(-sp%enn1(i)) spc1(2*i )=zsp(2*i )/(-sp%enn1(i)) spc2(2*i-1)=dsp(2*i-1)/(-sp%enn1(i)) spc2(2*i )=dsp(2*i )/(-sp%enn1(i)) end do call general_s2g0(grd,sp,spc1,pgrd) call general_s2g0(grd,sp,spc2,cgrd) return end subroutine general_zds2pcg subroutine general_zds2pcg_ad(grd,sp,zsp,dsp,pgrd,cgrd) !$$$ subprogram documentation block ! . . . . ! subprogram: zds2pcg transform spec. vort,div to psi,chi on grid ! prgmmr: kleist org: np23 date: 2006-07-15 ! ! abstract: transform spectral vorticity, divergence to psi and chi on grid ! ! program history log: ! 2006-07-15 kleist ! 2010-02-18 parrish, copy zds2pcg_ad to general_zds2pcg_ad--replace 'use specmod' with structure var sp ! also replace 'use gridmod' with input structure variable grd ! ! input argument list: ! zsp - input spherical harmonic coefficients of vorticity ! dsp - input spherical harmonic coefficients of divergence ! ! output argument list: ! pgrd - output psi on gaussian grid ! cgrd - output chi on gaussian grid ! ! attributes: ! language: f90 ! machine: ibm RS/6000 SP ! !$$$ use general_specmod, only: spec_vars use general_sub2grid_mod, only: sub2grid_info use kinds, only: r_kind,i_kind use constants, only: zero implicit none ! Passed variables type(spec_vars),intent(in ) :: sp type(sub2grid_info),intent(in ) :: grd real(r_kind),dimension(sp%nc) ,intent( out) :: zsp,dsp real(r_kind),dimension(grd%nlat,grd%nlon),intent(inout) :: pgrd,cgrd ! Local variables real(r_kind),dimension(sp%nc):: spc1,spc2 integer(i_kind) i ! Inverse laplacian call general_s2g0_ad(grd,sp,spc1,pgrd) call general_s2g0_ad(grd,sp,spc2,cgrd) pgrd=zero cgrd=zero spc1(1)=zero spc1(2)=zero spc2(1)=zero spc2(2)=zero do i=2,sp%ncd2 zsp(2*i-1)=spc1(2*i-1)/(-sp%enn1(i)) zsp(2*i )=spc1(2*i )/(-sp%enn1(i)) dsp(2*i-1)=spc2(2*i-1)/(-sp%enn1(i)) dsp(2*i )=spc2(2*i )/(-sp%enn1(i)) end do return end subroutine general_zds2pcg_ad subroutine general_zds2uvg(grd,sp,zsp,dsp,ugrd,vgrd) !$$$ subprogram documentation block ! . . . . ! subprogram: zds2uvg inverse of uvg2zds ! prgmmr: kleist org: np23 date: 2006-07-15 ! ! abstract: inverse of uvg2zds ! ! program history log: ! 2006-07-15 kleist ! 2007-05-15 errico include proper specification of pole points ! 2010-02-18 parrish, copy zds2uvg to general_zds2uvg--replace 'use specmod' with structure var sp ! also replace 'use gridmod' with input structure variable grd ! ! input argument list: ! zsp - input spherical harmonic coefficients of vorticity ! dsp - input spherical harmonic coefficients of divergence ! ! output argument list: ! ugrd - output u on gaussian grid ! vgrd - output v on gaussian grid ! ! attributes: ! language: f90 ! machine: ibm RS/6000 SP ! !$$$ use general_specmod, only: spec_vars use general_sub2grid_mod, only: sub2grid_info use kinds, only: r_kind,i_kind use constants, only: zero implicit none ! Passed variables type(spec_vars),intent(in ) :: sp type(sub2grid_info),intent(in ) :: grd real(r_kind),dimension(sp%nc) ,intent(in ) :: zsp,dsp real(r_kind),dimension(grd%nlat,grd%nlon),intent( out) :: ugrd,vgrd ! Local variables real(r_kind),dimension(grd%nlon,grd%nlat-2):: grdwrk1,grdwrk2 real(r_kind),dimension(sp%nc):: spcwrk1,spcwrk2 integer(i_kind) i,j,jj do i=1,sp%nc spcwrk1(i)=dsp(i) spcwrk2(i)=zsp(i) if(sp%factvml(i))then spcwrk1(i)=zero spcwrk2(i)=zero end if end do call general_sptez_v(sp,spcwrk1,spcwrk2,grdwrk1,grdwrk2,1) ! Reverse ordering in j direction from n-->s to s-->n ! and copy to array that includes pole points do j=2,grd%nlat-1 jj=grd%nlat-j do i=1,grd%nlon ugrd(j,i)=grdwrk1(i,jj) vgrd(j,i)=grdwrk2(i,jj) end do end do ! fill in pole points ! (replace earlier algorithm that assumed zero gradient next to pole) call general_spectra_pole_wind (grd,sp,ugrd,vgrd,spcwrk2,spcwrk1) return end subroutine general_zds2uvg subroutine general_zds2uvg_ad(grd,sp,zsp,dsp,ugrd,vgrd) !$$$ subprogram documentation block ! . . . . ! subprogram: zds2uvg_ad adjoint of zds2uvg ! prgmmr: kleist org: np23 date: 2006-07-15 ! ! abstract: adjoint of zds2uvg ! ! program history log: ! 2006-07-15 kleist ! 2007-04-22 errico - correction for proper treatment of equator ! - also add call to spectra_pole_scalar_ad ! 2008-04-11 safford - rm unused uses ! 2010-02-18 parrish, copy zds2uvg_ad to general_zds2uvg_ad--replace 'use specmod' with structure var sp ! also replace 'use gridmod' with input structure variable grd ! ! input argument list: ! zsp - input spherical harmonic coefficients of vorticity ! dsp - input spherical harmonic coefficients of divergence ! ugrd - input u on gaussian grid ! vgrd - input v on gaussian grid ! ! output argument list: ! zsp - output spherical harmonic coefficients of vorticity ! dsp - output spherical harmonic coefficients of divergence ! ! attributes: ! language: f90 ! machine: ibm RS/6000 SP ! !$$$ use general_specmod, only: spec_vars use general_sub2grid_mod, only: sub2grid_info use kinds, only: r_kind,i_kind use constants, only: zero,two implicit none ! Passed variables type(spec_vars),intent(in ) :: sp type(sub2grid_info),intent(in ) :: grd real(r_kind),dimension(grd%nlat,grd%nlon),intent(in ) :: ugrd,vgrd real(r_kind),dimension(sp%nc) ,intent(inout) :: zsp,dsp ! Local variables real(r_kind),dimension(grd%nlon,grd%nlat-2):: grdwrk1,grdwrk2 real(r_kind),dimension(sp%nc):: spcwrk1,spcwrk2 integer(i_kind) i,j,jj ! Transfer contents of input grid to local work array ! Reverse ordering in j direction from n-->s to s-->n do j=2,grd%nlat-1 jj=grd%nlat-j do i=1,grd%nlon grdwrk1(i,jj)=ugrd(j,i) grdwrk2(i,jj)=vgrd(j,i) end do end do do j=sp%jb,sp%je-mod(grd%nlat,2) do i=1,grd%nlon grdwrk1(i,j)=grdwrk1(i,j)/sp%wlat(j) grdwrk1(i,grd%nlat-1-j)=grdwrk1(i,grd%nlat-1-j)/sp%wlat(j) grdwrk2(i,j)=grdwrk2(i,j)/sp%wlat(j) grdwrk2(i,grd%nlat-1-j)=grdwrk2(i,grd%nlat-1-j)/sp%wlat(j) end do end do if (mod(grd%nlat,2) /= 0) then do i=1,grd%nlon grdwrk1(i,sp%je)=grdwrk1(i,sp%je)/(two*sp%wlat(sp%je)) grdwrk2(i,sp%je)=grdwrk2(i,sp%je)/(two*sp%wlat(sp%je)) end do endif call general_sptez_v(sp,spcwrk1,spcwrk2,grdwrk1,grdwrk2,-1) do i=2,sp%ncd2 spcwrk1(2*i-1)=spcwrk1(2*i-1)/sp%enn1(i) spcwrk1(2*i )=spcwrk1(2*i )/sp%enn1(i) spcwrk2(2*i-1)=spcwrk2(2*i-1)/sp%enn1(i) spcwrk2(2*i )=spcwrk2(2*i )/sp%enn1(i) end do do i=1,sp%nc spcwrk1(i)=spcwrk1(i)*float(grd%nlon) spcwrk2(i)=spcwrk2(i)*float(grd%nlon) end do do i=2*sp%jcap+3,sp%nc spcwrk1(i)=two*spcwrk1(i) spcwrk2(i)=two*spcwrk2(i) end do ! adjoint of pole fill call general_spectra_pole_wind_ad (grd,sp,ugrd,vgrd,spcwrk2,spcwrk1) do i=1,sp%nc zsp(i)=spcwrk2(i) dsp(i)=spcwrk1(i) if(sp%factvml(i))then zsp(i)=zero dsp(i)=zero end if end do return end subroutine general_zds2uvg_ad subroutine general_spectra_pole_scalar (grd,sp,field,coefs) !$$$ subprogram documentation block ! . . . . ! subprogram: spectra_pole_scalar ! prgmmr: errico org: date: 2007-05-15 ! ! abstract: fill pole values for scalar field using spectral coefficients ! ! program history log: ! 2007-05-15 errico ! 2009-11-24 parrish -- change values of starting assoc. legendre polynomial alp0(0),alp0(1) ! to be consistent with what is computed in sptranf0. ! alp0(0) = sqrt(half), instead of alp0(0)=one ! and alp0(1) = sqrt(three)*alp0(0), instead of alp0(1)=sqrt(three) ! 2010-02-18 parrish, spectra_pole_scalar to general_spectra_pole_scalar-- ! replace 'use specmod' with structure var sp ! also replace 'use gridmod' with input structure variable grd ! 2011-07-03 todling - use intrinsic sqrt (avoid old dsqrt-interface) ! 2014-12=03 derber - use new sp%alp0 field so that we do not have to ! recalculate ! ! input argument list: ! grd - specification of input grid ! sp - specification of spectral fields ! coefs - spherical harmonic coefficients of scalar field ! ! output argument list: ! field - scalar field (modified at poles only) ! ! attributes: ! language: f90 ! !$$$ use general_specmod, only: spec_vars use general_sub2grid_mod, only: sub2grid_info use kinds, only: r_kind,i_kind use constants, only: zero,half,three implicit none type(spec_vars),intent(in ) :: sp type(sub2grid_info),intent(in ) :: grd real(r_kind), intent(in ) :: coefs(sp%nc) ! all spectral coefs real(r_kind), intent(inout) :: field(grd%nlat,grd%nlon) ! field, including pole ! ! Local variables integer(i_kind) :: n ! order of assoc. legendre polynomial integer(i_kind) :: n1 ! offset for real zonal wavenumber m=0 coefs integer(i_kind) :: j ! longitude index real(r_kind) :: afac ! alp for S. pole real(r_kind) :: fpole_n, fpole_s ! value of scalar field at n and s pole ! ! The spectral coefs are assumed to be ordered ! alternating real, imaginary ! all m=0 first, followed by m=1, etc. ! ordered in ascending values of n-m ! the first index is 1, correspond to the real part of the global mean. ! triangular truncation assumed ! These conditions determine the value of n1. ! ! ! Compute projection of wavenumber 0 (only real values for this fpole_n=zero fpole_s=zero n1=1 do n=0,sp%jcap if (mod(n,2)==1) then afac=-sp%alp0(n) else afac= sp%alp0(n) endif fpole_n=fpole_n+sp%alp0(n)*coefs(2*n+n1) fpole_s=fpole_s+ afac*coefs(2*n+n1) enddo ! ! set field for all "longitudes" at the pole to the same value do j=1,grd%nlon field( 1,j)=fpole_s field(grd%nlat,j)=fpole_n enddo end subroutine general_spectra_pole_scalar ! ! x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x ! subroutine general_spectra_pole_scalar_ad (grd,sp,field,coefs) !$$$ subprogram documentation block ! . . . . ! subprogram: spectra_pole_scalar_ad ! prgmmr: errico org: date: 2007-05-15 ! ! abstract: adjoint of spectra_pole_scalar ! ! program history log: ! 2007-05-15 errico ! 2009-11-24 parrish -- change values of starting assoc. legendre polynomial alp0(0),alp0(1) ! to be consistent with what is computed in sptranf0. ! alp0(0) = sqrt(half), instead of alp0(0)=one ! and alp0(1) = sqrt(three)*alp0(0), instead of alp0(1)=sqrt(three) ! 2010-02-18 parrish, spectra_pole_scalar_ad to general_spectra_pole_scalar_ad-- ! replace 'use specmod' with structure var sp ! also replace 'use gridmod' with input structure variable grd ! 2014-12=03 derber - use new sp%alp0 field so that we do not have to ! recalculate ! ! input argument list: ! grd - specification of input grid ! sp - specification of spectral fields ! field - adjoint (dual) of field (only poles used here) ! coefs - adjoint (dual) of spherical harmonic coefficients ! ! output argument list: ! coefs - incremented adjoint (dual) of spherical harmonic coefficients ! ! attributes: ! language: f90 ! !$$$ use general_specmod, only: spec_vars use general_sub2grid_mod, only: sub2grid_info use kinds, only: r_kind,i_kind use constants, only: zero,half,three implicit none type(spec_vars),intent(in ) :: sp type(sub2grid_info),intent(in ) :: grd real(r_kind), intent(inout) :: coefs(sp%nc) ! adjoint of all spectral coefs real(r_kind), intent(in ) :: field(grd%nlat,grd%nlon) ! adjoint field, including pole ! ! Local variables integer(i_kind) :: n ! order of assoc. legendre polynomial integer(i_kind) :: n1 ! offset for real zonal wavenumber m=0 coefs integer(i_kind) :: j ! longitude index real(r_kind) :: afac ! alp for S. pole real(r_kind) :: fpole_n, fpole_s ! value of scalar field at n and s pole ! ! The spectral coefs are assumed to be ordered ! alternating real, imaginary ! all m=0 first, followed by m=1, etc. ! ordered in ascending values of n-m ! the first index is 1 ! triangular truncation assumed ! These conditions determine the value of n1. ! ! ! Compute projection of wavenumber 0 (only real values for this) fpole_n=zero fpole_s=zero do j=1,grd%nlon fpole_n=fpole_n+field(grd%nlat,j) fpole_s=fpole_s+field( 1,j) enddo n1=1 do n=0,sp%jcap if (mod(n,2)==1) then afac=-sp%alp0(n) else afac= sp%alp0(n) endif coefs(2*n+n1)=coefs(2*n+n1)+afac*fpole_s+sp%alp0(n)*fpole_n enddo ! end subroutine general_spectra_pole_scalar_ad ! ! x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x ! subroutine general_spectra_pole_wind (grd,sp,ufield,vfield,vort,divg) !$$$ subprogram documentation block ! . . . . ! subprogram: spectra_pole_wind grid to spectral ! prgmmr: errico org: date: 2007-05-15 ! ! abstract: fill pole values for vector wind field using spectral coefficients ! ! program history log: ! 2007-05-15 errico ! 2009-11-24 parrish -- change values of starting assoc. legendre polynomial alp1(1) ! to be consistent with what is computed in sptranf0. ! alp1(1) = sqrt(three)/two, instead of alp1(1)=sqrt(three/two) ! 2010-02-18 parrish, spectra_pole_wind to general_spectra_pole_wind-- ! replace 'use specmod' with structure var sp ! also replace 'use gridmod' with input structure variable grd ! ! input argument list: ! grd - specification of input grid ! sp - specification of spectral fields ! vort - spherical harmonic coefficients of vorticity ! divg - spherical harmonic coefficients of divergence ! ! output argument list: ! ufield - u wind component field (set at poles only) ! vfield - v wind component field (set at poles only) ! ! attributes: ! language: f90 ! !$$$ use general_specmod, only: spec_vars use general_sub2grid_mod, only: sub2grid_info use kinds, only: r_kind,i_kind use constants, only: zero,two,three,rearth,pi implicit none type(spec_vars),intent(in ) :: sp type(sub2grid_info),intent(in ) :: grd real(r_kind), intent(in ) :: vort(sp%nc) ! spect. coefs for vorticity real(r_kind), intent(in ) :: divg(sp%nc) ! spect. coefs for divergence real(r_kind), intent(inout) :: ufield(grd%nlat,grd%nlon) ! u field, including pole real(r_kind), intent(inout) :: vfield(grd%nlat,grd%nlon) ! v field, including pole ! ! Local variables integer(i_kind) :: n ! order of assoc. Legendre polynomial integer(i_kind) :: n1 ! offset value for location of m=1 coefs integer(i_kind) :: j ! longitude index real(r_kind) :: alp1(1:sp%jcap) ! Assoc Legendre Poly for m=1 at the North Pole real(r_kind) :: epsi1(1:sp%jcap) ! epsilon factor for zonal wavenumber m=1 real(r_kind) :: fnum, fden, fac real(r_kind) :: coslon, sinlon ! sines and cosines of longitudes real(r_kind) :: afac ! alp for S. pole real(r_kind) :: s_vort_R_n ! sum of real part of P(n)*vort(n)/(n*n+n) for N.pole real(r_kind) :: s_vort_I_n ! sum of imag part of P(n)*vort(n)/(n*n+n) for N.pole real(r_kind) :: s_divg_R_n ! sum of real part of P(n)*divg(n)/(n*n+n) for N.pole real(r_kind) :: s_divg_I_n ! sum of imag part of P(n)*divg(n)/(n*n+n) for N.pole real(r_kind) :: s_vort_R_s ! sum of real part of P(n)*vort(n)/(n*n+n) for S.pole real(r_kind) :: s_vort_I_s ! sum of imag part of P(n)*vort(n)/(n*n+n) for S.pole real(r_kind) :: s_divg_R_s ! sum of real part of P(n)*divg(n)/(n*n+n) for S.pole real(r_kind) :: s_divg_I_s ! sum of imag part of P(n)*divg(n)/(n*n+n) for S.pole real(r_kind) :: uR_n, vR_n ! twice real part of m=1 Fourier coef for u,v (N.pole) real(r_kind) :: uI_n, vI_n ! twice imag part of m=1 Fourier coef for u,v (N.pole) real(r_kind) :: uR_s, vR_s ! twice real part of m=1 Fourier coef for u,v (S.pole) real(r_kind) :: uI_s, vI_s ! twice imag part of m=1 Fourier coef for u,v (S.pole) real(r_kind) :: tworearth ! spectral components of u: ! u(n)=a( -e(n)*vort(n-1)/n + e(n+1)*vort(n+1)/(n+1) -i*divg(n)/(n*n+n) ! where a=earth's radius, vort and divg are spectral coefs, and e=epsi1 factor ! complex Fourier coef for wavenumber 1 of u at the pole is ! u_coef= sum_(n=1 to n=jcap+1) p_(n,m=1)*u(n) ! At rhe poles, the sum over vort contribution is simplified by noting that ! sum (n=1 to n=jcap+1) p_(n,m=1)*( vort contrib to u(n)) = ! -a*sum (n=1 to n=jcap) p_(n,m=1)*vort(n)/(n*n+n) ! for all vort(n), n=1,...,jcap ! Therefore ! u_coef= a* (- sum (n=1 to n=jcap) p_(n,m=1)*vort(n)/(n*n+n) ! - i*sum (n=1 to n=jcap) p_(n,m=1)*divg(n)/(n*n+n) ) ! v_coef=-i*u_coef ! u(lon)=2.*modulus(u_coef) cos(u_phase + lon/twopi) ! where the 2 is because m=-1 is implicitly considered, with the m=-1 ! Fourier coef equal to the complex conjugate of that for m=1 ! u_phase is atan(imag part of u_coef/ real part of u_coef) ! ! The spectral coefs are assumed to be ordered ! alternating real, imaginary ! all m=0 first, followed by m=1, etc. ! ordered in ascending values of n-m ! the first index is 1 ! triangular truncation assumed ! These conditions determine the value of n1. ! The phases of the spectra are assumed to be with respect ! to the first longitude being 0. ! n1=2*(sp%jcap+1) ! ! Specify cosine and sines of longitudes assuming that ! the phases of spectral coefs are with repect to the ! origin being the first longitude. do n=1,sp%jcap fnum=real(n**2-1, r_kind) fden=real(4*n**2-1, r_kind) epsi1(n)=sqrt(fnum/fden) enddo ! ! Compute Legendre polynomials / cos for m=1 at North Pole ! This is actually limit Pn,m / abs (cos) as pole is approached alp1(1)=sqrt(three)/two alp1(2)=sqrt(two+three)*alp1(1) do n=3,sp%jcap alp1(n)=(alp1(n-1)-epsi1(n-1)*alp1(n-2))/epsi1(n) enddo ! ! Replace Legendre polynomials by P/(n*n+n) do n=1,sp%jcap alp1(n)=alp1(n)/(n*n+n) enddo ! ! Compute sums of coefs weighted by P(n)/(n*n+n) s_vort_R_n=zero s_vort_I_n=zero s_divg_R_n=zero s_divg_I_n=zero s_vort_R_s=zero s_vort_I_s=zero s_divg_R_s=zero s_divg_I_s=zero do n=1,sp%jcap if (mod(n,2)==0) then afac=-alp1(n) else afac= alp1(n) endif s_vort_R_n = s_vort_R_n + alp1(n)*vort(2*n-1+n1) s_vort_I_n = s_vort_I_n + alp1(n)*vort(2*n +n1) s_divg_R_n = s_divg_R_n + alp1(n)*divg(2*n-1+n1) s_divg_I_n = s_divg_I_n + alp1(n)*divg(2*n +n1) s_vort_R_s = s_vort_R_s + afac*vort(2*n-1+n1) s_vort_I_s = s_vort_I_s + afac*vort(2*n +n1) s_divg_R_s = s_divg_R_s + afac*divg(2*n-1+n1) s_divg_I_s = s_divg_I_s + afac*divg(2*n +n1) enddo s_vort_R_s = -s_vort_R_s s_vort_I_s = -s_vort_I_s ! ! Determine 2* real and imag parts for m=1 u wind at pole ! The factor -1 if south is because as the south pole is approached, ! the limit of abs(cos)/cos = -1. tworearth=two*rearth uR_n= tworearth * (s_divg_I_n - s_vort_R_n) uI_n=-tworearth * (s_divg_R_n + s_vort_I_n) vR_n=-uI_n vI_n= uR_n uR_s= tworearth * (s_divg_I_s - s_vort_R_s) uI_s=-tworearth * (s_divg_R_s + s_vort_I_s) vR_s= uI_s vI_s=-uR_s ! ! Perform Fourier projection for m=1 at pole fac=two*pi/grd%nlon do j=1,grd%nlon coslon=cos(fac*(j-1)) sinlon=sin(fac*(j-1)) ufield(grd%nlat,j)=uR_n*coslon-uI_n*sinlon vfield(grd%nlat,j)=vR_n*coslon-vI_n*sinlon ufield( 1,j)=uR_s*coslon-uI_s*sinlon vfield( 1,j)=vR_s*coslon-vI_s*sinlon enddo ! end subroutine general_spectra_pole_wind ! ! x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x ! subroutine general_spectra_pole_wind_ad (grd,sp,ufield,vfield,vort,divg) !$$$ subprogram documentation block ! . . . . ! subprogram: spectra_pole_wind_ad ! prgmmr: errico org: date: 2007-05-15 ! ! abstract: adjoint of routine spectra_pole_wind ! ! program history log: ! 2007-05-15 errico ! 2009-11-24 parrish -- change values of starting assoc. legendre polynomial alp1(1) ! to be consistent with what is computed in sptranf0. ! alp1(1) = sqrt(three)/two, instead of alp1(1)=sqrt(three/two) ! 2010-02-18 parrish, spectra_pole_wind_ad to general_spectra_pole_wind_ad-- ! replace 'use specmod' with structure var sp ! also replace 'use gridmod' with input structure variable grd ! ! input argument list: ! grd - specification of input grid ! sp - specification of spectral fields ! ufield - adjoint (dual) of u wind component field (set at poles only) ! vfield - adjoint (dual) of v wind component field (set at poles only) ! vort - adjoint (dual) of spherical harmonic coefficients of vorticity ! divg - adjoint (dual) of spherical harmonic coefficients of divergence ! ! output argument list: ! vort - incremented adjoint (dual) of spherical harmonic coefficients of vorticity ! divg - incremented adjoint (dual) of spherical harmonic coefficients of divergence ! ! attributes: ! language: f90 ! !$$$ use general_specmod, only: spec_vars use general_sub2grid_mod, only: sub2grid_info use kinds, only: r_kind,i_kind use constants, only: zero,two,three,rearth,pi implicit none type(spec_vars),intent(in ) :: sp type(sub2grid_info),intent(in ) :: grd real(r_kind), intent(in ) :: ufield(grd%nlat,grd%nlon) ! adjoint of u field, including pole real(r_kind), intent(in ) :: vfield(grd%nlat,grd%nlon) ! adjoint of v field, including pole real(r_kind), intent(inout) :: vort(sp%nc) ! adjoint of spect. coefs for vorticity real(r_kind), intent(inout) :: divg(sp%nc) ! adjoint of spect. coefs for divergence ! ! Local variables integer(i_kind) :: n ! order of assoc. Legendre polynomial integer(i_kind) :: n1 ! offset value for location of m=1 coefs integer(i_kind) :: j ! longitude index real(r_kind) :: alp1(1:sp%jcap) ! Assoc Legendre Poly for m=1 at the North Pole real(r_kind) :: epsi1(1:sp%jcap) ! epsilon factor for zonal wavenumber m=1 real(r_kind) :: fnum, fden, fac real(r_kind) :: coslon, sinlon ! sines and cosines of longitudes real(r_kind) :: afac ! alp for S. pole real(r_kind) :: s_vort_R_n ! sum of real part of P(n)*vort(n)/(n*n+n) for N.pole real(r_kind) :: s_vort_I_n ! sum of imag part of P(n)*vort(n)/(n*n+n) for N.pole real(r_kind) :: s_divg_R_n ! sum of real part of P(n)*divg(n)/(n*n+n) for N.pole real(r_kind) :: s_divg_I_n ! sum of imag part of P(n)*divg(n)/(n*n+n) for N.pole real(r_kind) :: s_vort_R_s ! sum of real part of P(n)*vort(n)/(n*n+n) for S.pole real(r_kind) :: s_vort_I_s ! sum of imag part of P(n)*vort(n)/(n*n+n) for S.pole real(r_kind) :: s_divg_R_s ! sum of real part of P(n)*divg(n)/(n*n+n) for S.pole real(r_kind) :: s_divg_I_s ! sum of imag part of P(n)*divg(n)/(n*n+n) for S.pole real(r_kind) :: uR_n, vR_n ! twice real part of m=1 Fourier coef for u,v (N.pole) real(r_kind) :: uI_n, vI_n ! twice imag part of m=1 Fourier coef for u,v (N.pole) real(r_kind) :: uR_s, vR_s ! twice real part of m=1 Fourier coef for u,v (S.pole) real(r_kind) :: uI_s, vI_s ! twice imag part of m=1 Fourier coef for u,v (S.pole) real(r_kind) :: tworearth ! The phases of the spectra are assumed to be with respect ! to the first longitude being 0. ! n1=2*(sp%jcap+1) ! ! Specify cosine and sines of longitudes assuming that ! the phases of spectral coefs are with repect to the ! origin being the first longitude. do n=1,sp%jcap fnum=real(n**2-1, r_kind) fden=real(4*n**2-1, r_kind) epsi1(n)=sqrt(fnum/fden) enddo ! ! Compute Legendre polynomials / cos for m=1 at North Pole ! This is actually limit Pn,m / abs (cos) as pole is approached alp1(1)=sqrt(three)/two alp1(2)=sqrt(two+three)*alp1(1) do n=3,sp%jcap alp1(n)=(alp1(n-1)-epsi1(n-1)*alp1(n-2))/epsi1(n) enddo ! ! Replace Legendre polynomials by P/(n*n+n) do n=1,sp%jcap alp1(n)=alp1(n)/(n*n+n) enddo ! ! Perform adjoint of Fourier projection for m=1 at pole uR_n=zero uI_n=zero vR_n=zero vI_n=zero uR_s=zero uI_s=zero vR_s=zero vI_s=zero fac=two*pi/grd%nlon do j=1,grd%nlon coslon=cos(fac*(j-1)) sinlon=sin(fac*(j-1)) uR_n=uR_n+coslon*ufield(grd%nlat,j) uI_n=uI_n-sinlon*ufield(grd%nlat,j) vR_n=vR_n+coslon*vfield(grd%nlat,j) vI_n=vI_n-sinlon*vfield(grd%nlat,j) uR_s=uR_s+coslon*ufield( 1,j) uI_s=uI_s-sinlon*ufield( 1,j) vR_s=vR_s+coslon*vfield( 1,j) vI_s=vI_s-sinlon*vfield( 1,j) enddo ! the limit of abs(cos)/cos = -1. uI_n=uI_n-vR_n uR_n=uR_n+vI_n uI_s=uI_s+vR_s uR_s=uR_s-vI_s tworearth=two*rearth s_vort_R_n=-tworearth*uR_n s_vort_I_n=-tworearth*uI_n s_divg_R_n=-tworearth*uI_n s_divg_I_n= tworearth*uR_n s_vort_R_s= tworearth*uR_s s_vort_I_s= tworearth*uI_s s_divg_R_s=-tworearth*uI_s s_divg_I_s= tworearth*uR_s ! do n=1,sp%jcap if (mod(n,2)==0) then afac=-alp1(n) else afac= alp1(n) endif vort(2*n-1+n1)=vort(2*n-1+n1)+alp1(n)*s_vort_R_n & + afac*s_vort_R_s vort(2*n +n1)=vort(2*n +n1)+alp1(n)*s_vort_I_n & + afac*s_vort_I_s divg(2*n-1+n1)=divg(2*n-1+n1)+alp1(n)*s_divg_R_n & + afac*s_divg_R_s divg(2*n +n1)=divg(2*n +n1)+alp1(n)*s_divg_I_n & + afac*s_divg_I_s enddo ! end subroutine general_spectra_pole_wind_ad ! ! subroutine general_test_inverses(grd,sp,mype) !$$$ subprogram documentation block ! . . . . ! subprogram: test_inverses ! prgmmr: kleist org: np23 date: 2006-07-15 ! ! abstract: test that corresponding inverse routines for spectral transforms ! are indeed the inverses of each other (up to roundoff error) and ! that corresponding adjoints are indeed adjoints (up to roundoff ! error) ! ! program history log: ! 2006-07-15 kleist ! 2007-04-22 errico - addition of some further tests, including for vector ! transforms and adjoint testing ! 2009-01-02 todling - remove unused vars ! 2010-02-18 parrish, test_inverses to general_test_inverses-- ! replace 'use specmod' with structure var sp ! also replace 'use gridmod' with input structure variable grd ! 2013-10-19 todling - metguess now holds background ! ! input argument list: ! mype - processor number ! ! attributes: ! language: f90 ! machine: ibm RS/6000 SP ! ! Notes: ! ! The test of inverses requires either checking spectral coefs or checking ! fields, but the latter only if the starting field has been spectrally truncated, ! otherwise the starting field has components that can not be reconstituted ! by the spectra. ! ! The jacobian test for adjoints uses the fact that the matrix operator for ! the adjoint is the transpose of the forward operator matrix. Each of these ! matrices have elements in common. Individual elements can be implicitly ! determined by inputing unit vectors (say element j) to the operator ! (transform) and then looking at particular single values (say i) of the ouput: ! the result is element i,j of the matrix for the operator. ! ! The norm test uses the fact that = , where M^T is the adjoint ! of M and x and y are arbitrary vectors in the appropriate subspaces. This is ! the definition of the adjoint, defined for the norm <,>. ! !$$$ use kinds, only: r_kind,i_kind use guess_grids, only: ntguessig use general_specmod, only: spec_vars use general_sub2grid_mod, only: sub2grid_info use constants, only: zero,one use gsi_metguess_mod, only: gsi_metguess_bundle use gsi_bundlemod, only: gsi_bundlegetpointer use mpeu_util, only: die implicit none type(spec_vars),intent(in ) :: sp type(sub2grid_info),intent(in ) :: grd integer(i_kind),intent(in ) :: mype character(len=*),parameter::myname='general_test_inverses' integer(i_kind) :: index (5) integer(i_kind) :: i,j,n,ig,ncstep,istatus real(r_kind),dimension(grd%nlat,grd%nlon):: u1,v1,u2,u3,v2,t1,t2 real(r_kind),dimension(sp%nc):: s1,s2,s3 real(r_kind),dimension(sp%nc):: d1,d2,d3 real(r_kind):: diffmax, absmax ! smallfrac is expected size of lagest fractional roundoff error real(r_kind),parameter:: smallfrac=1.e-9_r_kind real(r_kind),dimension(:,:,:),pointer::ges_tv_nt=>NULL() s1=zero ; s2=zero ; s3=zero t1=zero ; t2=zero ; v1=zero call gsi_bundlegetpointer (gsi_metguess_bundle(ntguessig),'tv',ges_tv_nt, istatus) if(istatus/=0) call die(myname,'variable tv missing, ier=',istatus) ! use the following field to create a test field t1 call gather_stuff2(ges_tv_nt,t1,mype,0) ! only perform the test on one processor. if (mype==0) then ! load S1 with something write(777,*) ' *********************************************************' write(777,*) ' * Test spectral routines *' write(777,*) ' * diff=difference of numbers that should be the same *' write(777,*) ' * except for roundoff effects *' write(777,*) ' * fracdiff=fractional difference of numbers that *' write(777,*) ' * should be the same except for roundoff: only *' write(777,*) ' * printed if > parameter smallfrac *' write(777,*) ' *********************************************************' write(777,*) ' smallfrac=',smallfrac ! ! ! ********************************************************* ! TEST SCALAR TRANSFORM AND ITS INVERSE call general_g2s0(grd,sp,s1,t1) ! compute spectral coefs s1 call general_s2g0(grd,sp,s1,t2) ! compute spectrally truncated field t2 call general_g2s0(grd,sp,s3,t2) ! recompute spectra from field ! ! check all spectral coefficients write(777,*) ' ' write(777,*) ' **** Test that s2g0 and g2s0 are inverses ****' absmax=zero diffmax=zero do i=1,sp%nc if (s1(i)/=zero .and. abs((s1(i)-s3(i))/s1(i)) > smallfrac) then write(777,'(a,i6,1p3e18.10)') 'i,s1,s3,fracdiff = ' & ,i,s1(i),s3(i),(s1(i)-s3(i))/s1(i) end if if (abs(s1(i)) > absmax) absmax=abs(s1(i)) if (abs(s3(i)) > absmax) absmax=abs(s3(i)) if (abs(s1(i)-s3(i)) > diffmax) diffmax=abs(s1(i)-s3(i)) end do write(777,*) ' max of absolute values tested =',absmax write(777,*) ' max of absolute diff obtained =',diffmax ! ! ! ***************************************************************** ! TEST VECTOR TRANSFORM AND ITS INVERSE ! use same spectral coefs as for previous test u1=zero; u2=zero; v1=zero; v2=zero s2=zero; s3=zero d2=zero; d3=zero s1(1)=zero ! this component is always zero d1=0.1_r_kind*s1 call general_zds2uvg(grd,sp,s1,d1,u1,v1) call general_uvg2zds(grd,sp,s2,d2,u1,v1) ! write(777,*) ' ' write(777,*) ' **** Test that zds2uvg and uvg2zds are inverses for vorticity ****' absmax=zero diffmax=zero do i=1,sp%nc if (s1(i)/=zero .and. abs((s1(i)-s2(i))/s1(i)) > smallfrac) then write(777,'(a,i6,1p3e18.10)') 'i,s1,s2,fracdiff = ' & ,i,s1(i),s2(i),(s1(i)-s2(i))/s1(i) end if if (abs(s1(i)) > absmax) absmax=abs(s1(i)) if (abs(s2(i)) > absmax) absmax=abs(s2(i)) if (abs(s1(i)-s2(i)) > diffmax) diffmax=abs(s1(i)-s2(i)) end do write(777,*) ' max of absolute values tested =',absmax write(777,*) ' max of absolute diff obtained =',diffmax ! write(777,*) ' ' write(777,*) ' **** Test that zds2uvg and uvg2zds are inverses for divergence ****' absmax=zero diffmax=zero do i=1,sp%nc if (d1(i)/=zero .and. abs((d1(i)-d2(i))/d1(i)) > smallfrac) then write(777,'(a,i6,1p3e18.10)') 'i,d1,d2,fracdiff = ' & ,i,d1(i),d2(i),(d1(i)-d2(i))/d1(i) end if if (abs(d1(i)) > absmax) absmax=abs(d1(i)) if (abs(d2(i)) > absmax) absmax=abs(d2(i)) if (abs(d1(i)-d2(i)) > diffmax) diffmax=abs(d1(i)-d2(i)) end do write(777,*) ' max of absolute values tested =',absmax write(777,*) ' max of absolute diff obtained =',diffmax ! ! ! ********************************************************* ! TEST THAT ADJOINT SCALAR ROUTINES ARE INVERSES OF EACH OTHER call general_s2g0_ad(grd,sp,s1,t1) ! compute spectral adjoint coefs s1 call general_g2s0_ad(grd,sp,s1,t2) ! compute spectrally truncated adjoint field t2 call general_s2g0_ad(grd,sp,s3,t2) ! recompute spectra from field ! ! check all spectra write(777,*) ' ' write(777,*) ' **** Test that s2g0_ad and g2s0_ad are inverses ****' absmax=zero diffmax=zero do i=1,sp%nc if (s1(i)/=zero .and. abs((s1(i)-s3(i))/s1(i)) > smallfrac) then write(777,'(a,i6,1p3e18.10)') 'i,s1,s3,fracdiff = ' & ,i,s1(i),s3(i),(s1(i)-s3(i))/s1(i) end if if (abs(s1(i)) > absmax) absmax=abs(s1(i)) if (abs(s3(i)) > absmax) absmax=abs(s3(i)) if (abs(s1(i)-s3(i)) > diffmax) diffmax=abs(s1(i)-s3(i)) end do write(777,*) ' max of absolute values tested =',absmax write(777,*) ' max of absolute diff obtained =',diffmax! ! ! ! *********************************************************** ! TEST THAT ADJOINT VECTOR ROUTINES ARE INVERSES OF EACH OTHER u1=zero; v1=zero s2=zero; d2=zero s1(1)=zero d1=0.1_r_kind*s1 call general_uvg2zds_ad(grd,sp,s1,d1,u1,v1) call general_zds2uvg_ad(grd,sp,s2,d2,u1,v1) ! write(777,*) ' ' write(777,*) ' **** Test that zds2uvg_ad and uvg2zds_ad are' & ,' inverses for vorticity ****' absmax=zero diffmax=zero do i=1,sp%nc if (s1(i)/=zero .and. abs((s1(i)-s2(i))/s1(i)) > smallfrac) then write(777,'(a,i6,1p3e18.10)') 'i,s1,s2,fracdiff = ' & ,i,s1(i),s2(i),(s1(i)-s2(i))/s1(i) end if if (abs(s1(i)) > absmax) absmax=abs(s1(i)) if (abs(s2(i)) > absmax) absmax=abs(s2(i)) if (abs(s1(i)-s2(i)) > diffmax) diffmax=abs(s1(i)-s2(i)) end do write(777,*) ' max of absolute values tested =',absmax write(777,*) ' max of absolute diff obtained =',diffmax ! write(777,*) ' ' write(777,*) ' **** Test that zds2uvg_ad and uvg2zds_ad are' & ,' inverses for divergence ****' absmax=zero diffmax=zero do i=1,sp%nc if (d1(i)/=0 .and. abs((d1(i)-d2(i))/d1(i)) > smallfrac) then write(777,'(a,i6,1p3e18.10)') 'i,d1,d2,fracdiff = ' & ,i,d1(i),d2(i),(d1(i)-d2(i))/d1(i) end if if (abs(d1(i)) > absmax) absmax=abs(d1(i)) if (abs(d2(i)) > absmax) absmax=abs(d2(i)) if (abs(d1(i)-d2(i)) > diffmax) diffmax=abs(d1(i)-d2(i)) end do write(777,*) ' max of absolute values tested =',absmax write(777,*) ' max of absolute diff obtained =',diffmax ! ! ! *********************************************** ! TEST ADJOINT FOR SELECTED ELEMENTS OF JACOBIAN MATRIX ! test that s2g0_ad is adjoint of s2g0_ad ! write(777,*) ' ' write(777,*) ' **** Apply Jacobian test to selected elements' & ,' of s2g0_ad and s2g0 ***' ! absmax=zero diffmax=zero ! ! So only 5 lats tested to cut down on computation index(1)=1 ! S. pole index(2)=2 ! 1st Lat next to S. pole index(3)=(grd%nlat+1)/2 ! equator or ist N. of equator if no equator index(4)=grd%nlat-1 ! 1st lat next to N. pole index(5)=grd%nlat ! N. pole ! ! Only check a subset of spectral coefs to reduce computation ncstep=4*sp%jcap/3 if (mod(ncstep,2) == 0) ncstep=ncstep+1 ! then both real and imag parts tested do n=1,sp%nc,ncstep if (mod(n,2) ==0 .and. n <= 2*sp%jcap+2) then d1(n)=zero ! these are imag parts of coefs for zonal wave number 0 else i=3 ! only one longitude tested ! do ig=1,5 !loop over selected lats to test d1=zero; d2=zero u1=zero; u2=zero j = index(ig) d1(n)=one u2(j,i)=one call general_s2g0_ad(grd,sp,d2,u2) call general_s2g0(grd,sp,d1,u1) d3(n)=u1(j,i)-d2(n) if (abs(d3(n)) > smallfrac*(abs(d2(n))+abs(u1(j,i))) ) then write(777,'(a,2i7,1p3e18.10)') ' latindex,spec-index,s,g,diff ' & ,j,n,d2(n),u1(j,i),d3(n) endif if (abs(d2(n)) > absmax) absmax=abs(d2(n)) if (abs(u1(j,i)) > absmax) absmax=abs(u1(j,i)) if (abs(d3(n)) > diffmax) diffmax=abs(d3(n)) enddo ! loop over selected lats endif ! test if imag part of zonal wave 0 coef enddo ! loop over spec index n write(777,*) ' max of absolute values tested =',absmax write(777,*) ' max of absolute diff obtained =',diffmax ! ! ! ******************************************* ! TEST ADJOINT USING NORM TEST ! test that uvg2zds_ad is adjoint of uvg2zds ! s is vort spectral coefs here u1=zero; u2=zero; v1=zero; v2=zero s1=zero; s2=zero; d1=zero; d2=zero ! fill wind with random numbers, then spectrally truncate call random_number(u1) call random_number(v1) call general_uvg2zds(grd,sp,s1,d1,u1,v1) u1=zero v1=zero call general_zds2uvg(grd,sp,s1,d1,u1,v1) ! ! fill spectral adjoint variables with random numbers ! but with magnitudes like we have in real cases call random_number(d2) call random_number(s2) d2=d2*d1 ! this will set values that should be 0 to 0. s2=s2*s1 ! this will set values that should be 0 to 0. ! ! call 2 routines to compare s1=zero d1=zero u2=zero v2=zero call general_uvg2zds_ad(grd,sp,s2,d2,u2,v2) call general_uvg2zds(grd,sp,s1,d1,u1,v1) ! ! d3(1) is the norm in terms of spectra d3(1:3)=zero do i=1,sp%nc d3(1)=d3(1) + s1(i)*s2(i) + d1(i)*d2(i) enddo ! ! d3(2) is the norm in terms of grid values do i=1,grd%nlat do j=1,grd%nlon d3(2)=d3(2) + u1(i,j)*u2(i,j) + v1(i,j)*v2(i,j) enddo enddo ! ! d3(3) is the difference in the norms d3(3)=d3(1)-d3(2) ! write(777,*) ' ' write(777,*) ' **** Apply norm test to uvg2zds and uvg2zds_ad ****' write(777,'(a,1p3e18.10)') ' zdnorm, uvnorm, diff ',d3(1:3) ! ! ! **************************************************** ! TEST VECTOR TRANSFORM ADJOINT ZDS2UVG USING NORM TEST ! test that zds2uvg_ad is adjoint of zds2uvg ! s is vort spectral coefs here u1=zero; u2=zero; v1=zero; v2=zero s1=zero; s2=zero; d1=zero; d2=zero ! fill wind with random numbers, then spectrally truncate call random_number(u1) call random_number(v1) call general_uvg2zds(grd,sp,s1,d1,u1,v1) u1=zero v1=zero call general_zds2uvg(grd,sp,s1,d1,u1,v1) ! ! fill spectral adjoint variables with random numbers ! but with magnitudes like we have in real cases call random_number(d2) call random_number(s2) d2=d2*d1 ! this will set values that should be 0 to 0. s2=s2*s1 ! this will set values that should be 0 to 0. ! ! call 2 routines to compare s1=zero d1=zero u2=zero v2=zero call general_zds2uvg(grd,sp,s2,d2,u2,v2) call general_zds2uvg_ad(grd,sp,s1,d1,u1,v1) ! ! d3(1) is the norm in terms of spectra d3(1:3)=zero do i=1,sp%nc d3(1)=d3(1) + s1(i)*s2(i) + d1(i)*d2(i) enddo ! ! d3(2) is the norm in terms of grid values do i=1,grd%nlat do j=1,grd%nlon d3(2)=d3(2) + u1(i,j)*u2(i,j) + v1(i,j)*v2(i,j) enddo enddo ! ! d3(3) is the difference in the norms d3(3)=d3(1)-d3(2) ! write(777,*) ' ' write(777,*) ' **** Apply norm test to zds2uvg and zds2uvg_ad ****' write(777,'(a,1p3e18.10)') ' zdnorm, uvnorm, diff ',d3(1:3) ! ! ! ************************************************** ! TEST SCALAR TRANSFROM ADJOINT S2G USING NORM TEST ! u is scalar field here ! fill adjoint field with random numbers, then compute adjoint spectra call random_number(u1) u3=u1 s1=zero call general_s2g0_ad(grd,sp,s1,u3) ! fill spectral variables with random numbers ! but with magnitudes like we have in real cases call random_number(s2) s2=s2*s1 ! this will set values that should be 0 to 0. s3=s2 u2=zero call general_s2g0(grd,sp,s3,u2) ! ! d3(1) is the norm in terms of spectra d3(1:3)=zero do i=1,sp%nc d3(1)=d3(1) + s1(i)*s2(i) enddo ! ! d3(2) is the norm in terms of grid values do i=1,grd%nlat do j=1,grd%nlon d3(2)=d3(2) + u1(i,j)*u2(i,j) enddo enddo ! ! d3(3) is the difference in the norms d3(3)=d3(1)-d3(2) ! write(777,*) ' ' write(777,*) ' **** Apply norm test to s2g0 and s2g0_ad ****' write(777,'(a,1p3e18.10)') ' snorm, gnorm, diff ',d3(1:3) ! ! ! **************************************************** ! TEST SCALAR TRANSFORM ADJOINT G2S USING NORM TEST ! u is scalar field here ! fill field with random numbers, then spectrally truncate call random_number(u1) u3=u1 s1=zero call general_g2s0(grd,sp,s1,u3) ! ! fill spectral adjoint variables with random numbers ! but with magnitudes like we have in real cases call random_number(s2) s2=s2*s1 ! this will set values that should be 0 to 0. s3=s2 u2=zero call general_g2s0_ad(grd,sp,s3,u2) ! ! d3(1) is the norm in terms of spectra d3(1:3)=zero do i=1,sp%nc d3(1)=d3(1) + s1(i)*s2(i) enddo ! ! d3(2) is the norm in terms of grid values do i=1,grd%nlat do j=1,grd%nlon d3(2)=d3(2) + u1(i,j)*u2(i,j) enddo enddo ! ! d3(3) is the difference in the norms d3(3)=d3(1)-d3(2) ! write(777,*) ' ' write(777,*) ' **** Apply norm test to g2s0 and g2s0_ad ****' write(777,'(a,1p3e18.10)') ' snorm, gnorm, diff ',d3(1:3) ! ! end if ! end mype ! ! end subroutine general_test_inverses