/// <summary>
/// The run informed workflow.
/// </summary>
/// <param name="target">
/// The Target.
/// </param>
/// <returns>
/// The <see cref="ChargeStateCorrelationResult"/>.
/// </returns>
public ChargeStateCorrelationResult RunInformedWorkflow(PeptideTarget target)
{
Composition targetComposition = target.CompositionWithoutAdduct;
double targetMass = targetComposition.Mass;
string empiricalFormula = targetComposition.ToPlainString();
double targetNet = target.NormalizedElutionTime;
double targetNetMin = targetNet - this._parameters.NetTolerance;
double targetNetMax = targetNet + this._parameters.NetTolerance;
double reverseAlignedNetMin = targetNetMin;
double reverseAlignedNetMax = targetNetMax;
if (this._netAlignment != null)
{
double reverseAlignedNet = this.GetReverseAlignedNet(targetNet);
reverseAlignedNetMin = reverseAlignedNet - this._parameters.NetTolerance;
reverseAlignedNetMax = reverseAlignedNet + this._parameters.NetTolerance;
}
int scanLcSearchMin = (int)Math.Floor(reverseAlignedNetMin * this.NumberOfFrames);
int scanLcSearchMax = (int)Math.Ceiling(reverseAlignedNetMax * this.NumberOfFrames);
int iteration = (targetComposition == null) ? 1 : this._parameters.ChargeStateMax;
for (int chargeState = 1; chargeState <= iteration; chargeState++)
{
if (targetComposition != null)
{
Ion targetIon = new Ion(targetComposition, chargeState);
target.MassWithAdduct = targetIon.GetMonoIsotopicMz();
}
double minMzForSpectrum = target.MassWithAdduct - (1.6 / chargeState);
double maxMzForSpectrum = target.MassWithAdduct + (4.6 / chargeState);
// Generate Theoretical Isotopic Profile
IsotopicProfile theoreticalIsotopicProfile = this._theoreticalFeatureGenerator.GenerateTheorProfile(empiricalFormula, chargeState);
List<Peak> theoreticalIsotopicProfilePeakList = theoreticalIsotopicProfile.Peaklist.Cast<Peak>().ToList();
// Find XIC Features
IEnumerable<FeatureBlob> featureBlobs = this.FindFeatures(target.MassWithAdduct, scanLcSearchMin, scanLcSearchMax);
// Filter away small XIC peaks
featureBlobs = FeatureDetection.FilterFeatureList(featureBlobs, 0.25);
if(!featureBlobs.Any())
{
LcImsTargetResult result = new LcImsTargetResult
{
ChargeState = chargeState,
AnalysisStatus = AnalysisStatus.XicNotFound
};
target.ResultList.Add(result);
}
// Check each XIC Peak found
foreach (var featureBlob in featureBlobs)
{
// Setup result object
LcImsTargetResult result = new LcImsTargetResult
{
ChargeState = chargeState,
AnalysisStatus = AnalysisStatus.Positive
};
target.ResultList.Add(result);
FeatureBlobStatistics statistics = featureBlob.CalculateStatistics();
int unsaturatedIsotope = 0;
FeatureBlob isotopeFeature = null;
int scanLcMin = statistics.ScanLcMin;
int scanLcMax = statistics.ScanLcMax;
int scanImsMin = statistics.ScanImsMin;
int scanImsMax = statistics.ScanImsMax;
// TODO: Verify that there are no peaks at isotope #s 0.5 and 1.5?? (If we filter on drift time, this shouldn't actually be necessary)
// Find an unsaturated peak in the isotopic profile
for (int i = 1; i < 10; i++)
{
if (!statistics.IsSaturated) break;
// Target isotope m/z
double isotopeTargetMz = (target.CompositionWithoutAdduct != null) ? new Ion(targetComposition, chargeState).GetIsotopeMz(i) : target.MassWithAdduct;
// Find XIC Features
IEnumerable<FeatureBlob> newFeatureBlobs = this.FindFeatures(isotopeTargetMz, scanLcMin - 20, scanLcMax + 20);
// If no feature, then get out
if (!newFeatureBlobs.Any())
{
statistics = null;
break;
}
bool foundFeature = false;
foreach (var newFeatureBlob in newFeatureBlobs.OrderByDescending(x => x.PointList.Count))
{
var newStatistics = newFeatureBlob.CalculateStatistics();
if(newStatistics.ScanImsRep <= scanImsMax && newStatistics.ScanImsRep >= scanImsMin && newStatistics.ScanLcRep <= scanLcMax && newStatistics.ScanLcRep >= scanLcMin)
{
isotopeFeature = newFeatureBlob;
foundFeature = true;
break;
}
}
if(!foundFeature)
{
statistics = null;
break;
}
statistics = isotopeFeature.CalculateStatistics();
unsaturatedIsotope = i;
}
// Bad Feature, so get out
if (statistics == null)
{
result.AnalysisStatus = AnalysisStatus.IsotopicProfileNotFound;
continue;
}
// TODO: Calculate accurate NET and drift time using quadratic equation
int scanLcRep = statistics.ScanLcRep + 1;
int scanImsRep = statistics.ScanImsRep;
// Calculate NET using aligned data if applicable
double net = scanLcRep / this.NumberOfFrames;
if (this._netAlignment != null)
{
net = this._netAlignment.Interpolate(net);
}
FeatureBlob featureToUseForResult = unsaturatedIsotope > 0 ? isotopeFeature : featureBlob;
// Set data to result
result.FeatureBlobStatistics = statistics;
result.IsSaturated = unsaturatedIsotope > 0;
result.ScanLcRep = statistics.ScanLcRep;
result.NormalizedElutionTime = net;
result.DriftTime = this._uimfReader.GetDriftTime(statistics.ScanLcRep, statistics.ScanImsRep, true);
result.XicFeature = featureToUseForResult;
// Don't consider bogus results
if (scanImsRep < 5 || scanImsRep > this.NumberOfScans - 5)
{
result.AnalysisStatus = AnalysisStatus.DriftTimeError;
continue;
}
// Don't consider bogus results
if (scanLcRep < 3 || scanLcRep > this.NumberOfFrames - 4)
{
result.AnalysisStatus = AnalysisStatus.ElutionTimeError;
continue;
}
// TODO: ViperCompatibleMass Alignment???
if (target.TargetType == TargetType.Peptide)
{
// Filter by NET
if (net > targetNetMax || net < targetNetMin)
{
result.AnalysisStatus = AnalysisStatus.ElutionTimeError;
continue;
}
}
//Console.WriteLine(Target.PeptideSequence + "\t" + targetMass + "\t" + targetMz + "\t" + scanLcRep);
// Get ViperCompatibleMass Spectrum Data
XYData massSpectrum = this.GetMassSpectrum(scanLcRep, scanImsRep, minMzForSpectrum, maxMzForSpectrum);
List<Peak> massSpectrumPeakList = this._peakDetector.FindPeaks(massSpectrum);
//WriteXYDataToFile(massSpectrum, targetMz);
// Find Isotopic Profile
List<Peak> massSpectrumPeaks;
IsotopicProfile observedIsotopicProfile = this._msFeatureFinder.IterativelyFindMSFeature(massSpectrum, theoreticalIsotopicProfile, out massSpectrumPeaks);
// Add data to result
result.MassSpectrum = massSpectrum;
// No need to move on if the isotopic profile is not found
if (observedIsotopicProfile == null || observedIsotopicProfile.MonoIsotopicMass < 1)
{
result.AnalysisStatus = AnalysisStatus.IsotopicProfileNotFound;
continue;
}
// Add data to result
result.IsotopicProfile = observedIsotopicProfile;
result.MonoisotopicMass = observedIsotopicProfile.MonoIsotopicMass;
result.PpmError = Math.Abs(PeptideUtil.PpmError(targetMass, observedIsotopicProfile.MonoIsotopicMass));
// If not enough peaks to reach unsaturated isotope, no need to move on
if (observedIsotopicProfile.Peaklist.Count <= unsaturatedIsotope)
{
result.AnalysisStatus = AnalysisStatus.IsotopicProfileNotFound;
continue;
}
// If the mass error is too high, then ignore
if (result.PpmError > this._parameters.MassToleranceInPpm)
{
result.AnalysisStatus = AnalysisStatus.MassError;
continue;
}
// Correct for Saturation if needed
if (unsaturatedIsotope > 0)
{
IsotopicProfileUtil.AdjustSaturatedIsotopicProfile(observedIsotopicProfile, theoreticalIsotopicProfile, unsaturatedIsotope);
}
//WriteMSPeakListToFile(observedIsotopicProfile.Peaklist, targetMz);
// TODO: This is a hack to fix an issue where the peak width is being calculated way too large which causes the leftOfMonoPeakLooker to use too wide of a tolerance
MSPeak monoPeak = observedIsotopicProfile.getMonoPeak();
if (monoPeak.Width > 0.15) monoPeak.Width = 0.15f;
// Filter out flagged results
MSPeak peakToLeft = this._leftOfMonoPeakLooker.LookforPeakToTheLeftOfMonoPeak(monoPeak, observedIsotopicProfile.ChargeState, massSpectrumPeaks);
if (peakToLeft != null)
{
result.AnalysisStatus = AnalysisStatus.PeakToLeft;
continue;
}
double isotopicFitScore;
// Calculate isotopic fit score
if(unsaturatedIsotope > 0)
{
int unsaturatedScanLc = this.FindFrameNumberUseForIsotopicProfile(target.MassWithAdduct, scanLcRep, scanImsRep);
if (unsaturatedScanLc > 0)
{
// Use the unsaturated profile if we were able to get one
XYData unsaturatedMassSpectrum = this.GetMassSpectrum(unsaturatedScanLc, scanImsRep, minMzForSpectrum, maxMzForSpectrum);
//WriteXYDataToFile(unsaturatedMassSpectrum, targetMz);
List<Peak> unsaturatedMassSpectrumPeakList = this._peakDetector.FindPeaks(unsaturatedMassSpectrum);
isotopicFitScore = this._isotopicPeakFitScoreCalculator.GetFit(theoreticalIsotopicProfilePeakList, unsaturatedMassSpectrumPeakList, 0.15, this._parameters.MassToleranceInPpm);
}
else
{
// Use the saturated profile
isotopicFitScore = this._isotopicPeakFitScoreCalculator.GetFit(theoreticalIsotopicProfilePeakList, massSpectrumPeakList, 0.15, this._parameters.MassToleranceInPpm);
}
}
else
{
isotopicFitScore = this._isotopicPeakFitScoreCalculator.GetFit(theoreticalIsotopicProfilePeakList, massSpectrumPeakList, 0.15, this._parameters.MassToleranceInPpm);
}
// Add data to result
result.IsotopicFitScore = isotopicFitScore;
// Filter out bad isotopic fit scores
if (isotopicFitScore > this._parameters.IsotopicFitScoreThreshold && unsaturatedIsotope == 0)
{
result.AnalysisStatus = AnalysisStatus.IsotopicFitScoreError;
continue;
}
Console.WriteLine(chargeState + "\t" + unsaturatedIsotope + "\t" + statistics.ScanLcMin + "\t" + statistics.ScanLcMax + "\t" + statistics.ScanLcRep + "\t" + statistics.ScanImsMin + "\t" + statistics.ScanImsMax + "\t" + statistics.ScanImsRep + "\t" + isotopicFitScore.ToString("0.0000") + "\t" + result.NormalizedElutionTime.ToString("0.0000") + "\t" + result.DriftTime.ToString("0.0000"));
}
// TODO: Isotope Correlation (probably not going to do because of saturation issues)
}
// Charge State Correlation (use first unsaturated XIC feature)
List<ChargeStateCorrelationResult> chargeStateCorrelationResultList = new List<ChargeStateCorrelationResult>();
ChargeStateCorrelationResult bestCorrelationResult = null;
double bestCorrelationSum = -1;
List<LcImsTargetResult> resultList = target.ResultList.Where(x => x.AnalysisStatus == AnalysisStatus.Positive).OrderBy(x => x.IsotopicFitScore).ToList();
int numResults = resultList.Count;
for (int i = 0; i < numResults; i++)
{
LcImsTargetResult referenceResult = resultList[i];
ChargeStateCorrelationResult chargeStateCorrelationResult = new ChargeStateCorrelationResult(target, referenceResult);
chargeStateCorrelationResultList.Add(chargeStateCorrelationResult);
for (int j = i + 1; j < numResults; j++)
{
LcImsTargetResult testResult = resultList[j];
double correlation = FeatureCorrelator.CorrelateFeaturesUsingLc(referenceResult.XicFeature, testResult.XicFeature);
chargeStateCorrelationResult.CorrelationMap.Add(testResult, correlation);
Console.WriteLine(referenceResult.FeatureBlobStatistics.ScanLcRep + "\t" + referenceResult.FeatureBlobStatistics.ScanImsRep + "\t" + testResult.FeatureBlobStatistics.ScanLcRep + "\t" + testResult.FeatureBlobStatistics.ScanImsRep + "\t" + correlation);
}
List<LcImsTargetResult> possibleBestResultList;
double correlationSum = chargeStateCorrelationResult.GetBestCorrelation(out possibleBestResultList);
if(correlationSum > bestCorrelationSum)
{
bestCorrelationSum = correlationSum;
bestCorrelationResult = chargeStateCorrelationResult;
}
}
// TODO: Score Target
// TODO: Quantify Target (return isotopic profile abundance)
return bestCorrelationResult;
}