Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ public static List<SpectrumPeak> GetNormalizedPeaks(List<SpectrumPeak> spectrum,
return spectrum.Select(n => new SpectrumPeak { Mass = n.Mass, Intensity = Math.Pow(n.Intensity, powFactor) / maxIntensity * maxValue }).ToList();
}

public static List<SpectrumPeak> GetNormalizedByTotalIntensityPeaks(List<SpectrumPeak> spectrum) {
var sumIntensity = spectrum.Sum(n => n.Intensity);
Comment on lines +67 to +68
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetNormalizedByTotalIntensityPeaks() divides by sumIntensity without guarding against sumIntensity == 0. If a spectrum can contain only zero-intensity peaks, this will produce NaN/Infinity intensities and break downstream similarity calculations. Add a fast-path for spectrum.Count == 0 and sumIntensity <= 0 (e.g., return an empty list or the original list) to keep the normalization numerically safe.

Suggested change
public static List<SpectrumPeak> GetNormalizedByTotalIntensityPeaks(List<SpectrumPeak> spectrum) {
var sumIntensity = spectrum.Sum(n => n.Intensity);
public static List<SpectrumPeak> GetNormalizedByTotalIntensityPeaks(List<SpectrumPeak> spectrum) {
if (spectrum == null || spectrum.Count == 0) {
return new List<SpectrumPeak>();
}
var sumIntensity = spectrum.Sum(n => n.Intensity);
if (sumIntensity <= 0d) {
// All intensities are zero or non-positive; return an empty normalized spectrum
// to avoid division by zero and unstable similarity calculations.
return new List<SpectrumPeak>();
}

Copilot uses AI. Check for mistakes.
return spectrum.Select(n => new SpectrumPeak { Mass = n.Mass, Intensity = n.Intensity / sumIntensity }).ToList();
}
Comment on lines +67 to +70
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetNormalizedByTotalIntensityPeaks() divides by sumIntensity without guarding sumIntensity == 0. If a spectrum contains only zero-intensity peaks, this will yield NaN/Infinity intensities and break downstream similarity calculations. Add a guard (e.g., return an empty list or all-zero intensities) when sumIntensity <= 0 (and consider also handling spectrum == null for callers that may pass null).

Copilot uses AI. Check for mistakes.

public static List<SpectrumPeak> GetBinnedSpectrum(List<SpectrumPeak> spectrum, double delta = 100, int maxPeaks = 12) {

var peaks = new List<SpectrumPeak>();
Expand Down
159 changes: 155 additions & 4 deletions src/Common/CommonStandard/Algorithm/Scoring/MsScanMatching.cs
Original file line number Diff line number Diff line change
Expand Up @@ -792,11 +792,13 @@
return new double[2] { (double)counter / (double)libCounter, counter };
}

public static double GetSpetralEntropySimilarity(List<SpectrumPeak> peaks1, List<SpectrumPeak> peaks2, double bin) {
var combinedSpectrum = SpectrumHandler.GetCombinedSpectrum(peaks1, peaks2, bin);
public static double GetSpectralEntropySimilarity(List<SpectrumPeak> peaks1, List<SpectrumPeak> peaks2, double bin) {
if (!IsComparedAvailable(peaks1, peaks2)) return -1d;

var combinedSpectrum = SpectrumHandler.GetCombinedSpectrum(SpectrumHandler.GetNormalizedByTotalIntensityPeaks(peaks1), SpectrumHandler.GetNormalizedByTotalIntensityPeaks(peaks2), bin);
var entropy12 = GetSpectralEntropy(combinedSpectrum);
var entropy1 = GetSpectralEntropy(peaks1);
var entropy2 = GetSpectralEntropy(peaks2);
var entropy1 = GetSpectralEntropy(SpectrumHandler.GetBinnedSpectrum(peaks1, bin));
var entropy2 = GetSpectralEntropy(SpectrumHandler.GetBinnedSpectrum(peaks2, bin));
Comment on lines +800 to +801
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SpectrumHandler.GetBinnedSpectrum(peaksX, bin) is ambiguous because SpectrumHandler has two overloads that can both be called with two arguments (one uses optional parameters). This will fail compilation. Disambiguate by using a named argument (e.g., bin: bin to select the 2-arg binning overload, or delta: bin to select the top-N overload), or rename one overload to avoid ambiguity.

Suggested change
var entropy1 = GetSpectralEntropy(SpectrumHandler.GetBinnedSpectrum(peaks1, bin));
var entropy2 = GetSpectralEntropy(SpectrumHandler.GetBinnedSpectrum(peaks2, bin));
var entropy1 = GetSpectralEntropy(SpectrumHandler.GetBinnedSpectrum(peaks1, bin: bin));
var entropy2 = GetSpectralEntropy(SpectrumHandler.GetBinnedSpectrum(peaks2, bin: bin));

Copilot uses AI. Check for mistakes.
Comment on lines +795 to +801
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetSpectralEntropySimilarity() normalizes peaks via GetNormalizedByTotalIntensityPeaks(). If total intensity is 0, that normalization produces non-finite intensities and this method can return NaN. After adding a guard in the normalization helper, consider also handling degenerate/non-finite entropy results here (e.g., return -1/0).

Copilot uses AI. Check for mistakes.

return 1 - (2 * entropy12 - entropy1 - entropy2) * 0.5;
}
Expand Down Expand Up @@ -4070,6 +4072,155 @@
else { return Math.Pow(covariance, 2) / scalarM / scalarR * peakCountPenalty; }
}

/// <summary>
/// Calculates an enhanced dot product–based spectral similarity score between a query scan
/// and a reference scan over a specified m/z range.
/// This method is intended to evaluate the similarity of an experimental MS/MS spectrum
/// with respect to a reference (library or theoretical) spectrum.
/// </summary>
/// <param name="prop1">The query scan to compare (experimental MS/MS spectrum). Must provide a non-null spectrum.</param>
/// <param name="prop2">The reference scan to compare against (library or theoretical MS/MS spectrum). Must provide a non-null spectrum.</param>
/// <param name="bin">The bin width, in m/z units, used to merge and compare spectral peaks. Must be a positive value.</param>
/// <param name="massBegin">The lower bound of the m/z range to include in the comparison.</param>
/// <param name="massEnd">The upper bound of the m/z range to include in the comparison.</param>
/// <param name="penalty">
/// A weighting factor controlling how unmatched peaks in the query scan contribute to the similarity score.
/// Values should be between 0 and 1:
/// a value of 1 yields behavior equivalent to the reverse dot product,
/// while a value of 0 yields behavior equivalent to the standard dot product.
/// </param>
/// <returns>
/// A double value representing the enhanced dot product spectral similarity score.
/// Returns -1 if the scans cannot be compared, or 0 if no valid peaks are found
/// in the specified m/z range.
/// </returns>
/// <remarks>
/// This method is based on the enhanced dot product similarity approach described in:
/// Xing, S., Charron-Lamoureux, V., Zhao, H. N., El Abiead, Y., Wang, M., & Dorrestein, P. C.
/// "Reverse Spectral Search Reimagined: A Simple but Overlooked Solution for Chimeric Spectral Annotation."
/// Analytical Chemistry, 2025, 97(33).
/// https://pubs.acs.org/doi/10.1021/acs.analchem.5c02047
/// </remarks>
public static double GetEnhancedDotProduct(IMSScanProperty prop1, IMSScanProperty prop2, double bin,
double massBegin, double massEnd, double penalty) {
double scalarM = 0, scalarR = 0, covariance = 0;
double sumM = 0, sumL = 0;
if (!IsComparedAvailable(prop1, prop2))
{
return -1;
}

var peaks1 = prop1.Spectrum;
var peaks2 = prop2.Spectrum;

double minMz = Math.Max(massBegin, Math.Min(peaks1[0].Mass, peaks2[0].Mass));
double maxMz = Math.Min(massEnd, Math.Max(peaks1[peaks1.Count - 1].Mass, peaks2[peaks2.Count - 1].Mass));

double focusedMz = minMz;
int remaindIndexM = 0, remaindIndexL = 0;
int counter = 0;

SummedPeak[] measuredMassBuffer = ArrayPool<SummedPeak>.Shared.Rent(peaks1.Count + peaks2.Count);
SummedPeak[] referenceMassBuffer = ArrayPool<SummedPeak>.Shared.Rent(peaks1.Count + peaks2.Count);
int size = 0;

double sumMeasure = 0, sumReference = 0, baseM = double.MinValue, baseR = double.MinValue;

while (focusedMz <= maxMz) {
sumL = 0;
for (int i = remaindIndexL; i < peaks2.Count; i++) {
if (peaks2[i].Mass < focusedMz - bin) continue;
else if (focusedMz - bin <= peaks2[i].Mass && peaks2[i].Mass < focusedMz + bin)
sumL += peaks2[i].Intensity;
else { remaindIndexL = i; break; }
}

sumM = 0;
for (int i = remaindIndexM; i < peaks1.Count; i++) {
if (peaks1[i].Mass < focusedMz - bin) continue;
else if (focusedMz - bin <= peaks1[i].Mass && peaks1[i].Mass < focusedMz + bin)
sumM += peaks1[i].Intensity;
else { remaindIndexM = i; break; }
}

if (sumM <= 0) {
measuredMassBuffer[size] = new SummedPeak(focusedMz: focusedMz, intensity: sumM);
if (sumM > baseM) baseM = sumM;

referenceMassBuffer[size] = new SummedPeak(focusedMz: focusedMz, intensity: sumL);
if (sumL > baseR) baseR = sumL;
}
else {
measuredMassBuffer[size] = new SummedPeak(focusedMz: focusedMz, intensity: sumM);
if (sumM > baseM) baseM = sumM;

referenceMassBuffer[size] = new SummedPeak(focusedMz: focusedMz, intensity: sumL);
if (sumL > baseR) baseR = sumL;

counter++;
}
size++;

if (focusedMz + bin > Math.Max(peaks1[peaks1.Count - 1].Mass, peaks2[peaks2.Count - 1].Mass)) break;
if (focusedMz + bin > peaks2[remaindIndexL].Mass && focusedMz + bin <= peaks1[remaindIndexM].Mass)
focusedMz = peaks1[remaindIndexM].Mass;
else if (focusedMz + bin <= peaks2[remaindIndexL].Mass && focusedMz + bin > peaks1[remaindIndexM].Mass)
focusedMz = peaks2[remaindIndexL].Mass;
else
focusedMz = Math.Min(peaks1[remaindIndexM].Mass, peaks2[remaindIndexL].Mass);
}

if (baseM == 0 || baseR == 0) {
ArrayPool<SummedPeak>.Shared.Return(measuredMassBuffer);
ArrayPool<SummedPeak>.Shared.Return(referenceMassBuffer);
return 0;
}

var eSpectrumCounter = 0;
var lSpectrumCounter = 0;
for (int i = 0; i < size; i++) {
measuredMassBuffer[i] = new SummedPeak(focusedMz: measuredMassBuffer[i].FocusedMz, intensity: measuredMassBuffer[i].Intensity / baseM);
referenceMassBuffer[i] = new SummedPeak(focusedMz: referenceMassBuffer[i].FocusedMz, intensity: referenceMassBuffer[i].Intensity / baseR);
sumMeasure += measuredMassBuffer[i].Intensity;
sumReference += referenceMassBuffer[i].Intensity;

if (measuredMassBuffer[i].Intensity > 0.1) eSpectrumCounter++;
if (referenceMassBuffer[i].Intensity > 0.1) lSpectrumCounter++;
}

var cutoff = 0.01;
for (int i = 0; i < size; i++) {
if (referenceMassBuffer[i].Intensity < cutoff) {
continue;
}

if (measuredMassBuffer[i].Intensity == 0d) {
scalarM += measuredMassBuffer[i].Intensity * (1 - penalty) * measuredMassBuffer[i].FocusedMz;
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In GetEnhancedDotProduct(), penalty currently has no effect: when measuredMassBuffer[i].Intensity == 0d, the code adds measuredMassBuffer[i].Intensity * (1 - penalty) * ..., which is always 0. This contradicts the XML docs stating unmatched query peaks are weighted by penalty. Update the formula so penalty actually changes the score (or remove the parameter if it’s not intended).

Suggested change
scalarM += measuredMassBuffer[i].Intensity * (1 - penalty) * measuredMassBuffer[i].FocusedMz;
// Penalize unmatched query peaks by assigning them a reduced contribution
// based on the corresponding reference peak intensity.
scalarM += (1 - penalty) * referenceMassBuffer[i].Intensity * referenceMassBuffer[i].FocusedMz;

Copilot uses AI. Check for mistakes.
}
else {
scalarM += measuredMassBuffer[i].Intensity * measuredMassBuffer[i].FocusedMz;
}
scalarR += referenceMassBuffer[i].Intensity * referenceMassBuffer[i].FocusedMz;
Comment on lines +4198 to +4203
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The penalty parameter currently has no effect in the measuredMassBuffer[i].Intensity == 0d branch because you're multiplying by measuredMassBuffer[i].Intensity (which is 0), so the penalized term is always 0. As written, the score is effectively identical to the reverse dot product for the bins you include. Revisit this branch so the penalty actually influences scalarM/the final score (e.g., apply the penalty to a non-zero term, or incorporate bins where the reference intensity is below cutoff if you intend to penalize unmatched query peaks).

Suggested change
scalarM += measuredMassBuffer[i].Intensity * (1 - penalty) * measuredMassBuffer[i].FocusedMz;
}
else {
scalarM += measuredMassBuffer[i].Intensity * measuredMassBuffer[i].FocusedMz;
}
scalarR += referenceMassBuffer[i].Intensity * referenceMassBuffer[i].FocusedMz;
// Penalize unmatched reference peaks by reducing their contribution to scalarR.
scalarR += referenceMassBuffer[i].Intensity * (1 - penalty) * referenceMassBuffer[i].FocusedMz;
}
else {
scalarM += measuredMassBuffer[i].Intensity * measuredMassBuffer[i].FocusedMz;
scalarR += referenceMassBuffer[i].Intensity * referenceMassBuffer[i].FocusedMz;
}

Copilot uses AI. Check for mistakes.
covariance += Math.Sqrt(measuredMassBuffer[i].Intensity * referenceMassBuffer[i].Intensity) * measuredMassBuffer[i].FocusedMz;
}

ArrayPool<SummedPeak>.Shared.Return(measuredMassBuffer);
ArrayPool<SummedPeak>.Shared.Return(referenceMassBuffer);

var peakCountPenalty = 1.0;
if (lSpectrumCounter == 1) peakCountPenalty = 0.75;
else if (lSpectrumCounter == 2) peakCountPenalty = 0.88;
else if (lSpectrumCounter == 3) peakCountPenalty = 0.94;
else if (lSpectrumCounter == 4) peakCountPenalty = 0.97;

if (scalarM == 0 || scalarR == 0) {
return 0;
}
else {
return Math.Pow(covariance, 2) / scalarM / scalarR * peakCountPenalty;
}
}

