Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
c49edbe
first implementation of LST-DA
jjokella Dec 5, 2024
0fb734a
bugfix: re-introduce default for general update
jjokella Dec 12, 2024
b186564
bugfix: lai is a patch-array
jjokella Dec 12, 2024
35c9c93
bugfix: typo
jjokella Dec 12, 2024
929471f
dev: skin temperature state vector, first implementation
jjokella Dec 12, 2024
0f3738b
dev: skin temperature state vector, first implementation II
jjokella Dec 12, 2024
53998fb
bugfix: t_skin declarations
jjokella Dec 13, 2024
75ac19f
bugfix: location of `newgridcell = .false`
jjokella Jan 23, 2025
34a47ca
LST-DA with TSKIN: use same obs_index_p setting as for TG/TV
jjokella Jan 23, 2025
43d1ba3
LST-DA: state-vector with TSKIN, plus TG and TV
jjokella Jan 23, 2025
3df6fd0
LST-DA: state-vector with TSKIN, plus TSOIL and TV
jjokella Jan 23, 2025
9e88c2a
LST-DA: add debug output for first layer `t_soisno`
jjokella Jan 24, 2025
ff0373a
LST-DA: all temperature layers updated
jjokella Jan 28, 2025
4bd276d
bugfix: LST-DA missing `end do`
jjokella Jan 29, 2025
75eb0ec
Merge branch 'tsmp-pdaf-patched' into tsmp-pdaf-patched-lstda
jjokella Mar 17, 2025
d41c6b2
Merge branch 'tsmp-pdaf-patched' into tsmp-pdaf-patched-lstda
jjokella Apr 25, 2025
0778801
introduce `clmupdate_T.eq.4`
jjokella Apr 25, 2025
6d8babe
Merge branch 'tsmp-pdaf-patched' into tsmp-pdaf-patched-lstda
jjokella Oct 9, 2025
fb67c10
fortitude fixes
jjokella Oct 9, 2025
75f2b8d
keep indentation of SM-case
jjokella Oct 9, 2025
2b7f957
syntax fix
jjokella Oct 9, 2025
6a6706a
line length fixes
jjokella Oct 9, 2025
90cefc6
Merge branch 'tsmp-pdaf-patched' into tsmp-pdaf-patched-lstda
jjokella Oct 17, 2025
9936414
CI-fix: handle clmswc_mask_snow as integer (#29)
jjokella Oct 29, 2025
9625bee
Merge branch 'tsmp-pdaf-patched' into tsmp-pdaf-patched-lstda
jjokella Nov 14, 2025
319bd8c
style changes
jjokella Nov 14, 2025
35b1dc0
set_clm_statevec_T: debug output to dedicated subroutine
jjokella Nov 14, 2025
58501ab
update_clm_T: all declarations to dedicated subroutine
jjokella Nov 14, 2025
3847db7
compilation fixes
jjokella Nov 14, 2025
ee6cc3e
compilation fixes for DEBUG
jjokella Nov 14, 2025
7174f1b
add LSTDA of TSKIN/TVEG (clmupdate_T==5)
jjokella Nov 20, 2025
5c307e5
LSTDA for local filters: first implementation
jjokella Dec 11, 2025
d1cb4d3
bugfix
jjokella Dec 11, 2025
afc7202
bugfix: initialize and use begp, endp throughout init_n_domains_clm
jjokella Dec 11, 2025
95a0e5f
Merge branch 'tsmp-pdaf-patched' into tsmp-pdaf-patched-lstda
jjokella Dec 18, 2025
f9e886b
Merge branch 'tsmp-pdaf-patched-lstda' into tsmp-pdaf-patched-lstda-l…
jjokella Dec 18, 2025
747dee9
correct condition for tsoisno_mype.update.* debug output
jjokella Dec 19, 2025
58025fa
Merge branch 'tsmp-pdaf-patched-lstda' into tsmp-pdaf-patched-lstda-l…
jjokella Dec 19, 2025
38ffe0e
LSTDA: remove unused variants
jjokella Jan 23, 2026
2a5657f
Merge branch 'tsmp-pdaf-patched-lstda' into tsmp-pdaf-patched-lstda-l…
jjokella Jan 23, 2026
e67133a
remove unused index array
jjokella Jan 23, 2026
8eee110
small change
jjokella Jan 23, 2026
249c4ad
remove unused variable `pc`
jjokella Jan 24, 2026
1203bae
remove unused clmupdate_T values
jjokella Jan 24, 2026
b3d0415
LSTDA: remodel clmupdate_T==2 with gridcell-averages
jjokella Jan 24, 2026
78266fc
CLM:T_mask_snow: possibility to remove snow from LSTDA
jjokella Jan 30, 2026
43916e8
LSTDA: only update of first-layer-soil-temperature > freezing point
jjokella Jan 30, 2026
eb2f6bd
Merge branch 'tsmp-pdaf-patched' into tsmp-pdaf-patched-lstda-v2-3
jjokella Feb 13, 2026
cc24bd1
input CLM:T_mask_T: setting the temperature theshold for T-update
jjokella Feb 13, 2026
3cb7dbe
Merge branch 'tsmp-pdaf-patched-lstda' into tsmp-pdaf-patched-lstda-v2-3
jjokella Feb 13, 2026
aebde39
LST-DA: input option CLM:increment_type
jjokella Feb 25, 2026
9eaf7f5
LST-DA: input option CLM:T_max_increment
jjokella Feb 25, 2026
7a39038
Merge branch 'tsmp-pdaf-patched-lstda' into tsmp-pdaf-patched-lstda-v2-3
jjokella Feb 25, 2026
9b58f24
bugfix: remove the increment_type
jjokella Feb 25, 2026
b1f7664
LSTDA: clmupdate_T==3, added t_grnd to state vector
jjokella Feb 25, 2026
5583f0e
bugfixes: if-condition names
jjokella Feb 25, 2026
e6dcd08
LSTDA-bugfix: non-update set correctly
jjokella Feb 26, 2026
bde2960
LSTDA: restrict updates to clmT_max_increment
jjokella Feb 26, 2026
790316b
Merge branch 'tsmp-pdaf-patched-lstda' into tsmp-pdaf-patched-lstda-v2-3
jjokella Feb 27, 2026
2b707f9
LSTDA: smarter increment warning structure
jjokella Feb 27, 2026
7d06515
Added cap for LSTADA from Claude
visakhraja Mar 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 54 additions & 2 deletions docs/users_guide/running_tsmp_pdaf/input_enkfpf.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ statevec_max_layer =
t_printensemble =
watmin_switch =
swc_mask_snow =
T_mask_snow =
increment_type =
T_mask_T =
T_max_increment =

[COSMO]
nprocs =
Expand Down Expand Up @@ -484,12 +488,14 @@ CLM (standalone only).
`CLM:update_T`: (integer) Flag for updating of ground and vegetation
temperature.

Currently only CLM3.5

- 0: No update of ground and vegetation temperature

- 1: Update of ground and vegetation temperature

- 2: Update of soil and vegetation temperature (prognostic variables)
based on comparison of satellite LST with skin temperature
(diagnostic variable)

Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docs for CLM:update_T describe modes 0/1/2, but the code now implements clmupdate_T==3 as well. Please update this section to document mode 3 (and any differences between 2 and 3) so users don’t accidentally enable an undocumented mode.

Suggested change
- 3: Alternative (experimental) implementation of the soil and
vegetation temperature update. For details on the algorithm and
differences to mode 2, please refer to the CLM update routine
in the TSMP-PDAF source code.

Copilot uses AI. Check for mistakes.
### CLM:print_swc ###

`CLM:print_swc`: (integer) If set to `1`, the updated soil moisture
Expand Down Expand Up @@ -601,6 +607,50 @@ Only takes effect if `CLM:update_swc``is switched on.

Default setting is `0`: No masking of columns with snow cover.

### CLM:T_mask_snow ###

`CLM:T_mask_snow`: (integer) Switch for masking columns with snow
cover from T updates.

Snow covers larger than 1mm are switched off for the update.

Only takes effect if `CLM:update_T``is switched on.

Default setting is `0`: No masking of columns with snow cover.

### CLM:increment_type ###

`CLM:increment_type`: (integer) Switch for changing increment type in
T-update.

- `0`: Multiplicative increment
- `1`: Additive increment

Only takes effect if `CLM:update_T` is switched on.

Default setting is `0`: Multiplicative increment.

### CLM:T_max_increment ###

`CLM:T_max_increment`: (double) Maximum T-increment to update
(additively).

Only takes effect if `CLM:update_T` is switched on and
`CLM:increment_type` is set to `1`.

Default setting is `5.0`: Updates larger than 5K are not applied.

### CLM:T_mask_T ###

`CLM:T_mask_T`: (double) Temperature difference to add to freezing
temperature resulting in a threshold temperature for masking out T
updates. Whenever the soil temperature of the surface layer falls
below this threshold temperature, no T is not updated.

Only takes effect if `CLM:update_T``is switched on.

Default setting is `0.`: Masking updates below freezing temperaturs.
Comment on lines +648 to +652
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several formatting/typo issues in the new CLM:T_mask_* docs: extra backticks in "`CLM:update_T``is switched on", grammar in "no T is not updated", and spelling in "freezing temperaturs". Please clean these up to avoid confusing rendered documentation.

Suggested change
below this threshold temperature, no T is not updated.
Only takes effect if `CLM:update_T``is switched on.
Default setting is `0.`: Masking updates below freezing temperaturs.
below this threshold temperature, no T is updated.
Only takes effect if `CLM:update_T` is switched on.
Default setting is `0.`: Masking updates below freezing temperatures.

Copilot uses AI. Check for mistakes.

(enkfpf:cosmo)=
## [COSMO] ##

Expand Down Expand Up @@ -900,6 +950,8 @@ Default: 0, output turned off.
| | `statevec_max_layer` | 25 |
| | `t_printensemble` | -2 |
| | `watmin_switch` | 0 |
| | `T_mask_snow` | 0 |
| | `T_mask_T` | 0.0 |
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The defaults table at the end was updated for T_mask_snow/T_mask_T, but it doesn’t list the new increment_type and T_max_increment keys that were added above. Please extend the table so it stays complete/consistent with the documented parameters.

Suggested change
| | `T_mask_T` | 0.0 |
| | `T_mask_T` | 0.0 |
| | `increment_type` | 0 |
| | `T_max_increment` | 0.0 |

Copilot uses AI. Check for mistakes.
| `[COSMO]` | | |
| | `nprocs` | 0 |
| | `dtmult` | 0 |
Expand Down
100 changes: 99 additions & 1 deletion interface/framework/init_dim_obs_f_pdaf.F90
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ SUBROUTINE init_dim_obs_f_pdaf(step, dim_obs_f)
#ifdef CLMFIVE
use GridcellType, only: grc
use ColumnType, only : col
use PatchType, only : patch
! use GetGlobalValuesMod, only: GetGlobalWrite
! use clm_varcon, only: nameg
use enkf_clm_mod, only: state_clm2pdaf_p
Expand All @@ -145,6 +146,8 @@ 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_swc
use enkf_clm_mod, only: clmupdate_T
!hcp end
#endif
#endif
Expand All @@ -164,9 +167,11 @@ SUBROUTINE init_dim_obs_f_pdaf(step, dim_obs_f)
integer :: ierror
INTEGER :: max_var_id ! Multi-scale DA
INTEGER :: sum_dim_obs_p
INTEGER :: p ! CLM Patch index
INTEGER :: c ! CLM Column index
INTEGER :: g ! CLM Gridcell index
INTEGER :: cg
INTEGER :: pg
INTEGER :: i,j,k ! Counters
INTEGER :: cnt ! Counters
INTEGER :: cnt_interp ! Counter for interpolation grid cells
Expand Down Expand Up @@ -935,6 +940,8 @@ SUBROUTINE init_dim_obs_f_pdaf(step, dim_obs_f)
do i = 1, dim_obs
obs(i) = clm_obs(i)

if(clmupdate_swc==1) then

do g = begg,endg
newgridcell = .true.

Expand Down Expand Up @@ -1005,9 +1012,100 @@ SUBROUTINE init_dim_obs_f_pdaf(step, dim_obs_f)
end if

end if

end do
end do

else if(clmupdate_T==1 .or. clmupdate_T==2 .or. clmupdate_T==3) then
#ifdef CLMFIVE
! patch loop
do g = begg,endg
newgridcell = .true.

do p = begp,endp

pg = patch%gridcell(p)

if(pg == g) then
if(newgridcell) then
! Sets first patch/column in a gridcell. TODO: Make
! patch / column information part of the observation
! file

if(is_use_dr) then
deltax = abs(lon(g)-clmobs_lon(i))
deltay = abs(lat(g)-clmobs_lat(i))
end if

if(((is_use_dr).and.(deltax<=clmobs_dr(1)).and.(deltay<=clmobs_dr(2))).or. &
((.not. is_use_dr).and.(longxy_obs(i) == longxy(g-begg+1)) .and. (latixy_obs(i) == latixy(g-begg+1)))) then

! Set index in state vector, LST will be computed
! for first patch appearing here
obs_index_p(cnt) = state_clm2pdaf_p(p,1)

!write(*,*) 'obs_index_p(',cnt,') is',obs_index_p(cnt)
obs_p(cnt) = clm_obs(i)
if(multierr==1) clm_obserr_p(cnt) = clm_obserr(i)
cnt = cnt + 1

Comment on lines +1042 to +1050
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the clmupdate_T observation-indexing branch, the code only selects the first patch per gridcell (newgridcell guard). If clmupdate_T==1 is patch-based, this will under-observe/under-update the state. Please align obs_index_p generation with the actual state-vector layout for each clmupdate_T mode (gridcell-mean vs patch-level).

Copilot uses AI. Check for mistakes.
end if

newgridcell = .false.

end if
end if

end do
end do
#else
! gridcell loop
do g = begg,endg

if(is_use_dr) then
deltax = abs(lon(g)-clmobs_lon(i))
deltay = abs(lat(g)-clmobs_lat(i))
end if

if(((is_use_dr).and.(deltax<=clmobs_dr(1)).and.(deltay<=clmobs_dr(2))).or. &
((.not. is_use_dr).and.(longxy_obs(i) == longxy(g-begg+1)) .and. (latixy_obs(i) == latixy(g-begg+1)))) then
obs_index_p(cnt) = g-begg+1
end if

!write(*,*) 'obs_index_p(',cnt,') is',obs_index_p(cnt)
obs_p(cnt) = clm_obs(i)
if(multierr==1) clm_obserr_p(cnt) = clm_obserr(i)
cnt = cnt + 1

end do
#endif
else

print *, "TSMP-PDAF mype(w)=", mype_world, ": WARNING unsupported update in setting obs_index_p."
print *, "TSMP-PDAF mype(w)=", mype_world, ": WARNING using default gridcell loop."
! call abort_parallel()

! gridcell loop with layer information as default!
do g = begg,endg

if(is_use_dr) then
deltax = abs(lon(g)-clmobs_lon(i))
deltay = abs(lat(g)-clmobs_lat(i))
end if

if(((is_use_dr).and.(deltax<=clmobs_dr(1)).and.(deltay<=clmobs_dr(2))).or. &
((.not. is_use_dr).and.(longxy_obs(i) == longxy(g-begg+1)) .and. (latixy_obs(i) == latixy(g-begg+1)))) then
obs_index_p(cnt) = g-begg+1 + ((endg-begg+1) * (clmobs_layer(i)-1))
end if

!write(*,*) 'obs_index_p(',cnt,') is',obs_index_p(cnt)
obs_p(cnt) = clm_obs(i)
if(multierr==1) clm_obserr_p(cnt) = clm_obserr(i)
cnt = cnt + 1

end do

end if

end do

if(obs_interp_switch==1) then
Expand Down
102 changes: 101 additions & 1 deletion interface/framework/init_dim_obs_pdaf.F90
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ SUBROUTINE init_dim_obs_pdaf(step, dim_obs_p)
#ifdef CLMFIVE
use GridcellType, only: grc
use ColumnType, only : col
use PatchType, only : patch
! use GetGlobalValuesMod, only: GetGlobalWrite
! use clm_varcon, only: nameg
use enkf_clm_mod, only: state_clm2pdaf_p
Expand All @@ -141,6 +142,8 @@ 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_swc
use enkf_clm_mod, only: clmupdate_T
!hcp end
#endif
#endif
Expand All @@ -160,9 +163,12 @@ SUBROUTINE init_dim_obs_pdaf(step, dim_obs_p)
integer :: ierror
INTEGER :: max_var_id ! Multi-scale DA
INTEGER :: sum_dim_obs_p
INTEGER :: p ! CLM Patch index
INTEGER :: c ! CLM Column index
INTEGER :: g ! CLM Gridcell index
INTEGER :: cg
INTEGER :: pg
INTEGER :: pc
INTEGER :: i,j,k ! Counters
INTEGER :: cnt ! Counters
INTEGER :: cnt_interp ! Counter for interpolation grid cells
Expand Down Expand Up @@ -928,6 +934,8 @@ SUBROUTINE init_dim_obs_pdaf(step, dim_obs_p)
do i = 1, dim_obs
obs(i) = clm_obs(i)

if(clmupdate_swc==1) then

do g = begg,endg
newgridcell = .true.

Expand Down Expand Up @@ -998,9 +1006,101 @@ SUBROUTINE init_dim_obs_pdaf(step, dim_obs_p)
end if

end if

end do
end do

else if(clmupdate_T==1 .or. clmupdate_T==2 .or. clmupdate_T==3) then
#ifdef CLMFIVE
! patch loop
do g = begg,endg
newgridcell = .true.

do p = begp,endp

pg = patch%gridcell(p)
pc = patch%column(p)
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pc = patch%column(p) is assigned but never used in this new clmupdate_T observation-indexing branch. Please remove the unused variable/assignment to avoid warnings and keep the code focused.

Suggested change
pc = patch%column(p)

Copilot uses AI. Check for mistakes.

if(pg == g) then
if(newgridcell) then
! Sets first patch/column in a gridcell. TODO: Make
! patch / column information part of the observation
! file

if(is_use_dr) then
deltax = abs(lon(g)-clmobs_lon(i))
deltay = abs(lat(g)-clmobs_lat(i))
end if

if(((is_use_dr).and.(deltax<=clmobs_dr(1)).and.(deltay<=clmobs_dr(2))).or. &
((.not. is_use_dr).and.(longxy_obs(i) == longxy(g-begg+1)) .and. (latixy_obs(i) == latixy(g-begg+1)))) then

! Set index in state vector, LST will be computed
! for first patch appearing here
obs_index_p(cnt) = state_clm2pdaf_p(p,1)

!write(*,*) 'obs_index_p(',cnt,') is',obs_index_p(cnt)
obs_p(cnt) = clm_obs(i)
if(multierr==1) clm_obserr_p(cnt) = clm_obserr(i)
cnt = cnt + 1
Comment on lines +1037 to +1044
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For clmupdate_T==1 the state vector appears to be patch-based, but this branch picks only the first patch per gridcell and assigns obs_index_p for that single patch. This likely leaves many patches never observed/updated. Please clarify the intended domain (gridcell vs patch) for update_T==1 and adjust observation indexing accordingly (e.g., use a gridcell-mean state layout, or generate indices for all relevant patches).

Copilot uses AI. Check for mistakes.

end if

newgridcell = .false.

end if
end if

end do
end do
#else
! gridcell loop
do g = begg,endg

if(is_use_dr) then
deltax = abs(lon(g)-clmobs_lon(i))
deltay = abs(lat(g)-clmobs_lat(i))
end if

if(((is_use_dr).and.(deltax<=clmobs_dr(1)).and.(deltay<=clmobs_dr(2))).or. &
((.not. is_use_dr).and.(longxy_obs(i) == longxy(g-begg+1)) .and. (latixy_obs(i) == latixy(g-begg+1)))) then
obs_index_p(cnt) = g-begg+1
end if

!write(*,*) 'obs_index_p(',cnt,') is',obs_index_p(cnt)
obs_p(cnt) = clm_obs(i)
if(multierr==1) clm_obserr_p(cnt) = clm_obserr(i)
cnt = cnt + 1

end do
#endif
else

print *, "TSMP-PDAF mype(w)=", mype_world, ": WARNING unsupported update in setting obs_index_p."
print *, "TSMP-PDAF mype(w)=", mype_world, ": WARNING using default gridcell loop."
! call abort_parallel()

! gridcell loop with layer information as default!
do g = begg,endg

if(is_use_dr) then
deltax = abs(lon(g)-clmobs_lon(i))
deltay = abs(lat(g)-clmobs_lat(i))
end if

if(((is_use_dr).and.(deltax<=clmobs_dr(1)).and.(deltay<=clmobs_dr(2))).or. &
((.not. is_use_dr).and.(longxy_obs(i) == longxy(g-begg+1)) .and. (latixy_obs(i) == latixy(g-begg+1)))) then
obs_index_p(cnt) = g-begg+1 + ((endg-begg+1) * (clmobs_layer(i)-1))
end if

!write(*,*) 'obs_index_p(',cnt,') is',obs_index_p(cnt)
obs_p(cnt) = clm_obs(i)
if(multierr==1) clm_obserr_p(cnt) = clm_obserr(i)
cnt = cnt + 1

end do

end if

end do

if(obs_interp_switch==1) then
Expand Down
1 change: 0 additions & 1 deletion interface/framework/localize_covar_pdaf.F90
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ SUBROUTINE localize_covar_pdaf(dim_p, dim_obs, HP, HPH)
USE shr_kind_mod , only : r8 => shr_kind_r8
USE mod_read_obs, ONLY: clmobs_lon
USE mod_read_obs, ONLY: clmobs_lat
USE enkf_clm_mod, ONLY: clmupdate_T
USE enkf_clm_mod, ONLY: clm_begc
USE enkf_clm_mod, ONLY: clm_endc
USE enkf_clm_mod, ONLY: state_pdaf2clm_c_p
Expand Down
Loading