midas_randomPert sourceΒΆ

  1program midas_randomPert
  2  !
  3  !:Purpose: Main program for generating an ensemble of random perturbations,
  4  !          based on the B matrix (can be homogeneous and isotropic, ensemble-based
  5  !          or a weighted sum of covariance matrices).
  6  !
  7  !          ---
  8  !
  9  !:Algorithm: Uses a gaussian pseudorandom generator to produce an ensemble
 10  !            of control vectors.  These control vectors are transformed into
 11  !            pertubations in physical space, through the action of the square-root
 12  !            of the background-error covariance matrix.  The ensemble of perturbations
 13  !            can then be debiased and some smoothing can be applied.
 14  !            The perturbations thus obtained can then be interpolated and added
 15  !            to a provided ensemble mean and/or be mixed with some previous time
 16  !            perturbations.
 17  !
 18  !            ---
 19  !
 20  !:File I/O:
 21  !
 22  !============================================== ==============================================================
 23  ! Input and Output Files                         Description of file
 24  !============================================== ==============================================================
 25  ! ``analysisgrid``                               In - File defining grid on which perturbations are generated
 26  ! ``targetgrid``                                 In - File defining destination ensemble grid
 27  ! ``bgcov``                                      In - Static (i.e. NMC) B matrix file for NWP fields
 28  ! ``seaIceAnalysis``                             In - Sea-ice analysis, to set under-ice perturbations to zero
 29  ! ``pert_$YYYYMMDDHH_000_$NNNN``                 In - Previous date perturbations, when ensemble not provided
 30  ! ``$YYYYMMDDHH_006_$NNNN``                      In/Out - Ensemble member files
 31  ! ``pert_$YYYYMMDDHH_000_$NNNN``                 Out - Output perturbations
 32  !============================================== ==============================================================
 33  !
 34  !
 35  !:Synopsis: Below is a summary of the ``randomPert`` program calling sequence:
 36  !
 37  !             - **Initial setups:**
 38  !
 39  !               - Initialize the horizontal and vertical grid objects; for the
 40  !                 generation of the perturbations from ``analysisgrid`` and
 41  !                 for the ensemble output from ``targetgrid``.
 42  !
 43  !               - Setup the B matrices: ``bmat_setup``.
 44  !
 45  !               - Allocate arrays and a stateVector object on the ``analysisgrid``,
 46  !                 to receive the perturbations and first statistical moments.
 47  !
 48  !               - Initialize the pseudorandom generator : ``rng_setup``.
 49  !
 50  !             - **Generation of the ensemble of perturbations**.
 51  !               Iterate until the number of required perturbations (``NENS``)
 52  !               is generated:
 53  !
 54  !               - Generate a random control vector from a unit variance gaussian
 55  !                 distribution: ``rng_gaussian``.
 56  !
 57  !               - Transform control vector to physical space perturbation using
 58  !                 ``bmat_sqrtB``.
 59  !
 60  !             - **Unbias the ensemble of perturbations** if ``REMOVE_MEAN`` is true.
 61  !
 62  !             - **Smooth variances** if ``smoothVariances`` is true.
 63  !
 64  !             - **Read ensemble mean** if ``readEnsMean`` is true:
 65  !
 66  !               - Allocate a stateVector object on the ``targetgrid``, and then
 67  !                 read the ensemble mean using ``gio_readFromFile``.
 68  !
 69  !               - if ``setPertZeroUnderIce`` is true, allocate a stateVector
 70  !                 object and read sea-ice analysis using ``gio_readFromFile``.
 71  !                 Where the ice fraction is higher than ``iceFractionThreshold``,
 72  !                 the perturbations are set to zero.
 73  !
 74  !             - **Read and debias previous date perturbations** if
 75  !               ``previousDateFraction`` is higher than zero.
 76  !
 77  !             - **Final steps**:
 78  !
 79  !               - Allocate a statevector object on the ``targetgrid``, copy ocean
 80  !                 mask and interpolate perturbations using ``int_interp_gsv``
 81  !
 82  !               - If ``previousDateFraction`` higher than zero, average previous
 83  !                 date perturbations with the ones generated for the present date.
 84  !
 85  !               - If ``readEnsMean`` is true, add the ensemble mean to the perturbations.
 86  !
 87  !               - Write the perturbations using ``gio_writeToFile``.  If ``readEnsMean``
 88  !                 is true, write the ensemble mean as well.
 89  !
 90  !               ---
 91  !
 92  !:Options: `List of namelist blocks <../namelists_in_each_program.html#randompert>`_
 93  !          that can affect the ``randomPert`` program.
 94  !
 95  !         * ``&NAMENKF`` define specific parameters to configure the ``randomPert``
 96  !           program and is read by the program.
 97  !
 98  !         * The background-error covariance matrix contributions are defined through
 99  !           the namelists ``&NAMBHI``, for the homogeneous and isotropic part,
