Skip to content

Refactor boundary condition interface structure #1477

Open
glemieux wants to merge 332 commits intoNGEET:mainfrom
glemieux:bc-refactor-patchindexing
Open

Refactor boundary condition interface structure #1477
glemieux wants to merge 332 commits intoNGEET:mainfrom
glemieux:bc-refactor-patchindexing

Conversation

@glemieux
Copy link
Copy Markdown
Contributor

@glemieux glemieux commented Sep 30, 2025

Initial refactor of the FATES interface design

Description:

This pull request refactors the HLM-FATES interface design as an initial step towards enabling "dynamic multi-column" FATES runs for NGEE-Arctic cross cut task 4. In this regard, this redesign is intended to address design assumptions in which all FATES patches on a given site are associated with a single column. Given that this redesign will touch on a large amount of code (on both sides), this PR is intended as a minimum-viable case that introduces the new design setup and only refactors a subset of boundary condition variables to prove the concept.

Given the scope of the undertaking, the refactor is also used as an opportunity to introduce a standard method of passing variable data across the HLM-FATES interface and introducing a common vocabulary for associated variables. The intent is to minimize the amount of necessary shared code between FATES and the HLM.

This pull request must be coordinated with E3SM-Project/E3SM#7482 and ESCOMP/CTSM#3705.

For more details, wee NGEET/fates-users-guide#109 for updates to the developer's guide section detail the interface design document (work in progress)

Collaborators:

@rgknox @ckoven

Expectation of Answer Changes:

Checklist

If this is your first time contributing, please read the CONTRIBUTING document.

All checklist items must be checked to enable merging this pull request:

Contributor

  • The in-code documentation has been updated with descriptive comments
  • The documentation has been assessed to determine if updates are necessary

Integrator

  • FATES PASS/FAIL regression tests were run
  • Evaluation of test results for answer changes was performed and results provided
  • FATES-CLM6 Code Freeze: satellite phenology regression tests are b4b

If satellite phenology regressions are not b4b, please hold merge and notify the FATES development team.

Documentation

Test Results:

CTSM (or) E3SM (specify which) test hash-tag:

CTSM (or) E3SM (specify which) baseline hash-tag:

FATES baseline hash-tag:

Test Output:

Technically we don't really need a subgrid_indices array for the patch-side currently.
…o the api registry type

This will help next development steps in which the update of certain variables will be conducted prior to others
@glemieux glemieux marked this pull request as ready for review January 28, 2026 17:36
@glemieux glemieux moved this to Finding Reviewers in FATES Pull Request Planning and Status Jan 28, 2026
type(fates_interface_variable_type), allocatable :: fates_vars(:)

! Array of keys associated with the interface variables
character(len=48), allocatable, private :: key(:)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

bump this up in case we want long keys in the future?

! Is registry have a FATES patch that exists?
logical, private :: active

! Container array of interface variables indexed by key
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do the indices of hlm_vars and fates_vars have correspondence with each other? If so, lets note that here in the description

integer :: num_api_vars_update_init_dims ! number of variables dimensions needed during initialization
integer :: num_api_vars_update_init ! number of variables that update only at initialization
integer :: num_api_vars_update_daily ! number of variables that update daily
integer :: num_api_vars_update_timestep ! number of variables that update on the model timestep
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We have variables that need to update at different times during the model time-step. If these transfers need to happen at different times, how can we group them into one "timestep" group?

Comment thread main/EDMainMod.F90 Outdated
currentPatch => currentSite%youngest_patch
do while(associated(currentPatch))

if (currentPatch%nocomp_pft_label .ne. nocomp_bareground) then
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Was this needed for consistency, speed, removing spurious behavior?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This was for spurious behavior, but might actually be defunct code that should be removed per recent development. I'll double check this.

currentPatch => this%sites(s)%oldest_patch
do while (associated(currentPatch))

if (currentPatch%nocomp_pft_label .ne. nocomp_bareground) then
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think one of our plans was to get rid of these conditionals by implementing that refactor that removes bareground patches before this, right? Are we punting on that?

