Skip to content

Commit 9bffa2c

Browse files
committed
Generalize the Rosenbrock substep kernel over state dimension
`_rosenbrock_update` hardcoded the 8-component 2M+P3 state: it built `one(SMatrix{8,8,FT})` and computed the channel mask internally from the named `MicroState2MP3` fields. Re-express it over any `StaticArrays.StaticVector{N}`, sizing the identity and the dense diagonal matrices (P, S, S⁻¹) from the static length `N`, and accept the channel mask `z` as an argument so the kernel carries no scheme-specific channel knowledge. The equilibration, `(I/h − P J P)` solve, and positivity clamp are unchanged. The 2M+P3 channel mask (`_rosenbrock_channel_mask`) stays scheme-specific; the `RosenbrockAverage` entry now computes it and threads it into the generic kernel. This makes a future fixed-dimension state (e.g. a 4-species 1M vector with its own mask) a drop-in without touching the solver. 2M+P3 behavior is preserved bit-for-bit: the operations are identical, only generic over dimension. The `RosenbrockAverage` regression suite passes for Float64 and Float32 with unchanged tolerances, the 0-byte/call allocation assertion still holds, and JET is report-free on the 2M entry for both float types.
1 parent 60f1561 commit 9bffa2c

2 files changed

Lines changed: 12 additions & 11 deletions

File tree

src/BMT_rosenbrock.jl

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -115,31 +115,31 @@ Forward-Euler substep, floored at zero.
115115
@inline _euler_update(x, f, h) = max.(x .+ h .* f, 0)
116116

117117
"""
118-
_rosenbrock_update(x, f, J, h)
118+
_rosenbrock_update(x, f, J, z, h)
119119
120120
One linearized-implicit (Rosenbrock-Euler) substep: solve
121121
122122
(I/h - P J P) Δx = f
123123
124124
and return `max.(x + Δx, 0)`, where `P = Diagonal(z)` is the channel
125-
projection of [`_rosenbrock_channel_mask`](@ref). The system is solved in
125+
projection built from the per-scheme channel mask `z` (e.g.
126+
[`_rosenbrock_channel_mask`](@ref) for 2M+P3). The system is solved in
126127
equilibrated variables: with `S = Diagonal(|x| + h |f| + ϵ)` the similarity
127128
transform `S⁻¹ A S` is O(1)-conditioned — the raw rows span ~9 orders of
128129
magnitude (number vs mass species), and an unscaled Float32 factorization
129130
bleeds roundoff from the large rows into empty species as phantom mass.
130131
Equilibration is exact in exact arithmetic and keeps roundoff relative to
131132
each species' own scale.
132133
"""
133-
@inline function _rosenbrock_update(x::MicroState2MP3{FT}, f, J, h) where {FT}
134-
I₈ = one(SA.SMatrix{8, 8, FT})
135-
z = _rosenbrock_channel_mask(x)
134+
@inline function _rosenbrock_update(x::SA.StaticVector{N, FT}, f, J, z, h) where {N, FT}
135+
Iₙ = one(SA.SMatrix{N, N, FT})
136136
s = abs.(x) .+ h .* abs.(f) .+ eps(FT)
137137
# dense diagonal matrices: an `SDiagonal` wrapper here defeats the
138138
# optimizer's static-array stack allocation (heap spills per substep)
139-
P = I₈ .* z'
140-
S = I₈ .* s'
141-
S⁻¹ = I₈ .* inv.(s)'
142-
A = I₈ / h - S⁻¹ * (P * J * P) * S
139+
P = Iₙ .* z'
140+
S = Iₙ .* s'
141+
S⁻¹ = Iₙ .* inv.(s)'
142+
A = Iₙ / h - S⁻¹ * (P * J * P) * S
143143
Δx = S * (A \ (S⁻¹ * f))
144144
return max.(x .+ Δx, 0)
145145
end
@@ -197,7 +197,8 @@ fields as the `Instantaneous` entry (without the activation diagnostic).
197197
x_prev = x
198198
x = if all(isfinite, x)
199199
J = FD.jacobian(g, x)
200-
all(isfinite, J) ? _rosenbrock_update(x, f, J, h) : _euler_update(x, f, h)
200+
z = _rosenbrock_channel_mask(x)
201+
all(isfinite, J) ? _rosenbrock_update(x, f, J, z, h) : _euler_update(x, f, h)
201202
else
202203
_euler_update(x, f, h)
203204
end

test/rosenbrock_mode_tests.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ function test_rosenbrock_mode(FT)
147147
logλ_m = consistent_logλ(FT(0.45), x_mixb)
148148
xm = step(x_mixb, FT(0.45), FT(253), FT(4e-4), logλ_m, FT(10), 16)
149149
@test all(isfinite, xm)
150-
@test xm[2] < FT(1e6) # no multi-decade phantom droplet number
150+
@test xm[2] < FT(1e6) # no phantom droplet number orders of magnitude above the reference
151151
end
152152
end
153153

0 commit comments

Comments
 (0)