package com.compomics.util.experiment.massspectrometry.indexes;
import com.compomics.util.experiment.massspectrometry.Precursor;
import java.util.ArrayList;
import java.util.HashMap;
import org.apache.commons.math.util.FastMath;
/**
* This map stores the precursors indexed by mass.
*
* @author Marc Vaudel
*/
public class PrecursorMap {
/**
* The precursor m/z tolerance.
*/
double precursorTolerance;
/**
* Boolean indicating whether the precursor m/z tolerance is in ppm.
*/
boolean ppm;
/**
* Map of the precursors by bin and m/z.
*/
private HashMap<Integer, HashMap<Double, ArrayList<PrecursorWithTitle>>> precursorsMap = new HashMap<Integer, HashMap<Double, ArrayList<PrecursorWithTitle>>>();
/**
* An m/z anchor to determine the bins in ppm
*/
private static final double mzAnchor = 1000;
/**
* The log of the m/z anchor.
*/
private static final double mzAnchorLog = FastMath.log(mzAnchor);
/**
* The scaling factor used for the bins in ppm
*/
private double scalingFactor;
/**
* The minimal m/z found.
*/
private Double minMz = null;
/**
* The maximal m/z found.
*/
private Double maxMz = null;
/**
* Builds a precursor map.
*
* @param precursors map of the precursors indexed by spectrum title
* @param precursorTolerance the precursor mass tolerance to use
* @param ppm boolean indicating whether the tolerance is in ppm
*/
public PrecursorMap(HashMap<String, Precursor> precursors, double precursorTolerance, boolean ppm) {
this.precursorTolerance = precursorTolerance;
this.ppm = ppm;
if (ppm) {
scalingFactor = FastMath.log((1000000 - precursorTolerance) / (1000000 + precursorTolerance));
}
for (String spectrumTitle : precursors.keySet()) {
Precursor precursor = precursors.get(spectrumTitle);
PrecursorWithTitle precursorWithTitle = new PrecursorWithTitle(precursor, spectrumTitle);
double mz = precursor.getMz();
if (minMz == null || mz < minMz) {
minMz = mz;
}
if (maxMz == null || mz > maxMz) {
maxMz = mz;
}
Integer bin = getBin(mz);
HashMap<Double, ArrayList<PrecursorWithTitle>> precursorsInBin = precursorsMap.get(bin);
if (precursorsInBin == null) {
precursorsInBin = new HashMap<Double, ArrayList<PrecursorWithTitle>>(2);
precursorsMap.put(bin, precursorsInBin);
}
ArrayList<PrecursorWithTitle> precursorsAtMz = precursorsInBin.get(mz);
if (precursorsAtMz == null) {
precursorsAtMz = new ArrayList<PrecursorWithTitle>(1);
precursorsInBin.put(mz, precursorsAtMz);
}
precursorsAtMz.add(precursorWithTitle);
}
}
/**
* Returns the bin corresponding to the given m/z.
*
* @param mz the m/z
*
* @return the bin
*/
private Integer getBin(double mz) {
if (ppm) {
return getBinPpm(mz);
} else {
return getBinAbsolute(mz);
}
}
/**
* Returns the bin corresponding to the given m/z with absolute tolerance in
* Th.
*
* @param mz the m/z
*
* @return the bin
*/
private Integer getBinAbsolute(double mz) {
Integer bin = (int) (mz / precursorTolerance);
return bin;
}
/**
* Returns the bin corresponding to the given mz with relative tolerance in
* ppm.
*
* @param mz the m/z
*
* @return the bin
*/
private Integer getBinPpm(double mz) {
int bin = (int) ((FastMath.log(mz) - mzAnchorLog) / scalingFactor);
return bin;
}
/**
* Returns a list containing the precursors matching the given m/z.
* TODO: check only one/two bins when possible
*
* @param referenceMz a mz to query
*
* @return a list containing the precursors matching the given m/z
*/
public ArrayList<PrecursorWithTitle> getMatchingSpectra(double referenceMz) {
int bin0 = getBin(referenceMz);
ArrayList<PrecursorWithTitle> result = new ArrayList<PrecursorWithTitle>(0);
HashMap<Double, ArrayList<PrecursorWithTitle>> binContent = precursorsMap.get(bin0 - 1);
if (binContent != null) {
for (Double precursorMz : binContent.keySet()) {
double error;
if (ppm) {
error = 1000000 * (precursorMz - referenceMz) / referenceMz;
} else {
error = precursorMz - referenceMz;
}
if (Math.abs(error) <= precursorTolerance) {
result.addAll(binContent.get(precursorMz));
}
}
}
binContent = precursorsMap.get(bin0);
if (binContent != null) {
for (Double precursorMz : binContent.keySet()) {
double error;
if (ppm) {
error = 1000000 * (precursorMz - referenceMz) / referenceMz;
} else {
error = precursorMz - referenceMz;
}
if (Math.abs(error) <= precursorTolerance) {
result.addAll(binContent.get(precursorMz));
}
}
}
binContent = precursorsMap.get(bin0 + 1);
if (binContent != null) {
for (Double precursorMz : binContent.keySet()) {
double error;
if (ppm) {
error = 1000000 * (precursorMz - referenceMz) / referenceMz;
} else {
error = precursorMz - referenceMz;
}
if (Math.abs(error) <= precursorTolerance) {
result.addAll(binContent.get(precursorMz));
}
}
}
return result;
}
/**
* Returns the bins in the map.
*
* @return the bins in the map
*/
public ArrayList<Integer> getBins() {
return new ArrayList<Integer>(precursorsMap.keySet());
}
/**
* Returns the precursors at the given bin indexed by mass. Null if none
* found.
*
* @param bin the bin number
*
* @return the precursors at the given bin
*/
public HashMap<Double, ArrayList<PrecursorWithTitle>> getPrecursorsInBin(int bin) {
return precursorsMap.get(bin);
}
/**
* Returns the mass associated with the given bin, the middle of the bin.
*
* @param bin the bin number
*
* @return the mass associated with the given bin
*/
public Double getMass(int bin) {
if (ppm) {
return FastMath.exp((scalingFactor * bin) + mzAnchorLog);
} else {
return precursorTolerance * (0.5 + bin);
}
}
/**
* Returns the minimal m/z encountered among the precursors.
*
* @return the minimal m/z encountered among the precursors
*/
public Double getMinMz() {
return minMz;
}
/**
* Returns the maximal m/z encountered among the precursors.
*
* @return the maximal m/z encountered among the precursors
*/
public Double getMaxMz() {
return maxMz;
}
/**
* Convenience class storing the precursor and corresponding spectrum title.
*/
public class PrecursorWithTitle {
/**
* The precursor
*/
public final Precursor precursor;
/**
* The spectrum title
*/
public final String spectrumTitle;
/**
* Constructor.
*
* @param precursor the precursor
* @param spectrumTitle the spectrum title
*/
public PrecursorWithTitle(Precursor precursor, String spectrumTitle) {
this.precursor = precursor;
this.spectrumTitle = spectrumTitle;
}
}
}