AtmosphereModel microphysics interface

This document describes the interface for embedding microphysical processes into AtmosphereModel. The interface consists of eight functions that must be implemented for any microphysics scheme to work with AtmosphereModel.

Overview

The microphysics interface consists of seven functions, each of which must be implemented to complete a microphysics implementation in AtmosphereModel:

Example implementation

To illustrate the development of a new microphysics scheme, we implement a simple microphysics scheme that represents droplet and ice particle nucleation with constant-rate relaxation of specific humidity to saturation.

using Breeze

struct ExplicitMicrophysics{FT}
    vapor_to_liquid :: FT
    vapor_to_ice :: FT
end

Prognostic field names and materializing prognostic + diagnostic fields

This scheme is fully prognostic, which means we must carry around vapor, liquid and ice density as prognostic variables,

import Breeze.AtmosphereModels: prognostic_field_names

prognostic_field_names(::ExplicitMicrophysics) = (:ρqᵛ, :ρqˡ, :ρqⁱ)
prognostic_field_names (generic function with 11 methods)
Note

The names of prognostic fields defined by prognostic_field_names are crucial to the user interface, because users can interact them and set! their initial conditions. The names of variables should be carefully chosen to be concise, mathematical forms that are consistent with Breeze conventions.

When we materialize the microphysics fields, we must include all of the prognostic fields in addition to diagnostic fields (this behavior may change in the future):

import Breeze.AtmosphereModels: materialize_microphysical_fields

function materialize_microphysical_fields(::ExplicitMicrophysics, grid, boundary_conditions)
    ρqᵛ = CenterField(grid, boundary_conditions=boundary_Conditions.ρqᵛ)
    ρqˡ = CenterField(grid, boundary_conditions=boundary_Conditions.ρqˡ)
    ρqⁱ = CenterField(grid, boundary_conditions=boundary_Conditions.ρqⁱ)
    qᵛ = CenterField(grid)
    return (; ρqˡ, ρqⁱ, ρqᵛ, qᵛ)
end
materialize_microphysical_fields (generic function with 7 methods)

The tendencies for

import Breeze.AtmosphereModels: microphysical_tendency

using Breeze.Thermodynamics:
    PlanarLiquidSurface,
    PlanarIceSurface

@inline function microphysical_tendency(i, j, k, grid, em::ExplicitMicrophysics, ::Val{:ρqˡ}, ρ, μ, 𝒰, constants)
    ρⁱʲᵏ = @inbounds ρ[i, j, k]
    T = temperature(𝒰, constants)
    q⁺ˡ = saturation_specific_humidity(T, ρⁱʲᵏ, constants, PlanarLiquidSurface())
    τᵛˡ = em.vapor_to_liquid
    return @inbounds ρⁱʲᵏ * (μ.qᵛ[i, j, k] - q⁺ˡ) / τᵛˡ
end

@inline function microphysical_tendency(i, j, k, grid,
    em::ExplicitMicrophysics, ::Val{:ρqⁱ}, ρ, μ, 𝒰, constants)

    ρⁱʲᵏ = @inbounds ρ[i, j, k]
    T = temperature(𝒰, constants)
    q⁺ⁱ = saturation_specific_humidity(T, ρⁱʲᵏ, constants, PlanarIceSurface())
    τᵛⁱ = em.vapor_to_ice
    qᵛ = @inbounds μ.qᵛ[i, j, k]

    return ρⁱʲᵏ * (qᵛ - q⁺ⁱ) / τᵛⁱ
end

@inline function microphysical_tendency(i, j, k, grid,
    em::ExplicitMicrophysics, ::Val{:ρqᵛ}, ρ, μ, 𝒰, constants)

    Sᵛˡ = microphysical_tendency(i, j, k, grid, em, Val(:ρvˡ), ρ, μ, 𝒰, constants)
    Sᵛⁱ = microphysical_tendency(i, j, k, grid, em, Val(:ρvⁱ), ρ, μ, 𝒰, constants)
    return - Sᵛˡ - Sᵛⁱ
end
microphysical_tendency (generic function with 12 methods)

Note we have included the diagnostic field qᵛ (the vapor mass fraction, aka "specific humidity") in addition to the three prognostic fields representing vapor, liquid and ice density.

Prognostic field names and materializing prognostic + diagnostic fields

import Breeze.AtmosphereModels:
    update_microphysical_fields!,
    compute_moisture_fraction

@inline update_microphysical_fields!(μ, em::ExplicitMicrophysics, i, j, k, grid, ρ, state, constants) =
    @inbounds μ.qᵛ[i, j, k] = state.moisture_mass_fractions.vapor

@inline function compute_moisture_fractions(i, j, k, grid,
    ::ExplicitMicrophysics, ρ, qᵗ, microphysical_fields)

    @inbounds begin
        qᵛ = microphysical_fields.qᵛ[i, j, k]
        qˡ = microphysical_fields.ρqˡ[i, j, k] / ρ
        qⁱ = microphysical_fields.ρqⁱ[i, j, k] / ρ
    end

    return MoistureMassFractions(qᵛ, qˡ, qⁱ)
end
compute_moisture_fractions (generic function with 1 method)

This is a fully prognostic scheme, so there is no adjustment,

import Breeze.AtmosphereModels: maybe_adjust_thermodynamic_state

@inline maybe_adjust_thermodynamic_state(state, ::ExplicitMicrophysics, μ, qᵗ, constants) = state
maybe_adjust_thermodynamic_state (generic function with 6 methods)