Surface Conditions
The lower boundary is where the atmosphere exchanges momentum, heat, and moisture with whatever lies beneath it (ocean, land, sea ice, or an idealized slab). ClimaAtmos collects everything controlling this boundary into one object, AtmosSurface, stored as atmos.surface. It is read at each step to fill p.precomputed.sfc_conditions, the surface fluxes and values consumed as boundary conditions by the dynamical core, radiation, and turbulence schemes.
The User Guide covers the options and how to choose; the Developer Guide covers the design, data flow, and how to extend or debug it.
User Guide
The four knobs
AtmosSurface has four fields, each with one purpose:
flux_scheme: computes turbulent fluxes from air–surface differences in temperature, humidity etc.temperature: sets the surface temperatureT_sfc.boundary_overrides: pins surface properties at user-specified values.surface_albedo: sets the shortwave reflectivity seen by radiation (distinct direct and diffuse components).
Set these directly when building a model, or let them be chosen by a setup or by YAML keys.
Flux scheme (flux_scheme)
The closure turning the surface–to–lowest-level difference into turbulent fluxes of momentum, heat, and moisture:
MoninObukhov: Monin–Obukhov Similarity Theory (MOST); fluxes follow from roughness length and near-surface stability. Heat fluxes (shf/lhforθ_flux/q_flux) orustarmay instead be prescribed (common for LES). For time-varying prescribed fluxes, passfluxesas a callable(t, FT) -> HeatFluxes/θAndQFluxes; it is resolved once per update (e.g. TRMM_LBA's diurnal SHF/LHF), whilez0/ustarstay constant.ExchangeCoefficients: bulk fluxes with fixedCd/Ch; simpler and cheaper, for idealized constant exchange coefficients (rather than coefficients determined by MOST).nothing: no atmos-side computation; an external driver supplies the conditions (see Coupling).
Temperature source (temperature)
What T_sfc is; the flux scheme then uses it (and surface humidity) for the air–surface gradients:
AnalyticTemperature:T_sfc = f(coordinates, params, t), per point. Covers a uniform constant (AnalyticTemperature(Returns(FT(300)))), a zonally-symmetric SST, or a time-varying profile (e.g., GABLS).SlabOceanTemperature: prognostic;T_sfcread fromY.sfc.T, evolved by a slab-ocean energy budget. The only type that adds a prognostic state.ExternalTemperature: read from a time-varying external input; valid only when the setup populatesexternal_forcing.surface_fields.CoupledTemperature: read from aFieldthe coupler writes into (see Coupling).
There is no dedicated constant type. Use AnalyticTemperature(Returns(FT(300))), wrapping the value in FT(...) to keep the broadcast type-stable.
Boundary overrides (boundary_overrides)
By default, surface values come from physics (pressure hydrostatically extrapolated, humidity saturated at T_sfc, zero winds, unit gustiness/moisture availability). SurfaceBoundaryOverrides pins a value to a fixed override; each field defaults to nothing (use the physical default). Currently only q_vap, u, v, and gustiness are consumed by surface_state_to_conditions; the p and beta fields are accepted and stored but not yet applied (the surface density comes from SurfaceFluxes.surface_density). Many idealized setups nevertheless set p for future use.
Albedo (surface_albedo)
Sets the shortwave reflectivity passed to the radiation scheme. Three models:
ConstantAlbedo: a single value applied to both direct and diffuse shortwave.RegressionFunctionAlbedo: the Jin et al. (2011) ocean parameterization, a solar-zenith-angle-dependent direct albedo plus a separate diffuse albedo, with wind-speed-dependent surface roughness.CouplerAlbedo: albedo supplied by an external driver (the coupler).
Direct vs. diffuse The model carries distinct direct_sw_surface_albedo and diffuse_sw_surface_albedo fields. ConstantAlbedo sets them equal, RegressionFunctionAlbedo computes them separately.
Spectral Both atmosphere-side models write a single value across every shortwave band, and the RegressionFunctionAlbedo scheme treats the refractive index as wavelength-independent. The RRTMGP interface arrays are band-resolved ((nbnd_sw, ncol)), so per-band albedo is a supported extension point but it would require a model that fills bands with distinct values.
Longwave surface reflectivity Albedo is shortwave-only; longwave surface reflectivity is handled separately through surface_emissivity.
See the Ocean Surface Albedo page for the Jin (2011) RegressionFunctionAlbedo formulation.
Choosing
flux_scheme and temperature are independent axes, and you set both (the other two fields take defaults). Each row below is a compatible pair, not an either/or:
| If you want… | flux_scheme | temperature |
|---|---|---|
| Stability-dependent fluxes over a prescribed SST | MoninObukhov(; z0 = …) | AnalyticTemperature(…) |
| Fixed-coefficient bulk fluxes | ExchangeCoefficients(; Cd, Ch) | AnalyticTemperature(…) |
| Prescribed heat fluxes (constant or time-varying) | MoninObukhov(; z0, shf, lhf) or MoninObukhov(; z0, fluxes = (t,FT)->…) | AnalyticTemperature(…) |
| An interactive slab ocean surface | MoninObukhov(…) | SlabOceanTemperature(…) |
| Surface temperature from data | MoninObukhov(…) | ExternalTemperature(…) |
| Coupler owns the surface (atmos skips fluxes) | nothing | unused; coupler writes sfc_conditions |
| Coupler sets SST; atmos computes fluxes | MoninObukhov(…) | CoupledTemperature(field) |
When you set shf/lhf (or θ_flux/q_flux), those fluxes are used as prescribed: MOST does not compute them. They appear under MoninObukhov only because the prescribed-flux path currently lives inside that type (a historical conflation; see the Developer Guide). The required z0 is used solely for the momentum closure, and only when ustar is not also prescribed: when both fluxes and ustar are given (as in every idealized LES setup), MOST does nothing and the surface is fully prescribed.
Setting the surface in a runscript
Build an AtmosSurface and hand it to AtmosModel. For example, Monin–Obukhov fluxes over a fixed 290 K sea surface with a constant albedo:
import ClimaAtmos as CA
import ClimaAtmos.SurfaceConditions as SC
FT = Float64
surface = CA.AtmosSurface(;
flux_scheme = SC.MoninObukhov(; z0 = FT(1e-4)),
temperature = SC.AnalyticTemperature(Returns(FT(290))),
surface_albedo = CA.ConstantAlbedo{FT}(; α = FT(0.07)),
# boundary_overrides defaults to all-`nothing` (physical defaults)
)
model = CA.AtmosModel(; surface, microphysics_model = CA.DryModel())Omitted fields take their defaults. You can also pass the surface fields directly to AtmosModel (CA.AtmosModel(; flux_scheme = …, temperature = …)), which assembles the AtmosSurface for you. To swap in an interactive slab ocean use temperature = SC.SlabOceanTemperature{FT}(); for prescribed heat fluxes, flux_scheme = SC.MoninObukhov(; z0 = FT(1e-4), shf = …, lhf = …).
File-driven surface
ForcingFromFile assembles the existing independent temperature and flux_scheme components described above (ExternalTemperature from the file's ts, with an interactive MoninObukhov flux scheme) rather than introducing another surface-policy hierarchy.
Configuring from YAML
Three of the four AtmosSurface fields are YAML-configurable (resolved by AtmosSurface(::AtmosConfig, params, FT; setup_type)). Setup-provided pieces take precedence for flux_scheme and boundary_overrides; for temperature, prognostic_surface: SlabOceanSST overrides the setup, while PrescribedSST falls back to the setup piece:
surface_setupsetsflux_scheme:"DefaultExchangeCoefficients"(default),"DefaultMoninObukhov", or"PrescribedSurface"(→nothing).prognostic_surfacesetstemperature:"PrescribedSST"(default) or"SlabOceanSST"(→SlabOceanTemperature).albedo_modelsetssurface_albedo:"ConstantAlbedo"(default),"RegressionFunctionAlbedo", or"CouplerAlbedo".
For example:
surface_setup: "DefaultMoninObukhov" # flux_scheme
prognostic_surface: "PrescribedSST" # temperature
albedo_model: "ConstantAlbedo" # surface_albedoThe fourth field, boundary_overrides, has no YAML key: it is populated by a setup's surface_condition (its overrides field), or left at the all-nothing default.
The two surface_setup markers, DefaultMoninObukhov and DefaultExchangeCoefficients, are lightweight placeholders that the config-driven constructor resolves into a concrete flux_scheme against params (a default roughness length or exchange coefficient).
Coupling to an external driver
The coupler still builds a complete AtmosSurface (all four fields are present); the two patterns differ only in the flux_scheme/temperature pair:
- Atmosphere skips surface computation:
flux_scheme = nothing(YAML"PrescribedSurface").update_surface_conditions!early-returns, sotemperatureis never read (leave it at its default).init_sfc_conditions_zero!pre-fills safe defaults at cache-build so RRTMGP / diagnostic EDMF never see uninitialized memory, and the coupler overwritessfc_conditionsdirectly. - Atmosphere computes fluxes from a coupler-supplied SST: a real
flux_scheme(e.g.MoninObukhov(…)) together withtemperature = CoupledTemperature(field). The coupler writesT_sfcintofieldbetween steps; the atmosphere reads it and computes the surface fluxes. Per-cell boundary overrides can be aFields.Field{<:SurfaceBoundaryOverrides}on the cache. Seetest/coupler_compatibility.jl.
Developer guide
The design rationale, data flow, dispatch chains, extension points, and debugging checklist are in Surface Conditions Internals in the Developer Guide.