Mantid
Loading...
Searching...
No Matches
LoadDNSSCD.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 +
13#include "MantidAPI/Run.h"
34#include <Poco/DateTime.h>
35#include <Poco/DateTimeFormat.h>
36#include <Poco/DateTimeFormatter.h>
37#include <Poco/DateTimeParser.h>
38#include <Poco/DirectoryIterator.h>
39#include <Poco/File.h>
40#include <Poco/Path.h>
41#include <boost/date_time/posix_time/posix_time.hpp>
42#include <boost/exception/diagnostic_information.hpp>
43#include <boost/exception_ptr.hpp>
44#include <boost/regex.hpp>
45
46#include <algorithm>
47#include <iomanip>
48#include <iterator>
49#include <map>
50
51//========================
52// helper functions
53namespace {
54void eraseSubStr(std::string &str, const std::string &toErase) {
55 // Search for the substring in string
56 size_t pos = str.find(toErase);
57 if (pos != std::string::npos) {
58 // If found then erase it from string
59 str.erase(pos, toErase.length());
60 }
61}
62
63std::string parseTime(std::string &str) {
64 // remove unnecessary symbols
65 eraseSubStr(str, "#");
66 eraseSubStr(str, "start");
67 eraseSubStr(str, "stopped");
68 eraseSubStr(str, "at");
69 auto it =
70 std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace<char>(ch, std::locale::classic()); });
71 str.erase(str.begin(), it);
72 using namespace boost::posix_time;
73 // try to parse as a posix time
74 try {
75 auto time = time_from_string(str);
76 return to_iso_extended_string(time);
77 } catch (std::exception &) {
78 int tzd;
79 Poco::DateTime dt;
80 bool ok = Poco::DateTimeParser::tryParse(str, dt, tzd);
81 if (ok) {
82 auto time = Poco::DateTimeFormatter::format(dt, "%Y-%m-%dT%H:%M:%S");
83 return time;
84 }
85 std::string result("");
86 return result;
87 }
88}
89
90} // anonymous namespace
91//============================
92
93using namespace Mantid::Kernel;
94using namespace Mantid::API;
95using namespace Mantid::DataObjects;
96using namespace Mantid::Geometry;
97
98namespace Mantid::MDAlgorithms {
99
101
102//----------------------------------------------------------------------------------------------
105LoadDNSSCD::LoadDNSSCD() : m_columnSep("\t, ;"), m_nDims(4), m_tof_max(20000.0) {}
106
114 // DNS data acquisition writes ascii files with .d_dat extension
115 int confidence(0);
116 if ((descriptor.extension() == ".d_dat") && descriptor.isAscii()) {
117 confidence = 80;
118 }
119 return confidence;
120}
121
122//----------------------------------------------------------------------------------------------
126 std::vector<std::string> exts(1, ".d_dat");
127 declareProperty(std::make_unique<MultipleFileProperty>("Filenames", exts),
128 "Select one or more DNS SCD .d_dat files to load."
129 "Files must be measured at the same conditions.");
130
131 declareProperty(std::make_unique<WorkspaceProperty<IMDEventWorkspace>>("OutputWorkspace", "", Direction::Output),
132 "An output MDEventWorkspace.");
133
135 std::make_unique<WorkspaceProperty<IMDEventWorkspace>>("NormalizationWorkspace", "", Direction::Output),
136 "An output normalization MDEventWorkspace.");
137
138 const std::vector<std::string> normOptions = {"monitor", "time"};
139 declareProperty("Normalization", "monitor", std::make_shared<StringListValidator>(normOptions),
140 "Algorithm will create a separate normalization workspace. "
141 "Choose whether it should contain monitor counts or time.");
142
143 const std::vector<std::string> wsOptions = {"raw", "HKL"};
144 declareProperty("LoadAs", "HKL", std::make_shared<StringListValidator>(wsOptions),
145 "Choose whether the algorithm should load raw data"
146 "or convert to H,K,L,dE space");
147
148 auto mustBePositive = std::make_shared<BoundedValidator<double>>();
149 mustBePositive->setLower(0.0);
150 auto reasonableAngle = std::make_shared<BoundedValidator<double>>();
151 reasonableAngle->setLower(5.0);
152 reasonableAngle->setUpper(175.0);
153 auto mustBe3D = std::make_shared<ArrayLengthValidator<double>>(3);
154 auto mustBe2D = std::make_shared<ArrayLengthValidator<double>>(2);
155 std::vector<double> u0(3, 0), v0(3, 0);
156 u0[0] = 1.;
157 u0[1] = 1.;
158 v0[2] = 1.;
159
160 declareProperty(std::make_unique<PropertyWithValue<double>>("a", 1.0, mustBePositive->clone(), Direction::Input),
161 "Lattice parameter a in Angstrom");
162 declareProperty(std::make_unique<PropertyWithValue<double>>("b", 1.0, mustBePositive->clone(), Direction::Input),
163 "Lattice parameter b in Angstrom");
164 declareProperty(std::make_unique<PropertyWithValue<double>>("c", 1.0, std::move(mustBePositive), Direction::Input),
165 "Lattice parameter c in Angstrom");
167 std::make_unique<PropertyWithValue<double>>("alpha", 90.0, reasonableAngle->clone(), Direction::Input),
168 "Angle between b and c in degrees");
169 declareProperty(std::make_unique<PropertyWithValue<double>>("beta", 90.0, reasonableAngle->clone(), Direction::Input),
170 "Angle between a and c in degrees");
172 std::make_unique<PropertyWithValue<double>>("gamma", 90.0, std::move(reasonableAngle), Direction::Input),
173 "Angle between a and b in degrees");
174
176 "OmegaOffset", 0.0, std::make_shared<BoundedValidator<double>>(), Direction::Input),
177 "Angle in degrees between (HKL1) and the beam axis"
178 "if the goniometer is at zero.");
179 declareProperty(std::make_unique<ArrayProperty<double>>("HKL1", std::move(u0), mustBe3D->clone()),
180 "Indices of the vector in reciprocal space in the horizontal plane at "
181 "angle Omegaoffset, "
182 "if the goniometer is at zero.");
183
184 declareProperty(std::make_unique<ArrayProperty<double>>("HKL2", std::move(v0), std::move(mustBe3D)),
185 "Indices of a second vector in reciprocal space in the horizontal plane "
186 "not parallel to HKL1");
187
188 std::vector<double> ttl(2, 0);
189 ttl[1] = 180.0;
190 declareProperty(std::make_unique<ArrayProperty<double>>("TwoThetaLimits", std::move(ttl), std::move(mustBe2D)),
191 "Range (min, max) of scattering angles (2theta, in degrees) to consider. "
192 "Everything out of this range will be cut.");
193
194 declareProperty(std::make_unique<WorkspaceProperty<API::ITableWorkspace>>("LoadHuberFrom", "", Direction::Input,
196 "A table workspace to load a list of raw sample rotation angles. "
197 "Huber angles given in the data files will be ignored.");
198
201 "A workspace name to save a list of raw sample rotation angles.");
202
203 auto mustBeIntPositive = std::make_shared<BoundedValidator<int>>();
204 mustBeIntPositive->setLower(0);
206 std::make_unique<PropertyWithValue<int>>("ElasticChannel", 0, std::move(mustBeIntPositive), Direction::Input),
207 "Elastic channel number. Only for TOF data.");
208
209 auto mustBeNegative = std::make_shared<BoundedValidator<double>>();
210 mustBeNegative->setUpper(0.0);
212 std::make_unique<PropertyWithValue<double>>("DeltaEmin", -10.0, std::move(mustBeNegative), Direction::Input),
213 "Minimal energy transfer to consider. Should be <=0. Only for TOF data.");
214}
215
216//----------------------------------------------------------------------------------------------
221 ColumnVector<double> huber = tws->getVector("Huber(degrees)");
222 // set huber[0] for each run in m_data
223 for (auto &ds : m_data) {
224 ds.huber = huber[0];
225 }
226 // dublicate runs for each huber in the table
227 std::vector<ExpData> old(m_data);
228 for (size_t i = 1; i < huber.size(); ++i) {
229 for (auto &ds : old) {
230 ds.huber = huber[i];
231 m_data.emplace_back(ds);
232 }
233 }
234}
235
236//----------------------------------------------------------------------------------------------
240 std::vector<double> huber;
241 huber.reserve(m_data.size());
242 std::transform(m_data.cbegin(), m_data.cend(), std::back_inserter(huber), [](const auto &ds) { return ds.huber; });
243 // remove dublicates
244 std::sort(huber.begin(), huber.end());
245 huber.erase(unique(huber.begin(), huber.end()), huber.end());
246
247 Mantid::API::ITableWorkspace_sptr huberWS = WorkspaceFactory::Instance().createTable("TableWorkspace");
248 huberWS->addColumn("double", "Huber(degrees)");
249 for (size_t i = 0; i < huber.size(); i++) {
250 huberWS->appendRow();
251 huberWS->cell<double>(i, 0) = huber[i];
252 }
253 return huberWS;
254}
255//----------------------------------------------------------------------------------------------
259 MultipleFileProperty *multiFileProp = dynamic_cast<MultipleFileProperty *>(getPointerToProperty("Filenames"));
260 if (!multiFileProp) {
261 throw std::logic_error("Filenames property must have MultipleFileProperty type.");
262 }
263 std::vector<std::string> filenames = VectorHelper::flattenVector(multiFileProp->operator()());
264 if (filenames.empty())
265 throw std::invalid_argument("Must specify at least one filename.");
266
267 // set type of normalization
268 std::string normtype = getProperty("Normalization");
269 if (normtype == "monitor") {
270 m_normtype = "Monitor";
271 m_normfactor = 1.0;
272 } else {
273 m_normtype = "Timer";
274 m_normfactor = 0.0; // error for time should be 0
275 }
276
277 g_log.notice() << "The normalization workspace will contain " << m_normtype << ".\n";
278
279 ExperimentInfo_sptr expinfo = std::make_shared<ExperimentInfo>();
280 API::Run &run = expinfo->mutableRun();
281 for (const auto &fname : filenames) {
282 std::map<std::string, std::string> str_metadata;
283 std::map<std::string, double> num_metadata;
284 try {
285 read_data(fname, str_metadata, num_metadata);
286 // if no stop_time, take file_save_time
287 std::string time(str_metadata["stop_time"]);
288 if (time.empty()) {
289 g_log.warning() << "stop_time is empty! File save time will be used instead." << std::endl;
290 time = str_metadata["file_save_time"];
291 }
292 updateProperties<std::string>(run, str_metadata, time);
293 updateProperties<double>(run, num_metadata, time);
294 } catch (...) {
295 g_log.warning() << "Failed to read file " << fname;
296 g_log.warning() << ". This file will be ignored. " << std::endl;
297 g_log.debug() << boost::current_exception_diagnostic_information() << std::endl;
298 }
299 }
300
301 if (m_data.empty())
302 throw std::runtime_error("No valid DNS files have been provided. Nothing to load.");
303
304 // merge data with different time channel number is not allowed
305 auto ch_n = m_data.front().nchannels;
306 bool same_channel_number =
307 std::all_of(m_data.cbegin(), m_data.cend(), [ch_n](const ExpData &d) { return (d.nchannels == ch_n); });
308 if (!same_channel_number)
309 throw std::runtime_error("Error: cannot merge data with different TOF channel numbers.");
310
311 std::string load_as = getProperty("LoadAs");
312 if (load_as == "raw")
313 m_nDims = 3;
314
316 m_OutWS->addExperimentInfo(expinfo);
317
318 // load huber angles from a table workspace if given
319 ITableWorkspace_sptr huberWS = getProperty("LoadHuberFrom");
320 if (huberWS) {
321 g_log.notice() << "Huber angles will be loaded from " << huberWS->getName() << std::endl;
322 loadHuber(huberWS);
323 }
324
325 // get wavelength
326 TimeSeriesProperty<double> *wlprop = dynamic_cast<TimeSeriesProperty<double> *>(expinfo->run().getProperty("Lambda"));
327 // assume, that lambda is in nm
328 double wavelength = wlprop->minValue() * 10.0; // needed to estimate extents => minValue
329 run.addProperty("wavelength", wavelength);
330 run.getProperty("wavelength")->setUnits("Angstrom");
331
332 if (load_as == "raw") {
333 fillOutputWorkspaceRaw(wavelength);
334 } else {
335 fillOutputWorkspace(wavelength);
336 }
337
338 std::string saveHuberTableWS = getProperty("SaveHuberTo");
339 if (!saveHuberTableWS.empty()) {
341 setProperty("SaveHuberTo", huber_table);
342 }
343 setProperty("OutputWorkspace", m_OutWS);
344}
345
346int LoadDNSSCD::splitIntoColumns(std::list<std::string> &columns, std::string &str) {
347 boost::split(columns, str, boost::is_any_of(m_columnSep), boost::token_compress_on);
348 return static_cast<int>(columns.size());
349}
350
351//----------------------------------------------------------------------------------------------
352
353template <class T>
354void LoadDNSSCD::updateProperties(API::Run &run, std::map<std::string, T> &metadata, std::string time) {
355 auto it = metadata.begin();
356 while (it != metadata.end()) {
357 TimeSeriesProperty<T> *timeSeries(nullptr);
358 std::string name(it->first);
359 std::string units;
360 // std::regex does not work for rhel7, thus boost
361 boost::regex reg("([-_a-zA-Z]+)\\[(.*)]");
362 boost::smatch match;
363 if (boost::regex_search(name, match, reg) && match.size() > 2) {
364 std::string new_name(match.str(1));
365 units.assign(match.str(2));
366 name = new_name;
367 }
368 if (run.hasProperty(name)) {
369 timeSeries = dynamic_cast<TimeSeriesProperty<T> *>(run.getLogData(name));
370 if (!timeSeries)
371 throw std::invalid_argument("Log '" + name + "' already exists but the values are a different type.");
372 } else {
373 timeSeries = new TimeSeriesProperty<T>(name);
374 if (!units.empty())
375 timeSeries->setUnits(units);
376 run.addProperty(timeSeries);
377 }
378 timeSeries->addValue(time, it->second);
379 ++it;
380 }
381}
382//----------------------------------------------------------------------------------------------
384void LoadDNSSCD::fillOutputWorkspace(double wavelength) {
385
386 // dimensions
387 std::vector<std::string> vec_ID(4);
388 vec_ID[0] = "H";
389 vec_ID[1] = "K";
390 vec_ID[2] = "L";
391 vec_ID[3] = "DeltaE";
392
393 std::vector<std::string> dimensionNames(4);
394 dimensionNames[0] = "H";
395 dimensionNames[1] = "K";
396 dimensionNames[2] = "L";
397 dimensionNames[3] = "DeltaE";
398
400
401 double a, b, c, alpha, beta, gamma;
402 a = getProperty("a");
403 b = getProperty("b");
404 c = getProperty("c");
405 alpha = getProperty("alpha");
406 beta = getProperty("beta");
407 gamma = getProperty("gamma");
408 std::vector<double> u = getProperty("HKL1");
409 std::vector<double> v = getProperty("HKL2");
410
411 // load empty DNS instrument to access L1 and L2
412 auto loadAlg = AlgorithmManager::Instance().create("LoadEmptyInstrument");
413 loadAlg->setChild(true);
414 loadAlg->setLogging(false);
415 loadAlg->initialize();
416 loadAlg->setProperty("InstrumentName", "DNS");
417 loadAlg->setProperty("OutputWorkspace", "__DNS_Inst");
418 loadAlg->execute();
419 MatrixWorkspace_sptr instWS = loadAlg->getProperty("OutputWorkspace");
420 const auto &instrument = instWS->getInstrument();
421 const auto &samplePosition = instrument->getSample()->getPos();
422 const auto &sourcePosition = instrument->getSource()->getPos();
423 const auto beamVector = samplePosition - sourcePosition;
424 const auto l1 = beamVector.norm();
425 // calculate tof1
426 auto velocity = PhysicalConstants::h / (PhysicalConstants::NeutronMass * wavelength * 1e-10); // m/s
427 auto tof1 = 1e+06 * l1 / velocity; // microseconds
428 g_log.debug() << "TOF1 = " << tof1 << std::endl;
429 // calculate incident energy
430 auto Ei = 0.5 * PhysicalConstants::NeutronMass * velocity * velocity / PhysicalConstants::meV;
431 g_log.debug() << "Ei = " << Ei << std::endl;
432
433 double dEmin = getProperty("DeltaEmin");
434 // estimate extents
435 double qmax = 4.0 * M_PI / wavelength;
436 std::vector<double> extentMins = {-qmax * a, -qmax * b, -qmax * c, dEmin};
437 std::vector<double> extentMaxs = {qmax * a, qmax * b, qmax * c, Ei};
438
439 // Get MDFrame of HKL type with RLU
440 auto unitFactory = makeMDUnitFactoryChain();
441 auto unit = unitFactory->create(Units::Symbol::RLU.ascii());
442 Mantid::Geometry::HKL frame(unit);
443
444 // add dimensions
445 for (size_t i = 0; i < m_nDims; ++i) {
446 std::string id = vec_ID[i];
447 std::string name = dimensionNames[i];
449 id, name, frame, static_cast<coord_t>(extentMins[i]), static_cast<coord_t>(extentMaxs[i]), 5)));
450 }
451
452 // Set coordinate system
453 m_OutWS->setCoordinateSystem(coordinateSystem);
454
455 // calculate RUB matrix
457 o = Mantid::Geometry::OrientedLattice(a, b, c, alpha, beta, gamma);
458 o.setUFromVectors(Mantid::Kernel::V3D(u[0], u[1], u[2]), Mantid::Kernel::V3D(v[0], v[1], v[2]));
459
460 double omega_offset = getProperty("OmegaOffset");
461 omega_offset *= -1.0 * deg2rad;
462 DblMatrix rotm(3, 3);
463 rotm[0][0] = std::cos(omega_offset);
464 rotm[0][1] = 0.0;
465 rotm[0][2] = std::sin(omega_offset);
466 rotm[1][0] = 0.0;
467 rotm[1][1] = 1.0;
468 rotm[1][2] = 0.0;
469 rotm[2][0] = -std::sin(omega_offset);
470 rotm[2][1] = 0.0;
471 rotm[2][2] = std::cos(omega_offset);
472
473 DblMatrix ub(o.getUB());
474 ub = rotm * ub;
475 o.setUB(ub);
476 DblMatrix ub_inv(ub);
477 // invert the UB matrix
478 ub_inv.Invert();
479
480 // Creates a new instance of the MDEventInserter to output workspace
481 MDEventWorkspace<MDEvent<4>, 4>::sptr mdws_mdevt_4 =
482 std::dynamic_pointer_cast<MDEventWorkspace<MDEvent<4>, 4>>(m_OutWS);
483 MDEventInserter<MDEventWorkspace<MDEvent<4>, 4>::sptr> inserter(mdws_mdevt_4);
484
485 // create a normalization workspace
486 IMDEventWorkspace_sptr normWS = m_OutWS->clone();
487
488 // Creates a new instance of the MDEventInserter to norm workspace
489 MDEventWorkspace<MDEvent<4>, 4>::sptr normws_mdevt_4 =
490 std::dynamic_pointer_cast<MDEventWorkspace<MDEvent<4>, 4>>(normWS);
491 MDEventInserter<MDEventWorkspace<MDEvent<4>, 4>::sptr> norm_inserter(normws_mdevt_4);
492
493 // scattering angle limits
494 std::vector<double> tth_limits = getProperty("TwoThetaLimits");
495 double theta_min = tth_limits[0] * deg2rad / 2.0;
496 double theta_max = tth_limits[1] * deg2rad / 2.0;
497
498 // get elastic channel from the user input
499 int echannel_user = getProperty("ElasticChannel");
500
501 // Go though each element of m_data to convert to MDEvent
502 for (ExpData ds : m_data) {
503 uint16_t expInfoIndex = 0;
504 signal_t norm_signal(ds.norm);
505 signal_t norm_error = std::sqrt(m_normfactor * norm_signal);
506 double ki = 2.0 * M_PI / ds.wavelength;
507 for (size_t i = 0; i < ds.detID.size(); i++) {
508 const auto &detector = instWS->getDetector(i);
509 const auto &detectorPosition = detector->getPos();
510 const auto detectorVector = detectorPosition - samplePosition;
511 const auto l2 = detectorVector.norm();
512 auto tof2_elastic = 1e+06 * l2 / velocity;
513 // geometric elastic channel
514 auto echannel_geom = static_cast<int>(std::ceil(tof2_elastic / ds.chwidth));
515 // rotate the signal array to get elastic peak at right position
516 int ch_diff = echannel_geom - echannel_user;
517 if ((echannel_user > 0) && (ch_diff < 0)) {
518 std::rotate(ds.signal[i].begin(), ds.signal[i].begin() - ch_diff, ds.signal[i].end());
519 } else if ((echannel_user > 0) && (ch_diff > 0)) {
520 std::rotate(ds.signal[i].rbegin(), ds.signal[i].rbegin() + ch_diff, ds.signal[i].rend());
521 }
522 detid_t detid(ds.detID[i]);
523 double theta = 0.5 * (ds.detID[i] * 5.0 - ds.deterota) * deg2rad;
524 auto nchannels = static_cast<int64_t>(ds.signal[i].size());
525 if ((theta > theta_min) && (theta < theta_max)) {
527 for (int64_t channel = 0; channel < nchannels; channel++) {
529 double signal = ds.signal[i][channel];
530 signal_t error = std::sqrt(signal);
531 double tof2 = static_cast<double>(channel) * ds.chwidth + 0.5 * ds.chwidth; // bin centers
532 double dE = 0.0;
533 if (nchannels > 1) {
534 double v2 = 1e+06 * l2 / tof2;
535 dE = Ei - 0.5 * PhysicalConstants::NeutronMass * v2 * v2 / PhysicalConstants::meV;
536 }
537 if (dE > dEmin) {
538 double kf = std::sqrt(ki * ki - 2.0e-20 * PhysicalConstants::NeutronMass * dE * PhysicalConstants::meV /
540 double tlab = std::atan2(ki - kf * cos(2.0 * theta), kf * sin(2.0 * theta));
541 double omega = (ds.huber - ds.deterota) * deg2rad - tlab;
542 V3D uphi(-cos(omega), 0, -sin(omega));
543 double qabs = 0.5 * std::sqrt(ki * ki + kf * kf - 2.0 * ki * kf * cos(2.0 * theta)) / M_PI;
544 V3D hphi = uphi * qabs; // qabs = ki * sin(theta), for elastic case;
545 V3D hkl = ub_inv * hphi;
546 std::vector<Mantid::coord_t> millerindex(4);
547 millerindex[0] = static_cast<float>(hkl.X());
548 millerindex[1] = static_cast<float>(hkl.Y());
549 millerindex[2] = static_cast<float>(hkl.Z());
550 millerindex[3] = static_cast<float>(dE);
551 PARALLEL_CRITICAL(addValues) {
552 inserter.insertMDEvent(static_cast<float>(signal), static_cast<float>(error * error),
553 static_cast<uint16_t>(expInfoIndex), 0, detid, millerindex.data());
554
555 norm_inserter.insertMDEvent(static_cast<float>(norm_signal), static_cast<float>(norm_error * norm_error),
556 static_cast<uint16_t>(expInfoIndex), 0, detid, millerindex.data());
557 }
558 }
560 }
562 }
563 }
564 }
565 setProperty("NormalizationWorkspace", normWS);
566}
567
568//----------------------------------------------------------------------------------------------
571void LoadDNSSCD::fillOutputWorkspaceRaw(double wavelength) {
572
573 // dimensions
574 std::vector<std::string> vec_ID(3);
575 vec_ID[0] = "Theta";
576 vec_ID[1] = "Omega";
577 vec_ID[2] = "TOF";
578
579 std::vector<std::string> dimensionNames(3);
580 dimensionNames[0] = "Scattering Angle";
581 dimensionNames[1] = "Omega";
582 dimensionNames[2] = "TOF";
583
585
586 // load empty DNS instrument to access L1 and L2
587 auto loadAlg = AlgorithmManager::Instance().create("LoadEmptyInstrument");
588 loadAlg->setChild(true);
589 loadAlg->setLogging(false);
590 loadAlg->initialize();
591 loadAlg->setProperty("InstrumentName", "DNS");
592 loadAlg->setProperty("OutputWorkspace", "__DNS_Inst");
593 loadAlg->execute();
594 MatrixWorkspace_sptr instWS = loadAlg->getProperty("OutputWorkspace");
595 const auto &instrument = instWS->getInstrument();
596 const auto &samplePosition = instrument->getSample()->getPos();
597 const auto &sourcePosition = instrument->getSource()->getPos();
598 const auto beamVector = samplePosition - sourcePosition;
599 const auto l1 = beamVector.norm();
600 // calculate tof1
601 auto velocity = PhysicalConstants::h / (PhysicalConstants::NeutronMass * wavelength * 1e-10); // m/s
602 auto tof1 = 1e+06 * l1 / velocity; // microseconds
603 g_log.debug() << "TOF1 = " << tof1 << std::endl;
604 // calculate incident energy
605 auto Ei = 0.5 * PhysicalConstants::NeutronMass * velocity * velocity / PhysicalConstants::meV;
606 g_log.debug() << "Ei = " << Ei << std::endl;
607
608 // estimate extents
609 // scattering angle limits
610 std::vector<double> tth_limits = getProperty("TwoThetaLimits");
611 double theta_min = tth_limits[0] / 2.0;
612 double theta_max = tth_limits[1] / 2.0;
613
614 std::vector<double> extentMins = {theta_min, 0.0, tof1};
615 std::vector<double> extentMaxs = {theta_max, 360.0, m_tof_max};
616
617 // Get MDFrame of HKL type with RLU
618 // auto unitFactory = makeMDUnitFactoryChain();
619 // auto unit = unitFactory->create(Units::Symbol::RLU.ascii());
620 // Mantid::Geometry::HKL frame(unit);
621 const Kernel::UnitLabel unitLabel("Degrees");
622 Mantid::Geometry::GeneralFrame frame("Scattering Angle", unitLabel);
623
624 // add dimensions
625 for (size_t i = 0; i < 3; ++i) {
626 std::string id = vec_ID[i];
627 std::string name = dimensionNames[i];
629 name, id, frame, static_cast<coord_t>(extentMins[i]), static_cast<coord_t>(extentMaxs[i]), 5)));
630 }
631
632 // Set coordinate system
633 m_OutWS->setCoordinateSystem(coordinateSystem);
634
635 // Creates a new instance of the MDEventInserter to output workspace
636 MDEventWorkspace<MDEvent<3>, 3>::sptr mdws_mdevt_3 =
637 std::dynamic_pointer_cast<MDEventWorkspace<MDEvent<3>, 3>>(m_OutWS);
638 MDEventInserter<MDEventWorkspace<MDEvent<3>, 3>::sptr> inserter(mdws_mdevt_3);
639
640 // create a normalization workspace
641 IMDEventWorkspace_sptr normWS = m_OutWS->clone();
642
643 // Creates a new instance of the MDEventInserter to norm workspace
644 MDEventWorkspace<MDEvent<3>, 3>::sptr normws_mdevt_3 =
645 std::dynamic_pointer_cast<MDEventWorkspace<MDEvent<3>, 3>>(normWS);
646 MDEventInserter<MDEventWorkspace<MDEvent<3>, 3>::sptr> norm_inserter(normws_mdevt_3);
647
648 // get elastic channel from the user input
649 int echannel_user = getProperty("ElasticChannel");
650
651 // Go though each element of m_data to convert to MDEvent
652 for (ExpData ds : m_data) {
653 uint16_t expInfoIndex = 0;
654 signal_t norm_signal(ds.norm);
655 signal_t norm_error = std::sqrt(m_normfactor * norm_signal);
656 for (size_t i = 0; i < ds.detID.size(); i++) {
657 const auto &detector = instWS->getDetector(i);
658 const auto &detectorPosition = detector->getPos();
659 const auto detectorVector = detectorPosition - samplePosition;
660 const auto l2 = detectorVector.norm();
661 auto tof2_elastic = 1e+06 * l2 / velocity;
662 // geometric elastic channel
663 auto echannel_geom = static_cast<int>(std::ceil(tof2_elastic / ds.chwidth));
664 // rotate the signal array to get elastic peak at right position
665 int ch_diff = echannel_geom - echannel_user;
666 if ((echannel_user > 0) && (ch_diff < 0)) {
667 std::rotate(ds.signal[i].begin(), ds.signal[i].begin() - ch_diff, ds.signal[i].end());
668 } else if ((echannel_user > 0) && (ch_diff > 0)) {
669 std::rotate(ds.signal[i].rbegin(), ds.signal[i].rbegin() + ch_diff, ds.signal[i].rend());
670 }
671
672 detid_t detid(ds.detID[i]);
673 double theta = 0.5 * (ds.detID[i] * 5.0 - ds.deterota);
674 auto nchannels = static_cast<int64_t>(ds.signal[i].size());
675 if ((theta > theta_min) && (theta < theta_max)) {
677 for (int64_t channel = 0; channel < nchannels; channel++) {
679 double signal = ds.signal[i][channel];
680 signal_t error = std::sqrt(signal);
681 double tof2(tof2_elastic);
682 if (nchannels > 1) {
683 tof2 = static_cast<double>(channel) * ds.chwidth + 0.5 * ds.chwidth; // bin centers
684 }
685 double omega = (ds.huber - ds.deterota);
686
687 std::vector<Mantid::coord_t> datapoint(3);
688 datapoint[0] = static_cast<float>(theta);
689 datapoint[1] = static_cast<float>(omega);
690 datapoint[2] = static_cast<float>(tof1 + tof2);
691 PARALLEL_CRITICAL(addValues) {
692 inserter.insertMDEvent(static_cast<float>(signal), static_cast<float>(error * error),
693 static_cast<uint16_t>(expInfoIndex), 0, detid, datapoint.data());
694
695 norm_inserter.insertMDEvent(static_cast<float>(norm_signal), static_cast<float>(norm_error * norm_error),
696 static_cast<uint16_t>(expInfoIndex), 0, detid, datapoint.data());
697 }
699 }
701 }
702 }
703 }
704 setProperty("NormalizationWorkspace", normWS);
705}
706
707void LoadDNSSCD::read_data(const std::string &fname, std::map<std::string, std::string> &str_metadata,
708 std::map<std::string, double> &num_metadata) {
709 std::ifstream file(fname);
710 std::string line;
711 std::string::size_type n;
712 std::string s;
713 boost::regex reg1("^#\\s+(\\w+):(.*)");
714 boost::regex reg2("^#\\s+((\\w+\\s)+)\\s+(-?\\d+(,\\d+)*(\\.\\d+(e\\d+)?)?)");
715 boost::smatch match;
716 getline(file, line);
717 n = line.find("DNS");
718 if (n == std::string::npos) {
719 throw std::invalid_argument("Not a DNS file");
720 }
721 // get file save time
722 Poco::File pfile(fname);
723 Poco::DateTime lastModified = pfile.getLastModified();
724 std::string wtime(Poco::DateTimeFormatter::format(lastModified, "%Y-%m-%dT%H:%M:%S"));
725 str_metadata.insert(std::make_pair("file_save_time", wtime));
726
727 // get file basename
728 Poco::Path p(fname);
729 str_metadata.insert(std::make_pair("run_number", p.getBaseName()));
730
731 // parse metadata
732 while (getline(file, line)) {
733 n = line.find("Lambda");
734 if (n != std::string::npos) {
735 boost::regex re("[\\s]+");
736 s = line.substr(5);
737 boost::sregex_token_iterator it(s.begin(), s.end(), re, -1);
738 boost::sregex_token_iterator reg_end;
739 getline(file, line);
740 std::string s2 = line.substr(2);
741 boost::sregex_token_iterator it2(s2.begin(), s2.end(), re, -1);
742 for (; (it != reg_end) && (it2 != reg_end); ++it) {
743 std::string token(it->str());
744 if (token.find_first_not_of(' ') == std::string::npos) {
745 ++it2;
746 continue;
747 }
748 if (token == "Mono") {
749 str_metadata.insert(std::make_pair(token, it2->str()));
750 } else {
751 num_metadata.insert(std::make_pair(token, std::stod(it2->str())));
752 }
753 ++it2;
754 }
755 }
756 // parse start and stop time
757 n = line.find("start");
758 if (n != std::string::npos) {
759 str_metadata.insert(std::make_pair("start_time", parseTime(line)));
760 getline(file, line);
761 str_metadata.insert(std::make_pair("stop_time", parseTime(line)));
762 getline(file, line);
763 }
764 if (boost::regex_search(line, match, reg1) && match.size() > 2) {
765 str_metadata.insert(std::make_pair(match.str(1), match.str(2)));
766 }
767 if (boost::regex_search(line, match, reg2) && match.size() > 2) {
768 s = match.str(1);
769 s.erase(std::find_if_not(s.rbegin(), s.rend(), ::isspace).base(), s.end());
770 num_metadata.insert(std::make_pair(s, std::stod(match.str(3))));
771 }
772 n = line.find("DATA");
773 if (n != std::string::npos) {
774 break;
775 }
776 }
777
778 std::map<std::string, double>::const_iterator m = num_metadata.lower_bound("TOF");
779 g_log.debug() << "TOF Channels number: " << m->second << std::endl;
780 std::map<std::string, double>::const_iterator w = num_metadata.lower_bound("Time");
781 g_log.debug() << "Channel width: " << w->second << std::endl;
782
783 ExpData ds;
784 ds.deterota = num_metadata["DeteRota"];
785 ds.huber = num_metadata["Huber"];
786 ds.wavelength = 10.0 * num_metadata["Lambda[nm]"];
787 ds.norm = num_metadata[m_normtype];
788 ds.chwidth = w->second;
789 ds.nchannels = static_cast<size_t>(std::ceil(m->second));
790
791 // read data array
792 getline(file, line);
793
794 std::list<std::string> columns;
795 while (getline(file, line)) {
796 boost::trim(line);
797 const int cols = splitIntoColumns(columns, line);
798 if (cols > 0) {
799 ds.detID.emplace_back(std::stoi(columns.front()));
800 columns.pop_front();
801 std::vector<double> signal;
802 std::transform(columns.begin(), columns.end(), std::back_inserter(signal),
803 [](const std::string &s) { return std::stod(s); });
804 ds.signal.emplace_back(signal);
805 }
806 }
807 // DNS PA detector bank has only 24 detectors
808 ds.detID.resize(24);
809 ds.signal.resize(24);
810 m_data.emplace_back(ds);
811}
812
813} // namespace Mantid::MDAlgorithms
double error
#define PARALLEL_START_INTERRUPT_REGION
Begins a block to skip processing is the algorithm has been interupted Note the end of the block if n...
#define PARALLEL_CRITICAL(name)
#define PARALLEL_END_INTERRUPT_REGION
Ends a block to skip processing is the algorithm has been interupted Note the start of the block if n...
#define PARALLEL_FOR_IF(condition)
Empty definitions - to enable set your complier to enable openMP.
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
#define DECLARE_FILELOADER_ALGORITHM(classname)
DECLARE_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM macro when wri...
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Kernel::Property * getPointerToProperty(const std::string &name) const override
Get a property by name.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Kernel::Logger & g_log
Definition Algorithm.h:422
ColumnVector gives access to the column elements without alowing its resizing.
size_t size()
Size of the vector.
bool hasProperty(const std::string &name) const
Does the property exist on the object.
Kernel::Property * getLogData(const std::string &name) const
Access a single log entry.
Definition LogManager.h:141
Kernel::Property * getProperty(const std::string &name) const
Returns the named property as a pointer.
void addProperty(Kernel::Property *prop, bool overwrite=false)
Add data to the object in the form of a property.
Definition LogManager.h:91
A property to allow a user to specify multiple files to load.
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:35
A property class for workspaces.
static API::IMDEventWorkspace_sptr CreateMDWorkspace(size_t nd, const std::string &eventType="MDLeanEvent", const Mantid::API::MDNormalization &preferredNormalization=Mantid::API::MDNormalization::VolumeNormalization, const Mantid::API::MDNormalization &preferredNormalizationHisto=Mantid::API::MDNormalization::VolumeNormalization)
Create a MDEventWorkspace of the given type.
MDEventInserter : Helper class that provides a generic interface for adding events to an MDWorkspace ...
Templated class for the multi-dimensional event workspace.
GeneralFrame : Any MDFrame that isn't related to momemtum transfer.
HKL : HKL MDFrame.
Definition HKL.h:20
Class to implement UB matrix.
void setUB(const Kernel::DblMatrix &newUB)
Sets the UB matrix and recalculates lattice parameters.
const Kernel::DblMatrix & setUFromVectors(const Kernel::V3D &u, const Kernel::V3D &v)
Set the U rotation matrix, to provide the transformation, which translate an arbitrary vector V expre...
const Kernel::DblMatrix & getUB() const
Get the UB matrix.
Support for a property that holds an array of values.
BoundedValidator is a validator that requires the values to be between upper or lower bounds,...
Defines a wrapper around an open file.
static bool isAscii(const std::string &filename, const size_t nbytes=256)
Returns true if the file is considered ascii.
const std::string & extension() const
Access the file extension.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp: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
T Invert()
LU inversion routine.
Definition Matrix.cpp:924
The concrete, templated class for properties.
virtual void setUnits(const std::string &unit)
Sets the units of the property, as a string.
Definition Property.cpp:198
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
A specialised Property class for holding a series of time-value pairs.
TYPE minValue() const
Returns the minimum value found in the series.
void addValue(const Types::Core::DateAndTime &time, const TYPE &value)
Add a value to the map using a DateAndTime object.
A base-class for the a class that is able to return unit labels in different representations.
Definition UnitLabel.h:20
static const UnitLabel RLU
Reciprocal lattice units.
Class for 3D vectors.
Definition V3D.h:34
constexpr double X() const noexcept
Get x.
Definition V3D.h:238
constexpr double Y() const noexcept
Get y.
Definition V3D.h:239
constexpr double Z() const noexcept
Get z.
Definition V3D.h:240
LoadDNSSCD : Load a list of DNS .d_dat files into a MDEventWorkspace.
Definition LoadDNSSCD.h:27
double m_normfactor
factor to multiply the error^2 for normalization
Definition LoadDNSSCD.h:68
void exec() override
Run the algorithm.
size_t m_nDims
number of workspace dimensions
Definition LoadDNSSCD.h:60
void loadHuber(const API::ITableWorkspace_sptr &tws)
Read Huber angles from a given table workspace.
std::string m_normtype
type of normalization;
Definition LoadDNSSCD.h:66
void fillOutputWorkspaceRaw(double wavelength)
Fill output workspace with raw data theta, omega, tof space.
std::string m_columnSep
The column separator.
Definition LoadDNSSCD.h:57
Mantid::API::IMDEventWorkspace_sptr m_OutWS
Output IMDEventWorkspace.
Definition LoadDNSSCD.h:85
API::ITableWorkspace_sptr saveHuber()
Save Huber angles to a given table workspace.
void read_data(const std::string &fname, std::map< std::string, std::string > &str_metadata, std::map< std::string, double > &num_metadata)
void updateProperties(API::Run &run, std::map< std::string, T > &metadata, std::string time)
int splitIntoColumns(std::list< std::string > &columns, std::string &str)
void fillOutputWorkspace(double wavelength)
Fill output workspace with data converted to H, K, L, dE space.
void init() override
Initialise the properties.
std::vector< ExpData > m_data
Definition LoadDNSSCD.h:82
int confidence(Kernel::FileDescriptor &descriptor) const override
Returns a confidence value that this algorithm can load a file.
const std::string name() const override
Algorithm's name for identification.
Definition LoadDNSSCD.h:32
double m_tof_max
maximal TOF (for extends)
Definition LoadDNSSCD.h:63
std::shared_ptr< IMDEventWorkspace > IMDEventWorkspace_sptr
Shared pointer to Mantid::API::IMDEventWorkspace.
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
std::shared_ptr< ExperimentInfo > ExperimentInfo_sptr
Shared pointer to ExperimentInfo.
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
constexpr double deg2rad
Defines units/enum for Crystal work.
Definition AngleUnits.h:20
std::shared_ptr< MDHistoDimension > MDHistoDimension_sptr
Shared pointer to a MDHistoDimension.
std::vector< T > flattenVector(const std::vector< std::vector< T > > &v)
A convenience function to "flatten" the given vector of vectors into a single vector.
SpecialCoordinateSystem
Special coordinate systems for Q3D.
MDUnitFactory_uptr MANTID_KERNEL_DLL makeMDUnitFactoryChain()
Convience method. Pre-constructed builder chain.
std::enable_if< std::is_pointer< Arg >::value, bool >::type threadSafe(Arg workspace)
Thread-safety check Checks the workspace to ensure it is suitable for multithreaded access.
static constexpr double NeutronMass
Mass of the neutron in kg.
static constexpr double h
Planck constant in J*s.
static constexpr double meV
1 meV in Joules.
static constexpr double h_bar
Planck constant in J*s, divided by 2 PI.
float coord_t
Typedef for the data type to use for coordinate axes in MD objects such as MDBox, MDEventWorkspace,...
Definition MDTypes.h:27
int32_t detid_t
Typedef for a detector ID.
double signal_t
Typedef for the signal recorded in a MDBox, etc.
Definition MDTypes.h:36
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54
structure for experimental data
Definition LoadDNSSCD.h:71
std::vector< std::vector< double > > signal
Definition LoadDNSSCD.h:78