Skip to content

Commit 301ffaf

Browse files
Kai Dührkopmfleisch
authored andcommitted
better log messages and log parameters
fix parameter parsing (cherry picked from commit 948dfb3)
1 parent bc98173 commit 301ffaf

File tree

7 files changed

+171
-26
lines changed

7 files changed

+171
-26
lines changed

lcms2/src/main/java/de/unijena/bioinf/lcms/LCMSProcessing.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,4 +478,6 @@ public void closeStorages(){
478478
storageFactory.close();
479479
}
480480

481+
482+
481483
}

lcms2/src/main/java/de/unijena/bioinf/lcms/msms/MostIntensivePeakInIsolationWindowAssignmentStrategy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public Optional<MsMsTraceReference> getTraceFor(ProcessedSample sample, Ms2Spect
4444
// for simplicity we assume gaussian shape of the isolation window with 3*sigma = window radius
4545
final double sigma = window.getWindowWidth()/6d;
4646
Optional<ContiguousTrace> tr = contigousTraces.stream().max(Comparator.comparingDouble(x -> x.apexIntensity() * Math.exp(-Math.pow(x.averagedMz() - pmz, 2) / (2 * sigma * sigma))));
47-
return tr.isPresent() ? Optional.of(new MsMsTraceReference(ms2.getUid(), tr.get().getUid(), parentId)) : Optional.empty();
47+
return tr.isPresent() ? Optional.of(new MsMsTraceReference(ms2.getUid(), tr.get().getUid(), parentId, ms2.getScanId())) : Optional.empty();
4848

4949
}
5050
}

