// // $Id$ // // // Original author: Jarrett Egertson // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "pwiz/data/msdata/MSDataFile.hpp" #include "pwiz/data/msdata/Serializer_mzML.hpp" #include "pwiz/data/msdata/Diff.hpp" #include "pwiz/utility/misc/unit.hpp" #include "pwiz/utility/misc/Std.hpp" #include "pwiz_tools/common/FullReaderList.hpp" #include #include #include #include #include #include #include #include #include #define PRISM_VERSION "0.3" using namespace pwiz::data; using namespace pwiz::msdata; using namespace pwiz::util; using namespace pwiz::analysis; namespace po = boost::program_options; struct Optimization { Optimization(const string& s) : value(s) {} string value; }; // This overload is needed for using default_value when building a variable_map. The default_value function relies on boost::lexical_cast, which relies on this operator. ostream& operator <<(ostream& stream, const Optimization& opt) { return stream << opt.value; } /// Overload the 'validate' function for the user-defined Optimization option class. void validate(boost::any& v, const vector& values, Optimization*, int) { // Make sure no previous assignment to 'a' was made. po::validators::check_first_occurrence(v); // Extract the first string from 'values'. If there is more than // one string, it's an error, and exception will be thrown. const string& s = po::validators::get_single_string(values); // Try conversion to desired enum type try { SpectrumList_Demux::Params::stringToOptimization(s); v = boost::any(Optimization(s)); } catch (EnumConstantNotPresentException&) { throw po::invalid_option_value(s); } } class IterationListenerStream : public IterationListener { public: explicit IterationListenerStream(ostream &os = std::cerr) : os_(os), entfmt_("%5.1f"){} Status update(const UpdateMessage& updateMessage) override { auto percentProgress = 100.0 * float(updateMessage.iterationIndex) / float(updateMessage.iterationCount); os_ << "\rDemultiplexing: " << entfmt_ % percentProgress << "% (Spectrum " << updateMessage.iterationIndex << " of " << updateMessage.iterationCount << ") "; return Status_Ok; } private: ostream &os_; boost::format entfmt_; }; po::variables_map parseCommandLine(int argc, char **argv); void run(int argc, char ** argv) { // The Eigen library must be initialized to support multithreaded operations before creating the threads Eigen::initParallel(); auto start = boost::chrono::system_clock::now(); std::cerr << "Welcome to Prism v" << PRISM_VERSION << endl; auto vm = parseCommandLine(argc, argv); SpectrumList_Demux::Params demuxParams; demuxParams.massError = vm["massError"].as(); demuxParams.nnlsMaxIter = vm["nnlsMaxIter"].as(); demuxParams.nnlsEps = vm["nnlsEps"].as(); demuxParams.applyWeighting = !vm["noWeighting"].as(); demuxParams.demuxBlockExtra = vm["demuxBlockExtra"].as(); demuxParams.variableFill = vm["variableFill"].as(); demuxParams.regularizeSums = !vm["noSumNormalize"].as(); demuxParams.optimization = SpectrumList_Demux::Params::stringToOptimization(vm["optimization"].as().value); demuxParams.padScanTimes = vm["padScanTimes"].as(); demuxParams.interpolateRetentionTime = vm["interpolateRT"].as(); bool skipCentroiding = vm["skipCentroiding"].as(); FullReaderList readers; MSDataFile msd(vm["inputfile"].as(), &readers); IntegerSet levelsToCentroid(1,2); // Bypass centroiding SpectrumListPtr toDemuxPtr = msd.run.spectrumListPtr; if (!skipCentroiding) { SpectrumListPtr tempPtr = toDemuxPtr; toDemuxPtr.reset(new SpectrumList_PeakPicker(tempPtr, PeakDetectorPtr(boost::make_shared(3)), true, levelsToCentroid)); msd.filterApplied(); } SpectrumListPtr demux_list(new SpectrumList_Demux(toDemuxPtr, demuxParams)); msd.filterApplied(); msd.run.spectrumListPtr = demux_list; // set up progress indicator IterationListenerPtr listenerPtr(new IterationListenerStream(std::cerr)); IterationListenerRegistry listenerRegistry; listenerRegistry.addListener(listenerPtr, 10); MSDataFile::WriteConfig writeConfig; MSDataFile::write(msd, vm["outputfile"].as(), writeConfig, &listenerRegistry); std::cerr << endl; boost::chrono::duration sec = boost::chrono::system_clock::now() - start; std::cout << "Demultiplexing took " << sec.count() / 60.0 << " minutes." << std::endl; } po::variables_map parseCommandLine(int argc, char **argv) { po::options_description desc("PRISM arguments"); // Get default values SpectrumList_Demux::Params demuxParams; Optimization defaultOptimization(SpectrumList_Demux::Params::optimizationToString(demuxParams.optimization)); bool skipCentroiding = false; desc.add_options() ("help,h", "Produce this help message.") ("massError", po::value()->default_value(demuxParams.massError), "MS/MS error between spectra. Can specify number and units of either ppm or da, e.g. 10ppm or 0.4da. Default 10ppm.") ("variableFill", po::bool_switch()->default_value(demuxParams.variableFill), "MSX data was acquired with variable fill times.") ("demuxBlockExtra", po::value()->default_value(demuxParams.demuxBlockExtra), "Extra slop to include in the set of spectra used for demux. Number of spectra added is input * num spectra in one cycle") ("nnlsMaxIter", po::value()->default_value(demuxParams.nnlsMaxIter), "Maximum number of iterations for demux solve.") ("nnlsEps", po::value()->default_value(demuxParams.nnlsEps), "Epsilon value for testing for convergence of demux solve.") ("noSumNormalize", po::bool_switch()->default_value(!demuxParams.regularizeSums), "Do not normalize the raw demux output for a spectrum so it sums up to the multiplexed peak intensity") ("noWeighting", po::bool_switch()->default_value(!demuxParams.applyWeighting), "Use non-weighted non negative least squares") ("optimization", po::value()->default_value(defaultOptimization), "Use optimizations. Available optimizations are \"none\", and \"overlap_only\"" ) ("padScanTimes", po::bool_switch()->default_value(demuxParams.padScanTimes), "Pad scan times with small increment to prevent simultaneous spectra (which some software cannot handle)") ("interpolateRT", po::bool_switch()->default_value(demuxParams.interpolateRetentionTime), "Interpolate retention times. This only applies when overlap demultiplexing with no MSX (overlap only)") ("skipCentroiding", po::bool_switch()->default_value(skipCentroiding), "Set to true to use profile data if available") ("inputfile", po::value()->required(), "Input spectra file") ("outputfile", po::value()->required(), "Output spectra file"); po::positional_options_description p; p.add("inputfile", 1); p.add("outputfile", 1); po::variables_map vm; po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm); if (vm.count("help")) { std::cout << desc << endl; } notify(vm); return vm; } int main(int argc, char **argv) { try { run(argc, argv); return 0; } catch (std::exception& e) { std::cerr << e.what() << endl; } catch (...) { std::cerr << "Caught unknown exception.\n"; } return 1; }