Skip to content

Commit

Permalink
add default get_field methods
Browse files Browse the repository at this point in the history
  • Loading branch information
juliasloan25 committed Feb 27, 2025
1 parent b30a881 commit 0a7ac9c
Show file tree
Hide file tree
Showing 8 changed files with 27 additions and 18 deletions.
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ ClimaCoupler.jl Release Notes

### ClimaCoupler features

#### Add default `get_field` methods for surface models PR[#1210](https://github.com/CliMA/ClimaCoupler.jl/pull/1210)
Add default methods for `get_field` methods that are commonly
not extended for surface models. These return reasonable default
values, and can be extended by surface models that won't use the
defaults (e.g. the full land model).

#### Add coupler fields based on simulation type PR[#1207](https://github.com/CliMA/ClimaCoupler.jl/pull/1207)
Previously, the coupler fields were hardcoded to be the same for all
simulations, independent of what components were included. Now, each
Expand Down
19 changes: 14 additions & 5 deletions docs/src/interfacer.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,23 +118,22 @@ extrapolated to the surface, with units of [kg m^-3].
### SurfaceModelSimulation - required functions
Analogously to the `AtmosModelSimulation`, a `SurfaceModelSimulation`
requires additional functions to those required for a general `ComponentModelSimulation`.
- `get_field(::SurfaceModelSimulation. ::Val{property})`: This getter
- `get_field(::SurfaceModelSimulation, ::Val{property})`: This getter
function returns the value of the field property for the simulation at
the current time. For a `SurfaceModelSimulation`, it must be extended
for the following properties:

| Coupler name | Description | Units |
|-------------------|-------------|-------|
| `area_fraction` | fraction of the simulation grid surface area this model covers | |
| `beta` | factor that scales evaporation based on its estimated level of saturation | |
| `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_humidity` | surface humidity | kg kg^-1 |
| `surface_temperature` | surface temperature | K |

- `update_field!(::SurfaceModelSimulation. ::Val{property}, field)`:
- `update_field!(::SurfaceModelSimulation, ::Val{property}, field)`:
A function to update the value of property in the component model
simulation, using the values in `field` passed from the coupler
This update should be done in place. If this function
Expand All @@ -157,6 +156,18 @@ following properties:
| `surface_diffuse_albedo` | bulk diffuse surface albedo; needed if calculated externally of the surface model (e.g. ocean albedo from the atmospheric state) | |

### SurfaceModelSimulation - optional functions
- `get_field(::SurfaceModelSimulation, ::Val{property})`:
For some quantities, default `get_field` functions 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 |
| `energy` | globally integrated energy | J | `nothing` |
| `water` | globally integrated water | kg | `nothing` |

