Skip to content

Commit 98926c1

Browse files
Use @verbosity_specifier macro for NonlinearVerbosity
Replace the manual NonlinearVerbosity struct definition with the @verbosity_specifier macro from SciMLLogging.jl. This simplifies the code by letting the macro auto-generate: - The struct definition with toggles as fields - Preset constructors (None, Minimal, Standard, Detailed, All) - Group-level keyword constructors (error_control, numerical) - Individual field keyword constructors - Mixed group and individual settings support Changes: - lib/NonlinearSolveBase/src/verbosity.jl: Use @verbosity_specifier macro - lib/NonlinearSolveBase/src/NonlinearSolveBase.jl: Import @verbosity_specifier - Project.toml, lib/NonlinearSolveBase/Project.toml: Bump SciMLLogging to 1.7 - test/verbosity_tests.jl: Use regex matching for log messages 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent d2a9b8a commit 98926c1

File tree

5 files changed

+43
-160
lines changed

5 files changed

+43
-160
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ Reexport = "1.2.2"
118118
ReverseDiff = "1.15"
119119
SIAMFANLEquations = "1.0.1"
120120
SciMLBase = "2.127"
121-
SciMLLogging = "1.3"
121+
SciMLLogging = "1.7"
122122
SimpleNonlinearSolve = "2.11"
123123
SparseArrays = "1.10"
124124
SparseConnectivityTracer = "1"

lib/NonlinearSolveBase/Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ RecursiveArrayTools = "3"
8484
ReverseDiff = "1.15"
8585
SciMLBase = "2.127"
8686
SciMLJacobianOperators = "0.1.1"
87-
SciMLLogging = "1.3.1"
87+
SciMLLogging = "1.7"
8888
SciMLOperators = "1.7"
8989
SciMLStructures = "1.5"
9090
Setfield = "1.1.2"

lib/NonlinearSolveBase/src/NonlinearSolveBase.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ import SciMLBase: solve, init, __init, __solve, wrap_sol, get_root_indp, isinpla
2828

2929
using SciMLJacobianOperators: JacobianOperator, StatefulJacobianOperator
3030
using SciMLOperators: AbstractSciMLOperator, IdentityOperator
31-
using SciMLLogging: @SciMLMessage, AbstractVerbositySpecifier, AbstractVerbosityPreset, AbstractMessageLevel,
32-
None, Minimal, Standard, Detailed, All, Silent, InfoLevel, WarnLevel
31+
using SciMLLogging: @SciMLMessage, @verbosity_specifier, AbstractVerbositySpecifier,
32+
AbstractVerbosityPreset, AbstractMessageLevel,
33+
None, Minimal, Standard, Detailed, All, Silent, InfoLevel, WarnLevel
3334

3435
using SymbolicIndexingInterface: SymbolicIndexingInterface
3536
import SciMLStructures

lib/NonlinearSolveBase/src/verbosity.jl

