MPAS-JEDI
mpasjedi_vc_model2geovars_mod.F90
Go to the documentation of this file.
1 ! (C) Copyright 2020 UCAR
2 !
3 ! This software is licensed under the terms of the Apache Licence Version 2.0
4 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5 
7 
8 use iso_c_binding
9 
10 use fckit_configuration_module, only: fckit_configuration
11 use fckit_log_module, only: fckit_log
12 
13 !oops
14 use kinds, only : kind_real
15 
16 !ufo
17 use gnssro_mod_transform, only: geometric2geop
18 use ufo_vars_mod
19 
20 !MPAS-Model
21 use mpas_derived_types
22 use mpas_field_routines
23 use mpas_kind_types, only: rkind
24 use mpas_pool_routines
25 
26 !mpas-jedi
29 use mpas_geom_mod, only: mpas_geom
31 
32 !TODO: package the variable changes somewhere
34 !use mpas_vc_utils
35 
36 implicit none
37 
38 private
40 
42  contains
43  procedure, public :: create
44  procedure, public :: delete
45  procedure, public :: changevar
47 
48 ! --------------------------------------------------------------------------------------------------
49 
50 contains
51 
52 ! --------------------------------------------------------------------------------------------------
53 
54 subroutine create(self, geom, conf)
55 
56 class(mpasjedi_vc_model2geovars), intent(inout) :: self
57 type(mpas_geom), intent(in) :: geom
58 type(fckit_configuration), intent(in) :: conf
59 
60 end subroutine create
61 
62 ! --------------------------------------------------------------------------------------------------
63 
64 subroutine delete(self)
65 
66 class(mpasjedi_vc_model2geovars), intent(inout) :: self
67 
68 end subroutine delete
69 
70 ! --------------------------------------------------------------------------------------------------
71 
72 subroutine changevar(self, geom, xm, xg)
73 
74  class(mpasjedi_vc_model2geovars), intent(inout) :: self
75  type(mpas_geom), intent(in) :: geom !< mpas mesh descriptors
76  class(mpas_fields), intent(in) :: xm !< model state fields
77  class(mpas_fields), intent(inout) :: xg !< state containing geovar fields
78 
79  ! pool-related pointers
80  type(mpas_pool_type), pointer :: mFields
81  type(mpas_pool_data_type), pointer :: mdata, gdata
82 
83  ! reusable fields
84  type(field2dreal), pointer :: fieldr2_a, fieldr2_b
85 
86  ! reusable arrays
87  real(kind=kind_real), dimension(:), pointer :: ptrr1_a, ptrr1_b
88  real(kind=kind_real), dimension(:,:), pointer :: ptrr2_a, ptrr2_b
89  real(kind=kind_real), dimension(:,:), allocatable :: r2_a, r2_b
90 
91  ! iteration-specific variables
92  character(len=MAXVARLEN) :: geovar
93  integer :: nCells, nVertLevels, nVertLevelsP1
94  integer :: iVar, iCell, iLevel
95  real (kind=kind_real) :: lat
96 
97  ! surface variables
98  character(len=MAXVARLEN), parameter :: &
99  MPASSfcClassifyNames(5) = &
100  [ character(len=maxvarlen) :: 'ivgtyp', 'isltyp', 'landmask', 'xice', 'snowc']
101  character(len=MAXVARLEN), parameter :: &
102  CRTMSfcClassifyNames(7) = &
103  [var_sfc_vegtyp, var_sfc_landtyp, var_sfc_soiltyp, &
104  var_sfc_wfrac, var_sfc_lfrac, var_sfc_ifrac, var_sfc_sfrac]
105  type(mpas_pool_type), pointer :: CRTMSfcClassifyFields => null()
106  integer, dimension(:), pointer :: vegtyp, soiltyp
107  real(kind=kind_real), dimension(:), pointer :: wfrac, lfrac, ifrac, sfrac
108 
109  ! air pressure on w levels
110  real(kind=kind_real), allocatable :: plevels(:,:)
111 
112  ! config members
113  character(len=StrKIND), pointer :: &
114  config_microp_scheme, config_radt_cld_scheme
115  logical, pointer :: config_microp_re
116 
117  ! convenient local variables
118  mfields => xm % subFields
119  ncells = geom%nCellsSolve
120  nvertlevels = geom%nVertLevels
121  nvertlevelsp1 = geom%nVertLevelsP1
122 
123  ! CRTM surface type and fraction fields are interdependent
124  ! pre-calculate all such fields if any are requested
125  if ( any(xg%has(crtmsfcclassifynames)) ) then
126 
127  if (.not. all(xm%has(mpassfcclassifynames))) then
128  call abor1_ftn('mpasjedi_vc_model2geovars::changevar: xm must include MPASSfcClassifyNames!')
129  end if
130 
131  call da_template_pool(geom, crtmsfcclassifyfields, size(crtmsfcclassifynames), crtmsfcclassifynames)
132 
133  !! surface types
134  ! land type
135  call xm%copy_to('ivgtyp', crtmsfcclassifyfields, var_sfc_landtyp)
136 
137  ! veg type
138  ! uses ivgtyp as input
139  call mpas_pool_get_array(crtmsfcclassifyfields, var_sfc_vegtyp, vegtyp)
140  call xm%get('ivgtyp', mdata)
141  do icell = 1, ncells
142  vegtyp(icell) = convert_type_veg(mdata%i1%array(icell))
143  end do
144 
145  ! soil type
146  call mpas_pool_get_array(crtmsfcclassifyfields, var_sfc_soiltyp, soiltyp)
147  call xm%get('isltyp', mdata)
148  do icell = 1, ncells
149  soiltyp(icell) = convert_type_soil(mdata%i1%array(icell))
150  end do
151 
152  !! surface fractions
153  ! land, will be adjusted later
154  ! TODO: a binary landmask is a crude indicator of sub-grid land fraction
155  call mpas_pool_get_array(crtmsfcclassifyfields, var_sfc_lfrac, lfrac)
156  call xm%get('landmask', mdata) !'land-ocean mask (1=>land ; 0=>ocean)'
157  lfrac(1:ncells) = real(mdata%i1%array(1:ncells), rkind)
158 
159  ! ice
160  call mpas_pool_get_array(crtmsfcclassifyfields, var_sfc_ifrac, ifrac)
161  call xm%get('xice', mdata) !'fractional area coverage of sea-ice'
162  ifrac(1:ncells) = mdata%r1%array(1:ncells)
163 
164  ! snow
165  ! TODO: Investigate. snowc varies between 0. and 1., but Registry description indicates it is binary.
166  ! Similar comment to landmask.
167  call mpas_pool_get_array(crtmsfcclassifyfields, var_sfc_sfrac, sfrac)
168  call xm%get('snowc', mdata) !'flag for snow on ground (0=>no snow; 1=>otherwise)'
169  sfrac(1:ncells) = mdata%r1%array(1:ncells)
170 
171  ! water+snow+land
172  call mpas_pool_get_array(crtmsfcclassifyfields, var_sfc_wfrac, wfrac)
173  do icell = 1, ncells
174  if (ifrac(icell) > mpas_jedi_zero_kr) then
175  sfrac(icell) = max(min(sfrac(icell), mpas_jedi_one_kr - ifrac(icell)), mpas_jedi_zero_kr)
176  wfrac(icell) = max(mpas_jedi_one_kr - ifrac(icell) - sfrac(icell), mpas_jedi_zero_kr)
177  else if (sfrac(icell) > mpas_jedi_zero_kr) then
178  wfrac(icell) = max(mpas_jedi_one_kr - sfrac(icell), mpas_jedi_zero_kr)
179  else
180  wfrac(icell) = max(mpas_jedi_one_kr - lfrac(icell), mpas_jedi_zero_kr)
181  ifrac(icell) = mpas_jedi_zero_kr
182  sfrac(icell) = mpas_jedi_zero_kr
183  end if
184  lfrac(icell) = max(mpas_jedi_one_kr - ifrac(icell) - sfrac(icell) - wfrac(icell), mpas_jedi_zero_kr)
185  end do
186  end if
187 
188 
189  ! pre-calculate pressure on w levels
190  allocate(plevels(1:nvertlevelsp1,1:ncells))
191  call xm%get('pressure', ptrr2_a)
192  call xm%get('surface_pressure', ptrr1_a)
193  call pressure_half_to_full(ptrr2_a(:,1:ncells), geom%zgrid(:,1:ncells), ptrr1_a(1:ncells), &
194  ncells, nvertlevels, plevels)
195 
196 
197  ! populate xg
198  do ivar = 1, xg % nf
199 
200  geovar = trim(xg % fldnames(ivar))
201 
202  if (geom%has_identity(geovar)) then
203  ! "identity" variable changes are controlled in an external configuration file
204  ! through the geom object. Refer to the mpas_geom type for more information.
205  if (xm%has(geom%identity(geovar))) then
206  call xm%copy_to(geom%identity(geovar), xg, geovar)
207  else
208  call abor1_ftn('mpasjedi_vc_model2geovars::changevar: '&
209  &'state missing identity field for geovar => '//trim(geovar))
210  end if
211  else
212 
213  call xg%get(geovar, gdata)
214 
215  select case (trim(geovar))
216 
217  case ( var_tv ) !-virtual_temperature
218  call xm%get('temperature', ptrr2_a)
219  call xm%get('spechum', ptrr2_b)
220  allocate(r2_a(1:nvertlevels, 1:ncells))
221  call q_to_w( ptrr2_b(:,1:ncells), r2_a(:,1:ncells) )
222  call tw_to_tv( ptrr2_a(:,1:ncells), r2_a(:,1:ncells), gdata%r2%array(:,1:ncells) )
223  deallocate(r2_a)
224 
225  case ( var_mixr ) !-humidity_mixing_ratio
226  call xm%get('spechum', ptrr2_a)
227  call q_to_w(ptrr2_a(:,1:ncells), gdata%r2%array(:,1:ncells))
228  gdata%r2%array(:,1:ncells) = gdata%r2%array(:,1:ncells) * mpas_jedi_thousand_kr ! [kg/kg] -> [g/kg]
229 
230  ! Ensure positive-definite mixing ratios
231  ! with respect to precision of crtm::CRTM_Parameters::ZERO.
232  ! TODO: this should be moved to the mpas_fields%read step
233  ! only other place it should be needed is add_incr (already there)
234  where(gdata%r2%array(:,1:ncells) < mpas_jedi_zero_kr)
235  gdata%r2%array(:,1:ncells) = mpas_jedi_zero_kr
236  end where
237 
238  case ( var_prsi ) !-air_pressure_levels
239  gdata%r2%array(1:nvertlevelsp1,1:ncells) = plevels(1:nvertlevelsp1,1:ncells)
240 
241  case ( var_oz ) !-mole_fraction_of_ozone_in_air :TODO: not directly available from MPAS
242  !call xm%get('o3', mdata)
243  gdata%r2%array(:,1:ncells) = mpas_jedi_zero_kr !mdata%r2%array(:,1:nCells)
244 
245  case ( var_co2 ) !-mole_fraction_of_carbon_dioxide_in_air :TODO: not directly available from MPAS
246  !call xm%get('co2', mdata)
247  gdata%r2%array(:,1:ncells) = mpas_jedi_zero_kr !mdata%r2%array(:,1:nCells)
248 
249  case ( var_clw ) !-mass_content_of_cloud_liquid_water_in_atmosphere_layer
250  call q_fields_forward('qc', mfields, gdata%r2, plevels, ncells, nvertlevels)
251 
252  case ( var_cli ) !-mass_content_of_cloud_ice_in_atmosphere_layer
253  call q_fields_forward('qi', mfields, gdata%r2, plevels, ncells, nvertlevels)
254 
255  case ( var_clr ) !-mass_content_of_rain_in_atmosphere_layer
256  call q_fields_forward('qr', mfields, gdata%r2, plevels, ncells, nvertlevels)
257 
258  case ( var_cls ) !-mass_content_of_snow_in_atmosphere_layer
259  call q_fields_forward('qs', mfields, gdata%r2, plevels, ncells, nvertlevels)
260 
261  case ( var_clg ) !-mass_content_of_graupel_in_atmosphere_layer
262  call q_fields_forward('qg', mfields, gdata%r2, plevels, ncells, nvertlevels)
263 
264  case ( var_clh ) !-mass_content_of_hail_in_atmosphere_layer
265  call q_fields_forward('qh', mfields, gdata%r2, plevels, ncells, nvertlevels)
266 
267  case ( var_clwefr ) !-effective_radius_of_cloud water particle
268  call mpas_pool_get_config(geom % domain % blocklist % configs, 'config_microp_re', config_microp_re)
269  if (config_microp_re) then
270  call xm%get('re_cloud', ptrr2_a) !- [m]
271  gdata%r2%array(:,1:ncells) = ptrr2_a(:,1:ncells) * mpas_jedi_million_kr ! [m] -> [micron]
272  else
273  gdata%r2%array(:,1:ncells) = 10.0_kind_real ![micron] ! Or, we can refer to rre_cloud which is default for MPAS radiance scheme
274  end if
275 
276  case ( var_cliefr ) !-effective_radius_of_cloud ice particle
277  call mpas_pool_get_config(geom % domain % blocklist % configs, 'config_microp_re', config_microp_re)
278  if (config_microp_re) then
279  call xm%get('re_ice', ptrr2_a) !- [m]
280  gdata%r2%array(:,1:ncells) = ptrr2_a(:,1:ncells) * mpas_jedi_million_kr ! [m] -> [micron]
281  else
282  gdata%r2%array(:,1:ncells) = 30.0_kind_real ! [micron]
283  end if
284 
285  case ( var_clrefr ) !-effective_radius_of_rain water_particle
286  ! effective_radius_of_rain_water is not calculated in MPAS model physics
287  call xm%get('qr', fieldr2_a)
288  call mpas_pool_get_config(geom % domain % blocklist % configs, 'config_microp_scheme', config_microp_scheme)
289  call mpas_pool_get_config(geom % domain % blocklist % configs, 'config_microp_re', config_microp_re)
290  if (config_microp_re) then
291  allocate(r2_a(1:nvertlevels, 1:ncells))
292  allocate(r2_b(1:nvertlevels, 1:ncells))
293  if (trim(config_microp_scheme) == 'mp_thompson') then
294  call xm%get('nr', ptrr2_a) !- [nb kg^{-1}]: MPAS output for 2-moment MP scheme
295  r2_b(:,1:ncells) = ptrr2_a(:,1:ncells)
296  else
297  r2_b = mpas_jedi_one_kr
298  end if
299  call xm%get('rho', fieldr2_b) !- [kg m^{-3}]: Dry air density
300 
301  call effectrad_rainwater(fieldr2_a%array(:,1:ncells), fieldr2_b%array(:,1:ncells),&
302  r2_b(:,1:ncells), r2_a(:,1:ncells), config_microp_scheme, &
303  ncells, nvertlevels)
304  gdata%r2%array(:,1:ncells) = r2_a(:,1:ncells) * mpas_jedi_million_kr ! [m] -> [micron]
305  deallocate(r2_a)
306  deallocate(r2_b)
307  else
308  gdata%r2%array(:,1:ncells) = 999.0_kind_real ! [micron]
309  end if
310 
311  case ( var_clsefr ) !-effective_radius_of_snow particle
312  call mpas_pool_get_config(geom % domain % blocklist % configs, 'config_microp_re', config_microp_re)
313  if (config_microp_re) then
314  call xm%get('re_snow', mdata) !- [m]
315  gdata%r2%array(:,1:ncells) = mdata%r2%array(:,1:ncells) * mpas_jedi_million_kr ! [m] -> [micron]
316  else
317  gdata%r2%array(:,1:ncells) = 600.0_kind_real ! [micron]
318  end if
319 
320  case ( var_clgefr ) !-effective_radius_of_graupel_particle
321  !effective_radius_of_graupel is not calculated in MPAS model physics
322  call xm%get('qg', fieldr2_a)
323  call mpas_pool_get_config(geom % domain % blocklist % configs, 'config_microp_scheme', config_microp_scheme)
324  call mpas_pool_get_config(geom % domain % blocklist % configs, 'config_microp_re', config_microp_re)
325  if (config_microp_re) then
326  allocate(r2_a(1:nvertlevels, 1:ncells))
327  call xm%get('rho', fieldr2_b) !- [kg m^{-3}]: Dry air density
328  call effectrad_graupel(fieldr2_a%array(:,1:ncells), fieldr2_b%array(:,1:ncells), &
329  r2_a(:,1:ncells), config_microp_scheme, &
330  ncells, nvertlevels)
331  gdata%r2%array(:,1:ncells) = r2_a(:,1:ncells) * mpas_jedi_million_kr ! [m] -> [micron]
332  deallocate(r2_a)
333  else
334  gdata%r2%array(:,1:ncells) = 600.0_kind_real ! [micron]
335  end if
336 
337  case ( var_clhefr ) !-effective_radius_of_hail_particle :TODO: currently filled w/ default value
338  !The current MP schemes do not include hail (wsm7 has)
339  !call xm%get('re_hail', mdata)
340  !gdata%r2%array(:,1:nCells) = mdata%r2%array(:,1:nCells) * MPAS_JEDI_MILLION_kr ! [m] -> [micron]
341  gdata%r2%array(:,1:ncells) = 600.0_kind_real ! [micron]
342 
343  case ( var_cldfrac ) !-cloud_area_fraction_in_atmosphere_layer
344  call mpas_pool_get_config(geom % domain % blocklist % configs, &
345  'config_radt_cld_scheme', config_radt_cld_scheme)
346  call mpas_pool_get_config(geom % domain % blocklist % configs, &
347  'config_microp_scheme', config_microp_scheme)
348 
349  if ( trim(config_radt_cld_scheme) == mpas_jedi_off .or. &
350  trim(config_microp_scheme) == mpas_jedi_off ) then
351  gdata%r2%array(:,1:ncells) = mpas_jedi_lessone_kr
352  else if (xm%has('cldfrac')) then
353  call xm%copy_to('cldfrac', xg, geovar)
354  else
355  call abor1_ftn('mpasjedi_vc_model2geovars::changevar: cldfrac must be added to the state &
356  & variables in order to populate the var_cldfrac geovar with the MPAS diagnostic cloud &
357  & fraction')
358  end if
359 
360  case ( var_z ) !-geopotential_height, geopotential heights at midpoint
361  ! calculate midpoint geometricZ (unit: m):
362  allocate(r2_a(1:nvertlevels,1:ncells))
363  call geometricz_full_to_half(geom%zgrid(:,1:ncells), ncells, &
364  nvertlevels, r2_a(:,1:ncells))
365  do icell = 1, ncells
366  lat = geom%latCell(icell) * mpas_jedi_rad2deg_kr !- to Degrees
367  do ilevel = 1, nvertlevels
368  call geometric2geop(lat, r2_a(ilevel,icell), gdata%r2%array(ilevel,icell))
369  enddo
370  enddo
371  deallocate(r2_a)
372 
373  case ( var_geomz ) !-height
374  ! calculate midpoint geometricZ (unit: m):
375  call geometricz_full_to_half(geom%zgrid(:,1:ncells), ncells, &
376  nvertlevels, gdata%r2%array(:,1:ncells))
377 
378 !! begin surface variables
379  case ( var_sfc_z ) !-surface_geopotential_height
380  do icell=1,ncells
381  lat = geom%latCell(icell) * mpas_jedi_rad2deg_kr !- to Degrees
382  call geometric2geop(lat, geom%zgrid(1,icell), gdata%r1%array(icell))
383  enddo
384 
385  case ( var_sfc_geomz ) !-surface_altitude
386  gdata%r1%array(1:ncells) = geom%zgrid(1,1:ncells)
387 
388  case ( var_sfc_sdepth ) !-surface_snow_thickness
389  call xm%get('snowh', mdata)
390  gdata%r1%array(1:ncells) = mdata%r1%array(1:ncells) * mpas_jedi_thousand_kr ! [m] -> [mm]
391 
392  case ( var_sfc_vegfrac ) !-vegetation_area_fraction
393  call xm%get('vegfra', mdata)
394  gdata%r1%array(1:ncells) = mdata%r1%array(1:ncells) / 100.0_kind_real ! [unitless, 0~100] = [%, 0~1]
395 
396  case ( var_sfc_soilm ) !-volume_fraction_of_condensed_water_in_soil
397  ! NOTE: use 1st level
398  ! NOTE: units are equivalent with water density 1 g/cm3
399  call xm%get('smois', mdata)
400  gdata%r1%array(1:ncells) = mdata%r2%array(1,1:ncells) ! [m3/m3] -> [g/cm3]
401 
402  case ( var_sfc_soilt ) !-soil_temperature
403  ! NOTE: use 1st level
404  call xm%get('tslb', mdata)
405  gdata%r1%array(1:ncells) = mdata%r2%array(1,1:ncells) ! [K] -> [K]
406 
407  case ( var_sfc_landtyp, var_sfc_vegtyp, var_sfc_soiltyp, &
408  var_sfc_wfrac, var_sfc_lfrac, var_sfc_ifrac, var_sfc_sfrac )
409  ! CRTMSfcClassifyNames
410  !-land_type_index, vegetation_type_index, soil_type
411  !-water_area_fraction, land_area_fraction, ice_area_fraction, surface_snow_area_fraction
412  call xg%copy_from(geovar, crtmsfcclassifyfields)
413 
414  case ( var_sfc_wspeed ) !-surface_wind_speed
415  call xm%get('u10', ptrr1_a)
416  call xm%get('v10', ptrr1_b)
417  gdata%r1%array(1:ncells)=sqrt( ptrr1_a(1:ncells)**2 + ptrr1_b(1:ncells)**2 )
418 
419 !! end surface variables
420 
421  case default
422  call abor1_ftn('mpasjedi_vc_model2geovars::changevar: geovar not implemented => '//trim(geovar))
423 
424  end select
425 
426  end if
427 
428  end do !iVar
429 
430  if (associated(crtmsfcclassifyfields)) then
431  call mpas_pool_destroy_pool(crtmsfcclassifyfields)
432  end if
433 
434  deallocate(plevels)
435 
436 end subroutine changevar
437 
438 ! --------------------------------------------------------------------------------------------------
439 
441 
elemental subroutine, public q_to_w(specific_humidity, mixing_ratio)
integer function, public convert_type_soil(type_in)
subroutine, public pressure_half_to_full(pressure, zgrid, surface_pressure, nC, nV, pressure_f)
elemental subroutine, public tw_to_tv(temperature, mixing_ratio, virtual_temperature)
subroutine, public geometricz_full_to_half(zgrid_f, nC, nV, zgrid)
subroutine, public effectrad_rainwater(qr, rho, nr, re_qr, mp_scheme, ngrid, nVertLevels)
integer function, public convert_type_veg(type_in)
subroutine, public effectrad_graupel(qg, rho, re_qg, mp_scheme, ngrid, nVertLevels)
subroutine, public q_fields_forward(mqName, modelFields, qGeo, plevels, nCells, nVertLevels)
subroutine, public da_template_pool(geom, templatePool, nf, fieldnames)
Subset a pool from fields described in geom.
real(kind=kind_real), parameter mpas_jedi_thousand_kr
real(kind=kind_real), parameter mpas_jedi_zero_kr
real(kind=kind_real), parameter mpas_jedi_rad2deg_kr
real(kind=kind_real), parameter mpas_jedi_lessone_kr
character(len=3), parameter mpas_jedi_off
real(kind=kind_real), parameter mpas_jedi_million_kr
real(kind=kind_real), parameter mpas_jedi_one_kr
subroutine changevar(self, geom, xm, xg)
Fortran derived type to hold MPAS field.
Fortran derived type to hold geometry definition.