Mantid
Loading...
Searching...
No Matches
RotateSampleShape.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2024 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
9#include "MantidAPI/Run.h"
10#include "MantidAPI/Sample.h"
17#include <boost/algorithm/string/classification.hpp>
18#include <boost/algorithm/string/split.hpp>
19
20namespace Mantid::DataHandling {
21
22// Register the algorithm into the AlgorithmFactory
23DECLARE_ALGORITHM(RotateSampleShape)
24
25using namespace Mantid::Geometry;
26using namespace Mantid::Kernel;
27using namespace Mantid::API;
28
30const size_t NUM_AXES = 6;
31
35 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("Workspace", "", Direction::InOut),
36 "The workspace containing the sample whose orientation is to be rotated");
37
38 std::string axisHelp = ": degrees,x,y,z,1/-1 (1 for ccw, -1 for cw rotation).";
39 for (size_t i = 0; i < NUM_AXES; i++) {
40 std::ostringstream propName;
41 propName << "Axis" << i;
42 declareProperty(std::make_unique<PropertyWithValue<std::string>>(propName.str(), "", Direction::Input),
43 propName.str() + axisHelp);
44 }
45}
46
50 Workspace_sptr ws = getProperty("Workspace");
51 auto ei = std::dynamic_pointer_cast<ExperimentInfo>(ws);
52
53 if (!ei) {
54 // We're dealing with an MD workspace which has multiple experiment infos
55 auto infos = std::dynamic_pointer_cast<MultipleExperimentInfos>(ws);
56 if (!infos) {
57 throw std::invalid_argument("Input workspace does not support RotateSampleShape");
58 }
59 if (infos->getNumExperimentInfo() < 1) {
61 infos->addExperimentInfo(info);
62 }
63 ei = infos->getExperimentInfo(0);
64 }
65
66 std::string shapeXML;
67 bool isMeshShape = false;
68 if (!checkIsValidShape(ei, shapeXML, isMeshShape)) {
69 throw std::runtime_error("Input sample does not have a valid shape!");
70 }
71
72 // Create a goniometer with provided rotations
73 Goniometer gon;
75 if (gon.getNumberAxes() == 0)
76 g_log.warning() << "Empty goniometer created; will always return an "
77 "identity rotation matrix.\n";
78
79 const auto &sampleShapeRotation = gon.getR();
80 if (sampleShapeRotation == Kernel::Matrix<double>(3, 3, true)) {
81 // If the resulting rotationMatrix is Identity, ignore the calculatrion
82 g_log.warning("Rotation matrix set via RotateSampleShape is an Identity matrix. Ignored rotating sample shape");
83 return;
84 }
85
86 const auto &oldRotation = ei->run().getGoniometer().getR();
87 auto newSampleShapeRot = sampleShapeRotation * oldRotation;
88 if (isMeshShape) {
89 auto meshShape = std::dynamic_pointer_cast<MeshObject>(ei->sample().getShapePtr());
90 meshShape->rotate(newSampleShapeRot);
91 } else {
92 shapeXML = Geometry::ShapeFactory().addGoniometerTag(newSampleShapeRot, shapeXML);
94 }
95}
96
98 bool &isMeshShape) {
99 if (ei->sample().hasShape()) {
100 const auto csgShape = std::dynamic_pointer_cast<CSGObject>(ei->sample().getShapePtr());
101 if (csgShape && csgShape->hasValidShape()) {
102 shapeXML = csgShape->getShapeXML();
103 if (!shapeXML.empty()) {
104 return true;
105 }
106 } else {
107 const auto meshShape = std::dynamic_pointer_cast<MeshObject>(ei->sample().getShapePtr());
108 if (meshShape && meshShape->hasValidShape()) {
109 isMeshShape = true;
110 return true;
111 }
112 }
113 }
114 return false;
115}
116
118 for (size_t i = 0; i < NUM_AXES; i++) {
119 std::ostringstream propName;
120 propName << "Axis" << i;
121 std::string axisDesc = getPropertyValue(propName.str());
122 if (!axisDesc.empty()) {
123 std::vector<std::string> tokens;
124 boost::split(tokens, axisDesc, boost::algorithm::detail::is_any_ofF<char>(","));
125 if (tokens.size() != 5)
126 throw std::invalid_argument("Wrong number of arguments to parameter " + propName.str() +
127 ". Expected 5 comma-separated arguments.");
128
129 std::transform(tokens.begin(), tokens.end(), tokens.begin(),
130 [](const std::string &str) { return Strings::strip(str); });
131 if (!std::all_of(tokens.begin(), tokens.end(), [](const std::string &tokenStr) { return !tokenStr.empty(); })) {
132 throw std::invalid_argument("Empty axis parameters found!");
133 }
134
135 double angle = 0;
136 if (!Strings::convert(tokens[0], angle)) {
137 throw std::invalid_argument("Error converting angle string '" + tokens[0] + "' to a number.");
138 }
139
140 std::string axisName = "RotateSampleShapeAxis" + Strings::toString(i) + "_FixedValue";
141 double x = 0, y = 0, z = 0;
142 if (!Strings::convert(tokens[1], x))
143 throw std::invalid_argument("Error converting x string '" + tokens[1] + "' to a number.");
144 if (!Strings::convert(tokens[2], y))
145 throw std::invalid_argument("Error converting y string '" + tokens[2] + "' to a number.");
146 if (!Strings::convert(tokens[3], z))
147 throw std::invalid_argument("Error converting z string '" + tokens[3] + "' to a number.");
148 V3D vec(x, y, z);
149 if (vec.norm() < 1e-4)
150 throw std::invalid_argument("Rotation axis vector should be non-zero!");
151
152 int ccw = 0;
153 if (!Strings::convert(tokens[4], ccw)) {
154 throw std::invalid_argument("Error converting sense of roation '" + tokens[4] + "' to a number.");
155 }
156 if (ccw != 1 && ccw != -1) {
157 throw std::invalid_argument("The sense of rotation parameter must only be 1 (ccw) or -1 (cw)");
158 }
159 // Default to degrees
160 gon.pushAxis(axisName, x, y, z, angle, ccw);
161 }
162 }
163}
164
165} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
std::vector< T > const * vec
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.
Kernel::Logger & g_log
Definition Algorithm.h:422
This class is shared by a few Workspace types and holds information related to a particular experimen...
A property class for workspaces.
static void setSampleShape(API::ExperimentInfo &expt, const std::string &shapeXML, bool addTypeTag=true)
Set the shape via an XML string on the given experiment.
void exec() override
Run the algorithm.
bool checkIsValidShape(const API::ExperimentInfo_sptr &ei, std::string &shapeXML, bool &isMeshShape)
void init() override
Initialise the properties.
Class to represent a particular goniometer setting, which is described by the rotation matrix.
Definition Goniometer.h:55
void pushAxis(const std::string &name, double axisx, double axisy, double axisz, double angle=0., int sense=CCW, int angUnit=angDegrees)
Add an additional axis to the goniometer, closer to the sample.
const Kernel::DblMatrix & getR() const
Return global rotation matrix.
Class originally intended to be used with the DataHandling 'LoadInstrument' algorithm.
std::string addGoniometerTag(const Kernel::Matrix< double > &rotateMatrix, std::string xml)
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
Numerical Matrix class.
Definition Matrix.h:42
The concrete, templated class for properties.
Class for 3D vectors.
Definition V3D.h:34
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::shared_ptr< ExperimentInfo > ExperimentInfo_sptr
Shared pointer to ExperimentInfo.
const size_t NUM_AXES
How many axes (max) to define.
int convert(const std::string &A, T &out)
Convert a string into a number.
Definition Strings.cpp:696
std::string toString(const T &value)
Convert a number to a string.
Definition Strings.cpp:734
@ InOut
Both an input & output workspace.
Definition Property.h:55
@ Input
An input workspace.
Definition Property.h:53