Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
7c4c47a
start implementing rrtmgp interface
glwagner Nov 16, 2025
3232955
Merge branch 'main' into glw/rrtmgp
glwagner Nov 16, 2025
f5ecb5b
Update docs/src/radiative_transfer.md
glwagner Nov 18, 2025
ef1a99a
Update docs/src/radiative_transfer.md
glwagner Nov 18, 2025
5cc92b3
Update docs/src/radiative_transfer.md
glwagner Nov 18, 2025
95ff722
Update docs/src/radiative_transfer.md
glwagner Nov 18, 2025
bd386ed
Merge branch 'main' into glw/rrtmgp
glwagner Nov 18, 2025
83aeb6d
updates
glwagner Nov 20, 2025
9771b81
Merge branch 'main' into glw/rrtmgp
glwagner Dec 10, 2025
836340a
implement a single column radiation example
glwagner Dec 10, 2025
34de9ec
add to examples
glwagner Dec 10, 2025
c71fbf5
fix plots
glwagner Dec 10, 2025
cf7d174
Merge branch 'main' into glw/rrtmgp
glwagner Dec 10, 2025
c278eaf
update projects
glwagner Dec 10, 2025
dfbc019
Merge branch 'glw/rrtmgp' of https://github.com/NumericalEarth/Breeze…
glwagner Dec 10, 2025
fa3620d
Update src/RadiativeTransfer/radiative_transfer_model.jl
glwagner Dec 10, 2025
7e36b5d
fixes
glwagner Dec 10, 2025
a4eaae8
Merge branch 'glw/rrtmgp' of https://github.com/NumericalEarth/Breeze…
glwagner Dec 10, 2025
24bbae5
rm Dates from Project
glwagner Dec 10, 2025
618098f
add RelativeHumidity diagnostic (blame @navidcy for the scope creep)
glwagner Dec 10, 2025
836b392
Merge branch 'main' into glw/rrtmgp
glwagner Dec 11, 2025
6731428
Fix imports in RRTMGP extension
giordano Dec 11, 2025
655628b
Merge branch 'main' into glw/rrtmgp
glwagner Dec 11, 2025
fb9100f
`base_` -> `surface_`
giordano Dec 11, 2025
2e83e7d
reorganize
glwagner Dec 11, 2025
101b5de
Merge branch 'glw/rrtmgp' of https://github.com/NumericalEarth/Breeze…
glwagner Dec 11, 2025
c036fc5
updates
glwagner Dec 11, 2025
9932746
Merge branch 'main' into glw/rrtmgp
glwagner Dec 11, 2025
44c19dc
Apply suggestions from code review
giordano Dec 11, 2025
2f249d0
Fix import
giordano Dec 11, 2025
044900c
Apply suggestions from code review
giordano Dec 11, 2025
ee6946f
Remove duplicate include
giordano Dec 11, 2025
432e128
Remove stale imports
giordano Dec 11, 2025
8a67b0d
Add missing compat bound for `Dates`
giordano Dec 11, 2025
d49ff68
[docs] Add docstrings of `CelestialMechanics`
giordano Dec 11, 2025
c36b08f
[docs] Load RRTMGP to be able to include `update_state!` docstring
giordano Dec 11, 2025
f8191b4
fixes
glwagner Dec 12, 2025
825c9e5
Merge branch 'glw/rrtmgp' of https://github.com/NumericalEarth/Breeze…
glwagner Dec 12, 2025
d7f6894
Move GrayRadiativeTransferModel to src
glwagner Dec 12, 2025
fdcc688
deleted orphaned module
glwagner Dec 12, 2025
4cab27d
Remove stale imports
giordano Dec 12, 2025
f20106c
clean up
glwagner Dec 12, 2025
707fe92
Merge branch 'glw/rrtmgp' of https://github.com/NumericalEarth/Breeze…
glwagner Dec 12, 2025
5618153
update
glwagner Dec 12, 2025
9905e4a
Update docs/make.jl
glwagner Dec 12, 2025
a4d4394
fix tests
glwagner Dec 12, 2025
b9f2b9d
Merge branch 'glw/rrtmgp' of https://github.com/NumericalEarth/Breeze…
glwagner Dec 12, 2025
e241230
Update docs/src/radiative_transfer.md
glwagner Dec 12, 2025
908cc05
fix docs
glwagner Dec 12, 2025
f520e02
edit comment
glwagner Dec 12, 2025
4c6035d
comment
glwagner Dec 12, 2025
ad597a1
wrong date
glwagner Dec 12, 2025
4b37d96
delete unused example
glwagner Dec 12, 2025
240b329
Update src/AtmosphereModels/atmosphere_model.jl
glwagner Dec 12, 2025
24d71dd
fixes
glwagner Dec 12, 2025
f4f3abb
Merge branch 'glw/rrtmgp' of https://github.com/NumericalEarth/Breeze…
glwagner Dec 12, 2025
e135f8e
jeez
glwagner Dec 12, 2025
1264428
remoeve refs to radiative transfer model
glwagner Dec 12, 2025
dfaf629
redesign interface for radiation
glwagner Dec 12, 2025
f23c5b8
refactor to generic RadiativeTransferModel interface
glwagner Dec 12, 2025
d6c154f
Merge branch 'main' into glw/rrtmgp
glwagner Dec 12, 2025
79139ad
dont import Face
glwagner Dec 12, 2025
3cc0bce
fix show method
glwagner Dec 12, 2025
dc28ded
fix docs
glwagner Dec 12, 2025
adef7c5
fix
glwagner Dec 12, 2025
01edcb9
undefined export!
glwagner Dec 12, 2025
6b20e9d
Merge branch 'main' into glw/rrtmgp
glwagner Dec 13, 2025
5fc0971
probel bibtex ref
navidcy Dec 13, 2025
b36f44e
fix some citations
navidcy Dec 13, 2025
eda8556
don't define σ; reserve for Stefan-Boltzman
navidcy Dec 13, 2025
1a8d6d5
few tweaks
navidcy Dec 13, 2025
e9df6ab
remove extra const
glwagner Dec 13, 2025
3c398f7
proper docstring
glwagner Dec 13, 2025
c4dd760
fix RadiativeTransferModel show method
navidcy Dec 13, 2025
da3cbc2
remove unnecessary white space
navidcy Dec 13, 2025
55f1954
Merge branch 'main' into glw/rrtmgp
giordano Dec 15, 2025
e36d898
simplify pot temp prof in radiation example
glwagner Dec 15, 2025
4e35b8a
add a basic test for set in unit tests
glwagner Dec 15, 2025
29afe67
rm unused materalize plus tests for Field materiazliation
glwagner Dec 15, 2025
2c88a0e
bugfix
glwagner Dec 15, 2025
8c340b8
clean up radiation tests
glwagner Dec 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ makedocs(
"Overview" => "microphysics/microphysics_overview.md",
"Warm phase saturation adjustment" => "microphysics/saturation_adjustment.md",
],
"Radiative Transfer" => "radiative_transfer.md",
"Dycore equations and algorithms" => "dycore_equations_algorithms.md",
"Appendix" => Any[
"Notation" => "appendix/notation.md",
Expand Down
173 changes: 173 additions & 0 deletions docs/src/radiative_transfer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# Radiative Transfer

Breeze.jl integrates with [RRTMGP.jl](https://github.com/NumericalEarth/RRTMGP.jl) to provide radiative transfer capabilities for atmospheric simulations. The radiative transfer model computes longwave and shortwave radiative fluxes and heating rates, which are then incorporated into the moist static energy tendency equation.

## Overview

The radiative transfer implementation in Breeze uses a column-based approach, where each horizontal column of the 3D grid is treated independently. This allows efficient computation of radiative fluxes using RRTMGP's optimized column radiation solver.

## Basic Usage

To use radiative transfer in a Breeze simulation, create a `RadiativeTransferModel` and pass it to the `AtmosphereModel` constructor:

```julia
using Breeze
using Oceananigans

# Create grid
grid = RectilinearGrid(CPU(), Float64; size=(32, 32, 64), x=(0, 10_000), y=(0, 10_000), z=(0, 20_000))
Comment thread
glwagner marked this conversation as resolved.
Outdated

# Create radiative transfer model
rtm = RadiativeTransferModel(
grid;
surface_emissivity = 0.98,
surface_albedo_direct = 0.1,
surface_albedo_diffuse = 0.1,
cos_zenith = 0.5,
toa_solar_flux = 1360.0,
toa_longwave_flux = 0.0
)

# Create atmosphere model with radiative transfer
model = AtmosphereModel(
grid;
radiative_transfer = rtm
)
```

## Column Model Example

Here we demonstrate a simple column model calculation of radiative fluxes. This example sets up a single-column atmosphere and computes radiative heating rates.

```julia
using Breeze
using Oceananigans
using CairoMakie

# Create a single-column grid (1x1 horizontal, 64 vertical levels)
Comment thread
glwagner marked this conversation as resolved.
Outdated
grid = RectilinearGrid(CPU(), Float64;
size=(1, 1, 64),
x=(0, 1),
y=(0, 1),
z=(0, 20_000)
)
Comment thread
glwagner marked this conversation as resolved.
Outdated

# Thermodynamic constants
thermo = ThermodynamicConstants(Float64)
Comment thread
glwagner marked this conversation as resolved.
Outdated

# Reference state
reference_state = ReferenceState(grid, thermo,
base_pressure = 101325.0,
potential_temperature = 288.0
)

formulation = AnelasticFormulation(reference_state)

# Create radiative transfer model
rtm = RadiativeTransferModel(
grid;
surface_emissivity = 0.98,
surface_albedo_direct = 0.1,
surface_albedo_diffuse = 0.1,
cos_zenith = 0.5, # Solar zenith angle cosine
toa_solar_flux = 1360.0, # Top-of-atmosphere solar flux [W/m²]
toa_longwave_flux = 0.0
)

# Create atmosphere model
model = AtmosphereModel(
grid;
thermodynamics = thermo,
formulation = formulation,
radiative_transfer = rtm
)

# Initialize with a temperature profile
# Simple linear temperature profile decreasing with height
set!(model.temperature, (x, y, z) -> 288.0 - 0.0065 * z)

# Update model state
update_state!(model)

# Update radiative fluxes
Breeze.AtmosphereModels._update_radiative_fluxes!(rtm, model)

# Extract heating rates
nz = size(grid, 3)
heating_rate = zeros(Float64, nz)

using Oceananigans: interior
using Oceananigans.Grids: znode
using GPUArraysCore: @allowscalar

@allowscalar begin
ρᵣ = model.formulation.reference_state.density
for k in 1:nz
hr = Breeze.AtmosphereModels._radiative_heating_rate(
1, 1, k, grid, rtm,
ρᵣ,
thermo
)
# Convert from energy density tendency to heating rate per unit mass
heating_rate[k] = hr / ρᵣ[1, 1, k]
end
end

# Extract height levels for plotting
z_lev = [znode(1, 1, k, grid, Center(), Center(), Center()) for k in 1:nz]

# Plot heating rate profile
using CairoMakie

fig = Figure(resolution = (600, 400))
ax = Axis(fig[1, 1],
xlabel = "Heating Rate [K/day]",
ylabel = "Height [m]",
title = "Radiative Heating Rate Profile"
)

# Convert to K/day
cp = thermo.dry_air.heat_capacity
seconds_per_day = 86400.0
heating_rate_k_per_day = heating_rate .* seconds_per_day ./ cp

lines!(ax, heating_rate_k_per_day, z_lev, linewidth = 2)
fig
```

## Surface Properties

The `RadiativeTransferModel` requires several surface properties that are not part of the `AtmosphereModel`:

- **Surface temperature**: Temperature at the surface (can be extracted from model or specified)
- **Surface emissivity**: Longwave emissivity of the surface (typically 0.95-0.99)
- **Surface albedo (direct)**: Albedo for direct solar radiation
- **Surface albedo (diffuse)**: Albedo for diffuse solar radiation
- **Cosine of solar zenith angle**: Determines solar insolation
- **TOA solar flux**: Top-of-atmosphere solar flux [W/m²]
- **TOA longwave flux**: Top-of-atmosphere longwave flux [W/m²] (usually 0)

These properties are stored in the `RadiativeTransferModel` and can be updated as needed during the simulation.

## Reference Pressure

Breeze uses the **reference pressure** from the anelastic formulation for radiative transfer calculations, not the total pressure. This is consistent with the anelastic approximation where pressure perturbations are small compared to the reference state.

## Integration with Dynamics

Radiative heating is automatically added to the moist static energy tendency equation when a `RadiativeTransferModel` is provided. The heating rate is computed from flux differences:

```math
\frac{\partial (\rho e)}{\partial t} = \ldots + \rho_r \frac{g}{c_p} \frac{F_{k+1} - F_k}{\Delta p}
```

where ``F_k`` is the net radiative flux at level ``k``, ``\Delta p`` is the pressure difference across the layer, ``g`` is gravitational acceleration, ``c_p`` is specific heat capacity, and ``\rho_r`` is the reference density.

## Gray Atmosphere Model

The current implementation uses a gray atmosphere radiation model, which treats the atmosphere as having a single effective absorption coefficient. This is suitable for initial testing and development. Future versions will support full spectral radiation using RRTMGP's band-by-band calculations.

## Architecture Support

The radiative transfer implementation supports both CPU and GPU architectures. The grid conversion utilities automatically handle the conversion between Oceananigans' 3D grid format and RRTMGP's column-based format, including proper handling of CPU and GPU arrays.

27 changes: 27 additions & 0 deletions ext/RRTMGPExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module RRTMGPExt

import Breeze
import RRTMGP

# Load the RadiativeTransfer module when RRTMGP is available
include(joinpath(@__DIR__, "..", "src", "RadiativeTransfer", "RadiativeTransfer.jl"))

using .RadiativeTransfer

# Re-export RadiativeTransferModel in Breeze module
const RadiativeTransferModel = RadiativeTransfer.RadiativeTransferModel

# Make functions available to AtmosphereModels module by adding them to the module
# This allows the functions to be called from dynamics_kernel_functions.jl
Breeze.AtmosphereModels.eval(quote
const _radiative_heating_rate = $(RadiativeTransfer._radiative_heating_rate)
const _update_radiative_fluxes! = $(RadiativeTransfer._update_radiative_fluxes!)
end)

# Export RadiativeTransferModel from Breeze
Breeze.eval(quote
export RadiativeTransferModel
end)

end # module

9 changes: 6 additions & 3 deletions src/AtmosphereModels/atmosphere_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ end
formulation_pressure_solver(formulation, grid) = nothing

mutable struct AtmosphereModel{Frm, Arc, Tst, Grd, Clk, Thm, Den, Mom, Eng, Mse, Moi, Mfr,
Tmp, Prs, Ppa, Sol, Vel, Trc, Adv, Cor, Frc, Mic, Cnd, Cls, Cfs} <: AbstractModel{Tst, Arc}
Tmp, Prs, Ppa, Sol, Vel, Trc, Adv, Cor, Frc, Mic, Cnd, Cls, Cfs, Rad} <: AbstractModel{Tst, Arc}
architecture :: Arc
grid :: Grd
clock :: Clk
Expand All @@ -59,6 +59,7 @@ mutable struct AtmosphereModel{Frm, Arc, Tst, Grd, Clk, Thm, Den, Mom, Eng, Mse,
timestepper :: Tst
closure :: Cls
closure_fields :: Cfs
radiative_transfer :: Rad
end

function default_formulation(grid, thermo)
Expand Down Expand Up @@ -108,7 +109,8 @@ function AtmosphereModel(grid;
advection = Centered(order=2),
closure = nothing,
microphysics = nothing, # WarmPhaseSaturationAdjustment(),
timestepper = :RungeKutta3)
timestepper = :RungeKutta3,
radiative_transfer = nothing)

arch = grid.architecture
tracers = tupleit(tracers) # supports tracers=:c keyword argument (for example)
Expand Down Expand Up @@ -185,7 +187,8 @@ function AtmosphereModel(grid;
microphysical_fields,
timestepper,
closure,
closure_fields)
closure_fields,
radiative_transfer)

