@@ -103,21 +103,73 @@ function edmfx_nh_pressure_drag_tendency!(
103103 end
104104end
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)
121173end
122174
123175"""
0 commit comments