libpappsomspp
Library for mass spectrometry
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 "../../exception/exceptioninterrupted.h"
35 #include "../../msfile/msfileaccessor.h"
36 #include "../../msrun/private/timsmsrunreaderms2.h"
37 #include "../../processing/filters/filtertriangle.h"
38 #include "../../processing/filters/filterchargedeconvolution.h"
39 #include "../../msrun/output/mzxmloutput.h"
40 #include "wraptandemresults.h"
41 #include "xtandempresetreader.h"
42 #include "wraptandeminput.h"
43 
44 namespace pappso
45 {
46 
47 
48 TandemWrapperRun::TandemWrapperRun(const QString &tandem_binary,
49  const QString &tmp_dir)
50 {
51 
52  setTandemBinaryPath(tandem_binary);
53 
54  if(!tmp_dir.isEmpty())
55  {
56  mpa_temporaryDirectory = new QTemporaryDir(tmp_dir + "/xtpwrp");
57  }
58  else
59  {
60  mpa_temporaryDirectory = new QTemporaryDir(QDir::tempPath() + "/xtpwrp");
61  }
62  mpa_temporaryDirectory->setAutoRemove(true);
63  if(!mpa_temporaryDirectory->isValid())
64  {
66  QObject::tr("ERROR: unable to create temporary directory %1\n Please "
67  "check file system permissions")
68  .arg(mpa_temporaryDirectory->path()));
69  }
70 }
71 
73 {
74  if(mpa_temporaryDirectory != nullptr)
75  {
77  }
78 
79  if(m_xtProcess != nullptr)
80  {
81  m_xtProcess->deleteLater();
82  }
83 }
84 
85 void
86 TandemWrapperRun::setTandemBinaryPath(const QString &tandem_binary_path)
87 {
88 
89 
90  m_tandemBinary = tandem_binary_path;
91  QSettings settings;
92  if(m_tandemBinary.isEmpty())
93  {
95  settings.value("path/tandem_binary", "/usr/bin/tandem").toString();
96  }
97  // check for tandem executable
99 
100  qDebug() << m_tandemVersion;
101  settings.setValue("path/tandem_binary", m_tandemBinary);
102 }
103 
104 
105 const QString
106 TandemWrapperRun::checkXtandemVersion(const QString &tandem_bin_path)
107 {
108  qDebug();
109  // check tandem path
110  QFileInfo tandem_exe(tandem_bin_path);
111  if(!tandem_exe.exists())
112  {
113  // dir.path() returns the unique directory path
115  QObject::tr(
116  "X!Tandem software not found at %1.\nPlease check the X!Tandem "
117  "installation on your computer and set tandem.exe path.")
118  .arg(tandem_exe.absoluteFilePath()));
119  }
120  if(!tandem_exe.isReadable())
121  {
122  // dir.path() returns the unique directory path
124  QObject::tr("Please check permissions on X!Tandem software found at %1 "
125  "(file not readable).")
126  .arg(tandem_exe.absoluteFilePath()));
127  }
128  if(!tandem_exe.isExecutable())
129  {
130  // dir.path() returns the unique directory path
132  QObject::tr("Please check permissions on X!Tandem software found at %1 "
133  "(file not executable).")
134  .arg(tandem_exe.absoluteFilePath()));
135  }
136 
137 
138  QString version_return;
139  QStringList arguments;
140 
141  arguments << "-v";
142 
143  QProcess *xt_process = new QProcess();
144  // hk_process->setWorkingDirectory(QFileInfo(_hardklor_exe).absolutePath());
145 
146  xt_process->start(tandem_bin_path, arguments);
147 
148  if(!xt_process->waitForStarted())
149  {
151  QObject::tr("X!Tandem %1 process failed to start")
152  .arg(m_tandemVersion));
153  }
154 
155  while(xt_process->waitForReadyRead(1000))
156  {
157  }
158  /*
159  if (!xt_process->waitForFinished(_max_xt_time_ms)) {
160  throw pappso::PappsoException(QObject::tr("can't wait for X!Tandem process
161  to finish : timeout at %1").arg(_max_xt_time_ms));
162  }
163  */
164  QByteArray result = xt_process->readAll();
165 
166 
167  qDebug() << result.constData();
168 
169  // X! TANDEM Jackhammer TPP (2013.06.15.1 - LabKey, Insilicos, ISB)
170 
171  QRegExp parse_version("(.*) TANDEM ([A-Z,a-z, ]+) \\(([^ ,^\\)]*)(.*)");
172  qDebug() << parse_version;
173  // Pattern patt = Pattern.compile("X! TANDEM [A-Z]+ \\‍((.*)\\‍)",
174  // Pattern.CASE_INSENSITIVE);
175 
176  if(parse_version.exactMatch(result.constData()))
177  {
178  version_return = QString("X!Tandem %1 %2")
179  .arg(parse_version.capturedTexts()[2])
180  .arg(parse_version.capturedTexts()[3]); //.join(" ");
181  }
182  else
183  {
185  QObject::tr("This executable %1 may not be a valid X!Tandem software. "
186  "Please check your X!Tandem installation.")
187  .arg(tandem_bin_path));
188  }
189 
190  QProcess::ExitStatus Status = xt_process->exitStatus();
191  delete xt_process;
192  if(Status != 0)
193  {
194  // != QProcess::NormalExit
196  QObject::tr("error executing X!Tandem Status != 0 : %1 %2\n%3")
197  .arg(tandem_bin_path)
198  .arg(arguments.join(" ").arg(result.data())));
199  }
200  qDebug();
201  return version_return;
202 }
203 
204 void
206 {
207  QString message(m_xtProcess->readAllStandardOutput());
208  mp_monitor->appendText(message);
209 
210  if(message.toLower().contains("error"))
211  {
212  throw pappso::XtandemError(message);
213  }
214 
215  if(mp_monitor->shouldIstop())
216  {
217  m_xtProcess->kill();
218  delete m_xtProcess;
219  m_xtProcess = nullptr;
221  QObject::tr("X!Tandem stopped by the user"));
222  }
223 }
224 
225 void
227 {
228  mp_monitor->appendText(m_xtProcess->readAllStandardError());
229  if(mp_monitor->shouldIstop())
230  {
231  m_xtProcess->kill();
232  delete m_xtProcess;
233  m_xtProcess = nullptr;
235  QObject::tr("X!Tandem stopped by the user"));
236  }
237 }
238 
239 void
241  const QString &tmp_tandem_output,
242  const QString &final_tandem_output,
243  const QString &original_msdata_file_name)
244 {
245  mp_monitor->setStatus(QObject::tr("Rewriting X!Tandem XML result file"));
246 
247  WrapTandemResults wrap_output(final_tandem_output, original_msdata_file_name);
248 
249  wrap_output.setInputParameters("spectrum, timstof MS2 filters",
251  wrap_output.setInputParameters("spectrum, mzFormat",
252  QString("%1").arg((int)m_mzFormat));
253 
255  {
256  wrap_output.setInputParameters("output, spectrum index", "true");
257  }
258  else
259  {
260  }
261 
262  if(wrap_output.readFile(tmp_tandem_output))
263  {
264  }
265  else
266  {
268  QObject::tr("Error reading %1 X!Tandem output file :\n %2")
269  .arg(tmp_tandem_output)
270  .arg(wrap_output.errorString()));
271  }
272 }
273 
274 void
275 TandemWrapperRun::readTandemPresetFile(const QString &tandem_preset_file)
276 {
277  // get number of threads and centroid parameters from tandem preset
278 
279  XtandemPresetReader preset_handler;
280 
281 
282  if(preset_handler.readFile(tandem_preset_file))
283  {
284 
285  int ideal_number_of_thread = QThread::idealThreadCount();
286  int cpu_number = preset_handler.getNumberOfThreads();
287  qDebug() << " cpu_number=" << cpu_number;
288  // QThreadPool::globalInstance()->setMaxThreadCount(1);
289  if(cpu_number > ideal_number_of_thread)
290  {
291  cpu_number = ideal_number_of_thread;
292  }
293  else
294  {
295  if(cpu_number > 0)
296  {
297  QThreadPool::globalInstance()->setMaxThreadCount(cpu_number);
298 
299  qDebug() << " maxThreadCount="
300  << QThreadPool::globalInstance()->maxThreadCount();
301  }
302  }
303 
304  QString ms2_filters_str = preset_handler.getMs2FiltersOptions();
305  if(!ms2_filters_str.isEmpty())
306  {
308  std::make_shared<pappso::FilterSuiteString>(ms2_filters_str);
309  }
310  else
311  {
313  std::make_shared<pappso::FilterSuiteString>(
314  "chargeDeconvolution|0.02dalton mzExclusion|0.01dalton");
315  }
316  }
317  else
318  {
320  QObject::tr("Error reading %1 X!Tandem preset file :\n %2")
321  .arg(tandem_preset_file)
322  .arg(preset_handler.errorString()));
323  }
324 }
325 
326 
327 void
328 TandemWrapperRun::wrapTandemInputFile(const QString &tandem_input_file)
329 {
330  // read original tandem input file
331  // store original ms data file name
332  // create new mzXML data file in temporary directory
333  // create new tandem input file based on new mzXML file
334 
335 
336  QString mzxml_data_file_name =
337  mpa_temporaryDirectory->filePath("msdata.mzxml");
338  QString wrapped_tandem_input =
339  mpa_temporaryDirectory->filePath("input_tandem.xml");
340  QString wrapped_tandem_output =
341  mpa_temporaryDirectory->filePath("output_tandem.xml");
342 
343  WrapTandemInput wrap_tandem_input(
344  mzxml_data_file_name, wrapped_tandem_input, wrapped_tandem_output);
345 
346 
347  if(wrap_tandem_input.readFile(tandem_input_file))
348  {
349  }
350  else
351  {
353  QObject::tr("Error reading %1 X!Tandem input file :\n %2")
354  .arg(tandem_input_file)
355  .arg(wrap_tandem_input.errorString()));
356  }
357 
358 
359  /*
360  *
361  XtandemInputSaxHandler wrap_input(
362  mzxml_data_file_name, wrapped_tandem_input, wrapped_tandem_output);
363  QFile qfile(tandem_input_file);
364  if(!qfile.exists())
365  {
366  throw pappso::PappsoException(
367  QObject::tr("Tandem input file %1 does not exists")
368  .arg(QFileInfo(tandem_input_file).absoluteFilePath()));
369  }
370  QXmlInputSource xmlInputSource(&qfile);
371  QXmlSimpleReader simplereader;
372  simplereader.setContentHandler(&wrap_input);
373  simplereader.setErrorHandler(&wrap_input);
374 
375  if(simplereader.parse(xmlInputSource))
376  {
377  }
378  else
379  {
380  throw pappso::PappsoException(
381  QObject::tr("Error reading %1 X!Tandem input file :\n %2")
382  .arg(tandem_input_file)
383  .arg(wrap_input.errorString()));
384  }
385 */
386  // get number of threads and centroid parameters from tandem preset
388 
389 
390  // convert to mzXML
391  QString original_msdata_file_name =
392  wrap_tandem_input.getOriginalMsDataFileName();
393  if(convertOrginalMsData2mzXmlData(original_msdata_file_name,
394  mzxml_data_file_name))
395  {
396 
397 
398  // launch tandem
399  runTandem(wrapped_tandem_input);
400 
401  // rewrite tandem result file
403  wrapped_tandem_output,
404  wrap_tandem_input.getOriginalTandemOutputFileName(),
405  original_msdata_file_name);
406  }
407  else
408  {
409  // launch tandem on original file
410  runTandem(tandem_input_file);
411  }
412 }
413 
414 bool
416  const QString &target)
417 {
418  qDebug();
419  pappso::MsFileAccessor origin_access(origin, "runa1");
422  origin_access.getMsRunIds();
423  m_mzFormat = origin_access.getFileFormat();
424 
425  if(origin_access.getFileFormat() == pappso::MzFormat::brukerTims)
426  {
428  }
429 
430  if((origin_access.getFileFormat() == pappso::MzFormat::mzML) ||
431  (origin_access.getFileFormat() == pappso::MzFormat::brukerTims))
432  {
434  QObject::tr("Converting %1 to mzXML %2").arg(origin).arg(target));
435  pappso::MsRunReaderSPtr p_reader;
436  p_reader =
437  origin_access.msRunReaderSp(origin_access.getMsRunIds().front());
438 
439  pappso::TimsMsRunReaderMs2 *tims2_reader =
440  dynamic_cast<pappso::TimsMsRunReaderMs2 *>(p_reader.get());
441  if(tims2_reader != nullptr)
442  {
443  qDebug();
444  tims2_reader->setMs2BuiltinCentroid(true);
445 
446  if(msp_ms2FilterSuiteString != nullptr)
447  {
449  }
450  qDebug();
451  }
452 
453 
454  pappso::MzxmlOutput *p_mzxml_output;
455  QFile output_file(target);
456  // qDebug() << " TsvDirectoryWriter::writeSheet " <<
457  // QFileInfo(*_p_ofile).absoluteFilePath();
458  if(output_file.open(QIODevice::WriteOnly))
459  {
460  p_mzxml_output = new pappso::MzxmlOutput(
461  *mp_monitor, QTextStream(&output_file).device());
462 
463  p_mzxml_output->maskMs1(true);
464 
465  p_mzxml_output->setReadAhead(true);
466 
467  p_mzxml_output->write(p_reader.get());
468 
469  p_mzxml_output->close();
470 
471  delete p_mzxml_output;
472  }
473  else
474  {
476  QObject::tr("unable to write into %1 mzXML output file")
477  .arg(target));
478  }
479 
480  qDebug();
481  return true;
482  }
483  else
484  { // other mz data formats
485  return false;
486  }
487  return true;
488 }
489 
490 void
492  const QString &tandem_input_file)
493 {
494  mp_monitor = &monitor;
495 
496  wrapTandemInputFile(tandem_input_file);
497  mp_monitor = nullptr;
498 }
499 void
500 TandemWrapperRun::runTandem(const QString &tandem_input_file)
501 {
502  if(mp_monitor->shouldIstop())
503  {
505  QObject::tr("X!Tandem stopped by the user processing on file %1")
506  .arg(tandem_input_file));
507  }
508  m_xtProcess = new QProcess();
509  QStringList arguments;
510 
511  qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
512 
513  arguments << tandem_input_file;
514  // hk_process->setWorkingDirectory(QFileInfo(_hardklor_exe).absolutePath());
515  m_xtProcess->start(m_tandemBinary, arguments);
516 
517  qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
518 
519  connect(m_xtProcess,
520  &QProcess::readyReadStandardOutput,
521  this,
523  connect(m_xtProcess,
524  &QProcess::readyReadStandardError,
525  this,
527 
528 
529  qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
530 
531  mp_monitor->setStatus(QObject::tr("Running X!Tandem"));
532 
533  if(!m_xtProcess->waitForStarted())
534  {
536  QObject::tr("X!Tandem process failed to start"));
537  }
538 
539  qDebug() << m_tandemBinary << " " << m_xtProcess->arguments();
540  while(m_xtProcess->waitForFinished(m_maxTandemRunTimeMs) == false)
541  {
542  //_p_monitor->appendText(xt_process->readAll().data());
543  // data.append(xt_process->readAll());
544  if(mp_monitor->shouldIstop())
545  {
546  m_xtProcess->kill();
547  delete m_xtProcess;
548  m_xtProcess = nullptr;
550  QObject::tr("X!Tandem stopped by the user processing on file %1")
551  .arg(tandem_input_file));
552  }
553  }
554 
555  QProcess::ExitStatus Status = m_xtProcess->exitStatus();
556 
557  delete m_xtProcess;
558  if(Status != QProcess::ExitStatus::NormalExit)
559  {
560  // != QProcess::NormalExit
562  QObject::tr("error executing X!Tandem Status != 0 : %1")
563  .arg(m_tandemBinary));
564  }
565  m_xtProcess = nullptr;
566 }
567 
568 QString
570 {
571  if(msp_ms2FilterSuiteString == nullptr)
572  return "";
573  return msp_ms2FilterSuiteString.get()->toString();
574 }
575 
576 } // namespace pappso
MzFormat getFileFormat() const
get the raw format of mz data
MsRunReaderSPtr msRunReaderSp(MsRunIdCstSPtr ms_run_id)
void setPreferedFileReaderType(MzFormat format, FileReaderType reader_type)
given an mz format, explicitly set the prefered reader
std::vector< MsRunIdCstSPtr > getMsRunIds()
void setReadAhead(bool read_ahead)
Definition: mzxmloutput.cpp:93
void write(MsRunReader *p_msrunreader)
Definition: mzxmloutput.cpp:98
void maskMs1(bool mask_ms1)
QTemporaryDir * mpa_temporaryDirectory
pappso::MzFormat m_mzFormat
void run(UiMonitorInterface &monitor, const QString &tandem_input_file)
run a tandem job
void setTandemBinaryPath(const QString &tandem_binary_path)
UiMonitorInterface * mp_monitor
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:184
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