28#include "boost/math/constants/constants.hpp"
47const std::string WAVELENGTH_LOG(
"wavelength");
49constexpr double CONVERSION_PROGRESS_FRACTION = 0.9;
57std::string
join(
const std::vector<std::string> &messages) {
59 for (
const auto &message : messages)
60 joined += (joined.empty() ?
"" :
"; ") + message;
78 return "Convert from the detector vs scan index MDHistoWorkspace into a "
79 "MDEventWorkspace with units in Q_sample.";
87 std::stringstream inputWSmsg;
88 if (inputWS->getNumDims() != 3) {
89 inputWSmsg <<
"Incorrect number of dimensions";
90 }
else if (inputWS->getDimension(0)->getName() !=
"y" || inputWS->getDimension(1)->getName() !=
"x" ||
91 inputWS->getDimension(2)->getName() !=
"scanIndex") {
92 inputWSmsg <<
"Wrong dimensions";
93 }
else if (inputWS->getNumExperimentInfo() == 0) {
94 inputWSmsg <<
"Missing experiment info";
95 }
else if (inputWS->getExperimentInfo(0)->getInstrumentName() !=
"HB3A" &&
96 inputWS->getExperimentInfo(0)->getInstrumentName() !=
"WAND") {
97 inputWSmsg <<
"This only works for DEMAND (HB3A) or WAND (HB2C)";
98 }
else if (inputWS->getDimension(2)->getNBins() != inputWS->getExperimentInfo(0)->run().getNumGoniometers()) {
99 inputWSmsg <<
"goniometers not set correctly, did you run SetGoniometer "
100 "with Average=False";
102 std::string instrument = inputWS->getExperimentInfo(0)->getInstrumentName();
103 const auto run = inputWS->getExperimentInfo(0)->run();
104 size_t number_of_runs = inputWS->getDimension(2)->getNBins();
105 std::vector<std::string> logs;
106 if (instrument ==
"HB3A")
107 logs = {
"monitor",
"time"};
109 logs = {
"duration",
"monitor_count"};
110 for (
auto log : logs) {
111 if (run.hasProperty(log)) {
112 if (
static_cast<size_t>(run.getLogData(log)->size()) != number_of_runs)
113 inputWSmsg <<
"Log " << log <<
" has incorrect length, ";
115 inputWSmsg <<
"Missing required log " << log <<
", ";
119 return inputWSmsg.str();
123 std::map<std::string, std::string> result;
126 const auto group = std::dynamic_pointer_cast<WorkspaceGroup>(input);
128 const std::vector<double> wavelengths = this->
getProperty(
"Wavelength");
130 std::vector<std::string> inputMessages;
131 std::vector<std::string> wavelengthMessages;
132 if (
group && inputWorkspaces.empty())
133 inputMessages.emplace_back(
"The input group is empty");
134 if (!
group && wavelengths.size() > 1)
135 wavelengthMessages.emplace_back(
"Only one value can be given when InputWorkspace is a single workspace");
136 if (
group && wavelengths.size() > 1 && wavelengths.size() != inputWorkspaces.size())
137 wavelengthMessages.emplace_back(
"Give one value, or one value per group member: found " +
141 const bool checkWavelengths = wavelengthMessages.empty();
143 std::string instrument;
144 std::string instrumentOwner;
145 for (
size_t i = 0; i < inputWorkspaces.size(); ++i) {
146 const auto &inputWS = inputWorkspaces[i];
147 const std::string prefix =
group ?
"Member '" + memberName(*
group, i) +
"': " :
"";
149 inputMessages.emplace_back(
150 prefix + (
group ?
"must be an MDHistoWorkspace" :
"must be an MDHistoWorkspace or a WorkspaceGroup of them"));
154 if (!inputWSmsg.empty()) {
155 inputMessages.emplace_back(prefix + inputWSmsg);
158 const std::string memberInstrument = inputWS->getExperimentInfo(0)->getInstrumentName();
159 if (instrument.empty()) {
160 instrument = memberInstrument;
161 instrumentOwner =
group ? memberName(*
group, i) :
"";
162 }
else if (memberInstrument != instrument) {
163 inputMessages.emplace_back(prefix +
"instrument " + memberInstrument +
" differs from instrument " + instrument +
164 " of member '" + instrumentOwner +
"'");
166 if (checkWavelengths) {
169 }
catch (
const std::runtime_error &err) {
170 wavelengthMessages.emplace_back(prefix + err.what());
174 if (!inputMessages.empty())
175 result[
"InputWorkspace"] = join(inputMessages);
176 if (!wavelengthMessages.empty())
177 result[
"Wavelength"] = join(wavelengthMessages);
179 std::vector<double> minVals = this->
getProperty(
"MinValues");
180 std::vector<double> maxVals = this->
getProperty(
"MaxValues");
182 if (minVals.size() != 3 || maxVals.size() != 3) {
183 std::stringstream msg;
184 msg <<
"Must provide 3 values, 1 for every dimension";
185 result[
"MinValues"] = msg.str();
186 result[
"MaxValues"] = msg.str();
188 std::stringstream msg;
190 size_t rank = minVals.size();
191 for (
size_t i = 0; i < rank; ++i) {
192 if (minVals[i] >= maxVals[i]) {
193 if (msg.str().empty())
194 msg <<
"max not bigger than min ";
197 msg <<
"at index=" << (i + 1) <<
" (" << minVals[i] <<
">=" << maxVals[i] <<
")";
201 if (!msg.str().empty()) {
202 result[
"MinValues"] = msg.str();
203 result[
"MaxValues"] = msg.str();
216 "A detector-space MDHistoWorkspace, or a WorkspaceGroup of them from the same instrument.");
219 "Fallback incident wavelength, in Angstrom. As in HB3AAdjustSampleNorm, the 'wavelength' sample log "
220 "of each input workspace is used when present, and this value only when the log is missing. Give "
221 "one value for all inputs, or, for a WorkspaceGroup, one value per member in group order.");
223 "Correct the weights of events or signals and errors transformed into "
224 "reciprocal space by multiplying them "
225 "by the Lorentz multiplier:\n :math:`sin(2\\theta)cos(\\phi)/\\lambda^3`");
227 "It has to be 3 comma separated values, one for each dimension in "
229 "Values smaller then specified here will not be added to "
232 "A list of the same size and the same units as MinValues "
233 "list. Values higher or equal to the specified by "
234 "this list will be ignored");
238 "Geometrical correction for shift in vertical beam position due to wide beam.");
241 "An MDEventWorkspace in Q-sample. For a WorkspaceGroup input, a WorkspaceGroup whose members are named "
242 "<OutputWorkspace>_<input member name>, as in HB3AAdjustSampleNorm, or a single MDEventWorkspace if "
243 "MergeInputs is set.");
245 "For a WorkspaceGroup input, merge the converted members into one MDEventWorkspace with MergeMD, "
246 "using the box controller settings given here. Ignored for a single input workspace.");
254 const auto group = std::dynamic_pointer_cast<WorkspaceGroup>(input);
256 const std::vector<double> wavelengths = this->
getProperty(
"Wavelength");
264 const bool mergeInputs = this->
getProperty(
"MergeInputs");
265 std::vector<API::IMDEventWorkspace_sptr> outputWorkspaces;
266 Progress progress(
this, 0.0, mergeInputs ? CONVERSION_PROGRESS_FRACTION : 1.0, inputWorkspaces.size());
267 for (
size_t i = 0; i < inputWorkspaces.size(); ++i) {
269 outputWorkspaces.emplace_back(
convertWorkspace(inputWorkspaces[i], wavelength));
281 auto outputGroup = std::make_shared<WorkspaceGroup>();
282 for (
size_t i = 0; i < outputWorkspaces.size(); ++i) {
284 AnalysisDataService::Instance().addOrReplace(outputName +
"_" + memberName(*
group, i), outputWorkspaces[i]);
285 outputGroup->addWorkspace(outputWorkspaces[i]);
287 setProperty(
"OutputWorkspace", std::static_pointer_cast<API::Workspace>(outputGroup));
298 struct TemporaryWorkspaces {
299 std::vector<std::string> names;
300 TemporaryWorkspaces() =
default;
301 TemporaryWorkspaces(
const TemporaryWorkspaces &) =
delete;
302 TemporaryWorkspaces &operator=(
const TemporaryWorkspaces &) =
delete;
303 TemporaryWorkspaces(TemporaryWorkspaces &&) =
delete;
304 TemporaryWorkspaces &operator=(TemporaryWorkspaces &&) =
delete;
305 ~TemporaryWorkspaces() {
306 for (
const auto &
name : names)
307 AnalysisDataService::Instance().remove(
name);
310 for (
size_t i = 0; i < workspaces.size(); ++i) {
313 temporary.names.emplace_back(AnalysisDataService::Instance().uniqueHiddenName());
314 AnalysisDataService::Instance().addOrReplace(temporary.names.back(), workspaces[i]);
318 merge->setProperty(
"InputWorkspaces", temporary.names);
319 for (
const std::string
name : {
"SplitInto",
"SplitThreshold",
"MaxRecursionDepth"})
321 merge->executeAsChildAlg();
322 return merge->getProperty(
"OutputWorkspace");
330 std::vector<API::IMDHistoWorkspace_sptr> inputWorkspaces;
331 if (
const auto group = std::dynamic_pointer_cast<WorkspaceGroup>(input)) {
332 for (
size_t i = 0; i <
group->size(); ++i)
333 inputWorkspaces.emplace_back(std::dynamic_pointer_cast<API::IMDHistoWorkspace>(
group->getItem(i)));
335 inputWorkspaces.emplace_back(std::dynamic_pointer_cast<API::IMDHistoWorkspace>(input));
337 return inputWorkspaces;
346 const size_t index) {
347 if (wavelengths.empty())
349 if (wavelengths.size() == 1)
350 return wavelengths.front();
351 return wavelengths.at(
index);
364 const std::optional<double> &fallback,
const bool logChoice)
const {
366 if (run.hasProperty(WAVELENGTH_LOG)) {
367 double wavelength{0.0};
370 wavelength = run.getLogAsSingleValue(WAVELENGTH_LOG);
371 }
catch (
const std::logic_error &) {
373 throw std::runtime_error(
"The '" + WAVELENGTH_LOG +
"' sample log of the input workspace cannot be converted " +
374 "to a number: '" + run.getProperty(WAVELENGTH_LOG)->value() +
"'");
376 if (!(wavelength > 0.0) || !std::isfinite(wavelength))
377 throw std::runtime_error(
"The '" + WAVELENGTH_LOG +
"' sample log of the input workspace must be a positive " +
380 if (fallback && *fallback != wavelength)
381 g_log.
warning() <<
"The Wavelength property value " << *fallback <<
" Angstrom is ignored because the '"
382 << WAVELENGTH_LOG <<
"' sample log of the input workspace gives " << wavelength
384 g_log.
information() <<
"Using wavelength " << wavelength <<
" Angstrom from the '" << WAVELENGTH_LOG
390 throw std::runtime_error(
"No wavelength available: the input workspace has no '" + WAVELENGTH_LOG +
391 "' sample log and the Wavelength property is not set");
392 if (!(*fallback > 0.0) || !std::isfinite(*fallback))
393 throw std::runtime_error(
"The Wavelength property must be a positive number, found " +
std::to_string(*fallback));
395 g_log.
information() <<
"Using wavelength " << *fallback <<
" Angstrom from the Wavelength property\n";
405 const double wavelength) {
408 auto &expInfo = *(inputWS->getExperimentInfo(
static_cast<uint16_t
>(0)));
409 std::string instrument = expInfo.getInstrumentName();
411 std::vector<double> twotheta, azimuthal;
412 std::vector<int> detectorID;
413 if (instrument ==
"HB3A" && !expInfo.run().hasProperty(
"azimuthal")) {
414 const auto &di = expInfo.detectorInfo();
415 for (
size_t x = 0;
x < 512;
x++) {
416 for (
size_t y = 0;
y < 512 * 3;
y++) {
417 size_t n =
x +
y * 512;
418 if (!di.isMonitor(
n)) {
419 twotheta.push_back(di.twoTheta(
n));
420 azimuthal.push_back(di.azimuthal(
n));
421 detectorID.push_back(
static_cast<int>(1 +
n));
428 if (expInfo.run().hasProperty(
"detectorID")) {
432 detectorID.assign(azimuthal.size(), 0);
438 std::vector<double> minVals = this->
getProperty(
"MinValues");
439 std::vector<double> maxVals = this->
getProperty(
"MaxValues");
440 outputWS->addDimension(std::make_shared<Geometry::MDHistoDimension>(
441 "Q_sample_x",
"Q_sample_x", frame,
static_cast<coord_t>(minVals[0]),
static_cast<coord_t>(maxVals[0]), 1));
443 outputWS->addDimension(std::make_shared<Geometry::MDHistoDimension>(
444 "Q_sample_y",
"Q_sample_y", frame,
static_cast<coord_t>(minVals[1]),
static_cast<coord_t>(maxVals[1]), 1));
446 outputWS->addDimension(std::make_shared<Geometry::MDHistoDimension>(
447 "Q_sample_z",
"Q_sample_z", frame,
static_cast<coord_t>(minVals[2]),
static_cast<coord_t>(maxVals[2]), 1));
449 outputWS->initialize();
453 outputWS->splitBox();
455 auto mdws_mdevt_3 = std::dynamic_pointer_cast<MDEventWorkspace<MDEvent<3>, 3>>(outputWS);
458 double cop = this->
getProperty(
"ObliquityParallaxCoefficient");
459 float coeff =
static_cast<float>(cop);
461 float k = boost::math::float_constants::two_pi /
static_cast<float>(wavelength);
462 float inv_wl_cube =
static_cast<float>(1 / (wavelength * wavelength * wavelength));
464 std::string convention = Kernel::ConfigService::Instance().getString(
"Q.convention");
465 if (convention ==
"Crystallography") {
468 std::vector<Eigen::Vector3f> q_lab_pre;
469 std::vector<float> lorentz_pre;
470 q_lab_pre.reserve(azimuthal.size());
471 lorentz_pre.reserve(azimuthal.size());
472 for (
size_t m = 0;
m < azimuthal.size(); ++
m) {
473 auto twotheta_f =
static_cast<float>(twotheta[
m]);
474 auto azimuthal_f =
static_cast<float>(azimuthal[
m]);
475 q_lab_pre.push_back({-std::sin(twotheta_f) * std::cos(azimuthal_f) * k,
476 -std::sin(twotheta_f) * std::sin(azimuthal_f) * k * coeff, (1.f - std::cos(twotheta_f)) * k});
477 lorentz_pre.push_back(std::abs(std::sin(twotheta_f) * std::cos(azimuthal_f)) * inv_wl_cube);
480 const auto run = inputWS->getExperimentInfo(0)->run();
481 for (
size_t n = 0;
n < inputWS->getDimension(2)->getNBins();
n++) {
482 auto gon = run.getGoniometerMatrix(
n);
483 Eigen::Matrix3f goniometer;
484 for (
int i = 0; i < 3; i++)
485 for (
int j = 0; j < 3; j++)
486 goniometer(i, j) =
static_cast<float>(gon[i][j]);
487 goniometer = goniometer.inverse().eval();
488 auto goniometerIndex =
static_cast<uint16_t
>(
n);
489 for (
size_t m = 0;
m < azimuthal.size();
m++) {
490 size_t idx =
n * azimuthal.size() +
m;
492 if (signal > 0.f && std::isfinite(signal)) {
493 Eigen::Vector3f q_sample = goniometer * q_lab_pre[
m];
495 factor = lorentz_pre[
m];
497 inserter.insertMDEvent(signal * factor, signal * factor * factor, 0, goniometerIndex, detectorID[
m],
505 outputWS->splitAllIfNeeded(ts);
508 outputWS->refreshCache();
509 outputWS->copyExperimentInfos(*inputWS);
510 auto &outRun = outputWS->getExperimentInfo(0)->mutableRun();
511 if (outRun.hasProperty(WAVELENGTH_LOG)) {
512 outRun.removeLogData(WAVELENGTH_LOG);
515 outRun.getProperty(WAVELENGTH_LOG)->setUnits(
"Angstrom");
517 auto user_convention = Kernel::ConfigService::Instance().getString(
"Q.convention");
518 auto ws_convention = outputWS->getConvention();
519 if (user_convention != ws_convention) {
521 convention_alg->setProperty(
"InputWorkspace", outputWS);
522 convention_alg->executeAsChildAlg();
#define DECLARE_ALGORITHM(classname)
static std::unique_ptr< QThreadPool > tp
std::map< DeltaEMode::Type, std::string > index
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
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.
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.
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
bool getAlwaysStoreInADS() const override
Returns true if we always store in the AnalysisDataService.
void setBoxController(const Mantid::API::BoxController_sptr &bc, const Mantid::Geometry::Instrument_const_sptr &instrument)
Set the settings in the given box controller.
void initBoxControllerProps(const std::string &SplitInto="5", int SplitThreshold=1000, int MaxRecursionDepth=5)
Initialise the properties.
Abstract interface to MDHistoWorkspace, for use in exposing to Python.
ExperimentInfo_sptr getExperimentInfo(const uint16_t expInfoIndex)
Get the ExperimentInfo for the given Experiment-Info Index.
Helper class for reporting progress from algorithms.
Class to hold a set of workspaces.
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 ...
QSample : Q in the sample frame.
Kernel/ArrayBoundedValidator.h.
Support for a property that holds an array of values.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void warning(const std::string &msg)
Logs at warning level.
void information(const std::string &msg)
Logs at information level.
The concrete, templated class for properties.
A Thread Pool implementation that keeps a certain number of threads running (normally,...
A First-In-First-Out Thread Scheduler.
ConvertHFIRSCDtoMDE : TODO: DESCRIPTION.
API::IMDEventWorkspace_sptr mergeWorkspaces(const std::vector< API::IMDEventWorkspace_sptr > &workspaces)
Merge converted workspaces with MergeMD, with the box controller settings of this algorithm.
int version() const override
Algorithm's version for identification.
const std::string name() const override
Algorithms name for identification.
double resolveWavelength(const API::IMDHistoWorkspace &inputWS, const std::optional< double > &fallback, bool logChoice) const
Find the wavelength for a workspace, following HB3AAdjustSampleNorm: the 'wavelength' sample log when...
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
static std::vector< API::IMDHistoWorkspace_sptr > inputWorkspaceList(const API::Workspace_sptr &input)
The input workspaces to convert: the members of a group in order, or the single input workspace.
void exec() override
Execute the algorithm.
API::IMDEventWorkspace_sptr convertWorkspace(const API::IMDHistoWorkspace_sptr &inputWS, double wavelength)
Convert one detector-space workspace into a Q-sample MDEventWorkspace.
const std::string category() const override
Algorithm's category for identification.
void init() override
Initialize the algorithm's properties.
std::map< std::string, std::string > validateInputs() override
Perform validation of ALL the input properties of the algorithm.
std::string validateInputWorkspace(const API::IMDHistoWorkspace_sptr &inputWS) const
Check that a workspace can be converted.
static std::optional< double > fallbackWavelength(const std::vector< double > &wavelengths, size_t index)
The fallback wavelength given in the Wavelength property for an input workspace, if any.
std::shared_ptr< IMDEventWorkspace > IMDEventWorkspace_sptr
Shared pointer to Mantid::API::IMDEventWorkspace.
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::shared_ptr< IMDHistoWorkspace > IMDHistoWorkspace_sptr
shared pointer to Mantid::API::IMDHistoWorkspace
std::shared_ptr< BoxController > BoxController_sptr
Shared ptr to BoxController.
std::string join(ITERATOR_TYPE begin, ITERATOR_TYPE end, const std::string &separator, typename std::enable_if<!(std::is_same< typename std::iterator_traits< ITERATOR_TYPE >::iterator_category, std::random_access_iterator_tag >::value)>::type *=nullptr)
Join a set or vector of (something that turns into a string) together into one string,...
float coord_t
Typedef for the data type to use for coordinate axes in MD objects such as MDBox, MDEventWorkspace,...
std::string to_string(const wide_integer< Bits, Signed > &n)
Describes the direction (within an algorithm) of a Property.
@ Input
An input workspace.
@ Output
An output workspace.