Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f31dd12
feat(search): select proteoform candidates by most abundant isotopic …
trishorts Jun 16, 2026
ff13782
feat(search): tolerate apex misprediction with a most-abundant notch set
trishorts Jun 16, 2026
c3bd963
feat(search): extend most-abundant matching to Calibration and GPTMD
trishorts Jun 16, 2026
ca84338
feat(gptmd): expose acceptance filters in the task toml
trishorts Jun 16, 2026
4bf4351
fix(gptmd): add GptmdParameters filter resolution missed by case-typo…
trishorts Jun 16, 2026
093a5ad
fix(calibration): neutron-modulo precursor error so most-abundant mod…
trishorts Jun 17, 2026
00e256b
test(calibration): cover neutron-modulo precursor error and tolerance…
trishorts Jun 17, 2026
36f2a31
refactor(search): compose averagine apex offset locally after mzLib d…
trishorts Jun 17, 2026
0fd0f0f
fix(search): assign modern-search notch from PrecursorMassToMatch
trishorts Jun 29, 2026
fc8dcf3
test(most-abundant): cover acceptor interval methods and filter resol…
trishorts Jun 29, 2026
4a47de2
refactor(search): scope most-abundant matching to resolved envelopes
trishorts Jun 29, 2026
7b48403
refactor(search): follow mzLib rename to MostAbundantObservedNeutralMass
trishorts Jun 29, 2026
ec16167
Merge remote-tracking branch 'upstream/master' into mostAbundantMass
trishorts Jun 30, 2026
49a8c60
feat(gui): add Most Abundant Mass option to Calibrate/GPTMD/Search ta…
trishorts Jun 30, 2026
0cc2566
test(most-abundant): lift PR patch coverage above codecov target
trishorts Jun 30, 2026
05ae907
test(most-abundant): cover GPTMD most-abundant run for patch-coverage…
trishorts Jun 30, 2026
659fd68
refactor(most-abundant): source isotope spacing from deconvolution pa…
trishorts Jul 7, 2026
cca25b9
feat(most-abundant): report most-abundant precursor mass error in psmtsv
trishorts Jul 7, 2026
cc6cdd4
feat(most-abundant): consume mzLib 1.0.582 and its owned psmtsv colum…
trishorts Jul 10, 2026
f63f6ba
Merge branch 'master' into mostAbundantMass
Alexander-Sol Jul 10, 2026
a1142d9
Merge branch 'master' into mostAbundantMass
trishorts Jul 10, 2026
0cecb12
Merge branch 'master' into mostAbundantMass
trishorts Jul 10, 2026
3902981
Merge branch 'master' into mostAbundantMass
trishorts Jul 11, 2026
4102843
refactor(most-abundant): store observations on scan/PSM; let the acce…
trishorts Jul 14, 2026
0e4ba37
fix(most-abundant): strip isotope offsets before a mass difference is…
trishorts Jul 14, 2026
a9b0449
test(most-abundant): cover the observation/selection split and the is…
trishorts Jul 14, 2026
fe221b0
refactor(most-abundant): drive precursor-mass selection off CommonPar…
trishorts Jul 14, 2026
92e13e0
test(most-abundant): retarget to the CommonParameters API; cover the …
trishorts Jul 14, 2026
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
2 changes: 1 addition & 1 deletion MetaMorpheus/CMD/CMD.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<PackageReference Include="Microsoft.ML.CpuMath" Version="3.0.1" />
<PackageReference Include="Microsoft.ML.FastTree" Version="3.0.1" />
<PackageReference Include="Microsoft.NETCore.App" Version="2.2.8" />
<PackageReference Include="mzLib" Version="1.0.579" />
<PackageReference Include="mzLib" Version="1.0.581" />
<PackageReference Include="Nett" Version="0.15.0" />
<PackageReference Include="SQLite.Interop.dll" Version="1.0.103" />
<PackageReference Include="System.Data.SQLite" Version="1.0.118" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ protected override MetaMorpheusEngineResults RunSpecific()
numMs1MassChargeCombinationsConsidered,
numMs1MassChargeCombinationsThatAreIgnoredBecauseOfTooManyPeaks,
numMs2MassChargeCombinationsConsidered,
numMs2MassChargeCombinationsThatAreIgnoredBecauseOfTooManyPeaks
numMs2MassChargeCombinationsThatAreIgnoredBecauseOfTooManyPeaks,
CommonParameters.PrecursorMassMatchMode
);
}

