@@ -103,21 +103,73 @@ function edmfx_nh_pressure_drag_tendency!(
103
103
end
104
104
end
105
105
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
114
153
x_min = minimum (l)
115
- λ_0 = max (x_min * lower_bound / lambert_2_over_e (FT), upper_bound)
116
154
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)
121
173
end
122
174
123
175
"""
0 commit comments