Newer
Older
#include "MantidDataHandling/LoadDiffCal.h"
#include "MantidAPI/FileProperty.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/Progress.h"
#include "MantidAPI/TableRow.h"
#include "MantidDataHandling/LoadCalFile.h"
#include "MantidDataObjects/GroupingWorkspace.h"
#include "MantidDataObjects/MaskWorkspace.h"
#include "MantidDataObjects/TableWorkspace.h"
#include "MantidDataObjects/Workspace2D.h"
namespace Mantid {
namespace DataHandling {
using Mantid::API::FileProperty;
using Mantid::API::MatrixWorkspace;
using Mantid::API::MatrixWorkspace_sptr;
using Mantid::API::Progress;
using Mantid::API::ITableWorkspace;
using Mantid::API::ITableWorkspace_sptr;
using Mantid::DataObjects::GroupingWorkspace_sptr;
using Mantid::DataObjects::MaskWorkspace_sptr;
using Mantid::DataObjects::Workspace2D;
using Mantid::Kernel::Direction;
using Mantid::Kernel::PropertyWithValue;
using namespace H5;
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(LoadDiffCal)
//----------------------------------------------------------------------------------------------
/** Constructor
*/
LoadDiffCal::LoadDiffCal() {}
//----------------------------------------------------------------------------------------------
/** Destructor
*/
LoadDiffCal::~LoadDiffCal() {}
//----------------------------------------------------------------------------------------------
/// Algorithms name for identification. @see Algorithm::name
const std::string LoadDiffCal::name() const { return "LoadDiffCal"; }
/// Algorithm's version for identification. @see Algorithm::version
int LoadDiffCal::version() const { return 1; }
/// Algorithm's category for identification. @see Algorithm::category
const std::string LoadDiffCal::category() const {
return "DataHandling\\Instrument;Diffraction\\DataHandling";
}
/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary
const std::string LoadDiffCal::summary() const {
return "Loads a calibration file for powder diffraction";
}
//----------------------------------------------------------------------------------------------
/** Initialize the algorithm's properties.
*/
void LoadDiffCal::init() {
// 3 properties for getting the right instrument
LoadCalFile::getInstrument3WaysInit(this);
declareProperty(new FileProperty("Filename", "", FileProperty::Load,
{".h5", ".hd5", ".hdf", ".cal"}),
"Path to the .h5 file.");
declareProperty(new PropertyWithValue<bool>("MakeGroupingWorkspace", true,
Direction::Input),
"Set to true to create a GroupingWorkspace with called "
"WorkspaceName_group.");
declareProperty(
new PropertyWithValue<bool>("MakeCalWorkspace", true, Direction::Input),
"Set to true to create a CalibrationWorkspace with called "
"WorkspaceName_cal.");
new PropertyWithValue<bool>("MakeMaskWorkspace", true, Direction::Input),
"Set to true to create a MaskWorkspace with called WorkspaceName_mask.");
new PropertyWithValue<std::string>("WorkspaceName", "", Direction::Input),
"The base of the output workspace names. Names will have '_group', "
"'_cal', '_mask' appended to them.");
std::string grpName("Calibration Validation");
declareProperty("TofMin", 0., "Minimum for TOF axis. Defaults to 0.");
declareProperty("TofMax", EMPTY_DBL(),
"Maximum for TOF axis. Defaults to Unused.");
setPropertyGroup("TofMin", grpName);
setPropertyGroup("TofMax", grpName);
namespace { // anonymous
std::string readString(H5File &file, const std::string &path) {
try {
DataSet data = file.openDataSet(path);
std::string value;
data.read(value, data.getDataType(), data.getSpace());
return value;
} catch (H5::FileIException &e) {
} catch (H5::GroupIException &e) {
std::vector<NumT> readArrayCoerce(DataSet &dataset,
const DataType &desiredDataType) {
std::vector<NumT> result;
DataType dataType = dataset.getDataType();
DataSpace dataSpace = dataset.getSpace();
if (desiredDataType == dataType) {
result.resize(dataSpace.getSelectNpoints());
dataset.read(&result[0], dataType, dataSpace);
} else if (PredType::NATIVE_UINT32 == dataType) {
std::vector<uint32_t> temp(dataSpace.getSelectNpoints());
dataset.read(&temp[0], dataType, dataSpace);
result.assign(temp.begin(), temp.end());
} else if (PredType::NATIVE_FLOAT == dataType) {
std::vector<float> temp(dataSpace.getSelectNpoints());
dataset.read(&temp[0], dataType, dataSpace);
for (float value : temp)
result.push_back(static_cast<NumT>(value));
} else {
throw DataTypeIException();
}
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
bool endswith(const std::string &str, const std::string &ending) {
if (ending.size() > str.size()) {
return false;
}
return std::equal(str.begin() + str.size() - ending.size(), str.end(),
ending.begin());
}
void setGroupWSProperty(API::Algorithm *alg, const std::string &prefix,
GroupingWorkspace_sptr wksp) {
alg->declareProperty(
new WorkspaceProperty<DataObjects::GroupingWorkspace>(
"OutputGroupingWorkspace", prefix + "_group", Direction::Output),
"Set the the output GroupingWorkspace, if any.");
alg->setProperty("OutputGroupingWorkspace", wksp);
}
void setMaskWSProperty(API::Algorithm *alg, const std::string &prefix,
MaskWorkspace_sptr wksp) {
alg->declareProperty(
new WorkspaceProperty<DataObjects::MaskWorkspace>(
"OutputMaskWorkspace", prefix + "_mask", Direction::Output),
"Set the the output MaskWorkspace, if any.");
alg->setProperty("OutputMaskWorkspace", wksp);
}
void setCalWSProperty(API::Algorithm *alg, const std::string &prefix,
ITableWorkspace_sptr wksp) {
alg->declareProperty(
new WorkspaceProperty<ITableWorkspace>(
"OutputCalWorkspace", prefix + "_cal", Direction::Output),
"Set the output Diffraction Calibration workspace, if any.");
alg->setProperty("OutputCalWorkspace", wksp);
}
std::vector<double> LoadDiffCal::readDoubleArray(Group &group,
const std::string &name) {
std::vector<double> result;
try {
DataSet dataset = group.openDataSet(name);
result = readArrayCoerce<double>(dataset, PredType::NATIVE_DOUBLE);
} catch (H5::GroupIException &e) {
UNUSED_ARG(e);
g_log.information() << "Failed to open dataset \"" << name << "\"\n";
} catch (H5::DataTypeIException &e) {
g_log.information() << "DataSet \"" << name << "\" should be double"
<< "\n";
for (double &value : result) {
if (std::abs(value) < 1.e-10) {
value = 0.;
} else if (value != value) { // check for NaN
value = 0.;
std::vector<int32_t> LoadDiffCal::readInt32Array(Group &group,
const std::string &name) {
std::vector<int32_t> result;
try {
DataSet dataset = group.openDataSet(name);
result = readArrayCoerce<int32_t>(dataset, PredType::NATIVE_INT32);
} catch (H5::GroupIException &e) {
UNUSED_ARG(e);
g_log.information() << "Failed to open dataset \"" << name << "\"\n";
} catch (H5::DataTypeIException &e) {
g_log.information() << "DataSet \"" << name << "\" should be int32"
<< "\n";
}
void LoadDiffCal::getInstrument(H5File &file) {
// don't bother if there isn't a mask or grouping requested
bool makeMask = getProperty("MakeMaskWorkspace");
bool makeGrouping = getProperty("MakeGroupingWorkspace");
if ((!makeMask) & (!makeGrouping))
return;
// see if the user specified the instrument independently
if (LoadCalFile::instrumentIsSpecified(this)) {
m_instrument = LoadCalFile::getInstrument3Ways(this);
return;
}
std::string idf =
readString(file, "/calibration/instrument/instrument_source");
std::string instrumentName = readString(file, "/calibration/instrument/name");
g_log.debug() << "IDF : " << idf << "\n"
<< "NAME: " << instrumentName << "\n";
API::Algorithm_sptr childAlg =
this->createChildAlgorithm("LoadInstrument", 0.0, 0.1);
MatrixWorkspace_sptr tempWS(new Workspace2D());
childAlg->setProperty<MatrixWorkspace_sptr>("Workspace", tempWS);
if (idf.empty()) {
childAlg->setPropertyValue("InstrumentName", instrumentName);
} else {
childAlg->setPropertyValue("Filename", idf);
}
childAlg->setProperty("RewriteSpectraMap",
Mantid::Kernel::OptionalBool(false));
m_instrument = tempWS->getInstrument();
g_log.information() << "Loaded instrument \"" << m_instrument->getName()
<< "\" from \"" << m_instrument->getFilename() << "\"\n";
void LoadDiffCal::makeGroupingWorkspace(const std::vector<int32_t> &detids,
const std::vector<int32_t> &groups) {
bool makeWS = getProperty("MakeGroupingWorkspace");
if (!makeWS) {
g_log.information("Not making a GroupingWorkspace");
return;
}
size_t numDet = detids.size();
Progress progress(this, .4, .6, numDet);
GroupingWorkspace_sptr wksp =
boost::make_shared<DataObjects::GroupingWorkspace>(m_instrument);
wksp->setTitle(m_filename);
wksp->mutableRun().addProperty("Filename", m_filename);
for (size_t i = 0; i < numDet; ++i) {
detid_t detid = static_cast<detid_t>(detids[i]);
wksp->setValue(detid, groups[i]);
progress.report();
}
setGroupWSProperty(this, m_workspaceName, wksp);
void LoadDiffCal::makeMaskWorkspace(const std::vector<int32_t> &detids,
const std::vector<int32_t> &use) {
bool makeWS = getProperty("MakeMaskWorkspace");
if (!makeWS) {
g_log.information("Not making a MaskWorkspace");
return;
}
size_t numDet = detids.size();
Progress progress(this, .6, .8, numDet);
MaskWorkspace_sptr wksp =
boost::make_shared<DataObjects::MaskWorkspace>(m_instrument);
wksp->setTitle(m_filename);
wksp->mutableRun().addProperty("Filename", m_filename);
for (size_t i = 0; i < numDet; ++i) {
bool shouldUse = (use[i] > 0);
detid_t detid = static_cast<detid_t>(detids[i]);
// in maskworkspace 0=use, 1=dontuse
wksp->setMasked(detid, !shouldUse);
wksp->setValue(detid, (shouldUse ? 0. : 1.));
progress.report();
}
setMaskWSProperty(this, m_workspaceName, wksp);
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
namespace { // anonymous namespace
double calcTofMin(const double difc, const double difa, const double tzero,
const double tofmin) {
if (difa == 0.) {
if (tzero != 0.) {
// check for negative d-spacing
return std::max<double>(-1. * tzero, tofmin);
}
} else if (difa > 0) {
// check for imaginary part in quadratic equation
return std::max<double>(tzero - .25 * difc * difc / difa, tofmin);
}
// everything else is fine so just return supplied tofmin
return tofmin;
}
double calcTofMax(const double difc, const double difa, const double tzero,
const double tofmax) {
if (difa < 0.) {
// check for imaginary part in quadratic equation
return std::min<double>(tzero - .25 * difc * difc / difa, tofmax);
}
// everything else is fine so just return supplied tofmax
return tofmax;
}
} // end of anonymous namespace
void LoadDiffCal::makeCalWorkspace(const std::vector<int32_t> &detids,
const std::vector<double> &difc,
const std::vector<double> &difa,
const std::vector<double> &tzero,
const std::vector<int32_t> &dasids,
const std::vector<double> &offsets) {
bool makeWS = getProperty("MakeCalWorkspace");
if (!makeWS) {
g_log.information("Not making a calibration workspace");
return;
}
size_t numDet = detids.size();
Progress progress(this, .8, 1., numDet);
bool haveDasids = !dasids.empty();
bool haveOffsets = !offsets.empty();
double tofMin = getProperty("TofMin");
double tofMax = getProperty("TofMax");
bool useTofMax = !isEmpty(tofMax);
ITableWorkspace_sptr wksp = boost::make_shared<DataObjects::TableWorkspace>();
wksp->setTitle(m_filename);
wksp->addColumn("int", "detid");
wksp->addColumn("double", "difc");
wksp->addColumn("double", "difa");
wksp->addColumn("double", "tzero");
// only add these columns if they have values
if (haveDasids)
wksp->addColumn("int", "dasid");
if (haveOffsets)
wksp->addColumn("double", "offset");
// columns for valid range of data
wksp->addColumn("double", "tofmin");
if (useTofMax)
wksp->addColumn("double", "tofmax");
size_t badCount = 0;
for (size_t i = 0; i < numDet; ++i) {
API::TableRow newrow = wksp->appendRow();
newrow << detids[i];
newrow << difc[i];
newrow << difa[i];
newrow << tzero[i];
if (haveDasids)
newrow << dasids[i];
if (haveOffsets)
newrow << offsets[i];
// calculate tof range for information
const double tofMinRow = calcTofMin(difc[i], difa[i], tzero[i], tofMin);
std::stringstream msg;
if (tofMinRow != tofMin) {
msg << "TofMin shifted from " << tofMin << " to " << tofMinRow << " ";
}
newrow << tofMinRow;
if (useTofMax) {
const double tofMaxRow = calcTofMax(difc[i], difa[i], tzero[i], tofMax);
newrow << tofMaxRow;
if (tofMaxRow != tofMax) {
msg << "TofMax shifted from " << tofMax << " to " << tofMaxRow;
}
}
if (!msg.str().empty()) {
badCount += 1;
std::stringstream longMsg;
longMsg << "[detid=" << detids[i];
if (haveDasids)
longMsg << ", dasid=" << dasids[i];
longMsg << "] " << msg.str();
this->g_log.warning(longMsg.str());
}
if (badCount > 0) {
this->g_log.warning() << badCount
<< " rows have reduced time-of-flight range\n";
}
setCalWSProperty(this, m_workspaceName, wksp);
}
void LoadDiffCal::runLoadCalFile() {
bool makeCalWS = getProperty("MakeCalWorkspace");
bool makeMaskWS = getProperty("MakeMaskWorkspace");
bool makeGroupWS = getProperty("MakeGroupingWorkspace");
auto alg = createChildAlgorithm("LoadCalFile", 0., 1.);
alg->setPropertyValue("CalFilename", m_filename);
alg->setPropertyValue("InputWorkspace", getPropertyValue("InputWorkspace"));
alg->setPropertyValue("InstrumentName", getPropertyValue("InstrumentName"));
alg->setPropertyValue("InstrumentFilename",
getPropertyValue("InstrumentFilename"));
alg->setProperty<bool>("MakeOffsetsWorkspace", makeCalWS);
alg->setProperty<bool>("MakeGroupingWorkspace", makeGroupWS);
alg->setProperty<bool>("MakeMaskWorkspace", makeMaskWS);
alg->setPropertyValue("WorkspaceName", m_workspaceName);
alg->executeAsChildAlg();
if (makeCalWS) {
ITableWorkspace_sptr wksp = alg->getProperty("OutputCalWorkspace");
setCalWSProperty(this, m_workspaceName, wksp);
}
if (makeMaskWS) {
MatrixWorkspace_sptr wksp = alg->getProperty("OutputMaskWorkspace");
setMaskWSProperty(
this, m_workspaceName,
boost::dynamic_pointer_cast<DataObjects::MaskWorkspace>(wksp));
}
if (makeGroupWS) {
GroupingWorkspace_sptr wksp = alg->getProperty("OutputGroupingWorkspace");
setGroupWSProperty(this, m_workspaceName, wksp);
}
}
//----------------------------------------------------------------------------------------------
/** Execute the algorithm.
*/
void LoadDiffCal::exec() {
m_filename = getPropertyValue("Filename");
m_workspaceName = getPropertyValue("WorkspaceName");
if (endswith(m_filename, ".cal")) {
runLoadCalFile();
return;
}
// read in everything from the file
H5File file(m_filename, H5F_ACC_RDONLY);
H5::Exception::dontPrint();
getInstrument(file);
Progress progress(this, 0.1, 0.4, 8);
Group calibrationGroup;
try {
calibrationGroup = file.openGroup("calibration");
} catch (FileIException &e) {
e.printError();
throw std::runtime_error("Did not find group \"/calibration\"");
}
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
progress.report("Reading detid");
std::vector<int32_t> detids = readInt32Array(calibrationGroup, "detid");
progress.report("Reading dasid");
std::vector<int32_t> dasids = readInt32Array(calibrationGroup, "dasid");
progress.report("Reading group");
std::vector<int32_t> groups = readInt32Array(calibrationGroup, "group");
progress.report("Reading use");
std::vector<int32_t> use = readInt32Array(calibrationGroup, "use");
progress.report("Reading difc");
std::vector<double> difc = readDoubleArray(calibrationGroup, "difc");
progress.report("Reading difa");
std::vector<double> difa = readDoubleArray(calibrationGroup, "difa");
progress.report("Reading tzero");
std::vector<double> tzero = readDoubleArray(calibrationGroup, "tzero");
progress.report("Reading offset");
std::vector<double> offset = readDoubleArray(calibrationGroup, "offset");
file.close();
// verify the minimum is present
if (detids.empty()) {
throw std::runtime_error(
"File was missing required field \"/calibraion/detid\"");
}
if (difc.empty()) {
throw std::runtime_error(
"File was missing required field \"/calibraion/difc\"");
}
// fix up empty arrays
if (groups.empty())
groups.assign(detids.size(), 1); // all go to one spectrum
if (use.empty())
use.assign(detids.size(), 1); // use everything
if (difa.empty())
difa.assign(detids.size(), 0.); // turn off difa
if (tzero.empty())
tzero.assign(detids.size(), 0.); // turn off tzero
// create the appropriate output workspaces
makeGroupingWorkspace(detids, groups);
makeMaskWorkspace(detids, use);
makeCalWorkspace(detids, difc, difa, tzero, dasids, offset);
}
} // namespace DataHandling