Expand Down
23 changes: 21 additions & 2 deletions MetaMorpheus/EngineLayer/Calibration/DataPointAquisitionResults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public DataPointAquisitionResults(
int numMs1MassChargeCombinationsConsidered,
int numMs1MassChargeCombinationsThatAreIgnoredBecauseOfTooManyPeaks,
int numMs2MassChargeCombinationsConsidered,
int numMs2MassChargeCombinationsThatAreIgnoredBecauseOfTooManyPeaks)
int numMs2MassChargeCombinationsThatAreIgnoredBecauseOfTooManyPeaks,
PrecursorMassMatchMode precursorMassMatchMode = PrecursorMassMatchMode.Monoisotopic)
: base(dataPointAcquisitionEngine)
{
Psms = psms;
Expand All @@ -35,7 +36,25 @@ public DataPointAquisitionResults(
var ms1PpmRange = Ms1List.Select(b => (b.ExperimentalMz - b.TheoreticalMz) / b.TheoreticalMz).ToArray();
var ms2PpmRange = Ms2List.Select(b => (b.ExperimentalMz - b.TheoreticalMz) / b.TheoreticalMz).ToArray();

var precursorErrors = psms.Select(p => (p.ScanPrecursorMass - p.BioPolymerWithSetModsMonoisotopicMass.Value) / p.BioPolymerWithSetModsMonoisotopicMass.Value * 1e6).ToList();
// In most-abundant mode only, use the neutron-modulo residual of the precursor mass error
// instead of the raw difference. Most-abundant matching admits PSMs whose deconvoluted
// monoisotopic peak is off by +/-1-2 neutrons (the apex notch set), so the raw
// (ScanPrecursorMass - peptideMonoisotopic) difference carries whole-neutron offsets that are
// isotope-assignment errors, not instrument m/z drift; left in, they inflate the IQR and make
// calibration write runaway precursor tolerances (e.g. 1940 ppm). In default Monoisotopic mode
// the raw difference is used unchanged - baseline calibration behavior is preserved exactly.
bool stripNeutronOffsets = precursorMassMatchMode == PrecursorMassMatchMode.MostAbundant;
var precursorErrors = psms.Select(p =>
{
double deltaDa = p.ScanPrecursorMass - p.BioPolymerWithSetModsMonoisotopicMass.Value;
if (stripNeutronOffsets)
{
// AwayFromZero so the nearest-neutron assignment at an exact half-neutron boundary is
// parity-independent (default banker's rounding would snap to the even integer).
deltaDa -= Math.Round(deltaDa / Constants.C13MinusC12, MidpointRounding.AwayFromZero) * Constants.C13MinusC12;
Comment thread
nbollis marked this conversation as resolved.
Outdated
}
return deltaDa / p.BioPolymerWithSetModsMonoisotopicMass.Value * 1e6;
}).ToList();
PsmPrecursorIqrPpmError = precursorErrors.InterquartileRange();
PsmPrecursorMedianPpmError = precursorErrors.Median();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ public ClassicSearchEngine(SpectralMatch[] globalPsms, Ms2ScanWithSpecificMass[]
{
SpectralMatches = globalPsms;
ArrayOfSortedMS2Scans = arrayOfSortedMS2Scans;
MyScanPrecursorMasses = arrayOfSortedMS2Scans.Select(b => b.PrecursorMass).ToArray();
// PrecursorMassToMatch is the candidate-selection mass (equals PrecursorMass in
// monoisotopic mode; the most-abundant/average observed mass in most-abundant mode).
// The scans are sorted by this same quantity, keeping the binary search valid.
MyScanPrecursorMasses = arrayOfSortedMS2Scans.Select(b => b.PrecursorMassToMatch).ToArray();
VariableModifications = variableModifications;
FixedModifications = fixedModifications;
SilacLabels = silacLabels;
Expand Down
12 changes: 11 additions & 1 deletion MetaMorpheus/EngineLayer/CommonParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
bool useMostAbundantPrecursorIntensity = true,
DIAparameters diaParameters = null,
IFragmentationParams fragmentationParams = null,
PrecursorMassMatchMode precursorMassMatchMode = PrecursorMassMatchMode.Monoisotopic,
string rtPredictorName = "Chronologer")

{
Expand Down Expand Up @@ -95,6 +96,7 @@
MS2ChildScanDissociationType = ms2childScanDissociationType;
MS3ChildScanDissociationType = ms3childScanDissociationType;
UseMostAbundantPrecursorIntensity = useMostAbundantPrecursorIntensity;
PrecursorMassMatchMode = precursorMassMatchMode;
AssumeOrphanPeaksAreZ1Fragments = assumeOrphanPeaksAreZ1Fragments;
MaxHeterozygousVariants = maxHeterozygousVariants;
MinVariantDepth = minVariantDepth;
Expand Down Expand Up @@ -207,7 +209,13 @@
public DissociationType MS3ChildScanDissociationType { get; set; }

public bool UseMostAbundantPrecursorIntensity { get; set; }

/// <summary>
/// Which precursor mass is used to select theoretical proteoform candidates during search.
/// Defaults to <see cref="EngineLayer.PrecursorMassMatchMode.Monoisotopic"/>.
/// </summary>
public PrecursorMassMatchMode PrecursorMassMatchMode { get; set; }
public DIAparameters? DIAparameters { get; set; } //only for DIA analysis involving pseudo ms2 scan generation

Check warning on line 218 in MetaMorpheus/EngineLayer/CommonParameters.cs

View workflow job for this annotation

GitHub Actions / macos-latest

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 218 in MetaMorpheus/EngineLayer/CommonParameters.cs

View workflow job for this annotation

GitHub Actions / ubuntu-latest

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 218 in MetaMorpheus/EngineLayer/CommonParameters.cs

View workflow job for this annotation

GitHub Actions / windows-latest

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 218 in MetaMorpheus/EngineLayer/CommonParameters.cs

View workflow job for this annotation

GitHub Actions / Search with Built

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 218 in MetaMorpheus/EngineLayer/CommonParameters.cs

View workflow job for this annotation

GitHub Actions / Create Installer Artifact

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 218 in MetaMorpheus/EngineLayer/CommonParameters.cs

View workflow job for this annotation

GitHub Actions / Create Installer Artifact

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 218 in MetaMorpheus/EngineLayer/CommonParameters.cs

View workflow job for this annotation

GitHub Actions / Validate Installer Contents

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 218 in MetaMorpheus/EngineLayer/CommonParameters.cs

View workflow job for this annotation

GitHub Actions / Test on Windows

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
public IFragmentationParams FragmentationParameters { get; set; }
public string RTPredictorName { get; private set; }

Expand Down Expand Up @@ -282,7 +290,9 @@
ProductDeconvolutionParameters,
UseMostAbundantPrecursorIntensity,
DIAparameters,
FragmentationParameters);
FragmentationParameters,
PrecursorMassMatchMode,
RTPredictorName);
}

public void SetCustomProductTypes()
Expand Down
2 changes: 1 addition & 1 deletion MetaMorpheus/EngineLayer/EngineLayer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<PackageReference Include="Microsoft.ML.CpuMath" Version="3.0.1" />
<PackageReference Include="Microsoft.ML.FastTree" Version="3.0.1" />
<PackageReference Include="Microsoft.NETCore.App" Version="2.2.8" />
<PackageReference Include="mzLib" Version="1.0.579" />
<PackageReference Include="mzLib" Version="1.0.581" />
<PackageReference Include="NETStandard.Library" Version="2.0.3" />
<PackageReference Include="Nett" Version="0.15.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
Expand Down
11 changes: 7 additions & 4 deletions MetaMorpheus/EngineLayer/ModernSearch/ModernSearchEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ protected void IndexScoreScan(Ms2ScanWithSpecificMass scan, byte[] scoringTable,
// note that this is the OPPOSITE of the classic search (which calculates experimental masses from theoretical values)
// this is just PRELIMINARY precursor-mass filtering
// additional checks are made later to ensure that the theoretical precursor mass is acceptable
List<AllowedIntervalWithNotch> notches = MassDiffAcceptor.GetAllowedPrecursorMassIntervalsFromObservedMass(scan.PrecursorMass).ToList();
List<AllowedIntervalWithNotch> notches = MassDiffAcceptor.GetAllowedPrecursorMassIntervalsFromObservedMass(scan.PrecursorMassToMatch).ToList();
double lowestMassPeptideToLookFor = notches.Min(p => p.Minimum);
double highestMassPeptideToLookFor = notches.Max(p => p.Maximum);

Expand Down Expand Up @@ -305,7 +305,7 @@ protected void IncrementPeptideScoresInBin(int start, int end, List<int> bin, by
// at least one fragment in the scan will get fine-scored.

// add possible search results to the list of id's (only once)
if (scoringTable[peptideId] == 0 && MassDiffAcceptor.Accepts(scan.PrecursorMass, PeptideIndex[peptideId].MonoisotopicMass) >= 0)
if (scoringTable[peptideId] == 0 && MassDiffAcceptor.Accepts(scan.PrecursorMassToMatch, PeptideIndex[peptideId].MonoisotopicMass) >= 0)
{
peptidesPossiblyObserved.Add(peptideId);
}
Expand All @@ -324,7 +324,7 @@ protected void IncrementPeptideScoresInBin(int start, int end, List<int> bin, by

// if the peptide has met the score cutoff, add it to the list of peptides
// possibly observed so it can be re-scored with the "fine scoring" algorithm
if (score == byteScoreCutoff && MassDiffAcceptor.Accepts(scan.PrecursorMass, PeptideIndex[peptideId].MonoisotopicMass) >= 0)
if (score == byteScoreCutoff && MassDiffAcceptor.Accepts(scan.PrecursorMassToMatch, PeptideIndex[peptideId].MonoisotopicMass) >= 0)
{
peptidesPossiblyObserved.Add(peptideId);
}
Expand All @@ -345,7 +345,10 @@ protected SpectralMatch FineScorePeptide(int id, Ms2ScanWithSpecificMass scan, i
List<MatchedFragmentIon> matchedIons = MatchFragmentIons(scan, peptideTheorProducts, CommonParameters);

double thisScore = CalculatePeptideScore(scan.TheScan, matchedIons);
int notch = MassDiffAcceptor.Accepts(scan.PrecursorMass, peptide.MonoisotopicMass);
// Use PrecursorMassToMatch (the candidate-selection mass) so the final notch is consistent
// with the coarse selection above; in most-abundant mode this is the apex mass, not the
// monoisotopic PrecursorMass. PrecursorMass still drives fragment math / error reporting.
int notch = MassDiffAcceptor.Accepts(scan.PrecursorMassToMatch, peptide.MonoisotopicMass);

bool meetsScoreCutoff = thisScore >= CommonParameters.ScoreCutoff;
bool scoreImprovement = PeptideSpectralMatches[scanIndex] == null || (thisScore - PeptideSpectralMatches[scanIndex].RunnerUpScore) > -SpectralMatch.ToleranceForScoreDifferentiation;
Expand Down
21 changes: 21 additions & 0 deletions MetaMorpheus/EngineLayer/Ms2ScanWithSpecificMass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@
{
public Ms2ScanWithSpecificMass(MsDataScan mzLibScan, double precursorMonoisotopicPeakMz, int precursorCharge, string fullFilePath, CommonParameters commonParam,
IsotopicEnvelope[] neutralExperimentalFragments = null, double? precursorIntensity = null, int? envelopePeakCount = null, double? precursorFractionalIntensity = null,
double? precursorMassToMatch = null,
double precursorDeconvolutionScore = 0)
{
PrecursorMonoisotopicPeakMz = precursorMonoisotopicPeakMz;
PrecursorCharge = precursorCharge;
PrecursorMass = PrecursorMonoisotopicPeakMz.ToMass(precursorCharge);
// Mass used for candidate selection by the MassDiffAcceptor. Defaults to the monoisotopic
// PrecursorMass; in most-abundant mode the caller passes the envelope's most-abundant (or,
// for unresolved species, average) observed mass. PrecursorMass itself is left unchanged
// because it still drives fragment-bin math and precursor mass-error reporting.
PrecursorMassToMatch = precursorMassToMatch ?? PrecursorMass;
Comment thread
nbollis marked this conversation as resolved.
Outdated
PrecursorIntensity = precursorIntensity ?? 1;
PrecursorEnvelopePeakCount = envelopePeakCount ?? 1;
PrecursorFractionalIntensity = precursorFractionalIntensity ?? -1;
Expand Down Expand Up @@ -43,6 +49,21 @@
public MsDataScan TheScan { get; }
public double PrecursorMonoisotopicPeakMz { get; }
public double PrecursorMass { get; }

/// <summary>
/// The precursor mass used to select theoretical candidates via the <see cref="EngineLayer.MassDiffAcceptor"/>.
/// Equals <see cref="PrecursorMass"/> in monoisotopic mode; in most-abundant mode it is the
/// envelope's most-abundant (or average, if unresolved) observed neutral mass.
/// <para>
/// This is deliberately a separate property rather than an overwrite of <see cref="PrecursorMass"/>:
/// candidate selection is the only step that should key off the most-abundant peak. Fragment-bin
/// math and the reported precursor mass error must stay on the monoisotopic mass so that (a) the
/// error is the monoisotopic-vs-monoisotopic value comparable across search modes, and (b) it is
/// not conflated with the ±k-neutron apex offset the acceptor already models per-notch. Collapsing
/// the two would report error against a peak that is intentionally N neutrons off the monoisotopic.
/// </para>
/// </summary>
public double PrecursorMassToMatch { get; }
Comment thread
nbollis marked this conversation as resolved.
Outdated
public int PrecursorCharge { get; }
public double PrecursorIntensity { get; }
public int PrecursorEnvelopePeakCount { get; }
Expand All @@ -67,7 +88,7 @@
/// An array containing the intensities of the reporter ions for isobaric mass tags.
/// If multiplex quantification wasn't performed, this will be null
/// </summary>
public double[]? IsobaricMassTagReporterIonIntensities { get; private set; }

Check warning on line 91 in MetaMorpheus/EngineLayer/Ms2ScanWithSpecificMass.cs

View workflow job for this annotation

GitHub Actions / macos-latest

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 91 in MetaMorpheus/EngineLayer/Ms2ScanWithSpecificMass.cs

View workflow job for this annotation

GitHub Actions / ubuntu-latest

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 91 in MetaMorpheus/EngineLayer/Ms2ScanWithSpecificMass.cs

View workflow job for this annotation

GitHub Actions / windows-latest

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 91 in MetaMorpheus/EngineLayer/Ms2ScanWithSpecificMass.cs

View workflow job for this annotation

GitHub Actions / Search with Built

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 91 in MetaMorpheus/EngineLayer/Ms2ScanWithSpecificMass.cs

View workflow job for this annotation

GitHub Actions / Create Installer Artifact

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 91 in MetaMorpheus/EngineLayer/Ms2ScanWithSpecificMass.cs

View workflow job for this annotation

GitHub Actions / Create Installer Artifact

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 91 in MetaMorpheus/EngineLayer/Ms2ScanWithSpecificMass.cs

View workflow job for this annotation

GitHub Actions / Validate Installer Contents

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 91 in MetaMorpheus/EngineLayer/Ms2ScanWithSpecificMass.cs

View workflow job for this annotation

GitHub Actions / Test on Windows

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

public static IsotopicEnvelope[] GetNeutralExperimentalFragments(MsDataScan scan, CommonParameters commonParam)
{
Expand Down
Loading
Loading