-
Notifications
You must be signed in to change notification settings - Fork 122
/
SetGoniometer.cpp
159 lines (138 loc) · 6.07 KB
/
SetGoniometer.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source,
// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
// SPDX - License - Identifier: GPL - 3.0 +
#include "MantidCrystal/SetGoniometer.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/MultipleExperimentInfos.h"
#include "MantidAPI/Run.h"
#include "MantidGeometry/Instrument/Goniometer.h"
#include "MantidKernel/ListValidator.h"
#include "MantidKernel/Strings.h"
#include "MantidKernel/TimeSeriesProperty.h"
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
using Mantid::Geometry::Goniometer;
using namespace Mantid::Geometry;
namespace Mantid {
namespace Crystal {
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(SetGoniometer)
using namespace Mantid::Kernel;
using namespace Mantid::API;
/// How many axes (max) to define
const size_t NUM_AXES = 6;
/** Initialize the algorithm's properties.
*/
void SetGoniometer::init() {
declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("Workspace", "", Direction::InOut),
"An workspace that will be modified with the new goniometer created.");
std::vector<std::string> gonOptions{"None, Specify Individually", "Universal"};
declareProperty("Goniometers", gonOptions[0], std::make_shared<StringListValidator>(gonOptions),
"Set the axes and motor names according to goniometers that "
"we define in the code (Universal defined for SNS)");
std::string axisHelp = ": name, x,y,z, 1/-1 (1 for ccw, -1 for cw rotation). "
"A number of degrees can be used instead of name. "
"Leave blank for no axis";
for (size_t i = 0; i < NUM_AXES; i++) {
std::ostringstream propName;
propName << "Axis" << i;
declareProperty(std::make_unique<PropertyWithValue<std::string>>(propName.str(), "", Direction::Input),
propName.str() + axisHelp);
}
declareProperty("Average", true,
"Use the average value of the log, if false a separate "
"goniometer will be created for each value in the logs");
}
/** Execute the algorithm.
*/
void SetGoniometer::exec() {
Workspace_sptr ws = getProperty("Workspace");
auto ei = std::dynamic_pointer_cast<ExperimentInfo>(ws);
if (!ei) {
// We're dealing with an MD workspace which has multiple experiment infos
auto infos = std::dynamic_pointer_cast<MultipleExperimentInfos>(ws);
if (!infos) {
throw std::invalid_argument("Input workspace does not support Goniometer");
}
if (infos->getNumExperimentInfo() < 1) {
ExperimentInfo_sptr info(new ExperimentInfo());
infos->addExperimentInfo(info);
}
ei = infos->getExperimentInfo(0);
}
std::string gonioDefined = getPropertyValue("Goniometers");
// Create the goniometer
Goniometer gon;
if (gonioDefined == "Universal")
gon.makeUniversalGoniometer();
else
for (size_t i = 0; i < NUM_AXES; i++) {
std::ostringstream propName;
propName << "Axis" << i;
std::string axisDesc = getPropertyValue(propName.str());
if (!axisDesc.empty()) {
std::vector<std::string> tokens;
boost::split(tokens, axisDesc, boost::algorithm::detail::is_any_ofF<char>(","));
if (tokens.size() != 5)
throw std::invalid_argument("Wrong number of arguments to parameter " + propName.str() +
". Expected 5 comma-separated arguments.");
std::string axisName = tokens[0];
axisName = Strings::strip(axisName);
if (axisName.empty())
throw std::invalid_argument("The name must not be empty");
// If axisName is a number, add a new log value
double angle = 0;
if (Strings::convert(axisName, angle)) {
g_log.information() << "Axis " << i << " - create a new log value GoniometerAxis" << i << "_FixedValue\n";
axisName = "GoniometerAxis" + Strings::toString(i) + "_FixedValue";
try {
Types::Core::DateAndTime now = Types::Core::DateAndTime::getCurrentTime();
auto tsp = new Kernel::TimeSeriesProperty<double>(axisName);
tsp->addValue(now, angle);
tsp->setUnits("degree");
if (ei->mutableRun().hasProperty(axisName)) {
ei->mutableRun().removeLogData(axisName);
}
ei->mutableRun().addLogData(tsp);
} catch (...) {
g_log.error("Could not add axis");
}
}
double x = 0, y = 0, z = 0;
if (!Strings::convert(tokens[1], x))
throw std::invalid_argument("Error converting string '" + tokens[1] + "' to a number.");
if (!Strings::convert(tokens[2], y))
throw std::invalid_argument("Error converting string '" + tokens[2] + "' to a number.");
if (!Strings::convert(tokens[3], z))
throw std::invalid_argument("Error converting string '" + tokens[3] + "' to a number.");
V3D vec(x, y, z);
if (vec.norm() < 1e-4)
throw std::invalid_argument("Rotation axis vector should be non-zero!");
int ccw = 0;
Strings::convert(tokens[4], ccw);
if (ccw != 1 && ccw != -1)
throw std::invalid_argument("The ccw parameter must be 1 (ccw) or -1 "
"(cw) but no other value.");
// Default to degrees
gon.pushAxis(axisName, x, y, z, 0.0, ccw);
}
}
if (gon.getNumberAxes() == 0)
g_log.warning() << "Empty goniometer created; will always return an "
"identity rotation matrix.\n";
// All went well, copy the goniometer into it. It will throw if the log values
// cannot be found
try {
if (getProperty("Average"))
ei->mutableRun().setGoniometer(gon, true);
else
ei->mutableRun().setGoniometers(gon);
} catch (std::runtime_error &) {
g_log.error("No log values for goniometers");
}
}
} // namespace Crystal
} // namespace Mantid