From 99b5cef83e3784fd7bbc102c962641c48656b7c8 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Fri, 15 May 2026 19:37:10 +0200 Subject: [PATCH] Fix stepinfo crash when response never reaches risetime thresholds `stepinfo` indexed `res.t[i10]` / `res.t[i90]` without checking that `findfirst` actually located the rise-time crossings. For a response that never crosses the lower or upper threshold within the simulation window, `findfirst` returns `nothing` and the indexing then raised `MethodError: no method matching getindex(..., ::Nothing)`. The function now sets `i10`/`i90` to `0` (sentinel for "not reached"), sets `risetime = NaN`, and emits a warning. The `stepinfoplot` recipe skips the rise-time series when either index is zero. Co-Authored-By: Claude Opus 4.7 (1M context) --- lib/ControlSystemsBase/src/plotting.jl | 26 ++++++++++++++------------ lib/ControlSystemsBase/src/timeresp.jl | 9 ++++++++- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/lib/ControlSystemsBase/src/plotting.jl b/lib/ControlSystemsBase/src/plotting.jl index 4f5b38ad0..d6f2214dd 100644 --- a/lib/ControlSystemsBase/src/plotting.jl +++ b/lib/ControlSystemsBase/src/plotting.jl @@ -175,18 +175,20 @@ end label := @sprintf("Final value: %.3f", si.yf) [si.yf] end - @series begin - linestyle := :solid - linewidth --> 2 - color --> 2 - label := @sprintf("Rise time: %.3f", si.risetime) - si.res.t[si.i10:si.i90], si.res.y[1, si.i10:si.i90] - end - @series begin - color --> 2 - seriestype := :vline - label := @sprintf("Rise time threshold: %.1f%%-%.1f%%", 100si.risetime_th[1], 100si.risetime_th[2]) - [si.res.t[si.i10], si.res.t[si.i90]] + if si.i10 > 0 && si.i90 > 0 + @series begin + linestyle := :solid + linewidth --> 2 + color --> 2 + label := @sprintf("Rise time: %.3f", si.risetime) + si.res.t[si.i10:si.i90], si.res.y[1, si.i10:si.i90] + end + @series begin + color --> 2 + seriestype := :vline + label := @sprintf("Rise time threshold: %.1f%%-%.1f%%", 100si.risetime_th[1], 100si.risetime_th[2]) + [si.res.t[si.i10], si.res.t[si.i90]] + end end @series begin color --> 3 diff --git a/lib/ControlSystemsBase/src/timeresp.jl b/lib/ControlSystemsBase/src/timeresp.jl index ccc7fb9e3..806e9a8ac 100644 --- a/lib/ControlSystemsBase/src/timeresp.jl +++ b/lib/ControlSystemsBase/src/timeresp.jl @@ -550,7 +550,14 @@ function stepinfo(res::SimResult; y0 = nothing, yf = nothing, settling_th = 0.02 op = direction == 1 ? (>) : (<) i10 = findfirst(op.(y, y0 + risetime_th[1] * stepsize * direction)) i90 = findfirst(op.(y, y0 + risetime_th[2] * stepsize * direction)) - risetime = res.t[i90] - res.t[i10] + if i10 === nothing || i90 === nothing + @warn "Response did not reach the requested risetime threshold(s) within the simulation window" + i10 = i10 === nothing ? 0 : i10 + i90 = i90 === nothing ? 0 : i90 + risetime = oftype(float(res.t[1]), NaN) + else + risetime = res.t[i90] - res.t[i10] + end StepInfo(y0, yf, stepsize, peak, peaktime, overshoot, lowerpeak, lowerpeakind, undershoot, settlingtime, settlingtimeind, risetime, i10, i90, res, settling_th, risetime_th) end