/// <summary>
/// This program will return so called dot product similarity as described in the previous resport.
/// Stein, S. E. An Integrated Method for Spectrum Extraction. J.Am.Soc.Mass.Spectrom, 10, 770-781, 1999.
Expand Down Expand Up @@ -4238,7 +4389,7 @@
SummedPeak[] referenceMassBuffer = ArrayPool<SummedPeak>.Shared.Rent(peaks1.Count + peaks2.Count);
int size = 0;

double sumMeasure = 0, sumReference = 0, baseM = double.MinValue, baseR = double.MinValue;

Check warning on line 4392 in src/Common/CommonStandard/Algorithm/Scoring/MsScanMatching.cs

View workflow job for this annotation

GitHub Actions / test

The variable 'sumReference' is assigned but its value is never used

Check warning on line 4392 in src/Common/CommonStandard/Algorithm/Scoring/MsScanMatching.cs

View workflow job for this annotation

GitHub Actions / test

The variable 'sumMeasure' is assigned but its value is never used

Check warning on line 4392 in src/Common/CommonStandard/Algorithm/Scoring/MsScanMatching.cs

View workflow job for this annotation

GitHub Actions / publish-msdial5-console (ubuntu-latest, net8, linux-x64)

The variable 'sumReference' is assigned but its value is never used

