diff --git a/.circleci/config.yml b/.circleci/config.yml index 7c025b9d..955b88d2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,11 +1,11 @@ version: 2.1 # Anchors in case we need to override the defaults from the orb -#baselibs_version: &baselibs_version v7.33.0 -#bcs_version: &bcs_version v11.6.0 +#baselibs_version: &baselibs_version v8.32.0 +#bcs_version: &bcs_version v12.0.0 orbs: - ci: geos-esm/circleci-tools@4 + ci: geos-esm/circleci-tools@5 workflows: build-test: @@ -17,11 +17,13 @@ workflows: - docker-hub-creds matrix: parameters: - compiler: [ifort, gfortran] + # We cannot currently test with ifx because HDF4 Fortran interface + # cannot be compiled with ifx. The HDF4 Fortran interface is required + # for the GEOSldas + #compiler: [gfortran, ifort, ifx] + compiler: [gfortran, ifort] #baselibs_version: *baselibs_version repo: GEOSldas checkout_fixture: true mepodevelop: false - # This is needed due to the LDAS BRIDGE workflow - checkout_if_exists: false persist_workspace: false # Needs to be true to run fv3/gcm experiment, costs extra diff --git a/.github/workflows/push-to-develop.yml b/.github/workflows/push-to-develop.yml index ebbd3109..3ce6a02e 100644 --- a/.github/workflows/push-to-develop.yml +++ b/.github/workflows/push-to-develop.yml @@ -8,14 +8,15 @@ on: jobs: pull_request: name: Create Pull Request + if: github.repository_owner == 'GEOS-ESM' runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: fetch-depth: 0 - name: Run the action - uses: devops-infra/action-pull-request@v0.5.5 + uses: devops-infra/action-pull-request@v1 with: github_token: ${{ secrets.GITHUB_TOKEN }} source_branch: develop diff --git a/.github/workflows/validate_yaml_files.yml b/.github/workflows/validate_yaml_files.yml index 449db6e6..be70981f 100644 --- a/.github/workflows/validate_yaml_files.yml +++ b/.github/workflows/validate_yaml_files.yml @@ -15,7 +15,7 @@ jobs: validate-YAML: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - id: yaml-lint name: yaml-lint uses: ibiqlik/action-yamllint@v3 @@ -24,7 +24,7 @@ jobs: format: colored config_file: .yamllint.yml - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 if: always() with: name: yamllint-logfile diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 20796af2..eb0e8812 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -11,123 +11,40 @@ on: - "**.perl" - ".github/CODEOWNERS" - ".circleci/config.yml" + workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }} jobs: - build_ldas_ifort: - name: Build GEOSldas with ifort - if: "!contains(github.event.pull_request.labels.*.name, '0 diff trivial')" - runs-on: ubuntu-24.04 - container: - image: gmao/ubuntu24-geos-env:v7.33.0-intelmpi_2021.13-ifort_2021.13 - - env: - OMPI_ALLOW_RUN_AS_ROOT: 1 - OMPI_ALLOW_RUN_AS_ROOT_CONFIRM: 1 - OMPI_MCA_btl_vader_single_copy_mechanism: none - - steps: - # https://github.com/orgs/community/discussions/25678#discussioncomment-5242449 - - name: Delete huge unnecessary tools folder - run: rm -rf /opt/hostedtoolcache - - - name: Checkout LDAS - uses: actions/checkout@v4 - with: - fetch-depth: 1 - filter: blob:none - repository: GEOS-ESM/GEOSldas - - - name: Set all directories as git safe - run: | - git config --global --add safe.directory '*' - - - name: Versions etc. - run: | - ifort --version - mpirun --version - echo $BASEDIR - - - name: Mepo clone external repos - run: | - mepo clone --partial blobless - mepo status - - - name: Debug PR branch - run: echo "PR is coming from ${{ github.event.pull_request.head.ref }}" - - - name: Update other branches - if: ${{ github.event.pull_request.head.ref != 'main' && github.event.pull_request.head.ref != 'develop' }} - run: | - mepo checkout-if-exists ${GITHUB_HEAD_REF} - mepo status - - - name: CMake - run: | - cmake -B build -S . --install-prefix=${pwd}/install -DCMAKE_Fortran_COMPILER=ifort -DCMAKE_BUILD_TYPE=Debug -DMPIEXEC_PREFLAGS='--oversubscribe' -DUSE_F2PY=OFF - - - name: Build - run: | - cmake --build build -j 4 - cmake --install build - - - build_ldas_gfortran: - name: Build GEOSldas with gfortran - if: "!contains(github.event.pull_request.labels.*.name, '0 diff trivial')" - runs-on: ubuntu-24.04 - container: - image: gmao/ubuntu24-geos-env-mkl:v7.33.0-openmpi_5.0.5-gcc_14.2.0 - - env: - OMPI_ALLOW_RUN_AS_ROOT: 1 - OMPI_ALLOW_RUN_AS_ROOT_CONFIRM: 1 - OMPI_MCA_btl_vader_single_copy_mechanism: none - - steps: - # https://github.com/orgs/community/discussions/25678#discussioncomment-5242449 - - name: Delete huge unnecessary tools folder - run: rm -rf /opt/hostedtoolcache - - - name: Checkout LDAS - uses: actions/checkout@v4 - with: - fetch-depth: 1 - filter: blob:none - repository: GEOS-ESM/GEOSldas - - - name: Set all directories as git safe - run: | - git config --global --add safe.directory '*' - - - name: Versions etc. - run: | - gfortran --version - mpirun --version - echo $BASEDIR - - - name: Mepo clone external repos - run: | - mepo clone --partial blobless - mepo status - - - name: Debug PR branch - run: echo "PR is coming from ${{ github.event.pull_request.head.ref }}" - - - name: Update other branches - if: ${{ github.event.pull_request.head.ref != 'main' && github.event.pull_request.head.ref != 'develop' }} - run: | - mepo checkout-if-exists ${GITHUB_HEAD_REF} - mepo status - - - name: CMake - run: | - cmake -B build -S . --install-prefix=${pwd}/install -DCMAKE_Fortran_COMPILER=gfortran -DCMAKE_BUILD_TYPE=Debug -DMPIEXEC_PREFLAGS='--oversubscribe' -DUSE_F2PY=OFF - - - name: Build - run: | - cmake --build build -j 4 - cmake --install build + build_ldas: + strategy: + fail-fast: false + matrix: + # We cannot currently test with ifx because HDF4 Fortran interface + # cannot be compiled with ifx. The HDF4 Fortran interface is required + # for the GEOSldas + #compiler: [ifort, gfortran-15, ifx] + compiler: [ifort, gfortran-15] + build-type: [Debug] + uses: GEOS-ESM/CI-workflows/.github/workflows/geosgcm_build_tests.yml@project/geosgcm + with: + compiler: ${{ matrix.compiler }} + cmake-build-type: ${{ matrix.build-type }} + fixture-repo: GEOS-ESM/GEOSldas + run-mepo-develop: false + + # We cannot currently test with spack because spack does not build the + # HDF4 Fortran interface, which is required for the GEOSldas. + ####################################################################################### + # spack_build: # + # uses: GEOS-ESM/CI-workflows/.github/workflows/spack_gcc_build.yml@project/geosgcm # + # secrets: # + # BUILDCACHE_USERNAME: ${{ secrets.BUILDCACHE_USERNAME }} # + # BUILDCACHE_TOKEN: ${{ secrets.BUILDCACHE_TOKEN }} # + # with: # + # fixture-repo: GEOS-ESM/GEOSldas # + # run-mepo-develop: false # + # load-fms: true # + ####################################################################################### diff --git a/CMakeLists.txt b/CMakeLists.txt index 2cd58b9c..48baf88a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ esma_add_library(${this} SRCS GEOS_LdasGridComp.F90 SUBCOMPONENTS ${alldirs} SUBDIRS LDAS_Shared - DEPENDENCIES GEOSland_GridComp GEOSlandice_GridComp GEOSroute_GridComp makebcs MAPL + DEPENDENCIES GEOSland_GridComp GEOSlake_GridComp GEOSlandice_GridComp GEOSroute_GridComp makebcs MAPL INCLUDES ${INC_ESMF}) esma_add_subdirectory(GEOSldas_App) diff --git a/GEOS_LdasGridComp.F90 b/GEOS_LdasGridComp.F90 index f4109127..c49f4ed0 100644 --- a/GEOS_LdasGridComp.F90 +++ b/GEOS_LdasGridComp.F90 @@ -15,6 +15,7 @@ module GEOS_LdasGridCompMod use GEOS_LandAssimGridCompMod, only: LandAssimSetServices => SetServices use GEOS_LandiceGridCompMod, only: LandiceSetServices => SetServices use GEOS_RouteGridCompMod, only: RouteSetServices => SetServices + use GEOS_LakeGridCompMod, only: LakeSetServices => SetServices use GEOS_MetforceAvgGridCompMod, only: MetforceAvgSetServices => SetServices use GEOS_LandAvgGridCompMod, only: LandAvgSetServices => SetServices @@ -55,6 +56,7 @@ module GEOS_LdasGridCompMod ! All children integer,allocatable :: LAND(:) + integer,allocatable :: LAKE(:) integer,allocatable :: LANDICE(:) integer,allocatable :: ROUTE(:) integer,allocatable :: LANDPERT(:) @@ -68,6 +70,7 @@ module GEOS_LdasGridCompMod logical :: mwRTM logical :: ensemble_forcing ! switch between deterministic and ensemble forcing logical :: with_landice ! true if landice tiles requested by config + logical :: with_lake ! true if lake tiles requested by config logical :: with_land ! true if land tiles requested by config integer :: RUN_ROUTE ! 0:, no river routing, 1: routing w/o reservoirs, 2: routing w/ reservoirs @@ -178,11 +181,15 @@ subroutine SetServices(gc, rc) with_landice = .false. with_land = .false. -! with_lake = .false. + with_lake = .false. if (any(tile_types == MAPL_LANDICE)) with_landice = .true. if (any(tile_types == MAPL_LAND )) with_land = .true. -! if (any(tile_types == MAPL_LAKE )) with_lake = .true. + if (any(tile_types == MAPL_LAKE )) with_lake = .true. + + if (NUM_ENSEMBLE > 1 .and. with_lake) then + _ASSERT(.false., "Lake is not supported in ensemble mode.") + endif call MAPL_GetResource ( MAPL, LAND_ASSIM_STR, Label="LAND_ASSIM:", DEFAULT="NO", RC=STATUS) VERIFY_(STATUS) @@ -214,6 +221,7 @@ subroutine SetServices(gc, rc) endif if (with_land) allocate(LAND( NUM_ENSEMBLE),LANDPERT(NUM_ENSEMBLE)) + if (with_lake) allocate(LAKE( NUM_ENSEMBLE)) if (with_landice) allocate(LANDICE(NUM_ENSEMBLE)) if (RUN_ROUTE >= 1) then _ASSERT( with_land, "RUNOFF must be from the export of land_gridcomp for now.") @@ -261,6 +269,12 @@ subroutine SetServices(gc, rc) VERIFY_(status) endif + if (with_lake) then + childname = 'LAKE' // trim(ensid_string) + LAKE(i) = MAPL_AddChild(gc, name=childname, ss=LakeSetServices, rc=status) + VERIFY_(status) + endif + if (with_landice .and. i == 1) then childname='LANDICE'//trim(ensid_string) LANDICE(i) = MAPL_AddChild(gc, name=childname, ss=LandiceSetServices, rc=status) @@ -416,6 +430,7 @@ subroutine Initialize(gc, import, export, clock, rc) ! MAPL variables type(MAPL_LocStream) :: surf_locstream type(MAPL_LocStream) :: land_locstream + type(MAPL_LocStream) :: lake_locstream type(MAPL_LocStream) :: landice_locstream type(MAPL_LocStream) :: force_locstream type(MAPL_MetaComp), pointer :: MAPL=>null() ! GC's MAPL obj @@ -606,8 +621,9 @@ subroutine Initialize(gc, import, export, clock, rc) call MAPL_Get(MAPL, GCS=gcs, GCNAMES=gcnames, rc=status) VERIFY_(status) - ! Create LAND's locstreams as subset of Surface locstream + ! Create component locstreams as subsets of Surface locstream ! and add it to the children's MAPL objects + ! build the active forcing tile mask in tile-file order: LAND -> LAKE -> LANDICE. allocate(mask(0)) if (with_land) then call MAPL_LocStreamCreate( & @@ -620,7 +636,16 @@ subroutine Initialize(gc, import, export, clock, rc) VERIFY_(status) mask =[mask,MAPL_LAND] endif - + if (with_lake) then + call MAPL_LocStreamCreate( & + lake_locstream, & + surf_locstream, & + name=gcnames(LAKE(1)), & + mask=[MAPL_LAKE], & + rc=status ) + VERIFY_(status) + mask = [mask, MAPL_LAKE] + endif if (with_landice) then call MAPL_LocStreamCreate( & landice_locstream, & @@ -806,7 +831,13 @@ subroutine Initialize(gc, import, export, clock, rc) call ESMF_UserCompSetInternalState(gcs(LANDPERT(i)), 'TILE_COORD', tcwrap, status) VERIFY_(status) endif - + if (with_lake) then + call MAPL_GetObjectFromGC(gcs(LAKE(i)), CHILD_MAPL, rc=status) + VERIFY_(status) + + call MAPL_Set(CHILD_MAPL, LocStream=lake_locstream, rc=status) + VERIFY_(status) + endif if (with_landice .and. i == 1) then call MAPL_GetObjectFromGC(gcs(LANDICE(i)), CHILD_MAPL, rc=status) VERIFY_(status) @@ -1026,6 +1057,10 @@ subroutine Run(gc, import, export, clock, rc) call ESMF_GridCompRun(gcs(igc), importState=gex(igc), exportState=gim(LANDICE(i)), clock=clock, phase=4, userRC=status) VERIFY_(status) endif + if (with_lake) then + call ESMF_GridCompRun(gcs(igc), importState=gex(igc), exportState=gim(LAKE(i)), clock=clock, phase=5, userRC=status) + VERIFY_(status) + endif call MAPL_TimerOff(MAPL, gcnames(igc)) enddo @@ -1070,6 +1105,16 @@ subroutine Run(gc, import, export, clock, rc) call MAPL_TimerOff(MAPL, gcnames(igc)) endif ! with_land_ice + if (with_lake) then + igc = LAKE(i) + call MAPL_TimerOn(MAPL, gcnames(igc)) + call ESMF_GridCompRun(gcs(igc), importState=gim(igc), exportState=gex(igc), clock=clock, phase=1, userRC=status) + VERIFY_(status) + call ESMF_GridCompRun(gcs(igc), importState=gim(igc), exportState=gex(igc), clock=clock, phase=2, userRC=status) + VERIFY_(status) + call MAPL_TimerOff(MAPL, gcnames(igc)) + endif ! with_lake + if ( RUN_ROUTE >= 1 ) then igc = ROUTE(i) call MAPL_TimerOn(MAPL, gcnames(igc)) diff --git a/GEOSldas_App/GEOSldas_HIST.rc b/GEOSldas_App/GEOSldas_HIST.rc index dab8c269..2a566709 100644 --- a/GEOSldas_App/GEOSldas_HIST.rc +++ b/GEOSldas_App/GEOSldas_HIST.rc @@ -22,6 +22,8 @@ COLLECTIONS: # 'inst3_2d_lndfcstana_Nx' # 'const_1d_lnd_Nt' # 'const_2d_lnd_Nx' +# 'tavg24_1d_lake_Nt' +# 'tavg24_2d_lake_Nx' # 'tavg24_2d_glc_Nx' # 'tavg24_1d_glc_Nt' # 'tavg24_1d_issm_Nt' @@ -513,7 +515,64 @@ EASEv2_M36.LM: 1 'TPSURF_ANA_ENSSTD' , 'LANDASSIM' , 'TSURF_ANA_ENSSTD' , 'TP1_ANA_ENSSTD' , 'LANDASSIM' , 'TSOIL1_ANA_ENSSTD' :: - + tavg24_2d_lake_Nx.descr: '2d,Daily,Time-Averaged,Single-Level,Lake Diagnostics', + tavg24_2d_lake_Nx.nbits: 12, + tavg24_2d_lake_Nx.template: '%y4%m2%d2_%h2%n2z.nc4' , + tavg24_2d_lake_Nx.mode: 'time-averaged' , + tavg24_2d_lake_Nx.frequency: 240000 , + tavg24_2d_lake_Nx.ref_time: 000000 , + tavg24_2d_lake_Nx.format: 'CFIO' , + tavg24_2d_lake_Nx.regrid_exch: '../input/tile.data' , + tavg24_2d_lake_Nx.regrid_name: 'GRIDNAME' , + tavg24_2d_lake_Nx.grid_label: PC720x361-DC , # comment this line out for cube face output + tavg24_2d_lake_Nx.deflate: 1, + tavg24_2d_lake_Nx.fields: 'ALBVR' , 'LAKE' , 'ALBVR_LK' , + 'ALBVF' , 'LAKE' , 'ALBVF_LK' , + 'ALBNR' , 'LAKE' , 'ALBNR_LK' , + 'ALBNF' , 'LAKE' , 'ALBNF_LK' , + 'EMIS' , 'LAKE' , 'EMIS_LK' , + 'EVAPOUT' , 'LAKE' , + 'SUBLIM' , 'LAKE' , + 'RUNOFF' , 'LAKE' , 'RUNOFF_LK' , + 'SHOUT' , 'LAKE' , + 'HLATN' , 'LAKE' , + 'HLWUP' , 'LAKE' , 'HLWUP_LK' , + 'LWNDSRF' , 'LAKE' , + 'SWNDSRF' , 'LAKE' , + 'TST' , 'LAKE' , 'TST_LK' , + 'QST' , 'LAKE' , 'QST_LK' , + 'DELTS' , 'LAKE' , + 'DELQS' , 'LAKE' , + 'CHT' , 'LAKE' , + 'CQT' , 'LAKE' , + 'CMT' , 'LAKE' , + 'PS' , 'LAKE' , 'PS_LK' , + :: + tavg24_1d_lake_Nt.descr: 'Tile-space,Daily,Time-Averaged,Single-level,Lake Diagnostics', + tavg24_1d_lake_Nt.nbits: 12, + tavg24_1d_lake_Nt.template: '%y4%m2%d2_%h2%n2z.nc4' , + tavg24_1d_lake_Nt.mode: 'time-averaged' , + tavg24_1d_lake_Nt.format: 'CFIO', + tavg24_1d_lake_Nt.frequency: 240000 , + tavg24_1d_lake_Nt.ref_time: 000000 , + tavg24_1d_lake_Nt.fields: 'EVAPOUT' , 'LAKE' , + 'SUBLIM' , 'LAKE' , + 'RUNOFF' , 'LAKE' , 'RUNOFF_LK' , + 'SHOUT' , 'LAKE' , + 'HLATN' , 'LAKE' , + 'HLWUP' , 'LAKE' , 'HLWUP_LK' , + 'LWNDSRF' , 'LAKE' , + 'SWNDSRF' , 'LAKE' , + 'TST' , 'LAKE' , 'TST_LK' , + 'QST' , 'LAKE' , 'QST_LK' , + 'DELTS' , 'LAKE' , + 'DELQS' , 'LAKE' , + 'CHT' , 'LAKE' , + 'CQT' , 'LAKE' , + 'CMT' , 'LAKE' , + 'PS' , 'LAKE' , 'PS_LK' , + :: + tavg24_2d_glc_Nx.descr: '2d,Daily,Time-Averaged,Single-Level,Land Ice Diagnostics', tavg24_2d_glc_Nx.nbits: 12, tavg24_2d_glc_Nx.template: '%y4%m2%d2_%h2%n2z.nc4' , diff --git a/GEOSldas_App/GEOSldas_LDAS.rc b/GEOSldas_App/GEOSldas_LDAS.rc index 8ca6018c..e1ffb1d5 100644 --- a/GEOSldas_App/GEOSldas_LDAS.rc +++ b/GEOSldas_App/GEOSldas_LDAS.rc @@ -44,10 +44,10 @@ LSM_CHOICE: 1 # # land : 100 (non-glaciated land) # landice : 20 ( glaciated land) -# lake : 19 [not yet implemented] +# lake : 19 ( lakes) # # For example, include land and landice tiles as follows: -# TILE_TYPES: 100 20 +# TILE_TYPES: 100 19 20 # TILE_TYPES: 100 diff --git a/GEOSldas_App/ldas.py b/GEOSldas_App/ldas.py index 08b8456d..e72bf512 100644 --- a/GEOSldas_App/ldas.py +++ b/GEOSldas_App/ldas.py @@ -104,6 +104,7 @@ def __init__(self, cmdLineArgs): self.bcs_dir_landiceshared = '' self.tile_types = '' self.with_land = False + self.with_lake = False self.with_landice = False self.run_route = 0 self.with_issm = False @@ -232,10 +233,14 @@ def __init__(self, cmdLineArgs): self.tile_types = self.ExeInputs.get('TILE_TYPES',"100").split() if "100" in self.tile_types : - self.with_land = True - assert int(self.ExeInputs['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 "20" in self.tile_types : - self.with_landice = True + self.with_land = True + assert int(self.ExeInputs['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 "19" in self.tile_types : + self.with_lake = True + + if "20" in self.tile_types : + self.with_landice = True if self.with_landice == True: if int(self.ExeInputs.get('DO_ISSM'))==1: @@ -445,11 +450,15 @@ def __init__(self, cmdLineArgs): landpertRstFile=self.in_rstdir+'/'+tmpFile if ( os.path.isfile(landpertRstFile)) : self.has_geos_pert = True + if self.with_lake: + tmpFile=self.ExeInputs['RESTART_ID']+'.lake_internal_rst.'+y4m2d2_h2m2 + lakeRstFile=self.in_rstdir+'/'+tmpFile + assert os.path.isfile(lakeRstFile), 'lake_internal_rst file [%s] does not exist!' %(lakeRstFile) if self.with_landice: - tmpFile=self.ExeInputs['RESTART_ID']+'.landice_internal_rst.'+y4m2d2_h2m2 - landiceRstFile=self.in_rstdir+'/'+tmpFile - assert os.path.isfile(landiceRstFile), 'landice_internal_rst file [%s] does not exist!' %(landiceRstFile) + tmpFile=self.ExeInputs['RESTART_ID']+'.landice_internal_rst.'+y4m2d2_h2m2 + landiceRstFile=self.in_rstdir+'/'+tmpFile + assert os.path.isfile(landiceRstFile), 'landice_internal_rst file [%s] does not exist!' %(landiceRstFile) if self.run_route > 0: tmpFile=self.ExeInputs['RESTART_ID']+'.route_internal_rst.'+y4m2d2_h2m2 @@ -884,7 +893,7 @@ def createLnRstBc(self) : self.has_landassim_seed = True mk_outdir = self.exphome+'/'+exp_id+'/mk_restarts/' - if (RESTART_str in ['2', 'M'] and (self.with_land or self.with_landice)): + if (RESTART_str in ['2', 'M'] and (self.with_land or self.with_lake or self.with_landice)): bcs_path = self.ExeInputs['BCS_PATH'] while bcs_path[-1] == '/' : bcs_path = bcs_path[0:-1] bc_base = os.path.dirname(bcs_path) @@ -922,25 +931,27 @@ def createLnRstBc(self) : if self.with_land: catch_obj = catchANDcn(config_obj = config) catch_obj.remap() - if self.with_landice: - config['output']['surface']['remap_water'] = True - config['input']['surface']['zoom'] = '2' - landice_obj = other_restarts(config_obj = config) - landice_obj.remap() + if self.with_lake or self.with_landice: + config['output']['surface']['remap_water'] = True + config['input']['surface']['zoom'] = '2' + other_rst_obj = other_restarts(config_obj = config) + other_rst_obj.remap() #for ens in self.ensdirs : catchRstFile0 = '' vegdynRstFile0 = '' + lakeRstFile0 = '' landiceRstFile0 = '' issmRstFile0 = '' for iens in range(self.nens) : ensdir = self.ensdirs[iens] ensid = self.ensids[iens] - myCatchRst = myRstDir+'/'+self.catch +ensid +'_internal_rst' - myLandiceRst = myRstDir+'/'+ 'landice' +ensid +'_internal_rst' - myIssmRst = myRstDir+'/'+ 'issm' +ensid +'_internal_rst' - myVegRst = myRstDir+'/'+ 'vegdyn'+ensid +'_internal_rst' + myCatchRst = myRstDir+'/'+ self.catch +ensid +'_internal_rst' + myLakeRst = myRstDir+'/'+ 'lake' +ensid +'_internal_rst' + myLandiceRst = myRstDir+'/'+ 'landice' +ensid +'_internal_rst' + myIssmRst = myRstDir+'/'+ 'issm' +ensid +'_internal_rst' + myVegRst = myRstDir+'/'+ 'vegdyn' +ensid +'_internal_rst' myPertRst = myRstDir+'/'+ 'landpert' +ensid +'_internal_rst' myRouteRst = myRstDir+'/'+ 'route' +ensid +'_internal_rst' @@ -996,6 +1007,31 @@ def createLnRstBc(self) : else : vegdynRstFile = vegdynRstFile0 + lakeRstFile = '' + if self.with_lake : + if RESTART_str in ['1', '3'] : + lakeRstFile = rstpath+ensdir +'/'+ y4m2+'/'+self.ExeInputs['RESTART_ID']+'.'+'lake_internal_rst.'+y4m2d2_h2m2 + + if RESTART_str in ['2', 'M']: + lakeRstFile = glob.glob(self.exphome+'/'+exp_id+'/mk_restarts/*'+'lake_internal_rst.'+YYYYMMDD+'*')[0] + + if os.path.isfile(lakeRstFile) : + lakeLocal = self.rstdir+ensdir +'/'+ y4m2+'/'+self.ExeInputs['EXP_ID']+'.lake_internal_rst.'+y4m2d2_h2m2 + + if self.isZoomIn : + print ("Creating zoom-in of lake restart file... \n") + cmd=self.bindir + '/preprocess_ldas.x zoomin_lakerst '+ lakeRstFile +' ' + lakeLocal + ' '+ tmp_f2g_file.name + print ("cmd: " + cmd) + sp.call(shlex.split(cmd)) + else : + shutil.copy(lakeRstFile,lakeLocal) + + lakeRstFile = lakeLocal + if '0000' in ensdir : + lakeRstFile0 = lakeRstFile + else : + lakeRstFile = lakeRstFile0 + landiceRstFile = '' issmRstFile = '' if self.with_landice : @@ -1076,9 +1112,13 @@ def createLnRstBc(self) : os.symlink(catchRstFile, myCatchRst) os.symlink(vegdynRstFile, myVegRst) + if self.with_lake : + print("link lake restart: " + myLakeRst) + os.symlink(lakeRstFile, myLakeRst) + if self.with_landice : - print("link landice restart: " + myLandiceRst) - os.symlink(landiceRstFile, myLandiceRst) + print("link landice restart: " + myLandiceRst) + os.symlink(landiceRstFile, myLandiceRst) if self.run_route > 0 : print("link route restart: " + myRouteRst) @@ -1348,6 +1388,10 @@ def createRCFiles(self): rstval.append(self.catch) rstval.append('vegdyn') + if self.with_lake: + rstkey.append('LAKE') + rstval.append('lake') + if self.with_landice: rstkey.append('LANDICE') rstval.append('landice') diff --git a/GEOSldas_App/lenkf_j_template.py b/GEOSldas_App/lenkf_j_template.py index bd17e805..f5778f84 100644 --- a/GEOSldas_App/lenkf_j_template.py +++ b/GEOSldas_App/lenkf_j_template.py @@ -727,7 +727,8 @@ set THISDIR = $EXPDIR/output/$EXPDOMAIN/rs/$ENSDIR/Y${{eYEAR}}/M${{eMON}}/ if (! -e $THISDIR ) mkdir -p $THISDIR - set rstfs = (${{LANDMODEL}} 'landice' 'route' 'issm') + set rstfs = (${{LANDMODEL}} 'lake' 'landice' 'route' 'issm') + foreach rstf ( $rstfs ) 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}} diff --git a/GEOSldas_App/preprocess_ldas.F90 b/GEOSldas_App/preprocess_ldas.F90 index 67e3e9ef..099adf0a 100644 --- a/GEOSldas_App/preprocess_ldas.F90 +++ b/GEOSldas_App/preprocess_ldas.F90 @@ -118,6 +118,12 @@ program main call createZoominRestart(f2g_file, orig_catch, new_catch, 100) + else if (trim(option) == "zoomin_lakerst") then + orig_r = trim(arg1) + new_r = trim(arg2) + f2g_file = trim(arg3) + call createZoominRestart(f2g_file, orig_r, new_r, 19) + else if (trim(option) == "zoomin_landicerst") then orig_r = trim(arg1) diff --git a/GEOSmetforce_GridComp/GEOS_MetforceGridComp.F90 b/GEOSmetforce_GridComp/GEOS_MetforceGridComp.F90 index 2dc99129..d73ea488 100644 --- a/GEOSmetforce_GridComp/GEOS_MetforceGridComp.F90 +++ b/GEOSmetforce_GridComp/GEOS_MetforceGridComp.F90 @@ -86,7 +86,7 @@ module GEOS_MetforceGridCompMod 'DRPAR ', 'DFPAR ', 'UU ', & 'DZ ', 'DRNIR ', 'DFNIR ', & 'DRUVR ', 'DFUVR ', 'PLS '] - integer :: NUM_LAND_TILE, NUM_LANDICE_TILE + integer :: NUM_LAND_TILE, NUM_LAKE_TILE, NUM_LANDICE_TILE contains !BOP @@ -168,6 +168,15 @@ subroutine SetServices(gc, rc) ) VERIFY_(status) + ! phase 5: to lake + call MAPL_GridCompSetEntryPoint( & + gc, & + ESMF_METHOD_RUN, & + DistributeForcingToLake, & + rc=status & + ) + VERIFY_(status) + call MAPL_GridCompSetEntryPoint( & gc, & ESMF_METHOD_FINALIZE, & @@ -662,6 +671,7 @@ subroutine Initialize(gc, import, export, clock, rc) VERIFY_(status) NUM_LAND_TILE = count(tiletype == MAPL_LAND) + NUM_LAKE_TILE = count(tiletype == MAPL_LAKE) NUM_LANDICE_TILE = count(tiletype == MAPL_LANDICE) call MAPL_GetResource(MAPL, grid_type,Label="GEOSldas.GRID_TYPE:",RC=STATUS) @@ -1353,8 +1363,8 @@ subroutine DistributeForcingToLandIce(gc, export, landice_import, clock, rc) RETURN_(ESMF_SUCCESS) endif - i1 = NUM_LAND_TILE + 1 - i2 = NUM_LAND_TILE + NUM_LANDICE_TILE + i1 = NUM_LAND_TILE + NUM_LAKE_TILE + 1 + i2 = NUM_LAND_TILE + NUM_LAKE_TILE + NUM_LANDICE_TILE ! Get MAPL obj call MAPL_GetObjectFromGC(gc, MAPL, _RC) call MAPL_GetResource ( MAPL, AEROSOL_DEPOSITION, Label="AEROSOL_DEPOSITION:", DEFAULT=1, _RC) @@ -1404,6 +1414,107 @@ subroutine DistributeForcingToLandIce(gc, export, landice_import, clock, rc) end subroutine DistributeForcingToLandIce + subroutine DistributeForcingToLake(gc, export, lake_import, clock, rc) + + type(ESMF_GridComp), intent(inout) :: gc ! Gridded component + type(ESMF_State), intent(inout) :: export ! Export state + type(ESMF_State), intent(inout) :: lake_import ! Import state + type(ESMF_Clock), intent(inout) :: clock ! The clock + integer, optional, intent( out) :: rc ! Error code + + integer :: i1, i2, status + real, pointer :: out1d(:), in1d(:), tmp(:) + real, allocatable :: tmpreal(:) + character(len=ESMF_MAXSTR) :: Iam + + Iam = "metForce::DistributeForcingToLake" + + if (NUM_LAKE_TILE == 0) then + RETURN_(ESMF_SUCCESS) + endif + + ! Active forcing tile-space order is LAND -> LAKE -> LANDICE. + i1 = NUM_LAND_TILE + 1 + i2 = NUM_LAND_TILE + NUM_LAKE_TILE + + call MAPL_GetPointer(export, out1d, 'Tair', _RC) + call MAPL_GetPointer(lake_import, in1d, 'TA', _RC) + in1d = out1d(i1:i2) + + call MAPL_GetPointer(export, out1d, 'Qair', _RC) + call MAPL_GetPointer(lake_import, in1d, 'QA', _RC) + in1d = out1d(i1:i2) + + call MAPL_GetPointer(export, out1d, 'Psurf', _RC) + call MAPL_GetPointer(lake_import, in1d, 'PS', _RC) + in1d = out1d(i1:i2) + + call MAPL_GetPointer(export, out1d, 'Rainf_C', _RC) + call MAPL_GetPointer(lake_import, in1d, 'PCU', _RC) + in1d = out1d(i1:i2) + + call MAPL_GetPointer(export, out1d, 'Snowf', _RC) + call MAPL_GetPointer(lake_import, in1d, 'SNO', _RC) + in1d = out1d(i1:i2) + + call MAPL_GetPointer(export, out1d, 'LWdown', _RC) + call MAPL_GetPointer(lake_import, in1d, 'LWDNSRF', _RC) + in1d = out1d(i1:i2) + + call MAPL_GetPointer(export, out1d, 'PARdrct', _RC) + call MAPL_GetPointer(lake_import, in1d, 'DRPAR', _RC) + in1d = out1d(i1:i2) + + call MAPL_GetPointer(export, out1d, 'PARdffs', _RC) + call MAPL_GetPointer(lake_import, in1d, 'DFPAR', _RC) + in1d = out1d(i1:i2) + + call MAPL_GetPointer(export, out1d, 'Wind', _RC) + call MAPL_GetPointer(lake_import, in1d, 'UU', _RC) + in1d = out1d(i1:i2) + + call MAPL_GetPointer(lake_import, in1d, 'UWINDLMTILE', _RC) + in1d = out1d(i1:i2) + + call MAPL_GetPointer(lake_import, in1d, 'VWINDLMTILE', _RC) + in1d = 0.0 + + call MAPL_GetPointer(export, out1d, 'RefH', _RC) + call MAPL_GetPointer(lake_import, in1d, 'DZ', _RC) + in1d = out1d(i1:i2) + + call MAPL_GetPointer(export, out1d, 'Rainf', _RC) + call MAPL_GetPointer(export, tmp, 'Rainf_C', _RC) + call MAPL_GetPointer(lake_import, in1d, 'PLS', _RC) + in1d = out1d(i1:i2) - tmp(i1:i2) + + ! Shortwave split, following the existing landice convention. + allocate(tmpreal(NUM_LAKE_TILE), stat=status) + VERIFY_(status) + + call MAPL_GetPointer(export, out1d, 'SWdown', _RC) + tmpreal = 0.5 * out1d(i1:i2) + + call MAPL_GetPointer(lake_import, in1d, 'DRNIR', _RC) + in1d = 0.5 * tmpreal + + call MAPL_GetPointer(lake_import, in1d, 'DFNIR', _RC) + in1d = 0.5 * tmpreal + + call MAPL_GetPointer(export, out1d, 'PARdrct', _RC) + call MAPL_GetPointer(lake_import, in1d, 'DRUVR', _RC) + in1d = 0.5 * tmpreal - out1d(i1:i2) + + call MAPL_GetPointer(export, out1d, 'PARdffs', _RC) + call MAPL_GetPointer(lake_import, in1d, 'DFUVR', _RC) + in1d = 0.5 * tmpreal - out1d(i1:i2) + + deallocate(tmpreal) + + RETURN_(ESMF_SUCCESS) + + end subroutine DistributeForcingToLake + !BOP ! !IROTUINE: Finalize -- Finalize method for LDAS GridComp