Skip to content

Commit 2cb2b8d

Browse files
committed
add tests for atmos/surface fluxes
1 parent 6f34cd2 commit 2cb2b8d

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#
2+
# Flux consistency test (AMIP + bucket land)
3+
#
4+
# This test sets up an AMIP coupled simulation (atmosphere + bucket land + prescribed
5+
# ocean + prescribed sea ice), advances one coupling step, and verifies that the
6+
# surface radiative flux seen by the atmosphere matches what each surface model
7+
# computes/stores:
8+
# - Atmosphere: uses `sim.integrator.p.radiation.ᶠradiation_flux` (positive downward).
9+
# - Bucket land: compares against `sim.integrator.p.bucket.R_n` on land-dominant cells.
10+
# - Prescribed ocean: skipped — SST is prescribed so radiative fluxes are not computed
11+
# in the same way.
12+
# - Prescribed sea ice: not stored directly; compute using cache fields and compare
13+
# to the atmospheric flux on ice-dominant cells.
14+
15+
import Test: @test, @testset
16+
17+
# Use the AMIP setup helpers to construct a coupled simulation
18+
include(joinpath("..", "setup_run.jl"))
19+
20+
@testset "surface radiative flux consistency (AMIP + bucket land)" begin
21+
# Build AMIP configuration used in CI by default
22+
config_file = joinpath(pkgdir(ClimaCoupler), "config/ci_configs/amip_default.yml")
23+
config_dict = get_coupler_config_dict(config_file)
24+
25+
# Make sure radiation is computed during the first step
26+
config_dict["dt_rad"] = config_dict["dt"]
27+
28+
# Construct coupled simulation and run one coupling step
29+
cs = CoupledSimulation(config_dict)
30+
step!(cs)
31+
boundary_space = Interfacer.boundary_space(cs)
32+
33+
# Unpack component models
34+
(; atmos_sim, land_sim, ocean_sim, ice_sim) = cs.model_sims
35+
36+
# Atmosphere: radiative flux on the surface interface
37+
# Convention: positive downward to the surface
38+
atmos_flux = CC.Spaces.level(
39+
atmos_sim.integrator.p.radiation.ᶠradiation_flux.components.data.:1,
40+
CC.Utilities.half,
41+
)
42+
43+
# Bucket land: compare to net radiation stored in the bucket cache
44+
# Convention note: bucket R_n is stored with the same sign (positive upward,
45+
# see climaland_bucket.jl), so we compare atmos_flux ≈ R_n at land-dominant points.
46+
p = land_sim.integrator.p
47+
land_fraction = Interfacer.get_field(land_sim, Val(:area_fraction))
48+
land_flux = Interfacer.remap(land_sim.integrator.p.bucket.R_n, boundary_space)
49+
@. land_flux = ifelse(land_fraction 0, zero(land_flux), land_flux)
50+
51+
err_land = @. atmos_flux - land_flux
52+
@. err_land = ifelse(land_fraction 0, zero(err_land), err_land)
53+
@show "Bucket flux error: $(maximum(abs.(err_land)))"
54+
@test maximum(abs.(err_land)) < 5
55+
56+
# Prescribed ice: radiative fluxes aren't stored; compute from cache and compare
57+
p = ice_sim.integrator.p
58+
Y = ice_sim.integrator.u
59+
FT = eltype(Y)
60+
61+
# Radiative flux toward surface (positive downward)
62+
# TODO: get sigma from parameters
63+
σ = FT(5.67e-8)
64+
(; k_ice, h, T_base, ρ, c, α, ϵ) = p.params
65+
ice_rad_flux =
66+
(1 .- α) .* p.SW_d .+
67+
ϵ .* (p.LW_d .- σ .* Interfacer.get_field(ice_sim, Val(:surface_temperature)) .^ 4)
68+
@. ice_rad_flux = ifelse(p.area_fraction 0, zero(ice_rad_flux), ice_rad_flux)
69+
ice_fraction = Interfacer.get_field(ice_sim, Val(:area_fraction))
70+
71+
# Prescribed ocean: SST is prescribed, but for this test we can still compute
72+
# the radiative flux seen by the ocean surface using the same formula.
73+
α = Interfacer.get_field(ocean_sim, Val(:surface_direct_albedo))
74+
ϵ = Interfacer.get_field(ocean_sim, Val(:emissivity))
75+
ocean_rad_flux =
76+
(1 .- α) .* cs.fields.SW_d .+
77+
ϵ .* (
78+
cs.fields.LW_d .-
79+
σ .* Interfacer.get_field(ocean_sim, Val(:surface_temperature)) .^ 4
80+
)
81+
ocean_fraction = Interfacer.get_field(ocean_sim, Val(:area_fraction))
82+
@. ocean_rad_flux = ifelse(ocean_fraction 0, zero(ocean_rad_flux), ocean_rad_flux)
83+
84+
# Combine component fluxes by area-weighted sum (incl. bucket sign convention):
85+
combined_fluxes =
86+
.-land_fraction .* land_flux .+ ice_fraction .* ice_rad_flux .+
87+
ocean_fraction .* ocean_rad_flux
88+
err_fluxes = atmos_flux .+ combined_fluxes
89+
@show "Combined fluxes error: $(maximum(abs.(err_fluxes)))"
90+
@test maximum(abs.(err_fluxes)) < 8
91+
end

experiments/ClimaEarth/test/runtests.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ end
1717
@safetestset "component model test: slab ocean" begin
1818
include("component_model_tests/slab_ocean_tests.jl")
1919
end
20+
@safetestset "surface radiative flux consistency tests" begin
21+
include("fluxes_test.jl")
22+
end
2023
@safetestset "debug diagnostics: debug plots" begin
2124
include("debug_plots_tests.jl")
2225
end

0 commit comments

Comments
 (0)