Skip to content

Commit

Permalink
address review
Browse files Browse the repository at this point in the history
  • Loading branch information
juliasloan25 committed Feb 29, 2024
1 parent da7a857 commit 64df52d
Show file tree
Hide file tree
Showing 15 changed files with 130 additions and 63 deletions.
2 changes: 1 addition & 1 deletion docs/src/fieldexchanger.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The `FieldExchanger` needs to populate the coupler with
The component models are updated by broadcasting the coupler fields, via the `update_model_sims!` function. For an update, this function requires that `update_field!` is defined for the particular variable and component model. Currently, we support the:
- `AtmosModelSimulation`: `albedo`, `surface_temperature`
- if calculating fluxes in the atmospheric model: `roughness_momentum`, `roughness_buoyancy`, `beta`
- `SurfaceModelSimulation`: `air_density`, `turbulent_energy_flux`, `turbulent_moisture_flux`, `radiative_energy_flux`, `liquid_precipitation`, `snow_precipitation`
- `SurfaceModelSimulation`: `air_density`, `turbulent_energy_flux`, `turbulent_moisture_flux`, `radiative_energy_flux_sfc`, `liquid_precipitation`, `snow_precipitation`

If an `update_field!` function is not defined for a particular component model, it will be ignored.

Expand Down
36 changes: 20 additions & 16 deletions docs/src/interfacer.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ initialized with `SurfaceStub` with a zero `area_fracion`.
The `atmos_sim` should always be specified.


## Component models
## Component simulations
Individual component model simulations fall under `ComponentModelSimulation`,
which together combine to make the `CoupledSimulation`.
We have two types of `ComponentModelSimulations`: `AtmosModelSimulation` and
Expand All @@ -35,12 +35,11 @@ for these to be defined in a model’s own repository. Note that the dispatch
be replaced with the particular component model extending these functions.
- `init`: 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. This function currently
doesn’t have a unified interface for arguments and may vary across component
models, but this may change in the future.
is ready to be stepped in the coupled simulation. The interface for this
function varies across component models.
- `name(::ComponentModelSimulation)`: return a string containing the name of
this `ComponentModelSimulation`, which is used for printing information about
component models.
component models and writing to checkpoint files.
- `step!(::ComponentModelSimulation, t)`: A function to update the
simulation in-place with values calculate for time `t`. For the
models we currently have implemented, this is a simple wrapper around
Expand Down Expand Up @@ -73,10 +72,11 @@ for the following properties:
- `air_density`: air density at the surface of the atmosphere
- `air_temperature`: air temperature at the surface of the atmosphere
- `energy`: TODO
- `F_radiative_TOA`: incoming radiative flux at the top of the atmosphere
- `height_int`: TODO
- `height_sfc`: TODO (only required when using `PartitionedStateFluxes`)
- `liquid_precipitation`: liquid precipitation at the surface
- `radiative_energy_flux`: TODO
- `radiative_energy_flux_sfc`: radiative flux at the surface
- `radiative_energy_flux_toa`: incoming radiative flux at the top of the atmosphere
- `snow_precipitation`: snow precipitation at the surface
- `turbulent_energy_flux`: TODO
- `turbulent_moisture_flux`: TODO
Expand All @@ -87,13 +87,15 @@ for the following properties:
A function to update the value of property in the component model
simulation, using the values in `field`. This update should
be done in place. If this function isn't extended for a property,
that property will remain constant throughout the simulation.
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:
- `co2`: global mean co2
- `surface_albedo`: bulk surface albedo over the whole surface space
- `surface_temperature`: temperature over the whole surface space
- `turbulent_fluxes`: turbulent fluxes (note: only required to use
partitioned turbulent fluxes option - see our `FluxCalculator` module docs for more information)
`PartitionedStateFluxes` option - see our `FluxCalculator` module docs for more information)

