Newer
Older
Doucet, Mathieu
committed
#include "MantidAlgorithms/CalculateEfficiency.h"
#include "MantidAPI/SpectrumInfo.h"
Federico Montesino Pouzols
committed
#include "MantidAPI/WorkspaceFactory.h"
#include "MantidDataObjects/EventWorkspace.h"
#include "MantidDataObjects/EventList.h"
Federico Montesino Pouzols
committed
#include "MantidGeometry/IDetector.h"
#include "MantidKernel/BoundedValidator.h"
#include "MantidGeometry/Instrument.h"
#include "MantidGeometry/ICompAssembly.h"
#include "MantidGeometry/IDTypes.h"
#include "MantidGeometry/Instrument/Detector.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidGeometry/Instrument/RectangularDetector.h"
Doucet, Mathieu
committed
namespace Mantid {
namespace Algorithms {
Doucet, Mathieu
committed
// Register the class into the algorithm factory
DECLARE_ALGORITHM(CalculateEfficiency)
using namespace Kernel;
using namespace API;
using namespace Geometry;
using namespace DataObjects;
Doucet, Mathieu
committed
/** Initialization method.
*
*/
void CalculateEfficiency::init() {
Doucet, Mathieu
committed
declareProperty(
make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input),
"The workspace containing the flood data");
Doucet, Mathieu
committed
declareProperty(
make_unique<WorkspaceProperty<>>("OutputWorkspace", "",
Direction::Output),
"The name of the workspace to be created as the output of the algorithm");
Doucet, Mathieu
committed
auto positiveDouble = boost::make_shared<BoundedValidator<double>>();
Doucet, Mathieu
committed
positiveDouble->setLower(0);
declareProperty(
"MinEfficiency", EMPTY_DBL(), positiveDouble,
Doucet, Mathieu
committed
"Minimum efficiency for a pixel to be considered (default: no minimum).");
declareProperty(
"MaxEfficiency", EMPTY_DBL(), positiveDouble,
Doucet, Mathieu
committed
"Maximum efficiency for a pixel to be considered (default: no maximum).");
declareProperty("MaskedFullComponent", "",
"Component Name to fully mask according to the IDF file.");
declareProperty(
make_unique<ArrayProperty<int>>("MaskedEdges"),
"Number of pixels to mask on the edges: X-low, X-high, Y-low, Y-high");
declareProperty(
"MaskedComponent", "",
"Component Name to mask the edges according to the IDF file.");
Doucet, Mathieu
committed
}
/** Executes the algorithm
*
*/
void CalculateEfficiency::exec() {
Doucet, Mathieu
committed
// Minimum efficiency. Pixels with lower efficiency will be masked
double min_eff = getProperty("MinEfficiency");
// Maximum efficiency. Pixels with higher efficiency will be masked
double max_eff = getProperty("MaxEfficiency");
// Get the input workspace
MatrixWorkspace_sptr inputWS = getProperty("InputWorkspace");
MatrixWorkspace_sptr rebinnedWS; // = inputWS;
Doucet, Mathieu
committed
// BioSANS has 2 detectors and reduces one at the time: one is masked!
// We must use that masked detector
const std::string maskedFullComponent =
getPropertyValue("MaskedFullComponent");
if (!maskedFullComponent.empty()) {
g_log.debug() << "CalculateEfficiency: Masking Full Component: "
<< maskedFullComponent << "\n";
maskComponent(*inputWS, maskedFullComponent);
// BioSANS has 2 detectors and the front masks the back!!!!
// We must mask the shaded part to calculate efficency
std::vector<int> maskedEdges = getProperty("MaskedEdges");
if (!maskedEdges.empty() && (maskedEdges[0] > 0 || maskedEdges[1] > 0 ||
maskedEdges[2] > 0 || maskedEdges[3] > 0)) {
g_log.debug() << "CalculateEfficiency: Masking edges length = "
<< maskedEdges.size() << ")"
<< " of the component " << maskedFullComponent << "\n";
const std::string maskedComponent = getPropertyValue("MaskedComponent");
maskEdges(inputWS, maskedEdges[0], maskedEdges[1], maskedEdges[2],
maskedEdges[3], maskedComponent);
}
Doucet, Mathieu
committed
// Now create the output workspace
MatrixWorkspace_sptr outputWS; // = getProperty("OutputWorkspace");
Doucet, Mathieu
committed
// DataObjects::EventWorkspace_const_sptr inputEventWS =
// boost::dynamic_pointer_cast<const EventWorkspace>(inputWS);
Doucet, Mathieu
committed
IAlgorithm_sptr childAlg = createChildAlgorithm("Integration", 0.0, 0.2);
childAlg->setProperty<MatrixWorkspace_sptr>("InputWorkspace", inputWS);
childAlg->executeAsChildAlg();
rebinnedWS = childAlg->getProperty("OutputWorkspace");
Doucet, Mathieu
committed
Doucet, Mathieu
committed
outputWS = WorkspaceFactory::Instance().create(rebinnedWS);
WorkspaceFactory::Instance().initializeFromParent(*inputWS, *outputWS, false);
for (int i = 0; i < static_cast<int>(rebinnedWS->getNumberHistograms());
i++) {
outputWS->setSharedX(i, rebinnedWS->sharedX(i));
Doucet, Mathieu
committed
}
setProperty("OutputWorkspace", outputWS);
Doucet, Mathieu
committed
Doucet, Mathieu
committed
double sum = 0.0;
double err = 0.0;
int npixels = 0;
// Loop over spectra and sum all the counts to get normalization
// Skip monitors and masked detectors
sumUnmaskedDetectors(rebinnedWS, sum, err, npixels);
// Normalize each detector pixel by the sum we just found to get the
// relative efficiency. If the minimum and maximum efficiencies are
// provided, the pixels falling outside this range will be marked
// as 'masked' in both the input and output workspace.
// We mask detectors in the input workspace so that we can resum the
// counts to find a new normalization factor that takes into account
// the newly masked detectors.
normalizeDetectors(rebinnedWS, outputWS, sum, err, npixels, min_eff, max_eff);
if (!isEmpty(min_eff) || !isEmpty(max_eff)) {
Doucet, Mathieu
committed
// Recompute the normalization, excluding the pixels that were outside
// the acceptable efficiency range.
sumUnmaskedDetectors(rebinnedWS, sum, err, npixels);
// Now that we have a normalization factor that excludes bad pixels,
// recompute the relative efficiency.
// We pass EMPTY_DBL() to avoid masking pixels that might end up high or low
// after the new normalization.
normalizeDetectors(rebinnedWS, outputWS, sum, err, npixels, EMPTY_DBL(),
EMPTY_DBL());
Doucet, Mathieu
committed
}
}
/*
* Sum up all the unmasked detector pixels.
*
* @param rebinnedWS: workspace where all the wavelength bins have been grouped
*together
Doucet, Mathieu
committed
* @param sum: sum of all the unmasked detector pixels (counts)
* @param error: error on sum (counts)
* @param nPixels: number of unmasked detector pixels that contributed to sum
*/
void CalculateEfficiency::sumUnmaskedDetectors(MatrixWorkspace_sptr rebinnedWS,
double &sum, double &error,
int &nPixels) {
// Number of spectra
const int numberOfSpectra =
static_cast<int>(rebinnedWS->getNumberHistograms());
sum = 0.0;
error = 0.0;
nPixels = 0;
const auto &spectrumInfo = rebinnedWS->spectrumInfo();
for (int i = 0; i < numberOfSpectra; i++) {
progress(0.2 + 0.2 * i / numberOfSpectra, "Computing sensitivity");
// Skip if we have a monitor or if the detector is masked.
if (spectrumInfo.isMonitor(i) || spectrumInfo.isMasked(i))
continue;
// Retrieve the spectrum into a vector
auto &YValues = rebinnedWS->y(i);
auto &YErrors = rebinnedWS->e(i);
sum += YValues[0];
error += YErrors[0] * YErrors[0];
nPixels++;
}
Doucet, Mathieu
committed
g_log.debug() << "sumUnmaskedDetectors: unmasked pixels = " << nPixels
<< " from a total of " << numberOfSpectra << "\n";
Doucet, Mathieu
committed
}
/*
* Normalize each detector to produce the relative detector efficiency.
* Pixels that fall outside those efficiency limits will be masked in both
* the input and output workspaces.
*
* @param rebinnedWS: input workspace
* @param outputWS: output workspace containing the relative efficiency
* @param sum: sum of all the unmasked detector pixels (counts)
* @param error: error on sum (counts)
* @param nPixels: number of unmasked detector pixels that contributed to sum
*/
void CalculateEfficiency::normalizeDetectors(MatrixWorkspace_sptr rebinnedWS,
MatrixWorkspace_sptr outputWS,
double sum, double error,
int nPixels, double min_eff,
double max_eff) {
// Number of spectra
const size_t numberOfSpectra = rebinnedWS->getNumberHistograms();
// Empty vector to store the pixels that outside the acceptable efficiency
// range
std::vector<size_t> dets_to_mask;
const auto &spectrumInfo = rebinnedWS->spectrumInfo();
for (size_t i = 0; i < numberOfSpectra; i++) {
const double currProgress =
0.4 +
0.2 * (static_cast<double>(i) / static_cast<double>(numberOfSpectra));
progress(currProgress, "Computing sensitivity");
// If this spectrum is masked, skip to the next one
if (spectrumInfo.isMasked(i))
continue;
// Retrieve the spectrum into a vector
auto &YIn = rebinnedWS->y(i);
auto &EIn = rebinnedWS->e(i);
auto &YOut = outputWS->mutableY(i);
auto &EOut = outputWS->mutableE(i);
// If this detector is a monitor, skip to the next one
if (spectrumInfo.isMonitor(i)) {
YOut[0] = 1.0;
EOut[0] = 0.0;
continue;
Doucet, Mathieu
committed
}
// Normalize counts to get relative efficiency
YOut[0] = nPixels / sum * YIn[0];
const double err_sum = YIn[0] / sum * error;
EOut[0] = nPixels / std::abs(sum) *
std::sqrt(EIn[0] * EIn[0] + err_sum * err_sum);
// Mask this detector if the signal is outside the acceptable band
if (!isEmpty(min_eff) && YOut[0] < min_eff)
dets_to_mask.push_back(i);
if (!isEmpty(max_eff) && YOut[0] > max_eff)
dets_to_mask.push_back(i);
}
g_log.debug() << "normalizeDetectors: Masked pixels outside the acceptable "
"efficiency range [" << min_eff << "," << max_eff
<< "] = " << dets_to_mask.size()
<< " from a total of non masked = " << nPixels
<< " (from a total number of spectra in the ws = "
<< numberOfSpectra << ")\n";
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
// If we identified pixels to be masked, mask them now
if (!dets_to_mask.empty()) {
// Mask detectors that were found to be outside the acceptable efficiency
// band
try {
IAlgorithm_sptr mask = createChildAlgorithm("MaskDetectors", 0.8, 0.9);
// First we mask detectors in the output workspace
mask->setProperty<MatrixWorkspace_sptr>("Workspace", outputWS);
mask->setProperty<std::vector<size_t>>("WorkspaceIndexList",
dets_to_mask);
mask->execute();
mask = createChildAlgorithm("MaskDetectors", 0.9, 1.0);
// Then we mask the same detectors in the input workspace
mask->setProperty<MatrixWorkspace_sptr>("Workspace", rebinnedWS);
mask->setProperty<std::vector<size_t>>("WorkspaceIndexList",
dets_to_mask);
mask->execute();
} catch (std::invalid_argument &err) {
std::stringstream e;
e << "Invalid argument to MaskDetectors Child Algorithm: " << err.what();
g_log.error(e.str());
} catch (std::runtime_error &err) {
std::stringstream e;
e << "Unable to successfully run MaskDetectors Child Algorithm: "
<< err.what();
g_log.error(e.str());
Doucet, Mathieu
committed
}
Doucet, Mathieu
committed
}
/**
* Fully masks one component named componentName
* @param ws :: workspace with the respective instrument assigned
* @param componentName :: must be a known CompAssembly.
*/
void CalculateEfficiency::maskComponent(MatrixWorkspace &ws,
const std::string &componentName) {
auto instrument = ws.getInstrument();
try {
boost::shared_ptr<const Geometry::ICompAssembly> component =
boost::dynamic_pointer_cast<const Geometry::ICompAssembly>(
instrument->getComponentByName(componentName));
if (!component) {
g_log.warning("Component " + componentName +
" expected to be a CompAssembly, e.g., a bank. Component " +
componentName + " not masked!");
return;
}
std::vector<detid_t> detectorList;
for (int x = 0; x < component->nelements(); x++) {
boost::shared_ptr<Geometry::ICompAssembly> xColumn =
boost::dynamic_pointer_cast<Geometry::ICompAssembly>((*component)[x]);
for (int y = 0; y < xColumn->nelements(); y++) {
boost::shared_ptr<Geometry::Detector> detector =
boost::dynamic_pointer_cast<Geometry::Detector>((*xColumn)[y]);
if (detector) {
auto detID = detector->getID();
detectorList.push_back(detID);
}
}
}
auto indexList = ws.getIndicesFromDetectorIDs(detectorList);
auto &spectrumInfo = ws.mutableSpectrumInfo();
for (const auto &idx : indexList) {
ws.getSpectrum(idx).clearData();
spectrumInfo.setMasked(idx, true);
}
} catch (std::exception &) {
g_log.warning("Expecting the component " + componentName +
" to be a CompAssembly, e.g., a bank. Component not masked!");
}
}
* Mask edges of a RectangularDetector
* @param ws :: Input workspace
* @param left :: number of columns to mask left
* @param right :: number of columns to mask right
* @param high :: number of rows to mask top
* @param low :: number of rows to mask Bottom
* @param componentName :: Must be a RectangularDetector
*/
void CalculateEfficiency::maskEdges(MatrixWorkspace_sptr ws, int left,
int right, int high, int low,
const std::string &componentName) {
auto instrument = ws->getInstrument();
boost::shared_ptr<Mantid::Geometry::RectangularDetector> component;
try {
component =
boost::const_pointer_cast<Mantid::Geometry::RectangularDetector>(
boost::dynamic_pointer_cast<
const Mantid::Geometry::RectangularDetector>(
instrument->getComponentByName(componentName)));
} catch (std::exception &) {
g_log.warning("Expecting the component " + componentName +
" to be a RectangularDetector. maskEdges not executed.");
return;
}
if (!component) {
g_log.warning("Component " + componentName +
" is not a RectangularDetector. MaskEdges not executed.");
return;
}
std::vector<int> IDs;
359
360
361
362
363
364
365
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
402
403
int i = 0;
while (i < left * component->idstep()) {
IDs.push_back(component->idstart() + i);
i += 1;
}
// right
i = component->maxDetectorID() - right * component->idstep();
while (i < component->maxDetectorID()) {
IDs.push_back(i);
i += 1;
}
// low: 0,256,512,768,..,1,257,513
for (int row = 0; row < low; row++) {
i = row + component->idstart();
while (i < component->nelements() * component->idstep() -
component->idstep() + low + component->idstart()) {
IDs.push_back(i);
i += component->idstep();
}
}
// high # 255, 511, 767..
for (int row = 0; row < high; row++) {
i = component->idstep() + component->idstart() - row - 1;
while (i < component->nelements() * component->idstep() +
component->idstart()) {
IDs.push_back(i);
i += component->idstep();
}
}
g_log.debug() << "CalculateEfficiency::maskEdges Detector Ids to Mask:"
<< std::endl;
for (auto id : IDs) {
g_log.debug() << id << " ";
}
g_log.debug() << std::endl;
IAlgorithm_sptr maskAlg = createChildAlgorithm("MaskDetectors");
maskAlg->setChild(true);
maskAlg->setProperty("Workspace", ws);
maskAlg->setProperty("DetectorList", IDs);
maskAlg->execute();
}
Doucet, Mathieu
committed
} // namespace Algorithms
} // namespace Mantid