/*************************************************************************** musrview.cpp Author: Andreas Suter e-mail: andreas.suter@psi.ch ***************************************************************************/ /*************************************************************************** * Copyright (C) 2007-2026 by Andreas Suter * * andreas.suter@psi.ch * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_GIT_REV_H #include "git-revision.h" #endif #include "PMusr.h" #include "PMsgBox.h" #include "PMsrHandler.h" #include "PMusrWebCanvas.h" #include "PRunDataHandler.h" #include "PRunListCollection.h" #include "PStartupHandler.h" //-------------------------------------------------------------------------- /** *

Sends the usage description to the standard output. */ void musrview_syntax() { std::cout << std::endl << "usage: musrview [Options]"; std::cout << std::endl << " : msr/mlog input file"; std::cout << std::endl << " Options:"; std::cout << std::endl << " --help : display this help and exit."; std::cout << std::endl << " --version : output version information and exit."; std::cout << std::endl << " --show-dynamic-path : dumps the dynamic search paths and exit."; std::cout << std::endl << " -f, --fourier: will directly present the Fourier transform of the ."; std::cout << std::endl << " -a, --avg: will directly present the averaged data/Fourier of the ."; std::cout << std::endl << " -1, --one_to_one: calculate theory points only at data points."; std::cout << std::endl << " display options (musrview is web based, see the musrfit documentation):"; std::cout << std::endl << " --embedded : show the plots in an embedded browser window, i.e. musrview"; std::cout << std::endl << " behaves like a stand-alone application. This is the default"; std::cout << std::endl << " as long as ROOT provides the Qt6 web display."; std::cout << std::endl << " --browser : show the plots in the standard web browser of the system."; std::cout << std::endl << " --browser= : use a specific display, e.g. chrome, firefox, qt6, server."; std::cout << std::endl << " 'server' only prints the URL and starts no browser at all,"; std::cout << std::endl << " which is what to use when musrfit runs on a remote machine."; std::cout << std::endl << " --: "; std::cout << std::endl << " will produce a graphics-output-file without opening a window."; std::cout << std::endl << " the name is based on the , e.g. 3310.msr -> 3310_0.png"; std::cout << std::endl << " supported graphic-format-extension:"; std::cout << std::endl << " eps, pdf, gif, jpg, png, svg, xpm, root"; std::cout << std::endl << " example: musrview 3310.msr --png, will produce a files 3310_X.png"; std::cout << std::endl << " where 'X' stands for the plot number (starting form 0)"; std::cout << std::endl << " --ascii: "; std::cout << std::endl << " will produce an ascii dump of the data and fit as plotted."; std::cout << std::endl << " --timeout : given in seconds after which musrview terminates."; std::cout << std::endl << " If <= 0, no timeout will take place. Default is 0."; std::cout << std::endl << " -s, --show-errMsgBox: if this tag is defined, error message boxes are shown,"; std::cout << std::endl << " rather than only stderr output."; std::cout << std::endl << std::endl; } //-------------------------------------------------------------------------- /** *

Shows an error message in a message box. * * \param errMsg the message to be shown */ void musrview_error_msg(std::string errMsg) { int argc=0; char **argv=nullptr; TApplication app("musrviewErrorMsg", &argc, argv); new PMsgBox(errMsg, gClient->GetRoot(), 600, 200); app.Run(); } //-------------------------------------------------------------------------- /** *

Decides which web display musrview should use. * *

