bgckOcean_mod sourceΒΆ

  1module bgckOcean_mod
  2  ! MODULE bgckOcean_mod (prefix='ocebg' category='1. High-level functionality')
  3  !
  4  !:Purpose: to perform ocean data background check.
  5  !
  6  use midasMpi_mod
  7  use utilities_mod
  8  use obsSpaceData_mod
  9  use columnData_mod
 10  use codtyp_mod
 11  use gridStateVector_mod
 12  use gridStateVectorFileIO_mod
 13  use horizontalCoord_mod
 14  use verticalCoord_mod
 15  use statetocolumn_mod
 16  use bufr_mod
 17  use mathPhysConstants_mod
 18  use timeCoord_mod
 19  use message_mod
 20
 21  implicit none
 22
 23  save
 24  private
 25
 26  ! Public functions/subroutines
 27  public :: ocebg_bgCheckSST, ocebg_bgCheckSeaIce
 28
 29  ! External functions
 30  integer, external :: fnom, fclos
 31
 32  ! mpi topology
 33  integer           :: myLatBeg, myLatEnd
 34  integer           :: myLonBeg, myLonEnd
 35  integer           :: latPerPE, latPerPEmax, lonPerPE, lonPerPEmax
 36
 37  ! namelist variables with setting default values
 38  character(len=20) :: timeInterpType_nl = 'NEAREST' ! 'NEAREST' or 'LINEAR'
 39  integer           :: numObsBatches     = 20        ! number of batches for calling interp setup
 40  logical           :: checkWinds        = .false.   ! check winds for last 4 days to amplify error in zone of max wind speed
 41  integer           :: ndaysWinds        = 4         ! number of days in the 'winds' file to detect tropical storm (TS)
 42  integer           :: timeStepWinds     = 6         ! in hours, winds are available every timeStepWinds-hours
 43  integer           :: windForecastLeadtime = 6      ! in hours, lead time of wind forecast in the input file
 44  real(4)           :: minLatNH = 10.                ! min lat of N. hemisphere lat band where TS is detected
 45  real(4)           :: maxLatNH = 40.                ! max lat of N. hemisphere lat band where TS is detected
 46  real(4)           :: maxLatExceptionNH = 45.       ! max lat of N. hemisphere lat band allows TS to penetrate further North in some months
 47  integer           :: nmonthsExceptionNH  = 0       ! MUST NOT BE INCLUDED IN NAMELIST!
 48  character(len=3)  :: monthExceptionNH(12) = '   '  ! exceptional months where TS allowed to penetrated further North 
 49  real(4)           :: minLatSH = -35.               ! min lat of Southern hemisphere latutude band where TS is detected
 50  real(4)           :: maxLatSH = -10.               ! max lat of Southern hemisphere latutude band where TS is detected
 51  real(8)           :: smoothLenghtScale = 50000.    ! length scale. in m, to smooth the amplification error field
 52  real(8)           :: globalSelectCriteria(3) = (/5.d0, 25.d0, 30.d0/) ! global selection criteria
 53  logical           :: separateSelectCriteria = .false. ! apply separate selection criteria: sea/inland waters; insitu/satellite
 54  real(8)           :: inlandWaterSelectCriteriaSatData(3) = (/5.d0, 25.d0, 30.d0/) ! inland water, satellite selection criteria
 55  real(8)           :: inlandWaterSelectCriteriaInsitu(3)  = (/5.d0, 25.d0, 30.d0/) ! inland water, insitu selection criteria
 56  real(8)           :: seaWaterSelectCriteriaSatData(3)    = (/5.d0, 25.d0, 30.d0/) ! sea water, satellite selection criteria
 57  real(8)           :: seaWaterSelectCriteriaInsitu(3)     = (/5.d0, 25.d0, 30.d0/) ! sea water, insitu selection criteria
 58  real(4)           :: seaWaterThreshold = 0.1       ! threshold to distinguish inland water from sea water
 59  namelist /namOceanBGcheck/ timeInterpType_nl, numObsBatches, checkWinds, ndaysWinds, timeStepWinds, &
 60                             windForecastLeadtime, minLatNH, maxLatNH, maxLatExceptionNH, nmonthsExceptionNH, &
 61                             monthExceptionNH, minLatSH, maxLatSH, smoothLenghtScale, &
 62                             globalSelectCriteria, separateSelectCriteria, inlandWaterSelectCriteriaSatData, &
 63                             inlandWaterSelectCriteriaInsitu, seaWaterSelectCriteriaSatData, &
 64                             seaWaterSelectCriteriaInsitu, seaWaterThreshold 
 65
 66  character(len=3), parameter :: months(12) = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
 67
 68  contains
 69
 70  !----------------------------------------------------------------------------------------
 71  ! ocebg_bgCheckSST
 72  !----------------------------------------------------------------------------------------
 73  subroutine ocebg_bgCheckSST(obsData, dateStamp, columnTrlOnTrlLev, hco)
 74    !
 75    !:Purpose: to compute SST data background Check
 76    !
 77
 78    implicit none
 79
 80    ! Arguments:
 81    type(struct_obs)       ,   intent(inout) :: obsData           ! obsSpaceData object
 82    integer                ,   intent(in)    :: dateStamp         ! date stamp
 83    type(struct_columnData),   intent(inout) :: columnTrlOnTrlLev ! column data on trl levels
 84    type(struct_hco), pointer, intent(in)    :: hco               ! horizontal trl grid
 85
 86    ! Locals:
 87    type(struct_gsv)            :: stateVectorFGE        ! state vector containing std B estimation field
 88    type(struct_gsv)            :: stateVectorAmplFactor ! state vector for error amplification field
 89    real(4), pointer            :: stateVectorAmplFactor_ptr(:,:,:)
 90    type(struct_gsv)            :: stateVectorSeaWaterFraction ! statevector for sea water fraction
 91    integer                     :: nulnam, ierr, headerIndex, bodyIndex, obsFlag, obsVarno
 92    integer                     :: numberObs, numberObsRejected
 93    integer                     :: numberObsInsitu, numberObsInsituRejected, codeType  
 94    real(8)                     :: OER, OmP, FGE, bgCheck, seaWaterFraction
 95    type(struct_columnData)     :: columnFGE, columnSeaWaterFraction
 96    real(4), pointer            :: stateVectorFGE_ptr(:,:,:)
 97    logical                     :: checkMonth, llok
 98    integer                     :: lonIndex, latIndex, monthIndex, exceptMonthIndex
 99
