Newer
Older
Federico Montesino Pouzols
committed
#include "MantidAPI/ITableWorkspace.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidQtAPI/AlgorithmInputHistory.h"
#include "MantidQtAPI/AlgorithmRunner.h"
#include "MantidQtAPI/PythonRunner.h"
// #include "MantidQtCustomInterfaces/EnggDiffraction/EnggDiffractionModel.h"
#include "MantidQtCustomInterfaces/EnggDiffraction/EnggDiffractionPresenter.h"
Federico Montesino Pouzols
committed
#include "MantidQtCustomInterfaces/EnggDiffraction/EnggDiffractionPresWorker.h"
#include "MantidQtCustomInterfaces/EnggDiffraction/IEnggDiffractionView.h"
#include <boost/lexical_cast.hpp>
#include <Poco/File.h>
#include <Poco/Path.h>
Federico Montesino Pouzols
committed
#include <QThread>
using namespace Mantid::API;
using namespace MantidQt::CustomInterfaces;
namespace MantidQt {
namespace CustomInterfaces {
namespace {
Mantid::Kernel::Logger g_log("EngineeringDiffractionGUI");
}
const std::string EnggDiffractionPresenter::g_enginxStr = "ENGINX";
// discouraged at the moment
const bool EnggDiffractionPresenter::g_askUserCalibFilename = false;
EnggDiffractionPresenter::EnggDiffractionPresenter(IEnggDiffractionView *view)
: m_workerThread(NULL), m_calibFinishedOK(false), m_focusFinishedOK(false),
Federico Montesino Pouzols
committed
m_view(view) /*, m_model(new EnggDiffractionModel()), */ {
if (!m_view) {
throw std::runtime_error(
"Severe inconsistency found. Presenter created "
"with an empty/null view (engineeering diffraction interface). "
"Cannot continue.");
}
}
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
"it down immediately..." << std::endl;
Federico Montesino Pouzols
committed
m_workerThread->wait(10);
}
delete m_workerThread;
m_workerThread = NULL;
}
}
void EnggDiffractionPresenter::notify(
IEnggDiffractionPresenter::Notification notif) {
switch (notif) {
case IEnggDiffractionPresenter::Start:
processStart();
break;
case IEnggDiffractionPresenter::LoadExistingCalib:
processLoadExistingCalib();
break;
case IEnggDiffractionPresenter::CalcCalib:
processCalcCalib();
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::LogMsg:
processLogMsg();
break;
case IEnggDiffractionPresenter::InstrumentChange:
processInstChange();
case IEnggDiffractionPresenter::ShutDown:
processShutDown();
break;
}
}
void EnggDiffractionPresenter::processStart() {
EnggDiffCalibSettings cs = m_view->currentCalibSettings();
}
void EnggDiffractionPresenter::processLoadExistingCalib() {
EnggDiffCalibSettings cs = m_view->currentCalibSettings();
std::string fname = m_view->askExistingCalibFilename();
Federico Montesino Pouzols
committed
if (fname.empty()) {
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;
}
m_view->newCalibLoaded(vanNo, ceriaNo, fname);
}
void EnggDiffractionPresenter::processCalcCalib() {
std::string vanNo = m_view->newVanadiumNo();
std::string ceriaNo = 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 "
Federico Montesino Pouzols
committed
"take a few seconds... " << std::endl;
const std::string outFilename = outputCalibFilename(vanNo, ceriaNo);
m_view->enableCalibrateAndFocusActions(false);
// alternatively, this would be GUI-blocking:
// doNewCalibration(outFilename, vanNo, ceriaNo);
// calibrationFinished()
startAsyncCalibWorker(outFilename, vanNo, ceriaNo);
}
Federico Montesino Pouzols
committed
void EnggDiffractionPresenter::processFocusBasic() {
const std::string runNo = m_view->focusingRunNo();
const std::vector<bool> banks = m_view->focusingBanks();
Federico Montesino Pouzols
committed
inputChecksBeforeFocusBasic(runNo, banks);
} catch (std::invalid_argument &ia) {
m_view->userWarning("Error in the inputs required to focus a run",
ia.what());
return;
}
Federico Montesino Pouzols
committed
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
startFocusing(runNo, banks, "", "");
}
void EnggDiffractionPresenter::processFocusCropped() {
const std::string runNo = m_view->focusingCroppedRunNo();
const std::vector<bool> banks = m_view->focusingBanks();
const std::string specIDs = m_view->focusingCroppedSpectrumIDs();
try {
inputChecksBeforeFocusCropped(runNo, banks, specIDs);
} catch (std::invalid_argument &ia) {
m_view->userWarning(
"Error in the inputs required to focus a run in cropped mode",
ia.what());
return;
}
startFocusing(runNo, banks, specIDs, "");
}
void EnggDiffractionPresenter::processFocusTexture() {
std::string runNo = m_view->focusingTextureRunNo();
const std::vector<bool> banks = m_view->focusingBanks();
std::string dgFile = m_view->focusingTextureGroupingFile();
try {
inputChecksBeforeFocusTexture(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;
}
startFocusing(runNo, banks, "", dgFile);
}
/**
* 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 runNo run/file number to focus
* @param banks banks to include in the focusing, processed one at a time
*
* @param specIDs list of spectra to use when focusing. If not empty
* this is focusing in cropped mode.
*
* @param dgFile detector grouping file to define banks (texture). If
* not empty, this is focusing in texture mode.
*/
void EnggDiffractionPresenter::startFocusing(const std::string &runNo,
const std::vector<bool> banks,
const std::string &specIDs,
const std::string &dgFile) {
std::string optMsg = "";
if (!specIDs.empty()) {
optMsg = " (cropped)";
} else if (!dgFile.empty()) {
optMsg = " (texture)";
}
g_log.notice() << "EnggDiffraction GUI: starting new focusing" << optMsg
<< ". This may take some seconds... " << std::endl;
const std::string focusDir = m_view->focusingDir();
Federico Montesino Pouzols
committed
const std::vector<std::string> outFilenames =
outputFocusFilename(runNo, banks);
m_view->enableCalibrateAndFocusActions(false);
// GUI-blocking alternative:
Federico Montesino Pouzols
committed
// doFocusRun(focusDir, outFilenames, runNo, banks)
Federico Montesino Pouzols
committed
startAsyncFocusWorker(focusDir, outFilenames, runNo, banks);
Federico Montesino Pouzols
committed
void EnggDiffractionPresenter::processResetFocus() { m_view->resetFocus(); }
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
void EnggDiffractionPresenter::processLogMsg() {
std::vector<std::string> msgs = m_view->logMsgs();
for (size_t i = 0; i < msgs.size(); i++) {
g_log.information() << msgs[i] << std::endl;
}
}
void EnggDiffractionPresenter::processInstChange() {
const std::string err = "Changing instrument is not supported!";
g_log.error() << err << std::endl;
m_view->userError("Fatal error", err);
}
void EnggDiffractionPresenter::processShutDown() {
m_view->saveSettings();
cleanup();
}
/**
* 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 cannot be empty and must be an integer number.");
}
if (newCeriaNo.empty()) {
throw std::invalid_argument(
Federico Montesino Pouzols
committed
"The Ceria number cannot be empty and must be an integer number.");
Federico Montesino Pouzols
committed
EnggDiffCalibSettings cs = m_view->currentCalibSettings();
const std::string pixelCalib = cs.m_pixelCalibFilename;
if (pixelCalib.empty()) {
throw std::invalid_argument(
Federico Montesino Pouzols
committed
"You need to set a pixel (full) calibration in settings.");
}
const std::string templGSAS = cs.m_templateGSAS_PRM;
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
/**
* 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
*
* @return filename (without the full path)
*/
std::string
EnggDiffractionPresenter::outputCalibFilename(const std::string &vanNo,
const std::string &ceriaNo) {
std::string outFilename = "";
const std::string sugg = buildCalibrateSuggestedFilename(vanNo, ceriaNo);
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 = "";
}
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
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
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
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 (g_enginxStr != parts[0]) {
throw std::invalid_argument("The first component of the file name is not "
"the expected instrument name: " +
g_enginxStr + ".\n\n" + explMsg);
}
const std::string castMsg =
"It is not possible to interpret as an integer number ";
try {
boost::lexical_cast<int>(parts[1]);
} catch (std::runtime_error &) {
throw std::invalid_argument(
castMsg + "the Vanadium number part of the file name.\n\n" + explMsg);
}
try {
boost::lexical_cast<int>(parts[2]);
} catch (std::runtime_error &) {
throw std::invalid_argument(
castMsg + "the Ceria number part of the file name.\n\n" + explMsg);
}
instName = parts[0];
vanNo = parts[1];
ceriaNo = parts[2];
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.
Federico Montesino Pouzols
committed
*
* @param outFilename name for the output GSAS calibration file
* @param vanNo vanadium run number
* @param ceriaNo ceria run number
*/
Federico Montesino Pouzols
committed
void
EnggDiffractionPresenter::startAsyncCalibWorker(const std::string &outFilename,
const std::string &vanNo,
const std::string &ceriaNo) {
Federico Montesino Pouzols
committed
delete m_workerThread;
m_workerThread = new QThread(this);
EnggDiffWorker *worker =
new EnggDiffWorker(this, outFilename, vanNo, ceriaNo);
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
*/
Federico Montesino Pouzols
committed
void EnggDiffractionPresenter::doNewCalibration(const std::string &outFilename,
const std::string &vanNo,
const std::string &ceriaNo) {
Federico Montesino Pouzols
committed
g_log.notice() << "Generating new calibration file: " << outFilename
<< std::endl;
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);
try {
doCalib(cs, vanNo, ceriaNo, outFilename);
Federico Montesino Pouzols
committed
m_calibFinishedOK = true;
} catch (std::runtime_error &) {
Federico Montesino Pouzols
committed
g_log.error() << "The calibration calculations failed. One of the "
"algorithms did not execute correctly. See log messages "
Federico Montesino Pouzols
committed
"for details. " << std::endl;
} catch (std::invalid_argument &) {
g_log.error()
Federico Montesino Pouzols
committed
<< "The calibration calculations failed. Some input properties "
Federico Montesino Pouzols
committed
"were not valid. See log messages for details. " << std::endl;
}
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;
m_view->enableCalibrateAndFocusActions(true);
if (!m_calibFinishedOK) {
g_log.warning() << "The cablibration did not finished correctly."
<< std::endl;
} else {
const std::string vanNo = m_view->newVanadiumNo();
const std::string ceriaNo = m_view->newCeriaNo();
const std::string outFilename =
buildCalibrateSuggestedFilename(vanNo, ceriaNo);
m_view->newCalibLoaded(vanNo, ceriaNo, outFilename);
g_log.notice()
<< "Cablibration finished and ready as 'current calibration'."
<< std::endl;
}
if (m_workerThread) {
delete m_workerThread;
m_workerThread = NULL;
}
}
/**
* Build a suggested name for a new calibration, by appending instrument name,
* relevant run numbers, etc., like: ENGINX_241391_236516_both_banks.par
*
* @param vanNo number of the Vanadium run
* @param ceriaNo number of the Ceria run
*
* @return Suggested name for a new calibration file, following
* ENGIN-X practices
*/
std::string EnggDiffractionPresenter::buildCalibrateSuggestedFilename(
const std::string &vanNo, const std::string &ceriaNo) const {
// default and only one supported
std::string instStr = g_enginxStr;
std::string nameAppendix = "_both_banks";
if ("ENGIN-X" != m_view->currentInstrument()) {
instStr = "UNKNOWNINST";
nameAppendix = "_calibration";
}
// default extension for calibration files
const std::string calibExt = ".prm";
std::string sugg =
instStr + "_" + vanNo + "_" + ceriaNo + nameAppendix + calibExt;
return sugg;
}
/**
* 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
*/
void EnggDiffractionPresenter::doCalib(const EnggDiffCalibSettings &cs,
const std::string &vanNo,
const std::string &ceriaNo,
const std::string &outFilename) {
Federico Montesino Pouzols
committed
ITableWorkspace_sptr vanIntegWS;
MatrixWorkspace_sptr vanCurvesWS;
MatrixWorkspace_sptr ceriaWS;
Federico Montesino Pouzols
committed
loadOrCalcVanadiumWorkspaces(vanNo, cs.m_inputDirCalib, vanIntegWS,
vanCurvesWS, cs.m_forceRecalcOverwrite);
Federico Montesino Pouzols
committed
const std::string instStr = m_view->currentInstrument();
try {
auto load = Algorithm::fromString("Load");
load->initialize();
load->setPropertyValue("Filename", instStr + ceriaNo);
std::string ceriaWSName = "engggui_calibration_sample_ws";
load->setPropertyValue("OutputWorkspace", ceriaWSName);
load->execute();
AnalysisDataServiceImpl &ADS = Mantid::API::AnalysisDataService::Instance();
ceriaWS = ADS.retrieveWS<MatrixWorkspace>(ceriaWSName);
} catch (std::runtime_error &re) {
Federico Montesino Pouzols
committed
g_log.error()
<< "Error while loading calibration sample data. "
"Could not run the algorithm Load succesfully for the calibration "
"sample (run number: " +
ceriaNo + "). Error description: " + re.what() +
" Please check also the previous log messages for details.";
Federico Montesino Pouzols
committed
throw;
}
// Bank 1 and 2 - ENGIN-X
const size_t numBanks = 2;
std::vector<double> difc, tzero;
Federico Montesino Pouzols
committed
difc.resize(numBanks);
tzero.resize(numBanks);
for (size_t i = 0; i < difc.size(); i++) {
auto alg = Algorithm::fromString("EnggCalibrate");
try {
alg->initialize();
alg->setProperty("InputWorkspace", ceriaWS);
Federico Montesino Pouzols
committed
alg->setProperty("VanIntegrationWorkspace", vanIntegWS);
alg->setProperty("VanCurvesWorkspace", vanCurvesWS);
alg->setPropertyValue("Bank", boost::lexical_cast<std::string>(i + 1));
// TODO: figure out what should be done about the list of expected peaks
Federico Montesino Pouzols
committed
// to EnggCalibrate => it should be a default, as in EnggFitPeaks, that
// should be fixed in a nother ticket/issue
alg->setPropertyValue(
"ExpectedPeaks",
"3.1243, 2.7057, 1.9132, 1.6316, 1.5621, "
"1.3529, 1.2415, 1.2100, 1.1046, 1.0414, 0.9566, 0.9147, 0.9019, "
"0.8556, 0.8252, 0.8158, 0.7811");
alg->setPropertyValue("OutputParametersTableName",
"engggui_calibration_bank_" +
boost::lexical_cast<std::string>(i + 1));
alg->execute();
} catch (std::runtime_error &re) {
Federico Montesino Pouzols
committed
g_log.error() << "Error in calibration. ",
"Could not run the algorithm EnggCalibrate succesfully for bank " +
boost::lexical_cast<std::string>(i) + ". Error description: " +
re.what() + " Please check also the log messages for details.";
}
difc[i] = alg->getProperty("Difc");
tzero[i] = alg->getProperty("Zero");
g_log.notice() << " * Bank " << i + 1 << " calibrated, "
Federico Montesino Pouzols
committed
<< "difc: " << difc[i] << ", zero: " << tzero[i]
<< std::endl;
Federico Montesino Pouzols
committed
// Double horror: 1st use a python script
// 2nd: because runPythonCode does this by emitting a signal that goes to
// MantidPlot,
// it has to be done in the view (which is a UserSubWindow).
Poco::Path outFullPath(cs.m_inputDirCalib);
outFullPath.append(outFilename);
m_view->writeOutCalibFile(outFullPath.toString(), difc, tzero);
g_log.notice() << "Calibration file written as " << outFullPath.toString()
<< std::endl;
Federico Montesino Pouzols
committed
* Perform checks specific to normal/basic run focusing in addition to
* the general checks for any focusing (as done by
* inputChecksBeforeFocus() which is called from this method). Use
* always before running 'Focus'
*
* @param runNo run number to focus
Federico Montesino Pouzols
committed
* @param banks which banks to consider in the focusing
* @throws std::invalid_argument with an informative message.
Federico Montesino Pouzols
committed
void EnggDiffractionPresenter::inputChecksBeforeFocusBasic(
const std::string &runNo, const std::vector<bool> &banks) {
if (runNo.empty()) {
Federico Montesino Pouzols
committed
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
const std::string msg = "The sample run number to focus cannot be "
"empty and must be an integer number.";
throw std::invalid_argument(msg);
}
inputChecksBanks(banks);
inputChecksBeforeFocus();
}
/**
* Perform checks specific to focusing in "cropped" mode, in addition
* to the general checks for any focusing (as done by
* inputChecksBeforeFocus() which is called from this method). Use
* always before running 'FocusCropped'
*
* @param runNo run number to focus
* @param banks which banks to consider in the focusing
* @param specIDs list of spectra (as usual csv list of spectra in Mantid)
*
* @throws std::invalid_argument with an informative message.
*/
void EnggDiffractionPresenter::inputChecksBeforeFocusCropped(
const std::string &runNo, const std::vector<bool> &banks,
const std::string &specIDs) {
if (runNo.empty()) {
throw std::invalid_argument("To focus cropped the sample run number cannot "
"be empty and must be an integer number.");
Federico Montesino Pouzols
committed
if (specIDs.empty()) {
throw std::invalid_argument("The list of spectrum IDs cannot be empty when "
"focusing in 'cropped' mode.");
Federico Montesino Pouzols
committed
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
inputChecksBanks(banks);
inputChecksBeforeFocus();
}
/**
* Perform checks specific to focusing in "texture" mode, in addition
* to the general checks for any focusing (as done by
* inputChecksBeforeFocus() which is called from this method). Use
* always before running 'FocusCropped'
*
* @param runNo run number to focus
* @param dgFile file with detector grouping info
*
* @throws std::invalid_argument with an informative message.
*/
void EnggDiffractionPresenter::inputChecksBeforeFocusTexture(
const std::string &runNo, const std::string &dgFile) {
if (runNo.empty()) {
throw std::invalid_argument("To focus texture banks the sample run number "
"cannot be empty and must be an integer "
"number.");
}
if (dgFile.empty()) {
throw std::invalid_argument("A detector grouping file needs to be "
"specified when focusing texture banks.");
}
inputChecksBeforeFocus();
}
void EnggDiffractionPresenter::inputChecksBanks(const std::vector<bool> &banks) {
if (0 == banks.size()) {
const std::string msg =
"Error in specification of banks found when starting the "
"focusing process. Cannot continue.";
g_log.error() << msg << std::endl;
throw std::invalid_argument(msg);
}
if (banks.end() == std::find(banks.begin(), banks.end(), true)) {
const std::string msg =
"EnggDiffraction GUI: not focusing, as all the banks "
"have been unselected. You probably forgot to select at least one.";
g_log.warning() << msg << std::endl;
throw std::invalid_argument(msg);
}
}
/**
* Performs several checks on the current focusing inputs and
* settings. This should be done before starting any focus work. The
* message return should be shown to the user as a visible message
* (pop-up, error log, etc.)
*
* @throws std::invalid_argument with an informative message.
*/
void EnggDiffractionPresenter::inputChecksBeforeFocus() {
EnggDiffCalibSettings cs = m_view->currentCalibSettings();
const std::string pixelCalib = cs.m_pixelCalibFilename;
if (pixelCalib.empty()) {
throw std::invalid_argument(
"You need to set a pixel (full) calibration in settings.");
}
}
Federico Montesino Pouzols
committed
* Builds the names of the output focused files (one per bank), given
* the sample run number and which banks should be focused.
* @param runNo number of the run for which we want a focused output
* file name
*
Federico Montesino Pouzols
committed
* @param banks for every bank, (true/false) to consider it or not for
* the focusing
Federico Montesino Pouzols
committed
* @return filenames (without the full path)
Federico Montesino Pouzols
committed
std::vector<std::string>
EnggDiffractionPresenter::outputFocusFilename(const std::string &runNo,
Federico Montesino Pouzols
committed
const std::vector<bool> &banks) {
const std::string instStr = m_view->currentInstrument();
Federico Montesino Pouzols
committed
std::vector<std::string> res;
for (size_t b = 1; b <= banks.size(); b++) {
res.push_back(instStr + runNo + "_focused_bank_" +
boost::lexical_cast<std::string>(banks[b]));
}
return res;
}
/**
* Start the focusing algorithm(s) without blocking the GUI. This is
* based on Qt connect / signals-slots so that it goes in sync with
* the Qt event loop. For that reason this class needs to be a
* Q_OBJECT.
*
* @param dir directory (full path) for the focused output files
Federico Montesino Pouzols
committed
* @param outFilenames full names for the output focused runs
* @param runNo input run number
* @param bank instrument bank to focus
*/
void EnggDiffractionPresenter::startAsyncFocusWorker(
Federico Montesino Pouzols
committed
const std::string &dir, const std::vector<std::string> &outFilenames,
const std::string &runNo, const std::vector<bool> &banks) {
delete m_workerThread;
m_workerThread = new QThread(this);
EnggDiffWorker *worker =
Federico Montesino Pouzols
committed
new EnggDiffWorker(this, dir, outFilenames, runNo, banks);
worker->moveToThread(m_workerThread);
connect(m_workerThread, SIGNAL(started()), worker, SLOT(focus()));
connect(worker, SIGNAL(finished()), this, SLOT(focusingFinished()));
// 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();
}
/**
* Produce a new focused output file. This is what threads/workers
* should use to run the calculations required to process a 'focus'
* push or similar from the user.
*
* @param dir directory (full path) for the output focused files
Federico Montesino Pouzols
committed
* @param outFilenames names for the output focused files (one per bank)
* @param runNo input run number
Federico Montesino Pouzols
committed
*
* @param banks for every bank, (true/false) to consider it or not for
* the focusing
Federico Montesino Pouzols
committed
void EnggDiffractionPresenter::doFocusRun(
const std::string &dir, const std::vector<std::string> &outFilenames,
const std::string &runNo, const std::vector<bool> &banks) {
g_log.notice() << "Generating new focusing workspace(s) and file(s) "
<< std::endl;
Poco::Path fpath(dir);
// TODO: this is almost 100% common with doNewCalibrate() - refactor
EnggDiffCalibSettings cs = m_view->currentCalibSettings();
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'
if (!cs.m_inputDirCalib.empty() && Poco::File(cs.m_inputDirCalib).exists()) {
conf.appendDataSearchDir(cs.m_inputDirCalib);
}
if (!cs.m_inputDirRaw.empty() && Poco::File(cs.m_inputDirRaw).exists()) {
conf.appendDataSearchDir(cs.m_inputDirRaw);
}
Federico Montesino Pouzols
committed
// focus all requested banks
for (size_t bidx = 0; bidx < banks.size(); bidx++) {
if (!banks[bidx])
continue;
const std::string fullFilename =
fpath.append(outFilenames[bidx]).toString();
g_log.notice() << "Generating new focused file (bank " +
boost::lexical_cast<std::string>(bidx + 1) +
") for run " + runNo +
" into: " << outFilenames[bidx] << std::endl;
try {
doFocusing(cs, fullFilename, runNo, bidx + 1);
m_focusFinishedOK = true;
} catch (std::runtime_error &) {
g_log.error()
<< "The focusing calculations failed. One of the algorithms"
"did not execute correctly. See log messages for details."
<< std::endl;
} catch (std::invalid_argument &ia) {
g_log.error()
<< "The focusing failed. Some input properties were not valid. "
"See log messages for details. Error: " << ia.what() << std::endl;
}
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
}
// restore initial data search paths
conf.setDataSearchDirs(tmpDirs);
}
/**
* Method (Qt slot) to call when the focusing work has finished,
* possibly from a separate thread but sometimes not (as in this
* presenter class' test).
*/
void EnggDiffractionPresenter::focusingFinished() {
if (!m_view)
return;
m_view->enableCalibrateAndFocusActions(true);
if (!m_focusFinishedOK) {
g_log.warning() << "The cablibration did not finished correctly."
<< std::endl;
} else {
g_log.notice() << "Focusing finished - focused run ready." << std::endl;
}
if (m_workerThread) {
delete m_workerThread;
m_workerThread = NULL;
}
}
/**
* Focuses a run, produces a focused workspace, and saves it into a
* file.
*
* @param cs user settings for calibration (this does not calibrate but
* uses calibration input files such as vanadium runs
*
Federico Montesino Pouzols
committed
* @param fullFilename full path for the output (focused) filename
* @param runNo input run to focus
* @param bank instrument bank to focus
*/
void EnggDiffractionPresenter::doFocusing(const EnggDiffCalibSettings &cs,
const std::string &fullFilename,
Federico Montesino Pouzols
committed
const std::string &runNo,
size_t bank) {
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
ITableWorkspace_sptr vanIntegWS;
MatrixWorkspace_sptr vanCurvesWS;
MatrixWorkspace_sptr inWS;
const std::string vanNo = m_view->currentVanadiumNo();
loadOrCalcVanadiumWorkspaces(vanNo, cs.m_inputDirCalib, vanIntegWS,
vanCurvesWS, cs.m_forceRecalcOverwrite);
const std::string inWSName = "engggui_focusing_input_ws";
const std::string instStr = m_view->currentInstrument();
try {
auto load = Algorithm::fromString("Load");
load->initialize();
load->setPropertyValue("Filename", instStr + runNo);
load->setPropertyValue("OutputWorkspace", inWSName);
load->execute();
AnalysisDataServiceImpl &ADS = Mantid::API::AnalysisDataService::Instance();
inWS = ADS.retrieveWS<MatrixWorkspace>(inWSName);
} catch (std::runtime_error &re) {
g_log.error()
<< "Error while loading sample data for focusing. "
"Could not run the algorithm Load succesfully for the focusing "
"sample (run number: " +
runNo + "). Error description: " + re.what() +
" Please check also the previous log messages for details.";
throw;
}
const std::string outWSName = "engggui_focusing_output_ws";
try {
auto alg = Algorithm::fromString("EnggFocus");
alg->initialize();
alg->setProperty("InputWorkspace", inWSName);
alg->setProperty("OutputWorkspace", outWSName);
alg->setProperty("VanIntegrationWorkspace", vanIntegWS);
alg->setProperty("VanCurvesWorkspace", vanCurvesWS);
alg->setPropertyValue("Bank", boost::lexical_cast<std::string>(bank));
// TODO: use detector positions (from calibrate full) when available
// alg->setProperty(DetectorPositions, TableWorkspace)
alg->execute();
bool plotFocusedWS = m_view->focusedOutWorkspace();
if (plotFocusedWS == true) {
m_view->plotFocusedSpectrum();
} catch (std::runtime_error &re) {
g_log.error() << "Error in calibration. ",
"Could not run the algorithm EnggCalibrate succesfully for bank " +
boost::lexical_cast<std::string>(bank) + ". Error description: " +
re.what() + " Please check also the log messages for details.";
throw;
}
g_log.notice() << "Produced focused workspace: " << outWSName << std::endl;
try {
g_log.debug() << "Going to save focused output into nexus file: "
<< fullFilename << std::endl;
auto alg = Algorithm::fromString("SaveNexus");
alg->initialize();
alg->setPropertyValue("InputWorkspace", outWSName);
alg->setPropertyValue("Filename", fullFilename);
alg->execute();
} catch (std::runtime_error &re) {
g_log.error() << "Error in calibration. ",
"Could not run the algorithm EnggCalibrate succesfully for bank " +
boost::lexical_cast<std::string>(bank) + ". Error description: " +
re.what() + " Please check also the log messages for details.";
throw;
g_log.notice() << "Saved focused workspace as file: " << fullFilename
<< std::endl;
* Produce the two workspaces that are required to apply Vanadium
* corrections. Try to load them if precalculated results are
* available from files, otherwise load the source Vanadium run
* workspace and do the calculations.
* @param vanNo Vanadium run number
* @param inputDirCalib The 'calibration files' input directory given
* in settings
* @param vanIntegWS workspace where to create/load the Vanadium
* spectra integration
*
* @param vanCurvesWS workspace where to create/load the Vanadium
* aggregated per-bank curve
*
* @param forceRecalc whether to calculate Vanadium corrections even
* if the files of pre-calculated results are found
void EnggDiffractionPresenter::loadOrCalcVanadiumWorkspaces(
const std::string &vanNo, const std::string &inputDirCalib,
ITableWorkspace_sptr &vanIntegWS, MatrixWorkspace_sptr &vanCurvesWS,
bool forceRecalc) {
bool foundPrecalc = false;
std::string preIntegFilename, preCurvesFilename;
findPrecalcVanadiumCorrFilenames(vanNo, inputDirCalib, preIntegFilename,
preCurvesFilename, foundPrecalc);
if (forceRecalc || !foundPrecalc) {
g_log.notice()
<< "Calculating Vanadium corrections. This may take a few seconds..."
<< std::endl;