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

FATES-HOST parameter API #156

Closed
bandre-ucar opened this issue Dec 6, 2016 · 13 comments
Closed

FATES-HOST parameter API #156

bandre-ucar opened this issue Dec 6, 2016 · 13 comments

Comments

@bandre-ucar
Copy link
Contributor

bandre-ucar commented Dec 6, 2016

Summary of Issue:

Define and implement a fortran API for fates to exchange parameter information with the host model.

Requirements and design considerations:

Summary of API v0.1

  • Fates parameter type:
    • keep life simple initially by just having a single parameter type for all parameters.
      • Have integer and real data fields, only allocate the one that corresponds to the
      • Create a data object up to N=3 max dimensions,
        • scalar - allocate data as (1,1,1)
        • 1d array - allocate data as (C, 1, 1)
        • 2d array - allocate data as (A, B, 1)
type :: fates_parameters_type
    character() :: short_name
    character() :: numeric_type
    character() :: units
    integer :: num_dims
    integer :: size_dims(:) ! length num_dims
    real(r8), allocatable :: real_data(:, :, :) ! for now, assume 3D max data dimensions.
    integer, allocatable :: integer_data(:, :, :) ! for now, assume 3D max data dimensions.
  contains
    procedure :: Init()
    procedure :: Allocate() ! allocate the data for the appropriate numeric type
end type fates_parameter_type

! make this a linked list instead of an array?
type(fates_parameters_type), allocatable :: fates_params(:)

  • fates_register_parameters_for_hlm(fates_params)

    • Create an object for each parameter fates wants the host to read
    • Can be done in a central routine at first, but eventually consider moving everything to the smallest scope possible by having each fates module store it’s own parameters.
    • Returns an array of parameters
  • HLM loops over the array of fates parameters, extracts data from netcdf file using host infrastructure.

  • fates_receive_parameters_from_hlm(fates_params)

    • Pass fates_params to each module, extract data from the derived type, assigns it to fates internal storage.
    • Need to think more about whether to have a single central loop over parameters, or loop over array multiple times in different modules….

Depends on:

#155

@rgknox
Copy link
Contributor

rgknox commented Dec 6, 2016

This looks great @bandre-ucar, thanks for kicking this off. Some initial thoughts:

  1. I am trying to imagine a 3D parameter, I suppose it is possible, but it seems unlikely no? I can't even really think of many 2D parameters. I don't have strong feelings about this honestly, I suppose allocating for up to 3 dimensions does some future proofing.

  2. What are your thoughts about associating the data arrays in each object to unique variable name, or way to look it up in Fates internal routines? For instance, in history and restarts, we assign a unique index identifier to each variable (such as the long list of "ih_" and "ir_" integers at the top of those modules). Maybe for parameters we can create a global alias mapping. ie: vcmax25 => fates_params(ip_vcmax25)%real_data(:,:,:)?

Following up on question 2. When the parameter ultimately gets interpreted in our physics routines, I think it is important that the user does not see the extra dimensions for the variable, its confusing. Using the vcmax parameter as an example again: However it is handled, lets avoid the user from seeing something like vcmax25(ft,1,1). I think there are various ways to achieve this.

Regarding centralization and de-centralization of transferring the object list to fates internal memory. If I am interpreting this correctly, it seems like the question is whether to have separate object lists for different groupings of parameters, right? My opinion is that we are fine storing these parameters in one list for now.

Need to think more about whether to have a single central loop over parameters, or loop over array multiple times in different modules….

I'm not sure I'm clear on this one. Which array is being looped over multiple times, the fates_parameter_type structure, or something else?

@bandre-ucar
Copy link
Contributor Author

  1. If there is no foreseeable need for anything beyond scalar and 1-D, that's fine. Using OOP to deal with a bunch of complicated special types and duplicate interfaces to accommodate strong typing feels like overkill for what we need. Putting everything into a single general purpose container just keeps life simpler right now.

  2. We just use the fates_parameters_type data structure to shuttle information between the host and fates. See bullet one infates_receive_parameters_from_hlm:

    Pass fates_params to each module, extract data from the derived type, assigns it to fates internal storage.

    We copy the data out of the generic interface storage into the existing storage as a one time operation during initialization. No sense in adding the complication of trying to keep everything in a single array and using index mappings and aliases. This also keeps all the science code the same, no need to worry about the science code dealing with extra dimensions.

  3. Central loop over parameters - I think we want the parameters to be a linked list instead of an array so we can avoid keeping track of the global number of parameters that fates has and a bunch of indices (for each thread?) that only get used once. If we do that, we can use:

    • central loop pseudo code:
      def fates_receive_parameters_from_hlm(parameters):
          for p in parameters:
              call fates_receive_science_parameters_foo(p)
              call fates_receive_science_parameters_bar(p)
      
      then the science modules look something like:
      def fates_receive_science_parameters_foo(parameter):
          if parameter.name == 'foo1':
              foo1 = parameter.real_data(:)
          elif parameter.name == 'foo2':
              foo2 = parameter.integer_data(:)
      
    • multi-loop pseudo code:
       def fates_receive_parameters_from_hlm(parameters):
           call fates_receive_science_parameters_foo(parameters)
           call fates_receive_science_parameters_bar(parameters)
      
      with the science modules:
      def fates_receive_science_parameters_foo(parameters)
           for p in parameters:
               if p.name == 'foo1':
                   foo1 = p.real_data(:)
               elif p.name == 'foo2'
                  foo2 = p.integer_data(:)
      