Copy link
Copy Markdown
Contributor Author

@glemieux glemieux Feb 25, 2026

Choose a reason for hiding this comment

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

For now. We need this in place until we remove the code that instantiates a fates bareground patch, which is a more significant refactor. That said, we'll need to deal with it during the future multi-column fates pull request.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll add a comment above the check noting this.

do r = 1, this%npatches

! Get the gridcell index
g = this%registry(r)%GetGridcellIndex()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think technically, we match our fates sites with land-units now right? ELM could have multiple topo-units, each with a natural vegetation land-unit that we would want to match with. Matching at the LU level in CLM would also work.

Copy link
Copy Markdown
Contributor

@ckoven ckoven left a comment

Choose a reason for hiding this comment

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

Thanks @glemieux, this all looks good to me, only minor comments on first reading. Will continue to think some more about how this all works.

Comment on lines +392 to +394
subroutine SetLastState(this, last_state)

! This procedure sets the last state logical metadata for the calling interface variable
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not following (yet) how or why the is_last logic is used in practice. maybe some further details on that would be helpful here.

Comment on lines +1334 to +1345
! Update the litter flux counters not including the total flux counter
if (key == hlm_fates_litter_carbon_cellulose .or. &
key == hlm_fates_litter_carbon_labile .or. &
key == hlm_fates_litter_carbon_lignin .or. &
key == hlm_fates_litter_nitrogen_cellulose .or. &
key == hlm_fates_litter_nitrogen_labile .or. &
key == hlm_fates_litter_nitrogen_lignin .or. &
key == hlm_fates_litter_phosphorus_cellulose .or. &
key == hlm_fates_litter_phosphorus_labile .or. &
key == hlm_fates_litter_phosphorus_lignin) then
this%num_api_vars_litter_flux= this%num_api_vars_litter_flux + 1
end if
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I guess an alternative, which might be easier to maintain, would be to make an 'is_litter_flux' or something like that boolean flag for all interface variables rather than enumerating all the variables here?

Comment on lines +1603 to +1615
! Litter flux update
if (this%key(index) == hlm_fates_litter_carbon_cellulose .or. &
this%key(index) == hlm_fates_litter_nitrogen_cellulose .or. &
this%key(index) == hlm_fates_litter_phosphorus_cellulose .or. &
this%key(index) == hlm_fates_litter_carbon_labile .or. &
this%key(index) == hlm_fates_litter_nitrogen_labile .or. &
this%key(index) == hlm_fates_litter_phosphorus_labile .or. &
this%key(index) == hlm_fates_litter_carbon_lignin .or. &
this%key(index) == hlm_fates_litter_nitrogen_lignin .or. &
this%key(index) == hlm_fates_litter_phosphorus_lignin) then
count_litter_flux = count_litter_flux + 1
this%filter_litter_flux(count_litter_flux) = index
end if
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

similar comment to the above -- having a boolean flag attached to the variables might be easier to maintain than enumerating them here?

use shr_infnan_mod , only : nan => shr_infnan_nan, assignment(=)
use PRTGenericMod , only : prt_cnp_flex_allom_hyp
use FatesInterfaceVariableTypeMod, only : fates_interface_variable_type
use FatesInterfaceParametersMod
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think I agree that it makes sense to do an unlimited use statement rather than enumerating every possible interface variable name in the list of only variables, which might get quite long, but I guess the risk is that a reader of the code sees all the string indices in DefineInterfaceRegistry and it isn't obvious where those all come from. So I wonder if it would make sense to only have the open-ended use statement in that subroutine and restrict it to use only the integer parameters throughout the rest of the module?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

an alternative might be to give the string keys names something that is more obvious what they are, like fates_interface_var_longname?

Comment thread main/FatesInterfaceMod.F90
Comment thread main/FatesInterfaceMod.F90
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Under Review

Development

Successfully merging this pull request may close these issues.

3 participants