module m_Generate_element_Si implicit none public Generate_element_Si public get_S integer, parameter, private :: NONE = 0 integer, parameter, private :: TEMPERATURE = 1 integer, parameter, private :: SALINITY = 2 real, parameter, private :: TEM_MIN = -2.5 real, parameter, private :: TEM_MAX = 35.0 real, parameter, private :: SAL_MIN = 5.0 real, parameter, private :: SAL_MAX = 41.0 logical, parameter, private :: VERT_INTERP_GRID = .true. contains subroutine Generate_element_Si(S, obstype, fld, depths, nx, ny, nz, t) use mod_measurement use m_obs implicit none real, dimension(nobs), intent(inout) :: S ! input/output vector character(len=5), intent(in) :: obstype ! the model fld type in "fld" integer, intent(in) :: nx,ny,nz ! grid size real, intent(in) :: fld (nx,ny) ! field to be placed in Si real, intent(in) :: depths(nx,ny) ! depth mask -- needed for support integer, intent(in), optional :: t !time of fld integer :: iobs integer :: i, j, ip1, jp1 integer :: ix, jy, imin, imax, jmin, jmax, cnt logical :: isprofile real :: depth integer :: ns real, parameter :: undef = 999.9 ! land points have value huge() ! TEM, GTEM, SAL and GSAL come from profiles isprofile = (trim(obstype) .eq. 'SAL' .or.& trim(obstype) .eq. 'GSAL' .or.& trim(obstype) .eq. 'TEM' .or.& trim(obstype) .eq. 'GTEM') do iobs = 1, nobs if (trim(obstype) == obs(iobs) % id) then if (trim(obstype) .ne. 'TSLA' .or. obs(iobs) % date == t) then ! Get model gridcell i = obs(iobs) % ipiv j = obs(iobs) % jpiv ip1 = min(i + 1, nx) jp1 = min(j + 1, ny) depth = obs(iobs) % depth !TODO: 1. check consistency for ns = 1 vs ns = 0 ! 2. check consistency of running from -ns to +ns (this can ! lead perhaps for averaginf over -1 0 1 = 3 x 3 instead ! of 2 x 2 grid cells if ns = 1 if (depth .lt. 10.0 .and. .not. isprofile) then ! satellite data ns = obs(iobs) % ns if(ns .lt. 2) then ! point data : zero support S(iobs) = fld(i, j) * obs(iobs) % a1 & + fld(ip1, j) * obs(iobs) % a2 & + fld(ip1, jp1) * obs(iobs) % a3 & + fld(i, jp1) * obs(iobs) % a4 else ! data support assumed a square of 2ns * 2ns grid cells imin = max( 1, i - ns) imax = min(nx, i + ns) jmin = max( 1, j - ns) jmax = min(ny, j + ns) cnt = 0 S(iobs) = 0.0 do jy = jmin, jmax do ix = imin, imax ! Removes data on land, absolute values larger than 1000 and NaNs if (depths(ix, jy) > 1.0 .and. abs(fld(ix, jy)) < 10.0d3 .and. fld(ix, jy) + 1.0d0 /= fld(ix, jy)) then S(iobs) = S(iobs) + fld(ix, jy) cnt = cnt + 1 endif enddo enddo if (cnt == 0) then print *, ' observation on land ', i, j, obs(iobs) % d stop 'm_Generate_element_Sij: report bug to LB (laurentb@nersc.no)' end if S(iobs) = S(iobs) / real(cnt) endif elseif(isprofile) then ! in-situ data (in depth) print *,'(m_Generate_element_Si does not handle profiles yet)' stop '(m_Generate_element_Si)' else stop 'Generate_element_Sij: not a profile but depth is deeper than 10m' endif end if ! obs and model are at similar time end if ! (trim(obstype) == obs(iobs) % id) then end do end subroutine Generate_element_Si ! Get S = HA for in-situ data. Linearly interpolate for obs positioned ! between the layer centres; otherwise use the layer value for the obs above ! the middle of the first layer or below the middle of the last layer. ! ! Note - this procedure parses through all obs for each ensemble member ! to work out profiles. This indeed invlolves some redundancy because ! this work could be done only once. However, the penalty (I think) is ! quite small compared to the time required for reading the fields from ! files and does not worth modifying (and complicating) the code. ! subroutine get_S(S, obstag, nobs, obs, iens) use mod_measurement use m_insitu use m_get_mod_fld use m_io_mod_fld ! CKB, FM !use m_parse_blkdat use m_get_mod_xyz ! was: m_parse_blkdat use m_parameters implicit none real, dimension(nobs), intent(inout) :: S character(*), intent(in) :: obstag integer, intent(in) :: nobs type(measurement), dimension(nobs) :: obs integer, intent(in) :: iens real, parameter :: ONEMETER = 9806.0 ! obs stuff ! integer :: p, o integer, allocatable, dimension(:) :: ipiv, jpiv real, allocatable, dimension(:) :: a1, a2, a3, a4 ! grid stuff ! integer :: k integer :: ni, nj, nk real :: rdummy ! vertical stuff ! real, allocatable, dimension(:) :: zgrid, zcentre, zgrid_prev, zcentre_prev real, allocatable, dimension(:) :: v, v_prev ! fields & I/O stuff ! real, allocatable, dimension(:, :) :: dz2d, v2d, sstbias, mld, offset, z integer :: tlevel character(8) :: fieldtag character(3) :: cmem character(80) :: fname real, dimension(2, 2) :: dz_cell, v_cell real :: dz, depth, z0, z1, z01, delta integer :: field field = NONE if (nobs == 0) then return end if if (master .and. iens == 1) then if (VERT_INTERP_GRID) then print *, trim(obstag), ': vertical interpolation in grid space' else print *, trim(obstag), ': vertical interpolation in physical space' end if end if ! ! 1. Identify profiles presented in "obs" ! ! note that profiles are being used in the vertical superobing by each ! ensemble member... ! call insitu_setprofiles(obstag, nobs, obs) allocate(ipiv(nprof)) allocate(jpiv(nprof)) allocate(a1(nprof)) allocate(a2(nprof)) allocate(a3(nprof)) allocate(a4(nprof)) allocate(zgrid(nprof)) allocate(zgrid_prev(nprof)) allocate(zcentre(nprof)) allocate(zcentre_prev(nprof)) allocate(v(nprof)) allocate(v_prev(nprof)) ipiv = obs(pstart(1 : nprof)) % ipiv jpiv = obs(pstart(1 : nprof)) % jpiv a1 = obs(pstart(1 : nprof)) % a1 a2 = obs(pstart(1 : nprof)) % a2 a3 = obs(pstart(1 : nprof)) % a3 a4 = obs(pstart(1 : nprof)) % a4 ! ! 2. Map the observations for this ensemble member proceeding by layers ! to reduce I/O: ! ! -cycle through layers ! -find the middle of this layer ! -cycle through profiles ! -for each obs between the middle of the prev layer and the ! middle of this layer ! -interpolate the field value ! -write to S ! ! get grid dimensions ! !call parse_blkdat('idm ','integer', rdummy, ni) !call parse_blkdat('jdm ','integer', rdummy, nj) !call parse_blkdat('kdm ','integer', rdummy, nk) call get_mod_xyz(ni, nj, nk) ! [CKB,FM] Changed from using m_parse_blkdat allocate(v2d(ni, nj)) allocate(dz2d(ni, nj)) if (trim(obstag) == 'SAL' .or. trim(obstag) == 'GSAL') then fieldtag = 'saln ' field = SALINITY elseif (trim(obstag) == 'TEM' .or. trim(obstag) == 'GTEM') then fieldtag = 'temp ' field = TEMPERATURE else if (master) then print *, 'ERROR: get_S(): unknown observatioon tag "', trim(obstag), '"' end if stop end if write(cmem, '(i3.3)') iens fname = 'forecast'//cmem if (field == TEMPERATURE .and. prm_prmestexists('sstb')) then allocate(sstbias(ni, nj)) allocate(mld(ni, nj)) allocate(offset(ni, nj)) allocate(z(ni, nj)) z = 0.0d0 tlevel = 1 call get_mod_fld_new(trim(fname), sstbias, iens, 'sstb ', 0, tlevel, ni, nj) if (tlevel == -1) then if (master) then print *, 'ERROR: get_mod_fld_new(): failed for "sstb"' end if stop end if call get_mod_fld_new(trim(fname), mld, iens, 'dpmixl ', 0, tlevel, ni, nj) if (tlevel == -1) then if (master) then print *, 'ERROR: get_mod_fld_new(): failed for "dpmixl"' end if stop end if end if ! cycle through layers ! tlevel = 1 do k = 1, nk + 1 if (k == 1) then zgrid_prev = 0.0 zcentre_prev = 0.0 end if if (k <= nk) then ! read the depth and the requested field at this layer ! call get_mod_fld_new(trim(fname), dz2d, iens, 'dp ', k, tlevel, ni, nj) if (tlevel == -1) then if (master) then print *, 'ERROR: get_mod_fld_new(): failed for "dp"' end if stop end if call get_mod_fld_new(trim(fname), v2d, iens, fieldtag, k, tlevel, ni, nj) if (tlevel == -1) then if (master) then print *, 'ERROR: get_mod_fld_new(): failed for "', fieldtag, '"' end if stop end if end if ! calculate correction from SST bias at this depth ! if (field == TEMPERATURE .and. prm_prmestexists('sstb')) then offset = 0.0d0 z = z + dz2d / 2.0 ! at the middle of the layer where (mld > 0.0d0 .and. mld < 1.0d8) ! < 10000 m offset = sstbias * exp(-(z / mld) ** 2) end where v2d = v2d - offset z = z + dz2d / 2.0 end if ! cycle through profiles ! do p = 1, nprof if (k <= nk) then dz_cell(:, :) = dz2d(ipiv(p) : ipiv(p) + 1, jpiv(p) : jpiv(p) + 1) dz = dz_cell(1, 1) * a1(p) + dz_cell(2, 1) * a2(p)& + dz_cell(1, 2) * a3(p) + dz_cell(2, 2) * a4(p) dz = dz / ONEMETER zgrid(p) = zgrid_prev(p) + dz zcentre(p) = (zgrid_prev(p) + zgrid(p)) / 2.0 v_cell(:, :) = v2d(ipiv(p) : ipiv(p) + 1, jpiv(p) : jpiv(p) + 1) v(p) = v_cell(1, 1) * a1(p) + v_cell(2, 1) * a2(p)& + v_cell(1, 2) * a3(p) + v_cell(2, 2) * a4(p) else ! for the lower half of the last layer -- just use the layer value ! (note that there was no reading in this case, so that ! v = v_prev) zcentre(p) = zgrid(p) end if if (k == 1) then v_prev(p) = v(p) end if ! cycle through the obs, pick the ones in between the middle of the ! previous layer and the middle of this layer, interpolate the ! ensemble field to their locations, and save the results in S ! z0 = zcentre_prev(p) z1 = zcentre(p) z01 = zgrid_prev(p) if (z1 == z0) then cycle end if do while (pstart(p) <= pend(p)) o = pstart(p) depth = obs(o) % depth ! check that this obs is within the current layer ! if (depth > z1 .and. k <= nk) then exit ! next profile elseif (depth >= z0 .and. depth <= z1) then if (.not. VERT_INTERP_GRID) then ! interpolate linearly in physical space ! S(o) = (z1 - depth) / (z1 - z0) * v_prev(p) +& (depth - z0) / (z1 - z0) * v(p) else ! interpolate linearly in the grid space ! if (depth < z01) then delta = 0.5d0 * (depth - z0) / (z01 - z0) else delta = 0.5d0 + 0.5d0 * (depth - z01) / (z1 - z01) end if S(o) = (1.0d0 - delta) * v_prev(p) + delta * v(p) end if ! Here we check the range of interpolated ensemble values; ! the range of observed values is checked in insitu_QC(). ! if (field == SALINITY) then if ((S(o) < SAL_MIN .or. S(o) > SAL_MAX) .and. master) then print *, 'WARNING: get_S(): suspicious value (SAL): ',& 'iens =', iens, ', obs =', o, ', profile = ', p,& 'depth =', depth, ', S =', S(o) end if else if (field == TEMPERATURE) then if ((S(o) < TEM_MIN .or. S(o) > TEM_MAX) .and. master) then print *, 'WARNING: get_S(): suspicious value (TEM): ',& 'iens =', iens, ', obs =', o, ', profile = ', p,& 'depth =', depth, ', S =', S(o) end if end if else ! k == nk + 1 S(o) = v(p) end if ! go to the next obs ! pstart(p) = pstart(p) + 1 end do ! o end do ! p zgrid_prev = zgrid zcentre_prev = zcentre v_prev = v end do ! k deallocate(dz2d) deallocate(v2d) deallocate(v_prev) deallocate(v) deallocate(zcentre_prev) deallocate(zcentre) deallocate(zgrid_prev) deallocate(zgrid) deallocate(a4) deallocate(a3) deallocate(a2) deallocate(a1) deallocate(jpiv) deallocate(ipiv) if (allocated(sstbias)) then deallocate(sstbias) deallocate(mld) deallocate(offset) deallocate(z) end if end subroutine get_S end module m_Generate_element_Si