update_state!(model)

Expand Down
15 changes: 13 additions & 2 deletions src/AtmosphereModels/dynamics_kernel_functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -129,15 +129,26 @@ end
moisture_mass_fraction,
thermo,
microphysical_fields,
microphysics)
microphysics,
radiative_transfer)

# Compute the buoyancy flux term, ρᵣ w b
buoyancy_flux = ℑzᵃᵃᶜ(i, j, k, grid, ρ_w_bᶜᶜᶠ, velocities.w, reference_density,
temperature, moisture_mass_fraction, formulation, thermo)

# Compute radiative heating if radiative transfer is enabled
radiative_heating = if radiative_transfer === nothing
zero(eltype(energy_density))
else
# Call radiative heating rate function
# Note: This will be available when RRTMGP extension is loaded
_radiative_heating_rate(i, j, k, grid, radiative_transfer, reference_density, thermo)
end

return ( - div_Uc(i, j, k, grid, advection, velocities, energy_density)
+ buoyancy_flux
- ∇_dot_Jᶜ(i, j, k, grid, reference_density, closure, closure_fields, id, energy, clock, model_fields, nothing)
# + microphysical_energy_tendency(i, j, k, grid, formulation, microphysics, microphysical_fields)
+ ρe_forcing(i, j, k, grid, clock, model_fields))
+ ρe_forcing(i, j, k, grid, clock, model_fields)
+ radiative_heating)
end
8 changes: 7 additions & 1 deletion src/AtmosphereModels/update_atmosphere_model_state.jl
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,15 @@ function compute_tendencies!(model::AnelasticModel)
ρe = model.energy_density
e = model.moist_static_energy
Fρe = model.forcing.ρe
# Update radiative fluxes if radiative transfer is enabled
if model.radiative_transfer !== nothing
# Call update function - will be available when RRTMGP extension is loaded
_update_radiative_fluxes!(model.radiative_transfer, model)
end

