Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new "register" phase #572

Open
peverwhee opened this issue Jun 21, 2024 · 1 comment
Open

Add new "register" phase #572

peverwhee opened this issue Jun 21, 2024 · 1 comment

Comments

@peverwhee
Copy link
Collaborator

Description

In addition to "init," "timestep_init", "run," "timestep_final," and "final", there will be a "register" phase that will be called by the host prior to grid set-up.

Solution

The register phase will be implemented as any other phase, with the bonus of also handling any run-time constituents for a scheme (this will replace the existing dyn_const_routine field in the metadata header).

@peverwhee peverwhee self-assigned this Jun 21, 2024
@dustinswales
Copy link
Collaborator

@peverwhee This is a great idea and there is a need for such a phase for NOAA applications.
A few examples come to mind where this would be useful.

  • For physics suites that have groups called outside of the physics loop (i.e. from the dynamical core), we need to be able to initialize this group on the host side before both the physics and dynamical core. @yangfanglin @RuiyuSun
  • There are some physics schemes that require dynamic allocation of interstitials (e.g NRL photochemistry, RRTMGP, MG-MP). These schemes all have a run-time data component that needs to be known so that the interstitials can be allocated properly.

climbfuji pushed a commit that referenced this issue Oct 31, 2024
## Overview
This PR adds a new phase, *register*, that can be called by a host model
and used by schemes to perform any set up that needs to happen BEFORE
the grid is established.

NOTE: this PR also *removes* the old `dynamic_constituent_routine`
metadata implementation for runtime constituents.

## Description
I have implemented it as an "optional" phase, by which I mean that it is
not required that a host model call this phase (though I'm happy to be
overruled!). As a result, the register phase does not change the CCPP
"state" (but will produce an error if it is called after the `init`
phase).

More:
### Dynamic/run-time constituent handling:
- If a scheme has run-time constituents, those shall be allocated,
instantiated, and returned from the scheme's register phase. This
metadata is required (the framework determines that there are runtime
constituents from a scheme if there is a `ccpp_constituent_properties_t`
variable required):
```
[ <unique dynamic constituent local name> ]
  standard_name = <some unique standard name>
  dimensions = (:)
  type = ccpp_constituent_properties_t
  intent = out
  allocatable = true
```
- The standard name doesn't really matter but MUST be different from
other runtime constituent standard names in the scheme; it may be
easiest to standardize this to something like
`dynamic_constituents_for_<scheme>`
- The framework will then compile all scheme constituents into
module-level variables in the host cap called
`<suite>_dynamic_constituents`, which are then used to pack and
initialize the module level constituents object
`<host>_constituents_obj`.
- If there are no dynamic constituents registered by any schemes within
a suite, that suite's dynamic constituents array is allocated to 0.

*Generated host cap code examples*
1. Multiple schemes have dynamic constituents:
```
subroutine test_host_ccpp_physics_register(suite_name, errmsg, errflg)

      use ccpp_cld_suite_cap, only: cld_suite_register

      character(len=*)                         :: suite_name
      character(len=512)                       :: errmsg
      integer                                  :: errflg
      type(ccpp_constituent_properties_t),allocatable          :: dyn_const(:)
      type(ccpp_constituent_properties_t),allocatable          :: dyn_const_ice(:)
      integer                                  :: num_dyn_consts
      integer                                  :: const_index

      errflg = 0
      errmsg = ""
      if (trim(suite_name) == 'cld_suite') then
         call cld_suite_register(errflg=errflg, errmsg=errmsg, dyn_const=dyn_const,               &
              dyn_const_ice=dyn_const_ice)
         allocate(cld_suite_dynamic_constituents(0+size(dyn_const)+size(dyn_const_ice)))
         ! Pack the suite-level dynamic, run-time constituents array
         num_dyn_consts = 0
         do const_index = 1, size(dyn_const)
            cld_suite_dynamic_constituents(num_dyn_consts + const_index) = dyn_const(const_index)
         end do
         num_dyn_consts = num_dyn_consts + size(dyn_const)
         deallocate(dyn_const)
         do const_index = 1, size(cld_suite_dynamic_constituents)
            call cld_suite_dynamic_constituents(const_index)%standard_name(stdname,               &
                 errcode=errflg, errmsg=errmsg)
         end do
         do const_index = 1, size(dyn_const_ice)
            cld_suite_dynamic_constituents(num_dyn_consts + const_index) =                        &
                 dyn_const_ice(const_index)
         end do
         num_dyn_consts = num_dyn_consts + size(dyn_const_ice)
         deallocate(dyn_const_ice)
      else
         write(errmsg, '(3a)')"No suite named ", trim(suite_name), "found"
         errflg = 1
      end if

   end subroutine test_host_ccpp_physics_register
```
2.  No schemes have dynamic constituents:
```
subroutine test_host_ccpp_physics_register(suite_name, errmsg, errflg)

      use ccpp_ddt_suite_cap,  only: ddt_suite_register
      use ccpp_temp_suite_cap, only: temp_suite_register

      character(len=*)                         :: suite_name
      character(len=512)                       :: errmsg
      integer                                  :: errflg

      errflg = 0
      errmsg = ""
      if (trim(suite_name) == 'ddt_suite') then
         call ddt_suite_register(errflg=errflg, errmsg=errmsg)
         ! Suite does not return dynamic constituents; allocate to zero
         allocate(ddt_suite_dynamic_constituents(0))
      else if (trim(suite_name) == 'temp_suite') then
         call temp_suite_register(errflg=errflg, errmsg=errmsg, config_var=config_var)
         ! Suite does not return dynamic constituents; allocate to zero
         allocate(temp_suite_dynamic_constituents(0))
      else
         write(errmsg, '(3a)')"No suite named ", trim(suite_name), "found"
         errflg = 1
      end if

   end subroutine test_host_ccpp_physics_register
```

### Misc notes
Since this phase is called before the grid is initialized, variables are
not allocated at this time (that still happens in `init`) and no
variables with horizontal and vertical dimensions can be passed in.

## UI Changes
User interface changes?: Yes, but they're optional
If a host model wishes to utilize schemes' register phases, they must
add a call to `<host_model>_ccpp_physics_register(suite_name, errmsg,
errflg)`

## Testing
test removed: removed unit tests for dyn_const_routines (old
implementation of runtime constituent handling) - all pass
unit tests: Removed old dynamic constituents testing - all pass
system tests: Updated capgen and advection tests to include register
phases (with and without dynamic constituents)
- Also updated advection test CMakeLists to first run a version with
dynamic constituents in the wrong phase and have an expected error
- This is perhaps not the best way to test this, but it's what I came up
with
manual testing:

Fixes:
closes #572

---------

Co-authored-by: Courtney Peverley <[email protected]>
Co-authored-by: Courtney Peverley <[email protected]>
Co-authored-by: Courtney Peverley <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants