Skip to content

Commit b663c28

Browse files
committed
wip: basic microphysics 1m interface
1 parent 784884d commit b663c28

File tree

5 files changed

+621
-0
lines changed

5 files changed

+621
-0
lines changed

src/Microphysics/Microphysics.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module Microphysics
2+
3+
import CloudMicrophysics
4+
5+
using Oceananigans.Grids: Center, xnode, ynode, znode
6+
using Oceananigans.Fields: ZeroField
7+
8+
import Oceananigans.Fields: CenterField
9+
10+
const CM1 = CloudMicrophysics.Microphysics1M
11+
const CMNe = CloudMicrophysics.MicrophysicsNonEq
12+
const CMP = CloudMicrophysics.Parameters
13+
14+
export AbstractMicrophysics,
15+
16+
include("interface.jl")
17+
include("process_rates.jl")
18+
include("sedimentation_velocities.jl")
19+
include("microphysics_1m.jl")
20+
21+
end # module

src/Microphysics/interface.jl

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#####
2+
##### Generic fallbacks for microphysics
3+
#####
4+
5+
@inline microphysics_rhs(i, j, k, grid, ::Nothing, val_tracer_name, clock, fields) = zero(grid)
6+
7+
"""
8+
update_tendencies!(mp, model)
9+
10+
Update prognostic tendencies after they have been computed.
11+
"""
12+
update_tendencies!(mp, model) = nothing
13+
14+
"""
15+
update_microphysics_state!(mp, model)
16+
17+
Update microphysics state variables. Called at the end of update_state!.
18+
"""
19+
update_microphysics_state!(mp, model) = nothing
20+
21+
@inline microphysics_drift_velocity(mp, val_tracer_name) = nothing
22+
@inline microphysics_auxiliary_fields(mp) = NamedTuple()
23+
24+
25+
"""
26+
AbstractMicrophysics
27+
28+
Abstract type for microphysics models with continuous form microphysics reaction
29+
functions. To define a microphysial relaionship the following functions must have methods
30+
defined where `Microphysics` is a subtype of `AbstractMicrophysics`:
31+
32+
- `(mp::Microphysics)(i, j, k, grid, ::Val{:tracer_name}, clock, fields)` which
33+
returns the microphysics reaction for for each tracer.
34+
35+
- `required_microphysics_tracers(::Microphysics)` which returns a tuple of
36+
required `tracer_names`.
37+
38+
- `required_microphysics_auxiliary_fields(::Microphysics)` which returns
39+
a tuple of required auxiliary fields.
40+
41+
- `microphysics_auxiliary_fields(mp::Microphysics)` which returns a `NamedTuple`
42+
of the models auxiliary fields.
43+
44+
- `microphysics_drift_velocity(mp::Microphysics, ::Val{:tracer_name})` which
45+
returns a velocity fields (i.e. a `NamedTuple` of fields with keys `u`, `v` & `w`)
46+
for each tracer.
47+
48+
- `update_microphysics_state!(mp::Microphysics, model)` (optional) to update the
49+
model state.
50+
"""
51+
52+
abstract type AbstractMicrophysics end
53+
54+
# Required for when a model is defined but not for all tracers
55+
@inline (mp::AbstractMicrophysics)(i, j, k, grid, val_tracer_name, clock, fields) = zero(grid)
56+
57+
@inline extract_microphysics_fields(i, j, k, grid, fields, names::NTuple{1}) =
58+
@inbounds tuple(fields[names[1]][i, j, k])
59+
60+
@inline extract_microphysics_fields(i, j, k, grid, fields, names::NTuple{2}) =
61+
@inbounds (fields[names[1]][i, j, k],
62+
fields[names[2]][i, j, k])
63+
64+
@inline extract_microphysics_fields(i, j, k, grid, fields, names::NTuple{N}) where N =
65+
@inbounds ntuple(n -> fields[names[n]][i, j, k], Val(N))
66+
67+
@inline function microphysics_transition(i, j, k, grid, mp::AbstractMicrophysics,
68+
val_tracer_name, clock, fields)
69+
70+
names_to_extract = tuple(required_microphysics_tracers(mp)...,
71+
required_microphysics_auxiliary_fields(mp)...)
72+
73+
fields_ijk = extract_microphysics_fields(i, j, k, grid, fields, names_to_extract)
74+
75+
x = xnode(i, j, k, grid, Center(), Center(), Center())
76+
y = ynode(i, j, k, grid, Center(), Center(), Center())
77+
z = znode(i, j, k, grid, Center(), Center(), Center())
78+
79+
return mp(val_tracer_name, x, y, z, clock.time, fields_ijk...)
80+
end
81+
82+
@inline (mp::AbstractMicrophysics)(val_tracer_name, x, y, z, t, fields...) = zero(t)
83+
84+
microphysics_tracernames(tracers) = keys(tracers)
85+
microphysics_tracernames(tracers::Tuple) = tracers
86+
87+
add_microphysics_tracer(tracers::Tuple, name, grid) = tuple(tracers..., name)
88+
add_microphysics_tracer(tracers::NamedTuple, name, grid) = merge(tracers, (; name => CenterField(grid)))
89+
90+
@inline function has_microphysics_tracers(fields, required_fields, grid)
91+
user_specified_tracers = [name in microphysics_tracernames(fields) for name in required_fields]
92+
93+
if !all(user_specified_tracers) && any(user_specified_tracers)
94+
throw(ArgumentError("The microphysics model you have selected requires $required_fields.\n" *
95+
"You have specified some but not all of these as tracers so may be attempting\n" *
96+
"to use them for a different purpose. Please either specify all of the required\n" *
97+
"fields, or none and allow them to be automatically added."))
98+
99+
elseif !any(user_specified_tracers)
100+
for field_name in required_fields
101+
fields = add_microphysics_tracer(fields, field_name, grid)
102+
end
103+
else
104+
fields = fields
105+
end
106+
107+
return fields
108+
end
109+
110+
"""
111+
validate_microphysics(tracers, auxiliary_fields, mp, grid, clock)
112+
113+
Ensure that `tracers` contains microphysics tracers and `auxiliary_fields`
114+
contains microphysics auxiliary fields.
115+
"""
116+
@inline function validate_microphysics(tracers, auxiliary_fields, mp, grid, clock)
117+
req_tracers = required_microphysics_tracers(mp)
118+
tracers = has_microphysics_tracers(tracers, req_tracers, grid)
119+
req_auxiliary_fields = required_microphysics_auxiliary_fields(mp)
120+
121+
all(field tracernames(auxiliary_fields) for field in req_auxiliary_fields) ||
122+
error("$(req_auxiliary_fields) must be among the list of auxiliary fields to use $(typeof(mp).name.wrapper)")
123+
124+
# Return tracers and aux fields so that users may overload and
125+
# define their own special auxiliary fields
126+
return tracers, auxiliary_fields
127+
end
128+
129+
const AbstractMicrophysicsOrNothing = Union{Nothing, AbstractMicrophysics}
130+
required_microphysics_tracers(::AbstractMicrophysicsOrNothing) = ()
131+
required_microphysics_auxiliary_fields(::AbstractMicrophysicsOrNothing) = ()
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
struct Microphysics1M{TH, SV, PAR} <: AbstractMicrophysics
2+
thermodynamics::TH
3+
sedimentation_velocities:: SV
4+
parameters:: PAR
5+
end
6+
7+
function Microphysics1M(grid; thermodynamics, parameters)
8+
sedimentation_velocities = (; ρq_rai = ZFaceField(grid), ρq_sno = ZFaceField(grid))
9+
Microphysics1M(thermodynamics, sedimentation_velocities, parameters)
10+
end
11+
12+
required_microphysics_tracers(::Microphysics1M) = (:ρq_tot, :ρq_liq, :ρq_ice, :ρq_rai, :ρq_sno, :ρe_tot)
13+
required_microphysics_auxiliary_fields(::Microphysics1M) = (:rho, :PAR)
14+
15+
@inline specific(ρ, values...) = (value/ρ for value in values)
16+
17+
@inline function (mp::Microphysics1M)(::Val{:ρq_liq}, x, y, z, t, ρ, ρq_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno, ρe_tot, PAR)
18+
dt = PAR.dt
19+
q_tot, q_liq, q_ice, q_rain, q_snow = specific(ρ, ρq_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno)
20+
T = air_temperature(mp.thermodynamics, ρe_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno)
21+
22+
cond_vapor_liquid = cloud_liquid_condensation_rate(mp, q_tot, q_liq, q_ice, q_rain, q_snow, ρ, T, dt)
23+
auto_liquid_rain = autoconversion_liquid_to_rain_rate(mp, q_liq, ρ, dt)
24+
acc_cloud_rain = accretion_cloud_rain_rate(mp, q_liq, q_rain, ρ, dt)
25+
acc_cloud_snow = accretion_cloud_snow_rate(mp, q_liq, q_snow, ρ, T, dt)
26+
27+
return ρ * (cond_vapor_liquid.liq + auto_liquid_rain.liq + acc_cloud_rain.liq + acc_cloud_snow.liq)
28+
end
29+
30+
@inline function (mp::Microphysics1M)(::Val{:ρq_ice}, x, y, z, t, ρ, ρq_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno, ρe_tot, PAR)
31+
dt = PAR.dt
32+
q_tot, q_liq, q_ice, q_rain, q_snow = specific(ρ, ρq_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno)
33+
T = air_temperature(mp.thermodynamics, ρe_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno)
34+
35+
cond_vapor_ice = cloud_ice_condensation_rate(mp, q_tot, q_liq, q_ice, q_rain, q_snow, ρ, T, dt)
36+
auto_ice_snow = autoconversion_ice_to_snow_rate(mp, q_ice, dt)
37+
acc_ice_snow = accretion_ice_snow_rate(mp, q_ice, q_snow, ρ, dt)
38+
acc_ice_rain_snow = accretion_ice_rain_to_snow_rate(mp, q_ice, q_rain, ρ, dt)
39+
40+
return ρ * (cond_vapor_ice.ice + auto_ice_snow.ice + acc_ice_snow.ice + acc_ice_rain_snow.ice)
41+
end
42+
43+
@inline function (mp::Microphysics1M)(::Val{:ρq_rai}, x, y, z, t, ρ, ρq_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno, ρe_tot, PAR)
44+
dt = PAR.dt
45+
q_tot, q_liq, q_ice, q_rain, q_snow = specific(ρ, ρq_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno)
46+
T = air_temperature(mp.thermodynamics, ρe_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno)
47+
48+
auto_liquid_rain = autoconversion_liquid_to_rain_rate(mp, q_liq, ρ, dt)
49+
acc_cloud_rain = accretion_cloud_rain_rate(mp, q_liq, q_rain, ρ, dt)
50+
acc_snow_rain = accretion_snow_rain_rate(mp, q_rain, q_snow, ρ, T, dt)
51+
acc_cloud_snow = accretion_cloud_snow_rate(mp, q_liq, q_snow, ρ, T, dt)
52+
sink_ice_rain = rain_sink_from_ice_rate(mp, q_ice, q_rain, ρ, dt)
53+
evaporation_rain = rain_evaporation_rate(mp, q_tot, q_liq, q_ice, q_rain, q_snow, ρ, T, dt)
54+
melt_snow_rain = snow_melt_rate(mp, q_snow, ρ, T, dt)
55+
56+
return ρ * (auto_liquid_rain.rain + acc_cloud_rain.rain + acc_cloud_snow.rain + acc_ice_rain_snow.rain + acc_snow_rain.rain + sink_ice_rain.rain + evaporation_rain.rain + melt_snow_rain.rain)
57+
end
58+
59+
@inline function (mp::Microphysics1M)(::Val{:ρq_sno}, x, y, z, t, ρ, ρq_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno, ρe_tot, PAR)
60+
dt = PAR.dt
61+
q_tot, q_liq, q_ice, q_rain, q_snow = specific(ρ, ρq_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno)
62+
T = air_temperature(mp.thermodynamics, ρe_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno)
63+
64+
auto_ice_snow = autoconversion_ice_to_snow_rate(microphysics, q_ice, dt_ft)
65+
acc_ice_snow = accretion_ice_snow_rate(microphysics, q_ice, q_snow, ρi, dt_ft)
66+
acc_cloud = accretion_cloud_snow_rate(microphysics, q_liq, q_snow, ρi, Ti, dt_ft)
67+
acc_ice_rain_snow = accretion_ice_rain_to_snow_rate(microphysics, q_ice, q_rain, ρi, dt_ft)
68+
acc_snow_rain = accretion_snow_rain_rate(microphysics, q_rain, q_snow, ρi, Ti, dt_ft)
69+
melt_snow_rain = snow_melt_rate(microphysics, q_snow, ρi, Ti, dt_ft)
70+
deposition_vapor_snow = snow_deposition_rate(microphysics, q_tot, q_liq, q_ice, q_rain, q_snow, ρi, Ti, dt_ft)
71+
72+
return ρ * (auto_ice_snow.snow + acc_ice_snow.snow + acc_cloud.snow + acc_ice_rain_snow.snow + acc_snow_rain.snow + melt_snow_rain.snow + deposition_vapor_snow.snow)
73+
end
74+
75+
@inline function (mp::Microphysics1M)(::Val{:ρq_tot}, x, y, z, t, ρ, ρq_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno, ρe_tot, PAR)
76+
dρ_liq = mp(:ρq_liq, x, y, z, t, ρ, ρq_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno, ρe_tot, PAR)
77+
dρ_ice = mp(:ρq_ice, x, y, z, t, ρ, ρq_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno, ρe_tot, PAR)
78+
dρ_rai = mp(:ρq_rai, x, y, z, t, ρ, ρq_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno, ρe_tot, PAR)
79+
dρ_sno = mp(:ρq_sno, x, y, z, t, ρ, ρq_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno, ρe_tot, PAR)
80+
return dq_liq + dq_ice + dq_rai + dq_sno
81+
end
82+
83+
@inline function (mp::Microphysics1M)(::Val{:ρe_tot}, x, y, z, t, ρ, ρq_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno, ρe_tot, PAR)
84+
dt = PAR.dt
85+
q_tot, q_liq, q_ice, q_rain, q_snow = specific(ρ, ρq_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno)
86+
T = air_temperature(mp.thermodynamics, ρe_tot, ρq_liq, ρq_ice, ρq_rai, ρq_sno)
87+
Lv = latent_heat_vapor(mp.thermodynamics, T)
88+
Ls = latent_heat_sublimation(mp.thermodynamics, T)
89+
Lf = latent_heat_fusion(mp.thermodynamics, T)
90+
91+
cond_vapor_liquid = cloud_liquid_condensation_rate(mp, q_tot, q_liq, q_ice, q_rain, q_snow, ρ, T, dt)
92+
cond_vapor_ice = cloud_ice_condensation_rate(mp, q_tot, q_liq, q_ice, q_rain, q_snow, ρ, T, dt)
93+
acc_cloud_snow = accretion_cloud_snow_rate(mp, q_liq, q_snow, ρ, T, dt)
94+
rain_sink_ice = rain_sink_from_ice_rate(mp, q_ice, q_rain, ρ, T, dt)
95+
acc_snow_rain = accretion_snow_rain_rate(mp, q_rain, q_snow, ρ, T, dt)
96+
rain_evap = rain_evaporation_rate(mp, q_tot, q_liq, q_ice, q_rain, q_snow, ρ, T, dt)
97+
snow_melt = snow_melt_rate(mp, q_snow, ρ, T, dt)
98+
snow_dep = snow_deposition_rate(mp, q_tot, q_liq, q_ice, q_rain, q_snow, ρ, T, dt)
99+
100+
l_vapor_liquid = Lv * cond_vapor_liquid.liq
101+
l_cond_ice = Ls * cond_vapor_ice.ice
102+
l_acc_cloud_snow = Lf * acc_cloud_snow.snow
103+
l_rain_sink_ice = Lf * rain_sink_ice.snow
104+
l_acc_snow_rain = Lf * acc_snow_rain.snow
105+
l_rain_evap = Lv * rain_evap.rain
106+
l_snow_melt = Lf * snow_melt.snow
107+
l_snow_dep = Ls * snow_dep.snow
108+
109+
return ρ * (l_vapor_liquid + l_cond_ice + l_acc_cloud_snow + l_rain_sink_ice + l_acc_snow_rain + l_rain_evap + l_snow_melt + l_snow_dep)
110+
end
111+
112+
@inline microphysics_sedimentation_velocity(mp::Microphysics1M, ::Val{:ρq_rai}) =
113+
(; u = ZeroField(), v = ZeroField(), w = mp.sedimentation_velocities.ρq_rai)
114+
115+
@inline microphysics_sedimentation_velocity(mp::Microphysics1M, ::Val{:ρq_sno}) =
116+
(; u = ZeroField(), v = ZeroField(), w = mp.sedimentation_velocities.ρq_sno)
117+
118+
function update_microphysics_state!(mp::Microphysics1M, model, kernel_parameters; active_cells_map = nothing)
119+
w_velocities = mp.sedimentation_velocities
120+
arch = architecture(model.grid)
121+
grid = model.grid
122+
density = model.density
123+
parameters = mp.parameters
124+
125+
exclude_periphery = true
126+
for tracer_name in sedimenting_tracers(mp)
127+
tracer_field = model.tracers[tracer_name]
128+
#tracer_w_velocity_bc = mp.sedimentation_velocities[tracer_name].boundary_conditions.immersed
129+
w_kernel_args = tuple(Val(tracer_name), density, tracer_field, parameters)
130+
#w_kernel_args = tuple(Val(tracer_name), density, tracer_field, parameters, tracer_w_velocity_bc)
131+
launch!(arch, grid, kernel_parameters, compute_sedimentation_velocity!,
132+
w_velocities[tracer_name], grid, w_kernel_args;
133+
active_cells_map, exclude_periphery)
134+
fill_halo_regions!(w_velocities[tracer_name])
135+
end
136+
137+
return nothing
138+
end
139+
140+
@kernel function compute_sedimentation_velocity!(w_sed, grid, args)
141+
i, j, k = @index(Global, NTuple)
142+
@inbounds w_sed[i, j, k] = w_sedimentation_velocity(i, j, k, grid, args...)
143+
end
144+
145+
@inline function w_sedimentation_velocity(i, j, k, grid, microphysics::Microphysics1M, ::Val{:ρq_rai}, ρ, ρq_rai)
146+
ρᶜᶜᶠ = ℑzᵃᵃᶠ(i, j, k, grid, ρ)
147+
ρq_raiᶜᶜᶠ = ℑzᵃᵃᶠ(i, j, k, grid, ρq_rai)
148+
return CM1.terminal_velocity(microphysics.parameters.pr, microphysics.parameters.tv.rain, ρᶜᶜᶠ, ρq_raiᶜᶜᶠ/ ρᶜᶜᶠ)
149+
end
150+
151+
@inline function w_sedimentation_velocity(i, j, k, grid, microphysics::Microphysics1M, ::Val{:ρq_sno}, ρ, ρq_sno)
152+
ρᶜᶜᶠ = ℑzᵃᵃᶠ(i, j, k, grid, ρ)
153+
ρq_snoᶜᶜᶠ = ℑzᵃᵃᶠ(i, j, k, grid, ρq_sno)
154+
return CM1.terminal_velocity(microphysics.parameters.ps, microphysics.parameters.tv.snow, ρᶜᶜᶠ, ρq_snoᶜᶜᶠ/ ρᶜᶜᶠ)
155+
end

0 commit comments

Comments
 (0)