ρe_args = tuple(ρe, Val(1), e, Fρe, scalar_args...,
model.formulation, model.temperature,
model.moisture_mass_fraction, model.thermodynamics, model.microphysical_fields, model.microphysics)
model.moisture_mass_fraction, model.thermodynamics, model.microphysical_fields, model.microphysics, model.radiative_transfer)
launch!(arch, grid, :xyz, compute_moist_static_energy_tendency!, Gρe, grid, ρe_args)

ρqᵗ = model.moisture_density
Expand Down
3 changes: 3 additions & 0 deletions src/Breeze.jl
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,7 @@ using .Microphysics
include("TurbulenceClosures/TurbulenceClosures.jl")
using .TurbulenceClosures

# RadiativeTransfer extension will be loaded via ext/RRTMGPExt.jl when RRTMGP is available
# Export will be added by the extension

end # module Breeze
10 changes: 10 additions & 0 deletions src/RadiativeTransfer/RadiativeTransfer.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module RadiativeTransfer

export RadiativeTransferModel, _radiative_heating_rate, _update_radiative_fluxes!

include("radiative_transfer_model.jl")
include("grid_conversion.jl")
include("atmosphere_model_integration.jl")

end # module

61 changes: 61 additions & 0 deletions src/RadiativeTransfer/atmosphere_model_integration.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""
Integration of RadiativeTransferModel with AtmosphereModel.
"""

using Oceananigans: CenterField
using Oceananigans.Operators: ℑzᵃᵃᶜ

import ..AtmosphereModels: AtmosphereModel, AnelasticFormulation
import ..grid_conversion: reshape_from_columns

"""
_radiative_heating_rate(i, j, k, grid, rtm::RadiativeTransferModel,
reference_density, thermo)

