Skip to content

Commit c3ae4f1

Browse files
authored
Merge pull request #3793 from CliMA/aj/mixing_length
Part 1 of mixing length cleanup
2 parents ab0026b + e50375a commit c3ae4f1

File tree

1 file changed

+65
-13
lines changed

1 file changed

+65
-13
lines changed

src/prognostic_equations/edmfx_closures.jl

Lines changed: 65 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -103,21 +103,73 @@ function edmfx_nh_pressure_drag_tendency!(
103103
end
104104
end
105105

106-
# lambert_2_over_e(::Type{FT}) where {FT} = FT(LambertW.lambertw(FT(2) / FT(MathConstants.e)))
107-
lambert_2_over_e(::Type{FT}) where {FT} = FT(0.46305551336554884) # since we can evaluate
108-
109-
function lamb_smooth_minimum(
110-
l::SA.SVector,
111-
lower_bound::FT,
112-
upper_bound,
113-
) where {FT}
106+
"""
107+
lamb_smooth_minimum(l::SA.SVector{N, FT}, smoothness_param::FT, λ_floor::FT) where {N, FT}
108+
109+
Calculates a smooth minimum of the elements in the StaticVector `l`.
110+
111+
This function provides a differentiable approximation to the `minimum` function,
112+
yielding a value slightly larger than the true minimum, weighted towards the
113+
smallest elements. The degree of smoothness is controlled by an internally
114+
calculated parameter `λ₀`, which depends on the input parameters
115+
`smoothness_param` and `λ_floor`. A larger `λ₀` results in a smoother
116+
(less sharp) minimum approximation.
117+
118+
This implementation is based on an exponentially weighted average, with `λ₀`
119+
determined involving the minimum element `x_min` and a factor related to the
120+
Lambert W function evaluated at 2/e.
121+
122+
Arguments:
123+
- `l`: An `SVector{N, FT}` of N numbers for which to find the smooth minimum.
124+
- `smoothness_param`: A parameter (`FT`) influencing the scaling of the smoothness
125+
parameter `λ₀`. A larger value generally leads to a larger `λ₀`
126+
and a smoother minimum.
127+
- `λ_floor`: The minimum value (`FT`) allowed for the smoothness parameter `λ₀`.
128+
Ensures a minimum level of smoothing and prevents `λ₀` from
129+
becoming zero or negative. Must be positive.
130+
Returns:
131+
- The smooth minimum value (`FT`).
132+
133+
Algorithm:
134+
1. Find the hard minimum `x_min = minimum(l)`.
135+
2. Calculate the smoothness scale:
136+
`λ₀ = max(x_min * smoothness_param / W(2/e), λ_floor)`,
137+
where `W(2/e)` is the Lambert W function evaluated at 2/e.
138+
3. Ensure `λ₀` is positive (`>= eps(FT)`).
139+
4. Compute the exponentially weighted average:
140+
`smin = Σᵢ(lᵢ * exp(-(lᵢ - x_min) / λ₀)) / Σᵢ(exp(-(lᵢ - x_min) / λ₀))`
141+
"""
142+
function lamb_smooth_minimum(l, smoothness_param, λ_floor)
143+
FT = typeof(smoothness_param)
144+
145+
# Precomputed constant value of LambertW(2/e) for efficiency.
146+
# LambertW.lambertw(FT(2) / FT(MathConstants.e)) ≈ 0.46305551336554884
147+
lambert_2_over_e = FT(0.46305551336554884)
148+
149+
# Ensure the floor for the smoothness parameter is positive
150+
@assert λ_floor > 0 "λ_floor must be positive"
151+
152+
# 1. Find the minimum value in the vector
114153
x_min = minimum(l)
115-
λ_0 = max(x_min * lower_bound / lambert_2_over_e(FT), upper_bound)
116154

117-
num = sum(l_i -> l_i * exp(-(l_i - x_min) / λ_0), l)
118-
den = sum(l_i -> exp(-(l_i - x_min) / λ_0), l)
119-
smin = num / den
120-
return smin
155+
# 2. Calculate the smoothing parameter λ_0.
156+
# It scales with the minimum value and smoothness_param, bounded below by λ_floor.
157+
# Using a precomputed value for lambertw(2/e) for type stability and efficiency.
158+
lambda_scaling_term = x_min * smoothness_param / lambert_2_over_e
159+
λ_0 = max(lambda_scaling_term, λ_floor)
160+
161+
# 3. Ensure λ_0 is numerically positive (should be guaranteed by λ_floor > 0)
162+
λ_0_safe = max(λ_0, eps(FT))
163+
164+
# Calculate the numerator and denominator for the weighted average.
165+
# The exponent is -(l_i - x_min)/λ_0_safe, which is <= 0.
166+
numerator = sum(l_i -> l_i * exp(-(l_i - x_min) / λ_0_safe), l)
167+
denominator = sum(l_i -> exp(-(l_i - x_min) / λ_0_safe), l)
168+
169+
# 4. Calculate the smooth minimum.
170+
# The denominator is guaranteed to be >= 1 because the term with l_i = x_min
171+
# contributes exp(0) = 1. Add a safeguard for (unlikely) underflow issues.
172+
return numerator / max(eps(FT), denominator)
121173
end
122174

123175
"""

0 commit comments

Comments
 (0)