Check warning on line 4392 in src/Common/CommonStandard/Algorithm/Scoring/MsScanMatching.cs

View workflow job for this annotation

GitHub Actions / publish-msdial5-console (ubuntu-latest, net8, linux-x64)

The variable 'sumMeasure' is assigned but its value is never used

Check warning on line 4392 in src/Common/CommonStandard/Algorithm/Scoring/MsScanMatching.cs

View workflow job for this annotation

GitHub Actions / publish-msdial5-console (windows-latest, net48, win-x64)

The variable 'sumReference' is assigned but its value is never used

Check warning on line 4392 in src/Common/CommonStandard/Algorithm/Scoring/MsScanMatching.cs

View workflow job for this annotation

GitHub Actions / publish-msdial5-console (windows-latest, net48, win-x64)

The variable 'sumMeasure' is assigned but its value is never used

Check warning on line 4392 in src/Common/CommonStandard/Algorithm/Scoring/MsScanMatching.cs

View workflow job for this annotation

GitHub Actions / publish-msdial5

The variable 'sumReference' is assigned but its value is never used

Check warning on line 4392 in src/Common/CommonStandard/Algorithm/Scoring/MsScanMatching.cs

View workflow job for this annotation

GitHub Actions / publish-msdial5

The variable 'sumMeasure' is assigned but its value is never used

