Interacting with climt

As we saw in the Quickstart section, climt has two kinds of entities for the user to interact with: model components and model state. Here, we will take a closer look at both these elements.

Model State

The model state is a dictionary whose keys are names of quantities and values are sympl DataArrays. The sympl DataArray is a thin wrapper over the xarray DataArray that makes it units aware. To ensure model scripts are readable not just by specialists, names of quantities use the descriptive CF Convention. Only in the case where the CF Convention names are really unwieldy, like air_temperature_at_effective_cloud_top_defined_by_infrared_radiation for example, we use more convenient names.

DataArrays are a more human-friendly way of handling numerical arrays. DataArrays label the dimensions of an array and provide various mathematical functions which can be directly applied to arrays. sympl DataArrays in addition allow conversion between units, a feature required to allow interoperability between components which expect inputs in different units.

Let’s create a 3-d model state to see how useful DataArrays are:

In [1]: import climt

In [2]: import matplotlib.pyplot as plt

In [3]: import numpy as np

# Create some components
In [4]: radiation = climt.GrayLongwaveRadiation()

In [5]: convection = climt.DryConvectiveAdjustment()

We need to tell climt what the model dimensions are. This is done by the get_grid function. This function takes three arguments which are the number of grid points in the three directions, and provides a state dictionary containing the definition of a grid.

Passing this grid state dictionary onto get_default_state makes climt aware of the dimensions required by the model:

In [6]: grid = climt.get_grid(ny=3, nz=5)

# Get a state dictionary filled with required quantities
# for the components to run
In [7]: state = climt.get_default_state([radiation, convection], grid_state=grid)

In [8]: state['air_temperature']
Out[8]: 
<xarray.DataArray 'air_temperature' (mid_levels: 5, lat: 3, lon: 1)>
array([[[290.],
        [290.],
        [290.]],

       [[290.],
        [290.],
        [290.]],

       [[290.],
        [290.],
        [290.]],

       [[290.],
        [290.],
        [290.]],

       [[290.],
        [290.],
        [290.]]])
Dimensions without coordinates: mid_levels, lat, lon
Attributes:
    units:    degK

climt does not interpret any of the dimension attributes in state quantities other than units. The values and labels of coordinates are mainly for users and components. For instance, SimplePhysics requires that the y dimension be called latitude. So, any model that uses SimplePhysics has to label one of the dimensions as latitude.

As you can see, air_temperature has

  • a uniform value of 290
  • coordinates of latitude and mid_levels
  • units of degK, which is the notation used in climt (and Sympl) for degrees Kelvin.

It is also fairly easy to change units. The to_units() method can be used as below to return a DataArray with the equivalent temperature in degrees Farenheit:

In [9]: state['air_temperature'].to_units('degF')
Out[9]: 
<xarray.DataArray 'air_temperature' (mid_levels: 5, lat: 3, lon: 1)>
array([[[62.33],
        [62.33],
        [62.33]],

       [[62.33],
        [62.33],
        [62.33]],

       [[62.33],
        [62.33],
        [62.33]],

       [[62.33],
        [62.33],
        [62.33]],

       [[62.33],
        [62.33],
        [62.33]]])
Dimensions without coordinates: mid_levels, lat, lon
Attributes:
    units:    degF

Note

climt always names the vertical coordinate as mid_levels or interface_levels, however, the state dictionary will contain a key corresponding to the name of the vertical coordinate specified by get_grid.

As mentioned previously, DataArrays are a user-friendly way of handling numerical or numpy arrays. The numpy array underlying any DataArray is easily accessed using the values attribute:

In [10]: type(state['air_temperature'].values)
Out[10]: numpy.ndarray

and can also be modified easily:

In [11]: state['air_temperature'].values[:] = 291

The right hand side can also be any numpy array, as long as it has the same dimensions (or can be broadcasted to the same dimensions) as the current numpy array.

Note

It is recommended to use the syntax ...values[:] = ... rather than ...values = ..., as the former modifies the numpy array in-place. In either case, DataArrays check to ensure the dimensions (or shape) of the new data matches with the current dimensions.

You can perform any of the functions supported by xarray on the model state quantities.

In [12]: state['air_temperature'].sum()
Out[12]: 
<xarray.DataArray 'air_temperature' ()>
array(4365.)

You can also directly plot DataArrays:

In [13]: state['air_temperature'].plot()
Out[13]: <matplotlib.collections.QuadMesh at 0x7fd059deb358>

DataArrays are a very powerful way of dealing with array-oriented data, and you should read more about xarray, and not just for using climt!

Model Components

Components are representations of physical processes. You can see all available components in climt in the section Components.

All components take some inputs from the model state, and return outputs or tendencies along with diagnostics (if any).

Diagnostics are quantities computed while calculating outputs or tendencies. For example, a radiation component calculates heating rates. However, in the process of calculating these heating rates, it also calculates the radiative flux at each interface level.

# These are the tendencies returned by radiation
In [14]: radiation.tendency_properties
Out[14]: {'air_temperature': {'units': 'degK s^-1'}}

# These are the diagnostics returned by radiation
In [15]: radiation.diagnostic_properties
Out[15]: 
{'downwelling_longwave_flux_in_air': {'dims': ['interface_levels', '*'],
  'units': 'W m^-2',
  'alias': 'lw_down'},
 'upwelling_longwave_flux_in_air': {'dims': ['interface_levels', '*'],
  'units': 'W m^-2',
  'alias': 'lw_up'},
 'longwave_heating_rate': {'dims': ['mid_levels', '*'],
  'units': 'degK day^-1'}}

# These are the outputs returned by convection
In [16]: convection.output_properties
Out[16]: {'air_temperature': {'units': 'degK'}, 'specific_humidity': {'units': 'kg/kg'}}

# convection returns no diagnostics
In [17]: convection.diagnostic_properties
Out[17]: {}

No component will return both outputs and tendencies. The tendency of a quantity \(X\) is given by \(\frac{dX}{dt}\), and so the units of a quantity returned as a tendency will always have per second as as suffix: i.e, if a component is returning air_temperature as a tendency, then its units will be degK/s.