! File : p_prep_obs.F90 ! ! Created: unknown ! ! Author: unknown ! ! Purpose: Read data from different data sources and convert it to ! type(measurement). ! ! Description: The code calls different subroutines for particular types of ! input data, depending on the source, observation type and ! format. The output is the array of type(measurement) that ! contains, in particular, pivot points and bilinear ! interpolation coefficients for each observation. This array ! is written to "observations.uf" in binary format. ! ! Modifications: 30/01/2008 - Pavel Sakov gave a trim (formatted) -- sorry ! for that, could not stand -- and modified to allow in-situ ! argo data from ifremer. ! 02/09/2008 - Pavel Sakov added superobing for SST and SLA data ! 17/08/2010 PS - turned (3D) superobing on for Argo obs ! 09/11/2012 Geir Arne Waagbo: Added support for OSISAF ice drift obs program p_prep_obs use mod_measurement use mod_grid use m_read_CLS_header use m_read_CLS_data use m_read_CLS_SST_grid use m_read_MET_SST_grid use m_read_CLS_TSLA_grid use m_read_CLS_SST use m_read_CLS_SSH use m_read_CLS_SLA use m_read_CLS_TSLA use m_read_MET_SST use m_read_CERSAT_data use m_read_OSISAF_data use m_read_ifremer_argo use m_read_jpl_hice use m_read_FFI_glider use m_read_metno_icec use m_get_def_wet_point use m_write_wet_file use m_get_mod_grid use m_parse_blkdat use m_read_amsr_norsex use m_superobs use m_uobs implicit none integer, parameter :: STRLEN = 512 type (measurement), allocatable :: data(:) type (measurement), allocatable :: obs(:) type (grid) :: gr integer :: nx, ny real, allocatable, dimension(:,:) :: depths, modlat, modlon integer, parameter :: maxobs = 5000000 character(STRLEN) :: fname, fnamehdr, dataformat, producer character(len=3) :: form character(len=5) :: obstype character(len=1) :: offset integer :: nrobs integer :: grpoints, k real :: factor, var real :: rdummy, mindx, meandx logical :: data_eq_obs ! superobs logical :: dosuperob logical :: is3d integer :: nrsobs type(measurement), allocatable :: sobs(:) integer :: i integer :: nthisobs integer, allocatable, dimension(:) :: thisobs gr = default_grid data_eq_obs = .false. open(10, file = 'infile.data') read(10, '(a)') producer read(10, '(a)') obstype read(10, '(a)') fnamehdr read(10, '(a)') fname close(10) print *, 'Data producer: ', trim(producer) print *, 'Data to be processed for TOPAZ: ', trim(obstype) print *, 'Filenames to be processed are: "', trim(fnamehdr), '" "', trim(fname), '"' print *, 'Result of processing is stored in temporary file "observation.uf"' ! Get grid dimensions from blkdat.input ! call parse_blkdat('idm ', 'integer', rdummy, nx) call parse_blkdat('jdm ', 'integer', rdummy, ny) allocate(depths(nx, ny)) allocate(modlon(nx, ny)) allocate(modlat(nx, ny)) dosuperob = .false. is3d = .false. ! Fill the "data" array by calling subroutines specific for the producer ! and observation type ! if (trim(producer) == 'Reynolds') then if (trim(obstype) == 'SST') then dosuperob = .true. call read_CLS_header(fnamehdr, gr, dataformat, form, factor, var) grpoints = gr % nx * gr % ny allocate(data(grpoints)) allocate(obs(maxobs)) call read_CLS_data(fname, obstype, dataformat, gr, form, data, factor, var) print*, 'Reynolds- ', obstype, ' data has been scaled by a factor = ', factor else stop 'ERROR: Reynolds only produce SST' endif else if (trim(Producer) == 'MET') then if (trim(obstype) == 'SST') then dosuperob = .true. call read_MET_SST_grid(fnamehdr, gr) grpoints = gr % nx * gr % ny allocate(data(grpoints)) allocate(obs(maxobs)) call read_MET_SST(fname, gr, data) else stop 'ERROR: OSTIA (MET) only produces SST' endif else if (trim(Producer) == 'NSIDC-AMSR') then if (trim(obstype) == 'ICEC') then dosuperob = .true. call read_amsr_norsex(fname, gr, data, obstype) allocate (obs(maxobs)) else print *, 'No ',obstype, ' data from:', Producer stop 'ERROR: p_prep_obs' endif else if (trim(Producer) == 'METNO') then if (trim(obstype) == 'ICEC') then dosuperob = .true. call read_metno_icec_repro(fname, data, gr) allocate (obs(size(data))) elseif (index(obstype,'idrf')>0) then print *, 'OSISAF Ice Drift: ', obstype offset = obstype(5:5) print *, 'Offset: ', offset ! The number of days before analysis day dosuperob = .false. call read_CLS_header(fnamehdr, gr, dataformat, form, factor, var) grpoints = gr % nx ! NB - 2 vector components - irregular grid allocate(data(grpoints)) allocate(obs(maxobs)) call read_OSISAF_data(trim(fname), gr, data, grpoints, var, offset) print *, producer, obstype, 'data has been scaled by a factor = ', factor else print *, 'There can be no ', obstype,' data from', Producer stop endif elseif (trim(producer) == 'CLS') then if (trim(obstype) == 'SLA') then dosuperob = .true. ! call read_CLS_SST_grid() here because SST data grid has the same ! structure call read_CLS_SST_grid(fnamehdr, gr) grpoints = gr % nx * gr % ny allocate(data(grpoints)) allocate(obs(maxobs)) call read_CLS_SLA(fname, gr, data) elseif (trim(obstype) == 'SSH') then dosuperob = .true. call read_CLS_SST_grid(fnamehdr, gr) grpoints = gr % nx * gr % ny allocate(data(grpoints)) allocate(obs(maxobs)) call read_CLS_SSH(fname, gr, data) elseif (trim(obstype) == 'SST') then dosuperob = .true. call read_CLS_SST_grid(fnamehdr, gr) grpoints = gr % nx * gr % ny allocate(data(grpoints)) allocate(obs(maxobs)) call read_CLS_SST(fname, gr, data) elseif (trim(obstype) == 'TSLA') then dosuperob = .true. call read_CLS_TSLA_grid(fnamehdr, gr) print *, 'read_CLS_TSLA_grid finished, total # of obs = ', gr % nx grpoints = gr % nx allocate(data(grpoints)) allocate(obs(maxobs)) call read_CLS_TSLA(fname,gr,data) else print *, 'data of type "', trim(obstype),'" from producer "', producer, '" is not handled' stop 'ERROR: p_prep_obs' endif else if (trim(producer) == 'NSIDC') then if (trim(obstype) == 'ICEC') then dosuperob = .true. call read_CLS_header(fnamehdr, gr, dataformat, form, factor, var) grpoints = gr % nx * gr % ny allocate(data(grpoints)) allocate(obs(maxobs)) call read_CLS_data(fname, obstype, dataformat, gr, form, data, factor, var) print *, producer, obstype, 'data has been scaled by a factor = ', factor else print *, 'no data of type "', trim(obstype),'" from producer "', producer, '" is not handled' stop 'ERROR: p_prep_obs' endif else if (trim(producer) == 'CERSAT') then if (trim(obstype) == 'idrft') then dosuperob = .false. call read_CLS_header(fnamehdr, gr, dataformat, form, factor, var) grpoints = gr % nx ! NB - 2 vector components - irregular grid allocate(data(grpoints)) allocate(obs(maxobs)) call read_CERSAT_data(trim(fname), gr, data, grpoints, var) print *, producer, obstype, 'data has been scaled by a factor = ', factor else print *, 'no data of type "', trim(obstype),'" from producer "', producer, '" is not handled' stop 'ERROR: p_prep_obs' endif elseif (trim(producer) == 'IFREMER') then dosuperob = .true. is3d = .true. read(fnamehdr, *) var print *, 'variance =', var call read_ifremer_argo(fname, obstype, var, nx, ny, data) ! PS: This is a flag to denote that read_ifremer_argo() takes care of ! filling type(measurement) array "data" in a correct way, and it should ! not be re-processed by calling get_def_wet_point(). This may not match ! the ideology behind the workflow adopted in this module and may be ! changed in future. ! data_eq_obs = .true. elseif (trim(producer) == 'JPL') then dosuperob = .true. is3d = .false. read(fnamehdr, *) var print *, 'variance = ', var call read_jpl_hice(fname, obstype, var, nx, ny, data) ! see the comment for producer = IFREMER data_eq_obs = .true. elseif (trim(producer) == 'FFI') then dosuperob = .true. is3d = .true. read(fnamehdr, *) var print *, 'variance =', var call read_FFI_glider(fname, obstype, var, nx, ny, data) data_eq_obs = .true. elseif (trim(producer) == 'MYO') then if (trim(obstype) == 'TSLA') then dosuperob = .true. call read_MYO_TSLA_grid(fnamehdr, gr) print *, 'read_CLS_TSLA_grid finished, total # of obs = ', gr % nx grpoints = gr % nx allocate(data(grpoints)) allocate(obs(maxobs)) call read_MYO_TSLA(fnamehdr,fname,gr,data) print *, 'read_MYO_TSLA finished, SIZE(data) = ', SIZE(data) else print *, 'data of type "', trim(obstype),'" from producer "', producer, '" is not handled' stop 'ERROR: p_prep_obs' endif else print *, 'unknown producer ', trim(producer), ' in "infile.data"' stop 'ERROR: p_prep_obs' endif ! Read position and depth from model grid ! call get_mod_grid(modlon, modlat, depths, mindx, meandx, nx, ny) if (.not. data_eq_obs) then ! Compute bilinear coefficients ! Extract the defined and wet data points ! Write locations to ijfile to be used in TECPLOT ! call get_def_wet_point(obs, data, gr, depths, modlat, modlon, nrobs, nx, ny) else call check_forland(data, depths, size(data), nx, ny) nrobs = size(data) allocate(obs(nrobs)) obs = data end if deallocate(data) if (trim(obstype) == 'TSLA') then call set_re_TSLA(nrobs, obs, nx, ny, modlon, modlat) end if where (obs % d + 1.0 == obs % d) obs % status = .false. end where ! Superob dense 2D data ! if (dosuperob) then allocate(sobs(nrobs)) call superob(obstype, nrobs, obs, nx, ny, modlon, modlat, nrsobs, sobs, is3d) deallocate(obs) allocate(obs(nrsobs)) obs = sobs(1 : nrsobs) nrobs = nrsobs deallocate(sobs) end if if (nrobs .ge. maxobs) then print *, 'max No. of data reached, increase it!' stop 'ERROR: p_prep_obs' elseif (nrobs .le. 1) then print *, 'less than one observation in the whole dataset' !PS 4/9/2011 stop 'ERROR: p_prep_obs: Not worth the money' end if ! Write data to the binary file "observations.uf" ! call write_wet_file(obs, nrobs) call uobs_get(obs(1 : nrobs) % id, nrobs, .true.) allocate(thisobs(nrobs)) do i = 1, nuobs nthisobs = 0 do k = 1, nrobs if (trim(unique_obs(i)) == trim(obs(k) % id)) then nthisobs = nthisobs + 1 thisobs(nthisobs) = k end if end do if (nthisobs > 0) then call obs2nc(nthisobs, obs(thisobs(1 : nthisobs))) end if end do deallocate(thisobs) print *, 'Last observation:' print '(a)',' obs var id lon lat depth ipiv jpiv nsup'//& ' 4-bilin-coeffs active orig (i,j) dp age orig_id' print '(2g10.2,a6,3f6.1,3i6,4f5.1,l5,2i7,f7.1,2i5)', obs(nrobs) deallocate(obs) deallocate(depths) deallocate(modlon) deallocate(modlat) print *, 'prep_obs: end of processing' end program p_prep_obs subroutine obs2nc(nobs, obs) use mod_measurement use nfw_mod implicit none integer, parameter :: STRLEN = 512 integer, intent(in) :: nobs type(measurement), intent(in) :: obs(nobs) character(STRLEN) :: fname integer :: ncid, obsdimid(1), lon_id, lat_id, depth_id, d_id, var_id, age_id integer :: n_id, ipiv_id, jpiv_id integer :: n(nobs) ! Create netcdf file of observations ! write(fname, '(a, a, a)') 'observations-', trim(obs(1) % id), '.nc' print *, 'dumping observations to "', trim(fname), '"' call nfw_create(fname, nf_clobber, ncid) call nfw_def_dim(fname, ncid, 'nobs', nobs, obsdimid(1)) call nfw_def_var(fname, ncid, 'lon', nf_float, 1, obsdimid(1), lon_id) call nfw_def_var(fname, ncid, 'lat', nf_float, 1, obsdimid(1), lat_id) call nfw_def_var(fname, ncid, 'depth', nf_float, 1, obsdimid(1), depth_id) call nfw_def_var(fname, ncid, 'd', nf_float, 1, obsdimid(1), d_id) call nfw_def_var(fname, ncid, 'var', nf_float, 1, obsdimid(1), var_id) call nfw_def_var(fname, ncid, 'age', nf_int, 1, obsdimid(1), age_id) call nfw_def_var(fname, ncid, 'n', nf_int, 1, obsdimid(1), n_id) call nfw_def_var(fname, ncid, 'ipiv', nf_int, 1, obsdimid(1), ipiv_id) call nfw_def_var(fname, ncid, 'jpiv', nf_int, 1, obsdimid(1), jpiv_id) call nfw_enddef(fname, ncid) call nfw_put_var_double(fname, ncid, lon_id, obs(1:nobs) % lon) call nfw_put_var_double(fname, ncid, lat_id, obs(1:nobs) % lat) call nfw_put_var_double(fname, ncid, depth_id, obs(1:nobs) % depth) call nfw_put_var_double(fname, ncid, d_id, obs(1:nobs) % d) call nfw_put_var_double(fname, ncid, var_id, obs(1:nobs) % var) call nfw_put_var_int(fname, ncid, age_id, obs(1:nobs) % date) call nfw_put_var_int(fname, ncid, ipiv_id, obs(1:nobs) % ipiv) call nfw_put_var_int(fname, ncid, jpiv_id, obs(1:nobs) % jpiv) n = int(obs(1:nobs) % h) call nfw_put_var_int(fname, ncid, n_id, n) call nfw_close(fname, ncid) end subroutine obs2nc subroutine check_forland(data, depths, nrobs, ni, nj) use mod_measurement type (measurement), intent(inout), dimension(nrobs) :: data real, dimension(ni, nj), intent(in) :: depths integer, intent(in) :: nrobs integer, intent(in) :: ni, nj integer :: o, imin, jmin, imax, jmax, nmasked nmasked = 0 do o = 1, nrobs imin = max(1, data(o) % ipiv - 1) jmin = max(1, data(o) % jpiv - 1) imax = min(ni, data(o) % ipiv + 2) jmax = min(nj, data(o) % jpiv + 2) if (any(depths(imin:imax,jmin:jmax) < 10.0 .or. depths(imin:imax,jmin:jmax) == depths(imin:imax,jmin:jmax) + 1.0)) then data(o) % status = .false. nmasked = nmasked + 1 end if end do print *, " check_forland(): ", nmasked, "obs close to land masked" end subroutine check_forland