while (focusedMz <= maxMz) {
sumM = 0;
Expand Down Expand Up @@ -4337,7 +4488,7 @@
int[] existsID = ArrayPool<int>.Shared.Rent(availableIndex.Count);
double[] scalars = Enumerable.Repeat(0d, availableIndex.Count).ToArray();
double[][] covariances = Enumerable.Repeat(0, availableIndex.Count).Select(_ => Enumerable.Repeat(0d, availableIndex.Count).ToArray()).ToArray();
int klo = 0, khi = 0;

Check warning on line 4491 in src/Common/CommonStandard/Algorithm/Scoring/MsScanMatching.cs

View workflow job for this annotation

GitHub Actions / publish-msdial5-console (ubuntu-latest, net8, linux-x64)

The variable 'khi' is assigned but its value is never used

Check warning on line 4491 in src/Common/CommonStandard/Algorithm/Scoring/MsScanMatching.cs

View workflow job for this annotation

GitHub Actions / publish-msdial5-console (ubuntu-latest, net8, linux-x64)

The variable 'klo' is assigned but its value is never used

Check warning on line 4491 in src/Common/CommonStandard/Algorithm/Scoring/MsScanMatching.cs

View workflow job for this annotation

GitHub Actions / publish-msdial5-console (windows-latest, net48, win-x64)

The variable 'khi' is assigned but its value is never used

Check warning on line 4491 in src/Common/CommonStandard/Algorithm/Scoring/MsScanMatching.cs

View workflow job for this annotation

GitHub Actions / publish-msdial5-console (windows-latest, net48, win-x64)

The variable 'klo' is assigned but its value is never used

Check warning on line 4491 in src/Common/CommonStandard/Algorithm/Scoring/MsScanMatching.cs

View workflow job for this annotation

GitHub Actions / publish-msdial5

The variable 'khi' is assigned but its value is never used

Check warning on line 4491 in src/Common/CommonStandard/Algorithm/Scoring/MsScanMatching.cs

View workflow job for this annotation

GitHub Actions / publish-msdial5

The variable 'klo' is assigned but its value is never used

double focusedMz = mergedPeaks.Any() ? Math.Max(mergedPeaks.First().Mz, massBegin) : massBegin;
double maxMz = mergedPeaks.Any() ? Math.Min(massEnd, mergedPeaks.Last().Mz) : massEnd;
Expand Down
4 changes: 4 additions & 0 deletions src/Common/CommonStandard/DataObj/Result/MsScanMatchResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ public float ReverseDotProduct {
public bool IsAnnotationSuggested { get; set; } = false;
[Key(36)]
public double CollisionEnergy { get; set; }
[Key(37)]
public float EnhancedDotProduct { get; set; }
[Key(38)]
public float SpectralEntropy { get; set; }

public MsScanMatchResult Clone() {
return (MsScanMatchResult)MemberwiseClone();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public MsScanMatchResult CalculateScore(IMSIonProperty property, IMSScanProperty
var sqweightedDotProduct = MsScanMatching.GetWeightedDotProduct(scan, reference, parameter.Ms2Tolerance, parameter.MassRangeBegin, parameter.MassRangeEnd);
var sqsimpleDotProduct = MsScanMatching.GetSimpleDotProduct(scan, reference, parameter.Ms2Tolerance, parameter.MassRangeBegin, parameter.MassRangeEnd);
var sqreverseDotProduct = MsScanMatching.GetReverseDotProduct(scan, reference, parameter.Ms2Tolerance, parameter.MassRangeBegin, parameter.MassRangeEnd);
var sqenhancedDotProduct = MsScanMatching.GetEnhancedDotProduct(scan, reference, parameter.Ms2Tolerance, parameter.MassRangeBegin, parameter.MassRangeEnd, .6d);
var spectrumEntropy = MsScanMatching.GetSpectralEntropySimilarity(scan.Spectrum, reference.Spectrum, parameter.Ms2Tolerance);
Comment on lines +43 to +44
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetEnhancedDotProduct() returns -1 when spectra are unavailable, but the result is immediately passed to Math.Sqrt() and stored in EnhancedDotProduct. Math.Sqrt(-1) yields NaN, which can propagate into exports/serialization. Handle negative return values explicitly (e.g., set EnhancedDotProduct to -1/0 when sqenhancedDotProduct < 0) and consider extracting the .6d penalty into a named constant/parameter so its meaning is clear and configurable.

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +44
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 0.6d penalty passed to GetEnhancedDotProduct(...) is a hard-coded magic number with no explanation or link to configuration. Consider promoting this to a parameter (e.g., on MsRefSearchParameterBase) or at least a named constant so it can be tuned per workflow and is self-documenting.

Copilot uses AI. Check for mistakes.
var spectrumPenalty = reference.Spectrum != null && reference.Spectrum.Count == 1 ? true : false;
double[] matchedPeaksScores = null;
if (omics == TargetOmics.Lipidomics) {
Expand Down Expand Up @@ -93,6 +95,8 @@ public MsScanMatchResult CalculateScore(IMSIonProperty property, IMSScanProperty
MatchedPeaksCount = (float)matchedPeaksScores[1],
AcurateMassSimilarity = (float)ms1Similarity,
IsotopeSimilarity = (float)isotopeSimilarity,
EnhancedDotProduct = (float)Math.Sqrt(sqenhancedDotProduct),
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetEnhancedDotProduct() can return -1 when spectra are not comparable. Taking Math.Sqrt(-1) yields NaN, which then propagates into MsScanMatchResult and downstream export/scoring. Guard the sentinel (e.g., if the squared score < 0, keep EnhancedDotProduct as -1/null) before applying Math.Sqrt.

Suggested change
EnhancedDotProduct = (float)Math.Sqrt(sqenhancedDotProduct),
EnhancedDotProduct = sqenhancedDotProduct < 0 ? -1f : (float)Math.Sqrt(sqenhancedDotProduct),

Copilot uses AI. Check for mistakes.
SpectralEntropy = (float)spectrumEntropy,
Source = source,
AnnotatorID = id,
Priority = priority,
Expand Down
4 changes: 4 additions & 0 deletions src/MSDIAL5/MsdialCore/Export/IAnalysisMetadataAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ protected virtual string[] GetHeadersCore() {
"Simple dot product",
"Weighted dot product",
"Reverse dot product",
"Enhanced dot product",
"Spectrum entropy",
"Matched peaks count",
"Matched peaks percentage",
"Total score",
Expand Down Expand Up @@ -121,6 +123,8 @@ protected virtual Dictionary<string, string> GetContentCore(
{ "Simple dot product", ValueOrNull(matchResult?.SimpleDotProduct, "F3") },
{ "Weighted dot product", ValueOrNull(matchResult?.WeightedDotProduct, "F3") },
{ "Reverse dot product", ValueOrNull(matchResult?.ReverseDotProduct, "F3") },
{ "Enhanced dot product", ValueOrNull(matchResult?.EnhancedDotProduct, "F3") },
{ "Spectrum entropy", ValueOrNull(matchResult?.SpectralEntropy, "F3") },
{ "Matched peaks count", ValueOrNull(matchResult?.MatchedPeaksCount, "F2") },
{ "Matched peaks percentage", ValueOrNull(matchResult?.MatchedPeaksPercentage, "F2") },
{ "Total score", ValueOrNull(matchResult?.TotalScore, "F3") },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public class BaseAnalysisMetadataAccessorTests
"Simple dot product",
"Weighted dot product",
"Reverse dot product",
"Enhanced dot product",
"Spectrum entropy",
"Matched peaks count",
"Matched peaks percentage",
"Total score",
Comment on lines 39 to 46
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This header list now includes "Enhanced dot product" and "Spectrum entropy", but the existing GetContentTest() doesn’t assert that GetContent() produces these keys (and with correct formatting / sentinel handling). Adding coverage for these two new columns would help catch issues like NaN propagation or unexpected defaults.

Copilot uses AI. Check for mistakes.
Expand Down
Loading