Unless the user asked for something specific, the embedded Qt6 WebEngine * window is preferred, since it makes musrview appear as the stand-alone * application it has always been. If ROOT was built without the Qt6 web * display, the standard browser of the system is used instead. * * \param requested what the user asked for, empty if nothing was requested * * \return the display string to be handed to ROOT::RWebWindow::Show() */ std::string musrview_select_display(const std::string &requested) { if (!requested.empty()) return requested; if (const char *env = gSystem->Getenv("MUSRVIEW_BROWSER")) if (strlen(env) > 0) return std::string(env); // prefer the embedded window if ROOT provides it gSystem->RedirectOutput("/dev/null", "a"); const Int_t loaded = gSystem->Load("libROOTQt6WebDisplay"); gSystem->RedirectOutput(nullptr); if (loaded >= 0) return "qt6"; std::cout << std::endl << ">> musrview **INFO** ROOT has no Qt6 web display, using the standard browser."; std::cout << std::endl << ">> Rebuild ROOT with -Dqt6web=ON for an embedded window." << std::endl; return std::string(); } //-------------------------------------------------------------------------- /** *

The musrview program is used to show muSR fit results in graphical form. * From it also Fourier transforms, difference between data an theory can be formed. * *

Since version 1.13 musrview is based on the ROOT7 web graphics: the plots * are rendered by JSROOT either in an embedded browser window or in the * standard browser of the system, while the C++ side only holds the data. * * return: * - PMUSR_SUCCESS if everthing went smooth * - PMUSR_WRONG_STARTUP_SYNTAX if syntax error is encountered * - line number if an error in the msr-file was encountered which cannot be handled. * * \param argc number of input arguments * \param argv list of input arguments */ int main(int argc, char *argv[]) { int result{PMUSR_SUCCESS}; bool show_syntax{false}; int status; bool success{true}; std::string fileName{}; bool fourier{false}; bool avg{false}; bool theoAtData{false}; // theory points only at data points bool graphicsOutput{false}; bool asciiOutput{false}; std::string graphicsExtension{}; std::string display{}; int timeout{0}; bool show_errMsgBox{false}; std::stringstream errMsg; // add default shared library path /usr/local/lib if not already persent const char *dsp = gSystem->GetDynamicPath(); if (strstr(dsp, "/usr/local/lib") == nullptr) gSystem->AddDynamicPath("/usr/local/lib"); // check input arguments if (argc == 1) { musrview_syntax(); return PMUSR_SYNTAX_REQUEST; } for (int i=1; iGetDynamicPath() << "'" << std::endl << std::endl; return PMUSR_SUCCESS; } else if (!strcmp(argv[i], "--help")) { show_syntax = true; break; } else if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--fourier")) { fourier = true; } else if (!strcmp(argv[i], "-a") || !strcmp(argv[i], "--avg")) { avg = true; } else if (!strcmp(argv[i], "-1") || !strcmp(argv[i], "--one_to_one")) { theoAtData = true; } else if (!strcmp(argv[i], "--embedded")) { display = "qt6"; } else if (!strcmp(argv[i], "--browser")) { display = "default"; } else if (!strncmp(argv[i], "--browser=", 10)) { display = argv[i]+10; if (display.empty()) { show_syntax = true; break; } } else if (!strcmp(argv[i], "--eps") || !strcmp(argv[i], "--pdf") || !strcmp(argv[i], "--gif") || !strcmp(argv[i], "--jpg") || !strcmp(argv[i], "--png") || !strcmp(argv[i], "--svg") || !strcmp(argv[i], "--xpm") || !strcmp(argv[i], "--root")) { graphicsOutput = true; graphicsExtension = argv[i]+2; } else if (!strcmp(argv[i], "--ascii")) { asciiOutput = true; } else if (!strcmp(argv[i], "--timeout")) { if (i+1 < argc) { TString str(argv[i+1]); if (str.IsFloat()) { timeout = str.Atoi(); } else { show_syntax = true; break; } i++; } else { show_syntax = true; break; } } else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--show-errMsgBox")) { show_errMsgBox = true; } else { show_syntax = true; break; } } if (show_syntax) { musrview_syntax(); return PMUSR_WRONG_STARTUP_SYNTAX; } const bool batchMode = graphicsOutput || asciiOutput; // read startup file std::string startup_path_name{}; std::unique_ptr saxParser = std::make_unique(); std::unique_ptr startupHandler = std::make_unique(); if (!startupHandler->StartupFileFound()) { std::cerr << std::endl << ">> musrview **WARNING** couldn't find " << startupHandler->GetStartupFilePath().Data(); std::cerr << std::endl; } else { startup_path_name = startupHandler->GetStartupFilePath().Data(); saxParser->ConnectToHandler("PStartupHandler", startupHandler.get()); // parsing the file via ParseFile() seems to lead to problems in certain // environments; use parseXmlFile (see PStartupHandler.cpp) instead status = parseXmlFile(saxParser.get(), startup_path_name.c_str()); if (status) { // error std::cerr << std::endl << ">> musrview **WARNING** Reading/parsing musrfit_startup.xml failed."; std::cerr << std::endl << ">> Any graph will appear with random symbols and colors!"; std::cerr << std::endl; } else { startupHandler->CheckLists(); } } // read msr-file std::unique_ptr msrHandler = std::make_unique(fileName.c_str()); status = msrHandler->ReadMsrFile(); if (status != PMUSR_SUCCESS) { errMsg << msrHandler->GetLastErrorMsg(); switch (status) { case PMUSR_MSR_FILE_NOT_FOUND: errMsg << "\n"; errMsg << ">> musrview **ERROR** couldn't find '" << fileName << "'\n\n"; break; case PMUSR_MSR_SYNTAX_ERROR: errMsg << "\n"; errMsg << ">> musrview **SYNTAX ERROR** in file " << fileName << ", full stop here.\n\n"; break; default: errMsg << "\n"; errMsg << ">> musrview **UNKNOWN ERROR** when trying to read the msr-file.\n\n"; break; } std::cerr << errMsg.str(); if (show_errMsgBox) musrview_error_msg(errMsg.str()); return status; } // make a plot list vector PMsrPlotList *msrPlotList = msrHandler->GetMsrPlotList(); PIntVector plotList; bool runPresent; for (unsigned int i=0; isize(); i++) { for (unsigned int j=0; jat(i).fRuns.size(); j++) { runPresent = false; for (unsigned int k=0; kat(i).fRuns[j] == static_cast(plotList[k])) { runPresent = true; break; } } if (!runPresent) plotList.push_back(static_cast(msrPlotList->at(i).fRuns[j])); } } // read all the necessary runs (raw data) std::unique_ptr dataHandler; if (startupHandler) dataHandler = std::make_unique(msrHandler.get(), startupHandler->GetDataPathList()); else dataHandler = std::make_unique(msrHandler.get()); dataHandler->ReadData(); success = dataHandler->IsAllDataAvailable(); if (!success) { errMsg << ">> musrview **ERROR** Couldn't read all data files, will quit ...\n"; std::cerr << errMsg.str(); if (show_errMsgBox) musrview_error_msg(errMsg.str()); result = PMUSR_DATA_FILE_READ_ERROR; } // generate the necessary histogramms for the view std::unique_ptr runListCollection; if (result == PMUSR_SUCCESS) { runListCollection = std::make_unique(msrHandler.get(), dataHandler.get(), theoAtData); for (unsigned int i=0; iGetMsrRunList()->size(); i++) { runPresent = false; for (unsigned int j=0; j(plotList[j]) == i+1) { runPresent = true; break; } } if (runPresent) { success = runListCollection->Add(i, kView); if (!success) { errMsg << ">> musrview **ERROR** Couldn't handle run no " << i << " "; errMsg << (*msrHandler->GetMsrRunList())[i].GetRunName()->Data() << "\n"; std::cerr << errMsg.str(); if (show_errMsgBox) musrview_error_msg(errMsg.str()); result = PMUSR_MSR_RUN_ERROR; break; } } } } if (result != PMUSR_SUCCESS) { plotList.clear(); return result; } // A ROOT application is needed so that the web display and the graphics // export find a working environment. // // In the interactive case no TCanvas is created at all - the spectra travel // to JSROOT as a list of drawables - hence ROOT never opens a window of its // own. Only the graphics export builds a TCanvas, and that runs in batch. std::vector args; args.push_back(argv[0]); if (batchMode) args.push_back(const_cast("-b")); int cc = static_cast(args.size()); TApplication app("musrview", &cc, args.data()); std::vector> canvasList; bool ok = true; for (unsigned int i=0; iGetMsrPlotList()->size(); i++) { auto canvas = std::make_unique( static_cast(i), msrHandler->GetMsrTitle()->Data(), 1200, 900, startupHandler ? startupHandler->GetFourierDefaults() : PMsrFourierStructure{}, startupHandler ? startupHandler->GetMarkerList() : PIntVector{}, startupHandler ? startupHandler->GetColorList() : PIntVector{}, batchMode, fourier, avg, theoAtData); // ugly but rootcling cannot handle the spirit-parser framework canvas->SetMsrHandler(msrHandler.get()); canvas->SetRunListCollection(runListCollection.get()); if (!canvas->Build()) { errMsg << ">> musrview **ERROR** " << canvas->GetLastError() << "\n"; std::cerr << errMsg.str(); if (show_errMsgBox) musrview_error_msg(errMsg.str()); ok = false; result = PMUSR_MSR_RUN_ERROR; break; } canvas->SetTimeout(timeout); canvasList.push_back(std::move(canvas)); } if (!ok) { canvasList.clear(); plotList.clear(); return result; } if (graphicsOutput) { for (auto &canvas : canvasList) { if (!canvas->SaveGraphics(graphicsExtension)) { std::cerr << std::endl << ">> musrview **ERROR** " << canvas->GetLastError() << std::endl; result = PMUSR_MSR_RUN_ERROR; } } } if (asciiOutput) { // generate the export data file name TString str(fileName.c_str()); str.Remove(str.Last('.')); for (size_t i=0; i 1) { name += "_"; name += static_cast(i); } name += ".dat"; if (!canvasList[i]->ExportData(name.Data())) { std::cerr << std::endl << ">> musrview **ERROR** " << canvasList[i]->GetLastError() << std::endl; result = PMUSR_MSR_RUN_ERROR; } } } if (!batchMode) { const std::string where = musrview_select_display(display); // one window per PLOT block, exactly as before ROOT::RWebWindowsManager::SetSingleConnMode(false); for (auto &canvas : canvasList) { if (!canvas->Show(where)) { std::cerr << std::endl << ">> musrview **ERROR** " << canvas->GetLastError() << std::endl; result = PMUSR_MSR_RUN_ERROR; ok = false; break; } } if (ok) { // run until every window has been closed, or until the timeout expired const auto start = std::chrono::steady_clock::now(); while (true) { gSystem->ProcessEvents(); gSystem->Sleep(20); bool allDone = true; for (auto &canvas : canvasList) { if (!canvas->IsDone()) { allDone = false; break; } } if (allDone) break; if (timeout > 0) { const auto now = std::chrono::steady_clock::now(); if (std::chrono::duration_cast(now-start).count() >= timeout) { std::cout << std::endl << ">> musrview: timeout of " << timeout << " s reached, terminating." << std::endl; break; } } } } // End the process here. Tearing down an RWebWindow which drives the // embedded display blocks, because the Qt event loop ROOT started on our // behalf is never stopped again. Everything of interest has been written by // now, so leave immediately rather than hang after the last window closed. std::cout.flush(); std::cerr.flush(); gSystem->Exit(result, kFALSE); } // only reached in batch mode, where no window was ever opened canvasList.clear(); plotList.clear(); return result; }