Mantid
Loading...
Searching...
No Matches
SetGoniometer.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 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 +
10#include "MantidAPI/Run.h"
18
19#include <boost/algorithm/string/classification.hpp>
20#include <boost/algorithm/string/split.hpp>
21
23using namespace Mantid::Geometry;
24
25namespace Mantid::Crystal {
26
27// Register the algorithm into the AlgorithmFactory
28DECLARE_ALGORITHM(SetGoniometer)
29
30using namespace Mantid::Kernel;
31using namespace Mantid::API;
32
34const size_t NUM_AXES = 6;
35
39
40 auto threeVthree = std::make_shared<ArrayLengthValidator<double>>(9);
41 std::vector<double> zeroes(9, 0.);
42
43 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("Workspace", "", Direction::InOut),
44 "An workspace that will be modified with the new goniometer created.");
45
46 std::vector<std::string> gonOptions{"None, Specify Individually", "Universal"};
47 declareProperty("Goniometers", gonOptions[0], std::make_shared<StringListValidator>(gonOptions),
48 "Set the axes and motor names according to goniometers that "
49 "we define in the code (Universal defined for SNS)");
50
51 std::string axisHelp = ": name, x,y,z, 1/-1 (1 for ccw, -1 for cw rotation). "
52 "A number of degrees can be used instead of name. "
53 "Leave blank for no axis";
54 for (size_t i = 0; i < NUM_AXES; i++) {
55 std::ostringstream propName;
56 propName << "Axis" << i;
57 declareProperty(std::make_unique<PropertyWithValue<std::string>>(propName.str(), "", Direction::Input),
58 propName.str() + axisHelp);
59 }
60 declareProperty("Average", true,
61 "Use the average value of the log, if false a separate "
62 "goniometer will be created for each value in the logs");
63
64 declareProperty(std::make_unique<ArrayProperty<double>>("GoniometerMatrix", std::move(zeroes), threeVthree),
65 "Directly set the goniometer rotation matrix. Input should be in the form of a flattened 3x3 matrix");
66 for (size_t i = 0; i < NUM_AXES; i++) {
67 std::ostringstream propName;
68 propName << "Axis" << i;
69 setPropertySettings(propName.str(), std::make_unique<EnabledWhenProperty>("GoniometerMatrix", IS_DEFAULT));
70 }
71}
72
73std::map<std::string, std::string> SetGoniometer::validateInputs() {
74 std::vector<double> Gvec = getProperty("GoniometerMatrix");
75 std::map<std::string, std::string> issues;
76 const Kernel::DblMatrix GMatrix(Gvec);
77
78 // if a Goniometer Matrix is supplied, check it is a valid rotation
79 if (!isDefault("GoniometerMatrix")) {
80 for (size_t i = 0; i < NUM_AXES; i++) {
81 std::ostringstream propName;
82 propName << "Axis" << i;
83 if (!isDefault(propName.str())) {
84 issues[propName.str()] = "Can't provide a goniometer axis if a matrix string has also been provided";
85 }
86 }
87
88 if (GMatrix.numRows() == GMatrix.numCols()) {
89 bool isRot = GMatrix.isRotation();
90 if (!isRot) {
91 issues["GoniometerMatrix"] = "Supplied Goniometer Matrix is not a proper rotation";
92 }
93 } else {
94 // this should not be reached because of the input validator
95 issues["GoniometerMatrix"] = "Supplied Goniometer Matrix is not a proper rotation: Matrix is not Square";
96 }
97 }
98 return issues;
99}
100
104 Workspace_sptr ws = getProperty("Workspace");
105 auto ei = std::dynamic_pointer_cast<ExperimentInfo>(ws);
106
107 if (!ei) {
108 // We're dealing with an MD workspace which has multiple experiment infos
109 auto infos = std::dynamic_pointer_cast<MultipleExperimentInfos>(ws);
110 if (!infos) {
111 throw std::invalid_argument("Input workspace does not support Goniometer");
112 }
113 if (infos->getNumExperimentInfo() < 1) {
115 infos->addExperimentInfo(info);
116 }
117 ei = infos->getExperimentInfo(0);
118 }
119
120 // Create the goniometer
121 Goniometer gon;
122
123 if (isDefault("GoniometerMatrix")) {
124 std::string gonioDefined = getPropertyValue("Goniometers");
125 if (gonioDefined == "Universal")
127 else
128 for (size_t i = 0; i < NUM_AXES; i++) {
129 std::ostringstream propName;
130 propName << "Axis" << i;
131 std::string axisDesc = getPropertyValue(propName.str());
132
133 if (!axisDesc.empty()) {
134 std::vector<std::string> tokens;
135 boost::split(tokens, axisDesc, boost::algorithm::detail::is_any_ofF<char>(","));
136 if (tokens.size() != 5)
137 throw std::invalid_argument("Wrong number of arguments to parameter " + propName.str() +
138 ". Expected 5 comma-separated arguments.");
139
140 std::string axisName = tokens[0];
141 axisName = Strings::strip(axisName);
142 if (axisName.empty())
143 throw std::invalid_argument("The name must not be empty");
144
145 // If axisName is a number, add a new log value
146 double angle = 0;
147 if (Strings::convert(axisName, angle)) {
148 g_log.information() << "Axis " << i << " - create a new log value GoniometerAxis" << i << "_FixedValue\n";
149 axisName = "GoniometerAxis" + Strings::toString(i) + "_FixedValue";
150 try {
151 Types::Core::DateAndTime now = Types::Core::DateAndTime::getCurrentTime();
152 auto tsp = new Kernel::TimeSeriesProperty<double>(axisName);
153 tsp->addValue(now, angle);
154 tsp->setUnits("degree");
155 if (ei->mutableRun().hasProperty(axisName)) {
156 ei->mutableRun().removeLogData(axisName);
157 }
158 ei->mutableRun().addLogData(tsp);
159 } catch (...) {
160 g_log.error("Could not add axis");
161 }
162 }
163
164 double x = 0, y = 0, z = 0;
165 if (!Strings::convert(tokens[1], x))
166 throw std::invalid_argument("Error converting string '" + tokens[1] + "' to a number.");
167 if (!Strings::convert(tokens[2], y))
168 throw std::invalid_argument("Error converting string '" + tokens[2] + "' to a number.");
169 if (!Strings::convert(tokens[3], z))
170 throw std::invalid_argument("Error converting string '" + tokens[3] + "' to a number.");
171 V3D vec(x, y, z);
172 if (vec.norm() < 1e-4)
173 throw std::invalid_argument("Rotation axis vector should be non-zero!");
174
175 int ccw = 0;
176 Strings::convert(tokens[4], ccw);
177 if (ccw != 1 && ccw != -1)
178 throw std::invalid_argument("The ccw parameter must be 1 (ccw) or -1 "
179 "(cw) but no other value.");
180 // Default to degrees
181 gon.pushAxis(axisName, x, y, z, 0.0, ccw);
182 }
183 }
184
185 if (gon.getNumberAxes() == 0)
186 g_log.warning() << "Empty goniometer created; will always return an "
187 "identity rotation matrix.\n";
188
189 // All went well, copy the goniometer into it. It will throw if the log values
190 // cannot be found
191 try {
192 if (getProperty("Average"))
193 ei->mutableRun().setGoniometer(gon, true);
194 else
195 ei->mutableRun().setGoniometers(gon);
196 } catch (std::runtime_error &) {
197 g_log.error("No log values for goniometers");
198 }
199 } else {
200 const std::vector<double> gonVec = getProperty("GoniometerMatrix");
201 const Kernel::DblMatrix gonMat(gonVec);
202 ei->mutableRun().setGoniometer(Goniometer(gonMat), false);
203 }
204}
205
206} // namespace Mantid::Crystal
#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
bool isDefault(const std::string &name) const
This class is shared by a few Workspace types and holds information related to a particular experimen...
A property class for workspaces.
void init() override
Initialise the properties.
void exec() override
Run the algorithm.
std::map< std::string, std::string > validateInputs() override
Perform validation of ALL the input properties of the algorithm.
Class to represent a particular goniometer setting, which is described by the rotation matrix.
Definition Goniometer.h:55
void makeUniversalGoniometer()
Make a default universal goniometer with phi,chi,omega angles according to SNS convention.
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.
Support for a property that holds an array of values.
void setPropertySettings(const std::string &name, std::unique_ptr< IPropertySettings > settings)
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
bool isRotation() const
Check if a matrix represents a proper rotation @ return :: true/false.
Definition Matrix.cpp:1422
size_t numRows() const
Return the number of rows in the matrix.
Definition Matrix.h:144
size_t numCols() const
Return the number of columns in the matrix.
Definition Matrix.h:147
The concrete, templated class for properties.
A specialised Property class for holding a series of time-value pairs.
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.
MANTID_KERNEL_DLL std::string strip(const std::string &A)
strip pre/post spaces
Definition Strings.cpp:419
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