Lines changed: 32 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Create a `NonlinearVerbosity` using a preset configuration:
2929
- `SciMLLogging.Detailed()`: Comprehensive debugging information
3030
- `SciMLLogging.All()`: Maximum verbosity
3131
32-
NonlinearVerbosity(; error_control=nothing, performance=nothing, numerical=nothing, linear_verbosity=nothing, kwargs...)
32+
NonlinearVerbosity(; error_control=nothing, numerical=nothing, linear_verbosity=nothing, kwargs...)
3333
3434
Create a `NonlinearVerbosity` with group-level or individual field control.
3535
@@ -58,176 +58,58 @@ verbose = NonlinearVerbosity(
5858
)
5959
```
6060
"""
61-
@concrete struct NonlinearVerbosity <: AbstractVerbositySpecifier
62-
# Linear verbosity
63-
linear_verbosity
64-
# Error control
65-
non_enclosing_interval
66-
alias_u0_immutable
67-
linsolve_failed_noncurrent
68-
termination_condition
69-
# Numerical
70-
threshold_state
71-
end
72-
73-
# Group classifications
74-
const error_control_options = (
75-
:non_enclosing_interval, :alias_u0_immutable, :linsolve_failed_noncurrent,
76-
:termination_condition
77-
)
78-
const performance_options = ()
79-
const numerical_options = (:threshold_state,)
80-
81-
function option_group(option::Symbol)
82-
if option in error_control_options
83-
return :error_control
84-
elseif option in performance_options
85-
return :performance
86-
elseif option in numerical_options
87-
return :numerical
88-
else
89-
error("Unknown verbosity option: $option")
90-
end
91-
end
92-
93-
# Get all options in a group
94-
function group_options(verbosity::NonlinearVerbosity, group::Symbol)
95-
if group === :error_control
96-
return NamedTuple{error_control_options}(getproperty(verbosity, opt)
97-
for opt in error_control_options)
98-
elseif group === :performance
99-
return NamedTuple{performance_options}(getproperty(verbosity, opt)
100-
for opt in performance_options)
101-
elseif group === :numerical
102-
return NamedTuple{numerical_options}(getproperty(verbosity, opt)
103-
for opt in numerical_options)
104-
else
105-
error("Unknown group: $group")
106-
end
107-
end
108-
109-
function NonlinearVerbosity(;
110-
error_control = nothing, performance = nothing, numerical = nothing,
111-
linear_verbosity = nothing, kwargs...)
112-
# Validate group arguments
113-
if error_control !== nothing && !(error_control isa AbstractMessageLevel)
114-
throw(ArgumentError("error_control must be a SciMLLogging.AbstractMessageLevel, got $(typeof(error_control))"))
115-
end
116-
if performance !== nothing && !(performance isa AbstractMessageLevel)
117-
throw(ArgumentError("performance must be a SciMLLogging.AbstractMessageLevel, got $(typeof(performance))"))
118-
end
119-
if numerical !== nothing && !(numerical isa AbstractMessageLevel)
120-
throw(ArgumentError("numerical must be a SciMLLogging.AbstractMessageLevel, got $(typeof(numerical))"))
121-
end
122-
123-
# Validate individual kwargs
124-
for (key, value) in kwargs
125-
if !(key in error_control_options || key in performance_options ||
126-
key in numerical_options)
127-
throw(ArgumentError("Unknown verbosity option: $key. Valid options are: $(tuple(error_control_options..., performance_options..., numerical_options...))"))
128-
end
129-
if !(value isa AbstractMessageLevel)
130-
throw(ArgumentError("$key must be a SciMLLogging.AbstractMessageLevel, got $(typeof(value))"))
131-
end
132-
end
133-
134-
# Build arguments using NamedTuple for type stability
135-
# Use None() for linear_verbosity by default since BLAS errors are not fatal
136-
# in the nonlinear solver context (the solver handles singular matrices gracefully)
137-
default_args = (
138-
linear_verbosity = linear_verbosity === nothing ? None() : linear_verbosity,
139-
non_enclosing_interval = WarnLevel(),
140-
alias_u0_immutable = WarnLevel(),
141-
linsolve_failed_noncurrent = WarnLevel(),
142-
termination_condition = WarnLevel(),
143-
threshold_state = WarnLevel()
144-
)
145-
146-
# Apply group-level settings
147-
final_args = if error_control !== nothing || performance !== nothing ||
148-
numerical !== nothing
149-
NamedTuple{keys(default_args)}(
150-
_resolve_arg_value(
151-
key, default_args[key], error_control, performance, numerical)
152-
for key in keys(default_args)
153-
)
154-
else
155-
default_args
156-
end
61+
NonlinearVerbosity
15762

158-
# Apply individual overrides
159-
if !isempty(kwargs)
160-
final_args = merge(final_args, NamedTuple(kwargs))
161-
end
63+
@verbosity_specifier NonlinearVerbosity begin
64+
toggles = (:linear_verbosity, :non_enclosing_interval, :alias_u0_immutable,
65+
:linsolve_failed_noncurrent, :termination_condition, :threshold_state)
16266

163-
NonlinearVerbosity(values(final_args)...)
164-
end
165-
166-
# Constructor for verbosity presets following the hierarchical levels:
167-
# None < Minimal < Standard < Detailed < All
168-
# Each level includes all messages from levels below it plus additional ones
169-
function NonlinearVerbosity(verbose::AbstractVerbosityPreset)
170-
if verbose isa Minimal
171-
# Minimal: Only fatal errors and critical warnings
172-
# Use None() for linear_verbosity since BLAS errors are not fatal
173-
# in the nonlinear solver context (the solver handles singular matrices gracefully)
174-
NonlinearVerbosity(
67+
presets = (
68+
None = (
69+
linear_verbosity = None(),
70+
non_enclosing_interval = Silent(),
71+
alias_u0_immutable = Silent(),
72+
linsolve_failed_noncurrent = Silent(),
73+
termination_condition = Silent(),
74+
threshold_state = Silent()
75+
),
76+
Minimal = (
17577
linear_verbosity = None(),
17678
non_enclosing_interval = WarnLevel(),
17779
alias_u0_immutable = Silent(),
17880
linsolve_failed_noncurrent = WarnLevel(),
17981
termination_condition = Silent(),
18082
threshold_state = Silent()
181-
)
182-
elseif verbose isa Standard
183-
# Standard: Everything from Minimal + non-fatal warnings
184-
NonlinearVerbosity()
185-
elseif verbose isa Detailed
186-
# Detailed: Everything from Standard + debugging/solver behavior
187-
NonlinearVerbosity(
83+
),
84+
Standard = (
85+
linear_verbosity = None(),
86+
non_enclosing_interval = WarnLevel(),
87+
alias_u0_immutable = WarnLevel(),
88+
linsolve_failed_noncurrent = WarnLevel(),
89+
termination_condition = WarnLevel(),
90+
threshold_state = WarnLevel()
91+
),
92+
Detailed = (
18893
linear_verbosity = Detailed(),
18994
non_enclosing_interval = WarnLevel(),
19095
alias_u0_immutable = WarnLevel(),
19196
linsolve_failed_noncurrent = WarnLevel(),
19297
termination_condition = WarnLevel(),
19398
threshold_state = WarnLevel()
194-
)
195-
elseif verbose isa All
196-
# All: Maximum verbosity - every possible logging message at InfoLevel
197-
NonlinearVerbosity(
99+
),
100+
All = (
198101
linear_verbosity = Detailed(),
199102
non_enclosing_interval = WarnLevel(),
200103
alias_u0_immutable = WarnLevel(),
201104
linsolve_failed_noncurrent = WarnLevel(),
202105
termination_condition = WarnLevel(),
203106
threshold_state = InfoLevel()
204107
)
205-
end
206-
end
108+
)
207109

208-
@inline function NonlinearVerbosity(verbose::None)
209-
NonlinearVerbosity(
210-
None(),
211-
Silent(),
212-
Silent(),
213-
Silent(),
214-
Silent(),
215-
Silent()
110+
groups = (
111+
error_control = (:non_enclosing_interval, :alias_u0_immutable,
112+
:linsolve_failed_noncurrent, :termination_condition),
113+
numerical = (:threshold_state,)
216114
)
217115
end
218-
219-
# Helper function to resolve argument values based on group membership
220-
@inline function _resolve_arg_value(
221-
key::Symbol, default_val, error_control, performance, numerical)
222-
if key === :linear_verbosity
223-
return default_val
224-
elseif key in error_control_options && error_control !== nothing
225-
return error_control
226-
elseif key in performance_options && performance !== nothing
227-
return performance
228-
elseif key in numerical_options && numerical !== nothing
229-
return numerical
230-
else
231-
return default_val
232-
end
233-
end

test/verbosity_tests.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,12 @@
7474
int_prob = IntervalNonlinearProblem(g, (3.0, 5.0))
7575

7676
@test_logs (:info,
77-
"The interval is not an enclosing interval, opposite signs at the boundaries are required.") solve(
77+
r"The interval is not an enclosing interval, opposite signs at the boundaries are required.") solve(
7878
int_prob,
7979
ITP(), verbose = NonlinearVerbosity(non_enclosing_interval = SciMLLogging.InfoLevel()))
8080

81-
@test_logs (:error,
82-
"The interval is not an enclosing interval, opposite signs at the boundaries are required.") @test_throws ErrorException solve(
81+
@test_logs (:error,
82+
r"The interval is not an enclosing interval, opposite signs at the boundaries are required.") @test_throws ErrorException solve(
8383
int_prob,
8484
ITP(), verbose = NonlinearVerbosity(non_enclosing_interval = SciMLLogging.ErrorLevel()))
8585

@@ -88,12 +88,12 @@
8888
prob = NonlinearProblem(f, [1.0, 1.0])
8989

9090
@test_logs (:warn,
91-
"LU factorization failed, falling back to QR factorization. `A` is potentially rank-deficient.") match_mode=:any solve(
91+
r"LU factorization failed, falling back to QR factorization. `A` is potentially rank-deficient.") match_mode=:any solve(
9292
prob,
9393
verbose = NonlinearVerbosity(linear_verbosity = SciMLLogging.Detailed()))
9494

9595
@test_logs (:info,
96-
"LU factorization failed, falling back to QR factorization. `A` is potentially rank-deficient.") match_mode=:any solve(
96+
r"LU factorization failed, falling back to QR factorization. `A` is potentially rank-deficient.") match_mode=:any solve(
9797
prob,
9898
verbose = NonlinearVerbosity(linear_verbosity = LinearVerbosity(default_lu_fallback = SciMLLogging.InfoLevel())))
9999

@@ -102,7 +102,7 @@
102102
verbose = NonlinearVerbosity(linear_verbosity = SciMLLogging.Standard()))
103103

104104
@test_logs (:warn,
105-
"LU factorization failed, falling back to QR factorization. `A` is potentially rank-deficient.") match_mode=:any solve(
105+
r"LU factorization failed, falling back to QR factorization. `A` is potentially rank-deficient.") match_mode=:any solve(
106106
prob,
107107
verbose = NonlinearVerbosity(linear_verbosity = SciMLLogging.Detailed())
108108
)

0 commit comments

Comments
 (0)