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
23
25#include "MantidKernel/V3D.h"
26
27#include <algorithm>
28#include <boost/algorithm/string/trim.hpp>
29#include <fstream>
30#include <numeric>
31#include <sstream>
32#include <utility>
33
34namespace Mantid::DataHandling {
35
36// Register the class into the algorithm factory
37DECLARE_ALGORITHM(LoadIsawDetCal)
38
39using namespace Kernel;
40using namespace API;
41using namespace Geometry;
42using namespace DataObjects;
43
47 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("InputWorkspace", "", Direction::InOut,
48 std::make_shared<InstrumentValidator>()),
49 "The workspace containing the geometry to be calibrated.");
50
51 const auto exts = std::vector<std::string>({".DetCal", ".detcal", ".peaks", ".integrate"});
52 declareProperty(std::make_unique<API::MultipleFileProperty>("Filename", exts),
53 "The input filename of the ISAW DetCal file (Two files "
54 "allowed for SNAP) ");
55
56 declareProperty(std::make_unique<API::FileProperty>("Filename2", "", API::FileProperty::OptionalLoad, exts),
57 "The input filename of the second ISAW DetCal file (West "
58 "banks for SNAP) ");
59
60 declareProperty("TimeOffset", 0.0, "Time Offset", Direction::Output);
61}
62
63namespace {
64const constexpr double DegreesPerRadian = 180.0 / M_PI;
65
66std::string getBankName(const std::string &bankPart, const int idnum) {
67 if (bankPart == "WISHpanel" && idnum < 10) {
68 return bankPart + "0" + std::to_string(idnum);
69 } else {
70 return bankPart + std::to_string(idnum);
71 }
72}
73
74std::string getInstName(const API::Workspace_const_sptr &wksp) {
75 MatrixWorkspace_const_sptr matrixWksp = std::dynamic_pointer_cast<const MatrixWorkspace>(wksp);
76 if (matrixWksp) {
77 return matrixWksp->getInstrument()->getName();
78 }
79
80 PeaksWorkspace_const_sptr peaksWksp = std::dynamic_pointer_cast<const PeaksWorkspace>(wksp);
81 if (peaksWksp) {
82 return peaksWksp->getInstrument()->getName();
83 }
84
85 throw std::runtime_error("Failed to determine instrument name");
86}
87} // namespace
88
89std::map<std::string, std::string> LoadIsawDetCal::validateInputs() {
90 std::map<std::string, std::string> result;
91
92 // two detcal files is only valid for snap
93 std::vector<std::string> filenames = getFilenames();
94 if (filenames.empty()) {
95 result["Filename"] = "Must supply .detcal file";
96 } else if (filenames.size() == 2) {
97 Workspace_const_sptr wksp = getProperty("InputWorkspace");
98 const auto instname = getInstName(wksp);
99
100 if (instname != "SNAP") {
101 result["Filename"] = "Two files is only valid for SNAP";
102 }
103 } else if (filenames.size() > 2) {
104 result["Filename"] = "Supply at most two .detcal files";
105 }
106
107 return result;
108}
109
115 // Get the input workspace
116 Workspace_sptr ws = getProperty("InputWorkspace");
117 MatrixWorkspace_sptr inputW = std::dynamic_pointer_cast<MatrixWorkspace>(ws);
118 PeaksWorkspace_sptr inputP = std::dynamic_pointer_cast<PeaksWorkspace>(ws);
119
120 Instrument_sptr inst = getCheckInst(ws);
121
122 std::string instname = inst->getName();
123
124 const auto filenames = getFilenames();
125
126 // Output summary to log file
127 int count, id, nrows, ncols;
128 double width, height, depth, detd, x, y, z, base_x, base_y, base_z, up_x, up_y, up_z;
129 std::ifstream input(filenames[0].c_str(), std::ios_base::in);
130 std::string line;
131 std::string detname;
132 // Build a list of Rectangular Detectors
133 std::vector<std::shared_ptr<RectangularDetector>> detList;
134 for (int i = 0; i < inst->nelements(); i++) {
135 std::shared_ptr<RectangularDetector> det;
136 std::shared_ptr<ICompAssembly> assem;
137 std::shared_ptr<ICompAssembly> assem2;
138
139 det = std::dynamic_pointer_cast<RectangularDetector>((*inst)[i]);
140 if (det) {
141 detList.emplace_back(det);
142 } else {
143 // Also, look in the first sub-level for RectangularDetectors (e.g. PG3).
144 // We are not doing a full recursive search since that will be very long
145 // for lots of pixels.
146 assem = std::dynamic_pointer_cast<ICompAssembly>((*inst)[i]);
147 if (assem) {
148 for (int j = 0; j < assem->nelements(); j++) {
149 det = std::dynamic_pointer_cast<RectangularDetector>((*assem)[j]);
150 if (det) {
151 detList.emplace_back(det);
152
153 } else {
154 // Also, look in the second sub-level for RectangularDetectors (e.g.
155 // PG3).
156 // We are not doing a full recursive search since that will be very
157 // long for lots of pixels.
158 assem2 = std::dynamic_pointer_cast<ICompAssembly>((*assem)[j]);
159 if (assem2) {
160 for (int k = 0; k < assem2->nelements(); k++) {
161 det = std::dynamic_pointer_cast<RectangularDetector>((*assem2)[k]);
162 if (det) {
163 detList.emplace_back(det);
164 }
165 }
166 }
167 }
168 }
169 }
170 }
171 }
172 std::unordered_set<int> uniqueBanks; // for CORELLI and WISH
173 std::string bankPart = "bank";
174 if (instname == "WISH")
175 bankPart = "WISHpanel";
176 if (detList.empty()) {
177 // Get all components using ComponentInfo
178 auto expInfoWS = std::dynamic_pointer_cast<ExperimentInfo>(ws);
179 const auto &componentInfo = expInfoWS->componentInfo();
180
181 // iterate over the top level components, which contain the banks
182 size_t const rootIndex = componentInfo.root();
183 auto const topChildren = componentInfo.children(rootIndex);
184 for (size_t const i : topChildren) {
185 int bank = 0;
186 size_t bankParent = componentInfo.findBankParent(i, bankPart);
187 auto const children = componentInfo.children(bankParent);
188 for (size_t const child : children) {
189 std::string bankName = componentInfo.name(child);
190 boost::trim(bankName);
191 boost::erase_all(bankName, bankPart);
192 Strings::convert(bankName, bank);
193 if (bank != 0) {
194 uniqueBanks.insert(bank);
195 }
196 }
197 }
198 }
199
200 auto expInfoWS = std::dynamic_pointer_cast<ExperimentInfo>(ws);
201 auto &componentInfo = expInfoWS->mutableComponentInfo();
202 std::vector<ComponentScaling> rectangularDetectorScalings;
203
204 while (std::getline(input, line)) {
205 if (line[0] == '7') {
206 double mL1, mT0;
207 std::stringstream(line) >> count >> mL1 >> mT0;
208 setProperty("TimeOffset", mT0);
209 // Convert from cm to m
210 if (instname == "WISH")
211 center(0.0, 0.0, -mL1, "undulator", ws, componentInfo);
212 else
213 center(0.0, 0.0, -mL1, "moderator", ws, componentInfo);
214 // mT0 and time of flight are both in microsec
215 if (mT0 != 0.0) {
216 if (inputW) {
217 API::Run &run = inputW->mutableRun();
218 // Check to see if LoadEventNexus had T0 from TOPAZ Parameter file
219 auto alg1 = createChildAlgorithm("ChangeBinOffset");
220 alg1->setProperty<MatrixWorkspace_sptr>("InputWorkspace", inputW);
221 alg1->setProperty<MatrixWorkspace_sptr>("OutputWorkspace", inputW);
222 if (run.hasProperty("T0")) {
223 auto T0IDF = run.getPropertyValueAsType<double>("T0");
224 alg1->setProperty("Offset", mT0 - T0IDF);
225 } else {
226 alg1->setProperty("Offset", mT0);
227 }
228 alg1->executeAsChildAlg();
229 inputW = alg1->getProperty("OutputWorkspace");
230 // set T0 in the run parameters
231 run.addProperty<double>("T0", mT0, true);
232 } else if (inputP) {
233 // set T0 in the run parameters
234 API::Run &run = inputP->mutableRun();
235 run.addProperty<double>("T0", mT0, true);
236 }
237 }
238 }
239
240 if (line[0] != '5')
241 continue;
242
243 std::stringstream(line) >> count >> id >> nrows >> ncols >> width >> height >> depth >> detd >> x >> y >> z >>
244 base_x >> base_y >> base_z >> up_x >> up_y >> up_z;
245 if (id == 10 && filenames.size() == 2 && instname == "SNAP") {
246 input.close();
247 input.open(filenames[1].c_str());
248 while (std::getline(input, line)) {
249 if (line[0] != '5')
250 continue;
251
252 std::stringstream(line) >> count >> id >> nrows >> ncols >> width >> height >> depth >> detd >> x >> y >> z >>
253 base_x >> base_y >> base_z >> up_x >> up_y >> up_z;
254 if (id == 10)
255 break;
256 }
257 }
258 std::shared_ptr<RectangularDetector> det;
259 std::string bankName = getBankName(bankPart, id);
260 auto matchingDetector =
261 std::find_if(detList.begin(), detList.end(), [&bankName](const std::shared_ptr<RectangularDetector> &detector) {
262 return detector->getName() == bankName;
263 });
264 if (matchingDetector != detList.end()) {
265 det = *matchingDetector;
266 }
267
268 V3D rX(base_x, base_y, base_z);
269 V3D rY(up_x, up_y, up_z);
270
271 if (det) {
272 detname = det->getName();
273 center(x, y, z, detname, ws, componentInfo);
274
275 ComponentScaling detScaling;
276 detScaling.scaleX = CM_TO_M * width / det->xsize();
277 detScaling.scaleY = CM_TO_M * height / det->ysize();
278 detScaling.componentName = detname;
279 // Scaling will need both scale factors if LoadIsawPeaks or LoadIsawDetCal
280 // has already
281 // applied a calibration
282 if (inputW) {
283 Geometry::ParameterMap const &pmap = inputW->instrumentParameters();
284 auto oldscalex = pmap.getDouble(detname, std::string("scalex"));
285 auto oldscaley = pmap.getDouble(detname, std::string("scaley"));
286 if (!oldscalex.empty())
287 detScaling.scaleX *= oldscalex[0];
288 if (!oldscaley.empty())
289 detScaling.scaleY *= oldscaley[0];
290 }
291 if (inputP) {
292 Geometry::ParameterMap const &pmap = inputP->instrumentParameters();
293 auto oldscalex = pmap.getDouble(detname, std::string("scalex"));
294 auto oldscaley = pmap.getDouble(detname, std::string("scaley"));
295 if (!oldscalex.empty())
296 detScaling.scaleX *= oldscalex[0];
297 if (!oldscaley.empty())
298 detScaling.scaleY *= oldscaley[0];
299 }
300
301 rectangularDetectorScalings.emplace_back(detScaling);
302
303 doRotation(rX, rY, componentInfo, det);
304 }
305 auto bank = uniqueBanks.find(id);
306 if (bank == uniqueBanks.end())
307 continue;
308 int idnum = *bank;
309
310 bankName = getBankName(bankPart, idnum);
311 // Retrieve it
312 auto comp = inst->getComponentByName(bankName);
313 // for Corelli with sixteenpack under bank
314 if (instname == "CORELLI") {
315 const size_t bankIndex = componentInfo.indexOfAny(bankName);
316 const auto children = componentInfo.children(bankIndex);
317 if (!children.empty()) {
318 comp = IComponent_const_sptr(componentInfo.componentID(children[0]), NoDeleting());
319 }
320 }
321 if (comp) {
322 // Omitted scaling tubes
323 detname = comp->getFullName();
324 center(x, y, z, detname, ws, componentInfo);
325
326 bool doWishCorrection = (instname == "WISH"); // TODO: find out why this is needed for WISH
327 doRotation(rX, rY, componentInfo, comp, doWishCorrection);
328 }
329 }
330
331 // Do this last, to avoid the issue of invalidating DetectorInfo
332 applyScalings(ws, rectangularDetectorScalings);
333
334 setProperty("InputWorkspace", ws);
335}
336
347void LoadIsawDetCal::center(const double x, const double y, const double z, const std::string &detname,
348 const API::Workspace_sptr &ws, Geometry::ComponentInfo &componentInfo) {
349
350 Instrument_sptr inst = getCheckInst(ws);
351
352 IComponent_const_sptr comp = inst->getComponentByName(detname);
353 if (comp == nullptr) {
354 throw std::runtime_error("Component with name " + detname + " was not found.");
355 }
356
357 const V3D position(x * CM_TO_M, y * CM_TO_M, z * CM_TO_M);
358
359 const auto componentIndex = componentInfo.indexOf(comp->getComponentID());
360 componentInfo.setPosition(componentIndex, position);
361}
362
373 MatrixWorkspace_sptr inputW = std::dynamic_pointer_cast<MatrixWorkspace>(ws);
374 PeaksWorkspace_sptr inputP = std::dynamic_pointer_cast<PeaksWorkspace>(ws);
375
376 // Get some stuff from the input workspace
377 Instrument_sptr inst;
378 if (inputW) {
379 inst = std::const_pointer_cast<Instrument>(inputW->getInstrument());
380 if (!inst)
381 throw std::runtime_error("Could not get a valid instrument from the "
382 "MatrixWorkspace provided as input");
383 } else if (inputP) {
384 inst = std::const_pointer_cast<Instrument>(inputP->getInstrument());
385 if (!inst)
386 throw std::runtime_error("Could not get a valid instrument from the "
387 "PeaksWorkspace provided as input");
388 } else {
389 throw std::runtime_error("Could not get a valid instrument from the "
390 "workspace which does not seem to be valid as "
391 "input (must be either MatrixWorkspace or "
392 "PeaksWorkspace");
393 }
394
395 return inst;
396}
397
398std::vector<std::string> LoadIsawDetCal::getFilenames() {
399 std::vector<std::string> filenamesFromPropertyUnraveld;
400 std::vector<std::vector<std::string>> filenamesFromProperty = this->getProperty("Filename");
401 for (const auto &outer : filenamesFromProperty) {
402 std::copy(outer.begin(), outer.end(), std::back_inserter(filenamesFromPropertyUnraveld));
403 }
404
405 // shouldn't be used except for legacy cases
406 const std::string filename2 = this->getProperty("Filename2");
407 if (!filename2.empty())
408 filenamesFromPropertyUnraveld.emplace_back(filename2);
409
410 return filenamesFromPropertyUnraveld;
411}
412
423 const std::shared_ptr<const IComponent> &comp, bool doWishCorrection) {
424 // These are the ISAW axes
425 rX.normalize();
426 rY.normalize();
427
428 // These are the original axes
429 constexpr V3D oX(1., 0., 0.);
430 constexpr V3D oY(0., 1., 0.);
431
432 // Axis that rotates X
433 const V3D ax1 = oX.cross_prod(rX);
434 Quat Q1;
435 if (!ax1.nullVector(1e-12)) {
436 // Rotation angle from oX to rX
437 double angle1 = oX.angle(rX) * DegreesPerRadian;
438 if (doWishCorrection)
439 angle1 += 180.0;
440 // Create the first quaternion
441 Q1.setAngleAxis(angle1, ax1);
442 }
443
444 // Now we rotate the original Y using Q1
445 V3D roY = oY;
446 Q1.rotate(roY);
447 // Find the axis that rotates oYr onto rY
448 const V3D ax2 = roY.cross_prod(rY);
449 Quat Q2;
450 if (!ax2.nullVector(1e-12)) {
451 const double angle2 = roY.angle(rY) * DegreesPerRadian;
452 Q2.setAngleAxis(angle2, ax2);
453 }
454 // Final = those two rotations in succession; Q1 is done first.
455 const Quat Rot = Q2 * Q1;
456
457 // Then find the corresponding relative position
458 const auto componentIndex = componentInfo.indexOf(comp->getComponentID());
459
460 componentInfo.setRotation(componentIndex, Rot);
461}
462
473 const std::vector<ComponentScaling> &rectangularDetectorScalings) {
474
475 for (const auto &scaling : rectangularDetectorScalings) {
476 auto alg1 = createChildAlgorithm("ResizeRectangularDetector");
477 alg1->setProperty<Workspace_sptr>("Workspace", ws);
478 alg1->setProperty("ComponentName", scaling.componentName);
479 alg1->setProperty("ScaleX", scaling.scaleX);
480 alg1->setProperty("ScaleY", scaling.scaleY);
481 alg1->executeAsChildAlg();
482 }
483}
484
485} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
double height
Definition GetAllEi.cpp:155
double position
Definition GetAllEi.cpp:154
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:35
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 doRotation(Kernel::V3D rX, Kernel::V3D rY, Geometry::ComponentInfo &componentInfo, const std::shared_ptr< const Geometry::IComponent > &comp, bool doWishCorrection=false)
Perform the rotation for the calibration.
std::map< std::string, std::string > validateInputs() override
Perform validation of ALL the input properties of the algorithm.
void center(const double x, const double y, const double z, const std::string &detname, const API::Workspace_sptr &ws, Geometry::ComponentInfo &componentInfo)
Set the center of the supplied detector name.
void applyScalings(API::Workspace_sptr &ws, const std::vector< ComponentScaling > &rectangularDetectorScalings)
Apply the scalings from the calibration file.
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)
size_t indexOf(Geometry::IComponent const *id) const
void setPosition(size_t componentIndex, const Kernel::V3D &newPosition)
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
Class for quaternions.
Definition Quat.h:39
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:34
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:284
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
This functor is used as the deleter object of a shared_ptr to effectively erase ownership Raw pointer...
Definition IComponent.h:173
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< const IComponent > IComponent_const_sptr
Typdef of a shared pointer to a const IComponent.
Definition IComponent.h:167
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:696
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:55
@ Output
An output workspace.
Definition Property.h:54