### SurfaceModelSimulation - required functions
Analogously to the `AtmosModelSimulation`, a `SurfaceModelSimulation`
Expand All @@ -102,6 +104,7 @@ requires additional functions to those required for a general `ComponentModelSim
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:
- `air_density`: surface air density
- `area_fraction`: the fraction of the simulation grid surface area this model covers
- `beta`: TODO
- `roughness_buoyancy`: TODO
Expand All @@ -111,16 +114,17 @@ for the following properties:
- `surface_temperature`: surface temperature
- `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 atmosphere
model. This update should be done in place. If this function
simulation, using the values in `field` passed 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.
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:
- `air_density`: surface air density
- `area_fraction`: the fraction of the simulation grid surface area this model covers
- `liquid_precipitation`: liquid precipitation at the surface
- `radiative_energy_flux`: incoming radiative energy
- `radiative_energy_flux_sfc`: radiative energy flux at the surface
- `snow_precipitation`: snow precipitation at the surface
- `turbulent_energy_flux`: turbulent energy flux
- `turbulent_moisture_flux`: turbulent moisture flux
Expand All @@ -130,9 +134,9 @@ following properties:
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. Note that this function is only required when using the partitioned
surface fluxes option of ClimaCoupler.jl. See our `FluxCalculator` module
docs for more information.
coupler. Note that this function is only required when using the
`PartitionedStateFluxes` option of ClimaCoupler.jl. See our `FluxCalculator`
module docs for more information.

### Prescribed surface conditions - SurfaceStub
- `SurfaceStub` is a `SurfaceModelSimulation`, but it only contains
Expand Down
6 changes: 3 additions & 3 deletions experiments/AMIP/components/atmosphere/climaatmos_init.jl
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ function atmos_init(::Type{FT}, atmos_config_dict::Dict) where {FT}
end

# extensions required by the Interfacer
get_field(sim::ClimaAtmosSimulation, ::Val{:radiative_energy_flux}) =
get_field(sim::ClimaAtmosSimulation, ::Val{:radiative_energy_flux_sfc}) =
Fields.level(sim.integrator.p.radiation.ᶠradiation_flux, half)
get_field(sim::ClimaAtmosSimulation, ::Val{:liquid_precipitation}) = sim.integrator.p.precipitation.col_integrated_rain # kg/m^2/s
get_field(sim::ClimaAtmosSimulation, ::Val{:snow_precipitation}) = sim.integrator.p.precipitation.col_integrated_snow # kg/m^2/s
Expand Down Expand Up @@ -291,12 +291,12 @@ function get_model_state_vector(sim::ClimaAtmosSimulation)
end

