Skip to content

Commit f0087d7

Browse files
authored
Adds convenience saturation adjustment methods with reasonable defaults (#302)
* Add convenience SA methods with reasonable defaults * Reduce maxiter to 2 and bump patch version
1 parent 92fbddd commit f0087d7

File tree

6 files changed

+523
-73
lines changed

6 files changed

+523
-73
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "Thermodynamics"
22
uuid = "b60c26fb-14c3-4610-9d3e-2d17fe7ff00c"
33
authors = ["Climate Modeling Alliance"]
4-
version = "0.15.4"
4+
version = "0.15.5"
55

66
[deps]
77
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"

docs/src/HowToGuide.md

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,12 @@ e_int = -7.0e4
7676
q_tot = 0.01
7777
7878
# Solve for phase equilibrium
79-
# We select a root-finding method from RootSolvers.jl (e.g., SecantMethod, NewtonsMethod)
80-
# We specify the formulation (TD.ρe() means inputs are Density & Internal Energy)
79+
# We use the convenience method which handles defaults automatically
80+
# For ρe(), this defaults to the optimized fixed-iteration solver
8181
sol = TD.saturation_adjustment(
82-
RS.SecantMethod, # Method
8382
params, # Parameter set
8483
TD.ρe(), # Formulation
85-
ρ, e_int, q_tot, # Inputs
86-
10, # Max iterations
87-
1e-3 # Tolerance
84+
ρ, e_int, q_tot # Inputs
8885
)
8986
9087
T_equil = sol.T
@@ -193,30 +190,23 @@ params_f32 = TD.Parameters.ThermodynamicsParameters(FT)
193190
For GPU performance, avoiding branch divergence is important. For the `ρe` formulation, use the fixed-iteration path by passing `forced_fixed_iters=true` as the last positional argument.
194191

195192
```julia
196-
# GPU-optimized broadcasting
193+
# GPU-optimized broadcasting using the convenience method
194+
# This automatically uses the fast, branch-free fixed-iteration solver
197195
sol = TD.saturation_adjustment.(
198-
RS.NewtonsMethod,
199196
Ref(params_f32),
200197
Ref(TD.ρe()),
201-
ρ_gpu, e_int_gpu, q_tot_gpu,
202-
Ref(3), # maxiter (3 iterations → ~0.1 K accuracy)
203-
Ref(1e-4), # tol (ignored when forced_fixed_iters=true)
204-
nothing, # T_guess (ignored when forced_fixed_iters=true)
205-
true, # forced_fixed_iters
198+
ρ_gpu, e_int_gpu, q_tot_gpu
206199
)
207200
```
208201

209202
For CPU single calls, you can omit the last two arguments to use the standard solver:
210203

211204
```julia
212-
# CPU single-call (standard solver)
205+
# CPU single-call (uses defaults)
213206
sol = TD.saturation_adjustment(
214-
RS.NewtonsMethod,
215207
params_f32,
216208
TD.ρe(),
217-
ρ_val, e_int_val, q_tot_val,
218-
10, # maxiter
219-
1e-4, # tolerance
209+
ρ_val, e_int_val, q_tot_val
220210
)
221211
```
222212

@@ -257,7 +247,7 @@ If your model handles phase changes (microphysics), you might step `q_liq` and `
257247
ρ = 1.0
258248
e_int = -7.0e4
259249
q_tot = 0.01
260-
sol = TD.saturation_adjustment(RS.NewtonsMethod, params, TD.ρe(), ρ, e_int, q_tot, 15, 1e-4)
250+
sol = TD.saturation_adjustment(params, TD.ρe(), ρ, e_int, q_tot)
261251
262252
# Update thermodynamic variables
263253
T_new = sol.T
@@ -295,12 +285,9 @@ e_int = TD.internal_energy(params, T_sat, q_tot, q_liq_eq, q_ice_eq)
295285
# Define a function from e_int → q_liq through saturation adjustment
296286
function q_liq_from_e_int(e)
297287
sol = TD.saturation_adjustment(
298-
RS.SecantMethod,
299288
params,
300289
TD.ρe(),
301-
ρ, e, q_tot,
302-
20, # maxiter
303-
1e-5 # tolerance
290+
ρ, e, q_tot
304291
)
305292
return sol.q_liq
306293
end

src/saturation_adjustment.jl

Lines changed: 262 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,14 @@ to avoid branch divergence on GPUs.
114114
- `ρ`: Density [kg/m³]
115115
- `e_int`: Specific internal energy [J/kg]
116116
- `q_tot`: Total specific humidity [kg/kg]
117-
- `maxiter`: Number of Newton iterations (3 recommended for ~0.1 K accuracy)
117+
- `maxiter`: Number of Newton iterations (2 recommended for < 0.1 K accuracy)
118118
119119
# Returns
120120
- `NamedTuple` `(; T, q_liq, q_ice, converged)`
121121
122122
# Notes
123123
- The `converged` field is always `true` (no convergence check is performed).
124-
- With `maxiter = 3`, temperature accuracy is better than 0.1 K for typical atmospheric
124+
- With `maxiter = 2`, temperature accuracy is better than 0.1 K for typical atmospheric
125125
conditions (T < 320 K).
126126
- This is an internal helper function. For the public API, use [`saturation_adjustment`](@ref)
127127
with `forced_fixed_iters=true` as a positional argument.
@@ -672,6 +672,266 @@ function saturation_adjustment(
672672
return (; T, q_liq, q_ice, converged)
673673
end
674674

675+
# ---------------------------------------------
676+
# Convenience methods with reasonable defaults
677+
# ---------------------------------------------
678+
679+
"""
680+
saturation_adjustment(
681+
param_set,
682+
::ρe,
683+
ρ,
684+
e_int,
685+
q_tot;
686+
maxiter::Int = 2,
687+
)
688+
689+
Convenience method for `ρe` formulation with reasonable GPU-optimized defaults.
690+
691+
Uses `RS.NewtonsMethod` with `forced_fixed_iters=true` and `maxiter=2` for fast,
692+
branch-free execution on GPUs. For typical atmospheric conditions (T < 320 K),
693+
this achieves better than 0.1 K accuracy.
694+
695+
For more control over solver parameters, use the full signature with explicit method type.
696+
"""
697+
function saturation_adjustment(
698+
param_set::APS,
699+
::ρe,
700+
ρ,
701+
e_int,
702+
q_tot;
703+
maxiter::Int = 2,
704+
)
705+
return saturation_adjustment_ρe_fixed_iters(
706+
param_set,
707+
ρ,
708+
e_int,
709+
q_tot,
710+
maxiter,
711+
)
712+
end
713+
714+
"""
715+
saturation_adjustment(
716+
param_set,
717+
::pe,
718+
p,
719+
e_int,
720+
q_tot;
721+
maxiter::Int = 15,
722+
tol = nothing,
723+
T_guess = nothing,
724+
)
725+
726+
Convenience method for `pe` formulation with reasonable defaults.
727+
728+
Uses `RS.SecantMethod` with `maxiter=15` and relative tolerance `1e-4`.
729+
730+
For more control over solver parameters, use the full signature with explicit method type.
731+
"""
732+
function saturation_adjustment(
733+
param_set::APS,
734+
::pe,
735+
p,
736+
e_int,
737+
q_tot;
738+
maxiter::Int = 15,
739+
tol = nothing,
740+
T_guess = nothing,
741+
)
742+
FT = eltype(param_set)
743+
_tol = tol === nothing ? RS.RelativeSolutionTolerance(FT(1e-4)) : tol
744+
745+
return saturation_adjustment(
746+
RS.SecantMethod,
747+
param_set,
748+
pe(),
749+
p,
750+
e_int,
751+
q_tot,
752+
maxiter,
753+
_tol,
754+
T_guess,
755+
)
756+
end
757+
758+
"""
759+
saturation_adjustment(
760+
param_set,
761+
::ph,
762+
p,
763+
h,
764+
q_tot;
765+
maxiter::Int = 15,
766+
tol = nothing,
767+
T_guess = nothing,
768+
)
769+
770+
Convenience method for `ph` formulation with reasonable defaults.
771+
772+
Uses `RS.SecantMethod` with `maxiter=15` and relative tolerance `1e-4`.
773+
774+
For more control over solver parameters, use the full signature with explicit method type.
775+
"""
776+
function saturation_adjustment(
777+
param_set::APS,
778+
::ph,
779+
p,
780+
h,
781+
q_tot;
782+
maxiter::Int = 15,
783+
tol = nothing,
784+
T_guess = nothing,
785+
)
786+
FT = eltype(param_set)
787+
_tol = tol === nothing ? RS.RelativeSolutionTolerance(FT(1e-4)) : tol
788+
789+
return saturation_adjustment(
790+
RS.SecantMethod,
791+
param_set,
792+
ph(),
793+
p,
794+
h,
795+
q_tot,
796+
maxiter,
797+
_tol,
798+
T_guess,
799+
)
800+
end
801+
802+
"""
803+
saturation_adjustment(
804+
param_set,
805+
::pθ_li,
806+
p,
807+
θ_li,
808+
q_tot;
809+
maxiter::Int = 15,
810+
tol = nothing,
811+
T_guess = nothing,
812+
)
813+
814+
Convenience method for `pθ_li` formulation with reasonable defaults.
815+
816+
Uses `RS.SecantMethod` with `maxiter=15` and relative tolerance `1e-4`.
817+
818+
For more control over solver parameters, use the full signature with explicit method type.
819+
"""
820+
function saturation_adjustment(
821+
param_set::APS,
822+
::pθ_li,
823+
p,
824+
θ_li,
825+
q_tot;
826+
maxiter::Int = 15,
827+
tol = nothing,
828+
T_guess = nothing,
829+
)
830+
FT = eltype(param_set)
831+
_tol = tol === nothing ? RS.RelativeSolutionTolerance(FT(1e-4)) : tol
832+
833+
return saturation_adjustment(
834+
RS.SecantMethod,
835+
param_set,
836+
pθ_li(),
837+
p,
838+
θ_li,
839+
q_tot,
840+
maxiter,
841+
_tol,
842+
T_guess,
843+
)
844+
end
845+
846+
"""
847+
saturation_adjustment(
848+
param_set,
849+
::ρθ_li,
850+
ρ,
851+
θ_li,
852+
q_tot;
853+
maxiter::Int = 15,
854+
tol = nothing,
855+
T_guess = nothing,
856+
)
857+
858+
Convenience method for `ρθ_li` formulation with reasonable defaults.
859+
860+
Uses `RS.SecantMethod` with `maxiter=15` and relative tolerance `1e-4`.
861+
862+
For more control over solver parameters, use the full signature with explicit method type.
863+
"""
864+
function saturation_adjustment(
865+
param_set::APS,
866+
::ρθ_li,
867+
ρ,
868+
θ_li,
869+
q_tot;
870+
maxiter::Int = 15,
871+
tol = nothing,
872+
T_guess = nothing,
873+
)
874+
FT = eltype(param_set)
875+
_tol = tol === nothing ? RS.RelativeSolutionTolerance(FT(1e-4)) : tol
876+
877+
return saturation_adjustment(
878+
RS.SecantMethod,
879+
param_set,
880+
ρθ_li(),
881+
ρ,
882+
θ_li,
883+
q_tot,
884+
maxiter,
885+
_tol,
886+
T_guess,
887+
)
888+
end
889+
890+
"""
891+
saturation_adjustment(
892+
param_set,
893+
::pρ,
894+
p,
895+
ρ,
896+
q_tot;
897+
maxiter::Int = 15,
898+
tol = nothing,
899+
T_guess = nothing,
900+
)
901+
902+
Convenience method for `pρ` formulation with reasonable defaults.
903+
904+
Uses `RS.SecantMethod` with `maxiter=15` and relative tolerance `1e-4`.
905+
906+
For more control over solver parameters, use the full signature with explicit method type.
907+
"""
908+
function saturation_adjustment(
909+
param_set::APS,
910+
::pρ,
911+
p,
912+
ρ,
913+
q_tot;
914+
maxiter::Int = 15,
915+
tol = nothing,
916+
T_guess = nothing,
917+
)
918+
FT = eltype(param_set)
919+
_tol = tol === nothing ? RS.RelativeSolutionTolerance(FT(1e-4)) : tol
920+
921+
return saturation_adjustment(
922+
RS.SecantMethod,
923+
param_set,
924+
pρ(),
925+
p,
926+
ρ,
927+
q_tot,
928+
maxiter,
929+
_tol,
930+
T_guess,
931+
)
932+
end
933+
934+
675935
# ---------------------------------------------
676936
# Helper functions
677937
# ---------------------------------------------

0 commit comments

Comments
 (0)