diff --git a/src/core_atmosphere/physics/mpas_atmphys_initialize_real.F b/src/core_atmosphere/physics/mpas_atmphys_initialize_real.F index d6dc1bc0c1..facd88dd0b 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_initialize_real.F +++ b/src/core_atmosphere/physics/mpas_atmphys_initialize_real.F @@ -52,6 +52,13 @@ module mpas_atmphys_initialize_real ! * In subroutine physics_init_seaice, removed the initialization of isice_lu since it is now defined in ! Registry.xml and initialized in subroutine init_atm_static. ! Laura D. Fowler (laura@ucar.edu) / 2017-01-12. +! * generalized soilcat (isltyp) to work with either soiltype_top or soiltype_bot data. +! Jerold A. Herwehe (herwehe.jerry@epa.gov) / 2018-09-10. +! * added fractional soil type (soiltypf) and land use (landusef) dependent on config_frac_landuse option; +! otherwise dominant land use and soil type will be used by default. +! Jerold A. Herwehe (herwehe.jerry@epa.gov) / 2020-02-10. +! * added initialization of lai_modis from MODIS climatological monthly mean LAI for use by PX LSM. +! Jerold A. Herwehe (herwehe.jerry@epa.gov) / 2020-02-20. ! * In subroutine physics_init_seaice, added the initialization of the annual maximum snow albedo over seaice ! points to 0.75. ! * Laura D. Fowler (laura@ucar.edu) / 2022-03-15). @@ -88,9 +95,9 @@ subroutine physics_initialize_real(mesh, fg, dminfo, dims, configs) real(kind=RKIND),dimension(:,:),pointer:: albedo12m real(kind=RKIND),dimension(:),pointer:: seaice,xice,xland - real(kind=RKIND),dimension(:),pointer:: vegfra,shdmin,shdmax + real(kind=RKIND),dimension(:),pointer:: vegfra,shdmin,shdmax,lai_modis real(kind=RKIND),dimension(:),pointer:: snow,snowc,snowh - real(kind=RKIND),dimension(:,:),pointer:: greenfrac + real(kind=RKIND),dimension(:,:),pointer:: greenfrac,lai12m real(kind=RKIND),dimension(:),pointer:: skintemp,sst @@ -116,9 +123,11 @@ subroutine physics_initialize_real(mesh, fg, dminfo, dims, configs) call mpas_pool_get_array(mesh, 'greenfrac', greenfrac) call mpas_pool_get_array(mesh, 'shdmin', shdmin) call mpas_pool_get_array(mesh, 'shdmax', shdmax) + call mpas_pool_get_array(mesh, 'lai12m', lai12m) call mpas_pool_get_array(fg, 'sfc_albbck', sfc_albbck) call mpas_pool_get_array(fg, 'vegfra', vegfra) + call mpas_pool_get_array(fg, 'lai_modis', lai_modis) call mpas_pool_get_array(fg, 'snow', snow) call mpas_pool_get_array(fg, 'snowc', snowc) call mpas_pool_get_array(fg, 'snowh', snowh) @@ -157,10 +166,12 @@ subroutine physics_initialize_real(mesh, fg, dminfo, dims, configs) if(landmask(iCell) .eq. 0) sfc_albbck(iCell) = 0.08_RKIND enddo -!initialization of the green-ness (vegetation) fraction: interpolation of the monthly values to -!the initial date. get the min/max for each cell for the monthly green-ness fraction: +!initialization of the green-ness (vegetation) fraction and leaf area index: +!interpolation of the monthly values to the initial date, and +!get the min/max for each cell for the monthly green-ness fraction: initial_date = trim(config_start_time) call monthly_interp_to_date(nCellsSolve,initial_date,greenfrac,vegfra) + call monthly_interp_to_date(nCellsSolve,initial_date,lai12m,lai_modis) !calculates the maximum and minimum green-ness (vegetation) fraction: call monthly_min_max(nCellsSolve,greenfrac,shdmin,shdmax) @@ -304,9 +315,6 @@ subroutine init_soil_layers_depth(mesh, fg, dims, configs) call mpas_pool_get_config(configs, 'config_nsoillevels', config_nsoillevels) - if(config_nsoillevels .ne. 4) & - call physics_error_fatal('NOAH lsm uses 4 soil layers. Correct config_nsoillevels.') - do iCell = 1, nCellsSolve iSoil = 1 zs_fg(iSoil,iCell) = 0.5_RKIND * dzs_fg(iSoil,iCell) @@ -317,21 +325,45 @@ subroutine init_soil_layers_depth(mesh, fg, dims, configs) enddo enddo - do iCell = 1, nCellsSolve - dzs(1,iCell) = 0.10_RKIND - dzs(2,iCell) = 0.30_RKIND - dzs(3,iCell) = 0.60_RKIND - dzs(4,iCell) = 1.00_RKIND + if(config_nsoillevels .eq. 4) then ! for Noah lsm + + do iCell = 1, nCellsSolve + dzs(1,iCell) = 0.10_RKIND + dzs(2,iCell) = 0.30_RKIND + dzs(3,iCell) = 0.60_RKIND + dzs(4,iCell) = 1.00_RKIND + + iSoil = 1 + zs(iSoil,iCell) = 0.5_RKIND * dzs(iSoil,iCell) + do iSoil = 2, nSoilLevels + zs(iSoil,iCell) = zs(iSoil-1,iCell) & + + 0.5_RKIND * dzs(iSoil-1,iCell) & + + 0.5_RKIND * dzs(iSoil,iCell) + enddo - iSoil = 1 - zs(iSoil,iCell) = 0.5_RKIND * dzs(iSoil,iCell) - do iSoil = 2, nSoilLevels - zs(iSoil,iCell) = zs(iSoil-1,iCell) & - + 0.5_RKIND * dzs(iSoil-1,iCell) & - + 0.5_RKIND * dzs(iSoil,iCell) enddo - enddo + elseif(config_nsoillevels .eq. 2) then ! for PX lsm + + do iCell = 1, nCellsSolve + dzs(1,iCell) = 0.01_RKIND + dzs(2,iCell) = 0.99_RKIND + + iSoil = 1 + zs(iSoil,iCell) = 0.5_RKIND * dzs(iSoil,iCell) + do iSoil = 2, nSoilLevels + zs(iSoil,iCell) = zs(iSoil-1,iCell) & + + 0.5_RKIND * dzs(iSoil-1,iCell) & + + 0.5_RKIND * dzs(iSoil,iCell) + enddo + + enddo + + else + + call physics_error_fatal('NOAH lsm uses 4 and PX lsm uses 2 soil layers. Correct config_nsoillevels.') + + endif end subroutine init_soil_layers_depth @@ -409,8 +441,8 @@ subroutine init_soil_layers_properties(mesh, fg, dminfo, dims, configs) call mpas_log_write('Error in interpolation of sm_fg to MPAS grid: num_sm = $i', messageType=MPAS_LOG_CRIT, intArgs=(/num_sm/)) endif - if(config_nsoillevels .ne. 4) & - call physics_error_fatal('NOAH lsm uses 4 soil layers. Correct config_nsoillevels.') + if(config_nsoillevels .ne. 4 .and. config_nsoillevels .ne. 2) & + call physics_error_fatal('NOAH lsm uses 4 and PX lsm uses 2 soil layers. Correct config_nsoillevels.') if(.not.allocated(zhave) ) allocate(zhave(nFGSoilLevels+2,nCellsSolve) ) if(.not.allocated(st_input)) allocate(st_input(nFGSoilLevels+2,nCellsSolve)) @@ -441,14 +473,14 @@ subroutine init_soil_layers_properties(mesh, fg, dminfo, dims, configs) enddo -!... interpolate the soil temperature, soil moisture, and soil liquid temperature to the four -! layers used in the NOAH land surface scheme: +!... interpolate the soil temperature, soil moisture, and soil liquid temperature to either +! the four layers used in the NOAH land surface scheme or the two layers used in the PX LSM: do iCell = 1, nCellsSolve if(landmask(iCell) .eq. 1) then - noah: do iSoil = 1 , nSoilLevels + do iSoil = 1 , nSoilLevels input: do ifgSoil = 1 , nFGSoilLevels+2-1 if(iCell .eq. 1) call mpas_log_write('$i $i $r $r $r', & intArgs=(/iSoil,ifgSoil/), & @@ -477,7 +509,7 @@ subroutine init_soil_layers_properties(mesh, fg, dminfo, dims, configs) endif enddo input if(iCell.eq. 1) call mpas_log_write('') - enddo noah + enddo elseif(landmask(iCell) .eq. 0) then @@ -604,14 +636,16 @@ subroutine physics_init_seaice(mesh, input, dims, configs) real(kind=RKIND):: xice_threshold real(kind=RKIND):: mid_point_depth real(kind=RKIND),pointer:: tsk_seaice_threshold - real(kind=RKIND),dimension(:),pointer :: vegfra + real(kind=RKIND),dimension(:),pointer :: vegfra,lai_modis real(kind=RKIND),dimension(:),pointer :: seaice,snoalb,xice real(kind=RKIND),dimension(:),pointer :: skintemp,tmn,xland real(kind=RKIND),dimension(:,:),pointer:: tslb,smois,sh2o,smcrel + real(kind=RKIND),dimension(:,:),pointer:: landusef,soiltypf - logical, pointer :: config_frac_seaice + logical, pointer :: config_frac_seaice,config_frac_landuse character(len=StrKIND),pointer:: config_landuse_data - integer,pointer:: isice_lu + integer,pointer:: isice_lu,ismax_lu + integer:: i !note that this threshold is also defined in module_physics_vars.F.It is defined here to avoid !adding "use module_physics_vars" since this subroutine is only used for the initialization of @@ -625,19 +659,24 @@ subroutine physics_init_seaice(mesh, input, dims, configs) call mpas_pool_get_config(configs, 'config_frac_seaice' , config_frac_seaice) call mpas_pool_get_config(configs, 'config_tsk_seaice_threshold', tsk_seaice_threshold) call mpas_pool_get_config(configs, 'config_landuse_data' , config_landuse_data) + call mpas_pool_get_config(configs, 'config_frac_landuse' , config_frac_landuse) call mpas_pool_get_dimension(dims, 'nCellsSolve', nCellsSolve) call mpas_pool_get_dimension(dims, 'nSoilLevels', nSoilLevels) call mpas_pool_get_array(mesh, 'isice_lu', isice_lu) + call mpas_pool_get_array(mesh, 'ismax_lu', ismax_lu) call mpas_pool_get_array(mesh, 'landmask', landmask) call mpas_pool_get_array(mesh, 'lu_index', ivgtyp) - call mpas_pool_get_array(mesh, 'soilcat_top', isltyp) call mpas_pool_get_array(mesh, 'snoalb', snoalb) + call mpas_pool_get_array(mesh, 'landusef', landusef) + call mpas_pool_get_array(mesh, 'soilcat', isltyp) + call mpas_pool_get_array(mesh, 'soiltypf', soiltypf) call mpas_pool_get_array(input, 'seaice', seaice) call mpas_pool_get_array(input, 'xice', xice) call mpas_pool_get_array(input, 'vegfra', vegfra) + call mpas_pool_get_array(input, 'lai_modis', lai_modis) call mpas_pool_get_array(input, 'skintemp', skintemp) call mpas_pool_get_array(input, 'tmn', tmn) @@ -649,6 +688,7 @@ subroutine physics_init_seaice(mesh, input, dims, configs) call mpas_pool_get_array(input, 'smcrel', smcrel) call mpas_log_write('--- isice_lu = $i', intArgs=(/isice_lu/)) + call mpas_log_write('--- ismax_lu = $i', intArgs=(/ismax_lu/)) !assign the threshold value for xice as a function of config_frac_seaice: if(.not. config_frac_seaice) then @@ -678,10 +718,27 @@ subroutine physics_init_seaice(mesh, input, dims, configs) !... sea-ice points are converted to land points: if(landmask(iCell) .eq. 0) tmn(iCell) = 271.4_RKIND ivgtyp(iCell) = isice_lu +! if(.not. config_frac_landuse) then + landusef(isice_lu,iCell) = 1.0 ! defines dominant land use only + do i = 1,isice_lu-1 + landusef(i,iCell) = 0.0 + end do + do i = isice_lu+1,ismax_lu + landusef(i,iCell) = 0.0 + end do +! else + ! Placeholder for land conversion with fractional land use +! endif isltyp(iCell) = 16 snoalb(iCell) = 0.75 vegfra(iCell) = 0._RKIND xland(iCell) = 1._RKIND + lai_modis(iCell) = 0._RKIND + + soiltypf(16,iCell) = 1.0 ! Currently defines dominant soil type fraction only + do i = 1,15 + soiltypf(i,iCell) = 0.0 + end do !... recalculate the soil temperature and soil moisture: do iSoil = 1, nSoilLevels diff --git a/src/core_init_atmosphere/Registry.xml b/src/core_init_atmosphere/Registry.xml index b107d16953..08d82b0ae1 100644 --- a/src/core_init_atmosphere/Registry.xml +++ b/src/core_init_atmosphere/Registry.xml @@ -36,6 +36,10 @@ description="The number of first-guess atmospheric layers"/> + + + + + possible_values="`USGS' or `MODIFIED_IGBP_MODIS_NOAH' or `NLCD40'"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -529,13 +637,17 @@ + + + + @@ -595,6 +707,7 @@ + @@ -796,11 +909,17 @@ + + - + + + @@ -808,16 +927,19 @@ - + - - - + + + + - @@ -1226,6 +1351,10 @@ description="background surface albedo" packages="met_stage_out"/> + + diff --git a/src/core_init_atmosphere/mpas_init_atm_cases.F b/src/core_init_atmosphere/mpas_init_atm_cases.F index 7d43b5ee83..f60ff5c729 100644 --- a/src/core_init_atmosphere/mpas_init_atm_cases.F +++ b/src/core_init_atmosphere/mpas_init_atm_cases.F @@ -2668,7 +2668,7 @@ subroutine init_atm_case_gfs(block, mesh, nCells, nEdges, nVertLevels, fg, state real (kind=RKIND), dimension(:,:,:), pointer :: deriv_two real (kind=RKIND) :: target_z - integer :: iCell, iCell1, iCell2 , iEdge, i, k, nz, cell1, cell2 + integer :: iCell, iCell1, iCell2 , iEdge, i, k, nz, itr, itrp, cell1, cell2 integer, pointer :: nCellsSolve, nz1 integer :: nInterpPoints, ndims @@ -2715,7 +2715,7 @@ subroutine init_atm_case_gfs(block, mesh, nCells, nEdges, nVertLevels, fg, state real (kind=RKIND) :: p0 - real (kind=RKIND) :: etavs, ztemp, zd, zt, dz, str + real (kind=RKIND) :: etavs, ztemp, zd, zt, dz, str, grd, kfrac real (kind=RKIND) :: es, rs, xnutr, znut, rcv @@ -2744,6 +2744,24 @@ subroutine init_atm_case_gfs(block, mesh, nCells, nEdges, nVertLevels, fg, state real (kind=RKIND), pointer :: config_ztop logical, pointer :: config_tc_vertical_grid character (len=StrKIND), pointer :: config_specified_zeta_levels + logical, pointer :: config_spline_vertical_grid + real (kind=RKIND), pointer :: config_spline_x1 + real (kind=RKIND), pointer :: config_spline_y1 + real (kind=RKIND), pointer :: config_spline_x2 + real (kind=RKIND), pointer :: config_spline_y2 + real (kind=RKIND), pointer :: config_spline_x3 + real (kind=RKIND), pointer :: config_spline_y3 + real (kind=RKIND), pointer :: config_spline_x4 + real (kind=RKIND), pointer :: config_spline_y4 + real (kind=RKIND), pointer :: config_spline_x5 + real (kind=RKIND), pointer :: config_spline_y5 + real (kind=RKIND), pointer :: config_spline_x6 + real (kind=RKIND), pointer :: config_spline_y6 + real (kind=RKIND), pointer :: config_spline_x7 + real (kind=RKIND), pointer :: config_spline_y7 + real (kind=RKIND), pointer :: config_spline_x8 + real (kind=RKIND), pointer :: config_spline_y8 + logical, pointer :: config_EPA_vertical_grid logical, pointer :: config_use_spechumd integer, pointer :: config_nfglevels integer, pointer :: config_nfgsoillevels @@ -2807,6 +2825,31 @@ subroutine init_atm_case_gfs(block, mesh, nCells, nEdges, nVertLevels, fg, state real (kind=RKIND), dimension(:), pointer :: rh2 real (kind=RKIND), dimension(:), pointer :: t2m + ! For cubic spline definition of air quality layer thickness profile + integer, parameter :: nprof = 8 + real (kind=RKIND), dimension(nprof) :: & + xx, &!< x-dimension control point for cubic spline interpolation + yy !< y-dimension control point for cubic spline interpolation + real (kind=RKIND), parameter :: & + cadj = 0.3, &!< layer thickness adjustment coefficient + ctol = 0.001 !< tolerance for layer fit to model top + real (kind=RKIND), dimension(nprof) :: & + ya, &!< Thickness scaled to match model top + y2ndDer, &!< 2nd derivative at nodes of layer profile definition + acoef !< Tridiagonal decomposition factor + real (kind=RKIND) :: & + thick, &!< Interpolated layer thickness + zint, &!< Height for interpolation + ptef, &!< Profile top error factor + adj, &!< Thickness adjustment factor + xRatio, &!< X-dimension scale for interpolation + tvar, &!< temporary variable + aa, &!< temporary variable + bb, &!< temporary variable + cc !< temporary variable + real (kind=RKIND), dimension(nVertLevels+1) :: zwtemp + integer :: nlayerint, klo, khi, ncount + character (len=StrKIND) :: errstring character (len=StrKIND), pointer :: config_interface_projection @@ -2820,6 +2863,24 @@ subroutine init_atm_case_gfs(block, mesh, nCells, nEdges, nVertLevels, fg, state call mpas_pool_get_config(configs, 'config_ztop', config_ztop) call mpas_pool_get_config(configs, 'config_tc_vertical_grid', config_tc_vertical_grid) call mpas_pool_get_config(configs, 'config_specified_zeta_levels', config_specified_zeta_levels) + call mpas_pool_get_config(configs, 'config_EPA_vertical_grid', config_EPA_vertical_grid) + call mpas_pool_get_config(configs, 'config_spline_vertical_grid', config_spline_vertical_grid) + call mpas_pool_get_config(configs, 'config_spline_x1', config_spline_x1) + call mpas_pool_get_config(configs, 'config_spline_y1', config_spline_y1) + call mpas_pool_get_config(configs, 'config_spline_x2', config_spline_x2) + call mpas_pool_get_config(configs, 'config_spline_y2', config_spline_y2) + call mpas_pool_get_config(configs, 'config_spline_x3', config_spline_x3) + call mpas_pool_get_config(configs, 'config_spline_y3', config_spline_y3) + call mpas_pool_get_config(configs, 'config_spline_x4', config_spline_x4) + call mpas_pool_get_config(configs, 'config_spline_y4', config_spline_y4) + call mpas_pool_get_config(configs, 'config_spline_x5', config_spline_x5) + call mpas_pool_get_config(configs, 'config_spline_y5', config_spline_y5) + call mpas_pool_get_config(configs, 'config_spline_x6', config_spline_x6) + call mpas_pool_get_config(configs, 'config_spline_y6', config_spline_y6) + call mpas_pool_get_config(configs, 'config_spline_x7', config_spline_x7) + call mpas_pool_get_config(configs, 'config_spline_y7', config_spline_y7) + call mpas_pool_get_config(configs, 'config_spline_x8', config_spline_x8) + call mpas_pool_get_config(configs, 'config_spline_y8', config_spline_y8) call mpas_pool_get_config(configs, 'config_use_spechumd', config_use_spechumd) call mpas_pool_get_config(configs, 'config_nfglevels', config_nfglevels) call mpas_pool_get_config(configs, 'config_nfgsoillevels', config_nfgsoillevels) @@ -3113,6 +3174,127 @@ subroutine init_atm_case_gfs(block, mesh, nCells, nEdges, nVertLevels, fg, state if (k > 1) dzw(k-1) = zw(k)-zw(k-1) end do + else if (config_spline_vertical_grid) then + + call mpas_log_write('Setting up vertical levels using a cubic spline fit') + + zt = config_ztop + xx(1) = config_spline_x1 + yy(1) = config_spline_y1 + xx(2) = config_spline_x2 + yy(2) = config_spline_y2 + xx(3) = config_spline_x3 + yy(3) = config_spline_y3 + xx(4) = config_spline_x4 + yy(4) = config_spline_y4 + xx(5) = config_spline_x5 + yy(5) = config_spline_y5 + xx(6) = config_spline_x6 + yy(6) = config_spline_y6 + xx(7) = config_spline_x7 + yy(7) = config_spline_y7 + xx(8) = config_spline_x8 + yy(8) = config_spline_y8 + +! Set up cubic spline definition of layer thickness vertical profile + y2ndDer(1)=0.0 + y2ndDer(nprof)=0.0 + acoef(1)=0.0 + ya(1:nprof) = yy(1:nprof) + + do i=2,nprof-1 + xRatio=(xx(i)-xx(i-1))/(xx(i+1)-xx(i-1)) + tvar=1.0/(2.0+xRatio*y2ndDer(i-1)) + y2ndDer(i)=tvar*(xRatio-1.0) + acoef(i) = tvar*(6.0*((ya(i+1)-ya(i))/(xx(i+1)-xx(i)) & + -(ya(i)-ya(i-1))/(xx(i)-xx(i-1)))/(xx(i+1)-xx(i-1)) & + -xRatio*acoef(i-1)) + enddo + + do i=nprof-1,1,-1 + y2ndDer(i)=y2ndDer(i)*y2ndDer(i+1)+acoef(i) + enddo + +! Loop to set layer interface heights from air quality profile + ptef = 0. + zwtemp(1)= 0. + ncount = 0 + do while(abs(1.0-ptef) > ctol) + zwtemp(2) = ya(1) + zint = ya(1) + nlayerint = 2 + do while (nlayerint.lt.nz) + klo=1 + khi=nprof + do while (khi-klo.gt.1) + k=(khi+klo)/2 + if(xx(k).gt.zint)then + khi=k + else + klo=k + endif + enddo + cc=xx(khi)-xx(klo) + aa=(xx(khi)-zint)/cc + bb=(zint-xx(klo))/cc + thick=aa*ya(klo)+bb*ya(khi)+((aa**3-aa)*y2ndDer(klo)+ & + (bb**3-bb)*y2ndDer(khi))*(cc**2)/6. + zint = zint+thick + nlayerint = nlayerint+1 + zwtemp(nlayerint) = zint + enddo + if(ptef.eq.0.) then + ptef = zt/zint + if(ptef > 1.5) then + call mpas_log_write('*** Significant layer inflation required ***') + call mpas_log_write('*** You may want to use more layers ***') + call mpas_log_write('*** and/or a lower model top ***') + else if(ptef < 0.6) then + call mpas_log_write('*** Significant layer deflation required ***') + call mpas_log_write('*** You may want to use fewer layers ***') + call mpas_log_write('*** and/or a higher model top ***') + endif + else + ptef = zt/zint + endif + call mpas_log_write('Using first layer, ptef = $r $r',realArgs=(/ya(1),ptef/)) + ncount = ncount+1 + if(ncount > 20) then + call mpas_log_write('*** ERROR: layer adjustment failure ***') + call mpas_log_write(' *** Iteration did not converge ***') + call mpas_dmpar_global_abort('****************************************************************************', deferredAbort=.true.) + call mpas_dmpar_global_abort('ERROR: Aborted while setting up vertical levels for Air Quality applications', deferredAbort=.true.) + call mpas_dmpar_global_abort('****************************************************************************') + endif + adj = 1.0+(cadj*(ptef-1.0)) + ya(1:nprof) = ya(1:nprof)*adj + y2ndDer(1:nprof) = y2ndDer(1:nprof)*adj + enddo + +! Now make minor adjustment to layers to fit within specified top + do k=1,nz + zw(k) = zwtemp(k)*ptef + enddo + call mpas_log_write('Layers for Air Quality applications generated') + call mpas_log_write('Number of layers and Model top = $i $r',intArgs=(/nz1/),realArgs=(/zt/)) + call mpas_log_write('Adjusted bottom layer thickness (m) = $r',realArgs=(/zw(2)/)) + call mpas_log_write('') + + else if (config_EPA_vertical_grid) then + + call mpas_log_write('Setting up vertical levels as in MPAS 4.0 and earlier, but') + call mpas_log_write('*** Using custom layer definition ***') + + str = 3. + grd = 0.03 + zt = config_ztop + + do k=1,nz + kfrac = real(k-1)/real(nz1) + zw(k) = zt*((1-grd)*kfrac**str+grd*kfrac) + if (k > 1) dzw(k-1) = zw(k)-zw(k-1) + end do + ! ! Otherwise, use the vertical level configuration from MPAS v2.0 ! @@ -6620,32 +6802,39 @@ subroutine physics_idealized_init(mesh, fg) type (mpas_pool_type), intent(inout) :: fg !local variables: - integer :: iCell, iMonth, iSoil - integer, pointer :: nCells, nSoilLevels, nMonths - integer, dimension(:), pointer :: landmask, lu_index, soilcat_top - real (kind=RKIND), dimension(:), pointer :: ter, xice, shdmin, shdmax, vegfra, sfc_albbck, xland, seaice + integer :: iCell, iMonth, iSoil, iLandCat, iSoilCat + integer, pointer :: nCells, nSoilLevels, nMonths, nLandCat, nSoilCat + integer, dimension(:), pointer :: landmask, lu_index, soilcat + real (kind=RKIND), dimension(:), pointer :: ter, xice, shdmin, shdmax, vegfra, sfc_albbck, xland, seaice, lai_modis real (kind=RKIND), dimension(:), pointer :: snow, snowc, snoalb, snowh, skintemp, sst, tmn - real (kind=RKIND), dimension(:,:), pointer :: tslb, smcrel, sh2o, smois, dzs, albedo12m, greenfrac + real (kind=RKIND), dimension(:,:), pointer :: landusef, soiltypf + real (kind=RKIND), dimension(:,:), pointer :: tslb, smcrel, sh2o, smois, dzs, albedo12m, greenfrac, lai12m !--------------------------------------------------------------------------------------------- call mpas_pool_get_dimension(mesh, 'nCells', nCells) call mpas_pool_get_dimension(mesh, 'nSoilLevels', nSoilLevels) call mpas_pool_get_dimension(mesh, 'nMonths', nMonths) + call mpas_pool_get_dimension(mesh, 'nLandCat', nLandCat) + call mpas_pool_get_dimension(mesh, 'nSoilCat', nSoilCat) call mpas_pool_get_array(mesh, 'ter', ter) call mpas_pool_get_array(mesh, 'landmask', landmask) call mpas_pool_get_array(mesh, 'lu_index', lu_index) - call mpas_pool_get_array(mesh, 'soilcat_top', soilcat_top) + call mpas_pool_get_array(mesh, 'landusef', landusef) + call mpas_pool_get_array(mesh, 'soilcat', soilcat) + call mpas_pool_get_array(mesh, 'soiltypf', soiltypf) call mpas_pool_get_array(mesh, 'shdmin', shdmin) call mpas_pool_get_array(mesh, 'shdmax', shdmax) call mpas_pool_get_array(mesh, 'snoalb', snoalb) call mpas_pool_get_array(mesh, 'albedo12m', albedo12m) call mpas_pool_get_array(mesh, 'greenfrac', greenfrac) + call mpas_pool_get_array(mesh, 'lai12m', lai12m) call mpas_pool_get_array(fg, 'xice', xice) call mpas_pool_get_array(fg, 'vegfra', vegfra) call mpas_pool_get_array(fg, 'sfc_albbck', sfc_albbck) + call mpas_pool_get_array(fg, 'lai_modis', lai_modis) call mpas_pool_get_array(fg, 'xland', xland) call mpas_pool_get_array(fg, 'seaice', seaice) call mpas_pool_get_array(fg, 'snow', snow) @@ -6671,11 +6860,12 @@ subroutine physics_idealized_init(mesh, fg) xice(iCell) = 0.0 landmask(iCell) = 0 lu_index(iCell) = 0 - soilcat_top(iCell) = 0 + soilcat(iCell) = 0 shdmin(iCell) = 0.0 shdmax(iCell) = 0.0 vegfra(iCell) = 0.0 sfc_albbck(iCell) = 0.0 + lai_modis(iCell) = 0.0 xland(iCell) = 0.0 seaice(iCell) = 0.0 @@ -6698,11 +6888,22 @@ subroutine physics_idealized_init(mesh, fg) smois(iSoil,iCell) = 0.0 dzs(iSoil,iCell) = 0.0 end do + + !fractional soil type: + do iSoilCat = 1, nSoilCat + soiltypf(iSoilCat,iCell) = 0.0 + end do + + !fractional land use: + do iLandCat = 1, nLandCat + landusef(iLandCat,iCell) = 0.0 + end do - !monthly climatological surface albedo and greeness fraction: + !monthly climatological surface albedo, greeness fraction, and leaf area index: do iMonth = 1, nMonths albedo12m(iMonth,iCell) = 0.08 greenfrac(iMonth,iCell) = 0.0 + lai12m(iMonth,iCell) = 0.0 end do end do diff --git a/src/core_init_atmosphere/mpas_init_atm_static.F b/src/core_init_atmosphere/mpas_init_atm_static.F index b9d289d8b6..f7a8e13d01 100644 --- a/src/core_init_atmosphere/mpas_init_atm_static.F +++ b/src/core_init_atmosphere/mpas_init_atm_static.F @@ -88,11 +88,13 @@ end subroutine interp_accumulation_function ! 12.1.2.2 of the Fortran standard) and currently, only PGI does not support this, so ! use module level variables for now... ! + real (kind=RKIND), dimension(:,:), pointer :: landusef + real (kind=RKIND), dimension(:,:), pointer :: soiltypf integer (kind=I8KIND), dimension(:), pointer :: ter_integer integer (kind=I8KIND), dimension(:,:), pointer :: soilcomp_int real (kind=RKIND) :: soilcomp_msgval = 255.0_RKIND ! Modified later based on index file for soilcomp integer, dimension(:), pointer :: lu_index - integer, dimension(:), pointer :: soilcat_top + integer, dimension(:), pointer :: soilcat integer, dimension(:), pointer :: nhs integer, dimension(:,:), allocatable:: ncat ! Landmask is used by the accumulation function for maxsnoalb and soilcomp, @@ -153,6 +155,7 @@ subroutine init_atm_static(mesh, dims, configs) integer, pointer :: isice_lu, iswater_lu integer :: iswater_soil + integer, pointer :: ismax_lu integer, pointer :: nCells, nCellsSolve, nEdges, nVertices, maxEdges logical, pointer :: on_a_sphere real (kind=RKIND), pointer :: sphere_radius @@ -177,12 +180,16 @@ subroutine init_atm_static(mesh, dims, configs) integer (kind=I8KIND), dimension(:), pointer :: snoalb_integer real (kind=RKIND), dimension(:), pointer :: shdmin, shdmax real (kind=RKIND), dimension(:,:), pointer :: greenfrac + real (kind=RKIND), dimension(:,:), pointer :: lai12m + integer (kind=I8KIND), dimension(:,:), pointer :: lai12m_int real (kind=RKIND), dimension(:,:), pointer :: albedo12m integer (kind=I8KIND), dimension(:,:), pointer :: albedo12m_int + real (kind=RKIND), dimension(:,:), pointer :: landusef + real (kind=RKIND), dimension(:,:), pointer :: soiltypf real (kind=RKIND) :: fillval real (kind=RKIND), pointer :: missing_value integer, dimension(:), pointer :: lu_index - integer, dimension(:), pointer :: soilcat_top + integer, dimension(:), pointer :: soilcat integer, dimension(:), pointer :: landmask integer, dimension(:), pointer :: bdyMaskCell character(len=StrKIND), pointer :: mminlu @@ -262,13 +269,17 @@ subroutine init_atm_static(mesh, dims, configs) call mpas_pool_get_array(mesh, 'verticesOnCell', verticesOnCell) call mpas_pool_get_array(mesh, 'lu_index', lu_index) + call mpas_pool_get_array(mesh, 'landusef', landusef) call mpas_pool_get_array(mesh, 'mminlu', mminlu) call mpas_pool_get_array(mesh, 'isice_lu', isice_lu) call mpas_pool_get_array(mesh, 'iswater_lu', iswater_lu) - call mpas_pool_get_array(mesh, 'soilcat_top', soilcat_top) + call mpas_pool_get_array(mesh, 'ismax_lu', ismax_lu) + call mpas_pool_get_array(mesh, 'soilcat', soilcat) + call mpas_pool_get_array(mesh, 'soiltypf', soiltypf) call mpas_pool_get_array(mesh, 'landmask', landmask) call mpas_pool_get_array(mesh, 'snoalb', snoalb) call mpas_pool_get_array(mesh, 'greenfrac', greenfrac) + call mpas_pool_get_array(mesh, 'lai12m', lai12m) call mpas_pool_get_array(mesh, 'albedo12m', albedo12m) call mpas_pool_get_array(mesh, 'shdmin', shdmin) call mpas_pool_get_array(mesh, 'shdmax', shdmax) @@ -352,11 +363,14 @@ subroutine init_atm_static(mesh, dims, configs) write(mminlu,'(a)') 'USGS' case('MODIFIED_IGBP_MODIS_NOAH') write(mminlu,'(a)') 'MODIFIED_IGBP_MODIS_NOAH' + case('NLCD40') + write(mminlu,'(a)') 'NLCD40' case default call mpas_log_write('*****************************************************************', messageType=MPAS_LOG_ERR) call mpas_log_write('Invalid land use dataset '''//trim(config_landuse_data) & //''' selected for config_landuse_data', messageType=MPAS_LOG_ERR) - call mpas_log_write(' Possible options are: ''USGS'', ''MODIFIED_IGBP_MODIS_NOAH''', messageType=MPAS_LOG_ERR) + call mpas_log_write(' Possible options are: ''USGS'', ''MODIFIED_IGBP_MODIS_NOAH'', ', messageType=MPAS_LOG_ERR) + call mpas_log_write(' or ''NLCD40''' , messageType=MPAS_LOG_ERR) call mpas_log_write('*****************************************************************', messageType=MPAS_LOG_ERR) call mpas_log_write('Please correct the namelist.', messageType=MPAS_LOG_CRIT) end select surface_input_select0 @@ -396,48 +410,80 @@ subroutine init_atm_static(mesh, dims, configs) case('MODIFIED_IGBP_MODIS_NOAH') call mpas_log_write('Using 20-class MODIS 30-arc-second land cover dataset') geog_sub_path = 'modis_landuse_20class_30s/' + case('NLCD40') + call mpas_log_write('Using 20-class MODIS 30-arc-second land cover dataset for most of globe') + geog_sub_path = 'modis_landuse_20class_30s/' case default call mpas_log_write('*****************************************************************', messageType=MPAS_LOG_ERR) call mpas_log_write('Invalid land use dataset '''//trim(config_landuse_data) & //''' selected for config_landuse_data', messageType=MPAS_LOG_ERR) - call mpas_log_write(' Possible options are: ''USGS'', ''MODIFIED_IGBP_MODIS_NOAH''', messageType=MPAS_LOG_ERR) + call mpas_log_write(' Possible options are: ''USGS'', ''MODIFIED_IGBP_MODIS_NOAH'', ', messageType=MPAS_LOG_ERR) + call mpas_log_write(' or ''NLCD40''' , messageType=MPAS_LOG_ERR) call mpas_log_write('*****************************************************************', messageType=MPAS_LOG_ERR) call mpas_log_write('Please correct the namelist.', messageType=MPAS_LOG_CRIT) end select surface_input_select1 call mpas_log_write('--- start interpolate LU_INDEX') - call interp_landuse(mesh, tree, trim(geog_data_path)//trim(geog_sub_path), isice_lu, iswater_lu, & + call interp_landuse(configs, mesh, tree, trim(geog_data_path)//trim(geog_sub_path), isice_lu, iswater_lu, ismax_lu, & supersample_fac=supersample_fac_30s) call mpas_log_write('--- end interpolate LU_INDEX') + if (config_landuse_data == 'NLCD40') then + ! Incorporate NLCD fractional percentage land use for CONUS, with MODIS for rest of world + call mpas_log_write('Overlaying 40-class NLCD 9-arc-second land cover dataset for CONUS') + geog_sub_path = 'nlcd2011_ll_9s/' + call interp_landuse2(mesh, dims, configs, trim(config_geog_data_path)//trim(geog_sub_path), isice_lu, iswater_lu, ismax_lu) + end if + ! -! Interpolate SOILCAT_TOP +! Interpolate SOILCAT ! - geog_sub_path = 'soiltype_top_30s/' + surface_input_select2: select case(trim(config_landuse_data)) + case('USGS') + geog_sub_path = 'soiltype_top_30s/' + case('MODIFIED_IGBP_MODIS_NOAH') + geog_sub_path = 'soiltype_top_30s/' + case('NLCD40') + geog_sub_path = 'soiltype_bot_30s/' + case default + end select surface_input_select2 - call mpas_log_write('--- start interpolate SOILCAT_TOP') - call interp_soilcat(mesh, tree, trim(geog_data_path)//trim(geog_sub_path), iswater_soil, & + call mpas_log_write('--- start interpolate SOILCAT') + call interp_soilcat(configs, mesh, tree, trim(config_geog_data_path)//trim(geog_sub_path), iswater_soil, & supersample_fac=supersample_fac_30s) - call mpas_log_write('--- end interpolate SOILCAT_TOP') + call mpas_log_write('--- end interpolate SOILCAT') !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! KLUDGE TO FIX SOIL TYPE OVER ANTARCTICA !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - where (lu_index == isice_lu) soilcat_top = 16 +! where (lu_index == isice_lu) soilcat = 16 + do iCell = 1,nCells + if (lu_index(iCell) == isice_lu) then + do i = 1,16 + soiltypf(i,iCell) = 0.0 + end do + soilcat(iCell) = 16 + soiltypf(soilcat(iCell),iCell) = 1.0 + end if + end do !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! CORRECT INCONSISTENT SOIL AND LAND USE DATA !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! do iCell = 1,nCells if (lu_index(iCell) == iswater_lu .or. & - soilcat_top(iCell) == iswater_soil) then + soilcat(iCell) == iswater_soil) then if (lu_index(iCell) /= iswater_lu) then call mpas_log_write('Turning lu_index into water at $i', intArgs=(/iCell/)) + landusef(lu_index(iCell),iCell) = 0.0 lu_index(iCell) = iswater_lu + landusef(lu_index(iCell),iCell) = 1.0 ! Currently defines dominant land use fraction only + soiltypf(soilcat(iCell),iCell) = 1.0 ! Currently defines dominant soil type fraction only end if - if (soilcat_top(iCell) /= iswater_soil) then - call mpas_log_write('Turning soilcat_top into water at $i', intArgs=(/iCell/)) - soilcat_top(iCell) = iswater_soil + if (soilcat(iCell) /= iswater_soil) then + call mpas_log_write('Turning soilcat into water at $i', intArgs=(/iCell/)) + soilcat(iCell) = iswater_soil + soiltypf(soilcat(iCell),iCell) = 1.0 ! Currently defines dominant soil type fraction only end if end if end do @@ -943,6 +989,150 @@ subroutine init_atm_static(mesh, dims, configs) call mpas_log_write('--- end interpolate GREENFRAC') +! +! Interpolate LAI +! + + call mpas_log_write('Using MODIS LAI 30-arc-second data for climatological monthly leaf area index') + + geog_sub_path = 'lai_modis_30s/' + + ierr = mgr % init(trim(config_geog_data_path)//trim(geog_sub_path)) + if (ierr /= 0) then + call mpas_log_write('Error occurred when initalizing the interpolation of monthly leaf area index (lai12m)', & + messageType=MPAS_LOG_CRIT) + endif + + call mpas_pool_get_config(mgr % pool, 'tile_bdr', tile_bdr) + call mpas_pool_get_config(mgr % pool, 'tile_x', tile_nx) + call mpas_pool_get_config(mgr % pool, 'tile_y', tile_ny) + call mpas_pool_get_config(mgr % pool, 'tile_z', tile_nz) + call mpas_pool_get_config(mgr % pool, 'missing_value', missing_value) + call mpas_pool_get_config(mgr % pool, 'scale_factor', scalefactor_ptr) + scalefactor = scalefactor_ptr + + allocate(nhs(nCells)) + allocate(lai12m_int(tile_nz, nCells)) + nhs(:) = 0 + lai12m(:,:) = 0.0 + lai12m_int(:,:) = 0_I8KIND + fillval = 0.0 + + do iCell = 1, nCells + if (nhs(iCell) == 0) then + tile => null() + ierr = mgr % get_tile(latCell(iCell), lonCell(iCell), tile) + if (ierr /= 0 .or. .not. associated(tile)) then + call mpas_log_write('Could not get tile that contained cell $i', intArgs=(/iCell/), messageType=MPAS_LOG_CRIT) + end if + + ierr = mgr % push_tile(tile) + if (ierr /= 0) then + call mpas_log_write("Error pushing this tile onto the stack: "//trim(tile % fname), messageType=MPAS_LOG_CRIT) + end if + end if + + do while (.not. mgr % is_stack_empty()) + tile => mgr % pop_tile() + + if (tile % is_processed) then + cycle + end if + + call mpas_log_write('Processing tile: '//trim(tile % fname)) + + all_pixels_mapped_to_halo_cells = .true. + + do j = tile_bdr + 1, tile_ny + tile_bdr, 1 + do i = tile_bdr + 1, tile_nx + tile_bdr, 1 + + call mgr % tile_to_latlon(tile, j, i, lat_pt, lon_pt) + call mpas_latlon_to_xyz(xPixel, yPixel, zPixel, sphere_radius, lat_pt, lon_pt) + call mpas_kd_search(tree, (/xPixel, yPixel, zPixel/), res, max_distance=max_kdtree_distance2) + + if (.not. associated(res)) cycle + + ! + ! This field only matters for land cells, and for all but the outermost boundary cells, + ! we can safely assume that the nearest model grid cell contains the pixel (else, a different + ! cell would be nearest) + ! + if (landMask(res % id) == 1 .and. bdyMaskCell(res % id) < nBdyLayers) then + do k = 1, tile_nz + if (tile % tile(i, j, k) == missing_value) then + i8val = int(fillval, kind=I8KIND) + else + i8val = int(tile % tile(i,j,k), kind=I8KIND) + end if + lai12m_int(k, res % id) = lai12m_int(k, res % id) + i8val + end do + nhs(res % id) = nhs(res % id) + 1 + + if (res % id <= nCellsSolve) then + all_pixels_mapped_to_halo_cells = .false. + end if + + ! For outermost land cells, additional work is needed to verify that the pixel + ! actually lies within the nearest cell + else if (landMask(res % id) == 1) then + if (mpas_in_cell(xPixel, yPixel, zPixel, xCell(res % id), yCell(res % id), zCell(res % id), & + nEdgesOnCell(res % id), verticesOnCell(:,res % id), xVertex, yVertex, zVertex)) then + do k = 1, tile_nz + if (tile % tile(i, j, k) == missing_value) then + i8val = int(fillval, kind=I8KIND) + else + i8val = int(tile % tile(i,j,k), kind=I8KIND) + end if + lai12m_int(k, res % id) = lai12m_int(k, res % id) + i8val + end do + nhs(res % id) = nhs(res % id) + 1 + + if (res % id <= nCellsSolve) then + all_pixels_mapped_to_halo_cells = .false. + end if + end if + end if + end do + end do + + tile % is_processed = .true. + + if (.not. all_pixels_mapped_to_halo_cells) then + ierr = mgr % push_neighbors(tile) + if (ierr /= 0) then + call mpas_log_write("Error pushing the tile neighbors of: "//trim(tile%fname), messageType=MPAS_LOG_CRIT) + end if + end if + + end do + end do + + do iCell = 1, nCells + ! For land points that have no overlap with valid data, and for water points, + ! just use the fill value... + if (nhs(iCell) == 0) then + lai12m(:,iCell) = fillval + else + lai12m(:,iCell) = real(real(lai12m_int(:,iCell), kind=R8KIND) / real(nhs(iCell), kind=R8KIND), kind=RKIND) + lai12m(:,iCell) = lai12m(:,iCell) * scalefactor + end if + if (lu_index(iCell) == isice_lu) then + lai12m(:,iCell) = fillval + end if + end do + + deallocate(nhs) + deallocate(lai12m_int) + + ierr = mgr % finalize() + if (ierr /= 0) then + call mpas_log_write('Error occurred when finalizing the interpolation of monthly leaf area index (lai12m)', & + messageType=MPAS_LOG_CRIT) + endif + + call mpas_log_write('--- end interpolate LAI') + + ! ! Interpolate ALBEDO12M ! @@ -1769,16 +1959,18 @@ end subroutine categorical_interp_accumulation !> that isice and iswater are in the dataset's index file. ! !----------------------------------------------------------------------- - subroutine interp_landuse(mesh, kdtree, geog_data_path, isice_lu, iswater_lu, supersample_fac) + subroutine interp_landuse(configs, mesh, kdtree, geog_data_path, isice_lu, iswater_lu, ismax_lu, supersample_fac) implicit none ! Input variables type (mpas_pool_type), intent(inout) :: mesh type (mpas_kd_type), pointer, intent(in) :: kdtree + type (mpas_pool_type), intent(in) :: configs character (len=*), intent(in) :: geog_data_path integer, intent(out) :: isice_lu integer, intent(out) :: iswater_lu + integer, intent(out) :: ismax_lu integer, intent(in), optional :: supersample_fac ! Local variables @@ -1787,9 +1979,12 @@ subroutine interp_landuse(mesh, kdtree, geog_data_path, isice_lu, iswater_lu, su integer, pointer :: isice_lu_ptr integer, pointer :: iswater_lu_ptr + logical, pointer :: config_frac_landuse + real (kind=RKIND), pointer :: scalefactor - integer :: iCell + integer :: ncattot + integer :: i, iCell integer :: ierr ierr = mgr % init(trim(geog_data_path)) @@ -1798,8 +1993,11 @@ subroutine interp_landuse(mesh, kdtree, geog_data_path, isice_lu, iswater_lu, su return ! Program execution should not reach this statement since the preceding message is a critical error end if + call mpas_pool_get_config(configs, 'config_frac_landuse', config_frac_landuse) + call mpas_pool_get_dimension(mesh, 'nCells', nCells) call mpas_pool_get_array(mesh, 'lu_index', lu_index) + call mpas_pool_get_array(mesh, 'landusef', landusef) call mpas_pool_get_config(mgr % pool, 'scale_factor', scalefactor) call mpas_pool_get_config(mgr % pool, 'category_min', category_min) call mpas_pool_get_config(mgr % pool, 'category_max', category_max) @@ -1808,19 +2006,39 @@ subroutine interp_landuse(mesh, kdtree, geog_data_path, isice_lu, iswater_lu, su isice_lu = isice_lu_ptr iswater_lu = iswater_lu_ptr + ismax_lu = category_max allocate(ncat(category_min:category_max, nCells)) ncat(:,:) = 0 + landusef(:,:) = 0.0 call init_atm_map_static_data(mesh, mgr, kdtree, categorical_interp_criteria, categorical_interp_accumulation, & supersample_fac=supersample_fac) - do iCell = 1, nCells - ! Because maxloc returns the location of the maximum value of an array as if the - ! starting index of the array is 1, and dataset categories do not necessarily start - ! at 1, we need to use category_min to ensure the correct category location is chosen. - lu_index(iCell) = maxloc(ncat(:,iCell), dim=1) - 1 + category_min - end do + if (.not. config_frac_landuse) then ! compute dominant land use + do iCell = 1, nCells + ! Because maxloc returns the location of the maximum value of an array as if the + ! starting index of the array is 1, and dataset categories do not necessarily start + ! at 1, we need to use category_min to ensure the correct category location is chosen. + lu_index(iCell) = maxloc(ncat(:,iCell), dim=1) - 1 + category_min + landusef(lu_index(iCell),iCell) = 1.0 ! defines dominant land use only + end do + else ! compute fractional land use + do iCell = 1, nCells + ! Note that lu_index currently defines dominant land use only for ivgtyp, where + ! ivgtyp is used by Noah LSM (and PX LSM when landusef=0 everywhere). + lu_index(iCell) = maxloc(ncat(:,iCell), dim=1) - 1 + category_min + ncattot = 0 + do i = 1,ismax_lu + ncattot = ncattot + ncat(i,iCell) + end do + do i = 1,ismax_lu + if (ncat(i,iCell) > 0) then + landusef(i,iCell) = real(ncat(i,iCell))/ncattot ! defines fractional land use for PX LSM + end if + end do + end do + end if deallocate(ncat) ierr = mgr % finalize() @@ -1834,6 +2052,172 @@ subroutine interp_landuse(mesh, kdtree, geog_data_path, isice_lu, iswater_lu, su end subroutine interp_landuse + !*********************************************************************** + ! + ! routine interp_landuse2 + ! + !> \brief Interpolate NLCD land use for CONUS + !> \details + !> Interpolate NLCD land use by using the nlcd2011_ll_9s/ dataset. Then + !> overlay the interpolated NLCD data onto CONUS, leaving the previously- + !> interpolated MODIS land use in place over the rest of the globe. + ! + !----------------------------------------------------------------------- + subroutine interp_landuse2(mesh, dims, configs, geog_data_path, isice_lu, iswater_lu, ismax_lu) + + implicit none + + ! Input variables + type (mpas_pool_type), intent(inout) :: mesh + type (mpas_pool_type), intent(in) :: dims + type (mpas_pool_type), intent(in) :: configs + character (len=*), intent(in) :: geog_data_path + integer, intent(inout) :: isice_lu + integer, intent(inout) :: iswater_lu + integer, intent(inout) :: ismax_lu + + ! Local variables + type (mpas_geotile_mgr_type) :: mgr + character (len=StrKIND) :: fname + character (kind=c_char), dimension(StrKIND+1) :: c_fname + real (kind=c_float), dimension(:,:,:), pointer, contiguous :: rarray + real (kind=RKIND) :: scalefactor + real (kind=RKIND) :: dx, dy + real (kind=RKIND) :: lat_pt, lon_pt + real (kind=RKIND) :: start_lat, start_lon + real (kind=RKIND), dimension(:), pointer :: latCell, lonCell + integer, pointer :: nCells, maxEdges + integer, dimension(:), pointer :: nEdgesOnCell + integer, dimension(:,:), pointer :: cellsOnCell + integer (c_int) :: endian, isigned, istatus, wordsize + integer (c_int) :: nx, ny, nz + integer:: i,j,k + integer:: iCell,iPoint,iTileStart,iTileEnd,jTileStart,jTileEnd,iEnd,jEnd + type (c_ptr) :: rarray_ptr + + logical, pointer :: config_frac_landuse + + integer :: ncattot + integer :: ierr + + ierr = mgr % init(trim(geog_data_path)) + if (ierr /= 0) then + call mpas_log_write("Error occured initalizing interpolation for "//trim(geog_data_path), messageType=MPAS_LOG_CRIT) + return ! Program execution should not reach this statement since the preceding message is a critical error + end if + + call mpas_pool_get_config(configs, 'config_frac_landuse', config_frac_landuse) + + call mpas_pool_get_dimension(dims, 'nCells', nCells) + call mpas_pool_get_dimension(dims, 'maxEdges', maxEdges) + call mpas_pool_get_array(mesh, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(mesh, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(mesh, 'latCell', latCell) + call mpas_pool_get_array(mesh, 'lonCell', lonCell) + call mpas_pool_get_array(mesh, 'lu_index', lu_index) + call mpas_pool_get_array(mesh, 'landusef', landusef) + call mpas_pool_get_config(mgr % pool, 'category_min', category_min) + + ismax_lu = 40 + iswater_lu = 17 + isice_lu = 15 + nx = 6000 + ny = 3800 + nz = 40 + isigned = 0 + endian = 0 + wordsize = 2 + scalefactor = 0.1 + dx = 0.0025 + dy = 0.0025 + start_lat = 9.00125000 + start_lon = -139.99875000 + jEnd = 19001 + iEnd = 30001 + allocate(rarray(nx,ny,nz)) + allocate(ncat(ismax_lu,nCells)) + ncat(:,:) = 0 + + rarray_ptr = c_loc(rarray) + + do jTileStart = 1,jEnd,ny + jTileEnd = jTileStart + ny - 1 + + do iTileStart = 1,iEnd,nx + iTileEnd = iTileStart + nx - 1 + write(fname,'(a,i5.5,a1,i5.5,a1,i5.5,a1,i5.5)') trim(geog_data_path), & + iTileStart,'-',iTileEnd,'.',jTileStart,'-',jTileEnd + call mpas_log_write(trim(fname)) + call mpas_f_to_c_string(fname, c_fname) + + call read_geogrid(c_fname,rarray_ptr,nx,ny,nz,isigned,endian, & + wordsize,istatus) + call init_atm_check_read_error(istatus, fname) + rarray(:,:,:) = rarray(:,:,:) * real(scalefactor, kind=c_float) ! rarray contains fractional % (0-100) land use by category + + iPoint = 1 + do k=1,nz + do j=1,ny + do i=1,nx + ! Check if the NLCD dataset has zeros + if (nint(rarray(i,j,k)) == 0) cycle + + lat_pt = start_lat + (jTileStart + j - 2) * dy + lon_pt = start_lon + (iTileStart + i - 2) * dx + lat_pt = lat_pt * PI / 180.0 + lon_pt = lon_pt * PI / 180.0 + + iPoint = nearest_cell(lat_pt,lon_pt,iPoint,nCells,maxEdges, & + nEdgesOnCell,cellsOnCell, & + latCell,lonCell) + ncat(k,iPoint) = ncat(k,iPoint) + 1 + end do + end do + end do + + end do + end do + + if (.not. config_frac_landuse) then ! compute dominant NLCD land use for CONUS + do iCell = 1, nCells + if (maxloc(ncat(:,iCell), dim=1) < 21) cycle ! do not change MODIS land use outside CONUS + ! Because maxloc returns the location of the maximum value of an array as if the + ! starting index of the array is 1, and dataset categories do not necessarily start + ! at 1, we need to use category_min to ensure the correct category location is chosen. + lu_index(iCell) = maxloc(ncat(:,iCell), dim=1) - 1 + category_min + landusef(lu_index(iCell),iCell) = 1.0 ! defines dominant NLCD land use only + end do + else ! compute fractional NLCD land use for CONUS + do iCell = 1, nCells + if (maxloc(ncat(:,iCell), dim=1) < 21) cycle ! do not change MODIS land use outide CONUS + ! Note that lu_index currently defines dominant land use only for ivgtyp, where + ! ivgtyp is used by Noah LSM (and PX LSM when landusef=0 everywhere). + lu_index(iCell) = maxloc(ncat(:,iCell), dim=1) - 1 + category_min + ncattot = 0 + do i = 1,ismax_lu + ncattot = ncattot + ncat(i,iCell) + end do + do i = 1,ismax_lu + if (ncat(i,iCell) > 0) then + do j = 1,ismax_lu + landusef(j,iCell) = 0.0 ! clear MODIS landusef if NLCD (CONUS) cell + end do + end if + end do + do i = 1,ismax_lu + if (ncat(i,iCell) > 0) then + landusef(i,iCell) = real(ncat(i,iCell))/ncattot ! defines fractional land use for PX LSM + end if + end do + end do + end if + deallocate(rarray) + deallocate(ncat) + + nullify(category_min) + + end subroutine interp_landuse2 + !*********************************************************************** ! ! routine interp_soilcat @@ -1852,13 +2236,14 @@ end subroutine interp_landuse !> iswater is present in the dataset's index file. !> !----------------------------------------------------------------------- - subroutine interp_soilcat(mesh, kdtree, geog_data_path, iswater_soil, supersample_fac) + subroutine interp_soilcat(configs, mesh, kdtree, geog_data_path, iswater_soil, supersample_fac) implicit none ! Input variables type (mpas_pool_type), intent(inout) :: mesh type (mpas_kd_type), pointer, intent(in) :: kdtree + type (mpas_pool_type), intent(in) :: configs character (len=*), intent(in) :: geog_data_path integer, intent(out) :: iswater_soil integer, intent(in), optional :: supersample_fac @@ -1870,7 +2255,10 @@ subroutine interp_soilcat(mesh, kdtree, geog_data_path, iswater_soil, supersampl real (kind=RKIND), pointer :: scalefactor - integer :: iCell + logical, pointer :: config_frac_landuse + + integer :: ncattot + integer :: i, iCell integer :: ierr ierr = mgr % init(trim(geog_data_path)) @@ -1879,8 +2267,11 @@ subroutine interp_soilcat(mesh, kdtree, geog_data_path, iswater_soil, supersampl return end if + call mpas_pool_get_config(configs, 'config_frac_landuse', config_frac_landuse) + call mpas_pool_get_dimension(mesh, 'nCells', nCells) - call mpas_pool_get_array(mesh, 'soilcat_top', soilcat_top) + call mpas_pool_get_array(mesh, 'soilcat', soilcat) + call mpas_pool_get_array(mesh, 'soiltypf', soiltypf) call mpas_pool_get_config(mgr % pool, 'scale_factor', scalefactor) call mpas_pool_get_config(mgr % pool, 'category_min', category_min) call mpas_pool_get_config(mgr % pool, 'category_max', category_max) @@ -1890,16 +2281,40 @@ subroutine interp_soilcat(mesh, kdtree, geog_data_path, iswater_soil, supersampl allocate(ncat(category_min:category_max, nCells)) ncat(:,:) = 0 + soiltypf(:,:) = 0.0 call init_atm_map_static_data(mesh, mgr, kdtree, categorical_interp_criteria, categorical_interp_accumulation, & supersample_fac=supersample_fac) - do iCell = 1, nCells - ! Because maxloc returns the location of the maximum value of an array as if the - ! starting index of the array is 1, and dataset categories do not necessarily start - ! at 1, we need to use category_min to ensure the correct category location is chosen. - soilcat_top(iCell) = maxloc(ncat(:,iCell), dim=1) - 1 + category_min - end do + if(.not. config_frac_landuse) then ! compute dominant soil type for dominant land use + do iCell = 1, nCells + ! Because maxloc returns the location of the maximum value of an array as if the + ! starting index of the array is 1, and dataset categories do not necessarily start + ! at 1, we need to use category_min to ensure the correct category location is chosen. + soilcat(iCell) = maxloc(ncat(:,iCell), dim=1) - 1 + category_min + soiltypf(soilcat(iCell),iCell) = 1.0 ! defines dominant soil type only + end do + else ! compute fractional soil type for fractional land use + do iCell = 1,nCells + soilcat(iCell) = 1 + do i = 2,16 + if(ncat(i,iCell) > ncat(soilcat(iCell),iCell)) then + soilcat(iCell) = i ! currently defines dominant soil type only for isltyp + end if + end do + end do + do iCell = 1,nCells + ncattot = 0 + do i = 1,16 + ncattot = ncattot + ncat(i,iCell) + end do + do i = 1,16 + if(ncat(i,iCell) > 0) then + soiltypf(i,iCell) = real(ncat(i,iCell))/ncattot ! defines fractional soil type for PX LSM + end if + end do + end do + end if deallocate(ncat) ierr = mgr % finalize()