Skip to content

Commit e41d933

Browse files
authored
Decoy on the fly And Searching Match Ions with All Charges for decoy library generation (#2059)
* DecoyOnTheFly * DecoyOnTheFlyAndMatchedIonsWithAllChargesNoTestsYet * WithTestsAdded * DoChangesAsDecoyWasMovedTomzlib * undo accidental changes * AddingHeadersAndFixedSomeErrors * unnecessary codes removed * AddingMoreTests * IncreasingTestCoverage * more tests * Adding more tests * test * Add More Tests * ImprovedCodesAndMoreTests * MadeChangesFromRob * SmallChangesToMakeItClear * SmallChangesToMakeItClear * MoreTestsAdded * MoreChanges * mzlibUpdate Co-authored-by: Yuling1996 <dai59wisc.edu>
1 parent 0ebf374 commit e41d933

30 files changed

+633
-79
lines changed

CMD/CMD.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<PackageReference Include="Microsoft.ML" Version="1.3.1" />
2424
<PackageReference Include="Microsoft.ML.FastTree" Version="1.3.1" />
2525
<PackageReference Include="Microsoft.NETCore.App" Version="2.2.8" />
26-
<PackageReference Include="mzLib" Version="1.0.494" />
26+
<PackageReference Include="mzLib" Version="1.0.499" />
2727
<PackageReference Include="Nett" Version="0.13.0" />
2828
</ItemGroup>
2929

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ RUN apk add --no-cache bash
1111
RUN apk add --no-cache procps
1212

1313
## Set the entrypoint of the Docker image to CMD.dll
14-
ENTRYPOINT ["dotnet", "/metamorpheus/CMD.dll"]
14+
ENTRYPOINT ["dotnet", "/metamorpheus/CMD.dll"]

Dockerfile_WindowsInstaller

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ FROM mcr.microsoft.com/dotnet/runtime:5.0-nanoserver-1809
55
ADD /InstalledFiles/ /metamorpheus/
66

77
## Set the entrypoint of the Docker image to CMD.exe
8-
ENTRYPOINT ["/metamorpheus/CMD.exe"]
8+
ENTRYPOINT ["/metamorpheus/CMD.exe"]

EngineLayer/ClassicSearch/ClassicSearchEngine.cs

+87-35
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ public class ClassicSearchEngine : MetaMorpheusEngine
2222
private readonly PeptideSpectralMatch[] PeptideSpectralMatches;
2323
private readonly Ms2ScanWithSpecificMass[] ArrayOfSortedMS2Scans;
2424
private readonly double[] MyScanPrecursorMasses;
25+
private readonly bool WriteSpectralLibrary;
2526

2627
public ClassicSearchEngine(PeptideSpectralMatch[] globalPsms, Ms2ScanWithSpecificMass[] arrayOfSortedMS2Scans,
2728
List<Modification> variableModifications, List<Modification> fixedModifications, List<SilacLabel> silacLabels, SilacLabel startLabel, SilacLabel endLabel,
2829
List<Protein> proteinList, MassDiffAcceptor searchMode, CommonParameters commonParameters, List<(string FileName, CommonParameters Parameters)> fileSpecificParameters,
29-
SpectralLibrary spectralLibrary, List<string> nestedIds)
30+
SpectralLibrary spectralLibrary, List<string> nestedIds, bool writeSpectralLibrary)
3031
: base(commonParameters, fileSpecificParameters, nestedIds)
3132
{
3233
PeptideSpectralMatches = globalPsms;
@@ -39,9 +40,16 @@ public ClassicSearchEngine(PeptideSpectralMatch[] globalPsms, Ms2ScanWithSpecifi
3940
{
4041
TurnoverLabels = (startLabel, endLabel);
4142
}
42-
Proteins = proteinList;
43+
4344
SearchMode = searchMode;
4445
SpectralLibrary = spectralLibrary;
46+
WriteSpectralLibrary = writeSpectralLibrary;
47+
48+
// if we're doing a spectral library search, we can skip the reverse protein decoys generated by metamorpheus.
49+
// we will generate reverse peptide decoys w/ in the code just below this (and calculate spectral angles for them later).
50+
// we have to generate the reverse peptides instead of the usual reverse proteins because we generate decoy spectral
51+
// library spectra from their corresponding paired target peptides
52+
Proteins = spectralLibrary == null ? proteinList : proteinList.Where(p => !p.IsDecoy).ToList();
4553
}
4654

4755
protected override MetaMorpheusEngineResults RunSpecific()
@@ -66,19 +74,22 @@ protected override MetaMorpheusEngineResults RunSpecific()
6674
int[] threads = Enumerable.Range(0, maxThreadsPerFile).ToArray();
6775
Parallel.ForEach(threads, (i) =>
6876
{
69-
var fragmentsForDissociationTypes = new Dictionary<DissociationType, List<Product>>();
77+
var targetFragmentsForEachDissociationType = new Dictionary<DissociationType, List<Product>>();
78+
var decoyFragmentsForEachDissociationType = new Dictionary<DissociationType, List<Product>>();
7079

7180
// check if we're supposed to autodetect dissociation type from the scan header or not
7281
if (CommonParameters.DissociationType == DissociationType.Autodetect)
7382
{
7483
foreach (var item in GlobalVariables.AllSupportedDissociationTypes.Where(p => p.Value != DissociationType.Autodetect))
7584
{
76-
fragmentsForDissociationTypes.Add(item.Value, new List<Product>());
85+
targetFragmentsForEachDissociationType.Add(item.Value, new List<Product>());
86+
decoyFragmentsForEachDissociationType.Add(item.Value, new List<Product>());
7787
}
7888
}
7989
else
8090
{
81-
fragmentsForDissociationTypes.Add(CommonParameters.DissociationType, new List<Product>());
91+
targetFragmentsForEachDissociationType.Add(CommonParameters.DissociationType, new List<Product>());
92+
decoyFragmentsForEachDissociationType.Add(CommonParameters.DissociationType, new List<Product>());
8293
}
8394

8495
for (; i < Proteins.Count; i += maxThreadsPerFile)
@@ -89,22 +100,28 @@ protected override MetaMorpheusEngineResults RunSpecific()
89100
// digest each protein into peptides and search for each peptide in all spectra within precursor mass tolerance
90101
foreach (PeptideWithSetModifications peptide in Proteins[i].Digest(CommonParameters.DigestionParams, FixedModifications, VariableModifications, SilacLabels, TurnoverLabels))
91102
{
92-
foreach (var fragmentSet in fragmentsForDissociationTypes)
103+
PeptideWithSetModifications reversedOnTheFlyDecoy = null;
104+
105+
if (SpectralLibrary != null)
106+
{
107+
int[] newAAlocations = new int[peptide.BaseSequence.Length];
108+
reversedOnTheFlyDecoy = peptide.GetReverseDecoyFromTarget(newAAlocations);
109+
}
110+
111+
// clear fragments from the last peptide
112+
foreach (var fragmentSet in targetFragmentsForEachDissociationType)
93113
{
94114
fragmentSet.Value.Clear();
115+
decoyFragmentsForEachDissociationType[fragmentSet.Key].Clear();
95116
}
96117

118+
// score each scan that has an acceptable precursor mass
97119
foreach (ScanWithIndexAndNotchInfo scan in GetAcceptableScans(peptide.MonoisotopicMass, SearchMode))
98120
{
99-
if (SpectralLibrary != null && !SpectralLibrary.ContainsSpectrum(peptide.FullSequence, scan.TheScan.PrecursorCharge))
100-
{
101-
continue;
102-
}
103-
104121
var dissociationType = CommonParameters.DissociationType == DissociationType.Autodetect ?
105122
scan.TheScan.TheScan.DissociationType.Value : CommonParameters.DissociationType;
106123

107-
if (!fragmentsForDissociationTypes.TryGetValue(dissociationType, out var peptideTheorProducts))
124+
if (!targetFragmentsForEachDissociationType.TryGetValue(dissociationType, out var peptideTheorProducts))
108125
{
109126
//TODO: print some kind of warning here. the scan header dissociation type was unknown
110127
continue;
@@ -116,32 +133,19 @@ protected override MetaMorpheusEngineResults RunSpecific()
116133
peptide.Fragment(dissociationType, CommonParameters.DigestionParams.FragmentationTerminus, peptideTheorProducts);
117134
}
118135

119-
List<MatchedFragmentIon> matchedIons = MatchFragmentIons(scan.TheScan, peptideTheorProducts, CommonParameters);
136+
// match theoretical target ions to spectrum
137+
List<MatchedFragmentIon> matchedIons = MatchFragmentIons(scan.TheScan, peptideTheorProducts, CommonParameters,
138+
matchAllCharges: WriteSpectralLibrary);
120139

121-
double thisScore = CalculatePeptideScore(scan.TheScan.TheScan, matchedIons);
122-
bool meetsScoreCutoff = thisScore >= CommonParameters.ScoreCutoff;
140+
// calculate the peptide's score
141+
double thisScore = CalculatePeptideScore(scan.TheScan.TheScan, matchedIons, fragmentsCanHaveDifferentCharges: WriteSpectralLibrary);
123142

124-
// this is thread-safe because even if the score improves from another thread writing to this PSM,
125-
// the lock combined with AddOrReplace method will ensure thread safety
126-
if (meetsScoreCutoff)
143+
AddPeptideCandidateToPsm(scan, myLocks, thisScore, peptide, matchedIons);
144+
145+
146+
if (SpectralLibrary != null)
127147
{
128-
// valid hit (met the cutoff score); lock the scan to prevent other threads from accessing it
129-
lock (myLocks[scan.ScanIndex])
130-
{
131-
bool scoreImprovement = PeptideSpectralMatches[scan.ScanIndex] == null || (thisScore - PeptideSpectralMatches[scan.ScanIndex].RunnerUpScore) > -PeptideSpectralMatch.ToleranceForScoreDifferentiation;
132-
133-
if (scoreImprovement)
134-
{
135-
if (PeptideSpectralMatches[scan.ScanIndex] == null)
136-
{
137-
PeptideSpectralMatches[scan.ScanIndex] = new PeptideSpectralMatch(peptide, scan.Notch, thisScore, scan.ScanIndex, scan.TheScan, CommonParameters, matchedIons, 0);
138-
}
139-
else
140-
{
141-
PeptideSpectralMatches[scan.ScanIndex].AddOrReplace(peptide, thisScore, scan.Notch, CommonParameters.ReportAllAmbiguity, matchedIons, 0);
142-
}
143-
}
144-
}
148+
DecoyScoreForSpectralLibrarySearch(scan, reversedOnTheFlyDecoy, decoyFragmentsForEachDissociationType,dissociationType, myLocks);
145149
}
146150
}
147151
}
@@ -167,6 +171,54 @@ protected override MetaMorpheusEngineResults RunSpecific()
167171
return new MetaMorpheusEngineResults(this);
168172
}
169173

174+
private void DecoyScoreForSpectralLibrarySearch(ScanWithIndexAndNotchInfo scan,PeptideWithSetModifications reversedOnTheFlyDecoy, Dictionary<DissociationType, List<Product>> decoyFragmentsForEachDissociationType, DissociationType dissociationType,object[] myLocks)
175+
{
176+
// match decoy ions for decoy-on-the-fly
177+
var decoyTheoreticalFragments = decoyFragmentsForEachDissociationType[dissociationType];
178+
179+
if (decoyTheoreticalFragments.Count == 0)
180+
{
181+
reversedOnTheFlyDecoy.Fragment(dissociationType, CommonParameters.DigestionParams.FragmentationTerminus, decoyTheoreticalFragments);
182+
}
183+
184+
var decoyMatchedIons = MatchFragmentIons(scan.TheScan, decoyTheoreticalFragments, CommonParameters,
185+
matchAllCharges: WriteSpectralLibrary);
186+
187+
// calculate decoy's score
188+
var decoyScore = CalculatePeptideScore(scan.TheScan.TheScan, decoyMatchedIons, fragmentsCanHaveDifferentCharges: WriteSpectralLibrary);
189+
190+
AddPeptideCandidateToPsm(scan, myLocks, decoyScore, reversedOnTheFlyDecoy, decoyMatchedIons);
191+
}
192+
193+
194+
private void AddPeptideCandidateToPsm(ScanWithIndexAndNotchInfo scan, object[] myLocks, double thisScore, PeptideWithSetModifications peptide, List<MatchedFragmentIon> matchedIons)
195+
{
196+
bool meetsScoreCutoff = thisScore >= CommonParameters.ScoreCutoff;
197+
198+
// this is thread-safe because even if the score improves from another thread writing to this PSM,
199+
// the lock combined with AddOrReplace method will ensure thread safety
200+
if (meetsScoreCutoff)
201+
{
202+
// valid hit (met the cutoff score); lock the scan to prevent other threads from accessing it
203+
lock (myLocks[scan.ScanIndex])
204+
{
205+
bool scoreImprovement = PeptideSpectralMatches[scan.ScanIndex] == null || (thisScore - PeptideSpectralMatches[scan.ScanIndex].RunnerUpScore) > -PeptideSpectralMatch.ToleranceForScoreDifferentiation;
206+
207+
if (scoreImprovement)
208+
{
209+
if (PeptideSpectralMatches[scan.ScanIndex] == null)
210+
{
211+
PeptideSpectralMatches[scan.ScanIndex] = new PeptideSpectralMatch(peptide, scan.Notch, thisScore, scan.ScanIndex, scan.TheScan, CommonParameters, matchedIons, 0);
212+
}
213+
else
214+
{
215+
PeptideSpectralMatches[scan.ScanIndex].AddOrReplace(peptide, thisScore, scan.Notch, CommonParameters.ReportAllAmbiguity, matchedIons, 0);
216+
}
217+
}
218+
}
219+
}
220+
}
221+
170222
private IEnumerable<ScanWithIndexAndNotchInfo> GetAcceptableScans(double peptideMonoisotopicMass, MassDiffAcceptor searchMode)
171223
{
172224
foreach (AllowedIntervalWithNotch allowedIntervalWithNotch in searchMode.GetAllowedPrecursorMassIntervalsFromTheoreticalMass(peptideMonoisotopicMass).ToList())

EngineLayer/EngineLayer.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<PackageReference Include="Microsoft.ML" Version="1.3.1" />
2121
<PackageReference Include="Microsoft.ML.FastTree" Version="1.3.1" />
2222
<PackageReference Include="Microsoft.NETCore.App" Version="2.2.8" />
23-
<PackageReference Include="mzLib" Version="1.0.494" />
23+
<PackageReference Include="mzLib" Version="1.0.499" />
2424
<PackageReference Include="NETStandard.Library" Version="2.0.3" />
2525
<PackageReference Include="Nett" Version="0.13.0" />
2626
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />

0 commit comments

Comments
 (0)