Mantid
Loading...
Searching...
No Matches
SaveIsawPeaks.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"
11#include "MantidAPI/Sample.h"
22#include "MantidKernel/Utils.h"
23#include <boost/algorithm/string/trim.hpp>
24#include <filesystem>
25#include <fstream>
26
27using namespace Mantid::Geometry;
28using namespace Mantid::DataObjects;
29using namespace Mantid::Kernel;
30using namespace Mantid::API;
31
32namespace Mantid::Crystal {
33
34// Register the algorithm into the AlgorithmFactory
35DECLARE_ALGORITHM(SaveIsawPeaks)
36
37
39void SaveIsawPeaks::init() {
40 declareProperty(std::make_unique<WorkspaceProperty<PeaksWorkspace>>("InputWorkspace", "", Direction::Input,
41 std::make_shared<InstrumentValidator>()),
42 "An input PeaksWorkspace with an instrument.");
43
44 declareProperty("AppendFile", false,
45 "Append to file if true.\n"
46 "If false, new file (default).");
47
48 const std::vector<std::string> exts{".peaks", ".integrate"};
49 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Save, exts),
50 "Path to an ISAW-style peaks or integrate file to save.");
51
52 declareProperty(std::make_unique<WorkspaceProperty<Workspace2D>>("ProfileWorkspace", "", Direction::Input,
54 "An optional Workspace2D of profiles from integrating cylinder.");
55
56 declareProperty("RenumberPeaks", false,
57 "If true, sequential peak numbers\n"
58 "If false, keep original numbering (default).");
59}
60
64 // Section header
65 std::string header = "2 SEQN H K L COL ROW CHAN "
66 " L2 2_THETA AZ WL D IPK "
67 " INTI SIGI RFLG";
68
69 const std::string filename = getPropertyValue("Filename");
70 m_ws = getProperty("InputWorkspace");
71 const auto &peaks = m_ws->getPeaks();
72 inst = m_ws->getInstrument();
73 if (!inst)
74 throw std::runtime_error("No instrument in the Workspace. Cannot save DetCal file.");
75 const auto &detectorInfo = m_ws->detectorInfo();
76
77 // We must sort the peaks first by run, then bank #, and save the list of
78 // workspace indices of it
79 using bankMap_t = std::map<int, std::vector<size_t>>;
80 using runMap_t = std::map<int, bankMap_t>;
81 std::set<int, std::less<int>> uniqueBanks;
82 // We cannot assume the peaks have bank type detector modules, so we have a
83 // string to check this
84 std::string bankPart = "bank";
85 if (inst->getName() == "WISH")
86 bankPart = "WISHpanel";
87
88 // Get all children
89 const auto &componentInfo = m_ws->componentInfo();
90 // iterate over the top level components, which contain the banks
91 size_t const rootIndex = componentInfo.root();
92 auto const topChildren = componentInfo.children(rootIndex);
93 for (size_t const i : topChildren) {
94 size_t bankParent = componentInfo.findBankParent(i, bankPart);
95 auto const children = componentInfo.children(bankParent);
96 for (size_t const child : children) {
97 std::string bankName = componentInfo.name(child);
98 boost::trim(bankName);
99 boost::erase_all(bankName, bankPart);
100 int bank = 0;
101 Strings::convert(bankName, bank);
102 if (bank == 0)
103 continue;
104 if (bankMasked(child, detectorInfo))
105 continue;
106 // Track unique bank numbers
107 uniqueBanks.insert(bank);
108 }
109 }
110 runMap_t runMap;
111 for (size_t i = 0; i < peaks.size(); ++i) {
112 const Peak &p = peaks[i];
113 if (p.getIntMNP() != V3D(0, 0, 0))
115 int run = p.getRunNumber();
116 int bank = 0;
117 std::string bankName = p.getBankName();
118 if (bankName.size() <= 4) {
119 g_log.information() << "Could not interpret bank number of peak " << i << "(" << bankName << ")\n";
120 continue;
121 }
122 // Save the "bank" part once to check whether it really is a bank
123 if (bankPart == "?")
124 bankPart = bankName.substr(0, 4);
125 // Take out the "bank" part of the bank name and convert to an int
126 if (bankPart == "bank")
127 bankName = bankName.substr(4, bankName.size() - 4);
128 else if (bankPart == "WISHpanel")
129 bankName = bankName.substr(9, bankName.size() - 9);
130 Strings::convert(bankName, bank);
131
132 // Save in the map
133 runMap[run][bank].emplace_back(i);
134 }
136 header = "2 SEQN H K L M N P COL ROW CHAN "
137 " L2 2_THETA AZ WL D IPK "
138 " INTI SIGI RFLG";
139
140 if (!inst)
141 throw std::runtime_error("No instrument in PeaksWorkspace. Cannot save peaks file.");
142
143 if (bankPart != "bank" && bankPart != "WISHpanel" && bankPart != "?") {
144 std::ostringstream mess;
145 mess << "Detector module of type " << bankPart << " not supported in ISAWPeaks. Cannot save peaks file";
146 throw std::runtime_error(mess.str());
147 }
148
149 double l1;
150 V3D beamline;
151 double beamline_norm;
152 V3D samplePos;
153 inst->getInstrumentParameters(l1, beamline, beamline_norm, samplePos);
154
155 std::ofstream out;
156 bool append = getProperty("AppendFile");
157 const bool renumber = getProperty("RenumberPeaks");
158
159 // do not append if file does not exist
160 if (!std::filesystem::exists(filename))
161 append = false;
162
163 int appendPeakNumb = 0;
164 if (append) {
165 std::ifstream infile(filename.c_str());
166 std::string line;
167 while (!infile.eof()) // To get you all the lines.
168 {
169 getline(infile, line); // Saves the line in STRING.
170 if (infile.eof())
171 break;
172 std::stringstream ss(line);
173 double three;
174 ss >> three;
175 if (three == 3) {
176 int peakNumber;
177 ss >> peakNumber;
178 appendPeakNumb = std::max(peakNumber, appendPeakNumb);
179 }
180 }
181
182 infile.close();
183 out.open(filename.c_str(), std::ios::app);
184 appendPeakNumb = appendPeakNumb + 1;
185 } else {
186 out.open(filename.c_str());
187
188 const auto instrumentName = inst->getName();
189 std::string facilityName;
190 try {
191 facilityName = ConfigService::Instance().getInstrument(instrumentName).facility().name();
192 } catch (Exception::NotFoundError &) {
193 g_log.warning() << "Instrument " << instrumentName
194 << " not found at any defined facility. Setting facility "
195 "name to Unknown\n";
196 facilityName = "Unknown";
197 }
198 out << "Version: 2.0 Facility: " << facilityName;
199 out << " Instrument: " << instrumentName << " Date: ";
200
201 // TODO: The experiment date might be more useful than the instrument date.
202 // For now, this allows the proper instrument to be loaded back after
203 // saving.
204 Types::Core::DateAndTime expDate = inst->getValidFromDate() + 1.0;
205 out << expDate.toISO8601String();
207 out << " MOD";
208 out << '\n';
209
210 out << "6 L1 T0_SHIFT\n";
211 out << "7 " << std::setw(10);
212 out << std::setprecision(4) << std::fixed << (l1 * 100);
213 out << std::setw(12) << std::setprecision(3) << std::fixed;
214 // Time offset from property
215 const API::Run &run = m_ws->run();
216 double T0 = 0.0;
217 if (run.hasProperty("T0")) {
218 T0 = run.getPropertyValueAsType<double>("T0");
219 if (T0 != 0) {
220 g_log.notice() << "T0 = " << T0 << '\n';
221 }
222 }
223 out << T0 << '\n';
224
225 // Save .detcal info
226 out << "4 DETNUM NROWS NCOLS WIDTH HEIGHT DEPTH DETD CenterX "
227 " CenterY CenterZ BaseX BaseY BaseZ UpX UpY "
228 " UpZ\n";
229 // Here would save each detector...
230 for (const auto bank : uniqueBanks) {
231 // Build up the bank name
232 std::ostringstream mess;
233 if (bankPart == "bank")
234 mess << "bank" << bank;
235 else if (bankPart == "WISHpanel") {
236 mess << "WISHpanel" << std::setfill('0') << std::setw(2) << bank;
237 }
238
239 std::string bankName = mess.str();
240 // Retrieve it
241 std::shared_ptr<const IComponent> det = inst->getComponentByName(bankName);
242 if (inst->getName() == "CORELLI") // for Corelli with sixteenpack under bank
243 {
244 const size_t bankIndex = componentInfo.indexOfAny(bankName);
245 const auto children = componentInfo.children(bankIndex);
246 if (!children.empty()) {
247 det.reset(componentInfo.componentID(children[0]), NoDeleting());
248 }
249 }
250 if (det) {
251 // Center of the detector
252 V3D center = det->getPos();
253
254 // Distance to center of detector
255 double detd = (center - inst->getSample()->getPos()).norm();
256 int NCOLS, NROWS;
257 double xsize, ysize;
258 sizeBanks(bankName, NCOLS, NROWS, xsize, ysize);
259 // Base unit vector (along the horizontal, X axis)
260 int midX = NCOLS / 2;
261 int midY = NROWS / 2;
262 V3D base = findPixelPos(bankName, midX + 1, midY) - findPixelPos(bankName, midX, midY);
263 base.normalize();
264
265 // Up unit vector (along the vertical, Y axis)
266 V3D up = findPixelPos(bankName, midX, midY + 1) - findPixelPos(bankName, midX, midY);
267 up.normalize();
268
269 // Write the line
270 out << "5 " << std::setw(6) << std::right << bank << " " << std::setw(6) << std::right << NROWS << " "
271 << std::setw(6) << std::right << NCOLS << " " << std::setw(7) << std::right << std::fixed
272 << std::setprecision(4) << 100.0 * xsize << " " << std::setw(7) << std::right << std::fixed
273 << std::setprecision(4) << 100.0 * ysize << " "
274 << " 0.2000 " << std::setw(6) << std::right << std::fixed << std::setprecision(2) << 100.0 * detd << " "
275 << std::setw(9) << std::right << std::fixed << std::setprecision(4) << 100.0 * center.X() << " "
276 << std::setw(9) << std::right << std::fixed << std::setprecision(4) << 100.0 * center.Y() << " "
277 << std::setw(9) << std::right << std::fixed << std::setprecision(4) << 100.0 * center.Z() << " "
278 << std::setw(8) << std::right << std::fixed << std::setprecision(5) << base.X() << " " << std::setw(8)
279 << std::right << std::fixed << std::setprecision(5) << base.Y() << " " << std::setw(8) << std::right
280 << std::fixed << std::setprecision(5) << base.Z() << " " << std::setw(8) << std::right << std::fixed
281 << std::setprecision(5) << up.X() << " " << std::setw(8) << std::right << std::fixed << std::setprecision(5)
282 << up.Y() << " " << std::setw(8) << std::right << std::fixed << std::setprecision(5) << up.Z() << " \n";
283
284 } else
285 g_log.warning() << "Information about detector module " << bankName << " not found and recognised\n";
286 }
287 }
288 // HKL's are flipped by -1 because of the internal Q convention
289 // unless Crystallography convention
290 double qSign = -1.0;
291 if (m_ws->getConvention() == "Crystallography")
292 qSign = 1.0;
293
294 // Save all Peaks
295 // Go in order of run numbers
296 int sequenceNumber = appendPeakNumb;
297 for (const auto &runBankMap : runMap) {
298 // Start of a new run
299 const int run = runBankMap.first;
300 const auto &bankMap = runBankMap.second;
301
302 for (const auto &bankIDs : bankMap) {
303 // Start of a new bank.
304 const int bank = bankIDs.first;
305 const auto &ids = bankIDs.second;
306
307 if (!ids.empty()) {
308 // Write the bank header
309 out << "0 NRUN DETNUM CHI PHI OMEGA MONCNT\n";
310 out << "1 " << std::setw(5) << run << std::setw(7) << std::right << bank;
311
312 // Determine goniometer angles by calculating from the goniometer matrix
313 // of a peak in the list
314 Goniometer gon(peaks[ids[0]].getGoniometerMatrix());
315 std::vector<double> angles = gon.getEulerAngles("yzy");
316
317 double phi = angles[2];
318 double chi = angles[1];
319 double omega = angles[0];
320
321 out << std::setw(8) << std::fixed << std::setprecision(2) << chi << " ";
322 out << std::setw(8) << std::fixed << std::setprecision(2) << phi << " ";
323 out << std::setw(8) << std::fixed << std::setprecision(2) << omega << " ";
324
325 // Get the monitor count from the first peak (should all be the same for
326 // one run)
327 const size_t first_peak_index = ids[0];
328 const auto &first_peak = peaks[first_peak_index];
329 const double monct = first_peak.getMonitorCount();
330 out << std::setw(12) << static_cast<int>(monct) << '\n';
331 out << header << '\n';
332
333 // Go through each peak at this run / bank
334 for (auto wi : ids) {
335 const auto &peak = peaks[wi];
336
337 // Sequence (run) number
338 std::string firstNumber = "3";
340 firstNumber = "9";
341 }
342 if (renumber) {
343 out << firstNumber << std::setw(7) << sequenceNumber;
344 sequenceNumber++;
345 } else {
346 out << firstNumber << std::setw(7) << peak.getPeakNumber() + appendPeakNumb;
347 }
348
349 // HKL's are flipped by -1 because of the internal Q convention
350 // unless Crystallography convention
352 const V3D mod = peak.getIntMNP();
353 const auto intHKL = peak.getIntHKL();
354 out << std::setw(5) << Utils::round(qSign * intHKL.X()) << std::setw(5) << Utils::round(qSign * intHKL.Y())
355 << std::setw(5) << Utils::round(qSign * intHKL.Z());
356
357 out << std::setw(5) << Utils::round(qSign * mod[0]) << std::setw(5) << Utils::round(qSign * mod[1])
358 << std::setw(5) << Utils::round(qSign * mod[2]);
359 } else {
360 out << std::setw(5) << Utils::round(qSign * peak.getH()) << std::setw(5)
361 << Utils::round(qSign * peak.getK()) << std::setw(5) << Utils::round(qSign * peak.getL());
362 }
363
364 // Row/column
365 out << std::setw(8) << std::fixed << std::setprecision(2) << static_cast<double>(peak.getCol()) << " ";
366
367 out << std::setw(8) << std::fixed << std::setprecision(2) << static_cast<double>(peak.getRow()) << " ";
368
369 out << std::setw(8) << std::fixed << std::setprecision(0) << peak.getTOF() << " ";
370
371 out << std::setw(9) << std::fixed << std::setprecision(3) << (peak.getL2() * 100.0) << " ";
372
373 // This is the scattered beam direction
374 const V3D dir = peak.getDetPos() - inst->getSample()->getPos();
375 double scattering, azimuth;
376
377 // Two-theta = polar angle = scattering angle = between +Z vector and
378 // the scattered beam
379 scattering = dir.angle(V3D(0.0, 0.0, 1.0));
380
381 // "Azimuthal" angle: project the scattered beam direction onto the XY
382 // plane,
383 // and calculate the angle between that and the +X axis (right-handed)
384 azimuth = atan2(dir.Y(), dir.X());
385
386 out << std::setw(9) << std::fixed << std::setprecision(5) << scattering << " "; // two-theta scattering
387
388 out << std::setw(9) << std::fixed << std::setprecision(5) << azimuth << " ";
389
390 out << std::setw(10) << std::fixed << std::setprecision(6) << peak.getWavelength() << " ";
391
392 out << std::setw(9) << std::fixed << std::setprecision(4) << peak.getDSpacing() << " ";
393
394 out << std::setw(8) << std::fixed << std::setprecision(0) << int(peak.getBinCount()) << " ";
395
396 out << std::setw(10) << std::fixed << std::setprecision(2) << peak.getIntensity() << " ";
397
398 out << std::setw(7) << std::fixed << std::setprecision(2) << peak.getSigmaIntensity() << " ";
399
400 int thisReflag = 310;
401 out << std::setw(5) << thisReflag;
402
403 out << '\n';
404
405 Workspace2D_sptr wsProfile2D = getProperty("ProfileWorkspace");
406 if (wsProfile2D) {
407 out << "8";
408 const auto &yValues = wsProfile2D->y(wi);
409 for (size_t j = 0; j < yValues.size(); j++) {
410 out << std::setw(8) << static_cast<int>(yValues[j]);
411 if ((j + 1) % 10 == 0) {
412 out << '\n';
413 if (j + 1 != yValues.size())
414 out << "8";
415 }
416 }
417 }
418 }
419 }
420 }
421 }
422
423 out.flush();
424 out.close();
425}
426
427bool SaveIsawPeaks::bankMasked(size_t componentIndex, const Geometry::DetectorInfo &detectorInfo) {
428 const auto &componentInfo = this->m_ws->componentInfo();
429 auto children = componentInfo.children(componentIndex);
430
431 if (!children.empty() && componentInfo.name(children[0]) == "sixteenpack") {
432 children = componentInfo.children(children[0]);
433 }
434
435 for (const auto &colIndex : children) { // cppcheck-suppress useStlAlgorithm
436 auto grandchildren = componentInfo.children(colIndex);
437 for (const auto &rowIndex : grandchildren) { // cppcheck-suppress useStlAlgorithm
438 if (componentInfo.isDetector(rowIndex)) {
439 const auto detID = detectorInfo.detid(rowIndex);
440 if (detID < 0)
441 continue;
442 const auto detIndex = detectorInfo.indexOf(detID);
443 if (!detectorInfo.isMasked(detIndex)) {
444 return false;
445 }
446 }
447 }
448 }
449 return true;
450}
451
452V3D SaveIsawPeaks::findPixelPos(const std::string &bankName, int col, int row) {
453 auto parent = inst->getComponentByName(bankName);
454 if (parent->type() == "RectangularDetector") {
455 const auto RDet = std::dynamic_pointer_cast<const RectangularDetector>(parent);
456 const auto pixel = RDet->getAtXY(col, row);
457 return pixel->getPos();
458 } else {
459 const auto &componentInfo = this->m_ws->componentInfo();
460 const size_t parentIndex = componentInfo.indexOfAny(bankName);
461 auto children = componentInfo.children(parentIndex);
462
463 if (!children.empty() && componentInfo.name(children[0]) == "sixteenpack") {
464 children = componentInfo.children(children[0]);
465 }
466 int col0 = col - 1;
467 // WISH detectors are in bank in this order in instrument
468 if (inst->getName() == "WISH")
469 col0 = (col % 2 == 0 ? col / 2 + 75 : (col - 1) / 2);
470
471 auto grandchildren = componentInfo.children(children[col0]);
472 const auto *first = componentInfo.componentID(grandchildren[row - 1]);
473 return first->getPos();
474 }
475}
476void SaveIsawPeaks::sizeBanks(const std::string &bankName, int &NCOLS, int &NROWS, double &xsize, double &ysize) {
477 if (bankName == "None")
478 return;
479 const auto parent = inst->getComponentByName(bankName);
480 if (parent->type() == "RectangularDetector") {
481 const auto RDet = std::dynamic_pointer_cast<const RectangularDetector>(parent);
482
483 NCOLS = RDet->xpixels();
484 NROWS = RDet->ypixels();
485 xsize = RDet->xsize();
486 ysize = RDet->ysize();
487 } else {
488 const auto &componentInfo = this->m_ws->componentInfo();
489 const size_t parentIndex = componentInfo.indexOfAny(bankName);
490 auto children = componentInfo.children(parentIndex);
491
492 if (!children.empty() && componentInfo.name(children[0]) == "sixteenpack") {
493 children = componentInfo.children(children[0]);
494 }
495
496 auto grandchildren = componentInfo.children(children[0]);
497 NROWS = static_cast<int>(grandchildren.size());
498 NCOLS = static_cast<int>(children.size());
499 const auto *first = componentInfo.componentID(children[0]);
500 const auto *last = componentInfo.componentID(children[NCOLS - 1]);
501 xsize = first->getDistance(*last);
502 first = componentInfo.componentID(grandchildren[0]);
503 last = componentInfo.componentID(grandchildren[NROWS - 1]);
504 ysize = first->getDistance(*last);
505 }
506}
507void SaveIsawPeaks::writeOffsets(std::ofstream &out, double qSign, const std::vector<double> &offset) {
508 for (size_t i = 0; i < 3; i++) {
509 out << std::setw(12) << std::fixed << std::setprecision(6) << qSign * offset[i] << " ";
510 }
511}
512} // namespace Mantid::Crystal
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
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
@ Save
to specify a file to write to, the file may or may not exist
bool hasProperty(const std::string &name) const
Does the property exist on the object.
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.
Save a PeaksWorkspace to a ISAW-style ASCII .peaks file.
bool m_isModulatedStructure
Flag for writing modulated structures.
DataObjects::PeaksWorkspace_sptr m_ws
void writeOffsets(std::ofstream &out, double qSign, const std::vector< double > &offset)
void exec() override
Run the algorithm.
Kernel::V3D findPixelPos(const std::string &bankName, int col, int row)
find position for rectangular and non-rectangular
bool bankMasked(size_t componentIndex, const Geometry::DetectorInfo &detectorInfo)
Geometry::Instrument_const_sptr inst
void sizeBanks(const std::string &bankName, int &NCOLS, int &NROWS, double &xsize, double &ysize)
Mantid::Kernel::V3D getIntMNP() const override
Return the int MNP vector.
Definition BasePeak.cpp:115
int getRunNumber() const override
Return the run number this peak was measured at.
Definition BasePeak.cpp:77
Structure describing a single-crystal peak.
Definition Peak.h:34
const std::string & getBankName() const
Find the name of the bank that is the parent of the detector.
Definition Peak.cpp:347
Geometry::DetectorInfo is an intermediate step towards a DetectorInfo that is part of Instrument-2....
bool isMasked(const size_t index) const
Returns true if the detector is masked.
size_t indexOf(const detid_t id) const
Returns the index of the detector with the given detector ID.
detid_t detid(const size_t index) const
Class to represent a particular goniometer setting, which is described by the rotation matrix.
Definition Goniometer.h:55
std::vector< double > getEulerAngles(const std::string &convention="YZX")
Return Euler angles acording to a convention.
Exception for when an item is not found in a collection.
Definition Exception.h:145
void notice(const std::string &msg)
Logs at notice level.
Definition Logger.cpp:126
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
Class for 3D vectors.
Definition V3D.h:34
constexpr double X() const noexcept
Get x.
Definition V3D.h:238
double normalize()
Make a normalized vector (return norm value)
Definition V3D.cpp:129
constexpr double Y() const noexcept
Get y.
Definition V3D.h:239
double angle(const V3D &) const
Angle between this and another vector.
Definition V3D.cpp:162
constexpr double Z() const noexcept
Get z.
Definition V3D.h:240
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< Workspace2D > Workspace2D_sptr
shared pointer to Mantid::DataObjects::Workspace2D
int convert(const std::string &A, T &out)
Convert a string into a number.
Definition Strings.cpp:696
long round(double x)
Custom rounding method for a double->long because none is portable in C++ (!)
Definition Utils.h:37
@ Input
An input workspace.
Definition Property.h:53