Newer
Older
Russell Taylor
committed
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
Janik Zikovsky
committed
#include "MantidAlgorithms/AlignDetectors.h"
Gigg, Martyn Anthony
committed
#include "MantidAPI/FileProperty.h"
#include "MantidAPI/RawCountValidator.h"
#include "MantidAPI/WorkspaceUnitValidator.h"
Janik Zikovsky
committed
#include "MantidDataObjects/EventWorkspace.h"
#include "MantidDataObjects/OffsetsWorkspace.h"
#include "MantidKernel/CompositeValidator.h"
Janik Zikovsky
committed
#include "MantidKernel/PhysicalConstants.h"
Janik Zikovsky
committed
#include "MantidKernel/UnitFactory.h"
Russell Taylor
committed
#include <fstream>
Peterson, Peter
committed
#include <sstream>
Russell Taylor
committed
Janik Zikovsky
committed
using namespace Mantid::Kernel;
using namespace Mantid::API;
using namespace Mantid::Geometry;
using namespace Mantid::DataObjects;
using Mantid::DataObjects::OffsetsWorkspace;
namespace Mantid {
namespace Algorithms {
// Register the algorithm into the algorithm factory
Russell Taylor
committed
DECLARE_ALGORITHM(AlignDetectors)
Janik Zikovsky
committed
/// Applies the equation d=(TOF-tzero)/difc
struct func_difc_only {
func_difc_only(const double difc) : factor(1. / difc) {}
double operator()(const double tof) const { return factor * tof; }
/// 1./difc
double factor;
};
/// Applies the equation d=(TOF-tzero)/difc
struct func_difc_and_tzero {
func_difc_and_tzero(const double difc, const double tzero)
: factor(1. / difc), offset(-1. * tzero / difc) {}
double operator()(const double tof) const { return factor * tof + offset; }
/// 1./difc
double factor;
/// -tzero/difc
double offset;
};
struct func_difa {
func_difa(const double difc, const double difa, const double tzero) {
factor1 = -0.5 * difc / difa;
factor2 = 1. / difa;
factor3 = (factor1 * factor1) - (tzero / difa);
}
Janik Zikovsky
committed
double operator()(const double tof) const {
double second = std::sqrt((tof * factor2) + factor3);
if (second < factor1)
return factor1 - second;
else {
return factor1 + second;
}
}
Janik Zikovsky
committed
/// -0.5*difc/difa
double factor1;
/// 1/difa
double factor2;
/// (0.5*difc/difa)^2 - (tzero/difa)
double factor3;
};
class ConversionFactors {
public:
ConversionFactors(ITableWorkspace_const_sptr table) {
m_difcCol = table->getColumn("difc");
m_difaCol = table->getColumn("difa");
m_tzeroCol = table->getColumn("tzero");
this->generateDetidToRow(table);
}
Janik Zikovsky
committed
std::function<double(double)>
getConversionFunc(const std::set<detid_t> &detIds) {
const std::set<size_t> rows = this->getRow(detIds);
double difc = 0.;
double difa = 0.;
double tzero = 0.;
for (auto row = rows.begin(); row != rows.end(); ++row) {
difc += m_difcCol->toDouble(*row);
difa += m_difaCol->toDouble(*row);
tzero += m_tzeroCol->toDouble(*row);
}
if (rows.size() > 1) {
double norm = 1. / static_cast<double>(rows.size());
difc = norm * difc;
difa = norm * difa;
tzero = norm * tzero;
}
Janik Zikovsky
committed
if (difa == 0.) {
if (tzero == 0.) {
return func_difc_only(difc);
} else {
return func_difc_and_tzero(difc, tzero);
} else { // difa != 0.
return func_difa(difc, difa, tzero);
}
}
Janik Zikovsky
committed
private:
void generateDetidToRow(ITableWorkspace_const_sptr table) {
ConstColumnVector<int> detIDs = table->getVector("detid");
const size_t numDets = detIDs.size();
for (size_t i = 0; i < numDets; ++i) {
m_detidToRow[static_cast<detid_t>(detIDs[i])] = i;
}
}
Peterson, Peter
committed
std::set<size_t> getRow(const std::set<detid_t> &detIds) {
std::set<size_t> rows;
for (auto detId = detIds.begin(); detId != detIds.end(); ++detId) {
auto rowIter = m_detidToRow.find(*detId);
if (rowIter != m_detidToRow.end()) { // skip if not found
rows.insert(rowIter->second);
std::map<detid_t, size_t> m_detidToRow;
Column_const_sptr m_difcCol;
Column_const_sptr m_difaCol;
Column_const_sptr m_tzeroCol;
};
} // anonymous namespace
const std::string AlignDetectors::name() const { return "AlignDetectors"; }
int AlignDetectors::version() const { return 1; }
const std::string AlignDetectors::category() const { return "Diffraction\\Calibration"; }
const std::string AlignDetectors::summary() const {
return "Performs a unit change from TOF to dSpacing, correcting the X "
"values to account for small errors in the detector positions.";
Janik Zikovsky
committed
}
Russell Taylor
committed
/// (Empty) Constructor
Federico Montesino Pouzols
committed
AlignDetectors::AlignDetectors() : m_numberOfSpectra(0) {
this->tofToDmap = NULL;
}
Russell Taylor
committed
/// Destructor
AlignDetectors::~AlignDetectors() { delete this->tofToDmap; }
Russell Taylor
committed
//-----------------------------------------------------------------------
auto wsValidator = boost::make_shared<CompositeValidator>();
wsValidator->add<WorkspaceUnitValidator>("TOF");
wsValidator->add<RawCountValidator>();
Janik Zikovsky
committed
declareProperty(new WorkspaceProperty<API::MatrixWorkspace>(
"InputWorkspace", "", Direction::Input, wsValidator),
"A workspace with units of TOF");
Janik Zikovsky
committed
declareProperty(new WorkspaceProperty<API::MatrixWorkspace>(
"OutputWorkspace", "", Direction::Output),
"The name to use for the output workspace");
Janik Zikovsky
committed

Zhou, Wenduo
committed
std::vector<std::string> exts;
exts.push_back(".h5");
exts.push_back(".hd5");
exts.push_back(".hdf");

Zhou, Wenduo
committed
exts.push_back(".cal");
declareProperty(
new FileProperty("CalibrationFile", "", FileProperty::OptionalLoad, exts),
"Optional: The .cal file containing the position correction factors. "
"Either this or OffsetsWorkspace needs to be specified.");
declareProperty(
new WorkspaceProperty<ITableWorkspace>(
"CalibrationWorkspace", "", Direction::Input, PropertyMode::Optional),
"Optional: A Workspace containing the calibration information. Either "
"this or CalibrationFile needs to be specified.");
declareProperty(
new WorkspaceProperty<OffsetsWorkspace>(
"OffsetsWorkspace", "", Direction::Input, PropertyMode::Optional),
"Optional: A OffsetsWorkspace containing the calibration offsets. Either "
"this or CalibrationFile needs to be specified.");
// make group associations.
std::string calibrationGroup("Calibration");
setPropertyGroup("CalibrationFile", calibrationGroup);
setPropertyGroup("CalibrationWorkspace", calibrationGroup);
setPropertyGroup("OffsetsWorkspace", calibrationGroup);
}
std::map<std::string, std::string> AlignDetectors::validateInputs() {
std::map<std::string, std::string> result;
const std::string calFileName = getProperty("CalibrationFile");
if (!calFileName.empty())
numWays += 1;
ITableWorkspace_const_sptr calibrationWS =
getProperty("CalibrationWorkspace");
if (bool(calibrationWS))
numWays += 1;
OffsetsWorkspace_const_sptr offsetsWS = getProperty("OffsetsWorkspace");
if (bool(offsetsWS))
numWays += 1;
std::string message;
if (numWays == 0) {
message = "You must specify only one of CalibrationFile, "
"CalibrationWorkspace, OffsetsWorkspace.";
}
if (numWays > 1) {
message = "You must specify one of CalibrationFile, "
"CalibrationWorkspace, OffsetsWorkspace.";
}
if (!message.empty()) {
result["CalibrationFile"] = message;
result["CalibrationWorkspace"] = message;
}
void AlignDetectors::loadCalFile(MatrixWorkspace_sptr inputWS,
const std::string &filename) {
IAlgorithm_sptr alg = createChildAlgorithm("LoadDiffCal");
alg->setProperty("InputWorkspace", inputWS);
alg->setPropertyValue("Filename", filename);
alg->setProperty<bool>("MakeCalWorkspace", true);
alg->setProperty<bool>("MakeGroupingWorkspace", false);
alg->setProperty<bool>("MakeMaskWorkspace", false);
alg->setPropertyValue("WorkspaceName", "temp");
alg->executeAsChildAlg();
m_calibrationWS = alg->getProperty("OutputCalWorkspace");
}
void AlignDetectors::getCalibrationWS(MatrixWorkspace_sptr inputWS) {
m_calibrationWS = getProperty("CalibrationWorkspace");
if (m_calibrationWS)
return; // nothing more to do
OffsetsWorkspace_sptr offsetsWS = getProperty("OffsetsWorkspace");
if (offsetsWS) {
auto alg = createChildAlgorithm("ConvertDiffCal");
alg->setProperty("OffsetsWorkspace", offsetsWS);
alg->executeAsChildAlg();
m_calibrationWS = alg->getProperty("OutputWorkspace");
m_calibrationWS->setTitle(offsetsWS->getTitle());
return;
}
const std::string calFileName = getPropertyValue("CalibrationFile");
if (!calFileName.empty()) {
progress(0.0, "Reading calibration file");
loadCalFile(inputWS, calFileName);
return;
}
throw std::runtime_error("Failed to determine calibration information");
}
void setXAxisUnits(API::MatrixWorkspace_sptr outputWS) {
outputWS->getAxis(0)->unit() = UnitFactory::Instance().create("dSpacing");
Russell Taylor
committed
}
//-----------------------------------------------------------------------
Russell Taylor
committed
/** Executes the algorithm
* @throw Exception::FileError If the calibration file cannot be opened and
* read successfully
* @throw Exception::InstrumentDefinitionError If unable to obtain the
* source-sample distance
Russell Taylor
committed
*/
Russell Taylor
committed
// Get the input workspace
MatrixWorkspace_sptr inputWS = getProperty("InputWorkspace");
Russell Taylor
committed
Janik Zikovsky
committed
// Initialise the progress reporting object
m_numberOfSpectra = static_cast<int64_t>(inputWS->getNumberHistograms());
Janik Zikovsky
committed
// Check if its an event workspace
EventWorkspace_const_sptr eventW =
boost::dynamic_pointer_cast<const EventWorkspace>(inputWS);
if (eventW != NULL) {
Janik Zikovsky
committed
this->execEvent();
return;
Russell Taylor
committed
}
Roman Tolchenov
committed
API::MatrixWorkspace_sptr outputWS = getProperty("OutputWorkspace");
// If input and output workspaces are not the same, create a new workspace for
// the output
if (outputWS != inputWS) {
Russell Taylor
committed
outputWS = WorkspaceFactory::Instance().create(inputWS);
setProperty("OutputWorkspace", outputWS);
Russell Taylor
committed
}
Janik Zikovsky
committed
Russell Taylor
committed
// Set the final unit that our output workspace will have
Russell Taylor
committed
ConversionFactors converter = ConversionFactors(m_calibrationWS);
Progress progress(this, 0.0, 1.0, m_numberOfSpectra);
Russell Taylor
committed
// Loop over the histograms (detector spectra)
for (int64_t i = 0; i < m_numberOfSpectra; ++i) {
Russell Taylor
committed
PARALLEL_START_INTERUPT_REGION
Russell Taylor
committed
try {
// Get the input spectrum number at this workspace index
auto inSpec = inputWS->getSpectrum(size_t(i));
auto toDspacing = converter.getConversionFunc(inSpec->getDetectorIDs());
Russell Taylor
committed
// Get references to the x data
MantidVec &xOut = outputWS->dataX(i);
// Make sure reference to input X vector is obtained after output one
// because in the case
// where the input & output workspaces are the same, it might move if the
// vectors were shared.
const MantidVec &xIn = inSpec->readX();
std::transform(xIn.begin(), xIn.end(), xOut.begin(), toDspacing);
Russell Taylor
committed
// Copy the Y&E data
outputWS->dataY(i) = inSpec->readY();
outputWS->dataE(i) = inSpec->readE();
Russell Taylor
committed
} catch (Exception::NotFoundError &) {
Roman Tolchenov
committed
// Zero the data in this case
outputWS->dataX(i).assign(outputWS->readX(i).size(), 0.0);
outputWS->dataY(i).assign(outputWS->readY(i).size(), 0.0);
outputWS->dataE(i).assign(outputWS->readE(i).size(), 0.0);
Russell Taylor
committed
}
progress.report();
Russell Taylor
committed
PARALLEL_END_INTERUPT_REGION
Russell Taylor
committed
}
Russell Taylor
committed
PARALLEL_CHECK_INTERUPT_REGION
Russell Taylor
committed
}
//-----------------------------------------------------------------------
/**
* Execute the align detectors algorithm for an event workspace.
*/
void AlignDetectors::execEvent() {
// g_log.information("Processing event workspace");
// the calibration information is already read in at this point
// convert the input workspace into the event workspace we already know it is
const MatrixWorkspace_const_sptr matrixInputWS =
this->getProperty("InputWorkspace");
EventWorkspace_const_sptr inputWS =
boost::dynamic_pointer_cast<const EventWorkspace>(matrixInputWS);
// generate the output workspace pointer
API::MatrixWorkspace_sptr matrixOutputWS =
this->getProperty("OutputWorkspace");
EventWorkspace_sptr outputWS;
if (matrixOutputWS == matrixInputWS)
outputWS = boost::dynamic_pointer_cast<EventWorkspace>(matrixOutputWS);
else {
// Make a brand new EventWorkspace
outputWS = boost::dynamic_pointer_cast<EventWorkspace>(
API::WorkspaceFactory::Instance().create(
"EventWorkspace", inputWS->getNumberHistograms(), 2, 1));
// Copy geometry over.
API::WorkspaceFactory::Instance().initializeFromParent(inputWS, outputWS,
false);
// You need to copy over the data as well.
outputWS->copyDataFrom((*inputWS));
// Cast to the matrixOutputWS and save it
Janik Zikovsky
committed
matrixOutputWS = boost::dynamic_pointer_cast<MatrixWorkspace>(outputWS);
this->setProperty("OutputWorkspace", matrixOutputWS);
}
// Set the final unit that our output workspace will have
ConversionFactors converter = ConversionFactors(m_calibrationWS);
Janik Zikovsky
committed
Progress progress(this, 0.0, 1.0, m_numberOfSpectra);
PARALLEL_FOR_NO_WSP_CHECK()
for (int64_t i = 0; i < m_numberOfSpectra; ++i) {
PARALLEL_START_INTERUPT_REGION
Janik Zikovsky
committed
auto toDspacing = converter.getConversionFunc(
inputWS->getSpectrum(size_t(i))->getDetectorIDs());
outputWS->getEventList(i).convertTof(toDspacing);
progress.report();
PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION
if (outputWS->getTofMin() < 0.) {
Peterson, Peter
committed
std::stringstream msg;
msg << "Something wrong with the calibration. Negative minimum d-spacing "
"created. d_min = " << outputWS->getTofMin() << " d_max "
<< outputWS->getTofMax();
Peterson, Peter
committed
}
outputWS->clearMRU();
}
Janik Zikovsky
committed
} // namespace Algorithms
Russell Taylor
committed
} // namespace Mantid