"""
get_field(atmos_sim::ClimaAtmosSimulation, ::Val{:F_radiative_TOA})
get_field(atmos_sim::ClimaAtmosSimulation, ::Val{:radiative_energy_flux_toa})
Extension of Interfacer.get_field to get the net TOA radiation, which is a sum of the
upward and downward longwave and shortwave radiation.
"""
function get_field(atmos_sim::ClimaAtmosSimulation, ::Val{:F_radiative_TOA})
function get_field(atmos_sim::ClimaAtmosSimulation, ::Val{:radiative_energy_flux_toa})
radiation = atmos_sim.integrator.p.radiation.radiation_model
FT = eltype(atmos_sim.integrator.u)
# save radiation source
Expand Down
2 changes: 1 addition & 1 deletion experiments/AMIP/components/land/bucket_utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ function update_field!(sim::BucketSimulation, ::Val{:turbulent_moisture_flux}, f
ρ_liq = (LP.ρ_cloud_liq(sim.model.parameters.earth_param_set))
parent(sim.integrator.p.bucket.turbulent_fluxes.vapor_flux) .= parent(field ./ ρ_liq) # TODO: account for sublimation
end
function update_field!(sim::BucketSimulation, ::Val{:radiative_energy_flux}, field)
function update_field!(sim::BucketSimulation, ::Val{:radiative_energy_flux_sfc}, field)
parent(sim.integrator.p.bucket.R_n) .= parent(field)
end
function update_field!(sim::BucketSimulation, ::Val{:liquid_precipitation}, field)
Expand Down
2 changes: 1 addition & 1 deletion experiments/AMIP/components/ocean/eisenman_seaice_init.jl
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ end
function update_field!(sim::EisenmanIceSimulation, ::Val{:turbulent_energy_flux}, field)
parent(sim.integrator.p.Ya.F_turb) .= parent(field)
end
function update_field!(sim::EisenmanIceSimulation, ::Val{:radiative_energy_flux}, field)
function update_field!(sim::EisenmanIceSimulation, ::Val{:radiative_energy_flux_sfc}, field)
parent(sim.integrator.p.Ya.F_rad) .= parent(field)
end
function update_field!(sim::EisenmanIceSimulation, ::Val{:air_density}, field)
Expand Down
2 changes: 1 addition & 1 deletion experiments/AMIP/components/ocean/prescr_seaice_init.jl
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ end
function update_field!(sim::PrescribedIceSimulation, ::Val{:turbulent_energy_flux}, field)
parent(sim.integrator.p.F_turb_energy) .= parent(field)
end
function update_field!(sim::PrescribedIceSimulation, ::Val{:radiative_energy_flux}, field)
function update_field!(sim::PrescribedIceSimulation, ::Val{:radiative_energy_flux_sfc}, field)
parent(sim.integrator.p.F_radiative) .= parent(field)
end
function update_field!(sim::PrescribedIceSimulation, ::Val{:air_density}, field)
Expand Down
2 changes: 1 addition & 1 deletion experiments/AMIP/components/ocean/slab_ocean_init.jl
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ end
function update_field!(sim::SlabOceanSimulation, ::Val{:turbulent_energy_flux}, field)
parent(sim.integrator.p.F_turb_energy) .= parent(field)
end
function update_field!(sim::SlabOceanSimulation, ::Val{:radiative_energy_flux}, field)
function update_field!(sim::SlabOceanSimulation, ::Val{:radiative_energy_flux_sfc}, field)
parent(sim.integrator.p.F_radiative) .= parent(field)
end
function update_field!(sim::SlabOceanSimulation, ::Val{:air_density}, field)
Expand Down
2 changes: 1 addition & 1 deletion experiments/AMIP/coupler_driver.jl
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ coupler_field_names = (
:F_radiative,
:P_liq,
:P_snow,
:F_radiative_TOA,
:radiative_energy_flux_toa,
:P_net,
)
coupler_fields =
Expand Down
8 changes: 5 additions & 3 deletions src/ConservationChecker.jl
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,15 @@ function check_conservation!(
sim_name = Symbol(Interfacer.name(sim))
if sim isa Interfacer.AtmosModelSimulation
# save radiation source
parent(coupler_sim.fields.F_radiative_TOA) .= parent(Interfacer.get_field(sim, Val(:F_radiative_TOA)))
parent(coupler_sim.fields.radiative_energy_flux_toa) .=
parent(Interfacer.get_field(sim, Val(:radiative_energy_flux_toa)))

if isempty(ccs.toa_net_source)
radiation_sources_accum = sum(coupler_sim.fields.F_radiative_TOA .* FT(coupler_sim.Δt_cpl)) # ∫ J / m^2 dA
radiation_sources_accum = sum(coupler_sim.fields.radiative_energy_flux_toa .* FT(coupler_sim.Δt_cpl)) # ∫ J / m^2 dA
else
radiation_sources_accum =
sum(coupler_sim.fields.F_radiative_TOA .* FT(coupler_sim.Δt_cpl)) .+ ccs.toa_net_source[end] # ∫ J / m^2 dA
sum(coupler_sim.fields.radiative_energy_flux_toa .* FT(coupler_sim.Δt_cpl)) .+
ccs.toa_net_source[end] # ∫ J / m^2 dA
end
push!(ccs.toa_net_source, radiation_sources_accum)

Expand Down
6 changes: 3 additions & 3 deletions src/FieldExchanger.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ using ClimaCoupler: Interfacer, FluxCalculator, Regridder, Utilities
import_atmos_fields!(csf, model_sims, boundary_space, turbulent_fluxes)
Updates the coupler with the atmospheric fluxes. The `Interfacer.get_field` functions
(`:turbulent_energy_flux`, `:turbulent_moisture_flux`, `:radiative_energy_flux`, `:liquid_precipitation`, `:snow_precipitation`)
(`:turbulent_energy_flux`, `:turbulent_moisture_flux`, `:radiative_energy_flux_sfc`, `:liquid_precipitation`, `:snow_precipitation`)
have to be defined for the amtospheric component model type.
# Arguments
Expand All @@ -38,7 +38,7 @@ function import_atmos_fields!(csf, model_sims, boundary_space, turbulent_fluxes)
Regridder.dummmy_remap!(csf.ρ_sfc, FluxCalculator.calculate_surface_air_density(atmos_sim, csf.T_S))

# radiative fluxes
Regridder.dummmy_remap!(csf.F_radiative, Interfacer.get_field(atmos_sim, Val(:radiative_energy_flux)))
Regridder.dummmy_remap!(csf.F_radiative, Interfacer.get_field(atmos_sim, Val(:radiative_energy_flux_sfc)))

# precipitation
Regridder.dummmy_remap!(csf.P_liq, Interfacer.get_field(atmos_sim, Val(:liquid_precipitation)))
Expand Down Expand Up @@ -137,7 +137,7 @@ function update_sim!(sim::Interfacer.SurfaceModelSimulation, csf, turbulent_flux
end

# radiative fluxes
Interfacer.update_field!(sim, Val(:radiative_energy_flux), FT.(mask .* csf.F_radiative))
Interfacer.update_field!(sim, Val(:radiative_energy_flux_sfc), FT.(mask .* csf.F_radiative))

# precipitation
Interfacer.update_field!(sim, Val(:liquid_precipitation), csf.P_liq)
Expand Down
5 changes: 3 additions & 2 deletions src/FluxCalculator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -381,8 +381,9 @@ update_turbulent_fluxes_point!(sim::Interfacer.SurfaceStub, fields::NamedTuple,

"""
differentiate_turbulent_fluxes!(sim::Interfacer.SurfaceModelSimulation, args)
This function is required for the Eisenman sea ice model. For all other
models, we do nothing when it's called.
This function provides a placeholder for differentiating fluxes with respect to
surface temperature in surface energy balance calculations.
"""
differentiate_turbulent_fluxes!(::Interfacer.SurfaceModelSimulation, args) = nothing

Expand Down
94 changes: 76 additions & 18 deletions src/Interfacer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,55 @@ abstract type LandModelSimulation <: SurfaceModelSimulation end
abstract type OceanModelSimulation <: SurfaceModelSimulation end

"""
getfield(sim::ComponentModelSimulation, val::Val)
get_field(sim::AtmosModelSimulation, val::Val)
A getter function that should not allocate. Here we implement a default that
will raise an error if `get_field` isn't defined for all required fields of
a component model.
an atmosphere component model.
"""
get_field(sim::ComponentModelSimulation, val::Val) = error("undefined field $val for " * name(sim))
get_field(
sim::AtmosModelSimulation,
val::Union{
Val{:air_density},
Val{:air_temperature},
Val{:energy},
Val{:radiative_energy_flux_toa},
Val{:height_int},
Val{:height_sfc},
Val{:liquid_precipitation},
Val{:radiative_energy_flux_sfc},
Val{:snow_precipitation},
Val{:turblent_energy_flux},
Val{:turbulent_moisture_flux},
Val{:thermo_state_int},
Val{:uv_int},
Val{:water},
},
) = get_field_error(sim, val)

"""
get_field(sim::SurfaceModelSimulation, val::Val)
A getter function that should not allocate. Here we implement a default that
will raise an error if `get_field` isn't defined for all required fields of
a surface component model.
"""
get_field(
sim::SurfaceModelSimulation,
val::Union{
Val{:air_density},
Val{:area_fraction},
Val{:beta},
Val{:roughness_buoyancy},
Val{:roughness_momentum},
Val{:surface_albedo},
Val{:surface_humidity},
Val{:surface_temperature},
},
) = get_field_error(sim, val)

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


"""
SurfaceStub
Expand Down Expand Up @@ -157,23 +199,39 @@ function get_field(sim::ComponentModelSimulation, val::Val, colidx::Fields.Colum
end

"""
update_field!(::ComponentModelSimulation, ::Val, _...)
update_field!(::AtmosModelSimulation, ::Val, _...)
Default functions for updating fields at each timestep in an atmosphere
component model simulation. This should be extended by component models.
If it isn't extended, the field won't be updated and a warning will be raised.
"""
update_field!(
sim::AtmosModelSimulation,
val::Union{Val{:co2}, Val{:surface_albedo}, Val{:surface_temperature}, Val{:turbulent_fluxes}},
_,
) = update_field_warning(sim, val)

Default function for updating fields at each timestep in a component model
simulation. This should be extended by component models. If it isn't extended,
the field won't be updated and a warning will be raised.
"""
update_field!(sim::ComponentModelSimulation, val::Val{:air_density}, _) = update_field_warning(sim, val)
update_field!(sim::ComponentModelSimulation, val::Val{:area_fraction}, _) = update_field_warning(sim, val)
update_field!(sim::ComponentModelSimulation, val::Val{:co2}, _) = update_field_warning(sim, val)
update_field!(sim::ComponentModelSimulation, val::Val{:liquid_precipitation}, _) = update_field_warning(sim, val)
update_field!(sim::ComponentModelSimulation, val::Val{:radiative_energy_flux}, _) = update_field_warning(sim, val)
update_field!(sim::ComponentModelSimulation, val::Val{:snow_precipitation}, _) = update_field_warning(sim, val)
update_field!(sim::ComponentModelSimulation, val::Val{:surface_albedo}, _) = update_field_warning(sim, val)
update_field!(sim::ComponentModelSimulation, val::Val{:surface_temperature}, _) = update_field_warning(sim, val)
update_field!(sim::ComponentModelSimulation, val::Val{:turbulent_energy_flux}, _) = update_field_warning(sim, val)
update_field!(sim::ComponentModelSimulation, val::Val{:turbulent_fluxes}, _) = update_field_warning(sim, val)
update_field!(sim::ComponentModelSimulation, val::Val{:turbulent_moisture_flux}, _) = update_field_warning(sim, val)
update_field!(::SurfaceModelSimulation, ::Val, _...)
Default functions for updating fields at each timestep in an atmosphere
component model simulation. This should be extended by component models.
If it isn't extended, the field won't be updated and a warning will be raised.
"""
update_field!(
sim::SurfaceModelSimulation,
val::Union{
Val{:air_density},
Val{:area_fraction},
Val{:liquid_precipitation},
Val{:radiative_energy_flux_sfc},
Val{:snow_precipitation},
Val{:turbulent_energy_flux},
Val{:turbulent_moisture_flux},
},
_,
) = update_field_warning(sim, val)

update_field_warning(sim, val::Val{X}) where {X} =
@warn("`update_field!` is not extended for the `$X` field of " * name(sim) * ": skipping update.", maxlog = 1)

Expand Down
12 changes: 6 additions & 6 deletions test/conservation_checker_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ struct TestAtmos{I} <: Interfacer.AtmosModelSimulation
i::I
end
name(s::TestAtmos) = "TestAtmos"
get_field(s::TestAtmos, ::Val{:F_radiative_TOA}) = ones(s.i.space) .* 200
get_field(s::TestAtmos, ::Val{:radiative_energy_flux_toa}) = ones(s.i.space) .* 200
get_field(s::TestAtmos, ::Val{:water}) = ones(s.i.space) .* 1
function get_field(s::TestAtmos, ::Val{:energy})
FT = Domains.float_type(Meshes.domain(s.i.space.grid.topology.mesh))
Expand Down Expand Up @@ -74,13 +74,13 @@ for FT in (Float32, Float64)

# coupler fields
cf = (;
F_radiative_TOA = Fields.ones(space),
radiative_energy_flux_toa = Fields.ones(space),
P_net = Fields.zeros(space),
P_liq = Fields.zeros(space),
P_snow = Fields.zeros(space),
F_turb_moisture = Fields.zeros(space),
)
@. cf.F_radiative_TOA = 200
@. cf.radiative_energy_flux_toa = 200
@. cf.P_liq = -100

# init
Expand All @@ -103,7 +103,7 @@ for FT in (Float32, Float64)
)

# set non-zero radiation and precipitation
F_r = cf.F_radiative_TOA
F_r = cf.radiative_energy_flux_toa
P = cf.P_liq
Δt = cs.Δt_cpl

Expand Down Expand Up @@ -152,13 +152,13 @@ for FT in (Float32, Float64)

# coupler fields
cf = (;
F_radiative_TOA = Fields.ones(space),
radiative_energy_flux_toa = Fields.ones(space),
P_net = Fields.zeros(space),
P_liq = Fields.zeros(space),
P_snow = Fields.zeros(space),
F_turb_moisture = Fields.zeros(space),
)
@. cf.F_radiative_TOA = 200
@. cf.radiative_energy_flux_toa = 200
@. cf.P_liq = -100

# init
Expand Down
Loading

0 comments on commit 64df52d

Please sign in to comment.