From a9a6582161410b43205ebdbc167be0ad70d5ee74 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Tue, 22 Aug 2023 15:46:01 +0200 Subject: [PATCH 01/71] doc: remove mentions of `TSMP_pdaf` in the documentation remove completely the outdated `branches.md` --- README.md | 2 -- .../build_tsmp/build_examples_tsmppdaf.md | 11 ++----- doc/content/feat_branch/branches.md | 30 ------------------- doc/content/introduction.md | 5 +++- doc/index.rst | 1 - 5 files changed, 6 insertions(+), 43 deletions(-) delete mode 100644 doc/content/feat_branch/branches.md diff --git a/README.md b/README.md index 8c2db9579..fed084a9e 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,6 @@ Please see [getting started section](https://hpscterrsys.github.io/TSMP/content/ ## TSMP version history The model components used in TSMP are OASIS3-MCT v2, COSMO v5.01, CLM v3.5, ParFlow 3.2 for TSMP versions v1.2.1, v1.2.2 and v1.2.3, ParFlow 3.9 for version v1.3.3 and ParFlow 3.12 for version v1.4.0. TSMP supports ParFlow 3.7 onwards from version v1.3.3 onward. -Those who need to work with ParFlow 3.2, should use the branch `TSMP_pdaf`. - ## Citing TSMP If you use TSMP in a publication, please cite the these papers that describe the model's basic functionalities: diff --git a/doc/content/build_tsmp/build_examples_tsmppdaf.md b/doc/content/build_tsmp/build_examples_tsmppdaf.md index 260dd60bd..d0f61770c 100644 --- a/doc/content/build_tsmp/build_examples_tsmppdaf.md +++ b/doc/content/build_tsmp/build_examples_tsmppdaf.md @@ -46,7 +46,7 @@ avoid making a backup with `-WXYZ` option set to \"`build`\". Build commands for different machines. Remarks: -- For building TSMP-PDAF with DA, use branch `TSMP_pdaf` +- For building TSMP-PDAF with DA, use branch `master` - Parflow-3.9 is supported using version tag `3.1.0MCTPDAF`. For older versions, use `3.0.0MCTPDAF` (>=3.2, <3.7), `1.1.0MCTPDAF` (<3.2). @@ -58,19 +58,12 @@ Remarks: ./build_tsmp.ksh -m JUWELS -c clm-pfl -v 3.1.0MCTPDAF -O Intel -#### Remote build #### - -``` shell - ./build_tsmp_remote -m JUWELS -v 3.1.0MCTPDAF -O Intel -c clm-pfl -branch TSMP_pdaf -host juwels & - ./build_tsmp_remote -m JURECA -v 3.1.0MCTPDAF -O Intel -c clm-pfl -branch TSMP_pdaf -host jureca & -``` - ### Component Models ### The build commands were last tested for these component models - **TSMP** - - current revision of branch `TSMP_pdaf` from + - current revision of branch `master` from - **clm3\_5** diff --git a/doc/content/feat_branch/branches.md b/doc/content/feat_branch/branches.md deleted file mode 100644 index 257b0d0d6..000000000 --- a/doc/content/feat_branch/branches.md +++ /dev/null @@ -1,30 +0,0 @@ -# TSMP-PDAF related branches # - - -## Main branches ## - -| Branch | Information | -|---------------|---------------------------------------| -| `master` | current main branch | -| `TSMP_pdaf` | current main TSMP-PDAF branch | - -## Development branches ## - -| Branch | Information | -|------------------------|-----------------------------------------------------------------------| -| `pdaf_lenkf_fix` | TSMP-PDAF developments by Ching | -| `for2131-*` | Developments from FOR2131 | -| `for2131-supermuc` | @bschalge bash-scripts for SuperMUC plus developments | -| `for2131-natascha` | @nbrandhorst developments (cleaned up) | -| `for2131-natascha-dev` | @nbrandhorst developments without cleanup | -| `pdaf-cmem` | @spoll and @lvshaoning | -| `clm5-coupling` | @lstrebel | -| `doc` | documentation by @tschruff | -| `coupling-cos-pdaf` | | -| `agrocluster-dorina` | @dbaatz, freezed out earlier version of TSMP | -| `couple_icon` | | -| `TSMP_pdaf-crns` | Branch by @cphung for FOR2131 | -| `TSMP_pdaf-clm5` | @lstrebel, CLM5-PDAF, [Starting Instructions](./clm5-pdaf_starter.md) | - - -`*-pdaf`: Branches that are up-to-date with branch `TSMP_pdaf`. diff --git a/doc/content/introduction.md b/doc/content/introduction.md index 9e5c614a7..5a9580b5a 100644 --- a/doc/content/introduction.md +++ b/doc/content/introduction.md @@ -18,7 +18,10 @@ TSMP development has been driven by groups within the [Center for High-Performan ## TSMP-PDAF -TSMP-PDAF describes the branches of the TSMP-repositories usable for TSMP simulations coupled with the Parallel Data Assimilation Framework ([PDAF](http://pdaf.awi.de/trac/wiki)). +TSMP-PDAF describes the build commands of TSMP that can introduce + ensemble TSMP simulations and TSMP simulations coupled with PDAF, the + Parallel Data Assimilation Framework + ([PDAF](http://pdaf.awi.de/trac/wiki)). ## Web Resources diff --git a/doc/index.rst b/doc/index.rst index 22497f165..fbf072589 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -59,7 +59,6 @@ Welcome to TSMP documentation! :maxdepth: 3 :caption: Feature Branches: - content/feat_branch/branches.md content/feat_branch/clm5-pdaf_starter.md .. toctree:: From e245c427d9389b91c27a2bcb74ce95c0ddf9cb6a Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Wed, 30 Aug 2023 15:21:09 +0200 Subject: [PATCH 02/71] TSMP-PDAF: update introduction sentence --- doc/content/introduction.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/content/introduction.md b/doc/content/introduction.md index 5a9580b5a..8cc6d2079 100644 --- a/doc/content/introduction.md +++ b/doc/content/introduction.md @@ -18,11 +18,15 @@ TSMP development has been driven by groups within the [Center for High-Performan ## TSMP-PDAF -TSMP-PDAF describes the build commands of TSMP that can introduce - ensemble TSMP simulations and TSMP simulations coupled with PDAF, the +TSMP-PDAF describes the build commands of TSMP that can introduce data + assimilation for an ensemble of TSMP simulations using PDAF, the Parallel Data Assimilation Framework ([PDAF](http://pdaf.awi.de/trac/wiki)). +Note that TSMP-PDAF does not follow the multiple program multiple data +(MPMD) paradigm that TSMP does. Instead component models are loaded +into a single executable as libraries (@Kurtz2016). + ## Web Resources The main remote repository of TSMP is located on Github: From 9ceb0a5a5859f6b32735e22a16a7667904cc4613 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Wed, 6 Sep 2023 13:21:17 +0200 Subject: [PATCH 03/71] doc: performance remark about `da_interval`, `delt_obs` usage found by Bastian Waldowski --- doc/content/setup_tsmp/input_cmd.md | 4 ++++ doc/content/setup_tsmp/input_enkfpf.md | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/doc/content/setup_tsmp/input_cmd.md b/doc/content/setup_tsmp/input_cmd.md index c0568cabb..97f30bc79 100644 --- a/doc/content/setup_tsmp/input_cmd.md +++ b/doc/content/setup_tsmp/input_cmd.md @@ -51,6 +51,10 @@ the Gaussian probability distribution of the measurements. [`[DA]da_interval`](./input_enkfpf.md#dada_interval)) that are forward computed, before assimilation is actually performed. +In general, `delt_obs` should be as small as possible, in order to +avoid performance loss. See remark in +[`[DA]da_interval`](./input_enkfpf.md#dada_interval). + ## screen ## `screen` (integer) Control verbosity of PDAF diff --git a/doc/content/setup_tsmp/input_enkfpf.md b/doc/content/setup_tsmp/input_enkfpf.md index 8687b2f8d..56dc110e0 100644 --- a/doc/content/setup_tsmp/input_enkfpf.md +++ b/doc/content/setup_tsmp/input_enkfpf.md @@ -425,6 +425,14 @@ assimilation will be applied each 12 hours. simulated for 1 24-hour-step between data assimilation times. So an assimilation will be applied each 24 hours. +**Remark**: Performance analysis has shown that for an assimilation +every `n` hours, it is beneficial to specify `da_interval=n`, +`delt_obs 1`, instead of `da_interval=1`, `delt_obs n`. + +The suspected reason is that all tasks in the parallelization are +stopped and restarted after `da_interval` hours. In general, the +number of these stops and restarts should be minimized. + ### DA:stat_dumpoffset ### `DA:stat_dumpoffset`: File number offset for the data assimilation From 079c6ccbb2b08d609fa51454f000f251813927d0 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Thu, 7 Sep 2023 09:42:48 +0200 Subject: [PATCH 04/71] doc: Updating the remark about maximizing `da_interval` --- doc/content/setup_tsmp/input_enkfpf.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/content/setup_tsmp/input_enkfpf.md b/doc/content/setup_tsmp/input_enkfpf.md index 56dc110e0..cc6b5d9e4 100644 --- a/doc/content/setup_tsmp/input_enkfpf.md +++ b/doc/content/setup_tsmp/input_enkfpf.md @@ -429,9 +429,11 @@ assimilation will be applied each 24 hours. every `n` hours, it is beneficial to specify `da_interval=n`, `delt_obs 1`, instead of `da_interval=1`, `delt_obs n`. -The suspected reason is that all tasks in the parallelization are -stopped and restarted after `da_interval` hours. In general, the -number of these stops and restarts should be minimized. +In general, it is beneficial to set `da_interval` as large as possible +for a given setup. One reason is that after a simulation time of +`da_interval`, the routines `assimilate_pdaf` and `update_tsmp` are +called, each time assembling the state vectors and calling the PDAF +library. Minimizing the number of these calls reduces compute time. ### DA:stat_dumpoffset ### From c37cb831a5c67149b4c3eb9603b541f83a396ba4 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Thu, 7 Sep 2023 09:48:01 +0200 Subject: [PATCH 05/71] doc: Updating the remark about maximizing `da_interval` --- doc/content/setup_tsmp/input_enkfpf.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/content/setup_tsmp/input_enkfpf.md b/doc/content/setup_tsmp/input_enkfpf.md index cc6b5d9e4..c314bbb25 100644 --- a/doc/content/setup_tsmp/input_enkfpf.md +++ b/doc/content/setup_tsmp/input_enkfpf.md @@ -430,10 +430,11 @@ every `n` hours, it is beneficial to specify `da_interval=n`, `delt_obs 1`, instead of `da_interval=1`, `delt_obs n`. In general, it is beneficial to set `da_interval` as large as possible -for a given setup. One reason is that after a simulation time of +for a given setup. One reason is that after each simulation time of `da_interval`, the routines `assimilate_pdaf` and `update_tsmp` are -called, each time assembling the state vectors and calling the PDAF -library. Minimizing the number of these calls reduces compute time. +called, assembling EnKF state vectors and calling the PDAF +library. Maximizing `da_interal`, minimizes the number of these calls +and thus reduces compute time. ### DA:stat_dumpoffset ### From 05505cc13f93e2de6208f8ae5bbe8183a0990140 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Thu, 5 Oct 2023 12:58:25 +0200 Subject: [PATCH 06/71] intf_DA: document observation input `gw_indicator` used for input `gwmasking=2` in assembling the mixed state vector --- .../intf_DA/pdaf/framework/mod_read_obs.F90 | 23 +++++++++++----- .../intf_DA/pdaf/model/parflow/enkf_parflow.c | 26 ++++++++++++++++--- doc/content/setup_tsmp/input_enkfpf.md | 7 ++--- doc/content/setup_tsmp/input_obs.md | 13 ++++++++++ 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/bldsva/intf_DA/pdaf/framework/mod_read_obs.F90 b/bldsva/intf_DA/pdaf/framework/mod_read_obs.F90 index 8f1d7f9a8..7d55d7539 100755 --- a/bldsva/intf_DA/pdaf/framework/mod_read_obs.F90 +++ b/bldsva/intf_DA/pdaf/framework/mod_read_obs.F90 @@ -362,16 +362,24 @@ end subroutine read_obs_nc !> @param[out] no_obs Number of observations !> @details !> This subroutine reads the observation index arrays for usage in - !> the C-code for groundwater masking. + !> the enkf_parflow.c for groundwater masking. + !> + !> Only used, when ParFlow is one of the component models. !> !> Index is for ParFlow-type observations !> + !> Only used in `enkf_parflow.c` with `pf_gwmasking=2`. + !> + !> Outputs: + !> -------- + !> Number of observations in `no_obs`. + !> !> Index arrays that are set from NetCDF observation file: - !> - tidx_obs - !> - xidx_obs - !> - yidx_obs - !> - zidx_obs - !> - ind_obs + !> - `tidx_obs` + !> - `xidx_obs` + !> - `yidx_obs` + !> - `zidx_obs` + !> - `ind_obs` subroutine get_obsindex_currentobsfile(no_obs) bind(c,name='get_obsindex_currentobsfile') use mod_parallel_model, only: tcycle USE mod_assimilation, only: obs_filename @@ -465,12 +473,15 @@ end subroutine clean_obs_nc !> @details !> This subroutine deallocates the observation index arrays used in !> subroutine `get_obsindex_currentobsfile`. + !> + !> Only used in `enkf_parflow.c` with `pf_gwmasking=2`. subroutine clean_obs_pf() bind(c,name='clean_obs_pf') implicit none if(allocated(idx_obs_pf))deallocate(idx_obs_pf) if(allocated(x_idx_obs_pf))deallocate(x_idx_obs_pf) if(allocated(y_idx_obs_pf))deallocate(y_idx_obs_pf) if(allocated(z_idx_obs_pf))deallocate(z_idx_obs_pf) + if(allocated(ind_obs_pf)) deallocate(ind_obs_pf) end subroutine clean_obs_pf !> @author Wolfgang Kurtz, Guowei He diff --git a/bldsva/intf_DA/pdaf/model/parflow/enkf_parflow.c b/bldsva/intf_DA/pdaf/model/parflow/enkf_parflow.c index 8bd2e0ffb..25a01cd5b 100644 --- a/bldsva/intf_DA/pdaf/model/parflow/enkf_parflow.c +++ b/bldsva/intf_DA/pdaf/model/parflow/enkf_parflow.c @@ -565,19 +565,28 @@ void enkfparflowadvance(int tcycle, double current_time, double dt) if(pf_gwmasking == 2){ int no_obs,haveobs,tmpidx; MPI_Comm comm_couple_c = MPI_Comm_f2c(comm_couple); + + /* 1. Overwrite pressure with soil water content in + unsaturated part of `pf_statevec` */ + /* 2. Set saturation switch `subvec_gwind` */ PF2ENKF(saturation_out, subvec_sat); PF2ENKF(porosity_out, subvec_porosity); MPI_Allreduce(subvec_sat,subvec_mean,enkf_subvecsize,MPI_DOUBLE,MPI_SUM,comm_couple_c); - for(i=0;i=origin_local[0]) && (xidx_obs[i]<(origin_local[0]+nx_local))){ @@ -587,15 +596,26 @@ void enkfparflowadvance(int tcycle, double current_time, double dt) } } } + + /* If observation exists in subgrid AND observation is + pressure observation, set the column below the + observation to pressure in the state vector */ if(haveobs && ind_obs[i]==1){ for(j=0;j<=(zidx_obs[i]-origin_local[2]);j++){ + /* TODO: This code will only set the column INSIDE + the local domain / subgrid. In prinicpal, the + subgrid could be divided in z-direction and a + deeper subgrid could still contain soil water + content values below the pressure measurement. */ tmpidx = nx_local*ny_local*j + nx_local*(yidx_obs[i]-origin_local[1]) + (xidx_obs[i]-origin_local[0]); subvec_gwind[tmpidx] = 1.0; pf_statevec[tmpidx] = subvec_p[tmpidx]; } } } + if(task_id == 1 && pf_printgwmask == 1) enkf_printstatistics_pfb(subvec_gwind,"gwind_corrected",tstartcycle + stat_dumpoffset,outdir,3); + clean_obs_pf(); } } diff --git a/doc/content/setup_tsmp/input_enkfpf.md b/doc/content/setup_tsmp/input_enkfpf.md index c314bbb25..a65de4033 100644 --- a/doc/content/setup_tsmp/input_enkfpf.md +++ b/doc/content/setup_tsmp/input_enkfpf.md @@ -118,8 +118,9 @@ Must match with the specifications in the `*.pfidb` input. `PF:updateflag`: (integer) Type of state vector update in ParFlow. - 1: Assimilation of pressure data. State vector consists of - pressure values and is directly updated with pressure - observations. + pressure values (groundwater masking or a mixed state vector is + introduced through `PF:gwmasking`) and is directly updated with + pressure observations. - 2: Assimilation of soil moisture data. State vector consists of soil moisture content values and is updated with soil moisture @@ -133,7 +134,7 @@ Must match with the specifications in the `*.pfidb` input. ### PF:gwmasking ### `PF:gwmasking`: (integer) Groundwater masking for assimilation of -pressure data (updateflag=1) in ParFlow. +pressure data (`updateflag=1`) in ParFlow. - 0: No groundwater masking. diff --git a/doc/content/setup_tsmp/input_obs.md b/doc/content/setup_tsmp/input_obs.md index 70996eaa0..b42140093 100644 --- a/doc/content/setup_tsmp/input_obs.md +++ b/doc/content/setup_tsmp/input_obs.md @@ -82,6 +82,19 @@ where `nx` and `ny` are the number of grid cells in x- and y-direction respectively and `ix`, `iy` and `iz` are the positions of the observation in x-, y- and z-direction. +#### gw_indicator #### + +`gw_indicator`: (integer) Indicates if the observation is a pressure +observation. Values: `0`, `1`. + +- `0`: Soil water content observation (i.e. no pressure) +- `1`: Pressure observation + +Used only for `gwmasking=2` (mixed state vector). When pressure +observations are present, the state vector variable at the observation +location and below is set to pressure (it may have been set to soil +water content before). + #### ix_interp_d #### `ix_interp_d`: (real) Offset of the correct observation locations From 6a4280e6fa11a4d3960435b377b8d6ce2dc65519 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Thu, 5 Oct 2023 14:02:48 +0200 Subject: [PATCH 07/71] intf_DA: remove `double *dat;` from `enkf_parflow.c` Clearer, equally performant code by index handling. --- .../intf_DA/pdaf/model/parflow/enkf_parflow.c | 150 +++++++++--------- 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/parflow/enkf_parflow.c b/bldsva/intf_DA/pdaf/model/parflow/enkf_parflow.c index 25a01cd5b..b0d75ff99 100644 --- a/bldsva/intf_DA/pdaf/model/parflow/enkf_parflow.c +++ b/bldsva/intf_DA/pdaf/model/parflow/enkf_parflow.c @@ -1359,33 +1359,25 @@ void update_parflow () { int i,j,k; VectorUpdateCommHandle *handle; - double *dat; int do_pupd=0; - /* print updated ensemble */ - if(pf_updateflag == 3){ - dat = &pf_statevec[enkf_subvecsize]; - }else{ - dat = &pf_statevec[0]; - } - /* state damping */ if(pf_updateflag == 1){ if(pf_gwmasking == 0){ - for(i=0;i Date: Mon, 9 Oct 2023 17:31:08 +0200 Subject: [PATCH 08/71] typo fix --- bldsva/intf_DA/pdaf/model/parflow/enkf_parflow.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bldsva/intf_DA/pdaf/model/parflow/enkf_parflow.c b/bldsva/intf_DA/pdaf/model/parflow/enkf_parflow.c index b0d75ff99..56de457db 100644 --- a/bldsva/intf_DA/pdaf/model/parflow/enkf_parflow.c +++ b/bldsva/intf_DA/pdaf/model/parflow/enkf_parflow.c @@ -1468,7 +1468,7 @@ void update_parflow () { } /* print updated porosity values */ - if(pf_paramprintensemble) enkf_printstatistics_pfb(%pf_statevec[ioff], "update.param.poro", tstartcycle + stat_dumpoffset,pfoutfile_ens,3); + if(pf_paramprintensemble) enkf_printstatistics_pfb(&pf_statevec[ioff], "update.param.poro", tstartcycle + stat_dumpoffset,pfoutfile_ens,3); } From 757517f6dd323feccff156de5d81c309eed48e98 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Tue, 10 Oct 2023 14:37:42 +0200 Subject: [PATCH 09/71] documentation update --- bldsva/intf_DA/pdaf/framework/init_pdaf.F90 | 10 ++--- .../intf_DA/pdaf/model/parflow/enkf_parflow.c | 44 +++++++++++++++++++ 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/bldsva/intf_DA/pdaf/framework/init_pdaf.F90 b/bldsva/intf_DA/pdaf/framework/init_pdaf.F90 index 58997524c..0a3982e19 100644 --- a/bldsva/intf_DA/pdaf/framework/init_pdaf.F90 +++ b/bldsva/intf_DA/pdaf/framework/init_pdaf.F90 @@ -242,12 +242,12 @@ SUBROUTINE init_pdaf() if (screen > 2) then print *,"" - print *, "Parflow component, setting correct dim_state_p and dim_state" + print *, "TSMP-PDAF mype(w)=", mype_world, ": Parflow component, setting correct dim_state_p and dim_state" end if else if (screen > 2) then print *,"" - print *, "CLM component, setting dummy dim_state_p and dim_state" + print *, "TSMP-PDAF mype(w)=", mype_world, ": CLM component, setting dummy dim_state_p and dim_state" end if dim_state_p = 1 ! Local state dimension @@ -263,7 +263,7 @@ SUBROUTINE init_pdaf() dim_state_p = clm_statevecsize if (screen > 2) then - print *,"CLM: dim_state_p is ",dim_state_p + print *,"TSMP-PDAF mype(w)=", mype_world, ": CLM: dim_state_p is ",dim_state_p end if end if #endif @@ -272,7 +272,7 @@ SUBROUTINE init_pdaf() allocate(dim_state_p_count(npes_model)) call MPI_Gather(dim_state_p, 1, MPI_INTEGER, dim_state_p_count, 1, MPI_INTEGER, 0, comm_model, ierror) - if (mype_model == 0 .and. screen > 2) print *, "init_pdaf: dim_state_p_count in modified: ", dim_state_p_count + if (mype_model == 0 .and. screen > 2) print *, "TSMP-PDAF mype(w)=", mype_world, ": init_pdaf: dim_state_p_count in modified: ", dim_state_p_count IF (allocated(dim_state_p_stride)) deallocate(dim_state_p_stride) allocate(dim_state_p_stride(npes_model)) do i = 1, npes_model @@ -281,7 +281,7 @@ SUBROUTINE init_pdaf() dim_state_p_stride(i) = dim_state_p_count(j) + dim_state_p_stride(i) end do end do - if (mype_model == 0 .and. screen > 2) print *, "init_pdaf: dim_state_p_stride in modified: ", dim_state_p_stride + if (mype_model == 0 .and. screen > 2) print *, "TSMP-PDAF mype(w)=", mype_world, ": init_pdaf: dim_state_p_stride in modified: ", dim_state_p_stride if (mype_model == 0) then dim_state = sum(dim_state_p_count) diff --git a/bldsva/intf_DA/pdaf/model/parflow/enkf_parflow.c b/bldsva/intf_DA/pdaf/model/parflow/enkf_parflow.c index 56de457db..6d1915854 100644 --- a/bldsva/intf_DA/pdaf/model/parflow/enkf_parflow.c +++ b/bldsva/intf_DA/pdaf/model/parflow/enkf_parflow.c @@ -913,29 +913,73 @@ int enkf_getsubvectorsize(Grid *grid) { return (out); } +/*-------------------------------------------------------------------------*/ +/** + @author Wolfgang Kurtz, Guowei He, Mukund Pondkule + @brief Populate C-array (part of state vector) from ParFlow type Vector. + @param pf_vector ParFlow Vector containing data. + @param enkf_subvec C-array to be populated. + + Remark: + ------- + + `pf_vector` needs to be extracted from the ParFlow problem. + + Example 1: `pf_vector` extracted as output of `AdvanceRichards` and + after `InitVectorUpdate` and `FinalizeVectorUpdate`, as is the case + for pressures, saturation and porosity. + + Example 2: `pf_vector` extracted as `ProblemDataPermeabilityX` or + similar. + + Workflow: + --------- + 1. Obtain the grid information from ParFlow Vector. + 2. Iterate over the subgrids of `grid` + 3. Get SubvectorData corresponding to subgrid + 4. Populate `enkf_subvec` with SubvectorData + */ +/*--------------------------------------------------------------------------*/ void PF2ENKF(Vector *pf_vector, double *enkf_subvec) { + /* Obtain grid information from `pf_vector` */ Grid *grid = VectorGrid(pf_vector); int sg; + /* Iterate over subgrids */ ForSubgridI(sg, GridSubgrids(grid)) { + /* Subgrid instance */ Subgrid *subgrid = GridSubgrid(grid, sg); + /* Bottom-lower-left corner of subgrid */ int ix = SubgridIX(subgrid); int iy = SubgridIY(subgrid); int iz = SubgridIZ(subgrid); + /* Size of subgrid */ int nx = SubgridNX(subgrid); int ny = SubgridNY(subgrid); int nz = SubgridNZ(subgrid); + /* if (screen_wrapper > 2) { */ + /* printf("TSMP-PDAF-WRAPPER mype(w)=%d: sg, ix, iy, iz, nx, ny, nz = %d, %d, %d, %d, %d, %d, %d, \n", mype_world, sg, ix, iy, iz, nx, ny, nz); */ + /* } */ + /* (1) Access the Subvector inside `pf_vector` that + corresponds to grid `sg`, (2) Access data array + from this Subvector instance */ Subvector *subvector = VectorSubvector(pf_vector, sg); double *subvector_data = SubvectorData(subvector); + /* Iterate subgrid's cells */ int i, j, k; int counter = 0; + /* TODO: `counter` is reset to zero for each + subgrid-iteration. However, for multiple subgrids + in the loop this would overwrite the values in + enkf_subvec. So, this code is possibly only working + for a single subgrid. */ for (k = iz; k < iz + nz; k++) { for (j = iy; j < iy + ny; j++) { From 70e019f93174f15516e253253bcff71da05b89b5 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Tue, 10 Oct 2023 15:23:03 +0200 Subject: [PATCH 10/71] doc: better documentation of LEnKF command line input --- doc/content/setup_tsmp/input_cmd.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/doc/content/setup_tsmp/input_cmd.md b/doc/content/setup_tsmp/input_cmd.md index 97f30bc79..cc00a7353 100644 --- a/doc/content/setup_tsmp/input_cmd.md +++ b/doc/content/setup_tsmp/input_cmd.md @@ -188,14 +188,21 @@ will be performed every assimilation cycle with an observation error of ### Command Line Example: LENKF ### -`local_range` (real) set localization radius (cut-off-radius) +`local_range` (real) set localization radius (Cut-off-radius) - 0.0 by default - any positive value should work. -`srange` (integer): the support radius of the localization -- the localization weight radius; support range for 5th-order - polynomial -- by default set to `local_range` +`srange` (integer): the support radius of the localization, default: +equal to `local_range`. Usage in `PDAF_local_weight.F90` +- `locweight == 0`: `srange` is not used +- `locweight == 1`: `srange` is distance with weight `1/e`, for + avoiding sharp cut-off, `local_range` should be significantly larger + than `srange` +- `locweight == 2`: `srange` is the support range for 5th-order + polynomial, can safely be chosen the same as `local_range`, + parametrization change of the 5th-order polynomial is at `srange/2`, + see + . `locweight` (integer): set weight function for localization, - default=0 From af1e967bf6897adf72fb5886a5e965650b3eb13b Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Tue, 10 Oct 2023 16:27:36 +0200 Subject: [PATCH 11/71] doc: more documentation and reference for `HCP_TRUNCATE_SAT` --- doc/content/build_tsmp/build_environment_variables.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/content/build_tsmp/build_environment_variables.md b/doc/content/build_tsmp/build_environment_variables.md index eca011d3c..6b3915354 100644 --- a/doc/content/build_tsmp/build_environment_variables.md +++ b/doc/content/build_tsmp/build_environment_variables.md @@ -75,8 +75,12 @@ version 5 of CLM. ## HCP_TRUNCATE_SAT ## -If `HCP_TRUNCAT_SAt` is defined, the saturation truncation in -`problem_saturationtopressure.c` reads +Reference for the saturation truncation in Hung et al +(, Equation 11) + +If `HCP_TRUNCATE_SAT` is defined, the saturation truncation in +function `SaturationToPressure` (file +`problem_saturationtopressure.c`) reads ``` c++ if(psdat[ips] <= (s_res + 0.003) ) psdat[ips] = s_res + 0.003; From e35aba3951fc4d6c463e71c8f6641690b7522e7a Mon Sep 17 00:00:00 2001 From: Lukas Strebel Date: Tue, 24 Oct 2023 14:27:00 +0200 Subject: [PATCH 12/71] Start of Implementation of snow depth assimilation. Includes enkfpf.par reading of 'update_snow' and 'update_snow_repartitioning' options, the addition of snow depth to the statevector, the update_clm() adjustment and the repartitioning adapted from DART. Open issues with col%z / zi adjustment causing CH4 balance errors, and questionable update for synthetic obs. --- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 353 +++++++++++++++--- bldsva/intf_DA/pdaf/model/common/enkf.h | 2 + .../intf_DA/pdaf/model/common/read_enkfpar.c | 2 + bldsva/intf_DA/pdaf/model/wrapper_tsmp.c | 2 +- 4 files changed, 311 insertions(+), 48 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 614946bdc..0f2633995 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -32,12 +32,14 @@ module enkf_clm_mod integer :: da_comm_clm integer :: clm_statevecsize integer :: clm_varsize - integer :: clm_begg,clm_endg + integer :: clm_begg,clm_endg,clm_begc,clm_endc real(r8),allocatable :: clm_statevec(:) real(r8),allocatable :: clm_paramarr(:) !hcp LAI integer(c_int),bind(C,name="clmupdate_swc") :: clmupdate_swc integer(c_int),bind(C,name="clmupdate_T") :: clmupdate_T ! by hcp integer(c_int),bind(C,name="clmupdate_texture") :: clmupdate_texture + integer(c_int),bind(C,name="clmupdate_snow") :: clmupdate_snow + integer(c_int),bind(C,name="clmupdate_snow_repartitioning") :: clmupdate_snow_repartitioning integer(c_int),bind(C,name="clmprint_swc") :: clmprint_swc #endif integer(c_int),bind(C,name="clmprint_et") :: clmprint_et @@ -59,6 +61,7 @@ module enkf_clm_mod integer(c_int),bind(C,name="clmprefixlen") :: clmprefixlen integer :: statcomm + logical :: newgridcell contains #if defined CLMFIVE @@ -79,10 +82,12 @@ subroutine define_clm_statevec() !write(*,*) "----",begg,",",endg,",",begl,",",endl,",",begc,",",endc,",",begp,",",endp," -------" clm_begg = begg clm_endg = endg + clm_begc = begc + clm_endc = endc if(clmupdate_swc.eq.1) then - clm_varsize = (endg-begg+1) * nlevsoi - clm_statevecsize = (endg-begg+1) * nlevsoi + clm_varsize = (clm_endg-clm_begg+1) * nlevsoi + clm_statevecsize = (clm_endg-clm_begg+1) * nlevsoi endif if(clmupdate_swc.eq.2) then @@ -97,6 +102,14 @@ subroutine define_clm_statevec() clm_statevecsize = clm_statevecsize + 3*((endg-begg+1)*nlevsoi) endif + ! Snow assimilation + ! Case 1: Assimilation of snow depth : allocated 1 per column in CLM5 + ! But observations and history file 1 per grid cell and therefore statevecsize 1 per grid cell + if(clmupdate_snow.eq.1) then + clm_varsize = (clm_endg-clm_begg+1) ! Currently no combination of SWC and snow DA + clm_statevecsize = (clm_endg-clm_begg+1) ! So like this if snow is set it takes priority + endif + IF (allocated(clm_statevec)) deallocate(clm_statevec) allocate(clm_statevec(clm_statevecsize)) @@ -110,36 +123,66 @@ subroutine define_clm_statevec() subroutine set_clm_statevec() use clm_instMod, only : soilstate_inst, waterstate_inst use clm_varpar , only : nlevsoi + use ColumnType , only : col use shr_kind_mod, only: r8 => shr_kind_r8 implicit none real(r8), pointer :: swc(:,:) real(r8), pointer :: psand(:,:) real(r8), pointer :: pclay(:,:) real(r8), pointer :: porgm(:,:) - integer :: i,j,cc=1,offset=0 + + real(r8), pointer :: snow_depth(:) + + integer :: i,j,jj,g,cc=1,offset=0 swc => waterstate_inst%h2osoi_vol_col psand => soilstate_inst%cellsand_col pclay => soilstate_inst%cellclay_col porgm => soilstate_inst%cellorg_col - ! write swc values to state vector - cc = 1 - do i=1,nlevsoi - do j=clm_begg,clm_endg - clm_statevec(cc+offset) = swc(j,i) - cc = cc + 1 - end do - end do + snow_depth => waterstate_inst%snow_depth_col ! snow height of snow covered area (m) + + if(clmupdate_swc.ne.0) then + ! write swc values to state vector + cc = 1 + do i=1,nlevsoi + do j=clm_begg,clm_endg + ! Only get the SWC from the first column of each gridcell + ! and add it to the clm_statevec at the position of the gridcell (cc) + newgridcell = .true. + do jj=clm_begc,clm_endc + g = col%gridcell(jj) + if (g .eq. j) then + if (newgridcell) then + newgridcell = .false. + clm_statevec(cc+offset) = swc(jj,i) + endif + endif + end do + cc = cc + 1 + end do + end do + endif ! write texture values to state vector (if desired) if(clmupdate_texture.eq.1) then cc = 1 do i=1,nlevsoi do j=clm_begg,clm_endg - clm_statevec(cc+1*clm_varsize+offset) = psand(j,i) - clm_statevec(cc+2*clm_varsize+offset) = pclay(j,i) - cc = cc + 1 + ! Only get the parameters from the first column of each gridcell + ! and add it to the clm_statevec at the position of the gridcell (cc) + newgridcell = .true. + do jj=clm_begc,clm_endc + g = col%gridcell(jj) + if (g .eq. j) then + if (newgridcell) then + newgridcell = .false. + clm_statevec(cc+1*clm_varsize+offset) = psand(jj,i) + clm_statevec(cc+2*clm_varsize+offset) = pclay(jj,i) + endif + endif + end do + cc = cc + 1 end do end do endif @@ -149,18 +192,51 @@ subroutine set_clm_statevec() cc = 1 do i=1,nlevsoi do j=clm_begg,clm_endg - clm_statevec(cc+1*clm_varsize+offset) = psand(j,i) - clm_statevec(cc+2*clm_varsize+offset) = pclay(j,i) - clm_statevec(cc+3*clm_varsize+offset) = porgm(j,i) - cc = cc + 1 + ! Only get the parameters from the first column of each gridcell + ! and add it to the clm_statevec at the position of the gridcell (cc) + newgridcell = .true. + do jj=clm_begc,clm_endc + g = col%gridcell(jj) + if (g .eq. j) then + if (newgridcell) then + newgridcell = .false. + clm_statevec(cc+1*clm_varsize+offset) = psand(jj,i) + clm_statevec(cc+2*clm_varsize+offset) = pclay(jj,i) + clm_statevec(cc+3*clm_varsize+offset) = porgm(jj,i) + endif + endif + end do + cc = cc + 1 end do end do endif + ! Snow assimilation + ! Case 1: Snow depth + if(clmupdate_snow.eq.1) then + cc = 1 + do j=clm_begg,clm_endg + ! Only get the snow_depth from the first column of each gridcell + ! and add it to the clm_statevec at the position of the gridcell (cc) + newgridcell = .true. + do jj=clm_begc,clm_endc + g = col%gridcell(jj) + if (g .eq. j) then + if (newgridcell) then + newgridcell = .false. + clm_statevec(cc+offset) = snow_depth(jj) + endif + endif + end do + cc = cc + 1 + end do + endif + end subroutine subroutine update_clm() bind(C,name="update_clm") use clm_varpar , only : nlevsoi + use clm_time_manager , only : update_DA_nstep use shr_kind_mod , only : r8 => shr_kind_r8 use ColumnType , only : col use clm_instMod, only : soilstate_inst, waterstate_inst @@ -174,12 +250,14 @@ subroutine update_clm() bind(C,name="update_clm") real(r8), pointer :: pclay(:,:) real(r8), pointer :: porgm(:,:) + real(r8), pointer :: snow_depth(:) + real(r8), pointer :: dz(:,:) ! layer thickness depth (m) real(r8), pointer :: h2osoi_liq(:,:) ! liquid water (kg/m2) real(r8), pointer :: h2osoi_ice(:,:) real(r8) :: rliq,rice - - integer :: i,j,cc=1,offset=0 + real(r8) :: rsnow(clm_statevecsize) + integer :: i,j,jj,g,cc=1,offset=0 swc => waterstate_inst%h2osoi_vol_col watsat => soilstate_inst%watsat_col @@ -187,39 +265,59 @@ subroutine update_clm() bind(C,name="update_clm") pclay => soilstate_inst%cellclay_col porgm => soilstate_inst%cellorg_col + snow_depth => waterstate_inst%snow_depth_col ! snow height of snow covered area (m) + dz => col%dz h2osoi_liq => waterstate_inst%h2osoi_liq_col h2osoi_ice => waterstate_inst%h2osoi_ice_col + call update_DA_nstep() + ! write updated swc back to CLM - cc = 1 - do i=1,nlevsoi - do j=clm_begg,clm_endg -! rliq = h2osoi_liq(j,i)/(dz(j,i)*denh2o*swc(j,i)) -! rice = h2osoi_ice(j,i)/(dz(j,i)*denice*swc(j,i)) - - if(clm_statevec(cc+offset).le.watmin) then - swc(j,i) = watmin - else if(clm_statevec(cc+offset).ge.watsat(j,i)) then - swc(j,i) = watsat(j,i) - else - swc(j,i) = clm_statevec(cc+offset) - endif - ! update liquid water content -! h2osoi_liq(j,i) = swc(j,i) * dz(j,i)*denh2o !*rliq - ! update ice content -! h2osoi_ice(j,i) = swc(j,i) * dz(j,i)*denice !*rice - cc = cc + 1 - end do - end do + if(clmupdate_swc.ne.0) then + cc = 1 + do i=1,nlevsoi + do j=clm_begg,clm_endg + ! iterate through the columns and copy from the same gridcell + ! i.e. statevec position (cc) for each column + do jj=clm_begc,clm_endc + rliq = h2osoi_liq(jj,i)/(dz(jj,i)*denh2o*swc(jj,i)) + rice = h2osoi_ice(jj,i)/(dz(jj,i)*denice*swc(jj,i)) + + if(clm_statevec(cc+offset).le.watmin) then + swc(jj,i) = watmin + else if(clm_statevec(cc+offset).ge.watsat(jj,i)) then + swc(jj,i) = watsat(jj,i) + else + swc(jj,i) = clm_statevec(cc+offset) + endif + + if (isnan(swc(jj,i))) then + swc(jj,i) = watmin + print *, "WARNING: swc at j,i is nan: ", jj, i + endif + + ! update liquid water content + h2osoi_liq(jj,i) = swc(jj,i) * dz(jj,i)*denh2o*rliq + ! update ice content + h2osoi_ice(j,i) = swc(j,i) * dz(j,i)*denice*rice + end do + cc = cc + 1 + end do + end do + endif ! write updated texture back to CLM if(clmupdate_texture.eq.1) then cc = 1 do i=1,nlevsoi do j=clm_begg,clm_endg - psand(j,i) = clm_statevec(cc+1*clm_varsize+offset) - pclay(j,i) = clm_statevec(cc+2*clm_varsize+offset) + ! iterate through the columns and copy from the same gridcell + ! i.e. statevec position (cc) for each column + do jj=clm_begc,clm_endc + psand(jj,i) = clm_statevec(cc+1*clm_varsize+offset) + pclay(jj,i) = clm_statevec(cc+2*clm_varsize+offset) + end do cc = cc + 1 end do end do @@ -232,9 +330,13 @@ subroutine update_clm() bind(C,name="update_clm") cc = 1 do i=1,nlevsoi do j=clm_begg,clm_endg - psand(j,i) = clm_statevec(cc+1*clm_varsize+offset) - pclay(j,i) = clm_statevec(cc+2*clm_varsize+offset) - porgm(j,i) = clm_statevec(cc+3*clm_varsize+offset) + ! iterate through the columns and copy from the same gridcell + ! i.e. statevec position (cc) for each column + do jj=clm_begc,clm_endc + psand(jj,i) = clm_statevec(cc+1*clm_varsize+offset) + pclay(jj,i) = clm_statevec(cc+2*clm_varsize+offset) + porgm(jj,i) = clm_statevec(cc+3*clm_varsize+offset) + end do cc = cc + 1 end do end do @@ -242,7 +344,164 @@ subroutine update_clm() bind(C,name="update_clm") call clm_texture_to_parameters endif - end subroutine + ! Snow assimilation: + ! Case 1: Snow depth + ! Write updated snow depth back to CLM and then repartition snow and adjust related variables + if(clmupdate_snow.eq.1) then + cc = 1 + do j=clm_begg,clm_endg + ! iterate through the columns and copy from the same gridcell + ! i.e. statevec position (cc) for each column + do jj=clm_begc,clm_endc + ! Catch negative or 0 values from DA + if (clm_statevec(cc+offset).lt.0.0) then + print *, "WARNING: snow depth at g,c is negative: ", j, jj, clm_statevec(cc+offset) + else + rsnow(jj) = snow_depth(jj) - clm_statevec(cc+offset) + snow_depth(jj) = clm_statevec(cc+offset) + endif + end do + cc = cc + 1 + end do + if ( clmupdate_snow_repartitioning.ne.0) then + if ( ABS(SUM(rsnow(:))).gt.0.000001) then + call clm_repartition_snow() + end if + end if + endif + + end subroutine + + subroutine clm_repartition_snow() + use ColumnType, only : col + use clm_instMod, only : waterstate_inst + use clm_varpar, only : nlevsno, nlevsoi + use clm_varcon, only : bdsno + use shr_kind_mod, only : r8 => shr_kind_r8 + + implicit none + + real(r8), pointer :: snow_depth(:) + real(r8), pointer :: h2osno(:) + real(r8), pointer :: h2oliq(:,:) + real(r8), pointer :: h2oice(:,:) + + integer, pointer :: snlsno(:) + + real(r8) :: dzsno(clm_begc:clm_endc,nlevsno) + + real(r8) :: snowden, frac_swe, frac_liq, frac_ice + real(r8) :: gain_h2osno, gain_h2oliq, gain_h2oice, gain_dzsno + integer :: i,ii,j,jj,g,cc=1,offset=0 + + snow_depth => waterstate_inst%snow_depth_col ! snow height of snow covered area (m) + h2osno => waterstate_inst%h2osno_col ! col snow water (mm H2O) + h2oliq => waterstate_inst%h2osoi_liq_col ! col liquid water (kg/m2) (-nlevsno+1:nlevgrnd) + h2oice => waterstate_inst%h2osoi_ice_col ! col ice lens (kg/m2) (-nlevsno+1:nlevgrnd) + + snlsno => col%snl ! number of snow layers (negative) + + ! dz for snow layers is defined like in the history output as col%dz for the snow layers + dzsno(clm_begc:clm_endc, -nlevsno+1:0) = col%dz(clm_begc:clm_endc,-nlevsno+1:0) + + ! Iterate through all columns + do jj=clm_begc,clm_endc + if (h2osno(jj).lt.0.0) then ! No snow in column + print *, "WARNING: negative snow in col: ", jj, h2osno +! ! Set existing layers to near zero and let CLM do the layer aggregation + do i=1,-snlsno(jj) + h2oliq(jj,i) = 0.0_r8 + h2oice(jj,i) = 0.00000001_r8 + dzsno(jj,i) = 0.00000001_r8 + end do + snow_depth(jj) = sum(dzsno(jj,:)) + h2osno(jj) = sum(h2oice(jj,:)) + else ! snow (h2osno) present + if (snlsno(jj).lt.0) then ! snow layers in the column + + do i=1,-snlsno(jj) ! iterate through the snow layers + ii = nlevsoi - i + 1 + ! snow density prior for each layer + if (dzsno(jj,ii).gt.0.0_r8) then + snowden = (h2oliq(jj,ii) + h2oice(jj,ii) / dzsno(jj,ii)) + else + snowden = 0.0_r8 + endif + ! fraction of SWE in each active layers + if(h2osno(jj).gt.0.0_r8) then + ! repartition Option 1: Adjust bottom layer only (set weight to 1 for bottom 0 else) + if(clmupdate_snow_repartitioning.eq.1) then + if (ii .eq. nlevsno) then + frac_swe = 1.0_r8 + else + frac_swe = 0.0_r8 + end if + ! repartition Option 2: Adjust all active layers + else if (clmupdate_snow_repartitioning.eq.2) then + frac_swe = (h2oliq(jj,ii) + h2oice(jj,ii)) / h2osno(jj) + end if + else + frac_swe = 0.0_r8 ! no fraction SWE if no snow is present in column + end if ! end SWE fraction if + + ! fraction of liquid and ice + if ((h2oliq(jj,ii) + h2oice(jj,ii)).gt.0.0_r8) then + frac_liq = h2oliq(jj,ii) / (h2oliq(jj,ii) + h2oice(jj,ii)) + frac_ice = 1.0_r8 - frac_liq + else + frac_liq = 0.0_r8 + frac_ice = 0.0_r8 + end if + + ! SWE adjustment per layer + ! assumes identical layer distribution of liq and ice than before DA (frac_*) + ! post DA H2OSNO (SWE) is calculated from snow_depth like in main/clm_instMod.F90 + ! i.e. h2osno(jj) = snow_depth(jj) * bdsno but + ! require prior H2OSNO here and update it afterwards + ! column level adjustments + if (abs((snow_depth(jj) * bdsno) - h2osno(jj)).gt.0.0_r8) then + gain_h2osno = ((snow_depth(jj) * bdsno) - h2osno(jj)) * frac_swe + gain_h2oliq = gain_h2osno * frac_liq + gain_h2oice = gain_h2osno * frac_ice + else + gain_h2osno = 0.0_r8 + gain_h2oliq = 0.0_r8 + gain_h2oice = 0.0_r8 + end if + ! layer level adjustments + if (snowden.gt.0.0_r8) then + gain_dzsno = gain_h2osno / snowden + else + gain_dzsno = 0.0_r8 + end if + h2oliq(jj,ii) = h2oliq(jj,ii) + gain_h2oliq + h2oice(jj,ii) = h2oice(jj,ii) + gain_h2oice + +! ! Adjust snow layer dimensions so that CLM5 can calculate compaction / aggregation +! ! in the DART code dzsno is adjusted directly but in CLM5 dzsno is local and diagnostic +! ! i.e. calculated / assigned from frac_sno and dz(:, snow_layer) in SnowHydrologyMod +! ! therefore we adjust dz(:, snow_layer) here +! if (abs((snow_depth(jj) * bdsno) - h2osno(jj)).gt.0.0_r8) then +! col%dz(jj, ii) = col%dz(jj, ii) + gain_dzsno +! ! mid point and interface adjustments +! ! i.e. zsno (col%z(:, snow_layers)) and zisno (col%zi(:, snow_layers)) +! col%zi(jj, ii) = sum(col%dz(jj,ii:nlevsno))*-1.0_r8 +! if (ii.eq.nlevsno) then +! col%z(jj,ii) = col%zi(jj,ii) / 2.0_r8 +! else +! col%z(jj,ii) = sum(col%zi(jj, ii:ii+1)) / 2.0_r8 +! end if +! end if +! + end do ! End iteration of snow layers + endif ! End of snow layers present check + ! Finally adjust SWE (h2osno) since the prior value is no longer needed + ! both column level variables so we can adjust it outside the layer loop + h2osno(jj) = snow_depth(jj) * bdsno + end if ! End of snow present check + end do ! End of column iteration + + end subroutine subroutine clm_correct_texture() diff --git a/bldsva/intf_DA/pdaf/model/common/enkf.h b/bldsva/intf_DA/pdaf/model/common/enkf.h index 0de4087f6..d87bcace8 100755 --- a/bldsva/intf_DA/pdaf/model/common/enkf.h +++ b/bldsva/intf_DA/pdaf/model/common/enkf.h @@ -87,6 +87,8 @@ GLOBAL int nx_local,ny_local,nz_local; GLOBAL int clmupdate_swc; GLOBAL int clmupdate_T; GLOBAL int clmupdate_texture; +GLOBAL int clmupdate_snow; +GLOBAL int clmupdate_snow_repartitioning; GLOBAL int clmprint_swc; GLOBAL int clmprint_et; GLOBAL int dtmult_cosmo; diff --git a/bldsva/intf_DA/pdaf/model/common/read_enkfpar.c b/bldsva/intf_DA/pdaf/model/common/read_enkfpar.c index b77c65ef7..678bd2e6f 100755 --- a/bldsva/intf_DA/pdaf/model/common/read_enkfpar.c +++ b/bldsva/intf_DA/pdaf/model/common/read_enkfpar.c @@ -82,6 +82,8 @@ void read_enkfpar(char *parname) clmupdate_swc = iniparser_getint(pardict,"CLM:update_swc",1); clmupdate_T = iniparser_getint(pardict,"CLM:update_T",0); clmupdate_texture = iniparser_getint(pardict,"CLM:update_texture",0); + clmupdate_snow = iniparser_getint(pardict,"CLM:update_snow",0); + clmupdate_snow_repartitioning = iniparser_getint(pardict,"CLM:update_snow_repartitioning",1); clmprint_swc = iniparser_getint(pardict,"CLM:print_swc",0); clmprint_et = iniparser_getint(pardict,"CLM:print_et",0); diff --git a/bldsva/intf_DA/pdaf/model/wrapper_tsmp.c b/bldsva/intf_DA/pdaf/model/wrapper_tsmp.c index 9df503ea2..dafd49946 100644 --- a/bldsva/intf_DA/pdaf/model/wrapper_tsmp.c +++ b/bldsva/intf_DA/pdaf/model/wrapper_tsmp.c @@ -224,7 +224,7 @@ void print_update_pfb(){ void update_tsmp(){ #if defined CLMSA - if((model == tag_model_clm) && ((clmupdate_swc != 0) || (clmupdate_T != 0))){ + if((model == tag_model_clm) && ((clmupdate_swc != 0) || (clmupdate_T != 0) || (clmupdate_snow!=0))){ update_clm(); print_update_clm(&tcycle, &total_steps); } From a2669bedaec692be13665ed88cc1acb421140cb4 Mon Sep 17 00:00:00 2001 From: Lukas Strebel Date: Wed, 25 Oct 2023 11:03:55 +0200 Subject: [PATCH 13/71] Fixed the dz, zi, z calculations and adjusted the loop indexing to be correct for directly updating model variables instead of restart variables with different indexing. Simulation no longer causes a CH4 module error. --- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 63 ++++++++++--------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 0f2633995..0ae1585bd 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -389,7 +389,7 @@ subroutine clm_repartition_snow() integer, pointer :: snlsno(:) real(r8) :: dzsno(clm_begc:clm_endc,nlevsno) - + real(r8) :: h2osno_po(clm_begc:clm_endc) real(r8) :: snowden, frac_swe, frac_liq, frac_ice real(r8) :: gain_h2osno, gain_h2oliq, gain_h2oice, gain_dzsno integer :: i,ii,j,jj,g,cc=1,offset=0 @@ -418,9 +418,13 @@ subroutine clm_repartition_snow() h2osno(jj) = sum(h2oice(jj,:)) else ! snow (h2osno) present if (snlsno(jj).lt.0) then ! snow layers in the column - - do i=1,-snlsno(jj) ! iterate through the snow layers - ii = nlevsoi - i + 1 + ! Formulas below from DART use h2osno_po / h2osno_pr for after / before DA SWE + ! Here we have snow_depth after and h2osno before DA snow depth + ! Therefore need to have a transform to get h2osno_po + h2osno_po(jj) = (snow_depth(jj) * bdsno) ! calculations from Init using constant SBD + print *, "DBGSNOW h2osno_po", jj, h2osno_po(jj) + do ii=0,-snlsno(jj),-1 ! iterate through the snow layers + ! ii = nlevsoi - i + 1 ! DART VERSION: ii = nlevsoi - i + 1 ! snow density prior for each layer if (dzsno(jj,ii).gt.0.0_r8) then snowden = (h2oliq(jj,ii) + h2oice(jj,ii) / dzsno(jj,ii)) @@ -431,7 +435,7 @@ subroutine clm_repartition_snow() if(h2osno(jj).gt.0.0_r8) then ! repartition Option 1: Adjust bottom layer only (set weight to 1 for bottom 0 else) if(clmupdate_snow_repartitioning.eq.1) then - if (ii .eq. nlevsno) then + if (ii .eq. 0) then ! DART version indexing check against nlevsno but for us 0 frac_swe = 1.0_r8 else frac_swe = 0.0_r8 @@ -455,12 +459,8 @@ subroutine clm_repartition_snow() ! SWE adjustment per layer ! assumes identical layer distribution of liq and ice than before DA (frac_*) - ! post DA H2OSNO (SWE) is calculated from snow_depth like in main/clm_instMod.F90 - ! i.e. h2osno(jj) = snow_depth(jj) * bdsno but - ! require prior H2OSNO here and update it afterwards - ! column level adjustments - if (abs((snow_depth(jj) * bdsno) - h2osno(jj)).gt.0.0_r8) then - gain_h2osno = ((snow_depth(jj) * bdsno) - h2osno(jj)) * frac_swe + if (abs(h2osno_po(jj) - h2osno(jj)).gt.0.0_r8) then + gain_h2osno = (h2osno_po(jj) - h2osno(jj)) * frac_swe gain_h2oliq = gain_h2osno * frac_liq gain_h2oice = gain_h2osno * frac_ice else @@ -477,27 +477,32 @@ subroutine clm_repartition_snow() h2oliq(jj,ii) = h2oliq(jj,ii) + gain_h2oliq h2oice(jj,ii) = h2oice(jj,ii) + gain_h2oice -! ! Adjust snow layer dimensions so that CLM5 can calculate compaction / aggregation -! ! in the DART code dzsno is adjusted directly but in CLM5 dzsno is local and diagnostic -! ! i.e. calculated / assigned from frac_sno and dz(:, snow_layer) in SnowHydrologyMod -! ! therefore we adjust dz(:, snow_layer) here -! if (abs((snow_depth(jj) * bdsno) - h2osno(jj)).gt.0.0_r8) then -! col%dz(jj, ii) = col%dz(jj, ii) + gain_dzsno -! ! mid point and interface adjustments -! ! i.e. zsno (col%z(:, snow_layers)) and zisno (col%zi(:, snow_layers)) -! col%zi(jj, ii) = sum(col%dz(jj,ii:nlevsno))*-1.0_r8 -! if (ii.eq.nlevsno) then -! col%z(jj,ii) = col%zi(jj,ii) / 2.0_r8 -! else -! col%z(jj,ii) = sum(col%zi(jj, ii:ii+1)) / 2.0_r8 -! end if -! end if -! + ! Adjust snow layer dimensions so that CLM5 can calculate compaction / aggregation + ! in the DART code dzsno is adjusted directly but in CLM5 dzsno is local and diagnostic + ! i.e. calculated / assigned from frac_sno and dz(:, snow_layer) in SnowHydrologyMod + ! therefore we adjust dz(:, snow_layer) here + if (abs(h2osno_po(jj) - h2osno(jj)).gt.0.0_r8) then + col%dz(jj, ii) = col%dz(jj, ii) + gain_dzsno + ! mid point and interface adjustments + ! i.e. zsno (col%z(:, snow_layers)) and zisno (col%zi(:, snow_layers)) + ! DART version the sum goes from ilevel:nlevsno to fit with our indexing: + col%zi(jj, ii) = sum(col%dz(jj,ii:0))*-1.0_r8 + ! In DART the check is ilevel == nlevsno but here + if (ii.eq.0) then + col%z(jj,ii) = col%zi(jj,ii) / 2.0_r8 + else ! + col%z(jj,ii) = sum(col%zi(jj, ii:ii+1)) / 2.0_r8 + end if + end if + end do ! End iteration of snow layers endif ! End of snow layers present check ! Finally adjust SWE (h2osno) since the prior value is no longer needed - ! both column level variables so we can adjust it outside the layer loop - h2osno(jj) = snow_depth(jj) * bdsno + ! column level variables so we can adjust it outside the layer loop + h2osno(jj) = h2osno_po(jj) + + ! DEBUG + print *, "DBGSNOW DEPTHS: dz", col%dz(jj,:), "zi ", col%zi(jj,:), "z ", col%z(jj,:) end if ! End of snow present check end do ! End of column iteration From 7118edbdf610ddd46969d9aadf41ba74b1369dc0 Mon Sep 17 00:00:00 2001 From: Lukas Strebel Date: Wed, 25 Oct 2023 16:55:30 +0200 Subject: [PATCH 14/71] Added H2OSNO calculation based on prior snow depth and snow fraction. Removed some debug output. --- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 0ae1585bd..85be0ba45 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -364,7 +364,7 @@ subroutine update_clm() bind(C,name="update_clm") cc = cc + 1 end do if ( clmupdate_snow_repartitioning.ne.0) then - if ( ABS(SUM(rsnow(:))).gt.0.000001) then + if ( ABS(SUM(rsnow(:))).gt.0.000001) then call clm_repartition_snow() end if end if @@ -376,7 +376,7 @@ subroutine clm_repartition_snow() use ColumnType, only : col use clm_instMod, only : waterstate_inst use clm_varpar, only : nlevsno, nlevsoi - use clm_varcon, only : bdsno + use clm_varcon, only : bdsno, denice use shr_kind_mod, only : r8 => shr_kind_r8 implicit none @@ -385,22 +385,26 @@ subroutine clm_repartition_snow() real(r8), pointer :: h2osno(:) real(r8), pointer :: h2oliq(:,:) real(r8), pointer :: h2oice(:,:) - + real(r8), pointer :: frac_sno(:) + real(r8), pointer :: snowdp(:) integer, pointer :: snlsno(:) real(r8) :: dzsno(clm_begc:clm_endc,nlevsno) real(r8) :: h2osno_po(clm_begc:clm_endc) real(r8) :: snowden, frac_swe, frac_liq, frac_ice real(r8) :: gain_h2osno, gain_h2oliq, gain_h2oice, gain_dzsno + real(r8) :: rho_avg, z_avg integer :: i,ii,j,jj,g,cc=1,offset=0 snow_depth => waterstate_inst%snow_depth_col ! snow height of snow covered area (m) + snowdp => waterstate_inst%snowdp_col ! area-averaged snow height (m) h2osno => waterstate_inst%h2osno_col ! col snow water (mm H2O) h2oliq => waterstate_inst%h2osoi_liq_col ! col liquid water (kg/m2) (-nlevsno+1:nlevgrnd) h2oice => waterstate_inst%h2osoi_ice_col ! col ice lens (kg/m2) (-nlevsno+1:nlevgrnd) snlsno => col%snl ! number of snow layers (negative) + frac_sno => waterstate_inst%frac_sno_eff_col ! fraction of ground covered by snow ! dz for snow layers is defined like in the history output as col%dz for the snow layers dzsno(clm_begc:clm_endc, -nlevsno+1:0) = col%dz(clm_begc:clm_endc,-nlevsno+1:0) @@ -409,7 +413,7 @@ subroutine clm_repartition_snow() if (h2osno(jj).lt.0.0) then ! No snow in column print *, "WARNING: negative snow in col: ", jj, h2osno ! ! Set existing layers to near zero and let CLM do the layer aggregation - do i=1,-snlsno(jj) + do i=0,snlsno(jj),-1 h2oliq(jj,i) = 0.0_r8 h2oice(jj,i) = 0.00000001_r8 dzsno(jj,i) = 0.00000001_r8 @@ -421,8 +425,20 @@ subroutine clm_repartition_snow() ! Formulas below from DART use h2osno_po / h2osno_pr for after / before DA SWE ! Here we have snow_depth after and h2osno before DA snow depth ! Therefore need to have a transform to get h2osno_po - h2osno_po(jj) = (snow_depth(jj) * bdsno) ! calculations from Init using constant SBD - print *, "DBGSNOW h2osno_po", jj, h2osno_po(jj) + ! v1 init + ! h2osno_po(jj) = (snow_depth(jj) * bdsno) ! calculations from Init using constant SBD + ! v2 SoilTemperatureMod + if (snowdp(jj).gt.0.0_r8) then + rho_avg = min(800.0_r8, h2osno(jj)/snowdp(jj)) + else + rho_avg = 200.0_r8 + end if + if (frac_sno(jj).gt.0.0_r8 .and. snlsno(jj).lt.0.0_r8) then + h2osno_po(jj) = snow_depth(jj) * (rho_avg*frac_sno(jj)) + else + h2osno_po(jj) = snow_depth(jj) * denice + end if + do ii=0,-snlsno(jj),-1 ! iterate through the snow layers ! ii = nlevsoi - i + 1 ! DART VERSION: ii = nlevsoi - i + 1 ! snow density prior for each layer @@ -483,14 +499,14 @@ subroutine clm_repartition_snow() ! therefore we adjust dz(:, snow_layer) here if (abs(h2osno_po(jj) - h2osno(jj)).gt.0.0_r8) then col%dz(jj, ii) = col%dz(jj, ii) + gain_dzsno - ! mid point and interface adjustments + ! mid point and interface adjustments ! i.e. zsno (col%z(:, snow_layers)) and zisno (col%zi(:, snow_layers)) ! DART version the sum goes from ilevel:nlevsno to fit with our indexing: col%zi(jj, ii) = sum(col%dz(jj,ii:0))*-1.0_r8 ! In DART the check is ilevel == nlevsno but here if (ii.eq.0) then col%z(jj,ii) = col%zi(jj,ii) / 2.0_r8 - else ! + else col%z(jj,ii) = sum(col%zi(jj, ii:ii+1)) / 2.0_r8 end if end if @@ -499,10 +515,7 @@ subroutine clm_repartition_snow() endif ! End of snow layers present check ! Finally adjust SWE (h2osno) since the prior value is no longer needed ! column level variables so we can adjust it outside the layer loop - h2osno(jj) = h2osno_po(jj) - - ! DEBUG - print *, "DBGSNOW DEPTHS: dz", col%dz(jj,:), "zi ", col%zi(jj,:), "z ", col%z(jj,:) + h2osno(jj) = h2osno_po(jj) end if ! End of snow present check end do ! End of column iteration From 97b5a579aac5a40cf53346e4a5e1bcd54ea563a6 Mon Sep 17 00:00:00 2001 From: Lukas Strebel Date: Fri, 27 Oct 2023 07:34:33 +0200 Subject: [PATCH 15/71] Fix snow layer iteration lower boundary of by 1 causing NaN errors. --- bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 85be0ba45..ade3dd23d 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -413,7 +413,7 @@ subroutine clm_repartition_snow() if (h2osno(jj).lt.0.0) then ! No snow in column print *, "WARNING: negative snow in col: ", jj, h2osno ! ! Set existing layers to near zero and let CLM do the layer aggregation - do i=0,snlsno(jj),-1 + do i=0,snlsno(jj)+1,-1 h2oliq(jj,i) = 0.0_r8 h2oice(jj,i) = 0.00000001_r8 dzsno(jj,i) = 0.00000001_r8 @@ -438,8 +438,7 @@ subroutine clm_repartition_snow() else h2osno_po(jj) = snow_depth(jj) * denice end if - - do ii=0,-snlsno(jj),-1 ! iterate through the snow layers + do ii=0,snlsno(jj)+1,-1 ! iterate through the snow layers ! ii = nlevsoi - i + 1 ! DART VERSION: ii = nlevsoi - i + 1 ! snow density prior for each layer if (dzsno(jj,ii).gt.0.0_r8) then From f5c3bbbed38a59ac83562ba9604a8adae78c9c47 Mon Sep 17 00:00:00 2001 From: Lukas Strebel Date: Fri, 3 Nov 2023 15:49:18 +0100 Subject: [PATCH 16/71] Added: clmupdate_snow=2 in enkfpf.par now does SWE assimilation in the same way as snow depth is done with adjustments for starting with SWE and calculating snow depth at the end of repartitioning. --- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 97 ++++++++++++++----- 1 file changed, 72 insertions(+), 25 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index ade3dd23d..1fd059424 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -251,6 +251,7 @@ subroutine update_clm() bind(C,name="update_clm") real(r8), pointer :: porgm(:,:) real(r8), pointer :: snow_depth(:) + real(r8), pointer :: h2osno(:) real(r8), pointer :: dz(:,:) ! layer thickness depth (m) real(r8), pointer :: h2osoi_liq(:,:) ! liquid water (kg/m2) @@ -265,7 +266,8 @@ subroutine update_clm() bind(C,name="update_clm") pclay => soilstate_inst%cellclay_col porgm => soilstate_inst%cellorg_col - snow_depth => waterstate_inst%snow_depth_col ! snow height of snow covered area (m) + snow_depth => waterstate_inst%snow_depth_col ! snow height of snow covered area (m) + h2osno => waterstate_inst%h2osno_col ! snow water equivalent (mm) dz => col%dz h2osoi_liq => waterstate_inst%h2osoi_liq_col @@ -369,10 +371,34 @@ subroutine update_clm() bind(C,name="update_clm") end if end if endif + ! Case 2: Snow water equivalent + ! Write updated snow depth back to CLM and then repartition snow and adjust related variables + if(clmupdate_snow.eq.2) then + cc = 1 + do j=clm_begg,clm_endg + ! iterate through the columns and copy from the same gridcell + ! i.e. statevec position (cc) for each column + do jj=clm_begc,clm_endc + ! Catch negative or 0 values from DA + if (clm_statevec(cc+offset).lt.0.0) then + print *, "WARNING: SWE at g,c is negative: ", j, jj, clm_statevec(cc+offset) + else + rsnow(jj) = h2osno(jj) + h2osno(jj) = clm_statevec(cc+offset) + endif + end do + cc = cc + 1 + end do + if ( clmupdate_snow_repartitioning.ne.0) then + if ( ABS(SUM(rsnow(:) - h2osno(:))).gt.0.000001) then + call clm_repartition_snow(rsnow(:)) + end if + end if + endif end subroutine - subroutine clm_repartition_snow() + subroutine clm_repartition_snow(h2osno_in) use ColumnType, only : col use clm_instMod, only : waterstate_inst use clm_varpar, only : nlevsno, nlevsoi @@ -381,6 +407,8 @@ subroutine clm_repartition_snow() implicit none + real(r8), intent(in), optional :: h2osno_in(clm_begc:clm_endc) + real(r8), pointer :: snow_depth(:) real(r8), pointer :: h2osno(:) real(r8), pointer :: h2oliq(:,:) @@ -391,6 +419,7 @@ subroutine clm_repartition_snow() real(r8) :: dzsno(clm_begc:clm_endc,nlevsno) real(r8) :: h2osno_po(clm_begc:clm_endc) + real(r8) :: h2osno_pr(clm_begc:clm_endc) real(r8) :: snowden, frac_swe, frac_liq, frac_ice real(r8) :: gain_h2osno, gain_h2oliq, gain_h2oice, gain_dzsno real(r8) :: rho_avg, z_avg @@ -422,22 +451,30 @@ subroutine clm_repartition_snow() h2osno(jj) = sum(h2oice(jj,:)) else ! snow (h2osno) present if (snlsno(jj).lt.0) then ! snow layers in the column - ! Formulas below from DART use h2osno_po / h2osno_pr for after / before DA SWE - ! Here we have snow_depth after and h2osno before DA snow depth - ! Therefore need to have a transform to get h2osno_po - ! v1 init - ! h2osno_po(jj) = (snow_depth(jj) * bdsno) ! calculations from Init using constant SBD - ! v2 SoilTemperatureMod - if (snowdp(jj).gt.0.0_r8) then - rho_avg = min(800.0_r8, h2osno(jj)/snowdp(jj)) - else - rho_avg = 200.0_r8 - end if - if (frac_sno(jj).gt.0.0_r8 .and. snlsno(jj).lt.0.0_r8) then - h2osno_po(jj) = snow_depth(jj) * (rho_avg*frac_sno(jj)) - else - h2osno_po(jj) = snow_depth(jj) * denice + if (clmupdate_snow .eq. 1) then + ! Formulas below from DART use h2osno_po / h2osno_pr for after / before DA SWE + ! clmupdate_snow == 1 has snow_depth after and h2osno before DA snow depth + ! Therefore need to have a transform to get h2osno_po + ! v1 init + ! h2osno_po(jj) = (snow_depth(jj) * bdsno) ! calculations from Init using constant SBD + ! v2 SoilTemperatureMod + if (snowdp(jj).gt.0.0_r8) then + rho_avg = min(800.0_r8, h2osno(jj)/snowdp(jj)) + else + rho_avg = 200.0_r8 + end if + if (frac_sno(jj).gt.0.0_r8 .and. snlsno(jj).lt.0.0_r8) then + h2osno_po(jj) = snow_depth(jj) * (rho_avg*frac_sno(jj)) + else + h2osno_po(jj) = snow_depth(jj) * denice + end if + h2osno_pr(jj) = h2osno(jj) + else if (clmupdate_snow .eq. 2) then + ! for clmupdate_snow == 2 we have post H2OSNO as the main H2OSNO already + h2osno_po(jj) = h2osno(jj) + h2osno_pr(jj) = h2osno_in(jj) end if + do ii=0,snlsno(jj)+1,-1 ! iterate through the snow layers ! ii = nlevsoi - i + 1 ! DART VERSION: ii = nlevsoi - i + 1 ! snow density prior for each layer @@ -447,7 +484,7 @@ subroutine clm_repartition_snow() snowden = 0.0_r8 endif ! fraction of SWE in each active layers - if(h2osno(jj).gt.0.0_r8) then + if(h2osno_pr(jj).gt.0.0_r8) then ! repartition Option 1: Adjust bottom layer only (set weight to 1 for bottom 0 else) if(clmupdate_snow_repartitioning.eq.1) then if (ii .eq. 0) then ! DART version indexing check against nlevsno but for us 0 @@ -457,7 +494,7 @@ subroutine clm_repartition_snow() end if ! repartition Option 2: Adjust all active layers else if (clmupdate_snow_repartitioning.eq.2) then - frac_swe = (h2oliq(jj,ii) + h2oice(jj,ii)) / h2osno(jj) + frac_swe = (h2oliq(jj,ii) + h2oice(jj,ii)) / h2osno_pr(jj) end if else frac_swe = 0.0_r8 ! no fraction SWE if no snow is present in column @@ -474,8 +511,8 @@ subroutine clm_repartition_snow() ! SWE adjustment per layer ! assumes identical layer distribution of liq and ice than before DA (frac_*) - if (abs(h2osno_po(jj) - h2osno(jj)).gt.0.0_r8) then - gain_h2osno = (h2osno_po(jj) - h2osno(jj)) * frac_swe + if (abs(h2osno_po(jj) - h2osno_pr(jj)).gt.0.0_r8) then + gain_h2osno = (h2osno_po(jj) - h2osno_pr(jj)) * frac_swe gain_h2oliq = gain_h2osno * frac_liq gain_h2oice = gain_h2osno * frac_ice else @@ -496,7 +533,7 @@ subroutine clm_repartition_snow() ! in the DART code dzsno is adjusted directly but in CLM5 dzsno is local and diagnostic ! i.e. calculated / assigned from frac_sno and dz(:, snow_layer) in SnowHydrologyMod ! therefore we adjust dz(:, snow_layer) here - if (abs(h2osno_po(jj) - h2osno(jj)).gt.0.0_r8) then + if (abs(h2osno_po(jj) - h2osno_pr(jj)).gt.0.0_r8) then col%dz(jj, ii) = col%dz(jj, ii) + gain_dzsno ! mid point and interface adjustments ! i.e. zsno (col%z(:, snow_layers)) and zisno (col%zi(:, snow_layers)) @@ -512,9 +549,19 @@ subroutine clm_repartition_snow() end do ! End iteration of snow layers endif ! End of snow layers present check - ! Finally adjust SWE (h2osno) since the prior value is no longer needed - ! column level variables so we can adjust it outside the layer loop - h2osno(jj) = h2osno_po(jj) + + ! Column level variables + if (clmupdate_snow .eq. 1) then + ! Finally adjust SWE (h2osno) since the prior value is no longer needed + ! column level variables so we can adjust it outside the layer loop + h2osno(jj) = h2osno_po(jj) + else if (clmupdate_snow .eq. 2) then + ! Update the total snow depth to match udpates to layers for active snow layers + if (abs(h2osno_po(jj) - h2osno_pr(jj)) .gt. 0.0_r8 .and. snlsno(jj) < 0.0_r8) then + snow_depth(jj) = sum(col%dz(jj, snlsno(jj)+1:0)) + end if + end if + end if ! End of snow present check end do ! End of column iteration From 9f7bb3ddbead07219c55d1fd1a24de741e6cc5fe Mon Sep 17 00:00:00 2001 From: Lukas Strebel Date: Wed, 28 Feb 2024 09:30:03 +0100 Subject: [PATCH 17/71] Latest version to assimilate SWE inlcuding clmupdate_snow_repartitioning.eq.3 option of repartitioning by fraction. --- .../pdaf/framework/init_dim_obs_pdaf.F90 | 12 ++++ .../intf_DA/pdaf/framework/mod_read_obs.F90 | 6 +- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 60 +++++++++++++++---- 3 files changed, 64 insertions(+), 14 deletions(-) diff --git a/bldsva/intf_DA/pdaf/framework/init_dim_obs_pdaf.F90 b/bldsva/intf_DA/pdaf/framework/init_dim_obs_pdaf.F90 index c022bd888..44d813630 100755 --- a/bldsva/intf_DA/pdaf/framework/init_dim_obs_pdaf.F90 +++ b/bldsva/intf_DA/pdaf/framework/init_dim_obs_pdaf.F90 @@ -413,10 +413,22 @@ SUBROUTINE init_dim_obs_pdaf(step, dim_obs_p) #endif #endif + + ! DEBUG / TESTING ALL OBSERVATIONS ON TASK 0 + if (mype_filter == 0) then + dim_obs_p = dim_obs + else + dim_obs_p = 0 + end if + + + if (screen > 2) then print *, "TSMP-PDAF mype(w)=", mype_world, ": init_dim_obs_pdaf: dim_obs_p=", dim_obs_p end if + + ! add and broadcast size of local observation dimensions using mpi_allreduce call mpi_allreduce(dim_obs_p, tmp_dim_obs_f, 1, MPI_INTEGER, MPI_SUM, & comm_filter, ierror) diff --git a/bldsva/intf_DA/pdaf/framework/mod_read_obs.F90 b/bldsva/intf_DA/pdaf/framework/mod_read_obs.F90 index 7d55d7539..3a0353ae6 100755 --- a/bldsva/intf_DA/pdaf/framework/mod_read_obs.F90 +++ b/bldsva/intf_DA/pdaf/framework/mod_read_obs.F90 @@ -23,7 +23,9 @@ !------------------------------------------------------------------------------------------- module mod_read_obs - use iso_C_binding + use mod_parallel_model, only: tcycle + + use iso_C_binding implicit none integer, allocatable :: idx_obs_nc(:), x_idx_obs_nc(:), y_idx_obs_nc(:), z_idx_obs_nc(:), var_id_obs_nc(:,:) @@ -286,7 +288,7 @@ subroutine read_obs_nc(current_observation_filename) call check(nf90_get_var(ncid, clmobs_varid, clm_obs)) if (screen > 2) then - print *, "TSMP-PDAF mype(w)=", mype_world, ": clm_obs=", clm_obs + print *, "TSMP-PDAF mype(w)=", mype_world, ": clm_obs=", clm_obs, "at time", tcycle end if !check, if observation errors are present in observation file diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 1fd059424..ee1ed9614 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -79,7 +79,6 @@ subroutine define_clm_statevec() call get_proc_bounds(begg, endg, begl, endl, begc, endc, begp, endp) - !write(*,*) "----",begg,",",endg,",",begl,",",endl,",",begc,",",endc,",",begp,",",endp," -------" clm_begg = begg clm_endg = endg clm_begc = begc @@ -109,6 +108,12 @@ subroutine define_clm_statevec() clm_varsize = (clm_endg-clm_begg+1) ! Currently no combination of SWC and snow DA clm_statevecsize = (clm_endg-clm_begg+1) ! So like this if snow is set it takes priority endif + ! Case 2: Assimilation of snow water equivalent same as above + if(clmupdate_snow.eq.2) then + clm_varsize = (clm_endg-clm_begg+1) ! Currently no combination of SWC and snow DA + clm_statevecsize = (clm_endg-clm_begg+1) ! So like this if snow is set it takes priority + endif + IF (allocated(clm_statevec)) deallocate(clm_statevec) allocate(clm_statevec(clm_statevecsize)) @@ -132,6 +137,7 @@ subroutine set_clm_statevec() real(r8), pointer :: porgm(:,:) real(r8), pointer :: snow_depth(:) + real(r8), pointer :: h2osno(:) integer :: i,j,jj,g,cc=1,offset=0 @@ -141,6 +147,7 @@ subroutine set_clm_statevec() porgm => soilstate_inst%cellorg_col snow_depth => waterstate_inst%snow_depth_col ! snow height of snow covered area (m) + h2osno => waterstate_inst%h2osno_col ! snow water equivalent (mm) if(clmupdate_swc.ne.0) then ! write swc values to state vector @@ -231,6 +238,25 @@ subroutine set_clm_statevec() cc = cc + 1 end do endif + ! Case 2: SWE + if(clmupdate_snow.eq.2) then + cc = 1 + do j=clm_begg,clm_endg + ! Only get the SWE from the first column of each gridcell + ! and add it to the clm_statevec at the position of the gridcell (cc) + newgridcell = .true. + do jj=clm_begc,clm_endc + g = col%gridcell(jj) + if (g .eq. j) then + if (newgridcell) then + newgridcell = .false. + clm_statevec(cc+offset) = h2osno(jj) + endif + endif + end do + cc = cc + 1 + end do + endif end subroutine @@ -256,10 +282,13 @@ subroutine update_clm() bind(C,name="update_clm") real(r8), pointer :: dz(:,:) ! layer thickness depth (m) real(r8), pointer :: h2osoi_liq(:,:) ! liquid water (kg/m2) real(r8), pointer :: h2osoi_ice(:,:) - real(r8) :: rliq,rice + + real(r8) :: rliq,rice,incr_h2osno real(r8) :: rsnow(clm_statevecsize) integer :: i,j,jj,g,cc=1,offset=0 + integer, pointer :: snlsno(:) + swc => waterstate_inst%h2osoi_vol_col watsat => soilstate_inst%watsat_col psand => soilstate_inst%cellsand_col @@ -273,6 +302,8 @@ subroutine update_clm() bind(C,name="update_clm") h2osoi_liq => waterstate_inst%h2osoi_liq_col h2osoi_ice => waterstate_inst%h2osoi_ice_col + snlsno => col%snl ! number of snow layers (negative) + call update_DA_nstep() ! write updated swc back to CLM @@ -385,11 +416,17 @@ subroutine update_clm() bind(C,name="update_clm") else rsnow(jj) = h2osno(jj) h2osno(jj) = clm_statevec(cc+offset) + if ( clmupdate_snow_repartitioning.eq.3) then + incr_h2osno = h2osno(jj) / rsnow(jj) ! INC = New SWE / OLD SWE + do i=snlsno(jj)+1,0 + h2osoi_ice(jj,i) = h2osoi_ice(jj,i) * incr_h2osno + end do + end if endif end do cc = cc + 1 end do - if ( clmupdate_snow_repartitioning.ne.0) then + if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then if ( ABS(SUM(rsnow(:) - h2osno(:))).gt.0.000001) then call clm_repartition_snow(rsnow(:)) end if @@ -417,11 +454,12 @@ subroutine clm_repartition_snow(h2osno_in) real(r8), pointer :: snowdp(:) integer, pointer :: snlsno(:) - real(r8) :: dzsno(clm_begc:clm_endc,nlevsno) + real(r8) :: dzsno(clm_begc:clm_endc,-nlevsno+1:0) real(r8) :: h2osno_po(clm_begc:clm_endc) real(r8) :: h2osno_pr(clm_begc:clm_endc) real(r8) :: snowden, frac_swe, frac_liq, frac_ice - real(r8) :: gain_h2osno, gain_h2oliq, gain_h2oice, gain_dzsno + real(r8) :: gain_h2osno, gain_h2oliq, gain_h2oice + real(r8) :: gain_dzsno(-nlevsno+1:0) real(r8) :: rho_avg, z_avg integer :: i,ii,j,jj,g,cc=1,offset=0 @@ -436,7 +474,6 @@ subroutine clm_repartition_snow(h2osno_in) frac_sno => waterstate_inst%frac_sno_eff_col ! fraction of ground covered by snow ! dz for snow layers is defined like in the history output as col%dz for the snow layers dzsno(clm_begc:clm_endc, -nlevsno+1:0) = col%dz(clm_begc:clm_endc,-nlevsno+1:0) - ! Iterate through all columns do jj=clm_begc,clm_endc if (h2osno(jj).lt.0.0) then ! No snow in column @@ -476,7 +513,7 @@ subroutine clm_repartition_snow(h2osno_in) end if do ii=0,snlsno(jj)+1,-1 ! iterate through the snow layers - ! ii = nlevsoi - i + 1 ! DART VERSION: ii = nlevsoi - i + 1 + ! DART VERSION: ii = nlevsoi - i + 1 ! snow density prior for each layer if (dzsno(jj,ii).gt.0.0_r8) then snowden = (h2oliq(jj,ii) + h2oice(jj,ii) / dzsno(jj,ii)) @@ -522,9 +559,9 @@ subroutine clm_repartition_snow(h2osno_in) end if ! layer level adjustments if (snowden.gt.0.0_r8) then - gain_dzsno = gain_h2osno / snowden + gain_dzsno(ii) = gain_h2osno / snowden else - gain_dzsno = 0.0_r8 + gain_dzsno(ii) = 0.0_r8 end if h2oliq(jj,ii) = h2oliq(jj,ii) + gain_h2oliq h2oice(jj,ii) = h2oice(jj,ii) + gain_h2oice @@ -534,7 +571,7 @@ subroutine clm_repartition_snow(h2osno_in) ! i.e. calculated / assigned from frac_sno and dz(:, snow_layer) in SnowHydrologyMod ! therefore we adjust dz(:, snow_layer) here if (abs(h2osno_po(jj) - h2osno_pr(jj)).gt.0.0_r8) then - col%dz(jj, ii) = col%dz(jj, ii) + gain_dzsno + col%dz(jj, ii) = col%dz(jj, ii) + gain_dzsno(ii) ! mid point and interface adjustments ! i.e. zsno (col%z(:, snow_layers)) and zisno (col%zi(:, snow_layers)) ! DART version the sum goes from ilevel:nlevsno to fit with our indexing: @@ -558,10 +595,9 @@ subroutine clm_repartition_snow(h2osno_in) else if (clmupdate_snow .eq. 2) then ! Update the total snow depth to match udpates to layers for active snow layers if (abs(h2osno_po(jj) - h2osno_pr(jj)) .gt. 0.0_r8 .and. snlsno(jj) < 0.0_r8) then - snow_depth(jj) = sum(col%dz(jj, snlsno(jj)+1:0)) + snow_depth(jj) = snow_depth(jj) + sum(gain_dzsno(-nlevsno+1:0)) end if end if - end if ! End of snow present check end do ! End of column iteration From 78f945b803339abf6e8820b83e9786e2741fc564 Mon Sep 17 00:00:00 2001 From: Lukas Strebel Date: Wed, 27 Mar 2024 10:32:58 +0100 Subject: [PATCH 18/71] Added workaround for SEG11 fault in obs_op_pdaf for variable obs_index_p(:) for the French site. This workaround works for single grid cell simulations only and is only a temporary fix to proceed testing other parts. --- bldsva/intf_DA/pdaf/framework/obs_op_pdaf.F90 | 12 +++++++++--- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 18 +++++++++++------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/bldsva/intf_DA/pdaf/framework/obs_op_pdaf.F90 b/bldsva/intf_DA/pdaf/framework/obs_op_pdaf.F90 index 14d5e08a0..49909ac68 100644 --- a/bldsva/intf_DA/pdaf/framework/obs_op_pdaf.F90 +++ b/bldsva/intf_DA/pdaf/framework/obs_op_pdaf.F90 @@ -106,6 +106,7 @@ SUBROUTINE obs_op_pdaf(step, dim_p, dim_obs_p, state_p, m_state_p) ! *** operator H on vector or matrix column *** ! ********************************************* + ! If no special observation operator is compiled, use point observations lpointobs = .true. @@ -184,9 +185,14 @@ SUBROUTINE obs_op_pdaf(step, dim_p, dim_obs_p, state_p, m_state_p) if(lpointobs) then - DO i = 1, dim_obs_p - m_state_p(i) = state_p(obs_index_p(i)) - END DO + ! WORKAROUND FOR SUDDEN SEG 11 error + ! DO NOT LEAVE IN FINAL VERSION + ! JUST DURING DEBUG PROCESS + ! JUST VALID FOR SINGLE GRID CELLS WITH SINGLE VALUE OBS + m_state_p(1) = state_p(1) +! DO i = 1, dim_obs_p +! m_state_p(i) = state_p(obs_index_p(i)) +! END DO end if diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index ee1ed9614..4274d0365 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -391,7 +391,9 @@ subroutine update_clm() bind(C,name="update_clm") print *, "WARNING: snow depth at g,c is negative: ", j, jj, clm_statevec(cc+offset) else rsnow(jj) = snow_depth(jj) - clm_statevec(cc+offset) - snow_depth(jj) = clm_statevec(cc+offset) + if ( ABS(SUM(rsnow(:))) .gt.0.000001) then + snow_depth(jj) = clm_statevec(cc+offset) + endif endif end do cc = cc + 1 @@ -415,12 +417,14 @@ subroutine update_clm() bind(C,name="update_clm") print *, "WARNING: SWE at g,c is negative: ", j, jj, clm_statevec(cc+offset) else rsnow(jj) = h2osno(jj) - h2osno(jj) = clm_statevec(cc+offset) - if ( clmupdate_snow_repartitioning.eq.3) then - incr_h2osno = h2osno(jj) / rsnow(jj) ! INC = New SWE / OLD SWE - do i=snlsno(jj)+1,0 - h2osoi_ice(jj,i) = h2osoi_ice(jj,i) * incr_h2osno - end do + if ( ABS(SUM(rsnow(:) - clm_statevec(cc+offset))).gt.0.000001) then + h2osno(jj) = clm_statevec(cc+offset) + if ( clmupdate_snow_repartitioning.eq.3) then + incr_h2osno = h2osno(jj) / rsnow(jj) ! INC = New SWE / OLD SWE + do i=snlsno(jj)+1,0 + h2osoi_ice(jj,i) = h2osoi_ice(jj,i) * incr_h2osno + end do + end if end if endif end do From eafedfa50372ed043de97792623831c613af5728 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Wed, 22 May 2024 19:10:13 +0200 Subject: [PATCH 19/71] dco: mention size limit for `obs_filename` --- doc/content/setup_tsmp/input_cmd.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/content/setup_tsmp/input_cmd.md b/doc/content/setup_tsmp/input_cmd.md index c4201a2f7..0935c37b3 100644 --- a/doc/content/setup_tsmp/input_cmd.md +++ b/doc/content/setup_tsmp/input_cmd.md @@ -48,6 +48,11 @@ filter. See [Command Line Examples](#command-line-examples). `obs_filename` (string) Prefix for observation files. +Note: The prefix for observation files cannot be longer than 100 +characters! Otherwise, the observation filename will be cropped. In +these cases, usually an error message `No such file or directory` is +thrown in `next_observation_pdaf.F90`. + ## rms_obs ## `rms_obs` (real) Measurement error. This is the standard deviation of From 6d4cdb4d94b3554cd3dd20ebe2dc61b5c46f834c Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Thu, 23 May 2024 10:06:44 +0200 Subject: [PATCH 20/71] doc: update CLM5-PDAF build-commands --- doc/content/build_tsmp/build_examples_tsmppdaf.md | 5 ++--- doc/content/feat_branch/clm5-pdaf_starter.md | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/content/build_tsmp/build_examples_tsmppdaf.md b/doc/content/build_tsmp/build_examples_tsmppdaf.md index d0f61770c..ac0041325 100644 --- a/doc/content/build_tsmp/build_examples_tsmppdaf.md +++ b/doc/content/build_tsmp/build_examples_tsmppdaf.md @@ -111,11 +111,10 @@ The `-g` flag, is build to produce debugging information. ## Compile CLM5 with PDAF ## -CLM5-PDAF compilation, currently available only on branch -`TSMP_pdaf-clm5` (January 2023). +CLM5-PDAF compilation. ``` bash - ./build_tsmp.ksh -v 4.4.0MCTPDAF -c clm -m JURECA -O Intel +./build_tsmp.ksh -c clm5-pdaf -m JURECA -O Intel ``` ### Prerequisite1: CLM5 preparation ### diff --git a/doc/content/feat_branch/clm5-pdaf_starter.md b/doc/content/feat_branch/clm5-pdaf_starter.md index d145eb649..0f09834c4 100644 --- a/doc/content/feat_branch/clm5-pdaf_starter.md +++ b/doc/content/feat_branch/clm5-pdaf_starter.md @@ -74,7 +74,7 @@ export CSMDATA=$CESMDATAROOT/inputdata In the `tsmp/bldsva` directory: ```bash -./build_tsmp.ksh -c clm5 -m JURECA -O Intel +./build_tsmp.ksh -c clm5-pdaf -m JURECA -O Intel ``` Potential problems that can happen during this step: From 1377681777e9f457bac0a7333e4855a69fe6b632 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Thu, 23 May 2024 12:32:28 +0200 Subject: [PATCH 21/71] intf_oas3/clm5_0: update `NODENAME_REGEX` for JUWELS --- .../clm5_0/tsmp/cime/config/cesm/machines/config_machines.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bldsva/intf_oas3/clm5_0/tsmp/cime/config/cesm/machines/config_machines.xml b/bldsva/intf_oas3/clm5_0/tsmp/cime/config/cesm/machines/config_machines.xml index 27f2ab553..678a4065b 100644 --- a/bldsva/intf_oas3/clm5_0/tsmp/cime/config/cesm/machines/config_machines.xml +++ b/bldsva/intf_oas3/clm5_0/tsmp/cime/config/cesm/machines/config_machines.xml @@ -2375,7 +2375,7 @@ This allows using a different mpirun command to launch unit tests JSC JUELICH platform, os is linux, 48 pes/node, batch system is slurm - juwels.* + .*.juwels LINUX intel mpi From 9b2bd825c2ea3a9c06e3f336ab902a13e7c5c944 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Thu, 23 May 2024 14:42:30 +0200 Subject: [PATCH 22/71] doc: updating CLM5 build and setup commands --- doc/content/setup_tsmp/setup_examples.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/doc/content/setup_tsmp/setup_examples.md b/doc/content/setup_tsmp/setup_examples.md index fd04c2475..aae4d324a 100644 --- a/doc/content/setup_tsmp/setup_examples.md +++ b/doc/content/setup_tsmp/setup_examples.md @@ -151,11 +151,10 @@ By Lukas Strebel Cloning `TSMP` and `CLM5` ``` bash - git clone https://icg4geo.icg.kfa-juelich.de/ExternalRepos/tsmp-pdaf/tsmp.git TSMP + git clone https://github.com/HPSCTerrSys/TSMP cd TSMP - git checkout clm5-coupling - git clone -b release-clm5.0.29 https://github.com/ESCOMP/ctsm.git clm5_0 + git clone -b release-clm5.0.37 https://github.com/ESCOMP/ctsm.git clm5_0 cd clm5_0 ./manage_externals/checkout_externals cd .. @@ -164,9 +163,11 @@ Cloning `TSMP` and `CLM5` Build and setup commands ``` bash cd bldsva - ./build_tsmp.ksh -v 4.4.0MCT -c clm -m JUWELS -O Intel - # wait until build is finished - can take some time - ./setup_tsmp.ksh -v 4.4.0MCT -c clm -V nrw_5x -m JUWELS -O Intel + ./build_tsmp.ksh -c clm5 -m JUWELS -O Intel + # wait until build is finished - can take some + + # Setup command (needs to be checked) + ./setup_tsmp.ksh -c clm5 -V nrw_5x -m JUWELS -O Intel cd #Rundir given as last output line # Change number of tasks / cores: From fdfdaad960dbc27e1c7fb2328df03ee1ab721847 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Thu, 23 May 2024 15:25:17 +0200 Subject: [PATCH 23/71] clm5 debug output of state vector, first implementation - `tstartcycle`, `mype` read from C-code as input, not very elegant. --- .../intf_DA/pdaf/model/clm5_0/enkf_clm_5.F90 | 6 ++-- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 31 +++++++++++++++++-- bldsva/intf_DA/pdaf/model/common/enkf.h | 4 +-- bldsva/intf_DA/pdaf/model/wrapper_tsmp.c | 4 +-- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_5.F90 index efd2ec41f..07efc2048 100644 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_5.F90 @@ -715,7 +715,7 @@ end subroutine clm5_init !end subroutine cime_comp_barriers -subroutine clm_advance(ntstep) bind(C,name="clm_advance") +subroutine clm_advance(ntstep, tstartcycle, mype) bind(C,name="clm_advance") use seq_comm_mct, only: atm_layout, lnd_layout, ice_layout, glc_layout, & rof_layout, ocn_layout, wav_layout, esp_layout use shr_string_mod, only: shr_string_listGetIndexF @@ -730,6 +730,8 @@ subroutine clm_advance(ntstep) bind(C,name="clm_advance") implicit none integer(c_int),intent(in) :: ntstep + integer(c_int),intent(in) :: tstartcycle + integer(c_int),intent(in) :: mype integer :: counter=0 integer :: nstep @@ -2641,7 +2643,7 @@ subroutine clm_advance(ntstep) bind(C,name="clm_advance") !|--------------------------------------------------------- ! Calling PDAF Function to set state vector before assimiliation - call set_clm_statevec() + call set_clm_statevec(tstartcycle, mype) call t_startf ('CPL:RUN_LOOP_BSTOP') call mpi_barrier(mpicom_GLOID,ierr) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 72fb21688..9d1ae442d 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -108,16 +108,19 @@ subroutine define_clm_statevec() end subroutine - subroutine set_clm_statevec() + subroutine set_clm_statevec(tstartcycle, mype) use clm_instMod, only : soilstate_inst, waterstate_inst use clm_varpar , only : nlevsoi use shr_kind_mod, only: r8 => shr_kind_r8 implicit none + integer,intent(in) :: tstartcycle + integer,intent(in) :: mype real(r8), pointer :: swc(:,:) real(r8), pointer :: psand(:,:) real(r8), pointer :: pclay(:,:) real(r8), pointer :: porgm(:,:) integer :: i,j,cc=1,offset=0 + character (len = 34) :: fn !TSMP-PDAF: function name for state vector output swc => waterstate_inst%h2osoi_vol_col psand => soilstate_inst%cellsand_col @@ -158,9 +161,19 @@ subroutine set_clm_statevec() end do endif +#ifdef PDAF_DEBUG + ! TSMP-PDAF: For debug runs, output the state vector in files + WRITE(fn, "(a,i5.5,a,i5.5,a)") "clmstate_", mype, ".integrate.", tstartcycle + 1, ".txt" + OPEN(unit=71, file=fn, action="write") + DO i = 1, clm_statevecsize + WRITE (71,"(f12.8)") clm_statevec(i) + END DO + CLOSE(71) +#endif + end subroutine - subroutine update_clm() bind(C,name="update_clm") + subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") use clm_varpar , only : nlevsoi use shr_kind_mod , only : r8 => shr_kind_r8 use ColumnType , only : col @@ -169,6 +182,9 @@ subroutine update_clm() bind(C,name="update_clm") implicit none + integer,intent(in) :: tstartcycle + integer,intent(in) :: mype + real(r8), pointer :: swc(:,:) real(r8), pointer :: watsat(:,:) real(r8), pointer :: psand(:,:) @@ -181,6 +197,17 @@ subroutine update_clm() bind(C,name="update_clm") real(r8) :: rliq,rice integer :: i,j,cc=1,offset=0 + character (len = 31) :: fn !TSMP-PDAF: function name for state vector outpu + +#ifdef PDAF_DEBUG + ! TSMP-PDAF: For debug runs, output the state vector in files + WRITE(fn, "(a,i5.5,a,i5.5,a)") "clmstate_", mype, ".update.", step, ".txt" + OPEN(unit=71, file=fn, action="write") + DO i = 1, clm_statevecsize + WRITE (71,"(f12.8)") clm_statevec(i) + END DO + CLOSE(71) +#endif swc => waterstate_inst%h2osoi_vol_col watsat => soilstate_inst%watsat_col diff --git a/bldsva/intf_DA/pdaf/model/common/enkf.h b/bldsva/intf_DA/pdaf/model/common/enkf.h index 4ed79dd83..981f7a958 100755 --- a/bldsva/intf_DA/pdaf/model/common/enkf.h +++ b/bldsva/intf_DA/pdaf/model/common/enkf.h @@ -42,8 +42,8 @@ extern void clm_init(char *s); #ifdef CLMFIVE extern void clm5_init(char *s, int *pdaf_id, int *pdaf_max); #endif -extern void clm_advance(int *ntstep); -extern void update_clm(); +extern void clm_advance(int *ntstep, int *tstartcycle, int *mype); +extern void update_clm(int *tstartcycle, int *mype); #if defined CLMSA extern void print_update_clm(int *ts, int *ttot); #endif diff --git a/bldsva/intf_DA/pdaf/model/wrapper_tsmp.c b/bldsva/intf_DA/pdaf/model/wrapper_tsmp.c index baeda1759..b0c97cf85 100644 --- a/bldsva/intf_DA/pdaf/model/wrapper_tsmp.c +++ b/bldsva/intf_DA/pdaf/model/wrapper_tsmp.c @@ -144,7 +144,7 @@ void integrate_tsmp() { } /* Integrate CLM */ - clm_advance(&tsclm); + clm_advance(&tsclm, &tstartcycle, &mype_world); /* Debug output */ if (screen_wrapper > 1 && task_id==1) { @@ -204,7 +204,7 @@ void update_tsmp(){ #if defined CLMSA if((model == tag_model_clm) && ((clmupdate_swc != 0) || (clmupdate_T != 0))){ - update_clm(); + update_clm(&tstartcycle, &mype_world); print_update_clm(&tcycle, &total_steps); } #endif From 0fe61423abfc6c911174b954a6e28ae077d7db77 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Thu, 23 May 2024 15:36:48 +0200 Subject: [PATCH 24/71] bugfix: use `tstartcycle` --- bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 9d1ae442d..8d8efbc9a 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -201,7 +201,7 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") #ifdef PDAF_DEBUG ! TSMP-PDAF: For debug runs, output the state vector in files - WRITE(fn, "(a,i5.5,a,i5.5,a)") "clmstate_", mype, ".update.", step, ".txt" + WRITE(fn, "(a,i5.5,a,i5.5,a)") "clmstate_", mype, ".update.", tstartcycle, ".txt" OPEN(unit=71, file=fn, action="write") DO i = 1, clm_statevecsize WRITE (71,"(f12.8)") clm_statevec(i) From e10c31f87be484ae0c318f35b578a5ac64d2ac52 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Thu, 23 May 2024 17:20:22 +0200 Subject: [PATCH 25/71] clm5 debug output of obs_index_p, first implementation --- bldsva/intf_DA/pdaf/framework/init_dim_obs_pdaf.F90 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/bldsva/intf_DA/pdaf/framework/init_dim_obs_pdaf.F90 b/bldsva/intf_DA/pdaf/framework/init_dim_obs_pdaf.F90 index 02c6cea81..18259b3a5 100755 --- a/bldsva/intf_DA/pdaf/framework/init_dim_obs_pdaf.F90 +++ b/bldsva/intf_DA/pdaf/framework/init_dim_obs_pdaf.F90 @@ -170,6 +170,8 @@ SUBROUTINE init_dim_obs_pdaf(step, dim_obs_p) #endif #endif + character (len = 27) :: fn !TSMP-PDAF: function name for obs_index_p output + ! **************************************** ! *** Initialize observation dimension *** ! **************************************** @@ -866,6 +868,16 @@ SUBROUTINE init_dim_obs_pdaf(step, dim_obs_p) end if +#ifdef PDAF_DEBUG + ! TSMP-PDAF: For debug runs, output the state vector in files + WRITE(fn, "(a,i5.5,a,i5.5,a)") "obs_index_p_", mype_world, ".", step, ".txt" + OPEN(unit=71, file=fn, action="write") + DO i = 1, dim_obs_p + WRITE (71,"(i10)") obs_index_p(i) + END DO + CLOSE(71) +#endif + end if end if call mpi_allreduce(MPI_IN_PLACE,obs_nc2pdaf,dim_obs,MPI_INTEGER,MPI_SUM,comm_filter,ierror) From 3f7eddbba37a2b8b8770cb5c46181caf8600ac1c Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 24 May 2024 12:45:37 +0200 Subject: [PATCH 26/71] debug output of `clm_statevec` to double precision --- bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 8d8efbc9a..ff166db26 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -166,7 +166,7 @@ subroutine set_clm_statevec(tstartcycle, mype) WRITE(fn, "(a,i5.5,a,i5.5,a)") "clmstate_", mype, ".integrate.", tstartcycle + 1, ".txt" OPEN(unit=71, file=fn, action="write") DO i = 1, clm_statevecsize - WRITE (71,"(f12.8)") clm_statevec(i) + WRITE (71,"(f20.15)") clm_statevec(i) END DO CLOSE(71) #endif @@ -204,7 +204,7 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") WRITE(fn, "(a,i5.5,a,i5.5,a)") "clmstate_", mype, ".update.", tstartcycle, ".txt" OPEN(unit=71, file=fn, action="write") DO i = 1, clm_statevecsize - WRITE (71,"(f12.8)") clm_statevec(i) + WRITE (71,"(f20.15)") clm_statevec(i) END DO CLOSE(71) #endif From 1839d3e365f52cf632da2c64d631192f7cc0dc0f Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 5 Jul 2024 13:24:57 +0200 Subject: [PATCH 27/71] correct end subroutine names --- bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 395360e95..22f68bf7c 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -650,7 +650,7 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") end if endif - end subroutine + end subroutine update_clm subroutine clm_repartition_snow(h2osno_in) use ColumnType, only : col @@ -818,7 +818,7 @@ subroutine clm_repartition_snow(h2osno_in) end if ! End of snow present check end do ! End of column iteration - end subroutine update_clm + end subroutine clm_repartition_snow subroutine clm_correct_texture() From 0f5b80e802157a95b31e8ac4dc120ce3d0563c92 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 5 Jul 2024 13:27:50 +0200 Subject: [PATCH 28/71] remove `clmupdate_texture.eq.2` conditions should be carried out inside `clmupdate_texture.ne.0` --- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 43 ------------------- 1 file changed, 43 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 22f68bf7c..96abe8670 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -271,30 +271,6 @@ subroutine set_clm_statevec(tstartcycle, mype) end do endif - ! write texture incl. organic matter values to state vector (if desired) - if(clmupdate_texture.eq.2) then - cc = 1 - do i=1,nlevsoi - do j=clm_begg,clm_endg - ! Only get the parameters from the first column of each gridcell - ! and add it to the clm_statevec at the position of the gridcell (cc) - newgridcell = .true. - do jj=clm_begc,clm_endc - g = col%gridcell(jj) - if (g .eq. j) then - if (newgridcell) then - newgridcell = .false. - clm_statevec(cc+1*clm_varsize+offset) = psand(jj,i) - clm_statevec(cc+2*clm_varsize+offset) = pclay(jj,i) - clm_statevec(cc+3*clm_varsize+offset) = porgm(jj,i) - endif - endif - end do - cc = cc + 1 - end do - end do - endif - ! Snow assimilation ! Case 1: Snow depth if(clmupdate_snow.eq.1) then @@ -571,25 +547,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") call clm_texture_to_parameters endif - ! write updated texture incl. organic matter back to CLM - if(clmupdate_texture.eq.2) then - cc = 1 - do i=1,nlevsoi - do j=clm_begg,clm_endg - ! iterate through the columns and copy from the same gridcell - ! i.e. statevec position (cc) for each column - do jj=clm_begc,clm_endc - psand(jj,i) = clm_statevec(cc+1*clm_varsize+offset) - pclay(jj,i) = clm_statevec(cc+2*clm_varsize+offset) - porgm(jj,i) = clm_statevec(cc+3*clm_varsize+offset) - end do - cc = cc + 1 - end do - end do - call clm_correct_texture - call clm_texture_to_parameters - endif - ! Snow assimilation: ! Case 1: Snow depth ! Write updated snow depth back to CLM and then repartition snow and adjust related variables From 2902a13cea0e282f6805e662c67f516c35ccc089 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 5 Jul 2024 13:28:33 +0200 Subject: [PATCH 29/71] remove additional `newgridcell` --- bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 2 -- 1 file changed, 2 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 96abe8670..003c1cbd0 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -74,8 +74,6 @@ module enkf_clm_mod integer :: COMM_couple_clm ! CLM-version of COMM_couple ! (currently not used for clm5_0) logical :: newgridcell !only clm5_0 - - logical :: newgridcell contains #if defined CLMSA From 8c417df48ed745c33e0b89b5c95d1c63f32091be Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 5 Jul 2024 13:36:08 +0200 Subject: [PATCH 30/71] add `clmt_printensemble` condition to debug output --- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 003c1cbd0..793f445d2 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -74,6 +74,7 @@ module enkf_clm_mod integer :: COMM_couple_clm ! CLM-version of COMM_couple ! (currently not used for clm5_0) logical :: newgridcell !only clm5_0 + contains #if defined CLMSA @@ -310,13 +311,15 @@ subroutine set_clm_statevec(tstartcycle, mype) endif #ifdef PDAF_DEBUG - ! TSMP-PDAF: For debug runs, output the state vector in files - WRITE(fn, "(a,i5.5,a,i5.5,a)") "clmstate_", mype, ".integrate.", tstartcycle + 1, ".txt" - OPEN(unit=71, file=fn, action="write") - DO i = 1, clm_statevecsize - WRITE (71,"(f20.15)") clm_statevec(i) - END DO - CLOSE(71) + IF(clmt_printensemble == tstartcycle + 1 .OR. clmt_printensemble < 0) THEN + ! TSMP-PDAF: For debug runs, output the state vector in files + WRITE(fn, "(a,i5.5,a,i5.5,a)") "clmstate_", mype, ".integrate.", tstartcycle + 1, ".txt" + OPEN(unit=71, file=fn, action="write") + DO i = 1, clm_statevecsize + WRITE (71,"(f20.15)") clm_statevec(i) + END DO + CLOSE(71) + END IF #endif end subroutine set_clm_statevec From 042d43dfe3baf10bacc74060a5c84fc7e83720ad Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 5 Jul 2024 15:37:10 +0200 Subject: [PATCH 31/71] remove double code of debug output --- bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 9 --------- 1 file changed, 9 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 793f445d2..c21d5f409 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -377,15 +377,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") integer, pointer :: snlsno(:) -#ifdef PDAF_DEBUG - ! TSMP-PDAF: For debug runs, output the state vector in files - WRITE(fn, "(a,i5.5,a,i5.5,a)") "clmstate_", mype, ".update.", tstartcycle, ".txt" - OPEN(unit=71, file=fn, action="write") - DO i = 1, clm_statevecsize - WRITE (71,"(f20.15)") clm_statevec(i) - END DO - CLOSE(71) -#endif swc => waterstate_inst%h2osoi_vol_col watsat => soilstate_inst%watsat_col psand => soilstate_inst%cellsand_col From 788b595fc415cbb3406e6787e45a25c18a477e6a Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 5 Jul 2024 15:37:23 +0200 Subject: [PATCH 32/71] remove `snlsno` specification statement from executable section --- bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index c21d5f409..1a81b0518 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -363,6 +363,8 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") character (len = 32) :: fn5 !TSMP-PDAF: function name for state vector outpu character (len = 32) :: fn6 !TSMP-PDAF: function name for state vector outpu + integer, pointer :: snlsno(:) + #ifdef PDAF_DEBUG IF(clmt_printensemble == tstartcycle .OR. clmt_printensemble < 0) THEN ! TSMP-PDAF: For debug runs, output the state vector in files @@ -375,8 +377,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") END IF #endif - integer, pointer :: snlsno(:) - swc => waterstate_inst%h2osoi_vol_col watsat => soilstate_inst%watsat_col psand => soilstate_inst%cellsand_col From 1ab01f6e79f7e090d5a7381701df531a97811558 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Mon, 8 Jul 2024 18:56:25 +0200 Subject: [PATCH 33/71] copy out `average_swc_crp` The following warning is thrown during compilation: ``` clm5_0/enkf_clm_mod_5.F90(966): warning #6843: A dummy argument with an explicit INTENT(OUT) declaration is not given an explicit value. [PROFAVE] ``` --- .../intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 1a81b0518..ec55cabae 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -963,16 +963,16 @@ subroutine clm_texture_to_parameters() end do end subroutine clm_texture_to_parameters - subroutine average_swc_crp(profdat,profave) - use clm_varcon , only : zsoi + ! subroutine average_swc_crp(profdat,profave) + ! use clm_varcon , only : zsoi - implicit none + ! implicit none - real(r8),intent(in) :: profdat(10) - real(r8),intent(out) :: profave + ! real(r8),intent(in) :: profdat(10) + ! real(r8),intent(out) :: profave - error stop "Not implemented average_swc_crp" - end subroutine average_swc_crp + ! error stop "Not implemented average_swc_crp" + ! end subroutine average_swc_crp #endif subroutine domain_def_clm(lon_clmobs, lat_clmobs, dim_obs, & From 2206815f9f0bcf9b03237c34fa9c0e0dac56b3cc Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Mon, 8 Jul 2024 18:58:43 +0200 Subject: [PATCH 34/71] remove use-statement for `mod_parallel_model` `ifort` error message: ``` mod_read_obs.F90(26): error #7002: Error in opening the compiled module file. Check INCLUDE paths. [MOD_PARALLEL_MODEL] ``` --- bldsva/intf_DA/pdaf/framework/mod_read_obs.F90 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bldsva/intf_DA/pdaf/framework/mod_read_obs.F90 b/bldsva/intf_DA/pdaf/framework/mod_read_obs.F90 index ae0fcd54b..8e03a2033 100755 --- a/bldsva/intf_DA/pdaf/framework/mod_read_obs.F90 +++ b/bldsva/intf_DA/pdaf/framework/mod_read_obs.F90 @@ -23,8 +23,6 @@ !------------------------------------------------------------------------------------------- module mod_read_obs - use mod_parallel_model, only: tcycle - use iso_C_binding implicit none @@ -335,7 +333,7 @@ subroutine read_obs_nc(current_observation_filename) call check(nf90_get_var(ncid, clmobs_varid, clm_obs)) if (screen > 2) then - print *, "TSMP-PDAF mype(w)=", mype_world, ": clm_obs=", clm_obs, "at time", tcycle + print *, "TSMP-PDAF mype(w)=", mype_world, ": clm_obs=", clm_obs end if !check, if observation errors are present in observation file From 264f2101b3591cf82fee27ab35fdbd7336b45a81 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 12 Jul 2024 09:44:26 +0200 Subject: [PATCH 35/71] some documentation --- bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index ec55cabae..d54b6bb95 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -581,6 +581,9 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") rsnow(jj) = h2osno(jj) if ( ABS(SUM(rsnow(:) - clm_statevec(cc+offset))).gt.0.000001) then h2osno(jj) = clm_statevec(cc+offset) + ! JK: clmupdate_snow_repartitioning.eq.3 is experimental + ! JK: clmupdate_snow_repartitioning.eq.3 from NASA-Code (based on older CLM3.5 version) + ! https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 if ( clmupdate_snow_repartitioning.eq.3) then incr_h2osno = h2osno(jj) / rsnow(jj) ! INC = New SWE / OLD SWE do i=snlsno(jj)+1,0 @@ -655,6 +658,7 @@ subroutine clm_repartition_snow(h2osno_in) else ! snow (h2osno) present if (snlsno(jj).lt.0) then ! snow layers in the column if (clmupdate_snow .eq. 1) then + ! DART source: https://github.com/NCAR/DART/blob/main/models/clm/dart_to_clm.f90 ! Formulas below from DART use h2osno_po / h2osno_pr for after / before DA SWE ! clmupdate_snow == 1 has snow_depth after and h2osno before DA snow depth ! Therefore need to have a transform to get h2osno_po @@ -692,6 +696,8 @@ subroutine clm_repartition_snow(h2osno_in) if(clmupdate_snow_repartitioning.eq.1) then if (ii .eq. 0) then ! DART version indexing check against nlevsno but for us 0 frac_swe = 1.0_r8 + ! JK: Let CLM repartitioning do the job + ! afterwards. Provide all the snow in a single layer else frac_swe = 0.0_r8 end if From c99f6c42bf88c3b291cb592fda7f4ac8911d7273 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 12 Jul 2024 09:45:33 +0200 Subject: [PATCH 36/71] remove outdated observation-fix --- bldsva/intf_DA/pdaf/framework/init_dim_obs_pdaf.F90 | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/bldsva/intf_DA/pdaf/framework/init_dim_obs_pdaf.F90 b/bldsva/intf_DA/pdaf/framework/init_dim_obs_pdaf.F90 index e358fae4b..f5d2c691f 100755 --- a/bldsva/intf_DA/pdaf/framework/init_dim_obs_pdaf.F90 +++ b/bldsva/intf_DA/pdaf/framework/init_dim_obs_pdaf.F90 @@ -479,22 +479,10 @@ SUBROUTINE init_dim_obs_pdaf(step, dim_obs_p) #endif #endif - - ! DEBUG / TESTING ALL OBSERVATIONS ON TASK 0 - if (mype_filter == 0) then - dim_obs_p = dim_obs - else - dim_obs_p = 0 - end if - - - if (screen > 2) then print *, "TSMP-PDAF mype(w)=", mype_world, ": init_dim_obs_pdaf: dim_obs_p=", dim_obs_p end if - - ! add and broadcast size of local observation dimensions using mpi_allreduce call mpi_allreduce(dim_obs_p, sum_dim_obs_p, 1, MPI_INTEGER, MPI_SUM, & comm_filter, ierror) From 008f16b988cbb7baa133500aadda77b364208e40 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 12 Jul 2024 09:46:32 +0200 Subject: [PATCH 37/71] style changes as in master-branch --- bldsva/intf_DA/pdaf/framework/mod_read_obs.F90 | 2 +- bldsva/intf_DA/pdaf/framework/obs_op_pdaf.F90 | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/bldsva/intf_DA/pdaf/framework/mod_read_obs.F90 b/bldsva/intf_DA/pdaf/framework/mod_read_obs.F90 index 8e03a2033..1e9ffda52 100755 --- a/bldsva/intf_DA/pdaf/framework/mod_read_obs.F90 +++ b/bldsva/intf_DA/pdaf/framework/mod_read_obs.F90 @@ -23,7 +23,7 @@ !------------------------------------------------------------------------------------------- module mod_read_obs - use iso_C_binding + use iso_C_binding implicit none integer, allocatable :: idx_obs_nc(:), x_idx_obs_nc(:), y_idx_obs_nc(:), z_idx_obs_nc(:), var_id_obs_nc(:,:) diff --git a/bldsva/intf_DA/pdaf/framework/obs_op_pdaf.F90 b/bldsva/intf_DA/pdaf/framework/obs_op_pdaf.F90 index 0467bcbb8..8907c331a 100644 --- a/bldsva/intf_DA/pdaf/framework/obs_op_pdaf.F90 +++ b/bldsva/intf_DA/pdaf/framework/obs_op_pdaf.F90 @@ -106,7 +106,6 @@ SUBROUTINE obs_op_pdaf(step, dim_p, dim_obs_p, state_p, m_state_p) ! *** operator H on vector or matrix column *** ! ********************************************* - ! If no special observation operator is compiled, use point observations lpointobs = .true. From c97b42e07022fe09a434ba2331b4a8eb6ce0a316 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Thu, 18 Jul 2024 12:26:36 +0200 Subject: [PATCH 38/71] `clmstatevec_allcol` for SWE update --- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 77 ++++++++++++------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 6a38ba011..dd2ea41cb 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -293,21 +293,35 @@ subroutine set_clm_statevec(tstartcycle, mype) ! Case 2: SWE if(clmupdate_snow.eq.2) then cc = 1 - do j=clm_begg,clm_endg - ! Only get the SWE from the first column of each gridcell - ! and add it to the clm_statevec at the position of the gridcell (cc) - newgridcell = .true. - do jj=clm_begc,clm_endc - g = col%gridcell(jj) - if (g .eq. j) then - if (newgridcell) then - newgridcell = .false. - clm_statevec(cc+offset) = h2osno(jj) - endif - endif - end do - cc = cc + 1 - end do + + if(clmstatevec_allcol.eq.0) then + + do j=clm_begg,clm_endg + ! Only get the SWE from the first column of each gridcell + ! and add it to the clm_statevec at the position of the gridcell (cc) + newgridcell = .true. + do jj=clm_begc,clm_endc + g = col%gridcell(jj) + if (g .eq. j) then + if (newgridcell) then + newgridcell = .false. + clm_statevec(cc+offset) = h2osno(jj) + endif + endif + end do + cc = cc + 1 + end do + + else + + do jj=clm_begc,clm_endc + ! SWC from all columns of each gridcell + clm_statevec(cc+offset) = h2osno(jj) + cc = cc + 1 + end do + + end if + endif #ifdef PDAF_DEBUG @@ -572,32 +586,43 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! Case 2: Snow water equivalent ! Write updated snow depth back to CLM and then repartition snow and adjust related variables if(clmupdate_snow.eq.2) then - cc = 1 - do j=clm_begg,clm_endg + ! cc = 1 + ! do j=clm_begg,clm_endg ! iterate through the columns and copy from the same gridcell ! i.e. statevec position (cc) for each column - do jj=clm_begc,clm_endc + do j=clm_begc,clm_endc + + ! Set cc (the state vector index) from the + ! CLM5-grid-index and the `CLM5-layer-index times + ! num_gridcells` + if(clmstatevec_allcol.eq.0) then + cc = (col%gridcell(j) - clm_begg + 1) + else + cc = (j - clm_begc + 1) + end if + ! Catch negative or 0 values from DA if (clm_statevec(cc+offset).lt.0.0) then - print *, "WARNING: SWE at g,c is negative: ", j, jj, clm_statevec(cc+offset) + print *, "WARNING: SWE at g,c is negative: ", j, clm_statevec(cc+offset) else - rsnow(jj) = h2osno(jj) + rsnow(j) = h2osno(j) if ( ABS(SUM(rsnow(:) - clm_statevec(cc+offset))).gt.0.000001) then - h2osno(jj) = clm_statevec(cc+offset) + h2osno(j) = clm_statevec(cc+offset) ! JK: clmupdate_snow_repartitioning.eq.3 is experimental ! JK: clmupdate_snow_repartitioning.eq.3 from NASA-Code (based on older CLM3.5 version) ! https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 if ( clmupdate_snow_repartitioning.eq.3) then - incr_h2osno = h2osno(jj) / rsnow(jj) ! INC = New SWE / OLD SWE - do i=snlsno(jj)+1,0 - h2osoi_ice(jj,i) = h2osoi_ice(jj,i) * incr_h2osno + incr_h2osno = h2osno(j) / rsnow(j) ! INC = New SWE / OLD SWE + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno end do end if end if endif end do - cc = cc + 1 - end do + ! cc = cc + 1 + ! end do + if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then if ( ABS(SUM(rsnow(:) - h2osno(:))).gt.0.000001) then call clm_repartition_snow(rsnow(:)) From 36aa08284294e1df02c953440c60f91c4a99a475 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Wed, 30 Oct 2024 12:01:22 +0100 Subject: [PATCH 39/71] Snow-DA: Warn also for zero SWE --- bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index dd2ea41cb..955904801 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -602,8 +602,8 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") end if ! Catch negative or 0 values from DA - if (clm_statevec(cc+offset).lt.0.0) then - print *, "WARNING: SWE at g,c is negative: ", j, clm_statevec(cc+offset) + if (clm_statevec(cc+offset).le.0.0) then + print *, "WARNING: SWE at g,c is negative or zero: ", j, clm_statevec(cc+offset) else rsnow(j) = h2osno(j) if ( ABS(SUM(rsnow(:) - clm_statevec(cc+offset))).gt.0.000001) then From 988148a8ed09984284a9d026f393e7d8c479183c Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Wed, 30 Oct 2024 12:01:38 +0100 Subject: [PATCH 40/71] Snow-DA: more warnings during update --- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 955904801..e301cd4d3 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -617,6 +617,14 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno end do end if + + if (isnan(rsnow(j))) then + print *, "WARNING: rsnow at j is nan: ", j + endif + if (isnan(h2osno(j))) then + print *, "WARNING: h2osno at j is nan: ", j + endif + end if endif end do @@ -683,6 +691,14 @@ subroutine clm_repartition_snow(h2osno_in) end do snow_depth(jj) = sum(dzsno(jj,:)) h2osno(jj) = sum(h2oice(jj,:)) + + if (isnan(h2osno(jj))) then + print *, "WARNING: h2osno at jj is nan: ", jj + endif + if (isnan(snow_depth(jj))) then + print *, "WARNING: snow_depth at jj is nan: ", jj + endif + else ! snow (h2osno) present if (snlsno(jj).lt.0) then ! snow layers in the column if (clmupdate_snow .eq. 1) then @@ -798,6 +814,14 @@ subroutine clm_repartition_snow(h2osno_in) snow_depth(jj) = snow_depth(jj) + sum(gain_dzsno(-nlevsno+1:0)) end if end if + + if (isnan(h2osno(jj))) then + print *, "WARNING2: h2osno at jj is nan: ", jj + endif + if (isnan(snow_depth(jj))) then + print *, "WARNING2: snow_depth at jj is nan: ", jj + endif + end if ! End of snow present check end do ! End of column iteration From f367d8a3d9443c31c2b969d947c483ea9dba420f Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Tue, 4 Mar 2025 13:51:06 +0100 Subject: [PATCH 41/71] Snow-DA for eCLM --- .../pdaf/model/eclm/enkf_clm_mod_5.F90 | 355 +++++++++++++++++- 1 file changed, 354 insertions(+), 1 deletion(-) diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index c0b89c299..1e7cea324 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -50,6 +50,8 @@ module enkf_clm_mod integer(c_int),bind(C,name="clmupdate_swc") :: clmupdate_swc integer(c_int),bind(C,name="clmupdate_T") :: clmupdate_T ! by hcp integer(c_int),bind(C,name="clmupdate_texture") :: clmupdate_texture + integer(c_int),bind(C,name="clmupdate_snow") :: clmupdate_snow + integer(c_int),bind(C,name="clmupdate_snow_repartitioning") :: clmupdate_snow_repartitioning integer(c_int),bind(C,name="clmprint_swc") :: clmprint_swc #endif integer(c_int),bind(C,name="clmprint_et") :: clmprint_et @@ -270,6 +272,19 @@ subroutine define_clm_statevec(mype) clm_statevecsize = clm_statevecsize + 3*((endg-begg+1)*nlevsoi) endif + ! Snow assimilation + ! Case 1: Assimilation of snow depth : allocated 1 per column in CLM5 + ! But observations and history file 1 per grid cell and therefore statevecsize 1 per grid cell + if(clmupdate_snow.eq.1) then + clm_varsize = (clm_endg-clm_begg+1) ! Currently no combination of SWC and snow DA + clm_statevecsize = (clm_endg-clm_begg+1) ! So like this if snow is set it takes priority + endif + ! Case 2: Assimilation of snow water equivalent same as above + if(clmupdate_snow.eq.2) then + clm_varsize = (clm_endg-clm_begg+1) ! Currently no combination of SWC and snow DA + clm_statevecsize = (clm_endg-clm_begg+1) ! So like this if snow is set it takes priority + endif + !hcp LST DA if(clmupdate_T.eq.1) then error stop "Not implemented: clmupdate_T.eq.1" @@ -322,6 +337,8 @@ subroutine set_clm_statevec(tstartcycle, mype) real(r8), pointer :: psand(:,:) real(r8), pointer :: pclay(:,:) real(r8), pointer :: porgm(:,:) + real(r8), pointer :: snow_depth(:) + real(r8), pointer :: h2osno(:) integer :: i,j,jj,g,cc=0,offset=0 character (len = 34) :: fn !TSMP-PDAF: function name for state vector output character (len = 34) :: fn2 !TSMP-PDAF: function name for swc output @@ -331,6 +348,9 @@ subroutine set_clm_statevec(tstartcycle, mype) pclay => soilstate_inst%cellclay_col porgm => soilstate_inst%cellorg_col + snow_depth => waterstate_inst%snow_depth_col ! snow height of snow covered area (m) + h2osno => waterstate_inst%h2osno_col ! snow water equivalent (mm) + #ifdef PDAF_DEBUG IF(clmt_printensemble == tstartcycle + 1 .OR. clmt_printensemble < 0) THEN @@ -384,6 +404,60 @@ subroutine set_clm_statevec(tstartcycle, mype) end do endif + ! Snow assimilation + ! Case 1: Snow depth + if(clmupdate_snow.eq.1) then + cc = 1 + do j=clm_begg,clm_endg + ! Only get the snow_depth from the first column of each gridcell + ! and add it to the clm_statevec at the position of the gridcell (cc) + newgridcell = .true. + do jj=clm_begc,clm_endc + g = col%gridcell(jj) + if (g .eq. j) then + if (newgridcell) then + newgridcell = .false. + clm_statevec(cc+offset) = snow_depth(jj) + endif + endif + end do + cc = cc + 1 + end do + endif + ! Case 2: SWE + if(clmupdate_snow.eq.2) then + cc = 1 + + if(clmstatevec_allcol.eq.0) then + + do j=clm_begg,clm_endg + ! Only get the SWE from the first column of each gridcell + ! and add it to the clm_statevec at the position of the gridcell (cc) + newgridcell = .true. + do jj=clm_begc,clm_endc + g = col%gridcell(jj) + if (g .eq. j) then + if (newgridcell) then + newgridcell = .false. + clm_statevec(cc+offset) = h2osno(jj) + endif + endif + end do + cc = cc + 1 + end do + + else + + do jj=clm_begc,clm_endc + ! SWC from all columns of each gridcell + clm_statevec(cc+offset) = h2osno(jj) + cc = cc + 1 + end do + + end if + + endif + #ifdef PDAF_DEBUG IF(clmt_printensemble == tstartcycle + 1 .OR. clmt_printensemble < 0) THEN ! TSMP-PDAF: For debug runs, output the state vector in files @@ -419,10 +493,15 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") real(r8), pointer :: pclay(:,:) real(r8), pointer :: porgm(:,:) + real(r8), pointer :: snow_depth(:) + real(r8), pointer :: h2osno(:) + real(r8), pointer :: dz(:,:) ! layer thickness depth (m) real(r8), pointer :: h2osoi_liq(:,:) ! liquid water (kg/m2) real(r8), pointer :: h2osoi_ice(:,:) - real(r8) :: rliq,rice + + real(r8) :: rliq,rice,incr_h2osno + real(r8) :: rsnow(clm_statevecsize) real(r8) :: watmin_check ! minimum soil moisture for checking clm_statevec (mm) real(r8) :: watmin_set ! minimum soil moisture for setting swc (mm) real(r8) :: swc_update ! updated SWC in loop @@ -435,6 +514,7 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") character (len = 32) :: fn5 !TSMP-PDAF: function name for state vector outpu character (len = 32) :: fn6 !TSMP-PDAF: function name for state vector outpu + integer, pointer :: snlsno(:) logical :: swc_zero_before_update = .false. #ifdef PDAF_DEBUG @@ -455,9 +535,13 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") pclay => soilstate_inst%cellclay_col porgm => soilstate_inst%cellorg_col + snow_depth => waterstate_inst%snow_depth_col ! snow height of snow covered area (m) + h2osno => waterstate_inst%h2osno_col ! snow water equivalent (mm) + dz => col%dz h2osoi_liq => waterstate_inst%h2osoi_liq_col h2osoi_ice => waterstate_inst%h2osoi_ice_col + snlsno => col%snl ! number of snow layers (negative) #ifdef PDAF_DEBUG IF(clmt_printensemble == tstartcycle .OR. clmt_printensemble < 0) THEN @@ -634,8 +718,277 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") call clm_texture_to_parameters endif + ! Snow assimilation: + ! Case 1: Snow depth + ! Write updated snow depth back to CLM and then repartition snow and adjust related variables + if(clmupdate_snow.eq.1) then + cc = 1 + do j=clm_begg,clm_endg + ! iterate through the columns and copy from the same gridcell + ! i.e. statevec position (cc) for each column + do jj=clm_begc,clm_endc + ! Catch negative or 0 values from DA + if (clm_statevec(cc+offset).lt.0.0) then + print *, "WARNING: snow depth at g,c is negative: ", j, jj, clm_statevec(cc+offset) + else + rsnow(jj) = snow_depth(jj) - clm_statevec(cc+offset) + if ( ABS(SUM(rsnow(:))) .gt.0.000001) then + snow_depth(jj) = clm_statevec(cc+offset) + endif + endif + end do + cc = cc + 1 + end do + if ( clmupdate_snow_repartitioning.ne.0) then + if ( ABS(SUM(rsnow(:))).gt.0.000001) then + call clm_repartition_snow() + end if + end if + endif + ! Case 2: Snow water equivalent + ! Write updated snow depth back to CLM and then repartition snow and adjust related variables + if(clmupdate_snow.eq.2) then + ! cc = 1 + ! do j=clm_begg,clm_endg + ! iterate through the columns and copy from the same gridcell + ! i.e. statevec position (cc) for each column + do j=clm_begc,clm_endc + + ! Set cc (the state vector index) from the + ! CLM5-grid-index and the `CLM5-layer-index times + ! num_gridcells` + if(clmstatevec_allcol.eq.0) then + cc = (col%gridcell(j) - clm_begg + 1) + else + cc = (j - clm_begc + 1) + end if + + ! Catch negative or 0 values from DA + if (clm_statevec(cc+offset).le.0.0) then + print *, "WARNING: SWE at g,c is negative or zero: ", j, clm_statevec(cc+offset) + else + rsnow(j) = h2osno(j) + if ( ABS(SUM(rsnow(:) - clm_statevec(cc+offset))).gt.0.000001) then + h2osno(j) = clm_statevec(cc+offset) + ! JK: clmupdate_snow_repartitioning.eq.3 is experimental + ! JK: clmupdate_snow_repartitioning.eq.3 from NASA-Code (based on older CLM3.5 version) + ! https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 + if ( clmupdate_snow_repartitioning.eq.3) then + incr_h2osno = h2osno(j) / rsnow(j) ! INC = New SWE / OLD SWE + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno + end do + end if + + if (isnan(rsnow(j))) then + print *, "WARNING: rsnow at j is nan: ", j + endif + if (isnan(h2osno(j))) then + print *, "WARNING: h2osno at j is nan: ", j + endif + + end if + endif + end do + ! cc = cc + 1 + ! end do + + if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then + if ( ABS(SUM(rsnow(:) - h2osno(:))).gt.0.000001) then + call clm_repartition_snow(rsnow(:)) + end if + end if + endif + end subroutine update_clm + subroutine clm_repartition_snow(h2osno_in) + use ColumnType, only : col + use clm_instMod, only : waterstate_inst + use clm_varpar, only : nlevsno, nlevsoi + use clm_varcon, only : bdsno, denice + use shr_kind_mod, only : r8 => shr_kind_r8 + + implicit none + + real(r8), intent(in), optional :: h2osno_in(clm_begc:clm_endc) + + real(r8), pointer :: snow_depth(:) + real(r8), pointer :: h2osno(:) + real(r8), pointer :: h2oliq(:,:) + real(r8), pointer :: h2oice(:,:) + real(r8), pointer :: frac_sno(:) + real(r8), pointer :: snowdp(:) + integer, pointer :: snlsno(:) + + real(r8) :: dzsno(clm_begc:clm_endc,-nlevsno+1:0) + real(r8) :: h2osno_po(clm_begc:clm_endc) + real(r8) :: h2osno_pr(clm_begc:clm_endc) + real(r8) :: snowden, frac_swe, frac_liq, frac_ice + real(r8) :: gain_h2osno, gain_h2oliq, gain_h2oice + real(r8) :: gain_dzsno(-nlevsno+1:0) + real(r8) :: rho_avg, z_avg + integer :: i,ii,j,jj,g,cc=1,offset=0 + + snow_depth => waterstate_inst%snow_depth_col ! snow height of snow covered area (m) + snowdp => waterstate_inst%snowdp_col ! area-averaged snow height (m) + h2osno => waterstate_inst%h2osno_col ! col snow water (mm H2O) + h2oliq => waterstate_inst%h2osoi_liq_col ! col liquid water (kg/m2) (-nlevsno+1:nlevgrnd) + h2oice => waterstate_inst%h2osoi_ice_col ! col ice lens (kg/m2) (-nlevsno+1:nlevgrnd) + + snlsno => col%snl ! number of snow layers (negative) + + frac_sno => waterstate_inst%frac_sno_eff_col ! fraction of ground covered by snow + ! dz for snow layers is defined like in the history output as col%dz for the snow layers + dzsno(clm_begc:clm_endc, -nlevsno+1:0) = col%dz(clm_begc:clm_endc,-nlevsno+1:0) + ! Iterate through all columns + do jj=clm_begc,clm_endc + if (h2osno(jj).lt.0.0) then ! No snow in column + print *, "WARNING: negative snow in col: ", jj, h2osno +! ! Set existing layers to near zero and let CLM do the layer aggregation + do i=0,snlsno(jj)+1,-1 + h2oliq(jj,i) = 0.0_r8 + h2oice(jj,i) = 0.00000001_r8 + dzsno(jj,i) = 0.00000001_r8 + end do + snow_depth(jj) = sum(dzsno(jj,:)) + h2osno(jj) = sum(h2oice(jj,:)) + + if (isnan(h2osno(jj))) then + print *, "WARNING: h2osno at jj is nan: ", jj + endif + if (isnan(snow_depth(jj))) then + print *, "WARNING: snow_depth at jj is nan: ", jj + endif + + else ! snow (h2osno) present + if (snlsno(jj).lt.0) then ! snow layers in the column + if (clmupdate_snow .eq. 1) then + ! DART source: https://github.com/NCAR/DART/blob/main/models/clm/dart_to_clm.f90 + ! Formulas below from DART use h2osno_po / h2osno_pr for after / before DA SWE + ! clmupdate_snow == 1 has snow_depth after and h2osno before DA snow depth + ! Therefore need to have a transform to get h2osno_po + ! v1 init + ! h2osno_po(jj) = (snow_depth(jj) * bdsno) ! calculations from Init using constant SBD + ! v2 SoilTemperatureMod + if (snowdp(jj).gt.0.0_r8) then + rho_avg = min(800.0_r8, h2osno(jj)/snowdp(jj)) + else + rho_avg = 200.0_r8 + end if + if (frac_sno(jj).gt.0.0_r8 .and. snlsno(jj).lt.0.0_r8) then + h2osno_po(jj) = snow_depth(jj) * (rho_avg*frac_sno(jj)) + else + h2osno_po(jj) = snow_depth(jj) * denice + end if + h2osno_pr(jj) = h2osno(jj) + else if (clmupdate_snow .eq. 2) then + ! for clmupdate_snow == 2 we have post H2OSNO as the main H2OSNO already + h2osno_po(jj) = h2osno(jj) + h2osno_pr(jj) = h2osno_in(jj) + end if + + do ii=0,snlsno(jj)+1,-1 ! iterate through the snow layers + ! DART VERSION: ii = nlevsoi - i + 1 + ! snow density prior for each layer + if (dzsno(jj,ii).gt.0.0_r8) then + snowden = (h2oliq(jj,ii) + h2oice(jj,ii) / dzsno(jj,ii)) + else + snowden = 0.0_r8 + endif + ! fraction of SWE in each active layers + if(h2osno_pr(jj).gt.0.0_r8) then + ! repartition Option 1: Adjust bottom layer only (set weight to 1 for bottom 0 else) + if(clmupdate_snow_repartitioning.eq.1) then + if (ii .eq. 0) then ! DART version indexing check against nlevsno but for us 0 + frac_swe = 1.0_r8 + ! JK: Let CLM repartitioning do the job + ! afterwards. Provide all the snow in a single layer + else + frac_swe = 0.0_r8 + end if + ! repartition Option 2: Adjust all active layers + else if (clmupdate_snow_repartitioning.eq.2) then + frac_swe = (h2oliq(jj,ii) + h2oice(jj,ii)) / h2osno_pr(jj) + end if + else + frac_swe = 0.0_r8 ! no fraction SWE if no snow is present in column + end if ! end SWE fraction if + + ! fraction of liquid and ice + if ((h2oliq(jj,ii) + h2oice(jj,ii)).gt.0.0_r8) then + frac_liq = h2oliq(jj,ii) / (h2oliq(jj,ii) + h2oice(jj,ii)) + frac_ice = 1.0_r8 - frac_liq + else + frac_liq = 0.0_r8 + frac_ice = 0.0_r8 + end if + + ! SWE adjustment per layer + ! assumes identical layer distribution of liq and ice than before DA (frac_*) + if (abs(h2osno_po(jj) - h2osno_pr(jj)).gt.0.0_r8) then + gain_h2osno = (h2osno_po(jj) - h2osno_pr(jj)) * frac_swe + gain_h2oliq = gain_h2osno * frac_liq + gain_h2oice = gain_h2osno * frac_ice + else + gain_h2osno = 0.0_r8 + gain_h2oliq = 0.0_r8 + gain_h2oice = 0.0_r8 + end if + ! layer level adjustments + if (snowden.gt.0.0_r8) then + gain_dzsno(ii) = gain_h2osno / snowden + else + gain_dzsno(ii) = 0.0_r8 + end if + h2oliq(jj,ii) = h2oliq(jj,ii) + gain_h2oliq + h2oice(jj,ii) = h2oice(jj,ii) + gain_h2oice + + ! Adjust snow layer dimensions so that CLM5 can calculate compaction / aggregation + ! in the DART code dzsno is adjusted directly but in CLM5 dzsno is local and diagnostic + ! i.e. calculated / assigned from frac_sno and dz(:, snow_layer) in SnowHydrologyMod + ! therefore we adjust dz(:, snow_layer) here + if (abs(h2osno_po(jj) - h2osno_pr(jj)).gt.0.0_r8) then + col%dz(jj, ii) = col%dz(jj, ii) + gain_dzsno(ii) + ! mid point and interface adjustments + ! i.e. zsno (col%z(:, snow_layers)) and zisno (col%zi(:, snow_layers)) + ! DART version the sum goes from ilevel:nlevsno to fit with our indexing: + col%zi(jj, ii) = sum(col%dz(jj,ii:0))*-1.0_r8 + ! In DART the check is ilevel == nlevsno but here + if (ii.eq.0) then + col%z(jj,ii) = col%zi(jj,ii) / 2.0_r8 + else + col%z(jj,ii) = sum(col%zi(jj, ii:ii+1)) / 2.0_r8 + end if + end if + + end do ! End iteration of snow layers + endif ! End of snow layers present check + + ! Column level variables + if (clmupdate_snow .eq. 1) then + ! Finally adjust SWE (h2osno) since the prior value is no longer needed + ! column level variables so we can adjust it outside the layer loop + h2osno(jj) = h2osno_po(jj) + else if (clmupdate_snow .eq. 2) then + ! Update the total snow depth to match udpates to layers for active snow layers + if (abs(h2osno_po(jj) - h2osno_pr(jj)) .gt. 0.0_r8 .and. snlsno(jj) < 0.0_r8) then + snow_depth(jj) = snow_depth(jj) + sum(gain_dzsno(-nlevsno+1:0)) + end if + end if + + if (isnan(h2osno(jj))) then + print *, "WARNING2: h2osno at jj is nan: ", jj + endif + if (isnan(snow_depth(jj))) then + print *, "WARNING2: snow_depth at jj is nan: ", jj + endif + + end if ! End of snow present check + end do ! End of column iteration + + end subroutine clm_repartition_snow + subroutine clm_correct_texture() use clm_varpar , only : nlevsoi From cd9115a1453981cc22a2ad983b7e19e260d2219c Mon Sep 17 00:00:00 2001 From: lstrebel Date: Tue, 4 Mar 2025 15:55:10 +0100 Subject: [PATCH 42/71] Update enkf_clm_mod_5.F90 (#265) * Update enkf_clm_mod_5.F90 Updated snow depth (clmupdate_snow.eq.1) to include the clmupdate_snow_repartitioning.eq.3 option and updated the column iteriation counter to be identical to clmupdate_snow.eq.2 . * copy updates to eCLM-path --------- Co-authored-by: Johannes Keller --- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 49 ++++++++++++------- .../pdaf/model/eclm/enkf_clm_mod_5.F90 | 49 ++++++++++++------- 2 files changed, 64 insertions(+), 34 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 1e7cea324..9a9131f76 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -722,28 +722,43 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! Case 1: Snow depth ! Write updated snow depth back to CLM and then repartition snow and adjust related variables if(clmupdate_snow.eq.1) then - cc = 1 - do j=clm_begg,clm_endg + do j=clm_begc,clm_endc ! iterate through the columns and copy from the same gridcell ! i.e. statevec position (cc) for each column - do jj=clm_begc,clm_endc - ! Catch negative or 0 values from DA - if (clm_statevec(cc+offset).lt.0.0) then - print *, "WARNING: snow depth at g,c is negative: ", j, jj, clm_statevec(cc+offset) - else - rsnow(jj) = snow_depth(jj) - clm_statevec(cc+offset) - if ( ABS(SUM(rsnow(:))) .gt.0.000001) then - snow_depth(jj) = clm_statevec(cc+offset) - endif - endif - end do - cc = cc + 1 - end do - if ( clmupdate_snow_repartitioning.ne.0) then + + ! Set cc (the state vector index) from the + ! CLM5-grid-index and the `CLM5-layer-index times + ! num_gridcells` + if(clmstatevec_allcol.eq.0) then + cc = (col%gridcell(j) - clm_begg + 1) + else + cc = (j - clm_begc + 1) + end if + ! Catch negative or 0 values from DA + if (clm_statevec(cc+offset).lt.0.0) then + print *, "WARNING: snow depth at g,c is negative: ", cc, j, clm_statevec(cc+offset) + else + rsnow(j) = snow_depth(j) + if ( ABS(SUM(rsnow(:) - clm_statevec(cc+offset))) .gt.0.000001) then + snow_depth(j) = clm_statevec(cc+offset) + ! JK: clmupdate_snow_repartitioning.eq.3 is experimental + ! JK: clmupdate_snow_repartitioning.eq.3 from NASA-Code (based on older CLM3.5 version) + ! https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 + if ( clmupdate_snow_repartitioning.eq.3) then + incr_h2osno = snow_depth(j) / rsnow(j) ! INC = New SD / OLD SD + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno + end do + endif + endif + endif + end do + + if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then if ( ABS(SUM(rsnow(:))).gt.0.000001) then call clm_repartition_snow() end if - end if + end if endif ! Case 2: Snow water equivalent ! Write updated snow depth back to CLM and then repartition snow and adjust related variables diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index 1e7cea324..9a9131f76 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -722,28 +722,43 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! Case 1: Snow depth ! Write updated snow depth back to CLM and then repartition snow and adjust related variables if(clmupdate_snow.eq.1) then - cc = 1 - do j=clm_begg,clm_endg + do j=clm_begc,clm_endc ! iterate through the columns and copy from the same gridcell ! i.e. statevec position (cc) for each column - do jj=clm_begc,clm_endc - ! Catch negative or 0 values from DA - if (clm_statevec(cc+offset).lt.0.0) then - print *, "WARNING: snow depth at g,c is negative: ", j, jj, clm_statevec(cc+offset) - else - rsnow(jj) = snow_depth(jj) - clm_statevec(cc+offset) - if ( ABS(SUM(rsnow(:))) .gt.0.000001) then - snow_depth(jj) = clm_statevec(cc+offset) - endif - endif - end do - cc = cc + 1 - end do - if ( clmupdate_snow_repartitioning.ne.0) then + + ! Set cc (the state vector index) from the + ! CLM5-grid-index and the `CLM5-layer-index times + ! num_gridcells` + if(clmstatevec_allcol.eq.0) then + cc = (col%gridcell(j) - clm_begg + 1) + else + cc = (j - clm_begc + 1) + end if + ! Catch negative or 0 values from DA + if (clm_statevec(cc+offset).lt.0.0) then + print *, "WARNING: snow depth at g,c is negative: ", cc, j, clm_statevec(cc+offset) + else + rsnow(j) = snow_depth(j) + if ( ABS(SUM(rsnow(:) - clm_statevec(cc+offset))) .gt.0.000001) then + snow_depth(j) = clm_statevec(cc+offset) + ! JK: clmupdate_snow_repartitioning.eq.3 is experimental + ! JK: clmupdate_snow_repartitioning.eq.3 from NASA-Code (based on older CLM3.5 version) + ! https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 + if ( clmupdate_snow_repartitioning.eq.3) then + incr_h2osno = snow_depth(j) / rsnow(j) ! INC = New SD / OLD SD + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno + end do + endif + endif + endif + end do + + if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then if ( ABS(SUM(rsnow(:))).gt.0.000001) then call clm_repartition_snow() end if - end if + end if endif ! Case 2: Snow water equivalent ! Write updated snow depth back to CLM and then repartition snow and adjust related variables From 993156ad0c158e7ef896a36eee2f4525227d518e Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 14 Mar 2025 14:45:46 +0100 Subject: [PATCH 43/71] clmupdate_snow.eq.3: snow depth observations, h2osoi_ice in state --- .../pdaf/model/eclm/enkf_clm_mod_5.F90 | 378 +++++++++++------- 1 file changed, 233 insertions(+), 145 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index 9a9131f76..3a5b20c03 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -86,7 +86,7 @@ module enkf_clm_mod subroutine define_clm_statevec(mype) use shr_kind_mod, only: r8 => shr_kind_r8 use decompMod , only : get_proc_bounds - use clm_varpar , only : nlevsoi + use clm_varpar , only : nlevsoi, nlevsno, nlevgrnd use clm_varcon , only : ispval use ColumnType , only : col @@ -284,6 +284,11 @@ subroutine define_clm_statevec(mype) clm_varsize = (clm_endg-clm_begg+1) ! Currently no combination of SWC and snow DA clm_statevecsize = (clm_endg-clm_begg+1) ! So like this if snow is set it takes priority endif + ! Case 3: Assimilation of snow depth: adding h2osoi_ice in the state vector + if(clmupdate_snow.eq.3) then + clm_varsize = (clm_endg-clm_begg+1) + clm_statevecsize = 2*((clm_endg-clm_begg+1) * (nlevsno + nlevgrnd)) !2D h2osoi_ice + endif !hcp LST DA if(clmupdate_T.eq.1) then @@ -325,7 +330,7 @@ end subroutine cleanup_clm_statevec subroutine set_clm_statevec(tstartcycle, mype) use clm_instMod, only : soilstate_inst, waterstate_inst - use clm_varpar , only : nlevsoi + use clm_varpar , only : nlevsoi, nlevsno, nlevgrnd ! use clm_varcon, only: nameg, namec ! use GetGlobalValuesMod, only: GetGlobalWrite use ColumnType , only : col @@ -339,6 +344,7 @@ subroutine set_clm_statevec(tstartcycle, mype) real(r8), pointer :: porgm(:,:) real(r8), pointer :: snow_depth(:) real(r8), pointer :: h2osno(:) + real(r8), pointer :: h2oice(:,:) integer :: i,j,jj,g,cc=0,offset=0 character (len = 34) :: fn !TSMP-PDAF: function name for state vector output character (len = 34) :: fn2 !TSMP-PDAF: function name for swc output @@ -350,6 +356,7 @@ subroutine set_clm_statevec(tstartcycle, mype) snow_depth => waterstate_inst%snow_depth_col ! snow height of snow covered area (m) h2osno => waterstate_inst%h2osno_col ! snow water equivalent (mm) + h2osoi_ice => waterstate_inst%h2osoi_ice_col ! col ice lens (kg/m2) (-nlevsno+1:nlevgrnd) #ifdef PDAF_DEBUG IF(clmt_printensemble == tstartcycle + 1 .OR. clmt_printensemble < 0) THEN @@ -457,6 +464,49 @@ subroutine set_clm_statevec(tstartcycle, mype) end if endif + ! Case 3: Snow Depth with h2osoi_ice in state vector + if(clmupdate_snow.eq.3) then + + ! snow_depth into state vector + cc = 1 + do j=clm_begg,clm_endg + ! Only get the snow_depth from the first column of each gridcell + ! and add it to the clm_statevec at the position of the gridcell (cc) + newgridcell = .true. + do jj=clm_begc,clm_endc + g = col%gridcell(jj) + if (g .eq. j) then + if (newgridcell) then + newgridcell = .false. + clm_statevec(cc+offset) = snow_depth(jj) + endif + endif + end do + cc = cc + 1 + end do + + ! h2osoi_ice into state vector + cc = 1 + ! Additional loop over layers + do i=-nlevsno+1,nlevgrnd + do j=clm_begg,clm_endg + ! Only get the h2osoi_ice from the first column of each gridcell + ! and add it to the clm_statevec at the position of the gridcell (cc) + newgridcell = .true. + do jj=clm_begc,clm_endc + g = col%gridcell(jj) + if (g .eq. j) then + if (newgridcell) then + newgridcell = .false. + ! Shift by clm_varsize (size of snow-depth in state vector) + clm_statevec(cc+clm_varsize+offset) = h2osoi_ice(jj,i) + endif + endif + end do + cc = cc + 1 + end do + end do + endif #ifdef PDAF_DEBUG IF(clmt_printensemble == tstartcycle + 1 .OR. clmt_printensemble < 0) THEN @@ -576,113 +626,113 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! write updated swc back to CLM if(clmupdate_swc.ne.0) then - ! Set minimum soil moisture for checking the state vector and - ! for setting minimum swc for CLM - if(clmwatmin_switch.eq.3) then - ! CLM3.5 type watmin - watmin_check = 0.00 - watmin_set = 0.05 - else if(clmwatmin_switch.eq.5) then - ! CLM5.0 type watmin - watmin_check = watmin - watmin_set = watmin - else - ! Default - watmin_check = 0.0 - watmin_set = 0.0 - end if + ! Set minimum soil moisture for checking the state vector and + ! for setting minimum swc for CLM + if(clmwatmin_switch.eq.3) then + ! CLM3.5 type watmin + watmin_check = 0.00 + watmin_set = 0.05 + else if(clmwatmin_switch.eq.5) then + ! CLM5.0 type watmin + watmin_check = watmin + watmin_set = watmin + else + ! Default + watmin_check = 0.0 + watmin_set = 0.0 + end if - ! cc = 0 - do i=1,nlevsoi - ! CLM3.5: iterate over grid cells - ! CLM5.0: iterate over columns - ! do j=clm_begg,clm_endg - do j=clm_begc,clm_endc - - ! Update only those SWCs that are not excluded by ispval - if(state_clm2pdaf_p(j,i) .ne. ispval) then - - if(swc(j,i).eq.0.0) then - swc_zero_before_update = .true. - - ! Zero-SWC leads to zero denominator in computation of - ! rliq/rice, therefore setting rliq/rice to special - ! value - rliq = spval - rice = spval - else - swc_zero_before_update = .false. + ! cc = 0 + do i=1,nlevsoi + ! CLM3.5: iterate over grid cells + ! CLM5.0: iterate over columns + ! do j=clm_begg,clm_endg + do j=clm_begc,clm_endc - rliq = h2osoi_liq(j,i)/(dz(j,i)*denh2o*swc(j,i)) - rice = h2osoi_ice(j,i)/(dz(j,i)*denice*swc(j,i)) - !h2osoi_vol(c,j) = h2osoi_liq(c,j)/(dz(c,j)*denh2o) + h2osoi_ice(c,j)/(dz(c,j)*denice) - end if + ! Update only those SWCs that are not excluded by ispval + if(state_clm2pdaf_p(j,i) .ne. ispval) then - swc_update = clm_statevec(state_clm2pdaf_p(j,i)) + if(swc(j,i).eq.0.0) then + swc_zero_before_update = .true. - if(swc_update.le.watmin_check) then - swc(j,i) = watmin_set - else if(swc_update.ge.watsat(j,i)) then - swc(j,i) = watsat(j,i) - else - swc(j,i) = swc_update - endif + ! Zero-SWC leads to zero denominator in computation of + ! rliq/rice, therefore setting rliq/rice to special + ! value + rliq = spval + rice = spval + else + swc_zero_before_update = .false. - if (isnan(swc(j,i))) then - swc(j,i) = watmin_set - print *, "WARNING: swc at j,i is nan: ", j, i - endif + rliq = h2osoi_liq(j,i)/(dz(j,i)*denh2o*swc(j,i)) + rice = h2osoi_ice(j,i)/(dz(j,i)*denice*swc(j,i)) + !h2osoi_vol(c,j) = h2osoi_liq(c,j)/(dz(c,j)*denh2o) + h2osoi_ice(c,j)/(dz(c,j)*denice) + end if + + swc_update = clm_statevec(state_clm2pdaf_p(j,i)) + + if(swc_update.le.watmin_check) then + swc(j,i) = watmin_set + else if(swc_update.ge.watsat(j,i)) then + swc(j,i) = watsat(j,i) + else + swc(j,i) = swc_update + endif + + if (isnan(swc(j,i))) then + swc(j,i) = watmin_set + print *, "WARNING: swc at j,i is nan: ", j, i + endif - if(swc_zero_before_update) then - ! This case should not appear for hydrologically - ! active columns/layers, where always: swc > watmin - ! - ! If you want to make sure that no zero SWCs appear in - ! the code, comment out the error stop + if(swc_zero_before_update) then + ! This case should not appear for hydrologically + ! active columns/layers, where always: swc > watmin + ! + ! If you want to make sure that no zero SWCs appear in + ! the code, comment out the error stop #ifdef PDAF_DEBUG - ! error stop "ERROR: Update of zero-swc" - print *, "WARNING: Update of zero-swc" - print *, "WARNING: Any new H2O added to h2osoi_liq(j,i) with j,i = ", j, i + ! error stop "ERROR: Update of zero-swc" + print *, "WARNING: Update of zero-swc" + print *, "WARNING: Any new H2O added to h2osoi_liq(j,i) with j,i = ", j, i #endif - h2osoi_liq(j,i) = swc(j,i) * dz(j,i)*denh2o - h2osoi_ice(j,i) = 0.0 - else - ! update liquid water content - h2osoi_liq(j,i) = swc(j,i) * dz(j,i)*denh2o*rliq - ! update ice content - h2osoi_ice(j,i) = swc(j,i) * dz(j,i)*denice*rice - end if + h2osoi_liq(j,i) = swc(j,i) * dz(j,i)*denh2o + h2osoi_ice(j,i) = 0.0 + else + ! update liquid water content + h2osoi_liq(j,i) = swc(j,i) * dz(j,i)*denh2o*rliq + ! update ice content + h2osoi_ice(j,i) = swc(j,i) * dz(j,i)*denice*rice + end if - end if - ! cc = cc + 1 - end do + end if + ! cc = cc + 1 end do + end do #ifdef PDAF_DEBUG - IF(clmt_printensemble == tstartcycle .OR. clmt_printensemble < 0) THEN - - IF(clmupdate_swc.NE.0) THEN - ! TSMP-PDAF: For debug runs, output the state vector in files - WRITE(fn3, "(a,i5.5,a,i5.5,a)") "h2osoi_liq", mype, ".update.", tstartcycle, ".txt" - OPEN(unit=71, file=fn3, action="write") - WRITE (71,"(es22.15)") h2osoi_liq(:,:) - CLOSE(71) - - ! TSMP-PDAF: For debug runs, output the state vector in files - WRITE(fn4, "(a,i5.5,a,i5.5,a)") "h2osoi_ice", mype, ".update.", tstartcycle, ".txt" - OPEN(unit=71, file=fn4, action="write") - WRITE (71,"(es22.15)") h2osoi_ice(:,:) - CLOSE(71) - - ! TSMP-PDAF: For debug runs, output the state vector in files - WRITE(fn2, "(a,i5.5,a,i5.5,a)") "swcstate_", mype, ".update.", tstartcycle, ".txt" - OPEN(unit=71, file=fn2, action="write") - WRITE (71,"(es22.15)") swc(:,:) - CLOSE(71) - END IF - + IF(clmt_printensemble == tstartcycle .OR. clmt_printensemble < 0) THEN + + IF(clmupdate_swc.NE.0) THEN + ! TSMP-PDAF: For debug runs, output the state vector in files + WRITE(fn3, "(a,i5.5,a,i5.5,a)") "h2osoi_liq", mype, ".update.", tstartcycle, ".txt" + OPEN(unit=71, file=fn3, action="write") + WRITE (71,"(es22.15)") h2osoi_liq(:,:) + CLOSE(71) + + ! TSMP-PDAF: For debug runs, output the state vector in files + WRITE(fn4, "(a,i5.5,a,i5.5,a)") "h2osoi_ice", mype, ".update.", tstartcycle, ".txt" + OPEN(unit=71, file=fn4, action="write") + WRITE (71,"(es22.15)") h2osoi_ice(:,:) + CLOSE(71) + + ! TSMP-PDAF: For debug runs, output the state vector in files + WRITE(fn2, "(a,i5.5,a,i5.5,a)") "swcstate_", mype, ".update.", tstartcycle, ".txt" + OPEN(unit=71, file=fn2, action="write") + WRITE (71,"(es22.15)") swc(:,:) + CLOSE(71) END IF + + END IF #endif endif @@ -746,73 +796,111 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 if ( clmupdate_snow_repartitioning.eq.3) then incr_h2osno = snow_depth(j) / rsnow(j) ! INC = New SD / OLD SD - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno - end do + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno + end do endif endif endif end do if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then - if ( ABS(SUM(rsnow(:))).gt.0.000001) then - call clm_repartition_snow() - end if + if ( ABS(SUM(rsnow(:))).gt.0.000001) then + call clm_repartition_snow() + end if end if endif ! Case 2: Snow water equivalent ! Write updated snow depth back to CLM and then repartition snow and adjust related variables if(clmupdate_snow.eq.2) then - ! cc = 1 - ! do j=clm_begg,clm_endg - ! iterate through the columns and copy from the same gridcell - ! i.e. statevec position (cc) for each column - do j=clm_begc,clm_endc + ! cc = 1 + ! do j=clm_begg,clm_endg + ! iterate through the columns and copy from the same gridcell + ! i.e. statevec position (cc) for each column + do j=clm_begc,clm_endc - ! Set cc (the state vector index) from the - ! CLM5-grid-index and the `CLM5-layer-index times - ! num_gridcells` - if(clmstatevec_allcol.eq.0) then - cc = (col%gridcell(j) - clm_begg + 1) - else - cc = (j - clm_begc + 1) - end if + ! Set cc (the state vector index) from the + ! CLM5-grid-index and the `CLM5-layer-index times + ! num_gridcells` + if(clmstatevec_allcol.eq.0) then + cc = (col%gridcell(j) - clm_begg + 1) + else + cc = (j - clm_begc + 1) + end if - ! Catch negative or 0 values from DA - if (clm_statevec(cc+offset).le.0.0) then - print *, "WARNING: SWE at g,c is negative or zero: ", j, clm_statevec(cc+offset) - else - rsnow(j) = h2osno(j) - if ( ABS(SUM(rsnow(:) - clm_statevec(cc+offset))).gt.0.000001) then - h2osno(j) = clm_statevec(cc+offset) - ! JK: clmupdate_snow_repartitioning.eq.3 is experimental - ! JK: clmupdate_snow_repartitioning.eq.3 from NASA-Code (based on older CLM3.5 version) - ! https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 - if ( clmupdate_snow_repartitioning.eq.3) then - incr_h2osno = h2osno(j) / rsnow(j) ! INC = New SWE / OLD SWE - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno - end do - end if - - if (isnan(rsnow(j))) then - print *, "WARNING: rsnow at j is nan: ", j - endif - if (isnan(h2osno(j))) then - print *, "WARNING: h2osno at j is nan: ", j - endif + ! Catch negative or 0 values from DA + if (clm_statevec(cc+offset).le.0.0) then + print *, "WARNING: SWE at g,c is negative or zero: ", j, clm_statevec(cc+offset) + else + rsnow(j) = h2osno(j) + if ( ABS(SUM(rsnow(:) - clm_statevec(cc+offset))).gt.0.000001) then + h2osno(j) = clm_statevec(cc+offset) + ! JK: clmupdate_snow_repartitioning.eq.3 is experimental + ! JK: clmupdate_snow_repartitioning.eq.3 from NASA-Code (based on older CLM3.5 version) + ! https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 + if ( clmupdate_snow_repartitioning.eq.3) then + incr_h2osno = h2osno(j) / rsnow(j) ! INC = New SWE / OLD SWE + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno + end do + end if - end if - endif - end do - ! cc = cc + 1 - ! end do + if (isnan(rsnow(j))) then + print *, "WARNING: rsnow at j is nan: ", j + endif + if (isnan(h2osno(j))) then + print *, "WARNING: h2osno at j is nan: ", j + endif + + end if + endif + end do + ! cc = cc + 1 + ! end do - if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then - if ( ABS(SUM(rsnow(:) - h2osno(:))).gt.0.000001) then - call clm_repartition_snow(rsnow(:)) + if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then + if ( ABS(SUM(rsnow(:) - h2osno(:))).gt.0.000001) then + call clm_repartition_snow(rsnow(:)) + end if + end if + endif + ! Case 3: Snow depth with h2osoi_ice in state vector + ! Write updated h2osoi_ice back to CLM and then repartition snow and adjust related variables + if(clmupdate_snow.eq.3) then + do i=-nlevsno+1,nlevsoi + do j=clm_begc,clm_endc + ! iterate through the columns and copy from the same gridcell + ! i.e. statevec position (cc) for each column + + ! Set cc (the state vector index) from the + ! CLM5-grid-index and the `CLM5-layer-index times + ! num_gridcells` + if(clmstatevec_allcol.eq.0) then + cc = (col%gridcell(j) - clm_begg + 1) + (i + nlevsno -1)*(clm_endg - clm_begg + 1) + else + cc = (j - clm_begc + 1) + (i + nlevsno -1)*(clm_endc - clm_begc + 1) + end if + ! Catch negative or 0 values from DA + if (i .eq. -nlevsno+1) then + if (clm_statevec(cc+offset).lt.0.0) then + print *, "WARNING: snow_depth at g,c is negative: ", cc, j, clm_statevec(cc+offset) + end if end if + if (clm_statevec(cc+clm_varsize+offset).lt.0.0) then + print *, "WARNING: h2osoi_ice at g,c is negative: ", cc, j, i, clm_statevec(cc+clm_varsize+offset) + else + ! UPDATE + h2osoi_ice(j,i) = clm_statevec(cc+clm_varsize+offset) + endif + end do + end do + + ! How to interact with repartitioning? + if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then + if ( ABS(SUM(rsnow(:))).gt.0.000001) then + call clm_repartition_snow() end if + end if endif end subroutine update_clm From a5779e27a2551f67c18ad96a9f5deae174ce8d92 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 14 Mar 2025 16:31:31 +0100 Subject: [PATCH 44/71] snow: update eclm --- .../pdaf/model/eclm/enkf_clm_mod_5.F90 | 286 +++++++++--------- 1 file changed, 143 insertions(+), 143 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index 543f4494b..12cccd46c 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -627,113 +627,113 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! write updated swc back to CLM if(clmupdate_swc.ne.0) then - ! Set minimum soil moisture for checking the state vector and - ! for setting minimum swc for CLM - if(clmwatmin_switch.eq.3) then - ! CLM3.5 type watmin - watmin_check = 0.00 - watmin_set = 0.05 - else if(clmwatmin_switch.eq.5) then - ! CLM5.0 type watmin - watmin_check = watmin - watmin_set = watmin - else - ! Default - watmin_check = 0.0 - watmin_set = 0.0 - end if - - ! cc = 0 - do i=1,nlevsoi - ! CLM3.5: iterate over grid cells - ! CLM5.0: iterate over columns - ! do j=clm_begg,clm_endg - do j=clm_begc,clm_endc - - ! Update only those SWCs that are not excluded by ispval - if(state_clm2pdaf_p(j,i) .ne. ispval) then - - if(swc(j,i).eq.0.0) then - swc_zero_before_update = .true. + ! Set minimum soil moisture for checking the state vector and + ! for setting minimum swc for CLM + if(clmwatmin_switch.eq.3) then + ! CLM3.5 type watmin + watmin_check = 0.00 + watmin_set = 0.05 + else if(clmwatmin_switch.eq.5) then + ! CLM5.0 type watmin + watmin_check = watmin + watmin_set = watmin + else + ! Default + watmin_check = 0.0 + watmin_set = 0.0 + end if - ! Zero-SWC leads to zero denominator in computation of - ! rliq/rice, therefore setting rliq/rice to special - ! value - rliq = spval - rice = spval - else - swc_zero_before_update = .false. + ! cc = 0 + do i=1,nlevsoi + ! CLM3.5: iterate over grid cells + ! CLM5.0: iterate over columns + ! do j=clm_begg,clm_endg + do j=clm_begc,clm_endc + + ! Update only those SWCs that are not excluded by ispval + if(state_clm2pdaf_p(j,i) .ne. ispval) then + + if(swc(j,i).eq.0.0) then + swc_zero_before_update = .true. + + ! Zero-SWC leads to zero denominator in computation of + ! rliq/rice, therefore setting rliq/rice to special + ! value + rliq = spval + rice = spval + else + swc_zero_before_update = .false. - rliq = h2osoi_liq(j,i)/(dz(j,i)*denh2o*swc(j,i)) - rice = h2osoi_ice(j,i)/(dz(j,i)*denice*swc(j,i)) - !h2osoi_vol(c,j) = h2osoi_liq(c,j)/(dz(c,j)*denh2o) + h2osoi_ice(c,j)/(dz(c,j)*denice) - end if + rliq = h2osoi_liq(j,i)/(dz(j,i)*denh2o*swc(j,i)) + rice = h2osoi_ice(j,i)/(dz(j,i)*denice*swc(j,i)) + !h2osoi_vol(c,j) = h2osoi_liq(c,j)/(dz(c,j)*denh2o) + h2osoi_ice(c,j)/(dz(c,j)*denice) + end if - swc_update = clm_statevec(state_clm2pdaf_p(j,i)) + swc_update = clm_statevec(state_clm2pdaf_p(j,i)) - if(swc_update.le.watmin_check) then - swc(j,i) = watmin_set - else if(swc_update.ge.watsat(j,i)) then - swc(j,i) = watsat(j,i) - else - swc(j,i) = swc_update - endif + if(swc_update.le.watmin_check) then + swc(j,i) = watmin_set + else if(swc_update.ge.watsat(j,i)) then + swc(j,i) = watsat(j,i) + else + swc(j,i) = swc_update + endif - if (isnan(swc(j,i))) then - swc(j,i) = watmin_set - print *, "WARNING: swc at j,i is nan: ", j, i - endif + if (isnan(swc(j,i))) then + swc(j,i) = watmin_set + print *, "WARNING: swc at j,i is nan: ", j, i + endif - if(swc_zero_before_update) then - ! This case should not appear for hydrologically - ! active columns/layers, where always: swc > watmin - ! - ! If you want to make sure that no zero SWCs appear in - ! the code, comment out the error stop + if(swc_zero_before_update) then + ! This case should not appear for hydrologically + ! active columns/layers, where always: swc > watmin + ! + ! If you want to make sure that no zero SWCs appear in + ! the code, comment out the error stop #ifdef PDAF_DEBUG - ! error stop "ERROR: Update of zero-swc" - print *, "WARNING: Update of zero-swc" - print *, "WARNING: Any new H2O added to h2osoi_liq(j,i) with j,i = ", j, i + ! error stop "ERROR: Update of zero-swc" + print *, "WARNING: Update of zero-swc" + print *, "WARNING: Any new H2O added to h2osoi_liq(j,i) with j,i = ", j, i #endif - h2osoi_liq(j,i) = swc(j,i) * dz(j,i)*denh2o - h2osoi_ice(j,i) = 0.0 - else - ! update liquid water content - h2osoi_liq(j,i) = swc(j,i) * dz(j,i)*denh2o*rliq - ! update ice content - h2osoi_ice(j,i) = swc(j,i) * dz(j,i)*denice*rice - end if + h2osoi_liq(j,i) = swc(j,i) * dz(j,i)*denh2o + h2osoi_ice(j,i) = 0.0 + else + ! update liquid water content + h2osoi_liq(j,i) = swc(j,i) * dz(j,i)*denh2o*rliq + ! update ice content + h2osoi_ice(j,i) = swc(j,i) * dz(j,i)*denice*rice + end if - end if - ! cc = cc + 1 + end if + ! cc = cc + 1 + end do end do - end do #ifdef PDAF_DEBUG - IF(clmt_printensemble == tstartcycle .OR. clmt_printensemble < 0) THEN - - IF(clmupdate_swc.NE.0) THEN - ! TSMP-PDAF: For debug runs, output the state vector in files - WRITE(fn3, "(a,i5.5,a,i5.5,a)") "h2osoi_liq", mype, ".update.", tstartcycle, ".txt" - OPEN(unit=71, file=fn3, action="write") - WRITE (71,"(es22.15)") h2osoi_liq(:,:) - CLOSE(71) - - ! TSMP-PDAF: For debug runs, output the state vector in files - WRITE(fn4, "(a,i5.5,a,i5.5,a)") "h2osoi_ice", mype, ".update.", tstartcycle, ".txt" - OPEN(unit=71, file=fn4, action="write") - WRITE (71,"(es22.15)") h2osoi_ice(:,:) - CLOSE(71) - - ! TSMP-PDAF: For debug runs, output the state vector in files - WRITE(fn2, "(a,i5.5,a,i5.5,a)") "swcstate_", mype, ".update.", tstartcycle, ".txt" - OPEN(unit=71, file=fn2, action="write") - WRITE (71,"(es22.15)") swc(:,:) - CLOSE(71) - END IF + IF(clmt_printensemble == tstartcycle .OR. clmt_printensemble < 0) THEN + + IF(clmupdate_swc.NE.0) THEN + ! TSMP-PDAF: For debug runs, output the state vector in files + WRITE(fn3, "(a,i5.5,a,i5.5,a)") "h2osoi_liq", mype, ".update.", tstartcycle, ".txt" + OPEN(unit=71, file=fn3, action="write") + WRITE (71,"(es22.15)") h2osoi_liq(:,:) + CLOSE(71) + + ! TSMP-PDAF: For debug runs, output the state vector in files + WRITE(fn4, "(a,i5.5,a,i5.5,a)") "h2osoi_ice", mype, ".update.", tstartcycle, ".txt" + OPEN(unit=71, file=fn4, action="write") + WRITE (71,"(es22.15)") h2osoi_ice(:,:) + CLOSE(71) + + ! TSMP-PDAF: For debug runs, output the state vector in files + WRITE(fn2, "(a,i5.5,a,i5.5,a)") "swcstate_", mype, ".update.", tstartcycle, ".txt" + OPEN(unit=71, file=fn2, action="write") + WRITE (71,"(es22.15)") swc(:,:) + CLOSE(71) + END IF - END IF + END IF #endif endif @@ -797,73 +797,73 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 if ( clmupdate_snow_repartitioning.eq.3) then incr_h2osno = snow_depth(j) / rsnow(j) ! INC = New SD / OLD SD - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno - end do + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno + end do endif endif endif end do if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then - if ( ABS(SUM(rsnow(:))).gt.0.000001) then - call clm_repartition_snow() - end if + if ( ABS(SUM(rsnow(:))).gt.0.000001) then + call clm_repartition_snow() + end if end if endif ! Case 2: Snow water equivalent ! Write updated snow depth back to CLM and then repartition snow and adjust related variables if(clmupdate_snow.eq.2) then - ! cc = 1 - ! do j=clm_begg,clm_endg - ! iterate through the columns and copy from the same gridcell - ! i.e. statevec position (cc) for each column - do j=clm_begc,clm_endc + ! cc = 1 + ! do j=clm_begg,clm_endg + ! iterate through the columns and copy from the same gridcell + ! i.e. statevec position (cc) for each column + do j=clm_begc,clm_endc - ! Set cc (the state vector index) from the - ! CLM5-grid-index and the `CLM5-layer-index times - ! num_gridcells` - if(clmstatevec_allcol.eq.0) then - cc = (col%gridcell(j) - clm_begg + 1) - else - cc = (j - clm_begc + 1) - end if + ! Set cc (the state vector index) from the + ! CLM5-grid-index and the `CLM5-layer-index times + ! num_gridcells` + if(clmstatevec_allcol.eq.0) then + cc = (col%gridcell(j) - clm_begg + 1) + else + cc = (j - clm_begc + 1) + end if - ! Catch negative or 0 values from DA - if (clm_statevec(cc+offset).le.0.0) then - print *, "WARNING: SWE at g,c is negative or zero: ", j, clm_statevec(cc+offset) - else - rsnow(j) = h2osno(j) - if ( ABS(SUM(rsnow(:) - clm_statevec(cc+offset))).gt.0.000001) then - h2osno(j) = clm_statevec(cc+offset) - ! JK: clmupdate_snow_repartitioning.eq.3 is experimental - ! JK: clmupdate_snow_repartitioning.eq.3 from NASA-Code (based on older CLM3.5 version) - ! https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 - if ( clmupdate_snow_repartitioning.eq.3) then - incr_h2osno = h2osno(j) / rsnow(j) ! INC = New SWE / OLD SWE - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno - end do - end if + ! Catch negative or 0 values from DA + if (clm_statevec(cc+offset).le.0.0) then + print *, "WARNING: SWE at g,c is negative or zero: ", j, clm_statevec(cc+offset) + else + rsnow(j) = h2osno(j) + if ( ABS(SUM(rsnow(:) - clm_statevec(cc+offset))).gt.0.000001) then + h2osno(j) = clm_statevec(cc+offset) + ! JK: clmupdate_snow_repartitioning.eq.3 is experimental + ! JK: clmupdate_snow_repartitioning.eq.3 from NASA-Code (based on older CLM3.5 version) + ! https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 + if ( clmupdate_snow_repartitioning.eq.3) then + incr_h2osno = h2osno(j) / rsnow(j) ! INC = New SWE / OLD SWE + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno + end do + end if + + if (isnan(rsnow(j))) then + print *, "WARNING: rsnow at j is nan: ", j + endif + if (isnan(h2osno(j))) then + print *, "WARNING: h2osno at j is nan: ", j + endif - if (isnan(rsnow(j))) then - print *, "WARNING: rsnow at j is nan: ", j - endif - if (isnan(h2osno(j))) then - print *, "WARNING: h2osno at j is nan: ", j - endif + end if + endif + end do + ! cc = cc + 1 + ! end do + if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then + if ( ABS(SUM(rsnow(:) - h2osno(:))).gt.0.000001) then + call clm_repartition_snow(rsnow(:)) end if - endif - end do - ! cc = cc + 1 - ! end do - - if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then - if ( ABS(SUM(rsnow(:) - h2osno(:))).gt.0.000001) then - call clm_repartition_snow(rsnow(:)) end if - end if endif ! Case 3: Snow depth with h2osoi_ice in state vector ! Write updated h2osoi_ice back to CLM and then repartition snow and adjust related variables From 6a0c920137caa969735f799e155999261f1eb2c4 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 14 Mar 2025 16:40:50 +0100 Subject: [PATCH 45/71] clmupdate_snow.eq.3: snow_depth and h2osno in state vector Currently: - snow_depth observations - h2osno updated in state vector through correlations - updated h2osno used for updating h2osoi_ice in CLM - repartitioning: use same as h2osno-state-vector --- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 89 +++++++++++- .../pdaf/model/eclm/enkf_clm_mod_5.F90 | 129 +++++++++--------- 2 files changed, 150 insertions(+), 68 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 172c86297..cf7fff0e9 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -285,6 +285,11 @@ subroutine define_clm_statevec(mype) clm_varsize = (clm_endg-clm_begg+1) ! Currently no combination of SWC and snow DA clm_statevecsize = (clm_endg-clm_begg+1) ! So like this if snow is set it takes priority endif + ! Case 3: Assimilation of snow depth: adding swe in the state vector + if(clmupdate_snow.eq.3) then + clm_varsize = (clm_endg-clm_begg+1) + clm_statevecsize = 2*(clm_endg-clm_begg+1) + endif !hcp LST DA if(clmupdate_T.eq.1) then @@ -458,6 +463,29 @@ subroutine set_clm_statevec(tstartcycle, mype) end if endif + ! Case 3: Snow Depth with swe in state vector + if(clmupdate_snow.eq.3) then + + ! snow_depth and swe into state vector + cc = 1 + do j=clm_begg,clm_endg + ! Only get the snow_depth/swe from the first column of each gridcell + ! and add it to the clm_statevec at the position of the gridcell (cc) + newgridcell = .true. + do jj=clm_begc,clm_endc + g = col%gridcell(jj) + if (g .eq. j) then + if (newgridcell) then + newgridcell = .false. + clm_statevec(cc+offset) = snow_depth(jj) + clm_statevec(cc+clm_varsize+offset) = h2osno(jj) + endif + endif + end do + cc = cc + 1 + end do + + endif #ifdef PDAF_DEBUG IF(clmt_printensemble == tstartcycle + 1 .OR. clmt_printensemble < 0) THEN @@ -815,6 +843,63 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") end if end if endif + ! Case 3: Snow depth with swe in state vector + ! Use updated swe (from snow_depth observations) to update h2osoi_ice by increment + if(clmupdate_snow.eq.3) then + ! cc = 1 + ! do j=clm_begg,clm_endg + ! iterate through the columns and copy from the same gridcell + ! i.e. statevec position (cc) for each column + do j=clm_begc,clm_endc + + ! Set cc (the state vector index) from the + ! CLM5-grid-index and the `CLM5-layer-index times + ! num_gridcells` + if(clmstatevec_allcol.eq.0) then + cc = (col%gridcell(j) - clm_begg + 1) + else + cc = (j - clm_begc + 1) + end if + + ! Catch negative or 0 values from DA + if (clm_statevec(cc+offset).lt.0.0) then + print *, "WARNING: snow depth at g,c is negative: ", cc, j, clm_statevec(cc+offset) + end if + if (clm_statevec(cc+clm_varsize+offset).le.0.0) then + print *, "WARNING: SWE at g,c is negative or zero: ", j, clm_statevec(cc+clm_varsize+offset) + else + rsnow(j) = h2osno(j) + if ( ABS(SUM(rsnow(:) - clm_statevec(cc+clm_varsize+offset))).gt.0.000001) then + h2osno(j) = clm_statevec(cc+clm_varsize+offset) + ! JK: clmupdate_snow_repartitioning.eq.3 is experimental + ! JK: clmupdate_snow_repartitioning.eq.3 from NASA-Code (based on older CLM3.5 version) + ! https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 + if ( clmupdate_snow_repartitioning.eq.3) then + incr_h2osno = h2osno(j) / rsnow(j) ! INC = New SWE / OLD SWE + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno + end do + end if + + if (isnan(rsnow(j))) then + print *, "WARNING: rsnow at j is nan: ", j + endif + if (isnan(h2osno(j))) then + print *, "WARNING: h2osno at j is nan: ", j + endif + + end if + endif + end do + ! cc = cc + 1 + ! end do + + if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then + if ( ABS(SUM(rsnow(:) - h2osno(:))).gt.0.000001) then + call clm_repartition_snow(rsnow(:)) + end if + end if + endif end subroutine update_clm @@ -898,7 +983,7 @@ subroutine clm_repartition_snow(h2osno_in) h2osno_po(jj) = snow_depth(jj) * denice end if h2osno_pr(jj) = h2osno(jj) - else if (clmupdate_snow .eq. 2) then + else if (clmupdate_snow .eq. 2 .or. clmupdate_snow .eq. 3) then ! for clmupdate_snow == 2 we have post H2OSNO as the main H2OSNO already h2osno_po(jj) = h2osno(jj) h2osno_pr(jj) = h2osno_in(jj) @@ -986,7 +1071,7 @@ subroutine clm_repartition_snow(h2osno_in) ! Finally adjust SWE (h2osno) since the prior value is no longer needed ! column level variables so we can adjust it outside the layer loop h2osno(jj) = h2osno_po(jj) - else if (clmupdate_snow .eq. 2) then + else if (clmupdate_snow .eq. 2 .or. clmupdate_snow .eq. 3) then ! Update the total snow depth to match udpates to layers for active snow layers if (abs(h2osno_po(jj) - h2osno_pr(jj)) .gt. 0.0_r8 .and. snlsno(jj) < 0.0_r8) then snow_depth(jj) = snow_depth(jj) + sum(gain_dzsno(-nlevsno+1:0)) diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index 12cccd46c..cf7fff0e9 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -87,7 +87,7 @@ module enkf_clm_mod subroutine define_clm_statevec(mype) use shr_kind_mod, only: r8 => shr_kind_r8 use decompMod , only : get_proc_bounds - use clm_varpar , only : nlevsoi, nlevsno, nlevgrnd + use clm_varpar , only : nlevsoi use clm_varcon , only : ispval use ColumnType , only : col @@ -285,10 +285,10 @@ subroutine define_clm_statevec(mype) clm_varsize = (clm_endg-clm_begg+1) ! Currently no combination of SWC and snow DA clm_statevecsize = (clm_endg-clm_begg+1) ! So like this if snow is set it takes priority endif - ! Case 3: Assimilation of snow depth: adding h2osoi_ice in the state vector + ! Case 3: Assimilation of snow depth: adding swe in the state vector if(clmupdate_snow.eq.3) then clm_varsize = (clm_endg-clm_begg+1) - clm_statevecsize = 2*((clm_endg-clm_begg+1) * (nlevsno + nlevgrnd)) !2D h2osoi_ice + clm_statevecsize = 2*(clm_endg-clm_begg+1) endif !hcp LST DA @@ -331,7 +331,7 @@ end subroutine cleanup_clm_statevec subroutine set_clm_statevec(tstartcycle, mype) use clm_instMod, only : soilstate_inst, waterstate_inst - use clm_varpar , only : nlevsoi, nlevsno, nlevgrnd + use clm_varpar , only : nlevsoi ! use clm_varcon, only: nameg, namec ! use GetGlobalValuesMod, only: GetGlobalWrite use ColumnType , only : col @@ -345,7 +345,6 @@ subroutine set_clm_statevec(tstartcycle, mype) real(r8), pointer :: porgm(:,:) real(r8), pointer :: snow_depth(:) real(r8), pointer :: h2osno(:) - real(r8), pointer :: h2oice(:,:) integer :: i,j,jj,g,cc=0,offset=0 character (len = 34) :: fn !TSMP-PDAF: function name for state vector output character (len = 34) :: fn2 !TSMP-PDAF: function name for swc output @@ -357,7 +356,6 @@ subroutine set_clm_statevec(tstartcycle, mype) snow_depth => waterstate_inst%snow_depth_col ! snow height of snow covered area (m) h2osno => waterstate_inst%h2osno_col ! snow water equivalent (mm) - h2osoi_ice => waterstate_inst%h2osoi_ice_col ! col ice lens (kg/m2) (-nlevsno+1:nlevgrnd) #ifdef PDAF_DEBUG IF(clmt_printensemble == tstartcycle + 1 .OR. clmt_printensemble < 0) THEN @@ -465,13 +463,13 @@ subroutine set_clm_statevec(tstartcycle, mype) end if endif - ! Case 3: Snow Depth with h2osoi_ice in state vector + ! Case 3: Snow Depth with swe in state vector if(clmupdate_snow.eq.3) then - ! snow_depth into state vector + ! snow_depth and swe into state vector cc = 1 do j=clm_begg,clm_endg - ! Only get the snow_depth from the first column of each gridcell + ! Only get the snow_depth/swe from the first column of each gridcell ! and add it to the clm_statevec at the position of the gridcell (cc) newgridcell = .true. do jj=clm_begc,clm_endc @@ -480,33 +478,13 @@ subroutine set_clm_statevec(tstartcycle, mype) if (newgridcell) then newgridcell = .false. clm_statevec(cc+offset) = snow_depth(jj) + clm_statevec(cc+clm_varsize+offset) = h2osno(jj) endif endif end do cc = cc + 1 end do - ! h2osoi_ice into state vector - cc = 1 - ! Additional loop over layers - do i=-nlevsno+1,nlevgrnd - do j=clm_begg,clm_endg - ! Only get the h2osoi_ice from the first column of each gridcell - ! and add it to the clm_statevec at the position of the gridcell (cc) - newgridcell = .true. - do jj=clm_begc,clm_endc - g = col%gridcell(jj) - if (g .eq. j) then - if (newgridcell) then - newgridcell = .false. - ! Shift by clm_varsize (size of snow-depth in state vector) - clm_statevec(cc+clm_varsize+offset) = h2osoi_ice(jj,i) - endif - endif - end do - cc = cc + 1 - end do - end do endif #ifdef PDAF_DEBUG @@ -865,43 +843,62 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") end if end if endif - ! Case 3: Snow depth with h2osoi_ice in state vector - ! Write updated h2osoi_ice back to CLM and then repartition snow and adjust related variables + ! Case 3: Snow depth with swe in state vector + ! Use updated swe (from snow_depth observations) to update h2osoi_ice by increment if(clmupdate_snow.eq.3) then - do i=-nlevsno+1,nlevsoi - do j=clm_begc,clm_endc - ! iterate through the columns and copy from the same gridcell - ! i.e. statevec position (cc) for each column - - ! Set cc (the state vector index) from the - ! CLM5-grid-index and the `CLM5-layer-index times - ! num_gridcells` - if(clmstatevec_allcol.eq.0) then - cc = (col%gridcell(j) - clm_begg + 1) + (i + nlevsno -1)*(clm_endg - clm_begg + 1) - else - cc = (j - clm_begc + 1) + (i + nlevsno -1)*(clm_endc - clm_begc + 1) - end if - ! Catch negative or 0 values from DA - if (i .eq. -nlevsno+1) then - if (clm_statevec(cc+offset).lt.0.0) then - print *, "WARNING: snow_depth at g,c is negative: ", cc, j, clm_statevec(cc+offset) - end if - end if - if (clm_statevec(cc+clm_varsize+offset).lt.0.0) then - print *, "WARNING: h2osoi_ice at g,c is negative: ", cc, j, i, clm_statevec(cc+clm_varsize+offset) - else - ! UPDATE - h2osoi_ice(j,i) = clm_statevec(cc+clm_varsize+offset) - endif - end do - end do + ! cc = 1 + ! do j=clm_begg,clm_endg + ! iterate through the columns and copy from the same gridcell + ! i.e. statevec position (cc) for each column + do j=clm_begc,clm_endc - ! How to interact with repartitioning? - if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then - if ( ABS(SUM(rsnow(:))).gt.0.000001) then - call clm_repartition_snow() + ! Set cc (the state vector index) from the + ! CLM5-grid-index and the `CLM5-layer-index times + ! num_gridcells` + if(clmstatevec_allcol.eq.0) then + cc = (col%gridcell(j) - clm_begg + 1) + else + cc = (j - clm_begc + 1) + end if + + ! Catch negative or 0 values from DA + if (clm_statevec(cc+offset).lt.0.0) then + print *, "WARNING: snow depth at g,c is negative: ", cc, j, clm_statevec(cc+offset) + end if + if (clm_statevec(cc+clm_varsize+offset).le.0.0) then + print *, "WARNING: SWE at g,c is negative or zero: ", j, clm_statevec(cc+clm_varsize+offset) + else + rsnow(j) = h2osno(j) + if ( ABS(SUM(rsnow(:) - clm_statevec(cc+clm_varsize+offset))).gt.0.000001) then + h2osno(j) = clm_statevec(cc+clm_varsize+offset) + ! JK: clmupdate_snow_repartitioning.eq.3 is experimental + ! JK: clmupdate_snow_repartitioning.eq.3 from NASA-Code (based on older CLM3.5 version) + ! https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 + if ( clmupdate_snow_repartitioning.eq.3) then + incr_h2osno = h2osno(j) / rsnow(j) ! INC = New SWE / OLD SWE + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno + end do + end if + + if (isnan(rsnow(j))) then + print *, "WARNING: rsnow at j is nan: ", j + endif + if (isnan(h2osno(j))) then + print *, "WARNING: h2osno at j is nan: ", j + endif + + end if + endif + end do + ! cc = cc + 1 + ! end do + + if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then + if ( ABS(SUM(rsnow(:) - h2osno(:))).gt.0.000001) then + call clm_repartition_snow(rsnow(:)) + end if end if - end if endif end subroutine update_clm @@ -986,7 +983,7 @@ subroutine clm_repartition_snow(h2osno_in) h2osno_po(jj) = snow_depth(jj) * denice end if h2osno_pr(jj) = h2osno(jj) - else if (clmupdate_snow .eq. 2) then + else if (clmupdate_snow .eq. 2 .or. clmupdate_snow .eq. 3) then ! for clmupdate_snow == 2 we have post H2OSNO as the main H2OSNO already h2osno_po(jj) = h2osno(jj) h2osno_pr(jj) = h2osno_in(jj) @@ -1074,7 +1071,7 @@ subroutine clm_repartition_snow(h2osno_in) ! Finally adjust SWE (h2osno) since the prior value is no longer needed ! column level variables so we can adjust it outside the layer loop h2osno(jj) = h2osno_po(jj) - else if (clmupdate_snow .eq. 2) then + else if (clmupdate_snow .eq. 2 .or. clmupdate_snow .eq. 3) then ! Update the total snow depth to match udpates to layers for active snow layers if (abs(h2osno_po(jj) - h2osno_pr(jj)) .gt. 0.0_r8 .and. snlsno(jj) < 0.0_r8) then snow_depth(jj) = snow_depth(jj) + sum(gain_dzsno(-nlevsno+1:0)) From d4d2003e790a91e4472bd904ac0aeb82973d0ec0 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Mon, 17 Mar 2025 07:06:14 +0100 Subject: [PATCH 46/71] Snow-DA: Re-introduce standard "point-obs" observation operator --- bldsva/intf_DA/pdaf/framework/obs_op_pdaf.F90 | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/bldsva/intf_DA/pdaf/framework/obs_op_pdaf.F90 b/bldsva/intf_DA/pdaf/framework/obs_op_pdaf.F90 index 172ec7d6a..97becfc66 100644 --- a/bldsva/intf_DA/pdaf/framework/obs_op_pdaf.F90 +++ b/bldsva/intf_DA/pdaf/framework/obs_op_pdaf.F90 @@ -320,14 +320,9 @@ SUBROUTINE obs_op_pdaf(step, dim_p, dim_obs_p, state_p, m_state_p) if(lpointobs) then - ! WORKAROUND FOR SUDDEN SEG 11 error - ! DO NOT LEAVE IN FINAL VERSION - ! JUST DURING DEBUG PROCESS - ! JUST VALID FOR SINGLE GRID CELLS WITH SINGLE VALUE OBS - m_state_p(1) = state_p(1) -! DO i = 1, dim_obs_p -! m_state_p(i) = state_p(obs_index_p(i)) -! END DO + DO i = 1, dim_obs_p + m_state_p(i) = state_p(obs_index_p(i)) + END DO end if From b3be8a3c33912ec1455c01e9a084cc2c38f74b99 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Mon, 17 Mar 2025 07:35:25 +0100 Subject: [PATCH 47/71] doc: Snow-DA inputs --- doc/content/setup_tsmp/input_enkfpf.md | 152 ++++++++++++++++--------- 1 file changed, 100 insertions(+), 52 deletions(-) diff --git a/doc/content/setup_tsmp/input_enkfpf.md b/doc/content/setup_tsmp/input_enkfpf.md index 4d1082a1a..8c773dc42 100644 --- a/doc/content/setup_tsmp/input_enkfpf.md +++ b/doc/content/setup_tsmp/input_enkfpf.md @@ -53,6 +53,8 @@ nprocs = update_swc = update_texture = update_T = +update_snow = +update_snow_repartitioning = print_swc = print_et = statevec_allcol = @@ -478,6 +480,50 @@ Currently only CLM3.5 - 1: Update of ground and vegetation temperature +### CLM:update_snow ### + +`CLM:update_snow`: (integer) Flag for Snow-DA. + +Only CLM5.0/eCLM. + +- 0 (Default): No Snow-DA + +- 1: Assimilation of snow depth. State vector: Snow depth. + +- 2: Assimilation of snow water equivalent. State vector: Snow water + equivalent. + +- 3: Assimilation of snow depth. State vector: Snow depth and snow + water equivalent. Snow water equivalent from state vector is used + in the update as in Case 2. + +See CLM Technical Note for more information on snow variable: +https://escomp.github.io/ctsm-docs/versions/release-clm5.0/html/tech_note/Snow_Hydrology/CLM50_Tech_Note_Snow_Hydrology.html +- snow depth: `waterstate_inst%snow_depth_col` +- snow water equivalent: `waterstate_inst%h2osno_col` + +### CLM:update_snow_repartitioning ### + +`CLM:update_snow_repartitioning`: (integer) Flag for snow-layer +repartitioning applied during Snow-DA. + +Only used if Snow-DA is switched on by setting `CLM:update_snow`. Only +CLM5.0/eCLM. + +- 0 (Default): No repartitioning. Not recommended if Snow-DA is used, + when state vector consists of diagnostic variables. + +- 1: Repartitioning routine in DA-interface: + `clm_repartition_snow`. Option 1: Adjusting the bottom layer only. + +- 2: Repartitioning routine in DA-interface: + `clm_repartition_snow`. Option 2: Adjusting all active layers. + +- 3 (Currently recommended): `h2osoi_ice` updated by increment based + on updated state vector variable. Further repartitioning left to + CLM-code. Based on + https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 + ### CLM:print_swc ### `CLM:print_swc`: (integer) If set to `1`, the updated soil moisture @@ -752,58 +798,60 @@ Default: 0, output turned off. ## Parameter Summary ## - | section | parameter | default value | - |:---------:|:-----------------------:|:-------------:| - | `[PF]` | | | - | | `problemname` | \- | - | | `nprocs` | 0 | - | | `starttime` | 0.0 | - | | `endtime` | 0 | - | | `simtime` | 0 | - | | `dt` | 0.0 | - | | `updateflag` | 1 | - | | `paramupdate` | 0 | - | | `paramupdate_frequency` | 1 | - | | `dampingfactor_param` | 1.0 | - | | `dampingfactor_state` | 1.0 | - | | `damping_switch_sm` | 1 | - | | `aniso_perm_y` | 1.0 | - | | `aniso_perm_z` | 1.0 | - | | `aniso_use_parflow` | 0 | - | | | | - | | `printensemble` | 1 | - | | `t_printensemble` | -1 | - | | `printstat` | 1 | - | | `paramprintensemble` | 1 | - | | `paramprintstat` | 1 | - | | `olfmasking` | 0 | - | | `olfmasking_param` | 0 | - | | `olfmasking_depth` | 0 | - | `[CLM]` | | | - | | `problemname` | \- | - | | `nprocs` | 0 | - | | `update_swc` | 1 | - | | `print_swc` | 0 | - | | `print_et` | 0 | - | | `statevec_allcol` | 0 | - | | `statevec_only_active` | 0 | - | | `statevec_max_layer` | 25 | - | | `t_printensemble` | -1 | - | | `watmin_switch` | 0 | - | `[COSMO]` | | | - | | `nprocs` | 0 | - | | `dtmult` | 0 | - | `[DA]` | | | - | | `nreal` | 0 | - | | `outdir` | \- | - | | `da_interval` | 1 | - | | `stat_dumpoffset` | 0 | - | | `screen_wrapper` | 1 | - | | `point_obs` | 1 | - | | `obs_interp_switch` | 0 | - | | `crns_flag` | 1 | - | | `da_crns_depth_tol` | 0.01 | - | | `print_obs_index` | 0 | + | section | parameter | default value | + |:---------:|:----------------------------:|:-------------:| + | `[PF]` | | | + | | `problemname` | \- | + | | `nprocs` | 0 | + | | `starttime` | 0.0 | + | | `endtime` | 0 | + | | `simtime` | 0 | + | | `dt` | 0.0 | + | | `updateflag` | 1 | + | | `paramupdate` | 0 | + | | `paramupdate_frequency` | 1 | + | | `dampingfactor_param` | 1.0 | + | | `dampingfactor_state` | 1.0 | + | | `damping_switch_sm` | 1 | + | | `aniso_perm_y` | 1.0 | + | | `aniso_perm_z` | 1.0 | + | | `aniso_use_parflow` | 0 | + | | | | + | | `printensemble` | 1 | + | | `t_printensemble` | -1 | + | | `printstat` | 1 | + | | `paramprintensemble` | 1 | + | | `paramprintstat` | 1 | + | | `olfmasking` | 0 | + | | `olfmasking_param` | 0 | + | | `olfmasking_depth` | 0 | + | `[CLM]` | | | + | | `problemname` | \- | + | | `nprocs` | 0 | + | | `update_swc` | 1 | + | | `update_snow` | 0 | + | | `update_snow_repartitioning` | 1 | + | | `print_swc` | 0 | + | | `print_et` | 0 | + | | `statevec_allcol` | 0 | + | | `statevec_only_active` | 0 | + | | `statevec_max_layer` | 25 | + | | `t_printensemble` | -1 | + | | `watmin_switch` | 0 | + | `[COSMO]` | | | + | | `nprocs` | 0 | + | | `dtmult` | 0 | + | `[DA]` | | | + | | `nreal` | 0 | + | | `outdir` | \- | + | | `da_interval` | 1 | + | | `stat_dumpoffset` | 0 | + | | `screen_wrapper` | 1 | + | | `point_obs` | 1 | + | | `obs_interp_switch` | 0 | + | | `crns_flag` | 1 | + | | `da_crns_depth_tol` | 0.01 | + | | `print_obs_index` | 0 | Default values for parameter file `enkfpf.par`. From 5f1e7ac0509c7d273adf82adfdbcd265645fded2 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Mon, 17 Mar 2025 07:58:23 +0100 Subject: [PATCH 48/71] doc: fix links --- doc/content/setup_tsmp/input_enkfpf.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/content/setup_tsmp/input_enkfpf.md b/doc/content/setup_tsmp/input_enkfpf.md index 8c773dc42..9d36e8541 100644 --- a/doc/content/setup_tsmp/input_enkfpf.md +++ b/doc/content/setup_tsmp/input_enkfpf.md @@ -498,7 +498,7 @@ Only CLM5.0/eCLM. in the update as in Case 2. See CLM Technical Note for more information on snow variable: -https://escomp.github.io/ctsm-docs/versions/release-clm5.0/html/tech_note/Snow_Hydrology/CLM50_Tech_Note_Snow_Hydrology.html + - snow depth: `waterstate_inst%snow_depth_col` - snow water equivalent: `waterstate_inst%h2osno_col` @@ -522,7 +522,7 @@ CLM5.0/eCLM. - 3 (Currently recommended): `h2osoi_ice` updated by increment based on updated state vector variable. Further repartitioning left to CLM-code. Based on - https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 + ### CLM:print_swc ### From d2bb862ae7a43a98cd591b6a189fc245115e755b Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Mon, 17 Mar 2025 11:21:40 +0100 Subject: [PATCH 49/71] Snow-DA: Refactoring to prepare for PR into `master` --- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 275 +++++------------- 1 file changed, 75 insertions(+), 200 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index cf7fff0e9..9490f3561 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -276,16 +276,13 @@ subroutine define_clm_statevec(mype) ! Snow assimilation ! Case 1: Assimilation of snow depth : allocated 1 per column in CLM5 ! But observations and history file 1 per grid cell and therefore statevecsize 1 per grid cell - if(clmupdate_snow.eq.1) then - clm_varsize = (clm_endg-clm_begg+1) ! Currently no combination of SWC and snow DA - clm_statevecsize = (clm_endg-clm_begg+1) ! So like this if snow is set it takes priority - endif ! Case 2: Assimilation of snow water equivalent same as above - if(clmupdate_snow.eq.2) then + if(clmupdate_snow.eq.1 .or. clmupdate_snow.eq.2) then clm_varsize = (clm_endg-clm_begg+1) ! Currently no combination of SWC and snow DA clm_statevecsize = (clm_endg-clm_begg+1) ! So like this if snow is set it takes priority endif - ! Case 3: Assimilation of snow depth: adding swe in the state vector + ! Case 3: Assimilation of snow depth: Snow depth and snow water + ! equivalent in the state vector if(clmupdate_snow.eq.3) then clm_varsize = (clm_endg-clm_begg+1) clm_statevecsize = 2*(clm_endg-clm_begg+1) @@ -410,63 +407,11 @@ subroutine set_clm_statevec(tstartcycle, mype) end do endif - ! Snow assimilation + ! Snow assimilation state vector ! Case 1: Snow depth - if(clmupdate_snow.eq.1) then - cc = 1 - do j=clm_begg,clm_endg - ! Only get the snow_depth from the first column of each gridcell - ! and add it to the clm_statevec at the position of the gridcell (cc) - newgridcell = .true. - do jj=clm_begc,clm_endc - g = col%gridcell(jj) - if (g .eq. j) then - if (newgridcell) then - newgridcell = .false. - clm_statevec(cc+offset) = snow_depth(jj) - endif - endif - end do - cc = cc + 1 - end do - endif ! Case 2: SWE - if(clmupdate_snow.eq.2) then - cc = 1 - - if(clmstatevec_allcol.eq.0) then - - do j=clm_begg,clm_endg - ! Only get the SWE from the first column of each gridcell - ! and add it to the clm_statevec at the position of the gridcell (cc) - newgridcell = .true. - do jj=clm_begc,clm_endc - g = col%gridcell(jj) - if (g .eq. j) then - if (newgridcell) then - newgridcell = .false. - clm_statevec(cc+offset) = h2osno(jj) - endif - endif - end do - cc = cc + 1 - end do - - else - - do jj=clm_begc,clm_endc - ! SWC from all columns of each gridcell - clm_statevec(cc+offset) = h2osno(jj) - cc = cc + 1 - end do - - end if - - endif - ! Case 3: Snow Depth with swe in state vector - if(clmupdate_snow.eq.3) then - - ! snow_depth and swe into state vector + ! Case 3: Snow depth + SWE + if(clmupdate_snow.ne.0) then cc = 1 do j=clm_begg,clm_endg ! Only get the snow_depth/swe from the first column of each gridcell @@ -477,8 +422,18 @@ subroutine set_clm_statevec(tstartcycle, mype) if (g .eq. j) then if (newgridcell) then newgridcell = .false. - clm_statevec(cc+offset) = snow_depth(jj) - clm_statevec(cc+clm_varsize+offset) = h2osno(jj) + + if(clmupdate_snow.eq.1) then + clm_statevec(cc+offset) = snow_depth(jj) + else if(clmupdate_snow.eq.2) then + clm_statevec(cc+offset) = h2osno(jj) + else if(clmupdate_snow.eq.3) then + clm_statevec(cc+offset) = snow_depth(jj) + clm_statevec(cc+clm_varsize+offset) = h2osno(jj) + else + error stop "Wrong input for clmupdate_snow" + end if + endif endif end do @@ -531,6 +486,7 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") real(r8) :: rliq,rice,incr_h2osno real(r8) :: rsnow(clm_statevecsize) + real(r8) :: nsnow(clm_statevecsize) real(r8) :: watmin_check ! minimum soil moisture for checking clm_statevec (mm) real(r8) :: watmin_set ! minimum soil moisture for setting swc (mm) real(r8) :: swc_update ! updated SWC in loop @@ -749,156 +705,75 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! Snow assimilation: ! Case 1: Snow depth - ! Write updated snow depth back to CLM and then repartition snow and adjust related variables - if(clmupdate_snow.eq.1) then + ! Case 2: Snow water equivalent + ! Case 3: Snow depth (assimilated) and SWE (used for increment) in state vector + ! Write updated snow variable back to CLM and then repartition snow and adjust related variables + if(clmupdate_snow.ne.0) then do j=clm_begc,clm_endc - ! iterate through the columns and copy from the same gridcell - ! i.e. statevec position (cc) for each column + ! Iterate through the columns + + ! For each column index, copy the gridcell-linked state vector + ! value at state vector index `cc` ! Set cc (the state vector index) from the - ! CLM5-grid-index and the `CLM5-layer-index times - ! num_gridcells` - if(clmstatevec_allcol.eq.0) then - cc = (col%gridcell(j) - clm_begg + 1) - else - cc = (j - clm_begc + 1) - end if + ! CLM5-grid-index + cc = (col%gridcell(j) - clm_begg + 1) + ! Catch negative or 0 values from DA - if (clm_statevec(cc+offset).lt.0.0) then - print *, "WARNING: snow depth at g,c is negative: ", cc, j, clm_statevec(cc+offset) - else - rsnow(j) = snow_depth(j) - if ( ABS(SUM(rsnow(:) - clm_statevec(cc+offset))) .gt.0.000001) then - snow_depth(j) = clm_statevec(cc+offset) - ! JK: clmupdate_snow_repartitioning.eq.3 is experimental - ! JK: clmupdate_snow_repartitioning.eq.3 from NASA-Code (based on older CLM3.5 version) - ! https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 - if ( clmupdate_snow_repartitioning.eq.3) then - incr_h2osno = snow_depth(j) / rsnow(j) ! INC = New SD / OLD SD - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno - end do - endif - endif - endif - end do + if (clm_statevec(cc+offset).le.0.0) then + print *, "WARNING: Snow-statevec is negative/zero at cc. cc, j, offset, clm_statevec(cc+offset): ", cc, j, offset, clm_statevec(cc+offset) + end if - if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then - if ( ABS(SUM(rsnow(:))).gt.0.000001) then - call clm_repartition_snow() - end if - end if - endif - ! Case 2: Snow water equivalent - ! Write updated snow depth back to CLM and then repartition snow and adjust related variables - if(clmupdate_snow.eq.2) then - ! cc = 1 - ! do j=clm_begg,clm_endg - ! iterate through the columns and copy from the same gridcell - ! i.e. statevec position (cc) for each column - do j=clm_begc,clm_endc - - ! Set cc (the state vector index) from the - ! CLM5-grid-index and the `CLM5-layer-index times - ! num_gridcells` - if(clmstatevec_allcol.eq.0) then - cc = (col%gridcell(j) - clm_begg + 1) - else - cc = (j - clm_begc + 1) - end if + if (clmupdate_snow.eq.1) then + rsnow(j) = snow_depth(j) + else if (clmupdate_snow.eq.2) then + rsnow(j) = h2osno(j) + else if (clmupdate_snow.eq.3) then + rsnow(j) = h2osno(j) + end if - ! Catch negative or 0 values from DA - if (clm_statevec(cc+offset).le.0.0) then - print *, "WARNING: SWE at g,c is negative or zero: ", j, clm_statevec(cc+offset) - else - rsnow(j) = h2osno(j) - if ( ABS(SUM(rsnow(:) - clm_statevec(cc+offset))).gt.0.000001) then - h2osno(j) = clm_statevec(cc+offset) - ! JK: clmupdate_snow_repartitioning.eq.3 is experimental - ! JK: clmupdate_snow_repartitioning.eq.3 from NASA-Code (based on older CLM3.5 version) - ! https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 - if ( clmupdate_snow_repartitioning.eq.3) then - incr_h2osno = h2osno(j) / rsnow(j) ! INC = New SWE / OLD SWE - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno - end do - end if - - if (isnan(rsnow(j))) then - print *, "WARNING: rsnow at j is nan: ", j - endif - if (isnan(h2osno(j))) then - print *, "WARNING: h2osno at j is nan: ", j - endif + if (clmupdate_snow.eq.1 .or. clmupdate_snow.eq.2) then + nsnow(j) = clm_statevec(cc+offset) + else if (clmupdate_snow.eq.3) then + nsnow(j) = clm_statevec(cc+clm_varsize+offset) + end if + + ! Update state variable to CLM + ! Not needed for repartioning-case 3? + if(clmupdate_snow.eq.1) then + snow_depth(j) = nsnow(j) + end if + if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3) then + h2osno(j) = nsnow(j) + end if - end if - endif - end do - ! cc = cc + 1 - ! end do + end do - if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then - if ( ABS(SUM(rsnow(:) - h2osno(:))).gt.0.000001) then - call clm_repartition_snow(rsnow(:)) - end if + ! Repartitioning + if ( clmupdate_snow_repartitioning.eq.1 .and. clmupdate_snow_repartitioning.eq.2) then + + if ( SUM(ABS(rsnow(:) - nsnow(:))).gt.0.000001) then + call clm_repartition_snow(rsnow(:)) end if - endif - ! Case 3: Snow depth with swe in state vector - ! Use updated swe (from snow_depth observations) to update h2osoi_ice by increment - if(clmupdate_snow.eq.3) then - ! cc = 1 - ! do j=clm_begg,clm_endg - ! iterate through the columns and copy from the same gridcell - ! i.e. statevec position (cc) for each column - do j=clm_begc,clm_endc - - ! Set cc (the state vector index) from the - ! CLM5-grid-index and the `CLM5-layer-index times - ! num_gridcells` - if(clmstatevec_allcol.eq.0) then - cc = (col%gridcell(j) - clm_begg + 1) - else - cc = (j - clm_begc + 1) - end if - ! Catch negative or 0 values from DA - if (clm_statevec(cc+offset).lt.0.0) then - print *, "WARNING: snow depth at g,c is negative: ", cc, j, clm_statevec(cc+offset) - end if - if (clm_statevec(cc+clm_varsize+offset).le.0.0) then - print *, "WARNING: SWE at g,c is negative or zero: ", j, clm_statevec(cc+clm_varsize+offset) - else - rsnow(j) = h2osno(j) - if ( ABS(SUM(rsnow(:) - clm_statevec(cc+clm_varsize+offset))).gt.0.000001) then - h2osno(j) = clm_statevec(cc+clm_varsize+offset) - ! JK: clmupdate_snow_repartitioning.eq.3 is experimental - ! JK: clmupdate_snow_repartitioning.eq.3 from NASA-Code (based on older CLM3.5 version) - ! https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 - if ( clmupdate_snow_repartitioning.eq.3) then - incr_h2osno = h2osno(j) / rsnow(j) ! INC = New SWE / OLD SWE - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno - end do - end if - - if (isnan(rsnow(j))) then - print *, "WARNING: rsnow at j is nan: ", j - endif - if (isnan(h2osno(j))) then - print *, "WARNING: h2osno at j is nan: ", j - endif + end if - end if - endif - end do - ! cc = cc + 1 - ! end do + if ( clmupdate_snow_repartitioning.eq.3) then - if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then - if ( ABS(SUM(rsnow(:) - h2osno(:))).gt.0.000001) then - call clm_repartition_snow(rsnow(:)) + do j=clm_begc,clm_endc + if ( ABS(rsnow(j) - nsnow(j)).gt.0.000001) then + if ( ABS(rsnow(j)).gt.0.000001) then + ! Update h2osoi_ice with increment + incr_h2osno = nsnow(j) / rsnow(j) ! INC = New snow var / OLD snow var + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno + end do + end if end if - end if + end do + + end if + endif end subroutine update_clm From a2ff7076f74dbbbfeb8df1bf6bc11afbb9be6d26 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Mon, 17 Mar 2025 11:27:40 +0100 Subject: [PATCH 50/71] Snow-DA: Refactoring to prepare for PR into `master` --- .../pdaf/model/eclm/enkf_clm_mod_5.F90 | 275 +++++------------- 1 file changed, 75 insertions(+), 200 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index cf7fff0e9..9490f3561 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -276,16 +276,13 @@ subroutine define_clm_statevec(mype) ! Snow assimilation ! Case 1: Assimilation of snow depth : allocated 1 per column in CLM5 ! But observations and history file 1 per grid cell and therefore statevecsize 1 per grid cell - if(clmupdate_snow.eq.1) then - clm_varsize = (clm_endg-clm_begg+1) ! Currently no combination of SWC and snow DA - clm_statevecsize = (clm_endg-clm_begg+1) ! So like this if snow is set it takes priority - endif ! Case 2: Assimilation of snow water equivalent same as above - if(clmupdate_snow.eq.2) then + if(clmupdate_snow.eq.1 .or. clmupdate_snow.eq.2) then clm_varsize = (clm_endg-clm_begg+1) ! Currently no combination of SWC and snow DA clm_statevecsize = (clm_endg-clm_begg+1) ! So like this if snow is set it takes priority endif - ! Case 3: Assimilation of snow depth: adding swe in the state vector + ! Case 3: Assimilation of snow depth: Snow depth and snow water + ! equivalent in the state vector if(clmupdate_snow.eq.3) then clm_varsize = (clm_endg-clm_begg+1) clm_statevecsize = 2*(clm_endg-clm_begg+1) @@ -410,63 +407,11 @@ subroutine set_clm_statevec(tstartcycle, mype) end do endif - ! Snow assimilation + ! Snow assimilation state vector ! Case 1: Snow depth - if(clmupdate_snow.eq.1) then - cc = 1 - do j=clm_begg,clm_endg - ! Only get the snow_depth from the first column of each gridcell - ! and add it to the clm_statevec at the position of the gridcell (cc) - newgridcell = .true. - do jj=clm_begc,clm_endc - g = col%gridcell(jj) - if (g .eq. j) then - if (newgridcell) then - newgridcell = .false. - clm_statevec(cc+offset) = snow_depth(jj) - endif - endif - end do - cc = cc + 1 - end do - endif ! Case 2: SWE - if(clmupdate_snow.eq.2) then - cc = 1 - - if(clmstatevec_allcol.eq.0) then - - do j=clm_begg,clm_endg - ! Only get the SWE from the first column of each gridcell - ! and add it to the clm_statevec at the position of the gridcell (cc) - newgridcell = .true. - do jj=clm_begc,clm_endc - g = col%gridcell(jj) - if (g .eq. j) then - if (newgridcell) then - newgridcell = .false. - clm_statevec(cc+offset) = h2osno(jj) - endif - endif - end do - cc = cc + 1 - end do - - else - - do jj=clm_begc,clm_endc - ! SWC from all columns of each gridcell - clm_statevec(cc+offset) = h2osno(jj) - cc = cc + 1 - end do - - end if - - endif - ! Case 3: Snow Depth with swe in state vector - if(clmupdate_snow.eq.3) then - - ! snow_depth and swe into state vector + ! Case 3: Snow depth + SWE + if(clmupdate_snow.ne.0) then cc = 1 do j=clm_begg,clm_endg ! Only get the snow_depth/swe from the first column of each gridcell @@ -477,8 +422,18 @@ subroutine set_clm_statevec(tstartcycle, mype) if (g .eq. j) then if (newgridcell) then newgridcell = .false. - clm_statevec(cc+offset) = snow_depth(jj) - clm_statevec(cc+clm_varsize+offset) = h2osno(jj) + + if(clmupdate_snow.eq.1) then + clm_statevec(cc+offset) = snow_depth(jj) + else if(clmupdate_snow.eq.2) then + clm_statevec(cc+offset) = h2osno(jj) + else if(clmupdate_snow.eq.3) then + clm_statevec(cc+offset) = snow_depth(jj) + clm_statevec(cc+clm_varsize+offset) = h2osno(jj) + else + error stop "Wrong input for clmupdate_snow" + end if + endif endif end do @@ -531,6 +486,7 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") real(r8) :: rliq,rice,incr_h2osno real(r8) :: rsnow(clm_statevecsize) + real(r8) :: nsnow(clm_statevecsize) real(r8) :: watmin_check ! minimum soil moisture for checking clm_statevec (mm) real(r8) :: watmin_set ! minimum soil moisture for setting swc (mm) real(r8) :: swc_update ! updated SWC in loop @@ -749,156 +705,75 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! Snow assimilation: ! Case 1: Snow depth - ! Write updated snow depth back to CLM and then repartition snow and adjust related variables - if(clmupdate_snow.eq.1) then + ! Case 2: Snow water equivalent + ! Case 3: Snow depth (assimilated) and SWE (used for increment) in state vector + ! Write updated snow variable back to CLM and then repartition snow and adjust related variables + if(clmupdate_snow.ne.0) then do j=clm_begc,clm_endc - ! iterate through the columns and copy from the same gridcell - ! i.e. statevec position (cc) for each column + ! Iterate through the columns + + ! For each column index, copy the gridcell-linked state vector + ! value at state vector index `cc` ! Set cc (the state vector index) from the - ! CLM5-grid-index and the `CLM5-layer-index times - ! num_gridcells` - if(clmstatevec_allcol.eq.0) then - cc = (col%gridcell(j) - clm_begg + 1) - else - cc = (j - clm_begc + 1) - end if + ! CLM5-grid-index + cc = (col%gridcell(j) - clm_begg + 1) + ! Catch negative or 0 values from DA - if (clm_statevec(cc+offset).lt.0.0) then - print *, "WARNING: snow depth at g,c is negative: ", cc, j, clm_statevec(cc+offset) - else - rsnow(j) = snow_depth(j) - if ( ABS(SUM(rsnow(:) - clm_statevec(cc+offset))) .gt.0.000001) then - snow_depth(j) = clm_statevec(cc+offset) - ! JK: clmupdate_snow_repartitioning.eq.3 is experimental - ! JK: clmupdate_snow_repartitioning.eq.3 from NASA-Code (based on older CLM3.5 version) - ! https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 - if ( clmupdate_snow_repartitioning.eq.3) then - incr_h2osno = snow_depth(j) / rsnow(j) ! INC = New SD / OLD SD - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno - end do - endif - endif - endif - end do + if (clm_statevec(cc+offset).le.0.0) then + print *, "WARNING: Snow-statevec is negative/zero at cc. cc, j, offset, clm_statevec(cc+offset): ", cc, j, offset, clm_statevec(cc+offset) + end if - if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then - if ( ABS(SUM(rsnow(:))).gt.0.000001) then - call clm_repartition_snow() - end if - end if - endif - ! Case 2: Snow water equivalent - ! Write updated snow depth back to CLM and then repartition snow and adjust related variables - if(clmupdate_snow.eq.2) then - ! cc = 1 - ! do j=clm_begg,clm_endg - ! iterate through the columns and copy from the same gridcell - ! i.e. statevec position (cc) for each column - do j=clm_begc,clm_endc - - ! Set cc (the state vector index) from the - ! CLM5-grid-index and the `CLM5-layer-index times - ! num_gridcells` - if(clmstatevec_allcol.eq.0) then - cc = (col%gridcell(j) - clm_begg + 1) - else - cc = (j - clm_begc + 1) - end if + if (clmupdate_snow.eq.1) then + rsnow(j) = snow_depth(j) + else if (clmupdate_snow.eq.2) then + rsnow(j) = h2osno(j) + else if (clmupdate_snow.eq.3) then + rsnow(j) = h2osno(j) + end if - ! Catch negative or 0 values from DA - if (clm_statevec(cc+offset).le.0.0) then - print *, "WARNING: SWE at g,c is negative or zero: ", j, clm_statevec(cc+offset) - else - rsnow(j) = h2osno(j) - if ( ABS(SUM(rsnow(:) - clm_statevec(cc+offset))).gt.0.000001) then - h2osno(j) = clm_statevec(cc+offset) - ! JK: clmupdate_snow_repartitioning.eq.3 is experimental - ! JK: clmupdate_snow_repartitioning.eq.3 from NASA-Code (based on older CLM3.5 version) - ! https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 - if ( clmupdate_snow_repartitioning.eq.3) then - incr_h2osno = h2osno(j) / rsnow(j) ! INC = New SWE / OLD SWE - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno - end do - end if - - if (isnan(rsnow(j))) then - print *, "WARNING: rsnow at j is nan: ", j - endif - if (isnan(h2osno(j))) then - print *, "WARNING: h2osno at j is nan: ", j - endif + if (clmupdate_snow.eq.1 .or. clmupdate_snow.eq.2) then + nsnow(j) = clm_statevec(cc+offset) + else if (clmupdate_snow.eq.3) then + nsnow(j) = clm_statevec(cc+clm_varsize+offset) + end if + + ! Update state variable to CLM + ! Not needed for repartioning-case 3? + if(clmupdate_snow.eq.1) then + snow_depth(j) = nsnow(j) + end if + if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3) then + h2osno(j) = nsnow(j) + end if - end if - endif - end do - ! cc = cc + 1 - ! end do + end do - if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then - if ( ABS(SUM(rsnow(:) - h2osno(:))).gt.0.000001) then - call clm_repartition_snow(rsnow(:)) - end if + ! Repartitioning + if ( clmupdate_snow_repartitioning.eq.1 .and. clmupdate_snow_repartitioning.eq.2) then + + if ( SUM(ABS(rsnow(:) - nsnow(:))).gt.0.000001) then + call clm_repartition_snow(rsnow(:)) end if - endif - ! Case 3: Snow depth with swe in state vector - ! Use updated swe (from snow_depth observations) to update h2osoi_ice by increment - if(clmupdate_snow.eq.3) then - ! cc = 1 - ! do j=clm_begg,clm_endg - ! iterate through the columns and copy from the same gridcell - ! i.e. statevec position (cc) for each column - do j=clm_begc,clm_endc - - ! Set cc (the state vector index) from the - ! CLM5-grid-index and the `CLM5-layer-index times - ! num_gridcells` - if(clmstatevec_allcol.eq.0) then - cc = (col%gridcell(j) - clm_begg + 1) - else - cc = (j - clm_begc + 1) - end if - ! Catch negative or 0 values from DA - if (clm_statevec(cc+offset).lt.0.0) then - print *, "WARNING: snow depth at g,c is negative: ", cc, j, clm_statevec(cc+offset) - end if - if (clm_statevec(cc+clm_varsize+offset).le.0.0) then - print *, "WARNING: SWE at g,c is negative or zero: ", j, clm_statevec(cc+clm_varsize+offset) - else - rsnow(j) = h2osno(j) - if ( ABS(SUM(rsnow(:) - clm_statevec(cc+clm_varsize+offset))).gt.0.000001) then - h2osno(j) = clm_statevec(cc+clm_varsize+offset) - ! JK: clmupdate_snow_repartitioning.eq.3 is experimental - ! JK: clmupdate_snow_repartitioning.eq.3 from NASA-Code (based on older CLM3.5 version) - ! https://github.com/NASA-LIS/LISF/blob/master/lis/surfacemodels/land/clm2/da_snow/clm2_setsnowvars.F90 - if ( clmupdate_snow_repartitioning.eq.3) then - incr_h2osno = h2osno(j) / rsnow(j) ! INC = New SWE / OLD SWE - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno - end do - end if - - if (isnan(rsnow(j))) then - print *, "WARNING: rsnow at j is nan: ", j - endif - if (isnan(h2osno(j))) then - print *, "WARNING: h2osno at j is nan: ", j - endif + end if - end if - endif - end do - ! cc = cc + 1 - ! end do + if ( clmupdate_snow_repartitioning.eq.3) then - if ( clmupdate_snow_repartitioning.ne.0 .and. clmupdate_snow_repartitioning.ne.3) then - if ( ABS(SUM(rsnow(:) - h2osno(:))).gt.0.000001) then - call clm_repartition_snow(rsnow(:)) + do j=clm_begc,clm_endc + if ( ABS(rsnow(j) - nsnow(j)).gt.0.000001) then + if ( ABS(rsnow(j)).gt.0.000001) then + ! Update h2osoi_ice with increment + incr_h2osno = nsnow(j) / rsnow(j) ! INC = New snow var / OLD snow var + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno + end do + end if end if - end if + end do + + end if + endif end subroutine update_clm From 09854d46fec3e7ddbd6bb4039f4588a2c1a20c59 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Mon, 17 Mar 2025 18:36:12 +0100 Subject: [PATCH 51/71] Snow-DA: adapt dimension of `rsnow`, `nsnow` --- bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index 9490f3561..13bd051b4 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -485,8 +485,8 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") real(r8), pointer :: h2osoi_ice(:,:) real(r8) :: rliq,rice,incr_h2osno - real(r8) :: rsnow(clm_statevecsize) - real(r8) :: nsnow(clm_statevecsize) + real(r8) :: rsnow(clm_begc:clm_endc) + real(r8) :: nsnow(clm_begc:clm_endc) real(r8) :: watmin_check ! minimum soil moisture for checking clm_statevec (mm) real(r8) :: watmin_set ! minimum soil moisture for setting swc (mm) real(r8) :: swc_update ! updated SWC in loop From f44b95b5453b48e579867689190a5df14444f2c9 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Mon, 17 Mar 2025 18:37:30 +0100 Subject: [PATCH 52/71] Snow-DA: try to fix update conditions --- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 46 ++++++++++--------- .../pdaf/model/eclm/enkf_clm_mod_5.F90 | 42 +++++++++-------- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 9490f3561..39711a7e3 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -485,8 +485,8 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") real(r8), pointer :: h2osoi_ice(:,:) real(r8) :: rliq,rice,incr_h2osno - real(r8) :: rsnow(clm_statevecsize) - real(r8) :: nsnow(clm_statevecsize) + real(r8) :: rsnow(clm_begc:clm_endc) + real(r8) :: nsnow(clm_begc:clm_endc) real(r8) :: watmin_check ! minimum soil moisture for checking clm_statevec (mm) real(r8) :: watmin_set ! minimum soil moisture for setting swc (mm) real(r8) :: swc_update ! updated SWC in loop @@ -719,11 +719,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! CLM5-grid-index cc = (col%gridcell(j) - clm_begg + 1) - ! Catch negative or 0 values from DA - if (clm_statevec(cc+offset).le.0.0) then - print *, "WARNING: Snow-statevec is negative/zero at cc. cc, j, offset, clm_statevec(cc+offset): ", cc, j, offset, clm_statevec(cc+offset) - end if - if (clmupdate_snow.eq.1) then rsnow(j) = snow_depth(j) else if (clmupdate_snow.eq.2) then @@ -737,14 +732,19 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") else if (clmupdate_snow.eq.3) then nsnow(j) = clm_statevec(cc+clm_varsize+offset) end if - - ! Update state variable to CLM - ! Not needed for repartioning-case 3? - if(clmupdate_snow.eq.1) then - snow_depth(j) = nsnow(j) - end if - if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3) then - h2osno(j) = nsnow(j) + + if (nsnow(j).gt.0.0) then + ! Update state variable to CLM + ! Not needed for repartioning-case 3? + if(clmupdate_snow.eq.1) then + snow_depth(j) = nsnow(j) + end if + if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3) then + h2osno(j) = nsnow(j) + end if + else + ! Catch negative or 0 values from DA + print *, "WARNING: Snow-statevec is negative/zero at cc. cc, j, offset, clm_statevec(cc+offset): ", cc, j, offset, clm_statevec(cc+offset) end if end do @@ -761,13 +761,15 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if ( clmupdate_snow_repartitioning.eq.3) then do j=clm_begc,clm_endc - if ( ABS(rsnow(j) - nsnow(j)).gt.0.000001) then - if ( ABS(rsnow(j)).gt.0.000001) then - ! Update h2osoi_ice with increment - incr_h2osno = nsnow(j) / rsnow(j) ! INC = New snow var / OLD snow var - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno - end do + if (nsnow(j).gt.0.0) then + if ( ABS(rsnow(j) - nsnow(j)).gt.0.000001) then + if ( rsnow(j).ne.0.0) then + ! Update h2osoi_ice with increment + incr_h2osno = nsnow(j) / rsnow(j) ! INC = New snow var / OLD snow var + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno + end do + end if end if end if end do diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index 13bd051b4..39711a7e3 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -719,11 +719,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! CLM5-grid-index cc = (col%gridcell(j) - clm_begg + 1) - ! Catch negative or 0 values from DA - if (clm_statevec(cc+offset).le.0.0) then - print *, "WARNING: Snow-statevec is negative/zero at cc. cc, j, offset, clm_statevec(cc+offset): ", cc, j, offset, clm_statevec(cc+offset) - end if - if (clmupdate_snow.eq.1) then rsnow(j) = snow_depth(j) else if (clmupdate_snow.eq.2) then @@ -737,14 +732,19 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") else if (clmupdate_snow.eq.3) then nsnow(j) = clm_statevec(cc+clm_varsize+offset) end if - - ! Update state variable to CLM - ! Not needed for repartioning-case 3? - if(clmupdate_snow.eq.1) then - snow_depth(j) = nsnow(j) - end if - if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3) then - h2osno(j) = nsnow(j) + + if (nsnow(j).gt.0.0) then + ! Update state variable to CLM + ! Not needed for repartioning-case 3? + if(clmupdate_snow.eq.1) then + snow_depth(j) = nsnow(j) + end if + if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3) then + h2osno(j) = nsnow(j) + end if + else + ! Catch negative or 0 values from DA + print *, "WARNING: Snow-statevec is negative/zero at cc. cc, j, offset, clm_statevec(cc+offset): ", cc, j, offset, clm_statevec(cc+offset) end if end do @@ -761,13 +761,15 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if ( clmupdate_snow_repartitioning.eq.3) then do j=clm_begc,clm_endc - if ( ABS(rsnow(j) - nsnow(j)).gt.0.000001) then - if ( ABS(rsnow(j)).gt.0.000001) then - ! Update h2osoi_ice with increment - incr_h2osno = nsnow(j) / rsnow(j) ! INC = New snow var / OLD snow var - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno - end do + if (nsnow(j).gt.0.0) then + if ( ABS(rsnow(j) - nsnow(j)).gt.0.000001) then + if ( rsnow(j).ne.0.0) then + ! Update h2osoi_ice with increment + incr_h2osno = nsnow(j) / rsnow(j) ! INC = New snow var / OLD snow var + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno + end do + end if end if end if end do From 19ac5e3ee659eb12ce27161bb544ef7a72154354 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Mon, 17 Mar 2025 18:47:14 +0100 Subject: [PATCH 53/71] Snow-DA: debug output of `h2osoi_ice` --- bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 13 +++++++++++++ bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 39711a7e3..78ff66690 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -537,7 +537,9 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") OPEN(unit=71, file=fn5, action="write") WRITE (71,"(es22.15)") h2osoi_liq(:,:) CLOSE(71) + END IF + IF(clmupdate_swc.NE.0 .OR. clmupdate_snow.NE.0) THEN ! TSMP-PDAF: For debug runs, output the state vector in files WRITE(fn6, "(a,i5.5,a,i5.5,a)") "h2osoi_ice", mype, ".bef_up.", tstartcycle, ".txt" OPEN(unit=71, file=fn6, action="write") @@ -776,6 +778,17 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") end if +#ifdef PDAF_DEBUG + IF(clmt_printensemble == tstartcycle .OR. clmt_printensemble < 0) THEN + + ! TSMP-PDAF: For debug runs, output the state vector in files + WRITE(fn4, "(a,i5.5,a,i5.5,a)") "h2osoi_ice", mype, ".update.", tstartcycle, ".txt" + OPEN(unit=71, file=fn4, action="write") + WRITE (71,"(es22.15)") h2osoi_ice(:,:) + CLOSE(71) + + END IF +#endif endif end subroutine update_clm diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index 39711a7e3..78ff66690 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -537,7 +537,9 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") OPEN(unit=71, file=fn5, action="write") WRITE (71,"(es22.15)") h2osoi_liq(:,:) CLOSE(71) + END IF + IF(clmupdate_swc.NE.0 .OR. clmupdate_snow.NE.0) THEN ! TSMP-PDAF: For debug runs, output the state vector in files WRITE(fn6, "(a,i5.5,a,i5.5,a)") "h2osoi_ice", mype, ".bef_up.", tstartcycle, ".txt" OPEN(unit=71, file=fn6, action="write") @@ -776,6 +778,17 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") end if +#ifdef PDAF_DEBUG + IF(clmt_printensemble == tstartcycle .OR. clmt_printensemble < 0) THEN + + ! TSMP-PDAF: For debug runs, output the state vector in files + WRITE(fn4, "(a,i5.5,a,i5.5,a)") "h2osoi_ice", mype, ".update.", tstartcycle, ".txt" + OPEN(unit=71, file=fn4, action="write") + WRITE (71,"(es22.15)") h2osoi_ice(:,:) + CLOSE(71) + + END IF +#endif endif end subroutine update_clm From 46dc8d0949e4a78accd0d9c79d6a1d0e2099e7c5 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Tue, 18 Mar 2025 17:05:03 +0100 Subject: [PATCH 54/71] Snow-DA: No layer variable used for setting `obs_index_p` --- bldsva/intf_DA/pdaf/framework/init_dim_obs_f_pdaf.F90 | 8 +++++++- bldsva/intf_DA/pdaf/framework/init_dim_obs_pdaf.F90 | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/bldsva/intf_DA/pdaf/framework/init_dim_obs_f_pdaf.F90 b/bldsva/intf_DA/pdaf/framework/init_dim_obs_f_pdaf.F90 index d3f1484d3..a34125f73 100755 --- a/bldsva/intf_DA/pdaf/framework/init_dim_obs_f_pdaf.F90 +++ b/bldsva/intf_DA/pdaf/framework/init_dim_obs_f_pdaf.F90 @@ -135,6 +135,7 @@ SUBROUTINE init_dim_obs_f_pdaf(step, dim_obs_f) USE enkf_clm_mod, only: domain_def_clm USE enkf_clm_mod, only: get_interp_idx use enkf_clm_mod, only: clmstatevec_allcol + use enkf_clm_mod, only: clmupdate_snow !hcp end #endif #endif @@ -966,7 +967,12 @@ SUBROUTINE init_dim_obs_f_pdaf(step, dim_obs_f) end if #endif else - obs_index_p(cnt) = g-begg+1 + ((endg-begg+1) * (clmobs_layer(i)-1)) + if(clmupdate_snow.ne.0) then + ! Snow-DA: no layer in state vector variables + obs_index_p(cnt) = g-begg+1 + else + obs_index_p(cnt) = g-begg+1 + ((endg-begg+1) * (clmobs_layer(i)-1)) + end if end if !write(*,*) 'obs_index_p(',cnt,') is',obs_index_p(cnt) diff --git a/bldsva/intf_DA/pdaf/framework/init_dim_obs_pdaf.F90 b/bldsva/intf_DA/pdaf/framework/init_dim_obs_pdaf.F90 index e1a6ee4e0..beece1221 100755 --- a/bldsva/intf_DA/pdaf/framework/init_dim_obs_pdaf.F90 +++ b/bldsva/intf_DA/pdaf/framework/init_dim_obs_pdaf.F90 @@ -131,6 +131,7 @@ SUBROUTINE init_dim_obs_pdaf(step, dim_obs_p) USE enkf_clm_mod, only: domain_def_clm USE enkf_clm_mod, only: get_interp_idx use enkf_clm_mod, only: clmstatevec_allcol + use enkf_clm_mod, only: clmupdate_snow !hcp end #endif #endif @@ -959,7 +960,12 @@ SUBROUTINE init_dim_obs_pdaf(step, dim_obs_p) end if #endif else - obs_index_p(cnt) = g-begg+1 + ((endg-begg+1) * (clmobs_layer(i)-1)) + if(clmupdate_snow.ne.0) then + ! Snow-DA: no layer in state vector variables + obs_index_p(cnt) = g-begg+1 + else + obs_index_p(cnt) = g-begg+1 + ((endg-begg+1) * (clmobs_layer(i)-1)) + end if end if !write(*,*) 'obs_index_p(',cnt,') is',obs_index_p(cnt) From 3f5e0c9e1e56cf8670597ab082059b37567e401f Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Tue, 18 Mar 2025 17:14:55 +0100 Subject: [PATCH 55/71] Snow-DA: Default for `CLM:update_snow_repartitioning` to `3` --- bldsva/intf_DA/pdaf/model/common/read_enkfpar.c | 2 +- doc/content/setup_tsmp/input_enkfpf.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/common/read_enkfpar.c b/bldsva/intf_DA/pdaf/model/common/read_enkfpar.c index fcadb26a7..22958091f 100755 --- a/bldsva/intf_DA/pdaf/model/common/read_enkfpar.c +++ b/bldsva/intf_DA/pdaf/model/common/read_enkfpar.c @@ -79,7 +79,7 @@ void read_enkfpar(char *parname) clmupdate_T = iniparser_getint(pardict,"CLM:update_T",0); clmupdate_texture = iniparser_getint(pardict,"CLM:update_texture",0); clmupdate_snow = iniparser_getint(pardict,"CLM:update_snow",0); - clmupdate_snow_repartitioning = iniparser_getint(pardict,"CLM:update_snow_repartitioning",1); + clmupdate_snow_repartitioning = iniparser_getint(pardict,"CLM:update_snow_repartitioning",3); clmprint_swc = iniparser_getint(pardict,"CLM:print_swc",0); clmprint_et = iniparser_getint(pardict,"CLM:print_et",0); clmstatevec_allcol = iniparser_getint(pardict,"CLM:statevec_allcol",0); diff --git a/doc/content/setup_tsmp/input_enkfpf.md b/doc/content/setup_tsmp/input_enkfpf.md index 9d36e8541..9761b8fc4 100644 --- a/doc/content/setup_tsmp/input_enkfpf.md +++ b/doc/content/setup_tsmp/input_enkfpf.md @@ -510,7 +510,7 @@ repartitioning applied during Snow-DA. Only used if Snow-DA is switched on by setting `CLM:update_snow`. Only CLM5.0/eCLM. -- 0 (Default): No repartitioning. Not recommended if Snow-DA is used, +- 0: No repartitioning. Not recommended if Snow-DA is used, when state vector consists of diagnostic variables. - 1: Repartitioning routine in DA-interface: @@ -519,7 +519,7 @@ CLM5.0/eCLM. - 2: Repartitioning routine in DA-interface: `clm_repartition_snow`. Option 2: Adjusting all active layers. -- 3 (Currently recommended): `h2osoi_ice` updated by increment based +- 3 (Default, Currently recommended): `h2osoi_ice` updated by increment based on updated state vector variable. Further repartitioning left to CLM-code. Based on From abf9bda48aa42488d3dfb878a247b037cc878e16 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Tue, 18 Mar 2025 17:21:41 +0100 Subject: [PATCH 56/71] Snow-DA: CLM3.5 variable declarations and error messages --- .../pdaf/model/clm3_5/enkf_clm_mod.F90 | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/bldsva/intf_DA/pdaf/model/clm3_5/enkf_clm_mod.F90 b/bldsva/intf_DA/pdaf/model/clm3_5/enkf_clm_mod.F90 index 5a8114de0..7918ce69d 100755 --- a/bldsva/intf_DA/pdaf/model/clm3_5/enkf_clm_mod.F90 +++ b/bldsva/intf_DA/pdaf/model/clm3_5/enkf_clm_mod.F90 @@ -65,6 +65,8 @@ module enkf_clm_mod integer(c_int),bind(C,name="clmupdate_swc") :: clmupdate_swc integer(c_int),bind(C,name="clmupdate_T") :: clmupdate_T ! by hcp integer(c_int),bind(C,name="clmupdate_texture") :: clmupdate_texture + integer(c_int),bind(C,name="clmupdate_snow") :: clmupdate_snow + integer(c_int),bind(C,name="clmupdate_snow_repartitioning") :: clmupdate_snow_repartitioning integer(c_int),bind(C,name="clmprint_swc") :: clmprint_swc #endif integer(c_int),bind(C,name="clmprint_et") :: clmprint_et @@ -147,6 +149,19 @@ subroutine define_clm_statevec(mype) error stop "Not implemented: clmupdate_texture.eq.2" endif + ! Snow assimilation + ! Case 1: Assimilation of snow depth : allocated 1 per column in CLM5 + ! But observations and history file 1 per grid cell and therefore statevecsize 1 per grid cell + ! Case 2: Assimilation of snow water equivalent same as above + if(clmupdate_snow.eq.1 .or. clmupdate_snow.eq.2) then + error stop "Not implemented: clmupdate_snow.eq.1 or clmupdate_snow.eq.1" + endif + ! Case 3: Assimilation of snow depth: Snow depth and snow water + ! equivalent in the state vector + if(clmupdate_snow.eq.3) then + error stop "Not implemented: clmupdate_snow.eq.3" + endif + !hcp LST DA if(clmupdate_T.eq.1) then clm_varsize = endg-begg+1 @@ -287,6 +302,14 @@ subroutine set_clm_statevec(tstartcycle, mype) end do endif + ! Snow assimilation state vector + ! Case 1: Snow depth + ! Case 2: SWE + ! Case 3: Snow depth + SWE + if(clmupdate_snow.ne.0) then + error stop "Not implemented: clmupdate_snow.ne.0" + endif + #ifdef PDAF_DEBUG IF(clmt_printensemble == tstartcycle + 1 .OR. clmt_printensemble < 0) THEN ! TSMP-PDAF: For debug runs, output the state vector in files @@ -534,6 +557,15 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") call clm_texture_to_parameters endif + ! Snow assimilation: + ! Case 1: Snow depth + ! Case 2: Snow water equivalent + ! Case 3: Snow depth (assimilated) and SWE (used for increment) in state vector + ! Write updated snow variable back to CLM and then repartition snow and adjust related variables + if(clmupdate_snow.ne.0) then + error stop "Not implemented: clmupdate_snow.ne.0" + endif + end subroutine update_clm subroutine clm_correct_texture() From 865b20fc08cb3c819c740684c8456bd2e48d6bad Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 21 Mar 2025 09:29:35 +0100 Subject: [PATCH 57/71] Snow-DA: bugfix repartitioning condition --- bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index 78ff66690..2f2d2470d 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -752,7 +752,7 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") end do ! Repartitioning - if ( clmupdate_snow_repartitioning.eq.1 .and. clmupdate_snow_repartitioning.eq.2) then + if ( clmupdate_snow_repartitioning.eq.1 .or. clmupdate_snow_repartitioning.eq.2) then if ( SUM(ABS(rsnow(:) - nsnow(:))).gt.0.000001) then call clm_repartition_snow(rsnow(:)) From 5c91e442831e4cdaa4a889a6f1e9295f9378ef22 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 21 Mar 2025 09:29:54 +0100 Subject: [PATCH 58/71] Snow-DA: document experimental nature of repartitioning routine --- doc/content/setup_tsmp/input_enkfpf.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/content/setup_tsmp/input_enkfpf.md b/doc/content/setup_tsmp/input_enkfpf.md index 9761b8fc4..5269cc3f4 100644 --- a/doc/content/setup_tsmp/input_enkfpf.md +++ b/doc/content/setup_tsmp/input_enkfpf.md @@ -513,10 +513,10 @@ CLM5.0/eCLM. - 0: No repartitioning. Not recommended if Snow-DA is used, when state vector consists of diagnostic variables. -- 1: Repartitioning routine in DA-interface: +- 1: (experimental) Repartitioning routine in DA-interface: `clm_repartition_snow`. Option 1: Adjusting the bottom layer only. -- 2: Repartitioning routine in DA-interface: +- 2: (experimental) Repartitioning routine in DA-interface: `clm_repartition_snow`. Option 2: Adjusting all active layers. - 3 (Default, Currently recommended): `h2osoi_ice` updated by increment based @@ -524,6 +524,10 @@ CLM5.0/eCLM. CLM-code. Based on +Note: Using the repartitioning scheme has lead to balance errors and +NaN-values in previous CLM-simulations. The function is still included +in the code, because it may be revisited and adapted in the future. + ### CLM:print_swc ### `CLM:print_swc`: (integer) If set to `1`, the updated soil moisture From 797eb5ea63b00f1490bf0bbafea6d9470660c78d Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 21 Mar 2025 09:33:43 +0100 Subject: [PATCH 59/71] Snow-DA: bugfix repartitioning condition II --- bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 78ff66690..2f2d2470d 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -752,7 +752,7 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") end do ! Repartitioning - if ( clmupdate_snow_repartitioning.eq.1 .and. clmupdate_snow_repartitioning.eq.2) then + if ( clmupdate_snow_repartitioning.eq.1 .or. clmupdate_snow_repartitioning.eq.2) then if ( SUM(ABS(rsnow(:) - nsnow(:))).gt.0.000001) then call clm_repartition_snow(rsnow(:)) From 0ea6e7cb4163744db9b32d0190d15ff19a592f0c Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 21 Mar 2025 09:35:40 +0100 Subject: [PATCH 60/71] Snow-DA: numerically better check for `rsnow.ne.0.0` --- bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 2 +- bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 2f2d2470d..ca765b79f 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -765,7 +765,7 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") do j=clm_begc,clm_endc if (nsnow(j).gt.0.0) then if ( ABS(rsnow(j) - nsnow(j)).gt.0.000001) then - if ( rsnow(j).ne.0.0) then + if ( ABS(rsnow(j)).gt.0.0) then ! Update h2osoi_ice with increment incr_h2osno = nsnow(j) / rsnow(j) ! INC = New snow var / OLD snow var do i=snlsno(j)+1,0 diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index 2f2d2470d..ca765b79f 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -765,7 +765,7 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") do j=clm_begc,clm_endc if (nsnow(j).gt.0.0) then if ( ABS(rsnow(j) - nsnow(j)).gt.0.000001) then - if ( rsnow(j).ne.0.0) then + if ( ABS(rsnow(j)).gt.0.0) then ! Update h2osoi_ice with increment incr_h2osno = nsnow(j) / rsnow(j) ! INC = New snow var / OLD snow var do i=snlsno(j)+1,0 From 963b562b9b961195cf2eaad7c11ecd0e98542d46 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 21 Mar 2025 10:05:31 +0100 Subject: [PATCH 61/71] Snow-DA: repartitioning function only for `clmupdate_snow.eq.1,2` --- bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 10 ++++++++-- bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 | 12 +++++++++--- doc/content/setup_tsmp/input_enkfpf.md | 10 ++++++---- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index ca765b79f..7cb5ccc9d 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -754,8 +754,14 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! Repartitioning if ( clmupdate_snow_repartitioning.eq.1 .or. clmupdate_snow_repartitioning.eq.2) then - if ( SUM(ABS(rsnow(:) - nsnow(:))).gt.0.000001) then - call clm_repartition_snow(rsnow(:)) + if (clmupdate_snow.eq.1 .or. clmupdate_snow.eq.2) then + if ( SUM(ABS(rsnow(:) - nsnow(:))).gt.0.000001) then + call clm_repartition_snow(rsnow(:)) + end if + end if + + if (clmupdate_snow.eq.3 .or. clmupdate_snow.eq.4) then + error stop "Not implemented: Repartioning 1/2 for clmupdate_snow.eq.3/4" end if end if diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index ca765b79f..67d37c34d 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -754,8 +754,14 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! Repartitioning if ( clmupdate_snow_repartitioning.eq.1 .or. clmupdate_snow_repartitioning.eq.2) then - if ( SUM(ABS(rsnow(:) - nsnow(:))).gt.0.000001) then - call clm_repartition_snow(rsnow(:)) + if (clmupdate_snow.eq.1 .or. clmupdate_snow.eq.2) then + if ( SUM(ABS(rsnow(:) - nsnow(:))).gt.0.000001) then + call clm_repartition_snow(rsnow(:)) + end if + end if + + if (clmupdate_snow.eq.3 .or. clmupdate_snow.eq.4) then + error stop "Not implemented: Repartioning 1/2 for clmupdate_snow.eq.3/4" end if end if @@ -873,7 +879,7 @@ subroutine clm_repartition_snow(h2osno_in) h2osno_po(jj) = snow_depth(jj) * denice end if h2osno_pr(jj) = h2osno(jj) - else if (clmupdate_snow .eq. 2 .or. clmupdate_snow .eq. 3) then + else if (clmupdate_snow .eq. 2) then ! for clmupdate_snow == 2 we have post H2OSNO as the main H2OSNO already h2osno_po(jj) = h2osno(jj) h2osno_pr(jj) = h2osno_in(jj) diff --git a/doc/content/setup_tsmp/input_enkfpf.md b/doc/content/setup_tsmp/input_enkfpf.md index 5269cc3f4..622caa781 100644 --- a/doc/content/setup_tsmp/input_enkfpf.md +++ b/doc/content/setup_tsmp/input_enkfpf.md @@ -513,11 +513,13 @@ CLM5.0/eCLM. - 0: No repartitioning. Not recommended if Snow-DA is used, when state vector consists of diagnostic variables. -- 1: (experimental) Repartitioning routine in DA-interface: - `clm_repartition_snow`. Option 1: Adjusting the bottom layer only. +- 1: (experimental, only for `CLM:update_snow=1,2`) Repartitioning + routine in DA-interface: `clm_repartition_snow`. Option 1: + Adjusting the bottom layer only. -- 2: (experimental) Repartitioning routine in DA-interface: - `clm_repartition_snow`. Option 2: Adjusting all active layers. +- 2: (experimental, only for `CLM:update_snow=1,2`) Repartitioning + routine in DA-interface: `clm_repartition_snow`. Option 2: + Adjusting all active layers. - 3 (Default, Currently recommended): `h2osoi_ice` updated by increment based on updated state vector variable. Further repartitioning left to From a029d6d5278a26eed5b2685948f5439eadf9a73b Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 21 Mar 2025 10:18:42 +0100 Subject: [PATCH 62/71] Snow-DA: rename `incr_h2osno` to `incr_sno` --- bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 6 +++--- bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 7cb5ccc9d..a6eff6c69 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -484,7 +484,7 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") real(r8), pointer :: h2osoi_liq(:,:) ! liquid water (kg/m2) real(r8), pointer :: h2osoi_ice(:,:) - real(r8) :: rliq,rice,incr_h2osno + real(r8) :: rliq,rice,incr_sno real(r8) :: rsnow(clm_begc:clm_endc) real(r8) :: nsnow(clm_begc:clm_endc) real(r8) :: watmin_check ! minimum soil moisture for checking clm_statevec (mm) @@ -773,9 +773,9 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if ( ABS(rsnow(j) - nsnow(j)).gt.0.000001) then if ( ABS(rsnow(j)).gt.0.0) then ! Update h2osoi_ice with increment - incr_h2osno = nsnow(j) / rsnow(j) ! INC = New snow var / OLD snow var + incr_sno = nsnow(j) / rsnow(j) ! INC = New snow var / OLD snow var do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_sno end do end if end if diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index 67d37c34d..f8c3c94e6 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -484,7 +484,7 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") real(r8), pointer :: h2osoi_liq(:,:) ! liquid water (kg/m2) real(r8), pointer :: h2osoi_ice(:,:) - real(r8) :: rliq,rice,incr_h2osno + real(r8) :: rliq,rice,incr_sno real(r8) :: rsnow(clm_begc:clm_endc) real(r8) :: nsnow(clm_begc:clm_endc) real(r8) :: watmin_check ! minimum soil moisture for checking clm_statevec (mm) @@ -773,9 +773,9 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if ( ABS(rsnow(j) - nsnow(j)).gt.0.000001) then if ( ABS(rsnow(j)).gt.0.0) then ! Update h2osoi_ice with increment - incr_h2osno = nsnow(j) / rsnow(j) ! INC = New snow var / OLD snow var + incr_sno = nsnow(j) / rsnow(j) ! INC = New snow var / OLD snow var do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_h2osno + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_sno end do end if end if From 84273e10e5831bd0c48fceb9b7a2ff1fc68bf9b1 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Thu, 27 Mar 2025 16:01:58 +0100 Subject: [PATCH 63/71] Snow-DA: Refactor Snow-DA preparing for introduction of `CLM:update_snow=4` --- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 101 ++++++++++++------ .../pdaf/model/eclm/enkf_clm_mod_5.F90 | 99 +++++++++++------ 2 files changed, 135 insertions(+), 65 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index a6eff6c69..3592696ab 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -282,7 +282,7 @@ subroutine define_clm_statevec(mype) clm_statevecsize = (clm_endg-clm_begg+1) ! So like this if snow is set it takes priority endif ! Case 3: Assimilation of snow depth: Snow depth and snow water - ! equivalent in the state vector + ! equivalent in the state vector. Update of h2osoi_ice if(clmupdate_snow.eq.3) then clm_varsize = (clm_endg-clm_begg+1) clm_statevecsize = 2*(clm_endg-clm_begg+1) @@ -484,9 +484,14 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") real(r8), pointer :: h2osoi_liq(:,:) ! liquid water (kg/m2) real(r8), pointer :: h2osoi_ice(:,:) - real(r8) :: rliq,rice,incr_sno - real(r8) :: rsnow(clm_begc:clm_endc) - real(r8) :: nsnow(clm_begc:clm_endc) + real(r8) :: rliq,rice + real(r8) :: incr_sno + real(r8) :: incr_sd + real(r8) :: incr_swe + real(r8) :: h2osno_in(clm_begc:clm_endc) + real(r8) :: snow_depth_in(clm_begc:clm_endc) + real(r8) :: h2osno_out(clm_begc:clm_endc) + real(r8) :: snow_depth_out(clm_begc:clm_endc) real(r8) :: watmin_check ! minimum soil moisture for checking clm_statevec (mm) real(r8) :: watmin_set ! minimum soil moisture for setting swc (mm) real(r8) :: swc_update ! updated SWC in loop @@ -722,31 +727,37 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") cc = (col%gridcell(j) - clm_begg + 1) if (clmupdate_snow.eq.1) then - rsnow(j) = snow_depth(j) + snow_depth_in(j) = snow_depth(j) else if (clmupdate_snow.eq.2) then - rsnow(j) = h2osno(j) + h2osno_in(j) = h2osno(j) else if (clmupdate_snow.eq.3) then - rsnow(j) = h2osno(j) + h2osno_in(j) = h2osno(j) end if - if (clmupdate_snow.eq.1 .or. clmupdate_snow.eq.2) then - nsnow(j) = clm_statevec(cc+offset) + if (clmupdate_snow.eq.1) then + snow_depth_out(j) = clm_statevec(cc+offset) + else if (clmupdate_snow.eq.2) then + h2osno_out(j) = clm_statevec(cc+offset) else if (clmupdate_snow.eq.3) then - nsnow(j) = clm_statevec(cc+clm_varsize+offset) + h2osno_out(j) = clm_statevec(cc+clm_varsize+offset) end if - if (nsnow(j).gt.0.0) then + if(clmupdate_snow.eq.1) then ! Update state variable to CLM ! Not needed for repartioning-case 3? - if(clmupdate_snow.eq.1) then - snow_depth(j) = nsnow(j) + if (snow_depth_out(j).gt.0.0) then + snow_depth(j) = snow_depth_out(j) + else + ! Catch negative or 0 values from DA + print *, "WARNING: Snow-depth is negative/zero at cc. cc, j, offset, snow_depth_out(j): ", cc, j, offset, snow_depth_out(j) end if - if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3) then - h2osno(j) = nsnow(j) + else if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3) then + if (h2osno_out(j).gt.0.0) then + h2osno(j) = h2osno_out(j) + else + ! Catch negative or 0 values from DA + print *, "WARNING: SWE is negative/zero at cc. cc, j, offset, h2osno(j): ", cc, j, offset, h2osno(j) end if - else - ! Catch negative or 0 values from DA - print *, "WARNING: Snow-statevec is negative/zero at cc. cc, j, offset, clm_statevec(cc+offset): ", cc, j, offset, clm_statevec(cc+offset) end if end do @@ -754,9 +765,15 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! Repartitioning if ( clmupdate_snow_repartitioning.eq.1 .or. clmupdate_snow_repartitioning.eq.2) then - if (clmupdate_snow.eq.1 .or. clmupdate_snow.eq.2) then - if ( SUM(ABS(rsnow(:) - nsnow(:))).gt.0.000001) then - call clm_repartition_snow(rsnow(:)) + if (clmupdate_snow.eq.1) then + if ( SUM(ABS(snow_depth_in(:) - snow_depth_out(:))).gt.0.000001) then + call clm_repartition_snow(snow_depth_in(:)) + end if + end if + + if (clmupdate_snow.eq.2) then + if ( SUM(ABS(h2osno_in(:) - h2osno_out(:))).gt.0.000001) then + call clm_repartition_snow(h2osno_in(:)) end if end if @@ -768,19 +785,37 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if ( clmupdate_snow_repartitioning.eq.3) then - do j=clm_begc,clm_endc - if (nsnow(j).gt.0.0) then - if ( ABS(rsnow(j) - nsnow(j)).gt.0.000001) then - if ( ABS(rsnow(j)).gt.0.0) then - ! Update h2osoi_ice with increment - incr_sno = nsnow(j) / rsnow(j) ! INC = New snow var / OLD snow var - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_sno - end do + if (clmupdate_snow.eq.1) then + do j=clm_begc,clm_endc + if (snow_depth_out(j).gt.0.0) then + if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then + if (snow_depth_in(j).gt.0.0) then + ! Update h2osoi_ice with increment + incr_sno = snow_depth_out(j) / snow_depth_in(j) + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_sno + end do + end if end if end if - end if - end do + end do + end if + + if (clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3) then + do j=clm_begc,clm_endc + if (h2osno_out(j).gt.0.0) then + if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then + if (h2osno_in(j).gt.0.0) then + ! Update h2osoi_ice with increment + incr_sno = h2osno_out(j) / h2osno_in(j) + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_sno + end do + end if + end if + end if + end do + end if end if @@ -879,7 +914,7 @@ subroutine clm_repartition_snow(h2osno_in) h2osno_po(jj) = snow_depth(jj) * denice end if h2osno_pr(jj) = h2osno(jj) - else if (clmupdate_snow .eq. 2 .or. clmupdate_snow .eq. 3) then + else if (clmupdate_snow .eq. 2) then ! for clmupdate_snow == 2 we have post H2OSNO as the main H2OSNO already h2osno_po(jj) = h2osno(jj) h2osno_pr(jj) = h2osno_in(jj) diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index f8c3c94e6..3592696ab 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -282,7 +282,7 @@ subroutine define_clm_statevec(mype) clm_statevecsize = (clm_endg-clm_begg+1) ! So like this if snow is set it takes priority endif ! Case 3: Assimilation of snow depth: Snow depth and snow water - ! equivalent in the state vector + ! equivalent in the state vector. Update of h2osoi_ice if(clmupdate_snow.eq.3) then clm_varsize = (clm_endg-clm_begg+1) clm_statevecsize = 2*(clm_endg-clm_begg+1) @@ -484,9 +484,14 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") real(r8), pointer :: h2osoi_liq(:,:) ! liquid water (kg/m2) real(r8), pointer :: h2osoi_ice(:,:) - real(r8) :: rliq,rice,incr_sno - real(r8) :: rsnow(clm_begc:clm_endc) - real(r8) :: nsnow(clm_begc:clm_endc) + real(r8) :: rliq,rice + real(r8) :: incr_sno + real(r8) :: incr_sd + real(r8) :: incr_swe + real(r8) :: h2osno_in(clm_begc:clm_endc) + real(r8) :: snow_depth_in(clm_begc:clm_endc) + real(r8) :: h2osno_out(clm_begc:clm_endc) + real(r8) :: snow_depth_out(clm_begc:clm_endc) real(r8) :: watmin_check ! minimum soil moisture for checking clm_statevec (mm) real(r8) :: watmin_set ! minimum soil moisture for setting swc (mm) real(r8) :: swc_update ! updated SWC in loop @@ -722,31 +727,37 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") cc = (col%gridcell(j) - clm_begg + 1) if (clmupdate_snow.eq.1) then - rsnow(j) = snow_depth(j) + snow_depth_in(j) = snow_depth(j) else if (clmupdate_snow.eq.2) then - rsnow(j) = h2osno(j) + h2osno_in(j) = h2osno(j) else if (clmupdate_snow.eq.3) then - rsnow(j) = h2osno(j) + h2osno_in(j) = h2osno(j) end if - if (clmupdate_snow.eq.1 .or. clmupdate_snow.eq.2) then - nsnow(j) = clm_statevec(cc+offset) + if (clmupdate_snow.eq.1) then + snow_depth_out(j) = clm_statevec(cc+offset) + else if (clmupdate_snow.eq.2) then + h2osno_out(j) = clm_statevec(cc+offset) else if (clmupdate_snow.eq.3) then - nsnow(j) = clm_statevec(cc+clm_varsize+offset) + h2osno_out(j) = clm_statevec(cc+clm_varsize+offset) end if - if (nsnow(j).gt.0.0) then + if(clmupdate_snow.eq.1) then ! Update state variable to CLM ! Not needed for repartioning-case 3? - if(clmupdate_snow.eq.1) then - snow_depth(j) = nsnow(j) + if (snow_depth_out(j).gt.0.0) then + snow_depth(j) = snow_depth_out(j) + else + ! Catch negative or 0 values from DA + print *, "WARNING: Snow-depth is negative/zero at cc. cc, j, offset, snow_depth_out(j): ", cc, j, offset, snow_depth_out(j) end if - if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3) then - h2osno(j) = nsnow(j) + else if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3) then + if (h2osno_out(j).gt.0.0) then + h2osno(j) = h2osno_out(j) + else + ! Catch negative or 0 values from DA + print *, "WARNING: SWE is negative/zero at cc. cc, j, offset, h2osno(j): ", cc, j, offset, h2osno(j) end if - else - ! Catch negative or 0 values from DA - print *, "WARNING: Snow-statevec is negative/zero at cc. cc, j, offset, clm_statevec(cc+offset): ", cc, j, offset, clm_statevec(cc+offset) end if end do @@ -754,9 +765,15 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! Repartitioning if ( clmupdate_snow_repartitioning.eq.1 .or. clmupdate_snow_repartitioning.eq.2) then - if (clmupdate_snow.eq.1 .or. clmupdate_snow.eq.2) then - if ( SUM(ABS(rsnow(:) - nsnow(:))).gt.0.000001) then - call clm_repartition_snow(rsnow(:)) + if (clmupdate_snow.eq.1) then + if ( SUM(ABS(snow_depth_in(:) - snow_depth_out(:))).gt.0.000001) then + call clm_repartition_snow(snow_depth_in(:)) + end if + end if + + if (clmupdate_snow.eq.2) then + if ( SUM(ABS(h2osno_in(:) - h2osno_out(:))).gt.0.000001) then + call clm_repartition_snow(h2osno_in(:)) end if end if @@ -768,19 +785,37 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if ( clmupdate_snow_repartitioning.eq.3) then - do j=clm_begc,clm_endc - if (nsnow(j).gt.0.0) then - if ( ABS(rsnow(j) - nsnow(j)).gt.0.000001) then - if ( ABS(rsnow(j)).gt.0.0) then - ! Update h2osoi_ice with increment - incr_sno = nsnow(j) / rsnow(j) ! INC = New snow var / OLD snow var - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_sno - end do + if (clmupdate_snow.eq.1) then + do j=clm_begc,clm_endc + if (snow_depth_out(j).gt.0.0) then + if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then + if (snow_depth_in(j).gt.0.0) then + ! Update h2osoi_ice with increment + incr_sno = snow_depth_out(j) / snow_depth_in(j) + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_sno + end do + end if end if end if - end if - end do + end do + end if + + if (clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3) then + do j=clm_begc,clm_endc + if (h2osno_out(j).gt.0.0) then + if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then + if (h2osno_in(j).gt.0.0) then + ! Update h2osoi_ice with increment + incr_sno = h2osno_out(j) / h2osno_in(j) + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_sno + end do + end if + end if + end if + end do + end if end if From c119c812cbba4cdcf0fbc27167268802de0ec350 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Thu, 27 Mar 2025 16:17:42 +0100 Subject: [PATCH 64/71] Snow-DA: Introduce `CLM:update_snow.eq.4` --- .../pdaf/model/clm3_5/enkf_clm_mod.F90 | 8 ++- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 57 ++++++++++++++++++- .../pdaf/model/eclm/enkf_clm_mod_5.F90 | 57 ++++++++++++++++++- doc/content/setup_tsmp/input_enkfpf.md | 16 ++++-- 4 files changed, 129 insertions(+), 9 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm3_5/enkf_clm_mod.F90 b/bldsva/intf_DA/pdaf/model/clm3_5/enkf_clm_mod.F90 index 7918ce69d..c056f183f 100755 --- a/bldsva/intf_DA/pdaf/model/clm3_5/enkf_clm_mod.F90 +++ b/bldsva/intf_DA/pdaf/model/clm3_5/enkf_clm_mod.F90 @@ -157,10 +157,16 @@ subroutine define_clm_statevec(mype) error stop "Not implemented: clmupdate_snow.eq.1 or clmupdate_snow.eq.1" endif ! Case 3: Assimilation of snow depth: Snow depth and snow water - ! equivalent in the state vector + ! equivalent in the state vector. Update of h2osoi_ice if(clmupdate_snow.eq.3) then error stop "Not implemented: clmupdate_snow.eq.3" endif + ! Case 4: Assimilation of snow depth: Snow depth and snow water + ! equivalent in the state vector. Update of h2osoi_ice, h2osoi_liq + ! and dz + if(clmupdate_snow.eq.4) then + error stop "Not implemented: clmupdate_snow.eq.4" + endif !hcp LST DA if(clmupdate_T.eq.1) then diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 3592696ab..da15ed1b1 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -287,6 +287,13 @@ subroutine define_clm_statevec(mype) clm_varsize = (clm_endg-clm_begg+1) clm_statevecsize = 2*(clm_endg-clm_begg+1) endif + ! Case 4: Assimilation of snow depth: Snow depth and snow water + ! equivalent in the state vector. Update of h2osoi_ice, h2osoi_liq + ! and dz + if(clmupdate_snow.eq.4) then + clm_varsize = (clm_endg-clm_begg+1) + clm_statevecsize = 2*(clm_endg-clm_begg+1) + endif !hcp LST DA if(clmupdate_T.eq.1) then @@ -430,6 +437,9 @@ subroutine set_clm_statevec(tstartcycle, mype) else if(clmupdate_snow.eq.3) then clm_statevec(cc+offset) = snow_depth(jj) clm_statevec(cc+clm_varsize+offset) = h2osno(jj) + else if(clmupdate_snow.eq.4) then + clm_statevec(cc+offset) = snow_depth(jj) + clm_statevec(cc+clm_varsize+offset) = h2osno(jj) else error stop "Wrong input for clmupdate_snow" end if @@ -732,6 +742,9 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") h2osno_in(j) = h2osno(j) else if (clmupdate_snow.eq.3) then h2osno_in(j) = h2osno(j) + else if (clmupdate_snow.eq.4) then + snow_depth_in(j) = snow_depth(j) + h2osno_in(j) = h2osno(j) end if if (clmupdate_snow.eq.1) then @@ -740,9 +753,12 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") h2osno_out(j) = clm_statevec(cc+offset) else if (clmupdate_snow.eq.3) then h2osno_out(j) = clm_statevec(cc+clm_varsize+offset) + else if (clmupdate_snow.eq.4) then + snow_depth_out(j) = clm_statevec(cc+offset) + h2osno_out(j) = clm_statevec(cc+clm_varsize+offset) end if - if(clmupdate_snow.eq.1) then + if(clmupdate_snow.eq.1 .or. clmupdate_snow.eq.4) then ! Update state variable to CLM ! Not needed for repartioning-case 3? if (snow_depth_out(j).gt.0.0) then @@ -751,7 +767,7 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! Catch negative or 0 values from DA print *, "WARNING: Snow-depth is negative/zero at cc. cc, j, offset, snow_depth_out(j): ", cc, j, offset, snow_depth_out(j) end if - else if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3) then + else if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3 .or. clmupdate_snow.eq.4) then if (h2osno_out(j).gt.0.0) then h2osno(j) = h2osno_out(j) else @@ -817,6 +833,43 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") end do end if + if (clmupdate_snow.eq.4) then + do j=clm_begc,clm_endc + if (h2osno_out(j).gt.0.0) then + if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then + if (h2osno_in(j).gt.0.0) then + ! Update h2osoi_ice/h2osoi_liq with increment + incr_swe = h2osno_out(j) / h2osno_in(j) + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_swe + h2osoi_liq(j,i) = h2osoi_liq(j,i) * incr_swe + if (isnan(h2osoi_ice(j,i))) then + print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i + endif + if (isnan(h2osoi_liq(j,i))) then + print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i + endif + end do + end if + end if + end if + if (snow_depth_out(j).gt.0.0) then + if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then + if (snow_depth_in(j).gt.0.0) then + ! Update snow_depth with increment + incr_sd = snow_depth_out(j) / snow_depth_in(j) + do i=snlsno(j)+1,0 + dz(j,i) = dz(j,i) * incr_sd + if (isnan(dz(j,i))) then + print *, "WARNING: dz at j,i is nan: ", j, i + endif + end do + end if + end if + end if + end do + end if + end if #ifdef PDAF_DEBUG diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index 3592696ab..da15ed1b1 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -287,6 +287,13 @@ subroutine define_clm_statevec(mype) clm_varsize = (clm_endg-clm_begg+1) clm_statevecsize = 2*(clm_endg-clm_begg+1) endif + ! Case 4: Assimilation of snow depth: Snow depth and snow water + ! equivalent in the state vector. Update of h2osoi_ice, h2osoi_liq + ! and dz + if(clmupdate_snow.eq.4) then + clm_varsize = (clm_endg-clm_begg+1) + clm_statevecsize = 2*(clm_endg-clm_begg+1) + endif !hcp LST DA if(clmupdate_T.eq.1) then @@ -430,6 +437,9 @@ subroutine set_clm_statevec(tstartcycle, mype) else if(clmupdate_snow.eq.3) then clm_statevec(cc+offset) = snow_depth(jj) clm_statevec(cc+clm_varsize+offset) = h2osno(jj) + else if(clmupdate_snow.eq.4) then + clm_statevec(cc+offset) = snow_depth(jj) + clm_statevec(cc+clm_varsize+offset) = h2osno(jj) else error stop "Wrong input for clmupdate_snow" end if @@ -732,6 +742,9 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") h2osno_in(j) = h2osno(j) else if (clmupdate_snow.eq.3) then h2osno_in(j) = h2osno(j) + else if (clmupdate_snow.eq.4) then + snow_depth_in(j) = snow_depth(j) + h2osno_in(j) = h2osno(j) end if if (clmupdate_snow.eq.1) then @@ -740,9 +753,12 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") h2osno_out(j) = clm_statevec(cc+offset) else if (clmupdate_snow.eq.3) then h2osno_out(j) = clm_statevec(cc+clm_varsize+offset) + else if (clmupdate_snow.eq.4) then + snow_depth_out(j) = clm_statevec(cc+offset) + h2osno_out(j) = clm_statevec(cc+clm_varsize+offset) end if - if(clmupdate_snow.eq.1) then + if(clmupdate_snow.eq.1 .or. clmupdate_snow.eq.4) then ! Update state variable to CLM ! Not needed for repartioning-case 3? if (snow_depth_out(j).gt.0.0) then @@ -751,7 +767,7 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! Catch negative or 0 values from DA print *, "WARNING: Snow-depth is negative/zero at cc. cc, j, offset, snow_depth_out(j): ", cc, j, offset, snow_depth_out(j) end if - else if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3) then + else if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3 .or. clmupdate_snow.eq.4) then if (h2osno_out(j).gt.0.0) then h2osno(j) = h2osno_out(j) else @@ -817,6 +833,43 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") end do end if + if (clmupdate_snow.eq.4) then + do j=clm_begc,clm_endc + if (h2osno_out(j).gt.0.0) then + if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then + if (h2osno_in(j).gt.0.0) then + ! Update h2osoi_ice/h2osoi_liq with increment + incr_swe = h2osno_out(j) / h2osno_in(j) + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_swe + h2osoi_liq(j,i) = h2osoi_liq(j,i) * incr_swe + if (isnan(h2osoi_ice(j,i))) then + print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i + endif + if (isnan(h2osoi_liq(j,i))) then + print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i + endif + end do + end if + end if + end if + if (snow_depth_out(j).gt.0.0) then + if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then + if (snow_depth_in(j).gt.0.0) then + ! Update snow_depth with increment + incr_sd = snow_depth_out(j) / snow_depth_in(j) + do i=snlsno(j)+1,0 + dz(j,i) = dz(j,i) * incr_sd + if (isnan(dz(j,i))) then + print *, "WARNING: dz at j,i is nan: ", j, i + endif + end do + end if + end if + end if + end do + end if + end if #ifdef PDAF_DEBUG diff --git a/doc/content/setup_tsmp/input_enkfpf.md b/doc/content/setup_tsmp/input_enkfpf.md index 622caa781..2d4445f91 100644 --- a/doc/content/setup_tsmp/input_enkfpf.md +++ b/doc/content/setup_tsmp/input_enkfpf.md @@ -494,8 +494,15 @@ Only CLM5.0/eCLM. equivalent. - 3: Assimilation of snow depth. State vector: Snow depth and snow - water equivalent. Snow water equivalent from state vector is used - in the update as in Case 2. + water equivalent. Requires + `CLM:update_snow_repartitioning=3`. `h2osoi_ice` updated based on + `h2osno`-increment. + +- 4: Assimilation of snow depth. State vector: Snow depth and snow + water equivalent. Requires + `CLM:update_snow_repartitioning=3`. `h2osoi_ice` and `h2osoi_liq` + updated based on `h2osno`-increment. `dz` updated based on + `snow_depth`-increment. See CLM Technical Note for more information on snow variable: @@ -521,8 +528,9 @@ CLM5.0/eCLM. routine in DA-interface: `clm_repartition_snow`. Option 2: Adjusting all active layers. -- 3 (Default, Currently recommended): `h2osoi_ice` updated by increment based - on updated state vector variable. Further repartitioning left to +- 3 (Default, Currently recommended): `h2osoi_ice` (and possibly + `h2osoi_liq` and `dz`) updated by increment based on updated + `h2osno` or `snow_depth`. Further repartitioning left to CLM-code. Based on From 42356c26660a646327ab3a31e9aa162df67ef9af Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 28 Mar 2025 12:48:35 +0100 Subject: [PATCH 65/71] Snow-DA: `CLM:update_snow=5,6,7` intermediate updates between `3` and `4`. --- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 162 +++++++++++++++++- .../pdaf/model/eclm/enkf_clm_mod_5.F90 | 162 +++++++++++++++++- doc/content/setup_tsmp/input_enkfpf.md | 15 ++ 3 files changed, 335 insertions(+), 4 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index da15ed1b1..0c034d3c0 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -294,6 +294,26 @@ subroutine define_clm_statevec(mype) clm_varsize = (clm_endg-clm_begg+1) clm_statevecsize = 2*(clm_endg-clm_begg+1) endif + ! Case 5: Assimilation of snow depth: Snow depth and snow water + ! equivalent in the state vector. Update of h2osoi_ice + ! and dz + if(clmupdate_snow.eq.5) then + clm_varsize = (clm_endg-clm_begg+1) + clm_statevecsize = 2*(clm_endg-clm_begg+1) + endif + ! Case 6: Assimilation of snow depth: Snow depth and snow water + ! equivalent in the state vector. Update of h2osoi_ice, h2osoi_liq + if(clmupdate_snow.eq.6) then + clm_varsize = (clm_endg-clm_begg+1) + clm_statevecsize = 2*(clm_endg-clm_begg+1) + endif + ! Case 7: Assimilation of snow depth: Snow depth and snow water + ! equivalent in the state vector. Update of h2osoi_ice + ! Should reproduce case 3 + if(clmupdate_snow.eq.6) then + clm_varsize = (clm_endg-clm_begg+1) + clm_statevecsize = 2*(clm_endg-clm_begg+1) + endif !hcp LST DA if(clmupdate_T.eq.1) then @@ -440,6 +460,15 @@ subroutine set_clm_statevec(tstartcycle, mype) else if(clmupdate_snow.eq.4) then clm_statevec(cc+offset) = snow_depth(jj) clm_statevec(cc+clm_varsize+offset) = h2osno(jj) + else if(clmupdate_snow.eq.5) then + clm_statevec(cc+offset) = snow_depth(jj) + clm_statevec(cc+clm_varsize+offset) = h2osno(jj) + else if(clmupdate_snow.eq.6) then + clm_statevec(cc+offset) = snow_depth(jj) + clm_statevec(cc+clm_varsize+offset) = h2osno(jj) + else if(clmupdate_snow.eq.7) then + clm_statevec(cc+offset) = snow_depth(jj) + clm_statevec(cc+clm_varsize+offset) = h2osno(jj) else error stop "Wrong input for clmupdate_snow" end if @@ -745,6 +774,15 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") else if (clmupdate_snow.eq.4) then snow_depth_in(j) = snow_depth(j) h2osno_in(j) = h2osno(j) + else if (clmupdate_snow.eq.5) then + snow_depth_in(j) = snow_depth(j) + h2osno_in(j) = h2osno(j) + else if (clmupdate_snow.eq.6) then + snow_depth_in(j) = snow_depth(j) + h2osno_in(j) = h2osno(j) + else if (clmupdate_snow.eq.7) then + snow_depth_in(j) = snow_depth(j) + h2osno_in(j) = h2osno(j) end if if (clmupdate_snow.eq.1) then @@ -756,9 +794,18 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") else if (clmupdate_snow.eq.4) then snow_depth_out(j) = clm_statevec(cc+offset) h2osno_out(j) = clm_statevec(cc+clm_varsize+offset) + else if (clmupdate_snow.eq.5) then + snow_depth_out(j) = clm_statevec(cc+offset) + h2osno_out(j) = clm_statevec(cc+clm_varsize+offset) + else if (clmupdate_snow.eq.6) then + snow_depth_out(j) = clm_statevec(cc+offset) + h2osno_out(j) = clm_statevec(cc+clm_varsize+offset) + else if (clmupdate_snow.eq.7) then + snow_depth_out(j) = clm_statevec(cc+offset) + h2osno_out(j) = clm_statevec(cc+clm_varsize+offset) end if - if(clmupdate_snow.eq.1 .or. clmupdate_snow.eq.4) then + if(clmupdate_snow.eq.1 .or. clmupdate_snow.eq.4 .or. clmupdate_snow.eq.5 .or. clmupdate_snow.eq.6 .or. clmupdate_snow.eq.7) then ! Update state variable to CLM ! Not needed for repartioning-case 3? if (snow_depth_out(j).gt.0.0) then @@ -767,7 +814,7 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! Catch negative or 0 values from DA print *, "WARNING: Snow-depth is negative/zero at cc. cc, j, offset, snow_depth_out(j): ", cc, j, offset, snow_depth_out(j) end if - else if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3 .or. clmupdate_snow.eq.4) then + else if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3 .or. clmupdate_snow.eq.4 .or. clmupdate_snow.eq.5 .or. clmupdate_snow.eq.6 .or. clmupdate_snow.eq.7) then if (h2osno_out(j).gt.0.0) then h2osno(j) = h2osno_out(j) else @@ -870,6 +917,117 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") end do end if + if (clmupdate_snow.eq.5) then + do j=clm_begc,clm_endc + if (h2osno_out(j).gt.0.0) then + if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then + if (h2osno_in(j).gt.0.0) then + ! Update h2osoi_ice/h2osoi_liq with increment + incr_swe = h2osno_out(j) / h2osno_in(j) + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_swe + ! h2osoi_liq(j,i) = h2osoi_liq(j,i) * incr_swe + if (isnan(h2osoi_ice(j,i))) then + print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i + endif + ! if (isnan(h2osoi_liq(j,i))) then + ! print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i + ! endif + end do + end if + end if + end if + if (snow_depth_out(j).gt.0.0) then + if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then + if (snow_depth_in(j).gt.0.0) then + ! Update snow_depth with increment + incr_sd = snow_depth_out(j) / snow_depth_in(j) + do i=snlsno(j)+1,0 + dz(j,i) = dz(j,i) * incr_sd + if (isnan(dz(j,i))) then + print *, "WARNING: dz at j,i is nan: ", j, i + endif + end do + end if + end if + end if + end do + end if + + if (clmupdate_snow.eq.6) then + do j=clm_begc,clm_endc + if (h2osno_out(j).gt.0.0) then + if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then + if (h2osno_in(j).gt.0.0) then + ! Update h2osoi_ice/h2osoi_liq with increment + incr_swe = h2osno_out(j) / h2osno_in(j) + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_swe + h2osoi_liq(j,i) = h2osoi_liq(j,i) * incr_swe + if (isnan(h2osoi_ice(j,i))) then + print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i + endif + if (isnan(h2osoi_liq(j,i))) then + print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i + endif + end do + end if + end if + end if + ! if (snow_depth_out(j).gt.0.0) then + ! if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then + ! if (snow_depth_in(j).gt.0.0) then + ! ! Update snow_depth with increment + ! incr_sd = snow_depth_out(j) / snow_depth_in(j) + ! do i=snlsno(j)+1,0 + ! dz(j,i) = dz(j,i) * incr_sd + ! if (isnan(dz(j,i))) then + ! print *, "WARNING: dz at j,i is nan: ", j, i + ! endif + ! end do + ! end if + ! end if + ! end if + end do + end if + + if (clmupdate_snow.eq.7) then + do j=clm_begc,clm_endc + if (h2osno_out(j).gt.0.0) then + if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then + if (h2osno_in(j).gt.0.0) then + ! Update h2osoi_ice/h2osoi_liq with increment + incr_swe = h2osno_out(j) / h2osno_in(j) + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_swe + ! h2osoi_liq(j,i) = h2osoi_liq(j,i) * incr_swe + if (isnan(h2osoi_ice(j,i))) then + print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i + endif + ! if (isnan(h2osoi_liq(j,i))) then + ! print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i + ! endif + end do + end if + end if + end if + ! if (snow_depth_out(j).gt.0.0) then + ! if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then + ! if (snow_depth_in(j).gt.0.0) then + ! ! Update snow_depth with increment + ! incr_sd = snow_depth_out(j) / snow_depth_in(j) + ! do i=snlsno(j)+1,0 + ! dz(j,i) = dz(j,i) * incr_sd + ! if (isnan(dz(j,i))) then + ! print *, "WARNING: dz at j,i is nan: ", j, i + ! endif + ! end do + ! end if + ! end if + ! end if + end do + end if + end if #ifdef PDAF_DEBUG diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index da15ed1b1..0c034d3c0 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -294,6 +294,26 @@ subroutine define_clm_statevec(mype) clm_varsize = (clm_endg-clm_begg+1) clm_statevecsize = 2*(clm_endg-clm_begg+1) endif + ! Case 5: Assimilation of snow depth: Snow depth and snow water + ! equivalent in the state vector. Update of h2osoi_ice + ! and dz + if(clmupdate_snow.eq.5) then + clm_varsize = (clm_endg-clm_begg+1) + clm_statevecsize = 2*(clm_endg-clm_begg+1) + endif + ! Case 6: Assimilation of snow depth: Snow depth and snow water + ! equivalent in the state vector. Update of h2osoi_ice, h2osoi_liq + if(clmupdate_snow.eq.6) then + clm_varsize = (clm_endg-clm_begg+1) + clm_statevecsize = 2*(clm_endg-clm_begg+1) + endif + ! Case 7: Assimilation of snow depth: Snow depth and snow water + ! equivalent in the state vector. Update of h2osoi_ice + ! Should reproduce case 3 + if(clmupdate_snow.eq.6) then + clm_varsize = (clm_endg-clm_begg+1) + clm_statevecsize = 2*(clm_endg-clm_begg+1) + endif !hcp LST DA if(clmupdate_T.eq.1) then @@ -440,6 +460,15 @@ subroutine set_clm_statevec(tstartcycle, mype) else if(clmupdate_snow.eq.4) then clm_statevec(cc+offset) = snow_depth(jj) clm_statevec(cc+clm_varsize+offset) = h2osno(jj) + else if(clmupdate_snow.eq.5) then + clm_statevec(cc+offset) = snow_depth(jj) + clm_statevec(cc+clm_varsize+offset) = h2osno(jj) + else if(clmupdate_snow.eq.6) then + clm_statevec(cc+offset) = snow_depth(jj) + clm_statevec(cc+clm_varsize+offset) = h2osno(jj) + else if(clmupdate_snow.eq.7) then + clm_statevec(cc+offset) = snow_depth(jj) + clm_statevec(cc+clm_varsize+offset) = h2osno(jj) else error stop "Wrong input for clmupdate_snow" end if @@ -745,6 +774,15 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") else if (clmupdate_snow.eq.4) then snow_depth_in(j) = snow_depth(j) h2osno_in(j) = h2osno(j) + else if (clmupdate_snow.eq.5) then + snow_depth_in(j) = snow_depth(j) + h2osno_in(j) = h2osno(j) + else if (clmupdate_snow.eq.6) then + snow_depth_in(j) = snow_depth(j) + h2osno_in(j) = h2osno(j) + else if (clmupdate_snow.eq.7) then + snow_depth_in(j) = snow_depth(j) + h2osno_in(j) = h2osno(j) end if if (clmupdate_snow.eq.1) then @@ -756,9 +794,18 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") else if (clmupdate_snow.eq.4) then snow_depth_out(j) = clm_statevec(cc+offset) h2osno_out(j) = clm_statevec(cc+clm_varsize+offset) + else if (clmupdate_snow.eq.5) then + snow_depth_out(j) = clm_statevec(cc+offset) + h2osno_out(j) = clm_statevec(cc+clm_varsize+offset) + else if (clmupdate_snow.eq.6) then + snow_depth_out(j) = clm_statevec(cc+offset) + h2osno_out(j) = clm_statevec(cc+clm_varsize+offset) + else if (clmupdate_snow.eq.7) then + snow_depth_out(j) = clm_statevec(cc+offset) + h2osno_out(j) = clm_statevec(cc+clm_varsize+offset) end if - if(clmupdate_snow.eq.1 .or. clmupdate_snow.eq.4) then + if(clmupdate_snow.eq.1 .or. clmupdate_snow.eq.4 .or. clmupdate_snow.eq.5 .or. clmupdate_snow.eq.6 .or. clmupdate_snow.eq.7) then ! Update state variable to CLM ! Not needed for repartioning-case 3? if (snow_depth_out(j).gt.0.0) then @@ -767,7 +814,7 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! Catch negative or 0 values from DA print *, "WARNING: Snow-depth is negative/zero at cc. cc, j, offset, snow_depth_out(j): ", cc, j, offset, snow_depth_out(j) end if - else if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3 .or. clmupdate_snow.eq.4) then + else if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3 .or. clmupdate_snow.eq.4 .or. clmupdate_snow.eq.5 .or. clmupdate_snow.eq.6 .or. clmupdate_snow.eq.7) then if (h2osno_out(j).gt.0.0) then h2osno(j) = h2osno_out(j) else @@ -870,6 +917,117 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") end do end if + if (clmupdate_snow.eq.5) then + do j=clm_begc,clm_endc + if (h2osno_out(j).gt.0.0) then + if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then + if (h2osno_in(j).gt.0.0) then + ! Update h2osoi_ice/h2osoi_liq with increment + incr_swe = h2osno_out(j) / h2osno_in(j) + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_swe + ! h2osoi_liq(j,i) = h2osoi_liq(j,i) * incr_swe + if (isnan(h2osoi_ice(j,i))) then + print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i + endif + ! if (isnan(h2osoi_liq(j,i))) then + ! print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i + ! endif + end do + end if + end if + end if + if (snow_depth_out(j).gt.0.0) then + if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then + if (snow_depth_in(j).gt.0.0) then + ! Update snow_depth with increment + incr_sd = snow_depth_out(j) / snow_depth_in(j) + do i=snlsno(j)+1,0 + dz(j,i) = dz(j,i) * incr_sd + if (isnan(dz(j,i))) then + print *, "WARNING: dz at j,i is nan: ", j, i + endif + end do + end if + end if + end if + end do + end if + + if (clmupdate_snow.eq.6) then + do j=clm_begc,clm_endc + if (h2osno_out(j).gt.0.0) then + if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then + if (h2osno_in(j).gt.0.0) then + ! Update h2osoi_ice/h2osoi_liq with increment + incr_swe = h2osno_out(j) / h2osno_in(j) + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_swe + h2osoi_liq(j,i) = h2osoi_liq(j,i) * incr_swe + if (isnan(h2osoi_ice(j,i))) then + print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i + endif + if (isnan(h2osoi_liq(j,i))) then + print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i + endif + end do + end if + end if + end if + ! if (snow_depth_out(j).gt.0.0) then + ! if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then + ! if (snow_depth_in(j).gt.0.0) then + ! ! Update snow_depth with increment + ! incr_sd = snow_depth_out(j) / snow_depth_in(j) + ! do i=snlsno(j)+1,0 + ! dz(j,i) = dz(j,i) * incr_sd + ! if (isnan(dz(j,i))) then + ! print *, "WARNING: dz at j,i is nan: ", j, i + ! endif + ! end do + ! end if + ! end if + ! end if + end do + end if + + if (clmupdate_snow.eq.7) then + do j=clm_begc,clm_endc + if (h2osno_out(j).gt.0.0) then + if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then + if (h2osno_in(j).gt.0.0) then + ! Update h2osoi_ice/h2osoi_liq with increment + incr_swe = h2osno_out(j) / h2osno_in(j) + do i=snlsno(j)+1,0 + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_swe + ! h2osoi_liq(j,i) = h2osoi_liq(j,i) * incr_swe + if (isnan(h2osoi_ice(j,i))) then + print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i + endif + ! if (isnan(h2osoi_liq(j,i))) then + ! print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i + ! endif + end do + end if + end if + end if + ! if (snow_depth_out(j).gt.0.0) then + ! if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then + ! if (snow_depth_in(j).gt.0.0) then + ! ! Update snow_depth with increment + ! incr_sd = snow_depth_out(j) / snow_depth_in(j) + ! do i=snlsno(j)+1,0 + ! dz(j,i) = dz(j,i) * incr_sd + ! if (isnan(dz(j,i))) then + ! print *, "WARNING: dz at j,i is nan: ", j, i + ! endif + ! end do + ! end if + ! end if + ! end if + end do + end if + end if #ifdef PDAF_DEBUG diff --git a/doc/content/setup_tsmp/input_enkfpf.md b/doc/content/setup_tsmp/input_enkfpf.md index 2d4445f91..c6c042f17 100644 --- a/doc/content/setup_tsmp/input_enkfpf.md +++ b/doc/content/setup_tsmp/input_enkfpf.md @@ -504,6 +504,21 @@ Only CLM5.0/eCLM. updated based on `h2osno`-increment. `dz` updated based on `snow_depth`-increment. +- 5: Assimilation of snow depth. State vector: Snow depth and snow + water equivalent. Requires + `CLM:update_snow_repartitioning=3`. `h2osoi_ice` updated based on + `h2osno`-increment. `dz` updated based on `snow_depth`-increment. + +- 6: Assimilation of snow depth. State vector: Snow depth and snow + water equivalent. Requires + `CLM:update_snow_repartitioning=3`. `h2osoi_ice` and `h2osoi_liq` + updated based on `h2osno`-increment. + +- 7: Assimilation of snow depth. State vector: Snow depth and snow + water equivalent. Requires + `CLM:update_snow_repartitioning=3`. `h2osoi_ice` updated based on + `h2osno`-increment. Should reproduce Case 3. + See CLM Technical Note for more information on snow variable: - snow depth: `waterstate_inst%snow_depth_col` From 4b74c7875d61e9232b700529efd2ec4b1d74c4b2 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 28 Mar 2025 14:38:13 +0100 Subject: [PATCH 66/71] Snow-DA: Fix error stop condition --- bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 4 ++-- bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 0c034d3c0..487adf8c5 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -840,8 +840,8 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") end if end if - if (clmupdate_snow.eq.3 .or. clmupdate_snow.eq.4) then - error stop "Not implemented: Repartioning 1/2 for clmupdate_snow.eq.3/4" + if (clmupdate_snow.ge.3) then + error stop "Not implemented: Repartioning 1/2 for clmupdate_snow.ge.3" end if end if diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index 0c034d3c0..487adf8c5 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -840,8 +840,8 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") end if end if - if (clmupdate_snow.eq.3 .or. clmupdate_snow.eq.4) then - error stop "Not implemented: Repartioning 1/2 for clmupdate_snow.eq.3/4" + if (clmupdate_snow.ge.3) then + error stop "Not implemented: Repartioning 1/2 for clmupdate_snow.ge.3" end if end if From 8c2e5e9c57499e19b9ee30609abf28e9fb50cecb Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 28 Mar 2025 14:42:02 +0100 Subject: [PATCH 67/71] Snow-DA: No SWE/SD update for `CLM:update_snow >= 3` --- bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 6 +++--- bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 487adf8c5..c3ac4c84d 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -805,16 +805,16 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") h2osno_out(j) = clm_statevec(cc+clm_varsize+offset) end if - if(clmupdate_snow.eq.1 .or. clmupdate_snow.eq.4 .or. clmupdate_snow.eq.5 .or. clmupdate_snow.eq.6 .or. clmupdate_snow.eq.7) then + if(clmupdate_snow.eq.1) then ! Update state variable to CLM - ! Not needed for repartioning-case 3? + ! Needed for Case 1/2 if they use repartioning function if (snow_depth_out(j).gt.0.0) then snow_depth(j) = snow_depth_out(j) else ! Catch negative or 0 values from DA print *, "WARNING: Snow-depth is negative/zero at cc. cc, j, offset, snow_depth_out(j): ", cc, j, offset, snow_depth_out(j) end if - else if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3 .or. clmupdate_snow.eq.4 .or. clmupdate_snow.eq.5 .or. clmupdate_snow.eq.6 .or. clmupdate_snow.eq.7) then + else if(clmupdate_snow.eq.2) then if (h2osno_out(j).gt.0.0) then h2osno(j) = h2osno_out(j) else diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index 487adf8c5..c3ac4c84d 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -805,16 +805,16 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") h2osno_out(j) = clm_statevec(cc+clm_varsize+offset) end if - if(clmupdate_snow.eq.1 .or. clmupdate_snow.eq.4 .or. clmupdate_snow.eq.5 .or. clmupdate_snow.eq.6 .or. clmupdate_snow.eq.7) then + if(clmupdate_snow.eq.1) then ! Update state variable to CLM - ! Not needed for repartioning-case 3? + ! Needed for Case 1/2 if they use repartioning function if (snow_depth_out(j).gt.0.0) then snow_depth(j) = snow_depth_out(j) else ! Catch negative or 0 values from DA print *, "WARNING: Snow-depth is negative/zero at cc. cc, j, offset, snow_depth_out(j): ", cc, j, offset, snow_depth_out(j) end if - else if(clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3 .or. clmupdate_snow.eq.4 .or. clmupdate_snow.eq.5 .or. clmupdate_snow.eq.6 .or. clmupdate_snow.eq.7) then + else if(clmupdate_snow.eq.2) then if (h2osno_out(j).gt.0.0) then h2osno(j) = h2osno_out(j) else From 7ffc8fd59a1776b81c101f395e4f11de5a2195fd Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 28 Mar 2025 14:46:39 +0100 Subject: [PATCH 68/71] Snow-DA: Remove difference-check for increment-updates --- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 20 ------------------- .../pdaf/model/eclm/enkf_clm_mod_5.F90 | 20 ------------------- 2 files changed, 40 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index c3ac4c84d..751683906 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -851,7 +851,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if (clmupdate_snow.eq.1) then do j=clm_begc,clm_endc if (snow_depth_out(j).gt.0.0) then - if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then if (snow_depth_in(j).gt.0.0) then ! Update h2osoi_ice with increment incr_sno = snow_depth_out(j) / snow_depth_in(j) @@ -859,7 +858,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_sno end do end if - end if end if end do end if @@ -867,7 +865,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if (clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3) then do j=clm_begc,clm_endc if (h2osno_out(j).gt.0.0) then - if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then if (h2osno_in(j).gt.0.0) then ! Update h2osoi_ice with increment incr_sno = h2osno_out(j) / h2osno_in(j) @@ -875,7 +872,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_sno end do end if - end if end if end do end if @@ -883,7 +879,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if (clmupdate_snow.eq.4) then do j=clm_begc,clm_endc if (h2osno_out(j).gt.0.0) then - if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then if (h2osno_in(j).gt.0.0) then ! Update h2osoi_ice/h2osoi_liq with increment incr_swe = h2osno_out(j) / h2osno_in(j) @@ -898,10 +893,8 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") endif end do end if - end if end if if (snow_depth_out(j).gt.0.0) then - if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then if (snow_depth_in(j).gt.0.0) then ! Update snow_depth with increment incr_sd = snow_depth_out(j) / snow_depth_in(j) @@ -912,7 +905,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") endif end do end if - end if end if end do end if @@ -920,7 +912,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if (clmupdate_snow.eq.5) then do j=clm_begc,clm_endc if (h2osno_out(j).gt.0.0) then - if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then if (h2osno_in(j).gt.0.0) then ! Update h2osoi_ice/h2osoi_liq with increment incr_swe = h2osno_out(j) / h2osno_in(j) @@ -935,10 +926,8 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! endif end do end if - end if end if if (snow_depth_out(j).gt.0.0) then - if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then if (snow_depth_in(j).gt.0.0) then ! Update snow_depth with increment incr_sd = snow_depth_out(j) / snow_depth_in(j) @@ -949,7 +938,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") endif end do end if - end if end if end do end if @@ -957,7 +945,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if (clmupdate_snow.eq.6) then do j=clm_begc,clm_endc if (h2osno_out(j).gt.0.0) then - if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then if (h2osno_in(j).gt.0.0) then ! Update h2osoi_ice/h2osoi_liq with increment incr_swe = h2osno_out(j) / h2osno_in(j) @@ -972,10 +959,8 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") endif end do end if - end if end if ! if (snow_depth_out(j).gt.0.0) then - ! if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then ! if (snow_depth_in(j).gt.0.0) then ! ! Update snow_depth with increment ! incr_sd = snow_depth_out(j) / snow_depth_in(j) @@ -986,7 +971,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! endif ! end do ! end if - ! end if ! end if end do end if @@ -994,7 +978,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if (clmupdate_snow.eq.7) then do j=clm_begc,clm_endc if (h2osno_out(j).gt.0.0) then - if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then if (h2osno_in(j).gt.0.0) then ! Update h2osoi_ice/h2osoi_liq with increment incr_swe = h2osno_out(j) / h2osno_in(j) @@ -1009,10 +992,8 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! endif end do end if - end if end if ! if (snow_depth_out(j).gt.0.0) then - ! if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then ! if (snow_depth_in(j).gt.0.0) then ! ! Update snow_depth with increment ! incr_sd = snow_depth_out(j) / snow_depth_in(j) @@ -1023,7 +1004,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! endif ! end do ! end if - ! end if ! end if end do end if diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index c3ac4c84d..751683906 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -851,7 +851,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if (clmupdate_snow.eq.1) then do j=clm_begc,clm_endc if (snow_depth_out(j).gt.0.0) then - if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then if (snow_depth_in(j).gt.0.0) then ! Update h2osoi_ice with increment incr_sno = snow_depth_out(j) / snow_depth_in(j) @@ -859,7 +858,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_sno end do end if - end if end if end do end if @@ -867,7 +865,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if (clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3) then do j=clm_begc,clm_endc if (h2osno_out(j).gt.0.0) then - if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then if (h2osno_in(j).gt.0.0) then ! Update h2osoi_ice with increment incr_sno = h2osno_out(j) / h2osno_in(j) @@ -875,7 +872,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_sno end do end if - end if end if end do end if @@ -883,7 +879,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if (clmupdate_snow.eq.4) then do j=clm_begc,clm_endc if (h2osno_out(j).gt.0.0) then - if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then if (h2osno_in(j).gt.0.0) then ! Update h2osoi_ice/h2osoi_liq with increment incr_swe = h2osno_out(j) / h2osno_in(j) @@ -898,10 +893,8 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") endif end do end if - end if end if if (snow_depth_out(j).gt.0.0) then - if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then if (snow_depth_in(j).gt.0.0) then ! Update snow_depth with increment incr_sd = snow_depth_out(j) / snow_depth_in(j) @@ -912,7 +905,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") endif end do end if - end if end if end do end if @@ -920,7 +912,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if (clmupdate_snow.eq.5) then do j=clm_begc,clm_endc if (h2osno_out(j).gt.0.0) then - if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then if (h2osno_in(j).gt.0.0) then ! Update h2osoi_ice/h2osoi_liq with increment incr_swe = h2osno_out(j) / h2osno_in(j) @@ -935,10 +926,8 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! endif end do end if - end if end if if (snow_depth_out(j).gt.0.0) then - if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then if (snow_depth_in(j).gt.0.0) then ! Update snow_depth with increment incr_sd = snow_depth_out(j) / snow_depth_in(j) @@ -949,7 +938,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") endif end do end if - end if end if end do end if @@ -957,7 +945,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if (clmupdate_snow.eq.6) then do j=clm_begc,clm_endc if (h2osno_out(j).gt.0.0) then - if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then if (h2osno_in(j).gt.0.0) then ! Update h2osoi_ice/h2osoi_liq with increment incr_swe = h2osno_out(j) / h2osno_in(j) @@ -972,10 +959,8 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") endif end do end if - end if end if ! if (snow_depth_out(j).gt.0.0) then - ! if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then ! if (snow_depth_in(j).gt.0.0) then ! ! Update snow_depth with increment ! incr_sd = snow_depth_out(j) / snow_depth_in(j) @@ -986,7 +971,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! endif ! end do ! end if - ! end if ! end if end do end if @@ -994,7 +978,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if (clmupdate_snow.eq.7) then do j=clm_begc,clm_endc if (h2osno_out(j).gt.0.0) then - if ( ABS(h2osno_in(j) - h2osno_out(j)).gt.0.000001) then if (h2osno_in(j).gt.0.0) then ! Update h2osoi_ice/h2osoi_liq with increment incr_swe = h2osno_out(j) / h2osno_in(j) @@ -1009,10 +992,8 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! endif end do end if - end if end if ! if (snow_depth_out(j).gt.0.0) then - ! if ( ABS(snow_depth_in(j) - snow_depth_out(j)).gt.0.000001) then ! if (snow_depth_in(j).gt.0.0) then ! ! Update snow_depth with increment ! incr_sd = snow_depth_out(j) / snow_depth_in(j) @@ -1023,7 +1004,6 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") ! endif ! end do ! end if - ! end if ! end if end do end if From 39568e64d8f2163500efc1ee5b7acf95ac7b3d45 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 28 Mar 2025 14:57:35 +0100 Subject: [PATCH 69/71] Snow-DA: `CLM:update_snow=4..7`, check both SD and SWE update with both increments or don't update at all --- .../pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 159 ++++-------------- .../pdaf/model/eclm/enkf_clm_mod_5.F90 | 159 ++++-------------- 2 files changed, 68 insertions(+), 250 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 751683906..7f1574691 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -876,135 +876,44 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") end do end if - if (clmupdate_snow.eq.4) then + if (clmupdate_snow.eq.4 .or. clmupdate_snow.eq.5 .or. clmupdate_snow.eq.6 .or. clmupdate_snow.eq.7) then do j=clm_begc,clm_endc if (h2osno_out(j).gt.0.0) then - if (h2osno_in(j).gt.0.0) then - ! Update h2osoi_ice/h2osoi_liq with increment - incr_swe = h2osno_out(j) / h2osno_in(j) - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_swe - h2osoi_liq(j,i) = h2osoi_liq(j,i) * incr_swe - if (isnan(h2osoi_ice(j,i))) then - print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i - endif - if (isnan(h2osoi_liq(j,i))) then - print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i - endif - end do - end if - end if - if (snow_depth_out(j).gt.0.0) then - if (snow_depth_in(j).gt.0.0) then - ! Update snow_depth with increment - incr_sd = snow_depth_out(j) / snow_depth_in(j) - do i=snlsno(j)+1,0 - dz(j,i) = dz(j,i) * incr_sd - if (isnan(dz(j,i))) then - print *, "WARNING: dz at j,i is nan: ", j, i - endif - end do - end if - end if - end do - end if - - if (clmupdate_snow.eq.5) then - do j=clm_begc,clm_endc - if (h2osno_out(j).gt.0.0) then - if (h2osno_in(j).gt.0.0) then - ! Update h2osoi_ice/h2osoi_liq with increment - incr_swe = h2osno_out(j) / h2osno_in(j) - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_swe - ! h2osoi_liq(j,i) = h2osoi_liq(j,i) * incr_swe - if (isnan(h2osoi_ice(j,i))) then - print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i - endif - ! if (isnan(h2osoi_liq(j,i))) then - ! print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i - ! endif - end do - end if - end if - if (snow_depth_out(j).gt.0.0) then - if (snow_depth_in(j).gt.0.0) then - ! Update snow_depth with increment - incr_sd = snow_depth_out(j) / snow_depth_in(j) - do i=snlsno(j)+1,0 - dz(j,i) = dz(j,i) * incr_sd - if (isnan(dz(j,i))) then - print *, "WARNING: dz at j,i is nan: ", j, i - endif - end do - end if - end if - end do - end if - - if (clmupdate_snow.eq.6) then - do j=clm_begc,clm_endc - if (h2osno_out(j).gt.0.0) then - if (h2osno_in(j).gt.0.0) then - ! Update h2osoi_ice/h2osoi_liq with increment - incr_swe = h2osno_out(j) / h2osno_in(j) - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_swe - h2osoi_liq(j,i) = h2osoi_liq(j,i) * incr_swe - if (isnan(h2osoi_ice(j,i))) then - print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i - endif - if (isnan(h2osoi_liq(j,i))) then - print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i - endif - end do - end if - end if - ! if (snow_depth_out(j).gt.0.0) then - ! if (snow_depth_in(j).gt.0.0) then - ! ! Update snow_depth with increment - ! incr_sd = snow_depth_out(j) / snow_depth_in(j) - ! do i=snlsno(j)+1,0 - ! dz(j,i) = dz(j,i) * incr_sd - ! if (isnan(dz(j,i))) then - ! print *, "WARNING: dz at j,i is nan: ", j, i - ! endif - ! end do - ! end if - ! end if - end do - end if - - if (clmupdate_snow.eq.7) then - do j=clm_begc,clm_endc - if (h2osno_out(j).gt.0.0) then - if (h2osno_in(j).gt.0.0) then - ! Update h2osoi_ice/h2osoi_liq with increment - incr_swe = h2osno_out(j) / h2osno_in(j) - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_swe - ! h2osoi_liq(j,i) = h2osoi_liq(j,i) * incr_swe - if (isnan(h2osoi_ice(j,i))) then - print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i - endif - ! if (isnan(h2osoi_liq(j,i))) then - ! print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i - ! endif - end do + if (h2osno_in(j).gt.0.0) then + if (snow_depth_out(j).gt.0.0) then + if (snow_depth_in(j).gt.0.0) then + ! Update h2osoi_ice/h2osoi_liq with increment + incr_swe = h2osno_out(j) / h2osno_in(j) + ! Update snow_depth with increment + incr_sd = snow_depth_out(j) / snow_depth_in(j) + do i=snlsno(j)+1,0 + + if (clmupdate_snow.eq.4 .or. clmupdate_snow.eq.5 .or. clmupdate_snow.eq.6 .or. clmupdate_snow.eq.7) then + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_swe + if (isnan(h2osoi_ice(j,i))) then + print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i + endif + end if + + if (clmupdate_snow.eq.4 .or. clmupdate_snow.eq.6) then + h2osoi_liq(j,i) = h2osoi_liq(j,i) * incr_swe + if (isnan(h2osoi_liq(j,i))) then + print *, "WARNING: h2osoi_liq at j,i is nan: ", j, i + endif + end if + + if (clmupdate_snow.eq.4 .or. clmupdate_snow.eq.5) then + dz(j,i) = dz(j,i) * incr_sd + if (isnan(dz(j,i))) then + print *, "WARNING: dz at j,i is nan: ", j, i + endif + end if + + end do + end if end if + end if end if - ! if (snow_depth_out(j).gt.0.0) then - ! if (snow_depth_in(j).gt.0.0) then - ! ! Update snow_depth with increment - ! incr_sd = snow_depth_out(j) / snow_depth_in(j) - ! do i=snlsno(j)+1,0 - ! dz(j,i) = dz(j,i) * incr_sd - ! if (isnan(dz(j,i))) then - ! print *, "WARNING: dz at j,i is nan: ", j, i - ! endif - ! end do - ! end if - ! end if end do end if diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index 751683906..7f1574691 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -876,135 +876,44 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") end do end if - if (clmupdate_snow.eq.4) then + if (clmupdate_snow.eq.4 .or. clmupdate_snow.eq.5 .or. clmupdate_snow.eq.6 .or. clmupdate_snow.eq.7) then do j=clm_begc,clm_endc if (h2osno_out(j).gt.0.0) then - if (h2osno_in(j).gt.0.0) then - ! Update h2osoi_ice/h2osoi_liq with increment - incr_swe = h2osno_out(j) / h2osno_in(j) - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_swe - h2osoi_liq(j,i) = h2osoi_liq(j,i) * incr_swe - if (isnan(h2osoi_ice(j,i))) then - print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i - endif - if (isnan(h2osoi_liq(j,i))) then - print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i - endif - end do - end if - end if - if (snow_depth_out(j).gt.0.0) then - if (snow_depth_in(j).gt.0.0) then - ! Update snow_depth with increment - incr_sd = snow_depth_out(j) / snow_depth_in(j) - do i=snlsno(j)+1,0 - dz(j,i) = dz(j,i) * incr_sd - if (isnan(dz(j,i))) then - print *, "WARNING: dz at j,i is nan: ", j, i - endif - end do - end if - end if - end do - end if - - if (clmupdate_snow.eq.5) then - do j=clm_begc,clm_endc - if (h2osno_out(j).gt.0.0) then - if (h2osno_in(j).gt.0.0) then - ! Update h2osoi_ice/h2osoi_liq with increment - incr_swe = h2osno_out(j) / h2osno_in(j) - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_swe - ! h2osoi_liq(j,i) = h2osoi_liq(j,i) * incr_swe - if (isnan(h2osoi_ice(j,i))) then - print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i - endif - ! if (isnan(h2osoi_liq(j,i))) then - ! print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i - ! endif - end do - end if - end if - if (snow_depth_out(j).gt.0.0) then - if (snow_depth_in(j).gt.0.0) then - ! Update snow_depth with increment - incr_sd = snow_depth_out(j) / snow_depth_in(j) - do i=snlsno(j)+1,0 - dz(j,i) = dz(j,i) * incr_sd - if (isnan(dz(j,i))) then - print *, "WARNING: dz at j,i is nan: ", j, i - endif - end do - end if - end if - end do - end if - - if (clmupdate_snow.eq.6) then - do j=clm_begc,clm_endc - if (h2osno_out(j).gt.0.0) then - if (h2osno_in(j).gt.0.0) then - ! Update h2osoi_ice/h2osoi_liq with increment - incr_swe = h2osno_out(j) / h2osno_in(j) - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_swe - h2osoi_liq(j,i) = h2osoi_liq(j,i) * incr_swe - if (isnan(h2osoi_ice(j,i))) then - print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i - endif - if (isnan(h2osoi_liq(j,i))) then - print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i - endif - end do - end if - end if - ! if (snow_depth_out(j).gt.0.0) then - ! if (snow_depth_in(j).gt.0.0) then - ! ! Update snow_depth with increment - ! incr_sd = snow_depth_out(j) / snow_depth_in(j) - ! do i=snlsno(j)+1,0 - ! dz(j,i) = dz(j,i) * incr_sd - ! if (isnan(dz(j,i))) then - ! print *, "WARNING: dz at j,i is nan: ", j, i - ! endif - ! end do - ! end if - ! end if - end do - end if - - if (clmupdate_snow.eq.7) then - do j=clm_begc,clm_endc - if (h2osno_out(j).gt.0.0) then - if (h2osno_in(j).gt.0.0) then - ! Update h2osoi_ice/h2osoi_liq with increment - incr_swe = h2osno_out(j) / h2osno_in(j) - do i=snlsno(j)+1,0 - h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_swe - ! h2osoi_liq(j,i) = h2osoi_liq(j,i) * incr_swe - if (isnan(h2osoi_ice(j,i))) then - print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i - endif - ! if (isnan(h2osoi_liq(j,i))) then - ! print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i - ! endif - end do + if (h2osno_in(j).gt.0.0) then + if (snow_depth_out(j).gt.0.0) then + if (snow_depth_in(j).gt.0.0) then + ! Update h2osoi_ice/h2osoi_liq with increment + incr_swe = h2osno_out(j) / h2osno_in(j) + ! Update snow_depth with increment + incr_sd = snow_depth_out(j) / snow_depth_in(j) + do i=snlsno(j)+1,0 + + if (clmupdate_snow.eq.4 .or. clmupdate_snow.eq.5 .or. clmupdate_snow.eq.6 .or. clmupdate_snow.eq.7) then + h2osoi_ice(j,i) = h2osoi_ice(j,i) * incr_swe + if (isnan(h2osoi_ice(j,i))) then + print *, "WARNING: h2osoi_ice at j,i is nan: ", j, i + endif + end if + + if (clmupdate_snow.eq.4 .or. clmupdate_snow.eq.6) then + h2osoi_liq(j,i) = h2osoi_liq(j,i) * incr_swe + if (isnan(h2osoi_liq(j,i))) then + print *, "WARNING: h2osoi_liq at j,i is nan: ", j, i + endif + end if + + if (clmupdate_snow.eq.4 .or. clmupdate_snow.eq.5) then + dz(j,i) = dz(j,i) * incr_sd + if (isnan(dz(j,i))) then + print *, "WARNING: dz at j,i is nan: ", j, i + endif + end if + + end do + end if end if + end if end if - ! if (snow_depth_out(j).gt.0.0) then - ! if (snow_depth_in(j).gt.0.0) then - ! ! Update snow_depth with increment - ! incr_sd = snow_depth_out(j) / snow_depth_in(j) - ! do i=snlsno(j)+1,0 - ! dz(j,i) = dz(j,i) * incr_sd - ! if (isnan(dz(j,i))) then - ! print *, "WARNING: dz at j,i is nan: ", j, i - ! endif - ! end do - ! end if - ! end if end do end if From f40140f9566b7456622e9a4db5727be7eaaef57e Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Fri, 28 Mar 2025 14:59:26 +0100 Subject: [PATCH 70/71] Snow-DA: Rule out very small SD/SWE for updates --- .../intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 16 ++++++++-------- .../intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 7f1574691..9ef496877 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -850,8 +850,8 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if (clmupdate_snow.eq.1) then do j=clm_begc,clm_endc - if (snow_depth_out(j).gt.0.0) then - if (snow_depth_in(j).gt.0.0) then + if (snow_depth_out(j).gt.1.0d-6) then + if (snow_depth_in(j).gt.1.0d-6) then ! Update h2osoi_ice with increment incr_sno = snow_depth_out(j) / snow_depth_in(j) do i=snlsno(j)+1,0 @@ -864,8 +864,8 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if (clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3) then do j=clm_begc,clm_endc - if (h2osno_out(j).gt.0.0) then - if (h2osno_in(j).gt.0.0) then + if (h2osno_out(j).gt.1.0d-6) then + if (h2osno_in(j).gt.1.0d-6) then ! Update h2osoi_ice with increment incr_sno = h2osno_out(j) / h2osno_in(j) do i=snlsno(j)+1,0 @@ -878,10 +878,10 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if (clmupdate_snow.eq.4 .or. clmupdate_snow.eq.5 .or. clmupdate_snow.eq.6 .or. clmupdate_snow.eq.7) then do j=clm_begc,clm_endc - if (h2osno_out(j).gt.0.0) then - if (h2osno_in(j).gt.0.0) then - if (snow_depth_out(j).gt.0.0) then - if (snow_depth_in(j).gt.0.0) then + if (h2osno_out(j).gt.1.0d-6) then + if (h2osno_in(j).gt.1.0d-6) then + if (snow_depth_out(j).gt.1.0d-6) then + if (snow_depth_in(j).gt.1.0d-6) then ! Update h2osoi_ice/h2osoi_liq with increment incr_swe = h2osno_out(j) / h2osno_in(j) ! Update snow_depth with increment diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index 7f1574691..9ef496877 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -850,8 +850,8 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if (clmupdate_snow.eq.1) then do j=clm_begc,clm_endc - if (snow_depth_out(j).gt.0.0) then - if (snow_depth_in(j).gt.0.0) then + if (snow_depth_out(j).gt.1.0d-6) then + if (snow_depth_in(j).gt.1.0d-6) then ! Update h2osoi_ice with increment incr_sno = snow_depth_out(j) / snow_depth_in(j) do i=snlsno(j)+1,0 @@ -864,8 +864,8 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if (clmupdate_snow.eq.2 .or. clmupdate_snow.eq.3) then do j=clm_begc,clm_endc - if (h2osno_out(j).gt.0.0) then - if (h2osno_in(j).gt.0.0) then + if (h2osno_out(j).gt.1.0d-6) then + if (h2osno_in(j).gt.1.0d-6) then ! Update h2osoi_ice with increment incr_sno = h2osno_out(j) / h2osno_in(j) do i=snlsno(j)+1,0 @@ -878,10 +878,10 @@ subroutine update_clm(tstartcycle, mype) bind(C,name="update_clm") if (clmupdate_snow.eq.4 .or. clmupdate_snow.eq.5 .or. clmupdate_snow.eq.6 .or. clmupdate_snow.eq.7) then do j=clm_begc,clm_endc - if (h2osno_out(j).gt.0.0) then - if (h2osno_in(j).gt.0.0) then - if (snow_depth_out(j).gt.0.0) then - if (snow_depth_in(j).gt.0.0) then + if (h2osno_out(j).gt.1.0d-6) then + if (h2osno_in(j).gt.1.0d-6) then + if (snow_depth_out(j).gt.1.0d-6) then + if (snow_depth_in(j).gt.1.0d-6) then ! Update h2osoi_ice/h2osoi_liq with increment incr_swe = h2osno_out(j) / h2osno_in(j) ! Update snow_depth with increment From 517fd75e80effb802eef349a34bdbb0caa6eb085 Mon Sep 17 00:00:00 2001 From: Johannes Keller Date: Sat, 29 Mar 2025 07:03:42 +0100 Subject: [PATCH 71/71] Snow-DA: Bugfix for `CLM:update_snow=7` --- bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 | 2 +- bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 index 9ef496877..58611cd7b 100755 --- a/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/clm5_0/enkf_clm_mod_5.F90 @@ -310,7 +310,7 @@ subroutine define_clm_statevec(mype) ! Case 7: Assimilation of snow depth: Snow depth and snow water ! equivalent in the state vector. Update of h2osoi_ice ! Should reproduce case 3 - if(clmupdate_snow.eq.6) then + if(clmupdate_snow.eq.7) then clm_varsize = (clm_endg-clm_begg+1) clm_statevecsize = 2*(clm_endg-clm_begg+1) endif diff --git a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 index 9ef496877..58611cd7b 100755 --- a/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 +++ b/bldsva/intf_DA/pdaf/model/eclm/enkf_clm_mod_5.F90 @@ -310,7 +310,7 @@ subroutine define_clm_statevec(mype) ! Case 7: Assimilation of snow depth: Snow depth and snow water ! equivalent in the state vector. Update of h2osoi_ice ! Should reproduce case 3 - if(clmupdate_snow.eq.6) then + if(clmupdate_snow.eq.7) then clm_varsize = (clm_endg-clm_begg+1) clm_statevecsize = 2*(clm_endg-clm_begg+1) endif