Mantid
Loading...
Searching...
No Matches
LoadIsawDetCal.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 +
8
12#include "MantidAPI/Run.h"
14
19
22
24#include "MantidKernel/V3D.h"
25
26#include <algorithm>
27#include <boost/algorithm/string/trim.hpp>
28#include <fstream>
29#include <numeric>
30#include <sstream>
31#include <utility>
32
33namespace Mantid::DataHandling {
34
35// Register the class into the algorithm factory
36DECLARE_ALGORITHM(LoadIsawDetCal)
37
38using namespace Kernel;
39using namespace API;
40using namespace Geometry;
41using namespace DataObjects;
42
46 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("InputWorkspace", "", Direction::InOut,
47 std::make_shared<InstrumentValidator>()),
48 "The workspace containing the geometry to be calibrated.");
49
50 const auto exts = std::vector<std::string>({".DetCal", ".detcal", ".peaks", ".integrate"});
51 declareProperty(std::make_unique<API::MultipleFileProperty>("Filename", exts),
52 "The input filename of the ISAW DetCal file (Two files "
53 "allowed for SNAP) ");
54
55 declareProperty(std::make_unique<API::FileProperty>("Filename2", "", API::FileProperty::OptionalLoad, exts),
56 "The input filename of the second ISAW DetCal file (West "
57 "banks for SNAP) ");
58
59 declareProperty("TimeOffset", 0.0, "Time Offset", Direction::Output);
60}
61
62namespace {
63const constexpr double DegreesPerRadian = 180.0 / M_PI;
64
65std::string getBankName(const std::string &bankPart, const int idnum) {
66 if (bankPart == "WISHpanel" && idnum < 10) {
67 return bankPart + "0" + std::to_string(idnum);
68 } else {
69 return bankPart + std::to_string(idnum);
70 }
71}
72
73std::string getInstName(const API::Workspace_const_sptr &wksp) {
74 MatrixWorkspace_const_sptr matrixWksp = std::dynamic_pointer_cast<const MatrixWorkspace>(wksp);
75 if (matrixWksp) {
76 return matrixWksp->getInstrumentName();
77 }
78
79 PeaksWorkspace_const_sptr peaksWksp = std::dynamic_pointer_cast<const PeaksWorkspace>(wksp);
80 if (peaksWksp) {
81 return peaksWksp->getInstrumentName();
82 }
83
84 throw std::runtime_error("Failed to determine instrument name");
85}
86} // namespace
87
88std::map<std::string, std::string> LoadIsawDetCal::validateInputs() {
89 std::map<std::string, std::string> result;
90
91 // two detcal files is only valid for snap
92 std::vector<std::string> filenames = getFilenames();
93 if (filenames.empty()) {
94 result["Filename"] = "Must supply .detcal file";
95 } else if (filenames.size() == 2) {
96 Workspace_const_sptr wksp = getProperty("InputWorkspace");
97 const auto instname = getInstName(wksp);
98
99 if (instname != "SNAP") {
100 result["Filename"] = "Two files is only valid for SNAP";
101 }
102 } else if (filenames.size() > 2) {
103 result["Filename"] = "Supply at most two .detcal files";
104 }
105
106 return result;
107}
108
114 // Get the input workspace
115 Workspace_sptr ws = getProperty("InputWorkspace");
116 MatrixWorkspace_sptr inputW = std::dynamic_pointer_cast<MatrixWorkspace>(ws);
117 PeaksWorkspace_sptr inputP = std::dynamic_pointer_cast<PeaksWorkspace>(ws);
118
119 Instrument_sptr inst = getCheckInst(ws);
120
121 std::string instname = inst->getName();
122
123 auto expInfoWS = std::dynamic_pointer_cast<ExperimentInfo>(ws);
124 auto &componentInfo = expInfoWS->mutableComponentInfo();
125
126 const auto filenames = getFilenames();
127
128 // Output summary to log file
129 int count, id, nrows, ncols;
130 double width, height, depth, detd, x, y, z, base_x, base_y, base_z, up_x, up_y, up_z;
131 std::ifstream input(filenames[0].c_str(), std::ios_base::in);
132 std::string line;
133 std::string detname;
134 // Build a list of Rectangular/Grid Detectors (as component indices)
135 std::vector<size_t> detList;
136 for (size_t i = 0; i < componentInfo.size(); ++i) {
137 if (componentInfo.isGridDetector(i)) {
138 detList.emplace_back(i);
139 }
140 }
141 std::unordered_set<int> uniqueBanks; // for CORELLI and WISH
142 std::string bankPart = "bank";
143 if (instname == "WISH")
144 bankPart = "WISHpanel";
145 if (detList.empty()) {
146 // iterate over the top level components, which contain the banks
147 size_t const rootIndex = componentInfo.root();
148 auto const topChildren = componentInfo.children(rootIndex);
149 for (size_t const i : topChildren) {
150 int bank = 0;
151 size_t bankParent = componentInfo.findBankParent(i, bankPart);
152 auto const children = componentInfo.children(bankParent);
153 for (size_t const child : children) {
154 std::string bankName = componentInfo.name(child);
155 boost::trim(bankName);
156 boost::erase_all(bankName, bankPart);
157 Strings::convert(bankName, bank);
158 if (bank != 0) {
159 uniqueBanks.insert(bank);
160 }
161 }
162 }
163 }
164
165 std::vector<ComponentScaling> rectangularDetectorScalings;
166
167 while (std::getline(input, line)) {
168 if (line[0] == '7') {
169 double mL1, mT0;
170 std::stringstream(line) >> count >> mL1 >> mT0;
171 setProperty("TimeOffset", mT0);
172 // Convert from cm to m
173 const std::string sourceName = instname == "WISH" ? "undulator" : "moderator";
174 size_t sourceIndex;
175 try {
176 sourceIndex = componentInfo.indexOfAny(sourceName);
177 } catch (const std::invalid_argument &) {
178 throw std::runtime_error("Component with name " + sourceName + " was not found.");
179 }
180 center(0.0, 0.0, -mL1, sourceIndex, componentInfo);
181 // mT0 and time of flight are both in microsec
182 if (mT0 != 0.0) {
183 if (inputW) {
184 API::Run &run = inputW->mutableRun();
185 // Check to see if LoadEventNexus had T0 from TOPAZ Parameter file
186 auto alg1 = createChildAlgorithm("ChangeBinOffset");
187 alg1->setProperty<MatrixWorkspace_sptr>("InputWorkspace", inputW);
188 alg1->setProperty<MatrixWorkspace_sptr>("OutputWorkspace", inputW);
189 if (run.hasProperty("T0")) {
190 auto T0IDF = run.getPropertyValueAsType<double>("T0");
191 alg1->setProperty("Offset", mT0 - T0IDF);
192 } else {
193 alg1->setProperty("Offset", mT0);
194 }
195 alg1->executeAsChildAlg();
196 inputW = alg1->getProperty("OutputWorkspace");
197 // set T0 in the run parameters
198 run.addProperty<double>("T0", mT0, true);
199 } else if (inputP) {
200 // set T0 in the run parameters
201 API::Run &run = inputP->mutableRun();
202 run.addProperty<double>("T0", mT0, true);
203 }
204 }
205 }
206
207 if (line[0] != '5')
208 continue;
209
210 std::stringstream(line) >> count >> id >> nrows >> ncols >> width >> height >> depth >> detd >> x >> y >> z >>
211 base_x >> base_y >> base_z >> up_x >> up_y >> up_z;
212 if (id == 10 && filenames.size() == 2 && instname == "SNAP") {
213 input.close();
214 input.open(filenames[1].c_str());
215 while (std::getline(input, line)) {
216 if (line[0] != '5')
217 continue;
218
219 std::stringstream(line) >> count >> id >> nrows >> ncols >> width >> height >> depth >> detd >> x >> y >> z >>
220 base_x >> base_y >> base_z >> up_x >> up_y >> up_z;
221 if (id == 10)
222 break;
223 }
224 }
225 std::string bankName = getBankName(bankPart, id);
226 auto matchingDetector = std::find_if(detList.begin(), detList.end(), [&bankName, &componentInfo](size_t index) {
227 return componentInfo.name(index) == bankName;
228 });
229
230 V3D rX(base_x, base_y, base_z);
231 V3D rY(up_x, up_y, up_z);
232
233 if (matchingDetector != detList.end()) {
234 const size_t bankIndex = *matchingDetector;
235 detname = componentInfo.name(bankIndex);
236 center(x, y, z, bankIndex, componentInfo);
237
238 ComponentScaling detScaling;
239 const auto grid = componentInfo.pixelGridComponent(bankIndex);
240 detScaling.scaleX = CM_TO_M * width / (grid.nX * grid.xStep);
241 detScaling.scaleY = CM_TO_M * height / (grid.nY * grid.yStep);
242 detScaling.componentName = detname;
243 // Scaling will need both scale factors if LoadIsawPeaks or LoadIsawDetCal
244 // has already
245 // applied a calibration
246 if (inputW) {
247 Geometry::ParameterMap const &pmap = inputW->instrumentParameters();
248 auto oldscalex = pmap.getDouble(detname, std::string("scalex"));
249 auto oldscaley = pmap.getDouble(detname, std::string("scaley"));
250 if (!oldscalex.empty())
251 detScaling.scaleX *= oldscalex[0];
252 if (!oldscaley.empty())
253 detScaling.scaleY *= oldscaley[0];
254 }
255 if (inputP) {
256 Geometry::ParameterMap const &pmap = inputP->instrumentParameters();
257 auto oldscalex = pmap.getDouble(detname, std::string("scalex"));
258 auto oldscaley = pmap.getDouble(detname, std::string("scaley"));
259 if (!oldscalex.empty())
260 detScaling.scaleX *= oldscalex[0];
261 if (!oldscaley.empty())
262 detScaling.scaleY *= oldscaley[0];
263 }
264
265 rectangularDetectorScalings.emplace_back(detScaling);
266
267 doRotation(rX, rY, componentInfo, bankIndex);
268 }
269 auto bank = uniqueBanks.find(id);
270 if (bank == uniqueBanks.end())
271 continue;
272 int idnum = *bank;
273
274 bankName = getBankName(bankPart, idnum);
275 // Retrieve it
276 size_t componentIndex;
277 try {
278 componentIndex = componentInfo.indexOfAny(bankName);
279 } catch (const std::invalid_argument &) {
280 continue;
281 }
282 // for Corelli with sixteenpack under bank
283 if (instname == "CORELLI") {
284 const auto children = componentInfo.children(componentIndex);
285 if (!children.empty()) {
286 componentIndex = children[0];
287 }
288 }
289 // Omitted scaling tubes
290 center(x, y, z, componentIndex, componentInfo);
291
292 bool doWishCorrection = (instname == "WISH"); // TODO: find out why this is needed for WISH
293 doRotation(rX, rY, componentInfo, componentIndex, doWishCorrection);
294 }
295
296 // Do this last, to avoid the issue of invalidating DetectorInfo
297 applyScalings(ws, rectangularDetectorScalings);
298
299 setProperty("InputWorkspace", ws);
300}
301
311void LoadIsawDetCal::center(const double x, const double y, const double z, const size_t componentIndex,
312 Geometry::ComponentInfo &componentInfo) {
313 const V3D position(x * CM_TO_M, y * CM_TO_M, z * CM_TO_M);
314 componentInfo.setPosition(componentIndex, position);
315}
316
327 MatrixWorkspace_sptr inputW = std::dynamic_pointer_cast<MatrixWorkspace>(ws);
328 PeaksWorkspace_sptr inputP = std::dynamic_pointer_cast<PeaksWorkspace>(ws);
329
330 // Get some stuff from the input workspace
331 Instrument_sptr inst;
332 if (inputW) {
333 inst = std::const_pointer_cast<Instrument>(inputW->getInstrument());
334 if (!inst)
335 throw std::runtime_error("Could not get a valid instrument from the "
336 "MatrixWorkspace provided as input");
337 } else if (inputP) {
338 inst = std::const_pointer_cast<Instrument>(inputP->getInstrument());
339 if (!inst)
340 throw std::runtime_error("Could not get a valid instrument from the "
341 "PeaksWorkspace provided as input");
342 } else {
343 throw std::runtime_error("Could not get a valid instrument from the "
344 "workspace which does not seem to be valid as "
345 "input (must be either MatrixWorkspace or "
346 "PeaksWorkspace");
347 }
348
349 return inst;
350}
351
352std::vector<std::string> LoadIsawDetCal::getFilenames() {
353 std::vector<std::string> filenamesFromPropertyUnraveld;
354 std::vector<std::vector<std::string>> filenamesFromProperty = this->getProperty("Filename");
355 for (const auto &outer : filenamesFromProperty) {
356 std::copy(outer.begin(), outer.end(), std::back_inserter(filenamesFromPropertyUnraveld));
357 }
358
359 // shouldn't be used except for legacy cases
360 const std::string filename2 = this->getProperty("Filename2");
361 if (!filename2.empty())
362 filenamesFromPropertyUnraveld.emplace_back(filename2);
363
364 return filenamesFromPropertyUnraveld;
365}
366
376void LoadIsawDetCal::doRotation(V3D rX, V3D rY, ComponentInfo &componentInfo, const size_t componentIndex,
377 bool doWishCorrection) {
378 // These are the ISAW axes
379 rX.normalize();
380 rY.normalize();
381
382 // These are the original axes
383 constexpr V3D oX(1., 0., 0.);
384 constexpr V3D oY(0., 1., 0.);
385
386 // Axis that rotates X
387 const V3D ax1 = oX.cross_prod(rX);
388 Quat Q1;
389 if (!ax1.nullVector(1e-12)) {
390 // Rotation angle from oX to rX
391 double angle1 = oX.angle(rX) * DegreesPerRadian;
392 if (doWishCorrection)
393 angle1 += 180.0;
394 // Create the first quaternion
395 Q1.setAngleAxis(angle1, ax1);
396 }
397
398 // Now we rotate the original Y using Q1
399 V3D roY = oY;
400 Q1.rotate(roY);
401 // Find the axis that rotates oYr onto rY
402 const V3D ax2 = roY.cross_prod(rY);
403 Quat Q2;
404 if (!ax2.nullVector(1e-12)) {
405 const double angle2 = roY.angle(rY) * DegreesPerRadian;
406 Q2.setAngleAxis(angle2, ax2);
407 }
408 // Final = those two rotations in succession; Q1 is done first.
409 const Quat Rot = Q2 * Q1;
410
411 componentInfo.setRotation(componentIndex, Rot);
412}
413
424 const std::vector<ComponentScaling> &rectangularDetectorScalings) {
425
426 for (const auto &scaling : rectangularDetectorScalings) {
427 auto alg1 = createChildAlgorithm("ResizeRectangularDetector");
428 alg1->setProperty<Workspace_sptr>("Workspace", ws);
429 alg1->setProperty("ComponentName", scaling.componentName);
430 alg1->setProperty("ScaleX", scaling.scaleX);
431 alg1->setProperty("ScaleY", scaling.scaleY);
432 alg1->executeAsChildAlg();
433 }
434}
435
436} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:547
double height
Definition GetAllEi.cpp:155
double position
Definition GetAllEi.cpp:154
std::map< DeltaEMode::Type, std::string > index
int count
counter
Definition Matrix.cpp:37
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
@ OptionalLoad
to specify a file to read but the file doesn't have to exist
bool hasProperty(const std::string &name) const
Does the property exist on the object.
void addProperty(Kernel::Property *prop, bool overwrite=false)
Add data to the object in the form of a property.
Definition LogManager.h:90
HeldType getPropertyValueAsType(const std::string &name) const
Get the value of a property as the given TYPE.
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:36
A property class for workspaces.
std::vector< std::string > getFilenames()
Geometry::Instrument_sptr getCheckInst(const API::Workspace_sptr &ws)
Gets the instrument of the workspace, checking that the workspace and the instrument are as expected.
void init() override
Initialisation method.
void center(const double x, const double y, const double z, const size_t componentIndex, Geometry::ComponentInfo &componentInfo)
Set the center of the supplied detector.
std::map< std::string, std::string > validateInputs() override
Perform validation of ALL the input properties of the algorithm.
void applyScalings(API::Workspace_sptr &ws, const std::vector< ComponentScaling > &rectangularDetectorScalings)
Apply the scalings from the calibration file.
void doRotation(Kernel::V3D rX, Kernel::V3D rY, Geometry::ComponentInfo &componentInfo, const size_t componentIndex, bool doWishCorrection=false)
Perform the rotation for the calibration.
void exec() override
Executes the algorithm.
ComponentInfo : Provides a component centric view on to the instrument.
void setRotation(size_t componentIndex, const Kernel::Quat &newRotation)
void setPosition(size_t componentIndex, const Kernel::V3D &newPosition)
Parameter map iterator typedef.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
Class for quaternions.
Definition Quat.h:38
void rotate(V3D &) const
Rotate a vector.
Definition Quat.cpp:397
void setAngleAxis(const double _deg, const V3D &_axis)
Constructor from an angle and axis.
Definition Quat.cpp:114
Class for 3D vectors.
Definition V3D.h:35
double normalize()
Make a normalized vector (return norm value)
Definition V3D.cpp:129
constexpr V3D cross_prod(const V3D &v) const noexcept
Cross product (this * argument)
Definition V3D.h:285
double angle(const V3D &) const
Angle between this and another vector.
Definition V3D.cpp:162
bool nullVector(const double tolerance=1e-3) const noexcept
Determine if the point is null.
Definition V3D.cpp:238
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::shared_ptr< const Workspace > Workspace_const_sptr
shared pointer to Mantid::API::Workspace (const version)
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::shared_ptr< const PeaksWorkspace > PeaksWorkspace_const_sptr
Typedef for a shared pointer to a const peaks workspace.
std::shared_ptr< PeaksWorkspace > PeaksWorkspace_sptr
Typedef for a shared pointer to a peaks workspace.
std::shared_ptr< Instrument > Instrument_sptr
Shared pointer to an instrument object.
int convert(const std::string &A, T &out)
Convert a string into a number.
Definition Strings.cpp:697
Generate a tableworkspace to store the calibration results.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ InOut
Both an input & output workspace.
Definition Property.h:56
@ Output
An output workspace.
Definition Property.h:55