This module contains functions that define the interface for coupling component models, as well as stub objects that contain prescribed fields. Here we explain each type of component model, and the functions that must be implemented to use a component model with ClimaCoupler.jl
A CoupledSimulation stores info for ESM run and contains each of the
component model simulations. We currently require that each CoupledSimulation
contains one atmos_sim, and at least one surface simulation (land_sim,
ocean_sim, and/or ice_sim). If a simulation surface type is not needed
for a given run, it may be omitted.
Individual component model simulations fall under ComponentModelSimulation,
which together combine to make the CoupledSimulation.
We have two types of ComponentModelSimulations: AtmosModelSimulation and
SurfaceModelSimulation. The two have different requirements,
which are detailed below. SurfaceModelSimulation is further divided into
SeaIceModelSimulation, LandModelSimulation, and OceanModelSimulation,
representing the 3 currently-supported options for surface models.
A component model simulation should be implemented as a struct that is a concrete subtype
of a ComponentModelSimulation. This struct should contain all of the
information needed to run that simulation.
Each ComponentModelSimulation must extend the following functions to be able
to use our coupler. For some existing models, these are defined within
ClimaCoupler.jl in that model’s file in experiments/ClimaEarth/components/, but it is preferable
for these to be defined in a model’s own repository. Note that the dispatch
::ComponentModelSimulation in the function definitions given below should
be replaced with the particular component model extending these functions.
-
constructor: construct and return an instance of the
ComponentModelSimulation, and perform all initialization. This function should return a simulation that is ready to be stepped in the coupled simulation. The interface for this function varies across component models. -
step!(::ComponentModelSimulation, t): A function to update the simulation in-place with values calculate for timet. For the models we currently have implemented, this is a simple wrapper around thestep!function implemented in SciMLBase.jl.
-
Checkpointer.get_model_prog_state(::ComponentModelSimulation): A function that returns the state vector of the simulation at its current state. This is used for checkpointing the simulation. -
Checkpointer.get_model_cache(::ComponentModelSimulation): A function that returns the cache of the simulation at its current state. This is used for checkpointing the simulation. -
Checkpointer.restore_cache(::ComponentModelSimulation, new_cache): A function that updates the cache of the simulation with the providednew_cache. This is used for restarting the simulation. -
get_field(::ComponentModelSimulation, ::Val{property}): Defaultget_fieldfunctions are provided forenergyandwaterfields, described in the table below. These quantities are used to track conservation, and the defaults returnnothing. To check conservation throughout a simulation, these functions must be extended for all models being run.
| Coupler name | Description | Units | Default value |
|---|---|---|---|
energy |
vertically integrated energy per surface area | J m⁻² | nothing |
water |
vertically integrated water per surface area | kg m⁻² | nothing |
add_coupler_fields!(coupler_field_names, ::ComponentModelSimulation): A set of default coupler exchange fields is initialized for each coupled simulation, but depending on the component models being run, additional coupler fields may be required. For example, the integrated land model requires the concentration of atmospheric CO2 for photosynthesis calculations, but the slab ocean does not.add_coupler_fields!is extended for any component model simulation that requires coupler fields in addition to the defaults, allowing us to allocate space for and exchange the extra fields only when necessary. All coupler fields are defined on the boundary space.- Any additional fields specified here will likely also require an
update_field!method defined for this component model, so the coupler can update the component. They may also require a new method ofimport_atmos_fields!orcombine_surfaces!to update the coupler fields from the component model that computes the field.
- Any additional fields specified here will likely also require an
The default coupler exchange fields are the following, defined in
default_coupler_fields() in the Interfacer module:
| Coupler name | Description | Units |
|---|---|---|
T_atmos |
atmosphere temperature at the bottom layer | K |
q_atmos |
atmosphere humidity at the bottom layer | kg kg⁻¹ |
ρ_atmos |
atmosphere air density at the bottom layer | kg m⁻³ |
z_int |
height of the first internal atmosphere level (center) | m |
z_sfc |
height of the bottom atmosphere layer (face) | m |
F_lh |
latent heat flux | W m⁻² |
F_sh |
sensible heat flux | W m⁻² |
F_turb_moisture |
turbulent moisture flux | kg m⁻² s⁻¹ |
F_turb_ρτxz |
turbulent momentum flux in the zonal direction | kg m⁻¹ s⁻² |
F_turb_ρτyz |
turbulent momentum flux in the meridional direction | kg m⁻¹ s⁻² |
F_radiative |
net radiative flux at the surface | W m⁻² |
emissivity |
surface emissivity | - |
T_sfc |
surface temperature, averaged across components | K |
P_liq |
liquid precipitation | kg m⁻² s⁻¹ |
P_snow |
snow precipitation | kg m⁻² s⁻¹ |
scalar_temp1 |
a surface scalar field used for intermediate calculations | - |
scalar_temp2 |
a surface scalar field used for intermediate calculations | - |
scalar_temp3 |
a surface scalar field used for intermediate calculations | - |
scalar_temp4 |
a surface scalar field used for intermediate calculations | - |
!!! note "What should be stored in the coupler exchange fields?" In general, the coupler fields should contain exchange fields for fluxes, including turbulent fluxes, radiative fluxes, and precipitation. They also hold any quantities that a component model requires from another component. For example, the atmosphere needs surface temperature and emissivity from the surface models to compute radiation, so the coupler allocates space to exchange them. The coupler exchange fields may also hold quantities from components that are used to compute turbulent fluxes. As a general rule, we tend to store such quantities that come from the atmosphere model, but access them when needed for surface models. This is because we compute fluxes indvidually for the interface between each surface and the atmosphere model. As a result, the atmosphere quantities are used for each of these calculations, so storing them in the coupler fields allows us to avoid regridding them to the coupler space multiple times per coupling timestep.
-
update_sim!(::ComponentModelSimulation, csf): A function to update each of the fields of the component model simulation that are updated by the coupler. ClimaCoupler.jl provides defaults of this function for bothAtmosModelSimulationandSurfaceModelSimulationthat update each of the fields expected by the coupler. This function will need to be extended for any model that requires additional fields (specified viaadd_coupler_fields!). -
set_cache!(sim::ComponentModelSimulation): A function to perform any initialization of the component model caches that isn't done during the model simulation initialization, and that must be done after the initial exchange. This is necessary, for example, when component models have cache interdependencies that must be handled in a specific order. Cache variables that are computed as part of the tendencies do not need to be set here.
In addition to the functions required for a general
ComponentModelSimulation, an AtmosModelSimulation requires the
following functions to retrieve and update its fields.
get_field(::AtmosModelSimulation. ::Val{property}): This getter function returns the value of the field property for the simulation in its current state. For anAtmosModelSimulation, it must be extended for the following properties:
| Coupler name | Description | Units |
|---|---|---|
air_density |
air density at the bottom cell centers of the atmosphere | kg m⁻³ |
air_pressure |
air pressure at the bottom cell centers of the atmosphere | Pa |
air_temperature |
air temperature at the bottom cell centers of the atmosphere | K |
height_int |
height at the bottom cell center of the atmosphere space | m |
height_sfc |
height at the bottom face of the atmosphere space | m |
liquid_precipitation |
liquid precipitation at the surface | kg m⁻² s⁻¹ |
radiative_energy_flux_sfc |
net radiative flux at the surface | W m⁻² |
radiative_energy_flux_toa |
net radiative flux at the top of the atmosphere | W m⁻² |
snow_precipitation |
snow precipitation at the surface | kg m⁻² s⁻¹ |
specific_humidity |
specific humidity at the bottom cell centers of the atmosphere | kg kg⁻¹ |
turbulent_energy_flux |
aerodynamic turbulent surface fluxes of energy (sensible and latent heat) | W m⁻² |
turbulent_moisture_flux |
aerodynamic turbulent surface fluxes of energy (evaporation) | kg m⁻² s⁻¹ |
u_int |
zonal wind velocity vector at the first internal model level | m s⁻¹ |
v_int |
meridional wind velocity vector at the first internal model level | m s⁻¹ |
update_field!(::AtmosModelSimulation. ::Val{property}, field): A function to update the value of property in the component model simulation, using the values infield. This update should be done in place. If this function isn't extended for a property, that property will remain constant throughout the simulation and a warning will be raised. This function is expected to be extended for the following properties, and may also be extended for any additional properties needed by a component model.
| Coupler name | Description | Units |
|---|---|---|
emissivity |
surface emissivity | |
surface_direct_albedo |
bulk direct surface albedo over the whole surface space | |
surface_diffuse_albedo |
bulk diffuse surface albedo over the whole surface space | |
surface_temperature |
temperature over the combined surface space | K |
turbulent_fluxes |
turbulent fluxes | W m⁻² |
ClimaAtmos should also add the following coupler fields for Monin-Obukhov similarity theory:
| Coupler name | Description | Units |
|---|---|---|
ustar |
friction velocity | m s⁻¹ |
L_MO |
Obukhov length | m |
buoyancy_flux |
flux of buoyancy | m⁻²s⁻³ |
Coupling with the integrated ClimaLandSimulation requires the following functions, in addition
to the functions required for coupling with a general SurfaceModelSimulation.
get_field(::AtmosModelSimulation. ::Val{property}): This getter function must be extended for the following properties:
| Coupler name | Description | Units |
|---|---|---|
co2 |
global mean co2 | ppm |
diffuse_fraction |
fraction of downwards shortwave flux that is direct | |
LW_d |
downwards longwave flux | W m⁻² |
SW_d |
downwards shortwave flux | W m⁻² |
!!! note
co2, diffuse_fraction, LW_d and SW_d will not be present in a ClimaAtmosSimulation
if the model is setup with no radiation. Because of this, a ClimaAtmosSimulation must have
radiation enabled if running with the full ClimaLand model.
Analogously to the AtmosModelSimulation, a SurfaceModelSimulation
requires additional functions to those required for a general ComponentModelSimulation.
get_field(::SurfaceModelSimulation, ::Val{property}): This getter function returns the value of the field property for the simulation at the current time. For aSurfaceModelSimulation, it must be extended for the following properties:
| Coupler name | Description | Units |
|---|---|---|
area_fraction |
fraction of the simulation grid surface area this model covers | |
roughness_buoyancy |
aerodynamic roughness length for buoyancy | m |
roughness_momentum |
aerodynamic roughness length for momentum | m |
surface_direct albedo |
bulk direct surface albedo | |
surface_diffuse albedo |
bulk diffuse surface albedo | |
surface_temperature |
surface temperature | K |
!!! note
area_fraction is expected to be defined on the boundary space of the simulation,
while all other fields will likely be on the simulation's own space.
update_field!(::SurfaceModelSimulation, ::Val{property}, field): A function to update the value of property in the component model simulation, using the values infieldpassed from the coupler This update should be done in place. If this function isn't extended for a property, that property will remain constant throughout the simulation and a warning will be raised. This function is expected to be extended for the following properties, and may also be extended for any additional properties needed by a component model.
| Coupler name | Description | Units |
|---|---|---|
area_fraction |
fraction of the simulation grid surface area this model covers | |
liquid_precipitation |
liquid precipitation at the surface | kg m⁻² s⁻¹ |
radiative_energy_flux_sfc OR LW_d, SW_d |
net radiative flux at the surface OR downward longwave, shortwave radiation | W m⁻² |
snow_precipitation |
snow precipitation at the surface | kg m⁻² s⁻¹ |
turbulent_energy_flux |
aerodynamic turbulent surface fluxes of energy (sensible and latent heat) | W m⁻² |
turbulent_moisture_flux |
aerodynamic turbulent surface fluxes of energy (evaporation) | kg m⁻² s⁻¹ |
!!! note
update_field!(::SurfaceModelSimulation, ::Val{:area_fraction}, field) is
not required to be extended for land models, since they're assumed to have a
constant area fraction.
get_field(::SurfaceModelSimulation, ::Val{property}): For some quantities, defaultget_fieldfunctions are provided, which may be overwritten or used as-is. These currently include the following:
| Coupler name | Description | Units | Default value |
|---|---|---|---|
beta |
factor that scales evaporation based on its estimated level of saturation | 1 | |
emissivity |
measure of how much energy a surface radiates | 1 | |
height_disp |
displacement height relative to the surface | m | 0 |
update_turbulent_fluxes!(::ComponentModelSimulation, fields::NamedTuple): This function updates the turbulent fluxes of the component model simulation at this point in horizontal space. The values are updated using the energy and moisture turbulent fluxes stored in fields which are calculated by the coupler.
SurfaceStubis aSurfaceModelSimulation, but it only contains required data in<surface_stub>.cache, e.g., for the calculation of surface fluxes through a prescribed surface state. This model is intended to be used for testing or as a simple stand-in model. The above adapter functions are already predefined forAbstractSurfaceStub, which is extended bySurfaceStubin thesurface_stub.jlfile, with the cache variables specified as:
get_field(sim::AbstractSurfaceStub, ::Val{:area_fraction}) = sim.cache.area_fraction
get_field(sim::AbstractSurfaceStub, ::Val{:beta}) = sim.cache.beta
get_field(sim::AbstractSurfaceStub, ::Val{:roughness_buoyancy}) = sim.cache.z0b
get_field(sim::AbstractSurfaceStub, ::Val{:roughness_momentum}) = sim.cache.z0m
get_field(sim::AbstractSurfaceStub, ::Val{:surface_direct_albedo}) = sim.cache.α_direct
get_field(sim::AbstractSurfaceStub, ::Val{:surface_diffuse_albedo}) = sim.cache.α_diffuse
get_field(sim::AbstractSurfaceStub, ::Val{:surface_temperature}) = sim.cache.T_sfc
and with the corresponding update_field! functions
function update_field!(sim::AbstractSurfaceStub, ::Val{:area_fraction}, field::ClimaCore.Fields.Field)
sim.cache.area_fraction .= field # `area_fraction` is on the boundary space, so it doesn't need remapping
end
function update_field!(sim::AbstractSurfaceStub, ::Val{:surface_temperature}, field::ClimaCore.Fields.Field)
Interfacer.remap!(sim.cache.T_sfc, field)
end
function update_field!(sim::AbstractSurfaceStub, ::Val{:surface_direct_albedo}, field::CC.Fields.Field)
Interfacer.remap!(sim.cache.α_direct, field)
end
function update_field!(sim::AbstractSurfaceStub, ::Val{:surface_diffuse_albedo}, field::CC.Fields.Field)
Interfacer.remap!(sim.cache.α_diffuse, field)
end
For component models that don't use ClimaCore Fields, some additional functions must be extended to enable remapping between the component model's grid and the boundary space of the coupled simulation. These are described below.
To regrid from a component model's grid to the boundary space, we can typically
use ClimaCore's Remapping module. Users may want to create a remapping object
containing both the ClimaCore.Remapping.Remapper object and scratch space to
reduce allocations during the remapping.
This has been done for the OceananigansSimulation in
experiments/ClimaEarth/components/ocean/oceananigans.jl.
This direction simply requires supplying a matrix of ClimaCore.Geometry.LatLongPoint
objects containing latitude/longitude pairs at each point of the source grid.
Remap the given field onto the target_space. If the field is already
on the target space or a compatible one, it is returned unchanged.
For ClimaCore Fields, this function is implemented by default and does not require a remapper object. Component models that use non-ClimaCore fields (such as Oceananigans) must extend this function to provide a method that handles remapping from their native field type to a ClimaCore Field on the target space.
!!! note "Performance"
The remap method allocates a new field and is not efficient for
performance-critical code. For better performance, use the in-place option
remap! instead.
Signature:
field: The source field to be remappedtarget_space: The target space (typically the boundary space) onto which the field should be remappedremapper: An optional remapper object returned byget_remapper_to_cc(sim). For ClimaCore Fields, this can benothing.
Returns: A new field remapped onto the target space
Remap the given source field onto the target_field in place. This is
the preferred method for remapping when performance is important, as it avoids
allocating a new field.
For ClimaCore Fields, this function is implemented by default. Component models
that use non-ClimaCore fields must extend this function to provide a method that
handles remapping from their native field type into the provided target_field.
Signature:
target_field: The destination field (must be a ClimaCore Field) where remapped data will be storedsource: The source field to be remapped (can be a ClimaCore Field or a non-ClimaCore field type)remapper: An optional remapper object returned byget_remapper_to_cc(sim). For ClimaCore Fields, this can benothing.
Returns: nothing (updates target_field in place)
Return the remapper object used to remap quantities from this component model's grid onto the boundary space.
By default, this function returns nothing, which is intended for use with components that
use the default remapping functions (i.e. components using ClimaCore Fields).
Components that require an alternative remapper (such as the XESMF regridder for Oceananigans)
should extend this function to return their remapper object. If this function is extended,
the component model will also need to extend remap and remap! to use the remapper object.
Signature:
sim: The component model simulation
Returns: A remapper object (or nothing for ClimaCore-based models)
Example: For an Oceananigans simulation, this returns a NamedTuple containing an XESMF regridder object with an initialized weight matrix, as well as scratch space to be used during remapping.
ClimaCoupler.Interfacer.CoupledSimulation
ClimaCoupler.Interfacer.AtmosModelSimulation
ClimaCoupler.Interfacer.SurfaceModelSimulation
ClimaCoupler.Interfacer.ComponentModelSimulation
ClimaCoupler.Interfacer.AbstractSurfaceStub
ClimaCoupler.Interfacer.SurfaceStub
ClimaCoupler.Interfacer.get_field
ClimaCoupler.Interfacer.update_field!
ClimaCoupler.Interfacer.AbstractSlabplanetSimulationMode
ClimaCoupler.Interfacer.AMIPMode
ClimaCoupler.Interfacer.SubseasonalMode
ClimaCoupler.Interfacer.SlabplanetMode
ClimaCoupler.Interfacer.SlabplanetAquaMode
ClimaCoupler.Interfacer.SlabplanetTerraMode
ClimaCoupler.Interfacer.set_cache!
ClimaCoupler.Interfacer.remap
ClimaCoupler.Interfacer.remap!
ClimaCoupler.Interfacer.boundary_space
ClimaCoupler.Interfacer.AbstractSimulation
ClimaCoupler.Interfacer.AbstractSimulationMode