libpappsomspp
Library for mass spectrometry
Loading...
Searching...
No Matches
tandemwrapperrun.cpp
Go to the documentation of this file.
1/**
2 * \file pappsomspp/processing/tandemwrapper/tandemwrapperrun.cpp
3 * \date 25/01/2020
4 * \author Olivier Langella
5 * \brief actually does really run tandem directly on Bruker's data
6 */
7
8/*******************************************************************************
9 * Copyright (c) 2020 Olivier Langella <Olivier.Langella@u-psud.fr>.
10 *
11 * This file is part of PAPPSOms-tools.
12 *
13 * PAPPSOms-tools is free software: you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation, either version 3 of the License, or
16 * (at your option) any later version.
17 *
18 * PAPPSOms-tools is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with PAPPSOms-tools. If not, see <http://www.gnu.org/licenses/>.
25 *
26 ******************************************************************************/
27
28#include "tandemwrapperrun.h"
29#include <QDebug>
30#include <QFileInfo>
31#include <QSettings>
32#include <QThread>
33#include <QThreadPool>
34#include <QRegularExpression>
35#include "../../exception/exceptioninterrupted.h"
36#include "../../msfile/msfileaccessor.h"
37#include "../../msrun/private/timsmsrunreaderms2.h"
38#include "../../processing/filters/filtertriangle.h"
39#include "../../processing/filters/filterchargedeconvolution.h"
40#include "../../msrun/output/mzxmloutput.h"
41#include "wraptandemresults.h"
42#include "xtandempresetreader.h"
43#include "wraptandeminput.h"
44
45namespace pappso
46{
47
48
49TandemWrapperRun::TandemWrapperRun(const QString &tandem_binary,
50 const QString &tmp_dir)
51{
52
53 setTandemBinaryPath(tandem_binary);
54
55 if(!tmp_dir.isEmpty())
56 {
57 mpa_temporaryDirectory = new QTemporaryDir(tmp_dir + "/xtpwrp");
58 }
59 else
60 {
61 mpa_temporaryDirectory = new QTemporaryDir(QDir::tempPath() + "/xtpwrp");
62 }
63 mpa_temporaryDirectory->setAutoRemove(true);
64 if(!mpa_temporaryDirectory->isValid())
65 {
67 QObject::tr("ERROR: unable to create temporary directory %1\n Please "
68 "check file system permissions")
69 .arg(mpa_temporaryDirectory->path()));
70 }
71}
72
74{
75 if(mpa_temporaryDirectory != nullptr)
76 {
78 }
79
80 if(m_xtProcess != nullptr)
81 {
82 m_xtProcess->deleteLater();
83 }
84}
85
86void
87TandemWrapperRun::setTandemBinaryPath(const QString &tandem_binary_path)
88{
89
90
91 m_tandemBinary = tandem_binary_path;
92 QSettings settings;
93 if(m_tandemBinary.isEmpty())
94 {
96 settings.value("path/tandem_binary", "/usr/bin/tandem").toString();
97 }
98 // check for tandem executable
100
101 qDebug() << m_tandemVersion;
102 settings.setValue("path/tandem_binary", m_tandemBinary);
103}
104
105
106const QString
107TandemWrapperRun::checkXtandemVersion(const QString &tandem_bin_path)
108{
109 qDebug();
110 // check tandem path
111 QFileInfo tandem_exe(tandem_bin_path);
112 if(!tandem_exe.exists())
113 {
114 // dir.path() returns the unique directory path
116 QObject::tr(
117 "X!Tandem software not found at %1.\nPlease check the X!Tandem "
118 "installation on your computer and set tandem.exe path.")
119 .arg(tandem_exe.absoluteFilePath()));
120 }
121 if(!tandem_exe.isReadable())
122 {
123 // dir.path() returns the unique directory path
125 QObject::tr("Please check permissions on X!Tandem software found at %1 "
126 "(file not readable).")
127 .arg(tandem_exe.absoluteFilePath()));
128 }
129 if(!tandem_exe.isExecutable())
130 {
131 // dir.path() returns the unique directory path
133 QObject::tr("Please check permissions on X!Tandem software found at %1 "
134 "(file not executable).")
135 .arg(tandem_exe.absoluteFilePath()));
136 }
137
138
139 QString version_return;
140 QStringList arguments;
141
142 arguments << "-v";
143
144 QProcess *xt_process = new QProcess();
145 // hk_process->setWorkingDirectory(QFileInfo(_hardklor_exe).absolutePath());
146
147 xt_process->start(tandem_bin_path, arguments);
148
149 if(!xt_process->waitForStarted())
150 {
152 QObject::tr("X!Tandem %1 process failed to start")
153 .arg(m_tandemVersion));
154 }
155
156 while(xt_process->waitForReadyRead(1000))
157 {
158 }
159 /*
160 if (!xt_process->waitForFinished(_max_xt_time_ms)) {
161 throw pappso::PappsoException(QObject::tr("can't wait for X!Tandem process
162 to finish : timeout at %1").arg(_max_xt_time_ms));
163 }
164 */
165 QByteArray result = xt_process->readAll();
166
167
168 qDebug() << result.constData();
169
170 // X! TANDEM Jackhammer TPP (2013.06.15.1 - LabKey, Insilicos, ISB)
171
172 QRegularExpression parse_version(
173 "(.*) TANDEM ([A-Z,a-z, ]+) \\(([^ ,^\\)]*)(.*)");
174 qDebug() << parse_version;
175 // Pattern patt = Pattern.compile("X! TANDEM [A-Z]+ \\‍((.*)\\‍)",
176 // Pattern.CASE_INSENSITIVE);
177 QRegularExpressionMatch match_parse_version =
178 parse_version.match(result.constData());
179 if(match_parse_version.hasMatch())
180 {
181 version_return = QString("X!Tandem %1 %2")
182 .arg(match_parse_version.captured(2))
183 .arg(match_parse_version.captured(3)); //.join(" ");
184 }
185 else
186 {
188 QObject::tr("This executable %1 may not be a valid X!Tandem software. "
189 "Please check your X!Tandem installation.")
190 .arg(tandem_bin_path));
191 }
192
193 QProcess::ExitStatus Status = xt_process->exitStatus();
194 delete xt_process;
195 if(Status != 0)
196 {
197 // != QProcess::NormalExit
199 QObject::tr("error executing X!Tandem Status != 0 : %1 %2\n%3")
200 .arg(tandem_bin_path)
201 .arg(arguments.join(" ").arg(result.data())));
202 }
203 qDebug();
204 return version_return;
205}
206
207void
209{
210 QString message(m_xtProcess->readAllStandardOutput());
211 mp_monitor->appendText(message);
212
213 if(message.toLower().contains("error"))
214 {
215 throw pappso::XtandemError(message);
216 }
217
219 {
220 m_xtProcess->kill();
221 delete m_xtProcess;
222 m_xtProcess = nullptr;
224 QObject::tr("X!Tandem stopped by the user"));
225 }
226}
227
228void
230{
231 mp_monitor->appendText(m_xtProcess->readAllStandardError());
233 {
234 m_xtProcess->kill();
235 delete m_xtProcess;
236 m_xtProcess = nullptr;
238 QObject::tr("X!Tandem stopped by the user"));
239 }
240}
241
242void
244 const QString &tmp_tandem_output,
245 const QString &final_tandem_output,
246 const QString &original_msdata_file_name)
247{
248 mp_monitor->setStatus(QObject::tr("Rewriting X!Tandem XML result file"));
249
250 WrapTandemResults wrap_output(final_tandem_output, original_msdata_file_name);
251
252 wrap_output.setInputParameters("spectrum, timstof MS2 filters",
254 wrap_output.setInputParameters("spectrum, mzFormat",
255 QString("%1").arg((int)m_mzFormat));
256
258 {
259 wrap_output.setInputParameters("output, spectrum index", "true");
260 }
261 else
262 {
263 }
264
265 if(m_conversionTime != 0)
266 {
267 wrap_output.setInputParameters(
268 "timing, tandemwrapper conversion time (sec)",
269 QString("%1").arg(m_conversionTime / 1000));
270 }
271
272 if(wrap_output.readFile(tmp_tandem_output))
273 {
274 }
275 else
276 {
278 QObject::tr("Error reading %1 X!Tandem output file :\n %2")
279 .arg(tmp_tandem_output)
280 .arg(wrap_output.errorString()));
281 }
282}
283
284void
285TandemWrapperRun::readTandemPresetFile(const QString &tandem_preset_file)
286{
287 // get number of threads and centroid parameters from tandem preset
288
289 XtandemPresetReader preset_handler;
290
291
292 if(preset_handler.readFile(tandem_preset_file))
293 {
294
295 int ideal_number_of_thread = QThread::idealThreadCount();
296 int cpu_number = preset_handler.getNumberOfThreads();
297 qDebug() << " cpu_number=" << cpu_number;
298 // QThreadPool::globalInstance()->setMaxThreadCount(1);
299 if(cpu_number > ideal_number_of_thread)
300 {
301 cpu_number = ideal_number_of_thread;
302 }
303 else
304 {
305 if(cpu_number > 0)
306 {
307 QThreadPool::globalInstance()->setMaxThreadCount(cpu_number);
308
309 qDebug() << " maxThreadCount="
310 << QThreadPool::globalInstance()->maxThreadCount();
311 }
312 }
313
314 QString ms2_filters_str = preset_handler.getMs2FiltersOptions();
315 if(!ms2_filters_str.isEmpty())
316 {
318 std::make_shared<pappso::FilterSuiteString>(ms2_filters_str);
319 }
320 else
321 {
323 std::make_shared<pappso::FilterSuiteString>(
324 "chargeDeconvolution|0.02dalton mzExclusion|0.01dalton");
325 }
326 }
327 else
328 {
330 QObject::tr("Error reading %1 X!Tandem preset file :\n %2")
331 .arg(tandem_preset_file)
332 .arg(preset_handler.errorString()));
333 }
334}
335
336
337void
338TandemWrapperRun::wrapTandemInputFile(const QString &tandem_input_file)
339{
340 // read original tandem input file
341 // store original ms data file name
342 // create new mzXML data file in temporary directory
343 // create new tandem input file based on new mzXML file
344
345
346 QString mzxml_data_file_name =
347 mpa_temporaryDirectory->filePath("msdata.mzxml");
348 QString wrapped_tandem_input =
349 mpa_temporaryDirectory->filePath("input_tandem.xml");
350 QString wrapped_tandem_output =
351 mpa_temporaryDirectory->filePath("output_tandem.xml");
352
353 WrapTandemInput wrap_tandem_input(
354 mzxml_data_file_name, wrapped_tandem_input, wrapped_tandem_output);
355
356
357 if(wrap_tandem_input.readFile(tandem_input_file))
358 {
359 }
360 else
361 {
363 QObject::tr("Error reading %1 X!Tandem input file :\n %2")
364 .arg(tandem_input_file)
365 .arg(wrap_tandem_input.errorString()));
366 }
367
368
369 if(m_tandemBinary.endsWith("tandemng") ||
370 m_tandemBinary.endsWith("tandemng.exe"))
371 {
372 // no wrapper
373 // launch tandem on original file
374 runTandem(tandem_input_file);
375 }
376 else
377 {
378 /*
379 *
380 XtandemInputSaxHandler wrap_input(
381 mzxml_data_file_name, wrapped_tandem_input, wrapped_tandem_output);
382 QFile qfile(tandem_input_file);
383 if(!qfile.exists())
384 {
385 throw pappso::PappsoException(
386 QObject::tr("Tandem input file %1 does not exists")
387 .arg(QFileInfo(tandem_input_file).absoluteFilePath()));
388 }
389 QXmlInputSource xmlInputSource(&qfile);
390 QXmlSimpleReader simplereader;
391 simplereader.setContentHandler(&wrap_input);
392 simplereader.setErrorHandler(&wrap_input);
393
394 if(simplereader.parse(xmlInputSource))
395 {
396 }
397 else
398 {
399 throw pappso::PappsoException(
400 QObject::tr("Error reading %1 X!Tandem input file :\n %2")
401 .arg(tandem_input_file)
402 .arg(wrap_input.errorString()));
403 }
404 */
405 // get number of threads and centroid parameters from tandem preset
407
408
409 // convert to mzXML
410 QString original_msdata_file_name =
411 wrap_tandem_input.getOriginalMsDataFileName();
412 if(convertOrginalMsData2mzXmlData(original_msdata_file_name,
413 mzxml_data_file_name))
414 {
415
416
417 // launch tandem
418 runTandem(wrapped_tandem_input);
419
420 // rewrite tandem result file
422 wrapped_tandem_output,
423 wrap_tandem_input.getOriginalTandemOutputFileName(),
424 original_msdata_file_name);
425 }
426 else
427 {
428 // launch tandem on original file
429 runTandem(tandem_input_file);
430 }
431 }
432}
433
434bool
436 const QString &target)
437{
438 qDebug();
439 pappso::MsFileAccessor origin_access(origin, "runa1");
442 origin_access.getMsRunIds();
443 m_mzFormat = origin_access.getFileFormat();
445 {
447 QObject::tr("%1 file format not known").arg(origin));
448 }
449
451 {
453 }
454
455 if((origin_access.getFileFormat() == pappso::MsDataFormat::mzML) ||
457 {
459 QObject::tr("Converting %1 to mzXML %2").arg(origin).arg(target));
461 p_reader =
462 origin_access.msRunReaderSPtr(origin_access.getMsRunIds().front());
463
464 pappso::TimsMsRunReaderMs2 *tims2_reader =
465 dynamic_cast<pappso::TimsMsRunReaderMs2 *>(p_reader.get());
466 if(tims2_reader != nullptr)
467 {
468 qDebug();
469 tims2_reader->setMs2BuiltinCentroid(true);
470
471 if(msp_ms2FilterSuiteString != nullptr)
472 {
474 }
475 qDebug();
476 }
477
478
479 pappso::MzxmlOutput *p_mzxml_output;
480 QFile output_file(target);
481 // qDebug() << " TsvDirectoryWriter::writeSheet " <<
482 // QFileInfo(*_p_ofile).absoluteFilePath();
483 if(output_file.open(QIODevice::WriteOnly))
484 {
485 QElapsedTimer timer;
487 timer.start();
488 p_mzxml_output = new pappso::MzxmlOutput(
489 *mp_monitor, QTextStream(&output_file).device());
490
491 p_mzxml_output->maskMs1(true);
492
493 p_mzxml_output->setReadAhead(true);
494
495 p_mzxml_output->write(p_reader.get());
496
497 p_mzxml_output->close();
498
499 delete p_mzxml_output;
500 m_conversionTime = timer.elapsed();
501
502 mp_monitor->setStatus(QObject::tr("Conversion finished in %1 seconds")
503 .arg(m_conversionTime / 1000));
504 }
505 else
506 {
508 QObject::tr("unable to write into %1 mzXML output file")
509 .arg(target));
510 }
511
512 qDebug();
513 return true;
514 }
515 else
516 { // other mz data formats
517 return false;
518 }
519 return true;
520}
521
522void
524 const QString &tandem_input_file)
525{
526 mp_monitor = &monitor;
527
528 wrapTandemInputFile(tandem_input_file);
529 mp_monitor = nullptr;
530}
531void
532TandemWrapperRun::runTandem(const QString &tandem_input_file)
533{
535 {
537 QObject::tr("X!Tandem stopped by the user processing on file %1")
538 .arg(tandem_input_file));
539 }
540 m_xtProcess = new QProcess();
541 QStringList arguments;
542
543 qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
544
545 arguments << tandem_input_file;
546 // hk_process->setWorkingDirectory(QFileInfo(_hardklor_exe).absolutePath());
547 m_xtProcess->start(m_tandemBinary, arguments);
548
549 qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
550
551 connect(m_xtProcess,
552 &QProcess::readyReadStandardOutput,
553 this,
555 connect(m_xtProcess,
556 &QProcess::readyReadStandardError,
557 this,
559
560
561 qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
562
563 mp_monitor->setStatus(QObject::tr("Running X!Tandem"));
564
565 if(!m_xtProcess->waitForStarted())
566 {
568 QObject::tr("X!Tandem process failed to start"));
569 }
570
571 qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
572 while(m_xtProcess->waitForFinished(m_maxTandemRunTimeMs) == false)
573 {
574 //_p_monitor->appendText(xt_process->readAll().data());
575 // data.append(xt_process->readAll());
577 {
578 m_xtProcess->kill();
579 delete m_xtProcess;
580 m_xtProcess = nullptr;
582 QObject::tr("X!Tandem stopped by the user processing on file %1")
583 .arg(tandem_input_file));
584 }
585 }
586
587 QProcess::ExitStatus Status = m_xtProcess->exitStatus();
588
589 delete m_xtProcess;
590 if(Status != QProcess::ExitStatus::NormalExit)
591 {
592 // != QProcess::NormalExit
594 QObject::tr("error executing X!Tandem Status != 0 : %1")
595 .arg(m_tandemBinary));
596 }
597 m_xtProcess = nullptr;
598}
599
600QString
602{
603 if(msp_ms2FilterSuiteString == nullptr)
604 return "";
605 return msp_ms2FilterSuiteString.get()->toString();
606}
607
608} // namespace pappso
MsRunReaderSPtr msRunReaderSPtr(MsRunIdCstSPtr ms_run_id)
void setPreferredFileReaderType(MsDataFormat format, FileReaderType reader_type)
given an mz format, explicitly set the preferred reader
MsDataFormat getFileFormat() const
get the raw format of mz data
std::vector< MsRunIdCstSPtr > getMsRunIds()
void setReadAhead(bool read_ahead)
void write(MsRunReader *p_msrunreader)
void maskMs1(bool mask_ms1)
QTemporaryDir * mpa_temporaryDirectory
void run(UiMonitorInterface &monitor, const QString &tandem_input_file)
run a tandem job
void setTandemBinaryPath(const QString &tandem_binary_path)
UiMonitorInterface * mp_monitor
pappso::MsDataFormat m_mzFormat
bool convertOrginalMsData2mzXmlData(const QString &origin, const QString &target)
void readTandemPresetFile(const QString &tandem_preset_file)
std::shared_ptr< FilterSuiteString > msp_ms2FilterSuiteString
void wrapTandemInputFile(const QString &tandem_input_file)
void writeFinalTandemOutput(const QString &tmp_tandem_output, const QString &final_tandem_output, const QString &original_msdata_file_name)
tandem output modification tandem output is modified to contain the Bruker's file as input and centro...
TandemWrapperRun(const QString &tandem_binary, const QString &tmp_dir)
prepare a tandem run
QString getMs2FilterSuiteString() const
gets the list of filters used on MS2 spectrum
void runTandem(const QString &tandem_input_file)
run a tandem job
const QString checkXtandemVersion(const QString &tandem_bin_path)
void setMs2FilterCstSPtr(pappso::FilterInterfaceCstSPtr filter)
void setMs2BuiltinCentroid(bool centroid)
enable or disable simple centroid filter on raw tims data for MS2
virtual void setStatus(const QString &status)=0
current status of the process
virtual void appendText(const QString &text)=0
append a text to a long report
virtual bool shouldIstop()=0
should the procces be stopped ? If true, then cancel process Use this function at strategic point of ...
const QString & getOriginalTandemOutputFileName() const
const QString & getOriginalTandemPresetFileName() const
const QString & getOriginalMsDataFileName() const
void setInputParameters(const QString &label_name_attribute, const QString &input_value)
virtual bool readFile(const QString &fileName)
const QString getMs2FiltersOptions() const
tries to keep as much as possible monoisotopes, removing any possible C13 peaks and changes multichar...
Definition aa.cpp:39
std::shared_ptr< MsRunReader > MsRunReaderSPtr
Definition msrunreader.h:56
@ unknown
unknown format
actually does really run tandem directly on Bruker's data
rewrites tandem xml input file with temporary files
rewrites tandem xml output file with temporary files
read tandem preset file to get centroid parameters and number of threads