100  !           and ``&NAMBEN`` for the ensemble part.  When the matrix is a weighted
101  !           sum, it is important to make sure to define the ``SCALEFACTOR`` values.
102  !           Note that it would be also possible to define contributions from
103  !           ``&NAMBDIFF`` for diffusion correlations and ``&NAMBCHM`` for homogeneous
104  !           and isotropic chemical constituents covariances.
105  !
106
107  use version_mod
108  use midasMpi_mod
109  use ramDisk_mod
110  use controlVector_mod
111  use gridStateVector_mod
112  use gridStateVectorFileIO_mod
113  use bMatrix_mod
114  use interpolation_mod
115  use verticalCoord_mod
116  use horizontalCoord_mod
117  use timeCoord_mod
118  use randomNumber_mod
119  use utilities_mod
120  use gridVariableTransforms_mod
121  implicit none
122
123  type(struct_gsv) :: stateVectorPert, stateVectorPertInterp
124  type(struct_gsv) :: stateVectorEnsMean, stateVectorIce
125
126  type(struct_vco), pointer :: vco_anl => null()
127  type(struct_hco), pointer :: hco_anl => null()
128  type(struct_hco), pointer :: hco_anlcore => null()
129  type(struct_hco), pointer :: hco_target => null()
130  type(struct_hco), pointer :: hco_targetcore => null()
131
132  real(4), pointer :: seaice_ptr(:,:,:)
133  real(8), pointer :: field(:,:,:), fieldInterp(:,:,:)
134
135  integer :: fclos, fnom, fstopc, newdate, dateStamp, datePrevious, dateStampPrevious
136  integer :: imode, ierr
137  integer :: memberIndex, lonIndex, latIndex, cvIndex, levIndex, nkgdim
138  integer :: datePrint, timePrint, nulnam, randomSeed
139  integer :: get_max_rss, n_grid_point, n_grid_point_glb
140
141  integer :: latPerPEa, latPerPEmaxa, myLatBega, myLatEnda
142  integer :: lonPerPEa, lonPerPEmaxa, myLonBega, myLonEnda
143  integer :: latPerPEt, latPerPEmaxt, myLatBegt, myLatEndt
144  integer :: lonPerPEt, lonPerPEmaxt, myLonBegt, myLonEndt
145
146  real(8) :: previousDateFactPrev, previousDateFactNoise
147  real(8), allocatable :: controlVector(:), controlVector_mpiglobal(:)
148  real(8), allocatable :: gdmean(:,:,:)
149  real(4), allocatable :: ensemble_r4(:,:,:,:)
150  real(4), allocatable :: ensemblePreviousDate_r4(:,:,:,:)
151  real(8), allocatable :: avg_pturb_var(:)
152  real(8), allocatable :: avg_pturb_var_glb(:)
153  real(8), allocatable :: pturb_var(:,:,:)
154
155  logical :: targetGridExists
156  character(len=10) :: dateString, datePreviousString
157  character(len=4)  :: memberString
158  character(len=25) :: outFileName, inFileName
159  character(len=64) :: ensMeanFileName = 'ensMeanState'
160  character(len=2)  :: typvarOut
161
162  ! Namelist variables
163  logical :: remove_mean          ! choose to remove mean from perturbations
164  logical :: smoothVariances      ! choose to impose horizontally constant perturbation variances
165  logical :: mpiTopoIndependent   ! choose to compute random numbers with mpi-topology-independent method 
166  logical :: readEnsMean          ! choose to read ens mean and add this to the perturbations
167  logical :: setPertZeroUnderIce  ! choose to set perturbation to zero under sea ice (for SST)
168  integer :: nens                 ! number of perturbations to compute
169  integer :: seed                 ! initial value of the random seed
170  integer :: numBits              ! number of bits to use when writing to standard files
171  character(len=12) :: out_etiket ! the 'etiket' to write to standard files
172  real(4) :: iceFractionThreshold ! ice fraction threshold to use in combination with 'setPertZeroUnderIce'
173  real(4) :: previousDateFraction ! relative amount of previous date perturbations to include in current perturbations
174  NAMELIST /NAMENKF/nens, seed, out_etiket, remove_mean,  &
175                    smoothVariances, mpiTopoIndependent, numBits, &
176                    readEnsMean, setPertZeroUnderIce, iceFractionThreshold, &
177                    previousDateFraction
178
179  call ver_printNameAndVersion('randomPert','Generation of random perturbations')
180
181  !
182  !- 0. MPI, tmg initialization
183  !
184  call mmpi_initialize
185  call tmg_init(mmpi_myid, 'TMG_INFO')
186
187  call utl_tmg_start(0,'Main')
188  write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
189
190  ierr = fstopc('MSGLVL','ERRORS',0)
191
192  !
193  !- 1. Set/Read values for the namelist NAMENKF
194  !
195  
196  !- 1.1 Setting default values
197  nens  = 10
198  seed  = -999        ! If -999, set random seed using the date
199  remove_mean = .true.
200  out_etiket='RANDOM_PERT' 
201  smoothVariances = .false.
202  mpiTopoIndependent = .false.
203  numBits = 32
204  readEnsMean = .false.
205  setPertZeroUnderIce = .false.
206  iceFractionThreshold = 0.2
207  previousDateFraction = -1.0
208
209  !- 1.2 Read the namelist
210  nulnam=0
211  ierr=fnom(nulnam, './flnml', 'FTN+SEQ+R/O', 0)
212  read(nulnam, nml=namenkf, iostat=ierr)
213  if(ierr.ne.0) call utl_abort('midas-randomPert: Error reading namelist')
214  if( mmpi_myid == 0 ) write(*,nml=namenkf)
215  ierr=fclos(nulnam)
216
217  if (readEnsMean) then
218    typvarOut = 'A'
219  else
220    typvarOut = 'R'
221  end if
222
223  if (previousDateFraction > 1.0) then
224    call utl_abort('midas-randomPert: previousDateFraction must be less than 1.0 to give stable results')
225  end if
226
227  write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
228
229  !
230  !- 2.  Initialization
231  !
232
233  ! Setup the ramdisk directory (if supplied)
234  call ram_setup
235
236  !- 2.1 Set the dateStamp, either from env variable or ensMeanState file
237
238  !- Initialize the Temporal grid and the dateStamp from env variable
239  call tim_setup()
240
241  ! If dateStamp not set by env variable, use date from ensMeanState, if available
242  if (tim_getDateStamp() == 0) then
243    if (readEnsMean) then
244      dateStamp = tim_getDatestampFromFile(ensMeanFileName)
245      call tim_setDatestamp(dateStamp)
246    else
247      call utl_abort('midas-randomPert: DateStamp must be set through env variable')
248    end if
249  end if
250  dateStamp = tim_getDateStamp()
251  imode = -3 ! stamp to printable date and time: YYYYMMDD, HHMMSShh
252  ierr = newdate(dateStamp, datePrint, timePrint, imode)
253  write(dateString, '(I10)') datePrint*100 + timePrint/1000000
254  if( mmpi_myid == 0 ) then
255    write(*,*) ' date= ', datePrint, ' time= ', timePrint, ' stamp= ', dateStamp
256    write(*,*) ' dateString = ', dateString
257  end if
258
259  !- 2.2 Initialize variables of the model states
260  call gsv_setup
261
262  !- 2.3 Initialize the horizontal grids from analysisgrid and targetgrid files
263  if (mmpi_myid == 0) write(*,*)
264  if (mmpi_myid == 0) write(*,*) 'Set hco parameters for analysis grid'
265  call hco_setupFromFile(hco_anl, './analysisgrid', 'ANALYSIS', 'Analysis' ) ! IN
266
267  if ( hco_anl % global ) then
268    hco_anlcore => hco_anl
269  else
270    !- Iniatilized the core (Non-Exteded) analysis grid
271    call hco_setupFromFile( hco_anlcore, './analysisgrid', 'COREGRID', 'AnalysisCore' ) ! IN
272  end if
273
274  inquire(file='./targetgrid',exist=targetGridExists)
275  if (targetGridExists) then
276    if (mmpi_myid == 0) write(*,*)
277    if (mmpi_myid == 0) write(*,*) 'Set hco parameters for target grid'
278
279    call hco_setupFromFile(hco_target, './targetgrid', 'ANALYSIS', 'Target' ) ! IN
280
281    if ( hco_target % global ) then
282      hco_targetcore => hco_target
283    else
284      !- Iniatilized the core (Non-Exteded) analysis grid
285      call hco_setupFromFile( hco_targetcore, './targetgrid', 'COREGRID', 'TargetCore' ) ! IN
286    end if
287  else
288    ! no targetgrid file, so use analysisgrid grid instead
289    hco_target => hco_anl
290    hco_targetcore => hco_anlcore
291  end if
292
293  call mmpi_setup_latbands(hco_anl % nj,                & ! IN
294                             latPerPEa, latPerPEmaxa, myLatBega, myLatEnda ) ! OUT
295  call mmpi_setup_lonbands(hco_anl % ni,                & ! IN
296                             lonPerPEa, lonPerPEmaxa, myLonBega, myLonEnda ) ! OUT
297
298  call mmpi_setup_latbands(hco_target % nj,                & ! IN
299                             latPerPEt, latPerPEmaxt, myLatBegt, myLatEndt ) ! OUT
300  call mmpi_setup_lonbands(hco_target % ni,                & ! IN
301                             lonPerPEt, lonPerPEmaxt, myLonBegt, myLonEndt ) ! OUT
302
303  !- 2.4 Initialize the vertical coordinate from the analysisgrid file
304  call vco_setupFromFile(vco_anl, './analysisgrid', ' ')
305 
306  !- 2.5 Initialize the B_hi matrix
307  call bmat_setup(hco_anl, hco_anlcore, vco_anl)
308
309  write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
310
311  !- 2.6 Initialize the gridded variable transform module
312  call gvt_setup(hco_anl,hco_anlcore,vco_anl)
313  if ( gsv_varExist(varName='HU') ) call gvt_setupRefFromTrialFiles('HU')
314
315  !- 2.7 Set randomSeed
316  if (seed == -999) then
317    ! If "seed" namelist value is -999, set random seed using the date
318    dateStamp = tim_getDateStamp()
319    if (dateStamp == -1) then
320      call utl_abort('midas-randomPert: dateStamp is not set, cannot be used to set random seed')
321    end if
322    imode = -3 ! stamp to printable date and time: YYYYMMDD, HHMMSShh
323    ierr = newdate(dateStamp, datePrint, timePrint, imode)
324    timePrint = timePrint/1000000
325    datePrint =  datePrint*100 + timePrint
326    ! Remove the century, keeping 2 digits of the year
327    randomSeed = datePrint - 100000000*(datePrint/100000000)
328  else
329    ! Otherwise, use value from namelist
330    randomSeed = seed
331  end if
332  write(*,*) 'midas-randomPert: randomSeed for set to ', randomSeed
333
334  !
335  !- 3. Memory allocations
336  !
337
338  !- 3.1 Allocate the stateVectorPert
339  call gsv_allocate(stateVectorPert, 1, hco_anl, vco_anl, &
340                    dateStamp_opt=dateStamp, mpi_local_opt=.true., &
341                    allocHeight_opt=.false., allocPressure_opt=.false., &
342                    hInterpolateDegree_opt='LINEAR')
343  nkgdim = stateVectorPert%nk
344  allocate(ensemble_r4(myLonBega:myLonEnda, myLatBega:myLatEnda, nkgdim, nEns))
345
346  !- 3.2 Allocate auxillary variables
347  allocate(gdmean(myLonBega:myLonEnda, myLatBega:myLatEnda, nkgdim))
348  allocate(controlVector(cvm_nvadim))
349  allocate(controlVector_mpiglobal(cvm_nvadim_mpiglobal))
350
351  write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
352
353  !
354  !- 4. Compute an ensemble of random perturbations
355  !
356
357  if( mmpi_myid == 0 ) write(*,*) '******************'
358  if( mmpi_myid == 0 ) write(*,*) 'midas-randomPert: COMPUTE the mean of the random ', &
359                                  'perturbations of all the members'
360
361  if( mpiTopoIndependent ) then
362    call rng_setup(abs(randomSeed))
363  else
364    call rng_setup(abs(randomSeed+mmpi_myid))
365  end if
366
367  gdmean(:,:,:) = 0.0D0
368  call gsv_getField(stateVectorPert,field)
369
370  !- 4.1 Generate a (potentially) biased ensemble
371  do memberIndex = 1, NENS
372
373    if( mmpi_myid == 0 ) write(*,*) '...computing member number= ', memberIndex
374
375    !- 4.1.1 Create a random control vector in spectral space
376
377    if( mpiTopoIndependent ) then
378      !- Global vector (for testing different mpi topology, less efficient)
379      do cvIndex = 1, cvm_nvadim_mpiglobal
380        controlVector_mpiglobal(cvIndex) = rng_gaussian()
381      end do
382      call bmat_reduceToMPILocal( controlVector, controlVector_mpiglobal )
383    else
384      !- Local vector (different randomSeed for each processor, more efficient)
385      do cvIndex = 1, cvm_nvadim
386        controlVector(cvIndex) = rng_gaussian()
387      end do
388    end if
389
390    !- 4.1.2 Transform to control variables in physical space
391    call bmat_sqrtB(controlVector, cvm_nvadim, & ! IN
392                    stateVectorPert           )  ! OUT
393
394    !- 4.1.3 Copy perturbations to big array and update ensemble sum
395    !$OMP PARALLEL DO PRIVATE (levIndex, latIndex, lonIndex)    
396    do levIndex = 1, nkgdim
397      do latIndex = myLatBega, myLatEnda
398        do lonIndex = myLonBega, myLonEnda
399          ensemble_r4(lonIndex, latIndex, levIndex, memberIndex) &
400               = real(field(lonIndex, latIndex, levIndex), 4)
401          gdmean(lonIndex, latIndex, levIndex) &
402               = gdmean(lonIndex, latIndex, levIndex) + field(lonIndex, latIndex, levIndex)
403        end do
404      end do
405    end do
406    !$OMP END PARALLEL DO
407
408  end do
409
410  call gsv_deallocate(stateVectorPert)
411  
412  !- 4.2 Remove the ensemble mean
413  if ( REMOVE_MEAN ) then
414
415    !$OMP PARALLEL DO PRIVATE (levIndex, latIndex, lonIndex)    
416    do levIndex = 1, nkgdim
417      do latIndex = myLatBega, myLatEnda
418        do lonIndex = myLonBega, myLonEnda
419          gdmean(lonIndex, latIndex, levIndex) &
420               = gdmean(lonIndex, latIndex, levIndex) / real(NENS, 8)
421        end do
422      end do
423    end do
424    !$OMP END PARALLEL DO
425
426    !$OMP PARALLEL DO PRIVATE (memberIndex, levIndex, latIndex, lonIndex)    
427    do memberIndex = 1, NENS
428      do levIndex = 1, nkgdim
429        do latIndex = myLatBega, myLatEnda
430          do lonIndex = myLonBega, myLonEnda
431            ensemble_r4(lonIndex, latIndex, levIndex, memberIndex) =  &
432                ensemble_r4(lonIndex, latIndex, levIndex, memberIndex) -  &
433                real(gdmean(lonIndex, latIndex, levIndex), 4)
434          end do
435        end do
436      end do
437    end do
438    !$OMP END PARALLEL DO
439
440  end if
441  
442  !- 4.3 Smooth variances to horizontally constant values
443  if ( smoothVariances ) then
444  
445    allocate(pturb_var(myLonBega:myLonEnda, myLatBega:myLatEnda, stateVectorPert%nk))
446    allocate(avg_pturb_var(stateVectorPert%nk), avg_pturb_var_glb(stateVectorPert%nk))
447    pturb_var(:,:,:) = 0.0D0
448    avg_pturb_var(:) = 0.0D0
449    avg_pturb_var_glb(:) = 0.0D0
450  
451    do memberIndex = 1, NENS  
452      !$OMP PARALLEL DO PRIVATE (levIndex, latIndex, lonIndex)  
453      do levIndex = 1, nkgdim
454        do latIndex = myLatBega, myLatEnda
455          do lonIndex = myLonBega, myLonEnda
456               pturb_var(lonIndex, latIndex, levIndex) = pturb_var(lonIndex, latIndex, levIndex) &
457                   + real(ensemble_r4(lonIndex, latIndex, levIndex, memberIndex), 8)**2
458          end do
459        end do
460      end do
461      !$OMP END PARALLEL DO
462    end do
463  
464    !$OMP PARALLEL DO PRIVATE (levIndex, latIndex, lonIndex)  
465    do levIndex = 1, nkgdim
466      do latIndex = myLatBega, myLatEnda
467        do lonIndex = myLonBega, myLonEnda
468          pturb_var(lonIndex, latIndex, levIndex) &
469               = pturb_var(lonIndex, latIndex, levIndex) / real(NENS, 8)
470        end do
471      end do
472    end do
473    !$OMP END PARALLEL DO
474  
475    do latIndex = myLatBega, myLatEnda
476      do lonIndex = myLonBega, myLonEnda
477        do levIndex = 1, nkgdim
478          avg_pturb_var(levIndex) = avg_pturb_var(levIndex) + pturb_var(lonIndex, latIndex, levIndex)
479        end do
480      end do
481    end do
482  
483    n_grid_point=(lonPerPEa)*(latPerPEa)
484    call rpn_comm_allreduce(n_grid_point, n_grid_point_glb, 1,  &
485                            "mpi_double_precision", "mpi_sum", "GRID", ierr)
486    call rpn_comm_allreduce(avg_pturb_var, avg_pturb_var_glb, nkgdim,  &
487                            "mpi_double_precision", "mpi_sum", "GRID", ierr)
488  
489    !$OMP PARALLEL DO PRIVATE (levIndex, latIndex, lonIndex)  
490    do levIndex = 1, nkgdim
491      do latIndex = myLatBega, myLatEnda
492        do lonIndex = myLonBega, myLonEnda
493          if( pturb_var(lonIndex, latIndex, levIndex) > 0.0d0 ) then
494            pturb_var(lonIndex, latIndex, levIndex) = sqrt( pturb_var(lonIndex, latIndex, levIndex))
495          end if
496        end do
497      end do
498    end do
499    !$OMP END PARALLEL DO
500  
501    do levIndex = 1, nkgdim
502      if( avg_pturb_var_glb(levIndex) > 0.0d0 ) then
503        avg_pturb_var_glb(levIndex) = sqrt( avg_pturb_var_glb(levIndex) / real(n_grid_point_glb, 8) )
504      end if
505    end do
506
507    !$OMP PARALLEL DO PRIVATE (memberIndex, levIndex, latIndex, lonIndex)    
508    do memberIndex = 1, NENS
509      do levIndex = 1, nkgdim
510        if( avg_pturb_var_glb(levIndex) > 0.0d0 ) then
511          do latIndex = myLatBega, myLatEnda
512            do lonIndex = myLonBega, myLonEnda
513              if( pturb_var(lonIndex, latIndex, levIndex) > 0.0d0 ) then
514                ensemble_r4(lonIndex, latIndex, levIndex, memberIndex) =   &
515                    ensemble_r4(lonIndex, latIndex, levIndex, memberIndex) *  &
516                    real(avg_pturb_var_glb(levIndex)/pturb_var(lonIndex, latIndex, levIndex), 4)
517              end if
518            end do
519          end do
520        end if
521      end do
522    end do
523    !$OMP END PARALLEL DO
524
525  end if
526
527  !- 4.4 Read ensemble mean state
528  if ( readEnsMean ) then
529    if (mmpi_myid == 0) write(*,*) 'midas-randomPert: reading ensemble mean state'
530
531    call gsv_allocate(stateVectorEnsMean, 1, hco_target, vco_anl, &
532                      dateStamp_opt=dateStamp, mpi_local_opt=.true., &
533                      allocHeight_opt=.false., allocPressure_opt=.false., &
534                      hInterpolateDegree_opt='LINEAR')
535    call gio_readFromFile(stateVectorEnsMean, ensMeanFileName, ' ', ' ',  &
536                          containsFullField_opt=.true.)
537
538  end if
539
540  !- 4.5 Set perturbations to zero under ice
541  if ( setPertZeroUnderIce ) then
542
543    ! read sea-ice analysis
544    call gsv_allocate(stateVectorIce, 1, hco_anl, vco_anl, dataKind_opt = 4, &
545                      datestamp_opt = -1, mpi_local_opt = .true.,    &
546                      varNames_opt = (/'LG'/), hInterpolateDegree_opt ='LINEAR')
547    call gio_readFromFile(stateVectorIce, './seaIceAnalysis', ' ','A', &
548                          unitConversion_opt=.false., containsFullField_opt=.true.)
549    call gsv_getField(stateVectorIce,seaice_ptr)
550
551    ! set perturbations to zero
552    !$OMP PARALLEL DO PRIVATE (memberIndex, levIndex, latIndex, lonIndex)    
553    do memberIndex = 1, NENS
554      do levIndex = 1, nkgdim
555        do latIndex = myLatBega, myLatEnda
556          do lonIndex = myLonBega, myLonEnda
557            if (seaice_ptr(lonIndex, latIndex, 1) >= iceFractionThreshold) then
558              ensemble_r4(lonIndex, latIndex, levIndex, memberIndex) = 0.0
559            end if
560          end do
561        end do
562      end do
563    end do
564    !$OMP END PARALLEL DO
565
566    call gsv_deallocate(stateVectorIce)
567
568  end if
569
570  !- 4.6 Add a fraction of previous date perturbations
571  if (previousDateFraction > 0.0) then
572
573    previousDateFactPrev  = previousDateFraction
574    ! reduce the amount of random perturbation to maintain steady-state variance
575    previousDateFactNoise = sqrt(1.0d0 - previousDateFraction**2)
576    if (mmpi_myid == 0) then
577      write(*,*) 'midas-randomPert: Scale factor applied to previous date       = ', &
578           previousDateFactPrev
579      write(*,*) 'midas-randomPert: Scale factor applied to random perturbation = ', &
580           previousDateFactNoise
581    end if
582
583    ! determine dateStamp of previous date
584    call incdatr(dateStampPrevious, dateStamp, -tim_windowsize)
585    imode = -3 ! stamp to printable date and time: YYYYMMDD, HHMMSShh
586    ierr    = newdate(dateStampPrevious, datePrint, timePrint, imode)
587    datePrevious =  datePrint*100 + timePrint/1000000
588    write(datePreviousString, '(I10)') datePrevious
589    write(*,*) 'midas-randomPert: previous date, stamp = ', datePrevious, dateStampPrevious
590
591    call gsv_allocate(stateVectorPert, 1, hco_target, vco_anl, &
592                      dateStamp_opt=dateStampPrevious, mpi_local_opt=.true., &
593                      allocHeight_opt=.false., allocPressure_opt=.false., &
594                      hInterpolateDegree_opt='LINEAR')
595    call gsv_getField(stateVectorPert,field)
596
597    deallocate(gdmean)
598    allocate(gdmean(myLonBegt:myLonEndt, myLatBegt:myLatEndt, nkgdim))
599    gdmean(:,:,:) = 0.0D0
600    allocate(ensemblePreviousDate_r4(myLonBegt:myLonEndt, myLatBegt:myLatEndt, nkgdim, nEns))
601
602    do memberIndex = 1, NENS
603
604      ! Read previous date perturbations (or perturbed analyses)
605      write(memberString, '(I4.4)') memberIndex
606      if (readEnsMean) then
607        inFileName = './'//trim(datePreviousString)//'_000_'//trim(memberString)
608      else
609        inFileName = './pert_'//trim(datePreviousString)//'_'//trim(memberString)
610      end if
611      if( mmpi_myid == 0 ) write(*,*) 'midas-randomPert: reading previous date file = ', inFileName
612
613      call gio_readFromFile(stateVectorPert, inFileName, ' ', ' ',  &
614                            containsFullField_opt=.true.)
615
616      ! Copy to big array and accumulate sum for computing mean
617      !$OMP PARALLEL DO PRIVATE (levIndex, latIndex, lonIndex)    
618      do levIndex = 1, nkgdim
619        do latIndex = myLatBegt, myLatEndt
620          do lonIndex = myLonBegt, myLonEndt
621            ensemblePreviousDate_r4(lonIndex, latIndex, levIndex, memberIndex) = &
622                 real(field(lonIndex, latIndex, levIndex), 4)
623            gdmean(lonIndex, latIndex, levIndex) = gdmean(lonIndex, latIndex, levIndex) &
624                                                   + field(lonIndex, latIndex, levIndex)
625          end do
626        end do
627      end do
628      !$OMP END PARALLEL DO
629
630    end do
631
632    ! Finish computing mean and remove it
633    !$OMP PARALLEL DO PRIVATE (levIndex, latIndex, lonIndex)    
634    do levIndex = 1, nkgdim
635      do latIndex = myLatBegt, myLatEndt
636        do lonIndex = myLonBegt, myLonEndt
637          gdmean(lonIndex, latIndex, levIndex) = gdmean(lonIndex, latIndex, levIndex) &
638                                                 / real(NENS, 8)
639        end do
640      end do
641    end do
642    !$OMP END PARALLEL DO
643
644    !$OMP PARALLEL DO PRIVATE (memberIndex, levIndex, latIndex, lonIndex)    
645    do memberIndex = 1, NENS
646      do levIndex = 1, nkgdim
647        do latIndex = myLatBegt, myLatEndt
648          do lonIndex = myLonBegt, myLonEndt
649            ensemblePreviousDate_r4(lonIndex, latIndex, levIndex, memberIndex) =  &
650                ensemblePreviousDate_r4(lonIndex, latIndex, levIndex, memberIndex) -  &
651                real(gdmean(lonIndex, latIndex, levIndex), 4)
652          end do
653        end do
654      end do
655    end do
656    !$OMP END PARALLEL DO
657
658    call gsv_deallocate(stateVectorPert)
659
660  end if
661
662  !- 4.7 Write the perturbations
663  do memberIndex = 1, NENS
664    if( mmpi_myid == 0 ) write(*,*)
665    if( mmpi_myid == 0 ) write(*,*) 'midas-randomPert: pre-processing for writing member number= ', memberIndex
666
667    call gsv_allocate(stateVectorPert, 1, hco_anl, vco_anl, &
668                      dateStamp_opt=dateStamp, mpi_local_opt=.true., &
669                      allocHeight_opt=.false., allocPressure_opt=.false., &
670                      hInterpolateDegree_opt='LINEAR')
671    call gsv_getField(stateVectorPert,field)
672    call gsv_allocate(stateVectorPertInterp, 1, hco_target, vco_anl, &
673                      dateStamp_opt=dateStamp, mpi_local_opt=.true., &
674                      allocHeight_opt=.false., allocPressure_opt=.false., &
675                      hInterpolateDegree_opt='LINEAR')
676
677    ! Copy mask if it exists
678    call gsv_copyMask(stateVectorEnsMean, stateVectorPertInterp)
679
680    !$OMP PARALLEL DO PRIVATE (levIndex, latIndex, lonIndex)    
681    do levIndex = 1, nkgdim
682      do latIndex = myLatBega, myLatEnda
683        do lonIndex = myLonBega, myLonEnda
684          field(lonIndex, latIndex, levIndex) = ensemble_r4(lonIndex, latIndex, levIndex, memberIndex)
685        end do
686      end do
687    end do
688    !$OMP END PARALLEL DO
689
690    ! interpolate perturbation to the target grid
691    call int_interp_gsv(stateVectorPert, stateVectorPertInterp)
692
693    call gsv_getField(stateVectorPertInterp,fieldInterp)
694
695    ! Average current and previous perturbations
696    if (previousDateFraction > 0.0) then
697      !$OMP PARALLEL DO PRIVATE (levIndex, latIndex, lonIndex)    
698      do levIndex = 1, nkgdim
699        do latIndex = myLatBegt, myLatEndt
700          do lonIndex = myLonBegt, myLonEndt
701            fieldInterp(lonIndex, latIndex, levIndex) =  &
702                 previousDateFactPrev * &
703                 ensemblePreviousDate_r4(lonIndex, latIndex, levIndex, memberIndex) + &
704                 previousDateFactNoise * &
705                 fieldInterp(lonIndex, latIndex, levIndex)
706          end do
707        end do
708      end do
709      !$OMP END PARALLEL DO
710    end if
711
712    ! add perturbation to supplied ensemble mean
713    if ( readEnsMean ) then
714      if (mmpi_myid == 0) write(*,*) 'midas-randomPert: adding the ensemble mean and perturbation'
715      call gsv_add(stateVectorEnsMean, stateVectorPertInterp)
716    end if
717
718    ! determine file name and write to file
719    write(memberString, '(I4.4)') memberIndex
720    if (readEnsMean) then
721      outFileName = './'//trim(dateString)//'_000_'//trim(memberString)
722    else
723      outFileName = './pert_'//trim(dateString)//'_'//trim(memberString)
724    end if
725
726    if( mmpi_myid == 0 ) write(*,*) 'midas-randomPert: processing file = ', outFileName
727    stateVectorPertInterp%etiket = 'UNDEFINED'
728    call gio_writeToFile(stateVectorPertInterp, outFileName, out_etiket,              & ! IN
729                         numBits_opt=numBits, unitConversion_opt=.true., &  ! IN
730                         containsFullField_opt=readEnsMean, typvar_opt=typvarOut) 
731
732    call gsv_deallocate(stateVectorPertInterp)
733    call gsv_deallocate(stateVectorPert)
734
735  end do
736
737  ! Write out ensemble mean state, if it exists
738  if (readEnsMean) then
739    ! determine file name and write to file
740    memberIndex = 0
741    write(memberString, '(I4.4)') memberIndex
742    outFileName = './'//trim(dateString)//'_000_'//trim(memberString)
743
744    if( mmpi_myid == 0 ) write(*,*) 'midas-randomPert: processing file = ', outFileName
745    stateVectorEnsMean%etiket = 'UNDEFINED'
746    call gio_writeToFile(stateVectorEnsMean, outFileName, out_etiket,              & ! IN
747                         numBits_opt=numBits, unitConversion_opt=.true., &  ! IN
748                         containsFullField_opt=.true., typvar_opt=typvarOut)
749  end if
750
751  write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
752
753  !
754  !- 5.  Memory deallocations
755  !
756  deallocate(gdmean)
757  deallocate(ensemble_r4)
758  if (previousDateFraction > 0.0) then
759    deallocate(ensemblePreviousDate_r4)
760  end if
761  deallocate(controlVector)  
762  deallocate(controlVector_mpiglobal)  
763
764  write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
765  call utl_tmg_stop(0)
766
767  !
768  !- 6.  MPI, tmg finalize
769  !  
770  call tmg_terminate(mmpi_myid, 'TMG_INFO')
771  call rpn_comm_finalize(ierr) 
772
773  write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
774
775  !
776  !- 7.  Ending
777  !
778  if( mmpi_myid == 0 ) write(*,*) '---------------------------------'
779  if( mmpi_myid == 0 ) write(*,*) ' MIDAS-RANDOMPERT ENDS'
780  if( mmpi_myid == 0 ) write(*,*) '---------------------------------'
781
782end program midas_randomPert