fix Swift type-checker errors in LsvAnalysis chained closures

This commit is contained in:
jess 2026-04-03 02:13:27 -07:00
parent 91a361732d
commit 399ee4229b
1 changed files with 60 additions and 50 deletions

View File

@ -41,22 +41,22 @@ func findExtrema(_ v: [Float], _ iSmooth: [Float], minProminence: Float) -> [(In
} }
} }
return candidates.filter { (idx, isMax) in var result: [(Int, Bool)] = []
for (idx, isMax) in candidates {
let val = iSmooth[idx] let val = iSmooth[idx]
let leftSlice = iSmooth[..<idx] let leftSlice = iSmooth[..<idx]
let rightSlice = iSmooth[(idx + 1)...] let rightSlice = iSmooth[(idx + 1)...]
let leftBound: Float
let rightBound: Float
if isMax { if isMax {
leftBound = leftSlice.min() ?? val let lb = leftSlice.min() ?? val
rightBound = rightSlice.min() ?? val let rb = rightSlice.min() ?? val
return val - max(leftBound, rightBound) >= minProminence if val - max(lb, rb) >= minProminence { result.append((idx, isMax)) }
} else { } else {
leftBound = leftSlice.max() ?? val let lb = leftSlice.max() ?? val
rightBound = rightSlice.max() ?? val let rb = rightSlice.max() ?? val
return min(leftBound, rightBound) - val >= minProminence if min(lb, rb) - val >= minProminence { result.append((idx, isMax)) }
} }
} }
return result
} }
/// Detect Q/HQ redox peak in the -100 to +600 mV window. /// Detect Q/HQ redox peak in the -100 to +600 mV window.
@ -75,13 +75,16 @@ func detectQhqPeak(_ points: [LsvPoint]) -> Float? {
let extrema = findExtrema(vVals, smoothed, minProminence: prominence) let extrema = findExtrema(vVals, smoothed, minProminence: prominence)
let candidates = extrema var bestIdx: Int? = nil
.filter { $0.1 && vVals[$0.0] >= -100 && vVals[$0.0] <= 600 } var bestVal: Float = -.infinity
.max(by: { smoothed[$0.0] < smoothed[$1.0] }) for (idx, isMax) in extrema {
guard isMax, vVals[idx] >= -100, vVals[idx] <= 600 else { continue }
if let (idx, _) = candidates { if smoothed[idx] > bestVal {
return vVals[idx] bestVal = smoothed[idx]
bestIdx = idx
}
} }
if let idx = bestIdx { return vVals[idx] }
return nil return nil
} }
@ -108,38 +111,33 @@ func deriveClPotentials(_ points: [LsvPoint]) -> ClPotentials {
let extrema = findExtrema(vVals, smoothed, minProminence: prominence) let extrema = findExtrema(vVals, smoothed, minProminence: prominence)
// v_free: most prominent cathodic peak (isMax==false) in +300 to -300 mV // v_free: most prominent cathodic peak (isMax==false) in +300 to -300 mV
let freePeak = extrema var vFree: Float = 100
.filter { !$0.1 && vVals[$0.0] >= -300 && vVals[$0.0] <= 300 } var vFreeDetected = false
.min(by: { smoothed[$0.0] < smoothed[$1.0] }) var freeIdx: Int? = nil
var freeBest: Float = .infinity
let vFree: Float for (idx, isMax) in extrema {
let vFreeDetected: Bool guard !isMax, vVals[idx] >= -300, vVals[idx] <= 300 else { continue }
let freeIdx: Int? if smoothed[idx] < freeBest {
if let (idx, _) = freePeak { freeBest = smoothed[idx]
vFree = vVals[idx] vFree = vVals[idx]
vFreeDetected = true vFreeDetected = true
freeIdx = idx freeIdx = idx
} else { }
vFree = 100
vFreeDetected = false
freeIdx = nil
} }
// v_total: secondary cathodic peak between (vFree-100) and -500, excluding free peak // v_total: secondary cathodic peak between (vFree-100) and -500, excluding free peak
let totalHi = vFree - 100 let totalHi = vFree - 100
let totalLo: Float = -500 let totalLo: Float = -500
let totalPeak = extrema var vTotal: Float = vFree - 300
.filter { !$0.1 && vVals[$0.0] >= totalLo && vVals[$0.0] <= totalHi && $0.0 != freeIdx } var vTotalDetected = false
.min(by: { smoothed[$0.0] < smoothed[$1.0] }) var totalBest: Float = .infinity
for (idx, isMax) in extrema {
var vTotal: Float guard !isMax, vVals[idx] >= totalLo, vVals[idx] <= totalHi, idx != freeIdx else { continue }
let vTotalDetected: Bool if smoothed[idx] < totalBest {
if let (idx, _) = totalPeak { totalBest = smoothed[idx]
vTotal = vVals[idx] vTotal = vVals[idx]
vTotalDetected = true vTotalDetected = true
} else { }
vTotal = vFree - 300
vTotalDetected = false
} }
vTotal = max(vTotal, -400) vTotal = max(vTotal, -400)
@ -179,18 +177,30 @@ func detectLsvPeaks(_ points: [LsvPoint]) -> [LsvPeak] {
} }
// largest peak in positive voltage region -> freeCl // largest peak in positive voltage region -> freeCl
let freeCl = extrema var freeClIdx: Int? = nil
.filter { $0.1 && vVals[$0.0] >= 0 } var freeClVal: Float = -.infinity
.max(by: { smoothed[$0.0] < smoothed[$1.0] }) for (idx, isMax) in extrema {
if let (idx, _) = freeCl { guard isMax, vVals[idx] >= 0 else { continue }
if smoothed[idx] > freeClVal {
freeClVal = smoothed[idx]
freeClIdx = idx
}
}
if let idx = freeClIdx {
peaks.append(LsvPeak(vMv: vVals[idx], iUa: smoothed[idx], kind: .freeCl)) peaks.append(LsvPeak(vMv: vVals[idx], iUa: smoothed[idx], kind: .freeCl))
} }
// largest peak in negative voltage region -> totalCl // largest peak in negative voltage region -> totalCl
let totalCl = extrema var totalClIdx: Int? = nil
.filter { $0.1 && vVals[$0.0] < 0 } var totalClVal: Float = -.infinity
.max(by: { smoothed[$0.0] < smoothed[$1.0] }) for (idx, isMax) in extrema {
if let (idx, _) = totalCl { guard isMax, vVals[idx] < 0 else { continue }
if smoothed[idx] > totalClVal {
totalClVal = smoothed[idx]
totalClIdx = idx
}
}
if let idx = totalClIdx {
peaks.append(LsvPeak(vMv: vVals[idx], iUa: smoothed[idx], kind: .totalCl)) peaks.append(LsvPeak(vMv: vVals[idx], iUa: smoothed[idx], kind: .totalCl))
} }