@bchristo
Copy link

bchristo commented Dec 7, 2016 via email

@ckoven
Copy link
Contributor

ckoven commented Dec 7, 2016

brad, I think the question here is the dimensionality of the input parameters, not the history outputs. though i completely agree that we ought to try to get support for 3d output -- which requires some lower level ncio changes -- too.

@rosiealice
Copy link
Contributor

rosiealice commented Dec 7, 2016 via email

@bchristo
Copy link

bchristo commented Dec 7, 2016 via email

@ckoven
Copy link
Contributor

ckoven commented Dec 7, 2016

Re the question of spatially explicit veg parameters, it seems like the main near-term one that we'd want is the initial cold-start number density (so that we could, e.g., have separate biogeographic provinces in a single run). Is that most eaaily accomplished via the PFT parameter file or a surface parameter dataset?

@mdietze
Copy link
Collaborator

mdietze commented Dec 7, 2016

I'd advocate for keeping initial conditions out of the parameter file

@bandre-ucar
Copy link
Contributor Author

My understanding is that CLM classifies input data roughly as:

  • parameters - basic universal scalars, pft arrays, etc.
  • surface data sets - spatially variable parameters.
  • land use time series or streams - temporally variable forcing / parameters.
  • initial conditions

A multi-dimensional parameter would be something like a 2D look-up table where a parameter varies with state in a non-analytical way.

We have negotiated with CLM to modify how the parameters are handled. We haven't had any discussions with CLM about changing handling for surface data sets and initial conditions. Those are different infrastructure and a completely different scope of work. If people want to make those changes, we need to start the discussion with CLM sooner rather than later.

@ckoven
Copy link
Contributor

ckoven commented Dec 7, 2016

To be clear, I am specifically referring to the PFT parameter "initd". This is configured as a pft-vector parameter, but in fact is actually a cold-start initial condition. This is the only thing I can imagine wanting to be geographically dependent any time soon. The reason we'd want this to vary geographically is to allow certain PFTs to exist in one place but not another.

@ckoven
Copy link
Contributor

ckoven commented Dec 8, 2016

thinking about this some more, I think we should just proceed with supporting only the currently-required parameter data shapes for now. we can come back to the initd issue at some later date via some sort of surface-dataset override of the PFT parameter. We'd only need that capability when/if we get to the point of having separate biogeographic provinces running in a pantropical model, and the complexity of having to support different geographic resolutions in a pft file seems like something to avoid at all costs.

@rosiealice
Copy link
Contributor

rosiealice commented Dec 8, 2016 via email

bandre-ucar added a commit that referenced this issue Mar 15, 2017
Merge remote-tracking branch 'pr/andre-ed-params'

Introduce an interface to pass parameter information from the host to
fates. Fates registers a set of parameters that it needs read in, and
indicates if they are fates only parameters, or need to be synced with
values from the host. The host reads the parameters and returns them
to fates.

This refactor attempted to be as minimally invasive as possible to the
fates science code. All existing storage and conventions for fates
parameters were left in place. The only exception was the
consolidation of all pft dimensioned parameters into the EDpftvarcon
type. Fates no longer uses variables from the host pftcon type.

This introduces dynamic allocation of pft level parameter in
preparation for setting the number of pfts at run time, but still
requires a hard coded number of pfts until the host code can be
modified.

Note that the default clm and old clm-ed parameter files have diverged
before this work began. To do this development in a bit for bit way,
the clm-ed parameter file was updated to agree with the clm default
parameter file. This is answer changing compared to the fates master
branch, but code was refactored in a bit for bit way. No netcdf
variables were added or removed in this PR.

Fixes: #155, #156

User interface changes?: Yes.

1. Users will need to update custom parameter files. This introduces a
new namelist variable, fates_paramfile. The fates parameters are
**always** read from the netcdf file pointed to by
fates_paramfile. All host parameters are **always** read from the
netcdf file pointed to by paramfile. The host paramfile and fates
paramfile **may** point to the same netcdf file.

2. All fates parameters and dimensions are now name spaced by
'fates_'. The variable names have remained the same, but the dimension
'param' is now 'fates_scalar'. A new default parameter file with the
required changes is available. See fates_params.c170308.nc in the
input data repo.

Code review: self. Discussion with clm-cmt, code walk through with
ckoven, rgknox, rosie

Testing:

  andre: 2017-03-14

    Test suite: ed - yellowstone gnu, intel, pgi
                     hobart nag
    Test baseline: 0ea3fed
    Test namelist changes: addition of fates_paramfile
    Test answer changes: bit for bit
    Test status: all tests pass

    Test suite: clm_short - yellowstone gnu, intel, pgi
    Test baseline: clm4_5_12_r195
    Test namelist changes: none
    Test answer changes: bit for bit
    Test status: all tests pass
@rgknox
Copy link
Contributor

rgknox commented Aug 15, 2017

I think we can close this right? @bandre-ucar gave us a smashing API on this front.

@rgknox rgknox closed this as completed Aug 15, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants