Skip to content

Commit

Permalink
add coupler fields based on sim types
Browse files Browse the repository at this point in the history
  • Loading branch information
juliasloan25 committed Feb 26, 2025
1 parent e6ba001 commit e92eabc
Show file tree
Hide file tree
Showing 15 changed files with 167 additions and 60 deletions.
9 changes: 9 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ ClimaCoupler.jl Release Notes

### ClimaCoupler features

#### 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
component model specifies the coupler fields it requires for coupling,
and these are used to construct the set of coupler fields.
TOA radiation and net precipitation are added only if conservation is enabled.
The coupler fields are also now stored as a ClimaCore Field of NamedTuples,
rather than as a NamedTuple of ClimaCore Fields.

#### Remove extra `get_field` functions PR[#1203](https://github.com/CliMA/ClimaCoupler.jl/pull/1203)
Removes the `get_field` functions for `air_density` for all models, which
were unused except for the `BucketSimulation` method, which is replaced by a
Expand Down
6 changes: 6 additions & 0 deletions docs/src/interfacer.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ of SciMLBase.jl.
- `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.
- `add_coupler_fields!(coupler_field_names::Set, ::ComponentModelSimulation)`:
A function that adds names of quantities the coupler must exchange
to support this component model. These will be added for each model
in addition to the existing defaults: `z0m_sfc`, `z0b_sfc`, `beta`,
`F_turb_energy`, `F_turb_moisture`, `F_turb_ρτxz`, `F_turb_ρτyz`,
`temp1`, and `temp2`.

### ComponentModelSimulation - optional functions
- `update_sim!(::ComponentModelSimulation, csf, turbulent_fluxes)`: A
Expand Down
15 changes: 15 additions & 0 deletions experiments/ClimaEarth/components/atmosphere/climaatmos.jl
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,21 @@ end

Interfacer.reinit!(sim::ClimaAtmosSimulation) = Interfacer.reinit!(sim.integrator)

"""
Extend Interfacer.add_coupler_fields! to add the fields required for ClimaAtmosSimulation.
The fields added are:
- `:surface_direct_albedo` (for radiation)
- `:surface_diffuse_albedo` (for radiation)
- `:ϵ_sfc` (for radiation)
- `:T_sfc` (for radiation)
- `:q_sfc` (for moisture)
"""
function Interfacer.add_coupler_fields!(coupler_field_names, ::ClimaAtmosSimulation)
atmos_coupler_fields = [:surface_direct_albedo, :surface_diffuse_albedo, :ϵ_sfc, :T_sfc, :q_sfc]
push!(coupler_field_names, atmos_coupler_fields...)
end

function FieldExchanger.update_sim!(atmos_sim::ClimaAtmosSimulation, csf, turbulent_fluxes)

u = atmos_sim.integrator.u
Expand Down
14 changes: 14 additions & 0 deletions experiments/ClimaEarth/components/land/climaland_bucket.jl
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,20 @@ end
Interfacer.step!(sim::BucketSimulation, t) = Interfacer.step!(sim.integrator, t - sim.integrator.t, true)
Interfacer.reinit!(sim::BucketSimulation) = Interfacer.reinit!(sim.integrator)

"""
Extend Interfacer.add_coupler_fields! to add the fields required for BucketSimulation.
The fields added are:
- `:ρ_sfc`
- `:F_radiative` (for radiation input)
- `:P_liq` (for precipitation input)
- `:P_snow` (for precipitation input)
"""
function Interfacer.add_coupler_fields!(coupler_field_names, ::BucketSimulation)
bucket_coupler_fields = [:ρ_sfc, :F_radiative, :P_liq, :P_snow]
push!(coupler_field_names, bucket_coupler_fields...)
end

# extensions required by FluxCalculator (partitioned fluxes)
function FluxCalculator.update_turbulent_fluxes!(sim::BucketSimulation, fields::NamedTuple)
(; F_turb_energy, F_turb_moisture) = fields
Expand Down
12 changes: 12 additions & 0 deletions experiments/ClimaEarth/components/ocean/eisenman_seaice.jl
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,18 @@ end
Interfacer.step!(sim::EisenmanIceSimulation, t) = Interfacer.step!(sim.integrator, t - sim.integrator.t, true)
Interfacer.reinit!(sim::EisenmanIceSimulation) = Interfacer.reinit!(sim.integrator)

"""
Extend Interfacer.add_coupler_fields! to add the fields required for EisenmanIceSimulation.
The fields added are:
- `:ρ_sfc` (for humidity calculation)
- `:F_radiative` (for radiation input)
"""
function Interfacer.add_coupler_fields!(coupler_field_names, ::EisenmanIceSimulation)
eisenman_coupler_fields = [:ρ_sfc, :F_radiative]
push!(coupler_field_names, eisenman_coupler_fields...)
end

# extensions required by FluxCalculator (partitioned fluxes)
function FluxCalculator.update_turbulent_fluxes!(sim::EisenmanIceSimulation, fields::NamedTuple)
(; F_turb_energy) = fields
Expand Down
12 changes: 12 additions & 0 deletions experiments/ClimaEarth/components/ocean/prescr_seaice.jl
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,18 @@ Interfacer.update_field!(sim::PrescribedIceSimulation, ::Val{:turbulent_moisture
Interfacer.step!(sim::PrescribedIceSimulation, t) = Interfacer.step!(sim.integrator, t - sim.integrator.t, true)
Interfacer.reinit!(sim::PrescribedIceSimulation) = Interfacer.reinit!(sim.integrator)

"""
Extend Interfacer.add_coupler_fields! to add the fields required for PrescribedIceSimulation.
The fields added are:
- `:ρ_sfc` (for humidity calculation)
- `:F_radiative` (for radiation input)
"""
function Interfacer.add_coupler_fields!(coupler_field_names, ::PrescribedIceSimulation)
ice_coupler_fields = [:ρ_sfc, :F_radiative]
push!(coupler_field_names, ice_coupler_fields...)
end

# extensions required by FluxCalculator (partitioned fluxes)
function FluxCalculator.update_turbulent_fluxes!(sim::PrescribedIceSimulation, fields::NamedTuple)
(; F_turb_energy) = fields
Expand Down
12 changes: 12 additions & 0 deletions experiments/ClimaEarth/components/ocean/slab_ocean.jl
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,18 @@ end
Interfacer.step!(sim::SlabOceanSimulation, t) = Interfacer.step!(sim.integrator, t - sim.integrator.t, true)
Interfacer.reinit!(sim::SlabOceanSimulation) = Interfacer.reinit!(sim.integrator)

"""
Extend Interfacer.add_coupler_fields! to add the fields required for SlabOceanSimulation.
The fields added are:
- `:ρ_sfc` (for humidity calculation)
- `:F_radiative` (for radiation input)
"""
function Interfacer.add_coupler_fields!(coupler_field_names, ::SlabOceanSimulation)
ocean_coupler_fields = [:ρ_sfc, :F_radiative]
push!(coupler_field_names, ocean_coupler_fields...)
end

# extensions required by FluxCalculator (partitioned fluxes)
function FluxCalculator.update_turbulent_fluxes!(sim::SlabOceanSimulation, fields::NamedTuple)
(; F_turb_energy) = fields
Expand Down
43 changes: 18 additions & 25 deletions experiments/ClimaEarth/setup_run.jl
Original file line number Diff line number Diff line change
Expand Up @@ -459,34 +459,27 @@ function setup_and_run(config_dict::AbstractDict)
global `CoupledSimulation` struct, `cs`, below.
=#

## coupler exchange fields
coupler_field_names = (
:T_sfc,
:z0m_sfc,
:z0b_sfc,
:ρ_sfc,
:q_sfc,
:surface_direct_albedo,
:surface_diffuse_albedo,
:beta,
:F_turb_energy,
:F_turb_moisture,
:F_turb_ρτxz,
:F_turb_ρτyz,
:F_radiative,
:P_liq,
:P_snow,
:radiative_energy_flux_toa,
:P_net,
:temp1,
:temp2,
)
coupler_fields = NamedTuple{coupler_field_names}(ntuple(i -> zeros(boundary_space), length(coupler_field_names)))
Utilities.show_memory_usage()

## model simulations
model_sims = (atmos_sim = atmos_sim, ice_sim = ice_sim, land_sim = land_sim, ocean_sim = ocean_sim)

## coupler exchange fields
coupler_field_names = Interfacer.default_coupler_fields()
for sim in model_sims
Interfacer.add_coupler_fields!(coupler_field_names, sim)
end
# add coupler fields required to track conservation, if specified
if energy_check
push!(coupler_field_names, :radiative_energy_flux_toa, :P_net)
end
# remove any duplicate entries from the list
unique!(coupler_field_names)

# construct a Field of NamedTuples to store the coupler fields
key_types = (coupler_field_names...,)
val_types = Tuple{(FT for _ in 1:length(coupler_field_names))...}
nt_type = NamedTuple{key_types, val_types}
coupler_fields = zeros(nt_type, boundary_space)

## dates
dates = (; date = [date], date0 = [date0])

Expand Down
6 changes: 5 additions & 1 deletion experiments/ClimaEarth/test/debug_plots_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ plot_field_names(sim::Interfacer.SurfaceStub) = (:stub_field,)
atmos_fields = NamedTuple{atmos_names}(ntuple(i -> CC.Fields.zeros(boundary_space), length(atmos_names)))
surface_fields = NamedTuple{surface_names}(ntuple(i -> CC.Fields.zeros(boundary_space), length(surface_names)))
stub_fields = NamedTuple{stub_names}(ntuple(i -> CC.Fields.zeros(boundary_space), length(stub_names)))
coupler_fields = NamedTuple{coupler_names}(ntuple(i -> CC.Fields.zeros(boundary_space), length(coupler_names)))

key_types = (coupler_names...,)
val_types = Tuple{(FT for _ in 1:length(coupler_names))...}
nt_type = NamedTuple{key_types, val_types}
coupler_fields = CC.Fields.zeros(nt_type, boundary_space)

model_sims = (;
atmos_sim = ClimaAtmosSimulation(atmos_fields),
Expand Down
23 changes: 3 additions & 20 deletions experiments/ClimaEarth/user_io/debug_plots.jl
Original file line number Diff line number Diff line change
Expand Up @@ -95,32 +95,15 @@ function debug(cs::Interfacer.CoupledSimulation, dir = "debug", cs_fields_ref =
end

"""
debug(cs_fields::NamedTuple, dir, cs_fields_ref = nothing)
debug(cs_fields::CC.Fields.Field, dir, cs_fields_ref = nothing)
Plot useful coupler fields (in `field_names`) and save plots to a directory.
If `cs_fields_ref` is provided (e.g., using a copy of cs.fields from the initialization),
plot the anomalies of the fields with respect to `cs_fields_ref`.
"""
function debug(cs_fields::NamedTuple, dir, cs_fields_ref = nothing)
field_names = (
:surface_direct_albedo,
:surface_diffuse_albedo,
:F_radiative,
:F_turb_energy,
:F_turb_moisture,
:F_turb_ρτxz,
:F_turb_ρτyz,
:P_liq,
:P_snow,
:T_sfc,
:ρ_sfc,
:q_sfc,
:beta,
:z0b_sfc,
:z0m_sfc,
:radiative_energy_flux_toa,
)
function debug(cs_fields::CC.Fields.Field, dir, cs_fields_ref = nothing)
field_names = propertynames(cs_fields)
fig = Makie.Figure(size = (1500, 800))
min_square_len = ceil(Int, sqrt(length(field_names)))
for i in 1:min_square_len, j in 1:min_square_len
Expand Down
8 changes: 3 additions & 5 deletions src/ConservationChecker.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This module contains functions that check global conservation of energy and wate
module ConservationChecker

import ..Interfacer, ..Utilities
import ClimaCore as CC

export AbstractConservationCheck, EnergyConservationCheck, WaterConservationCheck, check_conservation!

Expand Down Expand Up @@ -75,11 +76,8 @@ function check_conservation!(
ccs = cc.sums
(; model_sims) = coupler_sim

boundary_space = coupler_sim.boundary_space # thin shell approx (boundary_space[z=0] = boundary_space[z_top])

FT = eltype(coupler_sim.fields[1])

total = 0
FT = CC.Spaces.undertype(coupler_sim.boundary_space)
total = FT(0)

# save surfaces
for sim in model_sims
Expand Down
6 changes: 3 additions & 3 deletions src/FluxCalculator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ function calculate_surface_air_density(atmos_sim::Interfacer.AtmosModelSimulatio
end

"""
partitioned_turbulent_fluxes!(model_sims::NamedTuple, fields::NamedTuple, boundary_space::CC.Spaces.AbstractSpace, surface_scheme, thermo_params::TD.Parameters.ThermodynamicsParameters)
partitioned_turbulent_fluxes!(model_sims::NamedTuple, fields::CC.Fields.Field, boundary_space::CC.Spaces.AbstractSpace, surface_scheme, thermo_params::TD.Parameters.ThermodynamicsParameters)
The current setup calculates the aerodynamic fluxes in the coupler (assuming no regridding is needed)
using adapter function `get_surface_fluxes!`, which calls `SurfaceFluxes.jl`. The coupler saves
Expand All @@ -127,15 +127,15 @@ TODO:
"""
function partitioned_turbulent_fluxes!(
model_sims::NamedTuple,
fields::NamedTuple,
fields::CC.Fields.Field,
boundary_space::CC.Spaces.AbstractSpace,
surface_scheme,
thermo_params::TD.Parameters.ThermodynamicsParameters,
)

atmos_sim = model_sims.atmos_sim
csf = fields
FT = eltype(csf[1])
FT = CC.Spaces.undertype(boundary_space)

# reset coupler fields
csf.F_turb_ρτxz .*= FT(0)
Expand Down
37 changes: 37 additions & 0 deletions src/Interfacer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,32 @@ Return the floating point type backing `T`: `T` can either be an object or a typ
"""
float_type(::CoupledSimulation{FT}) where {FT} = FT

"""
default_coupler_fields()
Return a list of default coupler fields needed to run a simulation.
"""
default_coupler_fields() = [
# fields needed for flux calculations and exchange
:z0m_sfc,
:z0b_sfc,
:beta,
:F_turb_energy,
:F_turb_moisture,
:F_turb_ρτxz,
:F_turb_ρτyz,
# fields used for temporary storage during calculations
:temp1,
:temp2,
]

# fields needed for most surface models (not ClimaLandSimulation)
# :ρ_sfc,
# :F_radiative,
# # fields needed for bucket/land
# :P_liq,
# :P_snow,

"""
ComponentModelSimulation
Expand Down Expand Up @@ -209,6 +235,17 @@ update_field!(
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)


"""
add_coupler_fields!(coupler_fields, sim::ComponentModelSimulation, fields)
A function to add fields to the set of coupler fields. This should be extended
by component models that require coupler fields beyond the defaults.
If this function isn't extended, no additional fields will be added.
"""
add_coupler_fields!(coupler_fields, sim::ComponentModelSimulation) = nothing

"""
name(::ComponentModelSimulation)
Expand Down
11 changes: 11 additions & 0 deletions src/surface_stub.jl
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,17 @@ The stub surface simulation is not updated by this function. Extends `SciMLBase.
"""
reinit!(::AbstractSurfaceStub) = nothing

"""
Extend Interfacer.add_coupler_fields! to add the fields required for AbstractSurfaceStub.
The fields added are:
- `:ρ_sfc` (for humidity calculation)
"""
function Interfacer.add_coupler_fields!(coupler_field_names, ::AbstractSurfaceStub)
surface_coupler_fields = [:ρ_sfc]
push!(coupler_field_names, surface_coupler_fields...)
end

"""
step!(::AbstractSurfaceStub, t)
Expand Down
13 changes: 7 additions & 6 deletions test/flux_calculator_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,10 @@ for FT in (Float32, Float64)
:F_turb_ρτyz,
:F_turb_moisture,
)
fields = NamedTuple{coupler_cache_names}(
ntuple(i -> CC.Fields.zeros(boundary_space), length(coupler_cache_names)),
)
key_types = (coupler_cache_names...,)
val_types = Tuple{(FT for _ in 1:length(coupler_cache_names))...}
nt_type = NamedTuple{key_types, val_types}
fields = zeros(nt_type, boundary_space)

# calculate turbulent fluxes
thermo_params = get_thermo_params(atmos_sim)
Expand Down Expand Up @@ -248,14 +249,14 @@ for FT in (Float32, Float64)
windspeed #-ρ_sfc * Ch * windspeed(sc) * (cp_m * ΔT + ΔΦ)

# check the coupler field update
@test isapprox(parent(shf_analytical), parent(fields.F_turb_energy), rtol = 1e-6)
@test isapprox(parent(shf_analytical), Array(parent(fields.F_turb_energy)), rtol = 1e-6)

# test the surface field update
@test parent(fields.F_turb_energy) == parent(ocean_sim.integrator.p.F_aero)
@test Array(parent(fields.F_turb_energy)) == parent(ocean_sim.integrator.p.F_aero)

# test the atmos field update
FieldExchanger.update_sim!(atmos_sim, fields, nothing)
@test parent(fields.F_turb_energy) == -parent(atmos_sim.integrator.p.energy_bc)
@test Array(parent(fields.F_turb_energy)) == -parent(atmos_sim.integrator.p.energy_bc)

end
@test Array(parent(fields.F_turb_moisture))[1] FT(0)
Expand Down

0 comments on commit e92eabc

Please sign in to comment.