Newer
Older
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source
// & Institut Laue - Langevin
// SPDX - License - Identifier: GPL - 3.0 +
#include "EnggDiffractionPresWorker.h"
#include "EnggVanadiumCorrectionsModel.h"
#include "IEnggDiffractionView.h"
Federico Montesino Pouzols
committed
#include "MantidAPI/AlgorithmManager.h"
#include "MantidAPI/AnalysisDataService.h"
Federico Montesino Pouzols
committed
#include "MantidAPI/ITableWorkspace.h"
#include "MantidAPI/MatrixWorkspace.h"
Federico Montesino Pouzols
committed
#include "MantidAPI/TableRow.h"
#include "MantidKernel/Property.h"
Federico Montesino Pouzols
committed
#include "MantidKernel/StringTokenizer.h"
#include "MantidQtWidgets/Common/PythonRunner.h"
#include <algorithm>
#include <fstream>
#include <boost/lexical_cast.hpp>
#include <Poco/File.h>
#include <Poco/Path.h>
Federico Montesino Pouzols
committed
using namespace Mantid::API;
using namespace MantidQt::CustomInterfaces;
namespace MantidQt {
namespace CustomInterfaces {
namespace {
Mantid::Kernel::Logger g_log("EngineeringDiffractionGUI");
}
const std::string EnggDiffractionPresenter::g_runNumberErrorStr =
" cannot be empty, must be an integer number, valid ENGINX run number/s "
"or "
"valid directory/directories.";
// discouraged at the moment
const bool EnggDiffractionPresenter::g_askUserCalibFilename = false;
Federico Montesino Pouzols
committed
const std::string EnggDiffractionPresenter::g_calibBanksParms =
"engggui_calibration_banks_parameters";
int EnggDiffractionPresenter::g_croppedCounter = 0;
int EnggDiffractionPresenter::g_plottingCounter = 0;
bool EnggDiffractionPresenter::g_abortThread = false;
std::string EnggDiffractionPresenter::g_lastValidRun = "";
std::string EnggDiffractionPresenter::g_calibCropIdentifier = "SpectrumNumbers";
std::string EnggDiffractionPresenter::g_sumOfFilesFocus = "";
EnggDiffractionPresenter::EnggDiffractionPresenter(IEnggDiffractionView *view)
: m_workerThread(nullptr), m_calibFinishedOK(false),
m_focusFinishedOK(false), m_rebinningFinishedOK(false), m_view(view),
m_viewHasClosed(false),
m_vanadiumCorrectionsModel(
boost::make_shared<EnggVanadiumCorrectionsModel>(
m_view->currentCalibSettings(), m_view->currentInstrument())) {
if (!m_view) {
throw std::runtime_error(
"Severe inconsistency found. Presenter created "
"with an empty/null view (Engineering diffraction interface). "
"Cannot continue.");
}
m_currentInst = m_view->currentInstrument();
}
EnggDiffractionPresenter::~EnggDiffractionPresenter() { cleanup(); }
/**
* Close open sessions, kill threads etc., save settings, etc. for a
* graceful window close/destruction
*/
void EnggDiffractionPresenter::cleanup() {
// m_model->cleanup();
Federico Montesino Pouzols
committed
// this may still be running
if (m_workerThread) {
if (m_workerThread->isRunning()) {
g_log.notice() << "A calibration process is currently running, shutting "
Federico Montesino Pouzols
committed
m_workerThread->wait(10);
}
delete m_workerThread;
m_workerThread = nullptr;
Federico Montesino Pouzols
committed
}
// Remove the workspace which is loaded when the interface starts
auto &ADS = Mantid::API::AnalysisDataService::Instance();
if (ADS.doesExist(g_calibBanksParms)) {
ADS.remove(g_calibBanksParms);
}
void EnggDiffractionPresenter::notify(
IEnggDiffractionPresenter::Notification notif) {
// Check the view is valid - QT can send multiple notification
// signals in any order at any time. This means that it is possible
// to receive a shutdown signal and subsequently an input example
// for example. As we can't guarantee the state of the viewer
// after calling shutdown instead we shouldn't do anything after
if (m_viewHasClosed) {
return;
}
switch (notif) {
case IEnggDiffractionPresenter::Start:
processStart();
break;
case IEnggDiffractionPresenter::LoadExistingCalib:
processLoadExistingCalib();
break;
case IEnggDiffractionPresenter::CalcCalib:
processCalcCalib();
break;
case IEnggDiffractionPresenter::CropCalib:
ProcessCropCalib();
break;
case IEnggDiffractionPresenter::FocusRun:
Federico Montesino Pouzols
committed
processFocusBasic();
break;
case IEnggDiffractionPresenter::FocusCropped:
processFocusCropped();
break;
case IEnggDiffractionPresenter::FocusTexture:
processFocusTexture();
break;
case IEnggDiffractionPresenter::ResetFocus:
processResetFocus();
case IEnggDiffractionPresenter::RebinTime:
processRebinTime();
break;
case IEnggDiffractionPresenter::RebinMultiperiod:
processRebinMultiperiod();
break;
case IEnggDiffractionPresenter::LogMsg:
processLogMsg();
break;
case IEnggDiffractionPresenter::InstrumentChange:
processInstChange();
case IEnggDiffractionPresenter::RBNumberChange:
processRBNumberChange();
break;
case IEnggDiffractionPresenter::ShutDown:
processShutDown();
break;
case IEnggDiffractionPresenter::StopFocus:
processStopFocus();
break;
}
}
void EnggDiffractionPresenter::processStart() { m_view->showStatus("Ready"); }
void EnggDiffractionPresenter::processLoadExistingCalib() {
const std::string fname = m_view->askExistingCalibFilename();
Federico Montesino Pouzols
committed
if (fname.empty()) {
return;
}
Federico Montesino Pouzols
committed
updateNewCalib(fname);
}
/**
* Grab a calibration from a (GSAS calibration) file
* (.prm/.par/.iparm) and set/use it as current calibration.
*
* @param fname name/path of the calibration file
*/
void EnggDiffractionPresenter::updateNewCalib(const std::string &fname) {
Poco::Path pocoPath;
const bool pathValid = pocoPath.tryParse(fname);
if (!pathValid || fname.empty() || pocoPath.isDirectory()) {
// Log that we couldn't open the file - its probably and invalid
// path which will be regenerated
g_log.warning("Could not open GSAS calibration file: " + fname);
Federico Montesino Pouzols
committed
return;
}
std::string instName, vanNo, ceriaNo;
try {
parseCalibrateFilename(fname, instName, vanNo, ceriaNo);
} catch (std::invalid_argument &ia) {
m_view->userWarning("Invalid calibration filename : " + fname, ia.what());
return;
}
Federico Montesino Pouzols
committed
try {
Federico Montesino Pouzols
committed
updateCalibParmsTable();
m_view->newCalibLoaded(vanNo, ceriaNo, fname);
} catch (std::runtime_error &rexc) {
m_view->userWarning("Problem while updating calibration.", rexc.what());
}
}
/**
* Get from a calibration file (GSAS instrument parameters file) the
* DIFC, DIFA, TZERO calibration parameters used for units
* conversions. Normally this is used on the ...all_banks.prm file
* which has the parameters for every bank included in the calibration
* process.
*
* @param fname name of the calibration/GSAS iparm file
* @param vanNo output param to hold the vanadium run
* @param ceriaNo output param to hold the ceria run
Federico Montesino Pouzols
committed
*/
void EnggDiffractionPresenter::grabCalibParms(const std::string &fname,
std::string &vanNo,
std::string &ceriaNo) {
Federico Montesino Pouzols
committed
std::vector<GSASCalibrationParms> parms;
// To grab the bank indices, lines like "INS BANK 2"
// To grab the difc,difa,tzero parameters, lines like:
// "INS 2 ICONS 18388.00 0.00 -6.76"
try {
std::ifstream prmFile(fname);
std::string line;
int opts = Mantid::Kernel::StringTokenizer::TOK_TRIM +
Mantid::Kernel::StringTokenizer::TOK_IGNORE_EMPTY;
while (std::getline(prmFile, line)) {
if (line.find("ICONS") != std::string::npos) {
Mantid::Kernel::StringTokenizer tokenizer(line, " ", opts);
const size_t numElems = 6;
if (tokenizer.count() == numElems) {
try {
size_t bid = boost::lexical_cast<size_t>(tokenizer[1]);
double difc = boost::lexical_cast<double>(tokenizer[3]);
double difa = boost::lexical_cast<double>(tokenizer[4]);
double tzero = boost::lexical_cast<double>(tokenizer[5]);
parms.emplace_back(GSASCalibrationParms(bid, difc, difa, tzero));
} catch (std::runtime_error &rexc) {
g_log.warning()
<< "Error when trying to extract parameters from this line: "
<< line
<< ". This calibration file may not load correctly. "
"Error details: "
<< rexc.what() << '\n';
Federico Montesino Pouzols
committed
}
} else {
g_log.warning() << "Could not parse correctly a parameters "
"definition line in this calibration file ("
<< fname << "). Did not find " << numElems
<< " elements as expected. The calibration may not "
Federico Montesino Pouzols
committed
}
} else if (line.find("CALIB") != std::string::npos) {
Mantid::Kernel::StringTokenizer tokenizer(line, " ", opts);
ceriaNo = tokenizer[2];
vanNo = tokenizer[3];
Federico Montesino Pouzols
committed
}
}
} catch (std::runtime_error &rexc) {
g_log.error()
<< "Error while loading calibration / GSAS IPARM file (" << fname
<< "). Could not parse the file. Please check its contents. Details: "
Federico Montesino Pouzols
committed
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
}
m_currentCalibParms = parms;
}
/**
* Puts in a table workspace, visible in the ADS, the per-bank
* calibration parameters for the current calibration.
*/
void EnggDiffractionPresenter::updateCalibParmsTable() {
if (m_currentCalibParms.empty()) {
return;
}
ITableWorkspace_sptr parmsTbl;
AnalysisDataServiceImpl &ADS = Mantid::API::AnalysisDataService::Instance();
if (ADS.doesExist(g_calibBanksParms)) {
parmsTbl = ADS.retrieveWS<ITableWorkspace>(g_calibBanksParms);
parmsTbl->setRowCount(0);
} else {
auto alg = Mantid::API::AlgorithmManager::Instance().createUnmanaged(
"CreateEmptyTableWorkspace");
alg->initialize();
alg->setPropertyValue("OutputWorkspace", g_calibBanksParms);
alg->execute();
parmsTbl = ADS.retrieveWS<ITableWorkspace>(g_calibBanksParms);
parmsTbl->addColumn("int", "bankid");
parmsTbl->addColumn("double", "difc");
parmsTbl->addColumn("double", "difa");
parmsTbl->addColumn("double", "tzero");
}
for (const auto &parms : m_currentCalibParms) {
// ADS.remove(FocusedFitPeaksTableName);
TableRow row = parmsTbl->appendRow();
row << static_cast<int>(parms.bankid) << parms.difc << parms.difa
<< parms.tzero;
}
}
void EnggDiffractionPresenter::processCalcCalib() {
const std::string vanNo = isValidRunNumber(m_view->newVanadiumNo());
const std::string ceriaNo = isValidRunNumber(m_view->newCeriaNo());
try {
inputChecksBeforeCalibrate(vanNo, ceriaNo);
} catch (std::invalid_argument &ia) {
m_view->userWarning("Error in the inputs required for calibrate",
ia.what());
g_log.notice() << "EnggDiffraction GUI: starting new calibration. This may "
const std::string outFilename = outputCalibFilename(vanNo, ceriaNo);
Federico Montesino Pouzols
committed
m_view->showStatus("Calculating calibration...");
Federico Montesino Pouzols
committed
m_view->enableCalibrateFocusFitUserActions(false);
// alternatively, this would be GUI-blocking:
// doNewCalibration(outFilename, vanNo, ceriaNo, "");
// calibrationFinished();
startAsyncCalibWorker(outFilename, vanNo, ceriaNo, "");
void EnggDiffractionPresenter::ProcessCropCalib() {
const std::string vanNo = isValidRunNumber(m_view->newVanadiumNo());
const std::string ceriaNo = isValidRunNumber(m_view->newCeriaNo());
int specNoNum = m_view->currentCropCalibBankName();
enum BankMode { SPECNOS = 0, NORTH = 1, SOUTH = 2 };
try {
inputChecksBeforeCalibrate(vanNo, ceriaNo);
if (m_view->currentCalibSpecNos().empty() &&
specNoNum == BankMode::SPECNOS) {
throw std::invalid_argument(
"The Spectrum Numbers field cannot be empty, must be a "
"valid range or a Bank Name can be selected instead.");
} catch (std::invalid_argument &ia) {
Federico Montesino Pouzols
committed
m_view->userWarning("Error in the inputs required for cropped calibration",
ia.what());
return;
}
g_log.notice()
<< "EnggDiffraction GUI: starting cropped calibration. This may "
const std::string outFilename = outputCalibFilename(vanNo, ceriaNo);
std::string specNo = "";
if (specNoNum == BankMode::NORTH) {
specNo = "North";
g_calibCropIdentifier = "Bank";
} else if (specNoNum == BankMode::SOUTH) {
specNo = "South";
g_calibCropIdentifier = "Bank";
} else if (specNoNum == BankMode::SPECNOS) {
g_calibCropIdentifier = "SpectrumNumbers";
specNo = m_view->currentCalibSpecNos();
}
Federico Montesino Pouzols
committed
m_view->showStatus("Calculating cropped calibration...");
Federico Montesino Pouzols
committed
m_view->enableCalibrateFocusFitUserActions(false);
startAsyncCalibWorker(outFilename, vanNo, ceriaNo, specNo);
Federico Montesino Pouzols
committed
void EnggDiffractionPresenter::processFocusBasic() {
std::vector<std::string> multi_RunNo =
isValidMultiRunNumber(m_view->focusingRunNo());
Federico Montesino Pouzols
committed
const std::vector<bool> banks = m_view->focusingBanks();
// reset global values
g_sumOfFilesFocus = "";
g_plottingCounter = 0;
// check if valid run number provided before focusin
try {
inputChecksBeforeFocusBasic(multi_RunNo, banks);
} catch (std::invalid_argument &ia) {
m_view->userWarning("Error in the inputs required to focus a run",
ia.what());
return;
int focusMode = m_view->currentMultiRunMode();
g_log.debug() << " focus mode selected Individual Run Files Separately \n";
// start focusing
startFocusing(multi_RunNo, banks, "", "");
} else if (focusMode == 1) {
g_log.debug() << " focus mode selected Focus Sum Of Files \n";
g_sumOfFilesFocus = "basic";
std::vector<std::string> firstRun;
firstRun.push_back(multi_RunNo[0]);
// to avoid multiple loops, use firstRun instead as the
// multi-run number is not required for sumOfFiles
startFocusing(firstRun, banks, "", "");
Federico Montesino Pouzols
committed
}
void EnggDiffractionPresenter::processFocusCropped() {
const std::vector<std::string> multi_RunNo =
isValidMultiRunNumber(m_view->focusingCroppedRunNo());
Federico Montesino Pouzols
committed
const std::vector<bool> banks = m_view->focusingBanks();
const std::string specNos = m_view->focusingCroppedSpectrumNos();
Federico Montesino Pouzols
committed
// reset global values
g_sumOfFilesFocus = "";
g_plottingCounter = 0;
// check if valid run number provided before focusin
try {
inputChecksBeforeFocusCropped(multi_RunNo, banks, specNos);
} catch (std::invalid_argument &ia) {
m_view->userWarning(
"Error in the inputs required to focus a run (in cropped mode)",
ia.what());
return;
int focusMode = m_view->currentMultiRunMode();
g_log.debug() << " focus mode selected Individual Run Files Separately \n";
startFocusing(multi_RunNo, banks, specNos, "");
} else if (focusMode == 1) {
g_log.debug() << " focus mode selected Focus Sum Of Files \n";
g_sumOfFilesFocus = "cropped";
std::vector<std::string> firstRun{multi_RunNo[0]};
// to avoid multiple loops, use firstRun instead as the
// multi-run number is not required for sumOfFiles
startFocusing(firstRun, banks, specNos, "");
Federico Montesino Pouzols
committed
}
}
void EnggDiffractionPresenter::processFocusTexture() {
const std::vector<std::string> multi_RunNo =
isValidMultiRunNumber(m_view->focusingTextureRunNo());
const std::string dgFile = m_view->focusingTextureGroupingFile();
Federico Montesino Pouzols
committed
// reset global values
g_sumOfFilesFocus = "";
g_plottingCounter = 0;
// check if valid run number provided before focusing
try {
inputChecksBeforeFocusTexture(multi_RunNo, dgFile);
} catch (std::invalid_argument &ia) {
m_view->userWarning(
"Error in the inputs required to focus a run (in texture mode)",
ia.what());
return;
int focusMode = m_view->currentMultiRunMode();
g_log.debug() << " focus mode selected Individual Run Files Separately \n";
startFocusing(multi_RunNo, std::vector<bool>(), "", dgFile);
} else if (focusMode == 1) {
g_log.debug() << " focus mode selected Focus Sum Of Files \n";
g_sumOfFilesFocus = "texture";
std::vector<std::string> firstRun{multi_RunNo[0]};
// to avoid multiple loops, use firstRun instead as the
// multi-run number is not required for sumOfFiles
startFocusing(firstRun, std::vector<bool>(), "", dgFile);
Federico Montesino Pouzols
committed
}
}
/**
* Starts a focusing worker, for different modes depending on the
* inputs provided. Assumes that the inputs have been checked by the
* respective specific processFocus methods (for normal, cropped,
* texture, etc. focusing).
*
* @param multi_RunNo vector of run/file number to focus
* @param banks banks to include in the focusing, processed one at a time
*
* @param specNos list of spectra to use when focusing. If not empty
* this implies focusing in cropped mode.
*
* @param dgFile detector grouping file to define banks (texture). If
* not empty, this implies focusing in texture mode.
*/
void EnggDiffractionPresenter::startFocusing(
const std::vector<std::string> &multi_RunNo, const std::vector<bool> &banks,
const std::string &specNos, const std::string &dgFile) {
Federico Montesino Pouzols
committed
std::string optMsg = "";
if (!specNos.empty()) {
Federico Montesino Pouzols
committed
optMsg = " (cropped)";
} else if (!dgFile.empty()) {
optMsg = " (texture)";
}
g_log.notice() << "EnggDiffraction GUI: starting new focusing" << optMsg
<< ". This may take some seconds... \n";
Federico Montesino Pouzols
committed
m_view->showStatus("Focusing...");
Federico Montesino Pouzols
committed
m_view->enableCalibrateFocusFitUserActions(false);
startAsyncFocusWorker(multi_RunNo, banks, specNos, dgFile);
Federico Montesino Pouzols
committed
void EnggDiffractionPresenter::processResetFocus() { m_view->resetFocus(); }
void EnggDiffractionPresenter::processRebinTime() {
const std::string runNo = isValidRunNumber(m_view->currentPreprocRunNo());
double bin = m_view->rebinningTimeBin();
Federico Montesino Pouzols
committed
try {
inputChecksBeforeRebinTime(runNo, bin);
} catch (std::invalid_argument &ia) {
m_view->userWarning(
"Error in the inputs required to pre-process (rebin) a run", ia.what());
return;
}
const std::string outWSName = "engggui_preproc_time_ws";
g_log.notice() << "EnggDiffraction GUI: starting new pre-processing "
"(re-binning) with a TOF bin into workspace '" +
outWSName +
"'. This "
"may take some seconds... \n";
Federico Montesino Pouzols
committed
Federico Montesino Pouzols
committed
m_view->showStatus("Rebinning by time...");
Federico Montesino Pouzols
committed
m_view->enableCalibrateFocusFitUserActions(false);
Federico Montesino Pouzols
committed
startAsyncRebinningTimeWorker(runNo, bin, outWSName);
}
void EnggDiffractionPresenter::processRebinMultiperiod() {
const std::string runNo = isValidRunNumber(m_view->currentPreprocRunNo());
Federico Montesino Pouzols
committed
size_t nperiods = m_view->rebinningPulsesNumberPeriods();
double timeStep = m_view->rebinningPulsesTime();
try {
inputChecksBeforeRebinPulses(runNo, nperiods, timeStep);
} catch (std::invalid_argument &ia) {
m_view->userWarning("Error in the inputs required to pre-process (rebin) a "
"run by pulse times",
ia.what());
return;
}
const std::string outWSName = "engggui_preproc_by_pulse_time_ws";
g_log.notice() << "EnggDiffraction GUI: starting new pre-processing "
"(re-binning) by pulse times into workspace '" +
outWSName +
"'. This "
"may take some seconds... \n";
Federico Montesino Pouzols
committed
Federico Montesino Pouzols
committed
m_view->showStatus("Rebinning by pulses...");
Federico Montesino Pouzols
committed
m_view->enableCalibrateFocusFitUserActions(false);
Federico Montesino Pouzols
committed
startAsyncRebinningPulsesWorker(runNo, nperiods, timeStep, outWSName);
}
void EnggDiffractionPresenter::processLogMsg() {
std::vector<std::string> msgs = m_view->logMsgs();
g_log.information() << msg << '\n';
}
}
void EnggDiffractionPresenter::processInstChange() {
m_currentInst = m_view->currentInstrument();
m_view->updateTabsInstrument(m_currentInst);
void EnggDiffractionPresenter::processRBNumberChange() {
const std::string rbn = m_view->getRBNumber();
auto valid = validateRBNumber(rbn);
m_view->enableTabs(valid);
m_view->showInvalidRBNumber(valid);
Federico Montesino Pouzols
committed
if (!valid) {
m_view->showStatus("Valid RB number required");
} else {
m_view->showStatus("Ready");
}
void EnggDiffractionPresenter::processShutDown() {
// Set that the view has closed in case QT attempts to fire another
// signal whilst we are shutting down. This stops notify before
// it hits the switch statement as the view could be in any state.
m_viewHasClosed = true;
Federico Montesino Pouzols
committed
m_view->showStatus("Closing...");
m_view->saveSettings();
cleanup();
}
void EnggDiffractionPresenter::processStopFocus() {
if (m_workerThread) {
if (m_workerThread->isRunning()) {
g_log.notice() << "A focus process is currently running, shutting "
"it down as soon as possible...\n";
g_abortThread = true;
g_log.warning() << "Focus Stop has been clicked, please wait until "
"current focus run process has been completed. \n";
* Check if an RB number is valid to work with it (retrieve data,
* calibrate, focus, etc.). For now this will accept any non-empty
* string. Later on we might be more strict about valid RB numbers /
* experiment IDs.
*
* @param rbn RB number as given by the user
*/
bool EnggDiffractionPresenter::validateRBNumber(const std::string &rbn) const {
return !rbn.empty();
}
* Checks if the provided run number is valid and if a directory is
* provided it will convert it to a run number. It also records the
* paths the user has browsed to.
*
* @param userPaths the input/directory given by the user via the "browse"
* button
*
* @return run_number 6 character string of a run number
*/
std::string EnggDiffractionPresenter::isValidRunNumber(
const std::vector<std::string> &userPaths) {
std::string run_number;
if (userPaths.empty() || userPaths.front().empty()) {
return run_number;
}
* Checks if the provided run number is valid and if a directory is provided
*
* @param paths takes the input/paths of the user
*
* @return vector of string multi_run_number, 6 character string of a run number
*/
std::vector<std::string> EnggDiffractionPresenter::isValidMultiRunNumber(
const std::vector<std::string> &paths) {
std::vector<std::string> multi_run_number;
if (paths.empty() || paths.front().empty())
return multi_run_number;
* Does several checks on the current inputs and settings. This should
* be done before starting any calibration work. The message return
* should in principle be shown to the user as a visible message
* (pop-up, error log, etc.)
*
* @param newVanNo number of the Vanadium run for the new calibration
* @param newCeriaNo number of the Ceria run for the new calibration
*
* @throws std::invalid_argument with an informative message.
*/
void EnggDiffractionPresenter::inputChecksBeforeCalibrate(
const std::string &newVanNo, const std::string &newCeriaNo) {
if (newVanNo.empty()) {
throw std::invalid_argument("The Vanadium number" + g_runNumberErrorStr);
}
if (newCeriaNo.empty()) {
throw std::invalid_argument("The Ceria number" + g_runNumberErrorStr);
const auto &cs = m_view->currentCalibSettings();
const auto &pixelCalib = cs.m_pixelCalibFilename;
Federico Montesino Pouzols
committed
if (pixelCalib.empty()) {
throw std::invalid_argument(
Federico Montesino Pouzols
committed
"You need to set a pixel (full) calibration in settings.");
}
const auto &templGSAS = cs.m_templateGSAS_PRM;
Federico Montesino Pouzols
committed
if (templGSAS.empty()) {
throw std::invalid_argument(
Federico Montesino Pouzols
committed
"You need to set a template calibration file for GSAS in settings.");
}
Federico Montesino Pouzols
committed
Federico Montesino Pouzols
committed
* What should be the name of the output GSAS calibration file, given
* the Vanadium and Ceria runs
*
* @param vanNo number of the Vanadium run, which is normally part of the name
* @param ceriaNo number of the Ceria run, which is normally part of the name
* @param bankName bank name when generating a file for an individual
* bank. Leave empty to use a generic name for all banks
*
* @return filename (without the full path)
*/
std::string
EnggDiffractionPresenter::outputCalibFilename(const std::string &vanNo,
Federico Montesino Pouzols
committed
const std::string &ceriaNo,
const std::string &bankName) {
std::string outFilename = "";
Federico Montesino Pouzols
committed
const std::string sugg =
buildCalibrateSuggestedFilename(vanNo, ceriaNo, bankName);
if (!g_askUserCalibFilename) {
outFilename = sugg;
} else {
outFilename = m_view->askNewCalibrationFilename(sugg);
if (!outFilename.empty()) {
// make sure it follows the rules
try {
std::string inst, van, ceria;
parseCalibrateFilename(outFilename, inst, van, ceria);
} catch (std::invalid_argument &ia) {
m_view->userWarning(
"Invalid output calibration filename: " + outFilename, ia.what());
outFilename = "";
}
return outFilename;
}
/**
* Parses the name of a calibration file and guesses the instrument,
* vanadium and ceria run numbers, assuming that the name has been
* build with buildCalibrateSuggestedFilename().
*
* @todo this is currently based on the filename. This method should
* do a basic sanity check that the file is actually a calibration
* file (IPARM file for GSAS), and get the numbers from the file not
* from the name of the file. Leaving this as a TODO for now, as the
* file template and contents are still subject to change.
*
* @param path full path to a calibration file
* @param instName instrument used in the file
* @param vanNo number of the Vanadium run used for this calibration file
* @param ceriaNo number of the Ceria run
*
* @throws invalid_argument with an informative message if tha name does
* not look correct (does not follow the conventions).
*/
void EnggDiffractionPresenter::parseCalibrateFilename(const std::string &path,
std::string &instName,
std::string &vanNo,
std::string &ceriaNo) {
instName = "";
vanNo = "";
ceriaNo = "";
Poco::Path fullPath(path);
const std::string &filename = fullPath.getFileName();
if (filename.empty()) {
return;
Federico Montesino Pouzols
committed
const std::string explMsg =
"Expected a file name like 'INSTR_vanNo_ceriaNo_....par', "
"where INSTR is the instrument name and vanNo and ceriaNo are the "
"numbers of the Vanadium and calibration sample (Ceria, CeO2) runs.";
std::vector<std::string> parts;
boost::split(parts, filename, boost::is_any_of("_"));
if (parts.size() < 4) {
throw std::invalid_argument(
"Failed to find at least the 4 required parts of the file name.\n\n" +
explMsg);
}
// check the rules on the file name
if (m_currentInst != parts[0]) {
throw std::invalid_argument("The first component of the file name is not "
"the expected instrument name: " +
m_currentInst + ".\n\n" + explMsg);
}
instName = parts[0];
Federico Montesino Pouzols
committed
}
/**
* Start the calibration work without blocking the GUI. This uses
* connect for Qt signals/slots so that it runs well with the Qt event
* loop. Because of that this class needs to be a Q_OBJECT.
*
* @param outFilename name for the output GSAS calibration file
* @param vanNo vanadium run number
* @param ceriaNo ceria run number
* @param specNos SpecNos or bank name to be passed
*/
void EnggDiffractionPresenter::startAsyncCalibWorker(
const std::string &outFilename, const std::string &vanNo,
const std::string &ceriaNo, const std::string &specNos) {
Federico Montesino Pouzols
committed
delete m_workerThread;
m_workerThread = new QThread(this);
EnggDiffWorker *worker =
new EnggDiffWorker(this, outFilename, vanNo, ceriaNo, specNos);
Federico Montesino Pouzols
committed
worker->moveToThread(m_workerThread);
connect(m_workerThread, SIGNAL(started()), worker, SLOT(calibrate()));
connect(worker, SIGNAL(finished()), this, SLOT(calibrationFinished()));
// early delete of thread and worker
connect(m_workerThread, SIGNAL(finished()), m_workerThread,
SLOT(deleteLater()), Qt::DirectConnection);
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
m_workerThread->start();
}
* Calculate a new calibration. This is what threads/workers should
* use to run the calculations in response to the user clicking
* 'calibrate' or similar.
*
* @param outFilename name for the output GSAS calibration file
* @param vanNo vanadium run number
* @param ceriaNo ceria run number
* @param specNos SpecNos or bank name to be passed
*/
Federico Montesino Pouzols
committed
void EnggDiffractionPresenter::doNewCalibration(const std::string &outFilename,
const std::string &vanNo,
const std::string &ceriaNo,
const std::string &specNos) {
g_log.notice() << "Generating new calibration file: " << outFilename << '\n';
Federico Montesino Pouzols
committed
Federico Montesino Pouzols
committed
EnggDiffCalibSettings cs = m_view->currentCalibSettings();
Federico Montesino Pouzols
committed
Mantid::Kernel::ConfigServiceImpl &conf =
Mantid::Kernel::ConfigService::Instance();
const std::vector<std::string> tmpDirs = conf.getDataSearchDirs();
// in principle, the run files will be found from 'DirRaw', and the
// pre-calculated Vanadium corrections from 'DirCalib'
Federico Montesino Pouzols
committed
if (!cs.m_inputDirCalib.empty() && Poco::File(cs.m_inputDirCalib).exists()) {
Federico Montesino Pouzols
committed
conf.appendDataSearchDir(cs.m_inputDirCalib);
Federico Montesino Pouzols
committed
}
if (!cs.m_inputDirRaw.empty() && Poco::File(cs.m_inputDirRaw).exists())
conf.appendDataSearchDir(cs.m_inputDirRaw);
for (const auto &browsed : m_browsedToPaths) {
conf.appendDataSearchDir(browsed);
}
Federico Montesino Pouzols
committed
try {
Federico Montesino Pouzols
committed
m_calibFinishedOK = true;
doCalib(cs, vanNo, ceriaNo, outFilename, specNos);
Federico Montesino Pouzols
committed
} catch (std::runtime_error &rexc) {
m_calibError = "The calibration calculations failed. One of the "
"algorithms did not execute correctly. See log messages for "
"further details.";
Federico Montesino Pouzols
committed
g_log.error()
<< "The calibration calculations failed. One of the "
"algorithms did not execute correctly. See log messages for "
"further details. Error: " +
m_calibError = "The calibration calculations failed. Some input properties "
"were not valid. See log messages for details. \n Error: " +
g_log.error()
Federico Montesino Pouzols
committed
<< "The calibration calculations failed. Some input properties "
"were not valid. See log messages for details. \n";
} catch (Mantid::API::Algorithm::CancelException &) {
g_log.error() << "Execution terminated by user. \n";
}
Federico Montesino Pouzols
committed
// restore normal data search paths
conf.setDataSearchDirs(tmpDirs);
* Method to call when the calibration work has finished, either from
* a separate thread or not (as in this presenter's' test).
*/
void EnggDiffractionPresenter::calibrationFinished() {
if (!m_view)
return;
Federico Montesino Pouzols
committed
m_view->enableCalibrateFocusFitUserActions(true);
if (!m_calibFinishedOK) {
if (!m_cancelled) {
m_view->userWarning("Calibration Error", m_calibError);
}
m_cancelled = false;
Federico Montesino Pouzols
committed
m_view->showStatus("Calibration didn't finish succesfully. Ready");
} else {
const std::string vanNo = isValidRunNumber(m_view->newVanadiumNo());
const std::string ceriaNo = isValidRunNumber(m_view->newCeriaNo());
Federico Montesino Pouzols
committed
updateCalibParmsTable();
Federico Montesino Pouzols
committed
m_view->newCalibLoaded(vanNo, ceriaNo, m_calibFullPath);
g_log.notice()
<< "Calibration finished and ready as 'current calibration'.\n";
Federico Montesino Pouzols
committed
m_view->showStatus("Calibration finished succesfully. Ready");
}
if (m_workerThread) {
delete m_workerThread;
Federico Montesino Pouzols
committed
m_workerThread = nullptr;
}
}
Federico Montesino Pouzols
committed
* Build a suggested name for a new calibration, by appending instrument name,
* relevant run numbers, etc., like: ENGINX_241391_236516_all_banks.par
*
* @param vanNo number of the Vanadium run
* @param ceriaNo number of the Ceria run
* @param bankName bank name when generating a file for an individual
* bank. Leave empty to use a generic name for all banks
*
* @return Suggested name for a new calibration file, following
* ENGIN-X practices
*/
std::string EnggDiffractionPresenter::buildCalibrateSuggestedFilename(
Federico Montesino Pouzols
committed
const std::string &vanNo, const std::string &ceriaNo,
const std::string &bankName) const {
// default and only one supported instrument for now
std::string instStr = m_currentInst;
Federico Montesino Pouzols
committed
std::string nameAppendix = "_all_banks";
if (!bankName.empty()) {
nameAppendix = "_" + bankName;
}
// default extension for calibration files
const std::string calibExt = ".prm";
std::string vanFilename = Poco::Path(vanNo).getBaseName();
std::string ceriaFilename = Poco::Path(ceriaNo).getBaseName();
std::string vanRun =
vanFilename.substr(vanFilename.find(instStr) + instStr.size());
std::string ceriaRun =
ceriaFilename.substr(ceriaFilename.find(instStr) + instStr.size());
vanRun.erase(0, std::min(vanRun.find_first_not_of('0'), vanRun.size() - 1));
ceriaRun.erase(
0, std::min(ceriaRun.find_first_not_of('0'), ceriaRun.size() - 1));
std::string sugg =
instStr + "_" + vanRun + "_" + ceriaRun + nameAppendix + calibExt;
return sugg;
}
Federico Montesino Pouzols
committed
std::vector<GSASCalibrationParms>
EnggDiffractionPresenter::currentCalibration() const {
return m_currentCalibParms;
}
/**
* Calculate a calibration, responding the the "new calibration"
* action/button.
*
* @param cs user settings
* @param vanNo Vanadium run number
* @param ceriaNo Ceria run number
* @param outFilename output filename chosen by the user
* @param specNos SpecNos or bank name to be passed
*/
void EnggDiffractionPresenter::doCalib(const EnggDiffCalibSettings &cs,
const std::string &vanNo,
const std::string &ceriaNo,
const std::string &outFilename,
const std::string &specNos) {
if (cs.m_inputDirCalib.empty()) {
m_calibError =
"No calibration directory selected. Please select a calibration "
"directory in Settings. This will be used to "
"cache Vanadium calibration data";