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->getInstrument()->getName();
77 }
78
79 PeaksWorkspace_const_sptr peaksWksp = std::dynamic_pointer_cast<const PeaksWorkspace>(wksp);
80 if (peaksWksp) {
81 return peaksWksp->getInstrument()->getName();
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 if (instname == "WISH")
174 center(0.0, 0.0, -mL1, "undulator", ws, componentInfo);
175 else
176 center(0.0, 0.0, -mL1, "moderator", ws, componentInfo);
177 // mT0 and time of flight are both in microsec
178 if (mT0 != 0.0) {
179 if (inputW) {
180 API::Run &run = inputW->mutableRun();
181 // Check to see if LoadEventNexus had T0 from TOPAZ Parameter file
182 auto alg1 = createChildAlgorithm("ChangeBinOffset");
183 alg1->setProperty<MatrixWorkspace_sptr>("InputWorkspace", inputW);
184 alg1->setProperty<MatrixWorkspace_sptr>("OutputWorkspace", inputW);
185 if (run.hasProperty("T0")) {
186 auto T0IDF = run.getPropertyValueAsType<double>("T0");
187 alg1->setProperty("Offset", mT0 - T0IDF);
188 } else {
189 alg1->setProperty("Offset", mT0);
190 }
191 alg1->executeAsChildAlg();
192 inputW = alg1->getProperty("OutputWorkspace");
193 // set T0 in the run parameters
194 run.addProperty<double>("T0", mT0, true);
195 } else if (inputP) {
196 // set T0 in the run parameters
197 API::Run &run = inputP->mutableRun();
198 run.addProperty<double>("T0", mT0, true);
199 }
200 }
201 }
202
203 if (line[0] != '5')
204 continue;
205
206 std::stringstream(line) >> count >> id >> nrows >> ncols >> width >> height >> depth >> detd >> x >> y >> z >>
207 base_x >> base_y >> base_z >> up_x >> up_y >> up_z;
208 if (id == 10 && filenames.size() == 2 && instname == "SNAP") {
209 input.close();
210 input.open(filenames[1].c_str());
211 while (std::getline(input, line)) {
212 if (line[0] != '5')
213 continue;
214
215 std::stringstream(line) >> count >> id >> nrows >> ncols >> width >> height >> depth >> detd >> x >> y >> z >>
216 base_x >> base_y >> base_z >> up_x >> up_y >> up_z;
217 if (id == 10)
218 break;
219 }
220 }
221 std::string bankName = getBankName(bankPart, id);
222 auto matchingDetector = std::find_if(detList.begin(), detList.end(), [&bankName, &componentInfo](size_t index) {
223 return componentInfo.name(index) == bankName;
224 });
225
226 V3D rX(base_x, base_y, base_z);
227 V3D rY(up_x, up_y, up_z);
228
229 if (matchingDetector != detList.end()) {
230 const size_t bankIndex = *matchingDetector;
231 detname = componentInfo.name(bankIndex);
232 center(x, y, z, detname, ws, componentInfo);
233
234 ComponentScaling detScaling;
235 const auto grid = componentInfo.pixelGridComponent(bankIndex);
236 detScaling.scaleX = CM_TO_M * width / (grid.nX * grid.xStep);
237 detScaling.scaleY = CM_TO_M * height / (grid.nY * grid.yStep);
238 detScaling.componentName = detname;
239 // Scaling will need both scale factors if LoadIsawPeaks or LoadIsawDetCal
240 // has already
241 // applied a calibration
242 if (inputW) {
243 Geometry::ParameterMap const &pmap = inputW->instrumentParameters();
244 auto oldscalex = pmap.getDouble(detname, std::string("scalex"));
245 auto oldscaley = pmap.getDouble(detname, std::string("scaley"));
246 if (!oldscalex.empty())
247 detScaling.scaleX *= oldscalex[0];
248 if (!oldscaley.empty())
249 detScaling.scaleY *= oldscaley[0];
250 }
251 if (inputP) {
252 Geometry::ParameterMap const &pmap = inputP->instrumentParameters();
253 auto oldscalex = pmap.getDouble(detname, std::string("scalex"));
254 auto oldscaley = pmap.getDouble(detname, std::string("scaley"));
255 if (!oldscalex.empty())
256 detScaling.scaleX *= oldscalex[0];
257 if (!oldscaley.empty())
258 detScaling.scaleY *= oldscaley[0];
259 }
260
261 rectangularDetectorScalings.emplace_back(detScaling);
262
263 IComponent_const_sptr bankComponent(componentInfo.componentID(bankIndex), NoDeleting());
264 doRotation(rX, rY, componentInfo, bankComponent);
265 }
266 auto bank = uniqueBanks.find(id);
267 if (bank == uniqueBanks.end())
268 continue;
269 int idnum = *bank;
270
271 bankName = getBankName(bankPart, idnum);
272 // Retrieve it
273 auto comp = inst->getComponentByName(bankName);
274 // for Corelli with sixteenpack under bank
275 if (instname == "CORELLI") {
276 const size_t bankIndex = componentInfo.indexOfAny(bankName);
277 const auto children = componentInfo.children(bankIndex);
278 if (!children.empty()) {
279 comp = IComponent_const_sptr(componentInfo.componentID(children[0]), NoDeleting());
280 }
281 }
282 if (comp) {
283 // Omitted scaling tubes
284 detname = comp->getFullName();
285 center(x, y, z, detname, ws, componentInfo);
286
287 bool doWishCorrection = (instname == "WISH"); // TODO: find out why this is needed for WISH
288 doRotation(rX, rY, componentInfo, comp, doWishCorrection);
289 }
290 }
291
292 // Do this last, to avoid the issue of invalidating DetectorInfo
293 applyScalings(ws, rectangularDetectorScalings);
294
295 setProperty("InputWorkspace", ws);
296}
297
308void LoadIsawDetCal::center(const double x, const double y, const double z, const std::string &detname,
309 const API::Workspace_sptr &ws, Geometry::ComponentInfo &componentInfo) {
310
311 Instrument_sptr inst = getCheckInst(ws);
312
313 IComponent_const_sptr comp = inst->getComponentByName(detname);
314 if (comp == nullptr) {
315 throw std::runtime_error("Component with name " + detname + " was not found.");
316 }
317
318 const V3D position(x * CM_TO_M, y * CM_TO_M, z * CM_TO_M);
319
320 const auto componentIndex = componentInfo.indexOf(comp->getComponentID());
321 componentInfo.setPosition(componentIndex, position);
322}
323
334 MatrixWorkspace_sptr inputW = std::dynamic_pointer_cast<MatrixWorkspace>(ws);
335 PeaksWorkspace_sptr inputP = std::dynamic_pointer_cast<PeaksWorkspace>(ws);
336
337 // Get some stuff from the input workspace
338 Instrument_sptr inst;
339 if (inputW) {
340 inst = std::const_pointer_cast<Instrument>(inputW->getInstrument());
341 if (!inst)
342 throw std::runtime_error("Could not get a valid instrument from the "
343 "MatrixWorkspace provided as input");
344 } else if (inputP) {
345 inst = std::const_pointer_cast<Instrument>(inputP->getInstrument());
346 if (!inst)
347 throw std::runtime_error("Could not get a valid instrument from the "
348 "PeaksWorkspace provided as input");
349 } else {
350 throw std::runtime_error("Could not get a valid instrument from the "
351 "workspace which does not seem to be valid as "
352 "input (must be either MatrixWorkspace or "
353 "PeaksWorkspace");
354 }
355
356 return inst;
357}
358
359std::vector<std::string> LoadIsawDetCal::getFilenames() {
360 std::vector<std::string> filenamesFromPropertyUnraveld;
361 std::vector<std::vector<std::string>> filenamesFromProperty = this->getProperty("Filename");
362 for (const auto &outer : filenamesFromProperty) {
363 std::copy(outer.begin(), outer.end(), std::back_inserter(filenamesFromPropertyUnraveld));
364 }
365
366 // shouldn't be used except for legacy cases
367 const std::string filename2 = this->getProperty("Filename2");
368 if (!filename2.empty())
369 filenamesFromPropertyUnraveld.emplace_back(filename2);
370
371 return filenamesFromPropertyUnraveld;
372}
373
384 const std::shared_ptr<const IComponent> &comp, bool doWishCorrection) {
385 // These are the ISAW axes
386 rX.normalize();
387 rY.normalize();
388
389 // These are the original axes
390 constexpr V3D oX(1., 0., 0.);
391 constexpr V3D oY(0., 1., 0.);
392
393 // Axis that rotates X
394 const V3D ax1 = oX.cross_prod(rX);
395 Quat Q1;
396 if (!ax1.nullVector(1e-12)) {
397 // Rotation angle from oX to rX
398 double angle1 = oX.angle(rX) * DegreesPerRadian;
399 if (doWishCorrection)
400 angle1 += 180.0;
401 // Create the first quaternion
402 Q1.setAngleAxis(angle1, ax1);
403 }
404
405 // Now we rotate the original Y using Q1
406 V3D roY = oY;
407 Q1.rotate(roY);
408 // Find the axis that rotates oYr onto rY
409 const V3D ax2 = roY.cross_prod(rY);
410 Quat Q2;
411 if (!ax2.nullVector(1e-12)) {
412 const double angle2 = roY.angle(rY) * DegreesPerRadian;
413 Q2.setAngleAxis(angle2, ax2);
414 }
415 // Final = those two rotations in succession; Q1 is done first.
416 const Quat Rot = Q2 * Q1;
417
418 // Then find the corresponding relative position
419 const auto componentIndex = componentInfo.indexOf(comp->getComponentID());
420
421 componentInfo.setRotation(componentIndex, Rot);
422}
423
434 const std::vector<ComponentScaling> &rectangularDetectorScalings) {
435
436 for (const auto &scaling : rectangularDetectorScalings) {
437 auto alg1 = createChildAlgorithm("ResizeRectangularDetector");
438 alg1->setProperty<Workspace_sptr>("Workspace", ws);
439 alg1->setProperty("ComponentName", scaling.componentName);
440 alg1->setProperty("ScaleX", scaling.scaleX);
441 alg1->setProperty("ScaleY", scaling.scaleY);
442 alg1->executeAsChildAlg();
443 }
444}
445
446} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:542
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 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)
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: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:171
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:165
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