100    call msg('ocebg_bgCheckSST', 'performing background check for the SST data...')
101
102    ! get mpi topology
103    call mmpi_setup_lonbands(hco%ni, lonPerPE, lonPerPEmax, myLonBeg, myLonEnd)
104    call mmpi_setup_latbands(hco%nj, latPerPE, latPerPEmax, myLatBeg, myLatEnd)
105    
106    ! Read the namelist
107    if (.not. utl_isNamelistPresent('namOceanBGcheck','./flnml')) then
108      if (mmpi_myid == 0) then
109        call msg('ocebg_bgCheckSST', 'namOceanBGcheck is missing in the namelist.')
110        call msg('ocebg_bgCheckSST', 'the default values will be taken.')
111      end if
112    else
113      ! reading namelist variables
114      nulnam = 0
115      ierr = fnom(nulnam, './flnml', 'FTN+SEQ+R/O', 0)
116      read(nulnam, nml = namOceanBGcheck, iostat = ierr)
117      if (ierr /= 0) call utl_abort('ocebg_bgCheckSST: Error reading namelist')
118      ierr = fclos(nulnam)
119      if (nmonthsExceptionNH /=0) then
120        call utl_abort('ocebg_bgCheckSST: check namOceanBGcheck namelist section: nmonthsExceptionNH should be removed')
121      end if
122      do monthIndex = 1, 12
123        if (monthExceptionNH(monthIndex) == '   ') exit
124        nmonthsExceptionNH = nmonthsExceptionNH + 1
125      end do
126    end if
127    call msg('ocebg_bgCheckSST', 'interpolation type: '//timeInterpType_nl)
128    call msg('ocebg_bgCheckSST', 'number obs batches: '//str(numObsBatches))
129    call msg('ocebg_bgCheckSST', 'check winds to detect tropical storms (TS): '//str(checkWinds))
130
131    if (separateSelectCriteria) then
132      call msg('ocebg_bgCheckSST', 'a separate selection criteria will be applied...')
133      write(*,'(a,3f7.2)') 'ocebg_bgCheckSST: inland water selection criteria for INSITU data rejection set to: ', &
134                           inlandWaterSelectCriteriaInsitu(:)
135      write(*,'(a,3f7.2)') 'ocebg_bgCheckSST: inland water selection criteria for SATELLITE data rejection set to: ', &
136                           inlandWaterSelectCriteriaSatData(:)
137      write(*,'(a,3f7.2)') 'ocebg_bgCheckSST: sea water selection criteria for INSITU data rejection set to: ', &
138                           seaWaterSelectCriteriaInsitu(:)
139      write(*,'(a,3f7.2)') 'ocebg_bgCheckSST: sea water selection criteria for SATELLITE data rejection set to: ', &
140                           seaWaterSelectCriteriaSatData(:)
141      call msg('ocebg_bgCheckSST', 'sea water fraction threshold is set to: '//str(seaWaterThreshold))
142      ! read sea water fraction
143      call gsv_allocate(stateVectorSeaWaterFraction, 1, hco, columnTrlOnTrlLev%vco, dataKind_opt = 4, &
144                        datestamp_opt = -1, mpi_local_opt = .true., &
145                        varNames_opt = (/'VF'/), hInterpolateDegree_opt ='LINEAR')
146      call gio_readFromFile(stateVectorSeaWaterFraction, './seaice_analysis', ' ','A', &
147                            unitConversion_opt=.false., containsFullField_opt=.true.)
148      ! Convert sea water fraction stateVector to column object
149      call col_setVco(columnSeaWaterFraction, col_getVco(columnTrlOnTrlLev))
150      call col_allocate(columnSeaWaterFraction, col_getNumCol(columnTrlOnTrlLev), varNames_opt = (/'VF'/))
151      call s2c_nl(stateVectorSeaWaterFraction, obsData, columnSeaWaterFraction, hco, varName_opt = 'VF', &
152                  timeInterpType = timeInterpType_nl, moveObsAtPole_opt = .true., &
153                  numObsBatches_opt = numObsBatches, dealloc_opt = .true.)
154    else
155      write(*,'(a,3f7.2)') 'ocebg_bgCheckSST: global selection criteria for data rejection is set to: ', globalSelectCriteria(:)
156    end if
157
158    ! Read First Guess Error (FGE) and put it into stateVector
159    call gsv_allocate(stateVectorFGE, 1, hco, columnTrlOnTrlLev%vco, dataKind_opt = 4, &
160                      hInterpolateDegree_opt = 'NEAREST', &
161                      datestamp_opt = -1, mpi_local_opt = .true., varNames_opt = (/'TM'/))
162    call gio_readFromFile(stateVectorFGE, './bgstddev', 'STDDEV', 'X', &
163                          unitConversion_opt=.false., containsFullField_opt=.true.)
164    if (checkWinds) then
165      call utl_tmg_start(122, '--checkWindsForSST') 
166      call msg('ocebg_bgCheckSST', 'looking for tropical storms...')
167      call msg('ocebg_bgCheckSST', 'number of days with available winds in the input winds file: '//str(ndaysWinds))
168      call msg('ocebg_bgCheckSST', 'winds are provided every: '//str(timeStepWinds)//' hours')
169      call msg('ocebg_bgCheckSST', 'wind forecast lead time: '//str(windForecastLeadtime)//' hours')
170      do exceptMonthIndex = 1, nmonthsExceptionNH
171        checkMonth = .False.
172        loop_month: do monthIndex = 1, 12
173          if (monthExceptionNH(exceptMonthIndex) == months(monthIndex)) then
174            checkMonth = .True.
175            exit loop_month
176          end if
177        end do loop_month
178        if (.not. checkMonth) then
179          write(*,*) 'ocebg_bgCheckSST: month should be one of these: ',months(:)
180          call utl_abort('ocebg_bgCheckSST: unknown month '//monthExceptionNH(exceptMonthIndex))
181        end if
182      end do
183      
184      ! amplification error field state vector  
185      call gsv_allocate(stateVectorAmplFactor, 1, hco, columnTrlOnTrlLev%vco, dataKind_opt = 4, &
186                        hInterpolateDegree_opt = 'LINEAR', datestamp_opt = dateStamp, &
187                        mpi_local_opt = .true., varNames_opt = (/'TM'/))
188      call gsv_getField(stateVectorAmplFactor, stateVectorAmplFactor_ptr)
189      stateVectorAmplFactor_ptr(myLonBeg:myLonEnd,myLatBeg:myLatEnd,1) = 1.0d0
190
191      call ocebg_getFGEamplification(stateVectorAmplFactor, dateStamp, hco)
192      
193      ! FGE state vector
194      call gsv_getField(stateVectorFGE, stateVectorFGE_ptr)
195
196      ! Apply tropical storm correction to the FGE field:
197      do latIndex = myLatBeg, myLatEnd
198        do lonIndex = myLonBeg, myLonEnd
199          stateVectorFGE_ptr(lonIndex, latIndex, 1) = stateVectorFGE_ptr(lonIndex, latIndex, 1) * &
200                                                      stateVectorAmplFactor_ptr(lonIndex, latIndex, 1)
201        end do
202      end do      
203      call gsv_deallocate(stateVectorAmplFactor)
204      call utl_tmg_stop(122)
205    end if
206
207    ! Convert FGE stateVector to column object
208    call col_setVco(columnFGE, col_getVco(columnTrlOnTrlLev))
209    call col_allocate(columnFGE, col_getNumCol(columnTrlOnTrlLev), varNames_opt = (/'TM'/))
210    call s2c_nl(stateVectorFGE, obsData, columnFGE, hco, varName_opt = 'TM', &
211                timeInterpType = timeInterpType_nl, moveObsAtPole_opt = .true., &
212                numObsBatches_opt = numObsBatches, dealloc_opt = .true.)
213
214    numberObs = 0
215    numberObsRejected = 0
216    numberObsInsitu = 0
217    numberObsInsituRejected = 0
218
219    do headerIndex = 1, obs_numheader(obsData)
220
221      bodyIndex = obs_headElem_i(obsData, obs_rln, headerIndex)
222      obsVarno  = obs_bodyElem_i(obsData, obs_vnm, bodyIndex)
223      llok = (obs_bodyElem_i(obsData, obs_ass, bodyIndex) == obs_assimilated)
224      if (llok) then
225        if (obsVarno == bufr_sst) then
226
227	  FGE = col_getElem(columnFGE, 1, headerIndex, 'TM')
228	  OmP = obs_bodyElem_r(obsData, OBS_OMP, bodyIndex)
229          OER = obs_bodyElem_r(obsData, OBS_OER, bodyIndex)
230	  codeType = obs_headElem_i(obsData, obs_ity, headerIndex)
231
232	  if (FGE /= MPC_missingValue_R8 .and. OmP /= MPC_missingValue_R8) then
233
234	    numberObs = numberObs + 1
235	    if (codeType /= codtyp_get_codtyp('satob')) numberObsInsitu = numberObsInsitu + 1
236	    call obs_bodySet_r(obsData, OBS_HPHT, bodyIndex, FGE)
237	    bgCheck = (OmP)**2 / (FGE**2 + OER**2)
238
239            if (separateSelectCriteria) then
240              seaWaterFraction = col_getElem(columnSeaWaterFraction, 1, headerIndex, 'VF')
241              if (seaWaterFraction <= seaWaterThreshold) then
242                if(codeType == codtyp_get_codtyp('satob')) then
243                  obsFlag = ocebg_setFlag(obsVarno, bgCheck, inlandWaterSelectCriteriaSatData)
244                else
245                  obsFlag = ocebg_setFlag(obsVarno, bgCheck, inlandWaterSelectCriteriaInsitu)
246                end if
247              else
248                if(codeType == codtyp_get_codtyp('satob')) then
249                  obsFlag = ocebg_setFlag(obsVarno, bgCheck, seaWaterSelectCriteriaSatData)
250                else
251                  obsFlag = ocebg_setFlag(obsVarno, bgCheck, seaWaterSelectCriteriaInsitu)
252                end if 
253              end if
254            else
255              obsFlag = ocebg_setFlag(obsVarno, bgCheck, globalSelectCriteria)
256            end if
257	
258            if (obsFlag >= 2) then
259              numberObsRejected = numberObsRejected + 1
260	      if (codeType /= codtyp_get_codtyp('satob')) numberObsInsituRejected = numberObsInsituRejected + 1
261	      write(*,'(i10,a,i5,4f10.4,i5)') numberObsRejected, &
262                                              ', sensor, codtype, lon, lat, obs.value, OmP, flag: '&
263                                              //obs_elem_c(obsData, 'STID' , headerIndex)//' data: ', codeType, &
264                                              obs_headElem_r(obsData, obs_lon, headerIndex) * MPC_DEGREES_PER_RADIAN_R8,&
265                                              obs_headElem_r(obsData, obs_lat, headerIndex) * MPC_DEGREES_PER_RADIAN_R8,&
266                                              obs_bodyElem_r(obsData, obs_var, bodyIndex), OmP, obsFlag 
267            end if
268
269	    ! update background check flags based on bgCheck
270            ! (element flags + global header flags)
271	    if (obsFlag == 1) then
272              call obs_bodySet_i(obsData, obs_flg, bodyIndex  , ibset(obs_bodyElem_i(obsData, obs_flg, bodyIndex)  , 13))
273            else if (obsFlag == 2) then
274              call obs_bodySet_i(obsData, obs_flg, bodyIndex  , ibset(obs_bodyElem_i(obsData, obs_flg, bodyIndex)  , 14))
275              call obs_bodySet_i(obsData, obs_flg, bodyIndex  , ibset(obs_bodyElem_i(obsData, obs_flg, bodyIndex)  , 16))
276              call obs_bodySet_i(obsData, obs_flg, bodyIndex  , ibset(obs_bodyElem_i(obsData, obs_flg, bodyIndex)  , 09))
277              call obs_headSet_i(obsData, obs_st1, headerIndex, ibset(obs_headElem_i(obsData, obs_st1, headerIndex), 06))
278            else if (obsFlag == 3) then
279              call obs_bodySet_i(obsData, obs_flg, bodyIndex  , ibset(obs_bodyElem_i(obsData, obs_flg, bodyIndex)  , 15))
280              call obs_bodySet_i(obsData, obs_flg, bodyIndex  , ibset(obs_bodyElem_i(obsData, obs_flg, bodyIndex)  , 16))
281              call obs_bodySet_i(obsData, obs_flg, bodyIndex  , ibset(obs_bodyElem_i(obsData, obs_flg, bodyIndex)  , 09))
282              call obs_headSet_i(obsData, obs_st1, headerIndex, ibset(obs_headElem_i(obsData, obs_st1, headerIndex), 06))
283            end if
284
285          end if
286	end if
287      end if
288
289    end do
290
291    if (numberObs > 0) then
292      write(*,*)
293      call msg('ocebg_bgCheckSST', 'background check of SST (TM) data is computed')
294      write(*,*) '***************************************************************************************'
295      write(*,'(a, i7,a,i7,a)') 'ocebg_bgCheckSST: total ', numberObsRejected, ' observations out of (ALL) ', numberObs,' rejected'
296      write(*,'(a, i7,a,i7,a)') 'ocebg_bgCheckSST: where ', numberObsInsituRejected, ' insitu observations out of ', &
297                                numberObsInsitu,' insitu obs. rejected'
298      write(*,*) '***************************************************************************************'
299      write(*,*)
300    end if
301
302    call gsv_deallocate(stateVectorFGE)
303    call col_deallocate(columnFGE)
304    if (separateSelectCriteria) then
305      call col_deallocate(columnSeaWaterFraction)
306      call gsv_deallocate(stateVectorSeaWaterFraction)
307    end if      
308
309  end subroutine ocebg_bgCheckSST
310
311  !----------------------------------------------------------------------------------------
312  ! ocebg_bgCheckSeaIce
313  !----------------------------------------------------------------------------------------
314  subroutine ocebg_bgCheckSeaIce(obsData)
315    !
316    !:Purpose: Compute sea ice data background check.
317    !           The rms of the difference between the observations and
318    !           the background values is calculated over a "swath" of data.
319    !           For satellite observations, this is based on a single passage
320    !           of the satellite over the domain.
321    !           A flag is then set for all observations in the swath
322    !           if the rms value is larger than the threshold
323    !           specified for the observation type.
324    !
325
326    implicit none
327
328    ! Arguments:
329    type(struct_obs), intent(inout) :: obsData  ! obsSpaceData object
330
331    ! Locals:
332    integer              :: nulnam, ierr
333    integer              :: headerIndex, bodyIndex, stationIndex, bodyCount
334    integer              :: obsChid, obsDate, obsTime, obsFlag
335    integer              :: obsDateStamp
336    integer, allocatable :: numberObs(:), bodyIndexList(:,:)
337    integer, allocatable :: minDateStamp(:), maxDateStamp(:), swathID(:)
338    real, allocatable    :: rmsDiff(:)
339    integer              :: swathIndex, nSwath
340    real(8)              :: OmP, deltaHours
341    integer              :: numberObsRejected
342    character(len=12)    :: cstnid
343    integer, external    :: newdate
344    integer              :: imode, prntdate, prnttime
345    integer, parameter   :: numStationMax = 14              ! maximum number of 'idStation' values
346
347    ! Namelist variables: (local)
348    integer           :: numStation = MPC_missingValue_INT  ! MUST NOT BE INCLUDED IN NAMELIST!
349    character(len=12) :: idStation(numStationMax) = 'null'  ! list of obsSpaceData 'idStation' values to consider
350    real              :: OmpRmsdThresh(numStationMax) = 0.0 ! rejection threshold applied to RMS of O-P for entire swath
351    integer           :: maxSwath = 10                      ! maximum number of swaths
352    integer           :: maxPerSwath = 200000               ! maximum number of data per swath
353    namelist /namIceBGcheck/ numStation, idStation, OmpRmsdThresh, maxSwath, maxPerSwath
354
355    call msg('ocebg_bgCheckSeaIce', 'performing background check for the SeaIce data...')
356 
357    ! Read the namelist
358    if (.not. utl_isNamelistPresent('namIceBGcheck','./flnml')) then
359      if (mmpi_myid == 0) then
360        call msg('ocebg_bgCheckSeaIce', 'namIceBGcheck is missing in the namelist.')
361        call msg('ocebg_bgCheckSeaIce', 'the default values will be taken.')
362      end if
363    else
364      ! reading namelist variables
365      nulnam = 0
366      ierr = fnom(nulnam, './flnml', 'FTN+SEQ+R/O', 0)
367      read(nulnam, nml = namIceBGcheck, iostat = ierr)
368      if (ierr /= 0) call utl_abort('ocebg_bgCheckSeaIce: Error reading namelist')
369      ierr = fclos(nulnam)
370      if (numStation /= MPC_missingValue_INT) then
371        call utl_abort('ocebg_bgCheckSeaIce: check namIceBGcheck namelist section: numStation should be removed')
372      end if
373      numStation = 0
374      do stationIndex = 1, numStationMax
375        if (trim(idStation(stationIndex)) == 'null') exit
376        numStation = numStation + 1
377      end do
378    end if
379    write(*, nml = namIceBGcheck)
380
381    allocate(numberObs(maxSwath), bodyIndexList(maxPerSwath,maxSwath))
382    allocate(minDateStamp(maxSwath), maxDateStamp(maxSwath), swathID(maxSwath))
383    allocate(rmsDiff(maxSwath))
384
385    STATION: do stationIndex = 1, numStation
386
387      call msg('ocebg_bgCheckSeaIce', 'doing idStation: '//idStation(stationIndex))
388
389      numberObs(:) = 0
390      rmsDiff(:) = 0.0
391      minDateStamp(:) = tim_getDatestamp()
392      maxDateStamp(:) = tim_getDatestamp()
393      nSwath = 0
394
395      HEADER: do headerIndex = 1, obs_numheader(obsData)
396
397        cstnid = obs_elem_c(obsData, 'STID' , headerIndex )
398        bodyIndex = obs_headElem_i(obsData, obs_rln, headerIndex)
399
400        obsFlag = obs_bodyElem_i(obsData, OBS_FLG, bodyIndex)
401
402        if ( (cstnid /= idStation(stationIndex)) .or. &
403             (obsFlag /= 0 .and. cstnid /= 'CIS_REGIONAL') ) cycle HEADER
404
405        obsChid = obs_headElem_i(obsData, obs_chid, headerIndex)
406        obsDate = obs_headElem_i(obsData, OBS_DAT, headerIndex)
407        obsTime = obs_headElem_i(obsData, OBS_ETM, headerIndex)
408
409        imode = 3 ! printable to stamp
410        ierr = newdate(obsDateStamp, obsDate, obsTime*10000, imode)
411
412        do swathIndex=1, nSwath
413
414          if(obsDateStamp <= maxDateStamp(swathIndex) .and. &
415             obsDateStamp >= minDateStamp(swathIndex) .and. &
416             obsChid == swathID(swathIndex)) exit
417
418          call difdatr(obsDateStamp, minDateStamp(swathIndex), deltaHours)
419            
420          if(abs(deltaHours) <= 0.5d0 .and. obsChid == swathID(swathIndex)) exit
421
422        end do
423
424        nSwath = max(nSwath, swathIndex)
425
426        if(swathIndex <= maxSwath) then
427
428          if(numberObs(swathIndex) == 0) then
429            minDateStamp(swathIndex) = obsDateStamp
430            maxDateStamp(swathIndex) = obsDateStamp
431          else
432            call difdatr(obsDateStamp, minDateStamp(swathIndex), deltaHours)
433            if(deltaHours < 0.0d0) minDateStamp(swathIndex) = obsDateStamp
434            call difdatr(obsDateStamp, maxDateStamp(swathIndex), deltaHours)
435            if(deltaHours > 0.0d0) maxDateStamp(swathIndex) = obsDateStamp
436          end if
437
438          swathID(swathIndex) = obsChid
439
440          OmP = obs_bodyElem_r(obsData, OBS_OMP, bodyIndex)
441
442          if (OmP /= MPC_missingValue_R8) then
443
444            numberObs(swathIndex) = numberObs(swathIndex) + 1
445            rmsDiff(swathIndex) = rmsDiff(swathIndex) + (OmP)**2
446            if (numberObs(swathIndex) <= maxPerSwath) then
447              bodyIndexList(numberObs(swathIndex), swathIndex) = bodyIndex
448            else
449              call utl_abort('ocebg_bgCheckSeaIce: Increase maxPerSwath')
450            end if
451
452          end if
453
454        else
455
456          call utl_abort('ocebg_bgCheckSeaIce: Increase maxSwath')
457
458        end if
459
460      end do HEADER
461
462      SWATH: do swathIndex = 1, maxSwath
463
464        if (numberObs(swathIndex) == 0) cycle SWATH
465
466        rmsDiff(swathIndex) = sqrt(rmsDiff(swathIndex)/numberObs(swathIndex))
467
468        call msg('ocebg_bgCheckSeaIce', 'swathIndex = '//str(swathIndex))
469        call msg('ocebg_bgCheckSeaIce', 'Timespan:')
470        imode = -3 ! stamp to printable
471        ierr = newdate(minDateStamp(swathIndex), prntdate, prnttime, imode)
472        call msg('ocebg_bgCheckSeaIce', 'From: '//str(prntdate)//str(prnttime/100))
473        ierr = newdate(maxDateStamp(swathIndex), prntdate, prnttime, imode)
474        call msg('ocebg_bgCheckSeaIce', 'To  : '//str(prntdate)//str(prnttime/100))
475
476        write(*,'(a,f10.4)') 'ocebg_bgCheckSeaIce: RMSD = ', rmsDiff(swathIndex)
477        write(*,'(a,i10)') 'ocebg_bgCheckSeaIce: nobs = ', numberObs(swathIndex)
478
479        if (rmsDiff(swathIndex) > OmpRmsdThresh(stationIndex)) then
480
481          numberObsRejected = 0
482          do bodyCount = 1, numberObs(swathIndex)
483            numberObsRejected = numberObsRejected + 1
484            ! update background check flag
485            call obs_bodySet_i(obsData, obs_flg, bodyIndexList(bodyCount,swathIndex), ibset(obs_bodyElem_i(obsData, obs_flg, bodyIndexList(bodyCount,swathIndex)), 10))
486          end do
487
488          write(*,'(a,i7,a)')'ocebg_bgCheckSeaIce: ********** reject: ', numberObsRejected, &
489                                    ' '//idStation(stationIndex)//' data'
490
491        end if
492
493      end do SWATH
494
495    end do STATION
496
497    deallocate(numberObs, bodyIndexList)
498    deallocate(minDateStamp, maxDateStamp, swathID)
499    deallocate(rmsDiff)
500
501  end subroutine ocebg_bgCheckSeaIce
502
503  !--------------------------------------------------------------------------
504  ! ocebg_setFlag
505  !--------------------------------------------------------------------------
506  function ocebg_setFlag(obsVarno, bgCheck, selectCriteria) result(obsFlag)
507    !
508    !:Purpose: Set background-check flags according to values set in a table.
509    !           Original values in table come from ECMWF.
510    !
511    implicit none
512
513    ! Arguments:
514    integer, intent(in) :: obsVarno          ! obsVarno, Universal Field-Identity Numbers defined in bufr_mod
515    real(8), intent(in) :: bgCheck           ! normalized background departure
516    real(8), intent(in) :: selectCriteria(:) ! selection criteria for three levels 
517    ! Result:
518    integer             :: obsFlag  ! obs flag
519
520    obsFlag = 0
521 
522    if (obsVarno == bufr_sst) then
523      if (bgCheck >= selectCriteria(1) .and. bgCheck < selectCriteria(2)) then
524        obsFlag = 1
525      else if (bgCheck >= selectCriteria(2) .and. bgCheck < selectCriteria(3)) then
526        obsFlag = 2
527      else if (bgCheck >= selectCriteria(3)) then
528        obsFlag = 3
529      end if
530    end if
531
532  end function ocebg_setFlag
533
534  !--------------------------------------------------------------------------
535  ! ocebg_getFGEamplification
536  !--------------------------------------------------------------------------
537  subroutine ocebg_getFGEamplification(stateVectorAmplFactor, dateStamp, hco)
538    !
539    !:Purpose: Read wind speed fields for the last four days.
540    !           In the operations: 
541    !           The background error used during the background check is then
542    !           amplified in those regions by a factor that varies from 1,
543    !           where the maximum wind speed is 21m/s or less, to 12,
544    !           where the maximum wind speed is 24m/s or more.
545    !           The factor is then filtered to produce a smoothly varying
546    !           field. This amplified background error is used only to
547    !           perform the background check. 
548    !
549    implicit none
550
551    ! Arguments:
552    type(struct_gsv),          intent(inout) :: stateVectorAmplFactor ! state vector to save amplification factor
553    integer         ,          intent(in)    :: dateStamp             ! date stamp
554    type(struct_hco), pointer, intent(in)    :: hco                   ! horizontal trl grid
555
556    ! Locals:
557    type(struct_gsv)          :: stateVector         ! state vector for surface winds
558    type(struct_vco), pointer :: vco_winds           ! vertical grid structure for winds
559    real(4)         , pointer :: uu_ptr4d(:,:,:,:)   ! pointer to get UU wind component
560    real(4)         , pointer :: vv_ptr4d(:,:,:,:)   ! pointer to get VV wind component
561    integer                   :: dataStampList(ndaysWinds * 24 / timeStepWinds) ! datastamp list for wind fields
562    real(4)                   :: windSpeed
563    integer                   :: hour, day, monthNumber
564    integer                   :: yyyy, ndays, timeStepIndex
565    real(8)                   :: deltaT, lat
566    integer                   :: lonIndex, latIndex, monthIndex
567    real(4)         , pointer :: stateVectorAmplFactor_ptr(:,:,:)
568    real(8)                   :: amplFactor
569
570    nullify(vco_winds)
571    
572    ! looking for the earliest valid time:
573    deltaT = - real(ndaysWinds * 24 - windForecastLeadtime)
574    call incdatr(dataStampList(1), dateStamp, deltaT)
575
576    ! scanning the datastamps up from the earliest to the last valid time
577    deltaT = real(timeStepWinds)
578    do timeStepIndex = 2, ndaysWinds * 24 / timeStepWinds
579      call incdatr(dataStampList(timeStepIndex), dataStampList(timeStepIndex - 1), deltaT)
580    end do
581
582    call vco_SetupFromFile(vco_winds,'./winds')
583    call gsv_allocate(stateVector, ndaysWinds * 24 / timeStepWinds, hco, vco_winds, &
584                      dateStampList_opt = dataStampList, dataKind_opt = 4, &
585                      varNames_opt=(/'UU','VV'/), mpi_local_opt=.true., &
586                      hInterpolateDegree_opt='LINEAR')
587      
588    call msg('ocebg_getFGEamplification', 'reading wind speed fields...')
589    do timeStepIndex = 1, ndaysWinds * 24 / timeStepWinds
590      call tim_dateStampToYYYYMMDDHH(dataStampList(timeStepIndex), hour, day, monthNumber, &
591                                     ndays, yyyy, verbose_opt = .False.)
592      call msg('ocebg_getFGEamplification', ' '//str(timeStepIndex)//str(dataStampList(timeStepIndex))//str(yyyy)//str(monthNumber)//str(day)//str(hour))
593      call gio_readFromFile(stateVector, './winds', ' ', ' ', stepIndex_opt = timeStepIndex, &
594                            containsFullField_opt=.true.)
595    end do
596
597    call gsv_getField(stateVector, uu_ptr4d, 'UU')
598    call gsv_getField(stateVector, vv_ptr4d, 'VV')
599    call gsv_getField(stateVectorAmplFactor, stateVectorAmplFactor_ptr)
600
601    write(*,*)
602    call msg('ocebg_getFGEamplification', 'detecting tropical storms (TS)...')
603
604    time_loop: do timeStepIndex = 1, ndaysWinds * 24 / timeStepWinds
605
606      call tim_dateStampToYYYYMMDDHH(dataStampList(timeStepIndex), hour, day, monthNumber, &
607                                     ndays, yyyy, verbose_opt = .False.)
608      do monthIndex = 1, nmonthsExceptionNH
609        if (months(monthNumber) == monthExceptionNH(monthIndex)) then
610          maxLatNH = maxLatExceptionNH
611          call msg('ocebg_getFGEamplification', 'TS is allowed to penetrate up to '//str(maxLatNH) &
612                     //' degrees North; current month: '//months(monthNumber))
613        end if
614      end do
615
616      do latIndex = myLatBeg, myLatEnd
617        do lonIndex = myLonBeg, myLonEnd
618
619          lat = real(hco%lat2d_4(lonIndex, latIndex), 8) * MPC_DEGREES_PER_RADIAN_R8
620          if ((lat > minLatNH .and. lat < maxLatNH) .or. (lat > minLatSH .and. lat < maxLatSH)) then
621            windSpeed = sqrt(uu_ptr4d(lonIndex, latIndex, 1, timeStepIndex) * &
622                             uu_ptr4d(lonIndex, latIndex, 1, timeStepIndex) + &
623                             vv_ptr4d(lonIndex, latIndex, 1, timeStepIndex) * &
624                             vv_ptr4d(lonIndex, latIndex, 1, timeStepIndex))
625            amplFactor = max(1.0d0, min(4.0d0 * max(0.0d0,(windSpeed - 20.75d0)), 12.0d0))
626            ! stateVectorAmplFactor_ptr may already be assigned with a value > 1.0 in a previous state 
627            if(amplFactor > stateVectorAmplFactor_ptr(lonIndex, latIndex, 1)) then
628              stateVectorAmplFactor_ptr(lonIndex, latIndex, 1) = amplFactor
629            end if
630          endif
631
632        end do
633      end do
634
635    end do time_loop
636
637    call gsv_deallocate(stateVector)
638
639    call gio_writeToFile(stateVectorAmplFactor, './amplification', 'ORIG')      
640    call gsv_smoothHorizontal(stateVectorAmplFactor, smoothLenghtScale)
641    call gio_writeToFile(stateVectorAmplFactor, './amplification', 'SMOOTH')        
642
643  end subroutine ocebg_getFGEamplification
644
645end module bgckOcean_mod