lcms2/src/main/java/de/unijena/bioinf/lcms/msms/MsMsTraceReference.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,17 @@ public class MsMsTraceReference implements Serializable {
88
public final int ms2Uid;
99
public final int traceUid;
1010
public final int rawScanIdxOfParent;
11+
public final int ms2scanid;
1112

12-
public MsMsTraceReference(int ms2Uid, int traceUid, int scanIdx) {
13+
public MsMsTraceReference(int ms2Uid, int traceUid, int scanIdx, int ms2scanid) {
1314
this.ms2Uid = ms2Uid;
1415
this.traceUid = traceUid;
1516
this.rawScanIdxOfParent = scanIdx;
17+
this.ms2scanid = ms2scanid;
1618
}
1719

1820
@Override
1921
public String toString() {
20-
return String.format(Locale.US, "MsMS at scan %d for trace %d (Ms2ID: %d)", rawScanIdxOfParent, traceUid, ms2Uid);
22+
return String.format(Locale.US, "[MsMs scan id=%d parent scan id=%d]" ,ms2scanid,rawScanIdxOfParent);
2123
}
2224
}

lcms2/src/main/java/de/unijena/bioinf/lcms/utils/TrackFeatureToFile.java

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package de.unijena.bioinf.lcms.utils;
22

33
import de.unijena.bioinf.ChemistryBase.ms.Deviation;
4+
import de.unijena.bioinf.lcms.ScanPointMapping;
45
import de.unijena.bioinf.lcms.align.AlignedMoI;
56
import de.unijena.bioinf.lcms.align.MoI;
67
import de.unijena.bioinf.lcms.merge.MergedTrace;
@@ -9,8 +10,13 @@
910
import de.unijena.bioinf.lcms.trace.ProcessedSample;
1011
import de.unijena.bioinf.lcms.trace.ProjectedTrace;
1112
import de.unijena.bioinf.lcms.trace.Rect;
13+
import de.unijena.bioinf.lcms.trace.segmentation.TraceSegment;
14+
import de.unijena.bioinf.lcms.traceextractor.MassOfInterestConfidenceEstimatorStrategy;
15+
import de.unijena.bioinf.ms.persistence.model.core.feature.AbstractFeature;
1216
import de.unijena.bioinf.ms.persistence.model.core.feature.AlignedFeatures;
1317
import it.unimi.dsi.fastutil.ints.IntArrayList;
18+
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
19+
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
1420
import jakarta.annotation.Nullable;
1521
import org.apache.commons.lang3.Range;
1622

@@ -19,6 +25,7 @@
1925
import java.io.PrintStream;
2026
import java.util.Arrays;
2127
import java.util.Locale;
28+
import java.util.stream.Collectors;
2229

2330
public class TrackFeatureToFile implements Tracker{
2431
private final PrintStream out;
@@ -43,10 +50,18 @@ public TrackFeatureToFile(File outFile, double massToTrack, Range<Double> retent
4350
@Override
4451
public void tracePicked(double mz, double rt, ProcessedSample sample, ContiguousTrace trace) {
4552
if (tracked(mz, rt)) {
46-
this.out.println(String.format(Locale.US,"trace picked with m/z = %f, rt = %f, in sample %s. Trace has the following segments: %s", mz, rt, sample.getRun().getName(), Arrays.toString(trace.getSegments())));
53+
ScanPointMapping mapping = sample.getMapping();
54+
this.out.println(String.format(Locale.US,"A trace picked with m/z = %f at rt = %f in sample %s. The trace contains the following segments: %s", mz, rt, sample.getRun().getName(), Arrays.stream(trace.getSegments()).map(s->toStr(s,mapping)).collect(Collectors.joining(", "))));
4755
}
4856
}
4957

58+
private String toStr(TraceSegment s, ScanPointMapping mapping) {
59+
double leftEdge = mapping.getRetentionTimeAt(s.leftEdge);
60+
double apex = mapping.getRetentionTimeAt(s.apex);
61+
double rightEdge = mapping.getRetentionTimeAt(s.rightEdge);
62+
return String.format("[%.1f-%.1f s] with apex at %.1f s",leftEdge, rightEdge, apex);
63+
}
64+
5065
private boolean tracked(double mz, double rt) {
5166
return (mz >= fromMz && mz <= toMz && retentionTimeToTrack.contains(rt));
5267
}
@@ -57,28 +72,45 @@ private boolean tracked(double mz, double minrt, double maxrt) {
5772
@Override
5873
public void moiAccepted(double mz, double retentionTime, ProcessedSample sample, ContiguousTrace trace, MoI moi) {
5974
if (tracked(mz, retentionTime)) {
60-
this.out.println(String.format(Locale.US,"MOI accepted with m/z = %f, rt = %f, confidence = %f in sample %s", mz, retentionTime, moi.getConfidence(), sample.getRun().getName()));
75+
float confidence = moi.getConfidence();
76+
String confidenceLabel;
77+
if (confidence >= MassOfInterestConfidenceEstimatorStrategy.CONFIDENT) confidenceLabel="highest, use for first alignment pass";
78+
else if (confidence >= MassOfInterestConfidenceEstimatorStrategy.KEEP_FOR_ALIGNMENT) confidenceLabel="accepted, keep feature alive";
79+
else if (confidence >= MassOfInterestConfidenceEstimatorStrategy.ACCEPT) confidenceLabel="accepted, for now";
80+
else confidenceLabel = "rejected";
81+
final double intensity = trace.intensity(moi.getScanId());
82+
final double relativeIntensity = moi.getIntensity();
83+
84+
this.out.println(String.format(Locale.US,"accept potential feature with m/z = %f, rt = %f s, intensity = %.1f (relative: %e), confidence = %f (%s) in sample %s", mz, retentionTime, intensity, relativeIntensity, confidence, confidenceLabel, sample.getRun().getName()));
6185
}
6286
}
6387

6488
@Override
6589
public void moiRejected(double mz, double retentionTime, ProcessedSample sample, ContiguousTrace trace, MoI moi) {
6690
if (tracked(mz, retentionTime)) {
67-
this.out.println(String.format(Locale.US,"MOI REJECTED with m/z = %f, rt = %f, confidence = %f in sample %s", mz, retentionTime, moi.getConfidence(), sample.getRun().getName()));
91+
float confidence = moi.getConfidence();
92+
String confidenceLabel;
93+
if (confidence >= MassOfInterestConfidenceEstimatorStrategy.CONFIDENT) confidenceLabel="highest, use for first alignment pass";
94+
else if (confidence >= MassOfInterestConfidenceEstimatorStrategy.KEEP_FOR_ALIGNMENT) confidenceLabel="accepted, keep feature alive";
95+
else if (confidence >= MassOfInterestConfidenceEstimatorStrategy.ACCEPT) confidenceLabel="accepted, for now";
96+
else confidenceLabel = "rejected";
97+
final double intensity = trace.intensity(moi.getScanId());
98+
final double relativeIntensity = moi.getIntensity();
99+
this.out.println(String.format(Locale.US,"reject potential feature with m/z = %f, rt = %f s, intensity = %.1f (relative: %e), confidence = %f (%s) in sample %s", mz, retentionTime, intensity,relativeIntensity, moi.getConfidence(), confidenceLabel, sample.getRun().getName()));
68100
}
69101
}
70102

71103
@Override
72104
public void alignMois(ProcessedSample rightSample, MoI left, MoI right) {
73105
if (tracked(left.getMz(), left.getRetentionTime())) {
74-
this.out.println(String.format(Locale.US,"ALIGN two MOIS with m/z %f, rt left = %f, rt right = %f in sample %s", left.getMz(), left.getRetentionTime(), right.getRetentionTime(), rightSample.getRun().getName()));
106+
this.out.println(String.format(Locale.US,"align two potential features with m/z = %f, rt left = %f s, rt right = %f s in sample %s", left.getMz(), left.getRetentionTime(), right.getRetentionTime(), rightSample.getRun().getName()));
75107
}
76108
}
77109

78110
@Override
79111
public void unalignedMoI(ProcessedSample s, MoI moI) {
80112
if (tracked(moI.getMz(), moI.getRetentionTime())) {
81-
this.out.println(String.format(Locale.US,"Cannot align MOI with m/z %f and rt %f in sample %s", moI.getMz(), moI.getRetentionTime(), moI.getRetentionTime(), s.getRun().getName()));
113+
this.out.println(String.format(Locale.US,"cannot align a potential feature with m/z = %f and rt = %f s in sample %s", moI.getMz(), moI.getRetentionTime(), moI.getRetentionTime(), s.getRun().getName()));
82114
}
83115
}
84116

@@ -87,34 +119,37 @@ public void moiDeleted(MoI moi) {
87119
if (tracked(moi.getMz(), moi.getRetentionTime())) {
88120
if (moi instanceof AlignedMoI) {
89121
AlignedMoI amo = (AlignedMoI) moi;
90-
this.out.println(String.format(Locale.US,"DELETE MOI with m/z %f and rt %f which was aligned in %d samples", moi.getMz(), moi.getRetentionTime(), amo.getAligned().length));
122+
this.out.println(String.format(Locale.US,"forget potential feature with m/z = %f and rt = %f s which was aligned in only %d samples", moi.getMz(), moi.getRetentionTime(), amo.getAligned().length));
91123
} else {
92-
this.out.println(String.format(Locale.US,"DELETE singleton MOI with m/z %f and rt %f", moi.getMz(), moi.getRetentionTime()));
124+
this.out.println(String.format(Locale.US,"forget potential feature with m/z = %f and rt = %f s because it did not align to any sample", moi.getMz(), moi.getRetentionTime()));
93125
}
94126
}
95127
}
96128

97129
@Override
98130
public void createRect(ProcessedSample sample, Rect r) {
99131
if (tracked(r.avgMz, r.minRt, r.maxRt)) {
100-
this.out.println(String.format(Locale.US,"create rect mz=%f..%f, rt=%f..%f",
132+
this.out.println(String.format(Locale.US,"Prepare merging a feature with mz=%f..%f and rt=%f..%f s",
101133
r.minMz,r.maxMz,r.minRt,r.maxRt));
102134
}
103135
}
104136

105137
@Override
106138
public void emptyRect(ProcessedSample sample, Rect r) {
107139
if (tracked(r.avgMz, r.minRt, r.maxRt)) {
108-
this.out.println(String.format(Locale.US,"do not find any traces in rect mz=%f..%f, rt=%f..%f in sample %s",
140+
this.out.println(String.format(Locale.US,"do not find any traces within mz=%f..%f, rt=%f..%f s in sample %s",
109141
r.minMz,r.maxMz,r.minRt,r.maxRt,sample.getRun().getName()));
110142
}
111143
}
112144

113145
@Override
114146
public void mergedTrace(ProcessedSample merged, ProcessedSample sample, Rect r, ProjectedTrace projectedTrace, MoI[] moisForSample) {
115-
double rt = merged.getMapping().getRetentionTimeAt(projectedTrace.getProjectedApex());
147+
ScanPointMapping m = merged.getMapping();
148+
double rt = m.getRetentionTimeAt(projectedTrace.getProjectedApex());
149+
double rt0 = m.getRetentionTimeAt(projectedTrace.getProjectedStartId());
150+
double rt1 = m.getRetentionTimeAt(projectedTrace.getProjectedEndId());
116151
if (tracked(projectedTrace.getAveragedMz(), rt)) {
117-
this.out.println(String.format(Locale.US,"merge trace m/z = %f, rt = %f", projectedTrace.getAveragedMz(), rt));
152+
this.out.println(String.format(Locale.US,"merge the trace with m/z = %f and rt = %f..%f s (apex at %f s) from sample %s into the merged sample.", projectedTrace.getAveragedMz(), rt0,rt1,rt, sample.getRun().getName()));
118153
}
119154
}
120155

@@ -123,7 +158,12 @@ public void assignMs2ToMergedTrace(ProcessedSample sample, ContiguousTrace[] sou
123158
double rtA = merged.getMapping().getRetentionTimeAt(projectedTrace.getProjectedStartId());
124159
double rtB = merged.getMapping().getRetentionTimeAt(projectedTrace.getProjectedEndId());
125160
if (tracked(projectedTrace.getAveragedMz(), rtA, rtB)) {
126-
this.out.println(String.format(Locale.US,"merge trace m/z = %f, rt = %f..%f in sample %s ASSIGN MS/MS: %s", projectedTrace.getAveragedMz(), rtA, rtB, sample.getRun().getName(), Arrays.toString(ids)));
161+
if (ids.length==0) {
162+
this.out.printf(Locale.US, "assign NO MS/MS to trace m/z = %f, rt = %f..%f s in sample %s%n", projectedTrace.getAveragedMz(), rtA, rtB, sample.getRun().getName());
163+
164+
} else {
165+
this.out.printf(Locale.US, "assign MS/MS to trace m/z = %f, rt = %f..%f s in sample %s with MS/MS scan ids are: %s%n", projectedTrace.getAveragedMz(), rtA, rtB, sample.getRun().getName(), Arrays.toString(ids));
166+
}
127167
}
128168
}
129169

@@ -132,12 +172,12 @@ public void rejectedForFeatureExtraction(@Nullable Rect r, @Nullable MergedTrace
132172
if (merged==null && r==null) return;
133173
if (merged==null) {
134174
if (tracked(r.avgMz, r.minRt, r.maxRt)) {
135-
this.out.println(String.format(Locale.US,"trace REJECTED with trace is null but rect is m/z = %f..%f, rt = %f..%f", r.minMz,r.maxMz,r.minRt,r.maxRt));
175+
this.out.println(String.format(Locale.US,"A merged trace with m/z = %f..%f, rt = %f..%f s is rejected because it does not contain any peaks", r.minMz,r.maxMz,r.minRt,r.maxRt));
136176
}
137177
} else {
138178
double rt = merged.retentionTime(merged.apex());
139179
if (tracked(merged.averagedMz(), rt)) {
140-
this.out.println(String.format(Locale.US,"trace REJECTED with m/z = %f, rt = %f", merged.averagedMz(), rt));
180+
this.out.println(String.format(Locale.US,"A merged trace with m/z = %f, rt = %f s is rejected because it does not contain any peaks", merged.averagedMz(), rt));
141181
}
142182
}
143183
}
@@ -146,23 +186,26 @@ public void rejectedForFeatureExtraction(@Nullable Rect r, @Nullable MergedTrace
146186
public void noFeatureFound(MergedTrace mergedTrace) {
147187
double rt = mergedTrace.retentionTime(mergedTrace.apex());
148188
if (tracked(mergedTrace.averagedMz(), rt)) {
149-
this.out.println(String.format(Locale.US,"NO FEATURE FOUND for trace with m/z = %f, rt = %f", mergedTrace.averagedMz(), rt));
189+
this.out.println(String.format(Locale.US,"A merged trace with m/z = %f, rt = %f s does not contain any feature", mergedTrace.averagedMz(), rt));
150190
}
151191
}
152192

153193
@Override
154194
public void startExtractingCompounds(ProcessedSample mergedSample, MergedTrace mergedTrace) {
155195
double rt = mergedSample.getMapping().getRetentionTimeAt(mergedTrace.apex());
156196
if (tracked(mergedTrace.averagedMz(), rt)) {
157-
this.out.println(String.format(Locale.US,"Extract compounds for m/z = %f, rt = %f", mergedTrace.averagedMz(), rt));
197+
this.out.println(String.format(Locale.US,"Start peak picking for trace with m/z = %f, rt = %f s", mergedTrace.averagedMz(), rt));
158198
}
159199
}
160200

161201
@Override
162202
public void importFeatures(MergedTrace mergedTrace, AlignedFeatures[] features) {
163-
double rt = mergedTrace.retentionTime(mergedTrace.apex());
164-
if (tracked(mergedTrace.averagedMz(), rt)) {
165-
this.out.println(String.format(Locale.US,"import trace with m/z = %f, rt = %f", mergedTrace.averagedMz(), rt));
203+
for (AlignedFeatures f : features) {
204+
if (tracked(f.getAverageMass(), f.getRetentionTime().getMiddleTime())) {
205+
LongOpenHashSet set = new LongOpenHashSet((f.getFeatures().get()).stream().mapToLong(AbstractFeature::getRunId).toArray());
206+
this.out.println(String.format(Locale.US,"Feature picked with m/z = %f and rt = %f..%f s with apex is at %f s. Feature is contained in the following samples: %s", mergedTrace.averagedMz(), f.getRetentionTime().getStartTime(),
207+
f.getRetentionTime().getEndTime(),f.getRetentionTime().getMiddleTime(), Arrays.stream(mergedTrace.getSamples()).filter(x->set.contains(x.getRun().getRunId())).map(x->x.getRun().getName()).collect(Collectors.joining(", "))));
208+
}
166209
}
167210
}
168211
}

sirius_cli/src/main/java/de/unijena/bioinf/ms/frontend/subtools/lcms_align/LcmsAlignOptions.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import de.unijena.bioinf.projectspace.ProjectSpaceManager;
3232
import de.unijena.bioinf.projectspace.ProjectSpaceManagerFactory;
3333
import lombok.Getter;
34+
import org.apache.commons.lang3.Range;
3435
import org.jetbrains.annotations.NotNull;
3536
import org.jetbrains.annotations.Nullable;
3637
import org.slf4j.LoggerFactory;
@@ -39,6 +40,8 @@
3940
import java.io.File;
4041
import java.io.IOException;
4142
import java.util.Optional;
43+
import java.util.regex.Matcher;
44+
import java.util.regex.Pattern;
4245

4346
@CommandLine.Command(name = "lcms-align", aliases = {"A"}, description = "@|bold <PREPROCESSING>|@ Align and merge compounds of multiple LCMS Runs. Use this tool if you want to import from mzML/mzXml. %n %n", versionProvider = Provide.Versions.class, mixinStandardHelpOptions = true, showDefaultValues = true)
4447
public class LcmsAlignOptions implements PreprocessingTool<PreprocessingJob<? extends ProjectSpaceManager>> {
@@ -107,6 +110,76 @@ static class SignalToNoiseOptions {
107110
)
108111
public Double ppmMax;
109112

113+
//////////////////////////////////////////////////////////////////////////////////////////
114+
// logging ///////////////////////////////////////////////////////////////////////
115+
//////////////////////////////////////////////////////////////////////////////////////////
116+
117+
public static class MassRangeOrValue implements CommandLine.ITypeConverter<Range<Double>> {
118+
119+
private static Pattern REGEX = Pattern.compile("(\\d+(?:\\.\\d+)?)(?:\\s*(?:\\.\\.\\.?|-)\\s*(\\d+(\\.\\d+)?))?");
120+
@Override
121+
public Range<Double> convert(String value) throws Exception {
122+
Matcher matcher = REGEX.matcher(value);
123+
if (matcher.find()) {
124+
String from = matcher.group(1);
125+
String to = matcher.group(2);
126+
if (to!=null && !to.isEmpty()) {
127+
return Range.of(Double.parseDouble(from), Double.parseDouble(to));
128+
} else {
129+
Deviation dev = new Deviation(10);
130+
double mz = Double.parseDouble(from);
131+
double delta = dev.absoluteFor(mz);
132+
return Range.of(mz-delta, mz+delta);
133+
}
134+
} else {
135+
throw new IllegalArgumentException("expect value in format \"FROM-TO\" or just \"VALUE\" (e.g. \"300.04-300.08\")");
136+
}
137+
}
138+
}
139+
public static class TimeRangeOrValue implements CommandLine.ITypeConverter<Range<Double>> {
140+
141+
private static Pattern REGEX = Pattern.compile("(\\d+(?:\\.\\d+)?)\\s*(?:\\s*(?:\\.\\.\\.?|-)\\s*(\\d+(?:\\.\\d+)?))?(\\s*m(?:in)?|s(?:ec)?)");
142+
@Override
143+
public Range<Double> convert(String value) throws Exception {
144+
Matcher matcher = REGEX.matcher(value);
145+
if (matcher.find()) {
146+
String from = matcher.group(1);
147+
String to = matcher.group(2);
148+
String unit = matcher.group(3);
149+
150+
double convFactor;
151+
if (unit.startsWith("s")) {
152+
convFactor = 1;
153+
} else if (unit.startsWith("m")) {
154+
convFactor = 60;
155+
} else {
156+
throw new IllegalArgumentException("Unknown unit \"" + unit + "\". Expect min or s.");
157+
}
158+
159+
if (to!=null && !to.isEmpty()) {
160+
return Range.of(Double.parseDouble(from)*convFactor, Double.parseDouble(to)*convFactor);
161+
} else {
162+
// use 10 seconds as tolerance
163+
double val = Double.parseDouble(from)*convFactor;
164+
return Range.of(val - 10, val + 10);
165+
}
166+
} else {
167+
throw new IllegalArgumentException("expect value in format \"FROM-TO\" or just \"VALUE\" (e.g. \"300.04-300.08\")");
168+
}
169+
}
170+
}
171+
172+
@CommandLine.Option(names={"--log-file"}, defaultValue = "lcms_log.txt", description = "Path to a filename that is used to log detected mass traces. Either --log-mass or --log-rt (or both) have to be specified to enable the logging.")
173+
public File logFile;
174+
175+
@CommandLine.Option(names={"--log-mz"}, description = "A target mass (range) for which logging should be enabled.", converter = MassRangeOrValue.class)
176+
public Range<Double> logMass;
177+
178+
@CommandLine.Option(names={"--log-rt"}, description = "A target retention time (range) for which logging should be enabled", converter = TimeRangeOrValue.class)
179+
public Range<Double> logRt;
180+
181+
182+
110183
//////////////////////////////////////////////////////////////////////////////////////////
111184
//hidden options ///////////////////////////////////////////////////////////////////////
112185
//////////////////////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)