global_cycle  1.9.0
land_increments.f90
Go to the documentation of this file.
1 
4 
5 module land_increments
6 
7  private
8 
9  public add_increment_soil
10  public add_increment_snow
11  public calculate_landinc_mask
12  public apply_land_da_adjustments_stc
13  public apply_land_da_adjustments_snd
14 
15  integer, parameter :: lsm_noah=1
17 contains
18 
42 
43 subroutine add_increment_soil(rla,rlo,stc_state,soilsnow_tile, soilsnow_fg_tile, &
44  lensfc,lsoil,idim,jdim, myrank)
45 
46  use utils
47  use gdswzd_mod
48  use read_write_data, only : idim_gaus, jdim_gaus, &
49  stc_inc_gaus, soilsnow_gaus
50  use mpi
51 
52  implicit none
53 
54  integer, intent(in) :: lensfc, lsoil, idim, jdim, myrank
55 
56  integer, intent(in) :: soilsnow_tile(lensfc), soilsnow_fg_tile(lensfc)
57  real, intent(inout) :: rla(lensfc), rlo(lensfc)
58  real, intent(inout) :: stc_state(lensfc, lsoil)
59 
60  integer :: iopt, nret, kgds_gaus(200)
61  integer :: igaus, jgaus, ij
62  integer :: mask_tile, mask_fg_tile
63  integer :: itile, jtile
64  integer :: j, ierr
65  integer :: igausp1, jgausp1
66  real :: fill
67 
68  integer, allocatable :: id1(:,:), id2(:,:), jdc(:,:)
69 
70  real :: wsum
71  real :: stc_inc(lsoil)
72  real, allocatable :: xpts(:), ypts(:), lats(:), lons(:)
73  real, allocatable :: dum2d(:,:), lats_rad(:), lons_rad(:)
74  real, allocatable :: agrid(:,:,:), s2c(:,:,:)
75 
76  integer :: k, nother, nsnowupd, nnosoilnear, nsoilupd, nsnowchange
77  logical :: gaus_has_soil
78 
79  integer, parameter :: lsoil_incr=3 ! number of layers to add incrments to
80 
81  ! this produces the same lat/lon as can be read in from the file
82 
83  kgds_gaus = 0
84  kgds_gaus(1) = 4 ! oct 6 - type of grid (gaussian)
85  kgds_gaus(2) = idim_gaus ! oct 7-8 - # pts on latitude circle
86  kgds_gaus(3) = jdim_gaus
87  kgds_gaus(4) = 90000 ! oct 11-13 - lat of origin
88  kgds_gaus(5) = 0 ! oct 14-16 - lon of origin
89  kgds_gaus(6) = 128 ! oct 17 - resolution flag
90  kgds_gaus(7) = -90000 ! oct 18-20 - lat of extreme point
91  kgds_gaus(8) = nint(-360000./float(idim_gaus)) ! oct 21-23 - lon of extreme point
92  kgds_gaus(9) = nint((360.0 / float(idim_gaus))*1000.0)
93  ! oct 24-25 - longitude direction incr.
94  kgds_gaus(10) = jdim_gaus/2 ! oct 26-27 - number of circles pole to equator
95  kgds_gaus(12) = 255 ! oct 29 - reserved
96  kgds_gaus(20) = 255 ! oct 5 - not used, set to 255
97 
98  print*
99  print*,'adjust soil temperature using gsi increments on gaussian grid'
100  print*,'adjusting first ', lsoil_incr, ' surface layers only'
101 
102  !----------------------------------------------------------------------
103  ! call gdswzd to compute the lat/lon of each gsi gaussian grid point.
104  !----------------------------------------------------------------------
105 
106  iopt = 0
107  fill = -9999.
108  allocate(xpts(idim_gaus*jdim_gaus))
109  allocate(ypts(idim_gaus*jdim_gaus))
110  allocate(lats(idim_gaus*jdim_gaus))
111  allocate(lons(idim_gaus*jdim_gaus))
112  xpts = fill
113  ypts = fill
114  lats = fill
115  lons = fill
116 
117  call gdswzd(kgds_gaus,iopt,(idim_gaus*jdim_gaus),fill,xpts,ypts,lons,lats,nret)
118 
119  if (nret /= (idim_gaus*jdim_gaus)) then
120  print*,'fatal error: problem in gdswzd. stop.'
121  call mpi_abort(mpi_comm_world, 12, ierr)
122  endif
123 
124  deallocate (xpts, ypts)
125 
126  allocate(dum2d(idim_gaus,jdim_gaus))
127  dum2d = reshape(lats, (/idim_gaus,jdim_gaus/) )
128  deallocate(lats)
129 
130  allocate(lats_rad(jdim_gaus))
131 
132  do j = 1, jdim_gaus
133  lats_rad(j) = dum2d(1,jdim_gaus-j+1) * 3.1415926 / 180.0
134  enddo
135 
136  dum2d = reshape(lons, (/idim_gaus,jdim_gaus/) )
137  deallocate(lons)
138  allocate(lons_rad(idim_gaus))
139  lons_rad = dum2d(:,1) * 3.1415926 / 180.0
140 
141  deallocate(dum2d)
142 
143  allocate(agrid(idim,jdim,2))
144  agrid(:,:,1) = reshape(rlo, (/idim,jdim/) )
145  agrid(:,:,2) = reshape(rla, (/idim,jdim/) )
146  agrid = agrid * 3.1415926 / 180.0
147 
148  allocate(id1(idim,jdim))
149  allocate(id2(idim,jdim))
150  allocate(jdc(idim,jdim))
151  allocate(s2c(idim,jdim,4))
152 
153  !----------------------------------------------------------------------
154  ! compute bilinear weights for each model point from the nearest
155  ! four gsi/gaussian points. does not account for mask. that
156  ! happens later.
157  !----------------------------------------------------------------------
158 
159  call remap_coef( 1, idim, 1, jdim, idim_gaus, jdim_gaus, &
160  lons_rad, lats_rad, id1, id2, jdc, s2c, agrid )
161 
162  deallocate(lons_rad, lats_rad, agrid)
163  !
164  ! initialize variables for counts statitics to be zeros
165  !
166 
167  !
168  nother = 0 ! grid cells not land
169  nsnowupd = 0 ! grid cells with snow (temperature not yet updated)
170  nsnowchange = 0 ! grid cells where no temp upd made, because snow occurence changed
171  nnosoilnear = 0 ! grid cells where model has soil, but 4 closest gaus grids don't
172  ! (no update made here)
173  nsoilupd = 0
174 
175 
176  ij_loop : do ij = 1, lensfc
177 
178  mask_tile = soilsnow_tile(ij)
179  mask_fg_tile = soilsnow_fg_tile(ij)
180 
181  !----------------------------------------------------------------------
182  ! mask: 1 - soil, 2 - snow, 0 - land-ice, -1 - not land
183  !----------------------------------------------------------------------
184 
185  if (mask_tile <= 0) then ! skip if neither soil nor snow
186  nother = nother + 1
187  cycle ij_loop
188  endif
189 
190 
191  ! get i,j index on array of (idim,jdim) from known ij
192 
193  jtile = (ij-1) / idim + 1
194  itile = mod(ij,idim)
195  if (itile==0) itile = idim
196 
197  !----------------------------------------------------------------------
198  ! if the snow analysis has chnaged to occurence of snow, skip the
199  ! temperature analysis
200  !----------------------------------------------------------------------
201 
202  if ((mask_fg_tile == 2 .and. mask_tile == 1) .or. &
203  (mask_fg_tile == 1 .and. mask_tile == 2) ) then
204  nsnowchange = nsnowchange + 1
205  cycle ij_loop
206  endif
207 
208  !----------------------------------------------------------------------
209  ! do update to soil temperature grid cells, using bilinear interp
210  !----------------------------------------------------------------------
211 
212  if (mask_tile == 1) then
213  ! these are the four nearest grid cells on the gaus grid
214  igaus = id1(itile,jtile)
215  jgaus = jdc(itile,jtile)
216  igausp1 = id2(itile,jtile)
217  jgausp1 = jdc(itile,jtile)+1
218 
219  ! make sure gaus grid has soil nearby
220  gaus_has_soil = .false.
221  if (soilsnow_gaus(igaus,jgaus) == 1 .or. &
222  soilsnow_gaus(igausp1,jgaus) == 1 .or. &
223  soilsnow_gaus(igausp1,jgausp1) == 1 .or. &
224  soilsnow_gaus(igaus,jgausp1) == 1) gaus_has_soil = .true.
225 
226  if (.not. gaus_has_soil) then
227  nnosoilnear = nnosoilnear + 1
228  cycle ij_loop
229  endif
230 
231  ! calcualate weighted increment over nearby grid cells that have soil
232 
233  ! Draper: to-do, code adding increments to soil moisture.
234  ! will require converting to soil wetness index first
235  ! (need to add soil properties to the increment file)
236 
237  nsoilupd = nsoilupd + 1
238 
239  stc_inc = 0.0
240  wsum = 0.0
241 
242  if (soilsnow_gaus(igaus,jgaus) == 1) then
243  do k = 1, lsoil_incr
244  stc_inc(k) = stc_inc(k) + (s2c(itile,jtile,1) * stc_inc_gaus(k,igaus,jgaus))
245  enddo
246  wsum = wsum + s2c(itile,jtile,1)
247  endif
248 
249  if (soilsnow_gaus(igausp1,jgaus) == 1) then
250  do k = 1, lsoil_incr
251  stc_inc(k) = stc_inc(k) + (s2c(itile,jtile,2) * stc_inc_gaus(k,igausp1,jgaus))
252  enddo
253  wsum = wsum + s2c(itile,jtile,2)
254  endif
255 
256  if (soilsnow_gaus(igausp1,jgausp1) == 1) then
257  do k = 1, lsoil_incr
258  stc_inc(k) = stc_inc(k) + (s2c(itile,jtile,3) * stc_inc_gaus(k,igausp1,jgausp1))
259  enddo
260  wsum = wsum + s2c(itile,jtile,3)
261  endif
262 
263  if (soilsnow_gaus(igaus,jgausp1) == 1) then
264  do k = 1, lsoil_incr
265  stc_inc(k) = stc_inc(k) + (s2c(itile,jtile,4) * stc_inc_gaus(k,igaus,jgausp1))
266  enddo
267  wsum = wsum + s2c(itile,jtile,4)
268  endif
269 
270  ! add increment
271  do k = 1, lsoil_incr
272  stc_inc(k) = stc_inc(k) / wsum
273  stc_state(ij,k) = stc_state(ij,k) + stc_inc(k)
274  ! todo, apply some bounds?
275  enddo
276 
277  ! don't update soil states if snow present.
278  elseif(mask_tile==2) then
279  nsnowupd = nsnowupd + 1
280 
281  endif ! if soil/snow point
282 
283  enddo ij_loop
284 
285  write(*,'(a,i2)') 'statistics of grids number processed for rank : ', myrank
286  write(*,'(a,i8)') ' soil grid cells updated = ',nsoilupd
287  write(*,'(a,i8)') ' (not updated) soil grid cells, no soil nearby on gsi grid = ',nnosoilnear
288  write(*,'(a,i8)') ' (not updated) soil grid cells, change in presence of snow = ', nsnowchange
289  write(*,'(a,i8)') ' (not updated yet) snow grid cells = ', nsnowupd
290  write(*,'(a,i8)') ' grid cells, without soil or snow = ', nother
291 
292  nother = 0 ! grid cells not land
293  nsnowupd = 0 ! grid cells where no temp upd made, because snow occurence changed
294  nnosoilnear = 0 ! grid cells where model has soil, but 4 closest gaus grids don't
295  ! (no update made here)
296  nsoilupd = 0
297 
298  deallocate(id1, id2, jdc, s2c)
299 
300 end subroutine add_increment_soil
301 
313 
314 subroutine add_increment_snow(snd_inc,mask,lensfc,snd)
316  implicit none
317 
318  integer, intent(in) :: lensfc
319  real, intent(in) :: snd_inc(lensfc)
320  integer, intent(in) :: mask(lensfc)
321  real, intent(inout) :: snd(lensfc)
322 
323  integer :: i
324 
325 
326  do i =1, lensfc
327  if (mask(i) > 0) then ! if land
328  snd(i) = max( snd(i) + snd_inc(i), 0.)
329  endif
330  enddo
331 
332 end subroutine add_increment_snow
333 
344 subroutine calculate_landinc_mask(smc,swe,vtype,lensfc,veg_type_landice,mask)
345 
346  implicit none
347 
348  integer, intent(in) :: lensfc, veg_type_landice
349  real, intent(in) :: smc(lensfc), swe(lensfc)
350  real, intent(in) :: vtype(lensfc)
351  integer, intent(out) :: mask(lensfc)
352 
353  integer :: i
354 
355  mask = -1 ! not land
356 
357  ! land (but not land-ice)
358  do i=1,lensfc
359  if (smc(i) .LT. 1.0) then
360  if (swe(i) .GT. 0.001) then ! snow covered land
361  mask(i) = 2
362  else ! non-snow covered land
363  mask(i) = 1
364  endif
365  end if ! else should work here too
366  if ( nint(vtype(i)) == veg_type_landice ) then ! land-ice
367  mask(i) = 0
368  endif
369  end do
370 
371 end subroutine calculate_landinc_mask
372 
380 
393 
394 subroutine apply_land_da_adjustments_stc(lsm, isot, ivegsrc,lensfc, &
395  lsoil, rsoiltype, mask, stc_bck, stc_anl, smc_adj, slc_adj )
397  use mpi
398  use set_soilveg_snippet_mod, only: set_soilveg
399  use sflx_snippet, only: frh2o
400 
401  implicit none
402 
403  integer, intent(in) :: lsm, lensfc, lsoil, isot, ivegsrc
404  real, intent(in) :: rsoiltype(lensfc) ! soil types, as real
405  integer, intent(in) :: mask(lensfc)
406  real, intent(in) :: stc_bck(lensfc, lsoil) , stc_anl(lensfc, lsoil)
407  real, intent(inout) :: smc_adj(lensfc,lsoil), slc_adj(lensfc,lsoil)
408 
409 
410  logical :: frzn_bck, frzn_anl
411 
412  integer :: i, l, n_freeze, n_thaw, ierr
413  integer :: myrank, soiltype, iret
414 
415  real :: slc_new
416 
417  real, parameter :: tfreez=273.16
418  real, dimension(30) :: maxsmc, bb, satpsi
419 
420  call mpi_comm_rank(mpi_comm_world, myrank, ierr)
421 
422  if (lsm .NE. lsm_noah) then
423  print *, 'FATAL ERROR: apply_land_da_adjustments not coded for models other than noah', lsm
424  call mpi_abort(mpi_comm_world, 10, ierr)
425  endif
426 
427  ! initialise soil properties
428  call set_soilveg(isot, ivegsrc, maxsmc, bb, satpsi, iret)
429  if (iret < 0) then
430  print *, 'FATAL ERROR: problem in set_soilveg'
431  call mpi_abort(mpi_comm_world, 10, ierr)
432  endif
433 
434  print *, 'Adjusting smc after stc DA update'
435 
436  n_freeze = 0
437  n_thaw = 0
438 
439  do i=1,lensfc
440  if (mask(i) > 0) then ! if soil location
441  do l = 1, lsoil
442  frzn_bck = (stc_bck(i,l) .LT. tfreez )
443  frzn_anl = (stc_anl(i,l) .LT. tfreez )
444 
445  if (frzn_bck .eqv. frzn_anl) then
446  cycle
447  elseif (frzn_bck .and. .not. frzn_anl) then
448  n_thaw = n_thaw + 1
449  else
450  n_freeze = n_freeze + 1
451  endif
452 
453  ! make adjustment (same routine for both)
454  soiltype = nint(rsoiltype(i))
455  ! bb and maxsmc are in the namelist_soilveg, need soiltype index
456  call frh2o(stc_anl(i,l), smc_adj(i,l),slc_adj(i,l), maxsmc(soiltype), &
457  bb(soiltype), satpsi(soiltype),slc_new)
458 
459  slc_adj(i,l) = max( min( slc_new, smc_adj(i,l)), 0.0 )
460  enddo
461  endif
462  enddo
463  print *, 'adjusted: ', n_thaw,' thawed,', n_freeze, ' frozen'
464 
465 end subroutine apply_land_da_adjustments_stc
466 
472 
481 
482 subroutine apply_land_da_adjustments_snd(lsm, lensfc, mask, swe_bck, snd_bck, snd_anl, swe_adj)
484  use mpi
485  use bulk_snow_module, only: calc_density
486 
487  implicit none
488 
489  integer, intent(in) :: lsm, lensfc
490  integer, intent(in) :: mask(lensfc)
491  real, intent(in) :: swe_bck(lensfc), snd_bck(lensfc)
492  real, intent(in) :: snd_anl(lensfc)
493  real, intent(inout) :: swe_adj(lensfc)
494 
495  integer :: ierr, myrank, i
496 
497  real :: density(lensfc)
498 
499  call mpi_comm_rank(mpi_comm_world, myrank, ierr)
500 
501  if (lsm .NE. lsm_noah) then
502  print *, 'FATAL ERROR: apply_land_da_adjustments not coded for models other than noah', lsm
503  call mpi_abort(mpi_comm_world, 10, ierr)
504  endif
505 
506  ! calculate snow density from forecasts
507  call calc_density(lensfc, mask, swe_bck, snd_bck, myrank, density)
508 
509  do i =1, lensfc
510  if ( mask(i)>0 ) then
511  swe_adj(i) = snd_anl(i)*density(i)
512  endif
513  enddo
514 
515 
516 end subroutine apply_land_da_adjustments_snd
517 
518 end module land_increments
Module containing utility routines.
Definition: utils.F90:7
real function tfreez(salinity)
Compute the freezing point of water as a function of salinity.
Definition: cycle.f90:1933