Newer
Older
Russell Taylor
committed
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidDataHandling/GroupDetectors2.h"
#include "MantidAPI/CommonBinsValidator.h"
Gigg, Martyn Anthony
committed
#include "MantidAPI/FileProperty.h"
#include "MantidAPI/SpectraAxis.h"
#include "MantidDataHandling/LoadDetectorsGroupingFile.h"
#include "MantidGeometry/Instrument/DetectorGroup.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidKernel/ListValidator.h"
Russell Taylor
committed
namespace Mantid {
namespace DataHandling {
Russell Taylor
committed
// Register the algorithm into the algorithm factory
DECLARE_ALGORITHM(GroupDetectors2)
using namespace Kernel;
using namespace API;
using namespace DataObjects;
Russell Taylor
committed
/// (Empty) Constructor
Steve Williams
committed
GroupDetectors2::GroupDetectors2() : m_FracCompl(0.0) {}
Russell Taylor
committed
/// Destructor
GroupDetectors2::~GroupDetectors2() {}
Michael Whitty
committed
const double GroupDetectors2::CHECKBINS = 0.10;
const double GroupDetectors2::OPENINGFILE = 0.03;
// if CHECKBINS+OPENINGFILE+2*READFILE > 1 then the algorithm might report
// progress > 100%
const double GroupDetectors2::READFILE = 0.15;
Russell Taylor
committed
void GroupDetectors2::init() {
declareProperty(new WorkspaceProperty<MatrixWorkspace>(
"InputWorkspace", "", Direction::Input,
boost::make_shared<CommonBinsValidator>()),
"The name of the input 2D workspace");
declareProperty(new WorkspaceProperty<MatrixWorkspace>("OutputWorkspace", "",
Direction::Output),
"The name of the output workspace");
new FileProperty("MapFile", "", FileProperty::OptionalLoad,
{".map", ".xml"}),
"A file that consists of lists of spectra numbers to group. See the "
"help\n"
"for the file format");
declareProperty(
"IgnoreGroupNumber", true,
"This option is only relevant if you're using MapFile.\n"
"If true the spectra will numbered sequentially, starting from one.\n"
"Otherwise, the group number will be used for the spectrum numbers.");
declareProperty(new PropertyWithValue<std::string>("GroupingPattern", "",
Direction::Input),
"Describes how this algorithm should group the detectors. "
"See full instruction list.");
declareProperty(
new ArrayProperty<specid_t>("SpectraList"),
"An array containing a list of the spectrum numbers to combine\n"
"(DetectorList and WorkspaceIndexList are ignored if this is set)");
declareProperty(new ArrayProperty<detid_t>("DetectorList"),
"An array of detector IDs to combine (WorkspaceIndexList is "
"ignored if this is\n"
"set)");
declareProperty(new ArrayProperty<size_t>("WorkspaceIndexList"),
"An array of workspace indices to combine");
declareProperty(
"KeepUngroupedSpectra", false,
"If true ungrouped spectra will be copied to the output workspace\n"
"and placed after the groups");
Michael Whitty
committed
std::vector<std::string> groupTypes(2);
groupTypes[0] = "Sum";
groupTypes[1] = "Average";
using Mantid::Kernel::StringListValidator;
declareProperty(
"Behaviour", "Sum", boost::make_shared<StringListValidator>(groupTypes),
"Whether to sum or average the values when grouping detectors.");
Doucet, Mathieu
committed
// Are we preserving event workspaces?
declareProperty("PreserveEvents", true, "Keep the output workspace as an "
"EventWorkspace, if the input has "
"events.");
declareProperty(
new WorkspaceProperty<MatrixWorkspace>("CopyGroupingFromWorkspace", "",
Direction::Input,
PropertyMode::Optional),
"The name of a workspace to copy the grouping from.\n "
"This can be either a normal workspace or a grouping workspace, but they "
"must be from the same instrument.\n"
"Detector ids are used to match up the spectra to be grouped.\n"
"If this option is selected all file and list options will be ignored.");
Russell Taylor
committed
}
Russell Taylor
committed
// Get the input workspace
Russell Taylor
committed
const MatrixWorkspace_const_sptr inputWS = getProperty("InputWorkspace");
// Check if it is an event workspace
Doucet, Mathieu
committed
const bool preserveEvents = getProperty("PreserveEvents");
EventWorkspace_const_sptr eventW =
boost::dynamic_pointer_cast<const EventWorkspace>(inputWS);
if (eventW != NULL && preserveEvents) {
this->execEvent();
return;
}
// Bin boundaries need to be the same, so do the full check on whether they
// actually are
if (!API::WorkspaceHelpers::commonBoundaries(inputWS)) {
g_log.error()
<< "Can only group if the histograms have common bin boundaries\n";
throw std::invalid_argument(
"Can only group if the histograms have common bin boundaries");
progress(m_FracCompl = CHECKBINS);
Steve Williams
committed
interruption_point();
Michael Whitty
committed
// some values loaded into this vector can be negative so this needs to be a
// signed type
Steve Williams
committed
std::vector<int64_t> unGroupedInds;
// the ungrouped list could be very big but might be none at all
Steve Williams
committed
unGroupedInds.reserve(numInHists);
for (size_t i = 0; i < numInHists; i++) {
Steve Williams
committed
unGroupedInds.push_back(i);
Russell Taylor
committed
}
Steve Williams
committed
getGroups(inputWS, unGroupedInds);
Russell Taylor
committed
// converting the list into a set gets rid of repeated values, here the
// multiple GroupDetectors2::USED become one USED at the start
const std::set<int64_t> unGroupedSet(unGroupedInds.begin(),
unGroupedInds.end());
Russell Taylor
committed
Steve Williams
committed
// Check what the user asked to be done with ungrouped spectra
const bool keepAll = getProperty("KeepUngroupedSpectra");
// ignore the one USED value in set or ignore all the ungrouped if the user
// doesn't want them
const size_t numUnGrouped = keepAll ? unGroupedSet.size() - 1 : 0;
MatrixWorkspace_sptr outputWS = WorkspaceFactory::Instance().create(
inputWS, m_GroupSpecInds.size() + numUnGrouped, inputWS->readX(0).size(),
inputWS->blocksize());
// prepare to move the requested histograms into groups, first estimate how
// long for progress reporting. +1 in the demonator gets rid of any divide by
// zero risk
double prog4Copy =
((1.0 - m_FracCompl) /
(static_cast<double>(numInHists - unGroupedSet.size()) + 1.)) *
(keepAll
? static_cast<double>(numInHists - unGroupedSet.size()) /
static_cast<double>(numInHists)
: 1.);
Gigg, Martyn Anthony
committed
// Build a new map
Janik Zikovsky
committed
const size_t outIndex = formGroups(inputWS, outputWS, prog4Copy);
Michael Whitty
committed
Steve Williams
committed
// If we're keeping ungrouped spectra
Steve Williams
committed
// copy them into the output workspace
Janik Zikovsky
committed
moveOthers(unGroupedSet, inputWS, outputWS, outIndex);
Steve Williams
committed
g_log.information() << name() << " algorithm has finished\n";
Steve Williams
committed
setProperty("OutputWorkspace", outputWS);
Steve Williams
committed
}
void GroupDetectors2::execEvent() {
// Get the input workspace
const MatrixWorkspace_const_sptr matrixInputWS =
getProperty("InputWorkspace");
EventWorkspace_const_sptr inputWS =
boost::dynamic_pointer_cast<const EventWorkspace>(matrixInputWS);
const size_t numInHists = inputWS->getNumberHistograms();
progress(m_FracCompl = CHECKBINS);
interruption_point();
// some values loaded into this vector can be negative so this needs to be a
// signed type
std::vector<int64_t> unGroupedInds;
// the ungrouped list could be very big but might be none at all
unGroupedInds.reserve(numInHists);
for (size_t i = 0; i < numInHists; i++) {
unGroupedInds.push_back(i);
}
// read in the input parameters to make that map, if KeepUngroupedSpectra was
// set we'll need a list of the ungrouped spectrra too
getGroups(inputWS, unGroupedInds);
// converting the list into a set gets rid of repeated values, here the
// multiple GroupDetectors2::USED become one USED at the start
const std::set<int64_t> unGroupedSet(unGroupedInds.begin(),
unGroupedInds.end());
// Check what the user asked to be done with ungrouped spectra
const bool keepAll = getProperty("KeepUngroupedSpectra");
// ignore the one USED value in set or ignore all the ungrouped if the user
// doesn't want them
const size_t numUnGrouped = keepAll ? unGroupedSet.size() - 1 : 0;
// Make a brand new EventWorkspace
EventWorkspace_sptr outputWS = boost::dynamic_pointer_cast<EventWorkspace>(
WorkspaceFactory::Instance().create(
"EventWorkspace", m_GroupSpecInds.size() + numUnGrouped,
inputWS->readX(0).size(), inputWS->blocksize()));
// Copy geometry over.
WorkspaceFactory::Instance().initializeFromParent(inputWS, outputWS, true);
// prepare to move the requested histograms into groups, first estimate how
// long for progress reporting. +1 in the demonator gets rid of any divide by
// zero risk
double prog4Copy =
((1.0 - m_FracCompl) /
(static_cast<double>(numInHists - unGroupedSet.size()) + 1.)) *
(keepAll
? static_cast<double>(numInHists - unGroupedSet.size()) /
static_cast<double>(numInHists)
: 1.);
// Build a new map
const size_t outIndex = formGroupsEvent(inputWS, outputWS, prog4Copy);
// If we're keeping ungrouped spectra
if (keepAll) {
// copy them into the output workspace
moveOthersEvent(unGroupedSet, inputWS, outputWS, outIndex);
}
// Set all X bins on the output
cow_ptr<MantidVec> XValues;
XValues.access() = inputWS->readX(0);
outputWS->setAllX(XValues);
g_log.information() << name() << " algorithm has finished\n";
setProperty("OutputWorkspace", outputWS);
/** Make a map containing spectra indexes to group, the indexes could have come
* from
* file, or an array, spectra numbers ...
Janik Zikovsky
committed
* @param workspace :: the user selected input workspace
* @param unUsedSpec :: spectra indexes that are not members of any group
Russell Taylor
committed
void GroupDetectors2::getGroups(API::MatrixWorkspace_const_sptr workspace,
std::vector<int64_t> &unUsedSpec) {
// this is the map that we are going to fill
m_GroupSpecInds.clear();
// There are several properties that may contain the user data go through them
// in order of precedence
const MatrixWorkspace_const_sptr groupingWS_sptr =
getProperty("CopyGroupingFromWorkspace");
if (groupingWS_sptr) {
DataObjects::GroupingWorkspace_const_sptr groupWS =
boost::dynamic_pointer_cast<const DataObjects::GroupingWorkspace>(
groupingWS_sptr);
if (groupWS) {
g_log.debug() << "Extracting grouping from GroupingWorkspace ("
<< groupWS->name() << ")" << std::endl;
processGroupingWorkspace(groupWS, workspace, unUsedSpec);
} else {
g_log.debug() << "Extracting grouping from MatrixWorkspace ("
<< groupingWS_sptr->name() << ")" << std::endl;
processMatrixWorkspace(groupingWS_sptr, workspace, unUsedSpec);
const std::string filename = getProperty("MapFile");
if (!filename
.empty()) { // The file property has been set so try to load the file
try {
Anders Markvardsen
committed
// check if XML file and if yes assume it is a XML grouping file
std::string filenameCopy(filename);
std::transform(filenameCopy.begin(), filenameCopy.end(),
filenameCopy.begin(), tolower);
if ((filenameCopy.find(".xml")) != std::string::npos) {
Anders Markvardsen
committed
processXMLFile(filename, workspace, unUsedSpec);
} else {
// the format of this input file format is described in
// "GroupDetectors2.h"
Anders Markvardsen
committed
processFile(filename, workspace, unUsedSpec);
}
} catch (std::exception &) {
g_log.error() << name() << ": Error reading input file " << filename
<< std::endl;
const std::string instructions = getProperty("GroupingPattern");
if (!instructions.empty()) {
spec2index_map specs2index;
const SpectraAxis *axis =
dynamic_cast<const SpectraAxis *>(workspace->getAxis(1));
if (axis)
axis->getSpectraIndexMap(specs2index);
std::stringstream commandsSS;
// Fill commandsSS with the contents of a map file
translateInstructions(instructions, commandsSS);
// readFile expects the first line to have already been removed, so we do
// that, even though we don't use it.
std::string firstLine;
std::getline(commandsSS, firstLine);
// We don't use lineNum either, but it's expected.
size_t lineNum = 0;
readFile(specs2index, commandsSS, lineNum, unUsedSpec,
/* don't ignore group numbers */ false);
return;
}
const std::vector<specid_t> spectraList = getProperty("SpectraList");
const std::vector<detid_t> detectorList = getProperty("DetectorList");
const std::vector<size_t> indexList = getProperty("WorkspaceIndexList");
// only look at these other parameters if the file wasn't set
if (!spectraList.empty()) {
workspace->getIndicesFromSpectra(spectraList, m_GroupSpecInds[0]);
g_log.debug() << "Converted " << spectraList.size()
<< " spectra numbers into spectra indices to be combined\n";
} else { // go through the rest of the properties in order of decreasing
// presidence, abort when we get the data we need ignore the rest
if (!detectorList.empty()) {
// we are going to group on the basis of detector IDs, convert from
// detectors to workspace indices
workspace->getIndicesFromDetectorIDs(detectorList, m_GroupSpecInds[0]);
g_log.debug() << "Found " << m_GroupSpecInds[0].size()
<< " spectra indices from the list of "
<< detectorList.size() << " detectors\n";
} else if (!indexList.empty()) {
g_log.debug() << "Read in " << m_GroupSpecInds[0].size()
<< " spectra indices to be combined\n";
// check we don't have an index that is too high for the workspace
size_t maxIn = static_cast<size_t>(workspace->getNumberHistograms() - 1);
Federico Montesino Pouzols
committed
auto indices0 = m_GroupSpecInds[0];
auto it = indices0.begin();
for (; it != indices0.end(); ++it) {
if (*it > maxIn) {
g_log.error() << "Spectra index " << *it
<< " doesn't exist in the input workspace, the highest "
"possible index is " << maxIn << std::endl;
throw std::out_of_range("One of the spectra requested to group does "
"not exist in the input workspace");
if (m_GroupSpecInds[0].empty()) {
g_log.information() << name() << ": File, WorkspaceIndexList, SpectraList, "
"and DetectorList properties are all "
"empty\n";
throw std::invalid_argument(
"All list properties are empty, nothing to group");
// up date unUsedSpec, this is used to find duplicates and when the user has
// set KeepUngroupedSpectra
Federico Montesino Pouzols
committed
auto indices0 = m_GroupSpecInds[0];
auto index = indices0.begin();
for (; index != indices0.end();
++index) { // the vector<int> m_GroupSpecInds[0] must not index contain
// numbers that don't exist in the workspaace
if (unUsedSpec[*index] != USED) {
} else
g_log.warning() << "Duplicate index, " << *index << ", found\n";
Steve Williams
committed
}
/** Read the spectra numbers in from the input file (the file format is in the
* source file "GroupDetectors2.h" and make an array of spectra indexes to group
Janik Zikovsky
committed
* @param fname :: the full path name of the file to open
* @param workspace :: a pointer to the input workspace, used to get spectra
* indexes from numbers
* @param unUsedSpec :: the list of spectra indexes that have been included in a
* group (so far)
Janik Zikovsky
committed
* @throw FileError if there's any problem with the file or its format
Steve Williams
committed
*/
void GroupDetectors2::processFile(std::string fname,
API::MatrixWorkspace_const_sptr workspace,
std::vector<int64_t> &unUsedSpec) {
// tring to open the file the user told us exists, skip down 20 lines to find
// out what happens if we can read from it
g_log.debug() << "Opening input file ... " << fname;
std::ifstream File(fname.c_str(), std::ios::in);
std::string firstLine;
// for error reporting keep a count of where we are reading in the file
Michael Whitty
committed
g_log.debug() << " file state failbit set after read attempt\n";
throw Exception::FileError("Couldn't read file", fname);
}
g_log.debug() << " success opening input file " << fname << std::endl;
progress(m_FracCompl += OPENINGFILE);
// check for a (user) cancel message
interruption_point();
// allow spectra number to spectra index look ups
spec2index_map specs2index;
const SpectraAxis *axis =
dynamic_cast<const SpectraAxis *>(workspace->getAxis(1));
if (axis) {
axis->getSpectraIndexMap(specs2index);
}
try {
// we don't use the total number of groups report at the top of the file but
// we'll tell them later if there is a problem with it for their diagnostic
// purposes
int totalNumberOfGroups = readInt(firstLine);
Michael Whitty
committed
// Reading file now ...
while (totalNumberOfGroups == EMPTY_LINE) {
if (!File)
throw Exception::FileError(
"The input file doesn't appear to contain any data", fname);
std::getline(File, firstLine), lineNum++;
totalNumberOfGroups = readInt(firstLine);
}
bool ignoreGroupNo = getProperty("IgnoreGroupNumber");
readFile(specs2index, File, lineNum, unUsedSpec, ignoreGroupNo);
if (m_GroupSpecInds.size() != static_cast<size_t>(totalNumberOfGroups)) {
g_log.warning() << "The input file header states there are "
<< totalNumberOfGroups << " but the file contains "
<< m_GroupSpecInds.size() << " groups\n";
// add some more info to the error messages, including the line number, to
// help users correct their files. These problems should cause the algorithm
// to stop
catch (std::invalid_argument &e) {
g_log.debug() << "Exception thrown: " << e.what() << std::endl;
File.close();
std::string error(e.what() + std::string(" near line number ") +
boost::lexical_cast<std::string>(lineNum));
if (File.fail()) {
error = "Input output error while reading file ";
}
throw Exception::FileError(error, fname);
} catch (boost::bad_lexical_cast &e) {
g_log.debug() << "Exception thrown: " << e.what() << std::endl;
File.close();
std::string error(std::string("Problem reading integer value \"") +
e.what() + std::string("\" near line number ") +
boost::lexical_cast<std::string>(lineNum));
if (File.fail()) {
error = "Input output error while reading file ";
}
throw Exception::FileError(error, fname);
}
File.close();
g_log.debug() << "Closed file " << fname << " after reading in "
<< m_GroupSpecInds.size() << " groups\n";
m_FracCompl += fileReadProg(m_GroupSpecInds.size(), specs2index.size());
Steve Williams
committed
return;
}
Anders Markvardsen
committed
/** Get groupings from XML file
Janik Zikovsky
committed
* @param fname :: the full path name of the file to open
* @param workspace :: a pointer to the input workspace, used to get spectra
* indexes from numbers
* @param unUsedSpec :: the list of spectra indexes that have been included in a
* group (so far)
Janik Zikovsky
committed
* @throw FileError if there's any problem with the file or its format
Anders Markvardsen
committed
*/
void GroupDetectors2::processXMLFile(std::string fname,
API::MatrixWorkspace_const_sptr workspace,
std::vector<int64_t> &unUsedSpec) {
// 1. Get maps for spectrum ID and detector ID
spec2index_map specs2index;
const SpectraAxis *axis =
dynamic_cast<const SpectraAxis *>(workspace->getAxis(1));
if (axis) {
Anders Markvardsen
committed
axis->getSpectraIndexMap(specs2index);
}
const detid2index_map detIdToWiMap =
workspace->getDetectorIDToWorkspaceIndexMap();
Michael Whitty
committed
// 2. Load XML file
DataHandling::LoadGroupXMLFile loader;
loader.setDefaultStartingGroupID(0);
loader.loadXMLFile(fname);
std::map<int, std::vector<detid_t>> mGroupDetectorsMap =
loader.getGroupDetectorsMap();
std::map<int, std::vector<int>> mGroupSpectraMap =
loader.getGroupSpectraMap();
// 3. Build m_GroupSpecInds
std::map<int, std::vector<detid_t>>::iterator dit;
for (dit = mGroupDetectorsMap.begin(); dit != mGroupDetectorsMap.end();
++dit) {
int groupid = dit->first;
std::vector<size_t> tempv;
m_GroupSpecInds.insert(std::make_pair(groupid, tempv));
Michael Whitty
committed
}
for (dit = mGroupDetectorsMap.begin(); dit != mGroupDetectorsMap.end();
++dit) {
int groupid = dit->first;
std::vector<detid_t> detids = dit->second;
storage_map::iterator sit;
sit = m_GroupSpecInds.find(groupid);
if (sit == m_GroupSpecInds.end())
continue;
std::vector<size_t> &wsindexes = sit->second;
for (int detid : detids) {
auto ind = detIdToWiMap.find(detid);
if (unUsedSpec[wsid] != (USED)) {
unUsedSpec[wsid] = (USED);
} else {
g_log.error() << "Detector with ID " << detid
<< " is not found in instrument " << std::endl;
std::map<int, std::vector<int>>::iterator pit;
for (pit = mGroupSpectraMap.begin(); pit != mGroupSpectraMap.end(); ++pit) {
int groupid = pit->first;
std::vector<int> spectra = pit->second;
storage_map::iterator sit;
sit = m_GroupSpecInds.find(groupid);
if (sit == m_GroupSpecInds.end())
continue;
std::vector<size_t> &wsindexes = sit->second;
for (int specid : spectra) {
auto ind = specs2index.find(specid);
if (unUsedSpec[wsid] != (USED)) {
unUsedSpec[wsid] = (USED);
} else {
g_log.error() << "Spectrum with ID " << specid
<< " is not found in instrument " << std::endl;
Michael Whitty
committed
Anders Markvardsen
committed
}
/** Get groupings from groupingworkspace
* @param groupWS :: the grouping workspace to use
* @param workspace :: a pointer to the input workspace, used to get spectra
* indexes from numbers
* @param unUsedSpec :: the list of spectra indexes that have been not included
* in a group (so far)
void GroupDetectors2::processGroupingWorkspace(
GroupingWorkspace_const_sptr groupWS,
API::MatrixWorkspace_const_sptr workspace,
std::vector<int64_t> &unUsedSpec) {
detid2index_map detIdToWiMap = workspace->getDetectorIDToWorkspaceIndexMap();
typedef std::map<size_t, std::set<size_t>> Group2SetMapType;
const size_t nspec = groupWS->getNumberHistograms();
for (size_t i = 0; i < nspec; ++i) {
// read spectra from groupingws
size_t groupid = static_cast<int>(groupWS->readY(i)[0]);
// group 0 is are unused spectra - don't process them
if (groupid > 0) {
if (group2WSIndexSetmap.find(groupid) == group2WSIndexSetmap.end()) {
// not found - create an empty set
group2WSIndexSetmap.insert(std::make_pair(groupid, std::set<size_t>()));
// get a reference to the set
std::set<size_t> &targetWSIndexSet = group2WSIndexSetmap[groupid];
const Geometry::IDetector_const_sptr det = groupWS->getDetector(i);
Geometry::DetectorGroup_const_sptr detGroup =
boost::dynamic_pointer_cast<const Geometry::DetectorGroup>(det);
if (detGroup) {
det_ids = detGroup->getDetectorIDs();
det_ids.push_back(det->getID());
}
for (int det_id : det_ids) {
// translate detectors to target det ws indexes
size_t targetWSIndex = detIdToWiMap[det_id];
targetWSIndexSet.insert(targetWSIndex);
// mark as used
if (unUsedSpec[targetWSIndex] != (USED)) {
unUsedSpec[targetWSIndex] = (USED);
} catch (Mantid::Kernel::Exception::NotFoundError) {
// the detector was not found - don't add it
// Build m_GroupSpecInds (group -> list of ws indices)
for (auto &dit : group2WSIndexSetmap) {
size_t groupid = dit.first;
std::set<size_t> &targetWSIndexSet = dit.second;
std::vector<size_t> tempv;
tempv.assign(targetWSIndexSet.begin(), targetWSIndexSet.end());
m_GroupSpecInds.insert(
std::make_pair(static_cast<specid_t>(groupid), tempv));
return;
}
/** Get groupings from a matrix workspace
* @param groupWS :: the matrix workspace to use
* @param workspace :: a pointer to the input workspace, used to get spectra
* indexes from numbers
* @param unUsedSpec :: the list of spectra indexes that have been not included
* in
* a group (so far)
void GroupDetectors2::processMatrixWorkspace(
MatrixWorkspace_const_sptr groupWS, MatrixWorkspace_const_sptr workspace,
std::vector<int64_t> &unUsedSpec) {
detid2index_map detIdToWiMap = workspace->getDetectorIDToWorkspaceIndexMap();
typedef std::map<size_t, std::set<size_t>> Group2SetMapType;
Group2SetMapType group2WSIndexSetmap;
const size_t nspec = groupWS->getNumberHistograms();
for (size_t i = 0; i < nspec; ++i) {
// read spectra from groupingws
size_t groupid = i;
if (group2WSIndexSetmap.find(groupid) == group2WSIndexSetmap.end()) {
// not found - create an empty set
group2WSIndexSetmap.insert(std::make_pair(groupid, std::set<size_t>()));
// get a reference to the set
std::set<size_t> &targetWSIndexSet = group2WSIndexSetmap[groupid];
std::vector<detid_t> det_ids;
const Geometry::IDetector_const_sptr det = groupWS->getDetector(i);
Geometry::DetectorGroup_const_sptr detGroup =
boost::dynamic_pointer_cast<const Geometry::DetectorGroup>(det);
det_ids = detGroup->getDetectorIDs();
for (int det_id : det_ids) {
// translate detectors to target det ws indexes
size_t targetWSIndex = detIdToWiMap[det_id];
targetWSIndexSet.insert(targetWSIndex);
// mark as used
if (unUsedSpec[targetWSIndex] != (USED)) {
unUsedSpec[targetWSIndex] = (USED);
} catch (Mantid::Kernel::Exception::NotFoundError) {
// the detector was not found - don't add it
// Build m_GroupSpecInds (group -> list of ws indices)
for (auto &dit : group2WSIndexSetmap) {
size_t groupid = dit.first;
std::set<size_t> &targetWSIndexSet = dit.second;
tempv.assign(targetWSIndexSet.begin(), targetWSIndexSet.end());
m_GroupSpecInds.insert(
std::make_pair(static_cast<specid_t>(groupid), tempv));
/** The function expects that the string passed to it contains an integer
* number,
Janik Zikovsky
committed
* @param line :: a line read from the file, we'll interpret this
* @return the integer read from the line, error code if not readable
Janik Zikovsky
committed
* @throw invalid_argument when the line contains more just an integer
* @throw boost::bad_lexical_cast when the string can't be interpreted as an
* integer
int GroupDetectors2::readInt(std::string line) {
// remove comments and white space (TOK_TRIM)
Poco::StringTokenizer dataComment(line, "#", Poco::StringTokenizer::TOK_TRIM);
if (dataComment.begin() != dataComment.end()) {
Poco::StringTokenizer data(*(dataComment.begin()), " ",
Poco::StringTokenizer::TOK_TRIM);
if (data.count() == 1) {
if (!data[0].empty()) {
try {
return boost::lexical_cast<int>(data[0]);
} catch (boost::bad_lexical_cast &e) {
g_log.debug() << "Exception thrown: " << e.what() << std::endl;
throw std::invalid_argument("Error reading file, integer expected");
}
}
} else {
if (data.count() == 0) {
// we expected an integer but there were more things on the line, before
// any #
g_log.debug() << "Error: found " << data.count()
<< " strings the first string is " << data[0] << std::endl;
throw std::invalid_argument(
"Problem reading file, a singe integer expected");
}
}
// we haven't found any data, return the nodata condition
return EMPTY_LINE;
}
/** Reads from the file getting in order: an unused integer, on the next line
* the number of
* spectra in the group and next one or more lines the spectra numbers, (format
* in GroupDetectors.h)
Janik Zikovsky
committed
* @param specs2index :: a map that links spectra numbers to indexes
* @param File :: the input stream that is linked to the file
* @param lineNum :: the last line read in the file, is updated by this function
* @param unUsedSpec :: list of spectra that haven't yet been included in a group
* @param ignoreGroupNumber :: ignore group numbers when numbering spectra
Janik Zikovsky
committed
* @throw invalid_argument if there is any problem with the file
void GroupDetectors2::readFile(spec2index_map &specs2index, std::istream &File,
std::vector<int64_t> &unUsedSpec,
bool ignoreGroupNumber) {
// go through the rest of the file reading in lists of spectra number to group
int oldSpectrumNo = 1;
int groupNo = EMPTY_LINE;
do {
std::getline(File, thisLine), lineNum++;
groupNo = readInt(thisLine);
// we haven't started reading a new group and so if the file ends here it
// is OK
if (!File)
return;
} while (groupNo == EMPTY_LINE && File);
// If we're ignoring the group number, use the old spectrum number way of
// just counting, otherwise use the group number.
const int spectrumNo = ignoreGroupNumber ? oldSpectrumNo++ : groupNo;
// the number of spectra that will be combined in the group
int numberOfSpectra = EMPTY_LINE;
do {
if (!File)
throw std::invalid_argument("Premature end of file, expecting an "
"integer with the number of spectra in the "
"group");
std::getline(File, thisLine), lineNum++;
} while (numberOfSpectra == EMPTY_LINE);
Federico Montesino Pouzols
committed
if (numberOfSpectra <= 0) {
throw std::invalid_argument("The number of spectra is zero or negative");
}
// the value of this map is the list of spectra numbers that will be
// combined into a group
m_GroupSpecInds[spectrumNo].reserve(numberOfSpectra);
do {
if (!File)
throw std::invalid_argument("Premature end of file, found number of "
"spectra specification but no spectra "
"list");
std::getline(File, thisLine), lineNum++;
// the spectra numbers that will be included in the group
readSpectraIndexes(thisLine, specs2index, m_GroupSpecInds[spectrumNo],
unUsedSpec);
} while (static_cast<int>(m_GroupSpecInds[spectrumNo].size()) <
numberOfSpectra);
if (static_cast<int>(m_GroupSpecInds[spectrumNo].size()) !=
numberOfSpectra) { // it makes no sense to continue reading the file,
// we'll stop here
throw std::invalid_argument(
std::string("Bad number of spectra specification or spectra list "
"near line number ") +
boost::lexical_cast<std::string>(lineNum));
// make regular progress reports and check for a cancellation notification
if ((m_GroupSpecInds.size() % INTERVAL) == 1) {
fileReadProg(m_GroupSpecInds.size(), specs2index.size());
/** The function expects that the string passed to it contains a series of
* integers,
* ranges specified with a '-' are possible
Janik Zikovsky
committed
* @param line :: a line read from the file, we'll interpret this
* @param specs2index :: a map with spectra numbers as indexes and index numbers
* as values
Janik Zikovsky
committed
* @param output :: the list of integers, with any ranges expanded
* @param unUsedSpec :: the list of spectra indexes that have been included in a
* group (so far)
Janik Zikovsky
committed
* @param seperator :: the symbol for the index range separator
* @throw invalid_argument when a number couldn't be found or the number is not
* in the spectra map
void GroupDetectors2::readSpectraIndexes(std::string line,
spec2index_map &specs2index,
std::vector<size_t> &output,
std::vector<int64_t> &unUsedSpec,
std::string seperator) {
Anders Markvardsen
committed
Poco::StringTokenizer dataComment(line, seperator, IGNORE_SPACES);
for (const auto &itr : dataComment) {
std::vector<size_t> specNums;
specNums.reserve(output.capacity());
RangeHelper::getList(itr, specNums);
Michael Whitty
committed
std::vector<size_t>::const_iterator specN = specNums.begin();
for (; specN != specNums.end(); ++specN) {
specid_t spectrumNum = static_cast<specid_t>(*specN);
spec2index_map::const_iterator ind = specs2index.find(spectrumNum);
if (ind == specs2index.end()) {
g_log.debug() << name() << ": spectrum number " << spectrumNum
<< " refered to in the input file was not found in the "
"input workspace\n";
throw std::invalid_argument(
"Spectrum number " + boost::lexical_cast<std::string>(spectrumNum) +
" not found");
if (unUsedSpec[ind->second] != USED) { // this array is used when the user
// sets KeepUngroupedSpectra, as
// well as to find duplicates
unUsedSpec[ind->second] = USED;
output.push_back(ind->second);
} else { // the spectra was already included in a group
output.push_back(ind->second);
Michael Whitty
committed
/** Called while reading input file to report progress (doesn't update
* m_FracCompl ) and
* check for algorithm cancel messages, doesn't look at file size to estimate
* progress
* @param numGroupsRead :: number of groups read from the file so far (not the
* number of spectra)
Janik Zikovsky
committed
* @param numInHists :: the total number of histograms in the input workspace
* @return estimate of the amount of algorithm progress obtained by reading from
* the file
Steve Williams
committed
*/
double GroupDetectors2::fileReadProg(
DataHandling::GroupDetectors2::storage_map::size_type numGroupsRead,
DataHandling::GroupDetectors2::storage_map::size_type numInHists) {
Steve Williams
committed
// I'm going to guess that there are half as many groups as spectra
double progEstim =
2. * static_cast<double>(numGroupsRead) / static_cast<double>(numInHists);
// but it might be more, in which case this complex function always increases
// but slower and slower
progEstim = READFILE * progEstim / (1 + progEstim);
progress(m_FracCompl + progEstim);
// check for a (user) cancel message
interruption_point();
return progEstim;
}
Janik Zikovsky
committed
Michael Whitty
committed
/**
* Move the user selected spectra in the input workspace into groups in the
* output workspace
Janik Zikovsky
committed
* @param inputWS :: user selected input workspace for the algorithm
* @param outputWS :: user selected output workspace for the algorithm
* @param prog4Copy :: the amount of algorithm progress to attribute to moving a
* single spectra
* @return number of new grouped spectra
Steve Williams
committed
*/
size_t GroupDetectors2::formGroups(API::MatrixWorkspace_const_sptr inputWS,
API::MatrixWorkspace_sptr outputWS,
const double prog4Copy) {
Michael Whitty
committed
// get "Behaviour" string
const std::string behaviour = getProperty("Behaviour");
int bhv = 0;
if (behaviour == "Average")
bhv = 1;
Michael Whitty
committed
API::MatrixWorkspace_sptr beh = API::WorkspaceFactory::Instance().create(
"Workspace2D", static_cast<int>(m_GroupSpecInds.size()), 1, 1);
Michael Whitty
committed
g_log.debug() << name() << ": Preparing to group spectra into "
<< m_GroupSpecInds.size() << " groups\n";
Steve Williams
committed
// where we are copying spectra to, we start copying to the start of the
// output workspace
size_t outIndex = 0;
// Only used for averaging behaviour. We may have a 1:1 map where a Divide
// would be waste as it would be just dividing by 1
bool requireDivide(false);
for (storage_map::const_iterator it = m_GroupSpecInds.begin();
it != m_GroupSpecInds.end(); ++it) {
Janik Zikovsky
committed
// This is the grouped spectrum
ISpectrum *outSpec = outputWS->getSpectrum(outIndex);
Anders Markvardsen
committed
// The spectrum number of the group is the key
outSpec->setSpectrumNo(it->first);
Janik Zikovsky
committed
// Start fresh with no detector IDs
outSpec->clearDetectorIDs();
Steve Williams
committed
// Copy over X data from first spectrum, the bin boundaries for all spectra
// are assumed to be the same here
Janik Zikovsky
committed
outSpec->dataX() = inputWS->readX(0);
// the Y values and errors from spectra being grouped are combined in the
// output spectrum
MantidVec &firstY = outSpec->dataY();
// Keep track of number of detectors required for masking
size_t nonMaskedSpectra(0);
beh->dataX(outIndex)[0] = 0.0;
beh->dataE(outIndex)[0] = 0.0;
for (unsigned long originalWI : it->second) {
Anders Markvardsen
committed
// detectors to add to firstSpecNum
const ISpectrum *fromSpectrum = inputWS->getSpectrum(originalWI);
Anders Markvardsen
committed
// Add up all the Y spectra and store the result in the first one
auto fEit = outSpec->dataE().begin();
Hahn, Steven
committed
auto Yit = fromSpectrum->dataY().cbegin();
auto Eit = fromSpectrum->dataE().cbegin();
for (auto fYit = firstY.begin(); fYit != firstY.end();
++fYit, ++fEit, ++Yit, ++Eit) {
*fYit += *Yit;
// Assume 'normal' (i.e. Gaussian) combination of errors
*fEit = std::sqrt((*fEit) * (*fEit) + (*Eit) * (*Eit));
Janik Zikovsky
committed
// detectors to add to the output spectrum
outSpec->addDetectorIDs(fromSpectrum->getDetectorIDs());
try {
Geometry::IDetector_const_sptr det = inputWS->getDetector(originalWI);
if (!det->isMasked())
++nonMaskedSpectra;
} catch (Exception::NotFoundError &) {
// If a detector cannot be found, it cannot be masked
++nonMaskedSpectra;
}
Gigg, Martyn Anthony
committed
}
if (nonMaskedSpectra == 0)
++nonMaskedSpectra; // Avoid possible divide by zero
if (!requireDivide)
requireDivide = (nonMaskedSpectra > 1);
beh->dataY(outIndex)[0] = static_cast<double>(nonMaskedSpectra);
Anders Markvardsen
committed
Steve Williams
committed
// make regular progress reports and check for cancelling the algorithm
if (outIndex % INTERVAL == 0) {
m_FracCompl += INTERVAL * prog4Copy;
if (m_FracCompl > 1.0)
m_FracCompl = 1.0;
progress(m_FracCompl);
Steve Williams
committed
interruption_point();
}