123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- 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
|