ccpp-framework icon indicating copy to clipboard operation
ccpp-framework copied to clipboard

Errors when integer variable is output from one phase and used as an interstitial dimension in another

Open peverwhee opened this issue 3 months ago • 1 comments

Description

When a dimension is output from the init or register phase of a scheme and then used as a dimension of an interstitial variable for the run phase of that same scheme or another, you get this build error:

Cannot find variable for dimension, <dimension var>, of <interstitial variable>, at...
Found <dimension var> from excluded source, 'suite', at...

Steps to Reproduce

In the capgen_test, make the following modifications:

  1. Add an integer output variable to temp_adjust_init Fortran:
 subroutine temp_adjust_init (dim_inter, errmsg, errflg)
   integer, intent(out) :: dim_inter
     character(len=512),      intent(out)   :: errmsg
     integer,                 intent(out)   :: errflg

     errmsg = ''
     errflg = 0
     dim_inter = 3
   end subroutine temp_adjust_init

metadata addition:

[ dim_inter ]
  standard_name = dimension_for_interstitial_variable
  units = count
  dimensions = ()
  type = integer
  intent = out
  1. Add a new interstitial, intent(out) variable to the temp_adjust_run:

Fortran:

  subroutine temp_adjust_run(foo, timestep, interstitial_var, temp_prev, temp_layer, qv, ps,    &
       to_promote, promote_pcnst, errmsg, errflg, innie, outie, optsie)

    integer,                   intent(in)    :: foo
    real(kind_phys),           intent(in)    :: timestep
    real(kind_phys),           intent(inout),optional :: qv(:)
    real(kind_phys),           intent(inout) :: ps(:)
    REAL(kind_phys),           intent(in)    :: temp_prev(:)
    REAL(kind_phys),           intent(inout) :: temp_layer(foo)
    real(kind_phys),           intent(in)    :: to_promote(:)
    real(kind_phys),           intent(in)    :: promote_pcnst(:)
    integer,                   intent(out)   :: interstitial_var(:)
    character(len=512),        intent(out)   :: errmsg
    integer,                   intent(out)   :: errflg
    real(kind_phys), optional, intent(in)    :: innie
    real(kind_phys), optional, intent(out)   :: outie
    real(kind_phys), optional, intent(inout) :: optsie
    !----------------------------------------------------------------

    integer :: col_index

    errmsg = ''
    errflg = 0

    interstitial_var = 3
    if (size(interstitial_var) /= 3) then
       errflg = 1
       errmsg = 'interstitial variable not allocated properly!'
    end if

    if (.not. module_level_config) then
       ! do nothing
       return
    end if

    do col_index = 1, foo
       temp_layer(col_index) = temp_layer(col_index) + temp_prev(col_index)
       if (present(qv)) qv(col_index) = qv(col_index) + 1.0_kind_phys
    end do
    if (present(innie) .and. present(outie) .and. present(optsie)) then
       outie = innie * optsie
       optsie = optsie + 1.0_kind_phys
    end if

  END SUBROUTINE temp_adjust_run

Metadata addition:

[ interstitial_var ]
  standard_name = output_only_interstitial_variable
  units = 1
  dimensions = (dimension_for_interstitial_variable)
  type = integer
  intent = out
  1. Run the tests and observe the error.

peverwhee avatar Aug 28 '25 21:08 peverwhee

@gold2718 @climbfuji @dustinswales

I have a question about desired functionality, friends!

Currently, in suite_objects.py (Group.manage_variable), we are excluding local variable types when we try to find the dimensions for a variable in the calling tree (~line 2340):

# Finally, make sure all dimensions are accounted for
        emsg = self.add_variable_dimensions(local_var, _API_LOCAL_VAR_TYPES,
                                            adjust_intent=True,
                                            to_dict=self.call_list)

In this case, _API_LOCAL_VAR_TYPES is the list of types to exclude from the search (which is "suite" and "local").

My assumption is that we're excluding these types because we aren't sure if these variables have been initialized, so using them as dimensions is potentially problematic (I also imagine that before the register phase existed it didn't make sense to have allow suite-level variables to be dimensions).

My question is: do we wanted lessen this restriction (i.e allow dimensions to be suite-level variables) to allow for variables that are initialized in the register phase to be used to allocate variables in the init phase.

My thinking is that this will be useful for schemes that are reading in dimensions from a file and using those to allocate interstitial variables. For situations when the variable has not been properly initialized, Fortran should throw an error at runtime anyway.

Thoughts?

PS. I haven't actually figured out how to fully implement this, but thought I'd ask for feedback before burrowing further down this 🐰🕳️

peverwhee avatar Sep 02 '25 17:09 peverwhee