-
Simon Heybrock authoredSimon Heybrock authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
CalculateEfficiency.cpp 15.15 KiB
#include "MantidAlgorithms/CalculateEfficiency.h"
#include "MantidAPI/SpectrumInfo.h"
#include "MantidAPI/WorkspaceFactory.h"
#include "MantidDataObjects/EventWorkspace.h"
#include "MantidDataObjects/EventList.h"
#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"
#include <vector>
namespace Mantid {
namespace Algorithms {
// Register the class into the algorithm factory
DECLARE_ALGORITHM(CalculateEfficiency)
using namespace Kernel;
using namespace API;
using namespace Geometry;
using namespace DataObjects;
/** Initialization method.
*
*/
void CalculateEfficiency::init() {
declareProperty(
make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input),
"The workspace containing the flood data");
declareProperty(
make_unique<WorkspaceProperty<>>("OutputWorkspace", "",
Direction::Output),
"The name of the workspace to be created as the output of the algorithm");
auto positiveDouble = boost::make_shared<BoundedValidator<double>>();
positiveDouble->setLower(0);
declareProperty(
"MinEfficiency", EMPTY_DBL(), positiveDouble,
"Minimum efficiency for a pixel to be considered (default: no minimum).");
declareProperty(
"MaxEfficiency", EMPTY_DBL(), positiveDouble,
"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.");
}
/** Executes the algorithm
*
*/
void CalculateEfficiency::exec() {
// 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;
// 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);
}
// Now create the output workspace
MatrixWorkspace_sptr outputWS; // = getProperty("OutputWorkspace");
// DataObjects::EventWorkspace_const_sptr inputEventWS =
// boost::dynamic_pointer_cast<const EventWorkspace>(inputWS);
// Sum up all the wavelength bins
IAlgorithm_sptr childAlg = createChildAlgorithm("Integration", 0.0, 0.2);
childAlg->setProperty<MatrixWorkspace_sptr>("InputWorkspace", inputWS);
childAlg->executeAsChildAlg();
rebinnedWS = childAlg->getProperty("OutputWorkspace");
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));
}
setProperty("OutputWorkspace", outputWS);
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)) {
// 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());
}
}
/*
* Sum up all the unmasked detector pixels.
*
* @param rebinnedWS: workspace where all the wavelength bins have been grouped
*together
* @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++;
}
g_log.debug() << "sumUnmaskedDetectors: unmasked pixels = " << nPixels
<< " from a total of " << numberOfSpectra << "\n";
error = std::sqrt(error);
}
/*
* 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;
}
// 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";
// 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());
}
}
}
/**
* 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;
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();
}
} // namespace Algorithms
} // namespace Mantid