- `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
Expand All @@ -176,13 +187,11 @@ 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{:energy}) = nothing
get_field(sim::AbstractSurfaceStub, ::Val{:roughness_buoyancy}) = sim.cache.z0b
get_field(sim::AbstractSurfaceStub, ::Val{:roughness_momentum}) = sim.cache.z0m
get_field(sim::AbstractSurfaceStub, ::Union{Val{:surface_direct_albedo}, Val{:surface_diffuse_albedo}}) = sim.cache.α
get_field(sim::AbstractSurfaceStub, ::Val{:surface_humidity}) = TD.q_vap_saturation_generic.(sim.cache.thermo_params, sim.cache.T_sfc, sim.cache.ρ_sfc, sim.cache.phase)
get_field(sim::AbstractSurfaceStub, ::Val{:surface_temperature}) = sim.cache.T_sfc
get_field(sim::AbstractSurfaceStub, ::Val{:water}) = nothing
```
and with the corresponding `update_field!` functions
```
Expand Down
2 changes: 0 additions & 2 deletions experiments/ClimaEarth/components/ocean/eisenman_seaice.jl
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ end

# extensions required by Interfacer
Interfacer.get_field(sim::EisenmanIceSimulation, ::Val{:area_fraction}) = sim.integrator.p.area_fraction
Interfacer.get_field(sim::EisenmanIceSimulation, ::Val{:beta}) = convert(eltype(sim.integrator.u), 1.0)
Interfacer.get_field(sim::EisenmanIceSimulation, ::Val{:roughness_buoyancy}) =
@. sim.integrator.p.params.p_i.z0b * (sim.integrator.p.ice_area_fraction) +
sim.integrator.p.params.p_o.z0b .* (1 - sim.integrator.p.ice_area_fraction)
Expand All @@ -105,7 +104,6 @@ Interfacer.get_field(sim::EisenmanIceSimulation, ::Union{Val{:surface_direct_alb
sim.integrator.p.params.p_o.α .* (1 - sim.integrator.p.ice_area_fraction)
Interfacer.get_field(sim::EisenmanIceSimulation, ::Val{:surface_humidity}) = sim.integrator.u.q_sfc
Interfacer.get_field(sim::EisenmanIceSimulation, ::Val{:surface_temperature}) = sim.integrator.u.T_sfc
Interfacer.get_field(sim::EisenmanIceSimulation, ::Val{:water}) = nothing

"""
Interfacer.get_field(sim::EisenmanIceSimulation, ::Val{:energy})
Expand Down
2 changes: 0 additions & 2 deletions experiments/ClimaEarth/components/ocean/prescr_seaice.jl
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,12 @@ end

# extensions required by Interfacer
Interfacer.get_field(sim::PrescribedIceSimulation, ::Val{:area_fraction}) = sim.integrator.p.area_fraction
Interfacer.get_field(sim::PrescribedIceSimulation, ::Val{:beta}) = convert(eltype(sim.integrator.u), 1.0)
Interfacer.get_field(sim::PrescribedIceSimulation, ::Val{:roughness_buoyancy}) = sim.integrator.p.params.z0b
Interfacer.get_field(sim::PrescribedIceSimulation, ::Val{:roughness_momentum}) = sim.integrator.p.params.z0m
Interfacer.get_field(sim::PrescribedIceSimulation, ::Union{Val{:surface_direct_albedo}, Val{:surface_diffuse_albedo}}) =
sim.integrator.p.params.α
Interfacer.get_field(sim::PrescribedIceSimulation, ::Val{:surface_humidity}) = sim.integrator.p.q_sfc
Interfacer.get_field(sim::PrescribedIceSimulation, ::Val{:surface_temperature}) = sim.integrator.u.T_sfc
Interfacer.get_field(sim::PrescribedIceSimulation, ::Val{:water}) = nothing

"""
Interfacer.get_field(sim::PrescribedIceSimulation, ::Val{:energy})
Expand Down
2 changes: 0 additions & 2 deletions experiments/ClimaEarth/components/ocean/slab_ocean.jl
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,12 @@ end

# extensions required by Interfacer
Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:area_fraction}) = sim.integrator.p.area_fraction
Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:beta}) = convert(eltype(sim.integrator.u), 1.0)
Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:roughness_buoyancy}) = sim.integrator.p.params.z0b
Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:roughness_momentum}) = sim.integrator.p.params.z0m
Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:surface_direct_albedo}) = sim.integrator.p.α_direct
Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:surface_diffuse_albedo}) = sim.integrator.p.α_diffuse
Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:surface_humidity}) = sim.integrator.p.q_sfc
Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:surface_temperature}) = sim.integrator.u.T_sfc
Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:water}) = nothing

"""
Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:energy})
Expand Down
10 changes: 7 additions & 3 deletions src/Interfacer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,6 @@ an atmosphere component model.
get_field(
sim::AtmosModelSimulation,
val::Union{
Val{:energy},
Val{:height_int},
Val{:height_sfc},
Val{:liquid_precipitation},
Expand All @@ -166,7 +165,6 @@ get_field(
Val{:turbulent_moisture_flux},
Val{:thermo_state_int},
Val{:uv_int},
Val{:water},
},
) = get_field_error(sim, val)

Expand All @@ -181,7 +179,6 @@ get_field(
sim::SurfaceModelSimulation,
val::Union{
Val{:area_fraction},
Val{:beta},
Val{:roughness_buoyancy},
Val{:roughness_momentum},
Val{:surface_direct_albedo},
Expand All @@ -200,6 +197,13 @@ get_field(sim::ComponentModelSimulation, val::Val) = get_field_error(sim, val)

get_field_error(sim, val::Val{X}) where {X} = error("undefined field `$X` for " * name(sim))

# Set default values for fields that are not defined in all component models
get_field(sim::ComponentModelSimulation, ::Val{:beta}) = convert(eltype(sim.integrator.u), 1.0)
get_field(sim::ComponentModelSimulation, ::Val{:emissivity}) = convert(eltype(sim.integrator.u), 1.0)
get_field(sim::ComponentModelSimulation, ::Val{:height_disp}) = convert(eltype(sim.integrator.u), 0.0)
get_field(::ComponentModelSimulation, ::Val{:energy}) = nothing
get_field(::ComponentModelSimulation, ::Val{:water}) = nothing

"""
update_field!(::AtmosModelSimulation, ::Val, _...)
Expand Down
1 change: 0 additions & 1 deletion src/surface_stub.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ get_field(sim::AbstractSurfaceStub, ::Val{:surface_diffuse_albedo}) = sim.cache.
get_field(sim::AbstractSurfaceStub, ::Val{:surface_humidity}) =
TD.q_vap_saturation_generic.(sim.cache.thermo_params, sim.cache.T_sfc, sim.cache.ρ_sfc, sim.cache.phase)
get_field(sim::AbstractSurfaceStub, ::Val{:surface_temperature}) = sim.cache.T_sfc
get_field(sim::AbstractSurfaceStub, ::Val{:water}) = nothing

"""
update_field!(sim::AbstractSurfaceStub, ::Val{:area_fraction}, field::CC.Fields.Field)
Expand Down
3 changes: 0 additions & 3 deletions test/interfacer_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ end
# Test that get_field gives correct warnings for unextended fields
for value in (
:area_fraction,
:beta,
:roughness_buoyancy,
:roughness_momentum,
:surface_direct_albedo,
Expand All @@ -156,7 +155,6 @@ end

# Test that get_field gives correct warnings for unextended fields
for value in (
:energy,
:height_int,
:height_sfc,
:liquid_precipitation,
Expand All @@ -167,7 +165,6 @@ end
:turbulent_moisture_flux,
:thermo_state_int,
:uv_int,
:water,
)
val = Val(value)
@test_throws ErrorException("undefined field `$value` for " * Interfacer.name(sim)) Interfacer.get_field(
Expand Down

0 comments on commit 0a7ac9c

Please sign in to comment.