diff --git a/Sources/SpectroEDX/Proposer/ElementProposer.swift b/Sources/SpectroEDX/Proposer/ElementProposer.swift index aa1087e..a3b237c 100644 --- a/Sources/SpectroEDX/Proposer/ElementProposer.swift +++ b/Sources/SpectroEDX/Proposer/ElementProposer.swift @@ -18,7 +18,8 @@ // less than 100. // Tiers: an element found from its own peak, unambiguous, needed by the final fit, Si above 0.2 % of the counts and its peak in a band // where the background is healthy (data over model 0.8 to 1.25 off the lines) is proposed; every other found or tested element is a -// candidate with `possibleReason` (or `familyNote` for the confirmation rule) and is never proposed. +// candidate with `possibleReason` (or `familyNote` for the confirmation rule) and is never proposed. A candidate whose peak went to a +// pile-up column that the final fit keeps at or above its L_D is explained (`explainedBySumPeak`), not possible. // // What went (0.11.0): the all-candidate joint fit with its L_C pruning and single-candidate forward step, and the flank-misfit factor that // scaled a candidate's sigma_0 by its neighbour's misfit (x33 for Mg beside Al on a measured Al-alloy map, where Mg's own window fitted to @@ -61,6 +62,12 @@ public nonisolated struct ElementCandidate: Equatable, Sendable { /// Found by an L or M line whose confirmation line (K for L, L for M) lies outside the fitted range or too close to the beam energy: the /// confirmation rule could not run, so the find is possible only (`possibleReason` says so). A consumer lists these as "uncorroborated". public let unconfirmable: Bool + /// The label of the modelled sum peak that took the residual peak this element was tested on ("Al+Al sum"), when that sum + /// column stays in the final fit at or above its own L_D: the peak is pile-up, explained, and the element is not a "possible" + /// find (Ar K-alpha and Ag L-alpha under Al+Al in an Al alloy). nil otherwise. One spectrum cannot separate a real line within the + /// detector resolution of the sum from the sum itself (a planted Ar K-alpha of half the sum's counts is still explained); such a + /// candidate keeps its sum-peak conflict and its two-beam-current remedy, so the question stays visible. + public let explainedBySumPeak: String? /// net >= L_D: the proposer's bar. public var aboveDetectionLimit: Bool { net >= detectionLimit } @@ -75,11 +82,12 @@ public nonisolated struct ElementCandidate: Equatable, Sendable { public init(element: String, group: String, energyKeV: Double, net: Double, sigma: Double, sigmaZero: Double, criticalLevel: Double, detectionLimit: Double, conflicts: [LineConflict], suggestedRole: QuantificationMethod.ElementRole, holeRegionNote: String?, misfit: Double, familyNote: String? = nil, - possibleReason: String? = nil, unconfirmable: Bool = false) { + possibleReason: String? = nil, unconfirmable: Bool = false, explainedBySumPeak: String? = nil) { self.element = element; self.group = group; self.energyKeV = energyKeV; self.net = net; self.sigma = sigma self.sigmaZero = sigmaZero; self.criticalLevel = criticalLevel; self.detectionLimit = detectionLimit self.conflicts = conflicts; self.suggestedRole = suggestedRole; self.holeRegionNote = holeRegionNote; self.misfit = misfit self.familyNote = familyNote; self.possibleReason = possibleReason; self.unconfirmable = unconfirmable + self.explainedBySumPeak = explainedBySumPeak } } @@ -111,7 +119,13 @@ public nonisolated struct ProposalResult: Sendable { public var proposed: [ElementCandidate] { candidates.filter { $0.isProposed && !$0.hasSumPeakQuestion } } /// At or above L_D but sitting on a pile-up energy of the detected parents: "sum peak or this element?", not a finding. public var sumPeakQuestions: [ElementCandidate] { candidates.filter { $0.isProposed && $0.hasSumPeakQuestion } } - public var possible: [ElementCandidate] { candidates.filter { $0.aboveCriticalLevel && !$0.isProposed && $0.familyNote == nil } } + /// Above L_C, not proposed, not withheld and not explained by a modelled sum peak. + public var possible: [ElementCandidate] { + candidates.filter { $0.aboveCriticalLevel && !$0.isProposed && $0.familyNote == nil && $0.explainedBySumPeak == nil } + } + /// Tested on a residual peak that a modelled, detected sum peak explains better (`explainedBySumPeak` names it): the answer to + /// "sum peak or this element?" is the sum peak. Shown as explained pile-up, never as a possible element. + public var explainedBySumPeaks: [ElementCandidate] { candidates.filter { $0.explainedBySumPeak != nil } } /// At or above L_D but withheld by the family-consistency rule (`familyNote` says why). public var withheld: [ElementCandidate] { candidates.filter { $0.familyNote != nil } } /// The uncorroborated tier: found by an L or M line whose confirmation line the fitted range or the beam cannot hold (`unconfirmable`), @@ -306,6 +320,8 @@ public nonisolated struct ElementProposer: Sendable { /// An L or M nomination whose confirmation line lies outside the fitted range or above half the beam: it may join, but stays possible. var unconfirmable: String? var pass: Pass? + /// The sum column (its id, "sum:Al+Al") that took the peak this trial was last tested on; nil when an element took it. + var lostToSum: String? = nil } /// An element the loop added to the model. @@ -558,6 +574,10 @@ public nonisolated struct ElementProposer: Sendable { for e in eligible where e.key != best.key && !e.key.hasPrefix("sum:") { trials[e.key]?.reason = String(format: "The peak at %.3f keV went to %@, which explains it better (gain there %.0f against %.0f).", ep, best.key.hasPrefix("sum:") ? (sumInfo[best.key]?.label ?? best.key) : best.key, best.peakGain, e.peakGain) + // Explained only by a clear loss: outside the ambiguity margin at the peak, and no more total gain than the sum + // (a real line under the sum, Ar K-alpha 16 eV from Al+Al, gains more through its other lines). A tie stays possible. + let clear = best.peakGain - e.peakGain >= t.ambiguityGain && e.gain <= best.gain + trials[e.key]?.lostToSum = best.key.hasPrefix("sum:") && clear ? best.key : nil } model = p.model.groups fit = p @@ -674,9 +694,14 @@ public nonisolated struct ElementProposer: Sendable { return c.kind != .sumPeak || (c.requires.compactMap { parentNets[$0] }.min() ?? 0) * pairShare * sumPeakFraction >= net } } + // The sum columns of the final fit at or above their own L_D: a peak one of them took is explained pile-up. + var detectedSums: [String: String] = [:] + for (gi, g) in report.model.groups.enumerated() where g.element == "sum" { + if let st = stat(report, gi), st.net >= st.ld { detectedSums[g.id] = sumInfo[g.id]?.label ?? g.id } + } var candidates: [ElementCandidate] = [] func append(_ el: String, group: String, stat st: Stat, familyNote: String? = nil, possibleReason: String? = nil, - unconfirmable: Bool = false) throws { + unconfirmable: Bool = false, explainedBySumPeak: String? = nil) throws { let alpha = XRayLines.line(group)?.energy ?? 0 var note: String? if context.gridElements.contains(el) { @@ -696,7 +721,7 @@ public nonisolated struct ElementProposer: Sendable { element: el, group: group, energyKeV: alpha, net: st.net, sigma: st.sigma, sigmaZero: st.sigmaZero, criticalLevel: st.lc, detectionLimit: st.ld, conflicts: conflicts(el, group, alpha, st.net), suggestedRole: context.quantifyByDefault.contains(el) ? .quantify : .fitOnly, holeRegionNote: note, misfit: 1, - familyNote: familyNote, possibleReason: possibleReason, unconfirmable: unconfirmable)) + familyNote: familyNote, possibleReason: possibleReason, unconfirmable: unconfirmable, explainedBySumPeak: explainedBySumPeak)) } /// The numbers of a group whose limit cannot be stated: its area, no limit (never proposed). func unstated(_ p: Pass, _ gi: Int) -> Stat { Stat(net: p.result.values[gi], sigma: 0, sigmaZero: 0, lc: .infinity, ld: .infinity) } @@ -730,7 +755,8 @@ public nonisolated struct ElementProposer: Sendable { let st = tr.stat ?? tr.pass.flatMap { p in p.model.groups.firstIndex { $0.id == tr.group }.map { unstated(p, $0) } } ?? Stat(net: 0, sigma: 0, sigmaZero: 0, lc: .infinity, ld: .infinity) if let c = tr.confirmation { try append(el, group: tr.group, stat: st, familyNote: c) } - else { try append(el, group: tr.group, stat: st, possibleReason: tr.reason ?? "Tested but not added.") } + else { try append(el, group: tr.group, stat: st, possibleReason: tr.reason ?? "Tested but not added.", + explainedBySumPeak: tr.lostToSum.flatMap { detectedSums[$0] }) } } for f in overlapFinds where !candidates.contains(where: { $0.element == f.element }) { if let st = f.trial.stat { try append(f.element, group: f.trial.group, stat: st, possibleReason: f.trial.reason) } @@ -759,6 +785,10 @@ public nonisolated struct ElementProposer: Sendable { } notes += candidates.compactMap { c in c.familyNote.map { "Confirmation withheld \(c.element): \($0)" } } } + for label in Set(candidates.compactMap(\.explainedBySumPeak)).sorted() { + let els = candidates.filter { $0.explainedBySumPeak == label }.map(\.element) + notes.append("Explained as pile-up: \(els.joined(separator: ", ")) lost the peak they were tested on to the \(label) column, which the final fit keeps at or above its detection limit; they are not possible elements.") + } if overlapCheck { notes.append(String(format: "Overlap check (validation: none): every element whose lines all sit under modelled lines was fitted once with the settled model; %d reached L_D with a gain of at least %.0f and are possible only.", overlapFinds.count, t.minimumGain)) } diff --git a/Sources/SpectroEDX/Proposer/ProposalRules.swift b/Sources/SpectroEDX/Proposer/ProposalRules.swift index 5bcef4d..554a055 100644 --- a/Sources/SpectroEDX/Proposer/ProposalRules.swift +++ b/Sources/SpectroEDX/Proposer/ProposalRules.swift @@ -84,7 +84,8 @@ public nonisolated struct ProposalRules: Equatable, Sendable { /// Pure: the same result and rules always give the same picks. `resolutionMnKaEV` sets the FWHM that R1's distance is counted in /// (the room's detector width; the harness uses the app's default, 130 eV). public func apply(_ r: ProposalResult, resolutionMnKaEV: Double = 130) -> RuledProposal { - let proposedAll = r.candidates.filter(\.aboveDetectionLimit) // held and withheld candidates included: a K peak is a K peak + // Held and withheld candidates included: a K peak is a K peak. Not one explained by a sum peak: that peak is pile-up. + let proposedAll = r.candidates.filter { $0.aboveDetectionLimit && $0.explainedBySumPeak == nil } func z(_ element: String) -> Int { XRayLines.lines(of: element).first?.atomicNumber ?? 0 } func family(_ group: String) -> XRayFamily { XRayLines.line(group)?.family ?? .K } func name(_ group: String) -> String { UnlistedLineChecker.displayName(group) } diff --git a/Tests/SpectroCoreTests/Fixtures/spectro-golden/eds-proposer-sim.txt b/Tests/SpectroCoreTests/Fixtures/spectro-golden/eds-proposer-sim.txt index 9ee24ca..d9cccd9 100644 --- a/Tests/SpectroCoreTests/Fixtures/spectro-golden/eds-proposer-sim.txt +++ b/Tests/SpectroCoreTests/Fixtures/spectro-golden/eds-proposer-sim.txt @@ -27,6 +27,7 @@ region2(beta'' 300 counts/px).proposal.candidates[0].misfit = 3ff0000000000000 1 region2(beta'' 300 counts/px).proposal.candidates[0].familyNote = nil region2(beta'' 300 counts/px).proposal.candidates[0].possibleReason = "The peak at 2.737 keV went to Al+Mg sum, which explains it better (gain there 33387 against 29635)." region2(beta'' 300 counts/px).proposal.candidates[0].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[0].explainedBySumPeak = "Al+Mg sum" region2(beta'' 300 counts/px).proposal.candidates[1].element = "Ar" region2(beta'' 300 counts/px).proposal.candidates[1].group = "Ar_Ka" region2(beta'' 300 counts/px).proposal.candidates[1].energyKeV = 4007a95e9e1b089a 2.9577 @@ -53,6 +54,7 @@ region2(beta'' 300 counts/px).proposal.candidates[1].misfit = 3ff0000000000000 1 region2(beta'' 300 counts/px).proposal.candidates[1].familyNote = nil region2(beta'' 300 counts/px).proposal.candidates[1].possibleReason = "The peak at 3.227 keV went to Al+Si sum, which explains it better (gain there 212982 against 140417)." region2(beta'' 300 counts/px).proposal.candidates[1].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[1].explainedBySumPeak = "Al+Si sum" region2(beta'' 300 counts/px).proposal.candidates[2].element = "In" region2(beta'' 300 counts/px).proposal.candidates[2].group = "In_La" region2(beta'' 300 counts/px).proposal.candidates[2].energyKeV = 400a4bc6a7ef9db2 3.2869999999999999 @@ -68,6 +70,7 @@ region2(beta'' 300 counts/px).proposal.candidates[2].misfit = 3ff0000000000000 1 region2(beta'' 300 counts/px).proposal.candidates[2].familyNote = nil region2(beta'' 300 counts/px).proposal.candidates[2].possibleReason = "The peak at 3.477 keV went to Si+Si sum, which explains it better (gain there 24509 against 18445)." region2(beta'' 300 counts/px).proposal.candidates[2].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[2].explainedBySumPeak = "Si+Si sum" region2(beta'' 300 counts/px).proposal.candidates[3].element = "Sn" region2(beta'' 300 counts/px).proposal.candidates[3].group = "Sn_La" region2(beta'' 300 counts/px).proposal.candidates[3].energyKeV = 400b8d4fdf3b645a 3.444 @@ -94,6 +97,7 @@ region2(beta'' 300 counts/px).proposal.candidates[3].misfit = 3ff0000000000000 1 region2(beta'' 300 counts/px).proposal.candidates[3].familyNote = nil region2(beta'' 300 counts/px).proposal.candidates[3].possibleReason = "The peak at 3.477 keV went to Si+Si sum, which explains it better (gain there 24509 against 16177)." region2(beta'' 300 counts/px).proposal.candidates[3].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[3].explainedBySumPeak = "Si+Si sum" region2(beta'' 300 counts/px).proposal.candidates[4].element = "Pd" region2(beta'' 300 counts/px).proposal.candidates[4].group = "Pd_La" region2(beta'' 300 counts/px).proposal.candidates[4].energyKeV = 4006b573eab367a1 2.8386 @@ -109,6 +113,7 @@ region2(beta'' 300 counts/px).proposal.candidates[4].misfit = 3ff0000000000000 1 region2(beta'' 300 counts/px).proposal.candidates[4].familyNote = nil region2(beta'' 300 counts/px).proposal.candidates[4].possibleReason = "The peak at 2.737 keV went to Al+Mg sum, which explains it better (gain there 33387 against 5175)." region2(beta'' 300 counts/px).proposal.candidates[4].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[4].explainedBySumPeak = "Al+Mg sum" region2(beta'' 300 counts/px).proposal.candidates[5].element = "Ag" region2(beta'' 300 counts/px).proposal.candidates[5].group = "Ag_La" region2(beta'' 300 counts/px).proposal.candidates[5].energyKeV = 4007e00d1b71758e 2.9843999999999999 @@ -147,6 +152,7 @@ region2(beta'' 300 counts/px).proposal.candidates[5].misfit = 3ff0000000000000 1 region2(beta'' 300 counts/px).proposal.candidates[5].familyNote = nil region2(beta'' 300 counts/px).proposal.candidates[5].possibleReason = "The peak at 3.477 keV went to Si+Si sum, which explains it better (gain there 24509 against 4106)." region2(beta'' 300 counts/px).proposal.candidates[5].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[5].explainedBySumPeak = "Si+Si sum" region2(beta'' 300 counts/px).proposal.candidates[6].element = "Cd" region2(beta'' 300 counts/px).proposal.candidates[6].group = "Cd_La" region2(beta'' 300 counts/px).proposal.candidates[6].energyKeV = 40091205bc01a36e 3.1337999999999999 @@ -162,6 +168,7 @@ region2(beta'' 300 counts/px).proposal.candidates[6].misfit = 3ff0000000000000 1 region2(beta'' 300 counts/px).proposal.candidates[6].familyNote = nil region2(beta'' 300 counts/px).proposal.candidates[6].possibleReason = "The peak at 3.477 keV went to Si+Si sum, which explains it better (gain there 24509 against 4481)." region2(beta'' 300 counts/px).proposal.candidates[6].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[6].explainedBySumPeak = "Si+Si sum" region2(beta'' 300 counts/px).proposal.candidates[7].element = "Ru" region2(beta'' 300 counts/px).proposal.candidates[7].group = "Ru_La" region2(beta'' 300 counts/px).proposal.candidates[7].energyKeV = 400477ced916872b 2.5585 @@ -188,6 +195,7 @@ region2(beta'' 300 counts/px).proposal.candidates[7].misfit = 3ff0000000000000 1 region2(beta'' 300 counts/px).proposal.candidates[7].familyNote = "Ru Kα (19.28 keV) has a residual peak of 1.7 σ, below the 3 σ its Lα (2.56 keV) needs: not Ru." region2(beta'' 300 counts/px).proposal.candidates[7].possibleReason = nil region2(beta'' 300 counts/px).proposal.candidates[7].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[7].explainedBySumPeak = nil region2(beta'' 300 counts/px).proposal.candidates[8].element = "Bi" region2(beta'' 300 counts/px).proposal.candidates[8].group = "Bi_Ma" region2(beta'' 300 counts/px).proposal.candidates[8].energyKeV = 400360aa64c2f838 2.4222000000000001 @@ -203,6 +211,7 @@ region2(beta'' 300 counts/px).proposal.candidates[8].misfit = 3ff0000000000000 1 region2(beta'' 300 counts/px).proposal.candidates[8].familyNote = "Bi Lα (10.84 keV) has a residual peak of 2.4 σ, below the 3 σ its Mβ (2.53 keV) needs: not Bi." region2(beta'' 300 counts/px).proposal.candidates[8].possibleReason = nil region2(beta'' 300 counts/px).proposal.candidates[8].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[8].explainedBySumPeak = nil region2(beta'' 300 counts/px).proposal.candidates[9].element = "Cu" region2(beta'' 300 counts/px).proposal.candidates[9].group = "Cu_Ka" region2(beta'' 300 counts/px).proposal.candidates[9].energyKeV = 402018793dd97f63 8.0478000000000005 @@ -227,6 +236,7 @@ region2(beta'' 300 counts/px).proposal.candidates[9].misfit = 3ff0000000000000 1 region2(beta'' 300 counts/px).proposal.candidates[9].familyNote = nil region2(beta'' 300 counts/px).proposal.candidates[9].possibleReason = nil region2(beta'' 300 counts/px).proposal.candidates[9].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[9].explainedBySumPeak = nil region2(beta'' 300 counts/px).proposal.candidates[10].element = "Cl" region2(beta'' 300 counts/px).proposal.candidates[10].group = "Cl_Ka" region2(beta'' 300 counts/px).proposal.candidates[10].energyKeV = 4004faacd9e83e42 2.6223999999999998 @@ -242,6 +252,7 @@ region2(beta'' 300 counts/px).proposal.candidates[10].misfit = 3ff0000000000000 region2(beta'' 300 counts/px).proposal.candidates[10].familyNote = nil region2(beta'' 300 counts/px).proposal.candidates[10].possibleReason = "The peak at 2.737 keV went to Al+Mg sum, which explains it better (gain there 33387 against 1009)." region2(beta'' 300 counts/px).proposal.candidates[10].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[10].explainedBySumPeak = "Al+Mg sum" region2(beta'' 300 counts/px).proposal.candidates[11].element = "K" region2(beta'' 300 counts/px).proposal.candidates[11].group = "K_Ka" region2(beta'' 300 counts/px).proposal.candidates[11].energyKeV = 400a82a9930be0df 3.3138000000000001 @@ -257,6 +268,7 @@ region2(beta'' 300 counts/px).proposal.candidates[11].misfit = 3ff0000000000000 region2(beta'' 300 counts/px).proposal.candidates[11].familyNote = nil region2(beta'' 300 counts/px).proposal.candidates[11].possibleReason = "The peak at 3.477 keV went to Si+Si sum, which explains it better (gain there 24509 against 337)." region2(beta'' 300 counts/px).proposal.candidates[11].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[11].explainedBySumPeak = "Si+Si sum" region2(beta'' 300 counts/px).proposal.candidates[12].element = "Ta" region2(beta'' 300 counts/px).proposal.candidates[12].group = "Ta_La" region2(beta'' 300 counts/px).proposal.candidates[12].energyKeV = 40204ac083126e98 8.1460000000000008 @@ -272,6 +284,7 @@ region2(beta'' 300 counts/px).proposal.candidates[12].misfit = 3ff0000000000000 region2(beta'' 300 counts/px).proposal.candidates[12].familyNote = nil region2(beta'' 300 counts/px).proposal.candidates[12].possibleReason = "Adding it improves the fit by 13, below the 100 a new line needs." region2(beta'' 300 counts/px).proposal.candidates[12].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[12].explainedBySumPeak = nil region2(beta'' 300 counts/px).proposal.candidates[13].element = "Tm" region2(beta'' 300 counts/px).proposal.candidates[13].group = "Tm_La" region2(beta'' 300 counts/px).proposal.candidates[13].energyKeV = 401cb8a0902de00d 7.1802999999999999 @@ -287,6 +300,7 @@ region2(beta'' 300 counts/px).proposal.candidates[13].misfit = 3ff0000000000000 region2(beta'' 300 counts/px).proposal.candidates[13].familyNote = nil region2(beta'' 300 counts/px).proposal.candidates[13].possibleReason = "Adding it improves the fit by 13, below the 100 a new line needs." region2(beta'' 300 counts/px).proposal.candidates[13].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[13].explainedBySumPeak = nil region2(beta'' 300 counts/px).proposal.candidates[14].element = "Hf" region2(beta'' 300 counts/px).proposal.candidates[14].group = "Hf_La" region2(beta'' 300 counts/px).proposal.candidates[14].energyKeV = 401f989374bc6a7f 7.899 @@ -302,6 +316,7 @@ region2(beta'' 300 counts/px).proposal.candidates[14].misfit = 3ff0000000000000 region2(beta'' 300 counts/px).proposal.candidates[14].familyNote = nil region2(beta'' 300 counts/px).proposal.candidates[14].possibleReason = "Adding it improves the fit by 4, below the 100 a new line needs." region2(beta'' 300 counts/px).proposal.candidates[14].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[14].explainedBySumPeak = nil region2(beta'' 300 counts/px).proposal.candidates[15].element = "Pb" region2(beta'' 300 counts/px).proposal.candidates[15].group = "Pb_Ma" region2(beta'' 300 counts/px).proposal.candidates[15].energyKeV = 4002c467381d7dbf 2.3458999999999999 @@ -317,6 +332,7 @@ region2(beta'' 300 counts/px).proposal.candidates[15].misfit = 3ff0000000000000 region2(beta'' 300 counts/px).proposal.candidates[15].familyNote = "Pb Lα (10.55 keV) has a residual peak of 2.2 σ, below the 3 σ its Mβ (2.44 keV) needs: not Pb." region2(beta'' 300 counts/px).proposal.candidates[15].possibleReason = nil region2(beta'' 300 counts/px).proposal.candidates[15].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[15].explainedBySumPeak = nil region2(beta'' 300 counts/px).proposal.candidates[16].element = "Sb" region2(beta'' 300 counts/px).proposal.candidates[16].group = "Sb_La" region2(beta'' 300 counts/px).proposal.candidates[16].energyKeV = 400cd66cf41f212d 3.6046999999999998 @@ -332,6 +348,7 @@ region2(beta'' 300 counts/px).proposal.candidates[16].misfit = 3ff0000000000000 region2(beta'' 300 counts/px).proposal.candidates[16].familyNote = nil region2(beta'' 300 counts/px).proposal.candidates[16].possibleReason = "Adding it improves the fit by 7, below the 100 a new line needs." region2(beta'' 300 counts/px).proposal.candidates[16].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[16].explainedBySumPeak = nil region2(beta'' 300 counts/px).proposal.candidates[17].element = "Er" region2(beta'' 300 counts/px).proposal.candidates[17].group = "Er_La" region2(beta'' 300 counts/px).proposal.candidates[17].energyKeV = 401bcb5dcc63f141 6.9485999999999999 @@ -347,6 +364,7 @@ region2(beta'' 300 counts/px).proposal.candidates[17].misfit = 3ff0000000000000 region2(beta'' 300 counts/px).proposal.candidates[17].familyNote = nil region2(beta'' 300 counts/px).proposal.candidates[17].possibleReason = "Adding it improves the fit by 2, below the 100 a new line needs." region2(beta'' 300 counts/px).proposal.candidates[17].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[17].explainedBySumPeak = nil region2(beta'' 300 counts/px).proposal.candidates[18].element = "Ho" region2(beta'' 300 counts/px).proposal.candidates[18].group = "Ho_La" region2(beta'' 300 counts/px).proposal.candidates[18].energyKeV = 401ae0f9096bb98c 6.7196999999999996 @@ -362,6 +380,7 @@ region2(beta'' 300 counts/px).proposal.candidates[18].misfit = 3ff0000000000000 region2(beta'' 300 counts/px).proposal.candidates[18].familyNote = nil region2(beta'' 300 counts/px).proposal.candidates[18].possibleReason = "Adding it improves the fit by 1, below the 100 a new line needs." region2(beta'' 300 counts/px).proposal.candidates[18].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[18].explainedBySumPeak = nil region2(beta'' 300 counts/px).proposal.candidates[19].element = "Mo" region2(beta'' 300 counts/px).proposal.candidates[19].group = "Mo_La" region2(beta'' 300 counts/px).proposal.candidates[19].energyKeV = 400258793dd97f63 2.2932000000000001 @@ -389,6 +408,7 @@ region2(beta'' 300 counts/px).proposal.candidates[19].misfit = 3ff0000000000000 region2(beta'' 300 counts/px).proposal.candidates[19].familyNote = "Mo Kα (17.48 keV) has a residual peak of 1.4 σ, below the 3 σ its Lβ1 (2.39 keV) needs: not Mo." region2(beta'' 300 counts/px).proposal.candidates[19].possibleReason = nil region2(beta'' 300 counts/px).proposal.candidates[19].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[19].explainedBySumPeak = nil region2(beta'' 300 counts/px).proposal.candidates[20].element = "Tl" region2(beta'' 300 counts/px).proposal.candidates[20].group = "Tl_Ma" region2(beta'' 300 counts/px).proposal.candidates[20].energyKeV = 40022a9930be0ded 2.2707999999999999 @@ -416,6 +436,7 @@ region2(beta'' 300 counts/px).proposal.candidates[20].misfit = 3ff0000000000000 region2(beta'' 300 counts/px).proposal.candidates[20].familyNote = "Tl Lα (10.27 keV) has a residual peak of 1.7 σ, below the 3 σ its Mγ (2.57 keV) needs: not Tl." region2(beta'' 300 counts/px).proposal.candidates[20].possibleReason = nil region2(beta'' 300 counts/px).proposal.candidates[20].unconfirmable = false +region2(beta'' 300 counts/px).proposal.candidates[20].explainedBySumPeak = nil region2(beta'' 300 counts/px).proposal.sumPeaks: array 5 region2(beta'' 300 counts/px).proposal.sumPeaks[0].label = "Al+Al sum" region2(beta'' 300 counts/px).proposal.sumPeaks[0].elements: array 2 @@ -474,7 +495,7 @@ region2(beta'' 300 counts/px).proposal.refused[6].element = "N" region2(beta'' 300 counts/px).proposal.refused[6].reason = "Its line at 0.39 keV lies below the proposer's lowest tested line (0.45 keV): under the detector window the continuum is too model-dependent for a net area to be trusted. List it by hand if you know it is there." region2(beta'' 300 counts/px).proposal.currie.alpha = 3fa999999999999a 0.050000000000000003 region2(beta'' 300 counts/px).proposal.currie.beta = 3fa999999999999a 0.050000000000000003 -region2(beta'' 300 counts/px).proposal.notes: array 12 +region2(beta'' 300 counts/px).proposal.notes: array 15 region2(beta'' 300 counts/px).proposal.notes[0] = "Lines below 0.45 keV are not tested (a measured limit of the continuum model, not a property of the method)." region2(beta'' 300 counts/px).proposal.notes[1] = "Identification (validation: none): the residual's peaks at or above 5 σ of its top-hat filter (1.5 × below 1 keV); a candidate joins when Σ(y − μ)²/(μ + (0.01 μ)²) falls by at least 100, 20 % of that at its own lines, with its area at or above Currie L_D (α = β = 0.05, σ₀ from the fit's own variance with it absent) and no line against it; the final fit drops what it does not need (100). Kβ weights from NIST EPQ in this fit." region2(beta'' 300 counts/px).proposal.notes[2] = "Fit: least squares, weighted by 1/(y + (0.01 y)²) to decide, least squares, unweighted for the numbers shown; continuum: Kramers×Bernstein(9,5), split at 1.5596 keV (step broadened by the detector resolution); orders chosen on synthetic data; escape peaks: fitted for 1 line group above the Si K edge (1.839 keV); reference shapes: none; 6 residual peaks examined, 38 candidate fits, 41 fits in all." @@ -484,9 +505,12 @@ region2(beta'' 300 counts/px).proposal.notes[5] = "Confirmation withheld Bi: Bi region2(beta'' 300 counts/px).proposal.notes[6] = "Confirmation withheld Pb: Pb Lα (10.55 keV) has a residual peak of 2.2 σ, below the 3 σ its Mβ (2.44 keV) needs: not Pb." region2(beta'' 300 counts/px).proposal.notes[7] = "Confirmation withheld Mo: Mo Kα (17.48 keV) has a residual peak of 1.4 σ, below the 3 σ its Lβ1 (2.39 keV) needs: not Mo." region2(beta'' 300 counts/px).proposal.notes[8] = "Confirmation withheld Tl: Tl Lα (10.27 keV) has a residual peak of 1.7 σ, below the 3 σ its Mγ (2.57 keV) needs: not Tl." -region2(beta'' 300 counts/px).proposal.notes[9] = "Look-elsewhere: 38 candidates were tested, so about 0.02 chance passes of the area bar alone are expected; the gain and confirmation bars stand beside it." -region2(beta'' 300 counts/px).proposal.notes[10] = "Cu_Ka: Si-escape/parent ratio 22.5 % exceeds 5 %: an unmodelled line near the escape energy is likely (the 5 % flag is a sanity bound; the SDD Si escape is about 1 % at Cu Kα)" -region2(beta'' 300 counts/px).proposal.notes[11] = "a weak line next to Al Kα reads low by ≈ 300 counts per pooled spectrum on synthetic data (cause: the Al Kα incomplete-charge tail, unmodelled until a tail is measured on an Al matrix, `TailEstimate`); unmeasured on real data. Weak-line bias measured on synthetic data: Δ = −270 ± 31 counts at 900 planted Mg Kα (generator A, no tail: continuum–line projection) and −272 ± 23 at 882 (generator S); −325 ± 38 at 3000 (A) and −226 ± 22 at 2899 (S)" +region2(beta'' 300 counts/px).proposal.notes[9] = "Explained as pile-up: Rh, Pd, Cl lost the peak they were tested on to the Al+Mg sum column, which the final fit keeps at or above its detection limit; they are not possible elements." +region2(beta'' 300 counts/px).proposal.notes[10] = "Explained as pile-up: Ar lost the peak they were tested on to the Al+Si sum column, which the final fit keeps at or above its detection limit; they are not possible elements." +region2(beta'' 300 counts/px).proposal.notes[11] = "Explained as pile-up: In, Sn, Ag, Cd, K lost the peak they were tested on to the Si+Si sum column, which the final fit keeps at or above its detection limit; they are not possible elements." +region2(beta'' 300 counts/px).proposal.notes[12] = "Look-elsewhere: 38 candidates were tested, so about 0.02 chance passes of the area bar alone are expected; the gain and confirmation bars stand beside it." +region2(beta'' 300 counts/px).proposal.notes[13] = "Cu_Ka: Si-escape/parent ratio 22.5 % exceeds 5 %: an unmodelled line near the escape energy is likely (the 5 % flag is a sanity bound; the SDD Si escape is about 1 % at Cu Kα)" +region2(beta'' 300 counts/px).proposal.notes[14] = "a weak line next to Al Kα reads low by ≈ 300 counts per pooled spectrum on synthetic data (cause: the Al Kα incomplete-charge tail, unmodelled until a tail is measured on an Al matrix, `TailEstimate`); unmeasured on real data. Weak-line bias measured on synthetic data: Δ = −270 ± 31 counts at 900 planted Mg Kα (generator A, no tail: continuum–line projection) and −272 ± 23 at 882 (generator S); −325 ± 38 at 3000 (A) and −226 ± 22 at 2899 (S)" region2(beta'' 300 counts/px).proposal.passes = 41 region2(beta'' 300 counts/px).proposal.settled = true region2(beta'' 300 counts/px).proposal.reducedChiSquared = 3ff099f33b7c33b4 1.0375854800833908 diff --git a/Tests/SpectroCoreTests/Fixtures/spectro-golden/eds-realistic-l-sim.txt b/Tests/SpectroCoreTests/Fixtures/spectro-golden/eds-realistic-l-sim.txt index 55d1077..c11c447 100644 --- a/Tests/SpectroCoreTests/Fixtures/spectro-golden/eds-realistic-l-sim.txt +++ b/Tests/SpectroCoreTests/Fixtures/spectro-golden/eds-realistic-l-sim.txt @@ -1044,6 +1044,7 @@ seed0.four.unlistedProposal.candidates[0].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[0].familyNote = nil seed0.four.unlistedProposal.candidates[0].possibleReason = nil seed0.four.unlistedProposal.candidates[0].unconfirmable = false +seed0.four.unlistedProposal.candidates[0].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[1].element = "Ga" seed0.four.unlistedProposal.candidates[1].group = "Ga_La" seed0.four.unlistedProposal.candidates[1].energyKeV = 3ff1916872b020c5 1.0980000000000001 @@ -1069,6 +1070,7 @@ seed0.four.unlistedProposal.candidates[1].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[1].familyNote = nil seed0.four.unlistedProposal.candidates[1].possibleReason = nil seed0.four.unlistedProposal.candidates[1].unconfirmable = false +seed0.four.unlistedProposal.candidates[1].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[2].element = "Cu" seed0.four.unlistedProposal.candidates[2].group = "Cu_Ka" seed0.four.unlistedProposal.candidates[2].energyKeV = 402018793dd97f63 8.0478000000000005 @@ -1105,6 +1107,7 @@ seed0.four.unlistedProposal.candidates[2].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[2].familyNote = nil seed0.four.unlistedProposal.candidates[2].possibleReason = nil seed0.four.unlistedProposal.candidates[2].unconfirmable = false +seed0.four.unlistedProposal.candidates[2].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[3].element = "Pr" seed0.four.unlistedProposal.candidates[3].group = "Pr_Ma" seed0.four.unlistedProposal.candidates[3].energyKeV = 3fec985f06f69446 0.89359999999999995 @@ -1120,6 +1123,7 @@ seed0.four.unlistedProposal.candidates[3].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[3].familyNote = "Pr Lα (5.03 keV) has a residual peak of 1.7 σ, below the 3 σ its Mγ (1.13 keV) needs: not Pr." seed0.four.unlistedProposal.candidates[3].possibleReason = nil seed0.four.unlistedProposal.candidates[3].unconfirmable = false +seed0.four.unlistedProposal.candidates[3].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[4].element = "Eu" seed0.four.unlistedProposal.candidates[4].group = "Eu_Ma" seed0.four.unlistedProposal.candidates[4].energyKeV = 3ff195e9e1b089a0 1.0991 @@ -1135,6 +1139,7 @@ seed0.four.unlistedProposal.candidates[4].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[4].familyNote = "Eu Lα (5.85 keV) has a residual peak of 0.5 σ, below the 3 σ its Mα (1.10 keV) needs: not Eu." seed0.four.unlistedProposal.candidates[4].possibleReason = nil seed0.four.unlistedProposal.candidates[4].unconfirmable = false +seed0.four.unlistedProposal.candidates[4].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[5].element = "Sm" seed0.four.unlistedProposal.candidates[5].group = "Sm_Ma" seed0.four.unlistedProposal.candidates[5].energyKeV = 3ff0af4f0d844d01 1.0427999999999999 @@ -1161,6 +1166,7 @@ seed0.four.unlistedProposal.candidates[5].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[5].familyNote = "Sm Lα (5.64 keV) has a residual peak of 1.0 σ, below the 3 σ its Mβ (1.10 keV) needs: not Sm." seed0.four.unlistedProposal.candidates[5].possibleReason = nil seed0.four.unlistedProposal.candidates[5].unconfirmable = false +seed0.four.unlistedProposal.candidates[5].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[6].element = "Rb" seed0.four.unlistedProposal.candidates[6].group = "Rb_La" seed0.four.unlistedProposal.candidates[6].energyKeV = 3ffb1b089a027525 1.6940999999999999 @@ -1188,6 +1194,7 @@ seed0.four.unlistedProposal.candidates[6].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[6].familyNote = nil seed0.four.unlistedProposal.candidates[6].possibleReason = "The peak at 1.748 keV went to Ta, which explains it better (gain there 117 against 79)." seed0.four.unlistedProposal.candidates[6].unconfirmable = false +seed0.four.unlistedProposal.candidates[6].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[7].element = "Nd" seed0.four.unlistedProposal.candidates[7].group = "Nd_Ma" seed0.four.unlistedProposal.candidates[7].energyKeV = 3fee161e4f765fd9 0.94020000000000004 @@ -1203,6 +1210,7 @@ seed0.four.unlistedProposal.candidates[7].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[7].familyNote = "Nd Lα (5.23 keV) has a residual peak of 0.6 σ, below the 3 σ its Mβ (1.00 keV) needs: not Nd." seed0.four.unlistedProposal.candidates[7].possibleReason = nil seed0.four.unlistedProposal.candidates[7].unconfirmable = false +seed0.four.unlistedProposal.candidates[7].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[8].element = "Ce" seed0.four.unlistedProposal.candidates[8].group = "Ce_Ma" seed0.four.unlistedProposal.candidates[8].energyKeV = 3feb0e5604189375 0.84550000000000003 @@ -1218,6 +1226,7 @@ seed0.four.unlistedProposal.candidates[8].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[8].familyNote = "Ce Lα (4.84 keV) has a residual peak of 0.0 σ, below the 3 σ its Mγ (1.08 keV) needs: not Ce." seed0.four.unlistedProposal.candidates[8].possibleReason = nil seed0.four.unlistedProposal.candidates[8].unconfirmable = false +seed0.four.unlistedProposal.candidates[8].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[9].element = "Hf" seed0.four.unlistedProposal.candidates[9].group = "Hf_Ma" seed0.four.unlistedProposal.candidates[9].energyKeV = 3ffa504816f0068e 1.6446000000000001 @@ -1245,6 +1254,7 @@ seed0.four.unlistedProposal.candidates[9].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[9].familyNote = "Hf Lα (7.90 keV) has a residual peak of 0.0 σ, below the 3 σ its Mβ (1.70 keV) needs: not Hf." seed0.four.unlistedProposal.candidates[9].possibleReason = nil seed0.four.unlistedProposal.candidates[9].unconfirmable = false +seed0.four.unlistedProposal.candidates[9].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[10].element = "Zr" seed0.four.unlistedProposal.candidates[10].group = "Zr_La" seed0.four.unlistedProposal.candidates[10].energyKeV = 400056a161e4f766 2.0423 @@ -1272,6 +1282,7 @@ seed0.four.unlistedProposal.candidates[10].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[10].familyNote = "Zr Kα (15.78 keV) has a residual peak of 1.3 σ, below the 3 σ its Lα (2.04 keV) needs: not Zr." seed0.four.unlistedProposal.candidates[10].possibleReason = nil seed0.four.unlistedProposal.candidates[10].unconfirmable = false +seed0.four.unlistedProposal.candidates[10].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[11].element = "Ta" seed0.four.unlistedProposal.candidates[11].group = "Ta_Ma" seed0.four.unlistedProposal.candidates[11].energyKeV = 3ffb5c91d14e3bcd 1.7101 @@ -1299,6 +1310,7 @@ seed0.four.unlistedProposal.candidates[11].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[11].familyNote = nil seed0.four.unlistedProposal.candidates[11].possibleReason = nil seed0.four.unlistedProposal.candidates[11].unconfirmable = false +seed0.four.unlistedProposal.candidates[11].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[12].element = "P" seed0.four.unlistedProposal.candidates[12].group = "P_Ka" seed0.four.unlistedProposal.candidates[12].energyKeV = 40001b3d07c84b5e 2.0133000000000001 @@ -1326,6 +1338,7 @@ seed0.four.unlistedProposal.candidates[12].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[12].familyNote = nil seed0.four.unlistedProposal.candidates[12].possibleReason = "Adding it improves the fit by 8, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[12].unconfirmable = false +seed0.four.unlistedProposal.candidates[12].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[13].element = "Hg" seed0.four.unlistedProposal.candidates[13].group = "Hg_La" seed0.four.unlistedProposal.candidates[13].energyKeV = 4023fa5e353f7cee 9.9890000000000008 @@ -1341,6 +1354,7 @@ seed0.four.unlistedProposal.candidates[13].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[13].familyNote = nil seed0.four.unlistedProposal.candidates[13].possibleReason = "The peak at 9.888 keV went to Ge, which explains it better (gain there 16900529 against 1753845)." seed0.four.unlistedProposal.candidates[13].unconfirmable = false +seed0.four.unlistedProposal.candidates[13].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[14].element = "Gd" seed0.four.unlistedProposal.candidates[14].group = "Gd_Ma" seed0.four.unlistedProposal.candidates[14].energyKeV = 3ff2d3ae685db76b 1.1766799999999999 @@ -1356,6 +1370,7 @@ seed0.four.unlistedProposal.candidates[14].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[14].familyNote = "Gd Lα (6.06 keV) has a residual peak of 0.0 σ, below the 3 σ its Mα (1.18 keV) needs: not Gd." seed0.four.unlistedProposal.candidates[14].possibleReason = nil seed0.four.unlistedProposal.candidates[14].unconfirmable = false +seed0.four.unlistedProposal.candidates[14].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[15].element = "Lu" seed0.four.unlistedProposal.candidates[15].group = "Lu_La" seed0.four.unlistedProposal.candidates[15].energyKeV = 401e9f559b3d07c8 7.6555999999999997 @@ -1371,6 +1386,7 @@ seed0.four.unlistedProposal.candidates[15].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[15].familyNote = nil seed0.four.unlistedProposal.candidates[15].possibleReason = "Adding it improves the fit by 73, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[15].unconfirmable = false +seed0.four.unlistedProposal.candidates[15].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[16].element = "Ho" seed0.four.unlistedProposal.candidates[16].group = "Ho_La" seed0.four.unlistedProposal.candidates[16].energyKeV = 401ae0f9096bb98c 6.7196999999999996 @@ -1386,6 +1402,7 @@ seed0.four.unlistedProposal.candidates[16].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[16].familyNote = nil seed0.four.unlistedProposal.candidates[16].possibleReason = "Adding it improves the fit by 7, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[16].unconfirmable = false +seed0.four.unlistedProposal.candidates[16].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[17].element = "Zn" seed0.four.unlistedProposal.candidates[17].group = "Zn_Ka" seed0.four.unlistedProposal.candidates[17].energyKeV = 4021471de69ad42c 8.6388999999999996 @@ -1413,6 +1430,7 @@ seed0.four.unlistedProposal.candidates[17].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[17].familyNote = nil seed0.four.unlistedProposal.candidates[17].possibleReason = "Adding it improves the fit by 12, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[17].unconfirmable = false +seed0.four.unlistedProposal.candidates[17].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[18].element = "Re" seed0.four.unlistedProposal.candidates[18].group = "Re_La" seed0.four.unlistedProposal.candidates[18].energyKeV = 40214e075f6fd220 8.6524000000000001 @@ -1440,6 +1458,7 @@ seed0.four.unlistedProposal.candidates[18].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[18].familyNote = nil seed0.four.unlistedProposal.candidates[18].possibleReason = "Adding it improves the fit by 6, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[18].unconfirmable = false +seed0.four.unlistedProposal.candidates[18].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[19].element = "Tm" seed0.four.unlistedProposal.candidates[19].group = "Tm_La" seed0.four.unlistedProposal.candidates[19].energyKeV = 401cb8a0902de00d 7.1802999999999999 @@ -1455,6 +1474,7 @@ seed0.four.unlistedProposal.candidates[19].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[19].familyNote = nil seed0.four.unlistedProposal.candidates[19].possibleReason = "Adding it improves the fit by 7, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[19].unconfirmable = false +seed0.four.unlistedProposal.candidates[19].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[20].element = "Se" seed0.four.unlistedProposal.candidates[20].group = "Se_Ka" seed0.four.unlistedProposal.candidates[20].energyKeV = 402671a9fbe76c8b 11.222 @@ -1470,6 +1490,7 @@ seed0.four.unlistedProposal.candidates[20].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[20].familyNote = nil seed0.four.unlistedProposal.candidates[20].possibleReason = "Adding it improves the fit by 2, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[20].unconfirmable = false +seed0.four.unlistedProposal.candidates[20].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[21].element = "Tb" seed0.four.unlistedProposal.candidates[21].group = "Tb_La" seed0.four.unlistedProposal.candidates[21].energyKeV = 40191758e219652c 6.2728000000000002 @@ -1509,6 +1530,7 @@ seed0.four.unlistedProposal.candidates[21].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[21].familyNote = nil seed0.four.unlistedProposal.candidates[21].possibleReason = "Adding it improves the fit by 4, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[21].unconfirmable = false +seed0.four.unlistedProposal.candidates[21].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[22].element = "Ir" seed0.four.unlistedProposal.candidates[22].group = "Ir_La" seed0.four.unlistedProposal.candidates[22].energyKeV = 4022597f62b6ae7d 9.1747999999999994 @@ -1548,6 +1570,7 @@ seed0.four.unlistedProposal.candidates[22].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[22].familyNote = nil seed0.four.unlistedProposal.candidates[22].possibleReason = "Adding it improves the fit by 2, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[22].unconfirmable = false +seed0.four.unlistedProposal.candidates[22].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[23].element = "Dy" seed0.four.unlistedProposal.candidates[23].group = "Dy_La" seed0.four.unlistedProposal.candidates[23].energyKeV = 4019fb15b573eab3 6.4951999999999996 @@ -1563,6 +1586,7 @@ seed0.four.unlistedProposal.candidates[23].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[23].familyNote = nil seed0.four.unlistedProposal.candidates[23].possibleReason = "Adding it improves the fit by 0, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[23].unconfirmable = false +seed0.four.unlistedProposal.candidates[23].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[24].element = "Au" seed0.four.unlistedProposal.candidates[24].group = "Au_La" seed0.four.unlistedProposal.candidates[24].energyKeV = 40236d0e56041893 9.7129999999999992 @@ -1578,6 +1602,7 @@ seed0.four.unlistedProposal.candidates[24].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[24].familyNote = nil seed0.four.unlistedProposal.candidates[24].possibleReason = "Its area 0 is below its detection limit 278." seed0.four.unlistedProposal.candidates[24].unconfirmable = false +seed0.four.unlistedProposal.candidates[24].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[25].element = "Bi" seed0.four.unlistedProposal.candidates[25].group = "Bi_La" seed0.four.unlistedProposal.candidates[25].energyKeV = 4025ad916872b021 10.839 @@ -1593,6 +1618,7 @@ seed0.four.unlistedProposal.candidates[25].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[25].familyNote = nil seed0.four.unlistedProposal.candidates[25].possibleReason = "Adding it improves the fit by 16, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[25].unconfirmable = false +seed0.four.unlistedProposal.candidates[25].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[26].element = "Er" seed0.four.unlistedProposal.candidates[26].group = "Er_La" seed0.four.unlistedProposal.candidates[26].energyKeV = 401bcb5dcc63f141 6.9485999999999999 @@ -1608,6 +1634,7 @@ seed0.four.unlistedProposal.candidates[26].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[26].familyNote = nil seed0.four.unlistedProposal.candidates[26].possibleReason = "Only -45 % of its gain 589 is at its own lines (20 % needed): it mostly reshapes the background." seed0.four.unlistedProposal.candidates[26].unconfirmable = false +seed0.four.unlistedProposal.candidates[26].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[27].element = "La" seed0.four.unlistedProposal.candidates[27].group = "La_Ma" seed0.four.unlistedProposal.candidates[27].energyKeV = 3fea27525460aa65 0.81730000000000003 @@ -1623,6 +1650,7 @@ seed0.four.unlistedProposal.candidates[27].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[27].familyNote = "La Lα (4.65 keV) has a residual peak of 1.2 σ, below the 3 σ its Mγ (1.02 keV) needs: not La." seed0.four.unlistedProposal.candidates[27].possibleReason = nil seed0.four.unlistedProposal.candidates[27].unconfirmable = false +seed0.four.unlistedProposal.candidates[27].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[28].element = "Na" seed0.four.unlistedProposal.candidates[28].group = "Na_Ka" seed0.four.unlistedProposal.candidates[28].energyKeV = 3ff0a7ef9db22d0e 1.0409999999999999 @@ -1649,6 +1677,7 @@ seed0.four.unlistedProposal.candidates[28].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[28].familyNote = nil seed0.four.unlistedProposal.candidates[28].possibleReason = "Adding it improves the fit by 0, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[28].unconfirmable = false +seed0.four.unlistedProposal.candidates[28].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[29].element = "Os" seed0.four.unlistedProposal.candidates[29].group = "Os_La" seed0.four.unlistedProposal.candidates[29].energyKeV = 4021d25460aa64c3 8.9108000000000001 @@ -1664,6 +1693,7 @@ seed0.four.unlistedProposal.candidates[29].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[29].familyNote = nil seed0.four.unlistedProposal.candidates[29].possibleReason = "Adding it improves the fit by 2, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[29].unconfirmable = false +seed0.four.unlistedProposal.candidates[29].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[30].element = "Pt" seed0.four.unlistedProposal.candidates[30].group = "Pt_La" seed0.four.unlistedProposal.candidates[30].energyKeV = 4022e25aee631f8a 9.4420999999999999 @@ -1679,6 +1709,7 @@ seed0.four.unlistedProposal.candidates[30].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[30].familyNote = nil seed0.four.unlistedProposal.candidates[30].possibleReason = "Adding it improves the fit by 61, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[30].unconfirmable = false +seed0.four.unlistedProposal.candidates[30].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[31].element = "Sr" seed0.four.unlistedProposal.candidates[31].group = "Sr_La" seed0.four.unlistedProposal.candidates[31].energyKeV = 3ffce76c8b439581 1.8065 @@ -1705,6 +1736,7 @@ seed0.four.unlistedProposal.candidates[31].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[31].familyNote = "Sr Kα (14.16 keV) has a residual peak of 0.0 σ, below the 3 σ its Lα (1.81 keV) needs: not Sr." seed0.four.unlistedProposal.candidates[31].possibleReason = nil seed0.four.unlistedProposal.candidates[31].unconfirmable = false +seed0.four.unlistedProposal.candidates[31].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[32].element = "W" seed0.four.unlistedProposal.candidates[32].group = "W_La" seed0.four.unlistedProposal.candidates[32].energyKeV = 4020cb923a29c77a 8.3976000000000006 @@ -1732,6 +1764,7 @@ seed0.four.unlistedProposal.candidates[32].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[32].familyNote = nil seed0.four.unlistedProposal.candidates[32].possibleReason = "Adding it improves the fit by 3, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[32].unconfirmable = false +seed0.four.unlistedProposal.candidates[32].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[33].element = "Y" seed0.four.unlistedProposal.candidates[33].group = "Y_La" seed0.four.unlistedProposal.candidates[33].energyKeV = 3ffec2f837b4a234 1.9226000000000001 @@ -1747,6 +1780,7 @@ seed0.four.unlistedProposal.candidates[33].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[33].familyNote = "Y Kα (14.96 keV) has a residual peak of 0.9 σ, below the 3 σ its Lα (1.92 keV) needs: not Y." seed0.four.unlistedProposal.candidates[33].possibleReason = nil seed0.four.unlistedProposal.candidates[33].unconfirmable = false +seed0.four.unlistedProposal.candidates[33].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[34].element = "Yb" seed0.four.unlistedProposal.candidates[34].group = "Yb_La" seed0.four.unlistedProposal.candidates[34].energyKeV = 401da9c779a6b50b 7.4157999999999999 @@ -1762,6 +1796,7 @@ seed0.four.unlistedProposal.candidates[34].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[34].familyNote = nil seed0.four.unlistedProposal.candidates[34].possibleReason = "Adding it improves the fit by 18, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[34].unconfirmable = false +seed0.four.unlistedProposal.candidates[34].explainedBySumPeak = nil seed0.four.unlistedProposal.sumPeaks: n 0 sum 0000000000000000 0 fnv cbf29ce484222325 seed0.four.unlistedProposal.refused: array 7 seed0.four.unlistedProposal.refused[0].element = "H" diff --git a/Tests/SpectroCoreTests/Fixtures/spectro-golden/eds-unlisted-sim.txt b/Tests/SpectroCoreTests/Fixtures/spectro-golden/eds-unlisted-sim.txt index bbbb8b8..acdb14f 100644 --- a/Tests/SpectroCoreTests/Fixtures/spectro-golden/eds-unlisted-sim.txt +++ b/Tests/SpectroCoreTests/Fixtures/spectro-golden/eds-unlisted-sim.txt @@ -347,6 +347,7 @@ seed0.four.unlistedProposal.candidates[0].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[0].familyNote = nil seed0.four.unlistedProposal.candidates[0].possibleReason = nil seed0.four.unlistedProposal.candidates[0].unconfirmable = false +seed0.four.unlistedProposal.candidates[0].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[1].element = "Ga" seed0.four.unlistedProposal.candidates[1].group = "Ga_La" seed0.four.unlistedProposal.candidates[1].energyKeV = 3ff1916872b020c5 1.0980000000000001 @@ -372,6 +373,7 @@ seed0.four.unlistedProposal.candidates[1].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[1].familyNote = nil seed0.four.unlistedProposal.candidates[1].possibleReason = nil seed0.four.unlistedProposal.candidates[1].unconfirmable = false +seed0.four.unlistedProposal.candidates[1].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[2].element = "Cu" seed0.four.unlistedProposal.candidates[2].group = "Cu_Ka" seed0.four.unlistedProposal.candidates[2].energyKeV = 402018793dd97f63 8.0478000000000005 @@ -408,6 +410,7 @@ seed0.four.unlistedProposal.candidates[2].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[2].familyNote = nil seed0.four.unlistedProposal.candidates[2].possibleReason = nil seed0.four.unlistedProposal.candidates[2].unconfirmable = false +seed0.four.unlistedProposal.candidates[2].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[3].element = "Eu" seed0.four.unlistedProposal.candidates[3].group = "Eu_Ma" seed0.four.unlistedProposal.candidates[3].energyKeV = 3ff195e9e1b089a0 1.0991 @@ -423,6 +426,7 @@ seed0.four.unlistedProposal.candidates[3].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[3].familyNote = "Eu Lα (5.85 keV) has a residual peak of 0.0 σ, below the 3 σ its Mα (1.10 keV) needs: not Eu." seed0.four.unlistedProposal.candidates[3].possibleReason = nil seed0.four.unlistedProposal.candidates[3].unconfirmable = false +seed0.four.unlistedProposal.candidates[3].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[4].element = "Sm" seed0.four.unlistedProposal.candidates[4].group = "Sm_Ma" seed0.four.unlistedProposal.candidates[4].energyKeV = 3ff0af4f0d844d01 1.0427999999999999 @@ -438,6 +442,7 @@ seed0.four.unlistedProposal.candidates[4].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[4].familyNote = "Sm Lα (5.64 keV) has a residual peak of 0.0 σ, below the 3 σ its Mβ (1.10 keV) needs: not Sm." seed0.four.unlistedProposal.candidates[4].possibleReason = nil seed0.four.unlistedProposal.candidates[4].unconfirmable = false +seed0.four.unlistedProposal.candidates[4].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[5].element = "Pr" seed0.four.unlistedProposal.candidates[5].group = "Pr_Ma" seed0.four.unlistedProposal.candidates[5].energyKeV = 3fec985f06f69446 0.89359999999999995 @@ -453,6 +458,7 @@ seed0.four.unlistedProposal.candidates[5].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[5].familyNote = "Pr Lα (5.03 keV) has a residual peak of 0.0 σ, below the 3 σ its Mγ (1.13 keV) needs: not Pr." seed0.four.unlistedProposal.candidates[5].possibleReason = nil seed0.four.unlistedProposal.candidates[5].unconfirmable = false +seed0.four.unlistedProposal.candidates[5].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[6].element = "Na" seed0.four.unlistedProposal.candidates[6].group = "Na_Ka" seed0.four.unlistedProposal.candidates[6].energyKeV = 3ff0a7ef9db22d0e 1.0409999999999999 @@ -479,6 +485,7 @@ seed0.four.unlistedProposal.candidates[6].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[6].familyNote = nil seed0.four.unlistedProposal.candidates[6].possibleReason = "Adding it improves the fit by -13411, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[6].unconfirmable = false +seed0.four.unlistedProposal.candidates[6].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[7].element = "Ce" seed0.four.unlistedProposal.candidates[7].group = "Ce_Ma" seed0.four.unlistedProposal.candidates[7].energyKeV = 3feb0e5604189375 0.84550000000000003 @@ -494,6 +501,7 @@ seed0.four.unlistedProposal.candidates[7].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[7].familyNote = "Ce Lα (4.84 keV) has a residual peak of 0.5 σ, below the 3 σ its Mγ (1.08 keV) needs: not Ce." seed0.four.unlistedProposal.candidates[7].possibleReason = nil seed0.four.unlistedProposal.candidates[7].unconfirmable = false +seed0.four.unlistedProposal.candidates[7].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[8].element = "Rb" seed0.four.unlistedProposal.candidates[8].group = "Rb_La" seed0.four.unlistedProposal.candidates[8].energyKeV = 3ffb1b089a027525 1.6940999999999999 @@ -521,6 +529,7 @@ seed0.four.unlistedProposal.candidates[8].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[8].familyNote = "Rb Kα (13.40 keV) has a residual peak of 0.0 σ, below the 3 σ its Lβ1 (1.75 keV) needs: not Rb." seed0.four.unlistedProposal.candidates[8].possibleReason = nil seed0.four.unlistedProposal.candidates[8].unconfirmable = false +seed0.four.unlistedProposal.candidates[8].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[9].element = "Ta" seed0.four.unlistedProposal.candidates[9].group = "Ta_Ma" seed0.four.unlistedProposal.candidates[9].energyKeV = 3ffb5c91d14e3bcd 1.7101 @@ -548,6 +557,7 @@ seed0.four.unlistedProposal.candidates[9].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[9].familyNote = "Ta Lα (8.15 keV) has a residual peak of 0.0 σ, below the 3 σ its Mβ (1.77 keV) needs: not Ta." seed0.four.unlistedProposal.candidates[9].possibleReason = nil seed0.four.unlistedProposal.candidates[9].unconfirmable = false +seed0.four.unlistedProposal.candidates[9].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[10].element = "Zr" seed0.four.unlistedProposal.candidates[10].group = "Zr_La" seed0.four.unlistedProposal.candidates[10].energyKeV = 400056a161e4f766 2.0423 @@ -575,6 +585,7 @@ seed0.four.unlistedProposal.candidates[10].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[10].familyNote = "Zr Kα (15.78 keV) has a residual peak of 0.0 σ, below the 3 σ its Lα (2.04 keV) needs: not Zr." seed0.four.unlistedProposal.candidates[10].possibleReason = nil seed0.four.unlistedProposal.candidates[10].unconfirmable = false +seed0.four.unlistedProposal.candidates[10].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[11].element = "P" seed0.four.unlistedProposal.candidates[11].group = "P_Ka" seed0.four.unlistedProposal.candidates[11].energyKeV = 40001b3d07c84b5e 2.0133000000000001 @@ -602,6 +613,7 @@ seed0.four.unlistedProposal.candidates[11].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[11].familyNote = nil seed0.four.unlistedProposal.candidates[11].possibleReason = "Adding it improves the fit by 11, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[11].unconfirmable = false +seed0.four.unlistedProposal.candidates[11].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[12].element = "Gd" seed0.four.unlistedProposal.candidates[12].group = "Gd_Ma" seed0.four.unlistedProposal.candidates[12].energyKeV = 3ff2d3ae685db76b 1.1766799999999999 @@ -617,6 +629,7 @@ seed0.four.unlistedProposal.candidates[12].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[12].familyNote = "Gd Lα (6.06 keV) has a residual peak of 0.5 σ, below the 3 σ its Mα (1.18 keV) needs: not Gd." seed0.four.unlistedProposal.candidates[12].possibleReason = nil seed0.four.unlistedProposal.candidates[12].unconfirmable = false +seed0.four.unlistedProposal.candidates[12].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[13].element = "Hg" seed0.four.unlistedProposal.candidates[13].group = "Hg_La" seed0.four.unlistedProposal.candidates[13].energyKeV = 4023fa5e353f7cee 9.9890000000000008 @@ -632,6 +645,7 @@ seed0.four.unlistedProposal.candidates[13].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[13].familyNote = nil seed0.four.unlistedProposal.candidates[13].possibleReason = "The peak at 9.888 keV went to Ge, which explains it better (gain there 16113088 against 1417710)." seed0.four.unlistedProposal.candidates[13].unconfirmable = false +seed0.four.unlistedProposal.candidates[13].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[14].element = "Lu" seed0.four.unlistedProposal.candidates[14].group = "Lu_La" seed0.four.unlistedProposal.candidates[14].energyKeV = 401e9f559b3d07c8 7.6555999999999997 @@ -647,6 +661,7 @@ seed0.four.unlistedProposal.candidates[14].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[14].familyNote = nil seed0.four.unlistedProposal.candidates[14].possibleReason = "Adding it improves the fit by 22, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[14].unconfirmable = false +seed0.four.unlistedProposal.candidates[14].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[15].element = "Hf" seed0.four.unlistedProposal.candidates[15].group = "Hf_La" seed0.four.unlistedProposal.candidates[15].energyKeV = 401f989374bc6a7f 7.899 @@ -662,6 +677,7 @@ seed0.four.unlistedProposal.candidates[15].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[15].familyNote = nil seed0.four.unlistedProposal.candidates[15].possibleReason = "Adding it improves the fit by 23, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[15].unconfirmable = false +seed0.four.unlistedProposal.candidates[15].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[16].element = "Dy" seed0.four.unlistedProposal.candidates[16].group = "Dy_La" seed0.four.unlistedProposal.candidates[16].energyKeV = 4019fb15b573eab3 6.4951999999999996 @@ -677,6 +693,7 @@ seed0.four.unlistedProposal.candidates[16].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[16].familyNote = nil seed0.four.unlistedProposal.candidates[16].possibleReason = "Adding it improves the fit by 3, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[16].unconfirmable = false +seed0.four.unlistedProposal.candidates[16].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[17].element = "Tm" seed0.four.unlistedProposal.candidates[17].group = "Tm_La" seed0.four.unlistedProposal.candidates[17].energyKeV = 401cb8a0902de00d 7.1802999999999999 @@ -692,6 +709,7 @@ seed0.four.unlistedProposal.candidates[17].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[17].familyNote = nil seed0.four.unlistedProposal.candidates[17].possibleReason = "Adding it improves the fit by 2, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[17].unconfirmable = false +seed0.four.unlistedProposal.candidates[17].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[18].element = "Zn" seed0.four.unlistedProposal.candidates[18].group = "Zn_Ka" seed0.four.unlistedProposal.candidates[18].energyKeV = 4021471de69ad42c 8.6388999999999996 @@ -719,6 +737,7 @@ seed0.four.unlistedProposal.candidates[18].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[18].familyNote = nil seed0.four.unlistedProposal.candidates[18].possibleReason = "Adding it improves the fit by 2, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[18].unconfirmable = false +seed0.four.unlistedProposal.candidates[18].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[19].element = "Se" seed0.four.unlistedProposal.candidates[19].group = "Se_Ka" seed0.four.unlistedProposal.candidates[19].energyKeV = 402671a9fbe76c8b 11.222 @@ -734,6 +753,7 @@ seed0.four.unlistedProposal.candidates[19].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[19].familyNote = nil seed0.four.unlistedProposal.candidates[19].possibleReason = "Adding it improves the fit by 6, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[19].unconfirmable = false +seed0.four.unlistedProposal.candidates[19].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[20].element = "Ho" seed0.four.unlistedProposal.candidates[20].group = "Ho_La" seed0.four.unlistedProposal.candidates[20].energyKeV = 401ae0f9096bb98c 6.7196999999999996 @@ -749,6 +769,7 @@ seed0.four.unlistedProposal.candidates[20].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[20].familyNote = nil seed0.four.unlistedProposal.candidates[20].possibleReason = "Adding it improves the fit by 0, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[20].unconfirmable = false +seed0.four.unlistedProposal.candidates[20].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[21].element = "Tb" seed0.four.unlistedProposal.candidates[21].group = "Tb_La" seed0.four.unlistedProposal.candidates[21].energyKeV = 40191758e219652c 6.2728000000000002 @@ -776,6 +797,7 @@ seed0.four.unlistedProposal.candidates[21].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[21].familyNote = nil seed0.four.unlistedProposal.candidates[21].possibleReason = "Adding it improves the fit by 0, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[21].unconfirmable = false +seed0.four.unlistedProposal.candidates[21].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[22].element = "Re" seed0.four.unlistedProposal.candidates[22].group = "Re_La" seed0.four.unlistedProposal.candidates[22].energyKeV = 40214e075f6fd220 8.6524000000000001 @@ -803,6 +825,7 @@ seed0.four.unlistedProposal.candidates[22].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[22].familyNote = nil seed0.four.unlistedProposal.candidates[22].possibleReason = "Adding it improves the fit by 1, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[22].unconfirmable = false +seed0.four.unlistedProposal.candidates[22].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[23].element = "Au" seed0.four.unlistedProposal.candidates[23].group = "Au_La" seed0.four.unlistedProposal.candidates[23].energyKeV = 40236d0e56041893 9.7129999999999992 @@ -818,6 +841,7 @@ seed0.four.unlistedProposal.candidates[23].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[23].familyNote = nil seed0.four.unlistedProposal.candidates[23].possibleReason = "Its area 0 is below its detection limit 281." seed0.four.unlistedProposal.candidates[23].unconfirmable = false +seed0.four.unlistedProposal.candidates[23].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[24].element = "Bi" seed0.four.unlistedProposal.candidates[24].group = "Bi_La" seed0.four.unlistedProposal.candidates[24].energyKeV = 4025ad916872b021 10.839 @@ -833,6 +857,7 @@ seed0.four.unlistedProposal.candidates[24].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[24].familyNote = nil seed0.four.unlistedProposal.candidates[24].possibleReason = "Adding it improves the fit by -0, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[24].unconfirmable = false +seed0.four.unlistedProposal.candidates[24].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[25].element = "Er" seed0.four.unlistedProposal.candidates[25].group = "Er_La" seed0.four.unlistedProposal.candidates[25].energyKeV = 401bcb5dcc63f141 6.9485999999999999 @@ -848,6 +873,7 @@ seed0.four.unlistedProposal.candidates[25].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[25].familyNote = nil seed0.four.unlistedProposal.candidates[25].possibleReason = "Adding it improves the fit by -2724, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[25].unconfirmable = false +seed0.four.unlistedProposal.candidates[25].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[26].element = "Ir" seed0.four.unlistedProposal.candidates[26].group = "Ir_La" seed0.four.unlistedProposal.candidates[26].energyKeV = 4022597f62b6ae7d 9.1747999999999994 @@ -875,6 +901,7 @@ seed0.four.unlistedProposal.candidates[26].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[26].familyNote = nil seed0.four.unlistedProposal.candidates[26].possibleReason = "Adding it improves the fit by 9, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[26].unconfirmable = false +seed0.four.unlistedProposal.candidates[26].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[27].element = "La" seed0.four.unlistedProposal.candidates[27].group = "La_Ma" seed0.four.unlistedProposal.candidates[27].energyKeV = 3fea27525460aa65 0.81730000000000003 @@ -890,6 +917,7 @@ seed0.four.unlistedProposal.candidates[27].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[27].familyNote = "La Lα (4.65 keV) has a residual peak of 1.4 σ, below the 3 σ its Mγ (1.02 keV) needs: not La." seed0.four.unlistedProposal.candidates[27].possibleReason = nil seed0.four.unlistedProposal.candidates[27].unconfirmable = false +seed0.four.unlistedProposal.candidates[27].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[28].element = "Nd" seed0.four.unlistedProposal.candidates[28].group = "Nd_Ma" seed0.four.unlistedProposal.candidates[28].energyKeV = 3fee161e4f765fd9 0.94020000000000004 @@ -905,6 +933,7 @@ seed0.four.unlistedProposal.candidates[28].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[28].familyNote = "Nd Lα (5.23 keV) has a residual peak of 0.0 σ, below the 3 σ its Mβ (1.00 keV) needs: not Nd." seed0.four.unlistedProposal.candidates[28].possibleReason = nil seed0.four.unlistedProposal.candidates[28].unconfirmable = false +seed0.four.unlistedProposal.candidates[28].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[29].element = "Os" seed0.four.unlistedProposal.candidates[29].group = "Os_La" seed0.four.unlistedProposal.candidates[29].energyKeV = 4021d25460aa64c3 8.9108000000000001 @@ -920,6 +949,7 @@ seed0.four.unlistedProposal.candidates[29].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[29].familyNote = nil seed0.four.unlistedProposal.candidates[29].possibleReason = "Adding it improves the fit by -0, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[29].unconfirmable = false +seed0.four.unlistedProposal.candidates[29].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[30].element = "Pt" seed0.four.unlistedProposal.candidates[30].group = "Pt_La" seed0.four.unlistedProposal.candidates[30].energyKeV = 4022e25aee631f8a 9.4420999999999999 @@ -935,6 +965,7 @@ seed0.four.unlistedProposal.candidates[30].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[30].familyNote = nil seed0.four.unlistedProposal.candidates[30].possibleReason = "Adding it improves the fit by 43, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[30].unconfirmable = false +seed0.four.unlistedProposal.candidates[30].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[31].element = "Sr" seed0.four.unlistedProposal.candidates[31].group = "Sr_La" seed0.four.unlistedProposal.candidates[31].energyKeV = 3ffce76c8b439581 1.8065 @@ -961,6 +992,7 @@ seed0.four.unlistedProposal.candidates[31].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[31].familyNote = "Sr Kα (14.16 keV) has a residual peak of 0.8 σ, below the 3 σ its Lα (1.81 keV) needs: not Sr." seed0.four.unlistedProposal.candidates[31].possibleReason = nil seed0.four.unlistedProposal.candidates[31].unconfirmable = false +seed0.four.unlistedProposal.candidates[31].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[32].element = "W" seed0.four.unlistedProposal.candidates[32].group = "W_La" seed0.four.unlistedProposal.candidates[32].energyKeV = 4020cb923a29c77a 8.3976000000000006 @@ -988,6 +1020,7 @@ seed0.four.unlistedProposal.candidates[32].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[32].familyNote = nil seed0.four.unlistedProposal.candidates[32].possibleReason = "Adding it improves the fit by 1, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[32].unconfirmable = false +seed0.four.unlistedProposal.candidates[32].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[33].element = "Y" seed0.four.unlistedProposal.candidates[33].group = "Y_La" seed0.four.unlistedProposal.candidates[33].energyKeV = 3ffec2f837b4a234 1.9226000000000001 @@ -1003,6 +1036,7 @@ seed0.four.unlistedProposal.candidates[33].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[33].familyNote = "Y Kα (14.96 keV) has a residual peak of 0.4 σ, below the 3 σ its Lα (1.92 keV) needs: not Y." seed0.four.unlistedProposal.candidates[33].possibleReason = nil seed0.four.unlistedProposal.candidates[33].unconfirmable = false +seed0.four.unlistedProposal.candidates[33].explainedBySumPeak = nil seed0.four.unlistedProposal.candidates[34].element = "Yb" seed0.four.unlistedProposal.candidates[34].group = "Yb_La" seed0.four.unlistedProposal.candidates[34].energyKeV = 401da9c779a6b50b 7.4157999999999999 @@ -1018,6 +1052,7 @@ seed0.four.unlistedProposal.candidates[34].misfit = 3ff0000000000000 1 seed0.four.unlistedProposal.candidates[34].familyNote = nil seed0.four.unlistedProposal.candidates[34].possibleReason = "Adding it improves the fit by 11, below the 100 a new line needs." seed0.four.unlistedProposal.candidates[34].unconfirmable = false +seed0.four.unlistedProposal.candidates[34].explainedBySumPeak = nil seed0.four.unlistedProposal.sumPeaks: n 0 sum 0000000000000000 0 fnv cbf29ce484222325 seed0.four.unlistedProposal.refused: array 7 seed0.four.unlistedProposal.refused[0].element = "H" @@ -1573,6 +1608,7 @@ seed0.seven.unlistedProposal.candidates[0].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[0].familyNote = "Rb Kα (13.40 keV) has a residual peak of 0.0 σ, below the 3 σ its Lβ1 (1.75 keV) needs: not Rb." seed0.seven.unlistedProposal.candidates[0].possibleReason = nil seed0.seven.unlistedProposal.candidates[0].unconfirmable = false +seed0.seven.unlistedProposal.candidates[0].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[1].element = "Pt" seed0.seven.unlistedProposal.candidates[1].group = "Pt_Ma" seed0.seven.unlistedProposal.candidates[1].energyKeV = 4000676c8b439581 2.0505 @@ -1600,6 +1636,7 @@ seed0.seven.unlistedProposal.candidates[1].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[1].familyNote = "Pt Lα (9.44 keV) has a residual peak of 0.0 σ, below the 3 σ its Mα (2.05 keV) needs: not Pt." seed0.seven.unlistedProposal.candidates[1].possibleReason = nil seed0.seven.unlistedProposal.candidates[1].unconfirmable = false +seed0.seven.unlistedProposal.candidates[1].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[2].element = "Au" seed0.seven.unlistedProposal.candidates[2].group = "Au_Ma" seed0.seven.unlistedProposal.candidates[2].energyKeV = 4000fbb2fec56d5d 2.1229 @@ -1627,6 +1664,7 @@ seed0.seven.unlistedProposal.candidates[2].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[2].familyNote = "Au Lα (9.71 keV) has a residual peak of 0.0 σ, below the 3 σ its Mα (2.12 keV) needs: not Au." seed0.seven.unlistedProposal.candidates[2].possibleReason = nil seed0.seven.unlistedProposal.candidates[2].unconfirmable = false +seed0.seven.unlistedProposal.candidates[2].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[3].element = "Zr" seed0.seven.unlistedProposal.candidates[3].group = "Zr_La" seed0.seven.unlistedProposal.candidates[3].energyKeV = 400056a161e4f766 2.0423 @@ -1654,6 +1692,7 @@ seed0.seven.unlistedProposal.candidates[3].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[3].familyNote = "Zr Kα (15.78 keV) has a residual peak of 0.0 σ, below the 3 σ its Lα (2.04 keV) needs: not Zr." seed0.seven.unlistedProposal.candidates[3].possibleReason = nil seed0.seven.unlistedProposal.candidates[3].unconfirmable = false +seed0.seven.unlistedProposal.candidates[3].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[4].element = "Nb" seed0.seven.unlistedProposal.candidates[4].group = "Nb_La" seed0.seven.unlistedProposal.candidates[4].energyKeV = 400153c36113404f 2.1659000000000002 @@ -1692,6 +1731,7 @@ seed0.seven.unlistedProposal.candidates[4].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[4].familyNote = "Nb Kα (16.62 keV) has a residual peak of 0.0 σ, below the 3 σ its Lα (2.17 keV) needs: not Nb." seed0.seven.unlistedProposal.candidates[4].possibleReason = nil seed0.seven.unlistedProposal.candidates[4].unconfirmable = false +seed0.seven.unlistedProposal.candidates[4].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[5].element = "Ta" seed0.seven.unlistedProposal.candidates[5].group = "Ta_Ma" seed0.seven.unlistedProposal.candidates[5].energyKeV = 3ffb5c91d14e3bcd 1.7101 @@ -1719,6 +1759,7 @@ seed0.seven.unlistedProposal.candidates[5].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[5].familyNote = nil seed0.seven.unlistedProposal.candidates[5].possibleReason = nil seed0.seven.unlistedProposal.candidates[5].unconfirmable = false +seed0.seven.unlistedProposal.candidates[5].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[6].element = "Er" seed0.seven.unlistedProposal.candidates[6].group = "Er_La" seed0.seven.unlistedProposal.candidates[6].energyKeV = 401bcb5dcc63f141 6.9485999999999999 @@ -1734,6 +1775,7 @@ seed0.seven.unlistedProposal.candidates[6].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[6].familyNote = nil seed0.seven.unlistedProposal.candidates[6].possibleReason = "Adding it improves the fit by 15, below the 100 a new line needs." seed0.seven.unlistedProposal.candidates[6].unconfirmable = false +seed0.seven.unlistedProposal.candidates[6].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[7].element = "Lu" seed0.seven.unlistedProposal.candidates[7].group = "Lu_La" seed0.seven.unlistedProposal.candidates[7].energyKeV = 401e9f559b3d07c8 7.6555999999999997 @@ -1749,6 +1791,7 @@ seed0.seven.unlistedProposal.candidates[7].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[7].familyNote = nil seed0.seven.unlistedProposal.candidates[7].possibleReason = "Adding it improves the fit by 34, below the 100 a new line needs." seed0.seven.unlistedProposal.candidates[7].unconfirmable = false +seed0.seven.unlistedProposal.candidates[7].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[8].element = "Hf" seed0.seven.unlistedProposal.candidates[8].group = "Hf_La" seed0.seven.unlistedProposal.candidates[8].energyKeV = 401f989374bc6a7f 7.899 @@ -1764,6 +1807,7 @@ seed0.seven.unlistedProposal.candidates[8].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[8].familyNote = nil seed0.seven.unlistedProposal.candidates[8].possibleReason = "Adding it improves the fit by 14, below the 100 a new line needs." seed0.seven.unlistedProposal.candidates[8].unconfirmable = false +seed0.seven.unlistedProposal.candidates[8].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[9].element = "Hg" seed0.seven.unlistedProposal.candidates[9].group = "Hg_La" seed0.seven.unlistedProposal.candidates[9].energyKeV = 4023fa5e353f7cee 9.9890000000000008 @@ -1779,6 +1823,7 @@ seed0.seven.unlistedProposal.candidates[9].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[9].familyNote = nil seed0.seven.unlistedProposal.candidates[9].possibleReason = "Adding it improves the fit by 24, below the 100 a new line needs." seed0.seven.unlistedProposal.candidates[9].unconfirmable = false +seed0.seven.unlistedProposal.candidates[9].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[10].element = "Se" seed0.seven.unlistedProposal.candidates[10].group = "Se_Ka" seed0.seven.unlistedProposal.candidates[10].energyKeV = 402671a9fbe76c8b 11.222 @@ -1794,6 +1839,7 @@ seed0.seven.unlistedProposal.candidates[10].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[10].familyNote = nil seed0.seven.unlistedProposal.candidates[10].possibleReason = "Adding it improves the fit by 12, below the 100 a new line needs." seed0.seven.unlistedProposal.candidates[10].unconfirmable = false +seed0.seven.unlistedProposal.candidates[10].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[11].element = "Zn" seed0.seven.unlistedProposal.candidates[11].group = "Zn_Ka" seed0.seven.unlistedProposal.candidates[11].energyKeV = 4021471de69ad42c 8.6388999999999996 @@ -1821,6 +1867,7 @@ seed0.seven.unlistedProposal.candidates[11].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[11].familyNote = nil seed0.seven.unlistedProposal.candidates[11].possibleReason = "Adding it improves the fit by 8, below the 100 a new line needs." seed0.seven.unlistedProposal.candidates[11].unconfirmable = false +seed0.seven.unlistedProposal.candidates[11].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[12].element = "Tm" seed0.seven.unlistedProposal.candidates[12].group = "Tm_La" seed0.seven.unlistedProposal.candidates[12].energyKeV = 401cb8a0902de00d 7.1802999999999999 @@ -1836,6 +1883,7 @@ seed0.seven.unlistedProposal.candidates[12].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[12].familyNote = nil seed0.seven.unlistedProposal.candidates[12].possibleReason = "Adding it improves the fit by 3, below the 100 a new line needs." seed0.seven.unlistedProposal.candidates[12].unconfirmable = false +seed0.seven.unlistedProposal.candidates[12].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[13].element = "Dy" seed0.seven.unlistedProposal.candidates[13].group = "Dy_La" seed0.seven.unlistedProposal.candidates[13].energyKeV = 4019fb15b573eab3 6.4951999999999996 @@ -1851,6 +1899,7 @@ seed0.seven.unlistedProposal.candidates[13].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[13].familyNote = nil seed0.seven.unlistedProposal.candidates[13].possibleReason = "Adding it improves the fit by 3, below the 100 a new line needs." seed0.seven.unlistedProposal.candidates[13].unconfirmable = false +seed0.seven.unlistedProposal.candidates[13].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[14].element = "Re" seed0.seven.unlistedProposal.candidates[14].group = "Re_La" seed0.seven.unlistedProposal.candidates[14].energyKeV = 40214e075f6fd220 8.6524000000000001 @@ -1878,6 +1927,7 @@ seed0.seven.unlistedProposal.candidates[14].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[14].familyNote = nil seed0.seven.unlistedProposal.candidates[14].possibleReason = "Adding it improves the fit by 3, below the 100 a new line needs." seed0.seven.unlistedProposal.candidates[14].unconfirmable = false +seed0.seven.unlistedProposal.candidates[14].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[15].element = "Ho" seed0.seven.unlistedProposal.candidates[15].group = "Ho_La" seed0.seven.unlistedProposal.candidates[15].energyKeV = 401ae0f9096bb98c 6.7196999999999996 @@ -1893,6 +1943,7 @@ seed0.seven.unlistedProposal.candidates[15].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[15].familyNote = nil seed0.seven.unlistedProposal.candidates[15].possibleReason = "Adding it improves the fit by 2, below the 100 a new line needs." seed0.seven.unlistedProposal.candidates[15].unconfirmable = false +seed0.seven.unlistedProposal.candidates[15].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[16].element = "Tb" seed0.seven.unlistedProposal.candidates[16].group = "Tb_La" seed0.seven.unlistedProposal.candidates[16].energyKeV = 40191758e219652c 6.2728000000000002 @@ -1932,6 +1983,7 @@ seed0.seven.unlistedProposal.candidates[16].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[16].familyNote = nil seed0.seven.unlistedProposal.candidates[16].possibleReason = "Adding it improves the fit by 1, below the 100 a new line needs." seed0.seven.unlistedProposal.candidates[16].unconfirmable = false +seed0.seven.unlistedProposal.candidates[16].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[17].element = "Bi" seed0.seven.unlistedProposal.candidates[17].group = "Bi_La" seed0.seven.unlistedProposal.candidates[17].energyKeV = 4025ad916872b021 10.839 @@ -1947,6 +1999,7 @@ seed0.seven.unlistedProposal.candidates[17].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[17].familyNote = nil seed0.seven.unlistedProposal.candidates[17].possibleReason = "Adding it improves the fit by 17, below the 100 a new line needs." seed0.seven.unlistedProposal.candidates[17].unconfirmable = false +seed0.seven.unlistedProposal.candidates[17].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[18].element = "Ir" seed0.seven.unlistedProposal.candidates[18].group = "Ir_Ma" seed0.seven.unlistedProposal.candidates[18].energyKeV = 3fffadab9f559b3d 1.9799 @@ -1974,6 +2027,7 @@ seed0.seven.unlistedProposal.candidates[18].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[18].familyNote = "Ir Lα (9.17 keV) has a residual peak of 0.0 σ, below the 3 σ its Mβ (2.05 keV) needs: not Ir." seed0.seven.unlistedProposal.candidates[18].possibleReason = nil seed0.seven.unlistedProposal.candidates[18].unconfirmable = false +seed0.seven.unlistedProposal.candidates[18].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[19].element = "Os" seed0.seven.unlistedProposal.candidates[19].group = "Os_Ma" seed0.seven.unlistedProposal.candidates[19].energyKeV = 3ffe9eecbfb15b57 1.9137999999999999 @@ -2000,6 +2054,7 @@ seed0.seven.unlistedProposal.candidates[19].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[19].familyNote = "Os Lα (8.91 keV) has a residual peak of 0.0 σ, below the 3 σ its Mβ (1.98 keV) needs: not Os." seed0.seven.unlistedProposal.candidates[19].possibleReason = nil seed0.seven.unlistedProposal.candidates[19].unconfirmable = false +seed0.seven.unlistedProposal.candidates[19].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[20].element = "P" seed0.seven.unlistedProposal.candidates[20].group = "P_Ka" seed0.seven.unlistedProposal.candidates[20].energyKeV = 40001b3d07c84b5e 2.0133000000000001 @@ -2027,6 +2082,7 @@ seed0.seven.unlistedProposal.candidates[20].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[20].familyNote = nil seed0.seven.unlistedProposal.candidates[20].possibleReason = "Adding it improves the fit by 2, below the 100 a new line needs." seed0.seven.unlistedProposal.candidates[20].unconfirmable = false +seed0.seven.unlistedProposal.candidates[20].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[21].element = "Sr" seed0.seven.unlistedProposal.candidates[21].group = "Sr_La" seed0.seven.unlistedProposal.candidates[21].energyKeV = 3ffce76c8b439581 1.8065 @@ -2053,6 +2109,7 @@ seed0.seven.unlistedProposal.candidates[21].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[21].familyNote = "Sr Kα (14.16 keV) has a residual peak of 0.8 σ, below the 3 σ its Lα (1.81 keV) needs: not Sr." seed0.seven.unlistedProposal.candidates[21].possibleReason = nil seed0.seven.unlistedProposal.candidates[21].unconfirmable = false +seed0.seven.unlistedProposal.candidates[21].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[22].element = "W" seed0.seven.unlistedProposal.candidates[22].group = "W_La" seed0.seven.unlistedProposal.candidates[22].energyKeV = 4020cb923a29c77a 8.3976000000000006 @@ -2080,6 +2137,7 @@ seed0.seven.unlistedProposal.candidates[22].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[22].familyNote = nil seed0.seven.unlistedProposal.candidates[22].possibleReason = "Adding it improves the fit by 1, below the 100 a new line needs." seed0.seven.unlistedProposal.candidates[22].unconfirmable = false +seed0.seven.unlistedProposal.candidates[22].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[23].element = "Y" seed0.seven.unlistedProposal.candidates[23].group = "Y_La" seed0.seven.unlistedProposal.candidates[23].energyKeV = 3ffec2f837b4a234 1.9226000000000001 @@ -2095,6 +2153,7 @@ seed0.seven.unlistedProposal.candidates[23].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[23].familyNote = "Y Kα (14.96 keV) has a residual peak of 0.3 σ, below the 3 σ its Lβ1 (2.00 keV) needs: not Y." seed0.seven.unlistedProposal.candidates[23].possibleReason = nil seed0.seven.unlistedProposal.candidates[23].unconfirmable = false +seed0.seven.unlistedProposal.candidates[23].explainedBySumPeak = nil seed0.seven.unlistedProposal.candidates[24].element = "Yb" seed0.seven.unlistedProposal.candidates[24].group = "Yb_La" seed0.seven.unlistedProposal.candidates[24].energyKeV = 401da9c779a6b50b 7.4157999999999999 @@ -2110,6 +2169,7 @@ seed0.seven.unlistedProposal.candidates[24].misfit = 3ff0000000000000 1 seed0.seven.unlistedProposal.candidates[24].familyNote = nil seed0.seven.unlistedProposal.candidates[24].possibleReason = "Adding it improves the fit by 13, below the 100 a new line needs." seed0.seven.unlistedProposal.candidates[24].unconfirmable = false +seed0.seven.unlistedProposal.candidates[24].explainedBySumPeak = nil seed0.seven.unlistedProposal.sumPeaks: n 0 sum 0000000000000000 0 fnv cbf29ce484222325 seed0.seven.unlistedProposal.refused: array 7 seed0.seven.unlistedProposal.refused[0].element = "H" diff --git a/Tests/SpectroEDXTests/DemoEDXAutoIDTests.swift b/Tests/SpectroEDXTests/DemoEDXAutoIDTests.swift new file mode 100644 index 0000000..d91c829 --- /dev/null +++ b/Tests/SpectroEDXTests/DemoEDXAutoIDTests.swift @@ -0,0 +1,82 @@ +import Foundation +import Testing +import SpectroTestSupport +import SpectroCore +import SpectroEDX + +/// Auto ID (`ElementProposer.proposeOnRefinedAxis`, nothing listed) on the Poisson counts of the public AlMgSi demo pools +/// (mac4DSTEM `demo-edx`, `sim_edx.py` v2.0, `edx-demo-pools.json`). The truth is PLANTED ON SYNTHETIC DATA, not a measurement: +/// Al, Mg, Si, O, stray Cu K and sum peaks; no Ar, Ag or Pd. Every result stays `validation: "none"`. +/// +/// Red before: Ar K-alpha (2.957 keV, 15-27 L_D), Ag L-alpha (2.984 keV, 6-11 L_D) and Pd L-alpha (2.838 keV) were listed as +/// "possible" on every Al-rich pool, though the loop had already given their peak (2.97 keV) to the Al+Al sum column. Now they are +/// explained by that sum peak and are not possible. +struct DemoEDXAutoIDTests { + static func autoID(_ name: String) throws -> ProposalResult { + let c = DemoEDXPoolTests.named(name) + let input = DemoEDXPoolTests.input(c, noiseFree: false) + return try ElementProposer().proposeOnRefinedAxis(spectrum: input.counts, axis: c.axis, beamEnergy: 200, listed: [], + metadata: input.metadata, regionName: name, tables: DemoEDXPoolTests.tables).result + } + + @Test(arguments: ["L5", "M_A", "M_matrix"]) + func theAlAlSumExplainsArAndAg(_ name: String) throws { + let p = try Self.autoID(name) + let possible = Set(p.possible.map(\.element)), explained = Set(p.explainedBySumPeaks.map(\.element)) + DemoEDXPoolTests.say("\(name) auto ID: proposed \(p.proposed.map(\.element)), possible \(possible.sorted()), explained \(explained.sorted())") + #expect(p.proposed.contains { $0.element == "Al" }, "\(name)") + #expect(p.sumPeaks.contains { $0.label == "Al+Al sum" && $0.detected }, "\(name): \(p.sumPeaks)") + for el in ["Ar", "Ag"] { + #expect(!possible.contains(el), "\(name): \(el) is still possible") + #expect(!p.proposed.contains { $0.element == el } && !p.sumPeakQuestions.contains { $0.element == el }) + let c = try #require(p.candidates.first { $0.element == el }, "\(name): \(el) was not tested") + #expect(c.explainedBySumPeak == "Al+Al sum", "\(name): \(el) \(c.explainedBySumPeak ?? "nil")") + #expect(c.hasSumPeakQuestion, "\(name): \(el) has no Al+Al conflict") + } + #expect(p.notes.contains { $0.hasPrefix("Explained as pile-up: ") && $0.contains("Ar") && $0.contains("Al+Al sum") }, "\(name): \(p.notes)") + // The pile-up note carries no validation tag. + #expect(!p.notes.contains { $0.hasPrefix("Explained as pile-up") && $0.contains("(validation") }, "\(name): \(p.notes)") + // Explained is its own tier: never possible, proposed or withheld. + for c in p.explainedBySumPeaks { + #expect(!c.isProposed && c.familyNote == nil && !p.possible.contains(c)) + } + } + + /// The other side: a real Ar line planted under the Al+Al sum of L5 (Ar K-alpha 2.957 keV, 16 eV from the sum, and its K-beta + /// at 3.190 keV, 10 %). One spectrum cannot separate the two: with 1000 Ar counts (about half the sum) the sum column still + /// explains the peak (gain there 105800 against 105591), so Ar is explained, but it keeps the sum-peak question and its + /// two-current remedy; it is never dropped. With 4000 Ar counts it is a sum-peak question of the proposed tier. + @Test(arguments: [1000.0, 4000.0]) + func aRealArLineKeepsTheSumPeakQuestion(_ arCounts: Double) throws { + let c = DemoEDXPoolTests.named("L5") + let input = DemoEDXPoolTests.input(c, noiseFree: false) + var counts = input.counts.map { Double($0) } + for (e, area) in [(2.9577, arCounts), (3.1905, 0.1 * arCounts)] { + let fwhm = (130.0 * 130 + 2.5 * (e * 1000 - 5898.7)).squareRoot() / 1000, s = fwhm / 2.354820045 + for i in counts.indices { + let x = c.axis.energy(ofChannel: i) + counts[i] += area * c.axis.scale / (s * (2 * Double.pi).squareRoot()) * exp(-0.5 * pow((x - e) / s, 2)) + } + } + let r = try ElementProposer().proposeOnRefinedAxis(spectrum: counts.map { UInt64($0.rounded()) }, axis: c.axis, beamEnergy: 200, listed: [], + metadata: input.metadata, regionName: "L5+Ar", tables: DemoEDXPoolTests.tables).result + let ar = try #require(r.candidates.first { $0.element == "Ar" }) + DemoEDXPoolTests.say(String(format: "L5 + %.0f Ar: Ar %.1f L_D, proposed %d, question %d, possible %d, explained %@ | %@", arCounts, ar.significance, + r.proposed.contains(ar) ? 1 : 0, r.sumPeakQuestions.contains(ar) ? 1 : 0, r.possible.contains(ar) ? 1 : 0, + ar.explainedBySumPeak ?? "-", ar.possibleReason ?? "-")) + #expect(ar.hasSumPeakQuestion, "\(arCounts) Ar counts: no sum-peak question") + #expect(ar.conflicts.contains { $0.kind == .sumPeak && $0.remedy.contains("different beam currents") }) + if arCounts >= 4000 { + #expect(ar.explainedBySumPeak == nil && r.sumPeakQuestions.contains(ar), "\(arCounts) Ar counts: \(ar.possibleReason ?? "-")") + } else { + #expect(r.explainedBySumPeaks.contains(ar) || r.sumPeakQuestions.contains(ar) || r.possible.contains(ar)) + } + } + + /// Pure Mg5Si6 (L7): no Al, so no Al+Al column and nothing is explained by it. + @Test func noAlNoAlAlExplanation() throws { + let p = try Self.autoID("L7") + #expect(!p.sumPeaks.contains { $0.label == "Al+Al sum" }) + #expect(!p.explainedBySumPeaks.contains { $0.explainedBySumPeak == "Al+Al sum" }) + } +}