21#include <boost/regex.hpp>
34 : m_transform(), m_transformFromOriginal(), m_transformToOriginal(), m_transformFromIntermediate(),
35 m_transformToIntermediate(), m_axisAligned(true), m_outD(0),
36 m_NormalizeBasisVectors(false) {}
45 declareProperty(
"AxisAligned",
true,
"Perform binning aligned with the axes of the input MDEventWorkspace?");
47 for (
size_t i = 0; i < dimChars.size(); i++) {
50 std::string propName =
"AlignedDim" + dim;
54 "Enter it as a comma-separated list of values with the format: "
55 "'name,minimum,maximum,number_of_bins'. Leave blank for NONE.");
62 std::string grpName =
"Non-Aligned Binning";
65 std::unique_ptr<IPropertySettings> settings =
66 std::make_unique<VisibleWhenProperty>(
"AxisAligned",
IS_EQUAL_TO,
"0");
69 for (
size_t i = 0; i < dimChars.size(); i++) {
72 std::string propName =
"BasisVector" + dim;
75 "th output dimension."
76 "Format: 'name, units, x,y,z,..'.\n"
77 " name : string for the name of the output dimension.\n"
78 " units : string for the units of the output dimension.\n"
79 " x,y,z,...: vector definining the basis in the input dimensions "
81 "Leave blank for NONE.");
86 "Coordinates in the INPUT workspace that corresponds to "
87 "(0,0,0) in the OUTPUT workspace.\n"
88 "Enter as a comma-separated string.\n"
89 "Default: 0 in all dimensions (no translation).");
92 "The minimum, maximum edges of space of each dimension of "
93 "the OUTPUT workspace, as a comma-separated list");
96 "The number of bins for each dimension of the OUTPUT workspace.");
99 "Normalize the given basis vectors to unity. \n"
100 "If true, then a distance of 1 in the INPUT dimensions = 1 "
101 "in the OUTPUT dimensions.\n"
102 "If false, then a distance of norm(basis_vector) in the "
103 "INPUT dimension = 1 in the OUTPUT dimensions.");
106 "Force the input basis vectors to form an orthogonal coordinate system. "
107 "Only works in 3 dimension!");
139 if (input.size() < 3)
140 throw std::invalid_argument(
"Dimension string is too short to be valid: " + str);
145 size_t n_first_comma;
148 if (input[0] ==
'[') {
150 size_t n = input.find_first_of(
']', 1);
151 if (
n == std::string::npos)
152 throw std::invalid_argument(
"No closing ] character in the dimension name of : " + str);
154 n_first_comma = input.find_first_of(
',',
n);
155 if (n_first_comma == std::string::npos)
156 throw std::invalid_argument(
"No comma after the closing ] character in the dimension string: " + str);
159 n_first_comma = input.find_first_of(
',');
161 if (n_first_comma == std::string::npos)
162 throw std::invalid_argument(
"No comma in the dimension string: " + str);
163 if (n_first_comma == input.size() - 1)
164 throw std::invalid_argument(
"Dimension string ends in a comma: " + str);
169 throw std::invalid_argument(
"name should not be blank.");
173 input = input.substr(n_first_comma + 1);
174 std::vector<std::string> strs;
175 boost::split(strs, input, boost::is_any_of(
","));
176 if (strs.size() != this->m_inWS->getNumDims() + 1)
177 throw std::invalid_argument(
"Wrong number of values (expected 2 + # of "
178 "input dimensions) in the dimensions string: " +
184 throw std::invalid_argument(
"Number of bins for output dimension " +
Strings::toString(dim) +
" should be >= 1.");
189 double lengthInOutput = max - min;
190 if (lengthInOutput <= 0)
191 throw std::invalid_argument(
"The maximum extents for dimension " +
Strings::toString(dim) +
" should be > 0.");
195 for (
size_t d = 0;
d < this->
m_inWS->getNumDims();
d++)
197 throw std::invalid_argument(
"Error converting argument '" + strs[
d + 1] +
"' in the dimensions string '" + str +
211 basis = origBasis1 - origBasis0;
215 double basisLength = basis.
norm();
216 if (basisLength <= 0)
217 throw std::invalid_argument(
"direction should not be 0-length.");
220 double transformScaling = 1.0;
225 transformScaling = 1.0;
229 transformScaling = (1.0 / basisLength);
232 double lengthInInput = lengthInOutput / transformScaling;
236 double binningScaling = double(numBins) / (lengthInInput);
245 auto out = std::make_shared<MDHistoDimension>(
name,
name, *frame,
static_cast<coord_t>(min),
246 static_cast<coord_t>(max), numBins);
265 for (
char dimChar : dimChars) {
266 std::string propName =
"BasisVector0";
267 propName[11] = dimChar;
272 std::vector<double> extents = this->
getProperty(
"OutputExtents");
273 if (extents.size() !=
m_outD * 2)
276 "(2 for each dimension in the OUTPUT workspace).");
287 throw std::invalid_argument(
"The OutputBins parameter must have 1 entry "
288 "for each dimension in the OUTPUT workspace.");
298 for (
char dimChar : dimChars) {
299 std::string propName =
"BasisVector0";
300 propName[11] = dimChar;
303 }
catch (std::exception &e) {
304 throw std::invalid_argument(
"Error parsing the " + propName +
" parameter: " + std::string(e.what()));
311 throw std::runtime_error(
"No output dimensions were found in the MDEventWorkspace. Cannot bin!");
315 std::size_t totalBins =
317 [](std::size_t acc,
int val) -> std::size_t { return acc * static_cast<std::size_t>(val); });
319 constexpr std::size_t bytesPerBin = 3 *
sizeof(double) +
sizeof(
bool);
321 if (!memStr.empty()) {
322 memStr =
"You requested a total of " +
std::to_string(totalBins) +
" bins. " + memStr +
323 " Please reduce the number of bins or output dimensions.";
324 throw std::runtime_error(memStr);
328 std::vector<double> translVector;
331 }
catch (std::exception &e) {
332 throw std::invalid_argument(
"Error parsing the Translation parameter: " + std::string(e.what()));
336 if (translVector.empty())
337 translVector.resize(
m_inWS->getNumDims(), 0);
341 throw std::invalid_argument(
"The number of dimensions in the Translation parameter is "
342 "not consistent with the number of dimensions in the input workspace.");
346 throw std::runtime_error(
"More output dimensions were specified than input dimensions "
347 "exist in the MDEventWorkspace. Cannot bin!");
349 throw std::runtime_error(
"Inconsistent number of entries in scaling vector.");
361 size_t inD =
m_inWS->getNumDims();
364 bool ForceOrthogonal =
getProperty(
"ForceOrthogonal");
365 if (ForceOrthogonal &&
m_bases[0].getNumDims() == 3 &&
m_bases.size() >= 2) {
366 std::vector<VMD> firstTwo =
m_bases;
367 firstTwo.resize(2,
VMD(3));
393 auto ct = std::make_unique<DataObjects::CoordTransformAffine>(inD,
m_outD);
399 auto ctFrom = std::make_unique<DataObjects::CoordTransformAffine>(inD,
m_outD);
405 throw std::invalid_argument(
"The number of input dimensions in the CoordinateTransform "
406 "object is not consistent with the number of dimensions in the input "
409 throw std::invalid_argument(
"The number of output dimensions in the CoordinateTransform "
410 "object is not consistent with the number of dimensions specified in "
411 "the OutDimX, etc. properties.");
417 auto ctTo = std::make_unique<DataObjects::CoordTransformAffine>(inD,
m_outD);
435 throw std::runtime_error(
"Empty string passed to one of the AlignedDim0 parameters.");
439 if (input.size() < 4)
440 throw std::invalid_argument(
"Dimensions string is too short to be valid: " + str);
443 size_t n = std::string::npos;
444 for (
size_t i = 0; i < 3; i++) {
445 n = input.find_last_of(
',',
n);
446 if (
n == std::string::npos)
447 throw std::invalid_argument(
"Wrong number of values (4 are expected) "
448 "in the dimensions string: " +
451 throw std::invalid_argument(
"Dimension string starts with a comma: " + str);
455 std::string
name = input.substr(0,
n + 1);
459 input = input.substr(
n + 2);
460 std::vector<std::string> strs;
461 boost::split(strs, input, boost::is_any_of(
","));
462 if (strs.size() != 3)
463 throw std::invalid_argument(
"Wrong number of values (3 are expected) after the name "
464 "in the dimensions string: " +
474 throw std::invalid_argument(
"Name should not be blank.");
476 throw std::invalid_argument(
"Min should be > max.");
478 throw std::invalid_argument(
"Number of bins should be >= 1.");
481 size_t dim_index = 0;
483 dim_index =
m_inWS->getDimensionIndexByName(
name);
484 }
catch (std::runtime_error &) {
487 dim_index =
m_inWS->getDimensionIndexById(
name);
488 }
catch (std::runtime_error &) {
489 throw std::runtime_error(
"Dimension " +
name +
490 " was not found in the "
491 "MDEventWorkspace! Cannot continue.");
497 const auto &frame = inputDim->getMDFrame();
499 new MDHistoDimension(inputDim->getName(), inputDim->getDimensionId(), frame, min, max, numBins));
513 bool previousWasEmpty =
false;
515 for (
char dimChar : dimChars) {
516 std::string propName =
"AlignedDim0";
517 propName[10] = dimChar;
521 if (!prop.empty() && previousWasEmpty)
522 throw std::invalid_argument(
"Please enter the AlignedDim parameters in the order 0,1,2, etc.,"
523 "without skipping any entries.");
524 previousWasEmpty = prop.empty();
528 size_t inD =
m_inWS->getNumDims();
531 throw std::runtime_error(
"No output dimensions specified.");
533 throw std::runtime_error(
"More output dimensions were specified than input dimensions "
534 "exist in the MDEventWorkspace.");
539 for (
size_t i = 0; i < numDims; i++) {
540 std::string propName =
"AlignedDim0";
541 propName[10] = dimChars[i];
550 std::size_t totalBins =
552 [](std::size_t acc,
const std::shared_ptr<MDHistoDimension> &dim) -> std::size_t {
553 return acc * dim->getNBins();
556 constexpr std::size_t bytesPerBin = 3 *
sizeof(double) +
sizeof(
bool);
558 if (!memStr.empty()) {
559 memStr =
"You requested a total of " +
std::to_string(totalBins) +
" bins. " + memStr +
560 " Please reduce the number of bins or output dimensions.";
561 throw std::runtime_error(memStr);
584 std::vector<coord_t> unitScaling(
m_outD, 1.0);
585 std::vector<coord_t> zeroOrigin(
m_outD, 0.0);
595 auto tmp = std::make_unique<DataObjects::CoordTransformAffine>(inD,
m_outD);
601 g_log.
warning(
"SlicingAlgorithm: Your slice will cause the output "
602 "workspace to have fewer dimensions than the input. This will "
603 "affect your ability to create subsequent slices.");
621 throw std::runtime_error(
"SlicingAlgorithm::createTransform(): input "
622 "MDWorkspace must be set first!");
623 if (std::dynamic_pointer_cast<MatrixWorkspace>(
m_inWS))
624 throw std::runtime_error(this->
name() +
" cannot be run on a MatrixWorkspace!");
630 if (
m_inWS->numOriginalWorkspaces() > 0)
631 m_originalWS = std::dynamic_pointer_cast<IMDWorkspace>(
m_inWS->getOriginalWorkspace());
634 throw std::runtime_error(
"Cannot perform axis-aligned binning on a MDHistoWorkspace. "
635 "Please use non-axis aligned binning.");
638 throw std::runtime_error(
"SlicingAlgorithm::createTransform(): Cannot propagate "
639 "a transformation if the number of dimensions has changed.");
641 if (!
m_inWS->getTransformToOriginal())
642 throw std::runtime_error(
"SlicingAlgorithm::createTransform(): Cannot propagate "
643 "a transformation. There is no transformation saved from " +
649 if (inHisto->getNumExperimentInfo() > 0) {
650 const Run &run = inHisto->getExperimentInfo(0)->run();
654 if (prop->
value() ==
"1") {
655 throw std::runtime_error(
"This MDHistoWorkspace was modified by a binary operation "
656 "(e.g. Plus, Minus). "
657 "It is not currently possible to rebin a modified "
658 "MDHistoWorkspace because that requires returning to the "
660 "(unmodified) MDEventWorkspace, and so would give incorrect "
662 "Instead, you can use SliceMD and perform operations on the "
664 "MDEventWorkspaces, which preserve all events. "
665 "You can override this check by removing the "
666 "'mdhisto_was_modified' sample log.");
697 Matrix<coord_t> matToIntermediate = matOriginalToIntermediate * matToOriginal;
703 matToIntermediate.
Invert();
707 }
catch (std::runtime_error &) {
736 const size_t *
const chunkMax) {
737 size_t nd =
m_inWS->getNumDims();
740 auto func = std::make_unique<MDImplicitFunction>();
747 std::vector<VMD> bases;
754 if (chunkMin !=
nullptr)
756 if (chunkMax !=
nullptr)
763 bases.emplace_back(thisBase);
769 size_t boxDim = bases.
size();
772 VMD insidePoint = (o1 + o2) / 2.0;
778 func->addPlane(
MDPlane(
x * -1.0, o2));
779 }
else if (boxDim == nd || boxDim == nd - 1) {
792 std::vector<VMD> vectors;
794 for (
size_t ignoreIndex = 0; ignoreIndex < boxDim; ++ignoreIndex) {
797 for (
size_t baseIndex = 0; baseIndex < boxDim; ++baseIndex) {
798 if (baseIndex != ignoreIndex)
799 vectors.emplace_back(bases[baseIndex]);
804 if (boxDim == nd - 1)
808 func->addPlane(
MDPlane(vectors, o1, insidePoint));
809 func->addPlane(
MDPlane(vectors, o2, insidePoint));
816 " dimensions and " +
"therefore will assume orthogonality");
817 for (
auto &base : bases) {
820 func->addPlane(
MDPlane(base, o1));
821 func->addPlane(
MDPlane(base * -1.0, o2));
842 const size_t *
const chunkMax) {
843 size_t nd =
m_inWS->getNumDims();
845 std::vector<coord_t> function_min(nd, -1e30f);
846 std::vector<coord_t> function_max(nd, +1e30f);
847 for (
size_t bd = 0; bd <
m_outD; bd++) {
859 return std::make_unique<MDBoxImplicitFunction>(function_min, function_max);
888 std::vector<Mantid::Kernel::VMD> oldBasis;
889 for (
size_t i = 0; i < dimension; ++i) {
891 basisVector[i] = 1.0;
892 oldBasis.emplace_back(basisVector);
905 return std::fabs(oldVector.
scalar_prod(basisVector)) > 0.0;
915 const std::vector<Mantid::Kernel::VMD> &oldBasis)
const {
916 std::vector<size_t> indexWithProjection;
919 indexWithProjection.emplace_back(
index);
922 return indexWithProjection;
934 const std::string &units)
const {
935 if (indicesWithProjection.empty()) {
936 g_log.
warning() <<
"Slicing Algorithm: Chosen vector does not "
937 "project on any vector of the old basis.";
940 const auto &referenceMDFrame =
m_inWS->getDimension(indicesWithProjection[0])->getMDFrame();
942 for (
auto &
index : indicesWithProjection) {
943 const auto &toCheckMDFrame =
m_inWS->getDimension(
index)->getMDFrame();
944 if (!referenceMDFrame.isSameType(toCheckMDFrame)) {
945 g_log.
warning() <<
"Slicing Algorithm: New basis vector tries to "
946 "mix un-mixable MDFrame types.";
962 boost::regex pattern(
"in.*A.*\\^-1");
964 if (boost::regex_match(unit, pattern)) {
967 mdFrame->setMDUnit(md_unit);
968 }
else if (unit ==
"r") {
971 mdFrame->setMDUnit(md_unit);
972 }
else if (unit ==
"a") {
975 mdFrame->setMDUnit(md_unit);
std::map< DeltaEMode::Type, std::string > index
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
const std::string name() const override=0
function to return a name of the algorithm, must be overridden in all algorithms
bool hasProperty(const std::string &name) const
Does the property exist on the object.
Kernel::Property * getProperty(const std::string &name) const
Returns the named property as a pointer.
This class stores information regarding an experimental run as a series of log entries.
A generalized description of a N-dimensional hyperplane.
Support for a property that holds an array of values.
void setPropertySettings(const std::string &name, std::unique_ptr< IPropertySettings const > settings)
Add a PropertySettings instance to the chain of settings for a given property.
void setPropertyGroup(const std::string &name, const std::string &group)
Set the group for a given property.
void notice(const std::string &msg)
Logs at notice level.
void warning(const std::string &msg)
Logs at warning level.
void information(const std::string &msg)
Logs at information level.
T Invert()
LU inversion routine.
This class is responsible for memory statistics.
std::string checkAvailableMemory(std::size_t const requestedMemoryBytes) const
Check if there is enough space in memory to hold the requested amount of memory.
The concrete, templated class for properties.
Base class for properties.
virtual std::string value() const =0
Returns the value of the property as a string.
static std::vector< VMDBase > makeVectorsOrthogonal(std::vector< VMDBase > &vectors)
Make an orthogonal system with 2 input 3D vectors.
TYPE normalize()
Normalize this vector to unity length.
TYPE scalar_prod(const VMDBase &v) const
Scalar product of two vectors.
size_t getNumDims() const
static VMDBase getNormalVector(const std::vector< VMDBase > &vectors)
Given N-1 vectors defining a N-1 dimensional hyperplane in N dimensions, returns a vector that is nor...
std::unique_ptr< API::CoordTransform > m_transformFromOriginal
Coordinate transformation to save in the output workspace (original->binned)
Mantid::API::IMDWorkspace_sptr m_intermediateWS
Intermediate original workspace.
std::vector< size_t > m_dimensionToBinFrom
Index of the dimension in the MDEW for the dimension in the output.
std::unique_ptr< Mantid::Geometry::MDImplicitFunction > getImplicitFunctionForChunk(const size_t *const chunkMin, const size_t *const chunkMax)
Create an implicit function for picking boxes, based on the indexes in the output MDHistoWorkspace.
bool isProjectingOnFrame(const Mantid::Kernel::VMD &oldVector, const Mantid::Kernel::VMD &basisVector) const
Check if the two vectors are orthogonal or not.
std::unique_ptr< API::CoordTransform > m_transform
Coordinate transformation to apply.
std::vector< size_t > getIndicesWithProjection(const Mantid::Kernel::VMD &basisVector, const std::vector< Mantid::Kernel::VMD > &oldBasis) const
Get indices which have a projection contribution.
Mantid::API::IMDWorkspace_sptr m_originalWS
Original (MDEventWorkspace) that inWS was based on.
static std::string getDimensionChars()
void createGeneralTransform()
Loads the dimensions and create the coordinate transform, using the inputs.
void createTransform()
Read the algorithm properties and creates the appropriate transforms for slicing the MDEventWorkspace...
void createAlignedTransform()
Using the parameters, create a coordinate transformation for aligned cuts.
void setTargetUnits(Mantid::Geometry::MDFrame_uptr &frame, const std::string &units) const
std::vector< Mantid::Kernel::VMD > getOldBasis(size_t dimension) const
Mantid::Kernel::VMD m_inputMinPoint
Coordinates in the INPUT workspace corresponding to the minimum edge in all dimensions.
std::vector< double > m_maxExtents
For non-aligned, the maximum coordinate extents in each OUTPUT dimension.
Mantid::API::IMDWorkspace_sptr m_inWS
Input workspace.
std::unique_ptr< API::CoordTransform > m_transformToOriginal
Coordinate transformation to save in the output workspace (binned->original)
std::unique_ptr< DataObjects::CoordTransformAffine > m_transformFromIntermediate
Coordinate transformation to save in the output WS, from the intermediate WS.
std::vector< double > m_binningScaling
Scaling factor to apply for each basis vector (to map to the bins).
size_t m_outD
Number of dimensions in the output (binned) workspace.
void makeBasisVectorFromString(const std::string &str)
Generate the MDHistoDimension and basis vector for a given string from BasisVector0 etc.
Mantid::Geometry::MDFrame_uptr createMDFrameForNonAxisAligned(const std::string &units, const Mantid::Kernel::VMD &basisVector) const
Create an MDFrame for the Non-Axis-Aligned case.
bool m_NormalizeBasisVectors
The NormalizeBasisVectors option.
void processGeneralTransformProperties()
Reads the various Properties for the general (non-aligned) case and fills in members on the Algorithm...
std::vector< Mantid::Kernel::VMD > m_bases
Basis vectors of the output dimensions, normalized to unity length.
std::unique_ptr< Mantid::Geometry::MDImplicitFunction > getGeneralImplicitFunction(const size_t *const chunkMin, const size_t *const chunkMax)
Create an implicit function for picking boxes, based on the indexes in the output MDHistoWorkspace.
Mantid::Geometry::MDFrame_uptr extractMDFrameForNonAxisAligned(std::vector< size_t > indicesWithProjection, const std::string &units) const
Extract the MDFrame.
bool m_axisAligned
Set to true if the cut is aligned with the axes.
void initSlicingProps()
Initialise the properties.
std::vector< int > m_numBins
For non-aligned, the number of bins in each OUTPUT dimension.
SlicingAlgorithm()
Constructor.
Mantid::Kernel::VMD m_translation
Translation from the OUTPUT to the INPUT workspace i.e.
std::vector< double > m_transformScaling
Scaling factor to apply for each basis vector to transfor to the output dimensions.
std::vector< double > m_minExtents
For non-aligned, the minimum coordinate extents in each OUTPUT dimension.
std::unique_ptr< DataObjects::CoordTransformAffine > m_transformToIntermediate
Coordinate transformation to save in the intermediate WS.
void makeAlignedDimensionFromString(const std::string &str)
Generate a MDHistoDimension_sptr from a comma-sep string (for AlignedDim0, etc.) Must be called in or...
std::vector< Mantid::Geometry::MDHistoDimension_sptr > m_binDimensions
Bin dimensions to actually use.
std::shared_ptr< MDHistoWorkspace > MDHistoWorkspace_sptr
A shared pointer to a MDHistoWorkspace.
std::unique_ptr< MDFrame > MDFrame_uptr
std::shared_ptr< const IMDDimension > IMDDimension_const_sptr
Shared Pointer to const IMDDimension.
MANTID_KERNEL_DLL std::string strip(const std::string &A)
strip pre/post spaces
int convert(const std::string &A, T &out)
Convert a string into a number.
std::string toString(const T &value)
Convert a number to a string.
VMDBase< VMD_t > VMD
Define the VMD as using the double or float data type.
float coord_t
Typedef for the data type to use for coordinate axes in MD objects such as MDBox, MDEventWorkspace,...
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.