earth-system-radiation / pyRTE-RRTMGP

pyRTE-RRTMGP provides a Python interface to the RTE+RRTMGP Fortran software package
https://pyrte-rrtmgp.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
6 stars 0 forks source link

Python front end - encapsulating variables #31

Open RobertPincus opened 4 months ago

RobertPincus commented 4 months ago

In thinking about a user-friendly Python front end we think about how well the Fortran design would work.

Specifying problems

Radiation problems are defined in one of three ways - three sets of "optical properties". Using the Fortran notation:

Each variable might depend on a vertical coordinate (ilay) and/or a spectral coordinate (igpt); p also has a "moment" dimension (imom). The underlying kernels all operate on more than one profile at a time (icol) as the first dimension.

Planets receive radiation from their host star and emit radiation themselves. For the purposes of gas optics we treat these as distinct, so we have two gas optics variants:

Solving problems

The solvers depend on whether the sources are internal (lw for longwave) or external (sw for shortwave). Each takes a set of optical properties (e.g. noscat solvers use 1scl optical properties) and the corresponding set (int/ext) of source functions.

In practice shortwave problems always use a two-stream specification, while longwave solvers might use either a scalar or a two-stream specification.

Encapsulation

In the Fortran interface there is one class grouping together the optical properties and a second grouping together the longwave source functions; the shortwave source is passed around as a plain array. Using classes makes for a smaller number of variables and ensures the right grouping.

Does this approach make sense in Python too?

RobertPincus commented 4 months ago

We discussed this at a group meeting recently.

Conceptually we can think of each problem as having optical properties, sources (of radiation), and boundary conditions. The combination of optical properties and sources defines the kind of problem to be solved; it's also what comes back from the gas optics. Users will specify boundary conditions.

We could distinguish these, e.g.

gas_optics = rrtmgp.GasOptics(SCHEME_DATA)
atmosphere = xr.open_dataset(SOME_FILE) # temperature, pressure, gas concentrations 
optical_props, sources = gas_optics.gas_optics(atmosphere) 
bcs = atmosphere # with agreed names for surface properties, say
flux_up, flux_dn = rte.solve(optical_props, sources, bcs) 

This makes everything clear. One clear disadvantage is that the optical_props, sources, and boundary conditions share coordinates in space and along the spectrum. To me that suggests putting them in the same dataset:

gas_optics = rrtmgp.GasOptics(SCHEME_DATA)
atmosphere = xr.open_dataset(SOME_FILE) # temperature, pressure, gas concentrations 
problem = gas_optics.gas_optics(atmosphere) 
problem["surface_emissivity"]  = 1.
flux_up, flux_dn = rte.solve(problem) 

This is conceptually less clear and relies (more?) on conventions for arrays within datasets.

brendancol commented 3 weeks ago

@tcmetzger these are some nice "spelling" examples for us to implement

RobertPincus commented 3 weeks ago

@tcmetzger @sehnem One thought after discussions with @brendancol earlier today: optical properties and source functions both have a spectral dimension/discretization. In general, there are spectral "bands" that cover particular frequencies/wavelengths (and have starting and ending values of those coordinates); for the gas optics there may also be "g-points" within each band that aren't ordered in any particular way.

This comes up when creating a problem definition/set of optical properties from multiple components in the atmosphere (like gases and clouds) - not only do we have to ensure that the discretizations are the same but we need the ability to broadcasts values defined on bands to each g-point within the band. Like this, if we use a single dataset for the whole problem:

gas_optics = rrtmgp.GasOptics(SCHEME_DATA)
cloud_optics = rrtmgp.CloudOptics(MORE_SCHEME_DATA)

atmosphere = xr.open_dataset(SOME_FILE) # temperature, pressure, gas concentrations 
clouds = xr.open_dataset(SOME_OTHER_FILE) # cloud properties 

problem = gas_optics.gas_optics(atmosphere) 
# "problem" is defined on g-points 
cloud_optics(clouds).add_to(problem)  # or maybe problem += cloud_optics(clouds) 
#cloud_optics(clouds) is defined on bands 
problem["surface_emissivity"]  = 1.

flux_up, flux_dn = rte.solve(problem)