Compute the radiative heating rate contribution to moist static energy tendency
at grid point (i, j, k).

The heating rate is computed from flux differences:
hr = g * (flux_net[k+1] - flux_net[k]) / (cp * Δp)

and the contribution to energy density tendency is:
ρᵣ * hr

where ρᵣ is the reference density.
"""
@inline function _radiative_heating_rate(i, j, k, grid, rtm, reference_density, thermo)
# Get net fluxes
flux_net_lw = rtm.flux_lw.flux_net
flux_net_sw = rtm.flux_sw.flux_net

# Combine longwave and shortwave
flux_net_total = flux_net_lw .+ flux_net_sw

# Convert grid indices to column index
nx, ny, nz = size(grid)
icol = (j - 1) * nx + i

# Get flux at levels k and k+1 (fluxes are at levels, k is cell index)
# Level k corresponds to bottom of cell k
# Level k+1 corresponds to top of cell k
flux_bottom = @inbounds flux_net_total[k, icol]
flux_top = @inbounds flux_net_total[k+1, icol]

# Get pressure difference from atmospheric state
p_lev = rtm.atmospheric_state.p_lev
Δp = @inbounds p_lev[k+1, icol] - p_lev[k, icol]

# Get constants from thermodynamics
g = thermo.gravitational_acceleration
cp = thermo.dry_air.heat_capacity

# Compute heating rate per unit mass
hr = g * (flux_top - flux_bottom) / (cp * Δp)

# Get reference density at this point
ρᵣ = @inbounds reference_density[i, j, k]

# Return contribution to energy density tendency
return ρᵣ * hr
end

Loading
Loading