From 7dc875a8306dee3e1720933790162ea1121376c6 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Mon, 25 Mar 2024 14:34:25 -0400 Subject: [PATCH 1/4] Move LDAS_App to GEOSldas_App under GEOSldas_GridComp --- CMakeLists.txt | 4 +- GEOSlandassim_GridComp/mwRTM_routines.F90 | 606 +-- GEOSlandassim_GridComp/mwRTM_types.F90 | 278 +- GEOSldas_App/CMakeLists.txt | 49 + GEOSldas_App/GEOSldas.F90 | 34 + GEOSldas_App/GEOSldas_CAP.rc | 34 + GEOSldas_App/GEOSldas_ExtData.rc | 0 GEOSldas_App/GEOSldas_HIST.rc | 480 +++ GEOSldas_App/GEOSldas_LDAS.rc | 259 ++ GEOSldas_App/LDASsa_DEFAULT_inputs_adapt.nml | 122 + .../LDASsa_DEFAULT_inputs_catbias.nml | 166 + .../LDASsa_DEFAULT_inputs_ensprop.nml | 511 +++ GEOSldas_App/LDASsa_DEFAULT_inputs_ensupd.nml | 2361 ++++++++++++ GEOSldas_App/README_LDAS_App | 36 + .../ens_forcing/average_ensemble_forcing.py | 71 + GEOSldas_App/ens_forcing/enpert_forc.csh | 55 + GEOSldas_App/ens_forcing/ensemble_forc.py | 192 + GEOSldas_App/ens_forcing/regrid_forc.csh | 63 + GEOSldas_App/ens_forcing/test_enpert_forc.j | 40 + GEOSldas_App/ldas_setup | 1763 +++++++++ GEOSldas_App/lenkf.j.template | 849 +++++ GEOSldas_App/preprocess_ldas.F90 | 142 + GEOSldas_App/preprocess_ldas_routines.F90 | 3370 +++++++++++++++++ GEOSldas_App/process_hist.csh | 78 + GEOSldas_App/remap_config_ldas.py | 103 + .../LADAS/HISTORY.rc.atmens | 1083 ++++++ .../LADAS/HISTORY.rc.central | 98 + .../LADAS/exeinp.txt.Hy4dEnVar.atmens | 37 + .../LADAS/exeinp.txt.Hy4dEnVar.central | 39 + GEOSldas_App/tile_bin2nc4.F90 | 454 +++ .../config/Create_ccorr_cat_progn_default.m | 57 + .../util/config/generate_catchincr_hist.py | 121 + GEOSldas_App/util/config/rewind_GEOSldas.csh | 117 + .../util/inputs/ASCAT_sm_mask/CMakeLists.txt | 4 + .../inputs/ASCAT_sm_mask/ascat_mask_maker.F90 | 242 ++ .../mwRTM_params/Create_mwRTM_param_file.m | 240 ++ .../Create_vegopacity_8day_clim.m | 341 ++ ...reprocess_L2DCA_mwRTM_params_to_dailymat.m | 241 ++ .../mwRTM_params/fill_gaps_in_tiledata.m | 94 + .../get_L2_RTM_constants_tile_data.m | 218 ++ .../inputs/mwRTM_params/get_mwRTM_lookup.m | 167 + .../mwRTM_params/get_mwRTM_vegcls_based.m | 129 + .../inputs/mwRTM_params/mwrtm_bin2nc4.F90 | 241 ++ .../Run_get_L4_Tb_scale_SMAP.m | 178 + ...get_model_and_obs_clim_stats_latlon_grid.m | 115 + .../inputs/obs_scaling_params/dist_km2deg.m | 26 + .../get_ij_ind_from_latlon.m | 24 + .../get_model_and_obs_clim_stats.m | 729 ++++ ...get_model_and_obs_clim_stats_latlon_grid.m | 352 ++ .../obs_scaling_params/get_tile_num_for_obs.m | 125 + .../get_tile_num_in_cell_ij.m | 42 + .../write_netcdf_latlon_grid.m | 232 ++ .../obs_scaling_params/write_seqbin_file.m | 305 ++ GEOSldas_App/util/postproc/climatology/README | 24 + .../climatology/Run_L4_sm_clim_stats.m | 82 + .../Write_L4_sm_clim_stat_bin2nc4.m | 208 + .../climatology/get_model_clim_stats.m | 433 +++ .../climatology/read_seqbin_clim_pctl_file.m | 129 + .../climatology/write_seqbin_clim_pctl_file.m | 255 ++ .../util/postproc/compress_bit-shaved_nc4.sh | 36 + GEOSldas_App/util/postproc/write_smapL4SMqa.m | 1315 +++++++ .../util/shared/matlab/EASEv2_ind2latlon.m | 202 + .../util/shared/matlab/EASEv2_latlon2ind.m | 218 ++ .../util/shared/matlab/J2000_to_DateTime.m | 111 + .../shared/matlab/MAPL_ReadForcing_fullfile.m | 80 + .../util/shared/matlab/augment_date_time.m | 149 + .../util/shared/matlab/days_in_month.m | 16 + .../util/shared/matlab/get_dofyr_pentad.m | 19 + .../util/shared/matlab/is_leap_year.m | 30 + .../util/shared/matlab/pentad_of_year.m | 14 + .../util/shared/matlab/read_ObsFcstAna.m | 192 + .../util/shared/matlab/read_catparam.m | 298 ++ GEOSldas_App/util/shared/matlab/read_obslog.m | 51 + .../util/shared/matlab/read_obsparam.m | 80 + .../util/shared/matlab/read_smapL4SMaup.m | 152 + .../util/shared/matlab/read_smapL4SMlmc.m | 139 + .../util/shared/matlab/read_tilecoord.m | 240 ++ .../util/shared/matlab/read_tilegrids.m | 136 + GEOSldas_App/util/shared/matlab/tile2grid.m | 87 + 79 files changed, 21970 insertions(+), 443 deletions(-) create mode 100644 GEOSldas_App/CMakeLists.txt create mode 100644 GEOSldas_App/GEOSldas.F90 create mode 100644 GEOSldas_App/GEOSldas_CAP.rc create mode 100644 GEOSldas_App/GEOSldas_ExtData.rc create mode 100644 GEOSldas_App/GEOSldas_HIST.rc create mode 100644 GEOSldas_App/GEOSldas_LDAS.rc create mode 100644 GEOSldas_App/LDASsa_DEFAULT_inputs_adapt.nml create mode 100644 GEOSldas_App/LDASsa_DEFAULT_inputs_catbias.nml create mode 100644 GEOSldas_App/LDASsa_DEFAULT_inputs_ensprop.nml create mode 100644 GEOSldas_App/LDASsa_DEFAULT_inputs_ensupd.nml create mode 100644 GEOSldas_App/README_LDAS_App create mode 100644 GEOSldas_App/ens_forcing/average_ensemble_forcing.py create mode 100755 GEOSldas_App/ens_forcing/enpert_forc.csh create mode 100755 GEOSldas_App/ens_forcing/ensemble_forc.py create mode 100755 GEOSldas_App/ens_forcing/regrid_forc.csh create mode 100755 GEOSldas_App/ens_forcing/test_enpert_forc.j create mode 100755 GEOSldas_App/ldas_setup create mode 100644 GEOSldas_App/lenkf.j.template create mode 100644 GEOSldas_App/preprocess_ldas.F90 create mode 100644 GEOSldas_App/preprocess_ldas_routines.F90 create mode 100755 GEOSldas_App/process_hist.csh create mode 100644 GEOSldas_App/remap_config_ldas.py create mode 100644 GEOSldas_App/sample_config_files/LADAS/HISTORY.rc.atmens create mode 100644 GEOSldas_App/sample_config_files/LADAS/HISTORY.rc.central create mode 100644 GEOSldas_App/sample_config_files/LADAS/exeinp.txt.Hy4dEnVar.atmens create mode 100644 GEOSldas_App/sample_config_files/LADAS/exeinp.txt.Hy4dEnVar.central create mode 100644 GEOSldas_App/tile_bin2nc4.F90 create mode 100644 GEOSldas_App/util/config/Create_ccorr_cat_progn_default.m create mode 100755 GEOSldas_App/util/config/generate_catchincr_hist.py create mode 100755 GEOSldas_App/util/config/rewind_GEOSldas.csh create mode 100644 GEOSldas_App/util/inputs/ASCAT_sm_mask/CMakeLists.txt create mode 100644 GEOSldas_App/util/inputs/ASCAT_sm_mask/ascat_mask_maker.F90 create mode 100644 GEOSldas_App/util/inputs/mwRTM_params/Create_mwRTM_param_file.m create mode 100644 GEOSldas_App/util/inputs/mwRTM_params/Create_vegopacity_8day_clim.m create mode 100644 GEOSldas_App/util/inputs/mwRTM_params/Preprocess_L2DCA_mwRTM_params_to_dailymat.m create mode 100644 GEOSldas_App/util/inputs/mwRTM_params/fill_gaps_in_tiledata.m create mode 100644 GEOSldas_App/util/inputs/mwRTM_params/get_L2_RTM_constants_tile_data.m create mode 100644 GEOSldas_App/util/inputs/mwRTM_params/get_mwRTM_lookup.m create mode 100644 GEOSldas_App/util/inputs/mwRTM_params/get_mwRTM_vegcls_based.m create mode 100644 GEOSldas_App/util/inputs/mwRTM_params/mwrtm_bin2nc4.F90 create mode 100644 GEOSldas_App/util/inputs/obs_scaling_params/Run_get_L4_Tb_scale_SMAP.m create mode 100644 GEOSldas_App/util/inputs/obs_scaling_params/Run_get_model_and_obs_clim_stats_latlon_grid.m create mode 100644 GEOSldas_App/util/inputs/obs_scaling_params/dist_km2deg.m create mode 100644 GEOSldas_App/util/inputs/obs_scaling_params/get_ij_ind_from_latlon.m create mode 100644 GEOSldas_App/util/inputs/obs_scaling_params/get_model_and_obs_clim_stats.m create mode 100644 GEOSldas_App/util/inputs/obs_scaling_params/get_model_and_obs_clim_stats_latlon_grid.m create mode 100644 GEOSldas_App/util/inputs/obs_scaling_params/get_tile_num_for_obs.m create mode 100644 GEOSldas_App/util/inputs/obs_scaling_params/get_tile_num_in_cell_ij.m create mode 100644 GEOSldas_App/util/inputs/obs_scaling_params/write_netcdf_latlon_grid.m create mode 100644 GEOSldas_App/util/inputs/obs_scaling_params/write_seqbin_file.m create mode 100644 GEOSldas_App/util/postproc/climatology/README create mode 100644 GEOSldas_App/util/postproc/climatology/Run_L4_sm_clim_stats.m create mode 100644 GEOSldas_App/util/postproc/climatology/Write_L4_sm_clim_stat_bin2nc4.m create mode 100644 GEOSldas_App/util/postproc/climatology/get_model_clim_stats.m create mode 100644 GEOSldas_App/util/postproc/climatology/read_seqbin_clim_pctl_file.m create mode 100644 GEOSldas_App/util/postproc/climatology/write_seqbin_clim_pctl_file.m create mode 100644 GEOSldas_App/util/postproc/compress_bit-shaved_nc4.sh create mode 100644 GEOSldas_App/util/postproc/write_smapL4SMqa.m create mode 100644 GEOSldas_App/util/shared/matlab/EASEv2_ind2latlon.m create mode 100644 GEOSldas_App/util/shared/matlab/EASEv2_latlon2ind.m create mode 100644 GEOSldas_App/util/shared/matlab/J2000_to_DateTime.m create mode 100644 GEOSldas_App/util/shared/matlab/MAPL_ReadForcing_fullfile.m create mode 100644 GEOSldas_App/util/shared/matlab/augment_date_time.m create mode 100644 GEOSldas_App/util/shared/matlab/days_in_month.m create mode 100644 GEOSldas_App/util/shared/matlab/get_dofyr_pentad.m create mode 100644 GEOSldas_App/util/shared/matlab/is_leap_year.m create mode 100644 GEOSldas_App/util/shared/matlab/pentad_of_year.m create mode 100644 GEOSldas_App/util/shared/matlab/read_ObsFcstAna.m create mode 100644 GEOSldas_App/util/shared/matlab/read_catparam.m create mode 100644 GEOSldas_App/util/shared/matlab/read_obslog.m create mode 100644 GEOSldas_App/util/shared/matlab/read_obsparam.m create mode 100644 GEOSldas_App/util/shared/matlab/read_smapL4SMaup.m create mode 100644 GEOSldas_App/util/shared/matlab/read_smapL4SMlmc.m create mode 100644 GEOSldas_App/util/shared/matlab/read_tilecoord.m create mode 100644 GEOSldas_App/util/shared/matlab/read_tilegrids.m create mode 100644 GEOSldas_App/util/shared/matlab/tile2grid.m diff --git a/CMakeLists.txt b/CMakeLists.txt index d6a7018..b4c0974 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ esma_set_this () -esma_add_subdirectory(GEOSgcm_GridComp ) +esma_add_subdirectory(GEOSgcm_GridComp) set (alldirs GEOSmetforce_GridComp @@ -15,3 +15,5 @@ esma_add_library(${this} SUBDIRS LDAS_Shared DEPENDENCIES GEOSland_GridComp makebcs MAPL INCLUDES ${INC_ESMF}) + +esma_add_subdirectory(GEOSldas_App) diff --git a/GEOSlandassim_GridComp/mwRTM_routines.F90 b/GEOSlandassim_GridComp/mwRTM_routines.F90 index 8a2fc5b..45decf3 100644 --- a/GEOSlandassim_GridComp/mwRTM_routines.F90 +++ b/GEOSlandassim_GridComp/mwRTM_routines.F90 @@ -3,11 +3,11 @@ module mwRTM_routines ! subroutines for microwave radiative transfer model ! - ! Select a specific configuration of the RTM via the field - ! "RTM_ID" in the "obs_param" type. + ! Select a specific configuration of the RTM via the field + ! "RTM_ID" in the "obs_param" type. ! ! %RTM_ID = ID of radiative transfer model to use for Tb forward modeling - ! (subroutine get_obs_pred()) + ! (subroutine get_obs_pred()) ! 0 = none ! 1 = L-band tau-omega model as in De Lannoy et al. 2013 (doi:10.1175/JHM-D-12-092.1) (SMOS) ! 2 = same as 1 but without Pellarin atm corr (SMAP) @@ -22,12 +22,12 @@ module mwRTM_routines use MAPL_ConstantsMod, ONLY: & MAPL_PI, & MAPL_TICE - + use mwRTM_types, ONLY: & mwRTM_param_type, & mwRTM_param_nodata_check, & assignment (=) - + use LDAS_ensdrv_globals, ONLY: & logit, & logunit, & @@ -40,29 +40,29 @@ module mwRTM_routines LDAS_GENERIC_ERROR implicit none - + ! everything is private by default unless made public - + private - + public :: mwRTM_get_Tb, catch2mwRTM_vars - + ! --------------------------------------------------------- - + real, parameter :: Tb_sky = 2.7 ! cosmic mw background temp [K] complex, parameter :: diel_ice = (3.2, 0.1) ! dielec. const. of ice complex, parameter :: diel_air = (1.0, 0.0) ! dielec. const. of air complex, parameter :: diel_rock = (5.5, 0.2) ! dielec. const. of rock - + real, parameter :: diel_watinf = 4.9 ! dielec. const. of water @ high freq. ! (Stogryn 1971) - + real, parameter :: eps_0 = 8.854e-12 ! vacuum permittivity [Farads/meter] ! (Klein and Swift 1977) real, parameter :: rho_soil = 2.66 ! soil specific density [g/cm3] - + contains ! ********************************************************************** @@ -70,102 +70,102 @@ module mwRTM_routines ! Subroutine mwRTM_get_param() reads binary mwRTM files and is no longer used. ! ! The subroutine has been replaced by: - ! - Applications/LDAS_App/[..]/mwrtm_bin2nc4.F90 converts mwRTM files from binary to nc4 + ! - Components/GEOSldas_GridComp/GEOSldas_App/[..]/mwrtm_bin2nc4.F90 converts mwRTM files from binary to nc4 ! - get_mwrtm_param() in GEOS_LandAssimGridComp.F90 converts the internal state ! variables of the Land Assim GridComp into the mwRTM structure. ! ! reichle, 4 Aug 2020 - + ! subroutine mwRTM_get_param( N_catg, N_tile, d2g, tile_id, mwRTM_param_path, & ! need_mwRTM_param, mwp) -! +! ! ! Read microwave RTM parameters from file. ! ! -! ! reichle, 17 May 2011 +! ! reichle, 17 May 2011 ! ! reichle, 21 Oct 2011 - added input of mwRTM_param from file -! ! reichle, 23 Oct 2012 - removed look-up table option (too complicated with +! ! reichle, 23 Oct 2012 - removed look-up table option (too complicated with ! ! "new" (200+) soil classes) ! ! - added tile_id check when reading mwRTM params from file -! +! ! implicit none -! +! ! integer, intent(in) :: N_catg, N_tile -! +! ! integer, dimension(N_tile), intent(in) :: d2g, tile_id ! -! character(200), intent(in) :: mwRTM_param_path +! character(200), intent(in) :: mwRTM_param_path ! ! logical, intent(in) :: need_mwRTM_param ! ! type(mwRTM_param_type), dimension(N_tile), intent(out) :: mwp ! mwRTM parameters -! +! ! ! local variables -! +! ! integer, parameter :: N_search_dir_max = 5 -! +! ! integer :: n, N_search_dir, istat -! +! ! character( 80) :: fname -! +! ! character(100), dimension(N_search_dir_max) :: search_dir ! ! logical :: all_nodata, mwp_nodata -! +! ! character(len=*), parameter :: Iam = 'mwRTM_get_param' ! character(len=400) :: err_msg ! ! ! ---------------------------------------------------------------------- ! ! ! ! initialize -! +! ! do n=1,N_tile -! mwp(n) = nodata_generic +! mwp(n) = nodata_generic ! end do -! +! ! ! read mwRTM parameters from file -! +! ! if (logit) write (logunit,*) 'Reading microwave RTM parameters from file' -! +! ! fname = '/mwRTM_param.bin' -! +! ! N_search_dir = 2 ! specify sub-dirs of mwRTM_param_path to search for file "fname" -! +! ! search_dir(1) = 'mwRTM' ! search_dir(2) = '.' -! -! ! when called with optional argument "istat" subroutine "open_land_param_file()" -! ! will *NOT* stop upon failure to open the file -! +! +! ! when called with optional argument "istat" subroutine "open_land_param_file()" +! ! will *NOT* stop upon failure to open the file +! ! istat = open_land_param_file( 10, .false., .true., N_search_dir, fname, & ! mwRTM_param_path, search_dir, ignore_stop=.true.) -! +! ! if (istat==0) then -! +! ! call io_mwRTM_param_type( 'r', 10, N_tile, mwp, N_catg, tile_id, d2g ) -! +! ! close (10,status='keep') -! +! ! if (logit) write (logunit,*) 'done reading' ! if (logit) write (logunit,*) -! +! ! else -! +! ! if (logit) write (logunit,*) 'WARNING: Could not open file!' ! if (logit) write (logunit,*) -! +! ! end if -! +! ! ! check for no-data-values in parameters ! ! if any field is a nodata value, set all fields to nodata value ! ! all_nodata = .true. -! +! ! do n=1,N_tile -! +! ! call mwRTM_param_nodata_check( mwp(n), mwp_nodata ) -! +! ! if (.not. mwp_nodata) all_nodata = .false. -! +! ! end do ! ! ! stop if mwRTM parameters needed but not available (ie, all are no-data) @@ -178,7 +178,7 @@ module mwRTM_routines ! ! warn if mwRTM parameters are all no-data (may not be needed) ! ! if (all_nodata .and. logit) then -! +! ! write (logunit,*) '#########################################################' ! write (logunit,*) ! write (logunit,*) ' WARNING: *All* parameters for the microwave radiative ' @@ -188,47 +188,47 @@ module mwRTM_routines ! write (logunit,*) ! ! end if -! +! ! end subroutine mwRTM_get_param - + ! **************************************************************** subroutine catch2mwRTM_vars( N_tile, vegcls_catch, poros_catch, poros_mwRTM, & sfmc_catch, tsurf_catch, tp1_catch, sfmc_mwRTM, tsoil_mwRTM, tp1_in_Kelvin ) - + ! convert soil moisture, surface temperature, and soil temperature from the Catchment - ! model into soil moisture and soil temperature inputs for the microwave radiative - ! transfer model (mwRTM) + ! model into soil moisture and soil temperature inputs for the microwave radiative + ! transfer model (mwRTM) ! ! reichle, 11 Dec 2013 ! ! added optional switch to allow tp1_catch input in Kelvin ! - reichle & borescan, 6 Nov 2020 - + implicit none - + integer, intent(in) :: N_tile - + integer, dimension(N_tile), intent(in) :: vegcls_catch - + real, dimension(N_tile), intent(in) :: poros_catch, poros_mwRTM real, dimension(N_tile), intent(in) :: sfmc_catch, tsurf_catch, tp1_catch - + real, dimension(N_tile), intent(out) :: sfmc_mwRTM, tsoil_mwRTM logical, intent(in), optional :: tp1_in_Kelvin ! local variables - + logical :: tp1_in_K real, dimension(N_tile) :: tp1 - + ! ----------------------------------------------------------------------- ! ! reichle, 22 Oct 2012: scaling factor added because it is necessary for ! proper functioning of mwRTM calibration to SMOS obs - + sfmc_mwRTM = sfmc_catch * poros_mwRTM / poros_catch ! diagnose soil temperature to be used with mwRTM @@ -238,19 +238,19 @@ subroutine catch2mwRTM_vars( N_tile, vegcls_catch, poros_catch, poros_mwRTM, & ! NOTE: By default, "tp" is assumed to be in deg Celsius tp1_in_K = .false. - + if (present(tp1_in_Kelvin)) tp1_in_K = tp1_in_Kelvin tp1 = tp1_catch - + if (.not. tp1_in_K) tp1 = tp1 + MAPL_TICE ! convert units if not already in Kelvin - + tsoil_mwRTM = tp1 - + end subroutine catch2mwRTM_vars - + ! **************************************************************** - + subroutine mwRTM_get_Tb( N_tile, freq, inc_angle, mwp, elev, & LAI, soilmoist, soiltemp, SWE, Tair, RTM_ID, Tb_h, Tb_v ) @@ -259,7 +259,7 @@ subroutine mwRTM_get_Tb( N_tile, freq, inc_angle, mwp, elev, & !23 Nov 2010: - adapted to include the atmospheric correction ! GDL, code based on CMEMv3.0 ! - !Instead of passing on the whole diagnostic structure, + !Instead of passing on the whole diagnostic structure, !only pass rtmv, T5, Tc !GDL, 22Oct10 ! !N_cat could be either all tiles or only a part of them! @@ -268,12 +268,12 @@ subroutine mwRTM_get_Tb( N_tile, freq, inc_angle, mwp, elev, & !FYI: !SM vol/vol ~ g/cm3 !gravim SM (g/g) * bulk densit (g/cm3) = volum SM (vol/vol) ~ g/cm3 - ! + ! !== VWC: Vegetation water content in kg/m2 (=mm) !== T5: Soil temperature [K] - !== Tc: Vegetation canopy temperature [K] + !== Tc: Vegetation canopy temperature [K] ! - !21 Mar 2011: - temperature treatment changed + !21 Mar 2011: - temperature treatment changed !==> instead of passing on T5 and Tc (= tsurf) !we now pass on tsurf and tp1 and diagnose T10cm (~T5) from it HERE !==> tsurf and tp1 are now input variables @@ -289,16 +289,16 @@ subroutine mwRTM_get_Tb( N_tile, freq, inc_angle, mwp, elev, & ! 23 Nov 2011: - reichle: changed tsoil_threshold b/c QC now done for individual ! ensemble members ! 22 Oct 2012: - reichle: removed interception water ("capac") contribution - ! + ! !--------------------------------------------------- - + implicit none integer, intent(in) :: N_tile ! number of tiles real, intent(in) :: freq ! [Hz] real, intent(in) :: inc_angle ! [deg] - + type(mwRTM_param_type), dimension(N_tile), intent(in) :: mwp real, dimension(N_tile), intent(in) :: elev ! [m] @@ -310,82 +310,82 @@ subroutine mwRTM_get_Tb( N_tile, freq, inc_angle, mwp, elev, & real, dimension(N_tile), intent(in) :: Tair ! [K] integer, intent(in) :: RTM_ID - + real, dimension(N_tile), intent(out) :: Tb_h, Tb_v ! [K] - + ! -------------------- - + ! local variables - + real, parameter :: tsoil_threshold = MAPL_TICE+0.2 ! avoid "frozen" soil [K] real, parameter :: SWE_threshold = 1.e-4 ! avoid snow [kg/m2] - + integer :: n - + real :: inc, sin_inc, cos_inc - complex :: c_er, tmpc1, tmpc2 + complex :: c_er, tmpc1, tmpc2 real :: roh, rov, rsh, rsv real :: h_mc, Q, slope - + real :: vwc, Ah, Av, exptauh, exptauv, exptauh2, exptauv2, tmpreal - + real :: exptau_atm, tau_atm, Tb_ad, Tb_au - + real :: soiltemp_in_C, Tc - + !real :: er_r ! for realdobson logical :: veg_params_nodata - + character(len=*), parameter :: Iam = 'mwRTM_get_Tb' character(len=400) :: err_msg - + !--------------------------------------------------- - + !if (logit) write(logunit,*) 'entering mwRTM_get_Tb...' ! check first element of elevation against no-data-value ! (elevation is only needed for RTMs with Pellarin atm corr) select case (RTM_ID) - + case(1,3) - + if ( abs(elev(1)-nodata_generic)=mwp(n)%rgh_wmax) then - + h_mc = mwp(n)%rgh_hmin - + else - + slope = & (mwp(n)%rgh_hmin - mwp(n)%rgh_hmax)/ & (mwp(n)%rgh_wmax - mwp(n)%rgh_wmin) - + h_mc = mwp(n)%rgh_hmax + slope * (soilmoist(n) - mwp(n)%rgh_wmin) - + endif - + ! 2) polarization mixing, Q as defined in CMEM: - + if (freq < 2.e9) then select case (RTM_ID) - + case(1,2) - + Q = 0. ! Q is assumed zero at low frequency case(3,4) - - Q = 0.1771 * h_mc - - case default + + Q = 0.1771 * h_mc + + case default err_msg = 'unknown RTM_ID (during pol mixing)' call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) - + end select - - else - + + else + Q = 0.35 * (1.0 - exp(-0.6 * (mwp(n)%rgh_polmix**2) * (freq/1.e9) )) - + end if ! rough surface reflectivity select case (RTM_ID) - + case(1,2) - - rsh = ( (1-Q) * roh + Q * rov) * EXP(-h_mc*cos_inc**mwp(n)%rgh_nrh) + + rsh = ( (1-Q) * roh + Q * rov) * EXP(-h_mc*cos_inc**mwp(n)%rgh_nrh) rsv = ( (1-Q) * rov + Q * roh) * EXP(-h_mc*cos_inc**mwp(n)%rgh_nrv) - + case(3,4) - - rsh = ( (1-Q) * roh + Q * rov) * EXP(-h_mc*cos_inc**2) + + rsh = ( (1-Q) * roh + Q * rov) * EXP(-h_mc*cos_inc**2) rsv = ( (1-Q) * rov + Q * roh) * EXP(-h_mc*cos_inc**2) case default - + err_msg = 'unknown RTM_ID (during rough reflectivity)' call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) - + end select ! ------------------------------------------------------------- @@ -514,118 +514,118 @@ subroutine mwRTM_get_Tb( N_tile, freq, inc_angle, mwp, elev, & ! the if statement above, in conjunction with mwRTM_param_nodata_check() called earlier, ! ensures that we have "good" values for either (vegopacity) or (bh,bv,lewt) - + if ( LDAS_is_nodata( mwp(n)%vegopacity ) ) then - + ! == vwc: Vegetation water content in kg/m2 (=mm) ! needs to be the total columnar vegetation water content, ! depends on greenness/NDVI/LAI... ! VWC=LEWT*LAI, lewt is actually a time-varying parameter! - ! For now LEWT is guessed based on literature, and kept cst. - + ! For now LEWT is guessed based on literature, and kept cst. + vwc = mwp(n)%lewt * lai(n) ! removed contribution of interception water ("capac") ! - !! ! add bit of intercepted water as well - !! + !! ! add bit of intercepted water as well + !! !! vwc = vwc + capac(n) - + ! Vegetation optical thickness tau=b*VWC (eq. (2) in Crow et al. 2005) - + tmpreal = vwc/cos_inc - + exptauh = EXP( -mwp(n)%bh * tmpreal ) exptauv = EXP( -mwp(n)%bv * tmpreal ) else - + exptauh = EXP( -mwp(n)%vegopacity) exptauv = EXP( -mwp(n)%vegopacity) end if - + Tc = soiltemp(n) ! canopy temp = soil temp - + tmpreal = Tc * (1. - mwp(n)%omega) Ah = tmpreal * (1. - exptauh) Av = tmpreal * (1. - exptauv) - + ! Eq.(1) in Crow et al. 2005: ! ! Tb_tov = Tb_soil.e_p.exp(-tau/cos theta) + Tb_veg.(1 + r_r.exp(-tau/cos theta)) - + Tb_h(n) = soiltemp(n) * (1. - rsh) * exptauh + Ah * (1. + rsh * exptauh) Tb_v(n) = soiltemp(n) * (1. - rsv) * exptauv + Av * (1. + rsv * exptauv) - - + + ! ------------------------------------------------------------- ! ! Atmospheric correction ! - ! GDL 23nov10 + ! GDL 23nov10 select case (RTM_ID) - + case(1,3) - + exptauh2 = exptauh * exptauh exptauv2 = exptauv * exptauv - + if (freq<2.e9) then - + call ATMPELLARIN( elev(n)/1000., Tair(n), cos_inc, tau_atm, Tb_ad, Tb_au ) - + else - + err_msg = 'cannot compute atm corr for given freq' call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) - + end if - + exptau_atm = EXP( - tau_atm/cos_inc ) - + Tb_h(n) = Tb_h(n) + Tb_ad*rsh*exptauh2 ! top-of-veg Tb_h (incl atm. contrib.) Tb_v(n) = Tb_v(n) + Tb_ad*rsv*exptauv2 ! top-of-veg Tb_v (incl atm. contrib.) - + Tb_h(n) = Tb_h(n) * exptau_atm + Tb_au ! top-of-atmosphere Tb_h Tb_v(n) = Tb_v(n) * exptau_atm + Tb_au ! top-of-atmosphere Tb_v case(2,4) - + ! do nothing - + case default - + err_msg = 'unknown RTM_ID (during Pellarin atm corr)' call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) - + end select else ! snow present, soil frozen, or mwRTM params not available - + Tb_h(n) = nodata_generic Tb_v(n) = nodata_generic - + endif end do - + !if (logit) write(logunit,*) 'exiting mwRTM_get_Tb.' - + end subroutine mwRTM_get_Tb - - + + ! ********************************************************************** - + subroutine atmpellarin( Z, tair, costheta, tau_atm, tb_ad, tb_au) ! opacity and brightness temperature of atmosphere for low-freq microwave ! (up to and incl. L-band) ! - ! GDL, 23nov10 + ! GDL, 23nov10 ! Code based on CMEMv3.0 ! ! reichle, 16 May 2011: included in LDASsa @@ -639,13 +639,13 @@ subroutine atmpellarin( Z, tair, costheta, tau_atm, tb_ad, tb_au) real, intent(in) :: costheta ! cosine of incidence angle [dim-less] real, intent(in) :: Z ! elevation above sea-level [km] real, intent(in) :: tair ! surface air temperature [K] - + real, intent(out) :: tau_atm ! atmospheric opacity [dim-less] real, intent(out) :: tb_ad ! downwelling atm Tb [K] real, intent(out) :: tb_au ! upwelling atm Tb [K] ! local variables - + real :: GOSSAT, TAEQ !--------------------------------------------------------------------- @@ -654,24 +654,24 @@ subroutine atmpellarin( Z, tair, costheta, tau_atm, tb_ad, tb_au) tau_atm = exp( -3.926 - 0.2211 * Z - 0.00369 *tair) - + ! 2. Calculate up- and downward atmospheric radiation GOSSAT = exp(-tau_atm/ costheta) TAEQ = exp( 4.927 + 0.002195 * tair) - + tb_ad = TAEQ*(1. - GOSSAT) + Tb_sky * GOSSAT - + tb_au = TAEQ*(1. - GOSSAT) - + end subroutine atmpellarin - + ! ************************************************************ - + subroutine dielwang( FREQ, WC, TS, wt, wp, poros, sand, clay, eps) - + ! GDL, 28Mar11 ! Code adapted from CMEMv3.0 ! @@ -679,23 +679,23 @@ subroutine dielwang( FREQ, WC, TS, wt, wp, poros, sand, clay, eps) ! ! ! Purpose : - ! Calculate the dielectric constant of a wet soil + ! Calculate the dielectric constant of a wet soil ! Developed and validated for 1.4 and 5 GHz. ! ! Reference: - ! Wang and Schmugge, 1980: An empirical model for the + ! Wang and Schmugge, 1980: An empirical model for the ! complex dielectric permittivity of soils as a function of water ! content. IEEE Trans. Geosci. Rem. Sens., GE-18, No. 4, 288-295. ! !--------------------------------------------------------------------------- - + implicit none - + real, intent(in) :: FREQ ! microwave frequency [Hz] real, intent(in) :: TS ! soil temperature [deg C] - + real, intent(in) :: WC ! volumetric soil water content [m3/m3] - + real, intent(in) :: wt ! transition soil moisture [m3/m3] real, intent(in) :: wp ! wilting point [m3/m3] real, intent(in) :: poros ! porosity @@ -703,40 +703,40 @@ subroutine dielwang( FREQ, WC, TS, wt, wp, poros, sand, clay, eps) real, intent(in) :: clay ! clay fraction [0-1] complex, intent(out) :: eps ! dielectric constant of soil-water mixture [dim-less] - + ! ------------------------------------- - ! + ! ! local variables - complex, parameter :: j = (0. , 1. ) + complex, parameter :: j = (0. , 1. ) real :: gamma ! fitting parameter real :: ecl ! conductivity loss complex :: ew ! dielectric constant of water complex :: ex ! dielectric constant of the initially absorbed water - + real :: alpha - + !--------------------------------------------------------------------------- ! ! 0. Compute dielectric constant of free water ! ! assume soil salinity = 0 - + call DIEL_WAT( 2, 2, TS, 0., FREQ, clay, sand, poros, wc, ew) - + !--------------------------------------------------------------------------- ! ! 1. Calculate dielectric constant of soil-water mixture - + gamma = -0.57 * wp + 0.481 ! wt = 0.49 * wp + 0.165 transition SM parameter from Wang and Schmugge 1980; ! note typo in De Lannoy et al 2013 (0.48 instead of 0.49) - + IF (wc <= wt) THEN - + ex = diel_ice + (ew-diel_ice)*(wc/wt)*gamma eps = wc*ex + (poros-wc)*diel_air + (1.-poros)*diel_rock @@ -748,28 +748,28 @@ subroutine dielwang( FREQ, WC, TS, wt, wp, poros, sand, clay, eps) eps = wt*ex + (wc-wt)*ew + (poros-wc)*diel_air + (1.-poros)*diel_rock ENDIF - + !--------------------------------------------------------------------------- ! ! 2. add conductivity loss (Wang dielectric model) if (FREQ > 2.5e9) then - + alpha = 0. else - + alpha = min( 100.*wp, 26.) - + end if - + ecl = alpha * wc**2. - + eps = eps + j * ecl - + end subroutine dielwang - - + + ! ************************************************************ SUBROUTINE DIEL_WAT( medium, isal, T, sal, freq, clay, sand, poros, wc, ew) @@ -778,10 +778,10 @@ SUBROUTINE DIEL_WAT( medium, isal, T, sal, freq, clay, sand, poros, wc, ew) ! ! included in LDASsa, reichle - 2 Jun 2011 - ! Purpose : - ! Calculate dielectric constant of water in three different media : + ! Purpose : + ! Calculate dielectric constant of water in three different media : ! pure water, sea water, soil water - + ! Reference: ! Dielectric constant of pure water ! Ulaby p 2020 @@ -797,17 +797,17 @@ SUBROUTINE DIEL_WAT( medium, isal, T, sal, freq, clay, sand, poros, wc, ew) ! 1) Dobson '85. Modified Debye expression ! Stern_Gouy double layer theory ! 2) Ulaby p 2024 - + ! Interface : ! medium = pure water(0) sea water(1) soil water(2) ! isal = Stogryn (1) Klein and Swift (2) - + ! local variables : ! N : normality from salinity (Stogryn, modified by Klein and Swift 1977) ! T : temperature of water (C) ! ew : dielectric constant of water - ! sal : water salinity (psu = ppt(weight) ) - ! eps_w0 : static dielectric constant of pure water (Klein and Swift 1977) + ! sal : water salinity (psu = ppt(weight) ) + ! eps_w0 : static dielectric constant of pure water (Klein and Swift 1977) ! eps_sw0 : Static dielectric constant of soil water ! tau_w : relaxation time of pure water (stogryn, 1970) ! tau_sw : relaxation time of saline water @@ -816,7 +816,7 @@ SUBROUTINE DIEL_WAT( medium, isal, T, sal, freq, clay, sand, poros, wc, ew) !--------------------------------------------------------------------------- IMPLICIT NONE - + INTEGER, intent(in) :: medium ! 0=pure water, 1=sea water, 2=soil water INTEGER, intent(in) :: isal ! 1=Stogryn, 2=Klein and Swift REAL, intent(in) :: T ! temperature of water [C] @@ -828,13 +828,13 @@ SUBROUTINE DIEL_WAT( medium, isal, T, sal, freq, clay, sand, poros, wc, ew) real, intent(in) :: wc ! soil moisture [m3/m3] COMPLEX, intent(out) :: ew ! dielec. const. of water at given freq, T, sal, etc. - + ! local variables - - complex, parameter :: j = (0. , 1. ) - - real :: rho_b - + + complex, parameter :: j = (0. , 1. ) + + real :: rho_b + REAL :: N, omega, wc_c REAL :: sigma_eff REAL :: tau_w, tau_sw @@ -853,89 +853,89 @@ SUBROUTINE DIEL_WAT( medium, isal, T, sal, freq, clay, sand, poros, wc, ew) ! ! tau_w = 1./(2.*pi) * (1.1109e-10 - 3.824e-12 * T + 6.938e-14 * T**2 & ! - 5.096e-16 * T**3) - + omega = 2.0 * MAPL_PI * freq - rho_b = (1.-poros)*rho_soil ! soil bulk density [g/cm3] - - + rho_b = (1.-poros)*rho_soil ! soil bulk density [g/cm3] + + SELECT CASE (isal) - + CASE ( 1 ) ! Stogryn (1971) - + N = 0.9141 * sal * (1.707e-2 + sal*(1.205e-5 + sal*4.058e-9)) - + eps_sw0 = 87.74 + T*(-0.4008 + T*(9.398e-4 + T*1.410e-6)) a = 1. + N*(-0.2551 + N*(5.151e-2 - N*6.889e-3)) eps_sw0 = eps_sw0 * a - - bb = 1. + N*(-0.04896 + 0.1463e-2*T + N*(-0.02967 + N*5.644e-3)) - + + bb = 1. + N*(-0.04896 + 0.1463e-2*T + N*(-0.02967 + N*5.644e-3)) + tau_sw = tau_w * bb - + CASE ( 2 ) ! Klein and Swift (1977) - + eps_sw0 = 87.134 + T*(-1.949e-1 + T*(-1.276e-2 + T*2.491e-4)) - + a = 1. + sal*( 1.613e-5*T -3.656e-3 + sal*(3.210e-5 - sal*4.232e-7)) - + eps_sw0 = eps_sw0 * a - + bb = 1. + sal*(2.282e-5*T - 7.638e-4 + sal*(-7.760e-6 + sal*1.105e-8)) - + tau_sw = tau_w * bb - + END SELECT - - + + SELECT CASE (medium) - + CASE ( 0 ) ! pure water - + eps_w0 = 88.045 + T*(-0.4147 + T*(6.295e-4 + T*1.075e-5)) - + ew = diel_watinf + (eps_w0 - diel_watinf) / (1. - j * omega * tau_w) - + CASE ( 1 ) ! sea water - + err_msg = 'medium=1 (sea water) not implemented' call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) - - CASE ( 2 ) ! soil water + + CASE ( 2 ) ! soil water ! changed units of sand, clay to [0-1], reichle, 2 Jun 2011 - - ! Avoid negative sigma_eff for very sandy soils with low bulk densities. - + + ! Avoid negative sigma_eff for very sandy soils with low bulk densities. + sigma_eff = max( 0., -1.645 + 1.939*rho_b - 2.256*sand + 1.594*clay) - + ! Modified Debye expression, Dobson '85 - + wc_c = MAX(0.001, wc) ! to avoid dividing by zero - + ew = diel_watinf + (eps_sw0 - diel_watinf) / (1. - j * omega * tau_sw) & + j * sigma_eff / (omega * eps_0) * (rho_soil - rho_b) / (rho_soil * wc_c) - + END SELECT - + END SUBROUTINE DIEL_WAT - + ! ********************************************************************** - + SUBROUTINE MIRONOV( freq, mv, clayfrac, er_r ) - + ! Soil dielectric mixing model by Mironov et al IEEE TGRS 2009, doi:10.1109/TGRS.2008.2011631 ! ! 8 May 2023: Implementation taken from SMAP L2_SM_P retrieval model by qliu; clean-up by reichle - + IMPLICIT NONE REAL, INTENT(IN) :: freq ! microwave frequency [Hz] REAL, INTENT(IN) :: mv ! volumetric soil water content [m3/m3] REAL, INTENT(IN) :: clayfrac ! clay fraction [0-1] - + COMPLEX, INTENT(OUT) :: er_r ! complex dielectric constant of moist soil ! ------------------------------ @@ -953,15 +953,15 @@ SUBROUTINE MIRONOV( freq, mv, clayfrac, er_r ) REAL :: tmptauu, tmptauu2plus1, tmpdiffepsu REAL :: tmpreal, tmprealb, tmprealu - + ! -------------------------------------------------------------------------------------- f = freq ! Section IV C = clayfrac*100 ! Section VI - + !! Mironov's regression expressions based on Curtis, Dobson, and Hallikainen datasets !! - !! mvt : max bound water fraction + !! mvt : max bound water fraction !! !! eps(*) : dielectric constant (real part) and loss factor (imaginary part) !! n(*) : refractive index @@ -973,7 +973,7 @@ SUBROUTINE MIRONOV( freq, mv, clayfrac, er_r ) !! d: dry soil !! b: bound soil water (BSW) !! u: unbound (free) soil water (FSW) - + nd = 1.634 - 0.539e-2 * C + 0.2748e-4 * C**2 ! Eqn 17 kd = 0.03952 - 0.04038e-2 * C ! Eqn 18 mvt = 0.02863 + 0.30673e-2 * C ! Eqn 19 @@ -983,29 +983,29 @@ SUBROUTINE MIRONOV( freq, mv, clayfrac, er_r ) sigu = 0.3631 + 1.217e-2 * C ! Eqn 23 eps0u = 100. ! Eqn 24 tauu = 8.5e-12 ! Eqn 25 - + !! Debye relaxation equations for water as a function of frequency ! Eqn 16 - + tmp2PIf = 2.*MAPL_PI*f tmpeps0 = tmp2PIf*eps_0 - + tmptaub = tmp2PIf*taub tmptauu = tmp2PIf*tauu - + tmptaub2plus1 = 1. + tmptaub**2 tmptauu2plus1 = 1. + tmptauu**2 tmpdiffepsb = eps0b - diel_watinf tmpdiffepsu = eps0u - diel_watinf - - epsb_real = diel_watinf + tmpdiffepsb/tmptaub2plus1 + + epsb_real = diel_watinf + tmpdiffepsb/tmptaub2plus1 epsb_imag = tmpdiffepsb/tmptaub2plus1 * tmptaub + sigb/tmpeps0 - epsu_real = diel_watinf + tmpdiffepsu/tmptauu2plus1 + epsu_real = diel_watinf + tmpdiffepsu/tmptauu2plus1 epsu_imag = tmpdiffepsu/tmptauu2plus1 * tmptauu + sigu/tmpeps0 - - !! Refractive indices and normalized attenuation coefficients - + + !! Refractive indices and normalized attenuation coefficients + tmpreal = 1/sqrt(2.0) tmprealb = sqrt( epsb_real**2 + epsb_imag**2 ) @@ -1015,14 +1015,14 @@ SUBROUTINE MIRONOV( freq, mv, clayfrac, er_r ) kb = tmpreal * sqrt( tmprealb - epsb_real ) ! Eqn 15 nu = tmpreal * sqrt( tmprealu + epsu_real ) ! Eqn 14 ku = tmpreal * sqrt( tmprealu - epsu_real ) ! Eqn 15 - + IF (mv <= mvt) THEN - + nm = nd + (nb - 1.) * mv ! Eqn 12 km = kd + kb * mv ! Eqn 13 - + ELSE - + nm = nd + (nb - 1.) * mvt + (nu - 1.) * (mv - mvt) ! Eqn 12 km = kd + kb * mvt + ku * (mv - mvt) ! Eqn 13 @@ -1032,15 +1032,15 @@ SUBROUTINE MIRONOV( freq, mv, clayfrac, er_r ) er_r_real = nm**2 - km**2 ! Eqn 11 er_r_imag = 2. * nm * km ! Eqn 11 - + er_r = CMPLX(er_r_real,er_r_imag) - + END SUBROUTINE mironov - + ! ********************************************************************** - + end module mwRTM_routines - + ! ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #if 0 @@ -1048,15 +1048,15 @@ end module mwRTM_routines ! driver routines for testing program test_mwRTM_types - + use mwRTM_routines - + implicit none - + type(mwRTM_param_type) :: mwRTM_param - + mwRTM_param = -9999. - + write (*,*) mwRTM_param end program test_mwRTM_types diff --git a/GEOSlandassim_GridComp/mwRTM_types.F90 b/GEOSlandassim_GridComp/mwRTM_types.F90 index 5c88a94..5080c29 100644 --- a/GEOSlandassim_GridComp/mwRTM_types.F90 +++ b/GEOSlandassim_GridComp/mwRTM_types.F90 @@ -12,7 +12,7 @@ module mwRTM_types ! reichle, 21 Oct 2011 - added field "poros" to "mwRTM_param_type" ! ! -------------------------------------------------------------------------- - + use LDAS_ensdrv_globals, ONLY: & nodata_generic, & nodata_tol_generic, & @@ -23,22 +23,22 @@ module mwRTM_types LDAS_GENERIC_ERROR implicit none - + ! everything is private by default unless made public - + private - + public :: mwRTM_param_type public :: mwRTM_param_nodata_check - + public :: assignment (=) - + ! --------------------------------------------------------- - + ! model parameters - + type :: mwRTM_param_type - + ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! WARNING: When modifying this derived type make sure that the corresponding ! MPI STRUCTURE in module CLSM_ENSDRV_MPI is also updated, as are @@ -47,19 +47,19 @@ module mwRTM_types integer :: vegcls ! land cover/veg class (can differ from cat_param%vegcls ! ) integer :: soilcls ! soil class (can differ from cat_param%soilcls30! ) - + real :: sand ! sand fraction [0-1] real :: clay ! clay fraction [0-1] - + real :: poros ! porosity (can differ from cat_param%poros!) [m3/m3] ! parameters relating to soil mixing model - + real :: wang_wt ! Wang dielectric model: transition s.m. [m3/m3] real :: wang_wp ! Wang dielectric model: wilting point s.m. [m3/m3] - + ! parameters relating to emissivity of rough surface - + real :: rgh_hmin ! min roughness [dim-less] real :: rgh_hmax ! max roughness [dim-less] real :: rgh_wmin ! soil moisture for transition to hmax [m3/m3] @@ -67,24 +67,24 @@ module mwRTM_types real :: rgh_Nrh ! h-pol exponent for inc angle parameterization [dim-less] real :: rgh_Nrv ! v-pol exponent for inc angle parameterization [dim-less] real :: rgh_polmix ! polarization mixing parameter [dim-less] - + real :: omega ! single scattering albedo [dim-less] - ! parameters relating to vegetation opacity - + ! parameters relating to vegetation opacity + real :: bh ! veg b parameter (h-pol) (tau = b*VWC) [dim-less] real :: bv ! veg b parameter (v-pol) (tau = b*VWC) [dim-less] real :: lewt ! VWC = lewt*LAI [kg/m2] real :: vegopacity ! veg opacity = tau/cos(inc_angle) [dim-less] - + end type mwRTM_param_type - + ! --------------------------------------------------------- - + interface assignment (=) module procedure scalar2mwRTM_param end interface - + contains ! ********************************************************************** @@ -92,43 +92,43 @@ module mwRTM_types ! Subroutine io_mwRTM_param_type() reads and writes binary mwRTM files and is no longer used. ! ! The subroutine has been replaced by: - ! - Applications/LDAS_App/[..]/mwrtm_bin2nc4.F90 converts mwRTM files from binary to nc4 + ! - Components/GEOSldas_GridComp/GEOSldas_App/[..]/mwrtm_bin2nc4.F90 converts mwRTM files from binary to nc4 ! - get_mwrtm_param() in GEOS_LandAssimGridComp.F90 converts the internal state ! variables of the Land Assim GridComp into the mwRTM structure. ! ! reichle, 4 Aug 2020 -! subroutine io_mwRTM_param_type( action, unitnum, N_tile, mwp, N_catg, tile_id, d2g ) -! +! subroutine io_mwRTM_param_type( action, unitnum, N_tile, mwp, N_catg, tile_id, d2g ) +! ! ! read/write mwRTM_param for domain from/to file ! ! ! ! write: write mwRTM params for N_tile tiles in domain ! ! -! ! read: read mwRTM params for -! +! ! read: read mwRTM params for +! ! ! ! ! reichle, 1 Jun 2011 ! ! reichle, 21 Oct 2011 -- added "read" for mwRTM params ! ! reichle, 22 Oct 2012 -- added check for tile_id to mwRTM param input -! ! +! ! ! ! ------------------------------------------------------------------- -! +! ! implicit none -! +! ! character, intent(in) :: action -! +! ! integer, intent(in) :: unitnum -! +! ! integer, intent(in) :: N_tile ! =N_catd -! +! ! type(mwRTM_param_type), dimension(N_tile), intent(inout) :: mwp ! -! integer, optional, intent(in) :: N_catg +! integer, optional, intent(in) :: N_catg ! ! integer, optional, dimension(N_tile), intent(in) :: tile_id, d2g ! ! ! local variables -! +! ! integer :: n, N_tmp ! ! integer, dimension(:), allocatable :: tmpint @@ -140,125 +140,125 @@ module mwRTM_types ! ! ------------------------------------------------------------------ ! ! select case (action) -! +! ! case ('w','W') ! write mwp for all tiles in domain -! +! ! write (unitnum) N_tile -! +! ! write (unitnum) (mwp(n)%vegcls , n=1,N_tile) ! integer ! write (unitnum) (mwp(n)%soilcls , n=1,N_tile) ! integer -! -! write (unitnum) (mwp(n)%sand , n=1,N_tile) ! real -! write (unitnum) (mwp(n)%clay , n=1,N_tile) ! real -! -! write (unitnum) (mwp(n)%poros , n=1,N_tile) ! real -! -! write (unitnum) (mwp(n)%wang_wt , n=1,N_tile) ! real -! write (unitnum) (mwp(n)%wang_wp , n=1,N_tile) ! real -! -! write (unitnum) (mwp(n)%rgh_hmin , n=1,N_tile) ! real -! write (unitnum) (mwp(n)%rgh_hmax , n=1,N_tile) ! real -! write (unitnum) (mwp(n)%rgh_wmin , n=1,N_tile) ! real -! write (unitnum) (mwp(n)%rgh_wmax , n=1,N_tile) ! real -! write (unitnum) (mwp(n)%rgh_Nrh , n=1,N_tile) ! real -! write (unitnum) (mwp(n)%rgh_Nrv , n=1,N_tile) ! real -! write (unitnum) (mwp(n)%rgh_polmix, n=1,N_tile) ! real -! -! write (unitnum) (mwp(n)%omega , n=1,N_tile) ! real -! -! write (unitnum) (mwp(n)%bh , n=1,N_tile) ! real -! write (unitnum) (mwp(n)%bv , n=1,N_tile) ! real -! write (unitnum) (mwp(n)%lewt , n=1,N_tile) ! real -! +! +! write (unitnum) (mwp(n)%sand , n=1,N_tile) ! real +! write (unitnum) (mwp(n)%clay , n=1,N_tile) ! real +! +! write (unitnum) (mwp(n)%poros , n=1,N_tile) ! real +! +! write (unitnum) (mwp(n)%wang_wt , n=1,N_tile) ! real +! write (unitnum) (mwp(n)%wang_wp , n=1,N_tile) ! real +! +! write (unitnum) (mwp(n)%rgh_hmin , n=1,N_tile) ! real +! write (unitnum) (mwp(n)%rgh_hmax , n=1,N_tile) ! real +! write (unitnum) (mwp(n)%rgh_wmin , n=1,N_tile) ! real +! write (unitnum) (mwp(n)%rgh_wmax , n=1,N_tile) ! real +! write (unitnum) (mwp(n)%rgh_Nrh , n=1,N_tile) ! real +! write (unitnum) (mwp(n)%rgh_Nrv , n=1,N_tile) ! real +! write (unitnum) (mwp(n)%rgh_polmix, n=1,N_tile) ! real +! +! write (unitnum) (mwp(n)%omega , n=1,N_tile) ! real +! +! write (unitnum) (mwp(n)%bh , n=1,N_tile) ! real +! write (unitnum) (mwp(n)%bv , n=1,N_tile) ! real +! write (unitnum) (mwp(n)%lewt , n=1,N_tile) ! real +! ! case ('r','R') ! ! ! read the parameters for all global tiles (similar to read_land_parameters()) -! +! ! ! optional inputs N_catg and d2g must be present -! +! ! if ( (.not. present(N_catg )) .or. & ! (.not. present(tile_id)) .or. & ! (.not. present(d2g )) ) then ! err_msg = 'missing optional inputs N_catg, tile_id, d2g' ! call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) ! end if -! +! ! ! read how many tiles are in file and double-check against N_catg -! +! ! read (unitnum) N_tmp -! +! ! if (N_tmp .ne. N_catg) then ! err_msg = 'number of tiles in file .ne. N_catg' ! call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) ! end if -! +! ! ! allocate tmp vectors -! +! ! allocate(tmpint( N_catg)) ! allocate(tmpreal(N_catg)) -! +! ! ! read tile IDs (first record) -! +! ! read (unitnum) tmpint; ! ! ! make sure tile IDs match (works only for "SiB2_V2" and newer versions) -! +! ! if (any(tile_id/=tmpint(d2g(1:N_tile)))) then ! err_msg = 'mismatch of tile IDs for mwRTM_parameters' ! call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) ! end if -! +! ! ! read and subset from global tile space to domain -! +! ! read (unitnum) tmpint; mwp(1:N_tile)%vegcls = tmpint( d2g(1:N_tile)) ! read (unitnum) tmpint; mwp(1:N_tile)%soilcls = tmpint( d2g(1:N_tile)) -! +! ! read (unitnum) tmpreal; mwp(1:N_tile)%sand = tmpreal(d2g(1:N_tile)) ! read (unitnum) tmpreal; mwp(1:N_tile)%clay = tmpreal(d2g(1:N_tile)) -! +! ! read (unitnum) tmpreal; mwp(1:N_tile)%poros = tmpreal(d2g(1:N_tile)) -! -! read (unitnum) tmpreal; mwp(1:N_tile)%wang_wt = tmpreal(d2g(1:N_tile)) +! +! read (unitnum) tmpreal; mwp(1:N_tile)%wang_wt = tmpreal(d2g(1:N_tile)) ! read (unitnum) tmpreal; mwp(1:N_tile)%wang_wp = tmpreal(d2g(1:N_tile)) -! -! read (unitnum) tmpreal; mwp(1:N_tile)%rgh_hmin = tmpreal(d2g(1:N_tile)) +! +! read (unitnum) tmpreal; mwp(1:N_tile)%rgh_hmin = tmpreal(d2g(1:N_tile)) ! read (unitnum) tmpreal; mwp(1:N_tile)%rgh_hmax = tmpreal(d2g(1:N_tile)) ! read (unitnum) tmpreal; mwp(1:N_tile)%rgh_wmin = tmpreal(d2g(1:N_tile)) ! read (unitnum) tmpreal; mwp(1:N_tile)%rgh_wmax = tmpreal(d2g(1:N_tile)) ! read (unitnum) tmpreal; mwp(1:N_tile)%rgh_Nrh = tmpreal(d2g(1:N_tile)) ! read (unitnum) tmpreal; mwp(1:N_tile)%rgh_Nrv = tmpreal(d2g(1:N_tile)) -! read (unitnum) tmpreal; mwp(1:N_tile)%rgh_polmix = tmpreal(d2g(1:N_tile)) -! +! read (unitnum) tmpreal; mwp(1:N_tile)%rgh_polmix = tmpreal(d2g(1:N_tile)) +! ! read (unitnum) tmpreal; mwp(1:N_tile)%omega = tmpreal(d2g(1:N_tile)) -! +! ! read (unitnum) tmpreal; mwp(1:N_tile)%bh = tmpreal(d2g(1:N_tile)) ! read (unitnum) tmpreal; mwp(1:N_tile)%bv = tmpreal(d2g(1:N_tile)) ! read (unitnum) tmpreal; mwp(1:N_tile)%lewt = tmpreal(d2g(1:N_tile)) -! +! ! ! clean up ! ! deallocate(tmpreal) ! deallocate(tmpint) -! +! ! case default -! +! ! err_msg = 'unknown action ' // action ! call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) -! +! ! end select -! +! ! end subroutine io_mwRTM_param_type - + ! ************************************************************ - + subroutine scalar2mwRTM_param( mwRTM_param, scalar ) - + implicit none - + real, intent(in) :: scalar - + type(mwRTM_param_type), intent(out) :: mwRTM_param - + ! --------------------- mwRTM_param%vegcls = nint(scalar) @@ -281,41 +281,41 @@ subroutine scalar2mwRTM_param( mwRTM_param, scalar ) mwRTM_param%bv = scalar mwRTM_param%lewt = scalar mwRTM_param%vegopacity = scalar - + end subroutine scalar2mwRTM_param ! ************************************************************ - + subroutine mwRTM_param_nodata_check( mwp, mwp_nodata ) ! check microwave radiative transfer model parameters for no-data values ! ! if there is a no-data value in any required field, set "all" fields - ! within the corresponding group of parameters to no-data + ! within the corresponding group of parameters to no-data ! ! vegetation attenuation parameters can come from either of two sources: ! - static look-up table or calibrated parameters (bh, bv, lewt), to be combined ! with time-varying LAI ! - vegetation opacity (from file); varies with time ! - ! preprocessing of the mwRTM restart and vegopacity files must ensure that + ! preprocessing of the mwRTM restart and vegopacity files must ensure that ! either (bh,bv,lewt) or (vegopacity) is no-data ! ! - reichle, 13 July 2021 (revised for using vegopacity from file) - + implicit none - + type(mwRTM_param_type), intent(inout) :: mwp - + logical, intent( out), optional :: mwp_nodata - + ! local variables - + logical :: veg_atten_static_params_nodata, veg_params_nodata, other_params_nodata real :: realvegcls, realsoilcls - character(len=*), parameter :: Iam = 'mwRTM_param_nodata_check' + character(len=*), parameter :: Iam = 'mwRTM_param_nodata_check' character(len=400) :: err_msg ! ----------------------------------------------------------------------------- @@ -328,53 +328,53 @@ subroutine mwRTM_param_nodata_check( mwp, mwp_nodata ) veg_atten_static_params_nodata = & ( & - LDAS_is_nodata( mwp%bh ) .or. & + LDAS_is_nodata( mwp%bh ) .or. & LDAS_is_nodata( mwp%bv ) .or. & LDAS_is_nodata( mwp%lewt ) & ) - + if ( (.not. veg_atten_static_params_nodata) .and. (.not. LDAS_is_nodata( mwp%vegopacity )) ) then - - ! inconsistent mwRTM restart and vegopacity files: + + ! inconsistent mwRTM restart and vegopacity files: ! for a given tile, (bh, bv, lewt) from mwRTM restart and (vegopacity) from file must ! not both have good values - + err_msg = 'inconsistent mwRTM restart and vegopacity files: found good values for (bh,bv,lewt) *and* (vegopacity)' call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) - + end if ! return if *only* interested in checking no-data consistency for veg attenuation params - + if (.not. present(mwp_nodata)) return - ! - - - - - - - - - - - - - - - - - - - - - - + ! - - - - - - - - - - - - - - - - - - - - - - ! ! rest of subroutine only needs to run once - if ( veg_atten_static_params_nodata ) then - + if ( veg_atten_static_params_nodata ) then + ! make sure all related fields in this group are no-data if at least one is nodata - + mwp%bh = nodata_generic mwp%bv = nodata_generic mwp%lewt = nodata_generic - + end if - + ! veg_params_nodata = .true. if missing vegetation attenuation info altogether - + veg_params_nodata = & ( & veg_atten_static_params_nodata & .and. & - LDAS_is_nodata( mwp%vegopacity ) & + LDAS_is_nodata( mwp%vegopacity ) & ) ! ----------------------------------------------------------------------------- ! Group 2: Parameters for the rest of the tau-omega equations - + realvegcls = real(mwp%vegcls) realsoilcls = real(mwp%soilcls) @@ -395,12 +395,12 @@ subroutine mwRTM_param_nodata_check( mwp, mwp_nodata ) LDAS_is_nodata( mwp%rgh_Nrv ) .or. & LDAS_is_nodata( mwp%rgh_polmix ) .or. & LDAS_is_nodata( mwp%omega ) & - ) + ) if ( other_params_nodata ) then - + ! make sure all related fields in this group are no-data if at least one is nodata - + mwp%vegcls = nint(nodata_generic) mwp%soilcls = nint(nodata_generic) mwp%sand = nodata_generic @@ -416,27 +416,27 @@ subroutine mwRTM_param_nodata_check( mwp, mwp_nodata ) mwp%rgh_Nrv = nodata_generic mwp%rgh_polmix = nodata_generic mwp%omega = nodata_generic - + end if - + ! ----------------------------------------------------------------------------- - + ! need both groups for full tau-omega calculations: - + if ( veg_params_nodata .or. other_params_nodata ) then - + mwp_nodata = .true. - + else - + mwp_nodata = .false. - + end if - + end subroutine mwRTM_param_nodata_check - + end module mwRTM_types - + ! ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #if 0 @@ -444,15 +444,15 @@ end module mwRTM_types ! driver routines for testing program test_mwRTM_types - + use mwRTM_types - + implicit none - + type(mwRTM_param_type) :: mwRTM_param - + mwRTM_param = -9999. - + write (*,*) mwRTM_param mwRTM_param%lewt = 0.5 @@ -460,8 +460,8 @@ program test_mwRTM_types write (*,*) mwRTM_param call mwRTM_param_nodata_check(mwRTM_param) - - write (*,*) mwRTM_param + + write (*,*) mwRTM_param end program test_mwRTM_types diff --git a/GEOSldas_App/CMakeLists.txt b/GEOSldas_App/CMakeLists.txt new file mode 100644 index 0000000..1075229 --- /dev/null +++ b/GEOSldas_App/CMakeLists.txt @@ -0,0 +1,49 @@ + +ecbuild_add_executable ( + TARGET GEOSldas.x + SOURCES GEOSldas.F90 + LIBS GEOSldas_GridComp MAPL) + +ecbuild_add_executable ( + TARGET preprocess_ldas.x + SOURCES preprocess_ldas.F90 preprocess_ldas_routines.F90 + LIBS GEOSldas_GridComp MAPL) + +ecbuild_add_executable ( + TARGET tile_bin2nc4.x + SOURCES tile_bin2nc4.F90 + LIBS MAPL) + +ecbuild_add_executable ( + TARGET mwrtm_bin2nc4.x + SOURCES util/inputs/mwRTM_params/mwrtm_bin2nc4.F90 + LIBS GEOSlandassim_GridComp) + +set (scripts + process_hist.csh + remap_config_ldas.py + ens_forcing/average_ensemble_forcing.py + ens_forcing/ensemble_forc.py + ens_forcing/regrid_forc.csh + ens_forcing/enpert_forc.csh + util/config/rewind_GEOSldas.csh + ) + +install ( + PROGRAMS ${scripts} + DESTINATION bin + ) + +set(file ldas_setup) +configure_file(${file} ${file} @ONLY) +install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${file} DESTINATION bin) + +file(GLOB rc_files GEOSldas_*rc) +file(GLOB nml_files LDASsa_DEFAULT*nml) + +install( + FILES ${rc_files} ${nml_files} lenkf.j.template + DESTINATION etc + ) + +esma_add_subdirectories(util/inputs/ASCAT_sm_mask) diff --git a/GEOSldas_App/GEOSldas.F90 b/GEOSldas_App/GEOSldas.F90 new file mode 100644 index 0000000..5a09b3b --- /dev/null +++ b/GEOSldas_App/GEOSldas.F90 @@ -0,0 +1,34 @@ +#define I_AM_MAIN + +#include "MAPL_Generic.h" + +program LDAS_Main + + + ! !USES: + use MAPL + use GEOS_LDASGridCompMod, only: ROOT_SetServices => SetServices + + implicit none + + character(len=*), parameter :: Iam = "LDAS_Main" + type (MAPL_Cap) :: cap + type (MAPL_FargparseCLI) :: cli + type (MAPL_CapOptions) :: cap_options + integer :: status + +!EOP +!---------------------------------------------------------------------- +!BOC + + cli = MAPL_FargparseCLI() + cap_options = MAPL_CapOptions(cli) + cap_options%egress_file = 'EGRESS.ldas' + + cap = MAPL_Cap('LDAS', ROOT_SetServices, cap_options = cap_options) + call cap%run(_RC) + + !call MAPL_CAP(ROOT_SetServices, FinalFile='EGRESS.ldas', rc=status) + !VERIFY_(status) + +end program LDAS_Main diff --git a/GEOSldas_App/GEOSldas_CAP.rc b/GEOSldas_App/GEOSldas_CAP.rc new file mode 100644 index 0000000..4265093 --- /dev/null +++ b/GEOSldas_App/GEOSldas_CAP.rc @@ -0,0 +1,34 @@ +MAPLROOT_COMPNAME: GEOSldas + ROOT_NAME: GEOSldas + ROOT_CF: LDAS.rc + HIST_CF: HISTORY.rc + +# the date and segment will be set by run script lenk...j + +BEG_DATE: +END_DATE: +JOB_SGMT: +NUM_SGMT: + +HEARTBEAT_DT: 450 + +# Parameters for Cycled REPLAY Forecasts +# -------------------------------------- + +# default using shared memory +# The root of a node reads and shares + +USE_SHMEM: 1 + +BEG_REPDATE: YYYYMMDD +END_REPDATE: YYYYMMDD +FCST_SEGMENT: 00000000 + +#PERPETUAL_YEAR: YYYY +#PERPETUAL_MONTH: MM +#PERPETUAL_DAY: DD + +MAPL_ENABLE_TIMERS: YES +MAPL_ENABLE_MEMUTILS: NO +PRINTSPEC: 0 # (0: OFF, 1: IMPORT & EXPORT, 2: IMPORT, 3: EXPORT) +PRINTRC: 1 diff --git a/GEOSldas_App/GEOSldas_ExtData.rc b/GEOSldas_App/GEOSldas_ExtData.rc new file mode 100644 index 0000000..e69de29 diff --git a/GEOSldas_App/GEOSldas_HIST.rc b/GEOSldas_App/GEOSldas_HIST.rc new file mode 100644 index 0000000..adb5265 --- /dev/null +++ b/GEOSldas_App/GEOSldas_HIST.rc @@ -0,0 +1,480 @@ +# Sample HISTORY.rc file for GEOSldas +# +# This HISTORY template is edited by "ldas_setup" via "process_hist.csh". +# The strings '#ASSIM', '#EASE', and '#CUBE' are *not* linked to MAPL HISTORY +# functionality. For example, the line +# "#CUBE 'tavg24_2d_lnd_Nx'" +# does *not* mean that the 'lnd' output will be on a cube-sphere grid. + +#CUBE VERSION: 1 + +# Must edit 'EXPID' manually if HISTORY file is re-used without going +# through "ldas_setup". +# +EXPID: GEOSldas_expid + +COLLECTIONS: +#EASE 'tavg24_1d_lfs_Nt' +#CUBE 'tavg24_2d_lfs_Nx' +#EASE 'tavg24_1d_lnd_Nt' +#CUBE 'tavg24_2d_lnd_Nx' +#ASSIM 'SMAP_L4_SM_gph' +# 'inst1_1d_lnr_Nt' +# 'catch_progn_incr' +# 'inst3_1d_lndfcstana_Nt' +# 'inst3_2d_lndfcstana_Nx' + :: + +#CUBE GRID_LABELS: PC720x361-DC + +#CUBE :: + +#CUBE PC720x361-DC.GRID_TYPE: LatLon +#CUBE PC720x361-DC.IM_WORLD: 720 +#CUBE PC720x361-DC.JM_WORLD: 361 +#CUBE PC720x361-DC.POLE: PC +#CUBE PC720x361-DC.DATELINE: DC +#CUBE PC720x361-DC.LM: 1 + +# Detailed definition of the collections listed above +# +# Bit shaving: Retain only scientifically meaningful precision and modify meaningless +# bits to facilitate better compression in post-processing; +# *.nbits specifies the number of bits retained; +# For example, many MERRA-2 and FP products use nbits=12 and nbits=10, resp. +# IMPORTANT: To realize the disk space savings, bit-shaved output must be compressed +# after the GEOSldas simulation has finished. Binary files can be compressed +# with "gzip"; nc4 files can be compressed using the "compress_bit-shaved_nc4.sh" +# utility script. + + tavg24_1d_lfs_Nt.descr: 'Tile-space,Daily,Time-Averaged,Single-Level,Assimilation,Land Surface Forcings and States', + tavg24_1d_lfs_Nt.nbits: 12, + tavg24_1d_lfs_Nt.template: '%y4%m2%d2_%h2%n2z.bin', + tavg24_1d_lfs_Nt.archive: '%c/Y%y4', + tavg24_1d_lfs_Nt.mode: 'time-averaged', + tavg24_1d_lfs_Nt.frequency: 240000, + tavg24_1d_lfs_Nt.ref_time: 000000, + tavg24_1d_lfs_Nt.fields:'Tair' , 'METFORCE' , + 'Qair' , 'METFORCE' , + 'LWdown' , 'METFORCE' , + 'SWdown' , 'METFORCE' , + 'Wind' , 'METFORCE' , + 'Psurf' , 'METFORCE' , + 'Rainf_C' , 'METFORCE' , + 'Rainf' , 'METFORCE' , + 'Snowf' , 'METFORCE' , + 'RainfSnowf' , 'METFORCE' , + 'RefH' , 'METFORCE' , + 'CATDEF' , 'GridComp' , + 'RZEXC' , 'GridComp' , + 'SRFEXC' , 'GridComp' , + 'WESNN1' , 'GridComp' , + 'WESNN2' , 'GridComp' , + 'WESNN3' , 'GridComp' , + 'HLWUP' , 'GridComp' , + :: + + tavg24_2d_lfs_Nx.descr: '2d,Daily,Time-Averaged,Single-Level,Assimilation,Land Surface Forcings and States', + tavg24_2d_lfs_Nx.nbits: 12, + tavg24_2d_lfs_Nx.template: '%y4%m2%d2_%h2%n2z.nc4', + tavg24_2d_lfs_Nx.archive: '%c/Y%y4', + tavg24_2d_lfs_Nx.mode: 'time-averaged', + tavg24_2d_lfs_Nx.frequency: 240000, + tavg24_2d_lfs_Nx.ref_time: 000000, + tavg24_2d_lfs_Nx.format: 'CFIO', + tavg24_2d_lfs_Nx.regrid_exch: '../input/tile.data', + tavg24_2d_lfs_Nx.regrid_name: 'GRIDNAME', + tavg24_2d_lfs_Nx.grid_label: PC720x361-DC, + tavg24_2d_lfs_Nx.deflate: 2, + tavg24_2d_lfs_Nx.fields:'Tair' , 'METFORCE' , + 'Qair' , 'METFORCE' , + 'LWdown' , 'METFORCE' , + 'SWdown' , 'METFORCE' , + 'Wind' , 'METFORCE' , + 'Psurf' , 'METFORCE' , + 'Rainf_C' , 'METFORCE' , + 'Rainf' , 'METFORCE' , + 'Snowf' , 'METFORCE' , + 'RainfSnowf' , 'METFORCE' , + 'RefH' , 'METFORCE' , + 'CATDEF' , 'GridComp' , + 'RZEXC' , 'GridComp' , + 'SRFEXC' , 'GridComp' , + 'WESNN1' , 'GridComp' , + 'WESNN2' , 'GridComp' , + 'WESNN3' , 'GridComp' , + 'HLWUP' , 'GridComp' , + :: + + tavg24_1d_lnd_Nt.descr: 'Tile-space,Daily,Time-Averaged,Single-Level,Assimilation,Land Surface Diagnostics', + tavg24_1d_lnd_Nt.nbits: 12, + tavg24_1d_lnd_Nt.template: '%y4%m2%d2_%h2%n2z.bin', + tavg24_1d_lnd_Nt.mode: 'time-averaged', + tavg24_1d_lnd_Nt.frequency: 240000, + tavg24_1d_lnd_Nt.ref_time: 000000, + tavg24_1d_lnd_Nt.fields: 'WET3' , 'GridComp' , 'GWETPROF' , + 'WET2' , 'GridComp' , 'GWETROOT' , + 'WET1' , 'GridComp' , 'GWETTOP' , + 'WCPR' , 'GridComp' , 'PRMC' , + 'WCRZ' , 'GridComp' , 'RZMC' , + 'WCSF' , 'GridComp' , 'SFMC' , + 'TPSNOW' , 'GridComp' , + 'TPUNST' , 'GridComp' , 'TUNST' , + 'TPSAT' , 'GridComp' , 'TSAT' , + 'TPWLT' , 'GridComp' , 'TWLT' , + 'TPSURF' , 'GridComp' , 'TSURF' , + 'GRN' , 'VEGDYN' , + 'LAI' , 'VEGDYN' , + 'TP1' , 'GridComp' , + 'TP2' , 'GridComp' , + 'TP3' , 'GridComp' , + 'TP4' , 'GridComp' , + 'TP5' , 'GridComp' , + 'TP6' , 'GridComp' , + 'PRLAND' , 'GridComp' , 'PRECTOTLAND' , + 'SNOLAND' , 'GridComp' , 'PRECSNOLAND' , + 'TSLAND' , 'GridComp' , 'SNOMAS' , + 'SNOWDP' , 'GridComp' , 'SNODP' , + 'EVPSOI' , 'GridComp' , 'EVPSOIL' , + 'EVPVEG' , 'GridComp' , 'EVPTRNS' , + 'EVPINT' , 'GridComp' , 'EVPINTR' , + 'EVPICE' , 'GridComp' , 'EVPSBLN' , + 'RUNSURF' , 'GridComp' , 'RUNOFF' , + 'BASEFLOW' , 'GridComp' , + 'SMLAND' , 'GridComp' , + 'QINFIL' , 'GridComp' , + 'FRUST' , 'GridComp' , 'FRUNST' , + 'FRSAT' , 'GridComp' , + 'ASNOW' , 'GridComp' , 'FRSNO' , + 'FRWLT' , 'GridComp' , + 'DFPARLAND' , 'GridComp' , 'PARDFLAND' , + 'DRPARLAND' , 'GridComp' , 'PARDRLAND' , + 'SHLAND' , 'GridComp' , + 'LHLAND' , 'GridComp' , + 'EVLAND' , 'GridComp' , + 'LWLAND' , 'GridComp' , + 'SWLAND' , 'GridComp' , + 'GHLAND' , 'GridComp' , + 'TWLAND' , 'GridComp' , + 'TELAND' , 'GridComp' , + 'DWLAND' , 'GridComp' , 'WCHANGE' , + 'DHLAND' , 'GridComp' , 'ECHANGE' , + 'SPLAND' , 'GridComp' , + 'SPWATR' , 'GridComp' , + 'SPSNOW' , 'GridComp' , + 'PEATCLSM_WATERLEVEL', 'GridComp' , + 'PEATCLSM_FSWCHANGE' , 'GridComp' , +>>>HIST_AEROSOL<<< 'RMELTDU001' , 'GridComp' , +>>>HIST_AEROSOL<<< 'RMELTDU002' , 'GridComp' , +>>>HIST_AEROSOL<<< 'RMELTDU003' , 'GridComp' , +>>>HIST_AEROSOL<<< 'RMELTDU004' , 'GridComp' , +>>>HIST_AEROSOL<<< 'RMELTDU005' , 'GridComp' , +>>>HIST_AEROSOL<<< 'RMELTBC001' , 'GridComp' , +>>>HIST_AEROSOL<<< 'RMELTBC002' , 'GridComp' , +>>>HIST_AEROSOL<<< 'RMELTOC001' , 'GridComp' , +>>>HIST_AEROSOL<<< 'RMELTOC002' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNLAI' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNTLAI' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNSAI' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNTOTC' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNVEGC' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNROOT' , 'GridComp' , +>>>HIST_CATCHCNCLM45<<< 'CNFROOTC' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNNPP' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNGPP' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNSR' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNNEE' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNXSMR' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNADD' , 'GridComp' , +>>>HIST_CATCHCN<<< 'PARABS' , 'GridComp' , +>>>HIST_CATCHCN<<< 'PARINC' , 'GridComp' , +>>>HIST_CATCHCN<<< 'SCSAT' , 'GridComp' , +>>>HIST_CATCHCN<<< 'SCUNS' , 'GridComp' , +>>>HIST_CATCHCN<<< 'BTRANT' , 'GridComp' , 'BTRAN' , +>>>HIST_CATCHCN<<< 'SIF' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNLOSS' , 'GridComp' , 'CLOSS' , +>>>HIST_CATCHCN<<< 'CNBURN' , 'GridComp' , 'BURN' , +>>>HIST_CATCHCN<<< 'CNFSEL' , 'GridComp' , 'FSEL' , +>>>HIST_CATCHCN<<< 'EVPSNO' , 'GridComp' , +>>>HIST_CATCHCN<<< 'GHTSKIN' , 'GridComp' , +>>>HIST_CATCHCN<<< 'WAT10CM' , 'GridComp' , +>>>HIST_CATCHCN<<< 'WATSOI' , 'GridComp' , +>>>HIST_CATCHCN<<< 'ICESOI' , 'GridComp' , +>>>HIST_IRRIG<<< 'IRRIGRATE' , 'GridComp' , + :: + + tavg24_2d_lnd_Nx.descr: '2d,Daily,Time-Averaged,Single-Level,Assimilation,Land Surface Diagnostics', + tavg24_2d_lnd_Nx.nbits: 12, + tavg24_2d_lnd_Nx.template: '%y4%m2%d2_%h2%n2z.nc4', + tavg24_2d_lnd_Nx.mode: 'time-averaged', + tavg24_2d_lnd_Nx.frequency: 240000, + tavg24_2d_lnd_Nx.ref_time: 000000, + tavg24_2d_lnd_Nx.format: 'CFIO', + tavg24_2d_lnd_Nx.regrid_exch: '../input/tile.data' + tavg24_2d_lnd_Nx.regrid_name: 'GRIDNAME' + tavg24_2d_lnd_Nx.grid_label: PC720x361-DC + tavg24_2d_lnd_Nx.deflate: 2, + tavg24_2d_lnd_Nx.fields: 'WET3' , 'GridComp' , 'GWETPROF' , + 'WET2' , 'GridComp' , 'GWETROOT' , + 'WET1' , 'GridComp' , 'GWETTOP' , + 'WCPR' , 'GridComp' , 'PRMC' , + 'WCRZ' , 'GridComp' , 'RZMC' , + 'WCSF' , 'GridComp' , 'SFMC' , + 'TPSNOW' , 'GridComp' , + 'TPUNST' , 'GridComp' , 'TUNST' , + 'TPSAT' , 'GridComp' , 'TSAT' , + 'TPWLT' , 'GridComp' , 'TWLT' , + 'TPSURF' , 'GridComp' , 'TSURF' , + 'GRN' , 'VEGDYN' , + 'LAI' , 'VEGDYN' , + 'TP1' , 'GridComp' , + 'TP2' , 'GridComp' , + 'TP3' , 'GridComp' , + 'TP4' , 'GridComp' , + 'TP5' , 'GridComp' , + 'TP6' , 'GridComp' , + 'PRLAND' , 'GridComp' , 'PRECTOTLAND' , + 'SNOLAND' , 'GridComp' , 'PRECSNOLAND' , + 'TSLAND' , 'GridComp' , 'SNOMAS' , + 'SNOWDP' , 'GridComp' , 'SNODP' , + 'EVPSOI' , 'GridComp' , 'EVPSOIL' , + 'EVPVEG' , 'GridComp' , 'EVPTRNS' , + 'EVPINT' , 'GridComp' , 'EVPINTR' , + 'EVPICE' , 'GridComp' , 'EVPSBLN' , + 'RUNSURF' , 'GridComp' , 'RUNOFF' , + 'BASEFLOW' , 'GridComp' , + 'SMLAND' , 'GridComp' , + 'QINFIL' , 'GridComp' , + 'FRUST' , 'GridComp' , 'FRUNST' , + 'FRSAT' , 'GridComp' , + 'ASNOW' , 'GridComp' , 'FRSNO' , + 'FRWLT' , 'GridComp' , + 'DFPARLAND' , 'GridComp' , 'PARDFLAND' , + 'DRPARLAND' , 'GridComp' , 'PARDRLAND' , + 'SHLAND' , 'GridComp' , + 'LHLAND' , 'GridComp' , + 'EVLAND' , 'GridComp' , + 'LWLAND' , 'GridComp' , + 'SWLAND' , 'GridComp' , + 'GHLAND' , 'GridComp' , + 'TWLAND' , 'GridComp' , + 'TELAND' , 'GridComp' , + 'DWLAND' , 'GridComp' , 'WCHANGE' , + 'DHLAND' , 'GridComp' , 'ECHANGE' , + 'SPLAND' , 'GridComp' , + 'SPWATR' , 'GridComp' , + 'SPSNOW' , 'GridComp' , + 'PEATCLSM_WATERLEVEL', 'GridComp' , + 'PEATCLSM_FSWCHANGE' , 'GridComp' , +>>>HIST_AEROSOL<<< 'RMELTDU001' , 'GridComp' , +>>>HIST_AEROSOL<<< 'RMELTDU002' , 'GridComp' , +>>>HIST_AEROSOL<<< 'RMELTDU003' , 'GridComp' , +>>>HIST_AEROSOL<<< 'RMELTDU004' , 'GridComp' , +>>>HIST_AEROSOL<<< 'RMELTDU005' , 'GridComp' , +>>>HIST_AEROSOL<<< 'RMELTBC001' , 'GridComp' , +>>>HIST_AEROSOL<<< 'RMELTBC002' , 'GridComp' , +>>>HIST_AEROSOL<<< 'RMELTOC001' , 'GridComp' , +>>>HIST_AEROSOL<<< 'RMELTOC002' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNLAI' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNTLAI' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNSAI' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNTOTC' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNVEGC' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNROOT' , 'GridComp' , +>>>HIST_CATCHCNCLM45<<< 'CNFROOTC' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNNPP' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNGPP' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNSR' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNNEE' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNXSMR' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNADD' , 'GridComp' , +>>>HIST_CATCHCN<<< 'PARABS' , 'GridComp' , +>>>HIST_CATCHCN<<< 'PARINC' , 'GridComp' , +>>>HIST_CATCHCN<<< 'SCSAT' , 'GridComp' , +>>>HIST_CATCHCN<<< 'SCUNS' , 'GridComp' , +>>>HIST_CATCHCN<<< 'BTRANT' , 'GridComp' , 'BTRAN' , +>>>HIST_CATCHCN<<< 'SIF' , 'GridComp' , +>>>HIST_CATCHCN<<< 'CNLOSS' , 'GridComp' , 'CLOSS' , +>>>HIST_CATCHCN<<< 'CNBURN' , 'GridComp' , 'BURN' , +>>>HIST_CATCHCN<<< 'CNFSEL' , 'GridComp' , 'FSEL' , +>>>HIST_CATCHCN<<< 'EVPSNO' , 'GridComp' , +>>>HIST_CATCHCN<<< 'GHTSKIN' , 'GridComp' , +>>>HIST_CATCHCN<<< 'WAT10CM' , 'GridComp' , +>>>HIST_CATCHCN<<< 'WATSOI' , 'GridComp' , +>>>HIST_CATCHCN<<< 'ICESOI' , 'GridComp' , +>>>HIST_IRRIG<<< 'IRRIGRATE' , 'GridComp' , + :: + + SMAP_L4_SM_gph.descr: 'Tile-space,3-Hourly,Time-Averaged,Single-Level,Assimilation,SMAP L4_SM Land Geophysical Diagnostics', + SMAP_L4_SM_gph.nbits: 12, + SMAP_L4_SM_gph.template: '%y4%m2%d2_%h2%n2z.bin', + SMAP_L4_SM_gph.mode: 'time-averaged', + SMAP_L4_SM_gph.frequency: 030000, + SMAP_L4_SM_gph.ref_time: 000000, + SMAP_L4_SM_gph.fields: 'WCSF' , 'ENSAVG' , 'sm_surface' , + 'WCRZ' , 'ENSAVG' , 'sm_rootzone' , + 'WCPR' , 'ENSAVG' , 'sm_profile' , + 'WET1' , 'ENSAVG' , 'sm_surface_wetness' , + 'WET2' , 'ENSAVG' , 'sm_rootzone_wetness' , + 'WET3' , 'ENSAVG' , 'sm_profile_wetness' , + 'TPSURF' , 'ENSAVG' , 'surface_temp' , + 'TSOIL1TILE' , 'ENSAVG' , 'soil_temp_layer1' , + 'TSOIL2TILE' , 'ENSAVG' , 'soil_temp_layer2' , + 'TSOIL3TILE' , 'ENSAVG' , 'soil_temp_layer3' , + 'TSOIL4TILE' , 'ENSAVG' , 'soil_temp_layer4' , + 'TSOIL5TILE' , 'ENSAVG' , 'soil_temp_layer5' , + 'TSOIL6TILE' , 'ENSAVG' , 'soil_temp_layer6' , + 'SNOWMASS' , 'ENSAVG' , 'snow_mass' , + 'SNOWDP' , 'ENSAVG' , 'snow_depth' , + 'EVLAND' , 'ENSAVG' , 'land_evapotranspiration_flux' , + 'RUNSURF' , 'ENSAVG' , 'overland_runoff_flux' , + 'BASEFLOW' , 'ENSAVG' , 'baseflow_flux' , + 'SMLAND' , 'ENSAVG' , 'snow_melt_flux' , + 'QINFIL' , 'ENSAVG' , 'soil_water_infiltration_flux' , + 'FRSAT' , 'ENSAVG' , 'land_fraction_saturated' , + 'FRUST' , 'ENSAVG' , 'land_fraction_unsaturated' , + 'FRWLT' , 'ENSAVG' , 'land_fraction_wilting' , + 'ASNOW' , 'ENSAVG' , 'land_fraction_snow_covered' , + 'SHLAND' , 'ENSAVG' , 'heat_flux_sensible' , + 'LHLAND' , 'ENSAVG' , 'heat_flux_latent' , + 'GHLAND' , 'ENSAVG' , 'heat_flux_ground' , + 'SWLAND' , 'ENSAVG' , 'net_downward_shortwave_flux' , + 'LWLAND' , 'ENSAVG' , 'net_downward_longwave_flux' , + 'SWDOWNLAND' , 'ENSAVG' , 'radiation_shortwave_downward_flux' , + 'LWDNSRF' , 'ENSAVG' , 'radiation_longwave_absorbed_flux' , + 'RainfSnowf' , 'ENSAVG' , 'precipitation_total_surface_flux' , + 'SNO' , 'ENSAVG' , 'snowfall_surface_flux' , + 'PS' , 'ENSAVG' , 'surface_pressure' , + 'DZ' , 'ENSAVG' , 'height_lowatmmodlay' , + 'TA' , 'ENSAVG' , 'temp_lowatmmodlay' , + 'QA' , 'ENSAVG' , 'specific_humidity_lowatmmodlay' , + 'UU' , 'ENSAVG' , 'windspeed_lowatmmodlay' , + 'GRN' , 'VEGDYN' , 'vegetation_greenness_fraction' , + 'LAI' , 'VEGDYN' , 'leaf_area_index' , + 'PEATCLSM_WATERLEVEL', 'ENSAVG' , 'depth_to_water_table_from_surface_in_peat' , + 'PEATCLSM_FSWCHANGE' , 'ENSAVG' , 'free_surface_water_on_peat_flux' , + 'MWRTM_VEGOPACITY' , 'LANDASSIM' , 'mwrtm_vegopacity', , + :: + + inst1_1d_lnr_Nt.descr: 'Tile-space,1-Hourly,Instantaneous,Single-Level,Assimilation,Land Nature Run Diagnostics', + inst1_1d_lnr_Nt.nbits: 12, + inst1_1d_lnr_Nt.template: '%y4%m2%d2_%h2%n2z.bin' , + inst1_1d_lnr_Nt.mode: 'instantaneous' , + inst1_1d_lnr_Nt.frequency: 010000 , + inst1_1d_lnr_Nt.ref_time: 000000, + inst1_1d_lnr_Nt.fields: 'TPSURF' , 'ENSAVG' , 'surface_temp' , + 'TSOIL1TILE' , 'ENSAVG' , 'soil_temp_layer1' , + 'TPSNOW' , 'ENSAVG' , 'snow_temp_layer1' , + 'TB_LAND_1410MHZ_40DEG_HPOL' , 'LANDASSIM' , 'tb_h' , + 'TB_LAND_1410MHZ_40DEG_VPOL' , 'LANDASSIM' , 'tb_v' , + :: + +# For catch_progn_incr, *.frequency and *.ref_time must be consistent with the LDAS.rc resource +# parameters LANDASSIM_DT and LANDASSIM_T0. +# By default, no bit shaving for increments output. + + catch_progn_incr.descr: 'Tile-space,3-Hourly,Instantaneous,Single-Level,Assimilation,Ensemble-Average Land Prognostics Increments', + catch_progn_incr.template: '%y4%m2%d2_%h2%n2z.bin', + catch_progn_incr.mode: 'instantaneous', + catch_progn_incr.frequency: 030000, + catch_progn_incr.ref_time: 000000, + catch_progn_incr.fields: 'TCFSAT_INCR' , 'LANDASSIM' , + 'TCFTRN_INCR' , 'LANDASSIM' , + 'TCFWLT_INCR' , 'LANDASSIM' , + 'QCFSAT_INCR' , 'LANDASSIM' , + 'QCFTRN_INCR' , 'LANDASSIM' , + 'QCFWLT_INCR' , 'LANDASSIM' , + 'CAPAC_INCR' , 'LANDASSIM' , + 'CATDEF_INCR' , 'LANDASSIM' , + 'RZEXC_INCR' , 'LANDASSIM' , + 'SRFEXC_INCR' , 'LANDASSIM' , + 'GHTCNT1_INCR' , 'LANDASSIM' , + 'GHTCNT2_INCR' , 'LANDASSIM' , + 'GHTCNT3_INCR' , 'LANDASSIM' , + 'GHTCNT4_INCR' , 'LANDASSIM' , + 'GHTCNT5_INCR' , 'LANDASSIM' , + 'GHTCNT6_INCR' , 'LANDASSIM' , + 'WESNN1_INCR' , 'LANDASSIM' , + 'WESNN2_INCR' , 'LANDASSIM' , + 'WESNN3_INCR' , 'LANDASSIM' , + 'HTSNNN1_INCR' , 'LANDASSIM' , + 'HTSNNN2_INCR' , 'LANDASSIM' , + 'HTSNNN3_INCR' , 'LANDASSIM' , + 'SNDZN1_INCR' , 'LANDASSIM' , + 'SNDZN2_INCR' , 'LANDASSIM' , + 'SNDZN3_INCR' , 'LANDASSIM' , + :: + +# For lndfcstana, *.frequency and *.ref_time must be consistent with the LDAS.rc resource +# parameters LANDASSIM_DT and LANDASSIM_T0. +# Output of the ensemble std-dev (ENSSTD) requires simultaneous output of the ensemble mean. If the +# ensemble mean is not written out for a given field, that field's ENSSTD output will be MAPL_UNDEF. + + inst3_1d_lndfcstana_Nt.descr: 'Tile-space,3-Hourly,Instantaneous,Single-Level,Assimilation,Ensemble Land Forecast and Analysis Diagnostics', + inst3_1d_lndfcstana_Nt.nbits: 12, + inst3_1d_lndfcstana_Nt.template: '%y4%m2%d2_%h2%n2z.bin', + inst3_1d_lndfcstana_Nt.mode: 'instantaneous', + inst3_1d_lndfcstana_Nt.frequency: 030000, + inst3_1d_lndfcstana_Nt.ref_time: 000000, + inst3_1d_lndfcstana_Nt.fields: 'WCSF' , 'ENSAVG' , 'SFMC_FCST' , + 'WCRZ' , 'ENSAVG' , 'RZMC_FCST' , + 'WCPR' , 'ENSAVG' , 'PRMC_FCST' , + 'TPSURF' , 'ENSAVG' , 'TSURF_FCST' , + 'TSOIL1TILE' , 'ENSAVG' , 'TSOIL1_FCST' , + 'WCSF_ENSSTD' , 'ENSAVG' , 'SFMC_FCST_ENSSTD' , + 'WCRZ_ENSSTD' , 'ENSAVG' , 'RZMC_FCST_ENSSTD' , + 'WCPR_ENSSTD' , 'ENSAVG' , 'PRMC_FCST_ENSSTD' , + 'TPSURF_ENSSTD' , 'ENSAVG' , 'TSURF_FCST_ENSSTD' , + 'TSOIL1TILE_ENSSTD' , 'ENSAVG' , 'TSOIL1_FCST_ENSSTD' , + 'WCSF_ANA' , 'LANDASSIM' , 'SFMC_ANA' , + 'WCRZ_ANA' , 'LANDASSIM' , 'RZMC_ANA' , + 'WCPR_ANA' , 'LANDASSIM' , 'PRMC_ANA' , + 'TPSURF_ANA' , 'LANDASSIM' , 'TSURF_ANA' , + 'TSOIL1_ANA' , 'LANDASSIM' , 'TSOIL1_ANA' , + 'WCSF_ANA_ENSSTD' , 'LANDASSIM' , 'SFMC_ANA_ENSSTD' , + 'WCRZ_ANA_ENSSTD' , 'LANDASSIM' , 'RZMC_ANA_ENSSTD' , + 'WCPR_ANA_ENSSTD' , 'LANDASSIM' , 'PRMC_ANA_ENSSTD' , + 'TPSURF_ANA_ENSSTD' , 'LANDASSIM' , 'TSURF_ANA_ENSSTD' , + 'TSOIL1_ANA_ENSSTD' , 'LANDASSIM' , 'TSOIL1_ANA_ENSSTD' + :: + +# For lndfcstana, *.frequency and *.ref_time must be consistent with the LDAS.rc resource +# parameters LANDASSIM_DT and LANDASSIM_T0. +# Output of the ensemble std-dev (ENSSTD) requires simultaneous output of the ensemble mean. If the +# ensemble mean is not written out for a given field, that field's ENSSTD output will be MAPL_UNDEF. + + inst3_2d_lndfcstana_Nx.descr: '2d,3-Hourly,Instantaneous,Single-Level,Assimilation,Ensemble Land Forecast and Analysis Diagnostics', + inst3_2d_lndfcstana_Nx.nbits: 12, + inst3_2d_lndfcstana_Nx.template: '%y4%m2%d2_%h2%n2z.nc4', + inst3_2d_lndfcstana_Nx.archive: '%c/Y%y4', + inst3_2d_lndfcstana_Nx.mode: 'instantaneous', + inst3_2d_lndfcstana_Nx.frequency: 030000, + inst3_2d_lndfcstana_Nx.ref_time: 000000, + inst3_2d_lndfcstana_Nx.format: 'CFIO', + inst3_2d_lndfcstana_Nx.regrid_exch: '../input/tile.data', + inst3_2d_lndfcstana_Nx.regrid_name: 'GRIDNAME', + inst3_2d_lndfcstana_Nx.grid_label: PC720x361-DC, + inst3_2d_lndfcstana_Nx.deflate: 2, + inst3_2d_lndfcstana_Nx.fields: 'WCSF' , 'ENSAVG' , 'SFMC_FCST' , + 'WCRZ' , 'ENSAVG' , 'RZMC_FCST' , + 'WCPR' , 'ENSAVG' , 'PRMC_FCST' , + 'TPSURF' , 'ENSAVG' , 'TSURF_FCST' , + 'TSOIL1TILE' , 'ENSAVG' , 'TSOIL1_FCST' , + 'WCSF_ENSSTD' , 'ENSAVG' , 'SFMC_FCST_ENSSTD' , + 'WCRZ_ENSSTD' , 'ENSAVG' , 'RZMC_FCST_ENSSTD' , + 'WCPR_ENSSTD' , 'ENSAVG' , 'PRMC_FCST_ENSSTD' , + 'TPSURF_ENSSTD' , 'ENSAVG' , 'TSURF_FCST_ENSSTD' , + 'TSOIL1TILE_ENSSTD' , 'ENSAVG' , 'TSOIL1_FCST_ENSSTD' , + 'WCSF_ANA' , 'LANDASSIM' , 'SFMC_ANA' , + 'WCRZ_ANA' , 'LANDASSIM' , 'RZMC_ANA' , + 'WCPR_ANA' , 'LANDASSIM' , 'PRMC_ANA' , + 'TPSURF_ANA' , 'LANDASSIM' , 'TSURF_ANA' , + 'TSOIL1_ANA' , 'LANDASSIM' , 'TSOIL1_ANA' , + 'WCSF_ANA_ENSSTD' , 'LANDASSIM' , 'SFMC_ANA_ENSSTD' , + 'WCRZ_ANA_ENSSTD' , 'LANDASSIM' , 'RZMC_ANA_ENSSTD' , + 'WCPR_ANA_ENSSTD' , 'LANDASSIM' , 'PRMC_ANA_ENSSTD' , + 'TPSURF_ANA_ENSSTD' , 'LANDASSIM' , 'TSURF_ANA_ENSSTD' , + 'TSOIL1_ANA_ENSSTD' , 'LANDASSIM' , 'TSOIL1_ANA_ENSSTD' + :: + +# ========================== EOF ============================================================== diff --git a/GEOSldas_App/GEOSldas_LDAS.rc b/GEOSldas_App/GEOSldas_LDAS.rc new file mode 100644 index 0000000..df92fdb --- /dev/null +++ b/GEOSldas_App/GEOSldas_LDAS.rc @@ -0,0 +1,259 @@ +#################################################################################### +# # +# GEOSldas Resource Parameters # +# # +# Values below override the hardcoded default values # +# in *.F90 calls to MAPL_GetResource(). # +# # +# Users can further override the values below by # +# editing the "exeinp" file during ldas setup. # +# # +#################################################################################### + + +# ---- Using Catchment[CN] offline? +# +# 0: DEFAULT for GCM, (WW,CH,CM,CQ,FR) are required in Catchment restart file +# 1: DEFAULT for GEOSldas, (WW,CH,CM,CQ,FR) are optional +# 2: Option for GEOSldas, (WW,CH,CM,CQ,FR) are optional for input restart but will be in output +# restart; select when using GEOSldas to create restarts for the GCM. +# +CATCHMENT_OFFLINE: 1 + + +# ---- Catchment[CN] spinup mode +# +# 0 : No spinup (default) +# 1 : remove snow every Aug 1 in N. Hemisphere and every Feb 1 in S. Hemisphere +# +CATCHMENT_SPINUP: 0 + + +# ---- Choice of land surface model +# +# 1 : Catchment model (default) +# 2 : CatchmentCN-CLM4.0 +# +LSM_CHOICE: 1 + + +# ---- Domain definition +# +# The domain is determined by specifying a lat/lon rectangle in conjunction +# with ExcludeList and IncludeList files. The files contain the IDs of tiles to +# be excluded and included in the domain. +# Included are all tiles within the rectangle or the IncludeList but not in the ExcludeList. +# In case of conflict, the ExcludeList takes precedence. +# The default is a GLOBAL (land) domain. +# +# Specify extremities of lat/lon rectangle: +# Max lat/lon range: lon=-180:180, lat=-90:90. +# If only IncludeList should be used, specify dummy values such that: +# MINLON > MAXLON and MINLAT > MAXLAT. +# +# MINLON: -180. +# MAXLON: 180. +# MINLAT: -90. +# MAXLAT: 90. +# +# Path and filenames for ExcludeList and IncludeList files. +# (May leave blank.) +# +# EXCLUDE_FILE: '' +# INCLUDE_FILE: '' + + +# ---- Surface meteorological forcing: Horizonal interpolation +# +# 1 : bilinear interpolation (default) +# 0 : nearest neighbor +# +# When forcing is on CS grid, must set MET_HINTERP: 0 +# +MET_HINTERP: 1 + + +# ---- Specify if running model only or data assimilation +# +# NO : model only, with or without perturbations (default) +# YES : assimilation (full land analysis or just processing of obs for "innovations" output) +# +LAND_ASSIM: NO + +# ---- Specify land assimilation times (when "LAND_ASSIM: YES") +# +# LANDASSIM_DT : land analysis time step (seconds) +# LANDASSIM_T0 : land analysis "reference" time (hhmmss) +# +# LANDASSIM_T0 ("T0") and LANDASSIM_DT ("DT") define an infinite sequence of land analysis times: +# +# ..., T0-3*DT, T0-2*DT, T0-DT, T0, T0+DT, T0+2*DT, T0+3*DT, ... +# +# There is never a land analysis at the restart time. Otherwise, the land analysis times are +# independent of the restart time. There may be a land analysis at the final time. +# +# LANDASSIM_DT must be <=86400s, be a multiple of HEARTBEAT_DT, and evenly divide a day. +# Consequently, only HHMMSS information is needed for LANDASSIM_T0. +# +# Examples: +# T0=013000, DT=10800s --> land analysis whenever model time reaches 1:30z, 4:30z, ..., 22:30z. +# T0=163000, DT=10800s --> land analysis whenever model time reaches 1:30z, 4:30z, ..., 22:30z. +# T0=120000, DT=21600s --> land analysis whenever model time reaches 0z, 6z, 12z, and 18z. +# +# LANDASSIM_DT and LANDASSIM_T0 work almost but not quite like "frequency" and "reftime" from MAPL +# HISTORY. The difference is that MAPL HISTORY will not write output until "reftime" even when +# (reftime - restarttime)/frequency > 1. E.g., if frequency=3h, reftime=9z, and restarttime=0z, +# then MAPL HISTORY will not write output until 9z, whereas with DT=3h, T0=9z, and restarttime=0z, +# the land analysis will be run at 3z, 6z, 9z, 12z, ... +# +# LANDASSIM_DT: 10800 +# LANDASSIM_T0: 000000 + + +# ---- Perturbations: On/off +# +# If num_ensemble > 1, PERTURBATIONS will automatically be set to 1. +# +# 0 : No perturbactions. +# 1 : With perturbations. +# +PERTURBATIONS: 0 + +# ---- Perturbations: ID of first ensemble member +# +FIRST_ENS_ID: 0 + + +# ---- Ensemble forcing +# +# NO : Deterministic met forcing (default) +# YES : Ensemble met forcing +# - Typically used in land-atmosphere data assimilation (LADAS) configuration +# when coupled to 4dHybridEnVar ADAS. +# - Must have forcing with matching ensemble ID for each land ensemble member. +# - Forcing files must be stored in member-specific directories MET_PATH[NNN]/, +# where NNN is the 3-digit ensemble ID. +# - User-specified MET_PATH and MET_TAG must not contain ensemble IDs. +# - FIRST_ENS_ID may be used to align ensemble IDs. +# +ENSEMBLE_FORCING: NO + + +# ---- Path to special namelist input files +# +# Applies only for ensemble simulations. The variable values in the special +# namelist overwrite the DEFAULT values. There may be three files: +# LDASsa_SPECIAL_inputs_ensupd.nml +# LDASsa_SPECIAL_inputs_ensprop.nml +# LDASsa_SPECIAL_inputs_catbias.nml +# +# NML_INPUT_PATH: '' + + +# ---- Path to (L-band) microwave radiative transfer model (mwRTM) parameter files: +# +# [MWRTM_PATH]/[BCS_RESOLUTION]/mwRTM_param.nc4 - static (time-invariant) mwRTM parameters +# [MWRTM_PATH]/[BCS_RESOLUTION]/vegopacity.bin - seaonally-varying clim of veg opacity (OPTIONAL) +# +# Must be provided for +# - output of Tb through HISTORY or +# - Tb assimilation. +# Otherwise, leave unspecified (i.e., use default empty string). +# +# If empty or commented out, GEOSldas will search the restart directory. +# +# Must be tile-space data with resolution matching that of BCS (see BCS_RESOLUTION). +# +# mwRTM_param.nc4 can be created from a binary file with the program mwrtm_bin2nc4.x. +# +# Vegetation attenuation is *either* computed from static parameters (bh, bv, lewt) and LAI +# *or* prescribed from the vegopacity.bin file. If the latter is used, (bh, bv, lewt) must +# be no-data values in mwRTM_param.nc4. +# +# MWRTM_PATH: '' + + +# ---- Job segments: Length +# +# Specify period between GEOSldas.x restart and shutdown. +# Default is the entire simulation period (END_DATE minus BEG_DATE). +# Format: yyyymmdd hhmmss +# +# JOB_SGMT: 00000100 000000 + +# ---- Job segments: Number +# +# One lenkf.j job simulates (NUM_SGMT*JOB_SGMT) time, then re-submits itself. +# Avoid unnecessarily exiting and re-initializing GEOSldas.x by using +# NUM_SGMT=1 (default) and the max possible JOB_SGMT so that (NUM_SGMT*JOB_SGMT) +# completes within the job's wall time limit (12 hours at NCCS). +# If desired, request intermediate restart files using RECORD_FREQUENCY. +# +# NUM_SGMT: 1 + + +# ---- CHECKPOINT file output +# +# By default, CHECKPOINT (restart) files are are written at the end of each job segment. +# Restart files can also be written before exiting GEOSldas.x. +# The following resource parameters specify the frequency and reference time and date. +# +# RECORD_FREQUENCY: 0240000 # hhhmmss (can be greater than 1 day!) +# RECORD_REF_TIME: 000000 # hhmmss (reference time-of-day) +# RECORD_REF_DATE: 19790101 000000 # yyyymmdd hhmmss (date/time after which checkpoints are written) + + +# ---- Output: Write log file (YES/NO)? +# +LDAS_logit: YES + +# ---- Output: HISTORY definition of model diagnostics +# +# User-defined path and filename of output (HISTORY) specification file. +# If empty, ldas_setup will generate a default HISTORY.rc file. +# +# For the "catch_progn_incr" and "lndfcstana" output Collections, the "frequency" and "ref_time" +# settings must be consistent with "LANDASSIM_DT" and "LANDASSIM_T0" (see above). +# +# HISTRC_FILE: '' + +# ---- Concatenate sub-daily nc4 files into daily nc4 files and write monthly-mean output? +# +# Optional post-processing of model diagnostics output into bundled daily files and monthly means. +# Reduces the file count and (optionally) the output volume. +# +# Accurate monthly-means of time-average Collections require setting "ref_time" to "000000" in HISTRC_FILE! +# +# 0 : No post-processing (default). +# 1 : For complete days, concatenate (bundle) sub-daily nc4 files, if any, into daily nc4 files. +# For complete months, write monthly-mean nc4 files. +# 2 : As in 1, but delete daily nc4 files. +# +# POSTPROC_HIST: 0 + + +# ---- Name of file containing Surface GridComp resource parameters +# +SURFRC: LDAS.rc + + +#--------------------------------------------------------# +# Do not change the following parameters in GEOSldas # +#--------------------------------------------------------# + +# ---- No dycore for offline +# +DYCORE: none + +# ---- For MAPL_RestartOptional +# +MAPL_ENABLE_BOOTSTRAP: YES + +#----- Write restart or checkpoint by oserver +# +WRITE_RESTART_BY_OSERVER: NO + +# +# =================================== EOF ========================================== + + diff --git a/GEOSldas_App/LDASsa_DEFAULT_inputs_adapt.nml b/GEOSldas_App/LDASsa_DEFAULT_inputs_adapt.nml new file mode 100644 index 0000000..10fdcd6 --- /dev/null +++ b/GEOSldas_App/LDASsa_DEFAULT_inputs_adapt.nml @@ -0,0 +1,122 @@ +! +! namelist of EnKF inputs for adaptive filtering +! +! reichle, 14 Dec 2006 +! reichle, 24 Aug 2007 (added adapt_type=12) +! reichle, 21 Nov 2014 - renamed force_pert_type fields for consistency w/ met_force_type +! %tmp2m --> %tair (but note lower-case!) +! %dpt2m --> %qair (but note lower-case!) +! %wnd --> %wind (but note lower-case!) +! -------------------------------------------------------------------- + +&adapt_inputs + +! choose algorithm for adaptive filtering +! +! adapt_type = 0: no adaptive filtering +! adapt_type = 1: (NOT USED) +! adapt_type = 2: (NOT USED) +! adapt_type = 3: (NO LONGER USED) tuning of P only via time moving average of nOmBxOmB +! adapt_type = 4: (NO LONGER USED) in sync tuning of P and R via time moving average of +! nOmBxOmB (same as Desroziers based on nAmBxOmB and nOmAxOmB because +! with "perfect" EnKF update nOmBxOmB=nAmBxOmB=nOmAxOmB) +! adapt_type = 5: (NO LONGER USED) separate tuning of P and R via time moving average of +! AmBxOmB, HPHt, OmAxOmB, and R +! (a.k.a. "A0005" in RedArk_adapt, Desroziers et al, QJR Met Soc, 2005) +! adapt_type = 6: (NO LONGER USED) separate tuning of P and R via time moving average of +! nOmBxOmB and nOmBtm1xOmBt (Crow and Bolten, GRL, 2007) +! adapt_type = 7-9: (NO LONGER USED) +! adapt_type = 10: separate tuning of P and R via time moving average of +! AmBxOmB, HPHt, OmAxOmB, and R (Desroziers et al, QJR Met Soc, 2005) +! (a.k.a. "A0010" in WRR_3 "RedArk_adapt" +! adapt_type = 11: (NO LONGER USED) same as adapt_type=10 but with computation +! of E[AmB OmB] etc from ensemble +! adapt_type = 12: same as adapt_type=10 but tuning of P only +! +! adding a new adapt_type: +! - in clsm_adapt_routines.F90 write appropriate io_adapt_X, update_adapt_X and +! check "select case(adapt_type)" statement in get_adapt_param() +! - in clsm_ensdrv_main.F90 add necessary variables to declarations and fill +! in new information in each "select case(adapt_type)" + +adapt_type = 0 + +! Misc adapt params + +adapt_misc_param%gamma_P = 0.02 +adapt_misc_param%gamma_R = 0.02 + +adapt_misc_param%delta_P = 0.005 +adapt_misc_param%delta_R = 0.005 + +adapt_misc_param%beta_P = 1.06 +adapt_misc_param%beta_R = 1. + +adapt_misc_param%min_alpha_P = 0.01 +adapt_misc_param%max_alpha_P = 100. + +adapt_misc_param%min_alpha_R = 0.01 +adapt_misc_param%max_alpha_R = 100. + +! Determine if std of given forcing/prognostics perturbations is adaptive +! and which kind of innovations statistics is used for adaptive filtering. +! +! In the code, the variable "Pert_adapt" (N_adapt-by-N_catd) estimates +! the statistics of normalized innovations for different kinds of variables +! such as sfmc, rzmc, tsurf, etc. The latter classification loosely +! follows get_obs_pred(): +! +! 0 = *not* adaptive (default) +! 1 = adapt according to innovations of sfmc +! 2 = adapt according to innovations of rzmc +! 3 = adapt according to innovations of tsurf +! 4 = ... +! +! Mapping of innovations to adaptive feedback is done in two steps: +! +! 1.) Innovations of a given obs species i contribute to the j-th component +! Pert_adapt(j,:) as defined in obs_param(i)%adapt=j +! +! 2.) Perturbations of a given forcing/prognostic variable are adapted +! in response to Pert_adapt(j,:) as defined in the nml variables below. +! +! NOTE: +! Adaptive tuning of obs error covariance R is done for each obs species +! and does not require special mapping. + +adapt_force_pert%pcp = 0 +adapt_force_pert%sw = 0 +adapt_force_pert%lw = 0 +adapt_force_pert%tair = 0 +adapt_force_pert%qair = 0 +adapt_force_pert%wind = 0 + +adapt_progn_pert%tc1 = 0 +adapt_progn_pert%tc2 = 0 +adapt_progn_pert%tc4 = 0 +adapt_progn_pert%qa1 = 0 +adapt_progn_pert%qa2 = 0 +adapt_progn_pert%qa4 = 0 +adapt_progn_pert%capac = 0 +adapt_progn_pert%catdef = 0 +adapt_progn_pert%rzexc = 0 +adapt_progn_pert%srfexc = 0 +adapt_progn_pert%ght(1) = 0 +adapt_progn_pert%ght(2) = 0 +adapt_progn_pert%ght(3) = 0 +adapt_progn_pert%ght(4) = 0 +adapt_progn_pert%ght(5) = 0 +adapt_progn_pert%ght(6) = 0 +adapt_progn_pert%wesn(1) = 0 +adapt_progn_pert%wesn(2) = 0 +adapt_progn_pert%wesn(3) = 0 +adapt_progn_pert%htsn(1) = 0 +adapt_progn_pert%htsn(2) = 0 +adapt_progn_pert%htsn(3) = 0 +adapt_progn_pert%sndz(1) = 0 +adapt_progn_pert%sndz(2) = 0 +adapt_progn_pert%sndz(3) = 0 + +/ + +! =========================== EOF ======================================= diff --git a/GEOSldas_App/LDASsa_DEFAULT_inputs_catbias.nml b/GEOSldas_App/LDASsa_DEFAULT_inputs_catbias.nml new file mode 100644 index 0000000..cfdb79b --- /dev/null +++ b/GEOSldas_App/LDASsa_DEFAULT_inputs_catbias.nml @@ -0,0 +1,166 @@ +! +! DEFAULT namelist of catbias inputs for land EnKF +! +! reichle, 19 Oct 2005 +! reichle+draper, 26 Mar 2013 - renamed to LDASsa_DEFAULT_inputs_catbias.nml +! - revised structures for cat_bias_param +! +! -------------------------------------------------------------------- + +&cat_bias_inputs + +! ---------------------------------------------------------------------- +! +! bias correction is selected by cat_progn field +! +! Nparam indicates how many bias parameters are estimated +! per cat_progn field: +! +! Nparam = 0 - no bias correction +! +! Nparam = 1 - constant bias corr (w/o diurnal cycle) +! +! Nparam = 3 - diurnal sine/cosine bias corr +! Nparam = 5 - semi-diurnal sine/cosine bias corr +! +! Nparam = 2 - "time-of-day" bias corr with 2 separate bias estimates per day +! Nparam = 4 - "time-of-day" bias corr with 4 separate bias estimates per day +! Nparam = 8 - "time-of-day" bias corr with 8 separate bias estimates per day + +cat_bias_param%Nparam%tc1 = 0 ! 1 +cat_bias_param%Nparam%tc2 = 0 ! 1 +cat_bias_param%Nparam%tc4 = 0 ! 1 +cat_bias_param%Nparam%qa1 = 0 +cat_bias_param%Nparam%qa2 = 0 +cat_bias_param%Nparam%qa4 = 0 +cat_bias_param%Nparam%capac = 0 +cat_bias_param%Nparam%catdef = 0 +cat_bias_param%Nparam%rzexc = 0 +cat_bias_param%Nparam%srfexc = 0 +cat_bias_param%Nparam%ght(1) = 0 +cat_bias_param%Nparam%ght(2) = 0 +cat_bias_param%Nparam%ght(3) = 0 +cat_bias_param%Nparam%ght(4) = 0 +cat_bias_param%Nparam%ght(5) = 0 +cat_bias_param%Nparam%ght(6) = 0 +cat_bias_param%Nparam%wesn(1) = 0 +cat_bias_param%Nparam%wesn(2) = 0 +cat_bias_param%Nparam%wesn(3) = 0 +cat_bias_param%Nparam%htsn(1) = 0 +cat_bias_param%Nparam%htsn(2) = 0 +cat_bias_param%Nparam%htsn(3) = 0 +cat_bias_param%Nparam%sndz(1) = 0 +cat_bias_param%Nparam%sndz(2) = 0 +cat_bias_param%Nparam%sndz(3) = 0 + +! --------------------------------------------------------------------------- +! +! The bias estimate is updated from analysis increments whenever observations +! are available. The bias time scale relative to the temporal spacing of +! the observations is described by "tconst_bias". +! +! tconst_bias = dimensionless bias time constant +! +! DEFINITION: P_bias = tconst_bias * P_forecast (P is forecast error cov) +! +! tconst_bias is a.k.a. "gamma" in Dee's 2003 ECWMF proceedings paper +! +! ASSUMPTION: tconst << 1 +! +! CRUDE APPROXIMATION: tconst = dt_obs/tcorr +! +! where dt_obs is the interval between the updates from obs +! and tcorr is the bias time scale +! +! IN MORE DETAIL: +! +! starting from Dee & Todling, MWR, 2000, equation (9) we get +! +! b_k = (1-lambda)*b_(k-1) - lambda*innov +! +! => AR(1) time correlation exp(-dt/tcorr)=(1-lambda) +! +! use lambda = tconst*var_f/(var_f+var_o) to get +! +! tconst = (var_f+var_o)/var_f*(1-exp(-dt/tcorr)) +! +! note that (1-exp(-dt/tcorr)) = dt/tcorr for dt/tcorr<<1 + +cat_bias_param%tconst%tc1 = .2 ! .2 +cat_bias_param%tconst%tc2 = .2 ! .2 +cat_bias_param%tconst%tc4 = .2 ! .2 +cat_bias_param%tconst%qa1 = .2 +cat_bias_param%tconst%qa2 = .2 +cat_bias_param%tconst%qa4 = .2 +cat_bias_param%tconst%capac = .2 +cat_bias_param%tconst%catdef = .2 +cat_bias_param%tconst%rzexc = .2 +cat_bias_param%tconst%srfexc = .2 +cat_bias_param%tconst%ght(1) = .2 +cat_bias_param%tconst%ght(2) = .2 +cat_bias_param%tconst%ght(3) = .2 +cat_bias_param%tconst%ght(4) = .2 +cat_bias_param%tconst%ght(5) = .2 +cat_bias_param%tconst%ght(6) = .2 +cat_bias_param%tconst%wesn(1) = .2 +cat_bias_param%tconst%wesn(2) = .2 +cat_bias_param%tconst%wesn(3) = .2 +cat_bias_param%tconst%htsn(1) = .2 +cat_bias_param%tconst%htsn(2) = .2 +cat_bias_param%tconst%htsn(3) = .2 +cat_bias_param%tconst%sndz(1) = .2 +cat_bias_param%tconst%sndz(2) = .2 +cat_bias_param%tconst%sndz(3) = .2 + +! ----------------------------------------------------------------------- +! +! The underlying model for bias estimation is a constant bias +! (or constant diurnal cycle parameters). Should there be an +! extended period without observations, the bias must be relaxed +! to a reasonable estimate, so as not to get stuck at whatever +! it was when it was last updated. +! For now, the bias is relaxed to zero with a relaxation time specified +! below. (Effectively, this makes the underlying model for the bias +! an exponentially decay.) +! +! "trelax_bias" = bias relaxation time [s] +! +! DEFINITION: Bias(t+1) = exp(-dtstep/trelax_bias)*Bias(t) +! +! where dtstep is the model time step +! +! 1 day = 86400 s +! 2 days = 172800 s +! 7 days = 604800 s +! 14 days = 1209600 s +! 28 days = 2419200 s + +cat_bias_param%trelax%tc1 = 86400. +cat_bias_param%trelax%tc2 = 86400. +cat_bias_param%trelax%tc4 = 86400. +cat_bias_param%trelax%qa1 = 86400. +cat_bias_param%trelax%qa2 = 86400. +cat_bias_param%trelax%qa4 = 86400. +cat_bias_param%trelax%capac = 86400. +cat_bias_param%trelax%catdef = 86400. +cat_bias_param%trelax%rzexc = 86400. +cat_bias_param%trelax%srfexc = 86400. +cat_bias_param%trelax%ght(1) = 86400. +cat_bias_param%trelax%ght(2) = 86400. +cat_bias_param%trelax%ght(3) = 86400. +cat_bias_param%trelax%ght(4) = 86400. +cat_bias_param%trelax%ght(5) = 86400. +cat_bias_param%trelax%ght(6) = 86400. +cat_bias_param%trelax%wesn(1) = 86400. +cat_bias_param%trelax%wesn(2) = 86400. +cat_bias_param%trelax%wesn(3) = 86400. +cat_bias_param%trelax%htsn(1) = 86400. +cat_bias_param%trelax%htsn(2) = 86400. +cat_bias_param%trelax%htsn(3) = 86400. +cat_bias_param%trelax%sndz(1) = 86400. +cat_bias_param%trelax%sndz(2) = 86400. +cat_bias_param%trelax%sndz(3) = 86400. + +/ + +! =========================== EOF ======================================= diff --git a/GEOSldas_App/LDASsa_DEFAULT_inputs_ensprop.nml b/GEOSldas_App/LDASsa_DEFAULT_inputs_ensprop.nml new file mode 100644 index 0000000..dbb29e8 --- /dev/null +++ b/GEOSldas_App/LDASsa_DEFAULT_inputs_ensprop.nml @@ -0,0 +1,511 @@ +! +! namelist of EnKF inputs +! +! reichle, 23 Mar 2004 +! reichle, 10 May 2005 +! reichle, 21 Nov 2014 - re-interpreted progn_pert as perturbation flux forcing +! - renamed force_pert_type fields for consistency w/ met_force_type +! %tmp2m --> %tair (but note lower-case!) +! %dpt2m --> %qair (but note lower-case!) +! %wnd --> %wind (but note lower-case!) +! +! -------------------------------------------------------------------- + +&ens_prop_inputs + +! -------------------------------------------------------------------- +! +! ensemble size and ID's (integer IDs: first_ens_id:1:first_ens_id+N_ens-1) + +! N_ens and first_id are from landpert_gridcomp +! N_ens= 1 +! first_ens_id= 0 + +! -------------------------------------------------------------------- +! +! FORCING AND PROGNOSTICS PERTURBATIONS +! +! Forcing and prognostics perturbations are auto-regressive, order-one (AR(1)) +! time series of spatially correlated and cross-correlated 2d fields. +! +! The AR(1) time series are generated at intervals governed by "force_pert_dtstep" +! and "progn_pert_dtstep" (typically every 3 hours) with a neutral mean value. +! +! The std-dev of the AR(1) time series ("std"), their temporal correlations +! ("tcorr"), spatial correlations ("xcorr", "ycorr") and cross-correlations among +! variables ("ccorr") are specified via this nml inputs file. +! +! The AR(1) perturbations time series are linearly interpolated to and applied +! at every model time step (typically 7.5 min). (Note that the temporal correlation +! "tcorr" is NOT valid for the temporally interpolated time series.) +! +! Perturbations can be additive or multiplicative. +! +! FORCING perturbations are applied as follows: +! +! F = F + Pert for additive perturbations +! F = F * Pert for multiplicative perturbations +! +! PROGNOSTICS perturbations are interpreted as fluxes and are applied as follows: +! +! P = P + Pert*dt for additive perturbations +! P = P * Pert**dt for multiplicative perturbations +! +! UNITS of std-dev for FORCING perturbations: +! - If perturbations are additive, units match those of the perturbed field. +! - If perturbations are multiplicative, units are dimensionless [fraction]. +! For example, std_force_pert%pcp = 0.5 means that precip is perturbed with a +! std-dev that corresponds to 50% of the magnitude of the nominal precipitation. +! +! UNITS of std-dev for PROGNOSTICS perturbations: +! Prognostics perturbations are interpreted as fluxes so that their impact for +! a given prescribed std-dev does not depend on the model time step (model_dtstep). +! In the above equations for the application of the prognostics perturbations, +! the time step "dt" is in units of HOURS. +! - If perturbations are additive, units are flux PER HOUR into the perturbed field. +! For example, std_progn_pert%catdef = 0.24 kg/m2/HOUR. +! [ In earlier versions of LDASsa, the impact of the perturbations on the perturbed +! field depended on the model_dtstep. +! For example, std_progn_pert%catdef = 0.24 kg/m2/HOUR is equivalent +! to using the following in *earlier* versions of LDASsa: +! std_progn_pert%catdef = 0.03 kg/m2 and model_dtstep= 450s, or +! std_progn_pert%catdef = 0.08 kg/m2 and model_dtstep=1200s. ] +! - If perturbations are multiplicative, units are the dimensionless "flux" PER HOUR +! [fraction/hour]. +! For example, std_progn_pert%snow = 0.0012/HOUR means that snow is perturbed with a +! std-dev that corresponds to 0.12% of the magnitude of the snow pack in one hour. +! [ In earlier versions of LDASsa, the impact of the perturbations on the perturbed +! field depended on the model_dtstep. +! For example, std_progn_pert%catdef = 0.0012/HOUR is equivalent +! to using the following in *earlier* versions of LDASsa: +! std_progn_pert%snow = 0.0004 and model_dtstep=1200s. ] +! +! +! ------------------------------------------------------------------ +! +! all time steps in *seconds* +! +! all time steps MUST obey MOD(86400,dtstep)=0 and 0<=dtstep<=86400 +! +! *_pert_dtstep inputs must also be compatible with model_dtstep, force_dtstep +! and out_dtstep (see subroutine check_pert_dtstep()) +! +! 1200 = 20 min +! 3600 = 1 h +! 7200 = 2 h +! 10800 = 3 h +! 14400 = 4 h +! 21600 = 6 h +! 43200 = 12 h +! 86400 = 24 h + +progn_pert_dtstep = 10800 ! time step for generation of AR(1) prognostics perts [s] + +force_pert_dtstep = 10800 ! time step for generation of AR(1) forcing perts [s] + +! --------------------------------------------------------------------- +! +! forcing error (or forcing perturbation) parameters +! +! the mean is computed according to "typ" for unbiased perturbations +! and not specified here + +! string that describes the kind of forcing perturbations +! (see subroutine apply_force_pert() for details) + +descr_force_pert%pcp = 'pcp' +descr_force_pert%sw = 'sw' +descr_force_pert%lw = 'lw' +descr_force_pert%tair = 'tair' +descr_force_pert%qair = 'qair' +descr_force_pert%wind = 'wind' + +! specify whether forcing perturbations are additive or multiplicative +! +! additive: typ = 0. +! multiplicative and lognormal: typ = 1. +! +! real numbers are used so that "assemble_force()" can +! be used to assemble the forcing perturbation parameters + +typ_force_pert%pcp = 1. +typ_force_pert%sw = 1. +typ_force_pert%lw = 0. +typ_force_pert%tair = 0. +typ_force_pert%qair = 0. +typ_force_pert%wind = 1. + +! The perturbation (or error) std-dev can be specified as a spatially constant +! (default) value. Alternatively, perturbation std-dev values can be read from +! a netcdf-4 input file (where they may be spatially constant or distributed). +! See subroutines get_progn_pert_param() and get_force_pert_param(). +! +! Turn off all perturbations by setting std-dev values to zero and +! "stdfromfile" to false. +! +! Default, spatially homogeneous perturbations std-dev +! (used unless std-devs are read from file, see below) + +std_force_pert%pcp = 0. ! units if additive: [kg/m2/s], if multiplicative: [fraction] +std_force_pert%sw = 0. ! units if additive: [W/m^2] , if multiplicative: [fraction] +std_force_pert%lw = 0. ! units if additive: [W/m^2] , if multiplicative: [fraction] +std_force_pert%tair = 0. ! units if additive: [K] , if multiplicative: [fraction] +std_force_pert%qair = 0. ! units if additive: [kg/kg] , if multiplicative: [fraction] +std_force_pert%wind = 0. ! units if additive: [m/s] , if multiplicative: [fraction] + +! read error std-dev from file? (if .false., default values above apply) + +stdfromfile_force_pert%pcp = .false. +stdfromfile_force_pert%sw = .false. +stdfromfile_force_pert%lw = .false. +stdfromfile_force_pert%tair = .false. +stdfromfile_force_pert%qair = .false. +stdfromfile_force_pert%wind = .false. + +! specify file name (with full path) that contains std-dev values + +stdfilename_force_pert = '' + +! enforce zero (sample) mean across ensemble? + +zeromean_force_pert%pcp = .true. +zeromean_force_pert%sw = .true. +zeromean_force_pert%lw = .true. +zeromean_force_pert%tair = .true. +zeromean_force_pert%qair = .true. +zeromean_force_pert%wind = .true. + +! allow perturbations to be computed on coarsened grid? + +coarsen_force_pert%pcp = .false. +coarsen_force_pert%sw = .false. +coarsen_force_pert%lw = .false. +coarsen_force_pert%tair = .false. +coarsen_force_pert%qair = .false. +coarsen_force_pert%wind = .false. + +! max perturbation relative to standard normal +! (limits on range of random numbers: specify max absolute value +! allowed to be drawn from a standard normal distribution) + +std_normal_max_force_pert%pcp = 2.5 +std_normal_max_force_pert%sw = 2.5 +std_normal_max_force_pert%lw = 2.5 +std_normal_max_force_pert%tair = 2.5 +std_normal_max_force_pert%qair = 2.5 +std_normal_max_force_pert%wind = 2.5 + +! spatial correlation of forcing perturbations + +xcorr_force_pert%pcp = 0. ! [deg] +xcorr_force_pert%sw = 0. ! [deg] +xcorr_force_pert%lw = 0. ! [deg] +xcorr_force_pert%tair = 0. ! [deg] +xcorr_force_pert%qair = 0. ! [deg] +xcorr_force_pert%wind = 0. ! [deg] + +ycorr_force_pert%pcp = 0. ! [deg] +ycorr_force_pert%sw = 0. ! [deg] +ycorr_force_pert%lw = 0. ! [deg] +ycorr_force_pert%tair = 0. ! [deg] +ycorr_force_pert%qair = 0. ! [deg] +ycorr_force_pert%wind = 0. ! [deg] + +! temporal correlation of forcing perturbations + +tcorr_force_pert%pcp = 86400. ! [s] +tcorr_force_pert%sw = 86400. ! [s] +tcorr_force_pert%lw = 86400. ! [s] +tcorr_force_pert%tair = 86400. ! [s] +tcorr_force_pert%qair = 86400. ! [s] +tcorr_force_pert%wind = 86400. ! [s] + +! correlation coefficients -1 <= rho <= 1 +! +! specify only essential information, the other side of off-diagonals and +! the diagonal will be filled in later (subroutines read_ens_prop_inputs +! and get_force_pert_inputs) + +ccorr_force_pert%pcp%sw = 0. !-.8 +ccorr_force_pert%pcp%lw = 0. ! .5 +ccorr_force_pert%pcp%tair = 0. +ccorr_force_pert%pcp%qair = 0. +ccorr_force_pert%pcp%wind = 0. + +ccorr_force_pert%sw%lw = 0. ! -.6 ! -.5 +ccorr_force_pert%sw%tair = 0. ! .4 ! .8 +ccorr_force_pert%sw%qair = 0. +ccorr_force_pert%sw%wind = 0. + +ccorr_force_pert%lw%tair = 0 ! .4 ! .8 +ccorr_force_pert%lw%qair = 0. +ccorr_force_pert%lw%wind = 0. + +ccorr_force_pert%tair%qair = 0. ! .9 +ccorr_force_pert%tair%wind = 0. + +ccorr_force_pert%qair%wind = 0. + + + +! --------------------------------------------------------------------- +! +! model error (or progn_pert) parameters +! +! the mean is computed according to "typ" for unbiased perturbations +! and not specified here + +! string that describes the prognostics to be perturbed +! (see subroutine apply_progn_pert() for details) + +descr_progn_pert%catdef = 'catdef' +descr_progn_pert%rzexc = 'rzexc' +descr_progn_pert%srfexc = 'srfexc' +descr_progn_pert%snow = 'snow' +descr_progn_pert%tc = 'tc' +descr_progn_pert%ght(1) = 'ght1' +descr_progn_pert%ght(2) = 'ght2' +descr_progn_pert%ght(3) = 'ght3' +descr_progn_pert%ght(4) = 'ght4' +descr_progn_pert%ght(5) = 'ght5' +descr_progn_pert%ght(6) = 'ght6' + + +! specify whether model error is additive or multiplicative +! +! additive: typ = 0. +! multiplicative and lognormal: typ = 1. +! +! real numbers are used so that "assemble_state()" can +! be used to assemble the model error parameters + +typ_progn_pert%catdef = 0. +typ_progn_pert%rzexc = 0. +typ_progn_pert%srfexc = 0. +typ_progn_pert%snow = 1. +typ_progn_pert%tc = 0. +typ_progn_pert%ght(1) = 0. +typ_progn_pert%ght(2) = 0. +typ_progn_pert%ght(3) = 0. +typ_progn_pert%ght(4) = 0. +typ_progn_pert%ght(5) = 0. +typ_progn_pert%ght(6) = 0. + + +! The perturbation (or error) std-dev can be specified as a spatially constant +! (default) value. Alternatively, perturbation std-dev values can be read from +! a netcdf-4 input file (where they may be spatially constant or distributed). +! See subroutines get_progn_pert_param() and get_force_pert_param(). +! +! Turn off all perturbations by setting std-dev values to zero and +! "stdfromfile" to false. +! +! Default, spatially homogeneous perturbations std-dev +! (used unless std-devs are read from file, see below) + +std_progn_pert%catdef = 0. ! units if additive: [kg/m2/HOUR], if multiplicative: [fraction/HOUR] +std_progn_pert%rzexc = 0. ! units if additive: [kg/m2/HOUR], if multiplicative: [fraction/HOUR] +std_progn_pert%srfexc = 0. ! units if additive: [kg/m2/HOUR], if multiplicative: [fraction/HOUR] +std_progn_pert%snow = 0. ! units if additive: [kg/m2/HOUR], if multiplicative: [fraction/HOUR] +std_progn_pert%tc = 0. ! units if additive: [K /HOUR], if multiplicative: [fraction/HOUR] +std_progn_pert%ght(1) = 0. ! units if additive: [J /HOUR], if multiplicative: [fraction/HOUR] +std_progn_pert%ght(2) = 0. ! units if additive: [J /HOUR], if multiplicative: [fraction/HOUR] +std_progn_pert%ght(3) = 0. ! units if additive: [J /HOUR], if multiplicative: [fraction/HOUR] +std_progn_pert%ght(4) = 0. ! units if additive: [J /HOUR], if multiplicative: [fraction/HOUR] +std_progn_pert%ght(5) = 0. ! units if additive: [J /HOUR], if multiplicative: [fraction/HOUR] +std_progn_pert%ght(6) = 0. ! units if additive: [J /HOUR], if multiplicative: [fraction/HOUR] + +! GHT perturbations: +! +! for non-frozen conditions, ght(i) ~ 2.e6*dzgt(i)*tp(i) [deg C] +! +! std_progn_pert%ght(1) = 50000. ! translates into ~0.2 K +! std_progn_pert%ght(2) = 100000. ! translates into ~0.2 K +! std_progn_pert%ght(3) = 200000. ! translates into ~0.2 K +! std_progn_pert%ght(4) = 400000. ! translates into ~0.2 K +! std_progn_pert%ght(5) = 800000. ! translates into ~0.2 K +! std_progn_pert%ght(6) = 5000000. ! translates into ~0.2 K + +! read error std-dev from file? (if .false., default values above apply) + +stdfromfile_progn_pert%catdef = .false. +stdfromfile_progn_pert%rzexc = .false. +stdfromfile_progn_pert%srfexc = .false. +stdfromfile_progn_pert%snow = .false. +stdfromfile_progn_pert%tc = .false. +stdfromfile_progn_pert%ght(1) = .false. +stdfromfile_progn_pert%ght(2) = .false. +stdfromfile_progn_pert%ght(3) = .false. +stdfromfile_progn_pert%ght(4) = .false. +stdfromfile_progn_pert%ght(5) = .false. +stdfromfile_progn_pert%ght(6) = .false. + +! specify file name (with full path) that contains std-dev values + +stdfilename_progn_pert = '' + +! enforce zero (sample) mean across ensemble? + +zeromean_progn_pert%catdef = .true. +zeromean_progn_pert%rzexc = .true. +zeromean_progn_pert%srfexc = .true. +zeromean_progn_pert%snow = .true. +zeromean_progn_pert%tc = .true. +zeromean_progn_pert%ght(1) = .true. +zeromean_progn_pert%ght(2) = .true. +zeromean_progn_pert%ght(3) = .true. +zeromean_progn_pert%ght(4) = .true. +zeromean_progn_pert%ght(5) = .true. +zeromean_progn_pert%ght(6) = .true. + + +! allow perturbations to be computed on coarsened grid? + +coarsen_progn_pert%catdef = .false. +coarsen_progn_pert%rzexc = .false. +coarsen_progn_pert%srfexc = .false. +coarsen_progn_pert%snow = .false. +coarsen_progn_pert%tc = .false. +coarsen_progn_pert%ght(1) = .false. +coarsen_progn_pert%ght(2) = .false. +coarsen_progn_pert%ght(3) = .false. +coarsen_progn_pert%ght(4) = .false. +coarsen_progn_pert%ght(5) = .false. +coarsen_progn_pert%ght(6) = .false. + + +! max perturbation relative to standard normal +! (limits on range of random numbers: specify max absolute value +! allowed to be drawn from a standard normal distribution) + +std_normal_max_progn_pert%catdef = 2.5 +std_normal_max_progn_pert%rzexc = 2.5 +std_normal_max_progn_pert%srfexc = 2.5 +std_normal_max_progn_pert%snow = 2.5 +std_normal_max_progn_pert%tc = 2.5 +std_normal_max_progn_pert%ght(1) = 2.5 +std_normal_max_progn_pert%ght(2) = 2.5 +std_normal_max_progn_pert%ght(3) = 2.5 +std_normal_max_progn_pert%ght(4) = 2.5 +std_normal_max_progn_pert%ght(5) = 2.5 +std_normal_max_progn_pert%ght(6) = 2.5 + + +! model error spatial correlation [deg] +! (x runs east-west, y runs north-south) + +xcorr_progn_pert%catdef = 0. ! [deg] +xcorr_progn_pert%rzexc = 0. ! [deg] +xcorr_progn_pert%srfexc = 0. ! [deg] +xcorr_progn_pert%snow = 0. ! [deg] +xcorr_progn_pert%tc = 0. ! [deg] +xcorr_progn_pert%ght(1) = 0. ! [deg] +xcorr_progn_pert%ght(2) = 0. ! [deg] +xcorr_progn_pert%ght(3) = 0. ! [deg] +xcorr_progn_pert%ght(4) = 0. ! [deg] +xcorr_progn_pert%ght(5) = 0. ! [deg] +xcorr_progn_pert%ght(6) = 0. ! [deg] + +ycorr_progn_pert%catdef = 0. ! [deg] +ycorr_progn_pert%rzexc = 0. ! [deg] +ycorr_progn_pert%srfexc = 0. ! [deg] +ycorr_progn_pert%snow = 0. ! [deg] +ycorr_progn_pert%tc = 0. ! [deg] +ycorr_progn_pert%ght(1) = 0. ! [deg] +ycorr_progn_pert%ght(2) = 0. ! [deg] +ycorr_progn_pert%ght(3) = 0. ! [deg] +ycorr_progn_pert%ght(4) = 0. ! [deg] +ycorr_progn_pert%ght(5) = 0. ! [deg] +ycorr_progn_pert%ght(6) = 0. ! [deg] + +! model error temporal correlation [s] + +tcorr_progn_pert%catdef = 10800. ! [s] +tcorr_progn_pert%rzexc = 10800. ! [s] +tcorr_progn_pert%srfexc = 10800. ! [s] +tcorr_progn_pert%snow = 10800. ! [s] +tcorr_progn_pert%tc = 10800. ! [s] +tcorr_progn_pert%ght(1) = 10800. ! [s] +tcorr_progn_pert%ght(2) = 10800. ! [s] +tcorr_progn_pert%ght(3) = 10800. ! [s] +tcorr_progn_pert%ght(4) = 10800. ! [s] +tcorr_progn_pert%ght(5) = 10800. ! [s] +tcorr_progn_pert%ght(6) = 10800. ! [s] + +! correlation coefficients -1 <= rho <= 1 +! +! specify only essential information, the other side of off-diagonals and +! the diagonal will be filled in later (subroutines read_ens_prop_inputs +! and get_force_pert_inputs) +! +! (the default input list below was put together with matlab +! script create_ccorr_cat_progn_default.m) + +ccorr_progn_pert%catdef%rzexc = 0. +ccorr_progn_pert%catdef%srfexc = 0. +ccorr_progn_pert%catdef%snow = 0. +ccorr_progn_pert%catdef%tc = 0. +ccorr_progn_pert%catdef%ght(1) = 0. +ccorr_progn_pert%catdef%ght(2) = 0. +ccorr_progn_pert%catdef%ght(3) = 0. +ccorr_progn_pert%catdef%ght(4) = 0. +ccorr_progn_pert%catdef%ght(5) = 0. +ccorr_progn_pert%catdef%ght(6) = 0. + +ccorr_progn_pert%rzexc%srfexc = 0. +ccorr_progn_pert%rzexc%snow = 0. +ccorr_progn_pert%rzexc%tc = 0. +ccorr_progn_pert%rzexc%ght(1) = 0. +ccorr_progn_pert%rzexc%ght(2) = 0. +ccorr_progn_pert%rzexc%ght(3) = 0. +ccorr_progn_pert%rzexc%ght(4) = 0. +ccorr_progn_pert%rzexc%ght(5) = 0. +ccorr_progn_pert%rzexc%ght(6) = 0. + +ccorr_progn_pert%srfexc%snow = 0. +ccorr_progn_pert%srfexc%tc = 0. +ccorr_progn_pert%srfexc%ght(1) = 0. +ccorr_progn_pert%srfexc%ght(2) = 0. +ccorr_progn_pert%srfexc%ght(3) = 0. +ccorr_progn_pert%srfexc%ght(4) = 0. +ccorr_progn_pert%srfexc%ght(5) = 0. +ccorr_progn_pert%srfexc%ght(6) = 0. + +ccorr_progn_pert%snow%tc = 0. +ccorr_progn_pert%snow%ght(1) = 0. +ccorr_progn_pert%snow%ght(2) = 0. +ccorr_progn_pert%snow%ght(3) = 0. +ccorr_progn_pert%snow%ght(4) = 0. +ccorr_progn_pert%snow%ght(5) = 0. +ccorr_progn_pert%snow%ght(6) = 0. + +ccorr_progn_pert%tc%ght(1) = 0. +ccorr_progn_pert%tc%ght(2) = 0. +ccorr_progn_pert%tc%ght(3) = 0. +ccorr_progn_pert%tc%ght(4) = 0. +ccorr_progn_pert%tc%ght(5) = 0. +ccorr_progn_pert%tc%ght(6) = 0. + +ccorr_progn_pert%ght(1)%ght(2) = 0. +ccorr_progn_pert%ght(1)%ght(3) = 0. +ccorr_progn_pert%ght(1)%ght(4) = 0. +ccorr_progn_pert%ght(1)%ght(5) = 0. +ccorr_progn_pert%ght(1)%ght(6) = 0. + +ccorr_progn_pert%ght(2)%ght(3) = 0. +ccorr_progn_pert%ght(2)%ght(4) = 0. +ccorr_progn_pert%ght(2)%ght(5) = 0. +ccorr_progn_pert%ght(2)%ght(6) = 0. + +ccorr_progn_pert%ght(3)%ght(4) = 0. +ccorr_progn_pert%ght(3)%ght(5) = 0. +ccorr_progn_pert%ght(3)%ght(6) = 0. + +ccorr_progn_pert%ght(4)%ght(5) = 0. +ccorr_progn_pert%ght(4)%ght(6) = 0. + +ccorr_progn_pert%ght(5)%ght(6) = 0. + +/ + +! =========================== EOF ======================================= diff --git a/GEOSldas_App/LDASsa_DEFAULT_inputs_ensupd.nml b/GEOSldas_App/LDASsa_DEFAULT_inputs_ensupd.nml new file mode 100644 index 0000000..ece206c --- /dev/null +++ b/GEOSldas_App/LDASsa_DEFAULT_inputs_ensupd.nml @@ -0,0 +1,2361 @@ +! +! namelist of EnKF inputs for land EnKF update +! +! reichle, 28 Jan 2005 +! reichle, 13 Jun 2011 - updated for SMOS angles and downscaling ("FOV") +! reichle, 8 Jun 2017 - added "%flistpath" and "%flistname"; updated comments +! qliu+reichle, 29 Apr 2020 - added forecast error covariance inflation +! qzhang,wjiang,reichle, +! 7 May 2021 - removed "dtstep_assim" and "centered_update"; replaced with MAPL +! resource parameters "LANDASSIM_DT" and "LANDASSIM_T0" (in LDAS.rc) +! +! -------------------------------------------------------------------- + +&ens_upd_inputs + + +! ---------------------------------------------------------------------- +! +! update type - for details see subroutine cat_enkf_update() +! (note: all 3d updates use compact support) +! +! local = "1d", regional = "3d" +! +! # = no longer supported +! +! update_type = 0: NO assimilation, NO bias correction +! # update_type = 1: 1d soil moisture analysis; sfmc obs +! # update_type = 2: 3d soil moisture analysis; sfmc obs +! update_type = 3: 1d Tskin (assim incr NOT applied, use w/ bias corr) analysis; Tskin obs +! update_type = 4: 1d Tskin/ght1 (assim incr applied, use w/ or w/o bias corr) analysis; Tskin obs +! update_type = 5: 1d Tskin/ght1 (assim incr NOT applied, use w/ bias corr) analysis; Tskin obs +! update_type = 6: 1d soil moisture/Tskin/ght(1); TB obs +! update_type = 7: 3d Tskin/ght1 update; Tskin obs +! update_type = 8: 3d soil moisture/Tskin/ght(1); TB obs +! update_type = 9: 1d Tskin/ght1 update; FT obs +! update_type = 10: 3d soil moisture/Tskin/ght(1) excl. catdef unless PEATCLSM tile; TB obs +! update_type = 13: 3d soil moisture/Tskin/ght(1) excl. catdef unless PEATCLSM tile; sfmc and TB obs + +update_type = 0 + +out_obslog = .true. +out_ObsFcstAna = .false. +out_smapL4SMaup = .false. + +! --------------------------------------------------------------------- +! +! Compact support parameters - for 3d updates +! +! All correlations vanish outside of an ellipse with semi-axes xcompact +! and ycompact + +xcompact = 0. ! [deg] longitude +ycompact = 0. ! [deg] latitude + +! --------------------------------------------------------------------- +! +! forecast error covariance inflaction factor +! +! - assigns more weight to observations in analysis by inflating forecast error covariance +! - works on std-dev, i.e., var_inflated = var * inflation_fac**2 +! - typical values: 1 <= inflation_fac <= 1.5 +! - to turn off, set to any negative real number + +fcsterr_inflation_fac = -9999. + +! --------------------------------------------------------------------- +! +! Definition of measurement species and parameters +! +! NOTE: When additional types of measurements are included here, +! at least the following parameters and subroutines must be adapted: +! +! - N_obs_species_nml in clsm_ensupd_glob_param.f90 +! [- read_ens_upd_inputs()] +! [- collect_obs()] +! - read_obs() +! - get_obs_pred() +! - cat_enkf_update() +! +! +! Definition of obs_param_nml fields (see also enkf_types.F90): +! +! %descr = description +! %species = identifier for type of measurement +! %orbit = type of (half-)orbit +! 0 = n/a [eg., in situ obs] +! 1 = ascending +! 2 = descending +! 3 = ascending or descending +! 4 = geostationary +! %pol = polarization +! 0 = n/a [eg., multi-pol. retrieval] +! 1 = horizontal +! 2 = vertical +! 3 = ... +! %N_ang = # satellite viewing angles in species (radiance obs only) +! %ang = vector of satellite viewing angles +! %freq = frequency [Hz] +! %FOV = field-of-view *radius*, see NOTES below +! (if FOV==0. equate obs footprint w/ tile) +! %FOV_units = field-of-view units ('km' or 'deg'), see NOTES below +! %assim = Should this obs type be assimilated (state update)? (logical) +! %scale = Should this obs be scaled? (logical) +! %getinnov = Should innov be computed for this obs type (logical) +! (innovations are always computed if assim==.true.) +! %RTM_ID = ID of radiative transfer model to use for Tb forward modeling +! (subroutine get_obs_pred()) +! 0 = none +! 1 = L-band tau-omega model as in De Lannoy et al. 2013 (doi:10.1175/JHM-D-12-092.1) (SMOS) +! 2 = same as 1 but without Pellarin atm corr (SMAP) +! 3 = same as 1 but with Mironov and SMAP L2_SM pol mixing (SMOS) +! 4 = same as 3 but without Pellarin atm corr (targeted for SMAP L4_SM Version 8) +! %bias_Npar = number of obs bias states tracked per day (integer) +! %bias_trel = e-folding time scale of obs bias memory [s] +! %bias_tcut = cutoff time for confident obs bias estimate [s] +! %nodata = no-data-value +! %varname = equivalent model variable name (for "Obs_pred") +! %units = units (eg., 'K' or 'm3/m3') +! %path = path to measurement files +! %name = name identifier for file containing measurements +! %maskpath = path to obs mask file +! %maskname = filename for obs mask +! %scalepath = path to file(s) with scaling parameters +! %scalename = filename for scaling parameters +! %flistpath = path to file with list of obs file names +! %flistname = name of file with list of obs file names +! %errstd = default obs error std +! %std_normal_max = maximum allowed perturbation (relative to N(0,1)) +! %zeromean = enforce zero mean across ensemble +! %coarsen_pert = generate obs perturbations on coarser grid (see pert_param_type%coarsen) +! %xcorr = correlation length (deg) in longitude direction +! %ycorr = correlation length (deg) in latitude direction +! +! For observation perturbations, always use: +! +! tcorr = 0. (never temporally correlated) +! typ = 0 (always additive) +! ccorr = 0. (never cross-correlated) +! +! (these are specified in get_obs_pert_inputs() and not here) +! +! +! NOTES: +! +! Field-of-view (FOV) can be specified in units of [km] or [deg] lat/lon. +! Note the special case of FOV=0. below. +! If FOV is specified in units of [km], the FOV in units of [deg] lat/lon that +! is used to compute observation predictions will depend on latitude. +! If FOV is specified in units of [deg] lat/lon, its value remains constant and +! is independent of latitude. +! The choice of units also determines the shape function that is used to +! compute the observation predictions. +! Units of [km] are meant for observations that are based on relatively +! coarse-scale measurements (such as microwave data). The resolution of such obs +! in units of [km] is approximately constant across the globe and independent +! of latitude. Observation predictions are computed by averaging tile-based +! model forecasts out to a distance of fac_search_FOV_km*FOV using a Gaussian kernel, +! where fac_search_FOV_km=2.0 as of 28 March 2015. +! Specifically, the normalized square distance is defined as +! +! ndist2 = dx^2/FOV_x^2 + dy^2/FOV_y^2 +! +! where FOV_x and dx are the meridional FOV and the meridional distance between the obs +! and the tile (in units of deg lat/lon), with FOV_x proportional to 1/cos(lat). +! FOV_y and dy are the corresponding zonal values. +! The weights are then proportional to +! +! exp( -0.5*ndist2 ) +! +! The averaging is therefore over an ellipse in lat/lon space, with weights +! decreasing away from the center of the observation. +! A 2.0*FOV averaging footprint encapsulates about 91% of the power. A 1.0*FOV +! averaging footprint would encapsulate about 47% of the power. These numbers +! are meant to be approximately consistent with FOV numbers for microwave radiometers +! (see 3 Dec 2014 email from Ed Kim reproduced below). +! Note that weights are further adjusted based on tile area. +! Units of [deg] lat/lon are meant for observations that are based on +! relatively high-resolution measurements (such as infrared data). Such +! observations are often available on a lat/lon grid that is much coarser than +! the footprint of the underlying observations. The assimilated data product +! therefore has a resolution that varies with latitude. Observation predictions are +! computed by averaging over a constant kernel out to a distance of FOV. +! The averaging is therefore over a circle in lat/lon space, with weights that do not +! depend on the distance from the center of the observation. +! (Note that weights are further adjusted based on tile area.) +! If FOV=0., observation predictions are computed by assigning the model forecast +! associated with the tile to which the observation is formally assigned. +! This is useful if the resolution of the assimilated observations is higher +! than that of the model tile space. This might be the case for snow-cover-fraction +! observations. FOV=0 can also be useful for tile-based synthetic observations. +! +! +! ------------------------------------------------------------------------ +! +! Date: Wed, 3 Dec 2014 11:21:30 -0600 +! From: +! To: , +! Subject: FW: [SMAP] antenna pattern question +! +! Hi Rolf & Gabrielle, +! +! First, a little terminology: the weighted integral is what Level 1 folks call +! "beam efficiency". So, apparently, Steven is assuming the "-3dB beam efficiency" +! is ~50%. The calculated [SMAP] beam efficiency within the -3dB contour is +! 53.40% (v-pol), 53.83% (h-pol). +! If you draw the -3dB contour on the Earth's surface, for h-pol, 53.83% of the energy +! comes from inside the contour, and 100-53.83 = 46.17% comes from outside the contour. +! The accuracy of the 1/10 and 1/100 digits is questionable, anyway. +! So, if you used 53% for v-pol and 54% for h-pol, you should be fine. +! I guess this means Steven was not far off, if he is using "50%." +! This -3dB beam efficiency means we have significant energy coming from outside +! the 3dB footprint, which is the footprint we use to come up with the "40 km" footprint +! size number. +! And, this is why many folks who use microwave instruments prefer to use a contour that +! encloses a higher % of the beam energy as a better measure of the footprint size. +! One such measure is the "main beam efficiency (MBE)." This beamwidth is usually taken +! to be 2.5 times the 3dB beamwidth. The corresponding footprint size is then +! 2.5x 40km = 100km. +! The last calculation put the MBE at 89.23 for V-pol and 89.33 for H-pol. +! So, for h-pol, 89.33% of the energy comes from inside a 100km footprint, +! and 100-89.33 = 10.67% from outside. +! - Ed +! +! ------------------------------------------------------------------------ +! +! IMPORTANT: The number of measurement species defined below must *match* +! global parameter "N_obs_species_nml" +! +! Multi-angular observations (eg., SMOS) are defined as a single +! species here (in the nml file) and are later split into +! multiple species, each having a unique incidence angle +! (see subroutine read_ens_upd_inputs()) +! +! +! ------------------------------------------------------------------------ + +! 1 = AMSR_E_L2_Soil_Moisture_A (A = ascending = "day") + +obs_param_nml( 1)%descr = 'ae_l2_sm_a' +obs_param_nml( 1)%orbit = 1 +obs_param_nml( 1)%pol = 0 +obs_param_nml( 1)%N_ang = 0 +obs_param_nml( 1)%freq = 10.65e9 +obs_param_nml( 1)%FOV = 20. +obs_param_nml( 1)%FOV_units = 'km' +obs_param_nml( 1)%assim = .false. +obs_param_nml( 1)%scale = .false. +obs_param_nml( 1)%getinnov = .false. +obs_param_nml( 1)%RTM_ID = 0 +obs_param_nml( 1)%bias_Npar = 0 +obs_param_nml( 1)%bias_trel = 864000 +obs_param_nml( 1)%bias_tcut = 432000 +obs_param_nml( 1)%nodata = -9999. +obs_param_nml( 1)%varname = 'sfmc' +obs_param_nml( 1)%units = 'm3/m3' +obs_param_nml( 1)%path = '/land/l_data/AMSR/data/AMSR_E_L2_Land_V001/' +obs_param_nml( 1)%name = 'AMSR_E_L2_Land_' +obs_param_nml( 1)%maskpath = '' +obs_param_nml( 1)%maskname = '' +obs_param_nml( 1)%scalepath = '' +obs_param_nml( 1)%scalename = '' +obs_param_nml( 1)%flistpath = '' +obs_param_nml( 1)%flistname = '' +obs_param_nml( 1)%errstd = .02 +obs_param_nml( 1)%std_normal_max = 2.5 +obs_param_nml( 1)%zeromean = .true. +obs_param_nml( 1)%coarsen_pert = .false. +obs_param_nml( 1)%xcorr = 0.25 +obs_param_nml( 1)%ycorr = 0.25 +obs_param_nml( 1)%adapt = 0 + +! ------------------- +! +! 2 = AMSR_E_L2_Soil_Moisture_D (D = descending = "night") + +obs_param_nml( 2)%descr = 'ae_l2_sm_d' +obs_param_nml( 2)%orbit = 2 +obs_param_nml( 2)%pol = 0 +obs_param_nml( 2)%N_ang = 0 +obs_param_nml( 2)%freq = 10.65e9 +obs_param_nml( 2)%FOV = 20. +obs_param_nml( 2)%FOV_units = 'km' +obs_param_nml( 2)%assim = .false. +obs_param_nml( 2)%scale = .false. +obs_param_nml( 2)%getinnov = .false. +obs_param_nml( 2)%RTM_ID = 0 +obs_param_nml( 2)%bias_Npar = 0 +obs_param_nml( 2)%bias_trel = 864000 +obs_param_nml( 2)%bias_tcut = 432000 +obs_param_nml( 2)%nodata = -9999. +obs_param_nml( 2)%varname = 'sfmc' +obs_param_nml( 2)%units = 'm3/m3' +obs_param_nml( 2)%path = '/land/l_data/AMSR/data/AMSR_E_L2_Land_V001/' +obs_param_nml( 2)%name = 'AMSR_E_L2_Land_' +obs_param_nml( 2)%maskpath = '' +obs_param_nml( 2)%maskname = '' +obs_param_nml( 2)%scalepath = '' +obs_param_nml( 2)%scalename = '' +obs_param_nml( 2)%flistpath = '' +obs_param_nml( 2)%flistname = '' +obs_param_nml( 2)%errstd = .02 +obs_param_nml( 2)%std_normal_max = 2.5 +obs_param_nml( 2)%zeromean = .true. +obs_param_nml( 2)%coarsen_pert = .false. +obs_param_nml( 2)%xcorr = 0.25 +obs_param_nml( 2)%ycorr = 0.25 +obs_param_nml( 2)%adapt = 0 + +! -------------------------------------------------------------------- +! +! 3 = ISCCP_Tskin_GSWP2_grid_V1 + +obs_param_nml( 3)%descr = 'isccp_tskin_gswp2_v1' +obs_param_nml( 3)%orbit = 4 +obs_param_nml( 3)%pol = 0 +obs_param_nml( 3)%N_ang = 0 +obs_param_nml( 3)%freq = 0. +obs_param_nml( 3)%FOV = 0.6 +obs_param_nml( 3)%FOV_units = 'deg' +obs_param_nml( 3)%assim = .false. +obs_param_nml( 3)%scale = .false. +obs_param_nml( 3)%getinnov = .false. +obs_param_nml( 3)%RTM_ID = 0 +obs_param_nml( 3)%bias_Npar = 0 +obs_param_nml( 3)%bias_trel = 864000 +obs_param_nml( 3)%bias_tcut = 432000 +obs_param_nml( 3)%nodata = -9999. +obs_param_nml( 3)%varname = 'tsurf' +obs_param_nml( 3)%units = 'K' +obs_param_nml( 3)%path = '/land/l_data/ISCCP/GSWP2_1by1_V1/' +obs_param_nml( 3)%name = 'isccpdx_tskin.' +obs_param_nml( 3)%maskpath = '' +obs_param_nml( 3)%maskname = '' +obs_param_nml( 3)%scalepath = '' +obs_param_nml( 3)%scalename = '' +obs_param_nml( 3)%flistpath = '' +obs_param_nml( 3)%flistname = '' +obs_param_nml( 3)%errstd = 3. +obs_param_nml( 3)%std_normal_max = 2.5 +obs_param_nml( 3)%zeromean = .true. +obs_param_nml( 3)%coarsen_pert = .false. +obs_param_nml( 3)%xcorr = 0.6 +obs_param_nml( 3)%ycorr = 0.6 +obs_param_nml( 3)%adapt = 0 + +! -------------------------------------------------------------------- +! +! 4 = RedArkOSSE_sm + +obs_param_nml( 4)%descr = 'RedArkOSSE_sm' +obs_param_nml( 4)%orbit = 0 +obs_param_nml( 4)%pol = 0 +obs_param_nml( 4)%N_ang = 0 +obs_param_nml( 4)%freq = 0. +obs_param_nml( 4)%FOV = 0. +obs_param_nml( 4)%FOV_units = 'deg' +obs_param_nml( 4)%assim = .false. +obs_param_nml( 4)%scale = .false. +obs_param_nml( 4)%getinnov = .false. +obs_param_nml( 4)%RTM_ID = 0 +obs_param_nml( 4)%bias_Npar = 0 +obs_param_nml( 4)%bias_trel = 864000 +obs_param_nml( 4)%bias_tcut = 432000 +obs_param_nml( 4)%nodata = -999. +obs_param_nml( 4)%varname = 'sfmc' +obs_param_nml( 4)%units = 'm3/m3' +obs_param_nml( 4)%path = '/land/l_data/RedArk/Retrievals_36km/retrievals_20060508/' +obs_param_nml( 4)%name = 'SM_retrieval.' +obs_param_nml( 4)%maskpath = '' +obs_param_nml( 4)%maskname = '' +obs_param_nml( 4)%scalepath = '.' +obs_param_nml( 4)%scalename = '.' +obs_param_nml( 4)%flistpath = '' +obs_param_nml( 4)%flistname = '' +obs_param_nml( 4)%errstd = .04 +obs_param_nml( 4)%std_normal_max = 2.5 +obs_param_nml( 4)%zeromean = .true. +obs_param_nml( 4)%coarsen_pert = .false. +obs_param_nml( 4)%xcorr = 0. +obs_param_nml( 4)%ycorr = 0. +obs_param_nml( 4)%adapt = 0 + +! ------------------- +! +! 5 = RedArkOSSE_truth_50mm + +obs_param_nml( 5)%descr = 'RedArkOSSE_truth_50mm' +obs_param_nml( 5)%orbit = 0 +obs_param_nml( 5)%pol = 0 +obs_param_nml( 5)%N_ang = 0 +obs_param_nml( 5)%freq = 0. +obs_param_nml( 5)%FOV = 0. +obs_param_nml( 5)%FOV_units = 'deg' +obs_param_nml( 5)%assim = .false. +obs_param_nml( 5)%scale = .false. +obs_param_nml( 5)%getinnov = .false. +obs_param_nml( 5)%RTM_ID = 0 +obs_param_nml( 5)%bias_Npar = 0 +obs_param_nml( 5)%bias_trel = 864000 +obs_param_nml( 5)%bias_tcut = 432000 +obs_param_nml( 5)%nodata = -999. +obs_param_nml( 5)%varname = 'sfmc' +obs_param_nml( 5)%units = 'm3/m3' +obs_param_nml( 5)%path = '/land/l_data/RedArk/Truth/50mm_Soil_Moisture_Truth/' +obs_param_nml( 5)%name = 'red_ark_50mm.sm.' +obs_param_nml( 5)%maskpath = '' +obs_param_nml( 5)%maskname = '' +obs_param_nml( 5)%scalepath = '.' +obs_param_nml( 5)%scalename = '.' +obs_param_nml( 5)%flistpath = '' +obs_param_nml( 5)%flistname = '' +obs_param_nml( 5)%errstd = .0 +obs_param_nml( 5)%std_normal_max = 2.5 +obs_param_nml( 5)%zeromean = .true. +obs_param_nml( 5)%coarsen_pert = .false. +obs_param_nml( 5)%xcorr = 0. +obs_param_nml( 5)%ycorr = 0. +obs_param_nml( 5)%adapt = 0 + +! ------------------- +! +! 6 = RedArkOSSE_truth_400mm + +obs_param_nml( 6)%descr = 'RedArkOSSE_truth_400mm' +obs_param_nml( 6)%orbit = 0 +obs_param_nml( 6)%pol = 0 +obs_param_nml( 6)%N_ang = 0 +obs_param_nml( 6)%freq = 0. +obs_param_nml( 6)%FOV = 0. +obs_param_nml( 6)%FOV_units = 'deg' +obs_param_nml( 6)%assim = .false. +obs_param_nml( 6)%scale = .false. +obs_param_nml( 6)%getinnov = .false. +obs_param_nml( 6)%RTM_ID = 0 +obs_param_nml( 6)%bias_Npar = 0 +obs_param_nml( 6)%bias_trel = 864000 +obs_param_nml( 6)%bias_tcut = 432000 +obs_param_nml( 6)%nodata = -999. +obs_param_nml( 6)%varname = 'rzmc' +obs_param_nml( 6)%units = 'm3/m3' +obs_param_nml( 6)%path = '/land/l_data/RedArk/Truth/400mm_Soil_Moisture_Truth/' +obs_param_nml( 6)%name = 'red_ark_400mm.sm.' +obs_param_nml( 6)%maskpath = '' +obs_param_nml( 6)%maskname = '' +obs_param_nml( 6)%scalepath = '.' +obs_param_nml( 6)%scalename = '.' +obs_param_nml( 6)%flistpath = '' +obs_param_nml( 6)%flistname = '' +obs_param_nml( 6)%errstd = .0 +obs_param_nml( 6)%std_normal_max = 2.5 +obs_param_nml( 6)%zeromean = .true. +obs_param_nml( 6)%coarsen_pert = .false. +obs_param_nml( 6)%xcorr = 0. +obs_param_nml( 6)%ycorr = 0. +obs_param_nml( 6)%adapt = 0 + +! -------------------------------------------------------------------- +! +! 7 = RedArkOSSE_CLSMsynthSM + +obs_param_nml( 7)%descr = 'RedArkOSSE_CLSMsynthSM' +obs_param_nml( 7)%orbit = 0 +obs_param_nml( 7)%pol = 0 +obs_param_nml( 7)%N_ang = 0 +obs_param_nml( 7)%freq = 0. +obs_param_nml( 7)%FOV = 0. +obs_param_nml( 7)%FOV_units = 'deg' +obs_param_nml( 7)%assim = .false. +obs_param_nml( 7)%scale = .false. +obs_param_nml( 7)%getinnov = .false. +obs_param_nml( 7)%RTM_ID = 0 +obs_param_nml( 7)%bias_Npar = 0 +obs_param_nml( 7)%bias_trel = 864000 +obs_param_nml( 7)%bias_tcut = 432000 +obs_param_nml( 7)%nodata = -9999. +obs_param_nml( 7)%varname = 'sfmc' +obs_param_nml( 7)%units = 'm3/m3' +obs_param_nml( 7)%path = '/land/l_data/RedArk_OSSE/data/Retrievals_CLSM_synth/M0001_P0001_R0001_URI/std_synth_obs_0.020/' +obs_param_nml( 7)%name = 'CLSM_synth_sm.' +obs_param_nml( 7)%maskpath = '' +obs_param_nml( 7)%maskname = '' +obs_param_nml( 7)%scalepath = '.' +obs_param_nml( 7)%scalename = '.' +obs_param_nml( 7)%flistpath = '' +obs_param_nml( 7)%flistname = '' +obs_param_nml( 7)%errstd = .04 +obs_param_nml( 7)%std_normal_max = 2.5 +obs_param_nml( 7)%zeromean = .true. +obs_param_nml( 7)%coarsen_pert = .false. +obs_param_nml( 7)%xcorr = 0. +obs_param_nml( 7)%ycorr = 0. +obs_param_nml( 7)%adapt = 0 + +! -------------------------------------------------------------------- +! +! 8 = VivianaOK_CLSMsynthSM + +obs_param_nml( 8)%descr = 'VivianaOK_CLSMsynthSM' +obs_param_nml( 8)%orbit = 0 +obs_param_nml( 8)%pol = 0 +obs_param_nml( 8)%N_ang = 0 +obs_param_nml( 8)%freq = 0. +obs_param_nml( 8)%FOV = 0. +obs_param_nml( 8)%FOV_units = 'deg' +obs_param_nml( 8)%assim = .false. +obs_param_nml( 8)%scale = .false. +obs_param_nml( 8)%getinnov = .false. +obs_param_nml( 8)%RTM_ID = 0 +obs_param_nml( 8)%bias_Npar = 0 +obs_param_nml( 8)%bias_trel = 864000 +obs_param_nml( 8)%bias_tcut = 432000 +obs_param_nml( 8)%nodata = -9999. +obs_param_nml( 8)%varname = 'sfmc' +obs_param_nml( 8)%units = 'm3/m3' +obs_param_nml( 8)%path = '/discover/nobackup/vmaggion/Synth_sfmc/radar_sim/std_synth_sfmc_0.040/' +obs_param_nml( 8)%name = 'synth_sfmc_VivianaOK_' +obs_param_nml( 8)%maskpath = '' +obs_param_nml( 8)%maskname = '' +obs_param_nml( 8)%scalepath = '.' +obs_param_nml( 8)%scalename = '.' +obs_param_nml( 8)%flistpath = '' +obs_param_nml( 8)%flistname = '' +obs_param_nml( 8)%errstd = .04 +obs_param_nml( 8)%std_normal_max = 2.5 +obs_param_nml( 8)%zeromean = .true. +obs_param_nml( 8)%coarsen_pert = .false. +obs_param_nml( 8)%xcorr = 0. +obs_param_nml( 8)%ycorr = 0. +obs_param_nml( 8)%adapt = 0 + +! -------------------------------------------------------------------- +! +! 9 = AMSR_E_sm_LPRM_A_C (A = ascending = "day", C-band) + +obs_param_nml( 9)%descr = 'ae_sm_LPRM_a_C' +obs_param_nml( 9)%orbit = 1 +obs_param_nml( 9)%pol = 0 +obs_param_nml( 9)%N_ang = 0 +obs_param_nml( 9)%freq = 6.925e9 +obs_param_nml( 9)%FOV = 20. +obs_param_nml( 9)%FOV_units = 'km' +obs_param_nml( 9)%assim = .false. +obs_param_nml( 9)%scale = .false. +obs_param_nml( 9)%getinnov = .false. +obs_param_nml( 9)%RTM_ID = 0 +obs_param_nml( 9)%bias_Npar = 0 +obs_param_nml( 9)%bias_trel = 864000 +obs_param_nml( 9)%bias_tcut = 432000 +obs_param_nml( 9)%nodata = -9999. +obs_param_nml( 9)%varname = 'sfmc' +obs_param_nml( 9)%units = 'm3/m3' +obs_param_nml( 9)%path = '/land/l_data/AMSR/data/AMSR_E_sm_LPRM/L2_EASE/bin/' +obs_param_nml( 9)%name = 'AMSRsmUVA.EASE.v03.' +obs_param_nml( 9)%maskpath = '' +obs_param_nml( 9)%maskname = '' +obs_param_nml( 9)%scalepath = '' +obs_param_nml( 9)%scalename = '' +obs_param_nml( 9)%flistpath = '' +obs_param_nml( 9)%flistname = '' +obs_param_nml( 9)%errstd = .04 +obs_param_nml( 9)%std_normal_max = 2.5 +obs_param_nml( 9)%zeromean = .true. +obs_param_nml( 9)%coarsen_pert = .false. +obs_param_nml( 9)%xcorr = 0.25 +obs_param_nml( 9)%ycorr = 0.25 +obs_param_nml( 9)%adapt = 0 + +! ------------------- +! +! 10 = AMSR_E_sm_LPRM_D_C (D = descending = "night", C-band) + +obs_param_nml(10)%descr = 'ae_sm_LPRM_d_C' +obs_param_nml(10)%orbit = 2 +obs_param_nml(10)%pol = 0 +obs_param_nml(10)%N_ang = 0 +obs_param_nml(10)%freq = 6.925e9 +obs_param_nml(10)%FOV = 20. +obs_param_nml(10)%FOV_units = 'km' +obs_param_nml(10)%assim = .false. +obs_param_nml(10)%scale = .false. +obs_param_nml(10)%getinnov = .false. +obs_param_nml(10)%RTM_ID = 0 +obs_param_nml(10)%bias_Npar = 0 +obs_param_nml(10)%bias_trel = 864000 +obs_param_nml(10)%bias_tcut = 432000 +obs_param_nml(10)%nodata = -9999. +obs_param_nml(10)%varname = 'sfmc' +obs_param_nml(10)%units = 'm3/m3' +obs_param_nml(10)%path = '/land/l_data/AMSR/data/AMSR_E_sm_LPRM/L2_EASE/bin/' +obs_param_nml(10)%name = 'AMSRsmUVA.EASE.v03.' +obs_param_nml(10)%maskpath = '' +obs_param_nml(10)%maskname = '' +obs_param_nml(10)%scalepath = '' +obs_param_nml(10)%scalename = '' +obs_param_nml(10)%flistpath = '' +obs_param_nml(10)%flistname = '' +obs_param_nml(10)%errstd = .04 +obs_param_nml(10)%std_normal_max = 2.5 +obs_param_nml(10)%zeromean = .true. +obs_param_nml(10)%coarsen_pert = .false. +obs_param_nml(10)%xcorr = 0.25 +obs_param_nml(10)%ycorr = 0.25 +obs_param_nml(10)%adapt = 0 + +! ------------------- +! +! 11 = AMSR_E_sm_LPRM_A_X (A = ascending = "day", X-band) + +obs_param_nml(11)%descr = 'ae_sm_LPRM_a_X' +obs_param_nml(11)%orbit = 1 +obs_param_nml(11)%pol = 0 +obs_param_nml(11)%N_ang = 0 +obs_param_nml(11)%freq = 10.65e9 +obs_param_nml(11)%FOV = 20. +obs_param_nml(11)%FOV_units = 'km' +obs_param_nml(11)%assim = .false. +obs_param_nml(11)%scale = .false. +obs_param_nml(11)%getinnov = .false. +obs_param_nml(11)%RTM_ID = 0 +obs_param_nml(11)%bias_Npar = 0 +obs_param_nml(11)%bias_trel = 864000 +obs_param_nml(11)%bias_tcut = 432000 +obs_param_nml(11)%nodata = -9999. +obs_param_nml(11)%varname = 'sfmc' +obs_param_nml(11)%units = 'm3/m3' +obs_param_nml(11)%path = '/land/l_data/AMSR/data/AMSR_E_sm_LPRM/L2_EASE/bin/' +obs_param_nml(11)%name = 'AMSRsmUVA.EASE.v03.' +obs_param_nml(11)%maskpath = '' +obs_param_nml(11)%maskname = '' +obs_param_nml(11)%scalepath = '' +obs_param_nml(11)%scalename = '' +obs_param_nml(11)%flistpath = '' +obs_param_nml(11)%flistname = '' +obs_param_nml(11)%errstd = .04 +obs_param_nml(11)%std_normal_max = 2.5 +obs_param_nml(11)%zeromean = .true. +obs_param_nml(11)%coarsen_pert = .false. +obs_param_nml(11)%xcorr = 0.25 +obs_param_nml(11)%ycorr = 0.25 +obs_param_nml(11)%adapt = 0 + +! ------------------- +! +! 12 = AMSR_E_sm_LPRM_D_X (D = descending = "night", X-band) + +obs_param_nml(12)%descr = 'ae_sm_LPRM_d_X' +obs_param_nml(12)%orbit = 2 +obs_param_nml(12)%pol = 0 +obs_param_nml(12)%N_ang = 0 +obs_param_nml(12)%freq = 10.65e9 +obs_param_nml(12)%FOV = 20. +obs_param_nml(12)%FOV_units = 'km' +obs_param_nml(12)%assim = .false. +obs_param_nml(12)%scale = .false. +obs_param_nml(12)%getinnov = .false. +obs_param_nml(12)%RTM_ID = 0 +obs_param_nml(12)%bias_Npar = 0 +obs_param_nml(12)%bias_trel = 864000 +obs_param_nml(12)%bias_tcut = 432000 +obs_param_nml(12)%nodata = -9999. +obs_param_nml(12)%varname = 'sfmc' +obs_param_nml(12)%units = 'm3/m3' +obs_param_nml(12)%path = '/land/l_data/AMSR/data/AMSR_E_sm_LPRM/L2_EASE/bin/' +obs_param_nml(12)%name = 'AMSRsmUVA.EASE.v03.' +obs_param_nml(12)%maskpath = '' +obs_param_nml(12)%maskname = '' +obs_param_nml(12)%scalepath = '' +obs_param_nml(12)%scalename = '' +obs_param_nml(12)%flistpath = '' +obs_param_nml(12)%flistname = '' +obs_param_nml(12)%errstd = .04 +obs_param_nml(12)%std_normal_max = 2.5 +obs_param_nml(12)%zeromean = .true. +obs_param_nml(12)%coarsen_pert = .false. +obs_param_nml(12)%xcorr = 0.25 +obs_param_nml(12)%ycorr = 0.25 +obs_param_nml(12)%adapt = 0 + +! -------------------------------------------------------------------- +! +! 13 = ASCAT_SM_A (ASCAT soil moisture ascending) +! +! ASCAT: VV-pol, incidence angle 25-65 deg +! for now keep N_ang=0, pol=0 +! - reichle, 30 Jun 2015 + +obs_param_nml(13)%descr = 'ASCAT_SM_A' +obs_param_nml(13)%orbit = 1 +obs_param_nml(13)%pol = 0 +obs_param_nml(13)%N_ang = 0 +obs_param_nml(13)%freq = 5.255e9 +obs_param_nml(13)%FOV = 20. +obs_param_nml(13)%FOV_units = 'km' +obs_param_nml(13)%assim = .false. +obs_param_nml(13)%scale = .false. +obs_param_nml(13)%getinnov = .false. +obs_param_nml(13)%RTM_ID = 0 +obs_param_nml(13)%bias_Npar = 0 +obs_param_nml(13)%bias_trel = 864000 +obs_param_nml(13)%bias_tcut = 432000 +obs_param_nml(13)%nodata = -9999. +obs_param_nml(13)%varname = 'sfmc' +obs_param_nml(13)%units = 'm3/m3' +obs_param_nml(13)%path = '/discover/nobackup/rreichle/l_data/ASCAT/TUW_W5.4/EASE/CONUS/bin/' +obs_param_nml(13)%name = 'SDS_' +obs_param_nml(13)%maskpath = '' +obs_param_nml(13)%maskname = '' +obs_param_nml(13)%scalepath = '' +obs_param_nml(13)%scalename = '' +obs_param_nml(13)%flistpath = '' +obs_param_nml(13)%flistname = '' +obs_param_nml(13)%errstd = .04 +obs_param_nml(13)%std_normal_max = 2.5 +obs_param_nml(13)%zeromean = .true. +obs_param_nml(13)%coarsen_pert = .false. +obs_param_nml(13)%xcorr = 0.25 +obs_param_nml(13)%ycorr = 0.25 +obs_param_nml(13)%adapt = 0 + +! ------------------- +! +! 14 = ASCAT_SM_D (ASCAT soil moisture descending) +! +! TO DO: What is pol of backscatter used in retrieval? +! TO DO: How deal w/ inc angle? +! http://oiswww.eumetsat.org/WEBOPS/eps-pg/ASCAT/ASCAT-PG-4ProdOverview.htm + +obs_param_nml(14)%descr = 'ASCAT_SM_D' +obs_param_nml(14)%orbit = 2 +obs_param_nml(14)%pol = 0 +obs_param_nml(14)%N_ang = 0 +obs_param_nml(14)%freq = 5.255e9 +obs_param_nml(14)%FOV = 20. +obs_param_nml(14)%FOV_units = 'km' +obs_param_nml(14)%assim = .false. +obs_param_nml(14)%scale = .false. +obs_param_nml(14)%getinnov = .false. +obs_param_nml(14)%RTM_ID = 0 +obs_param_nml(14)%bias_Npar = 0 +obs_param_nml(14)%bias_trel = 864000 +obs_param_nml(14)%bias_tcut = 432000 +obs_param_nml(14)%nodata = -9999. +obs_param_nml(14)%varname = 'sfmc' +obs_param_nml(14)%units = 'm3/m3' +obs_param_nml(14)%path = '/discover/nobackup/rreichle/l_data/ASCAT/TUW_W5.4/EASE/CONUS/bin/' +obs_param_nml(14)%name = 'SDS_' +obs_param_nml(14)%maskpath = '' +obs_param_nml(14)%maskname = '' +obs_param_nml(14)%scalepath = '' +obs_param_nml(14)%scalename = '' +obs_param_nml(14)%flistpath = '' +obs_param_nml(14)%flistname = '' +obs_param_nml(14)%errstd = .04 +obs_param_nml(14)%std_normal_max = 2.5 +obs_param_nml(14)%zeromean = .true. +obs_param_nml(14)%coarsen_pert = .false. +obs_param_nml(14)%xcorr = 0.25 +obs_param_nml(14)%ycorr = 0.25 +obs_param_nml(14)%adapt = 0 + +! -------------------------------------------------------------------- +! +! 15 = SMOS_SM_A (SMOS soil moisture ascending) + +obs_param_nml(15)%descr = 'SMOS_SM_A' +obs_param_nml(15)%orbit = 1 +obs_param_nml(15)%pol = 0 +obs_param_nml(15)%N_ang = 0 +obs_param_nml(15)%freq = 1.41e9 +obs_param_nml(15)%FOV = 20. +obs_param_nml(15)%FOV_units = 'km' +obs_param_nml(15)%assim = .false. +obs_param_nml(15)%scale = .false. +obs_param_nml(15)%getinnov = .false. +obs_param_nml(15)%RTM_ID = 0 +obs_param_nml(15)%bias_Npar = 0 +obs_param_nml(15)%bias_trel = 864000 +obs_param_nml(15)%bias_tcut = 432000 +obs_param_nml(15)%nodata = -9999. +obs_param_nml(15)%varname = 'sfmc' +obs_param_nml(15)%units = 'm3/m3' +obs_param_nml(15)%path = '/discover/nobackup/projects/gmao/ssd/land/l_data/SMOS/EASEv2/ESA_REPR/SMOS_M36_SMUDP2/' +obs_param_nml(15)%name = '' +obs_param_nml(15)%maskpath = '' +obs_param_nml(15)%maskname = '' +obs_param_nml(15)%scalepath = '' +obs_param_nml(15)%scalename = '' +obs_param_nml(15)%flistpath = '' +obs_param_nml(15)%flistname = '' +obs_param_nml(15)%errstd = .04 +obs_param_nml(15)%std_normal_max = 2.5 +obs_param_nml(15)%zeromean = .true. +obs_param_nml(15)%coarsen_pert = .false. +obs_param_nml(15)%xcorr = 0.25 +obs_param_nml(15)%ycorr = 0.25 +obs_param_nml(15)%adapt = 0 + +! ------------------- +! +! 16 = SMOS_SM_D (SMOS soil moisture descending) + +obs_param_nml(16)%descr = 'SMOS_SM_D' +obs_param_nml(16)%orbit = 2 +obs_param_nml(16)%pol = 0 +obs_param_nml(16)%N_ang = 0 +obs_param_nml(16)%freq = 1.41e9 +obs_param_nml(16)%FOV = 20. +obs_param_nml(16)%FOV_units = 'km' +obs_param_nml(16)%assim = .false. +obs_param_nml(16)%scale = .false. +obs_param_nml(16)%getinnov = .false. +obs_param_nml(16)%RTM_ID = 0 +obs_param_nml(16)%bias_Npar = 0 +obs_param_nml(16)%bias_trel = 864000 +obs_param_nml(16)%bias_tcut = 432000 +obs_param_nml(16)%nodata = -9999. +obs_param_nml(16)%varname = 'sfmc' +obs_param_nml(16)%units = 'm3/m3' +obs_param_nml(16)%path = '/discover/nobackup/projects/gmao/ssd/land/l_data/SMOS/EASEv2/ESA_REPR/SMOS_M36_SMUDP2/' +obs_param_nml(16)%name = '' +obs_param_nml(16)%maskpath = '' +obs_param_nml(16)%maskname = '' +obs_param_nml(16)%scalepath = '' +obs_param_nml(16)%scalename = '' +obs_param_nml(16)%flistpath = '' +obs_param_nml(16)%flistname = '' +obs_param_nml(16)%errstd = .04 +obs_param_nml(16)%std_normal_max = 2.5 +obs_param_nml(16)%zeromean = .true. +obs_param_nml(16)%coarsen_pert = .false. +obs_param_nml(16)%xcorr = 0.25 +obs_param_nml(16)%ycorr = 0.25 +obs_param_nml(16)%adapt = 0 + +! -------------------------------------------------------------------- +! +! SMOS multi-angular brightness temperature +! +! "A" = ascending (6am *SMOS* overpass) +! "D" = descending (6pm *SMOS* overpass) +! +! "Tbh" = h-pol Tb +! "Tbv" = v-pol Tb +! +! +! "Regular" vs. "fitted" SMOS brightness temperatures: +! +! "Regular" Tb data ('SMOS_reg_Tb*'): +! Derived from SMOS SCLF1C data by Gabrielle De Lannoy. Observations within +! one-degree angular bins for a given time and location are averaged. +! Various quality controls steps are applied. +! A typical assimilation setup uses 7 angles. +! For details see De Lannoy et al (2013) doi:10.1175/JHM-D-12-092.1. +! +! "Fitted" Tb data ('SMOS_fit_Tb*'): +! Derived from "regular" SMOS Tb data by Gabrielle De Lannoy. Observations +! for a given time and location derived by fitting across available incidence +! angles. The resulting fitted observation at 40 deg incidence angle corresponds +! roughly to what the SMAP radiometer observes (a.k.a. "SMOS40"). +! A typical assimilation setup uses only one angle. +! +! +! Frequency: +! +! Date: Wed, 8 Jun 2011 02:34:40 -0500 +! From: +! To: +! +! In the ATBD it is written that the central frequency is 1.413 GHz. I +! have asked around but it seems more complicated than expected because +! the 64 antennas are working around 1.410 GHz (+-0.008 GHz) and there is +! no exact value for the frequency (mostly because the frequency of each +! antenna depends on the temperature...). Here we work with 1.413 GHz. +! + +! ------------------- +! +! 17 = SMOS_reg_Tbh_A + +obs_param_nml(17)%descr = 'SMOS_reg_Tbh_A' +obs_param_nml(17)%orbit = 1 +obs_param_nml(17)%pol = 1 +obs_param_nml(17)%N_ang = 7 +obs_param_nml(17)%ang(1) = 30. +obs_param_nml(17)%ang(2) = 35. +obs_param_nml(17)%ang(3) = 40. +obs_param_nml(17)%ang(4) = 45. +obs_param_nml(17)%ang(5) = 50. +obs_param_nml(17)%ang(6) = 55. +obs_param_nml(17)%ang(7) = 60. +obs_param_nml(17)%freq = 1.41e9 +obs_param_nml(17)%FOV = 20. +obs_param_nml(17)%FOV_units = 'km' +obs_param_nml(17)%assim = .false. +obs_param_nml(17)%scale = .false. +obs_param_nml(17)%getinnov = .false. +obs_param_nml(17)%RTM_ID = 2 +obs_param_nml(17)%bias_Npar = 0 +obs_param_nml(17)%bias_trel = 864000 +obs_param_nml(17)%bias_tcut = 432000 +obs_param_nml(17)%nodata = -9999. +obs_param_nml(17)%varname = 'Tb' +obs_param_nml(17)%units = 'K' +obs_param_nml(17)%path = '/discover/nobackup/projects/gmao/ssd/land/l_data/SMOS/EASEv2/ESA_REPR/SMOS_M36_SCLF1C_reg_nosky_noatm_v620_ESA_v102/' +obs_param_nml(17)%name = '' +obs_param_nml(17)%maskpath = '' +obs_param_nml(17)%maskname = '' +obs_param_nml(17)%scalepath = '' +obs_param_nml(17)%scalename = '' +obs_param_nml(17)%flistpath = '' +obs_param_nml(17)%flistname = '' +obs_param_nml(17)%errstd = 4. +obs_param_nml(17)%std_normal_max = 2.5 +obs_param_nml(17)%zeromean = .true. +obs_param_nml(17)%coarsen_pert = .false. +obs_param_nml(17)%xcorr = 0.25 +obs_param_nml(17)%ycorr = 0.25 +obs_param_nml(17)%adapt = 0 + +! ------------------- +! +! 18 = SMOS_reg_Tbh_D + +obs_param_nml(18)%descr = 'SMOS_reg_Tbh_D' +obs_param_nml(18)%orbit = 2 +obs_param_nml(18)%pol = 1 +obs_param_nml(18)%N_ang = 7 +obs_param_nml(18)%ang(1) = 30. +obs_param_nml(18)%ang(2) = 35. +obs_param_nml(18)%ang(3) = 40. +obs_param_nml(18)%ang(4) = 45. +obs_param_nml(18)%ang(5) = 50. +obs_param_nml(18)%ang(6) = 55. +obs_param_nml(18)%ang(7) = 60. +obs_param_nml(18)%freq = 1.41e9 +obs_param_nml(18)%FOV = 20. +obs_param_nml(18)%FOV_units = 'km' +obs_param_nml(18)%assim = .false. +obs_param_nml(18)%scale = .false. +obs_param_nml(18)%getinnov = .false. +obs_param_nml(18)%RTM_ID = 2 +obs_param_nml(18)%bias_Npar = 0 +obs_param_nml(18)%bias_trel = 864000 +obs_param_nml(18)%bias_tcut = 432000 +obs_param_nml(18)%nodata = -9999. +obs_param_nml(18)%varname = 'Tb' +obs_param_nml(18)%units = 'K' +obs_param_nml(18)%path = '/discover/nobackup/projects/gmao/ssd/land/l_data/SMOS/EASEv2/ESA_REPR/SMOS_M36_SCLF1C_reg_nosky_noatm_v620_ESA_v102/' +obs_param_nml(18)%name = '' +obs_param_nml(18)%maskpath = '' +obs_param_nml(18)%maskname = '' +obs_param_nml(18)%scalepath = '' +obs_param_nml(18)%scalename = '' +obs_param_nml(18)%flistpath = '' +obs_param_nml(18)%flistname = '' +obs_param_nml(18)%errstd = 4. +obs_param_nml(18)%std_normal_max = 2.5 +obs_param_nml(18)%zeromean = .true. +obs_param_nml(18)%coarsen_pert = .false. +obs_param_nml(18)%xcorr = 0.25 +obs_param_nml(18)%ycorr = 0.25 +obs_param_nml(18)%adapt = 0 + +! ------------------- +! +! 19 = SMOS_reg_Tbv_A + +obs_param_nml(19)%descr = 'SMOS_reg_Tbv_A' +obs_param_nml(19)%orbit = 1 +obs_param_nml(19)%pol = 2 +obs_param_nml(19)%N_ang = 7 +obs_param_nml(19)%ang(1) = 30. +obs_param_nml(19)%ang(2) = 35. +obs_param_nml(19)%ang(3) = 40. +obs_param_nml(19)%ang(4) = 45. +obs_param_nml(19)%ang(5) = 50. +obs_param_nml(19)%ang(6) = 55. +obs_param_nml(19)%ang(7) = 60. +obs_param_nml(19)%freq = 1.41e9 +obs_param_nml(19)%FOV = 20. +obs_param_nml(19)%FOV_units = 'km' +obs_param_nml(19)%assim = .false. +obs_param_nml(19)%scale = .false. +obs_param_nml(19)%getinnov = .false. +obs_param_nml(19)%RTM_ID = 2 +obs_param_nml(19)%bias_Npar = 0 +obs_param_nml(19)%bias_trel = 864000 +obs_param_nml(19)%bias_tcut = 432000 +obs_param_nml(19)%nodata = -9999. +obs_param_nml(19)%varname = 'Tb' +obs_param_nml(19)%units = 'K' +obs_param_nml(19)%path = '/discover/nobackup/projects/gmao/ssd/land/l_data/SMOS/EASEv2/ESA_REPR/SMOS_M36_SCLF1C_reg_nosky_noatm_v620_ESA_v102/' +obs_param_nml(19)%name = '' +obs_param_nml(19)%maskpath = '' +obs_param_nml(19)%maskname = '' +obs_param_nml(19)%scalepath = '' +obs_param_nml(19)%scalename = '' +obs_param_nml(19)%flistpath = '' +obs_param_nml(19)%flistname = '' +obs_param_nml(19)%errstd = 4. +obs_param_nml(19)%std_normal_max = 2.5 +obs_param_nml(19)%zeromean = .true. +obs_param_nml(19)%coarsen_pert = .false. +obs_param_nml(19)%xcorr = 0.25 +obs_param_nml(19)%ycorr = 0.25 +obs_param_nml(19)%adapt = 0 + +! ------------------- +! +! 20 = SMOS_reg_Tbv_D + +obs_param_nml(20)%descr = 'SMOS_reg_Tbv_D' +obs_param_nml(20)%orbit = 2 +obs_param_nml(20)%pol = 2 +obs_param_nml(20)%N_ang = 7 +obs_param_nml(20)%ang(1) = 30. +obs_param_nml(20)%ang(2) = 35. +obs_param_nml(20)%ang(3) = 40. +obs_param_nml(20)%ang(4) = 45. +obs_param_nml(20)%ang(5) = 50. +obs_param_nml(20)%ang(6) = 55. +obs_param_nml(20)%ang(7) = 60. +obs_param_nml(20)%freq = 1.41e9 +obs_param_nml(20)%FOV = 20. +obs_param_nml(20)%FOV_units = 'km' +obs_param_nml(20)%assim = .false. +obs_param_nml(20)%scale = .false. +obs_param_nml(20)%getinnov = .false. +obs_param_nml(20)%RTM_ID = 2 +obs_param_nml(20)%bias_Npar = 0 +obs_param_nml(20)%bias_trel = 864000 +obs_param_nml(20)%bias_tcut = 432000 +obs_param_nml(20)%nodata = -9999. +obs_param_nml(20)%varname = 'Tb' +obs_param_nml(20)%units = 'K' +obs_param_nml(20)%path = '/discover/nobackup/projects/gmao/ssd/land/l_data/SMOS/EASEv2/ESA_REPR/SMOS_M36_SCLF1C_reg_nosky_noatm_v620_ESA_v102/' +obs_param_nml(20)%name = '' +obs_param_nml(20)%maskpath = '' +obs_param_nml(20)%maskname = '' +obs_param_nml(20)%scalepath = '' +obs_param_nml(20)%scalename = '' +obs_param_nml(20)%flistpath = '' +obs_param_nml(20)%flistname = '' +obs_param_nml(20)%errstd = 4. +obs_param_nml(20)%std_normal_max = 2.5 +obs_param_nml(20)%zeromean = .true. +obs_param_nml(20)%coarsen_pert = .false. +obs_param_nml(20)%xcorr = 0.25 +obs_param_nml(20)%ycorr = 0.25 +obs_param_nml(20)%adapt = 0 + +! -------------------------------------------------- +! +! 21 = SMOS_fit_Tbh_A + +obs_param_nml(21)%descr = 'SMOS_fit_Tbh_A' +obs_param_nml(21)%orbit = 1 +obs_param_nml(21)%pol = 1 +obs_param_nml(21)%N_ang = 1 +obs_param_nml(21)%ang(1) = 40. +obs_param_nml(21)%freq = 1.41e9 +obs_param_nml(21)%FOV = 20. +obs_param_nml(21)%FOV_units = 'km' +obs_param_nml(21)%assim = .false. +obs_param_nml(21)%scale = .false. +obs_param_nml(21)%getinnov = .false. +obs_param_nml(21)%RTM_ID = 2 +obs_param_nml(21)%bias_Npar = 0 +obs_param_nml(21)%bias_trel = 864000 +obs_param_nml(21)%bias_tcut = 432000 +obs_param_nml(21)%nodata = -9999. +obs_param_nml(21)%varname = 'Tb' +obs_param_nml(21)%units = 'K' +obs_param_nml(21)%path = '/discover/nobackup/projects/gmao/ssd/land/l_data/SMOS/EASEv2/ESA_REPR/SMOS_M36_SCLF1C_fit_nosky_noatm_v620_ESA_v102/SMOS_fit_poly2/' +obs_param_nml(21)%name = '' +obs_param_nml(21)%maskpath = '' +obs_param_nml(21)%maskname = '' +obs_param_nml(21)%scalepath = '' +obs_param_nml(21)%scalename = '' +obs_param_nml(21)%flistpath = '' +obs_param_nml(21)%flistname = '' +obs_param_nml(21)%errstd = 1.5 +obs_param_nml(21)%std_normal_max = 2.5 +obs_param_nml(21)%zeromean = .true. +obs_param_nml(21)%coarsen_pert = .false. +obs_param_nml(21)%xcorr = 0.25 +obs_param_nml(21)%ycorr = 0.25 +obs_param_nml(21)%adapt = 0 + +! ------------------- +! +! 22 = SMOS_fit_Tbh_D + +obs_param_nml(22)%descr = 'SMOS_fit_Tbh_D' +obs_param_nml(22)%orbit = 2 +obs_param_nml(22)%pol = 1 +obs_param_nml(22)%N_ang = 1 +obs_param_nml(22)%ang(1) = 40. +obs_param_nml(22)%freq = 1.41e9 +obs_param_nml(22)%FOV = 20. +obs_param_nml(22)%FOV_units = 'km' +obs_param_nml(22)%assim = .false. +obs_param_nml(22)%scale = .false. +obs_param_nml(22)%getinnov = .false. +obs_param_nml(22)%RTM_ID = 2 +obs_param_nml(22)%bias_Npar = 0 +obs_param_nml(22)%bias_trel = 864000 +obs_param_nml(22)%bias_tcut = 432000 +obs_param_nml(22)%nodata = -9999. +obs_param_nml(22)%varname = 'Tb' +obs_param_nml(22)%units = 'K' +obs_param_nml(22)%path = '/discover/nobackup/projects/gmao/ssd/land/l_data/SMOS/EASEv2/ESA_REPR/SMOS_M36_SCLF1C_fit_nosky_noatm_v620_ESA_v102/SMOS_fit_poly2/' +obs_param_nml(22)%name = '' +obs_param_nml(22)%maskpath = '' +obs_param_nml(22)%maskname = '' +obs_param_nml(22)%scalepath = '' +obs_param_nml(22)%scalename = '' +obs_param_nml(22)%flistpath = '' +obs_param_nml(22)%flistname = '' +obs_param_nml(22)%errstd = 1.5 +obs_param_nml(22)%std_normal_max = 2.5 +obs_param_nml(22)%zeromean = .true. +obs_param_nml(22)%coarsen_pert = .false. +obs_param_nml(22)%xcorr = 0.25 +obs_param_nml(22)%ycorr = 0.25 +obs_param_nml(22)%adapt = 0 + +! ------------------- +! +! 23 = SMOS_fit_Tbv_A + +obs_param_nml(23)%descr = 'SMOS_fit_Tbv_A' +obs_param_nml(23)%orbit = 1 +obs_param_nml(23)%pol = 2 +obs_param_nml(23)%N_ang = 1 +obs_param_nml(23)%ang(1) = 40. +obs_param_nml(23)%freq = 1.41e9 +obs_param_nml(23)%FOV = 20. +obs_param_nml(23)%FOV_units = 'km' +obs_param_nml(23)%assim = .false. +obs_param_nml(23)%scale = .false. +obs_param_nml(23)%getinnov = .false. +obs_param_nml(23)%RTM_ID = 2 +obs_param_nml(23)%bias_Npar = 0 +obs_param_nml(23)%bias_trel = 864000 +obs_param_nml(23)%bias_tcut = 432000 +obs_param_nml(23)%nodata = -9999. +obs_param_nml(23)%varname = 'Tb' +obs_param_nml(23)%units = 'K' +obs_param_nml(23)%path = '/discover/nobackup/projects/gmao/ssd/land/l_data/SMOS/EASEv2/ESA_REPR/SMOS_M36_SCLF1C_fit_nosky_noatm_v620_ESA_v102/SMOS_fit_poly2/' +obs_param_nml(23)%name = '' +obs_param_nml(23)%maskpath = '' +obs_param_nml(23)%maskname = '' +obs_param_nml(23)%scalepath = '' +obs_param_nml(23)%scalename = '' +obs_param_nml(23)%flistpath = '' +obs_param_nml(23)%flistname = '' +obs_param_nml(23)%errstd = 1.5 +obs_param_nml(23)%std_normal_max = 2.5 +obs_param_nml(23)%zeromean = .true. +obs_param_nml(23)%coarsen_pert = .false. +obs_param_nml(23)%xcorr = 0.25 +obs_param_nml(23)%ycorr = 0.25 +obs_param_nml(23)%adapt = 0 + +! ------------------- +! +! 24 = SMOS_fit_Tbv_D + +obs_param_nml(24)%descr = 'SMOS_fit_Tbv_D' +obs_param_nml(24)%orbit = 2 +obs_param_nml(24)%pol = 2 +obs_param_nml(24)%N_ang = 1 +obs_param_nml(24)%ang(1) = 40. +obs_param_nml(24)%freq = 1.41e9 +obs_param_nml(24)%FOV = 20. +obs_param_nml(24)%FOV_units = 'km' +obs_param_nml(24)%assim = .false. +obs_param_nml(24)%scale = .false. +obs_param_nml(24)%getinnov = .false. +obs_param_nml(24)%RTM_ID = 2 +obs_param_nml(24)%bias_Npar = 0 +obs_param_nml(24)%bias_trel = 864000 +obs_param_nml(24)%bias_tcut = 432000 +obs_param_nml(24)%nodata = -9999. +obs_param_nml(24)%varname = 'Tb' +obs_param_nml(24)%units = 'K' +obs_param_nml(24)%path = '/discover/nobackup/projects/gmao/ssd/land/l_data/SMOS/EASEv2/ESA_REPR/SMOS_M36_SCLF1C_fit_nosky_noatm_v620_ESA_v102/SMOS_fit_poly2/' +obs_param_nml(24)%name = '' +obs_param_nml(24)%maskpath = '' +obs_param_nml(24)%maskname = '' +obs_param_nml(24)%scalepath = '' +obs_param_nml(24)%scalename = '' +obs_param_nml(24)%flistpath = '' +obs_param_nml(24)%flistname = '' +obs_param_nml(24)%errstd = 1.5 +obs_param_nml(24)%std_normal_max = 2.5 +obs_param_nml(24)%zeromean = .true. +obs_param_nml(24)%coarsen_pert = .false. +obs_param_nml(24)%xcorr = 0.25 +obs_param_nml(24)%ycorr = 0.25 +obs_param_nml(24)%adapt = 0 + + +! -------------------------------------------------------------------- +! +! 25 = [empty] + +obs_param_nml(25)%descr = 'NULL' +obs_param_nml(25)%orbit = -9999 +obs_param_nml(25)%pol = -9999 +obs_param_nml(25)%N_ang = -9999 +obs_param_nml(25)%freq = -9999. +obs_param_nml(25)%FOV = -9999. +obs_param_nml(25)%FOV_units = 'NULL' +obs_param_nml(25)%assim = .false. +obs_param_nml(25)%scale = .false. +obs_param_nml(25)%getinnov = .false. +obs_param_nml(25)%RTM_ID = -9999 +obs_param_nml(25)%bias_Npar = -9999 +obs_param_nml(25)%bias_trel = -9999 +obs_param_nml(25)%bias_tcut = -9999 +obs_param_nml(25)%nodata = -9999. +obs_param_nml(25)%varname = 'NULL' +obs_param_nml(25)%units = 'NULL' +obs_param_nml(25)%path = 'NULL' +obs_param_nml(25)%name = 'NULL' +obs_param_nml(25)%maskpath = 'NULL' +obs_param_nml(25)%maskname = 'NULL' +obs_param_nml(25)%scalepath = 'NULL' +obs_param_nml(25)%scalename = 'NULL' +obs_param_nml(25)%flistpath = 'NULL' +obs_param_nml(25)%flistname = 'NULL' +obs_param_nml(25)%errstd = -9999. +obs_param_nml(25)%std_normal_max = -9999. +obs_param_nml(25)%zeromean = .false. +obs_param_nml(25)%coarsen_pert = .false. +obs_param_nml(25)%xcorr = -9999. +obs_param_nml(25)%ycorr = -9999. +obs_param_nml(25)%adapt = -9999 + +! -------------------------------------------------------------------- +! +! 26 = LaRC Tskin GEOS-WEST +! + +obs_param_nml(26)%descr = 'LaRC_tskin-GOESW' +obs_param_nml(26)%orbit = 4 +obs_param_nml(26)%pol = 0 +obs_param_nml(26)%N_ang = 0 +obs_param_nml(26)%freq = 0. +obs_param_nml(26)%FOV = 0.17 +obs_param_nml(26)%FOV_units = 'deg' +obs_param_nml(26)%assim = .false. +obs_param_nml(26)%scale = .false. +obs_param_nml(26)%getinnov = .false. +obs_param_nml(26)%RTM_ID = 0 +obs_param_nml(26)%bias_Npar = 0 +obs_param_nml(26)%bias_trel = 864000 +obs_param_nml(26)%bias_tcut = 432000 +obs_param_nml(26)%nodata = -9999. +obs_param_nml(26)%varname = 'tsurf' +obs_param_nml(26)%units = 'K' +obs_param_nml(26)%path = '/discover/nobackup/csdraper/LaRC_float/GOES-WEST/' +obs_param_nml(26)%name = 'larc-v3.inst3_g15_Nch.' +obs_param_nml(26)%maskpath = '' +obs_param_nml(26)%maskname = '' +obs_param_nml(26)%scalepath = '' +obs_param_nml(26)%scalename = '' +obs_param_nml(26)%flistpath = '' +obs_param_nml(26)%flistname = '' +obs_param_nml(26)%errstd = 2. +obs_param_nml(26)%std_normal_max = 2.5 +obs_param_nml(26)%zeromean = .true. +obs_param_nml(26)%coarsen_pert = .false. +obs_param_nml(26)%xcorr = 0.17 +obs_param_nml(26)%ycorr = 0.17 +obs_param_nml(26)%adapt = 0 + +! -------------------------------------------------------------------- +! +! 27 = LaRC Tskin GEOS-EAST +! + +obs_param_nml(27)%descr = 'LaRC_tskin-GOESE' +obs_param_nml(27)%orbit = 4 +obs_param_nml(27)%pol = 0 +obs_param_nml(27)%N_ang = 0 +obs_param_nml(27)%freq = 0. +obs_param_nml(27)%FOV = 0.17 +obs_param_nml(27)%FOV_units = 'deg' +obs_param_nml(27)%assim = .false. +obs_param_nml(27)%scale = .false. +obs_param_nml(27)%getinnov = .false. +obs_param_nml(27)%RTM_ID = 0 +obs_param_nml(27)%bias_Npar = 0 +obs_param_nml(27)%bias_trel = 864000 +obs_param_nml(27)%bias_tcut = 432000 +obs_param_nml(27)%nodata = -9999. +obs_param_nml(27)%varname = 'tsurf' +obs_param_nml(27)%units = 'K' +obs_param_nml(27)%path = '/discover/nobackup/csdraper/LaRC_float/v4/GOES-EAST/' +obs_param_nml(27)%name = 'larc-v3.inst3_g13_Nch.' +obs_param_nml(27)%maskpath = '' +obs_param_nml(27)%maskname = '' +obs_param_nml(27)%scalepath = '' +obs_param_nml(27)%scalename = '' +obs_param_nml(27)%flistpath = '' +obs_param_nml(27)%flistname = '' +obs_param_nml(27)%errstd = 2. +obs_param_nml(27)%std_normal_max = 2.5 +obs_param_nml(27)%zeromean = .true. +obs_param_nml(27)%coarsen_pert = .false. +obs_param_nml(27)%xcorr = 0.17 +obs_param_nml(27)%ycorr = 0.17 +obs_param_nml(27)%adapt = 0 + +! -------------------------------------------------------------------- +! +! 28 = LaRC Tskin GEOS-MET09 +! + +obs_param_nml(28)%descr = 'LaRC_tskin-MET09' +obs_param_nml(28)%orbit = 4 +obs_param_nml(28)%pol = 0 +obs_param_nml(28)%N_ang = 0 +obs_param_nml(28)%freq = 0. +obs_param_nml(28)%FOV = 0.17 +obs_param_nml(28)%FOV_units = 'deg' +obs_param_nml(28)%assim = .false. +obs_param_nml(28)%scale = .false. +obs_param_nml(28)%getinnov = .false. +obs_param_nml(28)%RTM_ID = 0 +obs_param_nml(28)%bias_Npar = 0 +obs_param_nml(28)%bias_trel = 864000 +obs_param_nml(28)%bias_tcut = 432000 +obs_param_nml(28)%nodata = -9999. +obs_param_nml(28)%varname = 'tsurf' +obs_param_nml(28)%units = 'K' +obs_param_nml(28)%path = '/discover/nobackup/csdraper/LaRC_float/MET09/' +obs_param_nml(28)%name = 'larc-v3.inst3_mt9_Nch.' +obs_param_nml(28)%maskpath = '' +obs_param_nml(28)%maskname = '' +obs_param_nml(28)%scalepath = '' +obs_param_nml(28)%scalename = '' +obs_param_nml(28)%flistpath = '' +obs_param_nml(28)%flistname = '' +obs_param_nml(28)%errstd = 2. +obs_param_nml(28)%std_normal_max = 2.5 +obs_param_nml(28)%zeromean = .true. +obs_param_nml(28)%coarsen_pert = .false. +obs_param_nml(28)%xcorr = 0.17 +obs_param_nml(28)%ycorr = 0.17 +obs_param_nml(28)%adapt = 0 + +! -------------------------------------------------------------------- +! +! 25 = LaRC Tskin GEOS-FY2E +! + +obs_param_nml(29)%descr = 'LaRC_tskin-FY2E-' +obs_param_nml(29)%orbit = 4 +obs_param_nml(29)%pol = 0 +obs_param_nml(29)%N_ang = 0 +obs_param_nml(29)%freq = 0. +obs_param_nml(29)%FOV = 0.17 +obs_param_nml(29)%FOV_units = 'deg' +obs_param_nml(29)%assim = .false. +obs_param_nml(29)%scale = .false. +obs_param_nml(29)%getinnov = .false. +obs_param_nml(29)%RTM_ID = 0 +obs_param_nml(29)%bias_Npar = 0 +obs_param_nml(29)%bias_trel = 864000 +obs_param_nml(29)%bias_tcut = 432000 +obs_param_nml(29)%nodata = -9999. +obs_param_nml(29)%varname = 'tsurf' +obs_param_nml(29)%units = 'K' +obs_param_nml(29)%path = '/discover/nobackup/csdraper/LaRC_float/FY2E/' +obs_param_nml(29)%name = 'larc-v3.inst3_fye_Nch.' +obs_param_nml(29)%maskpath = '' +obs_param_nml(29)%maskname = '' +obs_param_nml(29)%scalepath = '' +obs_param_nml(29)%scalename = '' +obs_param_nml(29)%flistpath = '' +obs_param_nml(29)%flistname = '' +obs_param_nml(29)%errstd = 2. +obs_param_nml(29)%std_normal_max = 2.5 +obs_param_nml(29)%zeromean = .true. +obs_param_nml(29)%coarsen_pert = .false. +obs_param_nml(29)%xcorr = 0.17 +obs_param_nml(29)%ycorr = 0.17 +obs_param_nml(29)%adapt = 0 + +! -------------------------------------------------------------------- +! +! 30 = LaRC Tskin GEOS-MTSAT-2 +! + +obs_param_nml(30)%descr = 'LaRC_tskin-MTST2' +obs_param_nml(30)%orbit = 4 +obs_param_nml(30)%pol = 0 +obs_param_nml(30)%N_ang = 0 +obs_param_nml(30)%freq = 0. +obs_param_nml(30)%FOV = 0.17 +obs_param_nml(30)%FOV_units = 'deg' +obs_param_nml(30)%assim = .false. +obs_param_nml(30)%scale = .false. +obs_param_nml(30)%getinnov = .false. +obs_param_nml(30)%RTM_ID = 0 +obs_param_nml(30)%bias_Npar = 0 +obs_param_nml(30)%bias_trel = 864000 +obs_param_nml(30)%bias_tcut = 432000 +obs_param_nml(30)%nodata = -9999. +obs_param_nml(30)%varname = 'tsurf' +obs_param_nml(30)%units = 'K' +obs_param_nml(30)%path = '/discover/nobackup/csdraper/LaRC_float/MTSAT-2/' +obs_param_nml(30)%name = 'larc-v3.inst3_mt2_Nch.' +obs_param_nml(30)%maskpath = '' +obs_param_nml(30)%maskname = '' +obs_param_nml(30)%scalepath = '' +obs_param_nml(30)%scalename = '' +obs_param_nml(30)%flistpath = '' +obs_param_nml(30)%flistname = '' +obs_param_nml(30)%errstd = 2. +obs_param_nml(30)%std_normal_max = 2.5 +obs_param_nml(30)%zeromean = .true. +obs_param_nml(30)%coarsen_pert = .false. +obs_param_nml(30)%xcorr = 0.17 +obs_param_nml(30)%ycorr = 0.17 +obs_param_nml(30)%adapt = 0 + +! -------------------------------------------------------------------- +! +! SMAP L1C_TB brightness temperature (36 km EASE grid) +! +! "A" = ascending (6pm *SMAP* overpass) +! "D" = descending (6am *SMAP* overpass) +! +! "Tbh" = h-pol Tb +! "Tbv" = v-pol Tb +! +! ------------------- +! +! 31 = SMAP_L1C_Tbh_A + +obs_param_nml(31)%descr = 'SMAP_L1C_Tbh_A' +obs_param_nml(31)%orbit = 1 +obs_param_nml(31)%pol = 1 +obs_param_nml(31)%N_ang = 1 +obs_param_nml(31)%ang(1) = 40. +obs_param_nml(31)%freq = 1.41e9 +obs_param_nml(31)%FOV = 20. +obs_param_nml(31)%FOV_units = 'km' +obs_param_nml(31)%assim = .false. +obs_param_nml(31)%scale = .false. +obs_param_nml(31)%getinnov = .false. +obs_param_nml(31)%RTM_ID = 2 +obs_param_nml(31)%bias_Npar = 0 +obs_param_nml(31)%bias_trel = 864000 +obs_param_nml(31)%bias_tcut = 432000 +obs_param_nml(31)%nodata = -9999. +obs_param_nml(31)%varname = 'Tb' +obs_param_nml(31)%units = 'K' +obs_param_nml(31)%path = '/discover/nobackup/projects/gmao/smap/SMAP_L4/SMAP/L1C_TB/' +obs_param_nml(31)%name = '' +obs_param_nml(31)%maskpath = '' +obs_param_nml(31)%maskname = '' +obs_param_nml(31)%scalepath = '' +obs_param_nml(31)%scalename = '' +obs_param_nml(31)%flistpath = '' +obs_param_nml(31)%flistname = '' +obs_param_nml(31)%errstd = 4. +obs_param_nml(31)%std_normal_max = 2.5 +obs_param_nml(31)%zeromean = .true. +obs_param_nml(31)%coarsen_pert = .false. +obs_param_nml(31)%xcorr = 0.25 +obs_param_nml(31)%ycorr = 0.25 +obs_param_nml(31)%adapt = 0 + +! ------------------- +! +! 32 = SMAP_L1C_Tbh_D + +obs_param_nml(32)%descr = 'SMAP_L1C_Tbh_D' +obs_param_nml(32)%orbit = 2 +obs_param_nml(32)%pol = 1 +obs_param_nml(32)%N_ang = 1 +obs_param_nml(32)%ang(1) = 40. +obs_param_nml(32)%freq = 1.41e9 +obs_param_nml(32)%FOV = 20. +obs_param_nml(32)%FOV_units = 'km' +obs_param_nml(32)%assim = .false. +obs_param_nml(32)%scale = .false. +obs_param_nml(32)%getinnov = .false. +obs_param_nml(32)%RTM_ID = 2 +obs_param_nml(32)%bias_Npar = 0 +obs_param_nml(32)%bias_trel = 864000 +obs_param_nml(32)%bias_tcut = 432000 +obs_param_nml(32)%nodata = -9999. +obs_param_nml(32)%varname = 'Tb' +obs_param_nml(32)%units = 'K' +obs_param_nml(32)%path = '/discover/nobackup/projects/gmao/smap/SMAP_L4/SMAP/L1C_TB/' +obs_param_nml(32)%name = '' +obs_param_nml(32)%maskpath = '' +obs_param_nml(32)%maskname = '' +obs_param_nml(32)%scalepath = '' +obs_param_nml(32)%scalename = '' +obs_param_nml(32)%flistpath = '' +obs_param_nml(32)%flistname = '' +obs_param_nml(32)%errstd = 4. +obs_param_nml(32)%std_normal_max = 2.5 +obs_param_nml(32)%zeromean = .true. +obs_param_nml(32)%coarsen_pert = .false. +obs_param_nml(32)%xcorr = 0.25 +obs_param_nml(32)%ycorr = 0.25 +obs_param_nml(32)%adapt = 0 + +! ------------------- +! +! 33 = SMAP_L1C_Tbv_A + +obs_param_nml(33)%descr = 'SMAP_L1C_Tbv_A' +obs_param_nml(33)%orbit = 1 +obs_param_nml(33)%pol = 2 +obs_param_nml(33)%N_ang = 1 +obs_param_nml(33)%ang(1) = 40. +obs_param_nml(33)%freq = 1.41e9 +obs_param_nml(33)%FOV = 20. +obs_param_nml(33)%FOV_units = 'km' +obs_param_nml(33)%assim = .false. +obs_param_nml(33)%scale = .false. +obs_param_nml(33)%getinnov = .false. +obs_param_nml(33)%RTM_ID = 2 +obs_param_nml(33)%bias_Npar = 0 +obs_param_nml(33)%bias_trel = 864000 +obs_param_nml(33)%bias_tcut = 432000 +obs_param_nml(33)%nodata = -9999. +obs_param_nml(33)%varname = 'Tb' +obs_param_nml(33)%units = 'K' +obs_param_nml(33)%path = '/discover/nobackup/projects/gmao/smap/SMAP_L4/SMAP/L1C_TB/' +obs_param_nml(33)%name = '' +obs_param_nml(33)%maskpath = '' +obs_param_nml(33)%maskname = '' +obs_param_nml(33)%scalepath = '' +obs_param_nml(33)%scalename = '' +obs_param_nml(33)%flistpath = '' +obs_param_nml(33)%flistname = '' +obs_param_nml(33)%errstd = 4. +obs_param_nml(33)%std_normal_max = 2.5 +obs_param_nml(33)%zeromean = .true. +obs_param_nml(33)%coarsen_pert = .false. +obs_param_nml(33)%xcorr = 0.25 +obs_param_nml(33)%ycorr = 0.25 +obs_param_nml(33)%adapt = 0 + +! ------------------- +! +! 34 = SMAP_L1C_Tbv_D + +obs_param_nml(34)%descr = 'SMAP_L1C_Tbv_D' +obs_param_nml(34)%orbit = 2 +obs_param_nml(34)%pol = 2 +obs_param_nml(34)%N_ang = 1 +obs_param_nml(34)%ang(1) = 40. +obs_param_nml(34)%freq = 1.41e9 +obs_param_nml(34)%FOV = 20. +obs_param_nml(34)%FOV_units = 'km' +obs_param_nml(34)%assim = .false. +obs_param_nml(34)%scale = .false. +obs_param_nml(34)%getinnov = .false. +obs_param_nml(34)%RTM_ID = 2 +obs_param_nml(34)%bias_Npar = 0 +obs_param_nml(34)%bias_trel = 864000 +obs_param_nml(34)%bias_tcut = 432000 +obs_param_nml(34)%nodata = -9999. +obs_param_nml(34)%varname = 'Tb' +obs_param_nml(34)%units = 'K' +obs_param_nml(34)%path = '/discover/nobackup/projects/gmao/smap/SMAP_L4/SMAP/L1C_TB/' +obs_param_nml(34)%name = '' +obs_param_nml(34)%maskpath = '' +obs_param_nml(34)%maskname = '' +obs_param_nml(34)%scalepath = '' +obs_param_nml(34)%scalename = '' +obs_param_nml(34)%flistpath = '' +obs_param_nml(34)%flistname = '' +obs_param_nml(34)%errstd = 4. +obs_param_nml(34)%std_normal_max = 2.5 +obs_param_nml(34)%zeromean = .true. +obs_param_nml(34)%coarsen_pert = .false. +obs_param_nml(34)%xcorr = 0.25 +obs_param_nml(34)%ycorr = 0.25 +obs_param_nml(34)%adapt = 0 + +! -------------------------------------------------------------------- +! +! SMAP L2_SM_AP *downscaled* brightness temperature (9 km EASE grid) +! +! "A" = descending (6pm overpass) +! "D" = descending (6am overpass) +! +! "Tbh" = h-pol Tb +! "Tbv" = v-pol Tb +! + +! ------------------- +! +! 35 = SMAP_L2AP_Tbh_A + +obs_param_nml(35)%descr = 'SMAP_L2AP_Tbh_A' +obs_param_nml(35)%orbit = 1 +obs_param_nml(35)%pol = 1 +obs_param_nml(35)%N_ang = 1 +obs_param_nml(35)%ang(1) = 40. +obs_param_nml(35)%freq = 1.41e9 +obs_param_nml(35)%FOV = 5. +obs_param_nml(35)%FOV_units = 'km' +obs_param_nml(35)%assim = .false. +obs_param_nml(35)%scale = .false. +obs_param_nml(35)%getinnov = .false. +obs_param_nml(35)%RTM_ID = 2 +obs_param_nml(35)%bias_Npar = 0 +obs_param_nml(35)%bias_trel = 864000 +obs_param_nml(35)%bias_tcut = 432000 +obs_param_nml(35)%nodata = -9999. +obs_param_nml(35)%varname = 'Tb' +obs_param_nml(35)%units = 'K' +obs_param_nml(35)%path = '/discover/nobackup/projects/gmao/smap/SMAP_L4/SMAP/L2_SM_AP/' +obs_param_nml(35)%name = '' +obs_param_nml(35)%maskpath = '' +obs_param_nml(35)%maskname = '' +obs_param_nml(35)%scalepath = '' +obs_param_nml(35)%scalename = '' +obs_param_nml(35)%flistpath = '' +obs_param_nml(35)%flistname = '' +obs_param_nml(35)%errstd = 5. +obs_param_nml(35)%std_normal_max = 2.5 +obs_param_nml(35)%zeromean = .true. +obs_param_nml(35)%coarsen_pert = .false. +obs_param_nml(35)%xcorr = 0.0625 +obs_param_nml(35)%ycorr = 0.0625 +obs_param_nml(35)%adapt = 0 + +! ------------------- +! +! 36 = SMAP_L2AP_Tbh_D + +obs_param_nml(36)%descr = 'SMAP_L2AP_Tbh_D' +obs_param_nml(36)%orbit = 2 +obs_param_nml(36)%pol = 1 +obs_param_nml(36)%N_ang = 1 +obs_param_nml(36)%ang(1) = 40. +obs_param_nml(36)%freq = 1.41e9 +obs_param_nml(36)%FOV = 5. +obs_param_nml(36)%FOV_units = 'km' +obs_param_nml(36)%assim = .false. +obs_param_nml(36)%scale = .false. +obs_param_nml(36)%getinnov = .false. +obs_param_nml(36)%RTM_ID = 2 +obs_param_nml(36)%bias_Npar = 0 +obs_param_nml(36)%bias_trel = 864000 +obs_param_nml(36)%bias_tcut = 432000 +obs_param_nml(36)%nodata = -9999. +obs_param_nml(36)%varname = 'Tb' +obs_param_nml(36)%units = 'K' +obs_param_nml(36)%path = '/discover/nobackup/projects/gmao/smap/SMAP_L4/SMAP/L2_SM_AP/' +obs_param_nml(36)%name = '' +obs_param_nml(36)%maskpath = '' +obs_param_nml(36)%maskname = '' +obs_param_nml(36)%scalepath = '' +obs_param_nml(36)%scalename = '' +obs_param_nml(36)%flistpath = '' +obs_param_nml(36)%flistname = '' +obs_param_nml(36)%errstd = 5. +obs_param_nml(36)%std_normal_max = 2.5 +obs_param_nml(36)%zeromean = .true. +obs_param_nml(36)%coarsen_pert = .false. +obs_param_nml(36)%xcorr = 0.0625 +obs_param_nml(36)%ycorr = 0.0625 +obs_param_nml(36)%adapt = 0 + +! ------------------- +! +! 37 = SMAP_L2AP_Tbv_A + +obs_param_nml(37)%descr = 'SMAP_L2AP_Tbv_A' +obs_param_nml(37)%orbit = 1 +obs_param_nml(37)%pol = 2 +obs_param_nml(37)%N_ang = 1 +obs_param_nml(37)%ang(1) = 40. +obs_param_nml(37)%freq = 1.41e9 +obs_param_nml(37)%FOV = 5. +obs_param_nml(37)%FOV_units = 'km' +obs_param_nml(37)%assim = .false. +obs_param_nml(37)%scale = .false. +obs_param_nml(37)%getinnov = .false. +obs_param_nml(37)%RTM_ID = 2 +obs_param_nml(37)%bias_Npar = 0 +obs_param_nml(37)%bias_trel = 864000 +obs_param_nml(37)%bias_tcut = 432000 +obs_param_nml(37)%nodata = -9999. +obs_param_nml(37)%varname = 'Tb' +obs_param_nml(37)%units = 'K' +obs_param_nml(37)%path = '/discover/nobackup/projects/gmao/smap/SMAP_L4/SMAP/L2_SM_AP/' +obs_param_nml(37)%name = '' +obs_param_nml(37)%maskpath = '' +obs_param_nml(37)%maskname = '' +obs_param_nml(37)%scalepath = '' +obs_param_nml(37)%scalename = '' +obs_param_nml(37)%flistpath = '' +obs_param_nml(37)%flistname = '' +obs_param_nml(37)%errstd = 5. +obs_param_nml(37)%std_normal_max = 2.5 +obs_param_nml(37)%zeromean = .true. +obs_param_nml(37)%coarsen_pert = .false. +obs_param_nml(37)%xcorr = 0.0625 +obs_param_nml(37)%ycorr = 0.0625 +obs_param_nml(37)%adapt = 0 + +! ------------------- +! +! 38 = SMAP_L2AP_Tbv_D + +obs_param_nml(38)%descr = 'SMAP_L2AP_Tbv_D' +obs_param_nml(38)%orbit = 2 +obs_param_nml(38)%pol = 2 +obs_param_nml(38)%N_ang = 1 +obs_param_nml(38)%ang(1) = 40. +obs_param_nml(38)%freq = 1.41e9 +obs_param_nml(38)%FOV = 5. +obs_param_nml(38)%FOV_units = 'km' +obs_param_nml(38)%assim = .false. +obs_param_nml(38)%scale = .false. +obs_param_nml(38)%getinnov = .false. +obs_param_nml(38)%RTM_ID = 2 +obs_param_nml(38)%bias_Npar = 0 +obs_param_nml(38)%bias_trel = 864000 +obs_param_nml(38)%bias_tcut = 432000 +obs_param_nml(38)%nodata = -9999. +obs_param_nml(38)%varname = 'Tb' +obs_param_nml(38)%units = 'K' +obs_param_nml(38)%path = '/discover/nobackup/projects/gmao/smap/SMAP_L4/SMAP/L2_SM_AP/' +obs_param_nml(38)%name = '' +obs_param_nml(38)%maskpath = '' +obs_param_nml(38)%maskname = '' +obs_param_nml(38)%scalepath = '' +obs_param_nml(38)%scalename = '' +obs_param_nml(38)%flistpath = '' +obs_param_nml(38)%flistname = '' +obs_param_nml(38)%errstd = 5. +obs_param_nml(38)%std_normal_max = 2.5 +obs_param_nml(38)%zeromean = .true. +obs_param_nml(38)%coarsen_pert = .false. +obs_param_nml(38)%xcorr = 0.0625 +obs_param_nml(38)%ycorr = 0.0625 +obs_param_nml(38)%adapt = 0 + +! ------------------- +! +! 39 = SMAP_L2AP_FT_A + +obs_param_nml(39)%descr = 'SMAP_L2AP_FT_A' +obs_param_nml(39)%orbit = 1 +obs_param_nml(39)%pol = 0 +obs_param_nml(39)%N_ang = 0 +obs_param_nml(39)%freq = 1.26e9 +obs_param_nml(39)%FOV = 5. +obs_param_nml(39)%FOV_units = 'km' +obs_param_nml(39)%assim = .false. +obs_param_nml(39)%scale = .false. +obs_param_nml(39)%getinnov = .false. +obs_param_nml(39)%RTM_ID = 0 +obs_param_nml(39)%bias_Npar = 0 +obs_param_nml(39)%bias_trel = 864000 +obs_param_nml(39)%bias_tcut = 432000 +obs_param_nml(39)%nodata = -9999. +obs_param_nml(39)%varname = 'FT' +obs_param_nml(39)%units = '-' +obs_param_nml(39)%path = '/discover/nobackup/projects/gmao/smap/SMAP_L4/SMAP/L2_SM_AP/' +obs_param_nml(39)%name = '' +obs_param_nml(39)%maskpath = '' +obs_param_nml(39)%maskname = '' +obs_param_nml(39)%scalepath = '' +obs_param_nml(39)%scalename = '' +obs_param_nml(39)%flistpath = '' +obs_param_nml(39)%flistname = '' +obs_param_nml(39)%errstd = .0 +obs_param_nml(39)%std_normal_max = 2.5 +obs_param_nml(39)%zeromean = .true. +obs_param_nml(39)%coarsen_pert = .false. +obs_param_nml(39)%xcorr = 0.0625 +obs_param_nml(39)%ycorr = 0.0625 +obs_param_nml(39)%adapt = 0 + +! ------------------- +! +! 40 = SMAP_L2AP_FT_D + +obs_param_nml(40)%descr = 'SMAP_L2AP_FT_D' +obs_param_nml(40)%orbit = 2 +obs_param_nml(40)%pol = 0 +obs_param_nml(40)%N_ang = 0 +obs_param_nml(40)%freq = 1.26e9 +obs_param_nml(40)%FOV = 5. +obs_param_nml(40)%FOV_units = 'km' +obs_param_nml(40)%assim = .false. +obs_param_nml(40)%scale = .false. +obs_param_nml(40)%getinnov = .false. +obs_param_nml(40)%RTM_ID = 0 +obs_param_nml(40)%bias_Npar = 0 +obs_param_nml(40)%bias_trel = 864000 +obs_param_nml(40)%bias_tcut = 432000 +obs_param_nml(40)%nodata = -9999. +obs_param_nml(40)%varname = 'FT' +obs_param_nml(40)%units = '-' +obs_param_nml(40)%path = '/discover/nobackup/projects/gmao/smap/SMAP_L4/SMAP/L2_SM_AP/' +obs_param_nml(40)%name = '' +obs_param_nml(40)%maskpath = '' +obs_param_nml(40)%maskname = '' +obs_param_nml(40)%scalepath = '' +obs_param_nml(40)%scalename = '' +obs_param_nml(40)%flistpath = '' +obs_param_nml(40)%flistname = '' +obs_param_nml(40)%errstd = .0 +obs_param_nml(40)%std_normal_max = 2.5 +obs_param_nml(40)%zeromean = .true. +obs_param_nml(40)%coarsen_pert = .false. +obs_param_nml(40)%xcorr = 0.0625 +obs_param_nml(40)%ycorr = 0.0625 +obs_param_nml(40)%adapt = 0 + +! -------------------------------------------------------------------- +! +! SMAP L1C_TB_E *enhanced* brightness temperature (9 km EASE grid) +! +! "A" = ascending (6pm *SMAP* overpass) +! "D" = descending (6am *SMAP* overpass) +! +! "Tbh" = h-pol Tb +! "Tbv" = v-pol Tb +! +! IMPORTANT NOTE ON RESOLUTION AND THINNING: +! +! L1C_TB_E observations have ~27-km resolution, which is "enhanced" compared to +! the ~36-km resolution of the standard L1C TB data. Accordingly, +! - FOV is set to 15 km (vs. 20 km for standard L1C Tbs), and +! - xcorr and ycorr are set to 0.1875 deg (vs. 0.25 deg for standard L1C Tbs). +! +! There are two sets of four species defined for L1C_TB_E observations: +! - species 41-44, %descr = 'SMAP_L1C_Tb?_E09_?': *not* thinned (all obs on 9-km EASEv2 grid are kept) +! - species 45-48, %descr = 'SMAP_L1C_Tb?_E27_?': thinned to ~27-km spacing +! Thinning is implemented in subroutine read_obs_SMAP_halforbit_Tb(). +! +! ------------------- +! +! 41 = SMAP_L1C_Tbh_E09_A + +obs_param_nml(41)%descr = 'SMAP_L1C_Tbh_E09_A' +obs_param_nml(41)%orbit = 1 +obs_param_nml(41)%pol = 1 +obs_param_nml(41)%N_ang = 1 +obs_param_nml(41)%ang(1) = 40. +obs_param_nml(41)%freq = 1.41e9 +obs_param_nml(41)%FOV = 15. +obs_param_nml(41)%FOV_units = 'km' +obs_param_nml(41)%assim = .false. +obs_param_nml(41)%scale = .false. +obs_param_nml(41)%getinnov = .false. +obs_param_nml(41)%RTM_ID = 2 +obs_param_nml(41)%bias_Npar = 0 +obs_param_nml(41)%bias_trel = 864000 +obs_param_nml(41)%bias_tcut = 432000 +obs_param_nml(41)%nodata = -9999. +obs_param_nml(41)%varname = 'Tb' +obs_param_nml(41)%units = 'K' +obs_param_nml(41)%path = '/discover/nobackup/projects/gmao/smap/SMAP_L4/SMAP/L1C_TB_E/' +obs_param_nml(41)%name = '' +obs_param_nml(41)%maskpath = '' +obs_param_nml(41)%maskname = '' +obs_param_nml(41)%scalepath = '' +obs_param_nml(41)%scalename = '' +obs_param_nml(41)%flistpath = '' +obs_param_nml(41)%flistname = '' +obs_param_nml(41)%errstd = 4. +obs_param_nml(41)%std_normal_max = 2.5 +obs_param_nml(41)%zeromean = .true. +obs_param_nml(41)%coarsen_pert = .false. +obs_param_nml(41)%xcorr = 0.1875 +obs_param_nml(41)%ycorr = 0.1875 +obs_param_nml(41)%adapt = 0 + +! ------------------- +! +! 42 = SMAP_L1C_Tbh_E09_D + +obs_param_nml(42)%descr = 'SMAP_L1C_Tbh_E09_D' +obs_param_nml(42)%orbit = 2 +obs_param_nml(42)%pol = 1 +obs_param_nml(42)%N_ang = 1 +obs_param_nml(42)%ang(1) = 40. +obs_param_nml(42)%freq = 1.41e9 +obs_param_nml(42)%FOV = 15. +obs_param_nml(42)%FOV_units = 'km' +obs_param_nml(42)%assim = .false. +obs_param_nml(42)%scale = .false. +obs_param_nml(42)%getinnov = .false. +obs_param_nml(42)%RTM_ID = 2 +obs_param_nml(42)%bias_Npar = 0 +obs_param_nml(42)%bias_trel = 864000 +obs_param_nml(42)%bias_tcut = 432000 +obs_param_nml(42)%nodata = -9999. +obs_param_nml(42)%varname = 'Tb' +obs_param_nml(42)%units = 'K' +obs_param_nml(42)%path = '/discover/nobackup/projects/gmao/smap/SMAP_L4/SMAP/L1C_TB_E/' +obs_param_nml(42)%name = '' +obs_param_nml(42)%maskpath = '' +obs_param_nml(42)%maskname = '' +obs_param_nml(42)%scalepath = '' +obs_param_nml(42)%scalename = '' +obs_param_nml(42)%flistpath = '' +obs_param_nml(42)%flistname = '' +obs_param_nml(42)%errstd = 4. +obs_param_nml(42)%std_normal_max = 2.5 +obs_param_nml(42)%zeromean = .true. +obs_param_nml(42)%coarsen_pert = .false. +obs_param_nml(42)%xcorr = 0.1875 +obs_param_nml(42)%ycorr = 0.1875 +obs_param_nml(42)%adapt = 0 + +! ------------------- +! +! 43 = SMAP_L1C_Tbv_E09_A + +obs_param_nml(43)%descr = 'SMAP_L1C_Tbv_E09_A' +obs_param_nml(43)%orbit = 1 +obs_param_nml(43)%pol = 2 +obs_param_nml(43)%N_ang = 1 +obs_param_nml(43)%ang(1) = 40. +obs_param_nml(43)%freq = 1.41e9 +obs_param_nml(43)%FOV = 15. +obs_param_nml(43)%FOV_units = 'km' +obs_param_nml(43)%assim = .false. +obs_param_nml(43)%scale = .false. +obs_param_nml(43)%getinnov = .false. +obs_param_nml(43)%RTM_ID = 2 +obs_param_nml(43)%bias_Npar = 0 +obs_param_nml(43)%bias_trel = 864000 +obs_param_nml(43)%bias_tcut = 432000 +obs_param_nml(43)%nodata = -9999. +obs_param_nml(43)%varname = 'Tb' +obs_param_nml(43)%units = 'K' +obs_param_nml(43)%path = '/discover/nobackup/projects/gmao/smap/SMAP_L4/SMAP/L1C_TB_E/' +obs_param_nml(43)%name = '' +obs_param_nml(43)%maskpath = '' +obs_param_nml(43)%maskname = '' +obs_param_nml(43)%scalepath = '' +obs_param_nml(43)%scalename = '' +obs_param_nml(43)%flistpath = '' +obs_param_nml(43)%flistname = '' +obs_param_nml(43)%errstd = 4. +obs_param_nml(43)%std_normal_max = 2.5 +obs_param_nml(43)%zeromean = .true. +obs_param_nml(43)%coarsen_pert = .false. +obs_param_nml(43)%xcorr = 0.1875 +obs_param_nml(43)%ycorr = 0.1875 +obs_param_nml(43)%adapt = 0 + +! ------------------- +! +! 44 = SMAP_L1C_Tbv_E09_D + +obs_param_nml(44)%descr = 'SMAP_L1C_Tbv_E09_D' +obs_param_nml(44)%orbit = 2 +obs_param_nml(44)%pol = 2 +obs_param_nml(44)%N_ang = 1 +obs_param_nml(44)%ang(1) = 40. +obs_param_nml(44)%freq = 1.41e9 +obs_param_nml(44)%FOV = 15. +obs_param_nml(44)%FOV_units = 'km' +obs_param_nml(44)%assim = .false. +obs_param_nml(44)%scale = .false. +obs_param_nml(44)%getinnov = .false. +obs_param_nml(44)%RTM_ID = 2 +obs_param_nml(44)%bias_Npar = 0 +obs_param_nml(44)%bias_trel = 864000 +obs_param_nml(44)%bias_tcut = 432000 +obs_param_nml(44)%nodata = -9999. +obs_param_nml(44)%varname = 'Tb' +obs_param_nml(44)%units = 'K' +obs_param_nml(44)%path = '/discover/nobackup/projects/gmao/smap/SMAP_L4/SMAP/L1C_TB_E/' +obs_param_nml(44)%name = '' +obs_param_nml(44)%maskpath = '' +obs_param_nml(44)%maskname = '' +obs_param_nml(44)%scalepath = '' +obs_param_nml(44)%scalename = '' +obs_param_nml(44)%flistpath = '' +obs_param_nml(44)%flistname = '' +obs_param_nml(44)%errstd = 4. +obs_param_nml(44)%std_normal_max = 2.5 +obs_param_nml(44)%zeromean = .true. +obs_param_nml(44)%coarsen_pert = .false. +obs_param_nml(44)%xcorr = 0.1875 +obs_param_nml(44)%ycorr = 0.1875 +obs_param_nml(44)%adapt = 0 + +! ------------------- +! +! 45 = SMAP_L1C_Tbh_E27_A + +obs_param_nml(45)%descr = 'SMAP_L1C_Tbh_E27_A' +obs_param_nml(45)%orbit = 1 +obs_param_nml(45)%pol = 1 +obs_param_nml(45)%N_ang = 1 +obs_param_nml(45)%ang(1) = 40. +obs_param_nml(45)%freq = 1.41e9 +obs_param_nml(45)%FOV = 15. +obs_param_nml(45)%FOV_units = 'km' +obs_param_nml(45)%assim = .false. +obs_param_nml(45)%scale = .false. +obs_param_nml(45)%getinnov = .false. +obs_param_nml(45)%RTM_ID = 2 +obs_param_nml(45)%bias_Npar = 0 +obs_param_nml(45)%bias_trel = 864000 +obs_param_nml(45)%bias_tcut = 432000 +obs_param_nml(45)%nodata = -9999. +obs_param_nml(45)%varname = 'Tb' +obs_param_nml(45)%units = 'K' +obs_param_nml(45)%path = '/discover/nobackup/projects/gmao/smap/SMAP_L4/SMAP/L1C_TB_E/' +obs_param_nml(45)%name = '' +obs_param_nml(45)%maskpath = '' +obs_param_nml(45)%maskname = '' +obs_param_nml(45)%scalepath = '' +obs_param_nml(45)%scalename = '' +obs_param_nml(45)%flistpath = '' +obs_param_nml(45)%flistname = '' +obs_param_nml(45)%errstd = 4. +obs_param_nml(45)%std_normal_max = 2.5 +obs_param_nml(45)%zeromean = .true. +obs_param_nml(45)%coarsen_pert = .false. +obs_param_nml(45)%xcorr = 0.1875 +obs_param_nml(45)%ycorr = 0.1875 +obs_param_nml(45)%adapt = 0 + +! ------------------- +! +! 46 = SMAP_L1C_Tbh_E27_D + +obs_param_nml(46)%descr = 'SMAP_L1C_Tbh_E27_D' +obs_param_nml(46)%orbit = 2 +obs_param_nml(46)%pol = 1 +obs_param_nml(46)%N_ang = 1 +obs_param_nml(46)%ang(1) = 40. +obs_param_nml(46)%freq = 1.41e9 +obs_param_nml(46)%FOV = 15. +obs_param_nml(46)%FOV_units = 'km' +obs_param_nml(46)%assim = .false. +obs_param_nml(46)%scale = .false. +obs_param_nml(46)%getinnov = .false. +obs_param_nml(46)%RTM_ID = 2 +obs_param_nml(46)%bias_Npar = 0 +obs_param_nml(46)%bias_trel = 864000 +obs_param_nml(46)%bias_tcut = 432000 +obs_param_nml(46)%nodata = -9999. +obs_param_nml(46)%varname = 'Tb' +obs_param_nml(46)%units = 'K' +obs_param_nml(46)%path = '/discover/nobackup/projects/gmao/smap/SMAP_L4/SMAP/L1C_TB_E/' +obs_param_nml(46)%name = '' +obs_param_nml(46)%maskpath = '' +obs_param_nml(46)%maskname = '' +obs_param_nml(46)%scalepath = '' +obs_param_nml(46)%scalename = '' +obs_param_nml(46)%flistpath = '' +obs_param_nml(46)%flistname = '' +obs_param_nml(46)%errstd = 4. +obs_param_nml(46)%std_normal_max = 2.5 +obs_param_nml(46)%zeromean = .true. +obs_param_nml(46)%coarsen_pert = .false. +obs_param_nml(46)%xcorr = 0.1875 +obs_param_nml(46)%ycorr = 0.1875 +obs_param_nml(46)%adapt = 0 + +! ------------------- +! +! 47 = SMAP_L1C_Tbv_E27_A + +obs_param_nml(47)%descr = 'SMAP_L1C_Tbv_E27_A' +obs_param_nml(47)%orbit = 1 +obs_param_nml(47)%pol = 2 +obs_param_nml(47)%N_ang = 1 +obs_param_nml(47)%ang(1) = 40. +obs_param_nml(47)%freq = 1.41e9 +obs_param_nml(47)%FOV = 15. +obs_param_nml(47)%FOV_units = 'km' +obs_param_nml(47)%assim = .false. +obs_param_nml(47)%scale = .false. +obs_param_nml(47)%getinnov = .false. +obs_param_nml(47)%RTM_ID = 2 +obs_param_nml(47)%bias_Npar = 0 +obs_param_nml(47)%bias_trel = 864000 +obs_param_nml(47)%bias_tcut = 432000 +obs_param_nml(47)%nodata = -9999. +obs_param_nml(47)%varname = 'Tb' +obs_param_nml(47)%units = 'K' +obs_param_nml(47)%path = '/discover/nobackup/projects/gmao/smap/SMAP_L4/SMAP/L1C_TB_E/' +obs_param_nml(47)%name = '' +obs_param_nml(47)%maskpath = '' +obs_param_nml(47)%maskname = '' +obs_param_nml(47)%scalepath = '' +obs_param_nml(47)%scalename = '' +obs_param_nml(47)%flistpath = '' +obs_param_nml(47)%flistname = '' +obs_param_nml(47)%errstd = 4. +obs_param_nml(47)%std_normal_max = 2.5 +obs_param_nml(47)%zeromean = .true. +obs_param_nml(47)%coarsen_pert = .false. +obs_param_nml(47)%xcorr = 0.1875 +obs_param_nml(47)%ycorr = 0.1875 +obs_param_nml(47)%adapt = 0 + +! ------------------- +! +! 48 = SMAP_L1C_Tbv_E27_D + +obs_param_nml(48)%descr = 'SMAP_L1C_Tbv_E27_D' +obs_param_nml(48)%orbit = 2 +obs_param_nml(48)%pol = 2 +obs_param_nml(48)%N_ang = 1 +obs_param_nml(48)%ang(1) = 40. +obs_param_nml(48)%freq = 1.41e9 +obs_param_nml(48)%FOV = 15. +obs_param_nml(48)%FOV_units = 'km' +obs_param_nml(48)%assim = .false. +obs_param_nml(48)%scale = .false. +obs_param_nml(48)%getinnov = .false. +obs_param_nml(48)%RTM_ID = 2 +obs_param_nml(48)%bias_Npar = 0 +obs_param_nml(48)%bias_trel = 864000 +obs_param_nml(48)%bias_tcut = 432000 +obs_param_nml(48)%nodata = -9999. +obs_param_nml(48)%varname = 'Tb' +obs_param_nml(48)%units = 'K' +obs_param_nml(48)%path = '/discover/nobackup/projects/gmao/smap/SMAP_L4/SMAP/L1C_TB_E/' +obs_param_nml(48)%name = '' +obs_param_nml(48)%maskpath = '' +obs_param_nml(48)%maskname = '' +obs_param_nml(48)%scalepath = '' +obs_param_nml(48)%scalename = '' +obs_param_nml(48)%flistpath = '' +obs_param_nml(48)%flistname = '' +obs_param_nml(48)%errstd = 4. +obs_param_nml(48)%std_normal_max = 2.5 +obs_param_nml(48)%zeromean = .true. +obs_param_nml(48)%coarsen_pert = .false. +obs_param_nml(48)%xcorr = 0.1875 +obs_param_nml(48)%ycorr = 0.1875 +obs_param_nml(48)%adapt = 0 + +! -------------------------------------------------------------------- +! +! 49 = ASCAT_META_SM (ASCAT soil moisture ascending and descending orbits) +! +! https://navigator.eumetsat.int/product/EO:EUM:DAT:METOP:SOMO25 + +obs_param_nml(49)%descr = 'ASCAT_META_SM' +obs_param_nml(49)%orbit = 3 +obs_param_nml(49)%pol = 0 +obs_param_nml(49)%N_ang = 0 +obs_param_nml(49)%freq = 0 +obs_param_nml(49)%FOV = 20. +obs_param_nml(49)%FOV_units = 'km' +obs_param_nml(49)%assim = .false. +obs_param_nml(49)%scale = .false. +obs_param_nml(49)%getinnov = .false. +obs_param_nml(49)%RTM_ID = 0 +obs_param_nml(49)%bias_Npar = 0 +obs_param_nml(49)%bias_trel = 864000 +obs_param_nml(49)%bias_tcut = 432000 +obs_param_nml(49)%nodata = -9999. +obs_param_nml(49)%varname = 'sfds' +obs_param_nml(49)%units = '%' +obs_param_nml(49)%path = '/discover/nobackup/projects/gmao/smap/SMAP_Nature/ASCAT_EUMETSAT/Metop_A/' +obs_param_nml(49)%name = 'M02-ASCA-ASCSMO02' +obs_param_nml(49)%maskpath = '' +obs_param_nml(49)%maskname = '' +obs_param_nml(49)%scalepath = '' +obs_param_nml(49)%scalename = '' +obs_param_nml(49)%flistpath = '' +obs_param_nml(49)%flistname = '' +obs_param_nml(49)%errstd = 9. +obs_param_nml(49)%std_normal_max = 2.5 +obs_param_nml(49)%zeromean = .true. +obs_param_nml(49)%coarsen_pert = .false. +obs_param_nml(49)%xcorr = 0.25 +obs_param_nml(49)%ycorr = 0.25 +obs_param_nml(49)%adapt = 0 + +! -------------------------------------------------------------------- +! +! 50 = ASCAT_METB_SM (ASCAT soil moisture ascending and descending orbits) +! +! https://navigator.eumetsat.int/product/EO:EUM:DAT:METOP:SOMO25 + +obs_param_nml(50)%descr = 'ASCAT_METB_SM' +obs_param_nml(50)%orbit = 3 +obs_param_nml(50)%pol = 0 +obs_param_nml(50)%N_ang = 0 +obs_param_nml(50)%freq = 0 +obs_param_nml(50)%FOV = 20. +obs_param_nml(50)%FOV_units = 'km' +obs_param_nml(50)%assim = .false. +obs_param_nml(50)%scale = .false. +obs_param_nml(50)%getinnov = .false. +obs_param_nml(50)%RTM_ID = 0 +obs_param_nml(50)%bias_Npar = 0 +obs_param_nml(50)%bias_trel = 864000 +obs_param_nml(50)%bias_tcut = 432000 +obs_param_nml(50)%nodata = -9999. +obs_param_nml(50)%varname = 'sfds' +obs_param_nml(50)%units = '%' +obs_param_nml(50)%path = '/discover/nobackup/projects/gmao/smap/SMAP_Nature/ASCAT_EUMETSAT/Metop_B/' +obs_param_nml(50)%name = 'M01-ASCA-ASCSMO02' +obs_param_nml(50)%maskpath = '' +obs_param_nml(50)%maskname = '' +obs_param_nml(50)%scalepath = '' +obs_param_nml(50)%scalename = '' +obs_param_nml(50)%flistpath = '' +obs_param_nml(50)%flistname = '' +obs_param_nml(50)%errstd = 9. +obs_param_nml(50)%std_normal_max = 2.5 +obs_param_nml(50)%zeromean = .true. +obs_param_nml(50)%coarsen_pert = .false. +obs_param_nml(50)%xcorr = 0.25 +obs_param_nml(50)%ycorr = 0.25 +obs_param_nml(50)%adapt = 0 + +! -------------------------------------------------------------------- +! +! 51 = ASCAT_METC_SM (ASCAT soil moisture ascending and descending orbits) +! +! https://navigator.eumetsat.int/product/EO:EUM:DAT:METOP:SOMO25 + +obs_param_nml(51)%descr = 'ASCAT_METC_SM' +obs_param_nml(51)%orbit = 3 +obs_param_nml(51)%pol = 0 +obs_param_nml(51)%N_ang = 0 +obs_param_nml(51)%freq = 0 +obs_param_nml(51)%FOV = 20. +obs_param_nml(51)%FOV_units = 'km' +obs_param_nml(51)%assim = .false. +obs_param_nml(51)%scale = .false. +obs_param_nml(51)%getinnov = .false. +obs_param_nml(51)%RTM_ID = 0 +obs_param_nml(51)%bias_Npar = 0 +obs_param_nml(51)%bias_trel = 864000 +obs_param_nml(51)%bias_tcut = 432000 +obs_param_nml(51)%nodata = -9999. +obs_param_nml(51)%varname = 'sfds' +obs_param_nml(51)%units = '%' +obs_param_nml(51)%path = '/discover/nobackup/projects/gmao/smap/SMAP_Nature/ASCAT_EUMETSAT/Metop_C/' +obs_param_nml(51)%name = 'M03-ASCA-ASCSMO02' +obs_param_nml(51)%maskpath = '' +obs_param_nml(51)%maskname = '' +obs_param_nml(51)%scalepath = '' +obs_param_nml(51)%scalename = '' +obs_param_nml(51)%flistpath = '' +obs_param_nml(51)%flistname = '' +obs_param_nml(51)%errstd = 9. +obs_param_nml(51)%std_normal_max = 2.5 +obs_param_nml(51)%zeromean = .true. +obs_param_nml(51)%coarsen_pert = .false. +obs_param_nml(51)%xcorr = 0.25 +obs_param_nml(51)%ycorr = 0.25 +obs_param_nml(51)%adapt = 0 + +! -------------------------------------------------------------------- +! +! 52 = MODIS Aqua snow cover area fraction (SCF) +! +! MOD10C1.*.061: MODIS Aqua SCF, 0.05deg CMG, daytime (01:30pm local) ascending overpass, version V61 +! +! for rule-based snow cover analysis (no obs error/pert specs) + +obs_param_nml(52)%descr = 'MYD10C1' +obs_param_nml(52)%orbit = 1 +obs_param_nml(52)%pol = 0 +obs_param_nml(52)%N_ang = 0 +obs_param_nml(52)%freq = 0. +obs_param_nml(52)%FOV = 0. +obs_param_nml(52)%FOV_units = 'deg' +obs_param_nml(52)%assim = .false. +obs_param_nml(52)%scale = .false. +obs_param_nml(52)%getinnov = .false. +obs_param_nml(52)%RTM_ID = 0 +obs_param_nml(52)%bias_Npar = 0 +obs_param_nml(52)%bias_trel = 864000 +obs_param_nml(52)%bias_tcut = 432000 +obs_param_nml(52)%nodata = -9999. +obs_param_nml(52)%varname = 'asnow' +obs_param_nml(52)%units = 'm2/m2' +obs_param_nml(52)%path = '/discover/nobackup/projects/S2SHMA/MODIS/MYD10C1_V61/' +obs_param_nml(52)%name = 'MYD10C1.Ayyyyddd.061.hdf' +obs_param_nml(52)%maskpath = '' +obs_param_nml(52)%maskname = '' +obs_param_nml(52)%scalepath = '' +obs_param_nml(52)%scalename = '' +obs_param_nml(52)%flistpath = '' +obs_param_nml(52)%flistname = '' +obs_param_nml(52)%errstd = -9999. +obs_param_nml(52)%std_normal_max = -9999. +obs_param_nml(52)%zeromean = .false. +obs_param_nml(52)%coarsen_pert = .false. +obs_param_nml(52)%xcorr = 0. +obs_param_nml(52)%ycorr = 0. +obs_param_nml(52)%adapt = 0 + +! -------------------------------------------------------------------- +! +! 53 = MODIS Terra snow cover area fraction (SCF) +! +! MOD10C1.*.061: MODIS Terra SCF, 0.05deg CMG, daytime (10:30am local) descending overpass, version V61 +! +! for rule-based snow cover analysis (no obs error/pert specs) + +obs_param_nml(53)%descr = 'MOD10C1' +obs_param_nml(53)%orbit = 2 +obs_param_nml(53)%pol = 0 +obs_param_nml(53)%N_ang = 0 +obs_param_nml(53)%freq = 0. +obs_param_nml(53)%FOV = 0. +obs_param_nml(53)%FOV_units = 'deg' +obs_param_nml(53)%assim = .false. +obs_param_nml(53)%scale = .false. +obs_param_nml(53)%getinnov = .false. +obs_param_nml(53)%RTM_ID = 0 +obs_param_nml(53)%bias_Npar = 0 +obs_param_nml(53)%bias_trel = 864000 +obs_param_nml(53)%bias_tcut = 432000 +obs_param_nml(53)%nodata = -9999. +obs_param_nml(53)%varname = 'asnow' +obs_param_nml(53)%units = 'm2/m2' +obs_param_nml(53)%path = '/discover/nobackup/projects/S2SHMA/MODIS/MOD10C1_V61/' +obs_param_nml(53)%name = 'MOD10C1.Ayyyyddd.061.hdf' +obs_param_nml(53)%maskpath = '' +obs_param_nml(53)%maskname = '' +obs_param_nml(53)%scalepath = '' +obs_param_nml(53)%scalename = '' +obs_param_nml(53)%flistpath = '' +obs_param_nml(53)%flistname = '' +obs_param_nml(53)%errstd = -9999. +obs_param_nml(53)%std_normal_max = -9999. +obs_param_nml(53)%zeromean = .false. +obs_param_nml(53)%coarsen_pert = .false. +obs_param_nml(53)%xcorr = 0. +obs_param_nml(53)%ycorr = 0. +obs_param_nml(53)%adapt = 0 + + +! -------------------------------------------------------------------- + +/ + +! =========================== EOF ======================================= diff --git a/GEOSldas_App/README_LDAS_App b/GEOSldas_App/README_LDAS_App new file mode 100644 index 0000000..3cc60e0 --- /dev/null +++ b/GEOSldas_App/README_LDAS_App @@ -0,0 +1,36 @@ +README_GEOSldas_App: README file for GEOSldas/src/Components/GEOSldas_GridComp/GEOSldas_App + +rreichle, 8 July 2022: Initial version + +This directory contains a collection of programs, scripts, and config files, specifically: + +[..]/GEOSldas_App/ + +- Config files and programs/scripts needed by ldas_setup or lenkf.j (GEOSldas job script). + +[..]/GEOSldas_App/ens_forcing/ + +- Scripts needed by lenkf.j to process ensemble-based forcing (primarily for LADAS). + +[..]/GEOSldas_App/sample_config_files/ + +- Sample config files for ldas_setup and HISTORY. + +[..]/GEOSldas_App/util/config/ + +- Miscellaneous scripts to (manually) create/modify config files. + +[..]/GEOSldas_App/util/inputs/ + +- Miscellaneous scripts to (manually) create/modify input files. + +[..]/GEOSldas_App/util/postproc/ + +- Miscellaneous scripts for post-processing GEOSldas output. + +[..]/GEOSldas_App/util/shared/ + +- Miscellaneous shared reader and helper scripts (primarily matlab). + + +=============== EOF =============================================================== diff --git a/GEOSldas_App/ens_forcing/average_ensemble_forcing.py b/GEOSldas_App/ens_forcing/average_ensemble_forcing.py new file mode 100644 index 0000000..6c3c858 --- /dev/null +++ b/GEOSldas_App/ens_forcing/average_ensemble_forcing.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# +# module load python/GEOSpyD/Ana2019.03_py3.7 +# module load nco/4.8.1 +# +# Script for creating ensemble-average land forcing (lfo) files. +# +# Usage: average_ensemble_forcing.py [in_path] [out_path] [nens] +# +# where +# +# in_path : path to ensemble of lfo files +# out_path : path to output ensemble-average lfo files +# nens : number of lfo ensemble members +# +# The ensemble of input lfo files must be staged as follows: +# +# [in_path]/mem[NNN]/[EXPID].[HISTSPECa]_lfo_[HISTSPECb].[YYYYMMDD_HHMM]z.nc4 +# +# where +# +# [in_path] = command-line argument that specifies the path to the ensemble of lfo files +# [NNN] = three-digit ensemble ID (number from 1 to nens) +# [EXPID] = experiment ID +# [HISTSPECa/b] = other specs from HISTORY.rc (e.g., "tavg1_2d", "inst1_2d", "Nx+-") +# [YYYYMMDD_HHMM] = time stamp +# +# This convention matches the directory/file structure generated by the ensemble component +# of the GEOS atmospheric data assimilation system (ADAS). +# The ensemble-average files are created *separately* for each time, for each experiment ID, +# and for each distinct HISTORY spec. +# The ensemble-average files are placed in "out_path" (command-line argument) +# and will have the same name as the corresponding input files. + +import sys +import os +import glob +import subprocess as sp + +def averaging_forcing(in_path, out_path, nens): + """ The ensemble number will be appended to in_path starting from 001. + The out_path will be created if it does not exist. """ + if not os.path.exists(out_path): + os.makedirs(out_path) + files_list=[] + for i in range(1,nens+1): + sfx = '%03d'%(i) + folder = in_path+'/atmens/ensdiag/mem'+sfx + fs = sorted(glob.glob(folder+'/*lfo*.nc4')) + files_list.append(fs) + for fs in zip(*files_list): + k = 1 + # verify filenames are the same. + for f in fs: + n = f.rindex('/') + if (k==1): + f0 = f[n+1:] + k = k+1 + f1 = f[n+1:] + assert f0 == f1, "averaging different files. Each folder should have same files" + fnames = " ".join(fs) + cmd = 'ncea ' + fnames + ' ' + out_path +'/' + f0 + sp.call(cmd, shell=True) + +if __name__ == '__main__' : + + in_path = sys.argv[1] + out_path = sys.argv[2] + nens = int(sys.argv[3]) + + averaging_forcing(in_path, out_path, nens) diff --git a/GEOSldas_App/ens_forcing/enpert_forc.csh b/GEOSldas_App/ens_forcing/enpert_forc.csh new file mode 100755 index 0000000..6372a1d --- /dev/null +++ b/GEOSldas_App/ens_forcing/enpert_forc.csh @@ -0,0 +1,55 @@ +#!/usr/bin/csh -f + +# Script to create re-centered ensemble land forcing files (*inst1_2d_lfo*, *tavg1_2d_lfo*), +# typically used in LADAS: +# +# 1. Regrid coarse-resolution lfo files from the ADAS atmospheric ensemble to the higher +# resolution of the single-member central (or deterministic) simulation. +# +# 2. Compute perturbations from regridded lfo files. +# +# 3. Apply perturbations to lfo files from central simulation. +# +# Requires environment variables: +# ADAS_EXPDIR - path the ADAS experiment directory (ensemble of lfo files) +# GRID - target grid of forcing files (typically that of deterministic ADAS simulation) +# NENS - number of (LDAS) ensemble members (<= number of ADAS ensemble members) +# GEOSBIN - GEOSldas "ROOT" directory (typically GEOSldas/install/bin) +# +# Input data sets: +# 1. lfo files from coarse-resolution ADAS ensemble +# 2. lfo files from higher-resolution deterministic ADAS simulation +# +# Output data set: +# 1. ensemble of lfo files at resolution of deterministic ADAS simulation +# +# ------------------------------------------------------------------------------------ + +set force_cntr = "${ADAS_EXPDIR}/recycle/holdforc" +set force_orig = "${ADAS_EXPDIR}/atmens" +set force_rgd = "${ADAS_EXPDIR}/atmens/rgdlfo" +set outgrid = "${GRID}" + +mkdir $force_rgd + +$GEOSBIN/regrid_forc.csh $force_orig $force_rgd $outgrid + +rm -rf $force_orig/tmp* + +python $GEOSBIN/ensemble_forc.py $force_rgd $force_cntr $NENS + +cd $force_rgd +@ inens = 0 +while ($inens < $NENS) + @ inens ++ + if ($inens <10) then + set ENSDIR = `echo mem00${inens}` + else if($inens<100) then + set ENSDIR=`echo mem0${inens}` + endif + cd ${ENSDIR} + /bin/rm -rf *lfo*nc4 + $GEOSBIN/stripname nc4.Cpert nc4 + cd $force_rgd +end + diff --git a/GEOSldas_App/ens_forcing/ensemble_forc.py b/GEOSldas_App/ens_forcing/ensemble_forc.py new file mode 100755 index 0000000..413c570 --- /dev/null +++ b/GEOSldas_App/ens_forcing/ensemble_forc.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python +# +# module load python/GEOSpyD/Ana2019.03_py3.7 +# module load nco/4.8.1 +# +# Script for creating ensemble-perturbed land forcing (lfo) files. +# Usage: ensemble_forc.py [in_path] [cntr_path] [nens] +# +# where +# +# in_path : path to ensemble lfo files +# cntr_path : path to deterministic lfo files +# nens : number of ensemble members +#=========================================== + +import sys +import os +import glob +import subprocess as sp +import numpy as np +from netCDF4 import Dataset + + +def averaging_forcing(in_path, avg_path, nens): + """ The ensemble number will be appended to in_path starting from 001. + The out_path will be created if it does not exist. """ + print ( " average ensemble " ) + if not os.path.exists(avg_path): + os.makedirs(avg_path) + files_list=[] + for i in range(1,nens+1): + sfx = '%03d'%(i) + folder = in_path+'/mem'+sfx + fs = sorted(glob.glob(folder+'/*lfo*.nc4')) + files_list.append(fs) + for fs in zip(*files_list): + k = 1 + # verify filenames are the same. + for f in fs: + n = f.rindex('/') + if (k==1): + f0 = f[n+1:] + k = k+1 + f1 = f[n+1:] + assert f0 == f1, "averaging different files. Each folder should have same files" + fnames = " ".join(fs) + cmd = 'ncea ' + fnames + ' ' + avg_path +'/' + f0 + sp.call(cmd, shell=True) + +def mean_diff(ensm_path, cntr_path): + print ( " prepare central - ensmean for recentering ") + files_list=[] + folder = ensm_path + fs = sorted(glob.glob(folder+'/*lfo*.nc4')) + files_list.append(fs) + for fs in zip(*files_list): + k = 1 + for f in fs: + n = f.rindex('/') + if (k==1): + f0 = f[n+1:] + k = k+1 + f1 = f[n+1:] + assert f0 == f1, "central and ensmean should have same filenames" + cmd = 'ncbo --op_typ=sbt ' + cntr_path + '/' + f1 + ' ' + f + ' ' + f +'.dif' + #print ( cmd ) + sp.call(cmd, shell=True) + +def recenter_forc(in_path, avg_path, cntr_path, nens): + print ( "recenter ens forcing to central forcing " ) + Varlist = ["PRECCU", "PRECLS", "PRECSNO", "SWGDN" ] + print ( "Varlist for multipl-recentering: ", Varlist) + + mean_diff(avg_path, cntr_path) + + files_list1=[] + for i in range(1,nens+1): + sfx = '%03d'%(i) + folder = in_path+'/mem'+sfx + fs1 = sorted(glob.glob(folder+'/*lfo*.nc4')) + files_list1.append(fs1) + for fs in zip(*files_list1): + k = 1 + for f in fs: + n = f.rindex('/') + if (k==1): + f0 = f[n+1:] + k = k+1 + f1 = f[n+1:] + assert f0 == f1, "memeber and ensmean should have same filenames" + cmd = 'ncbo --op_typ=add ' + f + ' ' + avg_path +'/' + f1 + '.dif' + ' ' + f +'.Cpert' + sp.call(cmd, shell=True) + + files_list=[] + for i in range(1,nens+1): + sfx = '%03d'%(i) + folder = in_path+'/mem'+sfx + fs = sorted(glob.glob(folder+'/*tavg*lfo*.nc4')) + files_list.append(fs) + for fs in zip(*files_list): + k = 1 + for f in fs: + n = f.rindex('/') + if (k==1): + f0 = f[n+1:] + m_file = avg_path+'/'+f0 + Var_m = readlfo(m_file, Varlist) + c_file = cntr_path+'/'+f0 + Var_c = readlfo(c_file, Varlist) + k = k+1 + f1 = f[n+1:] + fout = f + '.Cpert' + Var_e = readlfo(f, Varlist) + Var_ux = fact_multpl(Var_m,Var_e,Var_c) + upd_vars(fout, Var_ux, Varlist) + +def readlfo(ncfile, Varlist): + wkfile = Dataset(ncfile, "r") + nydim = len(wkfile.dimensions["Ydim"]) + nxdim = len(wkfile.dimensions["Xdim"]) + nfdim = len(wkfile.dimensions["nf"]) + dim3 = [nfdim, nydim, nxdim] + nvar = len(Varlist) + vardata = np.zeros((nvar, dim3[0], dim3[1], dim3[2]), dtype=float) + for rdvar in iter(range(nvar)): + vardata[rdvar] = wkfile.variables[Varlist[rdvar]][0] + + wkfile.close() + return vardata + +def upd_vars(ncfile, Dataupd, Varupd): + + wkfile = Dataset(ncfile,'r+') + nvar = len(Varupd) + for rdvar in iter(range(nvar)) : + wkfile.variables[Varupd[rdvar]][0] = Dataupd[rdvar] + + wkfile.close() + +def fact_multpl(Var_dn,Var_up,Var_cn): + + dim = np.shape(Var_dn) + ndim = len(dim) + dim3 = [ dim[1], dim[2],dim[3] ] + ndim3 = len(dim3) + + #sum precip [0, 1, 2 from Varlist(4) ) + Var_3pup = np.zeros(dim3, dtype=float) + Var_3pdn = np.zeros(dim3, dtype=float) + Var_3pup[:,:,:] = Var_up[0,:,:,:] + Var_up[1,:,:,:] + Var_up[2,:,:,:] + Var_3pdn[:,:,:] = Var_dn[0,:,:,:] + Var_dn[1,:,:,:] + Var_dn[2,:,:,:] + nvec = 1 + for id in iter(range(ndim3)): + nvec = dim3[id]*nvec + + vecdn = np.reshape(Var_3pdn,nvec) + vecup = np.reshape(Var_3pup,nvec) + vecnew = np.zeros(nvec,dtype=float) + + ind = np.argwhere(vecdn>1.0e-10) + vecnew [ind] = vecup[ind] / vecdn[ind] + + Var_uu = np.zeros((dim3), dtype=float) + Var_uu = np.reshape(vecnew,(dim3)) + # apply the same factor to 3 precip + Var_upx = np.zeros((dim), dtype=float) + Var_upx[0,:,:,:] = Var_uu[:,:,:] * Var_cn[0,:,:,:] + Var_upx[1,:,:,:] = Var_uu[:,:,:] * Var_cn[1,:,:,:] + Var_upx[2,:,:,:] = Var_uu[:,:,:] * Var_cn[2,:,:,:] + # deal with swgdn + Var_3pup[:,:,:] = Var_up[3,:,:,:] + Var_3pdn[:,:,:] = Var_dn[3,:,:,:] + vecdn = np.reshape(Var_3pdn,nvec) + vecup = np.reshape(Var_3pup,nvec) + vecnew = np.zeros(nvec,dtype=float) + ind = np.argwhere(vecdn!=0) + vecnew [ind] = vecup[ind] / vecdn[ind] + Var_uu = np.zeros((dim3), dtype=float) + Var_uu = np.reshape(vecnew,(dim3)) + Var_upx[3,:,:,:] = Var_uu[:,:,:] * Var_cn[3,:,:,:] + + return Var_upx + + +if __name__ == '__main__' : + + in_path = sys.argv[1] + cntr_path = sys.argv[2] + nens = int(sys.argv[3]) + mean_path = in_path + '/lfomean' + averaging_forcing(in_path,mean_path, nens) + recenter_forc(in_path, mean_path, cntr_path, nens) diff --git a/GEOSldas_App/ens_forcing/regrid_forc.csh b/GEOSldas_App/ens_forcing/regrid_forc.csh new file mode 100755 index 0000000..d6b80f3 --- /dev/null +++ b/GEOSldas_App/ens_forcing/regrid_forc.csh @@ -0,0 +1,63 @@ +#!/usr/bin/csh -f + +set echo +setenv MYNAME regrid_forc.csh + +if ( $#argv < 3 ) then + echo " " + echo " SYNOPSIS " + echo " " + echo " $MYNAME force_in force_rgd outgrid" + echo " " + echo " where" + echo " force_in - path to orginal forcing, e.g., $ADAS_EXPDIR/atmens/" + echo " force_rgd - path to regrid forcing, e.g., $ADAS_EXPDIR/atmens/rgdlfo" + echo " outgrid - output grid , e.g., PE180x1080-CF ") + echo " NENS ensemble number , e.g., 24 should be env var ") + exit(0) +endif + +set ogdir = $1 +set rgdir = $2 +set outgrid = $3 + +set NENSin = $NENS +echo "check NENS, $NENSin " + +mkdir $ogdir/tmpd + +@ inens = 0 +while ($inens < $NENSin) + @ inens ++ + if ($inens <10) then + set ENSDIR = `echo mem00${inens}` + else if($inens<100) then + set ENSDIR=`echo mem0${inens}` + endif + /bin/ln -s $ogdir/ensdiag/${ENSDIR} $ogdir/tmpd/${ENSDIR} + mkdir $rgdir/${ENSDIR} +end + +cd $ogdir +/bin/ls -1 tmpd/mem*/*inst*lfo*nc4| awk 'NR == 1 {printf $0} NR > 1 {printf ", %s",$0} END {printf "\n"}' > tmpf +cat tmpf | sed 's/, /,/g' > tmpff +set ininst1 = (`cat tmpff `) +set outinst1 = (`cat tmpff | sed 's/tmpd/rgdlfo/g' `) + +/bin/ls -1 tmpd/mem*/*tavg*lfo*nc4| awk 'NR == 1 {printf $0} NR > 1 {printf ", %s",$0} END {printf "\n"}' > tmpf +cat tmpf | sed 's/, /,/g' > tmpff +set intavg1 = (`cat tmpff `) +set outtavg1 = (`cat tmpff | sed 's/tmpd/rgdlfo/g' `) + +set mympi = "mpirun" + + $mympi -np 24 $GEOSBIN/Regrid_Util.x -i $ininst1 \ + -o $outinst1 \ + -nx 4 -ny 6 \ + -ogrid $outgrid + + $mympi -np 24 $GEOSBIN/Regrid_Util.x -i $intavg1 \ + -o $outtavg1 \ + -nx 4 -ny 6 \ + -ogrid $outgrid + diff --git a/GEOSldas_App/ens_forcing/test_enpert_forc.j b/GEOSldas_App/ens_forcing/test_enpert_forc.j new file mode 100755 index 0000000..820027f --- /dev/null +++ b/GEOSldas_App/ens_forcing/test_enpert_forc.j @@ -0,0 +1,40 @@ +#!/usr/bin/csh + +# sample script for testing "enpert_forc.csh" +# +# must edit paths before submitting this script +# +# ----------------------------------------------------------- + +#SBATCH --ntasks=1 +#SBATCH --time=1:00:00 +#SBATCH --job-name=test_enpert_forc +#SBATCH --output=/discover/nobackup/[USER]/test_enpert_forc.o%j.txt +#SBATCH --export=NONE +#SBATCH --qos=debug + +# set environment variables needed by enpert_forc.csh + +setenv GEOSBIN /discover/nobackup/[USER]/GEOSldas/install/bin +setenv ADAS_EXPDIR /discover/nobackup/[USER]/[ADAS_EXPID]/ +setenv NENS 24 +setenv GRID PE180x1080-CF + +# load modules + +source $GEOSBIN/g5_modules + +# python should come with ESMA_env g5_modules +# module load python/GEOSpyD/Ana2019.03_py3.7 + +if ( -e /etc/os-release ) then + module load nco/4.8.1 +else + module load other/nco-4.6.8-gcc-5.3-sp3 +endif + +# execute test + +$GEOSBIN/enpert_forc.csh + +# ====================== EOF =================================== diff --git a/GEOSldas_App/ldas_setup b/GEOSldas_App/ldas_setup new file mode 100755 index 0000000..6cf1c5a --- /dev/null +++ b/GEOSldas_App/ldas_setup @@ -0,0 +1,1763 @@ +#!/usr/bin/env python3 + +import os +import sys +import glob +import copy +import linecache +import shutil +import argparse +import fileinput +import time +import resource +import subprocess as sp +import shlex +import tempfile +from dateutil import rrule +from datetime import datetime +from datetime import timedelta +from collections import OrderedDict +from dateutil.relativedelta import relativedelta +from remap_utils import * +from remap_catchANDcn import * +from remap_config_ldas import * + +""" +This script is intended to be run from any installed directory with GEOSldas.x and ldas_setup +(The default setup is ../install/bin) +""" + + +class LDASsetup: + + def __init__(self, cmdLineArgs): + """ + """ + # ------ + # Required exe input fields + # These fields are needed to pre-compute exp dir structure + # ------ + rqdExeInpKeys = ['EXP_ID', 'EXP_DOMAIN', 'NUM_LDAS_ENSEMBLE', + 'BEG_DATE', 'END_DATE','RESTART_PATH', + 'RESTART_DOMAIN','RESTART_ID','MET_TAG','MET_PATH','FORCE_DTSTEP','BCS_PATH', 'BCS_RESOLUTION'] + rqdExeInpKeys_rst = ['EXP_ID', 'EXP_DOMAIN', 'NUM_LDAS_ENSEMBLE', + 'BEG_DATE', 'END_DATE','MET_TAG','MET_PATH','FORCE_DTSTEP','BCS_PATH', 'BCS_RESOLUTION'] + + # These keywords are excluded from LDAS.rc (i.e., only needed in pre- or post-processing) + self.NoneLDASrcKeys=['EXP_ID', 'EXP_DOMAIN', + 'BEG_DATE', 'END_DATE','RESTART','RESTART_PATH', + 'RESTART_DOMAIN','RESTART_ID','BCS_PATH','TILING_FILE','GRN_FILE','LAI_FILE','LNFM_FILE','NIRDF_FILE', + 'VISDF_FILE','CATCH_DEF_FILE','NDVI_FILE', + 'NML_INPUT_PATH','HISTRC_FILE','RST_FROM_GLOBAL','JOB_SGMT','NUM_SGMT','POSTPROC_HIST', + 'MINLON','MAXLON','MINLAT','MAXLAT','EXCLUDE_FILE','INCLUDE_FILE','MWRTM_PATH','GRIDNAME', + 'ADAS_EXPDIR', 'BCS_RESOLUTION' ] + + # if built on sles15, BUILT_ON_SLES15 is "TRUE", else empty "" + BUILT_ON_SLES15 = "@BUILT_ON_SLES15@" + if BUILT_ON_SLES15 == "TRUE": + self.BUILT_ON_SLES15 = True + else: + self.BUILT_ON_SLES15 = False + + self.GEOS_SITE = "@GEOS_SITE@" + + # ------ + # Required resource manager input fields + # ------ + rqdRmInpKeys = ['account', 'walltime', 'ntasks_model', 'ntasks-per-node'] + # ------ + # Optional resource manager input fields + # ------ + optSlurmInpKeys = ['job_name', 'qos', 'oserver_nodes', 'writers-per-node'] + + # ------ + # ./ldsetup.py sample ... + # ------ + if 'exeinp' in cmdLineArgs: + # sample sub-command + # by construction, we can have + # either: {'exeinp': False, 'batinp': 'lasgh'} <-- 'lasgh'??? + # or: {'exeinp': True, 'batinp': None} + if cmdLineArgs['exeinp']: + _printExeInputKeys(rqdExeInpKeys) + elif cmdLineArgs['batinp'] : + _printRmInputKeys(rqdRmInpKeys, optSlurmInpKeys) + else: + raise Exception('not recognized option') + sys.exit(0) + + # ------ + # ./ldsetup.py setup ... + # ------ + # Instance variables + self.exeinpfile = cmdLineArgs['exeinpfile'] + self.batinpfile = cmdLineArgs['batinpfile'] + self.exphome = cmdLineArgs['exphome'].rstrip('/') + assert os.path.isdir(self.exphome) # exphome should exist + self.verbose = cmdLineArgs['verbose'] + self.runmodel = cmdLineArgs['runmodel'] + if self.runmodel : + print('\n The option "--runmodel" is out of date, not necessary anymore. \n') + self.daysperjob = cmdLineArgs['daysperjob'] + self.monthsperjob = cmdLineArgs['monthsperjob'] + self.rqdExeInp = OrderedDict() + self.rqdRmInp = OrderedDict() + self.optRmInp = OrderedDict() + self.rundir = None + self.blddir = None + self.blddirLn = None + self.outdir = None + self.out_path = None + self.inpdir = None + self.exefyl = None + self.islocal = False + self.catch = '' + self.has_mwrtm = False + self.has_vegopacity = False + self.assim = False + self.has_landassim_seed = False + self.has_geos_pert = False + self.nSegments = 1 + self.perturb = 0 + self.first_ens_id = 0 + self.ladas_coupling = 0 + self.in_rstfile = None + self.in_tilefile = 'None' # default string + self.ens_id_width = 6 # _eXXXX + self.bcs_land = '' + self.bcs_geom = '' + self.bcs_landshared = '' + + # ------ + # Read exe input file which is required to set up the dir + # ------ + self.rqdExeInp = self._parseInputFile(cmdLineArgs['exeinpfile']) + # verifing the required input + if 'RESTART' not in self.rqdExeInp : + self.rqdExeInp['RESTART'] = 1 + + if self.rqdExeInp['RESTART'].isdigit() : + if int(self.rqdExeInp['RESTART']) ==0 : + rqdExeInpKeys = rqdExeInpKeys_rst + self.rqdExeInp['RESTART_ID'] = "none" + self.rqdExeInp['RESTART_DOMAIN'] = "none" + self.rqdExeInp['RESTART_PATH'] = "none" + else: + if self.rqdExeInp['RESTART'] =='G' : + rqdExeInpKeys = rqdExeInpKeys_rst + self.rqdExeInp['RESTART_DOMAIN'] = "none" + else: + self.rqdExeInp['RESTART_ID'] = "none" + self.rqdExeInp['RESTART_DOMAIN'] = "none" + self.rqdExeInp['RESTART_PATH'] = "none" + + for key in rqdExeInpKeys : + assert key in self.rqdExeInp,' "%s" is required in the input file %s' % (key,self.exeinpfile) + + # print rqd exe inputs + if self.verbose: + print ('\nInputs from execfile:\n') + _printdict(self.rqdExeInp) + + # nens is an integer and =1 for model run + self.nens = int(self.rqdExeInp['NUM_LDAS_ENSEMBLE']) # fail if Nens's val is not int + assert self.nens>0, 'NUM_LDAS_ENSEMBLE [%d] <= 0' % self.nens + _mydir = self.exphome + '/' + self.rqdExeInp['EXP_ID'] + assert not os.path.isdir(_mydir), 'Dir [%s] already exists!' % _mydir + _mydir = None + self.ladas_coupling = int(self.rqdExeInp.get('LADAS_COUPLING',0)) + if self.ladas_coupling > 0: + assert 'ADAS_EXPDIR' in self.rqdExeInp, " need ADAS_EXPDIR in the input file %s" %(self.exeinpfile) + + self.first_ens_id = int(self.rqdExeInp.get('FIRST_ENS_ID',0)) + self.perturb = int(self.rqdExeInp.get('PERTURBATIONS',0)) + if self.nens > 1: + self.perturb = 1 + self.ensdirs = ['ens%04d'%iens for iens in range(self.first_ens_id, self.nens + self.first_ens_id)] + # if self.ens_id_width = 4, _width = '_e%04d' + _width = '_e%0{}d'.format(self.ens_id_width-2) + # self.ensids will be a list of [_e0000, _e0001, ...] + self.ensids = [ _width%iens for iens in range(self.first_ens_id, self.nens + self.first_ens_id)] + if (self.nens == 1) : + self.ensdirs_avg = self.ensdirs + self.ensids=[''] + else : + self.ensdirs_avg = self.ensdirs + ['ens_avg'] + + ## convert date-time strings to datetime object + ## start/end_time are converted to lists + ## ensure end>start + + self.begDates=[] + self.endDates=[] + self.begDates.append( + datetime.strptime( + self.rqdExeInp['BEG_DATE'], + '%Y%m%d %H%M%S' + ) + ) + self.endDates.append( + datetime.strptime( + self.rqdExeInp['END_DATE'], + '%Y%m%d %H%M%S' + ) + ) + if self.rqdExeInp['RESTART'].isdigit() : + if int(self.rqdExeInp['RESTART']) == 0 : + print ("No restart file (cold restart): Forcing start date to January 1, 0z") + year = self.begDates[0].year + self.begDates[0]=datetime(year =year,month=1,day =1,hour =0, minute= 0,second= 0) + + assert self.endDates[0]>self.begDates[0], \ + 'END_DATE <= BEG_DATE' + + self.job_sgmt = [] + if 'JOB_SGMT' in self.rqdExeInp: + self.job_sgmt.append("JOB_SGMT: "+self.rqdExeInp['JOB_SGMT']) + else: + _datediff = relativedelta(self.endDates[0],self.begDates[0]) + self.rqdExeInp['JOB_SGMT'] = "%04d%02d%02d %02d%02d%02d" %(_datediff.years, + _datediff.months, + _datediff.days, + _datediff.hours, + _datediff.minutes, + _datediff.seconds) + self.job_sgmt.append("JOB_SGMT: "+self.rqdExeInp['JOB_SGMT']) + + if 'NUM_SGMT' not in self.rqdExeInp: + self.rqdExeInp['NUM_SGMT'] = 1 + # hard set NUM_SGMT and NUM_SGMT + if (self.ladas_coupling > 0) : + if int(self.rqdExeInp['NUM_SGMT']) != 1 : + sys.exit("'NUM_SGMT' should be set to 1 with LADAS_COUPLING") + if self.rqdExeInp['JOB_SGMT'] != "00000000 060000" : + sys.exit("'JOB_SGMT' should be set to 00000000 060000 with LADAS_COUPLING") + + _years = int(self.rqdExeInp['JOB_SGMT'][0:4]) + _months = int(self.rqdExeInp['JOB_SGMT'][4:6]) + _days = int(self.rqdExeInp['JOB_SGMT'][6:8]) + assert self.rqdExeInp['JOB_SGMT'][8] == ' ' and self.rqdExeInp['JOB_SGMT'][9] != ' ', "JOB_SGMT format is not right" + _hours = int(self.rqdExeInp['JOB_SGMT'][9:11]) + _mins = int(self.rqdExeInp['JOB_SGMT'][11:13]) + _seconds= int(self.rqdExeInp['JOB_SGMT'][13:15]) + + + _difftime =timedelta(days = _years*365+_months*30+_days,hours = _hours,minutes=_mins,seconds=_seconds) + _difftime = int(self.rqdExeInp['NUM_SGMT'])*_difftime + print (int(self.rqdExeInp['NUM_SGMT'])) + _d = self.begDates[0] + _endDate = self.endDates[0] + _d = _d + _difftime + while _d < _endDate : + print (_difftime.days) + self.nSegments +=1 + print (_d.year, _d.month, _d.day) + self.begDates.append(_d) + self.endDates.insert(-1,_d) + _d = _d+ _difftime + + # make sure path is path + self.bcs_land = self.rqdExeInp['BCS_PATH']+ '/land/' + self.rqdExeInp['BCS_RESOLUTION']+'/' + self.bcs_geom = self.rqdExeInp['BCS_PATH']+ '/geometry/' + self.rqdExeInp['BCS_RESOLUTION']+'/' + self.bcs_landshared = self.rqdExeInp['BCS_PATH']+ '/land/shared/' + + if self.rqdExeInp['MET_PATH'][-1] != '/': + self.rqdExeInp['MET_PATH'] = self.rqdExeInp['MET_PATH']+'/' + if self.rqdExeInp['RESTART_PATH'][-1] != '/': + self.rqdExeInp['RESTART_PATH'] = self.rqdExeInp['RESTART_PATH']+'/' + + # make sure catchment and vegdyn restart files ( at least one for each) exist + if 'CATCH_DEF_FILE' not in self.rqdExeInp: + self.rqdExeInp['CATCH_DEF_FILE']= self.bcs_land + 'clsm/catchment.def' + assert os.path.isfile(self.rqdExeInp['CATCH_DEF_FILE']),"[%s] file does not exist " % self.rqdExeInp['CATCH_DEF_FILE'] + + self.rqdExeInp['RST_FROM_GLOBAL'] = 1 + if self.rqdExeInp['RESTART'].isdigit() : + if int(self.rqdExeInp['RESTART']) == 1 : + _numg = int(linecache.getline(self.rqdExeInp['CATCH_DEF_FILE'], 1).strip()) + _numd = _numg + ldas_domain = self.rqdExeInp['RESTART_PATH']+ \ + self.rqdExeInp['RESTART_ID'] + \ + '/output/'+self.rqdExeInp['RESTART_DOMAIN']+'/rc_out/'+self.rqdExeInp['RESTART_ID']+'.ldas_domain.txt' + if os.path.isfile(ldas_domain) : + _numd = int(linecache.getline(ldas_domain, 1).strip()) + + if _numg != _numd : + self.rqdExeInp['RST_FROM_GLOBAL'] = 0 + + self.rqdExeInp['LNFM_FILE'] = '' + if int(self.rqdExeInp['RST_FROM_GLOBAL']) == 1 : + self.rqdExeInp['TILING_FILE'] =glob.glob(self.bcs_geom + '*.til')[0] + self.rqdExeInp['GRN_FILE']= glob.glob(self.bcs_land + 'green_clim_*.data')[0] + self.rqdExeInp['LAI_FILE']= glob.glob(self.bcs_land + 'lai_clim_*.data')[0] + tmp_ = glob.glob(self.bcs_land + 'lnfm_clim_*.data') + if (len(tmp_) ==1) : + self.rqdExeInp['LNFM_FILE'] = tmp_[0] + self.rqdExeInp['NDVI_FILE'] = glob.glob(self.bcs_land + 'ndvi_clim_*.data')[0] + self.rqdExeInp['NIRDF_FILE']= glob.glob(self.bcs_land + 'nirdf_*.dat')[0] + self.rqdExeInp['VISDF_FILE']= glob.glob(self.bcs_land + 'visdf_*.dat')[0] + else : + inpdir=self.rqdExeInp['RESTART_PATH']+self.rqdExeInp['RESTART_ID']+'/input/' + self.rqdExeInp['TILING_FILE'] =os.path.realpath(glob.glob(inpdir+'*tile.data')[0]) + self.rqdExeInp['GRN_FILE']= os.path.realpath(glob.glob(inpdir+'green*data')[0]) + self.rqdExeInp['LAI_FILE']= os.path.realpath(glob.glob(inpdir+'lai*data')[0]) + tmp_ = glob.glob(self.bcs_land + 'lnfm_clim_*.data') + if (len(tmp_) == 1) : + self.rqdExeInp['LNFM_FILE'] = tmp_[0] + self.rqdExeInp['NDVI_FILE']= os.path.realpath(glob.glob(inpdir+'ndvi*data')[0]) + self.rqdExeInp['NIRDF_FILE']= os.path.realpath(glob.glob(inpdir+'nirdf*data')[0]) + self.rqdExeInp['VISDF_FILE']= os.path.realpath(glob.glob(inpdir+'visdf*data')[0]) + + if self.rqdExeInp['RESTART'].isdigit() : + if int(self.rqdExeInp['RESTART']) == 2 : + self.rqdExeInp['RST_FROM_GLOBAL'] = 1 + ldas_domain = self.rqdExeInp['RESTART_PATH']+ \ + self.rqdExeInp['RESTART_ID'] + \ + '/output/'+self.rqdExeInp['RESTART_DOMAIN']+'/rc_out/'+self.rqdExeInp['RESTART_ID']+'.ldas_domain.txt' + inpdir=self.rqdExeInp['RESTART_PATH']+self.rqdExeInp['RESTART_ID']+'/input/' + in_tilefiles_ = glob.glob(inpdir+'*tile.data') + if len(in_tilefiles_) == 0 : + inpdir=self.rqdExeInp['RESTART_PATH']+self.rqdExeInp['RESTART_ID']+'/output/'+self.rqdExeInp['RESTART_DOMAIN']+'/rc_out/' + in_tilefiles_ = glob.glob(inpdir+'MAPL_*.til') + if len(in_tilefiles_) == 0 : + in_tilefiles_ = glob.glob(inpdir+'/*.til') + self.in_tilefile =os.path.realpath(in_tilefiles_[0]) + + if os.path.isfile(ldas_domain) : + _numd = int(linecache.getline(ldas_domain, 1).strip()) + self.rqdExeInp['TILING_FILE'] =glob.glob(self.bcs_geom + '*.til')[0] + self.rqdExeInp['GRN_FILE']= glob.glob(self.bcs_land + 'green_clim_*.data')[0] + self.rqdExeInp['LAI_FILE']= glob.glob(self.bcs_land + 'lai_clim_*.data')[0] + tmp_ = glob.glob(self.bcs_land + 'lnfm_clim_*.data') + if (len(tmp_) == 1) : + self.rqdExeInp['LNFM_FILE'] = tmp_[0] + self.rqdExeInp['LNFM_FILE'] = glob.glob(self.bcs_land + 'lnfm_clim_*.data')[0] + self.rqdExeInp['NDVI_FILE'] = glob.glob(self.bcs_land + 'ndvi_clim_*.data')[0] + self.rqdExeInp['NIRDF_FILE']= glob.glob(self.bcs_land + 'nirdf_*.dat')[0] + self.rqdExeInp['VISDF_FILE']= glob.glob(self.bcs_land + 'visdf_*.dat')[0] + + if 'GRIDNAME' not in self.rqdExeInp : + tmptile =self.rqdExeInp['TILING_FILE'] + self.rqdExeInp['GRIDNAME'] = linecache.getline(tmptile, 3).strip() + + if 'LSM_CHOICE' not in self.rqdExeInp: + self.rqdExeInp['LSM_CHOICE'] = 1 + + if int(self.rqdExeInp['LSM_CHOICE']) == 1 : + self.catch = 'catch' + if int(self.rqdExeInp['LSM_CHOICE']) == 2 : + self.catch = 'catchcnclm40' + + assert int(self.rqdExeInp['LSM_CHOICE']) <= 2, "\nLSM_CHOICE=3 (Catchment-CN4.5) is no longer supported. Please set LSM_CHOICE to 1 (Catchment) or 2 (Catchment-CN4.0)" + + if 'POSTPROC_HIST' not in self.rqdExeInp: + self.rqdExeInp['POSTPROC_HIST'] = 0 + + if 'LADAS_COUPLING' not in self.rqdExeInp: + self.rqdExeInp['LADAS_COUPLING'] = 0 + + if 'RUN_IRRIG' not in self.rqdExeInp: + self.rqdExeInp['RUN_IRRIG'] = 0 + + if 'AEROSOL_DEPOSITION' not in self.rqdExeInp: + self.rqdExeInp['AEROSOL_DEPOSITION'] = 0 + # default is global + _domain_dic=OrderedDict() + _domain_dic['MINLON']=-180. + _domain_dic['MAXLON']= 180. + _domain_dic['MINLAT']= -90. + _domain_dic['MAXLAT']= 90. + _domain_dic['EXCLUDE_FILE']= "''" + _domain_dic['INCLUDE_FILE']= "''" + + for key,val in _domain_dic.items() : + if key in self.rqdExeInp : + _domain_dic[key]= self.rqdExeInp[key] + self.domain_def = tempfile.NamedTemporaryFile(mode='w', delete=False) + self.domain_def.write('&domain_inputs\n') + for key,val in _domain_dic.items() : + keyn=(key+" = ").ljust(16) + valn = str(val) + if '_FILE' in key: + self.domain_def.write(keyn+ "'"+valn+"'"+'\n') + else : + self.domain_def.write(keyn+ valn +'\n') + self.domain_def.write('/\n') + self.domain_def.close() + + # make sure bcs files exist + if self.rqdExeInp['RESTART'].isdigit() : + if int(self.rqdExeInp['RESTART']) >= 1 : + y4m2='Y%4d/M%02d' % (self.begDates[0].year, self.begDates[0].month) + y4m2d2_h2m2='%4d%02d%02d_%02d%02d' % (self.begDates[0].year, self.begDates[0].month, + self.begDates[0].day,self.begDates[0].hour,self.begDates[0].minute) + tmpFile=self.rqdExeInp['RESTART_ID']+'.'+self.catch+'_internal_rst.'+y4m2d2_h2m2 + tmpRstDir=self.rqdExeInp['RESTART_PATH']+'/'.join([self.rqdExeInp['RESTART_ID'],'output', + self.rqdExeInp['RESTART_DOMAIN'],'rs',self.ensdirs[0],y4m2]) + catchRstFile=tmpRstDir+'/'+tmpFile + + assert os.path.isfile(catchRstFile), self.catch+'_internal_rst file [%s] does not exist!' %(catchRstFile) + self.in_rstfile = catchRstFile + + if int(self.rqdExeInp['RESTART']) == 1 : + tmpFile=self.rqdExeInp['RESTART_ID']+'.vegdyn_internal_rst' + tmpRstDir=self.rqdExeInp['RESTART_PATH']+'/'.join([self.rqdExeInp['RESTART_ID'],'output', + self.rqdExeInp['RESTART_DOMAIN'],'rs',self.ensdirs[0]]) + vegdynRstFile=tmpRstDir+'/'+tmpFile + if not os.path.isfile(vegdynRstFile): + assert int(self.rqdExeInp['RST_FROM_GLOBAL']) == 1, 'restart from LDASsa should be global' + + tmpFile=self.rqdExeInp['RESTART_ID']+'.landpert_internal_rst.'+y4m2d2_h2m2 + tmpRstDir=self.rqdExeInp['RESTART_PATH']+'/'.join([self.rqdExeInp['RESTART_ID'],'output', + self.rqdExeInp['RESTART_DOMAIN'],'rs',self.ensdirs[0],y4m2]) + landpertRstFile=tmpRstDir+'/'+tmpFile + if ( os.path.isfile(landpertRstFile)) : + self.has_geos_pert = True + + elif (int(self.rqdExeInp['RESTART']) == 0) : + if (self.catch == 'catch'): + self.in_rstfile = '/discover/nobackup/projects/gmao/ssd/land/l_data/LandRestarts_for_Regridding' \ + '/Catch/M09/20170101/catch_internal_rst' + self.in_tilefile = '/discover/nobackup/projects/gmao/ssd/land/l_data/geos5/bcs/CLSM_params' \ + '/mkCatchParam_SMAP_L4SM_v002/SMAP_EASEv2_M09/SMAP_EASEv2_M09_3856x1624.til' + elif (self.catch == 'catchcnclm40'): + self.in_rstfile = '/discover/nobackup/projects/gmao/ssd/land/l_data/LandRestarts_for_Regridding' \ + '/CatchCN/M36/20150301_0000/catchcnclm40_internal_dummy' + self.in_tilefile = '/discover/nobackup/projects/gmao/bcs_shared/legacy_bcs/Heracles-NL/SMAP_EASEv2_M36/SMAP_EASEv2_M36_964x406.til' + elif (self.catch == 'catchcnclm45'): + self.in_rstfile = '/discover/nobackup/projects/gmao/ssd/land/l_data/LandRestarts_for_Regridding' \ + '/CatchCN/M36/19800101_0000/catchcnclm45_internal_dummy' + self.in_tilefile = '/discover/nobackup/projects/gmao/bcs_shared/legacy_bcs/Icarus-NLv3/Icarus-NLv3_EASE/SMAP_EASEv2_M36/SMAP_EASEv2_M36_964x406.til' + else: + sys.exit('need to provide at least dummy files') + self.in_rstfile = None + self.in_tilefile = None + + # DEAL WITH mwRTM input from exec + self.assim = True if self.rqdExeInp.get('LAND_ASSIM', 'NO').upper() == 'YES' else False + # verify mwrtm file + if 'MWRTM_PATH' in self.rqdExeInp : + self.rqdExeInp['MWRTM_PATH'] = self.rqdExeInp['MWRTM_PATH']+'/'+ self.rqdExeInp['BCS_RESOLUTION']+'/' + mwrtm_param_file_ = self.rqdExeInp['MWRTM_PATH']+'mwRTM_param.nc4' + vegopacity_file_ = self.rqdExeInp['MWRTM_PATH']+'vegopacity.bin' + if os.path.isfile(mwrtm_param_file_) : + self.has_mwrtm = True + self.mwrtm_file = mwrtm_param_file_ + else : + assert not mwrtm_param_file_.strip(), ' MWRTM_PATH: %s should contain mwRTM_param.nc4'% self.rqdExeInp['MWRTM_PATH'] + del self.rqdExeInp['MWRTM_PATH'] + if os.path.isfile(vegopacity_file_) : + self.has_vegopacity = True + self.rqdExeInp['VEGOPACITY_FILE'] = vegopacity_file_ + + # DEAL WITH optional input from exec + + # ------ + # Read rm input file + # Read (and pop from inpfile) the input required fields in to + # self.rqdRmInp. Fields left in inpDictFromFile are then + # read in to self.optRmInp + # ------ + # re-using inpDictFromFile + + inpDictFromFile = self._parseInputFile(cmdLineArgs['batinpfile']) + + # REQUIRED inputs + for key in rqdRmInpKeys: + self.rqdRmInp[key] = inpDictFromFile.pop(key) + + # checks on rqd rm inputs + ## account and walltime should exist + assert self.rqdRmInp['account'] + if cmdLineArgs['account'] != 'None': + self.rqdRmInp['account'] = cmdLineArgs['account'] + assert self.rqdRmInp['walltime'] + ## ntasks_model is a +ve integer + _ntasks = int(self.rqdRmInp['ntasks_model']) + assert _ntasks>0 + self.rqdRmInp['ntasks_model'] = _ntasks + _ntasks = None + + # print rqd rm inputs + if self.verbose: + print ('\n\nRequired inputs for resource manager:') + _printdict(self.rqdRmInp) + + # OPTIONAL inputs + for key in inpDictFromFile: + assert key in optSlurmInpKeys, \ + 'unknown resource manager key [%s]' % key + self.optRmInp[key] = inpDictFromFile[key] + + # print opt rm inputs + if self.verbose: + print ('\n\nOptional inputs for resource manager:') + _printdict(self.optRmInp) + + # ------ + # set top level directories + # rundir, inpdir, outdir, blddir + # executable + # exefyl + # ------ + + self.bindir = os.path.dirname(os.path.realpath(__file__)) + self.blddir = self.bindir.rsplit('/',1)[0] + exefyl = '/bin/GEOSldas.x' + tmp_execfyl= self.blddir+exefyl + assert os.path.isfile(tmp_execfyl),\ + 'Executable [%s] does not exist!' % tmp_execfyl + tmp_expid = self.rqdExeInp['EXP_ID'] + tmp_expdir = os.path.abspath(self.exphome + '/' + self.rqdExeInp['EXP_ID']) + self.rundir = tmp_expdir + '/run' + self.inpdir = tmp_expdir + '/input' + self.outdir = tmp_expdir + '/output' + self.scratchdir = tmp_expdir + '/scratch' + self.blddirLn = tmp_expdir + '/build' + self.out_path = self.outdir+'/'+self.rqdExeInp['EXP_DOMAIN'] + self.bcsdir = self.outdir+'/'+self.rqdExeInp['EXP_DOMAIN']+'/rc_out/' + self.rstdir = self.outdir+'/'+self.rqdExeInp['EXP_DOMAIN']+'/rs/' + self.exefyl = self.blddirLn+exefyl + + tmp_expid = None + tmp_expdir = None + + my_ntasks_per_node = int(self.rqdRmInp['ntasks-per-node']) + + # default number of nodes + my_nodes = self.rqdRmInp['ntasks_model'] // my_ntasks_per_node + if self.rqdRmInp['ntasks_model'] % my_ntasks_per_node > 0 : + my_nodes = my_nodes + 1 + + # default is set to 0 ( no output server) + if 'oserver_nodes' not in self.optRmInp : + self.optRmInp['oserver_nodes'] = 0 + + self.optRmInp['nodes'] = my_nodes + int(self.optRmInp['oserver_nodes']) + + if (int(self.optRmInp['oserver_nodes']) >=1) : + self.rqdExeInp['WRITE_RESTART_BY_OSERVER'] = "YES" + # set default for now + if 'writers-per-node' not in self.optRmInp: + self.optRmInp['writers-per-node'] = 5 + else: + self.optRmInp['writers-per-node'] = 0 + + + def _parseInputFile(self, inpfile): + """ + Private method: parse input file and return a dict of options + Input: input file + Output: dict + """ + + inpdict = OrderedDict() + errstr = "line [%d] of [%s] is not in the form 'key: value'" + + fin = open(inpfile, 'r') + linenum = 0 + for line in fin: + linenum += 1 + line = line.strip() + # blank line + if not line: + continue + if '"GEOSldas=>"' in line: + continue + # get "GEOSldas=>" default in GEOS_LandGrid.rc + if 'GEOSldas=>' in line: + line = line.split('GEOSldas=>')[1] + # handle comments + position = line.find('#') + if position==0: # comment line + continue + if position>0: # strip out comment + line = line[:position] + # we expect a line to be of the form + # key = value + assert ':' in line, errstr % (linenum, inpfile) + + key, val = line.split(':',1) + key = key.strip() + val = val.strip() + if not key or not val: + print ("WARNING: " + errstr % (linenum, inpfile)) + continue + #raise Exception(errstr % (linenum, inpfile)) + if key in inpdict: + raise Exception('Duplicate key [%s] in [%s]' % (key, inpfile)) + inpdict[key] = val.strip() + fin.close() + + return inpdict + + + def _mkdir_p(self,path): + """ + Private method: implement 'mkdir -p' functionality + """ + + if os.path.isdir(path): + return + else: + os.makedirs(path) + + def createDirStructure(self): + """ + Create required dir structure + """ + + status = False + + # shorthands + _nens = self.nens + + # run/inp/wrk dirs + self._mkdir_p(self.exphome+'/'+self.rqdExeInp['EXP_ID']) + self._mkdir_p(self.rundir) + self._mkdir_p(self.inpdir) + self._mkdir_p(self.outdir) + self._mkdir_p(self.scratchdir) + + #-start-shorthand-function- + def _getDirName(outtyp, ensdir, yyyymm): + return '/'.join([ + self.outdir, + self.rqdExeInp['EXP_DOMAIN'], + outtyp, # ana/cat/rs/rc_out + ensdir, + yyyymm + ]) + #-end-shorthand-function- + + # met forcing dir + myMetDir = self.inpdir + '/met_forcing' + self._mkdir_p(myMetDir) + + # ensxxxx directories + nSegments = self.nSegments + for iseg in range(nSegments): + _start = self.begDates[iseg] + _end = self.endDates[iseg] + + # Yyyyy/Mmm between StartDateTime and EndDateTime + newDate = _start + y4m2_list = [('Y%4d/M%02d' % (newDate.year, newDate.month))] + while newDate<_end: + newDate += relativedelta(months=1) + y4m2_list.append('Y%4d/M%02d' % (newDate.year, newDate.month)) + + # ExpDomain/ana/, /cat/ directories + for ensdir in self.ensdirs_avg: + for y4m2 in y4m2_list: + self._mkdir_p(_getDirName('ana', ensdir, y4m2)) + self._mkdir_p(_getDirName('cat', ensdir, y4m2)) + + # ExpDomain/rs/ directories + for ensdir in self.ensdirs: + for y4m2 in y4m2_list: + self._mkdir_p(_getDirName('rs', ensdir, y4m2)) + + # ExpDomain/rc_out/ - only for _start + self._mkdir_p(_getDirName('rc_out', '', y4m2_list[0])) + + # restart dir + self._mkdir_p(self.inpdir + '/restart') + + status = True + return status + + # create link, BCs , restarts + def createLnRstBc(self) : + # link bld dir + status = False + + _nens = self.nens + + os.symlink(self.blddir, self.blddirLn) + + # met forcing dir + self.ensemble_forcing = True if self.rqdExeInp.get('ENSEMBLE_FORCING', 'NO').upper() == 'YES' else False + + myMetPath ='' + for _i in range(self.first_ens_id, _nens + self.first_ens_id) : + str_ens = '' + if ( _nens != 1 and self.ensemble_forcing): + str_ens = '%03d'%(_i) + metpath = self.rqdExeInp['MET_PATH'].rstrip('/')+str_ens + myMetDir = self.inpdir + '/met_forcing' + myMetPath = myMetDir + '/' + metpath.split('/')[-1] + os.symlink(metpath, myMetPath) + # update 'met_path' to use relative path from outdir + if ( not self.ensemble_forcing): + break + if ( _nens !=1 and self.ensemble_forcing) : + # replace last three character with '%s" + self.rqdExeInp['MET_PATH'] = os.path.relpath(myMetPath, self.rundir)[:-3]+'%s' + else: + self.rqdExeInp['MET_PATH'] = os.path.relpath(myMetPath, self.rundir) + + # update tile file + tile= self.rqdExeInp['TILING_FILE'] + short_tile= os.path.basename(self.rqdExeInp['TILING_FILE']) + newtile = self.bcsdir+'/'+short_tile + shutil.copy(tile, newtile) + tile=newtile + # if three extra lines exist, remove them and save it to inputdir + + print ('\nCorrect the tile file if it is an old EASE tile format... \n') + EASEtile=self.bcsdir+'/MAPL_'+short_tile + cmd = self.bindir + '/preprocess_ldas.x correctease '+ tile + ' '+ EASEtile + if self.BUILT_ON_SLES15 : + print ("Executables were built on SLES15 and must be run on SLES15: " + cmd) + else: + print ("cmd: " + cmd) + + sp.call(shlex.split(cmd)) + + if os.path.isfile(EASEtile) : + #update tile file name + short_tile ='MAPL_'+short_tile + tile=EASEtile + # setup BC files + + catchment_def = self.rqdExeInp['CATCH_DEF_FILE'] + exp_id = self.rqdExeInp['EXP_ID'] + + _start = self.begDates[0] + _y4m2d2h2m2 ='%4d%02d%02d%02d%02d' % (_start.year, _start.month,_start.day,_start.hour,_start.minute) + + dzsf = '50.0' + if 'SURFLAY' in self.rqdExeInp : + dzsf = self.rqdExeInp['SURFLAY'] + + # These are dummy values for *cold* restart: + wemin_in = '13' # WEmin input/output for scale_catch(cn), + wemin_out = '13' # + if 'WEMIN_IN' in self.rqdExeInp : + wemin_in = self.rqdExeInp['WEMIN_IN'] + if 'WEMIN_OUT' in self.rqdExeInp : + wemin_out = self.rqdExeInp['WEMIN_OUT'] + + + tmp_f2g_file = tempfile.NamedTemporaryFile(delete=False) + cmd = self.bindir +'/preprocess_ldas.x c_f2g ' + tile + ' ' + self.domain_def.name + ' '+ self.out_path + ' ' + catchment_def + ' ' + exp_id + ' ' + _y4m2d2h2m2 + ' '+ dzsf + ' ' + tmp_f2g_file.name + + print ('Creating f2g file: '+ tmp_f2g_file.name +'....\n') + print ("cmd: " + cmd) + sp.call(shlex.split(cmd)) + # check if it is local or global + with open(tmp_f2g_file.name) as f2gfile : + head=[next(f2gfile) for x in range(2)] + if(head[0].strip() != head[1].strip()) : + self.islocal= True + #os.remove(self.domain_def.name) + + # update tile domain + if self.islocal: + newlocalTile = tile+'.domain' + print ("\nCreating local tile file :"+ newlocalTile) + print ("\n by excluding land type MAPL_Land_ExcludeFromDomain=1100...\n") + cmd = self.bindir +'/preprocess_ldas.x c_localtile ' + tile + ' ' + newlocalTile + ' '+ tmp_f2g_file.name + print ("cmd: " + cmd) + sp.call(shlex.split(cmd)) + short_tile=short_tile +'.domain' + tile = newlocalTile + + myTile=self.inpdir+'/tile.data' + os.symlink(tile,myTile) + + + bcs=[self.rqdExeInp['GRN_FILE'], + self.rqdExeInp['LAI_FILE'], + self.rqdExeInp['NDVI_FILE'], + self.rqdExeInp['NIRDF_FILE'], + self.rqdExeInp['VISDF_FILE'] ] + if (self.rqdExeInp['LNFM_FILE'] != ''): + bcs += [self.rqdExeInp['LNFM_FILE']] + if (self.has_vegopacity): + bcs += [self.rqdExeInp['VEGOPACITY_FILE']] + bcstmp=[] + for bcf in bcs : + shutil.copy(bcf, self.bcsdir+'/') + bcstmp=bcstmp+[self.bcsdir+'/'+os.path.basename(bcf)] + bcs=bcstmp + + if self.islocal: + print ("Creating the boundary files for the simulation domain...\n") + bcs_tmp=[] + for bcf in bcs : + cmd = self.bindir +'/preprocess_ldas.x c_localbc ' + bcf + ' '+ bcf+'.domain' + ' '+ tmp_f2g_file.name + print ("cmd: " + cmd) + sp.call(shlex.split(cmd)) + bcs_tmp=bcs_tmp+[bcf+'.domain'] + bcs=bcs_tmp + + + # link BC + print ("linking bcs...") + bcnames=['green','lai','ndvi','nirdf','visdf'] + if (self.rqdExeInp['LNFM_FILE'] != ''): + bcnames += ['lnfm'] + if (self.has_vegopacity): + bcnames += ['vegopacity'] + for bcln,bc in zip(bcnames,bcs) : + myBC=self.inpdir+'/'+bcln+'.data' + os.symlink(bc,myBC) + + if ("catchcn" in self.catch): + os.symlink(self.bcs_landshared + 'CO2_MonthlyMean_DiurnalCycle.nc4', \ + self.inpdir+'/CO2_MonthlyMean_DiurnalCycle.nc4') + + # create and link restart + print ("Creating and linking restart...") + _start = self.begDates[0] + + y4m2='Y%4d/M%02d'%(_start.year, _start.month) + y4m2d2_h2m2 ='%4d%02d%02d_%02d%02d' % (_start.year, _start.month,_start.day,_start.hour,_start.minute) + + myRstDir = self.inpdir + '/restart/' + + rstpath = self.rqdExeInp['RESTART_PATH']+ \ + self.rqdExeInp['RESTART_ID'] + \ + '/output/'+self.rqdExeInp['RESTART_DOMAIN']+'/rs/' + rcoutpath = self.rqdExeInp['RESTART_PATH']+ \ + self.rqdExeInp['RESTART_ID'] + \ + '/output/'+self.rqdExeInp['RESTART_DOMAIN']+'/rc_out/' + + # pass into remap_config_ldas + sponsorid = self.rqdRmInp['account'] + exp_id = self.rqdExeInp['EXP_ID'] + exp_dir = self.exphome + out_bcdir = self.rqdExeInp['BCS_PATH'] + out_tilefile = self.rqdExeInp['TILING_FILE'] + RESTART_str = str(self.rqdExeInp['RESTART']) + YYYYMMDD = '%4d%02d%02d' % (_start.year, _start.month,_start.day) + YYYYMMDDHH= '%4d%02d%02d%02d' % (_start.year, _start.month,_start.day, _start.hour) + rstid = self.rqdExeInp['RESTART_ID'] + rstdomain = self.rqdExeInp['RESTART_DOMAIN'] + rstpath0 = self.rqdExeInp['RESTART_PATH'] + + # just copy the landassim pert seed if it exists + for iens in range(self.nens) : + _ensdir = self.ensdirs[iens] + _ensid = self.ensids[iens] + landassim_seeds = rstpath + _ensdir + '/' + y4m2+'/' + rstid + '.landassim_obspertrseed_rst.'+y4m2d2_h2m2 + if os.path.isfile(landassim_seeds) and self.assim : + _seeds = self.rstdir + _ensdir + '/' + y4m2+'/' + exp_id + '.landassim_obspertrseed_rst.'+y4m2d2_h2m2 + shutil.copy(landassim_seeds, _seeds) + os.symlink(_seeds, myRstDir+ '/landassim_obspertrseed'+ _ensid +'_rst') + self.has_landassim_seed = True + mk_outdir = self.exphome+'/'+exp_id+'/mk_restarts/' + + if (RESTART_str != '1'): + bcs_path = self.rqdExeInp['BCS_PATH'] + while bcs_path[-1] == '/' : bcs_path = bcs_path[0:-1] + bc_base = os.path.dirname(bcs_path) + bc_version = os.path.basename(bcs_path) + + remap_tpl = os.path.dirname(os.path.realpath(__file__)) + '/remap_params.tpl' + config = yaml_to_config(remap_tpl) + + config['slurm']['account'] = self.rqdRmInp['account'] + config['slurm']['qos'] = 'debug' + + config['input']['surface']['catch_tilefile'] = self.in_tilefile + config['input']['shared']['expid'] = self.rqdExeInp['RESTART_ID'] + config['input']['shared']['yyyymmddhh'] = YYYYMMDDHH + config['input']['shared']['rst_dir'] = os.path.dirname(self.in_rstfile)+'/' + config['input']['surface']['wemin'] = wemin_in + config['input']['surface']['catch_model'] = self.catch + + config['output']['shared']['out_dir'] = mk_outdir + config['output']['surface']['catch_remap'] = True + config['output']['surface']['catch_tilefile'] = self.rqdExeInp['TILING_FILE'] + config['output']['shared']['bc_base'] = bc_base + config['output']['shared']['bc_version'] = bc_version + config['output']['surface']['EASE_grid'] = self.rqdExeInp['BCS_RESOLUTION'] + + config['output']['shared']['expid'] = self.rqdExeInp['EXP_ID'] + config['output']['surface']['surflay'] = dzsf + config['output']['surface']['wemin'] = wemin_out + + config = remap_config_ldas( config, RESTART_str, self.rqdExeInp['RESTART_PATH'], self.rqdExeInp['RESTART_ID']) + + catch_obj = catchANDcn(config_obj = config) + catch_obj.remap() + + #for ens in self.ensdirs : + catchRstFile0 = '' + vegdynRstFile0 = '' + for iens in range(self.nens) : + ensdir = self.ensdirs[iens] + ensid = self.ensids[iens] + myCatchRst = myRstDir+'/'+self.catch +ensid +'_internal_rst' + myVegRst = myRstDir+'/'+'vegdyn'+ensid +'_internal_rst' + myPertRst = myRstDir+'/'+ 'landpert' +ensid +'_internal_rst' + + catchRstFile = '' + vegdynRstFile = '' + pertRstFile = '' + print ("restart: " + self.rqdExeInp['RESTART']) + + if self.rqdExeInp['RESTART'].isdigit() : + + if int(self.rqdExeInp['RESTART']) == 0 or int(self.rqdExeInp['RESTART']) == 2 : + vegdynRstFile = glob.glob(self.bcs_land + 'vegdyn_*.dat')[0] + catchRstFile = glob.glob(self.exphome+'/'+exp_id+'/mk_restarts/*'+self.catch+'_internal_rst.'+YYYYMMDD+'*')[0] + else : # RESTART == 1 + catchRstFile = rstpath+ensdir +'/'+ y4m2+'/'+self.rqdExeInp['RESTART_ID']+'.'+self.catch+'_internal_rst.'+y4m2d2_h2m2 + vegdynRstFile= rstpath+ensdir +'/'+self.rqdExeInp['RESTART_ID']+ '.vegdyn_internal_rst' + if not os.path.isfile(vegdynRstFile): # no vegdyn restart from LDASsa + if not os.path.isfile(vegdynRstFile0): + vegdynRstFile = glob.glob(self.bcs_land + 'vegdyn_*.dat')[0] + else : + vegdynRstFile = glob.glob(self.bcs_land + 'vegdyn_*.dat')[0] + catchRstFile = glob.glob(self.exphome+'/'+exp_id+'/mk_restarts/*'+self.catch+'_internal_rst.'+YYYYMMDD+'*')[0] + + # catchment restart file + if os.path.isfile(catchRstFile) : + catchLocal = self.rstdir+ensdir +'/'+ y4m2+'/'+self.rqdExeInp['EXP_ID']+'.'+self.catch+'_internal_rst.'+y4m2d2_h2m2 + if self.islocal : + print( "Creating local catchment restart file... \n") + cmd=self.bindir +'/preprocess_ldas.x c_localcatchrst '+ catchRstFile +' ' + catchLocal + ' '+ tmp_f2g_file.name + print ("cmd: "+cmd) + sp.call(shlex.split(cmd)) + else : + shutil.copy(catchRstFile,catchLocal) + + catchRstFile = catchLocal + + if '0000' in ensdir : + catchRstFile0 = catchRstFile + else : # re-use 0000 catch file + catchRstFile = catchRstFile0 + + # vegdyn restart file + if os.path.isfile(vegdynRstFile) : + vegdynLocal = self.rstdir+ensdir +'/'+self.rqdExeInp['EXP_ID']+'.vegdyn_internal_rst' + if self.islocal : + print ("Creating the local veg restart file... \n") + cmd=self.bindir + '/preprocess_ldas.x c_localvegrst '+ vegdynRstFile +' ' + vegdynLocal + ' '+ tmp_f2g_file.name + print ("cmd: " + cmd) + sp.call(shlex.split(cmd)) + else : + shutil.copy(vegdynRstFile,vegdynLocal) + + vegdynRstFile = vegdynLocal + + if '0000' in ensdir : + vegdynRstFile0 = vegdynRstFile + else : + vegdynRstFile = vegdynRstFile0 + + if (self.has_geos_pert and self.perturb == 1) : + pertRstFile = rstpath+ensdir +'/'+ y4m2+'/'+self.rqdExeInp['RESTART_ID']+'.landpert_internal_rst.'+y4m2d2_h2m2 + pertLocal = self.rstdir+ensdir +'/'+ y4m2+'/'+self.rqdExeInp['EXP_ID']+'.landpert_internal_rst.'+y4m2d2_h2m2 + shutil.copy(pertRstFile,pertLocal) + pertRstFile = pertLocal + + print ('catchRstFile: ' + catchRstFile) + print ('vegdynRstFile: ' + vegdynRstFile) + os.symlink(catchRstFile, myCatchRst) + os.symlink(vegdynRstFile, myVegRst) + if ( self.has_geos_pert and self.perturb == 1 ): + os.symlink(pertRstFile, myPertRst) + + # catch_param restar file + catch_param_file = self.bcsdir+'/'+ y4m2+'/'+self.rqdExeInp['EXP_ID']+'.ldas_catparam.'+y4m2d2_h2m2+'z.bin' + assert os.path.isfile(catch_param_file), "need catch_param file %s" % catch_param_file + + if self.has_mwrtm : + mwRTMRstFile = self.mwrtm_file + mwRTMLocal = self.bcsdir+'/'+ y4m2+'/'+self.rqdExeInp['EXP_ID']+'.ldas_mwRTMparam.'+y4m2d2_h2m2+'z.nc4' + if self.islocal : + print ("Creating the local mwRTM restart file... \n") + cmd= self.bindir +'/preprocess_ldas.x c_localmwrtmrst '+ mwRTMRstFile +' ' + mwRTMLocal + ' '+ tmp_f2g_file.name + + print ("cmd: " + cmd) + sp.call(shlex.split(cmd)) + else : + shutil.copy(mwRTMRstFile,mwRTMLocal) + + mwRTMRstFile = mwRTMLocal + mymwRTMRst = myRstDir+'/mwrtm_param_rst' + os.symlink(mwRTMRstFile, mymwRTMRst) + + # update 'restart_path' to use relative path from outdir + print ("Updating restart path...") + self.rqdExeInp['RESTART_PATH'] = myRstDir + #if os.path.isfile(tmp_f2g_file.name): + # os.remove(tmp_f2g_file.name) + status = True + return status + + def createRCFiles(self): + """ + (1) get resource files form DEFAULT rc files from /etc + (2) update from customed rc files + (2) write rc files to the run directory + """ + + status = False + + for mydir in [self.blddirLn, self.rundir]: + assert os.path.isdir(mydir), \ + 'dir [%s] does not exist!' % mydir + + # first copy ldsetup input files to rundir + # if a file w/ the same name already exists at rundir + # append 1,2,3 etc, to the filename + ## exe inp file + exefilename = self.exeinpfile.rstrip('/').split('/')[-1] + newfilename = exefilename + _nens = self.nens + ctr = 0 + while os.path.isfile(self.rundir+'/'+newfilename): + ctr += 1 + newfilename = exefilename + '.%d' % ctr + shutil.copy(self.exeinpfile, self.rundir+'/'+newfilename) + ## bat inp file + batfilename = self.batinpfile.rstrip('/').split('/')[-1] + newfilename = batfilename + ctr = 0 + while os.path.isfile(self.rundir+'/'+newfilename): + ctr += 1 + newfilename = batfilename + '.%d' % ctr + shutil.copy(self.batinpfile, self.rundir+'/'+newfilename) + + etcdir = self.blddirLn + '/etc' + + #defalt nml + default_nml = glob.glob(etcdir+'/LDASsa_DEFAULT_inputs_*.nml') + for nmlfile in default_nml: + shortfile=self.rundir+'/'+nmlfile.split('/')[-1] + shutil.copy2(nmlfile, shortfile) + # special nml + special_nml=[] + if 'NML_INPUT_PATH' in self.rqdExeInp : + special_nml = glob.glob(self.rqdExeInp['NML_INPUT_PATH']+'/LDASsa_SPECIAL_inputs_*.nml') + for nmlfile in special_nml: + shortfile=nmlfile.split('/')[-1] + shutil.copy2(nmlfile, self.rundir+'/'+shortfile) + + # get optimzed NX and IMS + optimized_distribution_file = tempfile.NamedTemporaryFile(delete=False) + print ("Optimizing... decomposition of processes.... \n") + cmd = self.bindir + '/preprocess_ldas.x optimize '+ self.inpdir+'/tile.data '+ str(self.rqdRmInp['ntasks_model']) + ' ' + optimized_distribution_file.name + ' ' + self.rundir + print ("cmd: " + cmd) + print ("IMS.rc or JMS.rc would be generated on " + self.rundir) + sp.call(shlex.split(cmd)) + optinxny=self._parseInputFile(optimized_distribution_file.name) + if (int(optinxny['NX']) == 1): + if int(optinxny['NY']) != int(self.rqdRmInp['ntasks_model']): + self.rqdRmInp['ntasks_model']=optinxny['NY'] + print ('adjust ntasks_model %d for cubed-sphere grid' % int(self.rqdRmInp['ntasks_model'])) + + + #os.remove(optimized_distribution_file.name) + + # DEFAULT rc files + default_rc = glob.glob(etcdir+'/GEOSldas_*.rc') + assert len(default_rc)==4 + print (default_rc) + for rcfile in default_rc: + shortfile=rcfile.rsplit('GEOSldas_',1)[1] + print (shortfile + ' ' + etcdir + ' ' + self.rundir) + if shortfile =='HIST.rc': + tmprcfile=self.rundir+'/HISTORY.rc' + histrc_file=rcfile + + _file_found = False + if 'HISTRC_FILE' in self.rqdExeInp : + _tmpfile = self.rqdExeInp['HISTRC_FILE'].replace("'",'').replace('"','') + if(os.path.isfile(_tmpfile)) : + _file_found = True + else : + assert not _tmpfile.strip(), "HISTRC_FILE: %s is NOT a file. " %_tmpfile + + if _file_found : + histrc_file = self.rqdExeInp['HISTRC_FILE'] + shutil.copy2(histrc_file,tmprcfile) + else : + shutil.copy2(histrc_file,tmprcfile) + GRID='EASE ' + self.rqdExeInp['GRIDNAME'] + ' ' +tmprcfile + if '-CF' in self.rqdExeInp['GRIDNAME'] : + GRID ='CUBE ' + self.rqdExeInp['GRIDNAME'] + ' ' +tmprcfile + _assim = '1' if self.assim else '0' + cmd =self.bindir +'/process_hist.csh '+ str(self.rqdExeInp['LSM_CHOICE']) + ' ' + str(self.rqdExeInp['AEROSOL_DEPOSITION']) + \ + ' ' + GRID + ' ' + str(self.rqdExeInp['RUN_IRRIG']) + ' ' + _assim + ' '+ str(self.nens) + print(cmd) + #os.system(cmd) + sp.call(shlex.split(cmd)) + for line in fileinput.input(tmprcfile,inplace=True): + print (line.rstrip().replace('GEOSldas_expid',self.rqdExeInp['EXP_ID'])) + # just copy an empty ExtData.rc + if shortfile=='ExtData.rc' : + shutil.copy2(rcfile, self.rundir+'/'+shortfile) + + if shortfile == 'CAP.rc': + tmprcfile = self.rundir+'/CAP.rc' + shutil.copy2(rcfile,tmprcfile) + + _num_sgmt = int(self.rqdExeInp['NUM_SGMT']) + + for line in fileinput.input(tmprcfile,inplace=True): + print (line.rstrip().replace('JOB_SGMT:',self.job_sgmt[0])) + for line in fileinput.input(tmprcfile,inplace=True): + print (line.rstrip().replace('NUM_SGMT:','NUM_SGMT: %d'% _num_sgmt)) + for line in fileinput.input(tmprcfile,inplace=True): + print (line.rstrip().replace('BEG_DATE:',self.begDates[0].strftime('BEG_DATE: %Y%m%d %H%M%S'))) + for line in fileinput.input(tmprcfile,inplace=True): + print (line.rstrip().replace('END_DATE:',self.endDates[-1].strftime('END_DATE: %Y%m%d %H%M%S'))) + + if shortfile == 'LDAS.rc' : + ldasrcInp = OrderedDict() + # land default + default_surfrcInp = self._parseInputFile(etcdir+'/GEOS_SurfaceGridComp.rc') + for key,val in default_surfrcInp.items() : + ldasrcInp[key] = val + + # ldas default, may overwrite land default + default_ldasrcInp = self._parseInputFile(rcfile) + for key,val in default_ldasrcInp.items() : + ldasrcInp[key] = val + + # exeinp, may overwrite ldas default + for key,val in self.rqdExeInp.items(): + if key not in self.NoneLDASrcKeys: + ldasrcInp[key]= val + + # overide by optimized distribution + #for key,val in optinxny.items(): + # ldasrcInp[key]= val + + # create BC in rc file + tmpl_ = '' + if self.nens >1 : + tmpl_='%s' + if self.perturb == 1: + ldasrcInp['PERTURBATIONS'] ='1' + bcval=['../input/green','../input/lai','../input/lnfm','../input/ndvi','../input/nirdf','../input/visdf'] + bckey=['GREEN','LAI','LNFM','NDVI','NIRDF','VISDF'] + for key, val in zip(bckey,bcval): + keyn = key+'_FILE' + valn = val+'.data' + ldasrcInp[keyn]= valn + if('catchcn' in self.catch): + ldasrcInp['CO2_MonthlyMean_DiurnalCycle_FILE']= '../input/CO2_MonthlyMean_DiurnalCycle.nc4' + + # create restart item in RC + catch_ = self.catch.upper() + + if catch_+'_INTERNAL_RESTART_TYPE' in ldasrcInp : + # avoid duplicate + del ldasrcInp[ catch_ +'_INTERNAL_RESTART_TYPE'] + if catch_+'_INTERNAL_CHECKPOINT_TYPE' in ldasrcInp : + # avoid duplicate + del ldasrcInp[ catch_ +'_INTERNAL_CHECKPOINT_TYPE'] + if 'VEGDYN_INTERNAL_RESTART_TYPE' in ldasrcInp : + # avoid duplicate + del ldasrcInp['VEGDYN_INTERNAL_RESTART_TYPE'] + + rstkey=[catch_,'VEGDYN'] + rstval=[self.catch,'vegdyn'] + + if self.has_mwrtm : + keyn='LANDASSIM_INTERNAL_RESTART_FILE' + valn='../input/restart/mwrtm_param_rst' + ldasrcInp[keyn]= valn + if self.has_vegopacity : + keyn='VEGOPACITY_FILE' + valn='../input/vegopacity.data' + ldasrcInp[keyn]= valn + + if self.nens > 1 : + keyn='ENS_ID_WIDTH' + valn=str(self.ens_id_width) + ldasrcInp[keyn]= valn + + if self.has_landassim_seed and self.assim : + keyn='LANDASSIM_OBSPERTRSEED_RESTART_FILE' + valn='../input/restart/landassim_obspertrseed'+tmpl_+'_rst' + ldasrcInp[keyn]= valn + + if self.assim: + keyn='LANDASSIM_OBSPERTRSEED_CHECKPOINT_FILE' + valn='landassim_obspertrseed'+tmpl_+'_checkpoint' + ldasrcInp[keyn]= valn + + for key,val in zip(rstkey,rstval) : + keyn = key+ '_INTERNAL_RESTART_FILE' + valn = '../input/restart/'+val+tmpl_+'_internal_rst' + ldasrcInp[keyn]= valn + + # checkpoint file and its type + keyn = catch_ + '_INTERNAL_CHECKPOINT_FILE' + valn = self.catch+tmpl_+'_internal_checkpoint' + ldasrcInp[keyn]= valn + + # specify LANDPERT restart file + if (self.perturb == 1): + keyn = 'LANDPERT_INTERNAL_RESTART_FILE' + valn = '../input/restart/landpert'+tmpl_+'_internal_rst' + ldasrcInp[keyn]= valn + # for lat/lon and EASE tile space, specify LANDPERT checkpoint file here (via MAPL); + # for cube-sphere tile space, Landpert GC will set up LANDPERT checkpoint file + if ('-CF' not in self.rqdExeInp['GRIDNAME']): + keyn = 'LANDPERT_INTERNAL_CHECKPOINT_FILE' + valn = 'landpert'+tmpl_+'_internal_checkpoint' + ldasrcInp[keyn]= valn + + # write LDAS.rc + fout =open(self.rundir+'/'+shortfile,'w') + # ldasrcInp['NUM_LDAS_ENSEMBLE']=ldasrcInp.pop('NUM_ENSEMBLE') + for key,val in optinxny.items(): + keyn=(key+":").ljust(36) + fout.write(keyn+str(val)+'\n') + for key,val in ldasrcInp.items() : + keyn=(key+":").ljust(36) + fout.write(keyn+str(val)+'\n') + fout.write("OUT_PATH:".ljust(36)+self.out_path+'\n') + fout.write("EXP_ID:".ljust(36)+self.rqdExeInp['EXP_ID']+'\n') + fout.write("TILING_FILE:".ljust(36)+"../input/tile.data\n") + + fout.close() + + fout=open(self.rundir+'/'+'cap_restart','w') + #fout.write(self.rqdExeInp['BEG_DATE']) + fout.write(self.begDates[0].strftime('%Y%m%d %H%M%S')) + fout.close() + status=True + return status + + def createBatchRun(self): + """ + """ + + status = False + + os.chdir(self.rundir) + fout =open(self.rundir+'/ldas_batchrun.j','w') + fout.write("#!/bin/bash -f\n") + jobid = None + SBATCHQSUB = 'sbatch' + expid = self.rqdExeInp['EXP_ID'] + if self.GEOS_SITE == 'NAS': + SBATCHQSUB = 'qsub' + fout.write("\nsed -i 's/if($capdate<$enddate) "+SBATCHQSUB+"/#if($capdate<$enddate) "+SBATCHQSUB+"/g' lenkf.j\n\n") + nSegments = self.nSegments + for iseg in range(nSegments): + if iseg ==0 : + fout.write("jobid%d=$(echo $(sbatch lenkf.j) | cut -d' ' -f 4)\n"%(iseg)) + fout.write("echo $jobid%d\n"%iseg ) + else : + _start = self.begDates[iseg] + myDateTime = '%04d%02d%02d_%02d%02dz' % \ + (_start.year, _start.month, _start.day,_start.hour,_start.minute) + _logfile = os.path.relpath( + '/'.join([ + self.outdir, + self.rqdExeInp['EXP_DOMAIN'], + 'rc_out', + 'Y%04d' % _start.year, + 'M%02d' % _start.month, + '.'.join([expid, 'ldas_log', myDateTime, 'txt']), + ]), + self.rundir) + _errfile = os.path.relpath( + '/'.join([ + self.outdir, + self.rqdExeInp['EXP_DOMAIN'], + 'rc_out', + 'Y%04d' % _start.year, + 'M%02d' % _start.month, + '.'.join([expid, 'ldas_err', myDateTime, 'txt']), + ]), + self.rundir) + + #fout.write("jobid%d=$(echo $(sbatch --dependency=afterany:$jobid%d --output=%s --error=%s lenkf.j) | cut -d' ' -f 4)\n"%(iseg,iseg-1,_logfile, _errfile)) + fout.write("jobid%d=$(echo $(sbatch --dependency=afterok:$jobid%d lenkf.j) | cut -d' ' -f 4)\n"%(iseg,iseg-1)) + fout.write("echo $jobid%d\n"%iseg ) + fout.write("\nsed -i 's/#if($capdate<$enddate) "+SBATCHQSUB+"/if($capdate<$enddate) "+SBATCHQSUB+"/g' lenkf.j\n\n") + fout.close() + + sp.call(['chmod', '755', self.rundir+'/ldas_batchrun.j']) + status = True + return status + + + def createRunScripts(self): + """ + """ + + status = False + + os.chdir(self.rundir) + lenkf=self.blddir+'/etc/lenkf.j.template' + shutil.copy(lenkf,'lenkf.j') + + my_qos='allnccs' + if self.GEOS_SITE == 'NAS': my_qos = 'normal' + if 'qos' in self.optRmInp : + my_qos = self.optRmInp['qos'] + + my_job=self.rqdExeInp['EXP_ID'] + if 'job_name' in self.optRmInp : + my_job = self.optRmInp['job_name'] + + start = self.begDates[0] + expid = self.rqdExeInp['EXP_ID'] + myDateTime = '%04d%02d%02d_%02d%02dz' % \ + (start.year, start.month, start.day,start.hour,start.minute) + my_logfile = os.path.relpath( + '/'.join([ + self.outdir, + self.rqdExeInp['EXP_DOMAIN'], + 'rc_out', + 'Y%04d' % start.year, + 'M%02d' % start.month, + '.'.join([expid, 'ldas_log', myDateTime, 'txt']), + ]), + self.rundir) + my_errfile = os.path.relpath( + '/'.join([ + self.outdir, + self.rqdExeInp['EXP_DOMAIN'], + 'rc_out', + 'Y%04d' % start.year, + 'M%02d' % start.month, + '.'.join([expid, 'ldas_err', myDateTime, 'txt']), + ]), + self.rundir) + with open(lenkf,'rt') as fin: + with open('lenkf.j','wt') as fout : + for line in fin : + if self.GEOS_SITE == 'NAS': + if '#SBATCH' in line: + continue + if 'sbatch $HOMDIR/lenkf.j' in line: + continue + + if self.GEOS_SITE == 'NCCS': + if '#PBS' in line: + continue + if 'qsub $HOMDIR/lenkf.j' in line: + continue + + if 'MY_ACCOUNT' in line : + fout.write(line.replace('MY_ACCOUNT',self.rqdRmInp['account'])) + elif 'MY_WALLTIME' in line : + fout.write(line.replace('MY_WALLTIME',self.rqdRmInp['walltime'])) + elif 'MY_NODES' in line : + line_ = line.replace('MY_NODES',str(self.optRmInp['nodes'])) + line_ = line_.replace('MY_NTASKS_PER_NODE',str(self.rqdRmInp['ntasks-per-node'])) + line_ = line_.replace('MY_CONSTRAINT', 'cas_ait') + fout.write(line_) + if self.GEOS_SITE == "NCCS" : + if self.BUILT_ON_SLES15 : + fout.write("#SBATCH --constraint=mil\n") + else: + assert int(self.rqdRmInp['ntasks-per-node']) <= 46, 'ntasks-per-node should be <=46 for cas' + fout.write("#SBATCH --constraint=cas\n") + + elif 'MY_OSERVER_NODES' in line : + fout.write(line.replace('MY_OSERVER_NODES',str(self.optRmInp['oserver_nodes']))) + elif 'MY_WRITERS_NPES' in line : + fout.write(line.replace('MY_WRITERS_NPES', str(self.optRmInp['writers-per-node']))) + elif 'MY_QOS' in line : + if 'allnccs' not in my_qos or 'normal' not in my_qos: + fout.write(line.replace('MY_QOS',my_qos)) + elif 'MY_JOB' in line : + fout.write(line.replace('MY_JOB',my_job)) + elif 'MY_EXPID' in line : + fout.write(line.replace('MY_EXPID',self.rqdExeInp['EXP_ID'])) + elif 'MY_EXPDOMAIN' in line : + fout.write(line.replace('MY_EXPDOMAIN',self.rqdExeInp['EXP_DOMAIN'])) + elif 'MY_LOGFILE' in line : + fout.write(line.replace('MY_LOGFILE',my_logfile)) + elif 'MY_ERRFILE' in line : + fout.write(line.replace('MY_ERRFILE',my_errfile)) + elif 'MY_MODEL' in line : + fout.write(line.replace('MY_MODEL',self.catch)) + elif 'MY_POSTPROC_HIST' in line : + fout.write(line.replace('MY_POSTPROC_HIST',str(self.rqdExeInp['POSTPROC_HIST']))) + elif 'MY_FIRST_ENS_ID' in line : + fout.write(line.replace('MY_FIRST_ENS_ID',str(self.first_ens_id))) + elif 'MY_LADAS_COUPLING' in line : + fout.write(line.replace('MY_LADAS_COUPLING',str(self.ladas_coupling))) + elif 'MY_ENSEMBLE_FORCING' in line : + fout.write(line.replace('MY_ENSEMBLE_FORCING',self.rqdExeInp.get('ENSEMBLE_FORCING', 'NO').upper())) + elif 'MY_ADAS_EXPDIR' in line : + if self.ladas_coupling > 0: + fout.write(line.replace('MY_ADAS_EXPDIR', self.rqdExeInp['ADAS_EXPDIR'])) + else : + my_expdir = self.exphome + '/' + self.rqdExeInp['EXP_ID'] + fout.write(line.replace('MY_EXPDIR',my_expdir)) + + sp.call(['chmod', '755', 'lenkf.j']) + + expdir = '/'.join(self.rundir.rstrip('/').split('/')[:-1]) + print ('\nExperiment directory: %s' % expdir) + print () + status = True + return status + +def _printdict(d): + """ + Private method: print a 'flat' dictionary + """ + + for key, val in d.items(): + print (key.ljust(23), ':', val) + +def _printExeInputKeys(rqdExeInpKeys): + """ + Private method: print sample exe input + """ + + print ('####################################################################################') + print ('# #') + print ('# REQUIRED INPUTS #') + print ('# #') + print ('# These inputs are needed to set up output dir structure. #') + print ('# #') + print ('####################################################################################') + print () + print ('############################################################') + print ('# #') + print ('# EXPERIMENT INFO #') + print ('# #') + print ('# Format for start/end times is yyyymmdd hhmmss. #') + print ('# #') + print ('############################################################') + print () + print ('EXP_ID:') + print ('EXP_DOMAIN:') + print ('NUM_LDAS_ENSEMBLE:') + print ('BEG_DATE:') + print ('END_DATE:') + print () + print ('############################################################') + print ('# #') + print ('# RESTART INFO #') + print ('# #') + print ('# (i) Select "RESTART" option: #') + print ('# #') + print ('# Use one of the following options if you *have* a #') + print ('# GEOSldas restart file: #') + print ('# #') + print ('# RESTART: 1 #') + print ('# YES, have restart file from GEOSldas #') + print ('# in SAME tile space (grid) with SAME boundary #') + print ('# conditions and SAME snow model parameter (WEMIN). #') + print ('# The restart domain can be for the same or #') + print ('# a larger one. #') + print ('# #') + print ('# RESTART: 2 #') + print ('# YES, have restart file from GEOSldas but #') + print ('# in a DIFFERENT tile space (grid) or with #') + print ('# DIFFERENT boundary conditions or DIFFERENT snow #') + print ('# model parameter (WEMIN). #') + print ('# Restart *must* be for the GLOBAL domain. #') + print ('# #') + print ('# Use one of the following options if you DO NOT have a #') + print ('# GEOSldas restart file #') + print ('# (works for global domain ONLY!): #') + print ('# #') + print ('# RESTART: 0 #') + print ('# Cold start from some old restart for Jan 1, 0z. #') + print ('# #') + print ('# RESTART: M #') + print ('# Re-tile from archived MERRA-2 restart file. #') + print ('# #') + print ('# RESTART: F #') + print ('# Re-tile from FP (Forward Processing) restart file. #') + print ('# #') + print ('# RESTART: G #') + print ('# Re-tile from any AGCM catch[cnclmxx]_internal_rst file.#') + print ('# #') + print ('# -------------------------------------------------------- #') + print ('# IMPORTANT: #') + print ('# Except for RESTART=1, SPIN-UP is REQUIRED in almost #') + print ('# all cases. #') + print ('# -------------------------------------------------------- #') + print ('# #') + print ('# #') + print ('# (ii) Specify experiment ID/location of restart file: #') + print ('# #') + print ('# For RESTART=1 or RESTART=2: #') + print ('# Specify RESTART_ID, RESTART_PATH, RESTART_DOMAIN with #') + print ('# restarts stored as follows: #') + print ('# RESTART_PATH/RESTART_ID/output/RESTART_DOMAIN/rs/ #') + print ('# #') + print ('# For RESTART=0 or RESTART=M or RESTART=F: #') + print ('# There is no need to specify RESTART_ID, RESTART_PATH, #') + print ('# and RESTART_DOMAIN. #') + print ('# #') + print ('# For RESTART=G: #') + print ('# RESTART_ID : full_path_to_AGCM_experiment_directory #') + print ('# RESTART_PATH : full_path_of_the_AGCM_restart_file #') + print ('# RESTART_DOMAIN is NOT required. #') + print ('# #') + print ('############################################################') + print () + print ('RESTART:') + print ('#RESTART_ID:') + print ('#RESTART_PATH:') + print ('#RESTART_DOMAIN:') + print () + print ('############################################################') + print ('# #') + print ('# SURFACE METEOROLOGICAL FORCING #') + print ('# #') + print ('# Surface meteorological forcing time step is in seconds. #') + print ('# #') + print ('# For more information, see: #') + print ('# GEOSldas/doc/README.MetForcing_and_BCS.md #') + print ('# #') + print ('############################################################') + print () + print ('MET_TAG:') + print ('MET_PATH:') + print ('FORCE_DTSTEP:') + print () + print ('############################################################') + print ('# #') + print ('# LAND BOUNDARY CONDITIONS (BCS) #') + print ('# #') + print ('# Path to and (atmospheric) resolution of BCS. #') + print ('# #') + print ('# For more information, see: #') + print ('# GEOSldas/doc/README.MetForcing_and_BCS.md #') + print ('# [..]/GEOSsurface_GridComp/Utils/Raster/make_bcs #') + print ('# #') + print ('############################################################') + print () + print ('BCS_PATH:') + print ('BCS_RESOLUTION:') + print () + print ('############################################################') + print ('# #') + print ('# LADAS COUPLING #') + print ('# #') + print ('# Coupling of LDAS to ADAS ("LADAS"): #') + print ('# #') + print ('# 0 -- LDAS not coupled with ADAS (default) #') + print ('# 1 -- LDAS coupled with central member of ADAS #') + print ('# 2 -- LDAS coupled with ens component of ADAS #') + print ('# #') + print ('# Requirements for LADAS_COUPLING > 0: #') + print ('# #') + print ('# (0) Specify ADAS_EXPDIR = [full_path]/[ADAS_EXPID] #') + print ('# #') + print ('# (1) BEG_DATE must be consistent with first cycle date #') + print ('# and time of ADAS experiment (time is typically #') + print ('# 3z, 9z, 15z, or 21z) #') + print ('# #') + print ('# (2) EXP_DOMAIN must be global CS grid as in ADAS exp #') + print ('# #') + print ('# (3) MET_TAG must be set to [ADAS_EXPID]__Nx+- #') + print ('# MET_PATH must be set as follows for #') + print ('# LADAS_COUPLING = 1: #') + print ('# [full_path]/[LDAS_EXPID]/scratch/ #') + print ('# LADAS_COUPLING = 2: #') + print ('# [ADAS_EXPDIR]/atmens/ensdiag/forc #') + print ('# After ldas exp setup, verify the following link: #') + print ('# ../input/met_forcing/forc -> [MET_PATH] #') + print ('# #') + print ('# (4) BCS_PATH must be consistent with that of #') + print ('# [ADAS_EXPDIR][/run/lnbcs #') + print ('# #') + print ('# (5) JOB_SGMT must match ADAS analysis window #') + print ('# (typically 6h) #') + print ('# #') + print ('# (6) NUM_SGMT must be set to 1 #') + print ('# #') + print ('# (7) HISTORY: #') + print ('# - instantaneous "catch_progn_incr" must be in #') + print ('# HISTORY collection #') + print ('# - time step must match that of LDAS analysis #') + print ('# - for LADAS_COUPLING=2, HISTORY must include #') + print ('# "catch_progn_incr[ENS_INDEX]" #') + print ('# #') + print ('############################################################') + print () + print ('LADAS_COUPLING: 0') + print () + print () + + _fn = '../etc/GEOSldas_LDAS.rc' # run ldas_setup from /bin directory + + with open(_fn) as _f: + i_ = 1 + for line in _f: + if ( i_ < 5 or i_ >10): # ignore lines 5-10 - may need to change if GEOSldas_LDAS.rc is edited + sys.stdout.write(line) + sys.stdout.flush() + i_ += 1 + print () + print () + + _fn = '../etc/GEOS_SurfaceGridComp.rc' # run ldas_setup from /bin directory + + with open(_fn) as _f : + i_ = 1 + for line in _f: + if ( 5<=i_ and i_<=21) : # ignore lines 5-21 - may need to change if GEOS_SurfaceGridComp.rc is edited + i_ +=1 + continue + if '"GEOSldas=>"' in line: + sys.stdout.write(line) + elif 'GEOSldas=>' in line: + line0 = line.split("GEOSldas=>")[1] + sys.stdout.write(line0) + elif not line.strip() or line.strip().startswith('#'): + sys.stdout.write(line) + sys.stdout.flush() + i_ += 1 + print () + print () + +def _printRmInputKeys(rqdRmInpKeys, optRmInpKeys): + """ + Private method: print sample resource manager input + """ + + print ('#') + print ('# REQUIRED inputs') + print ('#') + print ('# NOTE:') + print ('# - account = computational project number') + print ('# [At NCCS: Use command "getsponsor" to see available account number(s).]' ) + print ('# - walltime = walltime requested; format is HH:MM:SS (hours/minutes/seconds)') + print ('# - ntasks_model = number of processors requested for the model (typically 112; output server is not included)') + print ('# - ntasks-per-node = number of tasks per node (typically 46 for cascade* and 40 for skylake nodes)') + print ('# [If >40, cascade nodes will be allocated, else cascade or skylake.]') + print ('# [*NCCS recommends <=46 cores per node on SCU16 (cascade) due to OS issues (as of 6 Oct 2021).]') + print ('#') + for key in rqdRmInpKeys: + print (key + ':') + print () + print ('#') + print ('# OPTIONAL inputs') + print ('#') + print ('# NOTE:') + print ('# - job_name = name of experiment; default is "exp_id"') + print ('# - qos = quality-of-service; do not specify by default; specify "debug" for faster but limited service.') + print ('# - oserver_nodes = number of nodes for oserver ( default is 0 )') + print ('# - writers-per-node = tasks per oserver_node for writing ( default is 5 ),') + print ('# IMPORTANT REQUIREMENT: total #writers = writers-per-node * oserver_nodes >= 2') + print ('# Jobs will hang when oserver_nodes = writers-per-node = 1.') + print ('#') + for key in optRmInpKeys: + print ('#'+key + ':') + +def parseCmdLine(): + """ + parse command line arguments and return a dict of options + """ + #print 'in: parseCmdLine' + p = argparse.ArgumentParser( + description= \ + "Script to setup a GEOSldas experiment. The script requires "\ + "two (2) input files, one for the Fortran executable and the " \ + "other for the resource manager (SLURM). For sample input " \ + "files use './ldas_setup sample -h'.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + p_sub = p.add_subparsers(help='sub-command help') + + # subparser: sample command + p_sample = p_sub.add_parser( + 'sample', + help='write sample input files', + description='Print sample input files - either for the '\ + 'Fortran executable or the resource manager (SLURM)', + ) + group = p_sample.add_mutually_exclusive_group(required=True) + group.add_argument( + '--exeinp', + help='print sample input file used to generate RC files for GEOSldas App.', + action='store_true', + ) + group.add_argument( + '--batinp', + help='print sample input file for SLURM ', + action='store_true', + ) + # subparser: setup command + p_setup = p_sub.add_parser( + 'setup', + help='setup LDAS experiment', + description="The 'setup' sub-command is used to setup a GEOSldas " \ + "experiment. The positional argument 'exphome' is used to create " \ + "work_path (exphome+/output) and run_path (exphome+/run)." + ) + p_setup.add_argument( + '-v', + '--verbose', + help='verbose output', + action='store_true', + ) + p_setup.add_argument('exphome', help='experiment location') + p_setup.add_argument( + 'exeinpfile', + help='input file with arguments used to generate RC files for GEOSldas App', + ) + p_setup.add_argument( + 'batinpfile', + help='input file with arguments for SLURM', + ) + p_setup.add_argument( + '--account', + help='replace computing/sponsor account in batinp file', + type=str, default='None' + ) + p_setup.add_argument( + '--runmodel', + help='Obsolete.', + action='store_true', + ) + spltgrp = p_setup.add_mutually_exclusive_group() + spltgrp.add_argument( + '--daysperjob', + type=int, + metavar='N', + help='Obsolete. Use NUM_SGMT and JOB_SGMT in exeinp file.', + ) + spltgrp.add_argument( + '--monthsperjob', + type=int, + metavar='N', + help='Obsolete. Use NUM_SGMT and JOB_SGMT in exeinp file.', + ) + + return p.parse_args() + + +if __name__=='__main__': + + resource.setrlimit(resource.RLIMIT_STACK, (resource.RLIM_INFINITY, resource.RLIM_INFINITY)) + #print "reading params...." + args = vars(parseCmdLine()) # vars converts to dict + ld = LDASsetup(args) + + print ("creating dir structure") + status = ld.createDirStructure() + assert(status) + + print ("creating restart and bc") + status = ld.createLnRstBc() + assert(status) + + print ("creating RC Files") + status =ld.createRCFiles() + assert status + + print ("creating gcm style batch Run scripts lenkf.j") + status = ld.createRunScripts() + + print ("creating batch Run scripts") + status = ld.createBatchRun() + assert (status) diff --git a/GEOSldas_App/lenkf.j.template b/GEOSldas_App/lenkf.j.template new file mode 100644 index 0000000..7324b76 --- /dev/null +++ b/GEOSldas_App/lenkf.j.template @@ -0,0 +1,849 @@ +#!/bin/csh -f + +# GEOSldas job script ("lenkf" = Land Ensemble Kalman Filter) +# +# usage: lenkf.j [-debug] + +####################################################################### +# Batch Parameters for Run Job +####################################################################### + +#SBATCH --output=MY_EXPDIR/scratch/GEOSldas_log_txt +#SBATCH --error=MY_EXPDIR/scratch/GEOSldas_err_txt +#SBATCH --account=MY_ACCOUNT +#SBATCH --time=MY_WALLTIME +#SBATCH --nodes=MY_NODES --ntasks-per-node=MY_NTASKS_PER_NODE +#SBATCH --job-name=MY_JOB +#SBATCH --qos=MY_QOS + +#PBS -l walltime=MY_WALLTIME +#PBS -l select=MY_NODES:ncpus=40:mpiprocs=40:model=MY_CONSTRAINT +#PBS -N MY_JOB +#PBS -q MY_QOS +#PBS -W group_list=MY_ACCOUNT +#PBS -o MY_EXPDIR/scratch/GEOSldas_log_txt +#PBS -e MY_EXPDIR/scratch/GEOSldas_err_txt +#PBS -j oe + +####################################################################### +# System Settings and Architecture Specific Environment Variables +####################################################################### +umask 022 +limit stacksize unlimited +setenv ARCH `uname` + +setenv EXPID MY_EXPID +setenv EXPDOMAIN MY_EXPDOMAIN +setenv EXPDIR MY_EXPDIR +setenv ESMADIR $EXPDIR/build/ +setenv GEOSBIN $ESMADIR/bin/ +# need to unsetenv LD_LIBRARY_PATH for execution of LDAS within the coupled land-atm DAS +unsetenv LD_LIBRARY_PATH + +set debug_flag = 0 +if ( "$1" == "-debug" ) then + set debug_flag = 1 +endif +unset argv +setenv argv + +source $GEOSBIN/g5_modules + +# OPENMPI flags +# Turn off warning about TMPDIR on NFS +setenv OMPI_MCA_shmem_mmap_enable_nfs_warning 0 +# pre-connect MPI procs on mpi_init +setenv OMPI_MCA_mpi_preconnect_all 1 +setenv OMPI_MCA_coll_tuned_bcast_algorithm 7 +setenv OMPI_MCA_coll_tuned_scatter_algorithm 2 +setenv OMPI_MCA_coll_tuned_reduce_scatter_algorithm 3 +setenv OMPI_MCA_coll_tuned_allreduce_algorithm 3 +setenv OMPI_MCA_coll_tuned_allgather_algorithm 4 +setenv OMPI_MCA_coll_tuned_allgatherv_algorithm 3 +setenv OMPI_MCA_coll_tuned_gather_algorithm 1 +setenv OMPI_MCA_coll_tuned_barrier_algorithm 0 +# required for a tuned flag to be effective +setenv OMPI_MCA_coll_tuned_use_dynamic_rules 1 +# disable file locks +setenv OMPI_MCA_sharedfp "^lockedfile,individual" + +# By default, ensure 0-diff across processor architecture by limiting MKL's freedom to pick algorithms. +# As of June 2021, MKL_CBWR=AVX2 is fastest setting that works for both haswell and skylake at NCCS. +# Change to MKL_CBWR=AUTO for fastest execution at the expense of results becoming processor-dependent. +#setenv MKL_CBWR "COMPATIBLE" +#setenv MKL_CBWR "AUTO" +setenv MKL_CBWR "AVX2" + +#setenv LD_LIBRARY_PATH ${LD_LIBRARY_PATH}:${BASEDIR}/${ARCH}/lib +# reversed sequence for LADAS_COUPLING (Sep 2020) (needed when coupling with ADAS using different BASEDIR) +setenv LD_LIBRARY_PATH ${BASEDIR}/${ARCH}/lib:${ESMADIR}/lib:${LD_LIBRARY_PATH} + +module load nco + +setenv RUN_CMD "$GEOSBIN/esma_mpirun -np " + +####################################################################### +# Experiment Specific Environment Variables +####################################################################### + +setenv HOMDIR $EXPDIR/run/ +setenv SCRDIR $EXPDIR/scratch +setenv MODEL MY_MODEL +setenv MYNAME `finger $USER | cut -d: -f3 | head -1` +setenv POSTPROC_HIST MY_POSTPROC_HIST + +# LADAS_COUPLING : 0 -- stand-alone LDAS (no coupling to ADAS) +# : 1 -- LDAS coupled to central (deterministic) component of ADAS +# : 2 -- LDAS coupled to atmospheric ensemble component of ADAS + +setenv LADAS_COUPLING MY_LADAS_COUPLING +setenv ENSEMBLE_FORCING MY_ENSEMBLE_FORCING +setenv ADAS_EXPDIR MY_ADAS_EXPDIR + +set NENS = `grep NUM_LDAS_ENSEMBLE: $HOMDIR/LDAS.rc | cut -d':' -f2` +set END_DATE = `grep END_DATE: $HOMDIR/CAP.rc | cut -d':' -f2` +set NUM_SGMT = `grep NUM_SGMT: $HOMDIR/CAP.rc | cut -d':' -f2` + +####################################################################### +# if LADAS_COUPLING==2, compute ens avg of atmens forcing +####################################################################### + +if ( $LADAS_COUPLING == 2 && $ENSEMBLE_FORCING == "NO" ) then + cd $HOMDIR + set force_in = $ADAS_EXPDIR + set force_out = `grep MET_PATH: $HOMDIR/LDAS.rc | cut -d ':' -f2` + python $GEOSBIN/average_ensemble_forcing.py $force_in $force_out $NENS +endif + +/bin/rm -f $HOMDIR/lenkf_job_completed.txt + +####################################################################### +# Set Experiment Run Parameters +####################################################################### + +####################################################################### +# Move to Scratch Directory and Copy .rc .nml .rst files +####################################################################### + +if (! -e $SCRDIR ) mkdir -p $SCRDIR +cd $SCRDIR +/bin/rm -rf *.* +/bin/cp $HOMDIR/cap_restart . +/bin/cp -f $HOMDIR/*.rc . +/bin/cp -f $HOMDIR/*.nml . + +set LSMCHOICE = `grep -n -m 1 "LSM_CHOICE" $HOMDIR/LDAS.rc | cut -d':' -f3` + +####################################################################### +# if $LADAS_COUPLING == 1: LDAS coupled to central ADAS simulation +####################################################################### + +if ( $LADAS_COUPLING == 1 ) then + + if ( $ENSEMBLE_FORCING == "YES" ) then + + # create perturbed forcing from central simulation and atm ensemble + + # python should come with ESMA_env g5_modules + #module load python/GEOSpyD/Ana2019.03_py3.7 + set forcgrid = `grep GEOSldas.GRIDNAME LDAS.rc | cut -d':' -f2 | awk '{print $1}'` + setenv GRID $forcgrid + $GEOSBIN/enpert_forc.csh + cd $SCRDIR + else + + # move central-simulation forcing held in met_forcing to scratch dir + echo "move lfo_Nx+- met forcing from $EXPDIR/input/met_forcing to $SCRDIR" + /bin/mv $EXPDIR/input/met_forcing/*lfo_Nx+-*nc4 $SCRDIR/. + + endif +endif + +####################################################################### +# Create HISTORY Collection Directories +####################################################################### + +set collections = '' +foreach line ("`cat HISTORY.rc`") + set firstword = `echo $line | awk '{print $1}'` + set firstchar = `echo $firstword | cut -c1` + set secondword = `echo $line | awk '{print $2}'` + + if ( $firstword == "::" ) goto done + + if ( $firstchar != "#" ) then + set collection = `echo $firstword | sed -e "s/'//g"` + set collections = `echo $collections $collection` + if ( $secondword == :: ) goto done + endif + + if ( $firstword == COLLECTIONS: ) then + set collections = `echo $secondword | sed -e "s/'//g"` + endif +end + +done: + +@ n_c = 0 +if ($POSTPROC_HIST > 0) then + foreach ThisCol ($collections) + set ref_t = `cat HISTORY.rc | grep ${ThisCol}.ref_time: | cut -d':' -f2 | cut -d',' -f1` + if ( $ref_t != '000000' ) then + echo ${ThisCol}.ref_time should be '000000' + @ n_c = $n_c + 1 + endif + end +endif +if ($n_c >= 1) then + exit +endif + +####################################################################### +# Domain Decomposition +####################################################################### +set npes_nx = `grep NX: LDAS.rc | cut -d':' -f2 ` +set npes_ny = `grep NY: LDAS.rc | cut -d':' -f2 ` +@ numprocs = $npes_nx * $npes_ny +if( -e IMS.rc ) then + set oldtasks = `head -n 1 IMS.rc` + if($numprocs != $oldtasks) then + $GEOSBIN/preprocess_ldas.x optimize ../input/tile.data $numprocs nothing nothing nothing + endif +endif + +if( -e JMS.rc ) then + set oldtasks = `head -n 1 JMS.rc | cut -c1-5` + if($numprocs != $oldtasks) then + $GEOSBIN/preprocess_ldas.x optimize ../input/tile.data $numprocs nothing nothing nothing + endif +endif + +set gridname = `grep GEOSldas.GRIDNAME LDAS.rc | cut -d':' -f2 | cut -d'-' -f2 | awk '{print $1}'` +if ( "$gridname" == "CF" ) then + set new_ny = `echo "NY: "$numprocs` + sed -i "/NY:/c\\$new_ny" LDAS.rc +else + set new_nx = `echo "NX: "$numprocs` + sed -i "/NX:/c\\$new_nx" LDAS.rc +endif + +####################################################################### +# Create Strip Utility to Remove Multiple Blank Spaces +####################################################################### + +set FILE = strip +/bin/rm $FILE +cat << EOF > $FILE +#!/bin/ksh +/bin/mv \$1 \$1.tmp +touch \$1 +while read line +do +echo \$line >> \$1 +done < \$1.tmp +exit +EOF +chmod +x $FILE + +################################################################## +###### +###### Perform multiple iterations of Model Run +###### +################################################################## + +@ counter = 1 +while ( $counter <= ${NUM_SGMT} ) + + /bin/rm -f EGRESS.ldas + /bin/cp -f $HOMDIR/CAP.rc . + ./strip CAP.rc + + # Set Time Variables for Current_(c), Ending_(e), and Segment_(s) dates + # --------------------------------------------------------------------- + set nymdc = `cat cap_restart | cut -c1-8` + set nhmsc = `cat cap_restart | cut -c10-15` + set nymde = `cat CAP.rc | grep END_DATE: | cut -d: -f2 | cut -c2-9` + set nhmse = `cat CAP.rc | grep END_DATE: | cut -d: -f2 | cut -c11-16` + set nymds = `cat CAP.rc | grep JOB_SGMT: | cut -d: -f2 | cut -c2-9` + set nhmss = `cat CAP.rc | grep JOB_SGMT: | cut -d: -f2 | cut -c11-16` + + # Compute Time Variables at the Finish_(f) of current segment + # ----------------------------------------------------------- + set nyear = `echo $nymds | cut -c1-4` + set nmonth = `echo $nymds | cut -c5-6` + set nday = `echo $nymds | cut -c7-8` + set nhour = `echo $nhmss | cut -c1-2` + set nminute = `echo $nhmss | cut -c3-4` + set nsec = `echo $nhmss | cut -c5-6` + @ dt = $nsec + 60 * $nminute + 3600 * $nhour + 86400 * $nday + + set nymdf = $nymdc + set nhmsf = $nhmsc + set date = `$GEOSBIN/tick $nymdf $nhmsf $dt` + set nymdf = $date[1] + set nhmsf = $date[2] + set year = `echo $nymdf | cut -c1-4` + set month = `echo $nymdf | cut -c5-6` + set day = `echo $nymdf | cut -c7-8` + + @ month = $month + $nmonth + while( $month > 12 ) + @ month = $month - 12 + @ year = $year + 1 + end + @ year = $year + $nyear + @ nymdf = $year * 10000 + $month * 100 + $day + + if( $nymdf > $nymde ) set nymdf = $nymde + if( $nymdf == $nymde ) then + if( $nhmsf > $nhmse ) set nhmsf = $nhmse + endif + + set yearc = `echo $nymdc | cut -c1-4` + set yearf = `echo $nymdf | cut -c1-4` + + # Prescribed LAI/SAI for CATCHCN + # ------------------------------- + + set PRESCRIBE_DVG = `grep PRESCRIBE_DVG LDAS.rc | cut -d':' -f2` + if( ${PRESCRIBE_DVG} == 3 ) then + set FCSTDATE = `grep FCAST_BEGTIME $HOMDIR/LDAS.rc | cut -d':' -f2` + if( `echo $FCSTDATE | cut -d' ' -f1` == "" ) then + set CAPRES = `cat cap_restart` + set CAPRES1 = `echo $CAPRES | cut -d' ' -f1` + set CAPRES2 = `echo $CAPRES | cut -d' ' -f2` + set CAPRES = 'FCAST_BEGTIME: '`echo $CAPRES1``echo $CAPRES2` + echo $CAPRES >> $HOMDIR/LDAS.rc + /bin/cp -p $HOMDIR/LDAS.rc . + endif + endif + + if( ${PRESCRIBE_DVG} >= 1 ) then + + # Modify local CAP.rc Ending date if Finish time exceeds Current year boundary + # ---------------------------------------------------------------------------- + + if( $yearf > $yearc ) then + @ yearf = $yearc + 1 + @ nymdf = $yearf * 10000 + 0101 + set oldstring = `cat CAP.rc | grep END_DATE:` + set newstring = "END_DATE: $nymdf $nhmsf" + /bin/mv CAP.rc CAP.tmp + cat CAP.tmp | sed -e "s?$oldstring?$newstring?g" > CAP.rc + endif + + # Creaate VEGDATA FIle Links + # -------------------------- + + if( ${PRESCRIBE_DVG} == 1 ) set VEGYR = $yearc + if( ${PRESCRIBE_DVG} >= 2 ) set VEGYR = CLIM + + set FILE = vegfile + set nz = 1 + /bin/rm CNLAI* + /bin/rm CNSAI* + + while ( $nz <= 3 ) + set nv = 1 + while ($nv <= 4 ) + /bin/ln -s ../VEGDATA/CNLAI${nv}${nz}_${VEGYR}.data CNLAI${nv}${nz}.data + /bin/ln -s ../VEGDATA/CNSAI${nv}${nz}_${VEGYR}.data CNSAI${nv}${nz}.data + echo "CNLAI${nv}${nz}_FILE: CNLAI${nv}${nz}.data" >> $FILE + echo "CNSAI${nv}${nz}_FILE: CNSAI${nv}${nz}.data" >> $FILE + @ nv++ + end + @ nz++ + end + /bin/mv LDAS.rc LDAS.rc.tmp + cat LDAS.rc.tmp $FILE >> LDAS.rc + /bin/rm LDAS.rc.tmp $FILE + endif + + # ---------------------------------------------------------------------------- + + set bYEAR = `cat cap_restart | cut -c1-4` + set bMON = `cat cap_restart | cut -c5-6` + set bDAY = `cat cap_restart | cut -c7-8` + set bHour = `cat cap_restart | cut -c10-11` + set bMin = `cat cap_restart | cut -c12-13` + + if($counter == 1) then + set logYEAR = $bYEAR + set logMON = $bMON + set logDAY = $bDAY + set logHour = $bHour + set logMin = $bMin + endif + + set old_mwrtm_file = $EXPDIR/output/$EXPDOMAIN/rc_out/Y${bYEAR}/M${bMON}/${EXPID}.ldas_mwRTMparam.${bYEAR}${bMON}${bDAY}_${bHour}${bMin}z.nc4 + set old_catch_param = $EXPDIR/output/$EXPDOMAIN/rc_out/Y${bYEAR}/M${bMON}/${EXPID}.ldas_catparam.${bYEAR}${bMON}${bDAY}_${bHour}${bMin}z.bin + if ( -l "$old_mwrtm_file" ) then + set old_mwrtm_file = `/usr/bin/readlink -f $old_mwrtm_file` + endif + if ( -l "$old_catch_param" ) then + set old_catch_param = `/usr/bin/readlink -f $old_catch_param` + endif + + + /bin/cp LDAS.rc $EXPDIR/output/$EXPDOMAIN/rc_out/Y${bYEAR}/M${bMON}/${EXPID}.ldas_LDAS_rc.${bYEAR}${bMON}${bDAY}_${bHour}${bMin}z.txt + /bin/cp CAP.rc $EXPDIR/output/$EXPDOMAIN/rc_out/Y${bYEAR}/M${bMON}/${EXPID}.ldas_CAP_rc.${bYEAR}${bMON}${bDAY}_${bHour}${bMin}z.txt + + # Run GEOSldas.x + # -------------- + # clean up + $GEOSBIN/RmShmKeys_sshmpi.csh + + # Debugging + # --------- + if ( $debug_flag == 1 ) then + echo "" + echo "------------------------------------------------------------------" + echo "" + echo "lenkf.j -debug:" + echo "" + echo "To start debugging, you must now go to the experiment's scratch directory." + echo "From there, source g5_modules and launch your debugging tool with GEOSldas.x, e.g.," + echo "" + echo " cd $SCRDIR + echo " source $GEOSBIN/g5_modules [for bash or zsh: source g5_modules.[z]sh]" + echo " module load tview [at NCCS] + echo " totalview $GEOSBIN/GEOSldas.x" + echo "" + echo "Availability of tools depends on the computing system and may require" + echo "loading modules. For more information, check with your computing center." + echo "See also GEOSldas Wiki at https://github.com/GEOS-ESM/GEOSldas/wiki" + echo "" + exit + endif + + @ oserver_nodes = MY_OSERVER_NODES + @ writers = MY_WRITERS_NPES + + if (! $?SLURM_NTASKS) then + set total_npes = `wc -l $PBS_NODEFILE | awk '{print $1}'` + else + set total_npes = $SLURM_NTASKS + endif + + if ($oserver_nodes == 0) then + set oserver_options = "" + else + set oserver_options = "--oserver_type multigroup --nodes_output_server $oserver_nodes --npes_backend_pernode $writers" + endif + + $RUN_CMD $total_npes $GEOSBIN/GEOSldas.x --npes_model $numprocs $oserver_options + + if( -e EGRESS.ldas ) then + set rc = 0 + echo GEOSldas Run Status: $rc + else + set rc = -1 + echo GEOSldas Run Status: $rc + echo "ERROR: GEOSldas run FAILED, exit without post-processing" + exit + endif + + + ####################################################################### + # Move Legacy LDASsa Files to ana/ens_avg Directory + ####################################################################### + + # must be done before moving HISTORY files + + set ObsFcses = `ls *.ldas_ObsFcstAna.*.bin` + foreach obsfcs ( $ObsFcses ) + set ThisTime = `echo $obsfcs | rev | cut -d'.' -f2 | rev` + set TY = `echo $ThisTime | cut -c1-4` + set TM = `echo $ThisTime | cut -c5-6` + set THISDIR = $EXPDIR/output/$EXPDOMAIN/ana/ens_avg/Y${TY}/M${TM}/ + if (! -e $THISDIR ) mkdir -p $THISDIR + /bin/mv $obsfcs ${THISDIR}$obsfcs + end + + set smapL4s = `ls *.ldas_tile_inst_smapL4SMaup.*.bin` + foreach smapl4 ( $smapL4s ) + set ThisTime = `echo $smapl4 | rev | cut -d'.' -f2 | rev` + set TY = `echo $ThisTime | cut -c1-4` + set TM = `echo $ThisTime | cut -c5-6` + set THISDIR = $EXPDIR/output/$EXPDOMAIN/ana/ens_avg/Y${TY}/M${TM}/ + if (! -e $THISDIR ) mkdir -p $THISDIR + /bin/mv $smapl4 ${THISDIR}$smapl4 + end + + + ####################################################################### + # Move HISTORY Files to cat/ens Directory + ####################################################################### + + set outfiles = `ls $EXPID.*[bin,nc4]` + set TILECOORD=`ls ../output/*/rc_out/*ldas_tilecoord.bin` + + # Move current files to /cat/ens + # ------------------------------ + + foreach ofile ( $outfiles ) + set ThisTime = `echo $ofile | rev | cut -d'.' -f2 | rev` + set TY = `echo $ThisTime | cut -c1-4` + set TM = `echo $ThisTime | cut -c5-6` + if ($NENS == 1) then + set THISDIR = $EXPDIR/output/$EXPDOMAIN/cat/ens0000/Y${TY}/M${TM}/ + else + set THISDIR = $EXPDIR/output/$EXPDOMAIN/cat/ens_avg/Y${TY}/M${TM}/ + endif + if (! -e $THISDIR ) mkdir -p $THISDIR + + set file_ext = `echo $ofile | rev | cut -d'.' -f1 | rev` + + if($file_ext == nc4) then + /bin/mv $ofile $THISDIR/. + else + set binfile = `echo $ofile | rev | cut -d'.' -f2- | rev` + set decr_file = `echo $ofile | rev | cut -d'.' -f3- | rev`.ctl + ($GEOSBIN/tile_bin2nc4.x $binfile $decr_file $TILECOORD ; \ + /bin/mv ${binfile}.nc4 $THISDIR/. ; \ + /bin/rm ${binfile}.bin) & + endif + end + wait + + ####################################################################### + # Post-Process model diagnostic output + # (1) Concatenate sub-daily files to daily files + # (2) Write monthly means + ####################################################################### + + if ($POSTPROC_HIST > 0) then + + set PWD = `pwd` + + if ($NENS == 1) then + set OUTDIR = $EXPDIR/output/$EXPDOMAIN/cat/ens0000/ + else + set OUTDIR = $EXPDIR/output/$EXPDOMAIN/cat/ens_avg/ + endif + + set MONTHDIRS = `ls -d $OUTDIR/*/*` + + foreach THISMONTH ($MONTHDIRS) + + set MM = `echo $THISMONTH | rev | cut -d'/' -f1 | cut -c1-2 | rev` + set YYYY = `echo $THISMONTH | rev | cut -d'/' -f2 | cut -c1-4 | rev` + set NDAYS = `cal $MM $YYYY | awk 'NF {DAYS = $NF}; END {print DAYS}'` + + cd $THISMONTH + + foreach ThisCol ($collections) + # if monthly exists, move on to the next collection + if (-f $EXPID.${ThisCol}.monthly.$YYYY$MM.nc4) continue + + # create daily and remove the sub-daily + # ------------------------------------------------------------------ + set day=1 + while ($day <= $NDAYS) + if ( $day < 10 ) set DD=0${day} + if ( $day >= 10 ) set DD=${day} + @ day++ + set time_steps = `ls -1 $EXPID.$ThisCol.${YYYY}${MM}${DD}_* | rev | cut -d'.' -f2 | rev` + set LEN_SUB = `echo $#time_steps` + + # no file or just one file? nothing to concatenate, move on to the next collection + if ($LEN_SUB <= 1) continue + + # check if day is complete (get HISTORY time step from first two files) + set hour1 = `echo $time_steps[1] | cut -c10-11` + set min1 = `echo $time_steps[1] | cut -c12-13` + set hour2 = `echo $time_steps[2] | cut -c10-11` + set min2 = `echo $time_steps[2] | cut -c12-13` + @ dt_hist = ($hour2 - $hour1) * 60 + ($min2 - $min1) + @ N_per_day = (24 * 60) / $dt_hist + # not enough sub-daily files? move on to the next collection + if($LEN_SUB < $N_per_day) continue + + set tstep2 = \"`echo $time_steps | sed 's/\ /\","/g'`\" + +# ---------------------------------------------------------------------------- +# +# WARNING: The following block MUST begin in column 1!!! Do NOT indent!!! + +cat << EOF > timestamp.cdl +netcdf timestamp { +dimensions: +time = UNLIMITED ; // (NT currently) +string_length = 14 ; +variables: +char time_stamp (time, string_length) ; + +data: + +time_stamp = +DATAVALUES; +} +EOF + + sed -i -e "s/NT/$LEN_SUB/g" timestamp.cdl + sed -i -e "s/DATAVALUES/$tstep2/g" timestamp.cdl + ncgen -k4 -o timestamp.nc4 timestamp.cdl + ncrcat -h $EXPID.$ThisCol.${YYYY}${MM}${DD}_* ${EXPID}.${ThisCol}.$YYYY$MM$DD.nc4 + ncks -4 -h -v time_stamp timestamp.nc4 -A ${EXPID}.${ThisCol}.$YYYY$MM$DD.nc4 + /bin/rm timestamp.cdl + /bin/rm timestamp.nc4 + # rudimentary check for desired nc4 file; if ok, delete sub-daily files + if ( -f ${EXPID}.${ThisCol}.$YYYY$MM$DD.nc4 ) then + if ( ! -z ${EXPID}.${ThisCol}.$YYYY$MM$DD.nc4 ) then + /bin/rm $EXPID.${ThisCol}.${YYYY}${MM}${DD}_*.nc4 + endif + endif + end # concatenate for each day + + # write monthly mean file and (optionally) remove daily files + # ------------------------------------------------------------------ + + # NOTE: Collections written with daily frequency ("tavg24" and "inst24") have not + # been concatenated into daily files. There are two possibilities for the + # time stamps of files to be averaged: + # *.YYYYMMDD.* daily files from concatenation of sub-daily files + # *.YYYYMMDD_HHMM.* daily (avg or inst) files written directly by HISTORY.rc + + set time_steps = `ls -1 $EXPID.$ThisCol.${YYYY}${MM}??.* | rev | cut -d'.' -f2 | rev` + set time_steps_ = `ls -1 $EXPID.$ThisCol.${YYYY}${MM}??_* | rev | cut -d'.' -f2 | cut -d'_' -f2 | rev` + set LEN = `echo $#time_steps` + set LEN_ = `echo $#time_steps_` + + # check if month is complete + if ($LEN != 0) then + set dayl = `echo $time_steps[$LEN] | cut -c1-8` + set day1 = `echo $time_steps[1] | cut -c1-8` + @ NAVAIL = ($dayl - $day1) + 1 + else if( $LEN_ != 0 ) then + set dayl = `echo $time_steps_[$LEN_] | cut -c1-8` + set day1 = `echo $time_steps_[1] | cut -c1-8` + @ NAVAIL = ($dayl - $day1) + 1 + else + @ NAVAIL = 0 + endif + + # not enough days for monthly mean? move on to the next collection + if($NAVAIL != $NDAYS) continue + + # create monthly-mean nc4 file + ncra -h $EXPID.$ThisCol.${YYYY}${MM}*.nc4 ${EXPID}.${ThisCol}.monthly.$YYYY$MM.nc4 + + if($POSTPROC_HIST == 2) then + # rudimentary check for desired nc4 file; if ok, delete daily files + if ( -f ${EXPID}.${ThisCol}.monthly.$YYYY$MM.nc4 ) then + if ( ! -z ${EXPID}.${ThisCol}.monthly.$YYYY$MM.nc4 ) then + /bin/rm $EXPID.${ThisCol}.${YYYY}${MM}* + endif + endif + continue + endif + + end # each collection + end # each month + cd $PWD + endif # POSTPROC_HIST > 0 + + ####################################################################### + # Rename Final Checkpoints => Restarts for Next Segment and Archive + # Note: cap_restart contains the current NYMD and NHMS + ####################################################################### + + set eYEAR = `cat cap_restart | cut -c1-4` + set eMON = `cat cap_restart | cut -c5-6` + set eDAY = `cat cap_restart | cut -c7-8` + set eHour = `cat cap_restart | cut -c10-11` + set eMin = `cat cap_restart | cut -c12-13` + + # Create rc_out/YYYY/MM + # --------------------- + + set THISDIR = $EXPDIR/output/$EXPDOMAIN/rc_out/Y${eYEAR}/M${eMON}/ + if (! -e $THISDIR ) mkdir -p $THISDIR + + # Move mwrtm and cat_param + + set new_mwrtm_file = $EXPDIR/output/$EXPDOMAIN/rc_out/Y${eYEAR}/M${eMON}/${EXPID}.ldas_mwRTMparam.${eYEAR}${eMON}${eDAY}_${eHour}${eMin}z.nc4 + set new_catch_param = $EXPDIR/output/$EXPDOMAIN/rc_out/Y${eYEAR}/M${eMON}/${EXPID}.ldas_catparam.${eYEAR}${eMON}${eDAY}_${eHour}${eMin}z.bin + + if (-f $old_mwrtm_file) then + if ( -l "$new_mwrtm_file" ) then + /bin/rm -f $new_mwrtm_file + endif + /bin/ln -rs $old_mwrtm_file $new_mwrtm_file + /bin/rm ../input/restart/mwrtm_param_rst + /bin/ln -rs $new_mwrtm_file ../input/restart/mwrtm_param_rst + endif + + if (-f $old_catch_param) then + if ( -l "$new_catch_param" ) then + /bin/rm -f $new_catch_param + endif + /bin/ln -rs $old_catch_param $new_catch_param + endif + + # Move Intermediate Checkpoints to RESTARTS directory + # --------------------------------------------------- + + @ inens = MY_FIRST_ENS_ID + @ enens = $inens + $NENS + while ($inens < $enens) + if ($inens <10) then + set ENSDIR = `echo ens000${inens}` + else if($inens<100) then + set ENSDIR=`echo ens00${inens}` + else if($inens < 1000) then + set ENSDIR =`echo ens0${inens}` + else + set ENSDIR = `echo ens${inens}` + endif + set ENSID = `echo $ENSDIR | cut -c4-7` + set ENSID = _e${ENSID} + if ( $NENS == 1) set ENSID ='' + set THISDIR = $EXPDIR/output/$EXPDOMAIN/rs/$ENSDIR/Y${eYEAR}/M${eMON}/ + if (! -e $THISDIR ) mkdir -p $THISDIR + + set rstf = ${MODEL} + if (-f ${rstf}${ENSID}_internal_checkpoint ) then + set tmp_file = $EXPDIR/output/$EXPDOMAIN/rs/$ENSDIR/Y${eYEAR}/M${eMON}/${EXPID}.${rstf}_internal_rst.${eYEAR}${eMON}${eDAY}_${eHour}${eMin} + /bin/mv ${rstf}${ENSID}_internal_checkpoint $tmp_file + /bin/rm -f $EXPDIR/input/restart/${rstf}${ENSID}_internal_rst + /bin/ln -rs $tmp_file $EXPDIR/input/restart/${rstf}${ENSID}_internal_rst + endif + + set rstf = 'landpert' + if (-f ${rstf}${ENSID}_internal_checkpoint ) then + set tmp_file = $EXPDIR/output/$EXPDOMAIN/rs/$ENSDIR/Y${eYEAR}/M${eMON}/${EXPID}.${rstf}_internal_rst.${eYEAR}${eMON}${eDAY}_${eHour}${eMin} + # copy generic restart file to final location/name but remove lat/lon variables + # (lat/lon variables are not correct when running in EASE-grid tile space) + ncks -4 -O -C -x -v lat,lon ${rstf}${ENSID}_internal_checkpoint $tmp_file + /bin/rm -f ${rstf}${ENSID}_internal_checkpoint + set old_rst = `/usr/bin/readlink -f $EXPDIR/input/restart/${rstf}${ENSID}_internal_rst` + /bin/rm -f $EXPDIR/input/restart/${rstf}${ENSID}_internal_rst + /bin/ln -rs $tmp_file $EXPDIR/input/restart/${rstf}${ENSID}_internal_rst + /usr/bin/gzip $old_rst & + endif + + set rstf = 'landassim_obspertrseed' + if (-f ${rstf}${ENSID}_checkpoint ) then + set tmp_file = $EXPDIR/output/$EXPDOMAIN/rs/$ENSDIR/Y${eYEAR}/M${eMON}/${EXPID}.${rstf}_rst.${eYEAR}${eMON}${eDAY}_${eHour}${eMin} + /bin/mv ${rstf}${ENSID}_checkpoint $tmp_file + /bin/rm -f $EXPDIR/input/restart/${rstf}${ENSID}_rst + /bin/ln -rs $tmp_file $EXPDIR/input/restart/${rstf}${ENSID}_rst + endif + # move intermediate check point files to output/$EXPDOMAIN/rs/$ENSDIR/Yyyyy/Mmm/ directories + # ------------------------------------------------------------------------------------------- + + set rstfiles1 = `ls ${MODEL}${ENSID}_internal_checkpoint.*` + set rstfiles2 = `ls landpert${ENSID}_internal_checkpoint.*` + set rstfiles3 = `ls landassim_obspertrseed${ENSID}_checkpoint.*` + + foreach rfile ( $rstfiles1 ) + set ThisTime = `echo $rfile | rev | cut -d'.' -f2 | rev` + set TY = `echo $ThisTime | cut -c1-4` + set TM = `echo $ThisTime | cut -c5-6` + set THISDIR = $EXPDIR/output/$EXPDOMAIN/rs/$ENSDIR/Y${TY}/M${TM}/ + if (! -e $THISDIR ) mkdir -p $THISDIR + /bin/mv $rfile ${THISDIR}${EXPID}.${MODEL}_internal_rst.${ThisTime}.nc4 + /usr/bin/gzip ${THISDIR}${EXPID}.${MODEL}_internal_rst.${ThisTime}.nc4 & + end + + foreach rfile ( $rstfiles2 ) + set ThisTime = `echo $rfile | rev | cut -d'.' -f2 | rev` + set TY = `echo $ThisTime | cut -c1-4` + set TM = `echo $ThisTime | cut -c5-6` + set THISDIR = $EXPDIR/output/$EXPDOMAIN/rs/$ENSDIR/Y${TY}/M${TM}/ + if (! -e $THISDIR ) mkdir -p $THISDIR + (ncks -4 -O -C -x -v lat,lon $rfile ${THISDIR}${EXPID}.landpert_internal_rst.${ThisTime}.nc4;\ + /usr/bin/gzip ${THISDIR}${EXPID}.landpert_internal_rst.${ThisTime}.nc4; \ + /bin/rm -f $rfile) & + end + + foreach rfile ( $rstfiles3 ) + set ThisTime = `echo $rfile | rev | cut -d'.' -f2 | rev` + set TY = `echo $ThisTime | cut -c1-4` + set TM = `echo $ThisTime | cut -c5-6` + set THISDIR = $EXPDIR/output/$EXPDOMAIN/rs/$ENSDIR/Y${TY}/M${TM}/ + if (! -e $THISDIR ) mkdir -p $THISDIR + /bin/mv $rfile ${THISDIR}${EXPID}.landassim_obspertrseed_rst.${ThisTime}.nc4 + end + + @ inens ++ + end ## end of while ($inens < $NENS) + wait + ##################### + # update cap_restart + # ################## + + set CO2LINE = `grep -n -m 1 "CO2_YEAR" $HOMDIR/LDAS.rc | cut -d':' -f1` + + if ( $CO2LINE >= 1 ) then + + # Update reference year for Carbon Tracker CO2 + ############################################## + + set CO2_BEFORE = `sed -n "${CO2LINE}p;d" LDAS.rc | cut -d':' -f2` + set CAP_BEFORE = `head -1 $HOMDIR/cap_restart | cut -c1-4` + @ DY = $CAP_BEFORE - $CO2_BEFORE + @ CO2_AFTER = `head -1 cap_restart | cut -c1-4` - $DY + set CO2UPDATE = "CO2_YEAR: $CO2_AFTER" + sed -i "${CO2LINE} s|.*|$CO2UPDATE|" LDAS.rc + /bin/rm -f $HOMDIR//LDAS.rc + /bin/cp -p LDAS.rc $HOMDIR/LDAS.rc + endif + + /bin/rm -f $HOMDIR/cap_restart + /bin/cp cap_restart $HOMDIR/cap_restart + + ####################################################################### + # Update Iteration Counter + ####################################################################### + + set enddate = `echo $END_DATE | cut -c1-8` + set endhour = `echo $END_DATE | cut -c10-11` + set capdate = `cat cap_restart | cut -c1-8` + set caphour = `cat cap_restart | cut -c10-11` + + if ( $capdate < $enddate ) then + @ counter = $counter + 1 + else if ( $capdate == $enddate && $caphour < $endhour ) then + @ counter = $counter + 1 + else + @ counter = ${NUM_SGMT} + 1 + endif + +## End of the while ( $counter <= ${NUM_SGMT} ) loop ## +end + +####################################################################### +# Set Next Log and Error Files +####################################################################### + +set logfile = $EXPDIR/output/$EXPDOMAIN/rc_out/Y${logYEAR}/M${logMON}/${EXPID}.ldas_log.${logYEAR}${logMON}${logDAY}_${logHour}${logMin}z.txt +set errfile = $EXPDIR/output/$EXPDOMAIN/rc_out/Y${logYEAR}/M${logMON}/${EXPID}.ldas_err.${logYEAR}${logMON}${logDAY}_${logHour}${logMin}z.txt + +if (-f GEOSldas_log_txt) then + /bin/cp GEOSldas_log_txt $logfile + /bin/rm -f GEOSldas_log_txt +endif + +if(-f GEOSldas_err_txt) then + /bin/cp GEOSldas_err_txt $errfile + /bin/rm -f GEOSldas_err_txt +endif + +####################################################################### +# Re-Submit Job +####################################################################### + +if ( $LADAS_COUPLING > 0 ) then + if ( $rc == 0 ) then + echo 'SUCCEEDED' > $HOMDIR/lenkf_job_completed.txt + endif +else + if ( $rc == 0 ) then + cd $HOMDIR + #don't change below line(not even extra space) + if($capdate<$enddate) sbatch $HOMDIR/lenkf.j + if($capdate<$enddate) qsub $HOMDIR/lenkf.j + endif +endif diff --git a/GEOSldas_App/preprocess_ldas.F90 b/GEOSldas_App/preprocess_ldas.F90 new file mode 100644 index 0000000..4d5725e --- /dev/null +++ b/GEOSldas_App/preprocess_ldas.F90 @@ -0,0 +1,142 @@ + +! how to use : +! ./preprocess_ldas option arg1 arg2 arg3 + +program main + + use preprocess_ldas_routines, ONLY: & + createf2g, & + createLocalTilefile, & + createLocalBC, & + createLocalVegRestart, & + createLocalmwRTMRestart, & + createLocalCatchRestart, & + correctEase, & + convert_pert_rst, & + optimize_latlon + + implicit none + + character(len=20 ) :: option + character(len=512) :: arg1 + character(len=512) :: arg2 + character(len=512) :: arg3 + character(len=512) :: arg4 + character(len=512) :: arg5 + character(len=512) :: arg6 + character(len=512) :: arg7 + character(len=512) :: arg8 + + character(len=512) :: orig_tile + character(len=512) :: new_tile + character(len=512) :: domain_def_file + character(len=512) :: catch_def_file + character(len=512) :: out_path + character(len=512) :: exp_id + character(len=512) :: orig_catch + character(len=512) :: new_rtm + character(len=512) :: orig_rtm + character(len=512) :: new_catch + character(len=512) :: orig_BC + character(len=512) :: new_BC + character(len=512) :: orig_Veg + character(len=512) :: new_veg + character(len=512) :: orig_ease + character(len=512) :: new_ease + character(len=512) :: f2g_file + character(len=12 ) :: ymdhm + character(len=12 ) :: SURFLAY + + call get_command_argument(1,option) + call get_command_argument(2,arg1) + call get_command_argument(3,arg2) + call get_command_argument(4,arg3) + call get_command_argument(5,arg4) + call get_command_argument(6,arg5) + call get_command_argument(7,arg6) + call get_command_argument(8,arg7) + call get_command_argument(9,arg8) + + if( trim(option) == "c_f2g") then + + ! (1) generate 'f2g.txt' + ! (2) generate tile.domain if it is local + + orig_tile = arg1 + domain_def_file = arg2 + out_path = arg3 + catch_def_file = arg4 + exp_id = arg5 + ymdhm = trim(adjustl(arg6)) + SURFLAY = trim(adjustl(arg7)) + f2g_file = arg8 + + call createf2g(orig_tile,domain_def_file,trim(out_path),catch_def_file,trim(exp_id),ymdhm, SURFLAY, f2g_file) + + else if (trim(option) == "c_localtile") then + + orig_tile = arg1 + new_tile = arg2 + f2g_file = arg3 + call createLocalTilefile(f2g_file, orig_tile,new_tile) + + else if (trim(option) == "c_localbc" ) then + + orig_BC = arg1 + new_BC = arg2 + f2g_file = arg3 + + call createLocalBC(f2g_file, orig_BC, new_BC) + + else if (trim(option) == "c_localvegrst") then + + orig_veg = arg1 + new_veg = arg2 + f2g_file = arg3 + + call createLocalVegRestart(f2g_file, orig_veg, new_veg) + + else if (trim(option) == "c_localmwrtmrst") then + + orig_rtm = arg1 + new_rtm = arg2 + f2g_file = arg3 + + call createLocalmwRTMRestart(f2g_file, orig_rtm, new_rtm) + + else if (trim(option) == "c_localcatchrst") then + + orig_catch = arg1 + new_catch = arg2 + f2g_file = arg3 + + call createLocalCatchRestart(f2g_file, orig_catch, new_catch) + + else if (trim(option)=="correctease") then + + orig_ease = arg1 + new_ease = arg2 + + call correctEase(orig_ease,new_ease) + + else if (trim(option)=="c_convert_pert") then + + out_path = arg3 + exp_id = arg4 + + call convert_pert_rst(arg1,arg2, out_path,exp_id) + + else if (trim(option) == "optimize") then + + + call optimize_latlon(arg1,arg2, arg3, arg4) + + else + + print*, " wrong preprocess option:",option + + end if + +end program main + +! ====================== EOF ======================================================= diff --git a/GEOSldas_App/preprocess_ldas_routines.F90 b/GEOSldas_App/preprocess_ldas_routines.F90 new file mode 100644 index 0000000..4a7e9ec --- /dev/null +++ b/GEOSldas_App/preprocess_ldas_routines.F90 @@ -0,0 +1,3370 @@ + +module preprocess_ldas_routines + + ! collection of subroutines and functions needed for GEOSldas pre-processing + ! + ! The *.F90 module i was created as follows: + ! + ! 1.) git mv preprocess_ldas.F90 preprocess_ldas_routines.F90 (for best possible git diff) + ! 2.) removed main program from file and put into new file preprocess_ldas.F90 + ! 3.) moved additional helper subroutines and functions to here: + ! - LDAS_read_til_file() [from LDAS_TileCoordRoutines.F90] + ! - read_grid_elev() [from LDAS_TileCoordRoutines.F90] + ! - fix_dateline_bug_in_tilecoord() [from LDAS_TileCoordRoutines.F90] + ! - read_catchment_def() [from LDAS_TileCoordRoutines.F90] + ! - is_cat_in_box() [from LDAS_TileCoordRoutines.F90] + ! - domain_setup() [from LDAS_ensdrv_init_routines.F90] + ! - read_exclude_or_includelist() [from LDAS_ensdrv_init_routines.F90] + ! - read_cat_param() [from LDAS_ensdrv_init_routines.F90] + ! - is_in_list() [from LDAS_ensdrv_functions.F90] + ! - is_in_domain() [from LDAS_ensdrv_functions.F90] + ! - word_count() [from LDAS_ensdrv_functions.F90] + ! - open_land_param_file() [from LDAS_ensdrv_functions.F90] + + use netcdf + + use MAPL + + use MAPL_BaseMod, ONLY: & + NTYPS => MAPL_NumVegTypes, & + MAPL_Land + + use MAPL_ConstantsMod, ONLY: & + MAPL_RADIUS ! Earth radius + + use LDAS_ensdrv_Globals, ONLY: & + logit, & + logunit, & + nodata_generic, & + nodata_tol_generic + + use LDAS_TileCoordType, ONLY: & + tile_coord_type, & + grid_def_type, & + operator (==), & + io_grid_def_type + + use LDAS_TileCoordRoutines, ONLY: & + LDAS_create_grid_g, & + get_minExtent_grid, & + io_domain_files + + use nr_ran2_gasdev, ONLY: & + NRANDSEED + + use LDAS_DateTimeMod, ONLY: & + date_time_type + + use force_and_cat_progn_pert_types, ONLY: & + N_progn_pert_max, & + N_force_pert_max + + use catch_types, ONLY: & + cat_param_type + + use catch_constants, ONLY: & + N_gt => CATCH_N_GT + + use LDAS_ensdrv_functions, ONLY: & + get_io_filename + + use LDAS_ExceptionsMod, ONLY: & + ldas_abort, & + LDAS_GENERIC_ERROR + + use gFTL_StringVector + + use pFIO + + implicit none + + private + + public :: createf2g + public :: createLocalTilefile + public :: createLocalBC + public :: createLocalCatchRestart + public :: createLocalVegRestart + public :: createLocalmwRTMRestart + public :: correctEase + public :: optimize_latlon + public :: convert_pert_rst + + character(10), private :: tmpstring10 + character(40), private :: tmpstring40 + + ! Tile type for land that is to be excluded from the simulation domain. + ! (GEOSldas allows for non-global simulations and repeated "zooming" + ! of the domain while MAPL generally assumes a complete (global) tile + ! space. The *_ExcludeFromDomain tile type makes it possible to work + ! with complete (global) tile files (ie, make use of MAPL functionality) + ! and also maintain GEOSldas functionality. + + integer, parameter :: MAPL_Land_ExcludeFromDomain = 1100 + +contains + + ! ******************************************************************** + + subroutine createf2g(orig_tile,domain_def,out_path,catch_def_file,exp_id,ymdhm, SURFLAY, f2g_file) + + implicit none + character(*) :: orig_tile + character(*) :: domain_def + character(*) :: out_path + character(*) :: catch_def_file + character(*) :: exp_id + character(*) :: ymdhm + character(*) :: SURFLAY + character(*) :: f2g_file + + real :: minlon,maxlon,minlat,maxlat + character(len=512):: exclude_file,include_file + character(len=512):: bcs_path + logical :: file_exist + logical :: d_exist,c_exist + + integer :: n + + type(grid_def_type) :: tile_grid_g,tile_grid_d + type(tile_coord_type), dimension(:), pointer :: tile_coord_g => null() + type(tile_coord_type), dimension(:), pointer :: tile_coord_d => null() + integer, dimension(:), pointer :: f2g => null() + integer, dimension(:), pointer :: d2g => null() + integer, dimension(:), pointer :: d2f => null() + integer :: N_catg, N_catd,n1,n2,N_catf + + type(cat_param_type), dimension(:), allocatable :: cp + real :: dzsf + + namelist / domain_inputs / & + minlon, maxlon,minlat,maxlat, & + exclude_file,include_file + + inquire(file=trim(orig_tile),exist=file_exist) + if( .not. file_exist) stop ("original tile file not exist") + + inquire(file=trim(domain_def),exist=d_exist) + if( .not. d_exist) then + print*,"no domain definition file" + endif + + inquire(file=trim(catch_def_file),exist=c_exist) + if( .not. c_exist) then + print*,"no catchment definition file:" , catch_def_file + endif + + + if(d_exist) then + open (10, file=trim(domain_def), delim='apostrophe', action='read', status='old') + read (10, nml= domain_inputs) + close(10) + else + minlon = -180. + maxlon = 180. + minlat = -90. + maxlat = 90. + exclude_file = ' ' + include_file = ' ' + endif + + call LDAS_read_til_file(orig_tile,catch_def_file,tile_grid_g,tile_coord_g,f2g) + + N_catg=size(tile_coord_g) + + ! include and exclude files are absolute + + call domain_setup( & + N_catg, tile_coord_g, & + tile_grid_g, & + ' ', exclude_file, ' ', include_file, & + trim(out_path), 'exp_domain ', trim(exp_id), & + minlon, minlat, maxlon, maxlat, & + N_catd, d2g, tile_coord_d, & + tile_grid_d ) + + allocate(cp(N_catd)) + + read(SURFLAY,*) dzsf + print*, "SURFLAY: ", dzsf + n1 = index(catch_def_file,'/clsm/') + bcs_path(1:n1-1) = catch_def_file(1:n1-1) + call read_cat_param( N_catg, N_catd, d2g, tile_coord_d, dzsf, bcs_path(1:n1-1), bcs_path(1:n1-1),bcs_path(1:n1-1), & + cp ) + call write_cat_param(cp,N_catd) + + allocate(d2f(N_catd)) + d2f = 0 + N_catf = size(f2g) + if( N_catf /= N_catg) then + n = 1 + do n1 = 1,N_catd + do n2 = n, N_catf + if (d2g(n1) == f2g(n2)) then + d2f(n1) = n2 + n = n2+1 + exit + endif + enddo + enddo + if(any(d2f == 0)) stop " Domain includes those excluded tiles" + print*," f2g now is d2f " + else + d2f = d2g + endif + open(40,file=f2g_file,form='formatted',action='write') + write(40,*)N_catf + write(40,*)N_catd + do n=1,N_catd + write(40,*)d2f(n) + enddo + do n=1,N_catd + write(40,*)d2g(n) + enddo + close(40) + if (associated(f2g)) deallocate(f2g) + if (associated(d2g)) deallocate(d2g) + if (associated(d2f)) deallocate(d2f) + + contains + + ! ******************************************************************** + + logical function is_in_list(N_list, list, this_one) + + ! checks whether "this_one" is element of list + + ! reichle, 2 May 2003 + + implicit none + + integer :: N_list, this_one + integer, dimension(N_list) :: list + + integer :: n + + ! ------------------------------------ + + is_in_list = .false. + + do n=1,N_list + + if (list(n)==this_one) then + is_in_list = .true. + exit + end if + end do + + end function is_in_list + + ! ****************************************************************** + + logical function is_in_domain( & + this_cat_exclude, this_cat_include, this_cat_in_box ) + + ! determine whether catchment is in domain + ! + ! The domain is set up using (if present) an "ExcludeList" of catchments + ! to be excluded, an "IncludeList" (if present) of catchments to be included, + ! and the bounding box of a rectangular "zoomed" area (as specified + ! in the "exeinp" file used in ldas_setup). + ! + ! order of precedence: + ! 1. exclude catchments on ExcludeList + ! 2. include catchments on IncludeList or catchments within rectangular domain + ! (i.e., catchments in ExcludeList are *always* excluded) + ! + ! reichle, 7 May 2003 + ! reichle, 9 May 2005 - redesign (no more continents) + ! + ! ---------------------------------------------------------------- + + implicit none + + logical :: this_cat_include, this_cat_exclude, this_cat_in_box + + is_in_domain = .false. + + ! if catchment is NOT in ExcludeList + + if (.not. this_cat_exclude) then + + ! if catchment is within bounding box OR in IncludeList + + if ((this_cat_in_box) .or. (this_cat_include)) then + + is_in_domain = .true. + + end if + end if + + end function is_in_domain + + ! ******************************************************************* + + logical function is_cat_in_box( & + this_minlon, this_minlat, this_maxlon, this_maxlat, & + minlon, minlat, maxlon, maxlat ) + + ! determine whether catchment is within bounding box - reichle, 7 May 2003 + + implicit none + + real :: this_minlon, this_minlat, this_maxlon, this_maxlat + real :: minlon, minlat, maxlon, maxlat + + if ( (this_minlon >= minlon) .and. & + (this_maxlon <= maxlon) .and. & + (this_minlat >= minlat) .and. & + (this_maxlat <= maxlat) ) then + is_cat_in_box = .true. + else + is_cat_in_box = .false. + end if + + end function is_cat_in_box + + ! ******************************************************************** + + subroutine domain_setup( & + N_cat_global, tile_coord_global, & + tile_grid_g, & + exclude_path, exclude_file, include_path, include_file, & + work_path, exp_domain, exp_id, & + minlon, minlat, maxlon, maxlat, & + N_cat_domain, d2g, tile_coord, tile_grid_d ) + + ! Set up modeling domain and determine index vectors mapping from the + ! domain to global catchment space. + ! Determine actual bounding box for domain. + ! Also return tile_coord for domain and tile_grid_d for domain. + ! + ! ----------------------- + ! + ! The domain is set up using (if present) an "ExcludeList" of catchments + ! to be excluded, an "IncludeList" (if present) of catchments to be included, + ! and the bounding box of a rectangular "zoomed" area (as specified + ! in the "exeinp" file used in ldas_setup). + ! + ! order of precedence: + ! 1. exclude catchments in ExcludeList + ! 2. include catchments in IncludeList or catchments within rectangular domain + ! (i.e., catchments in ExcludeList are *always* excluded) + ! + ! input: + ! + ! input/output: + ! tile_grid_g : def of global tile definition grid + ! minlon, maxlon, etc: coordinates of bounding box of domain units as + ! in tile_coord file, that is longitude -180:180, + ! latitude -90:90 + ! + ! output: + ! N_cat_domain = number of catchments in zoomed domain + ! (for which model integration is conducted) + ! d2g = index from domain to global tiles + ! tile_coord_d = tile_coord vector for domain + ! tile_grid_d = def of smallest subgrid of global tile_grid_g that contains + ! all catchments (or tiles) in the domain (tile_grid_d%i_offg, + ! tile_grid_d%j_offg are offsets in indices between tile_grid_g + ! and tile_grid_d) + ! N_catd_cont = number of catchments of (full) domain on each continent + ! + ! + ! - reichle, May 7, 2003 + ! - reichle, Nov 7, 2003 - computation of bounding box of actual domain + ! - reichle, Jul 20, 2004 - fixed initialization of min_min_lon etc + ! - reichle, May 11, 2005 - minor output path changes for redesign + ! - reichle, May 16, 2005 - add output of tile_grid_d + ! - reichle, Aug 18, 2005 - reinstated minlon, maxlon, minlat, maxlat + ! - reichle, Jul 23, 2010 - major overhaul + ! + ! ---------------------------------------------------------- + + implicit none + + integer, intent(in) :: N_cat_global + + type(tile_coord_type), dimension(:), pointer :: tile_coord_global ! input + + type(grid_def_type), intent(in) :: tile_grid_g + + character(*), intent(in) :: exclude_path, include_path + character(*), intent(in) :: exclude_file, include_file + + character(*), intent(in) :: work_path + + character(*), intent(in) :: exp_domain, exp_id + + real, intent(in) :: minlon, minlat ! from nml inputs + real, intent(in) :: maxlon, maxlat ! from nml inputs + + integer, intent(out) :: N_cat_domain + + integer, dimension(:), pointer :: d2g ! output + + type(tile_coord_type), dimension(:), pointer :: tile_coord ! output + + type(grid_def_type), intent(out) :: tile_grid_d + + ! locals + + integer :: n, this_tileid, this_catpfaf, N_exclude, N_include, indomain, rc + + integer, dimension(N_cat_global) :: ExcludeList, IncludeList, tmp_d2g + + real :: this_minlon, this_minlat, this_maxlon, this_maxlat + + logical :: this_cat_exclude, this_cat_include, this_cat_in_box + + integer :: this_i_indg, this_j_indg + + type(grid_def_type) :: tmp_grid_def + logical :: c3_grid + character(512) :: fname + + character(len=*), parameter :: Iam = 'domain_setup' + character(len=400) :: err_msg + + ! ------------------------------------------------------------ + + if (logit) write (logunit,*) 'Setting up domain: ' + if (logit) write (logunit,*) + + ! ------------------------------------------------------------ + ! + ! try reading *domain.txt, *tilecoord.txt, and *tilegrids.txt files + + call io_domain_files( 'r', work_path, exp_id, & + N_cat_domain, d2g, tile_coord, tmp_grid_def, tile_grid_d, rc ) + + if (rc==0) then ! read was successful + + ! minimal consistency check + + if (.not. tile_grid_g==tmp_grid_def) then + err_msg = 'existing domain files inconsistent with ' // & + 'global tile_grid_g from tile_coord_file' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + end if + + write (logunit,*) 'Domain successfully defined from existing files above.' + write (logunit,*) + + else + + print*, "Creating domain..., reading IncludeList and ExludeList if present..." + ! ------------------------------------------------------------ + ! + ! load ExcludeList: catchments listed in this file will *always* be excluded + + fname = trim(exclude_path) // '/' // trim(exclude_file) + + call read_exclude_or_includelist(N_cat_global, fname, ExcludeList, N_exclude) + + ! load IncludeList: catchments listed in this file will be included + ! (unless excluded via ExcludeList) + + fname = trim(include_path) // '/' // trim(include_file) + + call read_exclude_or_includelist(N_cat_global, fname, IncludeList, N_include) + ! ----------------- + ! + ! find and count catchments that are in the domain + + c3_grid = .false. + if(index(tile_grid_g%gridtype,"c3")/=0) c3_grid = .true. + + indomain = 0 ! initialize + + do n=1,N_cat_global + + this_tileid = tile_coord_global(n)%tile_id + + if( .not. c3_grid) then + this_minlon = tile_coord_global(n)%min_lon + this_minlat = tile_coord_global(n)%min_lat + this_maxlon = tile_coord_global(n)%max_lon + this_maxlat = tile_coord_global(n)%max_lat + else ! c3 grid can straddle the lat-lon + this_minlon = tile_coord_global(n)%com_lon + this_minlat = tile_coord_global(n)%com_lat + this_maxlon = tile_coord_global(n)%com_lon + this_maxlat = tile_coord_global(n)%com_lat + endif + + + this_cat_exclude = is_in_list( N_exclude, ExcludeList(1:N_exclude), this_tileid ) + this_cat_include = is_in_list( N_include, IncludeList(1:N_include), this_tileid ) + + this_cat_in_box = & + is_cat_in_box(this_minlon,this_minlat,this_maxlon,this_maxlat, & + minlon, minlat, maxlon, maxlat ) + + if (is_in_domain( & + this_cat_exclude, this_cat_include, this_cat_in_box )) then + + indomain = indomain + 1 + tmp_d2g(indomain) = n + + end if + + end do + + N_cat_domain = indomain + + if (N_cat_domain .eq. 0) then + err_msg = 'No catchments found in domain' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + else + if (logit) then + write (logunit,*) 'Number of catchments in domain = ', N_cat_domain + write (logunit,*) + end if + end if + + ! ------------------------------------------------------------------- + ! + ! assemble d2g, tile_coord, tile_grid_d + + allocate(d2g( N_cat_domain)) + allocate(tile_coord(N_cat_domain)) + + d2g(1:N_cat_domain) = tmp_d2g(1:N_cat_domain) + + tile_coord = tile_coord_global(d2g) + + ! finalize extent of actual domain: + ! determine smallest subgrid of tile_grid_d that contains all + ! catchments/tiles in domain + + tile_grid_d = get_minExtent_grid( N_cat_domain, tile_coord%i_indg, tile_coord%j_indg, & + tile_coord%min_lon, tile_coord%min_lat, tile_coord%max_lon, tile_coord%max_lat, & + tile_grid_g) + + ! output domain files + + tmp_grid_def = tile_grid_g ! cannot use intent(in) tile_grid_g w/ io_domain_files + + call io_domain_files( 'w', work_path, exp_id, & + N_cat_domain, d2g, tile_coord, tmp_grid_def, tile_grid_d, rc ) + + end if ! domain/tilecoord/tilegrids files exist + + ! output extent of domain and tile_grid_d to logunit + + if (logit) write (logunit,*) 'Actual extent of domain grid:' + if (logit) write (logunit,*) 'min lon = ', tile_grid_d%ll_lon + if (logit) write (logunit,*) 'max lon = ', tile_grid_d%ur_lon + if (logit) write (logunit,*) 'min lat = ', tile_grid_d%ll_lat + if (logit) write (logunit,*) 'max lat = ', tile_grid_d%ur_lat + if (logit) write (logunit,*) + + tmpstring40 = 'tile_grid_d' + + if (logit) call io_grid_def_type('w', logunit, tile_grid_d, tmpstring40) + + print*, "Done with " // trim(Iam) + + end subroutine domain_setup + + ! ************************************************************************* + + subroutine read_exclude_or_includelist(N_cat, fname, MyList, N_list) + + ! read numbers/IDs of catchments in MyList (ExcludeList or IncludeList) + ! + ! format of MyList file: ASCII list of tile IDs + ! + ! N_list = number of catchments in MyList + ! + ! reichle, 2 May 2003 + ! + ! -------------------------------------------------------------- + + implicit none + + ! N_cat = max number of catchments allowed in list + ! (use N_cat_global when calling this subroutine) + + integer, intent(in) :: N_cat + character(*), intent(in) :: fname + + integer, intent(out) :: N_list + + integer, dimension(N_cat), intent(out) :: MyList + + ! locals + + integer :: istat, tmpint + + logical :: file_exists + + character(len=*), parameter :: Iam = 'read_exclude_or_includelist' + character(len=400) :: err_msg + + ! ----------------------------------------------------------- + + N_list = 0 + + inquire( file=fname, exist=file_exists) + + if (file_exists) then + + open(10, file=fname, form='formatted', action='read', & + status='old', iostat=istat) + + if (istat==0) then + + if (logit) write (logunit,*) & + 'reading ExcludeList or IncludeList from ', trim(fname) + if (logit) write (logunit,*) + + do + read(10,*,iostat=istat) tmpint + + if (istat==-1) then + if (logit) write (logunit,*) ' found ', N_list, ' catchments on list' + exit + else if (istat/=0) then + err_msg = 'read error other than end-of-file' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + else + N_list = N_list+1 + MyList(N_list) = tmpint + end if + + if (N_list>N_cat) then + + write (tmpstring10,*) N_cat + write (tmpstring40,*) N_list + + err_msg = 'N_list=' // trim(tmpstring40) & + // ' > N_cat=' // trim(tmpstring10) + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + + end if + end do + + close(10,status='keep') + + else + + if (logit) write (logunit,*) & + 'could not open ExcludeList or IncludeList file ', trim(fname) + + end if + + else + + if (logit) write (logunit,*) & + 'ExcludeList or IncludeList file does not exist: ', trim(fname) + + end if + + if (logit) write (logunit,*) + + end subroutine read_exclude_or_includelist + + ! **************************************************************** + + integer function word_count( mystring ) + + ! count number of words in "mystring" (delimited by space) + ! + ! - reichle, 31 Mar 2015 + + implicit none + + character(len=*) :: mystring + + integer :: N_words, N_string, ii + + logical :: current_is_space, next_is_space + + N_words = 0 + + current_is_space = (mystring(1:1)==' ') + + if (.not. current_is_space) N_words = N_words + 1 + + do ii=2,len(mystring) + + next_is_space = (mystring(ii:ii)==' ') + + if (current_is_space .and. .not. next_is_space) N_words = N_words + 1 + + current_is_space = next_is_space + + end do + + word_count = N_words + + end function word_count + + ! *********************************************************************** + + integer function open_land_param_file( unitnumber, formatted_file, is_big_endian, & + N_search_dir, fname, pathname, search_dir, ignore_stop ) + + ! reichle, 13 Dec 2010 + ! reichle, 21 Oct 2011 - added optional output "istat" + ! reichle, 11 Dec 2013 - moved from "clsm_ensdrv_drv_routines.F90" + ! and converted to function + + ! try reading land or mwRTM parameter files from various sub-dirs for + ! compatibility with old and new parameter directory structures + + ! fname = file name (without path) of parameter file + ! pathname = path to parameter file + ! search_dir = vector (length N_search_dir) of subdirectories to search + ! for file fname + + ! ignore_stop = optional input, if present and .true., skip call to "stop_it()" + + implicit none + + integer :: unitnumber, N_search_dir + + logical :: formatted_file + + logical :: is_big_endian + + character(*) :: fname + + character(*) :: pathname + + character(*), dimension(:) :: search_dir + + logical, optional :: ignore_stop + + ! local variables + + character(len=512) :: filename + + integer :: i, istat + + logical :: ignore_stop_tmp + + character(len=*), parameter :: Iam = 'open_land_param_file' + character(len=400) :: err_msg + + ! ------------------------------------------------------------------ + ! + ! try opening file + + do i=1,N_search_dir + + filename = trim(pathname) // '/' // trim(search_dir(i)) // '/' // trim(fname) + + if (formatted_file) then + + open(unitnumber, file=filename, form='formatted', & + action='read', status='old', iostat=istat) + + else + + if (is_big_endian) then + + open(unitnumber, file=filename, form='unformatted', & + convert='big_endian', & + action='read', status='old', iostat=istat) + + else + + open(unitnumber, file=filename, form='unformatted', & + convert='little_endian', & + action='read', status='old', iostat=istat) + + end if + + end if + + if (istat==0) exit ! exit loop when first successful + + end do + + ! report back opened filename or stop (unless requested otherwise) + + if (istat==0) then + + if (logit) write (logunit,'(400A)') 'Reading from: ' // trim(filename) + + else + + if (logit) then + + write (logunit,*) 'Cannot find file ', trim(fname), ' in: ' + + do i=1,N_search_dir + write (logunit,*) trim(pathname) // '/' // trim(search_dir(i)) + end do + + end if + + ! figure out whether to stop + + ignore_stop_tmp = .false. ! default: stop if file not opened successfully + + if (present(ignore_stop)) ignore_stop_tmp = ignore_stop + + if (.not. ignore_stop_tmp) then + err_msg = 'ERROR opening file' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + end if + + end if + + if (logit) write (logunit,*) + + open_land_param_file = istat + + end function open_land_param_file + + ! ***************************************************************************************** + + subroutine read_cat_param( & + N_catg, N_catf, f2g, tile_coord_f, dzsf, veg_path, soil_path, top_path, & + cp ) + + ! Reads soil properties and topographic parameters from global files + ! and extracts data for the (full) domain. + ! + ! Additional parameters are derived from the ones that have been read + ! from files. + ! + ! cp = cat_param_f + ! + ! reichle, 12 May 2003 + ! reichle, 6 Jun 2005 - adapted to read "SiB2_V2" parameters + ! reichle, 5 Apr 2013 - removed alb_param_type fields "sc_albvr", "sc_albnr" + ! reichle, 25 Jul 2013 - removed LAI, GRN, and albedo inputs, renamed subroutine + ! from "read_land_parameters()" to "read_cat_param()" + ! reichle, 16 Nov 2015 - read static (JPL) veg height from boundary condition file + ! reichle, 14 Jul 2020 - work around for new "peat fraction" column in Icarus-NLv4 (ignore for now) + ! + ! ------------------------------------------------------------------- + + implicit none + + integer, intent(in) :: N_catg, N_catf + + type(tile_coord_type), dimension(:), pointer :: tile_coord_f ! intent(in) + + real, intent(in) :: dzsf + + integer, dimension(N_catf), intent(in) :: f2g + + character(*), intent(in) :: veg_path + character(*), intent(in) :: soil_path + character(*), intent(in) :: top_path + + type(cat_param_type), dimension(N_catf), intent(out) :: cp + + ! local variables + + integer, parameter :: N_search_dir_max = 5 + integer, parameter :: N_col_real_max = 18 ! "v15" soil_param.dat had 22 columns (incl. first 4 columns with integers) + + character( 80) :: fname + character(999) :: tmpstr999 + + character(100), dimension(N_search_dir_max) :: search_dir + + integer :: n, k, m, dummy_int, dummy_int2, istat, N_search_dir, N_col + + integer, dimension(N_catg) :: tmpint, tmpint2, tmptileid + + real, dimension(N_catg,N_col_real_max) :: tmpreal + + real :: dummy_real, dummy_real2, z_in_m, term1, term2 + + logical :: dummy_logical + + character(len=*), parameter :: Iam = 'read_cat_param' + character(len=400) :: err_msg + + real, dimension(NTYPS) :: VGZ2 + + ! legacy vegetation height look-up table (for backward compatibility) + ! + DATA VGZ2 /35.0, 20.0, 17.0, 0.6, 0.5, 0.6/ ! Dorman and Sellers (1989) + + ! --------------------------------------------------------------------- + + if (logit) write (logunit,*) 'reading Catchment model parameters' + if (logit) write (logunit,*) + + ! ----------------------------- + + ! Vegetation class + + if (logit) write (logunit,*) 'Reading vegetation class and, if available, height' + + fname = '/mosaic_veg_typs_fracs' + + N_search_dir = 2 ! specify sub-dirs of veg_path to search for file "fname" + + search_dir(1) = 'clsm' + search_dir(2) = 'VEGETATION-GSWP2' + + ! find out how many columns are in the (formatted) file + + istat = open_land_param_file( & + 10, .true., dummy_logical, N_search_dir, fname, trim(veg_path), search_dir) + + read(10,'(a)') tmpstr999 ! read first line + + close(10, status='keep') + + ! count words in first line (delimited by space) + + N_col = word_count( tmpstr999 ) + + ! read parameters + + istat = open_land_param_file( & + 10, .true., dummy_logical, N_search_dir, fname, trim(veg_path), search_dir) + + tmptileid = 0 + + tmpreal = nodata_generic + + select case (N_col) + + case (6) + + ! legacy vegetation height from look-up table + + if (logit) write (logunit,*) 'Using vegetation height look-up table' + + do n=1,N_catg + + read (10,*) tmptileid(n), dummy_int, tmpint(n) + + end do + + if ( (any(tmpint<1)) .or. (any(tmpint>NTYPS)) ) then + + err_msg = 'veg type (class) exceeds allowed min/max' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + + end if + + do n=1,N_catg + + tmpreal(n,1) = VGZ2( tmpint(n) ) + + end do + + + case (7,8) + + ! vegetation height from boundary condition file + + if (logit) write (logunit,*) 'reading vegetation height from file' + + do n=1,N_catg + + ! 7-th column contains veg height in m + ! 8-th column contains ASCAT z0 values (IGNORED for now, reichle, 31 Oct 2017) + + read (10,*) tmptileid(n), dummy_int, tmpint(n), & + dummy_int2, dummy_real, dummy_real2, tmpreal(n,1) + + end do + + case default + + err_msg = 'unknown number of columns in ' // trim(fname) + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + + end select + + close (10,status='keep') + + if (logit) write (logunit,*) 'done reading' + if (logit) write (logunit,*) + + do k=1,N_catf + + ! this check works only for "SiB2_V2" and newer versions + + if (tile_coord_f(k)%tile_id/=tmptileid(f2g(k))) then + err_msg = 'something wrong with veg parameters' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + end if + + cp(k)%vegcls = tmpint( f2g(k) ) + cp(k)%veghght = tmpreal(f2g(k),1) + + end do + + ! ----------------------------------- + + ! Soil parameters, surface layer time scales, and topographical parameters + + N_search_dir = 2 ! specify sub-dirs of path to search for file "fname" + + search_dir(1) = 'clsm' + search_dir(2) = '.' + + ! --------------------- + ! + ! Soil parameters + + if (logit) write (logunit,*) 'Reading soil parameters' + + fname = '/soil_param.dat' + + ! find out how many columns are in the (formatted) file + + istat = open_land_param_file( & + 10, .true., dummy_logical, N_search_dir, fname, soil_path, search_dir) + + read(10,'(a)') tmpstr999 ! read first line + + close(10, status='keep') + + ! count words in first line (delimited by space) + + N_col = word_count( tmpstr999 ) + + ! read parameters + + istat = open_land_param_file( & + 10, .true., dummy_logical, N_search_dir, fname, soil_path, search_dir) + + tmptileid = 0 + + tmpreal = nodata_generic + + do n=1,N_catg + + ! "SiB2_V2" version + + read (10,*) tmptileid(n), dummy_int, tmpint(n), tmpint2(n), & + (tmpreal(n,m), m=1,N_col-4) + + end do + + close (10,status='keep') + + if (logit) write (logunit,*) 'done reading' + if (logit) write (logunit,*) + + do k=1,N_catf + + if (tile_coord_f(k)%tile_id/=tmptileid(f2g(k))) then + err_msg = 'something wrong with soil parameters' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + end if + + cp(k)%bee = tmpreal(f2g(k),1) + cp(k)%psis = tmpreal(f2g(k),2) + cp(k)%poros = tmpreal(f2g(k),3) + cp(k)%cond = tmpreal(f2g(k),4) + cp(k)%wpwet = tmpreal(f2g(k),5) + cp(k)%dpth = tmpreal(f2g(k),6) + + cp(k)%soilcls30 = tmpint( f2g(k)) + cp(k)%soilcls100 = tmpint2(f2g(k)) + + end do + + ! additional soil parameters from recent versions of "soil_param.dat" + ! (eg. for use in calibration of the microwave radiative transfer model) + ! - reichle, 1 Apr 2015 + + select case (N_col) + + case (19,20) + + ! starting with "v16" (De Lannoy et al., 2014, doi:10.1002/2014MS000330), + ! soil_param.dat has 19 columns + + ! "Icarus-NLv4" has 20 columns (new, last column is peat fraction, ignore for now) + + do k=1,N_catf + + cp(k)%gravel30 = tmpreal(f2g(k), 7) + cp(k)%orgC30 = tmpreal(f2g(k), 8) + cp(k)%orgC = tmpreal(f2g(k), 9) + cp(k)%sand30 = tmpreal(f2g(k),10) + cp(k)%clay30 = tmpreal(f2g(k),11) + cp(k)%sand = tmpreal(f2g(k),12) + cp(k)%clay = tmpreal(f2g(k),13) + cp(k)%wpwet30 = tmpreal(f2g(k),14) + cp(k)%poros30 = tmpreal(f2g(k),15) + + end do + + case default + + do k=1,N_catf + + cp(k)%gravel30 = nodata_generic + cp(k)%orgC30 = nodata_generic + cp(k)%orgC = nodata_generic + cp(k)%sand30 = nodata_generic + cp(k)%clay30 = nodata_generic + cp(k)%sand = nodata_generic + cp(k)%clay = nodata_generic + cp(k)%wpwet30 = nodata_generic + cp(k)%poros30 = nodata_generic + + end do + + end select + + ! ------------------------------------ + + ! Surface layer timescales + + if (logit) write (logunit,*) 'Reading surface layer timescales atau/btau' + + fname = '/tau_param.dat' + + istat = open_land_param_file( & + 10, .true., dummy_logical, N_search_dir, fname, soil_path, search_dir) + + tmptileid = 0 + + tmpreal = nodata_generic + + do n=1,N_catg + + read (10,*) tmptileid(n), dummy_int, (tmpreal(n,m), m=1,4) + + end do + + close (10,status='keep') + + if (logit) write (logunit,*) 'done reading' + if (logit) write (logunit,*) + + do k=1,N_catf + + ! this check works only for "SiB2_V2" version + + if (tile_coord_f(k)%tile_id/=tmptileid(f2g(k))) then + err_msg = 'something wrong with tau parameters' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + end if + + ! select atau and btau depending on surface layer depth + + if (abs(dzsf-20.)<1e-4 ) then ! use atau2, btau2 + + cp(k)%atau = tmpreal(f2g(k),1) + cp(k)%btau = tmpreal(f2g(k),2) + + elseif (abs(dzsf-50.)<1e-4 ) then ! use atau5, btau5 + + cp(k)%atau = tmpreal(f2g(k),3) + cp(k)%btau = tmpreal(f2g(k),4) + + else + + err_msg = 'unknown value for dzsf' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + + end if + + end do + + ! make sure atau and btau are not unphysical + + if (any(cp%atau<=0)) then + err_msg = 'unphysical atau value(s)' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + end if + if (any(cp%btau<=0)) then + err_msg = 'unphysical btau value(s)' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + end if + + + ! ------------------------------------ + + ! Topographical parameters + + if (logit) write (logunit,*) 'Reading topo parameters (ar)' + + fname = '/ar.new' + + istat = open_land_param_file( & + 10, .true., dummy_logical, N_search_dir, fname, top_path, search_dir) + + tmptileid = 0 + + tmpreal = nodata_generic + + do n=1,N_catg + + read (10,*) tmptileid(n), dummy_int, (tmpreal(n,m), m=1,12) + + end do + + close (10,status='keep') + + if (logit) write (logunit,*) 'done reading' + if (logit) write (logunit,*) + + do k=1,N_catf + + ! this check works only for "SiB2_V2" version + + if (tile_coord_f(k)%tile_id/=tmptileid(f2g(k))) then + err_msg = 'something wrong with ar parameters' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + end if + + cp(k)%gnu = tmpreal(f2g(k),1) + cp(k)%ars1 = tmpreal(f2g(k),2) + cp(k)%ars2 = tmpreal(f2g(k),3) + cp(k)%ars3 = tmpreal(f2g(k),4) + cp(k)%ara1 = tmpreal(f2g(k),5) + cp(k)%ara2 = tmpreal(f2g(k),6) + cp(k)%ara3 = tmpreal(f2g(k),7) + cp(k)%ara4 = tmpreal(f2g(k),8) + cp(k)%arw1 = tmpreal(f2g(k),9) + cp(k)%arw2 = tmpreal(f2g(k),10) + cp(k)%arw3 = tmpreal(f2g(k),11) + cp(k)%arw4 = tmpreal(f2g(k),12) + + end do + + ! -------------------- + + if (logit) write (logunit,*) 'Reading topo parameters (bf)' + + fname = '/bf.dat' + + istat = open_land_param_file( & + 10, .true., dummy_logical, N_search_dir, fname, top_path, search_dir) + + tmptileid = 0 + + tmpreal = nodata_generic + + do n=1,N_catg + + read (10,*) tmptileid(n), dummy_int, (tmpreal(n,m), m=1,4) + + end do + + close (10,status='keep') + + if (logit) write (logunit,*) 'done reading' + if (logit) write (logunit,*) + + do k=1,N_catf + + ! this check works only for "SiB2_V2" version + + if (tile_coord_f(k)%tile_id/=tmptileid(f2g(k))) then + err_msg = 'something wrong with bf parameters' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + end if + + ! --------- + + if (cp(k)%gnu/=tmpreal(f2g(k),1)) then + err_msg = 'land(): something wrong with gnu' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + end if + cp(k)%bf1 = tmpreal(f2g(k),2) + cp(k)%bf2 = tmpreal(f2g(k),3) + cp(k)%bf3 = tmpreal(f2g(k),4) + + end do + + ! -------------------- + + if (logit) write (logunit,*) 'Reading topo parameters (ts)' + + fname = '/ts.dat' + + istat = open_land_param_file( & + 10, .true., dummy_logical, N_search_dir, fname, top_path, search_dir) + + tmptileid = 0 + + tmpreal = nodata_generic + + do n=1,N_catg + + read (10,*) tmptileid(n), dummy_int, (tmpreal(n,m), m=1,5) + + end do + + close (10,status='keep') + + if (logit) write (logunit,*) 'done reading' + if (logit) write (logunit,*) + do k=1,N_catf + + ! this check works only for "SiB2_V2" version + + if (tile_coord_f(k)%tile_id/=tmptileid(f2g(k))) then + err_msg = 'something wrong with ts parameters' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + end if + + ! ------- + + if (cp(k)%gnu/=tmpreal(f2g(k),1)) then + err_msg = 'land(): something wrong with gnu' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + end if + cp(k)%tsa1 = tmpreal(f2g(k),2) + cp(k)%tsa2 = tmpreal(f2g(k),3) + cp(k)%tsb1 = tmpreal(f2g(k),4) + cp(k)%tsb2 = tmpreal(f2g(k),5) + + end do + + ! --------------------------------------------------------------------- + + if (logit) write (logunit,*) 'computing derived land surface parameters...' + if (logit) write (logunit,*) + + do k=1,N_catf + + ! Three soil depths for soil moisture model: + ! + ! dzsf: surface layer + ! dzrz: root zone -> water capacity of the root zone + ! dzpr: unsaturated zone -> approx depth-to-bedrock + ! + ! NOTE: Units of dz** are [mm] while excess/deficits from catchment() + ! are in SI units (ie kg/m^2) or loosely speaking, in mm of water. + ! In other words, density of water (1000 kg/m^3) is built + ! into dz** (reichle, 5 Feb 04). + + cp(k)%dzsf = dzsf + + cp(k)%dzrz = 1000. + + ! changed re-setting of dzrz back to earlier value because + ! Sarith parameters are in fact consistent that the earlier version + ! reichle, 12 Sep 2007 + ! + ! cp(k)%dzpr = max(1500., cp(k)%dpth) + ! + ! previously, root zone depth ranged from .75m to 1m, which + ! is inconsistent with subroutine catchment(), where root + ! zone depth is hard-wired to 1m, and with the time scale + ! parameters, that have been derived for 1m root zone depth + ! (THE LATTER IS IN FACT *NOT* TRUE - reichle, 12 Sep 2007) + ! - reichle, 30 May 2003 + + cp(k)%dzpr = max(1000., cp(k)%dpth) + + if (cp(k)%dzrz > 0.75*cp(k)%dzpr) cp(k)%dzrz = 0.75*cp(k)%dzpr + + ! soil storages + + cp(k)%vgwmax = cp(k)%poros*cp(k)%dzrz + + z_in_m = cp(k)%dzpr/1000. + + term1 = -1.+((cp(k)%psis-z_in_m)/cp(k)%psis)**((cp(k)%bee-1.)/cp(k)%bee) + + term2 = cp(k)%psis*cp(k)%bee/(cp(k)%bee-1) + + cp(k)%cdcr1 = 1000.*cp(k)%poros*(z_in_m-(-term2*term1)) + + cp(k)%cdcr2 = (1.-cp(k)%wpwet)*cp(k)%poros*cp(k)%dzpr + + ! soil depths for ground temperature model + + if (N_gt/=6) then + + write (tmpstring10,*) N_gt + + err_msg = 'using N_gt = ' // trim(tmpstring10) // & + 'but only 6 layer depths are specified.' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + + end if + + cp(k)%dzgt(1) = 0.0988 + cp(k)%dzgt(2) = 0.1952 + cp(k)%dzgt(3) = 0.3859 + cp(k)%dzgt(4) = 0.7626 + cp(k)%dzgt(5) = 1.5071 + cp(k)%dzgt(6) = 10.0000 + + end do + + end subroutine read_cat_param + + ! ********************************************************************************** + + subroutine write_cat_param(cat_param, N_catd) + type(cat_param_type), intent(in) :: cat_param(:) + integer,intent(in) :: N_catd + character(len=512):: fname + type(date_time_type) :: start_time + + integer :: k,n + + read(ymdhm(1:4),*) start_time%year ! 4-digit year + read(ymdhm(5:6),*) start_time%month ! month in year + read(ymdhm(7:8),*) start_time%day ! day in month + read(ymdhm(9:10),*) start_time%hour ! hour of day + read(ymdhm(11:12),*) start_time%min + start_time%sec = 0 + start_time%pentad = -9999 ! pentad of year + start_time%dofyr = -9999 + + fname = get_io_filename(trim(out_path), trim(exp_id),'ldas_catparam', date_time=start_time, & + dir_name='rc_out', file_ext='.bin') + + open(10, file=trim(fname), form='unformatted', status='unknown', action='write') + + print*, 'Writing catparam file : ' // trim(fname) + + write (10) (cat_param(n)%dpth, n=1,N_catd) + + write (10) (cat_param(n)%dzsf, n=1,N_catd) + write (10) (cat_param(n)%dzrz, n=1,N_catd) + write (10) (cat_param(n)%dzpr, n=1,N_catd) + + do k=1,N_gt + write (10) (cat_param(n)%dzgt(k), n=1,N_catd) + end do + + write (10) (cat_param(n)%poros, n=1,N_catd) + write (10) (cat_param(n)%cond, n=1,N_catd) + write (10) (cat_param(n)%psis, n=1,N_catd) + write (10) (cat_param(n)%bee, n=1,N_catd) + + write (10) (cat_param(n)%wpwet, n=1,N_catd) + + write (10) (cat_param(n)%gnu, n=1,N_catd) + + write (10) (cat_param(n)%vgwmax, n=1,N_catd) + + write (10) (real(cat_param(n)%vegcls), n=1,N_catd) + write (10) (real(cat_param(n)%soilcls30), n=1,N_catd) + write (10) (real(cat_param(n)%soilcls100), n=1,N_catd) + + write (10) (cat_param(n)%bf1, n=1,N_catd) + write (10) (cat_param(n)%bf2, n=1,N_catd) + write (10) (cat_param(n)%bf3, n=1,N_catd) + write (10) (cat_param(n)%cdcr1, n=1,N_catd) + write (10) (cat_param(n)%cdcr2, n=1,N_catd) + write (10) (cat_param(n)%ars1, n=1,N_catd) + write (10) (cat_param(n)%ars2, n=1,N_catd) + write (10) (cat_param(n)%ars3, n=1,N_catd) + write (10) (cat_param(n)%ara1, n=1,N_catd) + write (10) (cat_param(n)%ara2, n=1,N_catd) + write (10) (cat_param(n)%ara3, n=1,N_catd) + write (10) (cat_param(n)%ara4, n=1,N_catd) + write (10) (cat_param(n)%arw1, n=1,N_catd) + write (10) (cat_param(n)%arw2, n=1,N_catd) + write (10) (cat_param(n)%arw3, n=1,N_catd) + write (10) (cat_param(n)%arw4, n=1,N_catd) + write (10) (cat_param(n)%tsa1, n=1,N_catd) + write (10) (cat_param(n)%tsa2, n=1,N_catd) + write (10) (cat_param(n)%tsb1, n=1,N_catd) + write (10) (cat_param(n)%tsb2, n=1,N_catd) + write (10) (cat_param(n)%atau, n=1,N_catd) + write (10) (cat_param(n)%btau, n=1,N_catd) + + write (10) (cat_param(n)%gravel30, n=1,N_catd) + write (10) (cat_param(n)%orgC30 , n=1,N_catd) + write (10) (cat_param(n)%orgC , n=1,N_catd) + write (10) (cat_param(n)%sand30 , n=1,N_catd) + write (10) (cat_param(n)%clay30 , n=1,N_catd) + write (10) (cat_param(n)%sand , n=1,N_catd) + write (10) (cat_param(n)%clay , n=1,N_catd) + write (10) (cat_param(n)%wpwet30 , n=1,N_catd) + write (10) (cat_param(n)%poros30 , n=1,N_catd) + + write (10) (cat_param(n)%veghght , n=1,N_catd) + + close (10,status='keep') + + end subroutine write_cat_param + + end subroutine createf2g + + ! ******************************************************************** + + subroutine readsize(f2g_file, N_catg,N_catf) + + implicit none + character(*), intent(in):: f2g_file + integer,intent(out) :: N_catg + integer,intent(out) :: N_catf + + logical :: file_exist + + inquire(file=f2g_file,exist=file_exist) + if(file_exist) then + open(40,file= f2g_file,form='formatted',action='read',status='old') + read(40,*)N_catg + read(40,*)N_catf + close(40) + else + print*, " wrong, no f2g.txt" + endif + end subroutine readsize + + ! ******************************************************************** + + subroutine readf2g(f2g_file, N_catf,f2g) + + implicit none + character(*), intent(in):: f2g_file + integer,intent(in) :: N_catf + integer,dimension(N_catf),intent(inout) :: f2g + + integer :: N_catg + logical :: file_exist + integer :: local_size,n + + inquire(file=f2g_file,exist=file_exist) + if(file_exist) then + open(40,file= f2g_file,form='formatted',action='read',status='old') + read(40,*)N_catg + read(40,*)local_size + + if(local_size /= N_catf) print*, "wrong f2g.txt" + + if(N_catg == N_catf) then + close(40) + return + endif + + do n=1,N_catf + read(40,*)f2g(n) + enddo + close(40) + ! call MAPL_sort(this%f2g) + else + print*, " wrong, no f2g.txt" + endif + + end subroutine readf2g + + ! ******************************************************************** + + subroutine createLocalTilefile(f2g_file, orig_tile,new_tile) + + implicit none + character(*), intent(in) :: f2g_file + character(*), intent(in) :: orig_tile + character(*), intent(in) :: new_tile + + character(len=256) :: line + character(len=3) :: MAPL_Land_STRING + character(len=4) :: MAPL_Land_ExcludeFromDomain_STRING + character(len=400) :: err_msg + + logical :: file_exist + + integer, dimension(:),allocatable :: f2g + integer :: N_catg, N_catf,n,stat, ty + integer :: N_tile,N_grid,g_id + + character(len=*), parameter :: Iam = 'createLocalTilefile' + + ! string handling below relies on MAPL_Land and MAPL_Land_ExcludeFromDomain + ! falling into a certain range + + ! verify that MAPL_Land has three digits + + if (MAPL_Land<100 .or. MAPL_Land>999) then + err_msg = 'string handling implemented only for 100<=MAPL_Land<=999' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + end if + + ! verify that MAPL_Land_ExcludeFromDomain has four digits + + if (MAPL_Land_ExcludeFromDomain<1000 .or. MAPL_Land_ExcludeFromDomain>9999) then + err_msg = 'string handling implemented only for 1000<=MAPL_Land_ExcludeFromDomain<=9999' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + end if + + ! convert integers to appropriate-length strings + + write (MAPL_Land_STRING, '(i3)') MAPL_Land + write (MAPL_Land_ExcludeFromDomain_STRING,'(i4)') MAPL_Land_ExcludeFromDomain + + inquire(file=trim(orig_tile),exist=file_exist) + if( .not. file_exist) stop ("original tile file does not exist") + + ! Set default local tile file name + call readsize( f2g_file, N_catg,N_catf) + if(N_catg == N_catf) then + print*, "It is global domain..." + return + endif + allocate(f2g(N_catf)) + call readf2g(f2g_file, N_catf,f2g) + + open(40,file=trim(orig_tile),action="read") + open(50,file=trim(new_tile),action="write") + + ! copy the header back into the output tile file + ! (also corrects bug in EASE *.til files that have "N_grid=1" in line 2 but + ! still contain three additional lines for second grid definition) + do n=1,5 + read(40,'(A)') line + if(n==1) then + read(line,*) N_tile + endif + if(n==2) then + read(line,*) N_grid + endif + write(50,'(A)') trim(line) + enddo + if (N_grid==2) then + do n=1,3 + read(40,'(A)') line + write(50,'(A)') trim(line) + enddo + endif + + g_id = 0 + do while(.true.) + ! read one line of *.til file + read(40,'(A)',IOSTAT=stat) line + if(IS_IOSTAT_END(stat)) exit + ! extract first "integer" in "line" and put into "ty" + read(line,*) ty + if( ty == MAPL_Land ) then + ! find index where MAPL_Land ("100") starts in "line" + n=index(line,MAPL_Land_STRING) + ! make sure that a space is available in front of MAPL_Land ("100") + if (n<=1) then + err_msg = 'string handling requires at least one blank space in first column of *.til file' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + end if + ! here g_id is (consecutive) id of the global *land* tiles + g_id=g_id+1 + if(.not. any( f2g(:) == g_id)) then + ! if tile is not in local domain, replace " 100" in "line" with "1100" + line(n-1:n+2)=MAPL_Land_ExcludeFromDomain_STRING + endif + endif + ! write "line" into the output tile file + write(50,'(A)') trim(line) + enddo + close(40) + close(50) + + end subroutine createLocalTilefile + + ! ******************************************************************** + + subroutine createLocalBC(f2g_file, orig_BC, new_BC) + + implicit none + character(*),intent(in) :: f2g_file + character(*),intent(in) :: orig_BC + character(*),intent(in) :: new_BC + + real,dimension(14) :: tmprealvec14 + real,allocatable :: tmpvec(:) + integer :: istat, N_catg,N_catf + integer,dimension(:),allocatable :: f2g + + call readsize(f2g_file, N_catg,N_catf) + if(N_catg==N_catf) return + allocate(f2g(N_catf)) + call readf2g(f2g_file, N_catf,f2g) + + allocate(tmpvec(N_catg)) + open(10,file=trim(orig_BC),form='unformatted',action='read',status='old',iostat=istat) + open(20,file=trim(new_BC),form='unformatted',action='write') + + do while(.true.) + read(10,iostat=istat) tmprealvec14 + if(IS_IOSTAT_END(istat)) exit + read(10) tmpvec + write(20) tmprealvec14 + write(20) tmpvec(f2g) + enddo + close(10) + close(20) + deallocate(tmpvec) + end subroutine createLocalBC + + ! ******************************************************************** + + subroutine createLocalCatchRestart(f2g_file, orig_catch, new_catch) + + implicit none + character(*),intent(in):: f2g_file + character(*),intent(in):: orig_catch + character(*),intent(in):: new_catch + integer,parameter :: subtile=4 + integer :: istat, filetype, rc,i, j, ndims + real,allocatable :: tmp1(:) + real,allocatable :: tmp2(:,:) + type(Netcdf4_FileFormatter) :: InFmt,OutFmt + type(FileMetadata) :: OutCfg + type(FileMetadata) :: InCfg + integer :: dim1,dim2 + type(StringVariableMap), pointer :: variables + type(Variable), pointer :: var + type(StringVariableMapIterator) :: var_iter + type(StringVector), pointer :: var_dimensions + character(len=:), pointer :: vname,dname + integer ::n, N_catg,N_catf + integer,dimension(:),allocatable :: f2g + + call readsize(f2g_file, N_catg,N_catf) + if(N_catg == N_catf) return + allocate(f2g(N_catf)) + call readf2g(f2g_file, N_catf,f2g) + + allocate(tmp1(N_catg)) + allocate(tmp2(N_catg,subtile)) + + ! check file type + + call MAPL_NCIOGetFileType(orig_catch, filetype,rc=rc) + + if (filetype /= 0) then + + print*, "Catchment restart is binary" + + ! binary + + open(10,file=trim(orig_catch),form='unformatted',action='read',status='old',iostat=istat) + open(20,file=trim(new_catch),form='unformatted',action='write') + + do n=1,30 + read(10) tmp1 + write(20) tmp1(f2g) + enddo + + do n=1,2 + read(10) tmp2 + write(20) tmp2(f2g,:) + enddo + + do n=1,20 + read(10) tmp1 + write(20) tmp1(f2g) + enddo + ! note : the offline restart does not have the last five variables + do n=1,4 + read(10,iostat=istat) tmp2 + if(.not. IS_IOSTAT_END(istat)) write(20) tmp2(f2g,:) + enddo + ! 57 WW + read(10,iostat=istat) tmp2 + if(.not. IS_IOSTAT_END(istat)) write(20) tmp2(f2g,:) + + close(10) + close(20) + else + + ! filetype = 0 : nc4 output file will also be nc4 + + call InFmt%open(trim(orig_catch), pFIO_READ,rc=rc) + InCfg = InFmt%read(rc=rc) + OutCfg = InCfg + + call OutCfg%modify_dimension('tile', size(f2g), rc=rc) + + call OutFmt%create(trim(new_catch),rc=rc) + call OutFmt%write(OutCfg,rc=rc) + + variables => InCfg%get_variables() + var_iter = variables%begin() + do while (var_iter /= variables%end()) + + vname => var_iter%key() + var => var_iter%value() + var_dimensions => var%get_dimensions() + + ndims = var_dimensions%size() + + if (trim(vname) =='time') then + call var_iter%next() + cycle + endif + + if (ndims == 1) then + call MAPL_VarRead (InFmt,vname,tmp1) + call MAPL_VarWrite(OutFmt,vname,tmp1(f2g)) + else if (ndims == 2) then + + dname => var%get_ith_dimension(2) + dim1=InCfg%get_dimension(dname) + do j=1,dim1 + call MAPL_VarRead ( InFmt,vname,tmp1 ,offset1=j) + call MAPL_VarWrite(OutFmt,vname,tmp1(f2g),offset1=j) + enddo + + else if (ndims == 3) then + + dname => var%get_ith_dimension(2) + dim1=InCfg%get_dimension(dname) + dname => var%get_ith_dimension(3) + dim2=InCfg%get_dimension(dname) + do i=1,dim2 + do j=1,dim1 + call MAPL_VarRead ( InFmt,vname,tmp1 ,offset1=j,offset2=i) + call MAPL_VarWrite(OutFmt,vname,tmp1(f2g) ,offset1=j,offset2=i) + enddo + enddo + + end if + call var_iter%next() + enddo + call inFmt%close(rc=rc) + call OutFmt%close(rc=rc) + end if ! file type nc4 + print*, "done create local catchment restart" + end subroutine createLocalCatchRestart + + ! ******************************************************************** + + subroutine createLocalmwRTMRestart(f2g_file, orig_mwrtm, new_mwrtm) + + implicit none + character(*),intent(in):: f2g_file + character(*),intent(in):: orig_mwrtm + character(*),intent(in):: new_mwrtm + integer,parameter :: subtile=4 + integer :: rc + real,allocatable :: tmp1(:) + type(Netcdf4_FileFormatter) :: InFmt,OutFmt + type(FileMetadata) :: OutCfg + type(FileMetadata) :: InCfg + + type(StringVariableMap), pointer :: variables + type(StringVariableMapIterator) :: var_iter + character(len=:), pointer :: vname + integer :: N_catg,N_catf + integer,dimension(:),allocatable :: f2g + + call readsize(f2g_file, N_catg,N_catf) + if(N_catg == N_catf) return + allocate(f2g(N_catf)) + call readf2g(f2g_file, N_catf,f2g) + + allocate(tmp1(N_catg)) + + ! nc4 in and out file will also be nc4 + call InFmt%open(trim(orig_mwrtm), pFIO_READ,rc=rc) + InCfg = InFmt%read(rc=rc) + OutCfg = InCfg + + call OutCfg%modify_dimension('tile', size(f2g), rc=rc) + + call OutFmt%create(trim(new_mwrtm),rc=rc) + call OutFmt%write(OutCfg,rc=rc) + + variables => InCfg%get_variables() + var_iter = variables%begin() + do while (var_iter /= variables%end()) + vname => var_iter%key() + call MAPL_VarRead (InFmt,vname,tmp1) + call MAPL_VarWrite(OutFmt,vname,tmp1(f2g)) + call var_iter%next() + enddo + + call inFmt%close(rc=rc) + call OutFmt%close(rc=rc) + + deallocate(f2g,tmp1) + + end subroutine createLocalmwRTMRestart + + ! ******************************************************************** + + subroutine createLocalVegRestart(f2g_file, orig_veg, new_veg) + + implicit none + character(*),intent(in):: f2g_file + character(*),intent(in):: orig_veg + character(*),intent(in):: new_veg + integer :: istat + real,allocatable :: rity(:) + real,allocatable :: z2(:) + real,allocatable :: ascatz0(:) + real,allocatable :: tmp(:) + + integer :: N_catg,N_catf + integer,dimension(:),allocatable :: f2g + integer :: filetype + type(Netcdf4_FileFormatter) :: InFmt,OutFmt + type(FileMetadata) :: OutCfg + type(FileMetadata) :: InCfg + + type(StringVariableMap), pointer :: variables + type(StringVariableMapIterator) :: var_iter + character(len=:), pointer :: vname + integer :: rc + + call readsize(f2g_file, N_catg,N_catf) + if(N_catg == N_catf) return + allocate(f2g(N_catf)) + call readf2g(f2g_file, N_catf,f2g) + + allocate(rity(N_catg)) + allocate(z2(N_catg)) + allocate(ascatz0(N_catg)) + + call MAPL_NCIOGetFileType(orig_veg, filetype,rc=rc) + + if (filetype /=0) then + open(10,file=trim(orig_veg),form='unformatted',action='read',status='old',iostat=istat) + open(20,file=trim(new_veg),form='unformatted',action='write') + read(10) rity + read(10) z2 + read(10) ascatz0 + write(20) rity(f2g) + write(20) z2(f2g) + write(20) ascatz0(f2g) + + close(10) + close(20) + else + ! nc4 in and out file will also be nc4 + call InFmt%open(trim(orig_veg), pFIO_READ,rc=rc) + InCfg = InFmt%read(rc=rc) + OutCfg = InCfg + + call OutCfg%modify_dimension('tile', size(f2g), rc=rc) + + call OutFmt%create(trim(new_veg),rc=rc) + call OutFmt%write(OutCfg,rc=rc) + + variables => InCfg%get_variables() + var_iter = variables%begin() + allocate(tmp(N_catg)) + do while (var_iter /= variables%end()) + vname => var_iter%key() + call MAPL_VarRead (InFmt,vname,tmp) + call MAPL_VarWrite(OutFmt,vname,tmp(f2g)) + call var_iter%next() + enddo + + call inFmt%close(rc=rc) + call OutFmt%close(rc=rc) + deallocate(tmp) + endif + deallocate(f2g) + + end subroutine createLocalVegRestart + + ! ******************************************************************** + + subroutine correctEase(orig_ease,new_ease) + + ! This subroutine corrects for a bug that is present in some EASE *.til files + ! through at least Icarus-NLv4. + ! Affected files state "N_grid=1" in line 2 of the header, but the header still includes + ! three additional lines for a second grid, which throws off the canonical *.til reader + ! (subroutine LDAS_read_til_file()). + ! + ! This subroutine creates a second, corrected version of the *.til file that can be + ! read with the canonical reader during ldas_setup. + ! + ! - reichle, 2 Aug 2020 + + implicit none + character(*),intent(in) :: orig_ease + character(*),intent(in) :: new_ease + logical :: file_exist,is_oldEASE + integer :: i, N_tile, N_grid + character(len=256) :: tmpline + + inquire(file=trim(orig_ease),exist=file_exist) + if( .not. file_exist) stop (" no ease_tile_file") + + open(55,file=trim(orig_ease),action='read') + read(55,*) N_tile + read(55,*) N_grid + read(55,*) + read(55,*) + read(55,*) + read(55,'(A)') tmpline + close(55) + + is_oldEASE= .false. + if(N_grid==1 .and. index(tmpline,'OCEAN')/=0) is_oldEASE=.true. + + if( is_oldEASE) then + open(55,file=trim(orig_ease),action='read') + open(56,file=trim(new_ease),action='write') + do i =1,5 + read(55,'(A)')tmpline + write(56,'(A)')trim(tmpline) + enddo + read(55,*) + read(55,*) + read(55,*) + do i=1,N_tile + read(55,'(A)')tmpline + write(56,'(A)')trim(tmpline) + enddo + close(56) + close(55) + end if + end subroutine correctEase + + ! ******************************************************************** + ! + ! subroutine to optimize the domain setup (domain decomposition and processor layout) + ! + ! The domain is cut into N_proc stripes, where + ! - N_proc is the number of processors used for the model simulation (i.e., excl. OSERVER tasks) + ! - a stripe cuts across the entire north-south extent of the domain (lat-lon, EASE) or + ! across an entire face (cube-sphere) -- see below + ! - each stripe must be at least two grid cells thick + ! - each stripe should contain roughly the same number of land *tiles* + ! + ! For lat-lon and EASE grid tile spaces: + ! - cut into N_proc stripes of size N_lat-by-IMS(k), k=1:N_proc, IMS(k)>=2, + ! where IMS(k)=no. of grid cells in longitude direction ("thickness" of stripe k) is + ! written into file IMS.rc + ! For cube-sphere grid tile spaces: + ! - cut into N_proc stripes of size N_face-by-JMS(k), k=1:N_proc, JMS(k)>=2, + ! where JMS(k)=no. of grid cells ("thickness" of stripe k) is written + ! into file JMS.rc + ! + ! cube-sphere lat-lon/EASE + ! --------------------------------------------------- + ! NX: 1 N_proc + ! NY: N_proc 1 + ! JMS.rc IMS.rc + + subroutine optimize_latlon(fname_tilefile, N_proc_string, optimized_file, run_dir) + + implicit none + + character(*), intent(in) :: fname_tilefile ! file name (with path) of tile file (*.til) + character(*), intent(in) :: N_proc_string ! *string* w/ no. of processors (or tasks), excl. OSERVER tasks + character(*), intent(in) :: optimized_file + character(*), intent(in) :: run_dir + + ! local variables + integer :: N_proc + integer :: N_tile,N_lon,N_lat,N_grid + integer,allocatable :: landPosition(:) + integer,allocatable :: IMS(:),JMS(:) + integer,allocatable :: local_land(:) + integer :: total_land + integer :: n,typ,tmpint + real :: tmpreal + integer :: avg_land,n0,local + integer :: i,s,e,j,k,n1,n2 + logical :: file_exist + character(len=256):: tmpLine + character(len=128):: gridname + real :: rate,rates(60),maxf(60) + integer :: IMGLOB, JMGLOB + integer :: face(6),face_land(6) + logical :: forward + character(len=:), allocatable :: IMS_file, JMS_File + + ! ----------------------------- + + read (N_proc_string,*) N_proc ! input is string for historical reasons... + + ! get tile info + + inquire(file=trim(fname_tilefile),exist=file_exist) + if( .not. file_exist) stop ( "tile file does not exist") + + open (10, file=trim(fname_tilefile), form='formatted', action='read') + read (10,*) N_tile + read (10,*) N_grid ! some number (?) + read (10,*) gridname ! some string describing tile definition grid (?) + read (10,*) N_lon + read (10,*) n_lat + + if (index(gridname,"CF") /=0) then ! cube-sphere tile space + + IMGLOB = N_lon ! e.g., 180 for c180 + JMGLOB = N_lat ! e.g., 1080 for c180 (6*180=1080) + if(JMGLOB/6 /= IMGLOB) stop " wrong im, jm" + + allocate(landPosition(JMGLOB)) + landPosition = 0 + total_land = 0 + + if(N_grid==2) then + read (10,*) ! some string describing ocean grid (?) + read (10,*) ! # ocean grid cells in longitude direction (N_i_ocn) (?) + read (10,*) + endif + + do n = 1,N_tile + read (10,*) & + typ, & ! 1 + tmpreal, & ! 2 * + tmpreal, & ! 3 + tmpreal, & ! 4 + i , & ! 5 + j ! 6 + !tmpreal, & ! 7 + !tmpint, & ! 8 + !tmpreal, & ! 9 * + !tmpint, & ! 10 + !tmpreal, & ! 11 + !tmpint ! 12 * (previously "tile_id") + if(typ==MAPL_Land) then + total_land=total_land+1 + landPosition(j) = landPosition(j)+1 + endif + + ! assume all land tiles are at the beginning + ! UNSAFE ASSUMPTION! - reichle, 2 Aug 2020 + + if (typ/=MAPL_Land .and. typ/=MAPL_Land_ExcludeFromDomain) then ! exit if not land + + if (logit) then + write (logunit,*) 'WARNING: Encountered first non-land tile in *.til file.' + write (logunit,*) ' Stop reading *.til file under the assumption that' + write (logunit,*) ' land tiles are first in *.til file.' + write (logunit,*) ' This is NOT a safe assumption beyond Icarus-NLv[x] tile spaces!!' + end if + + exit ! assuming land comes first in the til file + + end if + + enddo + close(10) + + if(mod(N_proc,6) /=0) then + print*,"WARNING: ntasks should be adjusted to multiple of 6 for cubed-sphere grid :",N_proc + N_proc = N_proc-mod(N_proc,6) + endif + + print*, "total tiles", total_land + + if(sum(landPosition) /= total_land) print*, "wrong counting of land" + + do k=1,6 + n1 = (k-1)*IMGLOB+1 + n2 = k*IMGLOB + face_land(k) = sum(landPosition(n1:n2)) + face(k) = nint(1.0*face_land(k)/total_land * N_proc) + if ( face(k) == 0) face(k) = 1 + enddo + + ! now make sure sum(face) == N_proc + k=sum(face)-N_proc + + if (k < 0) then + do i=1, -k + n=minloc(face,DIM=1) + face(n) = face(n)+1 + enddo + else + do i = 1,k + n=maxloc(face,DIM=1) + face(n) = face(n)-1 + enddo + endif + + if (sum(face) /= N_proc) stop " wrong proc face" + + ! 2) each process should have average land tiles + + ALLOCATE(JMS(N_proc)) + allocate(local_land(N_Proc)) + JMS = 0 + local_land = 0 + + local = 0 + n0 = 0 + j = 0 + do k=1,6 + n1 = (k-1)*IMGLOB+1 + n2 = k*IMGLOB + + do i=1,60 + rates(i) = -0.3 + i*0.01 + enddo + + maxf=rms_cs(rates) + i=minloc(maxf,DIM=1) + rate = rates(i) + avg_land = ceiling(1.0*face_land(k)/face(k)) + avg_land = avg_land - nint(rate*avg_land) + + tmpint = 0 + j = j+face(k) + forward = .true. + do n = n1,n2 + tmpint=tmpint+landPosition(n) + if((local+1) == j .and. n < n2) cycle + if(n==n2) then + local = local + 1 + local_land(local)=tmpint + JMS(local)=n-n0 + tmpint=0 + n0=n + cycle + endif + if(tmpint .ge. avg_land) then + local = local + 1 + if (n-n0 == 1) forward =.true. + if (forward) then + local_land(local)=tmpint + JMS(local)=n-n0 + tmpint=0 + n0=n + forward = .false. + else + local_land(local)=tmpint - landPosition(n) + JMS(local)=n-1-n0 + tmpint=landPosition(n) + n0=n-1 + forward = .true. + endif + endif + enddo + local = j + enddo + if( sum(JMS) /= JMGLOB) then + print*, sum(JMS), JMGLOB + stop ("wrong cs-domain distribution in the first place") + endif + ! adjust JMS.rc to make sure each processor has at least 2 grid cells in j dimension + ! (i.e., each proc's subdomain must include at least 2 latitude stripes; + ! stripes of grid cells may or may not contain land tiles) + j = 1 + do k = 1,6 + n1 = j + n2 = j+face(k)-1 + do i = n1,n2 + if(JMS(i) == 0) then + n = maxloc(JMS(n1:n2),DIM=1) + JMS(i) = 1 + JMS(n+n1-1) = JMS(n+n1-1)-1 + endif + if(JMS(i) == 1) then + n = maxloc(JMS(n1:n2),DIM=1) + JMS(i) = 2 + JMS(n+n1-1) = JMS(n+n1-1)-1 + endif + enddo + j=j+face(k) + enddo + + print*,"land_distribute: ",local_land + print*, "JMS.rc", JMS + if( sum(JMS) /= JMGLOB) then + print*, sum(JMS), JMGLOB + stop ("wrong cs-domain distribution") + endif + tmpint = 0 + k = 0 + do n = 1, N_proc + tmpint= tmpint+JMS(n) + if( tmpint == IMGLOB) then + k=k+1 + tmpint = 0 + endif + enddo + + if( k /=6 ) stop ("one or more processes may accross the face") + + open(10,file=optimized_file,action='write') + write(10,'(A)') "GEOSldas.GRIDNAME: " // trim(gridname) + write(10,'(A)') "GEOSldas.GRID_TYPE: Cubed-Sphere" + write(10,'(A)') "GEOSldas.NF: 6" + write(10,'(A,I6)') "GEOSldas.IM_WORLD: ", IMGLOB + write(10,'(A)') "GEOSldas.LM: 1" + write(10,'(A,I5)') "NY: ",N_proc + write(10,'(A)') "NX: 1" + write(10,'(A)') "GEOSldas.JMS_FILE: JMS.rc" + close(10) + + JMS_file = trim(run_dir)//"/JMS.rc" + open(10,file=JMS_file ,action='write') + write(10,'(I5,I5)') N_proc, maxval(face) + do n=1,N_proc + write(10,'(I8)') JMS(n) + enddo + close(10) + + else + + allocate(IMS(N_Proc)) + allocate(local_land(N_Proc)) + IMS=0 + local_land = 0 + + ! NOTE: + ! There is a bug in at least some EASE *.til files through at least Icarus-NLv4. + ! Affected files state "N_grid=1" in line 2 of the header, but the header still includes + ! three additional lines for a second grid. + ! + ! The "else" block below corrects for this bug. + ! + ! Elsewhere, LDAS pre-processing corrects for this bug through subroutine correctEase() in + ! preprocess_LDAS.F90, which creates a second, corrected version of the *.til file during + ! ldas_setup. + ! + ! -reichle, 2 Aug 2020 + + if(N_grid==2) then + read (10,*) ! some string describing ocean grid (?) + read (10,*) ! # ocean grid cells in longitude direction (N_i_ocn) (?) + read (10,*) + read(10,'(A)') tmpLine + else + read(10,'(A)') tmpLine + if (index(tmpLine,"OCEAN") /=0) then + read (10,*) ! some string describing ocean grid (?) + read (10,*) ! # ocean grid cells in longitude direction (N_i_ocn) (?) + read (10,*) + read(10,'(A)') tmpLine + endif + endif + + if (index(gridname,'EASE') /=0) then + s=0 + e=N_lon-1 + else + s=1 + e=N_lon + endif + allocate(landPosition(s:e)) + + landPosition=0 + total_land= 0 + + ! 1) read through tile file, put the land tile into the N_lon of bucket + + read (tmpLine,*) & + typ, & ! 1 + tmpreal, & ! 2 * + tmpreal, & ! 3 + tmpreal, & ! 4 + i ! 5 + if(typ==MAPL_Land) then + total_land=total_land+1 + landPosition(i) = landPosition(i)+1 + endif + + do n = 2,N_tile + read (10,*) & + typ, & ! 1 + tmpreal, & ! 2 * + tmpreal, & ! 3 + tmpreal, & ! 4 + i ! 5 + !tmpint, & ! 6 + !tmpreal, & ! 7 + !tmpint, & ! 8 + !tmpreal, & ! 9 * + !tmpint, & ! 10 + !tmpreal, & ! 11 + !tmpint ! 12 * (previously "tile_id") + if(typ==MAPL_Land) then + total_land=total_land+1 + landPosition(i) = landPosition(i)+1 + endif + + ! assume all land tiles are at the beginning + ! UNSAFE ASSUMPTION! - reichle, 2 Aug 2020 + + if (typ/=MAPL_Land .and. typ/=MAPL_Land_ExcludeFromDomain) then ! exit if not land + + if (logit) then + write (logunit,*) 'WARNING: Encountered first non-land tile in *.til file.' + write (logunit,*) ' Stop reading *.til file under the assumption that' + write (logunit,*) ' land tiles are first in *.til file.' + write (logunit,*) ' This is NOT a safe assumption beyond Icarus-NLv[x] tile spaces!!' + end if + + exit ! assuming land comes first in the til file + + end if + + enddo + + close(10) + + if(sum(landPosition) /= total_land) print*, "wrong counting of land" + + do n=1,60 + rates(n) = -0.3 + (n-1)*0.01 + enddo + + maxf=rms(rates) + n=minloc(maxf,DIM=1) + rate = rates(n) + + ! 2) each process should have average land tiles + + avg_land = ceiling(1.0*total_land/N_proc) + print*,"avg_land",avg_land + + ! rate is used to readjust the avg_land + ! in case that the last processors don't have any land tiles, + ! we can increase ther rates + + avg_land = avg_land - nint(rate*avg_land) + print*,"re adjust the avg_land",avg_land + tmpint = 0 + local = 1 + n0 = s-1 + forward = .true. + do n=s,e + tmpint=tmpint+landPosition(n) + if(local == N_proc .and. n < e) cycle ! all lefteover goes to the last process + if( n==e ) then + local_land(local)=tmpint + IMS(local)=n-n0 + exit + endif + + if( tmpint .ge. avg_land ) then + if (forward .or. n-n0 == 1 ) then + local_land(local)=tmpint + IMS(local)=n-n0 + tmpint=0 + n0=n + forward = .false. + else + local_land(local) = tmpint - landPosition(n) + IMS(local)=(n-1)-n0 + tmpint= landPosition(n) + n0 = n-1 + forward = .true. + endif + local = local + 1 + endif + enddo + print*,"rms rate: ", rms(rate) + + print*,"land_distribute: ",local_land + + if( sum(local_land) /= total_land) stop ("wrong distribution") + if( sum(IMS) /= N_lon) stop ("wrong domain distribution") + + ! redistribute IMS and try to make it >=2 (may be impossible for large N_Proc) + do i = 1, N_proc + if(IMS(i) == 0) then + n = maxloc(IMS,DIM=1) + IMS(i) = 1 + IMS(n) = IMS(n)-1 + endif + if(IMS(i) == 1) then + n = maxloc(IMS,DIM=1) + IMS(i) = 2 + IMS(n) = IMS(n)-1 + endif + enddo + if( any(IMS <=1) ) stop ("Each processor must have at least 2 longitude stripes. Request fewer processors.") + + open(10,file=optimized_file, action='write') + write(10,'(A)') "GEOSldas.GRID_TYPE: LatLon" + write(10,'(A)') "GEOSldas.GRIDNAME: "//trim(gridname) + write(10,'(A)') "GEOSldas.LM: 1" + write(10,'(A)') "GEOSldas.POLE: PE" + write(10,'(A)') "GEOSldas.DATELINE: DE" + write(10,'(A,I6)') "GEOSldas.IM_WORLD: ", N_lon + write(10,'(A,I6)') "GEOSldas.JM_WORLD: ", N_lat + + write(10,'(A,I5)') "NX: ",N_proc + write(10,'(A)') "NY: 1" + + write(10,'(A)') "GEOSldas.IMS_FILE: IMS.rc" + close(10) + + IMS_file = trim(run_dir)//"/IMS.rc" + open(10,file=IMS_file,action='write') + write(10,'(I5)') N_proc + do n=1,N_proc + write(10,'(I8)') IMS(n) + enddo + close(10) + + endif + + contains + + ! *************************************************************************** + + elemental function rms(rates) result (f) + real :: f + real,intent(in) :: rates + integer :: tmpint,local + integer :: n0,proc,n + integer :: avg_land + integer,allocatable :: local_land(:) + logical :: forward + + allocate (local_land(N_proc)) + local_land = 0 + avg_land = ceiling(1.0*total_land/N_proc) + avg_land = avg_land -nint(rates*avg_land) + + forward = .true. + tmpint = 0 + local = 1 + n0 = s-1 + do n=s,e + tmpint=tmpint+landPosition(n) + if(local == N_proc .and. n < e) cycle ! all lefteover goes to the last process + if( n==e ) then + local_land(local)=tmpint + exit + endif + + if( tmpint .ge. avg_land ) then + if (forward .or. n-n0 == 1 ) then + local_land(local)=tmpint + tmpint=0 + n0=n + forward = .false. + else + local_land(local) = tmpint - landPosition(n) + tmpint= landPosition(n) + n0 = n-1 + forward = .true. + endif + local = local + 1 + endif + enddo + f = 0.0 + do proc = 1, N_proc + f =max(f,1.0*abs(local_land(proc)-avg_land)) + enddo + deallocate(local_land) + end function rms + + ! *************************************************************************** + + elemental function rms_cs(rates) result (f) + real :: f + real,intent(in) :: rates + integer :: tmpint,local + integer :: proc,n + integer :: avg_land + integer,allocatable :: local_land(:) + integer :: n1,n2,n0 + logical :: forward + + allocate (local_land(face(k))) + local_land = 0 + avg_land = ceiling(1.0*face_land(k)/face(k)) + avg_land = avg_land -nint(rates*avg_land) + if (avg_land <=0) then + f = face_land(k) + return + endif + + tmpint = 0 + local = 1 + + n1 = (k-1)*IMGLOB+1 + n2 = k*IMGLOB + tmpint = 0 + forward = .true. + n0 = n1-1 + do n = n1,n2 + tmpint=tmpint+landPosition(n) + if(local == face(k) .and. n < n2) cycle ! all lefteover goes to the last process + if(n==n2) then + local_land(local)=tmpint + local = local + 1 + cycle + endif + if(tmpint .ge. avg_land) then + if (n -n0 == 1) forward = .true. ! if only one step, should not got backward + if (forward) then + local_land(local)=tmpint + tmpint=0 + n0 = n + forward = .false. + else + local_land(local) = tmpint - landPosition(n) + tmpint = landPosition(n) + n0 = n-1 + forward = .true. + endif + local = local + 1 + endif + enddo + + f = 0.0 + do proc = 1, face(k) + ! punish for no land tiles + f =max(f,1.0*abs(local_land(proc)-avg_land)) + enddo + deallocate(local_land) + end function rms_cs + + end subroutine optimize_latlon + + ! ******************************************************************** + + subroutine convert_pert_rst(pfile_name,pfile_nc4,in_path,exp_id) + + implicit none + character(*),intent(in) :: pfile_name + character(*),intent(in) :: in_path + character(*),intent(in) :: exp_id + character(*),intent(in) :: pfile_nc4 + + integer :: N_catf,N_lon,N_lat,N_lonf,N_latf + integer :: N_force_pert,N_progn_pert + integer,pointer :: f2g(:) + + type(tile_coord_type), dimension(:), pointer :: tile_coord_f => null() + + type(grid_def_type) :: pert_grid_g + type(grid_def_type) :: pert_grid_f + integer :: RC,istat + integer,allocatable :: Pert_rseed(:) + real,allocatable :: Force_pert_ntrmdt_f(:,:,:) + real,allocatable :: Progn_pert_ntrmdt_f(:,:,:) + + call io_domain_files('r',in_path, trim(exp_id),N_catf,f2g,tile_coord_f,pert_grid_g,pert_grid_f,RC) + + N_lon = pert_grid_g%N_lon + N_lat = pert_grid_g%N_lat + N_lonf= pert_grid_f%N_lon + N_latf= pert_grid_f%N_lat + + call i_pert_ldas(RC) + + call o_pert_GEOSldas(rc) + + contains + + ! *************************************************************************** + + subroutine i_pert_ldas(rc) + integer,intent(inout),optional :: rc + + integer :: nrandseed_tmp + type(grid_def_type) :: pert_grid_f_tmp + character(len=*), parameter :: Iam = 'io_pert_rstrt' + integer :: k + real, allocatable :: real_tmp(:) + + open(10, file=pfile_name, convert='big_endian',form='unformatted', status='old', & + action='read', iostat=istat) + + ! one additional header line (as of 21 May 2010)!!! + + call io_grid_def_type( 'r', 10, pert_grid_f_tmp ) + + read (10) nrandseed_tmp, N_force_pert, N_progn_pert + + ! check whether entries in file match passed arguments + ! (check does *not* include *_pert_param!) + + if ( (nrandseed_tmp /= NRANDSEED) ) then ! .or. & + ! (N_force_pert_tmp /= N_force_pert) .or. & + ! (N_progn_pert_tmp /= N_progn_pert) ) then + stop 'pert.rstrt file not compatible (1)' + end if + + allocate(Pert_rseed(NRANDSEED)) + allocate(Force_pert_ntrmdt_f(N_lonf,N_latf, N_Force_pert)) + allocate(Progn_pert_ntrmdt_f(N_lonf,N_latf, N_Progn_pert)) + + if ( index(pert_grid_f%gridtype,'LatLon')/=0 .or. & + index(pert_grid_f%gridtype,'LATLON')/=0 .or. & + index(pert_grid_f%gridtype,'latlon')/=0 ) then + + if ( (pert_grid_f_tmp%N_lon /= pert_grid_f%N_lon) .or. & + (pert_grid_f_tmp%N_lat /= pert_grid_f%N_lat) .or. & + (abs(pert_grid_f_tmp%ll_lon - pert_grid_f%ll_lon) > 1e-4) .or. & + (abs(pert_grid_f_tmp%ll_lat - pert_grid_f%ll_lat) > 1e-4) .or. & + (abs(pert_grid_f_tmp%dlon - pert_grid_f%dlon) > 1e-4) .or. & + (abs(pert_grid_f_tmp%dlat - pert_grid_f%dlat) > 1e-4) ) then + stop 'pert.rstrt file not compatible (2)' + end if + + else + + if ( index(pert_grid_f_tmp%gridtype,pert_grid_f%gridtype)==0 .or. & + (pert_grid_f_tmp%N_lon /= pert_grid_f%N_lon) .or. & + (pert_grid_f_tmp%N_lat /= pert_grid_f%N_lat) .or. & + (pert_grid_f_tmp%i_offg /= pert_grid_f%i_offg) .or. & + (pert_grid_f_tmp%j_offg /= pert_grid_f%j_offg) ) then + stop 'pert.rstrt file not compatible (3)' + end if + + end if + + ! reading + read (10) Pert_rseed(:) + allocate(real_tmp(N_lonf*N_latf)) + do k=1,N_force_pert + !read (10) ((Force_pert_ntrmdt_f(i,j,k), i=1,N_lonf),j=1,N_latf) + read (10) real_tmp(:) + Force_pert_ntrmdt_f(:,:,k) = reshape(real_tmp,[N_lonf, N_latf]) + end do + + do k=1,N_progn_pert + !read (10) ((Progn_pert_ntrmdt_f(i,j,k), i=1,N_lonf),j=1,N_latf) + read (10) real_tmp(:) + Progn_pert_ntrmdt_f(:,:,k) = reshape(real_tmp,[N_lonf, N_latf]) + end do + + close(10) + deallocate(real_tmp) + rc = 0 + end subroutine i_pert_ldas + + ! ******************************************************************** + + subroutine o_pert_GEOSldas(rc) + integer,intent(inout) :: rc + integer :: NCFOutID, STATUS + integer :: seeddim,latdim, londim, Nforce,NProgn + integer :: dims(3), seedid,forceid,prognid + integer :: xstart, ystart + integer :: shuffle, deflate, deflate_level + real :: fill_value + + fill_value = -9999. !1.0e+15 + shuffle = 1 + deflate = 1 + deflate_level = 2 + + !1) create file + status = NF90_CREATE (trim(pfile_nc4), NF90_NOCLOBBER + NF90_HDF5, NCFOutID) + + !2) define dims + ! status = NF_DEF_DIM(NCFOutID, 'nprogn' , N_progn_pert_max, Nprogn) + status = NF90_DEF_DIM(NCFOutID, 'nseed' , NRANDSEED, seeddim) + status = NF90_DEF_DIM(NCFOutID, 'lat' , N_lat, latdim) + status = NF90_DEF_DIM(NCFOutID, 'lon' , N_lon, londim) + status = NF90_DEF_DIM(NCFOutID, 'nforce' , N_force_pert_max, Nforce) + status = NF90_DEF_DIM(NCFOutID, 'nprogn' , N_progn_pert_max, Nprogn) + + ! 3) define vars + status = NF90_DEF_VAR(NCFOutID,'pert_rseed',NF90_DOUBLE,seeddim,seedid) + status = NF90_PUT_ATT(NCFOutID, seedid, 'long_name','perturbations_rseed') + status = NF90_PUT_ATT(NCFOutID, seedid, 'units', '1') + + dims(1)= londim + dims(2)= latdim + dims(3)= Nforce + + status = NF90_DEF_VAR(NCFOutID,'fpert_ntrmdt',NF90_REAL, dims, forceid) + !status = nf90_def_var_deflate(NCFOutID, forceid, shuffle, deflate, deflate_level) + status = NF90_PUT_ATT(NCFOutID, forceid, 'long_name', 'force_pert_intermediate') + status = NF90_PUT_ATT(NCFOutID, forceid, 'units', '1') + status = nf90_put_att(NCFOutID, forceid, '_FillValue', fill_value) + dims(1)= londim + dims(2)= latdim + dims(3)= Nprogn + + status = NF90_DEF_VAR(NCFOutID, 'ppert_ntrmdt', NF90_REAL, dims, prognid) + !status = nf90_def_var_deflate(NCFOutID, prognid, shuffle, deflate, deflate_level) + status = NF90_PUT_ATT(NCFOutID, prognid, 'long_name', 'progn_pert_intermediate') + status = NF90_PUT_ATT(NCFOutID, prognid, 'units', '1') + status = nf90_put_att(NCFOutID, prognid, '_FillValue', fill_value) + + + status = nf90_enddef(NCFOutID) + ! 4) writing + + status= NF90_PUT_VAR(NCFOutID,seedid ,real(Pert_rseed,kind=8)) + + xstart = 1 + pert_grid_f%i_offg + ystart = 1 + pert_grid_f%j_offg + + ! will change to MAPL default 1.0e+15 + !do i = 1, N_lonf + ! do j = 1, N_latf + ! do k = 1, N_force_pert + ! if (Force_pert_ntrmdt_f(i,j,k) < -9998) Force_pert_ntrmdt_f(i,j,k)=fill_value + ! enddo + ! enddo + !enddo + + status= NF90_PUT_VAR(NCFOutID, forceid, Force_pert_ntrmdt_f, start=[xstart,ystart,1], & + count=[N_lonf,N_latf,N_force_pert]) + + ! will change to MAPL default 1.0e+15 + !do i = 1, N_lonf + ! do j = 1, N_latf + ! do k = 1, N_progn_pert + ! if (Progn_pert_ntrmdt_f(i,j,k) < -9998) Progn_pert_ntrmdt_f(i,j,k)=fill_value + ! enddo + ! enddo + !enddo + + status= NF90_PUT_VAR(NCFOutID, prognid, Progn_pert_ntrmdt_f, start=[xstart,ystart,1], & + count=[N_lonf,N_latf,N_progn_pert]) + + STATUS = NF90_CLOSE (NCFOutID) + + deallocate(Force_pert_ntrmdt_f, Progn_pert_ntrmdt_f) + + rc = status + end subroutine o_pert_GEOSldas + + end subroutine convert_pert_rst + + ! ************************************************************************************************** + + subroutine LDAS_read_til_file( tile_file, catch_file, tile_grid_g, tile_coord_land, f2g ) + + ! read land tile information from *.til file + ! + ! This subroutine: + ! - is the GEOSldas version of the LDASsa subroutine read_til_file() and + ! - was known as LDAS_read_land_tile() when in LDAS_TileCoordRoutines.F90. + ! + ! inputs: + ! tile_file : *.til tile definition file (full path + name) + ! catch_file : catchment.def file (full path + name) + ! + ! outputs: + ! tile_grid_g : parameters of tile definition grid + ! tile_coord_land : coordinates of tiles (see tile_coord_type), + ! implemented as pointer which is allocated in + ! this subroutine + ! NOTE: number of *land* tiles can be diagnosed with size(tile_coord) + ! optional: + ! f2g : the full domain id to the global id + ! + ! "tile_id" is no longer read from *.til file and is now set in this + ! subroutine to match order of tiles in *.til file + ! - reichle, 22 Aug 2013 + ! + ! improved documentation of bug in some EASE *.til files (header says N_grid=1 but has two grid defs) + ! and minor clean-up + ! - reichle, 2 Aug 2020 + ! + ! ------------------------------------------------------------- + + implicit none + + character(*), intent(in) :: tile_file + character(*), intent(in) :: catch_file + type(grid_def_type), intent(inout):: tile_grid_g + type(tile_coord_type), dimension(:), pointer :: tile_coord_land ! out + integer, dimension(:), optional, pointer :: f2g ! out + + ! locals + type(tile_coord_type), dimension(:), allocatable :: tile_coord + integer, dimension(:), allocatable :: f2g_tmp ! out + + real :: ease_cell_area + integer :: i, N_tile, N_grid,tmpint1, tmpint2, tmpint3, tmpint4 + integer :: i_indg_offset, j_indg_offset, col_order + integer :: N_tile_land, n_lon, n_lat + logical :: ease_grid + integer :: typ,k,fid + + character(256) :: tmpline + character(128) :: gridname + character(512) :: fname + + character(len=*), parameter :: Iam = 'LDAS_read_til_file' + + ! --------------------------------------------------------------- + + i_indg_offset = 0 + j_indg_offset = 0 + + ! read *.til file header + + if (logit) write (logunit,'(400A)') trim(Iam), '(): reading from ' // trim(tile_file) + + open (10, file=trim(tile_file), form='formatted', action='read') + + read (10,*) N_tile ! number of all tiles in *.til file, incl non-land types + read (10,*) N_grid + read (10,*) gridname + read (10,*) n_lon + read (10,*) n_lat + + ! NOTE: + ! There is a bug in at least some EASE *.til files through at least Icarus-NLv4. + ! Affected files state "N_grid=1" in line 2 of the header, but the header still includes + ! three additional lines for a second grid. + ! LDAS pre-processing corrects for this bug through subroutine correctEase() in + ! preprocess_LDAS.F90, which creates a second, corrected version of the *.til file during + ! ldas_setup. Here, this corrected *.til file is read! + + if(N_grid==2) then + read (10,*) ! some string describing ocean grid (?) + read (10,*) ! # ocean grid cells in longitude direction (N_i_ocn) (?) + read (10,*) ! # ocean grid cells in latitude direction (N_j_ocn) (?) + endif + + ease_grid = .false. + col_order = 0 + + call LDAS_create_grid_g( gridname, n_lon, n_lat, & + tile_grid_g, i_indg_offset, j_indg_offset, ease_cell_area ) + + if (index(tile_grid_g%gridtype,'EASE')/=0) ease_grid = .true. ! 'EASEv1' or 'EASEv2' + if (index(tile_grid_g%gridtype,'SiB2')/=0) col_order=1 ! old bcs + + allocate(tile_coord(N_tile)) + allocate(f2g_tmp(N_tile)) + + i = 0 + fid = 0 + + ! WJ notes: i and k are the same---global ids + ! fid --- num in simulation domain + + do k=1,N_tile + + read(10,'(A)') tmpline + read(tmpline,*) typ + + ! tile type "MAPL_Land_ExcludeFromDomain" identifies land tiles to exclude + ! when non-global domain is created + + if (typ==MAPL_Land .or. typ==MAPL_Land_ExcludeFromDomain) then ! all land + + i=i+1 + tile_coord(i)%tile_id = k + + ! now keep only tiles that are not excluded by way of MAPL_Land_ExcludeFromDomain + + if (typ==MAPL_Land) then + fid=fid+1 + f2g_tmp(fid) = k + end if + + ! Not sure ".or. N_grid==1" will always work in the following conditional. + ! Some Tripolar grid *.til files may have N_grid=1. + ! - reichle, 2 Aug 2020 + + if (ease_grid .or. N_grid==1) then + + ! EASE grid til file has fewer columns + ! (excludes "tile_id", "frac_pfaf", and "area") + + read (tmpline,*) & + tile_coord(i)%typ, & ! 1 + tile_coord(i)%pfaf, & ! 2 + tile_coord(i)%com_lon, & ! 3 + tile_coord(i)%com_lat, & ! 4 + tile_coord(i)%i_indg, & ! 5 + tile_coord(i)%j_indg, & ! 6 + tile_coord(i)%frac_cell ! 7 + + tile_coord(i)%frac_pfaf = nodata_generic + + ! compute area of tile in [km^2] (units convention in tile_coord structure) + + tile_coord(i)%area = ease_cell_area*tile_coord(i)%frac_cell/1000./1000. ! [km^2] + + else ! not ease grid + + if (col_order==1) then + + ! old "SiB2_V2" file format + + read (tmpline,*) & + tile_coord(i)%typ, & ! 1 + tile_coord(i)%pfaf, & ! 2 * + tile_coord(i)%com_lon, & ! 3 + tile_coord(i)%com_lat, & ! 4 + tile_coord(i)%i_indg, & ! 5 + tile_coord(i)%j_indg, & ! 6 + tile_coord(i)%frac_cell, & ! 7 + tmpint1, & ! 8 + tmpint2, & ! 9 * + tmpint3, & ! 10 + tile_coord(i)%frac_pfaf, & ! 11 + tmpint4, & ! 12 (previously "tile_id") + tile_coord(i)%area ! 13 + + else + + read (tmpline,*) & + tile_coord(i)%typ, & ! 1 + tile_coord(i)%area, & ! 2 * + tile_coord(i)%com_lon, & ! 3 + tile_coord(i)%com_lat, & ! 4 + tile_coord(i)%i_indg, & ! 5 + tile_coord(i)%j_indg, & ! 6 + tile_coord(i)%frac_cell, & ! 7 + tmpint1, & ! 8 + tile_coord(i)%pfaf, & ! 9 * + tmpint2, & ! 10 + tile_coord(i)%frac_pfaf, & ! 11 + tmpint3 ! 12 * (previously "tile_id") + + ! change units of area to [km^2] - 23 Sep 2010: fixed units, reichle + + tile_coord(i)%area = tile_coord(i)%area*MAPL_RADIUS*MAPL_RADIUS/1000./1000. + + end if ! col_order 1 + + end if ! (ease_grid) + + ! fix i_indg and j_indg such that they refer to a global grid + ! (see above) + + tile_coord(i)%i_indg = tile_coord(i)%i_indg + i_indg_offset + tile_coord(i)%j_indg = tile_coord(i)%j_indg + j_indg_offset + + else + + ! exit if not land + + if (logit) then + write (logunit,*) 'WARNING: Encountered first non-land tile in *.til file.' + write (logunit,*) ' Stop reading *.til file under the assumption that' + write (logunit,*) ' land tiles are first in *.til file.' + write (logunit,*) ' This is NOT a safe assumption beyond Icarus-NLv[x] tile spaces!!' + end if + + exit ! assuming land comes first in the til file + + endif + + end do + + close(10) + + N_tile_land=i + allocate(tile_coord_land(N_tile_land)) + tile_coord_land=tile_coord(1:N_tile_land) + ! pert_[x]_indg is not written into the tile_coord file and not needed in preprocessing + tile_coord_land%pert_i_indg = nint(nodata_generic) + tile_coord_land%pert_j_indg = nint(nodata_generic) + if(present(f2g)) then + allocate(f2g(fid)) + f2g = f2g_tmp(1:fid) + endif + + call read_catchment_def( catch_file, N_tile_land, tile_coord_land ) + + ! ---------------------------------------------------------------------- + ! + ! if elevation info is still needed, read *gridded* elevation data (check only first tile!) + + ! gridded elevation file is NOT available for EASE grids, where elevation information + ! is in catchment.def file + + if ( abs(tile_coord_land(1)%elev-nodata_generic)topo_DYN_ave.file') + open(10,file='topo_DYN_ave.file', action='read') + fname= '' + read(10,'(A)') fname + close(10) + call read_grid_elev( trim(fname), tile_grid_g, N_tile_land, tile_coord_land ) + + end if + + if ( abs(tile_coord_land(1)%elev-nodata_generic)tile_grid%dlon+latlon_tol) then + + if (logit) write (logunit,*) & + 'resetting min/maxlon in tile_id=', tile_coord(k)%tile_id + + ! "push" tile to the east of the dateline + + tile_coord(k)%min_lon = -180. + tile_coord(k)%max_lon = -180. + 0.5*tile_grid%dlon + + end if + + + ! check that com_lon is within tile definition grid cell + + if ( .not. ( & + (tile_coord(k)%com_lon >= this_minlon) .and. & + (tile_coord(k)%com_lon <= this_maxlon) ) ) then + if (logit) write (logunit,*) & + 'resetting com_lon in tile_id=', tile_coord(k)%tile_id + + tile_coord(k)%com_lon = & + 0.5*(tile_coord(k)%min_lon + tile_coord(k)%max_lon) + + end if + + end do + end if + + end subroutine fix_dateline_bug_in_tilecoord + + ! ********************************************************************** + + subroutine read_catchment_def( catchment_def_file, N_tile, tile_coord ) + + ! reichle, 17 May 2011: read elevation data if available + + ! format of catchment.def file + ! + ! Header line: N_tile + ! + ! Columns: tile_id, Pfaf, min_lon, max_lon, min_lat, max_lat, [elev] + ! + ! Elevation [m] is ONLY available for EASE grid tile definitions + + implicit none + + character(*), intent(in) :: catchment_def_file + + integer, intent(in) :: N_tile + + type(tile_coord_type), dimension(:), pointer :: tile_coord ! inout + + ! locals + + integer :: i, istat, tmpint1, sweep + + integer, dimension(N_tile) :: tmp_tileid, tmp_pfaf + + character(len=*), parameter :: Iam = 'read_catchment_def' + character(len=400) :: err_msg + + ! --------------------------------------------------------------- + + ! read file header + + if (logit) write (logunit,'(400A)') & + 'read_catchment_def(): reading from' // trim(catchment_def_file) + if (logit) write (logunit,*) + + ! sweep=1: Try reading 7 columns. If this fails, try again. + ! sweep=2: Read only 6 columns. + + do sweep=1,2 + + if (logit) write (logunit,*) 'starting sweep ', sweep + + open (10, file=trim(catchment_def_file), form='formatted', action='read') + + read (10,*) tmpint1 + + if (logit) write (logunit,*) 'file contains coordinates for ', tmpint1, ' tiles' + if (logit) write (logunit,*) + + if (N_tile/=tmpint1) then + print*,"need :", N_tile,"but have: ",tmpint1 + err_msg = 'tile_coord_file and catchment_def_file mismatch. (1)' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + end if + + do i=1,N_tile + + if (sweep==1) then + + ! read 7 columns, avoid using exact format specification + + read (10,*, iostat=istat) tmp_tileid(i), tmp_pfaf(i), & + tile_coord(i)%min_lon, & + tile_coord(i)%max_lon, & + tile_coord(i)%min_lat, & + tile_coord(i)%max_lat, & + tile_coord(i)%elev + + else + + ! read 6 columns, avoid using exact format specification + + read (10,*, iostat=istat) tmp_tileid(i), tmp_pfaf(i), & + tile_coord(i)%min_lon, & + tile_coord(i)%max_lon, & + tile_coord(i)%min_lat, & + tile_coord(i)%max_lat + + tile_coord(i)%elev = nodata_generic + + end if + + if (istat/=0) then ! read error + + if (sweep==1) then + + close(10,status='keep') + + if (logit) write (logunit,*) 'sweep 1 failed, trying sweep 2' + + exit ! exit sweep 1, try sweep 2 + + else + + call ldas_abort(LDAS_GENERIC_ERROR, Iam, 'sweep 2 failed') + + end if + + end if + + if (i==N_tile) then ! reached end of tile loop w/o read error + + close(10,status='keep') + + if (logit) write (logunit,*) 'sweep ', sweep, 'successfully completed' + + return + + end if + + end do ! loop through tiles + + end do ! loop through sweeps + + if ( any(tile_coord(1:N_tile)%tile_id/=tmp_tileid) .or. & + any(tile_coord(1:N_tile)%pfaf /=tmp_pfaf) ) then + + err_msg = 'tile_coord_file and catchment_def_file mismatch. (2)' + call ldas_abort(LDAS_GENERIC_ERROR, Iam, err_msg) + + end if + + end subroutine read_catchment_def + + ! ----------------------------------------------------------------- + + end subroutine LDAS_read_til_file + + ! ************************************************************************************ + +end module preprocess_ldas_routines + +! ====================== EOF ======================================================= + diff --git a/GEOSldas_App/process_hist.csh b/GEOSldas_App/process_hist.csh new file mode 100755 index 0000000..73d3b42 --- /dev/null +++ b/GEOSldas_App/process_hist.csh @@ -0,0 +1,78 @@ +#!/bin/csh -f + +## I am changed the CUBE/EASE logic +## if CUBE we produce 2D +## anything else, SMAP and other offline grids we produce tile space + +setenv LSM_CHOICE $1 +setenv AEROSOL_DEPOSITION $2 +setenv GRID $3 +setenv GRIDNAME $4 +setenv HISTRC $5 +setenv RUN_IRRIG $6 +setenv ASSIM $7 +setenv NENS $8 + +echo $GRIDNAME + +if($ASSIM == 1) then + sed -i 's|\#ASSIM|''|g' $HISTRC + sed -i '/^\#EASE/d' $HISTRC + sed -i '/^\#CUBE/d' $HISTRC +else + sed -i '/^\#ASSIM/d' $HISTRC +endif + +if($GRID == CUBE) then + sed -i '/^\#EASE/d' $HISTRC + sed -i 's|\#CUBE|''|g' $HISTRC + sed -i 's|GRIDNAME|'"$GRIDNAME"'|g' $HISTRC +else + sed -i '/^\#CUBE/d' $HISTRC + sed -i 's|\#EASE|''|g' $HISTRC + sed -i 's|GRIDNAME|'"$GRIDNAME"'|g' $HISTRC +endif + +if($LSM_CHOICE == 1) then + set GridComp = CATCH + sed -i '/^>>>HIST_CATCHCN<<>>HIST_CATCHCNCLM45<<>>HIST_CATCHCNCLM45<<>>HIST_CATCHCN<<>>HIST_CATCHCN<<>>HIST_CATCHCNCLM45<< 1) then + set GridComp = ENSAVG + sed -i 's|VEGDYN|'VEGDYN_e0000'|g' $HISTRC + sed -i 's|TP1|'TSOIL1TILE'|g' $HISTRC + sed -i 's|TP2|'TSOIL2TILE'|g' $HISTRC + sed -i 's|TP3|'TSOIL3TILE'|g' $HISTRC + sed -i 's|TP4|'TSOIL4TILE'|g' $HISTRC + sed -i 's|TP5|'TSOIL5TILE'|g' $HISTRC + sed -i 's|TP6|'TSOIL6TILE'|g' $HISTRC +# sed -i 's|DATAATM|'DATAATM0000'|g' $HISTRC +endif + +sed -i 's|GridComp|'$GridComp'|g' $HISTRC + +if($AEROSOL_DEPOSITION == 0) then + sed -i '/^>>>HIST_AEROSOL<<>>HIST_AEROSOL<<>>HIST_IRRIG<<>>HIST_IRRIG<< MY_exeinp.txt +# +# (2) Use the resource parameter settings below when editing MY_exeinp.txt +# +############################################################################## + +NUM_LDAS_ENSEMBLE: [NUM_ATM_ENSEMBLE] + +LADAS_COUPLING: 2 + +ADAS_EXPDIR: [full_path]/[ADAS_EXPDIR] + +MET_TAG: [ADAS_EXPID]__Nx+- +MET_PATH: [ADAS_EXPDIR]/atmens/ensdiag/mem + +MET_HINTERP: 0 + +LAND_ASSIM: YES + +LANDASSIM_DT: 10800 +LANDASSIM_T0: 013000 + +FIRST_ENS_ID: 1 + +ENSEMBLE_FORCING: YES + +JOB_SGMT: 00000000 060000 +NUM_SGMT: 1 + +################################# EOF ######################################## diff --git a/GEOSldas_App/sample_config_files/LADAS/exeinp.txt.Hy4dEnVar.central b/GEOSldas_App/sample_config_files/LADAS/exeinp.txt.Hy4dEnVar.central new file mode 100644 index 0000000..c30c880 --- /dev/null +++ b/GEOSldas_App/sample_config_files/LADAS/exeinp.txt.Hy4dEnVar.central @@ -0,0 +1,39 @@ +# +# Sample GEOSldas "exeinp" file for LADAS (central simulation) +# +# This sample is for the GEOSldas instance that is coupled with the central +# simulation component of the Hy4dEnVar ADAS: +# +# (1) Create exeinp template using: +# ldas_setup sample --exeinp > MY_exeinp.txt +# +# (2) Use the resource parameter settings below when editing MY_exeinp.txt +# +############################################################################## + +LADAS_COUPLING: 1 + +ADAS_EXPDIR: [full_path]/[ADAS_EXPDIR] + +MET_TAG: [ADAS_EXPID]__Nx+- +MET_PATH: ../../scratch +# option to use perturbed forcing created from central simulation and atm ensemble +# MET_PATH: [ADAS_EXPDIR]/atmens/rgdlfo + +MET_HINTERP: 0 + +LAND_ASSIM: YES + +LANDASSIM_DT: 10800 +LANDASSIM_T0: 013000 + +FIRST_ENS_ID: 1 + +ENSEMBLE_FORCING: NO +# option to use perturbed forcing created from central simulation and atm ensemble +# ENSEMBLE_FORCING: YES + +JOB_SGMT: 00000000 060000 +NUM_SGMT: 1 + +################################# EOF ######################################## diff --git a/GEOSldas_App/tile_bin2nc4.F90 b/GEOSldas_App/tile_bin2nc4.F90 new file mode 100644 index 0000000..df52c8b --- /dev/null +++ b/GEOSldas_App/tile_bin2nc4.F90 @@ -0,0 +1,454 @@ +PROGRAM tile_bin2nc4 + + implicit none + INCLUDE 'netcdf.inc' + + integer :: i,k, n, NTILES + integer :: NCFOutID, Vid, STATUS, CellID, TimID, nVars + character(256) :: Usage="tile_bin2nc4.x BINFILE DESCRIPTOR TILECOORD" + character(512) :: BINFILE, TILECOORD, DESCRIPTOR, arg(3) + character(128) :: MYNAME, BUF + integer, dimension(8) :: date_time_values + character (22) :: time_stamp + real, allocatable, dimension (:) :: lons, lats, var + integer, allocatable, dimension (:) :: tileid, i_index, j_index + integer :: myunit1, myunit2 + real :: undef + ! processing command line agruments + + I = command_argument_count() + + if( I /=3 ) then + print *, "Wrong Number of arguments: ", i + print *, trim(Usage) + stop + end if + + do n=1,I + call get_command_argument(n,arg(n)) + enddo + + call get_environment_variable ("MYNAME" ,MYNAME ) + read(arg(1),'(a)') BINFILE + read(arg(2),'(a)') DESCRIPTOR + read(arg(3),'(a)') TILECOORD + +! print *,MYNAME +! print *,trim(BINFILE) +! print *,trim(DESCRIPTOR) +! print *,trim(TILECOORD) + + ! reading TILECOORD + + open (newunit=myunit1, file = trim(TILECOORD), form = 'unformatted', action ='read') + read (myunit1) NTILES + allocate (lons (1:NTILES)) + allocate (lats (1:NTILES)) + allocate (tileid (1:NTILES)) + allocate (var (1:NTILES)) + allocate (i_index(1:NTILES)) + allocate (j_index(1:NTILES)) + + read (myunit1) tileid + read (myunit1) tileid + read (myunit1) tileid + read (myunit1) lons + read (myunit1) lats + read (myunit1) var + read (myunit1) var + read (myunit1) var + read (myunit1) var + read (myunit1) i_index + read (myunit1) j_index + + close (myunit1,status = 'keep') + + ! read binary and write NC4 + + open (newunit=myunit1, file = trim(DESCRIPTOR), form ='formatted', action = 'read') + nVars = 0 + + undef = 0.100000E+16 + k = 0 + do + read(myunit1, '(a)', iostat=status) buf + if (status /= 0) exit + k = k + 1 + if(buf(1:index(buf,' ') -1) == 'vars') then + i = index(buf,' ') + read (buf(i:),*, IOSTAT = n) nVars + endif + if(buf(1:index(buf,' ') -1) == 'undef') then + i = index(buf,' ') + read (buf(i:),*, IOSTAT = n) undef + endif + if(nVars /= 0) exit + + end do + + status = NF_CREATE (trim(BINFILE)//'.nc4', NF_NETCDF4, NCFOutID) + status = NF_DEF_DIM(NCFOutID, 'tile' , NTILES, CellID) + status = NF_DEF_DIM(NCFOutID, 'time' , NF_UNLIMITED, TimID) + + status = NF_DEF_VAR(NCFOutID, 'lon' , NF_FLOAT, 1 ,CellID, vid) + status = NF_PUT_ATT_TEXT(NCFOutID, vid, 'long_name', & + LEN_TRIM('longitude'), 'longitude') + status = NF_DEF_VAR(NCFOutID, 'lat' , NF_FLOAT, 1 ,CellID, vid) + status = NF_PUT_ATT_TEXT(NCFOutID, vid, 'long_name', & + LEN_TRIM('latitude'), 'latitude') + status = NF_DEF_VAR(NCFOutID, 'IG' , NF_INT, 1 ,CellID, vid) + status = NF_PUT_ATT_TEXT(NCFOutID, vid, 'long_name', & + LEN_TRIM('I_INDEX'), 'I_INDEX') + status = NF_DEF_VAR(NCFOutID, 'JG' , NF_INT, 1 ,CellID, vid) + status = NF_PUT_ATT_TEXT(NCFOutID, vid, 'long_name', & + LEN_TRIM('J_INDEX'), 'J_INDEX') + do n = 1, nVars + + read(myunit1, '(a)', iostat=status) buf + status = NF_DEF_VAR(NCFOutID,buf(1:index(buf,' ') -1) , NF_FLOAT, 2 ,(/CellID, TimID/), vid) + status = NF_PUT_ATT_TEXT(NCFOutID, vid, 'long_name', & + LEN_TRIM(getAttribute(buf(1:index(buf,' ') -1), LNAME = 1)), & + getAttribute(buf(1:index(buf,' ') -1), LNAME = 1)) + status = NF_PUT_ATT_TEXT(NCFOutID, vid, 'units', & + LEN_TRIM(getAttribute(buf(1:index(buf,' ') -1), UNT = 1)), & + getAttribute(buf(1:index(buf,' ') -1), UNT = 1)) + status = nf_put_att_real(NCFOutID, vid, '_FillValue',NF_FLOAT, 1, undef) + end do + + + call date_and_time(VALUES=date_time_values) + + write (time_stamp,'(i4.4,a1,i2.2,a1,i2.2,1x,a2,1x,i2.2,a1,i2.2,a1,i2.2)') & + date_time_values(1),'-',date_time_values(2),'-',date_time_values(3),'at', & + date_time_values(5),':',date_time_values(6),':',date_time_values(7) + + status = NF_PUT_ATT_TEXT(NCFOutID, NF_GLOBAL, 'CreatedBy', LEN_TRIM(MYNAME), & + trim(MYNAME)) + status = NF_PUT_ATT_TEXT(NCFOutID, NF_GLOBAL, 'Date' , LEN_TRIM(time_stamp),trim(time_stamp)) + + status = NF_ENDDEF(NCFOutID) + + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,'lon' ) ,(/1/),(/NTILES/),lons ) + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,'lat' ) ,(/1/),(/NTILES/),lats ) + status = NF_PUT_VARA_INT (NCFOutID,VarID(NCFOutID,'IG' ) ,(/1/),(/NTILES/),i_index ) + status = NF_PUT_VARA_INT (NCFOutID,VarID(NCFOutID,'JG' ) ,(/1/),(/NTILES/),j_index ) + + ! reading and writing + + open (newunit=myunit2, file = trim(BINFILE)//'.bin', form = 'unformatted', action = 'read') + + rewind (myunit1) + do i = 1, k + read(myunit1, '(a)', iostat=status) buf + end do + + do n = 1, nVars + read (myunit1, '(a)', iostat=status) buf + read (myunit2) var + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,buf(1:index(buf,' ') -1)) ,(/1,1/),(/NTILES,1/),var ) + end do + + STATUS = NF_CLOSE (NCFOutID) + close (myunit1) + close (myunit2) + + contains + + ! ---------------------------------------------------------------------- + + integer function VarID (NCFID, VNAME) + + integer, intent (in) :: NCFID + character(*), intent (in) :: VNAME + integer :: status + + STATUS = NF_INQ_VARID (NCFID, trim(VNAME) ,VarID) + IF (STATUS .NE. NF_NOERR) & + CALL HANDLE_ERR(STATUS, trim(VNAME)) + + end function VarID + + ! ----------------------------------------------------------------------- + + SUBROUTINE HANDLE_ERR(STATUS, Line) + + INTEGER, INTENT (IN) :: STATUS + CHARACTER(*), INTENT (IN) :: Line + + IF (STATUS .NE. NF_NOERR) THEN + PRINT *, trim(Line),': ',NF_STRERROR(STATUS) + STOP 'Stopped' + ENDIF + + END SUBROUTINE HANDLE_ERR + + ! *********************************************************************** + + FUNCTION getAttribute (SHORT_NAME, LNAME, UNT) result (str_atr) + + character(*), intent(in) :: SHORT_NAME + integer, intent (in), optional :: LNAME, UNT + character(128) :: str_atr, LONG_NAME, UNITS + + SELECT case (trim(SHORT_NAME)) + + ! For SM_L4 + ! reichle, 20 May 2020: verified SHORT_NAME and corrected UNITS to match SMAP L4_SM Product Specs; LONG_NAME (mostly) from GEOS_CatchGridComp.F90 + ! reichle, 14 Feb 2022: added "WATERTABLED" (now: "PEATCLSM_WATERLEVEL") and "FSWCHANGE" (now: "PEATCLSM_FSWCHANGE") + ! reichle, 21 Feb 2022: added "mwrtm_vegopacity" + + case ('sm_surface'); LONG_NAME = 'water_surface_layer'; UNITS = 'm3 m-3' + case ('sm_rootzone'); LONG_NAME = 'water_root_zone'; UNITS = 'm3 m-3' + case ('sm_profile'); LONG_NAME = 'water_ave_prof'; UNITS = 'm3 m-3' + case ('sm_surface_wetness'); LONG_NAME = 'surface_soil_wetness'; UNITS = '1' + case ('sm_rootzone_wetness'); LONG_NAME = 'root_zone_soil_wetness'; UNITS = '1' + case ('sm_profile_wetness'); LONG_NAME = 'ave_prof_soil_wetness'; UNITS = '1' + case ('surface_temp'); LONG_NAME = 'ave_catchment_temp_incl_snw'; UNITS = 'K' + case ('soil_temp_layer1'); LONG_NAME = 'soil_temperatures_layer_1'; UNITS = 'K' + case ('soil_temp_layer2'); LONG_NAME = 'soil_temperatures_layer_2'; UNITS = 'K' + case ('soil_temp_layer3'); LONG_NAME = 'soil_temperatures_layer_3'; UNITS = 'K' + case ('soil_temp_layer4'); LONG_NAME = 'soil_temperatures_layer_4'; UNITS = 'K' + case ('soil_temp_layer5'); LONG_NAME = 'soil_temperatures_layer_5'; UNITS = 'K' + case ('soil_temp_layer6'); LONG_NAME = 'soil_temperatures_layer_6'; UNITS = 'K' + case ('snow_mass'); LONG_NAME = 'snow_mass'; UNITS = 'kg m-2' + case ('snow_depth'); LONG_NAME = 'snow_depth_in_snow_covered_area'; UNITS = 'm' + case ('land_evapotranspiration_flux'); LONG_NAME = 'Evaporation_land'; UNITS = 'kg m-2 s-1' + case ('overland_runoff_flux'); LONG_NAME = 'runoff_flux'; UNITS = 'kg m-2 s-1' + case ('baseflow_flux'); LONG_NAME = 'baseflow_flux'; UNITS = 'kg m-2 s-1' + case ('snow_melt_flux'); LONG_NAME = 'Snowmelt_flux_land'; UNITS = 'kg m-2 s-1' + case ('soil_water_infiltration_flux'); LONG_NAME = 'rainwater_infiltration_flux'; UNITS = 'kg m-2 s-1' + case ('land_fraction_saturated'); LONG_NAME = 'fractional_area_of_saturated_zone'; UNITS = '1' + case ('land_fraction_unsaturated'); LONG_NAME = 'fractional_area_of_unsaturated_zone'; UNITS = '1' + case ('land_fraction_wilting'); LONG_NAME = 'fractional_area_of_wilting_zone'; UNITS = '1' + case ('land_fraction_snow_covered'); LONG_NAME = 'fractional_area_of_land_snowcover'; UNITS = '1' + case ('heat_flux_sensible'); LONG_NAME = 'Sensible_heat_flux_land'; UNITS = 'W m-2' + case ('heat_flux_latent'); LONG_NAME = 'Latent_heat_flux_land'; UNITS = 'W m-2' + case ('heat_flux_ground'); LONG_NAME = 'Ground_heating_land'; UNITS = 'W m-2' + case ('net_downward_shortwave_flux'); LONG_NAME = 'Net_shortwave_land'; UNITS = 'W m-2' + case ('net_downward_longwave_flux'); LONG_NAME = 'Net_longwave_land'; UNITS = 'W m-2' + case ('radiation_shortwave_downward_flux');LONG_NAME = 'Incident_shortwave_land'; UNITS = 'W m-2' + case ('radiation_longwave_absorbed_flux'); LONG_NAME = 'surface_absorbed_longwave_flux'; UNITS = 'W m-2' + case ('precipitation_total_surface_flux'); LONG_NAME = 'RainfSnowf'; UNITS = 'kg m-2 s-1' + case ('snowfall_surface_flux'); LONG_NAME = 'snowfall'; UNITS = 'kg m-2 s-1' + case ('surface_pressure'); LONG_NAME = 'surface_pressure'; UNITS = 'Pa' + case ('height_lowatmmodlay'); LONG_NAME = 'reference_height_for_Tair_Qair_Wind'; UNITS = 'm' + case ('temp_lowatmmodlay'); LONG_NAME = 'air_temperature_at_RefH'; UNITS = 'K' + case ('specific_humidity_lowatmmodlay'); LONG_NAME = 'specific_humidity_at_RefH'; UNITS = 'kg kg-1' + case ('windspeed_lowatmmodlay'); LONG_NAME = 'wind_speed_at_RefH'; UNITS = 'm s-1' + case ('vegetation_greenness_fraction'); LONG_NAME = 'greeness_fraction'; UNITS = '1' + case ('leaf_area_index'); LONG_NAME = 'leaf_area_index'; UNITS = 'm2 m-2' + case ('depth_to_water_table_from_surface_in_peat'); LONG_NAME = 'depth_to_water_table_from_surface_in_peat'; UNITS = 'm' + case ('free_surface_water_on_peat_flux'); LONG_NAME = 'change_in_free_surface_water_reservoir_on_peat'; UNITS = 'kg m-2 s-1' + case ('mwrtm_vegopacity'); LONG_NAME = 'Lband_microwave_vegopacity_normalized_with_cos_inc_angle'; UNITS = '1' + + ! additional defintions for SMAP Nature Run - reichle, 20 May 2020 + + case ('snow_temp_layer1'); LONG_NAME = 'temperature_top_snow_layer'; UNITS = 'K' + case ('tb_h'); LONG_NAME = 'brightness_temperature_land_1410MHz_40deg_Hpol'; UNITS = 'K' + case ('tb_v'); LONG_NAME = 'brightness_temperature_land_1410MHz_40deg_Vpol'; UNITS = 'K' + case ('TB_LAND_1410MHZ_40DEG_HPOL'); LONG_NAME = 'brightness_temperature_land_1410MHz_40deg_Hpol'; UNITS = 'K' + case ('TB_LAND_1410MHZ_40DEG_VPOL'); LONG_NAME = 'brightness_temperature_land_1410MHz_40deg_Vpol'; UNITS = 'K' + + ! Done for SM_L4 + + case ('Tair'); LONG_NAME = 'air_temperature_at_RefH'; UNITS = 'K' + case ('TA'); LONG_NAME = 'air_temperature_at_RefH'; UNITS = 'K' + case ('Qair'); LONG_NAME = 'specific_humidity_at_RefH'; UNITS = 'kg kg-1' + case ('QA'); LONG_NAME = 'specific_humidity_at_RefH'; UNITS = 'kg kg-1' + case ('LWdown'); LONG_NAME = 'surface_absorbed_longwave_flux'; UNITS = 'W m-2' + case ('LWDNSRF'); LONG_NAME = 'surface_absorbed_longwave_flux'; UNITS = 'W m-2' + case ('SWdown'); LONG_NAME = 'downward_shortwave_radiation'; UNITS = 'W m-2' + case ('Wind'); LONG_NAME = 'wind_speed_at_RefH'; UNITS = 'm s-1' + case ('UU'); LONG_NAME = 'wind_speed_at_RefH'; UNITS = 'm s-1' + case ('Psurf'); LONG_NAME = 'surface_pressure'; UNITS = 'Pa' + case ('PS'); LONG_NAME = 'surface_pressure'; UNITS = 'Pa' + case ('Rainf_C'); LONG_NAME = 'convective_rainfall'; UNITS = 'kg m-2 s-1' + case ('Rainf'); LONG_NAME = 'liquid_water_precipitation'; UNITS = 'kg m-2 s-1' + case ('Snowf'); LONG_NAME = 'total_snowfall'; UNITS = 'kg m-2 s-1' + case ('RainfSnowf'); LONG_NAME = 'RainfSnowf'; UNITS = 'kg m-2 s-1' + case ('SWnet'); LONG_NAME = 'downward_net_shortwave_radiation'; UNITS = 'W m-2' + case ('RefH'); LONG_NAME = 'reference_height_for_Tair_Qair_Wind'; UNITS = 'm' + case ('DZ'); LONG_NAME = 'reference_height_for_Tair_Qair_Wind'; UNITS = 'm' + case ('CATDEF'); LONG_NAME = 'catchment_deficit'; UNITS = 'kg m-2' + case ('RZEXC'); LONG_NAME = 'root_zone_excess'; UNITS = 'kg m-2' + case ('SRFEXC'); LONG_NAME = 'surface_excess'; UNITS = 'kg m-2' + case ('WESNN1'); LONG_NAME = 'snow_mass_layer_1'; UNITS = 'kg m-2' + case ('WESNN2'); LONG_NAME = 'snow_mass_layer_2'; UNITS = 'kg m-2' + case ('WESNN3'); LONG_NAME = 'snow_mass_layer_3'; UNITS = 'kg m-2' + case ('HTSNNN1'); LONG_NAME = 'heat_content_snow_layer_1'; UNITS = 'J m-2' + case ('HTSNNN2'); LONG_NAME = 'heat_content_snow_layer_2'; UNITS = 'J m-2' + case ('HTSNNN3'); LONG_NAME = 'heat_content_snow_layer_3'; UNITS = 'J m-2' + case ('SNDZN1'); LONG_NAME = 'snow_depth_layer_1'; UNITS = 'm' + case ('SNDZN2'); LONG_NAME = 'snow_depth_layer_2'; UNITS = 'm' + case ('SNDZN3'); LONG_NAME = 'snow_depth_layer_3'; UNITS = 'm' + case ('FICE1'); LONG_NAME = 'snow_frozen_fraction_layer_1'; UNITS = '1' + case ('FICE2'); LONG_NAME = 'snow_frozen_fraction_layer_2'; UNITS = '1' + case ('FICE3'); LONG_NAME = 'snow_frozen_fraction_layer_3'; UNITS = '1' + case ('ALBVR'); LONG_NAME = 'surface_reflectivity_for_visible_beam'; UNITS = '1' + case ('ALBVF'); LONG_NAME = 'surface_reflectivity_for_visible_diffuse'; UNITS = '1' + case ('ALBNR'); LONG_NAME = 'surface_reflectivity_for_near_infared_beam'; UNITS = '1' + case ('ALBNF'); LONG_NAME = 'surface_reflectivity_for_near_infrared_diffuse'; UNITS = '1' + case ('HLWUP'); LONG_NAME = 'surface_emitted_longwave_flux'; UNITS = 'W m-2' + case ('GWETPROF'); LONG_NAME = 'ave_prof_soil_wetness'; UNITS = '1' + case ('GWETROOT'); LONG_NAME = 'root_zone_soil_wetness'; UNITS = '1' + case ('GWETTOP'); LONG_NAME = 'surface_soil_wetness'; UNITS = '1' + case ('PRMC'); LONG_NAME = 'water_ave_prof'; UNITS = 'm3 m-3' + case ('RZMC'); LONG_NAME = 'water_root_zone'; UNITS = 'm3 m-3' + case ('SFMC'); LONG_NAME = 'water_surface_layer'; UNITS = 'm3 m-3' + case ('TPSNOW'); LONG_NAME = 'temperature_top_snow_layer'; UNITS = 'K' + case ('TUNST'); LONG_NAME = 'temperature_unsaturated_zone'; UNITS = 'K' + case ('TSAT'); LONG_NAME = 'temperature_saturated_zone'; UNITS = 'K' + case ('TWLT'); LONG_NAME = 'temperature_wilted_zone'; UNITS = 'K' + case ('TSURF'); LONG_NAME = 'ave_catchment_temp_incl_snw'; UNITS = 'K' + case ('TPSURF'); LONG_NAME = 'ave_catchment_temp_incl_snw'; UNITS = 'K' + case ('GRN'); LONG_NAME = 'greeness_fraction'; UNITS = '1' + case ('LAI'); LONG_NAME = 'leaf_area_index'; UNITS = '1' + case ('TP1'); LONG_NAME = 'soil_temperatures_layer_1'; UNITS = 'K' ! units now K, rreichle & borescan, 6 Nov 2020 + case ('TP2'); LONG_NAME = 'soil_temperatures_layer_2'; UNITS = 'K' ! units now K, rreichle & borescan, 6 Nov 2020 + case ('TP3'); LONG_NAME = 'soil_temperatures_layer_3'; UNITS = 'K' ! units now K, rreichle & borescan, 6 Nov 2020 + case ('TP4'); LONG_NAME = 'soil_temperatures_layer_4'; UNITS = 'K' ! units now K, rreichle & borescan, 6 Nov 2020 + case ('TP5'); LONG_NAME = 'soil_temperatures_layer_5'; UNITS = 'K' ! units now K, rreichle & borescan, 6 Nov 2020 + case ('TP6'); LONG_NAME = 'soil_temperatures_layer_6'; UNITS = 'K' ! units now K, rreichle & borescan, 6 Nov 2020 + case ('PRECTOTLAND');LONG_NAME = 'Total_precipitation_land'; UNITS = 'kg m-2 s-1' + case ('PRECSNOLAND');LONG_NAME = 'snowfall_land'; UNITS = 'kg m-2 s-1' + case ('SNOWMASS') ;LONG_NAME = 'snow_mass'; UNITS = 'kg m-2' + case ('SNOMAS') ;LONG_NAME = 'snow_mass'; UNITS = 'kg m-2' + case ('SNO'); LONG_NAME = 'snowfall'; UNITS = 'kg m-2 s-1' + case ('SNODP'); LONG_NAME = 'snow_depth_in_snow_covered_area'; UNITS = 'm' + case ('EVPSOIL'); LONG_NAME = 'baresoil_evap_energy_flux'; UNITS = 'W m-2' + case ('EVPTRNS'); LONG_NAME = 'transpiration_energy_flux'; UNITS = 'W m-2' + case ('EVPINTR'); LONG_NAME = 'interception_loss_energy_flux'; UNITS = 'W m-2' + case ('EVPSBLN'); LONG_NAME = 'snow_ice_evaporation_energy_flux'; UNITS = 'W m-2' + case ('RUNOFF'); LONG_NAME = 'runoff_flux'; UNITS = 'kg m-2 s-1' + case ('BASEFLOW'); LONG_NAME = 'baseflow_flux'; UNITS = 'kg m-2 s-1' + case ('SMLAND'); LONG_NAME = 'Snowmelt_flux_land'; UNITS = 'kg m-2 s-1' + case ('QINFIL'); LONG_NAME = 'rainwater_infiltration_flux'; UNITS = 'kg m-2 s-1' + case ('FRUNST'); LONG_NAME = 'fractional_area_of_unsaturated_zone'; UNITS = '1' + case ('FRSAT'); LONG_NAME = 'fractional_area_of_saturated_zone'; UNITS = '1' + case ('FRSNO'); LONG_NAME = 'fractional_area_of_land_snowcover'; UNITS = '1' + case ('FRWLT'); LONG_NAME = 'fractional_area_of_wilting_zone'; UNITS = '1' + case ('PARDFLAND'); LONG_NAME = 'surface_downwelling_par_diffuse_flux'; UNITS = 'W m-2' + case ('PARDRLAND'); LONG_NAME = 'surface_downwelling_par_beam_flux'; UNITS = 'W m-2' + case ('SHLAND'); LONG_NAME = 'Sensible_heat_flux_land'; UNITS = 'W m-2' + case ('LHLAND'); LONG_NAME = 'Latent_heat_flux_land'; UNITS = 'W m-2' + case ('EVLAND'); LONG_NAME = 'Evaporation_land'; UNITS = 'kg m-2 s-1' + case ('LWLAND'); LONG_NAME = 'Net_longwave_land'; UNITS = 'W m-2' + case ('SWLAND'); LONG_NAME = 'Net_shortwave_land'; UNITS = 'W m-2' + case ('SWDOWNLAND'); LONG_NAME = 'Incident_shortwave_land'; UNITS = 'W m-2' + case ('GHLAND'); LONG_NAME = 'Ground_heating_land'; UNITS = 'W m-2' + case ('TWLAND'); LONG_NAME = 'Avail_water_storage_land'; UNITS = 'kg m-2' + case ('TSLAND'); LONG_NAME = 'Total_snow_storage_land'; UNITS = 'kg m-2' + case ('TELAND'); LONG_NAME = 'Total_energy_storage_land'; UNITS = 'J m-2' + case ('WCHANGE'); LONG_NAME = 'rate_of_change_of_total_land_water'; UNITS = 'kg m-2 s-1' + case ('ECHANGE'); LONG_NAME = 'rate_of_change_of_total_land_energy'; UNITS = 'W m-2' + case ('SPLAND'); LONG_NAME = 'rate_of_spurious_land_energy_source'; UNITS = 'W m-2' + case ('SPWATR'); LONG_NAME = 'rate_of_spurious_land_water_source'; UNITS = 'kg m-2 s-1' + case ('SPSNOW'); LONG_NAME = 'rate_of_spurious_snow_energy'; UNITS = 'W m-2' + case ('PEATCLSM_WATERLEVEL');LONG_NAME = 'depth_to_water_table_from_surface_in_peat'; UNITS = 'm' + case ('PEATCLSM_FSWCHANGE'); LONG_NAME = 'change_in_free_surface_water_reservoir_on_peat'; UNITS = 'kg m-2 s-1' + case ('CNLAI'); LONG_NAME = 'CN_exposed_leaf-area_index'; UNITS = '1' + case ('CNTLAI'); LONG_NAME = 'CN_total_leaf-area_index'; UNITS = '1' + case ('CNSAI'); LONG_NAME = 'CN_exposed_stem-area_index'; UNITS = '1' + case ('CNTOTC'); LONG_NAME = 'CN_total_carbon'; UNITS = 'kg m-2' + case ('CNVEGC'); LONG_NAME = 'CN_total_vegetation_carbon'; UNITS = 'kg m-2' + case ('CNROOT'); LONG_NAME = 'CN_total_root_carbon'; UNITS = 'kg m-2' + case ('CNNPP'); LONG_NAME = 'CN_net_primary_production'; UNITS = 'kg m-2 s-1' + case ('CNGPP'); LONG_NAME = 'CN_gross_primary_production'; UNITS = 'kg m-2 s-1' + case ('CNSR'); LONG_NAME = 'CN_total_soil_respiration'; UNITS = 'kg m-2 s-1' + case ('CNNEE'); LONG_NAME = 'CN_net_ecosystem_exchange'; UNITS = 'kg m-2 s-1' + case ('CNXSMR'); LONG_NAME = 'abstract_C_pool_to_meet_excess_MR_demand'; UNITS = 'kg m-2' + case ('CNADD'); LONG_NAME = 'CN_added_to_maintain_positive_C'; UNITS = 'kg m-2 s-1' + case ('PARABS'); LONG_NAME = 'absorbed_PAR'; UNITS = 'W m-2' + case ('PARINC'); LONG_NAME = 'incident_PAR'; UNITS = 'W m-2' + case ('SCSAT'); LONG_NAME = 'saturated_stomatal_conductance'; UNITS = 'm s-1' + case ('SCUNS'); LONG_NAME = 'unstressed_stomatal_conductance'; UNITS = 'm s-1' + case ('BTRAN'); LONG_NAME = 'transpiration coefficient'; UNITS = '1' + case ('SIF'); LONG_NAME = 'solar induced fluorescence'; UNITS = 'umol m-2 sm s-1' + case ('CLOSS'); LONG_NAME = 'CN_carbon_loss_to_fire'; UNITS = 'kg m-2 s-1' + case ('BURN'); LONG_NAME = 'CN_fractional_area_burn_rate'; UNITS = 's-1' + case ('FSEL'); LONG_NAME = 'fire season length'; UNITS = 'days' + case ('EVPSNO'); LONG_NAME = 'snowpack_evaporation_energy_flux'; UNITS = 'W m-2' + case ('GHTSKIN'); LONG_NAME = 'Ground_heating_skin_temp'; UNITS = 'W m-2' + case ('WAT10CM'); LONG_NAME = 'soil moisture in Upper 10cm'; UNITS = 'kg m-2' + case ('WATSOI'); LONG_NAME = 'totoal soil moisture'; UNITS = 'kg m-2' + case ('ICESOI'); LONG_NAME = 'soil frozen water content'; UNITS = 'kg m-2' + case ('RMELTDU001'); LONG_NAME = 'flushed_out_dust_mass_flux_from_the_bottom_layer_bin_1'; UNITS = 'kg m-2 s-1' + case ('RMELTDU002'); LONG_NAME = 'flushed_out_dust_mass_flux_from_the_bottom_layer_bin_2'; UNITS = 'kg m-2 s-1' + case ('RMELTDU003'); LONG_NAME = 'flushed_out_dust_mass_flux_from_the_bottom_layer_bin_3'; UNITS = 'kg m-2 s-1' + case ('RMELTDU004'); LONG_NAME = 'flushed_out_dust_mass_flux_from_the_bottom_layer_bin_4'; UNITS = 'kg m-2 s-1' + case ('RMELTDU005'); LONG_NAME = 'flushed_out_dust_mass_flux_from_the_bottom_layer_bin_5'; UNITS = 'kg m-2 s-1' + case ('RMELTBC001'); LONG_NAME = 'flushed_out_black_carbon_mass_flux_from_the_bottom_layer_bin_1'; UNITS = 'kg m-2 s-1' + case ('RMELTBC002'); LONG_NAME = 'flushed_out_black_carbon_mass_flux_from_the_bottom_layer_bin_2'; UNITS = 'kg m-2 s-1' + case ('RMELTOC001'); LONG_NAME = 'flushed_out_organic_carbon_mass_flux_from_the_bottom_layer_bin_1'; UNITS = 'kg m-2 s-1' + case ('RMELTOC002'); LONG_NAME = 'flushed_out_organic_carbon_mass_flux_from_the_bottom_layer_bin_2'; UNITS = 'kg m-2 s-1' + + ! land assimilation increments for Catchment prognostic variables in coupled land-atmosphere DAS (#sqz 2020-01) + + case ('TCFSAT_INCR'); LONG_NAME = 'increment_canopy_temperature_saturated_zone'; UNITS = 'K' + case ('TCFTRN_INCR'); LONG_NAME = 'increment_canopy_temperature_transition_zone'; UNITS = 'K' + case ('TCFWLT_INCR'); LONG_NAME = 'increment_canopy_temperature_wilting_zone'; UNITS = 'K' + case ('QCFSAT_INCR'); LONG_NAME = 'increment_canopy_specific_humidity_saturated_zone'; UNITS = 'kg kg-1' + case ('QCFTRN_INCR'); LONG_NAME = 'increment_canopy_specific_humidity_transition_zone'; UNITS = 'kg kg-1' + case ('QCFWLT_INCR'); LONG_NAME = 'increment_canopy_specific_humidity_wilting_zone'; UNITS = 'kg kg-1' + case ('CAPAC_INCR'); LONG_NAME = 'increment_interception_reservoir_capac'; UNITS = 'kg m-2' + case ('CATDEF_INCR'); LONG_NAME = 'increment_catchment_deficit'; UNITS = 'kg m-2' + case ('RZEXC_INCR'); LONG_NAME = 'increment_root_zone_excess'; UNITS = 'kg m-2' + case ('SRFEXC_INCR'); LONG_NAME = 'increment_surface_excess'; UNITS = 'kg m-2' + case ('GHTCNT1_INCR'); LONG_NAME = 'increment_soil_heat_content_layer_1'; UNITS = 'J m-2' + case ('GHTCNT2_INCR'); LONG_NAME = 'increment_soil_heat_content_layer_2'; UNITS = 'J m-2' + case ('GHTCNT3_INCR'); LONG_NAME = 'increment_soil_heat_content_layer_3'; UNITS = 'J m-2' + case ('GHTCNT4_INCR'); LONG_NAME = 'increment_soil_heat_content_layer_4'; UNITS = 'J m-2' + case ('GHTCNT5_INCR'); LONG_NAME = 'increment_soil_heat_content_layer_5'; UNITS = 'J m-2' + case ('GHTCNT6_INCR'); LONG_NAME = 'increment_soil_heat_content_layer_6'; UNITS = 'J m-2' + case ('WESNN1_INCR'); LONG_NAME = 'increment_snow_mass_layer_1'; UNITS = 'kg m-2' + case ('WESNN2_INCR'); LONG_NAME = 'increment_snow_mass_layer_2'; UNITS = 'kg m-2' + case ('WESNN3_INCR'); LONG_NAME = 'increment_snow_mass_layer_3'; UNITS = 'kg m-2' + case ('HTSNNN1_INCR'); LONG_NAME = 'increment_heat_content_snow_layer_1'; UNITS = 'J m-2' + case ('HTSNNN2_INCR'); LONG_NAME = 'increment_heat_content_snow_layer_2'; UNITS = 'J m-2' + case ('HTSNNN3_INCR'); LONG_NAME = 'increment_heat_content_snow_layer_3'; UNITS = 'J m-2' + case ('SNDZN1_INCR'); LONG_NAME = 'increment_snow_depth_layer_1'; UNITS = 'm' + case ('SNDZN2_INCR'); LONG_NAME = 'increment_snow_depth_layer_2'; UNITS = 'm' + case ('SNDZN3_INCR'); LONG_NAME = 'increment_snow_depth_layer_3'; UNITS = 'm' + + ! land assimilation forecast and analysis for Catchment model diagnostics + + case ('SFMC_FCST'); LONG_NAME = 'soil_moisture_surface_forecast'; UNITS = 'm3 m-3' + case ('RZMC_FCST'); LONG_NAME = 'soil_moisture_rootzone_forecast'; UNITS = 'm3 m-3' + case ('PRMC_FCST'); LONG_NAME = 'soil_moisture_profile_forecast'; UNITS = 'm3 m-3' + case ('TSURF_FCST'); LONG_NAME = 'ave_catchment_temp_incl_snw_forecast'; UNITS = 'K' + case ('TSOIL1_FCST'); LONG_NAME = 'soil_temperatures_layer_1_forecast'; UNITS = 'K' + + case ('SFMC_FCST_ENSSTD'); LONG_NAME = 'soil_moisture_surface_forecast_ensstd'; UNITS = 'm3 m-3' + case ('RZMC_FCST_ENSSTD'); LONG_NAME = 'soil_moisture_rootzone_forecast_ensstd'; UNITS = 'm3 m-3' + case ('PRMC_FCST_ENSSTD'); LONG_NAME = 'soil_moisture_profile_forecast_ensstd'; UNITS = 'm3 m-3' + case ('TSURF_FCST_ENSSTD'); LONG_NAME = 'ave_catchment_temp_incl_snw_forecast_ensstd'; UNITS = 'K' + case ('TSOIL1_FCST_ENSSTD'); LONG_NAME = 'soil_temperatures_layer_1_forecast_ensstd'; UNITS = 'K' + + case ('SFMC_ANA'); LONG_NAME = 'soil_moisture_surface_analysis'; UNITS = 'm3 m-3' + case ('RZMC_ANA'); LONG_NAME = 'soil_moisture_rootzone_analysis'; UNITS = 'm3 m-3' + case ('PRMC_ANA'); LONG_NAME = 'soil_moisture_profile_analysis'; UNITS = 'm3 m-3' + case ('TSURF_ANA'); LONG_NAME = 'ave_catchment_temp_incl_snw_analysis'; UNITS = 'K' + case ('TSOIL1_ANA'); LONG_NAME = 'soil_temperatures_layer_1_analysis'; UNITS = 'K' + + case ('SFMC_ANA_ENSSTD'); LONG_NAME = 'soil_moisture_surface_analysis_ensstd'; UNITS = 'm3 m-3' + case ('RZMC_ANA_ENSSTD'); LONG_NAME = 'soil_moisture_rootzone_analysis_ensstd'; UNITS = 'm3 m-3' + case ('PRMC_ANA_ENSSTD'); LONG_NAME = 'soil_moisture_profile_analysis_ensstd'; UNITS = 'm3 m-3' + case ('TSURF_ANA_ENSSTD'); LONG_NAME = 'ave_catchment_temp_incl_snw_analysis_ensstd'; UNITS = 'K' + case ('TSOIL1_ANA_ENSSTD'); LONG_NAME = 'soil_temperatures_layer_1_analysis_ensstd'; UNITS = 'K' + + ! other land assimilation fields + + case ('MWRTM_VEGOPACITY'); LONG_NAME = 'Lband_microwave_vegopacity_normalized_with_cos_inc_angle'; UNITS = '1' + + ! default LONG_NAME and UNITS for nc4 files created by tile_bin2nc4.F90 (used for any SHORT_NAME not listed above): + + case default; LONG_NAME = 'not defined in tile_bin2nc4.F90'; UNITS = 'not defined in tile_bin2nc4.F90'; + + end select + + if (present(LNAME)) str_atr = trim (LONG_NAME) + if (present(UNT)) str_atr = trim (UNITS ) + + END FUNCTION getAttribute + +END PROGRAM tile_bin2nc4 diff --git a/GEOSldas_App/util/config/Create_ccorr_cat_progn_default.m b/GEOSldas_App/util/config/Create_ccorr_cat_progn_default.m new file mode 100644 index 0000000..c35942d --- /dev/null +++ b/GEOSldas_App/util/config/Create_ccorr_cat_progn_default.m @@ -0,0 +1,57 @@ + +% create string array to initialize ccorr_cat_progn in default namelist file +% +% reichle, 1 Jun 2005 +% reichle, 6 Dec 2013 - updated for new "progn_pert_type" + +sm={ +'catdef' +'rzexc' +'srfexc' +'snow' +'tc' +'ght(1)' +'ght(2)' +'ght(3)' +'ght(4)' +'ght(5)' +'ght(6)' } + +k=0; + +for i=1:length(sm) + for j=(i+1):length(sm) + + k=k+1; + + tmpstr = [ 'ccorr_progn_pert%', sm{i}, '%', sm{j}]; + + os{k} = tmpstr; + + es{k} = ' = 0.'; + + if (j==25) + + k=k+1; + + os{k} = ''; + es{k} = ''; + + end + + end +end + +sa = [ char(os) char(es) ]; + +diary tmp.txt + +disp(sa) + +diary off + +% ========= EOF ==================================== + + + + diff --git a/GEOSldas_App/util/config/generate_catchincr_hist.py b/GEOSldas_App/util/config/generate_catchincr_hist.py new file mode 100755 index 0000000..a692645 --- /dev/null +++ b/GEOSldas_App/util/config/generate_catchincr_hist.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +# +# This code works with the python version loaded by g5_modules associated with GEOSldas v17.13.1: +# python/GEOSpyD/Min4.11.0_py3.9_AND_Min4.8.3_py2.7 +# +# This script generates a sample HISTORY.rc file for GEOSldas to write Catchment +# model analysis increments in ensemble space, as needed in the weakly-coupled +# Hybrid-4DEnVar land-atm DAS (LADAS). + +import os +import glob +import subprocess as sp + +# ------------------------------------------------------------------ +# +# specify number of ensemble members here: + +nens = 32 + +# ------------------------------------------------------------------ +# +# some definitions of text elements in HISTORY.rc file + +heads = """ +# +# Sample GEOSldas HISTORY.rc file for LADAS (atm ensemble) +# +# - This sample HISTORY.rc is for the GEOSldas instance that is weakly coupled with the +# atmospheric ensemble component of the Hybrid-4DEnVar ADAS (ADASens). +# +# - The sample file was generated with the utility script +# "GEOSldas/src/Components/GEOSldas_GridComp/GEOSldas_App/util/config/generate_catchincr_hist.py". +# +# - The sample file triggers output of the GEOSldas "catch_progn_incr" collection in +# ensemble space, which is needed by ADASens. +# +# - The IDs of the ensemble members and their total number in GEOSldas must match +# those of ADASens. +# +# - The "catch_progn_incr" output is in tile space, which must be the same for +# GEOSldas and ADASens. +# +# +################################################################################## + +EXPID: MyGEOSldasAtmEns + +COLLECTIONS: +""" + +label = """ +:: +""" + +hist_template = """ +'catch_progn_incr' +:: +descr: 'Tile-space,3-Hourly,Instantaneous,Single-Level,Assimilation, Land Prognostics Increments', +template: '%y4%m2%d2_%h2%n2z.bin', +mode: 'instantaneous', +frequency: 030000, +ref_time: 013000, +fields: 'TCFSAT_INCR' , 'CATCHINCR_e' , + 'TCFTRN_INCR' , 'CATCHINCR_e' , + 'TCFWLT_INCR' , 'CATCHINCR_e' , + 'QCFSAT_INCR' , 'CATCHINCR_e' , + 'QCFTRN_INCR' , 'CATCHINCR_e' , + 'QCFWLT_INCR' , 'CATCHINCR_e' , + 'CAPAC_INCR' , 'CATCHINCR_e' , + 'CATDEF_INCR' , 'CATCHINCR_e' , + 'RZEXC_INCR' , 'CATCHINCR_e' , + 'SRFEXC_INCR' , 'CATCHINCR_e' , + 'GHTCNT1_INCR' , 'CATCHINCR_e' , + 'GHTCNT2_INCR' , 'CATCHINCR_e' , + 'GHTCNT3_INCR' , 'CATCHINCR_e' , + 'GHTCNT4_INCR' , 'CATCHINCR_e' , + 'GHTCNT5_INCR' , 'CATCHINCR_e' , + 'GHTCNT6_INCR' , 'CATCHINCR_e' , + 'WESNN1_INCR' , 'CATCHINCR_e' , + 'WESNN2_INCR' , 'CATCHINCR_e' , + 'WESNN3_INCR' , 'CATCHINCR_e' , + 'HTSNNN1_INCR' , 'CATCHINCR_e' , + 'HTSNNN2_INCR' , 'CATCHINCR_e' , + 'HTSNNN3_INCR' , 'CATCHINCR_e' , + 'SNDZN1_INCR' , 'CATCHINCR_e' , + 'SNDZN2_INCR' , 'CATCHINCR_e' , + 'SNDZN3_INCR' , 'CATCHINCR_e' , +""" + +# ------------------------------------------------------------------ +# +# write file "HISTORY.rc" with nens "catch_progn_incrXXXX" collections, +# one for each ensemble member + +with open('HISTORY.rc', 'w') as f: + f.write(heads) + collection, body = hist_template.split('::\n') + collection = collection.strip('\n').strip("'") + for i in range(nens): + i = i +1 + sfx = '%04d'%(i) + ids = "'"+collection+sfx+"'" + f.write(ids+'\n') + f.write(label) + lines = body.split('\n') + for i in range(nens): + i = i+1 + sfx = '%04d'%(i) + collect= collection+sfx+"." + for line in lines: + newline = line + if ":" in line : + newline = collect+line + if "CATCHINCR_e" in newline: + sfx = '%04d'%(i) + frep = 'CATCHINCR_e'+sfx + newline = newline.replace('CATCHINCR_e',frep) + f.write(newline+'\n') + f.write('::\n') + +# ====================== EOF ===================================== diff --git a/GEOSldas_App/util/config/rewind_GEOSldas.csh b/GEOSldas_App/util/config/rewind_GEOSldas.csh new file mode 100755 index 0000000..a52a195 --- /dev/null +++ b/GEOSldas_App/util/config/rewind_GEOSldas.csh @@ -0,0 +1,117 @@ +#!/bin/csh + +# rewind existing GEOSldas run to specified date/time + +setenv MYNAME rewind_GEOSldas.csh + +if ( $#argv < 4 ) then + echo " " + echo " NAME " + echo " " + echo " $MYNAME - rewind existing GEOSldas run to restart time of nymd nhms" + echo " " + echo " SYNOPSIS " + echo " " + echo " $MYNAME nymd nhms expid exppath " + echo " " + echo " where " + echo " nymd - restart date, as YYYYMMDD " + echo " time - restart time, as HHMMSS " + echo " expid - experiment name, e.g., ldas4coup " + echo " exppath - run directory path, e.g., /discover/nobackup/[user]/ " + echo " " + echo " DESCRIPTION " + echo " " + echo " This procedure rewinds and resets the GEOSldas experiment " + echo " specified by expid and exppath to the restart date/time " + echo " specified by nymd and nhms. " + echo " " + echo " Example of valid command line: " + echo " $MYNAME 20170829 210000 ldas4coup /discover/nobackup/qzhang " + exit(0) +endif + +set nymd = $1 +set nhms = $2 + +echo " ymd = $nymd " +echo " hms = $nhms " + +set yin = `echo $nymd | cut -c1-4` +set min = `echo $nymd | cut -c5-6` +set hin = `echo $nhms | cut -c1-4` + +set date = ${nymd}_${hin} + +set expid = $3 +set rundir = $4 +echo " expid = $expid " + +cd ${rundir}/${expid}/run +set nmem = `grep NUM_LDAS_ENSEMBLE: LDAS.rc | cut -d':' -f2` +cd ${rundir}/${expid} +set grid = `ls output` + +## rewind links to restart files + +@ NENS = $nmem + +set rsout = ${rundir}/${expid}/output/${grid}/rs +set rstin = ${rundir}/${expid}/input/restart +cd $rstin + +/bin/rm -rf catch*_internal_rst +/bin/rm -rf landpert*_internal_rst +/bin/rm -rf landassim_obspertrseed*_rst + +@ inens = 1 + +while ($inens <= $NENS) + + if ($inens < 10) then + set ENSDIR = `echo ens000${inens}` + set catin = `echo catch000${inens}` + set pertin = `echo landpert000${inens}` + set seedin = `echo obspertrseed000${inens}` + else if($inens < 100) then + set ENSDIR = `echo ens00${inens}` + set catin = `echo catch00${inens}` + set pertin = `echo landpert00${inens}` + set seedin = `echo obspertrseed00${inens}` + else if($inens < 1000) then + set ENSDIR = `echo ens0${inens}` + set catin = `echo catch0${inens}` + set pertin = `echo landpert0${inens}` + set seedin = `echo obspertrseed0${inens}` + else + set ENSDIR = `echo ens${inens}` + set catin = `echo catch${inens}` + set pertin = `echo landpert${inens}` + set seedin = `echo obspertrseed${inens}` + endif + + /bin/ln -s ${rsout}/${ENSDIR}/Y${yin}/M${min}/${expid}.catch_internal_rst.${date} ${catin}_internal_rst + + if (-e ${rsout}/${ENSDIR}/Y${yin}/M${min}/${expid}.landpert_internal_rst.${date}.gz ) then + gunzip ${rsout}/${ENSDIR}/Y${yin}/M${min}/${expid}.landpert_internal_rst.${date}.gz + endif + + /bin/ln -s ${rsout}/${ENSDIR}/Y${yin}/M${min}/${expid}.landpert_internal_rst.${date} ${pertin}_internal_rst + + /bin/ln -s ${rsout}/${ENSDIR}/Y${yin}/M${min}/${expid}.landassim_obspertrseed_rst.${date} landassim_${seedin}_rst + + @ inens ++ +end + +## -- remove records in rc_out +cd ${rundir}/${expid}/output/${grid}/rc_out/Y${yin}/M${min} +/bin/rm -rf *smapL4*${date}z.bin +/bin/rm -rf *.${date}z.txt +/bin/rm -rf *.${date}z.nml + +## -- reset cap_restart +cd ${rundir}/${expid}/run +/bin/rm -rf cap_restart +echo $nymd ${nhms} > cap_restart + +## EOF #################################################################### diff --git a/GEOSldas_App/util/inputs/ASCAT_sm_mask/CMakeLists.txt b/GEOSldas_App/util/inputs/ASCAT_sm_mask/CMakeLists.txt new file mode 100644 index 0000000..11114b0 --- /dev/null +++ b/GEOSldas_App/util/inputs/ASCAT_sm_mask/CMakeLists.txt @@ -0,0 +1,4 @@ +# build without installation + +add_executable(ascat_mask_maker.x ascat_mask_maker.F90) +target_link_libraries(ascat_mask_maker.x MAPL) diff --git a/GEOSldas_App/util/inputs/ASCAT_sm_mask/ascat_mask_maker.F90 b/GEOSldas_App/util/inputs/ASCAT_sm_mask/ascat_mask_maker.F90 new file mode 100644 index 0000000..e9b4bbd --- /dev/null +++ b/GEOSldas_App/util/inputs/ASCAT_sm_mask/ascat_mask_maker.F90 @@ -0,0 +1,242 @@ +! This program produces a combined mask for use with the assimilation of ASCAT soil moisture retrievals in GEOSldas. +! The combined mask is based on component masks from: +! +! Lindorfer, R., Wagner, W., Hahn, S., Kim, H., Vreugdenhil, M., Gruber, A., Fischer, M., & Trnka, M. (2023). +! Global Scale Maps of Subsurface Scattering Signals Impacting ASCAT Soil Moisture Retrievals (1.0.0) [Data set]. +! TU Wien. https://doi.org/10.48436/9a2y9-e5z14 +! +! The program provides the possibility to combine different masks (default is combination of subsurface and wetland masks) +! and interpolates onto a regular grid with a (hardwired) 0.1 degree lat/lon spacing and -90/-180 degree lower left +! corner used for quick indexing in the ASCAT observation reader QC routine. +! +! Author: AM Fox, March, 2024 + +program ascat_mask_maker + + use netcdf + + implicit none + + integer :: ncid, varid, dimid, ierr, len, N_gpi, dimids(2), N_lon, N_lat + integer :: i, j, closest_index, mask_mode + integer, dimension(:), allocatable :: cold_mask, wet_mask, veg_mask, subsurface_mask, combined_mask + + integer(kind=1), dimension(:,:), allocatable :: mask_out + integer(kind=1) :: missing_value + + real, dimension(:), allocatable :: asc_lon, asc_lat + real, dimension(:), allocatable :: lon, lat, distances + real :: d_lon, d_lat, ll_lon, ll_lat + + character(200) :: fname_in, mask_description, fname_out + + ! -------------------------------------------------------------------------------- + ! + ! hardwired variables + + ! ASCAT soil moisture mask file from Lindorfer et al 2023 + + fname_in = '/discover/nobackup/projects/gmao/smap/SMAP_Nature/ASCAT_EUMETSAT/Mask/subsurface_scattering_ASCAT_ERA5_Land.nc' + + ! Specification of how to combine the masks + ! Mask_mode = 1 (default) combines subsurface and wetland masks + ! Mask_mode = 2 uses only the subsurface mask + ! Mask_mode = 3 uses only the wetland mask + ! Mask_mode = 4 combines subsurface, wetland and vegetation masks + + mask_mode = 1 + + ! Specification of output grid and missing value + + d_lon = 0.1 + d_lat = 0.1 + ll_lon = -180.0 ! longitude of boundary of lower-left grid cell (longitude of lower left corner of grid) + ll_lat = -90.0 ! latitude of boundary of lower-left grid cell (latitude of lower left corner of grid) + + missing_value = -128 + + ! Specify the NetCDF file name for the output mask + + fname_out = 'ascat_combined_mask_p1.nc' + + ! ------------------------------- + + ! Open the NetCDF input file + ierr = nf90_open(fname_in, nf90_nowrite, ncid) + if (ierr /= nf90_noerr) stop 'Error opening file: ' // trim(fname_in) + + ! Data in original mask file are on the 12.5 km fixed Earth grid used for ASCAT (WARP5 grid) and + ! stored in the NetCDF file as 1-dimensional arrays of length N_gpi (over land only). + + ! Get the dimension ID + ierr = nf90_inq_dimid(ncid, 'gpi', dimid) + if (ierr /= nf90_noerr) stop 'Error getting dimension ID' + + ! Get the length of the dimension + ierr = nf90_inquire_dimension(ncid, dimid, len = N_gpi) + if (ierr /= nf90_noerr) stop 'Error inquiring dimension' + + print*, 'N_gpi = ', N_gpi + + ! Allocate the arrays + allocate(asc_lon( N_gpi)) + allocate(asc_lat( N_gpi)) + allocate(cold_mask( N_gpi)) + allocate(wet_mask( N_gpi)) + allocate(veg_mask( N_gpi)) + allocate(subsurface_mask(N_gpi)) + allocate(combined_mask( N_gpi)) + allocate(distances( N_gpi)) + + ! Get the variable IDs and read the variables + ierr = nf90_inq_varid(ncid, 'lon', varid) + ierr = nf90_get_var(ncid, varid, asc_lon) + ierr = nf90_inq_varid(ncid, 'lat', varid) + ierr = nf90_get_var(ncid, varid, asc_lat) + ierr = nf90_inq_varid(ncid, 'cold_mask', varid) + ierr = nf90_get_var(ncid, varid, cold_mask) + ierr = nf90_inq_varid(ncid, 'wet_mask', varid) + ierr = nf90_get_var(ncid, varid, wet_mask) + ierr = nf90_inq_varid(ncid, 'veg_mask', varid) + ierr = nf90_get_var(ncid, varid, veg_mask) + ierr = nf90_inq_varid(ncid, 'subsurface_mask', varid) + ierr = nf90_get_var(ncid, varid, subsurface_mask) + + ! Close the NetCDF file + ierr = nf90_close(ncid) + if (ierr /= nf90_noerr) stop 'Error closing file' + + ! Combine the masks (1-dim arrays) + select case (mask_mode) + case (1) + ! Combine wet_mask and subsurface_mask + mask_description = 'Combined subsurface and wetland mask' + where (wet_mask /= 0) + combined_mask = 1 + elsewhere + combined_mask = subsurface_mask + end where + case (2) + ! Use only subsurface_mask + mask_description = 'Used only subsurface mask' + combined_mask = subsurface_mask + case (3) + ! Use only wet_mask + mask_description = 'Used only wetland mask' + combined_mask = wet_mask + case (4) + ! Combine subsurface_mask, wet_mask, and veg_mask + mask_description = 'Combined subsurface, wetland, and vegetation mask' + where (wet_mask /= 0 .or. veg_mask /= 0) + combined_mask = 1 + elsewhere + combined_mask = subsurface_mask + end where + end select + + ! Re-map "combined_mask" from WARP5 input grid to regular lat/lon output grid (2-dim array) + + N_lon = nint( 360.0 / d_lon ) + N_lat = nint( 180.0 / d_lat ) + + allocate(lon(N_lon)) + allocate(lat(N_lat)) + + lon = [((ll_lon + (d_lon / 2)) + i * d_lon, i = 0, N_lon - 1)] ! NB using grid cell centers for nearest neighbor search + lat = [((ll_lat + (d_lat / 2)) + i * d_lat, i = 0, N_lat - 1)] + + allocate(mask_out( N_lon, N_lat)) + + do i = 1, N_lon + print*, lon(i) + do j = 1, N_lat + distances = (asc_lon - lon(i))**2 + (asc_lat - lat(j))**2 + closest_index = minloc(distances, dim = 1) + if (distances(closest_index) > 0.14**2) then + mask_out(i, j) = missing_value ! Note: ASCAT EUMETSAT reader masks everything .ne. 0 + else + mask_out(i, j) = combined_mask(closest_index) + end if + end do + end do + + ! Write out the mask to netcdf + ierr = nf90_create(fname_out, nf90_clobber, ncid) + if (ierr /= nf90_noerr) stop 'Error creating file: ' // fname_out + + ! Define the dimensions + ierr = nf90_def_dim(ncid, 'lon', N_lon, dimids(1)) + ierr = nf90_def_dim(ncid, 'lat', N_lat, dimids(2)) + + ! Define the global attributes + ierr = nf90_put_att(ncid, nf90_global, 'title', 'ASCAT combined mask') + ierr = nf90_put_att(ncid, nf90_global, 'source', 'Lindorfer et al 2023 doi:10.48436/9a2y9-e5z14') + ierr = nf90_put_att(ncid, nf90_global, 'description', mask_description) + + ! Define the variables + ierr = nf90_def_var(ncid, 'lat', nf90_real, dimids(2), varid) + ierr = nf90_put_att(ncid, varid, 'standard_name', 'latitude') + ierr = nf90_put_att(ncid, varid, 'long_name', 'grid cell center latitude') + ierr = nf90_put_att(ncid, varid, 'units', 'degrees_north') + ierr = nf90_put_att(ncid, varid, 'axis', 'Y') + + ierr = nf90_def_var(ncid, 'lon', nf90_real, dimids(1), varid) + ierr = nf90_put_att(ncid, varid, 'standard_name', 'longitude') + ierr = nf90_put_att(ncid, varid, 'long_name', 'grid cell center longitude') + ierr = nf90_put_att(ncid, varid, 'units', 'degrees_east') + ierr = nf90_put_att(ncid, varid, 'axis', 'X') + + ierr = nf90_def_var(ncid, 'mask', nf90_byte, dimids, varid) + ierr = nf90_put_att(ncid, varid, 'standard_name', 'combined_mask') + ierr = nf90_put_att(ncid, varid, 'long_name', 'combined mask') + ierr = nf90_put_att(ncid, varid, 'units', 'boolean') + ierr = nf90_put_att(ncid, varid, '_FillValue', missing_value) + + ierr = nf90_def_var(ncid, 'll_lon', nf90_real, varid) + ierr = nf90_put_att(ncid, varid, 'standard_name', 'longitude of lower left corner') + ierr = nf90_put_att(ncid, varid, 'long_name', 'longitude of lower left corner') + ierr = nf90_put_att(ncid, varid, 'units', 'degrees_east') + ierr = nf90_put_att(ncid, varid, 'axis', 'X') + + ierr = nf90_def_var(ncid, 'll_lat', nf90_real, varid) + ierr = nf90_put_att(ncid, varid, 'standard_name', 'latitude of lower left corner') + ierr = nf90_put_att(ncid, varid, 'long_name', 'latitude of lower left corner') + ierr = nf90_put_att(ncid, varid, 'units', 'degrees_north') + ierr = nf90_put_att(ncid, varid, 'axis', 'Y') + + ierr = nf90_def_var(ncid, 'd_lon', nf90_real, varid) + ierr = nf90_put_att(ncid, varid, 'standard_name', 'longitude grid spacing') + ierr = nf90_put_att(ncid, varid, 'long_name', 'longitude grid spacing') + ierr = nf90_put_att(ncid, varid, 'units', 'degrees') + ierr = nf90_put_att(ncid, varid, 'axis', 'X') + + ierr = nf90_def_var(ncid, 'd_lat', nf90_real, varid) + ierr = nf90_put_att(ncid, varid, 'long_name', 'latitude grid spacing') + ierr = nf90_put_att(ncid, varid, 'units', 'degrees') + ierr = nf90_put_att(ncid, varid, 'axis', 'Y') + + ! End define mode + ierr = nf90_enddef(ncid) + + ! Write the variables + ierr = nf90_inq_varid(ncid, 'lat', varid) + ierr = nf90_put_var(ncid, varid, lat) + ierr = nf90_inq_varid(ncid, 'lon', varid) + ierr = nf90_put_var(ncid, varid, lon) + ierr = nf90_inq_varid(ncid, 'mask', varid) + ierr = nf90_put_var(ncid, varid, mask_out) + if (ierr /= nf90_noerr) stop 'Error writing variable' + ierr = nf90_inq_varid(ncid, 'll_lon', varid) + ierr = nf90_put_var(ncid, varid, ll_lon) + ierr = nf90_inq_varid(ncid, 'll_lat', varid) + ierr = nf90_put_var(ncid, varid, ll_lat) + ierr = nf90_inq_varid(ncid, 'd_lon', varid) + ierr = nf90_put_var(ncid, varid, d_lon) + ierr = nf90_inq_varid(ncid, 'd_lat', varid) + ierr = nf90_put_var(ncid, varid, d_lat) + + ! Close the NetCDF file + ierr = nf90_close(ncid) + if (ierr /= nf90_noerr) stop 'Error closing file' + +end program ascat_mask_maker diff --git a/GEOSldas_App/util/inputs/mwRTM_params/Create_mwRTM_param_file.m b/GEOSldas_App/util/inputs/mwRTM_params/Create_mwRTM_param_file.m new file mode 100644 index 0000000..a478214 --- /dev/null +++ b/GEOSldas_App/util/inputs/mwRTM_params/Create_mwRTM_param_file.m @@ -0,0 +1,240 @@ +% --------------------------------------------------------------------------- +% This script is to generate the mwRTM_param.nc4 file for the GEOSldas mwRTM. +% All constant mwRTM parameters are in this file. They come from 3 different +% sources: cat_param, vegcls lookup table and preprocessed L2DCA daily mat files. +% Therefore, need to run Preprocess_L2DCA_mwRTM_into_dailymat.m before running +% this script. + +% qliu + rreichle, 29 Jul 2022 + +% ---------------------------------------------------------------------------- + +clear + +% add path to matlab functions in src/Components/GEOSldas_GridComp/GEOSldas_App/util/shared/matlab/ +addpath('../../shared/matlab/'); + +% option to fill small gaps based on neighboring grids, 1 is recommended. +fill_small_gaps = 1; + +% fill value in output file +fillValue = single(1.e15); + +% resolution of output parameters: only works with "M09" or "M36". +EASEv2_grid = 'M09'; + +% older version mwRTM_param.nc4 file for parameter names and attributes +fname_in = ['/home/qliu/smap/SMAP_Nature/bcs/RTM_params/RTMParam_SMAP_L4SM_v004/SMAP_EASEv2_',EASEv2_grid,'/mwRTM_param.nc4']; + +% target mwRTM_param file name +fname_out = ['/home/qliu/smap/SMAP_Nature/bcs/RTM_params/RTMParam_L2_omega_H_tmp/SMAP_EASEv2_',EASEv2_grid,'/mwRTM_param_L2_omega_H_fillValue_bhbvlewt.nc4']; + +% get inputs for fill_gaps_in_tiledata() below +if fill_small_gaps + + if strcmp(EASEv2_grid,'M09') + N_cells = 5; + iscube = 0; + elseif strcmp(EASEv2_grid,'M36') + N_cells = 3; + iscube = 0; + else + error('invalid resolution, use ''M09'' or ''M36'' only') + end + + tmpstr = num2str(N_cells); + + fname_out = strrep(fname_out,'.nc4','_',tmpstr,'gx',tmpstr,'gfilled.nc4'); +end + +% Do not overwrite if file exists +if exist(fname_out,'file') + + disp(['file exist ',fname_out]) + return + +end + +% GEOSldas experiment for tilecoord and cat_params +exp_path = '/home/qliu/smap/SMAP_Nature/SMAP_Nature_v10/'; + +if strcmp(EASEv2_grid,'M09') + exp_run = 'SMAP_Nature_v10.0'; + domain = 'SMAP_EASEv2_M09_GLOBAL'; +else + exp_run = 'SMAP_Nature_v10.0_M36'; + domain = 'SMAP_EASEv2_M36_GLOBAL'; +end + +fname_tc = [exp_path,exp_run,'/output/',domain,'/rc_out/',exp_run,'.ldas_tilecoord.bin']; +fname_tg = [exp_path,exp_run,'/output/',domain,'/rc_out/',exp_run,'.ldas_tilegrids.bin']; +fname_catparam = [exp_path,exp_run,'/output/',domain,'/rc_out/Y2015/M04/',exp_run,'.ldas_catparam.20150401_0000z.bin']; + +% If use L4 products in /css/smapl4/ +if ~exist(fname_tc, 'file') + fname_tc = [exp_path,exp_run,'/rc_out/',exp_run,'.ldas_tilecoord.bin']; + fname_tg = [exp_path,exp_run,'/rc_out/',exp_run,'.ldas_tilegrids.bin']; + fname_catparam = [exp_path,exp_run,'/rc_out/Y2015/M04/',exp_run,'.ldas_catparam.20150401_0000z.bin']; +end + +tc = read_tilecoord(fname_tc); + +% double check for tile order, may not work if exp_run uses older bcs +% version +if max(abs(transpose([1:tc.N_tile])-tc.tile_id)) > 0 + error('tile order is not strictly tile_id ascending, need to modify script to reorder') + return +end + +tg = read_tilegrids(fname_tg); +cat_param = read_catparam(fname_catparam, tc.N_tile); + +% L2RTM parameter source information +L2_version = 'R18290'; +L2_start_time.year = 2015; L2_start_time.month = 4; L2_start_time.day = 1; +L2_end_time.year = 2022; L2_end_time.month = 4; L2_end_time.day = 1; + +if strcmp(EASEv2_grid,'M36') + L2_file_tag = 'L2_SM_P'; +else + L2_file_tag = 'L2_SM_P_E'; +end + +% L2DCA based parameters +L2_param = get_L2_RTM_constants_tile_data(tc,L2_file_tag,... + L2_version,L2_start_time, L2_end_time); +omega = L2_param.Albedo; +hparam = L2_param.Roughness; +clear L2_param + +% cat_param based parameters +mwRTMparam.soilcls = int32(cat_param.soilcls30); +mwRTMparam.sand = cat_param.sand30/100.; +mwRTMparam.clay = cat_param.clay30/100.; + +% there are 0 values in poros30 for unknown reasons, set 0 to next minimum +% value (0.3741) +mwRTMparam.poros = max(0.3741, cat_param.poros30); +mwRTMparam.wang_wp = cat_param.wpwet30 .* cat_param.poros30; +mwRTMparam.wang_wt = 0.49*mwRTMparam.wang_wp + 0.165; +mwRTMparam.rgh_wmin = mwRTMparam.wang_wt; +mwRTMparam.rgh_wmax = mwRTMparam.poros; + +% Initialize the input and output file interface + +netcdf.setDefaultFormat('FORMAT_NETCDF4'); + +fin_id = netcdf.open(fname_in, 'NOWRITE'); +fout_id = netcdf.create(fname_out, 'NETCDF4'); + +if fout_id < 0, error(['Creating ' fname_out 'failed']); end + +finfo = ncinfo(fname_in); netcdf.close(fin_id) + +% Define Dimension (tile) +Dim_id = netcdf.defDim(fout_id,'tile',tc.N_tile); + +nvar_in_file = length(finfo.Variables); + +for i=1: nvar_in_file + + data_name = finfo.Variables(i).Name; + data_type = finfo.Variables(i).Datatype; + + data_type = 'float'; + + data_size = finfo.Variables(i).Size; + + varid(i) = netcdf.defVar(fout_id, data_name, data_type, Dim_id ); + + netcdf.defVarFill(fout_id, varid(i), false, fillValue); + + n_attr = length(finfo.Variables(i).Attributes); + + for iv = 1:n_attr + att_name = finfo.Variables(i).Attributes(iv).Name; + att_value = finfo.Variables(i).Attributes(iv).Value; + + netcdf.putAtt(fout_id, varid(i), att_name, att_value); + end + + netcdf.endDef(fout_id); + + startVAR = repmat([0], 1, length(data_size)); + countVAR = data_size; + + % get parameter values from their respective sources + % Total of 18 mwRTM parameters: + % 8 from cat_param: SOILCLS, SOIL, CLAY, POROS, WANGWT, WANTWP, RGHWMIN, RGHWMAX + % 4 from vegcls lookup table : VEGCLS, RGHNRH, RGHNRV,POLMIX + % 3 from L2RTM: RGHHMIN, RGHHMAX, OMEGA + % 3 are set to fillValue: BH,BV, OMEGA + + if strcmp(data_name,'MWRTM_OMEGA') + if fill_small_gaps + omega_filled = fill_gaps_in_tiledata(tc, tg, transpose(omega), N_cells, iscube ); + data = omega_filled; + else + data = omega; + end + elseif contains(data_name, 'MWRTM_RGHHM') + if fill_small_gaps + hparam_filled = = fill_gaps_in_tiledata(tc, tg, transpose(hparam), N_cells, iscube ); + data = hparam_filled; + else + data = hparam; + end + elseif strcmp(data_name,'MWRTM_SOILCLS') + data = mwRTMparam.soilcls; + elseif strcmp(data_name,'MWRTM_SAND') + data = mwRTMparam.sand; + elseif strcmp(data_name,'MWRTM_CLAY') + data = mwRTMparam.clay; + elseif strcmp(data_name,'MWRTM_POROS') + data = mwRTMparam.poros; + elseif strcmp(data_name,'MWRTM_WANGWT') + data = mwRTMparam.wang_wt; + elseif strcmp(data_name,'MWRTM_WANGWP') + data = mwRTMparam.wang_wp; + elseif strcmp(data_name,'MWRTM_RGHWMIN') + data = mwRTMparam.rgh_wmin; + elseif strcmp(data_name,'MWRTM_RGHWMAX') + data = mwRTMparam.rgh_wmax; + elseif strcmp(data_name,'MWRTM_LEWT') || contains(data_name, 'MWRTM_B') + data = fillValue .* ones(data_size,1); + else + if strcmp(EASEv2_grid,'M09') + dominant_M36vegcls = 1; + else + dominant_M36vegcls = 0; + end + tmp_rtm = get_mwRTM_vegcls_based( tc, dominant_M36vegcls,['EASEv2_',EASEv2_grid]); + if strcmp(data_name,'MWRTM_RGHNRH') + data = tmp_rtm.rgh_Nrh; + elseif strcmp(data_name,'MWRTM_RGHNRV') + data = tmp_rtm.rgh_Nrv; + elseif strcmp(data_name,'MWRTM_VEGCLS') + data = tmp_rtm.vegcls; + elseif strcmp(data_name,'MWRTM_RGHPOLMIX') + data = tmp_rtm.rgh_polmix; + end + end + + % earlier fillValue was -9999. replace with new fillValue (1.e15) + data(abs(data-(-9999.)) < abs(-9999.*1e-4)) = NaN; + + data(isnan(data)) = fillValue; + + netcdf.putVar(fout_id, varid(i), startVAR, countVAR, data); clear data + + netcdf.reDef(fout_id); + +end + +netcdf.endDef(fout_id); + +netcdf.close(fout_id); + +disp(['done writing ',fname_out]) + +% --------------------------EOF-------------------------------------------- diff --git a/GEOSldas_App/util/inputs/mwRTM_params/Create_vegopacity_8day_clim.m b/GEOSldas_App/util/inputs/mwRTM_params/Create_vegopacity_8day_clim.m new file mode 100644 index 0000000..19eb133 --- /dev/null +++ b/GEOSldas_App/util/inputs/mwRTM_params/Create_vegopacity_8day_clim.m @@ -0,0 +1,341 @@ +% script to create 8-day climatology of vegetation opacity for L-band microwave +% radiative transfer model (mwRTM) +% +% requires pre-processing of SMAP L2 data into daily *.mat files using +% Preprocess_L2DCA_mwRTM_params_to_dailymat.m +% +% output files written in MAPL_ReadForcing format +% +% qliu + rreichle, 29 Jul 2022 +% +% ------------------------------------------------------------------------------------- + +clear + +% add path to matlab functions in src/Components/GEOSldas_GridComp/GEOSldas_App/util/shared/matlab/ +addpath('../../shared/matlab/'); + +L2_Ascdes_all = {'_A_','_D_'}; + +out_Para = 'VOD'; + +L2_file_tag = 'L2_SM_P'; +L2_version = 'R18290'; + +if strcmp(L2_file_tag(end-1:end),'_E') + resolution = 'M09'; +else + resolution = 'M36'; +end + +out_path = '/discover/nobackup/qliu/matlab/SMAP/L2L4/VOD/QC_frozen_RFI/'; + +fill_small_gaps = 1; + +% provide a GEOSldas simulation with matching tile information +if strcmp(resolution,'M36') + L4_path = '/discover/nobackup/projects/gmao/smap/SMAP_Nature/SMAP_Nature_v9.x/'; + L4_version = 'SMAP_Nature_v9.1_M36'; + based_on_h5 = 0; + out_Nlon = 3856/4; + out_Nlat = 1624/4; +else + L4_path = '/css/smapl4/public/L4_Products/L4_SM/'; + L4_version = 'Vv6030'; + based_on_h5 = 1; + out_Nlon = 3856; + out_Nlat = 1624; +end + +ftilecoord = [L4_path,L4_version,'/output/SMAP_EASEv2_',resolution,'_GLOBAL/rc_out/', ... + L4_version,'.ldas_tilecoord.bin']; +ftilegrids = [L4_path,L4_version,'/output/SMAP_EASEv2_',resolution,'_GLOBAL/rc_out/', ... + L4_version,'.ldas_tilegrids.bin']; + +if ~exist(ftilecoord,'file') + ftilecoord = [L4_path,L4_version,'/rc_out/SPL4SM_', L4_version,'.ldas_tilecoord.bin']; + ftilegrids = [L4_path,L4_version,'/rc_out/SPL4SM_', L4_version,'.ldas_tilegrids.bin']; +end +% read tile info for binary output +tc = read_tilecoord( ftilecoord); +tg = read_tilegrids( ftilegrids); + +int_precision = 'int32'; +float_precision = 'float32'; + +for iAD = 1:2 + + L2_Ascdes = L2_Ascdes_all{iAD}; + L2_qc_yes = 1; + + dtstep = 10800; + + % time period for computing climatology + start_time.year = 2015; + start_time.month = 4; + start_time.day = 1; + + start_time.hour = 1; + start_time.min = 30; + start_time.sec = 0; + + end_time.year = 2022; + end_time.month = 4; + end_time.day = 1; + end_time.hour = start_time.hour; + end_time.min = start_time.min; + end_time.sec = start_time.sec; + + start_time = get_dofyr_pentad(start_time); + end_time = get_dofyr_pentad(end_time); + + % lookup table of month and day of first day in 8-day average (non-leap year) + + clim_8d_m1 = [ 1 1 1 1 2 2 2 2 ... + 3 3 3 3 4 4 4 ... + 5 5 5 5 6 6 6 6 ... + 7 7 7 7 8 8 8 8 ... + 9 9 9 9 10 10 10 ... + 11 11 11 11 12 12 12 12]; + + clim_8d_d1 = [ 1 9 17 25 2 10 18 26 ... + 6 14 22 30 7 15 23 ... + 1 9 17 25 2 10 18 26 ... + 4 12 20 28 5 13 21 29 ... + 6 14 22 30 8 16 24 ... + 1 9 17 25 3 11 19 27 ]; + + clim_8d_m2 = [clim_8d_m1(2:46) 1]; + clim_8d_d2 = [clim_8d_d1(2:46) 1]; + + % ----------------------------------------------------------------------- + % read from preprocessed daily mat file + if end_time.month ==1 + time_tag = [num2str(start_time.year,'%4.4d'),num2str(start_time.month,'%2.2d'), ... + '_',num2str(end_time.year-1,'%4.4d'),'12']; + else + time_tag = [num2str(start_time.year,'%4.4d'),num2str(start_time.month,'%2.2d'), ... + '_',num2str(end_time.year,'%4.4d'),num2str(end_time.month-1,'%2.2d')]; + end + + fname_clim = [out_path,'/',out_Para,'_clim_L2_',L2_version,L2_Ascdes,'8d_',resolution,'tile_',time_tag,'_w24d.bin']; + + L2_tau_clim_sum = zeros(46,out_Nlon,out_Nlat); + N_L2_clim_sum = zeros(46,out_Nlon,out_Nlat); + + if exist(fname_clim,'file') + + disp(['found preprocessed climatology file ',fname_clim]) + + else + + date_time = start_time; + + while 1 + + if (date_time.year ==end_time.year && ... + date_time.month==end_time.month && ... + date_time.day ==end_time.day ) + break + end + + outfile_tag = [num2str(date_time.year,'%4.4d'), ... + num2str(date_time.month,'%2.2d'), ... + num2str(date_time.day,'%2.2d')]; + + mat_fname = [out_path,'L2DCA_RTM_',L2_file_tag,'_',L2_version,L2_Ascdes, outfile_tag,'.mat']; + + if exist(mat_fname,'file') + + disp(['loading ', mat_fname]) + if contains(fname_clim,'VOD_') + load(mat_fname,'L2_tau') + elseif contains(fname_clim,'Albedo_') + load(mat_fname,'L2_omg') + L2_tau = L2_omg; clear L2_omg + elseif contains(fname_clim,'Roughness_') + load(mat_fname,'L2_h') + L2_tau = L2_h; clear L2_h + else + error(['unknown clim fname ',fname_clim]) + end + + d_idx = find(date_time.month == clim_8d_m1 & date_time.day >= clim_8d_d1); + if ~isempty(d_idx) + d_idx = d_idx(end); + else + d_idx = find(date_time.month == clim_8d_m2 & date_time.day < clim_8d_d2); + d_idx = d_idx(1); + end + + % compute climatology + tmp = max(L2_tau,0); % make sure no negative values in tau + tmp(isnan(tmp)) = 0; + + L2_tau_clim_sum(d_idx,:,:) = squeeze(L2_tau_clim_sum(d_idx,:,:)) + tmp; clear tmp + tmp = ~isnan(L2_tau); + N_L2_clim_sum(d_idx,:,:) = squeeze(N_L2_clim_sum(d_idx,:,:)) + tmp; clear tmp + + clear L2_tau + + else + + error('daily mat file not found') + + end + + date_time = augment_date_time(86400, date_time); + + end + + L2_tau_clim = L2_tau_clim_sum ./N_L2_clim_sum; + + + % regrid to til grid + + L2_tau_tile = NaN + ones(46, tc.N_tile); + for k = 1:tc.N_tile + L2_tau_tile(:,k) = L2_tau_clim(:,tc.i_indg(k)+1, tc.j_indg(k)+1); + end + + ifp = fopen(fname_clim,'w','l'); + + for n = 1: 48 + if n == 1 + y1 = 0; + y2 = 1; + + nidx = 46; + nidx_pre = 45; + nidx_nxt = 1; + elseif n == 2 + y1 = 1; + y2 = 1; + nidx = n-1; + nidx_pre = 46; + nidx_nxt = n; + elseif n == 47 + y1 = 1; + y2 = 2; + + nidx = n-1; + nidx_pre = n-2; + nidx_nxt = 1; + elseif n == 48 + y1 = 2; + y2 = 2; + nidx = 1; + nidx_pre = 46; + nidx_nxt = 2; + else + y1 = 1; + y2 = 1; + nidx = n-1; + nidx_pre = n-2; + nidx_nxt = n; + end + m1 = clim_8d_m1(nidx); + m2 = clim_8d_m2(nidx); + d1 = clim_8d_d1(nidx); + d2 = clim_8d_d2(nidx); + + header = [y1 m1 d1 0 0 0 y2 m2 d2 0 0 0 tc.N_tile 1]; + + tile_data = mean(L2_tau_tile([nidx_pre nidx nidx_nxt],:),1,"omitnan"); + tile_data(isnan(tile_data)) = 1.e15; + + fwrite( ifp, 14*4, int_precision ); % fortran_tag + fwrite( ifp, header, float_precision ); + fwrite( ifp, 14*4, int_precision ); % fortran_tag + + fwrite( ifp, tc.N_tile*4, int_precision );% fortran_tag + fwrite( ifp, tile_data(:), float_precision ); + fwrite( ifp, tc.N_tile*4, int_precision );% fortran_tag + + clear header tile_data + end + fclose(ifp) + end + +end + +% ======================== +% The final vegopacity.bin file contains data averaged (mean) of Asc +% and Des tau climatology. VOD is climatology,the other 2 parameters are +% time constant with maximum spatial coverage +data_clim_tile = NaN + ones(48, tc.N_tile,2); +for iAD = 1:2 + + L2_Ascdes = L2_Ascdes_all{iAD}; + + fname = [out_path,'/',out_Para,'_clim_L2_',L2_version,L2_Ascdes,'8d_',resolution,'tile_',time_tag,'_w24d.bin']; + + disp(['read ',fname]) + ifp = fopen(fname,'r','l'); + + for n = 1:48 + + fortran_tag = fread( ifp, 1, int_precision ); + tmp = fread( ifp, 14, float_precision ); + header(n,:) = tmp; + fortran_tag = fread( ifp, 1, int_precision ); + + fortran_tag = fread( ifp, 1, int_precision ); + tmp = fread( ifp, tc.N_tile, float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + data_clim_tile(n,:,iAD) = tmp; + + end + + fclose(ifp); +end + +data_clim_tile(data_clim_tile > 10.) = NaN; +data_clim_tile(data_clim_tile < 0.) = 0.; % set small negative values to 0 + +% averaging A, D values +tile_data = mean(data_clim_tile,3,"omitnan"); +fname_out = strrep(fname, L2_Ascdes,'_AD_'); +if fill_small_gaps + + if strcmp(resolution,'M09') + N_cells = 5; + iscube = 0; + elseif strcmp(resolution,'M36') + N_cells = 3; + iscube = 0; + else + error('invalid resolution, use ''M09'' or ''M36'' only') + end + + tmpstr = num2str(N_cells); + + fname_out = [fname_out(1:end-4),'_',tmpstr,'gx',tmpstr,'gfilled_test.bin']; + + tile_data = fill_gaps_in_tiledata(tc, tg, tile_data, N_cells, iscube ); + +end + +tile_data(isnan(tile_data)) = 1.e15; % fillValue = 1.e15 + +disp(['write ',fname_out]) + +ifp = fopen(fname_out,'w','l'); +for n = 1:48 + + % write header + + fwrite( ifp, 14*4, int_precision ); + fwrite( ifp, header(n,:), float_precision ); + fwrite( ifp, 14*4, int_precision ); + + % write science data + + fwrite( ifp, tc.N_tile*4, int_precision ); + fwrite( ifp, tile_data(n,:), float_precision ); + fwrite( ifp, tc.N_tile*4, int_precision ); +end +fclose(ifp); + +% ============================ EOF ====================================== diff --git a/GEOSldas_App/util/inputs/mwRTM_params/Preprocess_L2DCA_mwRTM_params_to_dailymat.m b/GEOSldas_App/util/inputs/mwRTM_params/Preprocess_L2DCA_mwRTM_params_to_dailymat.m new file mode 100644 index 0000000..061229b --- /dev/null +++ b/GEOSldas_App/util/inputs/mwRTM_params/Preprocess_L2DCA_mwRTM_params_to_dailymat.m @@ -0,0 +1,241 @@ +% Script to read SMAP L2 files and extract RTM variables (albedo, vegopacity, roughness) +% to store in global EASEv2 grid daily composite (Asc and Desc separately) mat files for future use. + +% Q. Liu 18 Jul 2022 + +clear + +% add path to matlab functions in src/Components/GEOSldas_GridComp/GEOSldas_App/util/shared/matlab/ +addpath('../../shared/matlab/'); + +L2_Ascdes = {'_A_','_D_'}; +L2_qc_yes = 1; + +% L2 file information. Use 'L2_SM_P' for M36 resolution and 'L2_SM_P_E' for M09 +SMAP_product = 'L2_SM_P'; %'L2_SM_P'; +L2_path = ['/discover/nobackup/projects/gmao/smap/SMAP_L4/SMAP/OPS/',SMAP_product,'/'] ; + +L2_version = 'R18290'; %'R17000'; + +out_path = '/discover/nobackup/qliu/matlab/SMAP/L2L4/VOD/QC_frozen_RFI/'; + +L2_dtstep = 10800; + +start_time.year = 2021; +start_time.month = 4; +start_time.day = 1; + +end_time.year = 2022; +end_time.month = 4; +end_time.day = 1; + +M09_Nlon = 3856; +M09_Nlat = 1624; + +M36_Nlon = M09_Nlon/4; +M36_Nlat = M09_Nlat/4; + +if strcmp(SMAP_product(end-1:end),'_E') + out_Nlon = M09_Nlon; + out_Nlat = M09_Nlat; +else + out_Nlon = M36_Nlon; + out_Nlat = M36_Nlat; +end + +% ----------------------------------------------------------------------- +% Read L2 data +for iorb = 1:length(L2_Ascdes) + + date_time = start_time; + date_time.hour = 0; + date_time.min = 0; + date_time.sec = 0; + + fname_L2_pre = []; + + while 1 + + if (date_time.year ==end_time.year && ... + date_time.month==end_time.month && ... + date_time.day ==end_time.day ) + break + end + + outfile_tag = [num2str(date_time.year,'%4.4d'), ... + num2str(date_time.month,'%2.2d'), ... + num2str(date_time.day,'%2.2d')]; + + mat_fname = [out_path,'L2DCA_RTM_',SMAP_product,'_',L2_version,L2_Ascdes{iorb}, outfile_tag,'.mat']; + + if exist(mat_fname,'file') + + disp(['mat file exist ',mat_fname]) + + else + + L2_tau = NaN + ones(out_Nlon,out_Nlat); + L2_omg = NaN + ones(out_Nlon,out_Nlat); + L2_h = NaN + ones(out_Nlon,out_Nlat); + + L2_data_path = [L2_path, '/Y', num2str(date_time.year,'%4.4d'), ... + '/M', num2str(date_time.month, '%2.2d'), ... + '/D', num2str(date_time.day, '%2.2d')]; + + % get list of all files in subdirectory of given date + L2_files_all = dir([L2_data_path, '/SMAP_',SMAP_product,'*',L2_Ascdes{iorb},'*_',L2_version,'_*.h5']); + + % check if multiple versions of the same half orbit file + % only keep the data with the highest version id + + L2_files = {}; + kk = 0; % counter of the final file list + % Remove duplicate files from the list when multiple versions exist + for ff = 1:length(L2_files_all) + % check if v002 or higher exist + if str2num(L2_files_all(ff).name(end-4:end-3)) > 1 + L2_files{kk} = L2_files_all(ff).name; + % if v002 or higher matches previous file in final flist, replace + % previous file in list. Only increase the final file counter when there is no duplicates + if ~strcmp(L2_files{kk}(1:end-5), L2_files_all(ff).name(1:end-5)) + kk = kk + 1; + end + else + kk = kk + 1; + L2_files{kk} = L2_files_all(ff).name; + end + end + + clear L2_fiels_all L2_fname + if ~isempty(L2_files) + + for ifile = 1:length(L2_files) + L2_fname{ifile} = [L2_data_path,'/', L2_files{ifile}]; + end + + if ~isempty(fname_L2_pre) + L2_fname((ifile+1):(ifile+length(fname_L2_pre))) = fname_L2_pre; + end + + fname_L2_pre = []; + + ii = 0; + for ifile = 1: length(L2_fname) + + fname = L2_fname{ifile}; + + disp(fname) + + L2_row = h5read(fname,'/Soil_Moisture_Retrieval_Data/EASE_row_index'); %zero-based + L2_row = L2_row + 1; + L2_col = h5read(fname,'/Soil_Moisture_Retrieval_Data/EASE_column_index'); + L2_col = L2_col + 1; + + L2_utc_seconds = h5read(fname,'/Soil_Moisture_Retrieval_Data/tb_time_seconds'); + + L2_vod = h5read(fname,'/Soil_Moisture_Retrieval_Data/vegetation_opacity_option3'); + fill_value = h5readatt(fname,'/Soil_Moisture_Retrieval_Data/vegetation_opacity_option3','_FillValue'); + L2_vod(L2_vod == fill_value) = NaN; + + L2_alb = h5read(fname,'/Soil_Moisture_Retrieval_Data/albedo_option3'); + fill_value = h5readatt(fname,'/Soil_Moisture_Retrieval_Data/albedo_option3','_FillValue'); + L2_alb(L2_alb == fill_value) = NaN; + + L2_rough = h5read(fname,'/Soil_Moisture_Retrieval_Data/roughness_coefficient_option3'); + fill_value = h5readatt(fname,'/Soil_Moisture_Retrieval_Data/roughness_coefficient_option3','_FillValue'); + L2_rough(L2_rough == fill_value) = NaN; + + % quality flag + L2_qf = h5read(fname,'/Soil_Moisture_Retrieval_Data/retrieval_qual_flag_option3'); + + % surface status land = 0, nonland = 1 + L2_ss = h5read(fname,'/Soil_Moisture_Retrieval_Data/grid_surface_status'); + + % surface flag + L2_sf = h5read(fname,'/Soil_Moisture_Retrieval_Data/surface_flag'); + + L2_rfi_h = h5read(fname,'/Soil_Moisture_Retrieval_Data/tb_qual_flag_h'); + L2_rfi_v = h5read(fname,'/Soil_Moisture_Retrieval_Data/tb_qual_flag_v'); + + % exclude points according to quality flag + if L2_qc_yes + + % QC based on retrieval quality flag + %L2_rt = bitget(L2_qf, 1); % only use retrieval_recommended + L2_rt = bitget(L2_qf, 3); % use retrieval_succeeded + + % QC b ased on surface flag + L2_frozen_model = bitget(L2_sf, 9); % model frozen ground + L2_snow = bitget(L2_sf,6); % snow and ice + L2_pice = bitget(L2_sf,7); % permanent snow and ice + L2_rfi_h_qf = bitget(L2_rfi_h,1); % quality flag RFI H + L2_rfi_v_qf = bitget(L2_rfi_v,1); % quality flag RFI V + idx = find(L2_rt == 0 & L2_frozen_model ==0 & ... + L2_snow == 0 & L2_pice ==0 & L2_ss == 0 & ... + L2_rfi_h_qf == 0 & L2_rfi_v_qf == 0); + + % only keep data/coord that pass QC + L2_vod = L2_vod(idx); + L2_alb = L2_alb(idx); + L2_rough = L2_rough(idx); + L2_row = L2_row(idx); + L2_col = L2_col(idx); + L2_utc_seconds = L2_utc_seconds(idx); + clear idx + end + + if ~isempty(L2_vod) + + % round date_time to nearest 3 hourly UTC + utc_t2k = round(double(L2_utc_seconds)/L2_dtstep)*L2_dtstep; + + [yr, doy, mm, dd, hr, mn] = J2000_to_DateTime( utc_t2k ); + + % use points for current UTC day only + idx = find(yr == date_time.year & mm == date_time.month & ... + dd == date_time.day); + + % points across UTC days will be saved in next daily file + if length(idx) < length(L2_utc_seconds) && ifile <= length(L2_files) + disp('L2 across UTC days') + ii = ii + 1; + fname_L2_pre{ii} = fname; + end + + L2_vod = L2_vod(idx); + L2_alb = L2_alb(idx); + L2_rough = L2_rough(idx); + L2_row = L2_row(idx); + L2_col = L2_col(idx); + L2_utc_seconds = L2_utc_seconds(idx); + hr = hr(idx); + clear idx + + % Map L2 to 2d grid + for k = 1:length(L2_vod) + this_col = L2_col(k); + this_row = L2_row(k); + + L2_tau(this_col, this_row) = L2_vod(k); + L2_omg(this_col, this_row) = L2_alb(k); + L2_h(this_col, this_row) = L2_rough(k); + end + end + end + + else + + disp(['no L2 data found in', L2_data_path]) + pause + end + + save(mat_fname,'L2_tau','L2_omg','L2_h') + clear L2_tau L2_omg L2_h + + end + + date_time = augment_date_time(86400, date_time); + end +end + +% ------------------------EOF----------------------------------------- diff --git a/GEOSldas_App/util/inputs/mwRTM_params/fill_gaps_in_tiledata.m b/GEOSldas_App/util/inputs/mwRTM_params/fill_gaps_in_tiledata.m new file mode 100644 index 0000000..03d5ea3 --- /dev/null +++ b/GEOSldas_App/util/inputs/mwRTM_params/fill_gaps_in_tiledata.m @@ -0,0 +1,94 @@ +function [tile_data_filled] = fill_gaps_in_tiledata(tile_coord, tile_grid_g, tile_data, N_cells, iscube ) + +% Fill missing values in tile-space data with the mean value (excl. NaNs) of surrounding grid cells. +% +% N_cells is number of grid cells averaged in each linear dimension; e.g., N_cells=5 averages across +% a 5-by-5 neighborhood (-2, -1, 0, 1, 2 in each direction). +% +% Uses tile2grid() --> Should work for EASE[_v2] and lat/lon tile spaces. +% Probably needs work for cube-sphere tile space!!! +% +% iscube: required input to alert user that fn is not ready for data in cube-sphere tile space +% +% tile_data[_filled] = N_fields-by-N_tile +% +% Q. Liu, 19 Jul 2022 +% reichle, 29 Jul 2022 - minor clean-up and generalization +% +% ----------------------------------------------------------------------------------------------- + +if ~exist('iscube'), error('Must specify if data is in cube-sphere tile space.'), end + +if iscube, error('Function not ready for data in cube-sphere tile space.'), end + +tc = tile_coord; +tg = tile_grid_g; + +%if strcmp(EASEv2_grid,'M09') +% N_cells = 5; +%elseif strcmp(EASEv2_grid,'M36') +% N_cells = 3; +%else +% error('input grid invalid, use only M09 or M36') +%end + +if size(tile_data,2) ~= tc.N_tile + error('N_tile incorrect, input data size should be [N_fields,N_tile].') +end + +grid_data = tile2grid(tile_data, tc, tg); + +N_f = size(tile_data,1); +N_lon = size(grid_data,1); +N_lat = size(grid_data,2); + +tile_data_filled = tile_data; + +Nshift = floor(N_cells/2); + +for ff = 1:N_f + + grid = grid_data(:,:,ff); + d_sum = zeros(size(grid)); + N_sum = zeros(size(grid)); + + for xshift = -Nshift:Nshift + for yshift = -Nshift:Nshift + + shift = circshift(grid,[xshift, yshift]); + + if xshift < 0 + shift(end+xshift+1:end,:) = NaN; + elseif xshift > 0 + shift(1:xshift, :) = NaN; + end + if yshift < 0 + shift(:,end+yshift+1:end) = NaN; + elseif yshift > 0 + shift(:,1:yshift ) = NaN; + end + + d_sum(~isnan(shift)) = d_sum(~isnan(shift)) + shift(~isnan(shift)); + N_sum(~isnan(shift)) = N_sum(~isnan(shift)) + 1; + + clear shift + end + end + + coarse = d_sum ./ N_sum; + coarse(isinf(coarse)) = NaN; + + idx_nan = find(isnan(tile_data(ff,:))); + + for i = 1:length(idx_nan) + + tile_data_filled(ff,idx_nan(i)) ... + = ... + coarse(tc.i_indg(idx_nan(i))+1, tc.j_indg(idx_nan(i))+1); + end + + clear grid d_sum smooth N_sum + +end + +% ===================== EOF ===================================== diff --git a/GEOSldas_App/util/inputs/mwRTM_params/get_L2_RTM_constants_tile_data.m b/GEOSldas_App/util/inputs/mwRTM_params/get_L2_RTM_constants_tile_data.m new file mode 100644 index 0000000..3125e89 --- /dev/null +++ b/GEOSldas_App/util/inputs/mwRTM_params/get_L2_RTM_constants_tile_data.m @@ -0,0 +1,218 @@ + +function out_mwRTM = ... + get_L2_RTM_constants_tile_data(tile_coord,L2_file_tag,... + L2_version,start_time, end_time) + +% function to compute the 2 RTM constant variables: Albedo and Roughness(H) based on the +% preprocessed data. Constants are taken as the long term temporal mean +% with maximum coverage across Asc/Desc passes. + +% Q. Liu 18 Jul 2022 + +L2_Ascdes_all = {'_A_','_D_'}; + +out_Para = {'Albedo','Roughness'}; + +%L2_file_tag = 'L2_SM_P_E'; +%L2_version = 'R18290'; + +if strcmp(L2_file_tag(end-1:end),'_E') + resolution = 'M09'; + out_Nlon = 3856; + out_Nlat = 1624; +else + resolution = 'M36'; + out_Nlon = 964; + out_Nlat = 406; +end + +% provide a GEOSldas with proper tile information +%if strcmp(resolution,'M36') +% L4_path = '/discover/nobackup/projects/gmao/smap/SMAP_Nature/SMAP_Nature_v9.x/'; +% L4_version = 'SMAP_Nature_v9.1_M36'; +% based_on_h5 = 0; +% out_Nlon = 3856/4; +% out_Nlat = 1624/4; +% ftilecoord = [L4_path,L4_version,'/output/SMAP_EASEv2_M36_GLOBAL/rc_out/', ... +% L4_version,'.ldas_tilecoord.bin']; +%else +% L4_path = '/css/smapl4/public/L4_Products/L4_SM/'; +% L4_version = 'Vv6030'; +% based_on_h5 = 1; +% out_Nlon = 3856; +% out_Nlat = 1624; +% ftilecoord = [L4_path,L4_version,'/rc_out/SPL4SM_', L4_version,'.ldas_tilecoord.bin']; + +%end + +% daily mat file path +mat_path = '/discover/nobackup/qliu/matlab/SMAP/L2L4/VOD/QC_frozen_RFI/'; + +int_precision = 'int32'; +float_precision = 'float32'; + +% read tile info for binary output +tc = tile_coord; +%tc = read_tilecoord( ftilecoord); + +% 2 parameters +for iPara = 1:length(out_Para) + + % 2 overpasses + for iAD = 1:2 + + L2_Ascdes = L2_Ascdes_all{iAD}; + L2_qc_yes = 1; + + dtstep = 10800; + + % time period for computing climatology + %start_time.year = 2015; + %start_time.month = 4; + %start_time.day = 1; + +% start_time.hour = 1; +% start_time.min = 30; +% start_time.sec = 0; +% +% end_time.year = 2022; +% end_time.month = 4; +% end_time.day = 1; +% end_time.hour = start_time.hour; +% end_time.min = start_time.min; +% end_time.sec = start_time.sec; +% +% start_time = get_dofyr_pentad(start_time); +% end_time = get_dofyr_pentad(end_time); + + % ----------------------------------------------------------------------- + % read time series of SMAP L2 fields + if end_time.month ==1 + time_tag = [num2str(start_time.year,'%4.4d'),num2str(start_time.month,'%2.2d'), ... + '_',num2str(end_time.year-1,'%4.4d'),'12']; + else + time_tag = [num2str(start_time.year,'%4.4d'),num2str(start_time.month,'%2.2d'), ... + '_',num2str(end_time.year,'%4.4d'),num2str(end_time.month-1,'%2.2d')]; + end + + % file to save long term mean tile data + fname_clim = [mat_path,'/',out_Para{iPara},'_clim_L2_',L2_version,L2_Ascdes,resolution,'tile_',time_tag,'.bin']; + + L2_para_clim_sum = zeros(out_Nlon,out_Nlat); + N_L2_clim_sum = zeros(out_Nlon,out_Nlat); + + % only do time loop if no previously saved climatology file is found + if ~exist(fname_clim,'file') + + start_time.day = 1; + start_time.hour = 0; + start_time.min = 0; + start_time.sec = 0; + + end_time.day = 1; + date_time = start_time; + + while 1 + + if (date_time.year ==end_time.year && ... + date_time.month==end_time.month && ... + date_time.day ==end_time.day ) + break + end + + dt_tag = [num2str(date_time.year,'%4.4d'), ... + num2str(date_time.month,'%2.2d'), ... + num2str(date_time.day,'%2.2d')]; + + mat_fname = [mat_path,'L2DCA_RTM_',L2_file_tag,'_',L2_version,L2_Ascdes, dt_tag,'.mat']; + + if exist(mat_fname,'file') + + disp(['loading ', mat_fname]) + if contains(fname_clim,'Albedo_') + load(mat_fname,'L2_omg') + L2_para = L2_omg; clear L2_omg + elseif contains(fname_clim,'Roughness_') + load(mat_fname,'L2_h') + L2_para = L2_h; clear L2_h + else + error(['unknown clim fname ',fname_clim]) + end + + % compute climatology + tmp = L2_para; + tmp(isnan(tmp)) = 0; + L2_para_clim_sum(:,:) = squeeze(L2_para_clim_sum(:,:)) + tmp; clear tmp + + tmp = ~isnan(L2_para); + N_L2_clim_sum(:,:) = squeeze(N_L2_clim_sum(:,:)) + tmp; clear tmp + + clear L2_para + + else + + error('daily mat file not found') + + end + + date_time = augment_date_time(86400, date_time); + + end + + L2_para_clim = L2_para_clim_sum ./N_L2_clim_sum; + L2_para_clim(N_L2_clim_sum < 1) = NaN; + + % regrid to til grid + + L2_para_tile = NaN + ones(tc.N_tile,1); + for k = 1:tc.N_tile + L2_para_tile(k,1) = L2_para_clim(tc.i_indg(k)+1, tc.j_indg(k)+1); + end + + ifp = fopen(fname_clim,'w','l'); + + + tile_data = L2_para_tile; % should be non-negative? + + tile_data(isnan(tile_data)) = 1.e15; + + fwrite( ifp, tc.N_tile*4, int_precision );% fortran_tag + fwrite( ifp, tile_data(:), float_precision ); + fwrite( ifp, tc.N_tile*4, int_precision );% fortran_tag + + clear tile_data + + fclose(ifp) + end + + end + + % combine Asc and Desc data for maximum spatial coverage + data_clim_tile = NaN + ones(tc.N_tile,2); + for iAD = 1:2 + + L2_Ascdes = L2_Ascdes_all{iAD}; + + fname = [mat_path,'/',out_Para{iPara},'_clim_L2_',L2_version,L2_Ascdes,resolution,'tile_',time_tag,'.bin']; + + disp(['read ',fname]) + ifp = fopen(fname,'r','l'); + + fortran_tag = fread( ifp, 1, int_precision ); + tmp = fread( ifp, tc.N_tile, float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + data_clim_tile(:,iAD) = tmp; + + fclose(ifp); + end + + % fllValue = 1.e15 + data_clim_tile(data_clim_tile > 10.) = NaN; + data_clim_tile(data_clim_tile < 0.) = 0.; % set small negative values to 0 + + % averaging A, D values + tile_data = mean(data_clim_tile,2,"omitnan"); + + eval(['out_mwRTM.',out_Para{iPara},'=tile_data;']) +end diff --git a/GEOSldas_App/util/inputs/mwRTM_params/get_mwRTM_lookup.m b/GEOSldas_App/util/inputs/mwRTM_params/get_mwRTM_lookup.m new file mode 100644 index 0000000..341d05c --- /dev/null +++ b/GEOSldas_App/util/inputs/mwRTM_params/get_mwRTM_lookup.m @@ -0,0 +1,167 @@ +function [ veg_lookup, soil_lookup ] = get_mwRTM_lookup(option); + +% Gabrielle De Lannoy, GSFC, 22Jul11 +% +% Same lookup table as in LDASsa +% +% Updated: +% GDL, 6 Sept 2011: 16 IGBP vegetation classes +% GDL, 27 Sept 2011: h=1.2 for conif forest (paper J. Grant), +% rather than h=1.6 (CMEM) +%================================================================== +% VEGETATION +%================================================================== +% +% N_vegcls = 8; +% +% ! 1 Broadleaf evergreen trees 0.1 0.1 0.12 0.14 0.10 0.30 1.0 0.0 +% ! 2 Broadleaf deciduous trees 0.1 0.1 0.12 0.14 0.10 0.2 1.0 2.0 +% ! 3 Needleleaf trees 0.1 0.1 0.12 0.12 0.08 0.1 1.75 0.0 +% ! 4 Grassland 0.1 0.1 0.05 0.11 0.09 0.15 1.0 0.0 +% ! 5 Broadleaf shrubs 0.1 0.1 0.12 0.12 0.10 0.2 1.0 1.0 +% ! 6 Dwarf trees 0.1 0.1 0.05 0.11 0.09 0.15 1.0 1.0 +% ! 7 Bare soil 0.1 0.1 0.00 0.00 0.00 0. 0.0 -1.0 +% ! 8 Desert soil 0.1 0.1 0.00 0.00 0.00 0. 0.0 -1.0 +% +% veg_lookup.rgh_hmin = [ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 ]*0.5; +% veg_lookup.rgh_hmax = [ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 ]*0.5; +% veg_lookup.omega = [ 0.12, 0.12, 0.12, 0.05, 0.12, 0.05, 0.00, 0.00 ]; +% veg_lookup.lewt = [ 0.30, 0.20, 0.10, 0.15, 0.20, 0.15, 0.00, 0.00 ]; +% veg_lookup.bh = [ 0.10, 0.10, 0.08, 0.09, 0.10, 0.09, 0.00, 0.00 ]; +% veg_lookup.bv = [ 0.14, 0.14, 0.12, 0.11, 0.12, 0.11, 0.00, 0.00 ]; +% veg_lookup.rgh_Nrh = [ 1.00, 1.00, 1.75, 1.00, 1.00, 1.00, 0.00, 0.00 ]; +% veg_lookup.rgh_Nrv = [ 0.00, 2.00, 0.00, 0.00, 1.00, 1.00, -1.00, -1.00 ]; +% veg_lookup.tag = {'rgh_hmin [-]', 'h_ [-]', '\omega [-]', 'lewt [-]',... +% 'b_h [-]', 'b_v [-]', 'Nr_h [-]', 'Nr_h [-]'}; + +%1 Evergreen Needleleaf Forest +%2 Evergreen Broadleaf Forest +%3 Deciduous Needleleaf Forest +%4 Deciduous Broadleaf Forest +%5 Mixed Forest +%6 Closed Shrublands +%7 Open Shrublands +%8 Woody Savannas +%9 Savannas +%10 Grasslands +%11 Permanent Wetlands +%12 Croplands +%13 Urban and Built-Up +%14 Cropland & Natural Vegetation +%15 Snow and Ice +%16 Barren or Sparsely Vegetated + + +N_vegcls = 16; +veg_lookup.tag = {'rgh_hmin [-]', 'rgh_hmax [-]', '\omega [-]', 'lewt [-]',... + 'b_h [-]', 'b_v [-]', 'Nr_h [-]', 'Nr_h [-]'}; + +if (strcmp(option,'CMEM') || strcmp(option,'Lit2')) + + %ECMWF CMEM-code + %veg_lookup.rgh_hmin = [ 1.6, 1.3, 1.6, 1, 1.3, 0.7, 0.7, 0.7, 0.5, 0.1, 0.1, 0.5, 0, 0.7, 0, 0.1 ]; + %veg_lookup.rgh_hmax = [ 1.6, 1.3, 1.6, 1, 1.3, 0.7, 0.7, 0.7, 0.5, 0.1, 0.1, 0.5, 0, 0.7, 0, 0.1 ]; + veg_lookup.rgh_hmin = [ 1.2, 1.3, 1.2, 1, 1.3, 0.7, 0.7, 0.7, 0.5, 0.1, 0.1, 0.5, 0, 0.7, 0, 0.1 ]; + veg_lookup.rgh_hmax = [ 1.2, 1.3, 1.2, 1, 1.3, 0.7, 0.7, 0.7, 0.5, 0.1, 0.1, 0.5, 0, 0.7, 0, 0.1 ]; + + veg_lookup.omega = [ 0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05 ]; + veg_lookup.lewt = [ 1, 1, 1, 1, 1, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0, 0.5, 0, 0 ]; + veg_lookup.bh = [ 0.33,0.33,0.33,0.33,0.33, 0.3, 0.3, 0.3, 0.2, 0.2, 0.2, 0.15, 0, 0.15, 0, 0 ]; + veg_lookup.bv = [ 0.33,0.33,0.33,0.33,0.33, 0.3, 0.3, 0.3, 0.2, 0.2, 0.2, 0.15, 0, 0.15, 0, 0 ]; + veg_lookup.rgh_Nrh = [ 1, 1.75,1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 ]; + veg_lookup.rgh_Nrv = [ 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, -1, 1, -1, 1,-1 ]; + veg_lookup.st_scale = [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]; + +elseif (strcmp(option,'CMEM_SMOS') || strcmp(option,'Lit3')) + + %ECMWF SMOS monitoring-setup + veg_lookup.rgh_hmin = [ 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66 ]; + veg_lookup.rgh_hmax = [ 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66, 1.66 ]; + + veg_lookup.omega = [ 0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05 ]; + veg_lookup.lewt = [ 1, 1, 1, 1, 1, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0, 0.5, 0, 0 ]; + veg_lookup.bh = [ 0.33,0.33,0.33,0.33,0.33, 0.3, 0.3, 0.3, 0.2, 0.2, 0.2, 0.15,0, 0.15,0, 0 ]; + veg_lookup.bv = [ 0.33,0.33,0.33,0.33,0.33, 0.3, 0.3, 0.3, 0.2, 0.2, 0.2, 0.15,0, 0.15,0, 0 ]; + veg_lookup.rgh_Nrh = [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]; + veg_lookup.rgh_Nrv = [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]; + veg_lookup.st_scale = [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]; + +elseif (strcmp(option,'SMAP') || strcmp(option,'Lit1')) + + %Peggy O'Neill's SMAP ATBD, Table 2 + veg_lookup.rgh_hmin = [ 0.16, 0.16, 0.16, 0.16, 0.16, 0.11, 0.11, 0.125, 0.156, 0.156, 0.156, 0.108, 0, 0.13, 0, 0.15 ]; + veg_lookup.rgh_hmax = [ 0.16, 0.16, 0.16, 0.16, 0.16, 0.11, 0.11, 0.125, 0.156, 0.156, 0.156, 0.108, 0, 0.13, 0, 0.15 ]; + veg_lookup.omega = [ 0.12, 0.12, 0.12, 0.12, 0.08, 0.05, 0.05, 0.12, 0.08, 0.05, 0.05, 0.05, 0, 0.065,0, 0 ]; + veg_lookup.lewt = [ 0.3, 0.3, 0.2, 0.2, 0.2, 0.2, 0.2, 0.15, 0.15, 0.15, 0.15, 0.15, 0, 0.15, 0, 0 ]; + veg_lookup.bh = [ 0.1, 0.1, 0.12, 0.12, 0.12, 0.11, 0.11, 0.11, 0.11, 0.1, 0.1, 0.11, 0, 0.11, 0, 0 ]; + veg_lookup.bv = [ 0.1, 0.1, 0.12, 0.12, 0.12, 0.11, 0.11, 0.11, 0.11, 0.1, 0.1, 0.11, 0, 0.11, 0, 0 ]; + veg_lookup.rgh_Nrh = [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]; + veg_lookup.rgh_Nrv = [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]; + veg_lookup.st_scale = [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]; + +elseif (strcmp(option,'Lit4')) + + %ECMWF CMEM-code; same as Lit2, but with new lewt values to go with new LAI values + %veg_lookup.rgh_hmin = [ 1.6, 1.3, 1.6, 1, 1.3, 0.7, 0.7, 0.7, 0.5, 0.1, 0.1, 0.5, 0, 0.7, 0, 0.1 ]; + %veg_lookup.rgh_hmax = [ 1.6, 1.3, 1.6, 1, 1.3, 0.7, 0.7, 0.7, 0.5, 0.1, 0.1, 0.5, 0, 0.7, 0, 0.1 ]; + veg_lookup.rgh_hmin = [ 1.2, 1.3, 1.2, 1, 1.3, 0.7, 0.7, 0.7, 0.5, 0.1, 0.1, 0.5, 0, 0.7, 0, 0.1 ]; + veg_lookup.rgh_hmax = [ 1.2, 1.3, 1.2, 1, 1.3, 0.7, 0.7, 0.7, 0.5, 0.1, 0.1, 0.5, 0, 0.7, 0, 0.1 ]; + + veg_lookup.omega = [ 0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05 ]; + veg_lookup.lewt = [ 1.9, 1.5, 1.5, 1.5, 1.7, 0.9, 0.8, 0.9, 0.8, 0.9, 0.8, 0.8, 0, 0.9, 0, 0 ]; + %veg_lookup.lewt = [ 1, 1, 1, 1, 1, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0, 0.5, 0, 0 ]; + veg_lookup.bh = [ 0.33,0.33,0.33,0.33,0.33, 0.3, 0.3, 0.3, 0.2, 0.2, 0.2, 0.15, 0, 0.15, 0, 0 ]; + veg_lookup.bv = [ 0.33,0.33,0.33,0.33,0.33, 0.3, 0.3, 0.3, 0.2, 0.2, 0.2, 0.15, 0, 0.15, 0, 0 ]; + veg_lookup.rgh_Nrh = [ 1, 1.75,1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 ]; + veg_lookup.rgh_Nrv = [ 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, -1, 1, -1, 1,-1 ]; + veg_lookup.st_scale = [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]; + + +elseif (strcmp(option,'Cal3c')) + + load('/hydro/gdelanno/proc_analysis/initial_lookup_3c_rgh_hmin_rgh_hmax_rgh_Nrh_rgh_Nrv_lewt_omega_bh_bv_st_scale.mat'); + +%was Cal2c before +elseif (strcmp(option,'CalD2')) + + load('/hydro/gdelanno/proc_analysis/initial_lookup_D2_rgh_hmin_rgh_hmax_rgh_Nrh_rgh_Nrv_lewt_omega_bh_bv_st_scale.mat'); + +else + + error(['ERROR: Option ',option,' does not exist']) + +end + +%================================================================== +% SOIL +%================================================================== +%1 Sand +%2 Loamy Sand +%3 Sandy Loam +%4 Loam (F) ==> Silt Loam +%5 Silt Loam (F) ==> Silt +%6 Silt (F) ==> Loam +%7 Sandy Clay Loam +%8 Clay Loam (F) ==> Silty Clay Loam +%9 Silty Clay Loam (F) ==> Clay Loam +%10 Sandy Clay Loam +%11 Silty Clay Loam +%12 Clay + + +N_soilcls = 12; + +soil_lookup.sf = [.92, .82, .58, .17, .10, .43, .58, .10, .32, .52, .06, .22]; +soil_lookup.cf = [.03, .06, .10, .13, .05, .18, .27, .34, .34, .42, .47, .58]; +soil_lookup.fc = [0.132, 0.156, 0.196, 0.27 , 0.361, 0.25 , 0.253, 0.334, 0.301, 0.288, 0.363, 0.353]; +soil_lookup.wp = [0.033, 0.051, 0.086, 0.169, 0.045, 0.148, 0.156, 0.249, 0.211, 0.199, 0.286, 0.276]; +soil_lookup.poros = [0.373, 0.386, 0.419, 0.476, 0.471, 0.437, 0.412, 0.478, 0.447, 0.415, 0.478, 0.45]; +soil_lookup.b = [3.3, 3.8, 4.34, 5.25, 3.63, 5.96, 7.32, 8.41, 8.34, 9.7, 10.78, 12.93]; +soil_lookup.PsiS = [-0.05, -0.07, -0.16, -0.65, -0.84, -0.24, -0.12, -0.63, -0.28, -0.12, -0.58, -0.27]; +soil_lookup.Ksat = [2.45E-05, 1.75E-05, 8.35E-06, 2.36E-06, 1.10E-06, 4.66E-06, 6.31E-06, 1.44E-06, 2.72E-06, 4.25E-06, 1.02E-06, 1.33E-06]; +soil_lookup.tag = {'Sand [-]', 'Clay [-]', 'Field Capacity [m3/m3]', ... + 'Wilting Point [m3/m3]', 'Porosity [m3/m3]', 'b [ ]', 'Psisat [m]', 'Ksat [m/s]'}; + +end + +%==========================EOF===================================== diff --git a/GEOSldas_App/util/inputs/mwRTM_params/get_mwRTM_vegcls_based.m b/GEOSldas_App/util/inputs/mwRTM_params/get_mwRTM_vegcls_based.m new file mode 100644 index 0000000..1e2ef02 --- /dev/null +++ b/GEOSldas_App/util/inputs/mwRTM_params/get_mwRTM_vegcls_based.m @@ -0,0 +1,129 @@ +function [mwRTMparam] = get_mwRTM_vegcls_based( tile_coord, dominant_M36vegcls,resolution) + +% Helper function to get RTM parameter values based on the vegcls lookup table +% before writing in the mwRTM_params.nc4 file. The function is called in +% Write_mwRTM_nc4_file.m + +% Q. Liu 20 Jul 2022 + + +% Vegetation-based RTM-parameters +lookup_option = 'Lit4'; + +%---------------------------------------------------------------------------- + +% Vegetation classes +% read in M36 & M09 vegcls for both M36 and M09 mwRTM +vegcls_fname = '/discover/nobackup/qliu/gdelanno_RTM/SMAP_aux/EASEV2/dominantIGBP36km.406x964.uint8'; + +ifp = fopen( vegcls_fname, 'r', 'b' ); +veg_clsM36 = fread( ifp, [406 964] ,'uint8'); +fclose(ifp); + +vegcls_fname = '/discover/nobackup/qliu/gdelanno_RTM/SMAP_aux/EASEV2/dominantIGBP09km.1624x3856.uint8'; + +ifp = fopen( vegcls_fname, 'r', 'b' ); +veg_clsM09 = fread( ifp, [1624 3856] ,'uint8'); +fclose(ifp); + +% list of parameters can be based on vegcls lookup table +fn_mwRTMparam = { ... + 'vegcls', ... + 'rgh_Nrh', 'rgh_Nrv', ... + 'rgh_polmix','omega', 'bh', 'bv', 'lewt'}; + +for i=1:length(fn_mwRTMparam) + + mwRTMparam.(fn_mwRTMparam{i}) = [NaN+zeros(length(tile_coord.N_tile),1)]; + +end + +[ veg_lookup, tmp ] = get_mwRTM_lookup(lookup_option); +clear tmp + +%==================================================================== + +%order of tiles depend on LDASsa-output; tile_coord.txt +%output from this code should *always* be sorted as [1:N_tile], that is - +%even if re-ordered output from an LDASsa-run is used as input, the +%resulting mwRTMparam.xxxx(:) is always monotonically increasingly sorted + +[row_M09,col_M09] = EASEv2_latlon2ind(tile_coord.com_lat,tile_coord.com_lon,'M09',1); +row_M09 = row_M09 +1; +col_M09 = col_M09 +1; + +[row_M36,col_M36] = EASEv2_latlon2ind(tile_coord.com_lat,tile_coord.com_lon,'M36',1); +row_M36 = row_M36 + 1; +col_M36 = col_M36 + 1; + +for tile = 1 : tile_coord.N_tile + + %since m10_p3, the tile-order has changed, so tile<>tile_id ! + tileid = tile_coord.tile_id(tile); + + if contains(resolution,'_M09') && ~dominant_M36vegcls + + jj = row_M09(tile); + ii = col_M09(tile); + i_vegcls = int32(veg_clsM09(jj,ii)); + + else + + % First, rely on M36 database + jj = row_M36(tile); + ii = col_M36(tile); + i_vegcls = int32(veg_clsM36(jj,ii)); + + % Next, fill missing vegcls with the dominant vegcls of the + % M09 grids + + if (i_vegcls<=0 || i_vegcls>200 || isnan(i_vegcls)) + + if contains(resolution,'M09') + tmp_ind = find(row_M36 == jj & col_M36 == ii); + for t = 1:length(tmp_ind) + i_vegcls(t) = int32(veg_clsM09(row_M09(tmp_ind(t)),col_M09(tmp_ind(t)))); + end + else + t=0; + for j1 = ((jj-1)*4+1):(jj*4) + for i1 = ((ii-1)*4+1):(ii*4) + t = t+1; + i_vegcls(t) = int32(veg_clsM09(j1,i1)); + end + end + end + + ind_veg = find(~(i_vegcls<=0 | i_vegcls>200 | isnan(i_vegcls))); + if ~isempty(ind_veg) + i_vegcls = i_vegcls(ind_veg); + end + i_vegcls = mode(i_vegcls); + + end + end + %====for valid vegetation classes in IGBP:==== + if (i_vegcls > 0 ) + + mwRTMparam.vegcls(tileid,1) = i_vegcls; %IGBP + + mwRTMparam.rgh_Nrh(tileid,1) = veg_lookup.rgh_Nrh(i_vegcls); + mwRTMparam.rgh_Nrv(tileid,1) = veg_lookup.rgh_Nrv(i_vegcls); + + mwRTMparam.rgh_polmix(tileid,1) = 0; + mwRTMparam.omega(tileid,1) = veg_lookup.omega(i_vegcls); + mwRTMparam.bh(tileid,1) = veg_lookup.bh(i_vegcls); + mwRTMparam.bv(tileid,1) = veg_lookup.bv(i_vegcls); + mwRTMparam.lewt(tileid,1) = veg_lookup.lewt(i_vegcls); + + else + + for i = 1:length(fn_mwRTMparam) + mwRTMparam.(fn_mwRTMparam{i})(tileid,1) = NaN; + end + + end + +end + +%===============================EOF====================================== diff --git a/GEOSldas_App/util/inputs/mwRTM_params/mwrtm_bin2nc4.F90 b/GEOSldas_App/util/inputs/mwRTM_params/mwrtm_bin2nc4.F90 new file mode 100644 index 0000000..114f24b --- /dev/null +++ b/GEOSldas_App/util/inputs/mwRTM_params/mwrtm_bin2nc4.F90 @@ -0,0 +1,241 @@ +!This program converts original mwrtm_param.bin to nc4. +!The original mwrtm_param.bin is the same order as the tile file and encode with big endian +!for example /gpfsm/dnb31/gdelanno/input/RTM_parms/EASEv2/L4SM_v001_Lit4_CalD0/SMAP_EASEv2_M36/mwRTM_param.bin +! +! Note: "mwrtm_param" file only includes time-invariant mwRTM parameters (i.e., excl. vegopacity) +! -reichle, 21 Feb 2022 +! +PROGRAM mwrtm_bin2nc4 + use mwRTM_types, only: mwRTM_param_type + use mwRTM_types, only: mwRTM_param_nodata_check + + implicit none + INCLUDE 'netcdf.inc' + + integer :: i,k, n, command_argument_count, NTILES + integer :: NCFOutID, Vid, STATUS, CellID, TimID, nVars + character(512):: Usage="mwrtm_bin2nc4.x mwrtm_BINFILE mwRTM_param.nc4" + character(512):: BINFILE, MWRTMNC4, arg(3) + real, allocatable, dimension (:) :: var + integer, allocatable,dimension (:) :: NT + character(len=:),allocatable :: shnms(:) + type(mwRTM_param_type), allocatable :: mwp(:) + integer :: unitnum + logical :: mwp_nodata + + + nVars = 18 + shnms = [ & + 'MWRTM_VEGCLS ',& + 'MWRTM_SOILCLS ',& + 'MWRTM_SAND ',& + 'MWRTM_CLAY ',& + 'MWRTM_POROS ',& + 'MWRTM_WANGWT ',& + 'MWRTM_WANGWP ',& + 'MWRTM_RGHHMIN ',& + 'MWRTM_RGHHMAX ',& + 'MWRTM_RGHWMIN ',& + 'MWRTM_RGHWMAX ',& + 'MWRTM_RGHNRH ',& + 'MWRTM_RGHNRV ',& + 'MWRTM_RGHPOLMIX',& + 'MWRTM_OMEGA ',& + 'MWRTM_BH ',& + 'MWRTM_BV ',& + 'MWRTM_LEWT '] + + ! processing command line agruments + I = command_argument_count() + + if( I /=2 ) then + print *, "Wrong Number of arguments: ", i + print *, trim(Usage) + stop + end if + + do n=1,I + call get_command_argument(n,arg(n)) + enddo + + read(arg(1),'(a)') BINFILE + read(arg(2),'(a)') MWRTMNC4 + + print *,trim(BINFILE) + print *,trim(MWRTMNC4) + + ! reading mwrtm_bin + + unitnum = 10 + open (unitnum, file = trim(BINFILE), CONVERT='BIG_ENDIAN',form = 'unformatted', action ='read') + read (unitnum) NTILES + print*, "Ntiles", NTILES + allocate(var(NTILES)) + allocate(NT(NTILES)) + + status = NF_CREATE (trim(MWRTMNC4), NF_NETCDF4, NCFOutID) + status = NF_DEF_DIM(NCFOutID, 'tile' , NTILES, CellID) + + do n = 1, nVars + + status = NF_DEF_VAR(NCFOutID,trim(shnms(n)) , NF_FLOAT, 1 ,CellID, vid) + status = NF_PUT_ATT_TEXT(NCFOutID, vid, 'long_name', & + LEN_TRIM(getAttribute(shnms(n), LNAME = 1)), & + getAttribute(shnms(n), LNAME = 1) ) + status = NF_PUT_ATT_TEXT(NCFOutID, vid, 'units', & + LEN_TRIM(getAttribute(shnms(n), UNT = 1)), & + getAttribute(shnms(n), UNT = 1)) + end do + + ! reading and writing + ! tile id + read(unitnum) NT ! read off the tile id + do i= 1, NTILES + if (i /= NT(i)) stop "not original one" + enddo + + allocate(mwp(NTILES)) + + read (unitnum) NT; mwp(1:NTILES)%vegcls = NT(1:NTILES) + read (unitnum) NT; mwp(1:NTILES)%soilcls = NT(1:NTILES) + + read (unitnum) VAR; mwp(1:NTILES)%sand = VAR(1:NTILES) + read (unitnum) VAR; mwp(1:NTILES)%clay = VAR(1:NTILES) + + read (unitnum) VAR; mwp(1:NTILES)%poros = VAR(1:NTILES) + + read (unitnum) VAR; mwp(1:NTILES)%wang_wt = VAR(1:NTILES) + read (unitnum) VAR; mwp(1:NTILES)%wang_wp = VAR(1:NTILES) + + read (unitnum) VAR; mwp(1:NTILES)%rgh_hmin = VAR(1:NTILES) + read (unitnum) VAR; mwp(1:NTILES)%rgh_hmax = VAR(1:NTILES) + read (unitnum) VAR; mwp(1:NTILES)%rgh_wmin = VAR(1:NTILES) + read (unitnum) VAR; mwp(1:NTILES)%rgh_wmax = VAR(1:NTILES) + read (unitnum) VAR; mwp(1:NTILES)%rgh_Nrh = VAR(1:NTILES) + read (unitnum) VAR; mwp(1:NTILES)%rgh_Nrv = VAR(1:NTILES) + read (unitnum) VAR; mwp(1:NTILES)%rgh_polmix = VAR(1:NTILES) + + read (unitnum) VAR; mwp(1:NTILES)%omega = VAR(1:NTILES) + + read (unitnum) VAR; mwp(1:NTILES)%bh = VAR(1:NTILES) + read (unitnum) VAR; mwp(1:NTILES)%bv = VAR(1:NTILES) + read (unitnum) VAR; mwp(1:NTILES)%lewt = VAR(1:NTILES) + + do i = 1, NTILES + call mwRTM_param_nodata_check( mwp(i), mwp_nodata ) + enddo + + VAR = real(mwp(1:NTILES)%vegcls) + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,trim(shnms(1))) ,(/1/),(/NTILES/),var ) + VAR = real(mwp(1:NTILES)%soilcls) + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,trim(shnms(2))) ,(/1/),(/NTILES/),var ) + + VAR = mwp(1:NTILES)%sand + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,trim(shnms(3))) ,(/1/),(/NTILES/),var ) + VAR = mwp(1:NTILES)%clay + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,trim(shnms(4))) ,(/1/),(/NTILES/),var ) + + VAR = mwp(1:NTILES)%poros + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,trim(shnms(5))) ,(/1/),(/NTILES/),var ) + + VAR = mwp(1:NTILES)%wang_wt + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,trim(shnms(6))) ,(/1/),(/NTILES/),var ) + VAR = mwp(1:NTILES)%wang_wp + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,trim(shnms(7))) ,(/1/),(/NTILES/),var ) + + VAR = mwp(1:NTILES)%rgh_hmin + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,trim(shnms(8))) ,(/1/),(/NTILES/),var ) + VAR = mwp(1:NTILES)%rgh_hmax + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,trim(shnms(9))) ,(/1/),(/NTILES/),var ) + VAR = mwp(1:NTILES)%rgh_wmin + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,trim(shnms(10))) ,(/1/),(/NTILES/),var ) + VAR = mwp(1:NTILES)%rgh_wmax + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,trim(shnms(11))) ,(/1/),(/NTILES/),var ) + VAR = mwp(1:NTILES)%rgh_Nrh + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,trim(shnms(12))) ,(/1/),(/NTILES/),var ) + VAR = mwp(1:NTILES)%rgh_Nrv + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,trim(shnms(13))) ,(/1/),(/NTILES/),var ) + VAR = mwp(1:NTILES)%rgh_polmix + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,trim(shnms(14))) ,(/1/),(/NTILES/),var ) + + VAR = mwp(1:NTILES)%omega + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,trim(shnms(15))) ,(/1/),(/NTILES/),var ) + + VAR = mwp(1:NTILES)%bh + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,trim(shnms(16))) ,(/1/),(/NTILES/),var ) + VAR = mwp(1:NTILES)%bv + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,trim(shnms(17))) ,(/1/),(/NTILES/),var ) + VAR = mwp(1:NTILES)%lewt + status = NF_PUT_VARA_REAL(NCFOutID,VarID(NCFOutID,trim(shnms(18))) ,(/1/),(/NTILES/),var ) + + + STATUS = NF_CLOSE (NCFOutID) + close (10) + + contains + + ! ---------------------------------------------------------------------- + + integer function VarID (NCFID, VNAME) + + integer, intent (in) :: NCFID + character(*), intent (in) :: VNAME + integer :: status + + STATUS = NF_INQ_VARID (NCFID, trim(VNAME) ,VarID) + IF (STATUS .NE. NF_NOERR) & + CALL HANDLE_ERR(STATUS, trim(VNAME)) + + end function VarID + + ! ----------------------------------------------------------------------- + + SUBROUTINE HANDLE_ERR(STATUS, Line) + + INTEGER, INTENT (IN) :: STATUS + CHARACTER(*), INTENT (IN) :: Line + + IF (STATUS .NE. NF_NOERR) THEN + PRINT *, trim(Line),': ',NF_STRERROR(STATUS) + STOP 'Stopped' + ENDIF + + END SUBROUTINE HANDLE_ERR + + ! *********************************************************************** + + FUNCTION getAttribute (SHORT_NAME, LNAME, UNT) result (str_atr) + + character(*), intent(in) :: SHORT_NAME + integer, intent (in), optional :: LNAME, UNT + character(128) :: str_atr, LONG_NAME, UNITS + + SELECT case (trim(SHORT_NAME)) + case('MWRTM_VEGCLS'); LONG_NAME = 'L-band RTM model: Vegetation class. Type is Unsigned32'; UNITS = '1' + case('MWRTM_SOILCLS'); LONG_NAME = 'L-band RTM model: Soil class. Type is Unsigned32'; UNITS = '1' + case('MWRTM_SAND'); LONG_NAME = 'L-band RTM model: Sand fraction'; UNITS = '1' + case('MWRTM_CLAY'); LONG_NAME = 'L-band RTM model: Clay fraction'; UNITS = '1' + case('MWRTM_POROS'); LONG_NAME = 'L-band RTM model: Porosity'; UNITS = 'm3 m-3' + case('MWRTM_WANGWT'); LONG_NAME = 'L-band RTM model: Wang dielectric model transition soil moisture'; UNITS = 'm3 m-3' + case('MWRTM_WANGWP'); LONG_NAME = 'L-band RTM model: Wang dielectric model wilting point soil moisture';UNITS = 'm3 m-3' + case('MWRTM_RGHHMIN'); LONG_NAME = 'L-band RTM model: Minimum microwave roughness parameter'; UNITS = '1' + case('MWRTM_RGHHMAX'); LONG_NAME = 'L-band RTM model: Maximum microwave roughness parameter'; UNITS = '1' + case('MWRTM_RGHWMIN'); LONG_NAME = 'L-band RTM model: Soil moisture value below which maximum microwave roughness parameter is used'; UNITS = 'm3 m-3' + case('MWRTM_RGHWMAX'); LONG_NAME = 'L-band RTM model: Soil moisture value above which minimum microwave roughness parameter is used'; UNITS = 'm3 m-3' + case('MWRTM_RGHNRH'); LONG_NAME = 'L-band RTM model: H-pol. Exponent for rough reflectivity parameterization'; UNITS = '1' + case('MWRTM_RGHNRV'); LONG_NAME = 'L-band RTM model: V-pol. Exponent for rough reflectivity parameterization'; UNITS = '1' + case('MWRTM_RGHPOLMIX'); LONG_NAME = 'L-band RTM model: Polarization mixing parameter'; UNITS = '1' + case('MWRTM_OMEGA'); LONG_NAME = 'L-band RTM model: Scattering albedo'; UNITS = '1' + case('MWRTM_BH'); LONG_NAME = 'L-band RTM model: H-pol. Vegetation b parameter'; UNITS = '1' + case('MWRTM_BV'); LONG_NAME = 'L-band RTM model: V-pol. Vegetation b parameter'; UNITS = '1' + case('MWRTM_LEWT'); LONG_NAME = 'L-band RTM model: Parameter to transform leaf area index into vegetation water content'; UNITS = 'kg m-2' + + case default; LONG_NAME = 'Checck_GridComp'; UNITS = 'Checck_GridComp'; + end select + + if (present(LNAME)) str_atr = trim (LONG_NAME) + if (present(UNT)) str_atr = trim (UNITS ) + + END FUNCTION getAttribute + +END PROGRAM mwrtm_bin2nc4 diff --git a/GEOSldas_App/util/inputs/obs_scaling_params/Run_get_L4_Tb_scale_SMAP.m b/GEOSldas_App/util/inputs/obs_scaling_params/Run_get_L4_Tb_scale_SMAP.m new file mode 100644 index 0000000..b7e914f --- /dev/null +++ b/GEOSldas_App/util/inputs/obs_scaling_params/Run_get_L4_Tb_scale_SMAP.m @@ -0,0 +1,178 @@ +% Calculate scaling files for Tb-DA based on SMAP Tb +% +% GDL, 11 Sep 2012 +% QLiu, Dec 2016 +%===================================================================== + +clear + +% add path to matlab functions in src/Components/GEOSldas_GridComp/GEOSldas_App/util/shared/matlab/ +addpath('../../shared/matlab/'); + +%====== + +run_months = [1:12 1:4]; %loop through 1:4 again to get complete pentads + +%exp_path = '/smap1/qliu/output/SMAP_Nature_v8.3/NRv8.3_innov_RTMv4/'; +%exp_run = {'SMAP_NRv8.3inv_RTMv4'}; +%exp_path = '/hydro/qliu/WORK/output/L4_SM_SMAP/'; +%exp_run = {'SPL4SM_OL4001'}; +%exp_path = '/smap1/qliu/output/SMAP_Nature_v8.3/NRv8.3_innov/S1/'; +%exp_run = {'SMAP_NRv8.3_innov'}; +exp_path = '/home/qliu/smap/SMAP_Nature/'; +exp_run = {'SPL4SM_OL7000'}; +domain = 'SMAP_EASEv2_M09_GLOBAL'; + +%Start and end year for each month +start_year = [repmat(2016,1,3) repmat(2015,1,9) repmat(2016,1,3) repmat(2015,1,1)]; %corresp to [1:12 1 2] +end_year = [repmat(2022,1,3) repmat(2021,1,9) repmat(2022,1,3) repmat(2021,1,1)]; %runs till end of run_months for end_year + +orbit = [ 2]; %1=A, 2=D !DO *NOT* USE ASC AND DESC TOGETHER! +pol = [ 1 2 ]; %1=H, 2=V +inc_ang = [ 40.0 ]; + +prefix_out = 'L4SM_OL7000_SMAPL1CR17000_zscore_stats_'; + +dt_assim = 3*60*60; % [seconds] land analysis time step, + % same as LANDASSIM_DT in GEOSldas) +t0_assim = 0; % [seconds] land analysis "reference" time (offset from 0z), + % same as LANDASSIM_T0 in GEOSldas (except for units), + % typically 0 in offline runs and 1.5*60*60 in LADAS + +%====== + +obs_param_fname = [exp_path, '/', exp_run{1}, '/output/', domain, '/rc_out/', ... + '/Y2015/M04/',exp_run{1}, '.ldas_obsparam.20150401_0000z.txt']; + +var_name = {'Tb'}; + +% added to identify SMOS or SMAP from runs that include both +descr = 'SMAP_L1C' ; % 'SMOS_fit' + +%====== +if (length(orbit) > 1) + error('ONLY pick one orbit!') +end + +if (orbit(1) == 1) int_Asc = 1; end %Asc +if (orbit(1) == 2) int_Asc = 0; end %Desc + +%====== +%TO GO FROM SMOS TO SMAP ONLY!!! +%int_Asc = abs(int_Asc - 1); +%====== + +%Spatial sampling +hscale = 0.0; % degrees lat/lon + +% Temporal sampling window(days), current hard coded and need to be divisive by 5 and be an odd number +w_days = 75; + +Ndata_min = 20; + +%To limit M09 tiles to administering M36 tiles only (smaller files), +%provide convert_grid +if isempty(strfind(prefix_out,'M09')) + convert_grid='EASEv2_M36'; +end + +if (mod(w_days,10) == 0) + disp('w_days should be 5, 15, 25, 35, ...') + error('Need an odd number of pentads |xxxxx|xxXxx|xxxxx|') +end +if (mod(w_days, 5) > 0) + error('Aiming at pentad files') +end + +% ------------------------------------------------------------------------ + +[N_obs_param, obs_param ] = read_obsparam(obs_param_fname); + +species =[]; + +for oo=1:length(orbit) + for pp=1:length(pol) + for aa=1:length(inc_ang) + + add_species = obs_param(strcmp(var_name,{obs_param.varname}) & ... + orbit(oo) == [obs_param.orbit] & ... + inc_ang(aa) == [obs_param.ang] & ... + pol(pp) == [obs_param.pol] & ... + ~cellfun(@isempty, strfind({obs_param.descr},descr))).species; + + species = union(species,add_species); + + end + end +end + +species +% ------------------ + +for n=1:length(exp_run) + + if (exist('convert_grid','var')) + + if exist('time_of_day_in_hours','var') + + + for j=1:length(time_of_day_in_hours) + + for k=1:length(run_months) + + get_model_and_obs_clim_stats( var_name, ... + run_months{k}, exp_path, exp_run{n}, domain, ... + start_year, end_year, ... + dt_assim, t0_assim, species, obs_param, ... + hscale, inc_ang, int_Asc, w_days, Ndata_min, prefix_out,... + convert_grid, time_of_day_in_hours(j) ); + + end + + end + + else + + get_model_and_obs_clim_stats( var_name, ... + run_months, exp_path, exp_run{n}, domain, start_year, end_year, ... + dt_assim, t0_assim, species, obs_param, ... + hscale, inc_ang, int_Asc, w_days, Ndata_min, prefix_out,... + convert_grid ); + + end + else + + if exist('time_of_day_in_hours','var') + + + for j=1:length(time_of_day_in_hours) + + for k=1:length(run_months) + + get_model_and_obs_clim_stats( var_name, ... + run_months{k}, exp_path, exp_run{n}, domain, ... + start_year, end_year, ... + dt_assim, t0_assim, species, obs_param, ... + hscale, inc_ang, int_Asc, w_days, Ndata_min, prefix_out,... + time_of_day_in_hours(j) ); + + end + + end + + else + + get_model_and_obs_clim_stats( var_name, ... + run_months, exp_path, exp_run{n}, domain, start_year, end_year, ... + dt_assim, t0_assim, species, obs_param, ... + hscale, inc_ang, int_Asc, w_days, Ndata_min, prefix_out); + + end + + end +end + + +% ============= EOF ==================================================== + + diff --git a/GEOSldas_App/util/inputs/obs_scaling_params/Run_get_model_and_obs_clim_stats_latlon_grid.m b/GEOSldas_App/util/inputs/obs_scaling_params/Run_get_model_and_obs_clim_stats_latlon_grid.m new file mode 100644 index 0000000..20fc383 --- /dev/null +++ b/GEOSldas_App/util/inputs/obs_scaling_params/Run_get_model_and_obs_clim_stats_latlon_grid.m @@ -0,0 +1,115 @@ +clear + +% ------------------------------------------------------------------- +% Begin user-defined inputs +% ------------------------------------------------------------------- + +% addpath('../../shared/matlab/'); +addpath('/discover/nobackup/amfox/current_GEOSldas/GEOSldas/src/Components/GEOSldas_GridComp/GEOSldas_App/util/shared/matlab') + +% Define the Open Loop experiment path, run name, domain, and output prefix + +exp_path = '/discover/nobackup/amfox/Experiments/OLv7_M36_ascat'; +exp_run = {'OLv7_M36_ascat'}; +domain = 'SMAP_EASEv2_M36_GLOBAL'; +prefix_out = 'M36_zscore_stats_'; + +% Define the Open Loop experiment start and end dates + +start_month = 4; +start_year = 2015; +end_month = 3; +end_year = 2021; + +% Define the species names + +species_names = {'ASCAT_META_SM','ASCAT_METB_SM','ASCAT_METC_SM'}; + +% Define whether to combine species + +combine_species_stats = 1; % 1 to combine all species into single set of statistics + +% Define the grid resolution (degrees) + +grid_resolution = 0.25; + +% Define moving window size over which statistics are calculated, +% and minimum number of data points required to calculate statistics + +w_days = 75; +Ndata_min = 5; + +% Define the assimilation time step and initial time + +dt_assim = 3*60*60; +t0_assim = 0; + +% Define print intervals + +print_each_DOY = 1; +print_each_pentad = 0; +print_all_pentads = 1; + +% Define output directory (takes form "domain"/stats/"out_dir") +out_dir = 'z_score_clim_quarter_degree'; + +% Define the months to run over, 1:12, plus a number of months required to complete the window +run_months = [1:12 1:ceil(w_days/30)]; + +% ------------------------------------------------------------------- +% End user-defined inputs +% ------------------------------------------------------------------- + +% Calculate the earliest and latest years for each month in the experiment +earliest_year = zeros(length(run_months),1); +latest_year = zeros(length(run_months),1); + +cnt = 0; +for month = run_months + % Initialize the earliest and latest year variables + cnt = cnt + 1; + + % Check if the current year/month combination is earlier than the earliest + if datenum(start_year, month, 1) < datenum(start_year, start_month, 1) + earliest_year(cnt) = start_year+1; + else + earliest_year(cnt) = start_year; + end + + % Check if the current year/month combination is later than the latest + if datenum(end_year, month, 1) > datenum(end_year, end_month,1) + latest_year(cnt) = end_year-1; + else + latest_year(cnt) = end_year; + end +end + +% assume "ldas_obsparam" file is available at 0z on first day of start_month/start_year + +YYYY = num2str( start_year, '%4.4d' ); +MM = num2str( start_month, '%2.2d' ); + +obs_param_fname = [exp_path, '/', exp_run{1}, '/output/', domain, '/rc_out/Y', YYYY, ... + '/M', MM, '/',exp_run{1}, '.ldas_obsparam.', YYYY, MM, '01_0000z.txt']; + +[N_obs_param, obs_param ] = read_obsparam(obs_param_fname); + +species =[]; + +for i = 1:length(species_names) + add_species = obs_param(strcmp(species_names(i),{obs_param.descr})).species; + species = union(species,add_species); +end + +if combine_species_stats + disp('Calculating stats by combining multiple species'); +end + +% Calculate the climatology statistics + +get_model_and_obs_clim_stats_latlon_grid( species_names, run_months, exp_path, exp_run{1}, domain, earliest_year, ... + latest_year, dt_assim, t0_assim, species, combine_species_stats, ... + grid_resolution, w_days, Ndata_min, prefix_out, print_each_DOY, ... + print_each_pentad, print_all_pentads, out_dir ); + +% ================= EOF ========================================================================= diff --git a/GEOSldas_App/util/inputs/obs_scaling_params/dist_km2deg.m b/GEOSldas_App/util/inputs/obs_scaling_params/dist_km2deg.m new file mode 100644 index 0000000..939c2e3 --- /dev/null +++ b/GEOSldas_App/util/inputs/obs_scaling_params/dist_km2deg.m @@ -0,0 +1,26 @@ +%========================================================================= + +function [dist_x_deg, dist_y_deg] = dist_km2deg( dist_km, lat ) + + MAPL_PI = 3.14159265358979323846; + MAPL_RADIUS = 6371.0E3; + + % distance between latitudes is equal (always full meridional radius circle) + % assumuming the Earth is a perfect ball. + % NOTE: MAPL_radius (Earth radius) is in [m] and dist_km is in [km] + + dist_y_deg = dist_km .* (180./MAPL_PI) ./ (MAPL_RADIUS./1000.); + + % distance between longitudes decreases towards the poles + % (radius of parallel circles decreases) + % NOTE: cos() needs argument in [rad], lat is in [deg] (-90:90) + + dist_x_deg = dist_y_deg ./ cos( MAPL_PI./180. .* lat ); + + if (any(dist_x_deg<0. | dist_y_deg<0.)) + disp( 'encountered negative distance' ); + end + +end + +%========================================================================= diff --git a/GEOSldas_App/util/inputs/obs_scaling_params/get_ij_ind_from_latlon.m b/GEOSldas_App/util/inputs/obs_scaling_params/get_ij_ind_from_latlon.m new file mode 100644 index 0000000..9edf5ca --- /dev/null +++ b/GEOSldas_App/util/inputs/obs_scaling_params/get_ij_ind_from_latlon.m @@ -0,0 +1,24 @@ +%========================================================================= + +function [i_ind,j_ind] = get_ij_ind_from_latlon( tile_grid, lat, lon) + + if (strcmp(tile_grid.gridtype,'EASEv2_M36')) + %row, col + [j_indg,i_indg] = ... + EASEv2_latlon2ind(lat,lon,'M36',1); + elseif (strcmp(tile_grid.gridtype,'EASEv2_M09')) + %row, col + [j_indg,i_indg] = ... + EASEv2_latlon2ind(lat,lon,'M09',1); + else + error('not ready for this grid'); + end + + % convert to index into array defined by tile_grid_d + + i_ind = i_indg - tile_grid.i_offg - (tile_grid.ind_base - 1); + j_ind = j_indg - tile_grid.j_offg - (tile_grid.ind_base - 1); + +end + +%========================================================================= diff --git a/GEOSldas_App/util/inputs/obs_scaling_params/get_model_and_obs_clim_stats.m b/GEOSldas_App/util/inputs/obs_scaling_params/get_model_and_obs_clim_stats.m new file mode 100644 index 0000000..8d8b242 --- /dev/null +++ b/GEOSldas_App/util/inputs/obs_scaling_params/get_model_and_obs_clim_stats.m @@ -0,0 +1,729 @@ + +function [] = get_model_and_obs_clim_stats( varname, ... + run_months, exp_path, exp_run, domain, start_year, end_year, ... + dt_assim, t0_assim, species, obs_param, ... + hscale, inc_angle, int_Asc, w_days, Ndata_min, prefix, ... + convert_grid , time_of_day_in_hours ) + +% +% get_model_and_obs_clim_stats.m +% +% Compute mean, stdv of model and observations from tile-based +% "innov" files for a selection of species. +% +% The main purpose of this function is to aggregate the information +% from the "innov" files so that the climatology statistics can +% be used in scaling of the observations before assimilation. +% +% One file with statistics is generated for every DOY (1,...,365). +% The temporal smoothing/averaging window (w_days) is given in days. +% +% Stats output file in a similar format as the SMOS-data files +% +% ==HEADER== +% N_tiles N_angles +% angles +% +% ==DATA== +% +% %%%% tile ID --> for all tiles (1:N_tiles, not sorted by tile_id!) +% lat +% lon +% +% [for angles] +% mean_obs_H +% std_obs_H +% mean_mod_H +% std_mod_H +% N_data_H +% +% mean_obs_V +% std_obs_V +% mean_mod_V +% std_mod_V +% N_data_V +% +% H_obs +% H_mod +% V_obs +% V_mod +% [end angles] +% +% GDL, 10 sept 2012 +% +% GDL, aug 2013: added 'convert_grid' (= EASEv2_M36, EASE_M36, ...) +% to project the Obs (always M36 for SMOS) and Fcst to M36 +% M09 obs are administered by tiles (0) that could be anywhere +% around the center of the observed pixel (M36) +% ----------- +% | X X X X | +% | X O O X | +% | X O O X | +% | X X X X | +% ----------- +% GDL, jan 2014: the above issue that "any" M09 tile in the center (0) +% could potentially administer the M36 obs is not true +% anymore with later LDASsa-tags. +% => no need to pass on 'convert_grid' for tags later than +% the summer of 2013 +% reichle, qliu, 13 July 2022: +% "convert_grid" is still needed to limit the number of tiles +% in the scaling parameter file. With "convert_grid" turned on, +% only the M09 tile to the northeast of the M36 center point +% is kept in the scaling parameter file, consistent with the +% "tmp_shift_lat" and "tmp_shift_lon" operations in the SMOS +% and SMAP Fortran readers. (It is not clear if this matlab +% function works properly if there is no M09 [land] tile +% immediately to the northeast of the M36 center point. In +% such a case, the Fortran reader assigns the nearest M09 +% land tile as the tile that administers the obs.) +% Presumably, scaling parameters for all M09 tiles could be kept +% if they are stored in (compressed) nc4 format. In this case, +% the NaN values for the scaling parameters of 15 out of each 16 +% M09 tiles can be compressed to almost nothing. +% +% ------------------------------------------------------------------- +% begin user-defined inputs +% ------------------------------------------------------------------- + +% obs species to be processed (see ens_upd_inputs.nml for a list) +% +% (only observation species that represent observations of the same +% model prognostic or diagnostic can be processed together!) + +nodata = -9999; +nodata_tol = 1e-4; + +% minimum number of data points to include in window statistics + +% N_data_min = w_days/10.; % initial screening on minimum # points in a +% % window to calculate a mean or stdv +% include a final decision about "good" stats later, when merging years + +% no-data-value for points that don't have good statistics + +no_data_stats = -9999.; + +disp('ASSUMING EASEv2 M36 observations'); + +if ~isempty(strfind(domain,'M36')) && isempty(strfind(obs_param(species(1)).descr, '_E')) + tol = 1E-3; +else + tol = 2; +end + +% output specs + +overwrite = 1; + +Nf = 5; %5 fields per polarization + +N_out_fields = 2*Nf+4; %14; + +write_ind_latlon = 'latlon_id'; %'latlon'; + +N_angle = length(inc_angle); +N_pol = 2; + +tmp_shift_lon = 0.01; +tmp_shift_lat = 0.005; + +store_all_M09inM36 = 0; +print_each_DOY = 0; + +% ------------------------------------------------------------------- +% end user-defined inputs +% ------------------------------------------------------------------- + +% assemble input and output paths + +%inpath = [ exp_path, '/output/', exp_run, '/', domain ]; +inpath = [ exp_path, '/', exp_run, '/output/', domain ]; + +outpath = [ inpath, '/stats/z_score_clim/' ]; + +% create outpath if it doesn't exist + +if exist(outpath)~=2 + eval(['!mkdir -p ', outpath]); +end + +% ------------------------------------------------------------- + +% assemble output file name + +ind = find(start_year == min(start_year)); +mi_m = min(run_months(ind)); +ind = find(end_year == max(end_year)); +ma_m = max(run_months(ind)); + +D(1) = 1; +P(1) = 1; +if mi_m > 1 + D(1) = sum(days_in_month( 2014, [1:mi_m-1]))+1; + P(1) = ceil(D(1)/5); +end +if ma_m > 1 + D(2) = sum(days_in_month( 2014, [1:ma_m])); +else + D(2) = 1; +end +P(2) = floor(D(2)/5); + +if run_months(1) ~= run_months(end) && run_months(2) ~= run_months(end) + disp('WARNING: incomplete pentad-windows; loop through additional months to get complete pentads'); +end + +fname_out_base = [ outpath, '/', prefix, ... + num2str(min(start_year)),'_doy',num2str(D(1)),'_', ... + num2str(max(end_year)), '_doy',num2str(D(2)), ... + '_hscale_', num2str(hscale,'%2.2f'), '_', ... + 'W_', num2str(w_days),'d_Nmin_', num2str(Ndata_min)]; + +fname_out_base_p = [ outpath, '/', prefix, ... + num2str(min(start_year)),'_p',num2str(P(1)),'_', ... + num2str(max(end_year)), '_p',num2str(P(2)), ... + '_hscale_', num2str(hscale,'%2.2f'), '_', ... + 'W_', num2str(round(w_days/5)),'p_Nmin_', num2str(Ndata_min)]; + +%fname_out_base = [fname_out_base, spec_tag]; + +if (int_Asc == 1) + Orbit_tag = '_A'; %'_Asc'; +else + Orbit_tag = '_D'; %'_Desc'; +end + +fname_out_base = [fname_out_base, Orbit_tag]; +fname_out_base_p = [fname_out_base_p, Orbit_tag]; + +if exist( 'time_of_day_in_hours', 'var') + + fname_out_base = [fname_out_base, '_', num2str(time_of_day_in_hours,'%2.2d'), 'z']; + fname_out_base_p = [fname_out_base_p, '_', num2str(time_of_day_in_hours,'%2.2d'), 'z']; + +end + +% ------------------------------------------------------------- + +% load catchment coordinates + +fname = [inpath, '/rc_out/', exp_run, '.ldas_tilecoord.bin']; +fnameg= [inpath, '/rc_out/', exp_run, '.ldas_tilegrids.bin']; + +[ tile_coord ] = read_tilecoord( fname ); +[ tile_grid ] = read_tilegrids( fnameg ); + +N_tile = length(tile_coord.tile_id); + +% ------------------------------------------------------------- + +% determine tiles to whose statistics the current obs will contribute to + +disp('pre-computing index for regional averaging') + +central_lat = tile_coord.com_lat; +central_lon = tile_coord.com_lon; +tile_coord_tile_id = tile_coord.tile_id; + +if (exist('convert_grid')) + + %1) convert to M36 EASE indices + %2) convert back to lat/lon at center of obs + if (~isempty(strfind(convert_grid, 'M36')) && ~isempty(strfind(convert_grid, 'EASEv2'))) + gridid = 'M36'; + [central_row,central_col] = EASEv2_latlon2ind(central_lat,central_lon,gridid,1); + [central_lat,central_lon] = EASEv2_ind2latlon(central_row,central_col,gridid); + elseif (~isempty(strfind(convert_grid, 'M36')) && ~isempty(strfind(convert_grid, 'EASEv1'))) + error('Must provide smapeasev1_latlon2ind() and smapeasev1_ind2latlon()!') + gridid = 'M36'; + [central_row,central_col] = smapeasev1_latlon2ind(central_lat,central_lon,gridid); + [central_lat,central_lon] = smapeasev1_ind2latlon(central_row,central_col,gridid); + else + error(['Unable to convert to ',convert_grid]) + end + + row_col_tmp = [central_row central_col]; + [unique_rc, ia, ic] = unique(row_col_tmp,'rows'); + + max_Hx_c = length(find(mode(ic)==ic)); + + %know which exact M09 tiles are actually administering the obs + %------------------- + tmp_lon = central_lon(ia)+tmp_shift_lon; + tmp_lat = central_lat(ia)+tmp_shift_lat; + + [N_tile_in_cell_ij, tile_num_in_cell_ij] = get_tile_num_in_cell_ij( ... + tile_coord, tile_grid); + + this_FOV = 20; + option = 'FOV_in_km'; + %overwrite ia with actual administering tile number + [ia] = get_tile_num_for_obs( tile_coord, tile_grid, ... + N_tile_in_cell_ij, tile_num_in_cell_ij, ... + option, this_FOV, tmp_lat, tmp_lon); + + ia = ia(ia>0 & ~isnan(ia)); + + obsnum = NaN+zeros(length(ic),1); + obsnum(ia) = [1:length(ia)]; + + N_tile_obs = length(ia); + + %------------------- + + if store_all_M09inM36 + + %Not maintained/elaborated + tile_coord_tile_id = zeros(N_tile_obs,max_Hx_c); + + disp(['centralizing obs on ',convert_grid,' grid before doing stats: max ',num2str(max_Hx_c),'tiles per obs cell']) + + for i=1:N_tile_obs + + tmp_ind = find(row_col_tmp(:,1) == unique_rc(i,1) & row_col_tmp(:,2) == unique_rc(i,2)); + + tile_coord_tile_id(i,1:length(tmp_ind)) = tile_coord.tile_id(tmp_ind); + + end + + else + + tile_coord_tile_id = tile_coord.tile_id(ia); + + end + +else + + N_tile_obs = N_tile; + ia = 1:N_tile; + ic = 1:N_tile; + obsnum = 1:N_tile; + +end + +lon_out = tile_coord.com_lon(ia); %NaN+zeros(N_tile,1); +lat_out = tile_coord.com_lat(ia); %NaN+zeros(N_tile,1); + +if hscale>0 + + for i=1:N_tile_obs + + this_lat = lat_out(i); + this_lon = lon_out(i); + + tmp_sq_distance = ... + (central_lon - this_lon).^2 + ... + (central_lat - this_lat).^2; + + hscale_ind{i} = find( tmp_sq_distance <= hscale^2 ); + end + +else + + hscale_ind = num2cell(ia); + +end + + +% initialize output statistics +% Note: Rolf suggests to have all species as one dimension, rather than +% N_pol and N_angle be specified here. Then subsample specifically +% when the files are written out. + +o_data = NaN+zeros(N_pol,N_tile_obs,N_angle,w_days); +m_data = NaN+zeros(N_pol,N_tile_obs,N_angle,w_days); +o_data2 = NaN+zeros(N_pol,N_tile_obs,N_angle,w_days); +m_data2 = NaN+zeros(N_pol,N_tile_obs,N_angle,w_days); +N_data = NaN+zeros(N_pol,N_tile_obs,N_angle,w_days); + +data_out = NaN+zeros(N_out_fields,N_tile_obs,N_angle); + +% ------------------------------------------------------------- + +% make sure t0_assim is *first* analysis time in a day + +t0_assim = mod( t0_assim, dt_assim ); + +count = 0; + +for imonth = 1:length(run_months) + + month = run_months(imonth); + + for day = 1:days_in_month( 2014, month) %2014 = random non-leap year + + if count < w_days + count = count + 1; + else + count = w_days; + end + + for seconds_in_day = t0_assim:dt_assim:(86400-1) + + hour = floor(seconds_in_day/3600); + + % check if diurnal stats are needed + + if exist('time_of_day_in_hours','var') + tmp_hour = time_of_day_in_hours; + else + tmp_hour = hour; % all hours of day will be included + end + + if hour==tmp_hour + + minute = floor( (seconds_in_day-hour*3600)/60 ); + + seconds = seconds_in_day-hour*3600-minute*60; + + if (seconds~=0) + input('something is wrong! Ctrl-c now') + end + + for year = start_year(imonth):end_year(imonth) + + YYYYMMDD = [ num2str(year, '%4.4d'), ... + num2str(month, '%2.2d'), ... + num2str(day, '%2.2d') ]; + + HHMM = [ num2str(hour, '%2.2d'), ... + num2str(minute, '%2.2d') ]; + + % read innov files + + fname = [ inpath, '/ana/ens_avg/', ... + 'Y', YYYYMMDD(1:4), '/', ... + 'M', YYYYMMDD(5:6), '/', ... + exp_run, '.ens_avg.ldas_ObsFcstAna.', ... + YYYYMMDD, '_', HHMM, 'z.bin' ]; + + ifp = fopen( fname, 'r', 'l' ); + + if (ifp > 0) % Proceed only if file exists (e.g. irregular SMOS swaths!) + + fclose(ifp); + + [ date_time, ... + obs_assim, ... + obs_species, ... + obs_tilenum, ... + obs_lon, ... + obs_lat, ... + obs_obs, ... + obs_obsvar, ... + obs_fcst, ... + obs_fcstvar, ... + obs_ana, ... + obs_anavar ... + ] = ... + read_ObsFcstAna( fname ); + + % remove tiles where obs_fcst is no-data (note: read_ObsFcstAna() returns NaN) + + idx = isnan(obs_fcst); + + obs_assim( idx) = []; + obs_species(idx) = []; + obs_tilenum(idx) = []; + obs_lon( idx) = []; + obs_lat( idx) = []; + obs_obs( idx) = []; + obs_obsvar( idx) = []; + obs_fcst( idx) = []; + obs_fcstvar(idx) = []; + obs_ana( idx) = []; + obs_anavar( idx) = []; + + % extract species of interest + + ind = []; + + for this_species = species + + ind = find( obs_species == this_species); + + if (~isempty(ind)) + + obs_tilenum_i = obs_tilenum(ind); + obs_obs_i = obs_obs(ind); + obs_fcst_i = obs_fcst(ind); + obs_lon_i = obs_lon(ind); + obs_lat_i = obs_lat(ind); + + % Check if any location receives more than 1 obs (or 1 species) + + tmp = sort(obs_tilenum_i); + same_tile = find(diff(tmp)==0); + + if (~isempty(same_tile)) + error('multiple obs of the same species at one location? - only last one in line is used'); + end + + % Organize the data in a big matrix + + angle = obs_param(this_species == [obs_param.species]).ang; + pol = obs_param(this_species == [obs_param.species]).pol; + + % pol intrinsically gives an index + % now find the index for the angle + angle_i = find(angle(1) == inc_angle); + + % Only writes lat-lon at exact obs locations, but with + % hscale>0, these obs are spread outside their exact + % location. This allows to calculate stats at lan-lons + % where no obs are available. + + %lon_out(obs_tilenum_i) = obs_lon_i; + %lat_out(obs_tilenum_i) = obs_lat_i; + + % obs_lat/lon are the actual M36 lat/lons, *not* the + % administering tiles, so the lat/lons for the obs and those + % in the tile_coord would not be identical. + % Still, they should be in the + % neighbourhood, so check here if that is true. + if (any(abs(tile_coord.com_lat(obs_tilenum_i)-obs_lat_i) > tol) || ... + any(abs(tile_coord.com_lon(obs_tilenum_i)-obs_lon_i) > tol) ) + error('Something wrong with tile_lat/lon') + end + + % map model tiles (e.g. all M09) to observation administering + % tiles (could be a reduced subset of all M09) + % -------------------------------------------------------- + obs_i = obsnum(obs_tilenum_i); + % -------------------------------------------------------- + + if (hscale == 0) + + % 11 May 2015: sum the obs and fcst within each day; + % and across years! + % some obs can be found at multiple hours within a day + % e.g. at the poles. + % **sum(...,"omitnan") of NaNs** results in zero, this need to be + % taken care of + o_data( pol(1),obs_i,angle_i,count) = sum([o_data( pol(1),obs_i,angle_i,count); obs_obs_i' ], "omitnan"); + m_data( pol(1),obs_i,angle_i,count) = sum([m_data( pol(1),obs_i,angle_i,count); obs_fcst_i' ], "omitnan"); + + % X^2 + o_data2(pol(1),obs_i,angle_i,count) = sum([o_data2(pol(1),obs_i,angle_i,count); obs_obs_i'.^2 ], "omitnan"); + m_data2(pol(1),obs_i,angle_i,count) = sum([m_data2(pol(1),obs_i,angle_i,count); obs_fcst_i'.^2 ], "omitnan"); + + % Sum of obs or model elements at each location + N_data(pol(1), obs_i,angle_i,count) = sum([N_data( pol(1),obs_i,angle_i,count); ~isnan([obs_obs_i])'], "omitnan"); + + else + + for i_ind = 1:length(obs_obs_i) + + % introduce a spatial effect of each observation on + % neighbouring statistics (through hscale) + s_eff = unique(hscale_ind{obs_i(i_ind)}); + %hscale_ind =[obs space] % + + % Sum of X + o_data(pol(1),s_eff,angle_i,count) = ... + sum([o_data( pol(1),s_eff,angle_i,count); repmat( obs_obs_i( i_ind), 1,length(s_eff))], "omitnan"); + m_data(pol(1),s_eff,angle_i,count) = ... + sum([m_data( pol(1),s_eff,angle_i,count); repmat( obs_fcst_i(i_ind), 1,length(s_eff))], "omitnan"); + + % Sum of X^2 + o_data2(pol(1),s_eff,angle_i,count) = ... + sum([o_data2(pol(1),s_eff,angle_i,count); repmat( obs_obs_i( i_ind).^2,1,length(s_eff))], "omitnan"); + m_data2(pol(1),s_eff,angle_i,count) = ... + sum([m_data2(pol(1),s_eff,angle_i,count); repmat( obs_fcst_i(i_ind).^2,1,length(s_eff))], "omitnan"); + + % Sum of obs or model elements at each location + N_data(pol(1),s_eff,angle_i,count) = ... + sum([N_data( pol(1),s_eff,angle_i,count); repmat(~isnan([obs_obs_i(i_ind)]), 1,length(s_eff))], "omitnan"); + + end + + end % (hscale == 0) + + end % ~isempty(ind) + + end % species + + end % if file present + + end % loop over multiple years + + end % hour == tmp_hour (time_of_day_in_hours) + + end % seconds_in_day + + %count = count+1; + + if count >= w_days %wait initially until enough data is built up + + end_time.year = 2014; + end_time.month = month; + end_time.day = day; + end_time.hour = hour; + end_time.min = minute; + end_time.sec = seconds; + + start_time = augment_date_time( -floor(w_days*(24*60*60)), end_time ); + + % At the end of each day, collect the obs and fcst of the last + % w_day period, and write out a statistics-file at [w_day - floor(w_day/2)] + + o_data(abs(o_data - nodata) <= nodata_tol) = NaN; + m_data(abs(o_data - nodata) <= nodata_tol) = NaN; + + % data_out = zeros(N_out_fields,1:N_tiles,N_angle); + + for pol=[0 1] + + pp = pol*Nf; + + N_hscale_window = sum(N_data(1+pol,:,:,1:w_days), 4,"omitnan"); + + if w_days == 95 + N_hscale_inner_window = sum(N_data(1+pol,:,:,((w_days+1)/2-15):((w_days+1)/2+15)),4,"omitnan"); + end + + % OBSERVATIONS + %---------------- + % o_data is a sum over neighbouring obs above; + % here then take a sum over the time steps in the window + data_out(1+pp,:,:) = sum( o_data( 1+pol,:,:,1:w_days),4,"omitnan"); + + % then make the average, by dividing over the sum of the number of + % timesteps and influencing obs at each location + data_out(1+pp,:,:) = data_out( 1+pp, :,:)./N_hscale_window; + + %stdv_H = sqrt(E[X^2] - E[X]^2) + data_out(2+pp,:,:) = sum( o_data2( 1+pol,:,:,1:w_days),4,"omitnan"); + data_out(2+pp,:,:) = data_out( 2+pp, :,:)./N_hscale_window; + data_out(2+pp,:,:) = sqrt( data_out( 2+pp, :,:) - data_out(1+pp,:,:).^2); + + % MODEL + %---------------- + data_out(3+pp,:,:) = sum( m_data( 1+pol,:,:,1:w_days),4,"omitnan"); + data_out(3+pp,:,:) = data_out( 3+pp, :,:)./N_hscale_window; + + data_out(4+pp,:,:) = sum( m_data2( 1+pol,:,:,1:w_days),4,"omitnan"); + data_out(4+pp,:,:) = data_out( 4+pp, :,:)./N_hscale_window; + data_out(4+pp,:,:) = sqrt( data_out( 4+pp, :,:) - data_out(3+pp,:,:).^2); + + data_out(5+pp,:,:) = N_hscale_window; + + % Toss out stats that are based on too little data + + data_out( [1:5]+pp,N_hscale_window < Ndata_min ) = NaN; + + if w_days == 95 + data_out([1:5]+pp,N_hscale_inner_window < (Ndata_min/2.5)) = NaN; + end + + end + + % Get the actual obs/model at the center point (for debugging only!!) + + data_out(11,:,:) = o_data(1,:,:,w_days-floor(w_days/2.0))./N_data(1,:,:,w_days-floor(w_days/2.0)); + data_out(12,:,:) = m_data(1,:,:,w_days-floor(w_days/2.0))./N_data(1,:,:,w_days-floor(w_days/2.0)); + data_out(13,:,:) = o_data(2,:,:,w_days-floor(w_days/2.0))./N_data(2,:,:,w_days-floor(w_days/2.0)); + data_out(14,:,:) = m_data(2,:,:,w_days-floor(w_days/2.0))./N_data(2,:,:,w_days-floor(w_days/2.0)); + + % Get rid of NaN before writing a file + + data_out(isnan(data_out)) = nodata; + %lon_out(isnan(lon_out)) = nodata; + %lat_out(isnan(lat_out)) = nodata; + + % write output file + + date_time = end_time; + date_time = augment_date_time( -floor(w_days*(24*60*60)/2.0), date_time ); + + % always 365 files + + DOY = date_time.dofyr; + + if(is_leap_year(date_time.year) && DOY>=59) + + DOY = DOY-1; + + error('This code should never hit a leap year'); + + end + + + fname_out = [fname_out_base, '_DOY', num2str(DOY,'%3.3d'), '.bin']; + + % check whether output file exists + + if (exist(fname_out)==2 && overwrite) + + disp(['output file exists. overwriting', fname_out]) + + elseif (exist(fname_out)==2 && ~overwrite) + + disp(['output file exists. not overwriting. returning']) + disp(['writing ', fname_out]) + return + + else + + disp(['creating ', fname_out]) + + end + + % write output for each DOY, sorted by all tiles + + if print_each_DOY + + write_seqbin_file(fname_out, lon_out, lat_out, ... + inc_angle, data_out(:,:,:), int_Asc, 0, ... % instead of writing the version#, write Ndata_min=0 + start_time, end_time, overwrite, ... + N_out_fields, write_ind_latlon, 'scaling', ... + tile_coord_tile_id) + else + + % if DOY is at middle of pentad, then copy the DOY to a pentad file + % DOY = pentad*5 - 2; ==> pentad = (DOY + 2)/5; + + pentad = (DOY + 2)/5; + + if mod((DOY + 2),5) == 0 + + write_seqbin_file(fname_out, lon_out, lat_out, ... + inc_angle, data_out(:,:,:), int_Asc, 0, ... + start_time, end_time, overwrite, ... + N_out_fields, write_ind_latlon, 'scaling', ... + tile_coord_tile_id) + + fname_out_p = [fname_out_base_p, '_p', num2str(pentad,'%2.2d'), '.bin']; + + copyfile(fname_out,fname_out_p); + + end + + end + + %clear idx_keep lon_out_write lat_out_write data_out_write tile_coord_tile_id_write + + % shift the window by one day and make room for the next day at the end + + o_data( :,:,:,1:w_days-1) = o_data( :,:,:,2:w_days); + m_data( :,:,:,1:w_days-1) = m_data( :,:,:,2:w_days); + o_data2(:,:,:,1:w_days-1) = o_data2(:,:,:,2:w_days); + m_data2(:,:,:,1:w_days-1) = m_data2(:,:,:,2:w_days); + N_data( :,:,:,1:w_days-1) = N_data( :,:,:,2:w_days); + + o_data( :,:,:,w_days) = NaN; + m_data( :,:,:,w_days) = NaN; + o_data2(:,:,:,w_days) = NaN; + m_data2(:,:,:,w_days) = NaN; + N_data( :,:,:,w_days) = NaN; + + data_out = NaN+0.0.*data_out; + + end + + end % day +end % month + + +% ==================== EOF ============================================== diff --git a/GEOSldas_App/util/inputs/obs_scaling_params/get_model_and_obs_clim_stats_latlon_grid.m b/GEOSldas_App/util/inputs/obs_scaling_params/get_model_and_obs_clim_stats_latlon_grid.m new file mode 100644 index 0000000..1d3a0c7 --- /dev/null +++ b/GEOSldas_App/util/inputs/obs_scaling_params/get_model_and_obs_clim_stats_latlon_grid.m @@ -0,0 +1,352 @@ + +function [] = get_model_and_obs_clim_stats_latlon_grid( species_names, ... + run_months, exp_path, exp_run, domain, start_year, end_year, ... + dt_assim, t0_assim, species, combine_species_stats, ... + resol, w_days, Ndata_min, prefix, print_each_DOY, ... + print_each_pentad, print_all_pentads,out_dir ) +% +% Adapted from get_model_and_obs_clim_stats.m +% +% Compute mean, stdv of model and observations from tile-based +% "innov" files for a selection of species on an Earth-fixed global +% lat/lon grid with resolution "resol". +% +% The main purpose of this function is to aggregate the information +% from the "innov" files so that the climatology statistics can +% be used in scaling of the observations before assimilation. +% +% One file with statistics is generated for every DOY (1,...,365). +% The temporal smoothing/averaging window (w_days) is given in days. +% +% We calculate the bias correction factors (scaling parameters) and +% write on an Earth-fixed lat/lon grid as there is no Earth-fixed +% regular grid for ASCAT observations. +% +% A. M. Fox - 27 Oct 2023 +% +% ------------------------------------------------------------------- +% begin user-defined inputs +% ------------------------------------------------------------------- + +nodata = -9999; +nodata_tol = 1e-4; +overwrite = 1; +Nf = 7; +N_pentads = 73; + +disp('ASSUMING obs are not on Earth-fixed regular grid (e.g., ASCAT)'); +disp(['Calculating scaling parameters on grid with resolution = ', num2str(resol) , ' degrees']); + +if combine_species_stats + N_species = 1; +else + N_species = length(species); +end + +inpath = [ exp_path, '/', exp_run, '/output/', domain ]; + +outpath = [ inpath, '/stats/', out_dir ]; + +% create outpath if it doesn't exist +if ~exist(outpath, 'dir') + mkdir(outpath); +end + +% assemble output file name +ind = start_year == min(start_year); +mi_m = min(run_months(ind)); +ind = end_year == max(end_year); +ma_m = max(run_months(ind)); + +D(1) = 1; +P(1) = 1; +if mi_m > 1 + D(1) = sum(days_in_month(2014, 1:mi_m-1)) + 1; + P(1) = ceil(D(1) / 5); +end +D(2) = sum(days_in_month(2014, 1:ma_m)); +P(2) = floor(D(2) / 5); + +fname_out_base_d = [outpath, '/', prefix, ... + num2str(min(start_year)), '_doy', num2str(D(1)), '_', ... + num2str(max(end_year)), '_doy', num2str(D(2)), ... + '_W_', num2str(w_days), 'd_Nmin_', num2str(Ndata_min)]; + +fname_out_base_p = [outpath, '/', prefix, ... + num2str(min(start_year)), '_p', num2str(P(1)), '_', ... + num2str(max(end_year)), '_p', num2str(P(2)), ... + '_W_', num2str(round(w_days/5)), 'p_Nmin_', num2str(Ndata_min)]; + +%====================================================== + +% Define lat/lon grid (dateline-on-edge, pole-on-edge) +% Define lower-left corner coordinates and grid cell size +ll_lon = -180; +ll_lat = -90; + +d_lon = resol; +d_lat = resol; + +% Calculate number of longitude and latitude grid cells +n_lon = round(360 / d_lon); +n_lat = round(180 / d_lat); + +% Calculate longitude and latitude values for the grid +ll_lons = linspace(ll_lon, ll_lon + (n_lon-1)*d_lon, n_lon); +ll_lats = linspace(ll_lat, ll_lat + (n_lat-1)*d_lat, n_lat); + +% Create grid index +obsnum = (1:n_lon*n_lat)'; +[i_out, j_out] = ind2sub([n_lon, n_lat], obsnum); +lon_out = ll_lons(i_out)'; +lat_out = ll_lats(j_out)'; +N_gridcells = length(obsnum); + +% initialize output statistics +o_data_sum = NaN(N_species, N_gridcells, w_days); +m_data_sum = NaN(N_species, N_gridcells, w_days); +o_data_sum2 = NaN(N_species, N_gridcells, w_days); +m_data_sum2 = NaN(N_species, N_gridcells, w_days); +m_data_min = NaN(N_species, N_gridcells, w_days); +m_data_max = NaN(N_species, N_gridcells, w_days); +N_data = NaN(N_species, N_gridcells, w_days); + +data_out = NaN(N_species, Nf, N_gridcells, N_pentads); +data2D = NaN(Nf, N_gridcells); + +% ------------------------------------------------------------- + +% make sure t0_assim is *first* analysis time in a day + +t0_assim = mod( t0_assim, dt_assim ); + +count = 0; + +for imonth = 1:length(run_months) + + month = run_months(imonth); + + for day = 1:days_in_month( 2014, month) %2014 = random non-leap year + + if count < w_days + count = count + 1; + else + count = w_days; + end + + for seconds_in_day = t0_assim:dt_assim:(86400-1) + + hour = floor( seconds_in_day/3600); + minute = floor((seconds_in_day-hour*3600)/60); + seconds = seconds_in_day-hour*3600-minute*60; + + if (seconds ~= 0) + input('something is wrong! Ctrl-c now') + end + + for year = start_year(imonth):end_year(imonth) + + YYYYMMDD = [num2str(year, '%4.4d'), num2str(month, '%2.2d'), num2str(day, '%2.2d')]; + HHMM = [num2str(hour, '%2.2d'), num2str(minute, '%2.2d')]; + + % read innov files + fname = [inpath, '/ana/ens_avg/', 'Y', YYYYMMDD(1:4), '/', 'M', YYYYMMDD(5:6), '/', exp_run, '.ens_avg.ldas_ObsFcstAna.', YYYYMMDD, '_', HHMM, 'z.bin']; + ifp = fopen(fname, 'r', 'l'); + + if (ifp > 0) % Proceed only if file exists + fclose(ifp); + [date_time, obs_assim, obs_species, obs_tilenum, obs_lon, obs_lat, obs_obs, obs_obsvar, obs_fcst, obs_fcstvar, obs_ana, obs_anavar] = read_ObsFcstAna(fname); + + % remove tiles where obs_fcst is no-data (note: read_ObsFcstAna() returns NaN) + + idx = isnan(obs_fcst); + + obs_assim( idx) = []; + obs_species(idx) = []; + obs_tilenum(idx) = []; + obs_lon( idx) = []; + obs_lat( idx) = []; + obs_obs( idx) = []; + obs_obsvar( idx) = []; + obs_fcst( idx) = []; + obs_fcstvar(idx) = []; + obs_ana( idx) = []; + obs_anavar( idx) = []; + + % extract species of interest + ind = []; + for scnt = 1:N_species + + if combine_species_stats + ind = find(ismember(obs_species, species)); + else + this_species = species(scnt); + ind = find(obs_species == this_species); + end + + if ~isempty(ind) + obs_tilenum_i = obs_tilenum(ind); + obs_obs_i = obs_obs( ind); + obs_fcst_i = obs_fcst( ind); + obs_lon_i = obs_lon( ind); + obs_lat_i = obs_lat( ind); + + % Check if any location receives more than 1 obs (or 1 species) + tmp = sort(obs_tilenum_i); + same_tile = find(diff(tmp) == 0, 1); + if ~isempty(same_tile) && ~combine_species_stats + error('multiple obs of the same species at one location? - only last one in line is used'); + end + + % Put obs lat/lon on our grid and figure out obsnum/grid index + i_idx = floor((obs_lon_i - ll_lon) / d_lon) + 1; + j_idx = floor((obs_lat_i - ll_lat) / d_lat) + 1; + [~, obs_idx] = ismember([i_idx, j_idx], [i_out, j_out], 'rows'); + obs_i = obsnum(obs_idx); + + o_data_sum( scnt, obs_i, count) = sum([o_data_sum( scnt, obs_i, count); obs_obs_i' ], "omitnan"); + m_data_sum( scnt, obs_i, count) = sum([m_data_sum( scnt, obs_i, count); obs_fcst_i' ], "omitnan"); + o_data_sum2(scnt, obs_i, count) = sum([o_data_sum2(scnt, obs_i, count); obs_obs_i'.^2 ], "omitnan"); + m_data_sum2(scnt, obs_i, count) = sum([m_data_sum2(scnt, obs_i, count); obs_fcst_i'.^2 ], "omitnan"); + m_data_min( scnt, obs_i, count) = min([m_data_min( scnt, obs_i, count); obs_fcst_i' ] ); + m_data_max( scnt, obs_i, count) = max([m_data_max( scnt, obs_i, count); obs_fcst_i' ] ); + N_data( scnt, obs_i, count) = sum([N_data( scnt, obs_i, count); ~isnan(obs_obs_i)' ], "omitnan"); + end + end + end + end + end + + if count >= w_days %wait initially until enough data is built up + end_time.year = 2014; + end_time.month = month; + end_time.day = day; + end_time.hour = hour; + end_time.min = minute; + end_time.sec = seconds; + + start_time = augment_date_time( -floor(w_days*(24*60*60)), end_time ); + + % At the end of each day, collect the obs and fcst of the last + % w_day period, and write out a statistics-file at [w_day - floor(w_day/2)] + o_data_sum(abs(o_data_sum - nodata) <= nodata_tol) = NaN; + m_data_sum(abs(m_data_sum - nodata) <= nodata_tol) = NaN; + + for i = 1:N_species + + N_window = sum(N_data( i,:,1:w_days), 3,"omitnan"); + + data2D(1,:) = sum(o_data_sum( i,:,1:w_days), 3,"omitnan")./N_window; + data2D(2,:) = sqrt(sum(o_data_sum2(i,:,1:w_days), 3,"omitnan")./N_window - data2D(1,:).^2); + data2D(3,:) = sum(m_data_sum( i,:,1:w_days), 3,"omitnan")./N_window; + data2D(4,:) = sqrt(sum(m_data_sum2(i,:,1:w_days), 3,"omitnan")./N_window - data2D(3,:).^2); + data2D(5,:) = N_window; + data2D(6,:) = min(m_data_min( i,:,1:w_days),[],3); % Want to use minimum mean daily value + data2D(7,:) = max(m_data_max( i,:,1:w_days),[],3); % Want to use maximum mean daily value + + % Set NaNs where there is not enough data + data2D([1:Nf],N_window=59) + error('This code should never hit a leap year'); + end + + if print_each_DOY + pentad = floor((DOY + 2)/5); + if combine_species_stats + fname_out = [fname_out_base_d, '_sp_ALL_DOY', num2str(DOY,'%3.3d'), '.nc4']; + else + fname_out = [fname_out_base_d,'_sp_', char(species_names(i)),'_DOY', num2str(DOY,'%3.3d'), '.nc4']; + end + if (exist(fname_out)==2 && overwrite) + disp(['Output file exists. overwriting', fname_out]) + elseif (exist(fname_out)==2 && ~overwrite) + disp(['Output file exists. not overwriting. returning']) + disp(['Writing ', fname_out]) + return + else + disp(['Creating ', fname_out]) + end + + % Write out the data + write_netcdf_latlon_grid( fname_out, i_out, j_out, ll_lons, ll_lats, data2D, pentad, ... + start_time, end_time, overwrite, Nf, ll_lon, ll_lat, d_lon, d_lat) + end + + if mod((DOY + 2),5) == 0 + pentad = (DOY + 2)/5; + data_out(i,:,:,pentad) = data2D; + start_time_p(pentad) = start_time; + end_time_p(pentad) = end_time; + if print_each_pentad + if combine_species_stats + fname_out = [fname_out_base_p, '_sp_ALL_p', num2str(pentad,'%2.2d'), '.nc4']; + else + fname_out = [fname_out_base_p, '_sp_', char(species_names(i)),'_p', num2str(pentad,'%2.2d'), '.nc4']; + end + if (exist(fname_out)==2 && overwrite) + disp(['Output file exists. overwriting', fname_out]) + elseif (exist(fname_out)==2 && ~overwrite) + disp(['Output file exists. not overwriting. returning']) + disp(['Writing ', fname_out]) + return + else + disp(['Creating ', fname_out]) + end + + % Write out the data + write_netcdf_latlon_grid( fname_out, i_out, j_out, ll_lons, ll_lats, data2D, pentad, ... + start_time, end_time, overwrite, Nf, ll_lon, ll_lat, d_lon, d_lat ) + end + end + + % Shift the data in the window to make room for next day + o_data_sum( i,:,1:w_days-1) = o_data_sum( i,:,2:w_days); + m_data_sum( i,:,1:w_days-1) = m_data_sum( i,:,2:w_days); + o_data_sum2(i,:,1:w_days-1) = o_data_sum2(i,:,2:w_days); + m_data_sum2(i,:,1:w_days-1) = m_data_sum2(i,:,2:w_days); + m_data_min( i,:,1:w_days-1) = m_data_min( i,:,2:w_days); + m_data_max( i,:,1:w_days-1) = m_data_max( i,:,2:w_days); + N_data( i,:,1:w_days-1) = N_data( i,:,2:w_days); + o_data_sum( i,:,w_days ) = NaN; + m_data_sum( i,:,w_days ) = NaN; + o_data_sum2(i,:,w_days ) = NaN; + m_data_sum2(i,:,w_days ) = NaN; + m_data_min( i,:,w_days ) = NaN; + m_data_max( i,:,w_days ) = NaN; + N_data( i,:,w_days ) = NaN; + + data2D = NaN+0.0.*data2D; + end + end % count >= w_days + end % day +end % month + +if print_all_pentads + for i = 1:N_species + data_o = squeeze(data_out(i,:,:,:)); + + if combine_species_stats + fname_out = [fname_out_base_d, '_sp_ALL_all_pentads.nc4']; + else + fname_out = [fname_out_base_d,'_sp_', char(species_names(i)),'_all_pentads.nc4']; + end + + if (exist(fname_out)==2 && overwrite) + disp(['Output file exists. overwriting', fname_out]) + elseif (exist(fname_out)==2 && ~overwrite) + disp(['Output file exists. not overwriting. returning']) + disp(['Writing ', fname_out]) + return + else + disp(['Creating ', fname_out]) + end + + write_netcdf_latlon_grid( fname_out, i_out, j_out, ll_lons, ll_lats, data_o, [1:73], ... + start_time_p, end_time_p, overwrite, Nf, ll_lon, ll_lat, d_lon, d_lat ) + end +end + + +% ==================== EOF ============================================== diff --git a/GEOSldas_App/util/inputs/obs_scaling_params/get_tile_num_for_obs.m b/GEOSldas_App/util/inputs/obs_scaling_params/get_tile_num_for_obs.m new file mode 100644 index 0000000..559853c --- /dev/null +++ b/GEOSldas_App/util/inputs/obs_scaling_params/get_tile_num_for_obs.m @@ -0,0 +1,125 @@ +%========================================================================= + +function [tile_num] = get_tile_num_for_obs(tile_coord, tile_grid,... + N_tile_in_cell_ij, tile_num_in_cell_ij, ... + option, this_FOV, lat, lon) + + N_dat = length(lat); + +% find one tile for each obs that "administers" the obs + +% get "max_dist" in deg lat/lon from field-of-view (FOV) +% +% "max_dist" = Maximum distance allowed between obs lat/lon and tile com_lat/com_lon +% when searching for a tile to which the obs will be assigned. +% +% NOTE: Subroutine get_tile_num_from_latlon() computes distances in Minkowski norm. + + if ~isempty(strfind(option,'FOV_in_deg')) + + max_dist_y = this_FOV; + max_dist_x(1:N_dat) = this_FOV; + + elseif ~isempty(strfind(option,'FOV_in_km')) + + % convert from [km] (FOV) to [deg] (max_dist_*) + + [max_dist_x, max_dist_y] = dist_km2deg( this_FOV, lat); + + else + + error('unknown FOV_option') + + end + + if (max_dist_y<0. || any(max_dist_x<0.)) + error('encountered negative max_dist'); + end + + tile_num = zeros(N_dat,1); + + for n=1:N_dat + + % make sure lat/lon is *inside* tile_grid (to within "max_dist"), + % otherwise do nothing + + if ( tile_grid.ll_lat <= (lat(n)+max_dist_y ) &&... + tile_grid.ll_lon <= (lon(n)+max_dist_x(n)) &&... + (lat(n)-max_dist_y ) <= tile_grid.ur_lat &&... + (lon(n)-max_dist_x(n)) <= tile_grid.ur_lon ) + + % min_dist = distance betw lat/lon in question and center-of-mass of + % matching tile + + min_dist_x = 1.e10; % initialize + min_dist_y = 1.e10; % initialize + + % determine grid cell that contains lat/lon + + [i_ind,j_ind] = get_ij_ind_from_latlon( tile_grid, lat(n), lon(n)); + + % make sure that i/j_ind is still within bounds + % (works in conjunction with if statement above re. ll/ur_lat/lon) + + i_ind = min( max(i_ind, 1), tile_grid.N_lon ); + j_ind = min( max(j_ind, 1), tile_grid.N_lat ); + + % map from i_ind, j_ind to tile_num + + if ( ~isempty(strfind(tile_grid.gridtype, 'EASE_M')) || ... + ~isempty(strfind(tile_grid.gridtype, 'EASE-M')) || ... + ~isempty(strfind(tile_grid.gridtype, 'EASEv2-M')) || ... + ~isempty(strfind(tile_grid.gridtype, 'EASEv2_M')) ) + + % ASSUMPTION: tiles match EASE or EASEv2 grid cells exactly + % (unless "outside" the domain, eg. water surface) + + if (N_tile_in_cell_ij(i_ind,j_ind)==1) + + tile_num(n)=tile_num_in_cell_ij(i_ind,j_ind,1); + + min_dist_x = abs(lon(n) - tile_coord.com_lon(tile_num(n))); + min_dist_y = abs(lat(n) - tile_coord.com_lat(tile_num(n))); + + elseif (N_tile_in_cell_ij(i_ind,j_ind)==0) + + % Do nothing. If given EASE or EASEv2 grid cell is not land, + % tile_num will not change from its initialized value. + + else + + error( 'something wrong for EASE grid'); + + end + + else + error('not ready'); + end + + if (tile_num(n)>0) + + outside_bbox = ( ... + lon(n) < tile_coord.min_lon(tile_num(n)) || ... + lon(n) > tile_coord.max_lon(tile_num(n)) || ... + lat(n) < tile_coord.min_lat(tile_num(n)) || ... + lat(n) > tile_coord.max_lat(tile_num(n)) ); + + too_far_away = ( ... + min_dist_x > max_dist_x(n) || ... + min_dist_y > max_dist_y ); + + % keep tile_num unless obs is outside the bounding box *and* too far away + + if (outside_bbox && too_far_away) + tile_num(n) = NaN; + end + + end + + end + + end + +end + +%========================================================================= diff --git a/GEOSldas_App/util/inputs/obs_scaling_params/get_tile_num_in_cell_ij.m b/GEOSldas_App/util/inputs/obs_scaling_params/get_tile_num_in_cell_ij.m new file mode 100644 index 0000000..5109089 --- /dev/null +++ b/GEOSldas_App/util/inputs/obs_scaling_params/get_tile_num_in_cell_ij.m @@ -0,0 +1,42 @@ +%========================================================================= + +function [N_tile_in_cell_ij, tile_num_in_cell_ij] = ... + get_tile_num_in_cell_ij(tile_coord, tile_grid) + + % Initialize + + max_tile_in_cell = 10; %for EASE grids, this is really just 1 + + tile_num_in_cell_ij = NaN+zeros(tile_grid.N_lon,tile_grid.N_lat,max_tile_in_cell); + + % adjust for 0-based indexing (eg., EASE grids) + + off_i = tile_grid.i_offg + (tile_grid.ind_base - 1); + off_j = tile_grid.j_offg + (tile_grid.ind_base - 1); + + % (re-)initialize + + N_tile_in_cell_ij = zeros(tile_grid.N_lon,tile_grid.N_lat); + + for n=1:tile_coord.N_tile + + i = tile_coord.i_indg(n) - off_i; + j = tile_coord.j_indg(n) - off_j; + + N_tile_in_cell_ij(i,j) = N_tile_in_cell_ij(i,j) + 1; + + k = N_tile_in_cell_ij(i,j); + + tile_num_in_cell_ij(i,j,k) = n; + + end + + max_N = max(max(N_tile_in_cell_ij)); + + disp(['Maximum number of tiles in tile def grid cell = ', num2str(max_N)]); + + tile_num_in_cell_ij = tile_num_in_cell_ij(:,:,1:max_N); + +end + +%========================================================================= \ No newline at end of file diff --git a/GEOSldas_App/util/inputs/obs_scaling_params/write_netcdf_latlon_grid.m b/GEOSldas_App/util/inputs/obs_scaling_params/write_netcdf_latlon_grid.m new file mode 100644 index 0000000..49969ad --- /dev/null +++ b/GEOSldas_App/util/inputs/obs_scaling_params/write_netcdf_latlon_grid.m @@ -0,0 +1,232 @@ +function [] = write_netcdf_latlon_grid( fname, colind, rowind, ll_lons, ll_lats, ... + data, pentad, start_time, end_time, overwrite, N_out_fields, ll_lon, ll_lat, d_lon, d_lat ) + + int_precision = 'NC_INT'; % precision of fortran tag + float_precision = 'NC_DOUBLE'; % precision of data in input file + + % Define the compression level (0-9, where 0 is no compression and 9 is maximum compression) + compression_level = 5; + + version = 0; + + % check dimensions + if size(data,1)~=N_out_fields + error('ERROR: size of data incompatible with N_out_fields') + end + + % check for presence of optional input "overwrite" + if ~exist('overwrite','var') + overwrite = 0; % default: do NOT overwrite existing files + end + + % check if file exists + if exist(fname,'file') + if overwrite==0 + disp(['RETURNING!!! -- NOT OVERWRITING EXISTING FILE ', fname]) + return + else + disp(['OVERWRITING ', fname]) + end + else + disp(['writing ', fname]) + end + + % Convert the cell arrays to matrices using cell2mat (to deal with all_pentad case) + year_mat = cell2mat({start_time.year}'); + month_mat = cell2mat({start_time.month}'); + day_mat = cell2mat({start_time.day}'); + hour_mat = cell2mat({start_time.hour}'); + min_mat = cell2mat({start_time.min}'); + sec_mat = cell2mat({start_time.sec}'); + % Use the matrices as input to the datetime function + d = datetime(year_mat, month_mat, day_mat, hour_mat, min_mat, sec_mat); + % Convert to serial date number + serialNum = datenum(d); + % Subtract serial date number of January 1, 1950 + daysSince1950 = serialNum - datenum('January 1, 1950'); + tmp_start_time = daysSince1950; + + % Convert the cell arrays to matrices using cell2mat (to deal with all_pentad case) + year_mat = cell2mat({end_time.year}'); + month_mat = cell2mat({end_time.month}'); + day_mat = cell2mat({end_time.day}'); + hour_mat = cell2mat({end_time.hour}'); + min_mat = cell2mat({end_time.min}'); + sec_mat = cell2mat({end_time.sec}'); + % Use the matrices as input to the datetime function + d = datetime(year_mat, month_mat, day_mat, hour_mat, min_mat, sec_mat); + % Convert to serial date number + serialNum = datenum(d); + % Subtract serial date number of January 1, 1950 + daysSince1950 = serialNum - datenum('January 1, 1950'); + tmp_end_time = daysSince1950; + + N_lon = length(ll_lons); + N_lat = length(ll_lats); + + % Have we got multiple pentads + if ismatrix(data) + N_pentad = 1; + else + N_pentad = size(data,3); + end + + % create netCDF file + netcdf.setDefaultFormat('FORMAT_NETCDF4'); + ncid = netcdf.create(fname, 'NETCDF4'); + + % define dimensions + dimid_pentad = netcdf.defDim(ncid, 'pentad', N_pentad); + dimid_lon = netcdf.defDim(ncid, 'lon', N_lon); + dimid_lat = netcdf.defDim(ncid, 'lat', N_lat); + + % define variables + + varid_version = netcdf.defVar(ncid, 'version', int_precision, []); + + varid_ll_lon = netcdf.defVar(ncid, 'll_lon', float_precision, []); + netcdf.putAtt(ncid, varid_ll_lon, 'standard_name', 'longitude of lower left corner'); + netcdf.putAtt(ncid, varid_ll_lon, 'long_name', 'longitude of lower left corner'); + netcdf.putAtt(ncid, varid_ll_lon, 'units', 'degrees_east'); + netcdf.putAtt(ncid, varid_ll_lon, 'axis', 'X'); + + varid_ll_lat = netcdf.defVar(ncid, 'll_lat', float_precision, []); + netcdf.putAtt(ncid, varid_ll_lat, 'standard_name', 'latitude of lower left corner'); + netcdf.putAtt(ncid, varid_ll_lat, 'long_name', 'latitude of lower left corner'); + netcdf.putAtt(ncid, varid_ll_lat, 'units', 'degrees_north'); + netcdf.putAtt(ncid, varid_ll_lat, 'axis', 'Y'); + + varid_d_lon = netcdf.defVar(ncid, 'd_lon', float_precision, []); + netcdf.putAtt(ncid, varid_d_lon, 'standard_name', 'longitude grid spacing'); + netcdf.putAtt(ncid, varid_d_lon, 'long_name', 'longitude grid spacing'); + netcdf.putAtt(ncid, varid_d_lon, 'units', 'degrees'); + netcdf.putAtt(ncid, varid_d_lon, 'axis', 'X'); + + varid_d_lat = netcdf.defVar(ncid, 'd_lat', float_precision, []); + netcdf.putAtt(ncid, varid_d_lat, 'standard_name', 'latitude grid spacing'); + netcdf.putAtt(ncid, varid_d_lat, 'long_name', 'latitude grid spacing'); + netcdf.putAtt(ncid, varid_d_lat, 'units', 'degrees'); + netcdf.putAtt(ncid, varid_d_lat, 'axis', 'Y'); + + varid_pentad = netcdf.defVar(ncid, 'pentad', int_precision, [dimid_pentad]); + netcdf.putAtt(ncid, varid_pentad, 'standard_name', 'pentad'); + netcdf.putAtt(ncid, varid_pentad, 'long_name', 'pentad'); + netcdf.putAtt(ncid, varid_pentad, 'units', '1'); + netcdf.putAtt(ncid, varid_pentad, 'axis', 'T'); + + varid_start_time = netcdf.defVar(ncid, 'start_time', float_precision, [dimid_pentad]); + netcdf.putAtt(ncid, varid_start_time, 'standard_name', 'start time'); + netcdf.putAtt(ncid, varid_start_time, 'long_name', 'start time'); + netcdf.putAtt(ncid, varid_start_time, 'axis', 'T'); + netcdf.putAtt(ncid, varid_start_time, 'units', 'days since 1950-01-01 00:00:00.0 +0000'); + + varid_end_time = netcdf.defVar(ncid, 'end_time', float_precision, [dimid_pentad]); + netcdf.putAtt(ncid, varid_end_time, 'standard_name', 'end time'); + netcdf.putAtt(ncid, varid_end_time, 'long_name', 'end time'); + netcdf.putAtt(ncid, varid_end_time, 'axis', 'T'); + netcdf.putAtt(ncid, varid_end_time, 'units', 'days since 1950-01-01 00:00:00.0 +0000'); + + varid_om = netcdf.defVar(ncid, 'o_mean', float_precision, [dimid_lat dimid_lon dimid_pentad]); + netcdf.defVarDeflate(ncid,varid_om,true,true,compression_level); + netcdf.putAtt(ncid, varid_om, 'standard_name', 'observation mean'); + netcdf.putAtt(ncid, varid_om, 'long_name', 'Observation mean for pentad calculated over all years for window length'); + netcdf.putAtt(ncid, varid_om, 'units', 'Degree of saturation (0-1)'); + + varid_ov = netcdf.defVar(ncid, 'o_std', float_precision, [dimid_lat dimid_lon dimid_pentad]); + netcdf.defVarDeflate(ncid,varid_ov,true,true,compression_level); + netcdf.putAtt(ncid, varid_ov, 'standard_name', 'observation standard deviation'); + netcdf.putAtt(ncid, varid_ov, 'long_name', 'Observation standard deviation for pentad calculated over all years for window length'); + netcdf.putAtt(ncid, varid_ov, 'units', 'Degree of saturation (0-1)'); + + varid_mm = netcdf.defVar(ncid, 'm_mean', float_precision, [dimid_lat dimid_lon dimid_pentad]); + netcdf.defVarDeflate(ncid,varid_mm,true,true,compression_level); + netcdf.putAtt(ncid, varid_mm, 'standard_name', 'model mean'); + netcdf.putAtt(ncid, varid_mm, 'long_name', 'Model mean for pentad calculated over all years for window length'); + netcdf.putAtt(ncid, varid_mm, 'units', 'Surface soil moisture (m^3 m^-3)'); + + varid_mv = netcdf.defVar(ncid, 'm_std', float_precision, [dimid_lat dimid_lon dimid_pentad]); + netcdf.defVarDeflate(ncid,varid_mv,true,true,compression_level); + netcdf.putAtt(ncid, varid_mv, 'standard_name', 'model standard deviation'); + netcdf.putAtt(ncid, varid_mv, 'long_name', 'Model standard deviation for pentad calculated over all years for window length'); + netcdf.putAtt(ncid, varid_mv, 'units', 'Surface soil moisture (m^3 m^-3)'); + + varid_mi = netcdf.defVar(ncid, 'm_min', float_precision, [dimid_lat dimid_lon]); + netcdf.defVarDeflate(ncid,varid_mi,true,true,compression_level); + netcdf.putAtt(ncid, varid_mi, 'standard_name', 'model minimum'); + netcdf.putAtt(ncid, varid_mi, 'long_name', 'Model minimum calculated over all years'); + netcdf.putAtt(ncid, varid_mi, 'units', 'Surface soil moisture (m^3 m^-3)'); + + varid_ma = netcdf.defVar(ncid, 'm_max', float_precision, [dimid_lat dimid_lon]); + netcdf.defVarDeflate(ncid,varid_ma,true,true,compression_level); + netcdf.putAtt(ncid, varid_ma, 'standard_name', 'model maximum'); + netcdf.putAtt(ncid, varid_ma, 'long_name', 'Model maximum calculated over all years'); + netcdf.putAtt(ncid, varid_ma, 'units', 'Surface soil moisture (m^3 m^-3)'); + + varid_ndata = netcdf.defVar(ncid, 'n_data', float_precision, [dimid_lat dimid_lon dimid_pentad]); + netcdf.defVarDeflate(ncid,varid_ndata,true,true,compression_level); + netcdf.putAtt(ncid, varid_ndata, 'standard_name', 'number of data points'); + netcdf.putAtt(ncid, varid_ndata, 'long_name', 'Number of data points for pentad calculated over all years for window length'); + netcdf.putAtt(ncid, varid_ndata, 'units', '1'); + + % end define mode + netcdf.endDef(ncid); + + % write data + netcdf.putVar(ncid, varid_pentad, pentad); + netcdf.putVar(ncid, varid_start_time, tmp_start_time); + netcdf.putVar(ncid, varid_end_time, tmp_end_time); + + netcdf.putVar(ncid, varid_ll_lon, ll_lon); + netcdf.putVar(ncid, varid_ll_lat, ll_lat); + netcdf.putVar(ncid, varid_d_lon, d_lon); + netcdf.putVar(ncid, varid_d_lat, d_lat); + + if N_pentad ==1 + + data_out = ones(N_out_fields,N_lat,N_lon ) * -999.0; + + for n = 1:N_out_fields + for i = 1:length(colind) + data_out(n,rowind(i),colind(i)) = data(n,i); + end + end + + netcdf.putVar(ncid,varid_om, data_out(1,:,:) ); + netcdf.putVar(ncid,varid_ov, data_out(2,:,:) ); + netcdf.putVar(ncid,varid_mm, data_out(3,:,:) ); + netcdf.putVar(ncid,varid_mv, data_out(4,:,:) ); + netcdf.putVar(ncid,varid_mi, data_out(6,:,:) ); + netcdf.putVar(ncid,varid_ma, data_out(7,:,:) ); + netcdf.putVar(ncid,varid_ndata, data_out(5,:,:) ); + + else + + data_out = ones(N_out_fields,N_lat,N_lon,N_pentad) * -999.0; + + for n = 1:N_out_fields + for i = 1:length(colind) + data_out(n,rowind(i),colind(i),:) = data(n,i,:); + end + end + + netcdf.putVar(ncid,varid_om, data_out(1,:,:,:) ); + netcdf.putVar(ncid,varid_ov, data_out(2,:,:,:) ); + netcdf.putVar(ncid,varid_mm, data_out(3,:,:,:) ); + netcdf.putVar(ncid,varid_mv, data_out(4,:,:,:) ); + netcdf.putVar(ncid,varid_ndata, data_out(5,:,:,:) ); + netcdf.putVar(ncid,varid_ma, max(data_out(7,:,:,:),[],4)); % Max over all pentads, always only 2D + + min_data = squeeze(data_out(6, :, :,:)); + min_data(min_data < -9998) = NaN; % Switch current missing value to NaN before calculating min + min_data_out = min(min_data,[],3); + min_data_out(isnan(min_data_out)) = -9999.; + + netcdf.putVar(ncid,varid_mi, min_data_out); % Min over all pentads, always only 2D + end + + % close netCDF file + netcdf.close(ncid); + +end + +% ================ EOF ======================================================== diff --git a/GEOSldas_App/util/inputs/obs_scaling_params/write_seqbin_file.m b/GEOSldas_App/util/inputs/obs_scaling_params/write_seqbin_file.m new file mode 100644 index 0000000..1415372 --- /dev/null +++ b/GEOSldas_App/util/inputs/obs_scaling_params/write_seqbin_file.m @@ -0,0 +1,305 @@ +function [] = write_seqbin_file(fname, colind, rowind,... + av_angle_bin, data, asc_flag, ... + version, ... + start_time, end_time, overwrite, N_out_fields, ... + write_ind_latlon, data_product,... + tile_id) %last argument is optional + +% write "fortran sequential" tile tavg files (identical to LDASsa output) +% +% optional input: +% +% overwrite = 0 -- do NOT overwrite existing files, print warning +% message, return +% overwrite = 1 -- overwrite existing files, print warning message +% +% De Lannoy, 4 Oct 2010 +% De Lannoy, 26 Sep 2012: added optional argument of tile_id +% used to write scaling files, with ''latlon_id''. +% De Lannoy, 25 Oct 2012: added the processor version number, +% inserted after the Asc_flag. +% ------------------------------------------------------------------ + +%N_out_fields % 1 - Col-index, 0-based; + % 2 - Row-index, 0-based; + %OR (for nearest neighbout) + % 1 - Lon; + % 2 - Lat; + +%N_out_fields % 1 - Tbh; + % 2 - Tbv; + + % 3 - heterogeneity index Tbh + % 4 - heterogeneity index Tbv + + % 5 - # SMOS pixels in EASE grid pixel Tbh + % 6 - # SMOS pixels in EASE grid pixel Tbv + + % 7 - RA Tbh + % 8 - RA Tbv + + %=> repeated for T3 and T4 (9-16) + + %OR FOR SMUDP2: + + % 1 - SM + % 2 - ST + % 3 - opacity + % 4 - Tbh; + % 5 - Tbv; + + % 6 - SM RSTD + % 7 - ST RSTD + % 8 - opac RSTD + + % 9 - stdv in SM (grid cell averaging) + % 10 - # small SMOS pixels inside 1 EASE grid cell; + + % 11 - omega; scattering albedo + % 12 - diff_albedos (om_H-om_V) + % 13 - max_roughness + % 14 - RSTD omega + % 15 - RSTD diff_omega + % 16 - RSTD max_roughness + +int_precision = 'int32'; % precision of fortran tag +float_precision = 'float32'; % precision of data in input file + +% check dimensions + +if size(data,1)~=N_out_fields + + error('ERROR: size of data incompatible with N_out_fields') + +end + +% check for presence of optional input "overwrite" + +if ~exist('overwrite','var') + + overwrite = 0; % default: do NOT overwrite existing files + +end + +% check if file exists + +if exist(fname,'file') + + if overwrite==0 + + disp(['RETURNING!!! -- NOT OVERWRITING EXISTING FILE ', fname]) + + return + + else + + disp(['OVERWRITING ', fname]) + + end + +else + disp(['writing ', fname]) + +end + +% open file + +ifp = fopen( fname, 'w', 'b' ); + +% determine number of grid cells ; further check dimensions + +N_grid = size(data,2); +N_angle= 1; + +if (length(size(data)) == 3) + N_angle = size(data,3); + data_org = data; + if (N_angle ~= length(av_angle_bin)) + disp(['ERROR in N_angle']) + return + end +end + +if (strcmp(write_ind_latlon,'latlon_id') && nargin == 14) + + if( size(tile_id,1) ~= N_grid ) + error('tile_id dimensions ??') + end + if ( size(tile_id,2) > 1) + disp(['# subgridcells per gridcell: ',num2str(size(tile_id,2))]); + end + +end + + +% write all records + +fortran_tag = 2*4; % length of each record in bytes + +count = fwrite( ifp, fortran_tag, int_precision ); +count = fwrite( ifp, [asc_flag version], int_precision ); +count = fwrite( ifp, fortran_tag, int_precision ); + +fortran_tag = 5*4; % length of each record in bytes + +count = fwrite( ifp, fortran_tag, int_precision ); +count = fwrite( ifp, [start_time.year, start_time.month, ... + start_time.day, start_time.hour, start_time.min], int_precision ); +count = fwrite( ifp, fortran_tag, int_precision ); + +count = fwrite( ifp, fortran_tag, int_precision ); +count = fwrite( ifp, [end_time.year, end_time.month, ... + end_time.day, end_time.hour, end_time.min], int_precision ); +count = fwrite( ifp, fortran_tag, int_precision ); + + +if (~(strcmp(data_product,'scaling') && strcmp(write_ind_latlon,'latlon_id') && nargin == 14)) + fortran_tag = 2*4; % length of each record in bytes + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, [N_grid N_angle], int_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); +else + fortran_tag = 3*4; % length of each record in bytes + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, [N_grid N_angle size(tile_id,2)], int_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); +end + +if (N_grid >= 1) + + fortran_tag = N_angle*4; %angles for which the output fields will be repeated below + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, squeeze(av_angle_bin(:)), float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + fortran_tag = N_grid*4; % length of each record in bytes + + if (strcmp(write_ind_latlon,'ind') ) + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, round(colind(:)), int_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, round(rowind(:)), int_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + elseif (strcmp(write_ind_latlon,'latlon') ) + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, colind(:), float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, rowind(:), float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + elseif (strcmp(write_ind_latlon,'latlon_id') && nargin == 14) + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, colind(:), float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, rowind(:), float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + for i=1:size(tile_id,2) + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, round(tile_id(:,i)), int_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + end + + else + + error('output-arguments do not line up') + + end + + fortran_tag = N_grid*4; + + for i=1:N_out_fields + + for j=1:N_angle + + if (N_angle > 1) + data = squeeze(data_org(:,:,j)); + end + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, data(i,:), float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + + end + + end + +else + + fortran_tag = N_angle*4; + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, squeeze(av_angle_bin(:)), float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + fortran_tag = 4; % length of each record in bytes + + if (strcmp(write_ind_latlon,'ind') ) + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, 0, int_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, 0, int_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + elseif (strcmp(write_ind_latlon,'latlon') ) + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, 0.0, float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, 0.0, float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + elseif (strcmp(write_ind_latlon,'latlon_id') && nargin == 14) + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, 0.0, float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, 0.0, float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, 0, int_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + else + + error('output-arguments do not line up') + + end + + for i=1:N_out_fields + + for j=1:N_angle + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, -999.0, float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + end + + end + +end + +fclose(ifp); + + diff --git a/GEOSldas_App/util/postproc/climatology/README b/GEOSldas_App/util/postproc/climatology/README new file mode 100644 index 0000000..0f2e3bd --- /dev/null +++ b/GEOSldas_App/util/postproc/climatology/README @@ -0,0 +1,24 @@ + +The scripts in this directory are used to generate climatology data from GEOSldas output. + +L4_SM Application: +------------------ +Post-process GEOSldas output into the soil moisture climatology nc4 file needed by the +SMAP L4SM ops system's "prcntl.py" script, which generates the soil moisture percentile +output ("sm_rootzone_pctl", "sm_profile_pctl") in the L4SM "Geophysical" (gph) file +collection. +The soil moisture climatology file needed by "prcntl.py" is created in two steps: + +1) Execute "Run_L4_sm_clim_stats.m" to create pentad binary climatology files + (73 for "sm_rootzone" + 73 for "sm_profile"). + +2) Execute "Write_L4_sm_clim_stat_bin2nc4.m" to create the single L4 input netcdf file + (*after* all binary pentad files are done). + +These jobs are memory intensive and typically take hours to run. On Discover, it is +necessary to use designated compute nodes. Step 1 may need to be run separately for +root-zone and profile soil moisture. + +Q. Liu, 29 Aug 2022 + +=========================== EOF ======================================================== \ No newline at end of file diff --git a/GEOSldas_App/util/postproc/climatology/Run_L4_sm_clim_stats.m b/GEOSldas_App/util/postproc/climatology/Run_L4_sm_clim_stats.m new file mode 100644 index 0000000..2534672 --- /dev/null +++ b/GEOSldas_App/util/postproc/climatology/Run_L4_sm_clim_stats.m @@ -0,0 +1,82 @@ +% Compute statistics of root-zone and profile soil moisture, which are needed to compute +% L4SM soil moisture percentile output. + +clear +addpath ../../shared/matlab +%------------------------------------------------------------------------- + +% experiment information +exp_path = {'/home/qliu/smap/SMAP_Nature/SMAP_Nature_v10/'}; +exp_run = {'SMAP_Nature_v10.0'}; + +domain = 'SMAP_EASEv2_M09_GLOBAL'; + +file_tag = {'tavg3_1d_lnr_Nt'}; + +% climatological period start and end year +start_year(1:12) = [repmat(2001,12,1)]; %start year for each month,the short start year was due to CPCU data inconsistance +end_year(1:12) = [repmat(2021,12,1)]; %end year for each month + +% only use 1 variable if running into memory issues +field_names = {'sm_rootzone','sm_profile'}; + +% convert soil moisture variables in "field_names" (if any) into +% wetness units +% (needed for L4SM because L4 ops script "prcntl.py" expects clim in +% wetness units, but clim is computed from Nature Run, which only +% outputs volumetric soil moisture) +out_wetness = 1; + +% linked to the output resolution +start_HHSS.hour = 1; +start_HHSS.min = 30; +start_HHSS.sec = 0; + +out_freq = 'pentad'; % pentad or monthly for now + +% now define the smoothing window based on the number of years of the clim period +if end_year(1) - start_year(1) > 9 + w_out_freq = 5; % smoothing window (number of pentads or months) +else + w_out_freq = 11; +end + +% minimum number of data requirement is based on number of years and window size +N_data_min = 2 * w_out_freq *(end_year(1)-start_year(1)+1); %per out_freq + +% ------------------ + +for n=1:length(exp_run) + + for ff = 1:length(field_names) + + if exist('time_of_day_in_hours','var') + + for j=1:length(time_of_day_in_hours) + + get_model_clim_stats( field_names{ff}, ... + exp_path{n}, exp_run{n}, domain, start_year, end_year, ... + out_freq, w_out_freq, file_tag{n}, ... + out_wetness, ... + N_data_min, time_of_day_in_hours(j) ) + + end + + else + + get_model_clim_stats( field_names{ff}, ... + exp_path{n}, exp_run{n}, domain, ... + start_year, end_year, start_HHSS, ... + out_freq, w_out_freq, file_tag{n}, ... + out_wetness, ... + N_data_min ) + + end + + end +end + + +% ============= EOF ==================================================== + + diff --git a/GEOSldas_App/util/postproc/climatology/Write_L4_sm_clim_stat_bin2nc4.m b/GEOSldas_App/util/postproc/climatology/Write_L4_sm_clim_stat_bin2nc4.m new file mode 100644 index 0000000..f476a5a --- /dev/null +++ b/GEOSldas_App/util/postproc/climatology/Write_L4_sm_clim_stat_bin2nc4.m @@ -0,0 +1,208 @@ +%========================================================================= +% script to convert SMAPL4 SM climatology statistic binary output to nc4 +% file. +% +% The tile_ids are in the same order as in ldas_tilecoord.txt +% +% Q. Liu - Jun 20, 2016 +% G. De Lannoy - Apr 21, 2015 +%========================================================================= + +clear all + +write_GEOSldas_tileorder = 0; + +time_stamp = '2001_p1_2021_p73'; +specs_tag = '_W_5p_Nmin_210'; + +bin_fpath = '/home/qliu/smap/SMAP_Nature/SMAP_Nature_v10/SMAP_Nature_v10.0/output/SMAP_EASEv2_M09_GLOBAL/stats/cli/'; + +file_out = ['L4SM_NRv10.0_cli_',time_stamp,specs_tag,'_sm_wetness_EASEv2_M09.nc4']; + +if write_GEOSldas_tileorder + file_out = ['GEOSldas_',file_out]; +end + +fname_out = [bin_fpath, file_out]; + +% read an arbitrary file to get header info +fname = [bin_fpath, 'cli_',time_stamp,specs_tag,'_sm_rootzone_wet_p06.bin']; + +N_stat = 99 + 5; % 99 percentile values + 5 stats (mean, stdv,min, max, N_data) + +[data_tmp, tile_id, lon, lat] = ... + read_seqbin_clim_pctl_file(fname, 1, N_stat, 'latlon_id','cli'); + +N_tile = length(tile_id); + +if write_GEOSldas_tileorder + + [tile_id_out, tile_idx] = sort(tile_id,'ascend'); + lon_out = lon(tile_idx); + lat_out = lat(tile_idx); +else + tile_id_out = tile_id; + lon_out = lon; + lat_out = lat; +end + +clear data_tmp + +netcdf.setDefaultFormat('FORMAT_NETCDF4'); +fout_id = netcdf.create(fname_out, 'NETCDF4'); + +if fout_id < 0, error(['Creating ' fname_out 'failed']); end + +% Setup global attributes + +NC_GLOBAL = netcdf.getConstant('GLOBAL'); + +netcdf.putAtt(fout_id, NC_GLOBAL, 'Title', ['SMAP L4 SM pentad clim. ',time_stamp,' statistics on EASEv2 M09']); + +netcdf.putAtt(fout_id, NC_GLOBAL, 'Filename', file_out); +netcdf.putAtt(fout_id, NC_GLOBAL, 'Institution', 'NASA GMAO'); +netcdf.putAtt(fout_id, NC_GLOBAL, 'History', ['File written by matlab-r2021a on ',datestr(now)]); +netcdf.putAtt(fout_id, NC_GLOBAL, 'Contact', 'NASA/GMAO Rolf Reichle'); +netcdf.putAtt(fout_id, NC_GLOBAL, 'Comments', 'NETCDF-4'); + +% Define dimensions: + +dimid1 = netcdf.defDim(fout_id,'tile',length(tile_id_out)); +dimid2 = netcdf.defDim(fout_id,'percentile_wetness' , 99); +dimid3 = netcdf.defDim(fout_id,'pentad', netcdf.getConstant('UNLIMITED')); + +% Define global variables: + +varid = netcdf.defVar(fout_id,'tile_id','int',dimid1); +netcdf.putAtt(fout_id,varid,'standard_name','tile_id'); +netcdf.putAtt(fout_id,varid,'long_name','tile_id'); +netcdf.putAtt(fout_id,varid,'units','-'); +netcdf.putVar(fout_id,varid,tile_id_out); + +varid = netcdf.defVar(fout_id,'lon','double',dimid1); +netcdf.putAtt(fout_id,varid,'standard_name','longitude'); +netcdf.putAtt(fout_id,varid,'long_name','longitude'); +netcdf.putAtt(fout_id,varid,'units','degrees_east'); +netcdf.putVar(fout_id,varid,lon_out); + +varid = netcdf.defVar(fout_id,'lat','double',dimid1); +netcdf.putAtt(fout_id,varid,'standard_name','latitude'); +netcdf.putAtt(fout_id,varid,'long_name','latitude'); +netcdf.putAtt(fout_id,varid,'units','degrees_north'); +netcdf.putVar(fout_id,varid,lat_out); + +% Synchronize global +netcdf.sync(fout_id) + +% Define groups and variables in each group + +vars = {'mean', 'stdv', 'min', 'max', 'N_data', 'percentile_UL'}; + +da_group_id(1) = netcdf.defGrp(fout_id, 'rootzone_wetness_cli_stat'); + +da_group_id(2) = netcdf.defGrp(fout_id, 'profile_wetness_cli_stat'); + +fillValue = single(1.e15); + +DeflateLevel = 5; + +% Put data into the data group: + +% Insert data: + +for i=1:length(da_group_id) %loop through groups + + start_time = 0; + + for k = 1:73 % loop through pentad + + clear data + + if i==1 + + fname = [bin_fpath, 'cli_',time_stamp,specs_tag,'_sm_rootzone_wet_p', ... + num2str(k, '%2.2d'), '.bin']; + + else + + fname = [bin_fpath, 'cli_',time_stamp,specs_tag,'_sm_profile_wet_p', ... + num2str(k, '%2.2d'), '.bin']; + + end + + [data_tmp, tile_id_old, lon_old, lat_old] = ... + read_seqbin_clim_pctl_file(fname, 1, 104, 'latlon_id','cli'); + + clear tile_id_old lon_old lat_old + + if write_GEOSldas_tileorder + data = data_tmp(tile_idx,:); + else + data = data_tmp; + end + + clear data_tmp + + for iv = [1 3 4 6:size(data,2)] + data(data(:,iv)>1, iv) = 1.; + end + + + data(data<-9998) = fillValue; + data(isnan(data)) = fillValue; + + + for iv = 1:4 % loop through variables + + if k == 1 + + varid(iv) = netcdf.defVar(da_group_id(i),vars{iv},'float',[dimid1,dimid3]); + netcdf.putAtt(da_group_id(i),varid(iv),'name', vars{iv}); + netcdf.putAtt(da_group_id(i),varid(iv),'units','wetness'); + netcdf.defVarFill(da_group_id(i),varid(iv),false,fillValue); + netcdf.defVarDeflate(da_group_id(i),varid(iv),true,true, DeflateLevel); + + end + + netcdf.putVar(da_group_id(i),varid(iv),[0,start_time], [N_tile,1], squeeze(data(:,iv))); + + end + + if k == 1 + + varid(5) = netcdf.defVar(da_group_id(i),vars{5},'int',[dimid1,dimid3]); + netcdf.putAtt(da_group_id(i),varid(5),'name', vars{5}); + netcdf.putAtt(da_group_id(i),varid(5),'units','-'); + netcdf.defVarDeflate(da_group_id(i),varid(5),true,true, DeflateLevel); + + end + + netcdf.putVar(da_group_id(i),varid(5),[0,start_time], [N_tile,1],squeeze(data(:,5))); + + if k==1 + + varid(6) = netcdf.defVar(da_group_id(i),vars{6},'float',[dimid2,dimid1,dimid3]); + netcdf.putAtt(da_group_id(i),varid(6),'name', vars{6}); + netcdf.putAtt(da_group_id(i),varid(6),'units','wetness'); + netcdf.defVarFill(da_group_id(i),varid(6),false,fillValue); + netcdf.defVarDeflate(da_group_id(i),varid(6),true,true, DeflateLevel); + + end + + netcdf.putVar(da_group_id(i),varid(6),[0,0,start_time], [99,N_tile, 1],squeeze(data(:,6:104)')); + + start_time = start_time +1; + end + + netcdf.sync(da_group_id(i)) + + +end + +% Synchronize: +netcdf.close(fout_id) + +%====================================EOF=================================== + + + diff --git a/GEOSldas_App/util/postproc/climatology/get_model_clim_stats.m b/GEOSldas_App/util/postproc/climatology/get_model_clim_stats.m new file mode 100644 index 0000000..7082a6c --- /dev/null +++ b/GEOSldas_App/util/postproc/climatology/get_model_clim_stats.m @@ -0,0 +1,433 @@ +function [] = get_model_clim_stats( fieldname, ... + exp_path, exp_run, domain, ... + start_year, end_year, start_HHSS, ... + out_freq, w_out_freq, file_tag, ... + out_wetness, ... + N_data_min, time_of_day_in_hours ) + +%======================================================================= +% +% Compute mean, stdv and CDF-stats of simulated fields from +% - tile-based "nc4" files +% - or gridded "h5" files +% for a selection of fieldname. +% +% The main purpose of this function is to aggregate the information +% from the "cat" files so that the climatology statistics can +% be used for the computation of percentiles. +% +% +% Need to fix those file_tag-s... at some point, we had to calculate cli-files +% based on all different file types... ugly +% +% +% One file with statistics is generated for every pentad or month. +% +% fieldname: (single) model field to be processed +% start_year: start year for each month (12 entries!) +% end_year: end year for each month (12 entries!) +% start_HHSS: hour, min, sec of first file (depends on output resolution) +% out_freq: 'monthly' or 'pentad' climatology files +% w_out_freq: number of months or pentads used in the temporal smoothing +% N_data_min: minimum number of data points to calculate a good stat +% +% 'cli'-file has a similar format as the SMOS-data files and +% observation scaling files for assimilation. +% +% ==HEADER== +% N_tiles N_fields N_stat +% fields +% +% ==DATA== +% +% %%%% tile ID --> for all tiles (1:N_tiles, not sorted by tile_id!) +% lat +% lon +% +% [for fields] +% mean +% std +% min +% max +% N_data +% CDF_parameter_1 OR UL_1st_percentile +% CDF_parameter_2 UL_2st_percentile +% CDF_parameter_... UL_... +% CDF_parameter_N UL_99st_percentile +% [end fields] +% +% GDL, feb 2014 +% +% ------------------------------------------------------------------- +% begin user-defined inputs +% ------------------------------------------------------------------- + +nodata = -9999; +nodata_tol = 1e-4; + +ens_tag = 'ens0000'; + +dtstep = 3*60*60; % hardwired 3-hourly if x-hourly output is given + +% when reading h5-data + +datagroup_name = '/Geophysical_Data/'; + +% output specs + +overwrite = 1; + +percentiles = [1:99]; + +N_stat = 5; %mean, stdv, min, max, N_data +N_CDF = length(percentiles); + +N_stat_CDF = N_stat+N_CDF; + +write_ind_latlon = 'latlon_id'; %'latlon'; + +% ------------------------------------------------------------------- +% end user-defined inputs +% ------------------------------------------------------------------- + +N_fields = 1; + +fieldno = 1; + +field_tag = ['_',fieldname]; + +% ------------------------------------------------------------------- + +% determine number of entries in smoothing time window + +if (std(end_year-start_year) ~= 0) + error('same number of years should contribute to each month') +end + +if strcmp(out_freq,'pentad') + n_days = 5; +elseif strcmp(out_freq,'monthly') + n_days = 365/12.0; % could adjust this and work w/ min_days=28, max_days=31 +end + +disp(['smoothing window is ',num2str(n_days*w_out_freq),' days']); + +n_time_count = round(w_out_freq * n_days * (max(end_year-start_year)+1) *... + ((24*60*60)./dtstep)); + +% auxiliary start-end_time to get 1 year climatology: +% - loop over all days in any non-leap year (365 days) +% - make sure to loop into the next year to cover all climatology pentads +% or months w/ the complete smoothing window + +start_time = start_HHSS; +start_time.year = 2014; +start_time.month = 1; +start_time.day = 1; + +start_time = get_dofyr_pentad(start_time); %ini correct pentad + +end_time = augment_date_time((365 + w_out_freq * n_days)*24*60*60, ... + start_time); + +% effective period used in climatology calculation + +start_time_true = start_time; +start_time_true.year = min(start_year); +tmp = find(start_year==min(start_year)); +start_time_true.month = tmp(1); +start_time_true = get_dofyr_pentad( start_time_true ); + +end_time_true = end_time; +end_time_true.year = max(end_year); +tmp = find(start_year==max(start_year)); +end_time_true.month = tmp(end); +end_time_true.day = days_in_month( end_time_true.year, end_time_true.month); +end_time_true = get_dofyr_pentad( end_time_true ); + +% assemble input and output paths + +inpath = [ exp_path, '/', exp_run, '/output/', domain ]; +outpath = [ inpath, '/stats/cli/' ]; + +% create outpath if it doesn't exist + +if ~exist(outpath,'dir') + eval(['!mkdir -p ', outpath]); +end + +% ------------------------------------------------------------- + +% load catchment coordinates + +fname = [inpath, '/rc_out/', exp_run, '.ldas_tilecoord.bin']; + +[ tile_coord ] = read_tilecoord( fname ); + +N_tile = tile_coord.N_tile; + +lat_out = tile_coord.com_lat; +lon_out = tile_coord.com_lon; +tile_coord_tile_id = tile_coord.tile_id; + +% determine if conversion of soil moisture variables to wetness is +% needed; if yes, get porosity from cat_param file + +convert_to_wetness = 0; + +if contains(field_tag,'sm_') & ~contains(field_tag,'wet') & out_wetness + + field_tag = [field_tag,'_wet']; + convert_to_wetness = 1; + + catfname = [exp_path,'/',exp_run,'/output/',domain,'/rc_out/','/Y2015/M01/',... + exp_run,'.ldas_catparam.','20150101_0000','z.bin']; + + cat_param = read_catparam( catfname, N_tile ); + + poros = cat_param.poros; clear cat_param + +end + +% ------------------------------------------------------------- + +% assemble output file name + +if strcmp(out_freq,'pentad') + + fname_out_base = [ outpath, '/', 'cli_', ... + num2str(start_time_true.year), '_p', ... + num2str(start_time_true.pentad),'_', ... + num2str(end_time_true.year), '_p', ... + num2str(end_time_true.pentad), '_', ... + 'W_', num2str(w_out_freq),'p_', ... + 'Nmin_', num2str(round(N_data_min),'%d'), ... + field_tag]; + +else strcmp(out_freq,'monthly') + + fname_out_base = [ outpath, '/', 'cli_', ... + num2str(start_time_true.year), '_M', ... + num2str(start_time_true.month),'_', ... + num2str(end_time_true.year), '_M', ... + num2str(end_time_true.month), '_', ... + 'W_', num2str(w_out_freq),'M_', ... + 'Nmin_', num2str(round(N_data_min),'%d'), ... + field_tag]; + +end + +if exist( 'time_of_day_in_hours', 'var') + + fname_out_base = [fname_out_base, '_', num2str(time_of_day_in_hours,'%2.2d'), 'z']; + +end + +% ------------------------------------------------------------- + +% initialize output statistics + +hist_data = zeros(N_tile,N_fields,n_time_count); + +time_count = 0; + +data_out = NaN+zeros(N_fields,N_tile,N_stat_CDF); + +% ------------------------------------------------------------- + +disp('climatology calculation') + +time_new = start_time; + +while 1 + + if (time_new.year == end_time.year &... + time_new.month == end_time.month &... + time_new.day == end_time.day &... + time_new.hour == end_time.hour &... + time_new.min == end_time.min &... + time_new.sec == end_time.sec ) + break + end + + % augment date_time + + time_old = time_new; + pentad_old = time_new.pentad; + month_old = time_new.month; + + time_new = augment_date_time(dtstep, time_old); + pentad_new = time_new.pentad; + month_new = time_new.month; + + % check if diurnal stats are needed + + if exist('time_of_day_in_hours','var') + tmp_hour = time_of_day_in_hours; + else + tmp_hour = time_old.hour; % all hours of day will be included + end + + if time_old.hour==tmp_hour + + minute = time_old.min; % floor( (seconds_in_day-hour*3600)/60 ); + seconds = time_old.sec; % seconds_in_day-hour*3600-minute*60; + + if (seconds~=0) + error('something is wrong! (seconds~=0)') + end + + for year = start_year(time_old.month):end_year(time_old.month) + + time_count = time_count+1; + + YYYYMMDD = [ num2str(year, '%4.4d'), ... + num2str(time_old.month, '%2.2d'), ... + num2str(time_old.day, '%2.2d') ]; + + HHMM = [ num2str(time_old.hour, '%2.2d'), ... + num2str(time_old.min, '%2.2d') ]; + + fname = [ inpath, ... + '/cat/', ens_tag, ... + '/Y', num2str(year, '%4.4d'), ... + '/M', num2str(time_old.month,'%2.2d'), ... + '/', exp_run, ... + '.', file_tag, '.', YYYYMMDD, '.nc4' ]; + + if ~exist(fname,'file') + + % try again with "_[HHMM]z" inserted into file name + + fname = [ inpath, ... + '/cat/', ens_tag, ... + '/Y', num2str(year, '%4.4d'), ... + '/M', num2str(time_old.month,'%2.2d'), ... + '/', exp_run, ... + '.', file_tag, '.', YYYYMMDD, '_', HHMM, 'z.nc4' ]; + + end + + disp(['reading ',fieldname,' from ',fname]) + data_tmp = ncread(fname, fieldname); + + if size(data_tmp,2) == 8 % hard-wired 3-hourly time step?? + tile_data_tmp = data_tmp(:,ceil(time_old.hour/3.)); clear data_tmp + elseif size(data_tmp,2) == 1 + tile_data_tmp = data_tmp; clear data_tmp + else + error(['data size is incorrect from ', fname]) + end + + for s=1:N_fields + + if ~convert_to_wetness + tile_data_tmp_1D = tile_data_tmp(:); + else + tile_data_tmp_1D = tile_data_tmp(:)./poros(:); + end + + good_data = find(~(abs(tile_data_tmp_1D - nodata) < nodata_tol)); + + tile_data_tmp_1D = tile_data_tmp_1D(good_data); + + %Keep a record of time series + + total_bin_good_ind = (time_count-1).*(N_fields*N_tile) + ... + (s-1)*N_tile+good_data'; + + hist_data(total_bin_good_ind) = tile_data_tmp_1D; + + end + + end % loop through years + + end % time_of_day_in_hours + + % check if output needs to be written + + if (time_count == n_time_count ) + + % write output + + for s=1:N_fields + + %edges = edge_min(s):edge_dx(s):edge_max(s); + + for tile=1:N_tile + + tmp = reshape(squeeze(hist_data(tile, s, :)),1,[]); + + data_out(s,tile,1) = mean( tmp,"omitnan"); % mean + data_out(s,tile,2) = std( tmp,"omitnan"); % stdv + data_out(s,tile,3) = min( tmp ); % min + data_out(s,tile,4) = max( tmp ); % max + data_out(s,tile,5) = sum(~isnan(tmp) ); % N_data + + % determine the CDF-parameters, or the edges for each + % percentile + + perc = round(percentiles./100*data_out(s,tile,5)); + + tmp = sort(tmp); + + data_out(s,tile,N_stat+1:N_stat+N_CDF) = tmp(perc); + + end + + end + + bad_ind = find(data_out(:,:,5) 1) + + if (strcmp(file_type,'cli')) + + if (strcmp(read_ind_latlon,'ind')) + + fortran_tag = fread( ifp, 1, int_precision ); + col_ind = fread( ifp, [1 N_grid], int_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + fortran_tag = fread( ifp, 1, int_precision ); + row_ind = fread( ifp, [1 N_grid], int_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + elseif (strcmp(read_ind_latlon,'latlon')) + + fortran_tag = fread( ifp, 1, int_precision ); + col_ind = fread( ifp, [1 N_grid], float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + fortran_tag = fread( ifp, 1, int_precision ); + row_ind = fread( ifp, [1 N_grid], float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + elseif (strcmp(read_ind_latlon,'latlon_id') ) + + fortran_tag = fread( ifp, 1, int_precision ); + col_ind = fread( ifp, [1 N_grid], float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + fortran_tag = fread( ifp, 1, int_precision ); + row_ind = fread( ifp, [1 N_grid], float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + fortran_tag = fread( ifp, 1, int_precision ); + tile_id = fread( ifp, [1 N_grid], int_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + else + + error('not sure how the file looks like, based on the combination of input-specs') + + end + + end + + data = NaN*ones(N_field,N_grid,N_stat); + + for j=1:N_stat + + for i=1:N_field + + if (j == 5 && strcmp(file_type,'cli')) + + fortran_tag = fread( ifp, 1, int_precision ); + tmp_data = fread( ifp, [1 N_grid], int_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + else + + fortran_tag = fread( ifp, 1, int_precision ); + tmp_data = fread( ifp, [1 N_grid], float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + end + + data(i,1:N_grid,j) = tmp_data(1:N_grid); + + end + + end + + data = squeeze(data); + +else + + data = NaN*ones(N_field,1); + col_ind = NaN; + row_ind = NaN; + fieldno = NaN; + +end + +fclose(ifp); + +% ======= EOF ========================================================== diff --git a/GEOSldas_App/util/postproc/climatology/write_seqbin_clim_pctl_file.m b/GEOSldas_App/util/postproc/climatology/write_seqbin_clim_pctl_file.m new file mode 100644 index 0000000..22f3569 --- /dev/null +++ b/GEOSldas_App/util/postproc/climatology/write_seqbin_clim_pctl_file.m @@ -0,0 +1,255 @@ +function [] = write_seqbin_clim_pctl_file(fname, colind, rowind,... + data, fieldno, N_stat,... + overwrite, ... + write_ind_latlon, file_type, tile_id) %last argument is optional + +% write "fortran binary sequential" tile files with climatology info +% or percentile output +% +% optional input: +% +% overwrite = 0 -- do NOT overwrite existing files, print warning +% message, return +% overwrite = 1 -- overwrite existing files, print warning message +% +% De Lannoy, 27 Feb 2014: adopted from write_seqbin_file.m +% ------------------------------------------------------------------ + +nodata_val = -9999.0; + +int_precision = 'int32'; % precision of fortran tag +float_precision = 'float32'; % precision of data in input file + +% check dimensions + +if size(data,1)~=length(fieldno) + + error('ERROR: size of data incompatible with N_fields') + +end + +N_grid = size(data,2); + +N_field = length(fieldno); + +if (length(size(data)) == 3) + + N_stat_tmp = size(data,3); + + data_org = data; + + if (N_stat_tmp ~= N_stat) + disp(['ERROR in N_stat ',num2str(N_stat_tmp),' vs ',num2str(N_stat)]) + return + end + +end + + +% check for presence of optional input "overwrite" + +if ~exist('overwrite','var') + + overwrite = 0; % default: do NOT overwrite existing files + +end + +% check if file exists + +if exist(fname,'file') + + if overwrite==0 + + disp(['RETURNING!!! -- NOT OVERWRITING EXISTING FILE ', fname]) + + return + + else + + disp(['OVERWRITING ', fname]) + + end + +else + disp(['writing ', fname]) + +end + +% open file + +ifp = fopen( fname, 'w', 'b' ); + +% determine number of grid cells ; further check dimensions + + +if (strcmp(write_ind_latlon,'latlon_id') && exist('tile_id','var')) + + if ( size(tile_id,1) ~= N_grid ) + error('tile_id dimensions ??') + end + if ( size(tile_id,2) > 1) + disp(['# subgridcells per gridcell: ',num2str(size(tile_id,2))]); + end + +end + + +% write all records + +if (strcmp(file_type,'cli')) + + % dimensions + + fortran_tag = 2*4; % length of each record in bytes + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, [N_grid N_field], int_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + +end + +if (N_grid >= 1) + + if (strcmp(file_type,'cli') ) + + fortran_tag = N_grid*4; % length of each record in bytes + + if (strcmp(write_ind_latlon,'ind') ) + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, round(colind(:)), int_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, round(rowind(:)), int_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + elseif (strcmp(write_ind_latlon,'latlon') ) + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, colind(:), float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, rowind(:), float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + elseif (strcmp(write_ind_latlon,'latlon_id') ) + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, colind(:), float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, rowind(:), float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + for i=1:size(tile_id,2) + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, round(tile_id(:,i)), int_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + end + + else + + error('output-arguments do not line up') + + end + + end + + fortran_tag = N_grid*4; + + for j=1:N_stat + + for i=1:N_field + + if (N_stat > 1) + data = squeeze(data_org(:,:,j)); + end + + if ( j == 5 && strcmp(file_type,'cli')) + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, round(data(i,:)), int_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + else + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, data(i,:), float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + end + + end + + end + +else + + if (strcmp(file_type,'cli')) + + fortran_tag = 4; % length of each record in bytes + + if (strcmp(write_ind_latlon,'ind') ) + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, 0, int_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, 0, int_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + elseif (strcmp(write_ind_latlon,'latlon') ) + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, 0.0, float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, 0.0, float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + elseif (strcmp(write_ind_latlon,'latlon_id')) + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, 0.0, float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, 0.0, float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, 0, int_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + + else + + error('output-arguments do not line up') + + end + + end + + for j=1:N_stat + + for i=1:N_field + + if ( j == 5 && strcmp(file_type,'cli')) + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, 0, int_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + else + count = fwrite( ifp, fortran_tag, int_precision ); + count = fwrite( ifp, nodata_val, float_precision ); + count = fwrite( ifp, fortran_tag, int_precision ); + end + + end + + end + +end + +fclose(ifp); + +%=========================EOF==================================== diff --git a/GEOSldas_App/util/postproc/compress_bit-shaved_nc4.sh b/GEOSldas_App/util/postproc/compress_bit-shaved_nc4.sh new file mode 100644 index 0000000..fb17a1b --- /dev/null +++ b/GEOSldas_App/util/postproc/compress_bit-shaved_nc4.sh @@ -0,0 +1,36 @@ +#!/usr/local/bin/bash +# +# lossless compression of (bit-shaved) nc4 output, operates recursively in +# +# usage: compress_bit-shaved_nc4.sh +# +# --------------------------------------------------------------- + +usage(){ + echo -e "\nUsage: $0 + = name of output directory with bit-shaved nc4 files +" + exit 1; +} + +if [[ $# -lt 1 ]]; then + usage + exits; +fi + +# deflation_level 1-9; higher level (e.g. 9) takes longer; recommended: 3 (based on L4_SM) +deflate_level=3 + +# By default, the ncks command is executed sequentially for each nc4 file +# in . Modify this script if multi-threading is needed to run +# the script on a compute node. + +for file in $(find $1 -name "*.nc4" -type f); do + #echo "Processing $file..." + # # For simple multi-threading: + ncks -L $deflate_level -O $file $file # <-- comment out this line +### ncks -L $deflate_level -O $file $file & # <-- uncomment this line +done; +###wait # <-- uncomment this line + +# ===================== EOF ======================================================= diff --git a/GEOSldas_App/util/postproc/write_smapL4SMqa.m b/GEOSldas_App/util/postproc/write_smapL4SMqa.m new file mode 100644 index 0000000..55be8d4 --- /dev/null +++ b/GEOSldas_App/util/postproc/write_smapL4SMqa.m @@ -0,0 +1,1315 @@ +function [] = write_smapL4SMqa( gph_aup_lmc_fnames, tilecoord_fname, tilegrids_fname ) + +% THE FOLLOWING PATH SHOULD BE ADDED IN MATLAB SCRIPT THAT CALLS THIS FUNCTION +% add path to matlab functions in src/Components/GEOSldas_GridComp/GEOSldas_App/util/shared/matlab/ +addpath('../shared/matlab/'); + +% Generate *.qa files from SMAP L4_SM "gph" or "aup" granules. +% +% Inputs: +% +% gph_and_aup_fnames : cell array of gph and/or aup granule (file) names +% tilecoord_fname : "tilecoord" file name +% tilegrids_fname : "tilegrids" file name +% +% The "tilecoord" and "tilegrids" information must work with all +% input granules that are to be converted. +% +% For each "gph" or "aup" granule, the *.qa file is written into +% the directory that holds the granule. +% +% Currently operates on binary LDASsa aup files. (Could be changed +% but would require h5 readers for "gph" and "aup" granules.) +% +% TBD: Insert official "h5" granule name into *.qa files. +% +% reichle, 12 Feb 2014 +% de lannoy, 17 Feb 2014: added lmc +% reichle, 18 Feb 2014: minor edits and clean-up +% +% ------------------------------------------------------------------------------------ + +% ######################### SAMPLE DRIVER SCRIPT ##################################### +% +% exppath = '/hydro/gdelanno/SMAP_Delivered/SMAP_L4_SM_D00500/'; +% +% expid = 'SMAP_D00500_L4_SM_synth_e001'; +% +% expdom = 'SMAP_EASEv2_M09_GLOBAL'; +% +% yyyymm = '200107'; +% +% gphpath = [exppath, '/', expid, '/', expdom, '/cat/ens_avg/', ... +% '/Y', yyyymm(1:4), '/M', yyyymm(5:6), '/']; +% +% auppath = [exppath, '/', expid, '/', expdom, '/ana/ens_avg/', ... +% '/Y', yyyymm(1:4), '/M', yyyymm(5:6), '/']; +% +% lmcpath = [exppath, '/', expid, '/', expdom, '/rc_out/', ... +% '/Y', yyyymm(1:4), '/M', yyyymm(5:6), '/']; +% +% tcpath = [exppath, '/', expid, '/', expdom, '/rc_out/']; +% +% tc_fname = [tcpath, expid, '.ldas_tilecoord.bin']; +% tg_fname = [tcpath, expid, '.ldas_tilegrids.bin']; +% +% gphnames = ... +% {[expid,'.ens_avg.ldas_tile_xhourly_out.20010725_0130z.bin'], ... +% [expid,'.ens_avg.ldas_tile_xhourly_out.20010725_0430z.bin'], ... +% [expid,'.ens_avg.ldas_tile_xhourly_out.20010725_0730z.bin'], ... +% [expid,'.ens_avg.ldas_tile_xhourly_out.20010725_1030z.bin'], ... +% [expid,'.ens_avg.ldas_tile_xhourly_out.20010725_1330z.bin'], ... +% [expid,'.ens_avg.ldas_tile_xhourly_out.20010725_1630z.bin'], ... +% [expid,'.ens_avg.ldas_tile_xhourly_out.20010725_1930z.bin'], ... +% [expid,'.ens_avg.ldas_tile_xhourly_out.20010725_2230z.bin']}; +% +% aupnames = ... +% {[expid,'.ens_avg.ldas_tile_inst_smapL4SMaup.20010725_0300z.bin'], ... +% [expid,'.ens_avg.ldas_tile_inst_smapL4SMaup.20010725_0600z.bin'], ... +% [expid,'.ens_avg.ldas_tile_inst_smapL4SMaup.20010725_0900z.bin'], ... +% [expid,'.ens_avg.ldas_tile_inst_smapL4SMaup.20010725_1200z.bin'], ... +% [expid,'.ens_avg.ldas_tile_inst_smapL4SMaup.20010725_1500z.bin'], ... +% [expid,'.ens_avg.ldas_tile_inst_smapL4SMaup.20010725_1800z.bin'], ... +% [expid,'.ens_avg.ldas_tile_inst_smapL4SMaup.20010725_2100z.bin'], ... +% [expid,'.ens_avg.ldas_tile_inst_smapL4SMaup.20010726_0000z.bin']}; +% +% % there should only be one "lmc" file: +% +% lmcname = [expid,'.ldas_smapL4SMlmc.20010725_0000z.bin']; +% +% % concatenate all file names into one cell array +% +% for ii=1:length(gphnames) +% +% fnames{ii} = [gphpath, gphnames{ii}]; +% +% end +% +% ii_off = length(gphnames); +% +% for ii=1:length(aupnames) +% +% fnames{ii+ii_off} = [auppath, aupnames{ii}]; +% +% end +% +% ii_off = ii_off + length(aupnames); +% +% fnames{ii_off+1} = [lmcpath, lmcname]; +% +% write_smapL4SMqa( fnames, tc_fname, tg_fname ); +% +% ##################### END SAMPLE DRIVER SCRIPT ##################################### + +% ------------------------------------------------------------------------------------ +% +% make sure that list of input file names is a cell array + +if ~iscell(gph_aup_lmc_fnames) + + error('write_smapL4SMqa.m: input list of file names must be a cell array') + +end + +% ---------------------------------------------- + +% read tile coordinate information + +tile_coord = read_tilecoord(tilecoord_fname); + +% Make sure LDASsa output was for EASEv2_M09 tile space + +[ tile_grid_g, tile_grid_d ] = read_tilegrids(tilegrids_fname); + +if isempty(strfind(tile_grid_d.gridtype, 'EASEv2_M09')) + error('Expecting aup file in EASEv2_M09 tile space'); +end + +% ---------------------------------------------- + +% process each file in list of input files + +for ii=1:length(gph_aup_lmc_fnames) + + this_fname = gph_aup_lmc_fnames{ii}; + + disp(['processing: ', this_fname]) + + % parse input string name to decide whether a "gph" or "aup" granule is to be converted + + if any( findstr( this_fname , 'ldas_tile_xhourly_out' )) + + get_gph_qa( this_fname, tile_coord ); + + elseif any( findstr( this_fname , 'ldas_tile_inst_smapL4SMaup' )) + + get_aup_qa( this_fname, tile_coord ); + + elseif any( findstr( this_fname , 'ldas_smapL4SMlmc' )) + + get_lmc_qa( this_fname, tile_coord ); + + else + + error('write_smapL4SMqa.m: something wrong with input file name') + + end + + disp(['---------------------------------------------------------']) + +end + + +% ********************************************************************************************* +% ********************************************************************************************* +% ********************************************************************************************* + + +function [] = get_gph_qa( gph_fname, tile_coord ) + +%==================================================================== +% +% Matlab function to produce .qa-files for SMAP L4_SM *gph* output. +% +% 30jan14: Gabrielle De Lannoy - initial draft +% 1feb14: Gabrielle De Lannoy - use land fraction to calculate stats +% - text edits, formatting +% 7feb14: Gabrielle De Lannoy - edits +% +% - currently operates on binary LDASsa gph files +% - TBD: file name change from "bin" to official "h5" granule name +% +%==================================================================== +% +% [QA] -- SMAP_L4_SM_PSD p.29: +% +% "... +% The QA file contains statistical information that will enable users +% to better assess the quality of the associated granule. +% QA products bear exactly the same name as the products [(.h5)] +% that they represent. The only difference in names is the extension. +% The extension for all QA products is *.qa. +% ..." +% +% [gph] -- SMAP_L4_SM_PSD p.4: +% "... +% The first Collection is a series of 3-hourly time average geophysical ("gph") +% land surface fields that are output by the L4_SM algorithm. This +% Collection will be of primary interest to most users. +% ..." +% +%==================================================================== + +check_on = 1; %1 = LDASsa sanity checks, write warnings + %2 = LDASsa sanity checks, stop if check fails + +nodata_val = -9999; +nodata_tol = 1E-4; + +tablefields = {'Fieldname','Units',... + 'Mean','Std-dev','Min','Max','N'}; + +Nstat = length(tablefields)-2; + +str_l = 51; +unt_l = 16; +num_l = 13; +num_s_l = 9; + +str_f = num2str(str_l); +unt_f = num2str(unt_l); +num_f = num2str(num_l); +num_s_f = num2str(num_s_l); + +delim = ','; + +tableformat = [ '%-',str_f,'s',delim,'%-',unt_f,'s']; +tableformat_sc = [ '%-',str_f,'s',delim,'%-',unt_f,'s']; + +for f=1:Nstat + if f~=Nstat + tableformat = [tableformat, delim,'%',num_f,'.4f']; + tableformat_sc = [tableformat_sc, delim,'%',num_f,'.4e']; + else + tableformat = [tableformat, delim,'%',num_s_f,'d\n']; + tableformat_sc = [tableformat_sc, delim,'%',num_s_f,'d\n']; + end +end + +out_collection_ID = 6; + +%N_out_fields = 40; % for raw LDASsa output (EXCL. sm in pctl units) +N_out_fields = 42; % for post-processed LDASsa output (INCL. sm in pctl units) + +%==================================================================== +% +% Grid information + +N_gridcells_M09 = length(tile_coord.com_lat); + +weights = tile_coord.frac_cell; + +%==================================================================== + +% expect LDASsa *ldas_tile_xhourly_out*.bin file (not "h5" output) + +% read LDASsa binary gph file and check fieldnames + +[fn, units] = get_data_tag(out_collection_ID, N_out_fields); + +tile_data = read_tile_data(gph_fname, tile_coord.N_tile, N_out_fields); + +if tile_coord.N_tile ~= size(tile_data,2) + error('Number of land tiles does not match the length of simulated vectors'); +end + +out_fname = [gph_fname(1:end-4), '.qa' ]; + +% assemble placeholder h5 file name + +%%ind = findstr(gph_fname,'/'); + +%%h5_fname = [gph_fname(ind(end)+1:end-4), '.h5']; + +h5_fname = ''; + +%============================================================ +% OUTPUT +%============================================================ + +ofp = fopen( out_fname, 'w' ); + +disp(['writing ',out_fname]); + +% header information: 4 lines + +fprintf(ofp, ['%s\n'],... + ['Quality Assessment for SMAP L4_SM Granule ', h5_fname]); +fprintf(ofp, ['%s%8d\n\n'],... + ['Number of L4_SM EASEv2 9 km land grid cells = '], N_gridcells_M09); + +% comma-delimited table: +% - 4 header lines (observation space) +% - X variable lines +% - footnotes + +%================================================================= +% Model space +%================================================================= + +for f = 1:str_l+1+unt_l+Nstat+(Nstat-1)*num_l+num_s_l + fprintf(ofp, '%s','=' ); +end +fprintf(ofp, '\n%s\n',... + 'Geophysical variables'); +for f = 1:str_l+1+unt_l+Nstat+(Nstat-1)*num_l+num_s_l + fprintf(ofp, '%s','=' ); +end +fprintf(ofp, '\n'); + +for f = 1:Nstat+2 + if f==1 fprintf(ofp, ['%-',str_f,'s',delim,''], [tablefields{f},'' ]); end + if f==2 fprintf(ofp, ['%-',unt_f,'s',delim,''], [tablefields{f},' (*1)']); end + if f==3 || f==4 fprintf(ofp, ['%', num_f,'s',delim,''], [tablefields{f},' (*2)']); end + if f>4 && f4 && f4 && f AmF_threshold(f); + + if f==17 + subset_exclzero = tmp_subset; + else + subset_exclzero = (subset_exclzero | tmp_subset); + end + +end + +%-----Raw aup-fields---------------------------------------------- + +for f = 17:length(fn) + + var = getfield(aup, fn{f}); + var(abs(var-nodata_val)4 && f 0 || return_mean) + + if return_mean + array_out = NaN*zeros(length(ia),1); + end + + N_bad = 0; + for i=1:size(unique_rc,1) + + if check_on > 0 + if (std(array( ... + M36_row_col(:,1) == unique_rc(i,1) & ... + M36_row_col(:,2) == unique_rc(i,2) ))... + > stdv_Tbobs_tol) + N_bad = N_bad+1; + end + end + + if return_mean + array_out(i) = mean(array( ... + M36_row_col(:,1) == unique_rc(i,1) & ... + M36_row_col(:,2) == unique_rc(i,2) )); + end + + end + + if N_bad > 0 + if check_on == 2 + error([num2str(N_bad),' M36 grid cells out of ',... + num2str(length(unique_rc)),' contain M09 obs with std-dev<>0']) + else + disp(['WARNING: ',num2str(N_bad),' M36 grid cells out of ',... + num2str(length(unique_rc)),' contain M09 obs with std-dev<>0']) + end + end + +end + +if ~return_mean + array = array(ia); +else + array = array_out; +end + +% ********************************************************************************************* + +function [ stats_out ] = stats_array( array_in, weights, Nstat ) + +% calculate elementary summary statistics for array_in +% +% input: array_in = numerical array (1-dimensional) +% weights = weight for each element in array_in (1-dimensional) +% +% output: array_out = [mean stdv min max N_data frac] +% +% GDL, 16 Jan 2014 +% GDL, 30 Jan 2014: remove NaN prior to the calculation of stats +% GDL, 1 Feb 2014: - weighted statistics for mean and stdv +% - add 6th statistic (frac): +% the actual used fraction of N_data (based on weights) +% GDL, 7 Feb 2014: added Nstat as input, to limit the returned output. +% should be revised in the future to select specific output, +% rather than to limit the output fields +% --------------------------------------------------------------------- + +nodata_val = -9999; +nodata_tol = 1E-5; + +stats_out = nodata_val+zeros(6,1); +stats_out(5) = 0 ; +stats_out(6) = 0 ; + +if length(weights) ~= length(array_in) + error('input vectors need to have the same dimensions') +end + +nodata_ind = (isnan(array_in) | ... + abs(array_in - nodata_val) +180.0; lon(msk2) = lon(msk2) - 360.0; +switch projection + case 'N' + idx = lat < 0.0; + lat(idx) = NaN; + lon(idx) = NaN; + case 'S' + idx = lat > 0.0; + lat(idx) = NaN; + lon(idx) = NaN; +end + +% ========= EOF ========================================================= diff --git a/GEOSldas_App/util/shared/matlab/EASEv2_latlon2ind.m b/GEOSldas_App/util/shared/matlab/EASEv2_latlon2ind.m new file mode 100644 index 0000000..e8afdd7 --- /dev/null +++ b/GEOSldas_App/util/shared/matlab/EASEv2_latlon2ind.m @@ -0,0 +1,218 @@ +% +% SMAPEASE2FORWARD The principal function is to perform forward transformation +% from (lat,lon)'s to (row,col)'s for a set of nested EASE +% grids defined at 1, 3, 9, and 36km grid resolutions. These +% grids are all based on the EASE-Grid 2.0 specification (WGS84 +% ellipsoid). +% +% SYNTAX [row,col] = smapease2forward(lat,lon,gridid) +% +% where gridid is a 3-character string enclosed in single +% quotes, in the form of {M|N|S}{01,03,09,36}. This subroutine +% accepts vector inputs and produce vector outputs. +% +% HISTORY This subroutine was adapted from the offical EASE-Grid-2.0 +% conversion utilities (written in IDL) developed by the +% NSIDC. +% +% Note that in NSIDC's original implementation, (row,col) are +% zero-based. In other words, the first cell is (0,0) and the +% last cell is (N-1,M-1), where N and M are the row and column +% dimensions of the array. In this MATLAB implementation, the +% same convention is used. In other words, the end point of +% the first cell is located at (r,c) = (-0.5,-0.5) whereas the +% end point of the last cell is located at (r,c) = (14615.5, +% 34703.5). Thus, +% +% [lat,lon] = smapease2inverse(-0.5,-0.5,'M01') returns: +% lat = 85.044566407398861 +% lon = 1.799999999999994e+02 +% +% [lat,lon] = smapease2inverse(14615.5,34703.5,'M01') returns: +% lat = -85.044566407398861 +% lon = -1.799999999999994e+02 +% +% The polar grids, on the other hand, are more complete in +% terms of latitude coverage: +% +% [lat,lon] = smapease2inverse(8999,8999,'N01') +% lat = 89.993669248945238 +% lon = -135 +% [lat,lon] = smapease2inverse(9000,9000,'N01') +% lat = 89.993669248945238 +% lon = 45 +% +% [lat,lon] = smapease2inverse(8999,8999,'S01') +% lat = -89.993669248945238 +% lon = -45 +% [lat,lon] = smapease2inverse(9000,9000,'S01') +% lat = -89.993669248945238 +% lon = 135 +% +% UPDATE North/south polar projections were added. (03/2012) +% +% REFERENCE Brodzik, M. J., B. Billingsley, T. Haran, B. Raup, and M. H. +% Savoie (2012): EASE-Grid 2.0: Incremental but Significant +% Improvements for Earth-Gridded Data Sets. ISPRS International +% Journal of Geo-Information, vol. 1, no. 1, pp. 32-45, +% http://www.mdpi.com/2220-9964/1/1/32/ +% +% Steven Chan, 11/2011 +% Email: steven.k.chan@jpl.nasa.gov + +function [row,col] = EASEv2_latlon2ind(lat,lon,gridid,return_rounded) + +% By design, [row, col] are real numbers, with the fractional portion indicating +% the position of the specified [lat, lon] coordinates between adjacent grid +% cell centers. E.g., col=0.5 indicates that the input longitude is on the +% boundary between grid cells associated with col=0 and col=1. +% If the [optional] input argument 'return_rounded' is present and ~=0, then +% [row, col] are rounded to the nearest integer. + +% Constants returned by EASE2_GRID_INFO.PRO +projection = gridid(1); +switch gridid + case 'M36' + map_scale_m = 36032.220840584; + cols = 964; + rows = 406; + r0 = (cols-1)/2; + s0 = (rows-1)/2; + case 'M09' + map_scale_m = 9008.055210146; + cols = 3856; + rows = 1624; + r0 = (cols-1)/2; + s0 = (rows-1)/2; + case 'M03' + map_scale_m = 3002.6850700487; + cols = 11568; + rows = 4872; + r0 = (cols-1)/2; + s0 = (rows-1)/2; + case 'M01' + map_scale_m = 1000.89502334956; + cols = 34704; + rows = 14616; + r0 = (cols-1)/2; + s0 = (rows-1)/2; + case 'N36' + map_scale_m = 36000.0; + cols = 500; + rows = 500; + r0 = (cols-1)/2; + s0 = (rows-1)/2; + case 'N09' + map_scale_m = 9000.0; + cols = 2000; + rows = 2000; + r0 = (cols-1)/2; + s0 = (rows-1)/2; + case 'N03' + map_scale_m = 3000.0; + cols = 6000; + rows = 6000; + r0 = (cols-1)/2; + s0 = (rows-1)/2; + case 'N01' + map_scale_m = 1000.0; + cols = 18000; + rows = 18000; + r0 = (cols-1)/2; + s0 = (rows-1)/2; + case 'S36' + map_scale_m = 36000.0; + cols = 500; + rows = 500; + r0 = (cols-1)/2; + s0 = (rows-1)/2; + case 'S09' + map_scale_m = 9000.0; + cols = 2000; + rows = 2000; + r0 = (cols-1)/2; + s0 = (rows-1)/2; + case 'S03' + map_scale_m = 3000.0; + cols = 6000; + rows = 6000; + r0 = (cols-1)/2; + s0 = (rows-1)/2; + case 'S01' + map_scale_m = 1000.0; + cols = 18000; + rows = 18000; + r0 = (cols-1)/2; + s0 = (rows-1)/2; + otherwise + disp(['ERROR: Incompatible grid specification.']); +end + +% Constants returned by EASE2_MAP_INFO.PRO +epsilon = 1.0e-6; +map_equatorial_radius_m = 6378137.0; +map_eccentricity = 0.081819190843; +e2 = map_eccentricity^2; +switch projection + case 'M' + map_reference_latitude = 0.0; + map_reference_longitude = 0.0; + map_second_reference_latitude = 30.0; + sin_phi1 = sin(map_second_reference_latitude*pi/180); + cos_phi1 = cos(map_second_reference_latitude*pi/180); + kz = cos_phi1/sqrt(1.0-e2*sin_phi1*sin_phi1); + case 'N' + map_reference_latitude = 90.0; + map_reference_longitude = 0.0; + case 'S' + map_reference_latitude = -90.0; + map_reference_longitude = 0.0; +end + +% Selected calculations inside WGS84_CONVERT.PRO and WGS84_CONVERT_XY.PRO +dlon = lon - map_reference_longitude; +msk1 = dlon < -180.0; dlon(msk1) = dlon(msk1) + 360.0; +msk2 = dlon > +180.0; dlon(msk2) = dlon(msk2) - 360.0; +phi = lat*pi/180.0; +lam = dlon*pi/180.0; +sin_phi = sin(phi); +q = (1.0-e2)*((sin_phi./(1.0-e2*sin_phi.*sin_phi))-(1.0/(2.0*map_eccentricity))*log((1.0-map_eccentricity*sin_phi)./(1.0+map_eccentricity*sin_phi))); +qp = 1.0-((1.0-e2)/(2.0*map_eccentricity)*log((1.0-map_eccentricity)/(1.0+map_eccentricity))); +switch projection + case 'M' + x = map_equatorial_radius_m*kz*lam; + y = (map_equatorial_radius_m*q)/(2.0*kz); + case 'N' + tmp = qp - q; + tmp(abs(tmp) < epsilon) = 0.0; + rho = map_equatorial_radius_m*sqrt(tmp); + x = rho.*sin(lam); + y = -rho.*cos(lam); + case 'S' + tmp = qp + q; + tmp(abs(tmp) < epsilon) = 0.0; + rho = map_equatorial_radius_m*sqrt(tmp); + x = rho.*sin(lam); + y = rho.*cos(lam); +end +row = s0-(y/map_scale_m); +col = r0+(x/map_scale_m); +switch projection + case 'N' + idx = lat < 0.0; + row(idx) = NaN; + col(idx) = NaN; + case 'S' + idx = lat > 0.0; + row(idx) = NaN; + col(idx) = NaN; +end + +if exist('return_rounded','var') + if return_rounded + col=round(col); + row=round(row); + end +end + +% ========= EOF ========================================================= diff --git a/GEOSldas_App/util/shared/matlab/J2000_to_DateTime.m b/GEOSldas_App/util/shared/matlab/J2000_to_DateTime.m new file mode 100644 index 0000000..d2e52d2 --- /dev/null +++ b/GEOSldas_App/util/shared/matlab/J2000_to_DateTime.m @@ -0,0 +1,111 @@ +function [yr, mm, dd, hr, mn, ss, doy, pen] = J2000_to_DateTime( J2000_seconds, epoch_id) +% +% Convert J2000 time [seconds] into calendar date time. +% +% J2000 time is used in SMAP products (epoch_id = "TT12"). See subfunction J2000_epoch() below. +% +% See also GEOSldas module LDAS_DateTimeMod.F90 +% +% reichle, 28 Jul 2028 +% +% --------------------------------------------------------------------------- + +if ~exist( 'epoch_id', 'var' ) epoch_id = 'TT12'; end % default is what SMAP uses + +date_time_epoch = J2000_epoch( epoch_id ); + +N = length(J2000_seconds); + +yr = zeros(N,1); +mm = zeros(N,1); +dd = zeros(N,1); +hr = zeros(N,1); +mn = zeros(N,1); +ss = zeros(N,1); +doy = zeros(N,1); +pen = zeros(N,1); + +% Loop through elements of J2000_seconds for now. In future, should vectorize +% augment_date_time.m, is_leap_year.m, days_in_month.m, get_dofyr_pentad.m + +for ii = 1:N + + % add (rounded) J2000_seconds to date_time_epoch + + date_time = augment_date_time( round(J2000_seconds), date_time_epoch ); + + yr( ii) = date_time.year ; + mm( ii) = date_time.month ; + dd( ii) = date_time.day ; + hr( ii) = date_time.hour ; + mn( ii) = date_time.min ; + ss( ii) = date_time.sec ; + pen(ii) = date_time.pentad; + doy(ii) = date_time.dofyr ; + +end + +% ---------------------------------------------------------------------------------- + +function [J2000_epoch_datetime] = J2000_epoch( epoch_id ) + +% definition of J2000 epochs +% +% "J2000 seconds" are elapsed seconds since J2000 Epoch, which is either +% +% - "UT12": 11:58:55.816 on 1 Jan 2000 in Coordinated Universal Time (UTC), or +% - "TT12": 12:00:00.000 on 1 Jan 2000 in Terrestrial Time (TT), or +% - "UT00": 00:00:00.000 on 1 Jan 2000 in Coordinated Universal Time (UTC) +% +% NOTE: Per SMAP L1C_TB data products specs document, SMAP time stamps use "UT12" +% but sample granules appear to be using "TT12". +% NOTE: Per Clara Draper (30 Jun 2015), the nc4 ASCAT soil moisture retrieval +% product uses "UT00". + +J2000_UT12.year = 2000; +J2000_UT12.month = 1; +J2000_UT12.day = 1; +J2000_UT12.hour = 11; +J2000_UT12.min = 58; +J2000_UT12.sec = 55; % rounded down +J2000_UT12.pentad = 1; +J2000_UT12.dofyr = 1; + +J2000_TT12.year = 2000; +J2000_TT12.month = 1; +J2000_TT12.day = 1; +J2000_TT12.hour = 12; +J2000_TT12.min = 0; +J2000_TT12.sec = 0; +J2000_TT12.pentad = 1; +J2000_TT12.dofyr = 1; + +J2000_UT00.year = 2000; +J2000_UT00.month = 1; +J2000_UT00.day = 1; +J2000_UT00.hour = 0; +J2000_UT00.min = 0; +J2000_UT00.sec = 0; +J2000_UT00.pentad = 1; +J2000_UT00.dofyr = 1; + +% ---------------------------------- + +switch epoch_id + +case 'UT12' + J2000_epoch_datetime = J2000_UT12; + +case 'TT12' + J2000_epoch_datetime = J2000_TT12; + +case 'UT00' + J2000_epoch_datetime = J2000_UT00; + +otherwise + + error('J2000_to_DateTime: unknown J2000 epoch_id') + +end + +% ======================= EOF ================================================= diff --git a/GEOSldas_App/util/shared/matlab/MAPL_ReadForcing_fullfile.m b/GEOSldas_App/util/shared/matlab/MAPL_ReadForcing_fullfile.m new file mode 100644 index 0000000..14f8cc6 --- /dev/null +++ b/GEOSldas_App/util/shared/matlab/MAPL_ReadForcing_fullfile.m @@ -0,0 +1,80 @@ +function [tile_data,N_tile,start_time,end_time] = ... + MAPL_ReadForcing_fullfile(fname,nodata,nodata_tolfrac) + +% Matlab version of MAPL_ReadForcing(). So far only reads complete file! +% +% Reads binary climatology or time series files (e.g., lai.dat, vegopacity.dat). + +% Q. Liu, 18 Jul 2022 +% rreichle, 29 Jul 2022 + +% ------------------------------------------------------------------------- +% +% check whether no-data variables are available on input + +if ~exist('nodata', 'var'), nodata = 1.e15; end % default: MAPL_UNDEF +if ~exist('nodata_tolfrac', 'var'), nodata_tolfrac = 1.e-4; end + +nodata_tol = abs( nodata*nodata_tolfrac ); + +% ------------------------------------------------------------------------- + +int_precision = 'int32'; % precision of fortran tag +float_precision = 'float32'; % precision of data in input file + +disp(['reading from ', fname]) + +ifp = fopen( fname, 'r', 'l' ); + +% time loop (continue until reach end-of-file) + +nn=0; + +while 1 + + nn = nn +1; + + % read header + + fortran_tag = fread( ifp, 1, int_precision ); + header = fread( ifp, 14, float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + % safe way to detect end-of-file + if isempty(header) + break + end + + start_time(nn).year = header( 1); + start_time(nn).month = header( 2); + start_time(nn).day = header( 3); + start_time(nn).hour = header( 4); + start_time(nn).min = header( 5); + start_time(nn).sec = header( 6); + + end_time( nn).year = header( 7); + end_time( nn).month = header( 8); + end_time( nn).day = header( 9); + end_time( nn).hour = header(10); + end_time( nn).min = header(11); + end_time( nn).sec = header(12); + + N_tile = header(13); + + % read science data + + fortran_tag = fread( ifp, 1, int_precision ); + tmp_data = fread( ifp, [1 N_tile], float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + tile_data(nn,:) = tmp_data; + +end + +% replace nodata values with NaN + +tile_data( abs( single(tile_data) - single(nodata) ) < nodata_tol ) = NaN; + +fclose(ifp); + +% ========================= EOF ============================================= diff --git a/GEOSldas_App/util/shared/matlab/augment_date_time.m b/GEOSldas_App/util/shared/matlab/augment_date_time.m new file mode 100644 index 0000000..f325707 --- /dev/null +++ b/GEOSldas_App/util/shared/matlab/augment_date_time.m @@ -0,0 +1,149 @@ + +function [date_time] = augment_date_time( dtstep, date_time_old ) + +% reichle, 22 Jun 2005 + +% dtstep in seconds + +date_time = date_time_old; + +dtstep_left = dtstep; + +if isnan(dtstep) + + error('ERROR: dtstep=NaN in augment_date_time.m'); + +elseif dtstep==0 % trivial case + + date_time = get_dofyr_pentad(date_time); + + return + +elseif dtstep>0 + + while (dtstep_left>0) + + % increase by one day at a time + + dtstep_tmp = min( dtstep_left, 86400 ); + + dtstep_left = round( dtstep_left - dtstep_tmp ); + + % compute secs_in_day from hh:mm:ss + + secs_in_day = date_time.hour*3600 + date_time.min*60 + date_time.sec; + + % augment + + secs_in_day = secs_in_day + dtstep_tmp; + + % compute new hh:mm:ss from secs_in_day + + date_time.hour = (floor(mod(secs_in_day,86400)/3600)); + date_time.min = (floor(mod(secs_in_day,86400)/60) - date_time.hour*60); + date_time.sec = (floor(mod(secs_in_day,86400)) ... + - date_time.hour*3600 ... + - date_time.min*60); + + % augment year/month/day and dofyr as necessary + + if ( secs_in_day >= 86400 ) + + % get number of days in month + + last_day = days_in_month( date_time.year, date_time.month); + + if (date_time.day==last_day) + + if (date_time.month==12) + + date_time.year = date_time.year + 1; + date_time.month = 1; + date_time.day = 1; + + else + + date_time.month = date_time.month + 1; + date_time.day = 1; + + end + + else + + date_time.day = date_time.day + 1; + + end + + end + + end + +else + + while (dtstep_left<0) + + % decrease by one day at a time + + dtstep_tmp = max( dtstep_left, -86400 ); + + dtstep_left = round( dtstep_left - dtstep_tmp ); + + % compute secs_in_day from hh:mm:ss + + secs_in_day = date_time.hour*3600 + date_time.min*60 + date_time.sec; + + % augment + + secs_in_day = secs_in_day + dtstep_tmp; + + % compute new hh:mm:ss from secs_in_day + + secs_in_day_tmp = secs_in_day + 86400; + + date_time.hour = (floor(mod(secs_in_day_tmp,86400)/3600)); + date_time.min = (floor(mod(secs_in_day_tmp,86400)/60)-date_time.hour*60); + date_time.sec = (floor(mod(secs_in_day_tmp,86400)) ... + - date_time.hour*3600 ... + - date_time.min*60); + + % augment year/month/day and dofyr as necessary + + if ( secs_in_day < 0 ) + + if (date_time.day==1) + + if (date_time.month==1) + + date_time.year = date_time.year - 1; + date_time.month = 12; + date_time.day = 31; + + else + + date_time.month = date_time.month - 1; + + % get number of days in previous month + + date_time.day = days_in_month( date_time.year, date_time.month); + + end + + else + + date_time.day = date_time.day - 1; + + end + + end + + end + +end + +% get dofyr and pentad + +date_time = get_dofyr_pentad(date_time); + + +% ****** EOF ******************************************************* + diff --git a/GEOSldas_App/util/shared/matlab/days_in_month.m b/GEOSldas_App/util/shared/matlab/days_in_month.m new file mode 100644 index 0000000..5e89e64 --- /dev/null +++ b/GEOSldas_App/util/shared/matlab/days_in_month.m @@ -0,0 +1,16 @@ + +function [n_days] = days_in_month( year, month ) + +% reichle, 22 Jun 2005 + +days_in_month_leap = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + +days_in_month_nonleap = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + +if (is_leap_year(year)) + n_days = days_in_month_leap(month); +else + n_days = days_in_month_nonleap(month); +end + +% **************************** EOF ******************************** \ No newline at end of file diff --git a/GEOSldas_App/util/shared/matlab/get_dofyr_pentad.m b/GEOSldas_App/util/shared/matlab/get_dofyr_pentad.m new file mode 100644 index 0000000..63432a9 --- /dev/null +++ b/GEOSldas_App/util/shared/matlab/get_dofyr_pentad.m @@ -0,0 +1,19 @@ + +function [ date_time ] = get_dofyr_pentad( date_time ) + +% compute dofyr and pentad for date_time + +date_time.dofyr = date_time.day; + +% add up days in months prior to current month + +for i=1:(date_time.month-1) + + date_time.dofyr = date_time.dofyr + days_in_month(date_time.year,i); + +end + +date_time.pentad = pentad_of_year(date_time.dofyr, date_time.year); + + +% ======================= EOF ================================== \ No newline at end of file diff --git a/GEOSldas_App/util/shared/matlab/is_leap_year.m b/GEOSldas_App/util/shared/matlab/is_leap_year.m new file mode 100644 index 0000000..1da1af7 --- /dev/null +++ b/GEOSldas_App/util/shared/matlab/is_leap_year.m @@ -0,0 +1,30 @@ +function [ leap ] = is_leap_year(year) + +% determine whether a given year is a leap yearb +% +% input: year, must be SCALAR ! +% +% output: leap = 0 if year is not leap year +% leap = 1 if year is leap year +% +% reichle, 1 Mar 2001 +% +% --------------------------------------------------------------------- + +if (length(year) ~= 1) + disp('error, input to is_leap_year() must be scalar, exiting...') + return +end + +if (mod(year,4) ~= 0) + leap = 0; +elseif (mod(year,400) == 0) + leap = 1; +elseif (mod(year,100) == 0) + leap = 0; +else + leap = 1; +end + +% ========= EOF ========================================================= + \ No newline at end of file diff --git a/GEOSldas_App/util/shared/matlab/pentad_of_year.m b/GEOSldas_App/util/shared/matlab/pentad_of_year.m new file mode 100644 index 0000000..cc82784 --- /dev/null +++ b/GEOSldas_App/util/shared/matlab/pentad_of_year.m @@ -0,0 +1,14 @@ + +function pentad = pentad_of_year(day_of_year, year) + +if (is_leap_year(year) & day_of_year>=59) + + pentad = floor((day_of_year-2)/5)+1; + +else + + pentad = floor((day_of_year-1)/5)+1; + +end + +% ======================= EOF ================================== diff --git a/GEOSldas_App/util/shared/matlab/read_ObsFcstAna.m b/GEOSldas_App/util/shared/matlab/read_ObsFcstAna.m new file mode 100644 index 0000000..9b3ec87 --- /dev/null +++ b/GEOSldas_App/util/shared/matlab/read_ObsFcstAna.m @@ -0,0 +1,192 @@ +function [date_time, ... + obs_assim, ... + obs_species, ... + obs_tilenum, ... + obs_lon, ... + obs_lat, ... + obs_obs, ... + obs_obsvar, ... + obs_fcst, ... + obs_fcstvar, ... + obs_ana, ... + obs_anavar ... + ] = ... + read_ObsFcstAna( fname, isLDASsa ) + +% +% read_ObsFcstAna.m can be used to read "ObsFcstAna" files that +% contain Observations and observation-space model forecasts and +% analysis data +% +% data format: +% see f90 subroutine output_ObsFcstAna() in module clsm_ensupd_enkf_update +% +% reichle, 4 Oct 2011 +% +% ------------------------------------------------------------------ + +int_precision = 'int32'; % precision of fortran tag +float_precision = 'float32'; % precision of data in input file +logical_precision = 'int32'; % precision of data in input file + +% initialize outputs in case file does not exist or is empty + +nodata = -9999; + +date_time = struct('year', nodata, ... + 'month', nodata, ... + 'day', nodata, ... + 'hour', nodata, ... + 'min', nodata, ... + 'sec', nodata, ... + 'dofyr', nodata, ... + 'pentad', nodata ); + +obs_assim = []; +obs_species = []; +obs_tilenum = []; +obs_lon = []; +obs_lat = []; +obs_obs = []; +obs_obsvar = []; +obs_fcst = []; +obs_fcstvar = []; +obs_ana = []; +obs_anavar = []; + +if exist('isLDASsa','var') && isLDASsa == 1 + machfmt = 'b'; % big-endian, LDASsa +else + machfmt = 'l'; % little-endian, GEOSldas +end + +% read file if it exists + +if exist(fname)==2 + + disp(['reading from ', fname ]) + + ifp = fopen( fname, 'r', machfmt ); + + % read N_obs and time stamp entry + + fortran_tag = fread( ifp, 1, int_precision ); + N_obs = fread( ifp, 1, int_precision ); + year = fread( ifp, 1, int_precision ); + month = fread( ifp, 1, int_precision ); + day = fread( ifp, 1, int_precision ); + hour = fread( ifp, 1, int_precision ); + minute = fread( ifp, 1, int_precision ); + second = fread( ifp, 1, int_precision ); + dofyr = fread( ifp, 1, int_precision ); + pentad = fread( ifp, 1, int_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + date_time.year = year; + date_time.month = month; + date_time.day = day; + date_time.hour = hour; + date_time.min = minute; + date_time.sec = second; + date_time.dofyr = dofyr; + date_time.pentad = pentad; + + % read observation assim flag + + fortran_tag = fread( ifp, 1, int_precision ); + tmp_data = fread( ifp, [N_obs 1], logical_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + obs_assim = zeros( N_obs, 1); + obs_assim( tmp_data~= 0 ) = 1; + + % read species information + + fortran_tag = fread( ifp, 1, int_precision ); + obs_species = fread( ifp, [N_obs 1], int_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + % read tile number information + + fortran_tag = fread( ifp, 1, int_precision ); + obs_tilenum = fread( ifp, [N_obs 1], int_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + % read longitude + + fortran_tag = fread( ifp, 1, int_precision ); + obs_lon = fread( ifp, [N_obs 1], float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + % read latitude + + fortran_tag = fread( ifp, 1, int_precision ); + obs_lat = fread( ifp, [N_obs 1], float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + + % read observation value + + fortran_tag = fread( ifp, 1, int_precision ); + obs_obs = fread( ifp, [N_obs 1], float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + % read observation variance + + fortran_tag = fread( ifp, 1, int_precision ); + obs_obsvar = fread( ifp, [N_obs 1], float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + + % read observation-space model forecast value + + fortran_tag = fread( ifp, 1, int_precision ); + obs_fcst = fread( ifp, [N_obs 1], float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + % read observation-space model forecast variance + + fortran_tag = fread( ifp, 1, int_precision ); + obs_fcstvar = fread( ifp, [N_obs 1], float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + + % read observation-space analysis value + + fortran_tag = fread( ifp, 1, int_precision ); + obs_ana = fread( ifp, [N_obs 1], float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + % read observation-space analysis variance + + fortran_tag = fread( ifp, 1, int_precision ); + obs_anavar = fread( ifp, [N_obs 1], float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + + % no-data check + % - single ensemble member integrations yield obs_obsvar==nodata) + % - in some cases obs_fcst (a.k.a. Obs_pred) is no-data-value, + % eg. SMOS Tb when snow is present) + + obs_obsvar( obs_obsvar == nodata ) = NaN; + + obs_fcst( obs_fcst == nodata ) = NaN; + obs_fcstvar( obs_fcstvar == nodata ) = NaN; + + obs_ana( obs_ana == nodata ) = NaN; + obs_anavar( obs_anavar == nodata ) = NaN; + + + % close file + + fclose(ifp); + +else % if exist(fname)==2 + + disp(['file does not exist: ', fname]) + +end + + +% ======= EOF ========================================================== diff --git a/GEOSldas_App/util/shared/matlab/read_catparam.m b/GEOSldas_App/util/shared/matlab/read_catparam.m new file mode 100644 index 0000000..394e405 --- /dev/null +++ b/GEOSldas_App/util/shared/matlab/read_catparam.m @@ -0,0 +1,298 @@ + +function [ cat_param, cat_param_units ] = read_catparam( fname, N_tile, isLDASsa ); + +% reichle, 2 Jun 2006 +% reichle, 16 Jul 2010 - added vegcls lookup table +% reichle, 28 Oct 2010 - added soilcls* +% - changed cat_param structure from "vector of +% structures" to "structure of vectors" +% reichle, 1 Apr 2015 - added new soil parameter fields (file_format==3) +% - added cat_param_units +% reichle, 28 Jul 2022 - cleaned up LDASsa/GEOSldas switch for commit into GEOSldas repo + +% NOTE: For large files this reader is inefficient (slow execution, +% excessive memory demand) due to the use of a matlab structure +% array. If better performance is required, convert to reading +% data into a regular matrix (as opposed to a structure array). +% +% +% parameter "vegcls" is land cover type: +% +% vegcls = 1: broadleaf evergreen trees +% vegcls = 2: broadleaf deciduous trees +% vegcls = 3: needleleaf trees +% vegcls = 4: grassland +% vegcls = 5: broadleaf shrubs +% vegcls = 6: dwarf trees +% vegcls = 7: bare soil +% vegcls = 8: desert soil +% +% +% parameters "soilcls30" and "soilcls100" are 0-30 cm and 0-100 cm soil class: +% +% Two similar but different look-up tables were used for the MERRA and +% Fortuna versions of the Catchment model. +% +% The first look-up table is from http://www.iges.org/gswp2/. [Note that +% the GSWP-2 documentation refers to these values as "Cosby" (even though +% they are different from the Cosby et al 1984 values, see below.] +% The GSWP-2 values were used by Sarith in the Richards' equation +% pre-processing steps and are used in subroutine catchment() (via Sarith's +% Catchment model parameter files): +% +% Soil Soil B Porosity Wilting Psis *Surface* +% Class Type (v/v) Point(v/v) (m) Ks (m/s) +% +% 1 : Sand 3.30 0.373 0.089 -0.05 0.0285 +% 2 : Loamy Sand 3.80 0.386 0.132 -0.07 0.0204 +% 3 : Sandy Loam 4.34 0.419 0.205 -0.16 0.0097 +% 4 : Silt Loam 5.25 0.476 0.355 -0.65 0.0027 +% 5 : Silt +% 6 : Loam 5.96 0.437 0.339 -0.24 0.0054 +% 7 : Sandy Clay Loam 7.32 0.412 0.379 -0.12 0.0073 +% 8 : Silty Clay Loam 8.41 0.478 0.521 -0.63 0.0017 +% 9 : Clay Loam 8.34 0.447 0.472 -0.28 0.0032 +% 10 : Sandy Clay 9.70 0.415 0.480 -0.12 0.0050 +% 11 : Silty Clay 10.78 0.478 0.598 -0.58 0.0012 +% 12 : Clay 12.93 0.450 0.613 -0.27 0.0015 +% +% The second look-up table is from Cosby et al. WRR 1984. These values +% were used by Randy in some of the Catchment model paramter pre-processing +% steps (other than the Richards' equation solver): +% +% Soil Soil B Porosity Wilting Psis *Surface* +% Class Type (v/v) Point(v/v) (m) Ks (m/s) +% +% 1 : Sand 2.79 0.339 0.0218 -0.0692 0.0542931 +% 2 : Loamy Sand 4.26 0.421 0.0599 -0.0363 0.0163963 +% 3 : Sandy Loam 4.74 0.434 0.1002 -0.1413 0.00609179 +% 4 : Silt Loam 5.33 0.476 0.1772 -0.7586 0.00327148 +% 5 : Silt 5.33 0.476 0.1772 -0.7586 0.00327148 +% 6 : Loam 5.25 0.439 0.1393 -0.3548 0.00393319 +% 7 : Sandy Clay Loam 6.77 0.404 0.1438 -0.1349 0.00518495 +% 8 : Silty Clay Loam 8.72 0.464 0.2477 -0.6166 0.00236998 +% 9 : Clay Loam 8.17 0.465 0.2144 -0.2630 0.00284934 +% 10 : Sandy Clay 10.73 0.406 0.2053 -0.0977 0.00840901 +% 11 : Silty Clay 10.39 0.468 0.2597 -0.3236 0.00156583 +% 12 : Clay 11.55 0.468 0.2240 -0.0389 0.00113434 +% +% +% In both cases, the *surface* Ks that is needed as input to subroutine +% catchment() was extrapolated from the Ks provided by GSWP-2 or Cosby et al +% using the vertical conductivity decay factor "gnu" (which might be +% inconsistent with "gnu" used elsewhere). +% +% +% Starting in late 2014, revised soil parameters can be used in LDASsa. +% For details see De Lannoy et al., 2014, doi:10.1002/2014MS000330. +% +% ------------------------------------------------------------------ + +if ~exist('isLDASsa','var') isLDASsa = 0; end % default is GEOSldas output + +% for backward compatibility, back out number of parameters in file +% from file size: + +% file size = N_param * (N_tile + 2) * bytes_per_datapoint + +tmps = dir(fname); + +if isLDASsa ~= 0 + machfmt = 'b'; % big-endian, LDASsa +else + machfmt = 'l'; % little-endian, GEOSldas +end + +N_param = tmps.bytes/((N_tile+2)*4); + +if N_param==40 + + file_format = 1; + + if isLDASsa ~= 0 + int_columns = 18; % vegcls + else + int_columns = []; % GEOSldas files contain only real*4 numbers + end + +elseif N_param==42 | N_param==51 | N_param==52 + + file_format = 2; + + if isLDASsa ~= 0 + int_columns = [ 18 19 20 ]; % vegcls, soilcls30, soilcls100 + else + int_columns = []; % GEOSldas files contain only real*4 numbers + end + +else + + error('read_catparam.m: something wrong with file size or format') + +end + +disp(['read_catparam.m: expecting ', num2str(N_param), ... + ' parameters in file with file_format ', num2str(file_format)]) + +% ---------------------------------------------------------------- + +int_precision = 'int32'; % precision of fortran tag +float_precision = 'float32'; % precision of data in input file + +disp(['read_catparam.m: reading from ', fname]) + +ifp = fopen( fname, 'r', machfmt ); + +for i=1:N_param + + fortran_tag = fread( ifp, 1, int_precision ); + if any(i==int_columns) + tmp = fread( ifp, [1 N_tile], int_precision ); + else + tmp = fread( ifp, [1 N_tile], float_precision ); + end + fortran_tag = fread( ifp, 1, int_precision ); + + tmp_data(i,:) = tmp; + +end + +disp(['read_catparam.m: assembling structure array']) + +switch file_format + + case {1} + + cat_param.dpth = tmp_data( 1,:)'; cat_param_units.dpth = '[mm]'; + + cat_param.dzsf = tmp_data( 2,:)'; cat_param_units.dzsf = '[mm]'; + cat_param.dzrz = tmp_data( 3,:)'; cat_param_units.dzrz = '[mm]'; + cat_param.dzpr = tmp_data( 4,:)'; cat_param_units.dzpr = '[mm]'; + + cat_param.dzgt(:,1) = tmp_data( 5,:)'; cat_param_units.dzgt = '[m]'; + cat_param.dzgt(:,2) = tmp_data( 6,:)'; cat_param_units.dzgt = '[m]'; + cat_param.dzgt(:,3) = tmp_data( 7,:)'; cat_param_units.dzgt = '[m]'; + cat_param.dzgt(:,4) = tmp_data( 8,:)'; cat_param_units.dzgt = '[m]'; + cat_param.dzgt(:,5) = tmp_data( 9,:)'; cat_param_units.dzgt = '[m]'; + cat_param.dzgt(:,6) = tmp_data(10,:)'; cat_param_units.dzgt = '[m]'; + + cat_param.poros = tmp_data(11,:)'; cat_param_units.poros = '[m3 m-3]'; + cat_param.cond = tmp_data(12,:)'; cat_param_units.cond = '[m s-1]'; + cat_param.psis = tmp_data(13,:)'; cat_param_units.psis = '[m H2O]'; + cat_param.bee = tmp_data(14,:)'; cat_param_units.bee = '[-]'; + + cat_param.wpwet = tmp_data(15,:)'; cat_param_units.wpwet = '[-]'; + + cat_param.gnu = tmp_data(16,:)'; cat_param_units.gnu = '[m-1]'; + + cat_param.vgwmax = tmp_data(17,:)'; cat_param_units.vgwmax = '[kg m-2]'; + + cat_param.vegcls = tmp_data(18,:)'; cat_param_units.vegcls = '[-]'; + + cat_param.bf1 = tmp_data(19,:)'; cat_param_units.bf1 = '[kg m-4]'; + cat_param.bf2 = tmp_data(20,:)'; cat_param_units.bf2 = '[m]'; + cat_param.bf3 = tmp_data(21,:)'; cat_param_units.bf3 = '[log(m)]'; + cat_param.cdcr1 = tmp_data(22,:)'; cat_param_units.cdcr1 = '[kg m-2]'; + cat_param.cdcr2 = tmp_data(23,:)'; cat_param_units.cdcr2 = '[kg m-2]'; + cat_param.ars1 = tmp_data(24,:)'; cat_param_units.ars1 = '[m2 kg-1]'; + cat_param.ars2 = tmp_data(25,:)'; cat_param_units.ars2 = '[m2 kg-1]'; + cat_param.ars3 = tmp_data(26,:)'; cat_param_units.ars3 = '[m4 kg-2]'; + cat_param.ara1 = tmp_data(27,:)'; cat_param_units.ara1 = '[m2 kg-1]'; + cat_param.ara2 = tmp_data(28,:)'; cat_param_units.ara2 = '[-]'; + cat_param.ara3 = tmp_data(29,:)'; cat_param_units.ara3 = '[m2 kg-1]'; + cat_param.ara4 = tmp_data(30,:)'; cat_param_units.ara4 = '[-]'; + cat_param.arw1 = tmp_data(31,:)'; cat_param_units.arw1 = '[m2 kg-1]'; + cat_param.arw2 = tmp_data(32,:)'; cat_param_units.arw2 = '[m2 kg-1]'; + cat_param.arw3 = tmp_data(33,:)'; cat_param_units.arw3 = '[m4 kg-2]'; + cat_param.arw4 = tmp_data(34,:)'; cat_param_units.arw4 = '[-]'; + cat_param.tsa1 = tmp_data(35,:)'; cat_param_units.tsa1 = '[-]'; + cat_param.tsa2 = tmp_data(36,:)'; cat_param_units.tsa2 = '[-]'; + cat_param.tsb1 = tmp_data(37,:)'; cat_param_units.tsb1 = '[-]'; + cat_param.tsb2 = tmp_data(38,:)'; cat_param_units.tsb2 = '[-]'; + cat_param.atau = tmp_data(39,:)'; cat_param_units.atau = '[-]'; + cat_param.btau = tmp_data(40,:)'; cat_param_units.btau = '[-]'; + + case {2} + + cat_param.dpth = tmp_data( 1,:)'; cat_param_units.dpth = '[mm]'; + + cat_param.dzsf = tmp_data( 2,:)'; cat_param_units.dzsf = '[mm]'; + cat_param.dzrz = tmp_data( 3,:)'; cat_param_units.dzrz = '[mm]'; + cat_param.dzpr = tmp_data( 4,:)'; cat_param_units.dzpr = '[mm]'; + + cat_param.dzgt(:,1) = tmp_data( 5,:)'; cat_param_units.dzgt = '[m]'; + cat_param.dzgt(:,2) = tmp_data( 6,:)'; cat_param_units.dzgt = '[m]'; + cat_param.dzgt(:,3) = tmp_data( 7,:)'; cat_param_units.dzgt = '[m]'; + cat_param.dzgt(:,4) = tmp_data( 8,:)'; cat_param_units.dzgt = '[m]'; + cat_param.dzgt(:,5) = tmp_data( 9,:)'; cat_param_units.dzgt = '[m]'; + cat_param.dzgt(:,6) = tmp_data(10,:)'; cat_param_units.dzgt = '[m]'; + + cat_param.poros = tmp_data(11,:)'; cat_param_units.poros = '[m3 m-3]'; + cat_param.cond = tmp_data(12,:)'; cat_param_units.cond = '[m s-1]'; + cat_param.psis = tmp_data(13,:)'; cat_param_units.psis = '[m H2O]'; + cat_param.bee = tmp_data(14,:)'; cat_param_units.bee = '[-]'; + + cat_param.wpwet = tmp_data(15,:)'; cat_param_units.wpwet = '[-]'; + + cat_param.gnu = tmp_data(16,:)'; cat_param_units.gnu = '[m-1]'; + + cat_param.vgwmax = tmp_data(17,:)'; cat_param_units.vgwmax = '[kg m-2]'; + + cat_param.vegcls = tmp_data(18,:)'; cat_param_units.vegcls = '[-]'; + cat_param.soilcls30 = tmp_data(19,:)'; cat_param_units.soilcls30 = '[-]'; + cat_param.soilcls100 = tmp_data(20,:)'; cat_param_units.soilcls100 = '[-]'; + + cat_param.bf1 = tmp_data(21,:)'; cat_param_units.bf1 = '[kg m-4]'; + cat_param.bf2 = tmp_data(22,:)'; cat_param_units.bf2 = '[m]'; + cat_param.bf3 = tmp_data(23,:)'; cat_param_units.bf3 = '[log(m)]'; + cat_param.cdcr1 = tmp_data(24,:)'; cat_param_units.cdcr1 = '[kg m-2]'; + cat_param.cdcr2 = tmp_data(25,:)'; cat_param_units.cdcr2 = '[kg m-2]'; + cat_param.ars1 = tmp_data(26,:)'; cat_param_units.ars1 = '[m2 kg-1]'; + cat_param.ars2 = tmp_data(27,:)'; cat_param_units.ars2 = '[m2 kg-1]'; + cat_param.ars3 = tmp_data(28,:)'; cat_param_units.ars3 = '[m4 kg-2]'; + cat_param.ara1 = tmp_data(29,:)'; cat_param_units.ara1 = '[m2 kg-1]'; + cat_param.ara2 = tmp_data(30,:)'; cat_param_units.ara2 = '[-]'; + cat_param.ara3 = tmp_data(31,:)'; cat_param_units.ara3 = '[m2 kg-1]'; + cat_param.ara4 = tmp_data(32,:)'; cat_param_units.ara4 = '[-]'; + cat_param.arw1 = tmp_data(33,:)'; cat_param_units.arw1 = '[m2 kg-1]'; + cat_param.arw2 = tmp_data(34,:)'; cat_param_units.arw2 = '[m2 kg-1]'; + cat_param.arw3 = tmp_data(35,:)'; cat_param_units.arw3 = '[m4 kg-2]'; + cat_param.arw4 = tmp_data(36,:)'; cat_param_units.arw4 = '[-]'; + cat_param.tsa1 = tmp_data(37,:)'; cat_param_units.tsa1 = '[-]'; + cat_param.tsa2 = tmp_data(38,:)'; cat_param_units.tsa2 = '[-]'; + cat_param.tsb1 = tmp_data(39,:)'; cat_param_units.tsb1 = '[-]'; + cat_param.tsb2 = tmp_data(40,:)'; cat_param_units.tsb2 = '[-]'; + cat_param.atau = tmp_data(41,:)'; cat_param_units.atau = '[-]'; + cat_param.btau = tmp_data(42,:)'; cat_param_units.btau = '[-]'; + + if N_param==51 | N_param==52 + + cat_param.gravel30 = tmp_data(43,:)'; cat_param_units.gravel30 = '[%vol]'; + cat_param.orgC30 = tmp_data(44,:)'; cat_param_units.orgC30 = '[%weight]'; + cat_param.orgC = tmp_data(45,:)'; cat_param_units.orgC = '[%weight]'; + cat_param.sand30 = tmp_data(46,:)'; cat_param_units.sand30 = '[%weight]'; + cat_param.clay30 = tmp_data(47,:)'; cat_param_units.clay30 = '[%weight]'; + cat_param.sand = tmp_data(48,:)'; cat_param_units.sand = '[%weight]'; + cat_param.clay = tmp_data(49,:)'; cat_param_units.clay = '[%weight]'; + cat_param.wpwet30 = tmp_data(50,:)'; cat_param_units.wpwet30 = '[-]'; + cat_param.poros30 = tmp_data(51,:)'; cat_param_units.poros30 = '[m3 m-3]'; + + end + + if N_param==52 + + cat_param.veghght = tmp_data(52,:)'; cat_param_units.veghght = '[m]'; + + end + + otherwise + + error('read_catparam.m: something wrong with file size or format') + +end + +fclose(ifp); + + +% =========== EOF =========================================== + diff --git a/GEOSldas_App/util/shared/matlab/read_obslog.m b/GEOSldas_App/util/shared/matlab/read_obslog.m new file mode 100644 index 0000000..9e5d1fc --- /dev/null +++ b/GEOSldas_App/util/shared/matlab/read_obslog.m @@ -0,0 +1,51 @@ + +function [ana_time, obs_descr, read_obs_subr, N_obs_read, obs_fname] = read_obslog(fname) + +% read LDASsa obs log files +% +% reichle, 2 Jan 2014 +% reichle, 11 Feb 2021 - corrected for post-launch file format +% +% ------------------------------------------------------------- + +% read file + +disp(['reading from ', fname]) + +fid = fopen( fname ); + +data = textscan(fid, '%s%s%s%d%s', ... + 'Delimiter', ',', ... + 'HeaderLines', 3 ); + +fclose(fid); + +disp('done reading file') + +% make sure that final line contained 'EOF', remove from data + +if strcmp(data{1}{end},'EOF') + + data{1}(end) = []; + data{2}(end) = []; + data{3}(end) = []; + data{4}(end) = []; + data{5}(end) = []; + +else + + error('read_obslog(): ERROR reading data') + +end + +% extract data into output variables + + +ana_time = data{1}; +obs_descr = data{2}; +read_obs_subr = data{3}; +N_obs_read = data{4}; +obs_fname = data{5}; + + +% ================== EOF ===================================== diff --git a/GEOSldas_App/util/shared/matlab/read_obsparam.m b/GEOSldas_App/util/shared/matlab/read_obsparam.m new file mode 100644 index 0000000..19e6605 --- /dev/null +++ b/GEOSldas_App/util/shared/matlab/read_obsparam.m @@ -0,0 +1,80 @@ +function [ N_obs_param, obs_param ] = read_obs_param( fname ) + +% Get observation parameters +% Format as in module enkf_types, subroutine write_obs_param +% +% Gabrielle De Lannoy - 26 Oct 2011 +% +% 1 Dec 2011 - reichle: minor modifications and check-in to CVS +% +% 8 Jun 2017 - reichle: added "flistpath" and "flistname" +% +% ------------------------------------------------------------------ + +fid = fopen(fname); + +disp(['Reading ',fname]); + +N_obs_param = fscanf(fid, '%d ', 1); + +for i=1:N_obs_param + + obs_param(i).descr = fscanf(fid, '%s ', 1); + obs_param(i).species = fscanf(fid, '%f ', 1); + obs_param(i).orbit = fscanf(fid, '%f ', 1); %1=A, 2=D + obs_param(i).pol = fscanf(fid, '%f ', 1); %1=H, 2=V + obs_param(i).N_ang = fscanf(fid, '%f ', 1); + + obs_param(i).ang = fscanf(fid, '%f ', obs_param(i).N_ang); + + obs_param(i).freq = fscanf(fid, '%f ', 1); + obs_param(i).FOV = fscanf(fid, '%f ', 1); + obs_param(i).FOV_units = fscanf(fid, '%s ', 1); + obs_param(i).assim = fscanf(fid, '%s ', 1); + obs_param(i).scale = fscanf(fid, '%s ', 1); + obs_param(i).getinnov = fscanf(fid, '%s ', 1); + obs_param(i).RTM_ID = fscanf(fid, '%f ', 1); + obs_param(i).bias_Npar = fscanf(fid, '%f ', 1); + obs_param(i).bias_trel = fscanf(fid, '%f ', 1); + obs_param(i).bias_tcut = fscanf(fid, '%f ', 1); + obs_param(i).nodata = fscanf(fid, '%f ', 1); + obs_param(i).varname = fscanf(fid, '%s ', 1); + obs_param(i).units = fscanf(fid, '%s ', 1); + obs_param(i).path = fscanf(fid, '%s ', 1); + obs_param(i).name = fscanf(fid, '%s ', 1); + obs_param(i).maskpath = fscanf(fid, '%s ', 1); + obs_param(i).maskname = fscanf(fid, '%s ', 1); + obs_param(i).scalepath = fscanf(fid, '%s ', 1); + obs_param(i).scalename = fscanf(fid, '%s ', 1); + obs_param(i).flistpath = fscanf(fid, '%s ', 1); + obs_param(i).flistname = fscanf(fid, '%s ', 1); + obs_param(i).errstd = fscanf(fid, '%f ', 1); + obs_param(i).std_normal_max = fscanf(fid, '%f ', 1); + obs_param(i).zeromean = fscanf(fid, '%s ', 1); + obs_param(i).coarsen_pert = fscanf(fid, '%s ', 1); + obs_param(i).xcorr = fscanf(fid, '%f ', 1); + obs_param(i).ycorr = fscanf(fid, '%f ', 1); + obs_param(i).adapt = fscanf(fid, '%f ', 1); + + % remove leading and trailing quotes from strings + + obs_param(i).descr = obs_param(i).descr( 2:end-1); + obs_param(i).FOV_units = obs_param(i).FOV_units(2:end-1); + obs_param(i).varname = obs_param(i).varname( 2:end-1); + obs_param(i).units = obs_param(i).units( 2:end-1); + obs_param(i).path = obs_param(i).path( 2:end-1); + obs_param(i).name = obs_param(i).name( 2:end-1); + obs_param(i).maskpath = obs_param(i).maskpath( 2:end-1); + obs_param(i).maskname = obs_param(i).maskname( 2:end-1); + obs_param(i).scalepath = obs_param(i).scalepath(2:end-1); + obs_param(i).scalename = obs_param(i).scalename(2:end-1); + obs_param(i).flistpath = obs_param(i).flistpath(2:end-1); + obs_param(i).flistname = obs_param(i).flistname(2:end-1); + +end + +fclose(fid); + +disp(['Done reading obs_param for ',num2str(N_obs_param),' species']); + +% =========================== EOF ==================================== diff --git a/GEOSldas_App/util/shared/matlab/read_smapL4SMaup.m b/GEOSldas_App/util/shared/matlab/read_smapL4SMaup.m new file mode 100644 index 0000000..9418d2b --- /dev/null +++ b/GEOSldas_App/util/shared/matlab/read_smapL4SMaup.m @@ -0,0 +1,152 @@ + +function [ aup, units ] = read_smapL4SMaup( fname, N_tile, isLDASsa ); + +% reichle, 26 Apr 2013 +% reichle, 5 Feb 2014 - added tb_[h/v]_obs_time_sec +% reichle, 21 Mar 2015 - changed units of soil moisture output from wetness +% [dimensionless] to volumetric soil moisture [m3/m3] +% reichle, 28 Jul 2022 - cleaned up LDASsa/GEOSldas switch for commit into GEOSldas repo + +% NOTE: For large files this reader is inefficient (slow execution, +% excessive memory demand) due to the use of a matlab structure +% array. If better performance is needed, convert to reading +% data into a regular matrix (as opposed to a structure array). + +if ~exist('isLDASsa','var') is_LDASsa = 0; end % default is GEOSldas output + +N_param = 31; % number of records + +dbl_records = [1 2]; % double precision records + +int_records = [3 4 5 6]; % integer records + + +% ---------------------------------------------------------------- + +int_precision = 'int32'; % precision of fortran tag and integer data in input file +float_precision = 'float32'; % precision of real data in input file +dbl_precision = 'float64'; % precision of real*8 data in input file + +disp(['read_smapL4SMaup.m: reading from ', fname]) + +if isLDASsa ~= 0 + machfmt = 'b'; % big-endian, LDASsa +else + machfmt = 'l'; % little-endian, GEOSldas +end + +ifp = fopen( fname, 'r', machfmt ); + +tmp_data = NaN*ones(N_param,N_tile); + +for i=1:N_param + + fortran_tag = fread( ifp, 1, int_precision ); + + if any(i==dbl_records) + + N_bytes = 8; + + else + + N_bytes = 4; + + end + + if (N_bytes*N_tile ~= fortran_tag) + + error('read_smapL4SMaup.m: inconsistent N_tile') + + end + + if any(i==int_records) + tmp = fread( ifp, [1 N_tile], int_precision ); + elseif any(i==dbl_records) + tmp = fread( ifp, [1 N_tile], dbl_precision ); + else + tmp = fread( ifp, [1 N_tile], float_precision ); + end + + fortran_tag = fread( ifp, 1, int_precision ); + + tmp_data(i,:) = tmp; + +end + +fclose(ifp); + +% --------------------------------------------------------- + +disp(['read_smapL4SMaup.m: assembling structure array']) + +aup.tb_h_obs_time_sec = tmp_data( 1,:)'; units{ 1} = '[s]'; +aup.tb_v_obs_time_sec = tmp_data( 2,:)'; units{ 2} = '[s]'; +aup.tb_h_resolution_flag = tmp_data( 3,:)'; units{ 3} = '[dimensionless]'; +aup.tb_v_resolution_flag = tmp_data( 4,:)'; units{ 4} = '[dimensionless]'; +aup.tb_h_orbit_flag = tmp_data( 5,:)'; units{ 5} = '[dimensionless]'; +aup.tb_v_orbit_flag = tmp_data( 6,:)'; units{ 6} = '[dimensionless]'; +aup.tb_h_obs = tmp_data( 7,:)'; units{ 7} = '[K]'; +aup.tb_v_obs = tmp_data( 8,:)'; units{ 8} = '[K]'; + +aup.tb_h_obs_assim = tmp_data( 9,:)'; units{ 9} = '[K]'; +aup.tb_v_obs_assim = tmp_data(10,:)'; units{10} = '[K]'; +aup.tb_h_obs_errstd = tmp_data(11,:)'; units{11} = '[K]'; +aup.tb_v_obs_errstd = tmp_data(12,:)'; units{12} = '[K]'; + +aup.tb_h_forecast = tmp_data(13,:)'; units{13} = '[K]'; +aup.tb_v_forecast = tmp_data(14,:)'; units{14} = '[K]'; +aup.tb_h_forecast_ensstd = tmp_data(15,:)'; units{15} = '[K]'; +aup.tb_v_forecast_ensstd = tmp_data(16,:)'; units{16} = '[K]'; + +aup.sm_surface_forecast = tmp_data(17,:)'; units{17} = '[m3 m-3]'; +aup.sm_rootzone_forecast = tmp_data(18,:)'; units{18} = '[m3 m-3]'; +aup.sm_profile_forecast = tmp_data(19,:)'; units{19} = '[m3 m-3]'; +aup.surface_temp_forecast = tmp_data(20,:)'; units{20} = '[K]'; +aup.soil_temp_layer1_forecast = tmp_data(21,:)'; units{21} = '[K]'; + +aup.sm_surface_analysis = tmp_data(22,:)'; units{22} = '[m3 m-3]'; +aup.sm_rootzone_analysis = tmp_data(23,:)'; units{23} = '[m3 m-3]'; +aup.sm_profile_analysis = tmp_data(24,:)'; units{24} = '[m3 m-3]'; +aup.surface_temp_analysis = tmp_data(25,:)'; units{25} = '[K]'; +aup.soil_temp_layer1_analysis = tmp_data(26,:)'; units{26} = '[K]'; + +aup.sm_surface_analysis_ensstd = tmp_data(27,:)'; units{27} = '[m3 m-3]'; +aup.sm_rootzone_analysis_ensstd = tmp_data(28,:)'; units{28} = '[m3 m-3]'; +aup.sm_profile_analysis_ensstd = tmp_data(29,:)'; units{29} = '[m3 m-3]'; +aup.surface_temp_analysis_ensstd = tmp_data(30,:)'; units{30} = '[K]'; +aup.soil_temp_layer1_analysis_ensstd = tmp_data(31,:)'; units{31} = '[K]'; + +% =========== EOF =========================================== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GEOSldas_App/util/shared/matlab/read_smapL4SMlmc.m b/GEOSldas_App/util/shared/matlab/read_smapL4SMlmc.m new file mode 100644 index 0000000..f0cf783 --- /dev/null +++ b/GEOSldas_App/util/shared/matlab/read_smapL4SMlmc.m @@ -0,0 +1,139 @@ + +function [ lmc, units ] = read_smapL4SMlmc( fname, N_tile, isLDASsa); + +% reichle, 26 Apr 2013 + +% NOTE: For large files this reader is inefficient (slow execution, +% excessive memory demand) due to the use of a matlab structure +% array. If you need better performance, convert to reading +% data into a regular matrix (as opposed to a structure array). +% +% GDL, 17 Feb 2014: - added units +% - revised fieldnames for consistency with L4_SM Product Specs Doc +% reichle, 27 May 2014: - changed wilting point output from "clsm_wpwet" to "clsm_wp" +% reichle, 17 Nov 2015: - added "veghght" output +% reichle, 28 Jul 2022 - cleaned up LDASsa/GEOSldas switch for commit into GEOSldas repo + +% ---------------------------------------------------------------- + +if ~exist('isLDASsa','var') isLDASsa = 0; end % default is GEOSldas output + +% for backward compatibility, back out number of parameters in file +% from file size: + +% file size = N_param * (N_tile + 2) * bytes_per_datapoint + +tmps = dir(fname); + +N_param = tmps.bytes/((N_tile+2)*4); + +if N_param==34 | N_param==35 + + int_records = [17 18]; + +else + + error('read_smapL4SMlmc.m: something wrong with file size or format') + +end + +disp(['read_smapL4SMlmc.m: expecting ', num2str(N_param), ' parameters in file']) + +% ---------------------------------------------------------------- + +int_precision = 'int32'; % precision of fortran tag +float_precision = 'float32'; % precision of data in input file + +disp(['read_smapL4SMlmc.m: reading from ', fname]) + +if isLDASsa ~= 0 + machfmt = 'b'; % big-endian, LDASsa +else + machfmt = 'l'; % little-endian, GEOSldas +end + +ifp = fopen( fname, 'r', machfmt ); + +for i=1:N_param + + fortran_tag = fread( ifp, 1, int_precision ); + + if (4*N_tile ~= fortran_tag) + + error('read_smapL4SMlmc.m: inconsistent N_tile') + + end + + if any(i==int_records) + tmp = fread( ifp, [1 N_tile], int_precision ); + else + tmp = fread( ifp, [1 N_tile], float_precision ); + end + + fortran_tag = fread( ifp, 1, int_precision ); + + tmp_data(i,:) = tmp; + +end + +fclose(ifp); + +% --------------------------------------------------------- + +disp(['read_smapL4SMlmc.m: assembling structure array']) + +lmc.cell_land_fraction = tmp_data( 1,:)'; units{ 1} = '[dimensionless]'; +lmc.cell_elevation = tmp_data( 2,:)'; units{ 2} = '[m]'; + +lmc.clsm_dzsf = tmp_data( 3,:)'; units{ 3} = '[m]'; +lmc.clsm_dzrz = tmp_data( 4,:)'; units{ 4} = '[m]'; +lmc.clsm_dzpr = tmp_data( 5,:)'; units{ 5} = '[m]'; + +lmc.clsm_dztsurf = tmp_data( 6,:)'; units{ 6} = '[m]'; + +lmc.clsm_dzgt1 = tmp_data( 7,:)'; units{ 7} = '[m]'; +lmc.clsm_dzgt2 = tmp_data( 8,:)'; units{ 8} = '[m]'; +lmc.clsm_dzgt3 = tmp_data( 9,:)'; units{ 9} = '[m]'; +lmc.clsm_dzgt4 = tmp_data(10,:)'; units{10} = '[m]'; +lmc.clsm_dzgt5 = tmp_data(11,:)'; units{11} = '[m]'; +lmc.clsm_dzgt6 = tmp_data(12,:)'; units{12} = '[m]'; + +lmc.clsm_poros = tmp_data(13,:)'; units{13} = '[m3 m-3]'; +lmc.clsm_wp = tmp_data(14,:)'; units{14} = '[m3 m-3]'; + +lmc.clsm_cdcr1 = tmp_data(15,:)'; units{15} = '[kg m-2]'; +lmc.clsm_cdcr2 = tmp_data(16,:)'; units{16} = '[kg m-2]'; + + +lmc.mwrtm_vegcls = tmp_data(17,:)'; units{17} = '[dimensionless]'; +lmc.mwrtm_soilcls = tmp_data(18,:)'; units{18} = '[dimensionless]'; + +lmc.mwrtm_sand = tmp_data(19,:)'; units{19} = '[dimensionless]'; +lmc.mwrtm_clay = tmp_data(20,:)'; units{20} = '[dimensionless]'; +lmc.mwrtm_poros = tmp_data(21,:)'; units{21} = '[m3 m-3]'; + +lmc.mwrtm_wangwt = tmp_data(22,:)'; units{22} = '[m3 m-3]'; +lmc.mwrtm_wangwp = tmp_data(23,:)'; units{23} = '[m3 m-3]'; + +lmc.mwrtm_rghhmin = tmp_data(24,:)'; units{24} = '[dimensionless]'; +lmc.mwrtm_rghhmax = tmp_data(25,:)'; units{25} = '[dimensionless]'; +lmc.mwrtm_rghwmin = tmp_data(26,:)'; units{26} = '[m3 m-3]'; +lmc.mwrtm_rghwmax = tmp_data(27,:)'; units{27} = '[m3 m-3]'; +lmc.mwrtm_rghnrh = tmp_data(28,:)'; units{28} = '[dimensionless]'; +lmc.mwrtm_rghnrv = tmp_data(29,:)'; units{29} = '[dimensionless]'; +lmc.mwrtm_rghpolmix = tmp_data(30,:)'; units{30} = '[dimensionless]'; + +lmc.mwrtm_omega = tmp_data(31,:)'; units{31} = '[dimensionless]'; + +lmc.mwrtm_bh = tmp_data(32,:)'; units{32} = '[dimensionless]'; +lmc.mwrtm_bv = tmp_data(33,:)'; units{33} = '[dimensionless]'; +lmc.mwrtm_lewt = tmp_data(34,:)'; units{34} = '[kg m-2]'; + +if N_param==35 + + lmc.clsm_veghght = tmp_data(35,:)'; units{35} = '[m]'; + +end + +% =========== EOF =========================================== + diff --git a/GEOSldas_App/util/shared/matlab/read_tilecoord.m b/GEOSldas_App/util/shared/matlab/read_tilecoord.m new file mode 100644 index 0000000..422f6fb --- /dev/null +++ b/GEOSldas_App/util/shared/matlab/read_tilecoord.m @@ -0,0 +1,240 @@ + +function [tile_coord ] = read_tilecoord( fname, bin2txt, isLDASsa ) + +% read tile coordinates from *_tilecoord.[ext] file written by LDASsa +% +% reichle, 29 Jun 2005 +% GDL, 22 Jun 2010 - changed i_atm/j_atm/frac_atm to i_indg/j_indg/frac_cell +% reichle, 31 May 2011 - accomodate new field "elev" (elevation) +% reichle, 7 Jan 2014 - added capability to read binary "tilecoord" files +% and to convert a binary file into a txt file +% file extension: ".txt" --> ASCII file +% ".bin" --> binary file +% ASCII option maintains backward compatibility +% +% jperket, 1 Dec 2017 - added flag for LDASsa, big-endian format +% reichle, 28 Jul 2022 - cleaned up LDASsa/GEOSldas switch for commit into GEOSldas repo +% +% ------------------------------------------------------------- + +if ~exist('isLDASsa','var') isLDASsa = 0; end % default is GEOSldas output + +int_precision = 'int32'; % precision of fortran tag +float_precision = 'float32'; % precision of data in input file + +% deal with "optional" bin2txt argument + +if ~exist('bin2txt','var') + + bin2txt = 0; + +end + +if isLDASsa ~= 0 + machfmt = 'b'; % big-endian, LDASsa +else + machfmt = 'l'; % little-endian, GEOSldas +end + +% determine file name extension + +file_ext = deblank(fname); + +file_ext = file_ext(end-3:end); + +if strcmp(file_ext,'.txt') + + is_binary = 0; + + if bin2txt + + error('read_tilecoord.m: ERROR -- bin2txt conversion ', ... + 'requires input file name for bin file'); + + end + +elseif strcmp(file_ext,'.bin') + + is_binary = 1; + +else + + error('read_tilecoord.m: ERROR - unknown file extension') + +end + + +% --------------------------------- +% +% read file + +disp(['reading from ', fname]) + +if is_binary + + % open *_tilecoord.bin file + + ifp = fopen( fname, 'r', machfmt); + + fortran_tag = fread( ifp, 1, int_precision ); + tile_coord.N_tile = fread( ifp, 1, int_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + Nt = tile_coord.N_tile; + + fortran_tag = fread( ifp, 1, int_precision ); + tile_coord.tile_id = fread( ifp, Nt, int_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + fortran_tag = fread( ifp, 1, int_precision ); + tile_coord.typ = fread( ifp, Nt, int_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + fortran_tag = fread( ifp, 1, int_precision ); + tile_coord.pfaf = fread( ifp, Nt, int_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + fortran_tag = fread( ifp, 1, int_precision ); + tile_coord.com_lon = fread( ifp, Nt, float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + fortran_tag = fread( ifp, 1, int_precision ); + tile_coord.com_lat = fread( ifp, Nt, float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + fortran_tag = fread( ifp, 1, int_precision ); + tile_coord.min_lon = fread( ifp, Nt, float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + fortran_tag = fread( ifp, 1, int_precision ); + tile_coord.max_lon = fread( ifp, Nt, float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + fortran_tag = fread( ifp, 1, int_precision ); + tile_coord.min_lat = fread( ifp, Nt, float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + fortran_tag = fread( ifp, 1, int_precision ); + tile_coord.max_lat = fread( ifp, Nt, float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + fortran_tag = fread( ifp, 1, int_precision ); + tile_coord.i_indg = fread( ifp, Nt, int_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + fortran_tag = fread( ifp, 1, int_precision ); + tile_coord.j_indg = fread( ifp, Nt, int_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + fortran_tag = fread( ifp, 1, int_precision ); + tile_coord.frac_cell = fread( ifp, Nt, float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + fortran_tag = fread( ifp, 1, int_precision ); + tile_coord.frac_pfaf = fread( ifp, Nt, float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + fortran_tag = fread( ifp, 1, int_precision ); + tile_coord.area = fread( ifp, Nt, float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + fortran_tag = fread( ifp, 1, int_precision ); + tile_coord.elev = fread( ifp, Nt, float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + + % if requested, convert to ASCII (txt) file + + if bin2txt + + fname_out = deblank(fname); + + fname_out = [fname_out(1:end-4), '.txt']; + + % open *_tilecoord.txt file + + disp(['writing to ', fname]) + + ofp = fopen( fname_out, 'wt' ); + + fprintf( ofp, '%i\n', tile_coord.N_tile ); + + for ii=1:tile_coord.N_tile + + fprintf( ofp,['%8i%10i%9i', ... + '%10.4f%10.4f%10.4f%10.4f%10.4f%10.4f', ... + '%5i%5i%13.6f%13.6f%13.4f%13.4f\n'], ... + [tile_coord.tile_id(ii), ... + tile_coord.typ(ii), ... + tile_coord.pfaf(ii), ... + tile_coord.com_lon(ii), ... + tile_coord.com_lat(ii), ... + tile_coord.min_lon(ii), ... + tile_coord.max_lon(ii), ... + tile_coord.min_lat(ii), ... + tile_coord.max_lat(ii), ... + tile_coord.i_indg(ii), ... + tile_coord.j_indg(ii), ... + tile_coord.frac_cell(ii), ... + tile_coord.frac_pfaf(ii), ... + tile_coord.area(ii), ... + tile_coord.elev(ii) ]); + + end + + fclose(ofp); + + disp('done writing file') + + end + +else + + % open *_tilecoord.txt file + + ifp = fopen( fname, 'rt' ); + + tmpdata = fscanf( ifp, '%f' ); + + % -------------------------------------------------- + + % process data + + tile_coord.N_tile = tmpdata(1); + + N_cols = (length(tmpdata)-1)/tile_coord.N_tile; + + tmpdata = reshape(tmpdata(2:end), [N_cols, tile_coord.N_tile])'; + + tile_coord.tile_id = tmpdata(:, 1); + tile_coord.typ = tmpdata(:, 2); + tile_coord.pfaf = tmpdata(:, 3); + tile_coord.com_lon = tmpdata(:, 4); + tile_coord.com_lat = tmpdata(:, 5); + tile_coord.min_lon = tmpdata(:, 6); + tile_coord.max_lon = tmpdata(:, 7); + tile_coord.min_lat = tmpdata(:, 8); + tile_coord.max_lat = tmpdata(:, 9); + tile_coord.i_indg = tmpdata(:,10); + tile_coord.j_indg = tmpdata(:,11); + tile_coord.frac_cell = tmpdata(:,12); + tile_coord.frac_pfaf = tmpdata(:,13); + tile_coord.area = tmpdata(:,14); + + if N_cols==15 + + tile_coord.elev = tmpdata(:,15); + + end + +end + +% close file + +fclose(ifp); + +disp('done reading file') + +% =========== EOF ======================================== + + diff --git a/GEOSldas_App/util/shared/matlab/read_tilegrids.m b/GEOSldas_App/util/shared/matlab/read_tilegrids.m new file mode 100644 index 0000000..4299768 --- /dev/null +++ b/GEOSldas_App/util/shared/matlab/read_tilegrids.m @@ -0,0 +1,136 @@ + +function [ tile_grid_g, tile_grid_d ] = read_tilegrids( fname, isLDASsa ) + +% read tile grid definitions for "global" and "domain" grids +% from *_tilegrids.[ext] file written by LDASsa +% +% reichle, 8 July 2010 +% reichle, 7 Jan 2014 - added capability to read binary "tilegrids" files +% file extension: ".txt" --> ASCII file +% ".bin" --> binary file +% jperket, 4 Dec 2017 - added flag for LDASsa, big-endian format +% reichle, 28 Jul 2022 - cleaned up LDASsa/GEOSldas switch for commit into GEOSldas repo +% +% ------------------------------------------------------------- + +if ~exist('isLDASsa','var') isLDASsa = 0; end % default is GEOSldas output + +int_precision = 'int32'; % precision of fortran tag +float_precision = 'float32'; % precision of data in input file + +if isLDASsa ~= 0 + machfmt = 'b'; % big-endian, LDASsa +else + machfmt = 'l'; % little-endian, GEOSldas +end + +% determine file name extension + +file_ext = deblank(fname); + +file_ext = file_ext(end-3:end); + +% --------------------------------- +% +% read file + +if strcmp(file_ext,'.txt') + + % read ASCII file + + disp(['reading from ', fname]) + + % open *_tilegrids.txt file + + ifp = fopen( fname, 'rt' ); + + % read contents into cell array + + A=textscan(ifp,'%s'); + + fclose(ifp); + + disp('done reading file') + + % -------------------------------------------------- + + % re-assemble into long string and evaluate + + C=[]; + + for i=1:length(A{1}) + + C=[C, A{1}{i}]; + + end + + eval(C) % now have structures "tile_grid_g" and "tile_grid_d" + +elseif strcmp(file_ext,'.bin') + + % read binary file + + disp(['reading from ', fname]) + + % open *_tilegrids.txt file + + ifp = fopen( fname, 'r', machfmt); + + % read contents + + % first record: "global" grid (tile_grid_g) + + fortran_tag = fread( ifp, 1, int_precision ); + tile_grid_g.gridtype = fread( ifp, 40, 'uint8=>char' ); + tile_grid_g.ind_base = fread( ifp, 1, int_precision ); + tile_grid_g.i_dir = fread( ifp, 1, int_precision ); + tile_grid_g.j_dir = fread( ifp, 1, int_precision ); + tile_grid_g.N_lon = fread( ifp, 1, int_precision ); + tile_grid_g.N_lat = fread( ifp, 1, int_precision ); + tile_grid_g.i_offg = fread( ifp, 1, int_precision ); + tile_grid_g.j_offg = fread( ifp, 1, int_precision ); + tile_grid_g.ll_lon = fread( ifp, 1, float_precision ); + tile_grid_g.ll_lat = fread( ifp, 1, float_precision ); + tile_grid_g.ur_lon = fread( ifp, 1, float_precision ); + tile_grid_g.ur_lat = fread( ifp, 1, float_precision ); + tile_grid_g.dlon = fread( ifp, 1, float_precision ); + tile_grid_g.dlat = fread( ifp, 1, float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + tile_grid_g.gridtype = deblank(tile_grid_g.gridtype'); + + % second record: "domain" grid (tile_grid_d) + + fortran_tag = fread( ifp, 1, int_precision ); + tile_grid_d.gridtype = fread( ifp, 40, 'uint8=>char' ); + tile_grid_d.ind_base = fread( ifp, 1, int_precision ); + tile_grid_d.i_dir = fread( ifp, 1, int_precision ); + tile_grid_d.j_dir = fread( ifp, 1, int_precision ); + tile_grid_d.N_lon = fread( ifp, 1, int_precision ); + tile_grid_d.N_lat = fread( ifp, 1, int_precision ); + tile_grid_d.i_offg = fread( ifp, 1, int_precision ); + tile_grid_d.j_offg = fread( ifp, 1, int_precision ); + tile_grid_d.ll_lon = fread( ifp, 1, float_precision ); + tile_grid_d.ll_lat = fread( ifp, 1, float_precision ); + tile_grid_d.ur_lon = fread( ifp, 1, float_precision ); + tile_grid_d.ur_lat = fread( ifp, 1, float_precision ); + tile_grid_d.dlon = fread( ifp, 1, float_precision ); + tile_grid_d.dlat = fread( ifp, 1, float_precision ); + fortran_tag = fread( ifp, 1, int_precision ); + + tile_grid_d.gridtype = deblank(tile_grid_d.gridtype'); + + % close file + + fclose(ifp); + + disp('done reading file') + +else + + error('read_tilegrids.m: ERROR - unknown file extension') + +end + +% ================== EOF ===================================== + diff --git a/GEOSldas_App/util/shared/matlab/tile2grid.m b/GEOSldas_App/util/shared/matlab/tile2grid.m new file mode 100644 index 0000000..0035eb3 --- /dev/null +++ b/GEOSldas_App/util/shared/matlab/tile2grid.m @@ -0,0 +1,87 @@ +function [ grid_data ] = ... + tile2grid( tile_data, tile_coord, tile_grid, nodata, nodata_tol ) + +% Mapping from tile to grid is based on fields "i_indg" and +% "j_indg" of tilecoord structure which are in reference to +% the *global* grid that underlies the tile definitions. +% Therefore, the input variable "tile_grid" must refer to *global* +% grid. + +% reichle, 26 Jan 2006 +% reichle, 25 Jul 2006 - expanded for RedArk_OSSE +% GDL, 22 Jun 2010 - adapted for latest LDAS-tag +% reichle, 8 Jul 2010 - use "tile_grid" as input, not bkwd-compatible! +% +% ----------------------------------------------------------------- + +% check whether no-data variables are available on input + +if ~exist('nodata'), nodata = -9999; end +if ~exist('nodata_tol'), nodata_tol = 1e-4; end + +% ----------------------------------------------------- + +N_fields = size(tile_data,1); + +% minimal check for consistency between tile_data and tile_coord + +if (size(tile_data,2)~=tile_coord.N_tile) + + input('tile2grid.m: Something wrong with N_tile, ctrl-c now!') + +end + +% ------------------------------------------------------ + +% initialize + +grid_data = zeros( tile_grid.N_lon, tile_grid.N_lat, N_fields ); + +for k=1:N_fields + + wgrid = zeros( tile_grid.N_lon, tile_grid.N_lat); + + % loop through tile space + + for n=1:tile_coord.N_tile + + i = tile_coord.i_indg(n) - (tile_grid.i_offg - (1-tile_grid.ind_base)); + j = tile_coord.j_indg(n) - (tile_grid.j_offg - (1-tile_grid.ind_base)); + + w = tile_coord.frac_cell(n); + + if (abs(tile_data(k,n)-nodata)>nodata_tol) + + grid_data(i,j,k) = grid_data(i,j,k) + w*tile_data(k,n); + + wgrid(i,j) = wgrid(i,j) + w; + + end + + end + + % normalize and set no-data-value + + for i=1:tile_grid.N_lon + + for j=1:tile_grid.N_lat + + if (wgrid(i,j)>0.) + + grid_data(i,j,k) = grid_data(i,j,k)/wgrid(i,j); + + else + + grid_data(i,j,k) = nodata; + + end + + end + end + +end + + +grid_data(find(grid_data==nodata)) = NaN; + +% ================ EOF ========================================= From c8d9d6c98a9bb100265c61d8a0872095ea86ab92 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Mon, 25 Mar 2024 16:18:22 -0400 Subject: [PATCH 2/4] Change repo name --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dcf8520..f895e3e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ -# Contributing to GEOSgcm_GridComp +# Contributing to GEOSldas_GridComp -Contributing code to GEOSgcm_GridComp should be done via a Pull Request to this repository. +Contributing code to GEOSldas_GridComp should be done via a Pull Request to this repository. ## Contributor License Agreement (CLA) From 5ea4eb5b17e9d967ed8bf997befa44712cb0b0fb Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Mon, 25 Mar 2024 16:18:27 -0400 Subject: [PATCH 3/4] Add github and circleci files --- .circleci/config.yml | 25 ++++++++++++++++ .github/CODEOWNERS | 18 +++++++++++ .../PULL_REQUEST_TEMPLATE/auto_pr_to_mapl3.md | 10 +++++++ .github/workflows/push-to-develop.yml | 30 +++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 .circleci/config.yml create mode 100644 .github/CODEOWNERS create mode 100644 .github/PULL_REQUEST_TEMPLATE/auto_pr_to_mapl3.md create mode 100644 .github/workflows/push-to-develop.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..990004d --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,25 @@ +version: 2.1 + +# Anchors in case we need to override the defaults from the orb +#baselibs_version: &baselibs_version v7.17.0 +#bcs_version: &bcs_version v11.4.0 + +orbs: + ci: geos-esm/circleci-tools@2 + +workflows: + build-test: + jobs: + # Build GEOSldas + - ci/build: + name: build-GEOSldas-on-<< matrix.compiler >> + context: + - docker-hub-creds + matrix: + parameters: + compiler: [ifort, gfortran] + #baselibs_version: *baselibs_version + repo: GEOSldas + checkout_fixture: true + mepodevelop: false + persist_workspace: false # Needs to be true to run fv3/gcm experiment, costs extra diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..9b4ec79 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,18 @@ +# This is a comment. +# Each line is a file pattern followed by one or more owners. +# Order is important; the last matching pattern takes the most +# precedence. + +# LDAS Gatekeepers own all files +* @GEOS-ESM/ldas-gatekeepers + +# The LDAS gatekeepers and CMake should know/approve these +/.github/ @GEOS-ESM/cmake-team @GEOS-ESM/ldas-gatekeepers +/.circleci/ @GEOS-ESM/cmake-team @GEOS-ESM/ldas-gatekeepers +/.codebuild/ @GEOS-ESM/cmake-team @GEOS-ESM/ldas-gatekeepers + +# The GEOS CMake Team should be notified about changes to the CMakeLists.txt files in this repository +CMakeLists.txt @GEOS-ESM/cmake-team @GEOS-ESM/ldas-gatekeepers + +# The GEOS CMake Team should be notified about and approve config changes +/config/ @GEOS-ESM/cmake-team @GEOS-ESM/ldas-gatekeepers diff --git a/.github/PULL_REQUEST_TEMPLATE/auto_pr_to_mapl3.md b/.github/PULL_REQUEST_TEMPLATE/auto_pr_to_mapl3.md new file mode 100644 index 0000000..55020d7 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/auto_pr_to_mapl3.md @@ -0,0 +1,10 @@ +## :memo: Automatic PR: `develop` → `release/MAPL-v3` + +### Description + + + +## :file_folder: Modified files + + + diff --git a/.github/workflows/push-to-develop.yml b/.github/workflows/push-to-develop.yml new file mode 100644 index 0000000..ebbd310 --- /dev/null +++ b/.github/workflows/push-to-develop.yml @@ -0,0 +1,30 @@ +name: Push to Develop + +on: + push: + branches: + - develop + +jobs: + pull_request: + name: Create Pull Request + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Run the action + uses: devops-infra/action-pull-request@v0.5.5 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + source_branch: develop + target_branch: release/MAPL-v3 + label: automatic,MAPL3,Skip Changelog + template: .github/PULL_REQUEST_TEMPLATE/auto_pr_to_mapl3.md + get_diff: true + assignee: ${{ github.actor }} + old_string: "" + new_string: ${{ github.event.commits[0].message }} + title: Auto PR - develop → MAPL-v3 - ${{ github.event.commits[0].message }} + From ef4da498b945ef710777cdeb89191ee60151943a Mon Sep 17 00:00:00 2001 From: Rolf Reichle Date: Tue, 26 Mar 2024 12:08:25 -0400 Subject: [PATCH 4/4] initial documentation (CHANGELOG.md, README.md) --- CHANGELOG.md | 12 +++++++++++- README.md | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index caab8cc..29d664c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,22 @@ -# Changelog +# Changelog for `GEOSldas_GridComp` All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +----------------------------- + ## [Unreleased] +----------------------------- + +## [v1.0.0] - 2024-03-26 + +- Inaugural version. 0-diff vs. GEOSldas v18.0.0. + +----------------------------- + ### Added ### Changed diff --git a/README.md b/README.md index ffe9bc4..1bcbcb8 100644 --- a/README.md +++ b/README.md @@ -1 +1,21 @@ # GEOSldas_GridComp + +The `GEOSldas_GridComp` repository contains the source code for the GEOS land modeling and data assimilation system (`GEOSldas`). + + +## History + +The repository was created in March 2024 by extracting the GEOSldas source code from the [`GEOSldas` fixture](https://github.com/GEOS-ESM/GEOSldas). + +The new repository was needed to facilitate the use of the `GEOSldas_GridComp` as an external repository in both the [`GEOSldas` fixture](https://github.com/GEOS-ESM/GEOSldas) and the [GEOS atmospheric data assimilation (`GEOSadas`) fixture](https://github.com/GEOS-ESM/GEOSadas). + + +## Contributing + +Please check out our [contributing guidelines](CONTRIBUTING.md). + +## License + +All files are currently licensed under the [Apache-2.0 license (`LICENSE`)](LICENSE). + +Previously, the code was licensed under the [NASA Open Source Agreement, Version 1.3 (`LICENSE-NOSA`)](LICENSE-NOSA).