Mantid
Loading...
Searching...
No Matches
Bin2DPowderDiffraction.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 +
24#include <algorithm>
25#include <fstream>
26#include <stdexcept>
27
28namespace Mantid::Algorithms {
29
30using namespace Kernel;
31using namespace API;
32using namespace Geometry;
33using namespace DataObjects;
34using namespace Mantid::HistogramData;
35
36// Register the algorithm into the AlgorithmFactory
37DECLARE_ALGORITHM(Bin2DPowderDiffraction)
38
39//----------------------------------------------------------------------------------------------
40
41
42const std::string Bin2DPowderDiffraction::name() const { return "Bin2DPowderDiffraction"; }
43
45int Bin2DPowderDiffraction::version() const { return 1; }
46
48const std::string Bin2DPowderDiffraction::category() const { return "Diffraction\\Focussing"; }
49
51const std::string Bin2DPowderDiffraction::summary() const {
52 return "Bins TOF powder diffraction event data in 2D space.";
53}
54
55//----------------------------------------------------------------------------------------------
59 auto wsValidator = std::make_shared<CompositeValidator>();
60 wsValidator->add<WorkspaceUnitValidator>("Wavelength");
61 wsValidator->add<SpectraAxisValidator>();
62 wsValidator->add<InstrumentValidator>();
63 wsValidator->add<HistogramValidator>();
64
66 std::make_unique<WorkspaceProperty<EventWorkspace>>("InputWorkspace", "", Direction::Input, wsValidator),
67 "An input EventWorkspace must be a Histogram workspace, not Point data. "
68 "X-axis units must be wavelength.");
69
70 declareProperty(std::make_unique<WorkspaceProperty<API::Workspace>>("OutputWorkspace", "", Direction::Output),
71 "An output workspace.");
72
73 const std::string docString = "A comma separated list of first bin boundary, width, last bin boundary. "
74 "Optionally "
75 "this can be followed by a comma and more widths and last boundary "
76 "pairs. "
77 "Negative width values indicate logarithmic binning.";
78 auto rebinValidator = std::make_shared<RebinParamsValidator>(true);
79 declareProperty(std::make_unique<ArrayProperty<double>>("dSpaceBinning", rebinValidator), docString);
80 declareProperty(std::make_unique<ArrayProperty<double>>("dPerpendicularBinning", rebinValidator), docString);
81
82 const std::vector<std::string> exts{".txt", ".dat"};
83 declareProperty(std::make_unique<FileProperty>("BinEdgesFile", "", FileProperty::OptionalLoad, exts),
84 "Optional: The ascii file containing the list of bin edges. "
85 "Either this or Axis1- and dPerpendicularBinning need to be specified.");
86
87 declareProperty(std::make_unique<PropertyWithValue<bool>>("NormalizeByBinArea", true),
88 "Normalize the binned workspace by the bin area.");
89}
90
91//----------------------------------------------------------------------------------------------
95 m_inputWS = this->getProperty("InputWorkspace");
96 m_numberOfSpectra = static_cast<int>(m_inputWS->getNumberHistograms());
97 g_log.debug() << "Number of spectra in input workspace: " << m_numberOfSpectra << "\n";
98
100
101 const bool normalizeByBinArea = this->getProperty("NormalizeByBinArea");
102 if (normalizeByBinArea) {
103 const auto startTime = std::chrono::high_resolution_clock::now();
104 normalizeToBinArea(outputWS);
105 addTimer("normalizeByBinArea", startTime, std::chrono::high_resolution_clock::now());
106 }
107
108 setProperty("OutputWorkspace", outputWS);
109}
110//----------------------------------------------------------------------------------------------
115std::map<std::string, std::string> Bin2DPowderDiffraction::validateInputs() {
116 std::map<std::string, std::string> result;
117
118 const auto useBinFile = !getPointerToProperty("BinEdgesFile")->isDefault();
119 const auto useBinning1 = !getPointerToProperty("dSpaceBinning")->isDefault();
120 const auto useBinning2 = !getPointerToProperty("dPerpendicularBinning")->isDefault();
121 if (!useBinFile && !useBinning1 && !useBinning2) {
122 const std::string msg = "You must specify either dSpaceBinning and "
123 "dPerpendicularBinning, or a BinEdgesFile.";
124 result["dSpaceBinning"] = msg;
125 result["dPerpendicularBinning"] = msg;
126 result["BinEdgesFile"] = msg;
127 } else if (useBinFile && (useBinning1 || useBinning2)) {
128 const std::string msg = "You must specify either dSpaceBinning and "
129 "dPerpendicularBinning, or a BinEdgesFile, but not both.";
130 result["BinEdgesFile"] = msg;
131 }
132
133 return result;
134}
135//----------------------------------------------------------------------------------------------
143
145 bool binsFromFile(false);
146 size_t dPerpSize = 0;
147 size_t dSize = 0;
148 MatrixWorkspace_sptr outputWS;
149 const auto &spectrumInfo = m_inputWS->spectrumInfo();
150
151 const std::string beFileName = getProperty("BinEdgesFile");
152 if (!beFileName.empty())
153 binsFromFile = true;
154
155 std::vector<double> dPerp, dBins;
156 std::vector<std::vector<double>> fileXbins;
157
158 // First create the output Workspace filled with zeros
159 auto startTime = std::chrono::high_resolution_clock::now();
160 if (binsFromFile) {
161 dPerp.clear();
162 ReadBinsFromFile(dPerp, fileXbins);
163 dPerpSize = dPerp.size();
164 // unify xbins
165 dSize = UnifyXBins(fileXbins);
166 g_log.debug() << "Maximal size of Xbins = " << dSize;
167 outputWS = WorkspaceFactory::Instance().create(m_inputWS, dPerpSize - 1, dSize, dSize - 1);
168 g_log.debug() << "Outws has " << outputWS->getNumberHistograms() << " histograms and " << outputWS->blocksize()
169 << " bins." << std::endl;
170
171 size_t idx = 0;
172 for (const auto &Xbins : fileXbins) {
173 g_log.debug() << "Xbins size: " << Xbins.size() << std::endl;
174 BinEdges binEdges(Xbins);
175 outputWS->setBinEdges(idx, binEdges);
176 idx++;
177 }
178
179 } else {
180 createAxisFromRebinParams(getProperty("dSpaceBinning"), dBins);
181 HistogramData::BinEdges binEdges(dBins);
182 dPerpSize = createAxisFromRebinParams(getProperty("dPerpendicularBinning"), dPerp);
183 dSize = binEdges.size();
184 outputWS = WorkspaceFactory::Instance().create(m_inputWS, dPerpSize - 1, dSize, dSize - 1);
185 for (size_t idx = 0; idx < dPerpSize - 1; idx++)
186 outputWS->setBinEdges(idx, binEdges);
187 auto abscissa = std::make_unique<BinEdgeAxis>(dBins);
188 outputWS->replaceAxis(0, std::move(abscissa));
189 }
190 addTimer("createWorkspace", startTime, std::chrono::high_resolution_clock::now());
191
192 startTime = std::chrono::high_resolution_clock::now();
193 outputWS->getAxis(0)->unit() = UnitFactory::Instance().create("dSpacing");
194
195 auto verticalAxis = std::make_unique<BinEdgeAxis>(dPerp);
196 auto verticalAxisRaw = verticalAxis.get();
197 // Meta data
198 verticalAxis->unit() = UnitFactory::Instance().create("dSpacingPerpendicular");
199 verticalAxis->title() = "d_p";
200 outputWS->replaceAxis(1, std::move(verticalAxis));
201
202 Progress prog(this, 0.0, 1.0, m_numberOfSpectra);
203 auto numSpectra = static_cast<int64_t>(m_numberOfSpectra);
204 std::vector<std::vector<double>> newYValues(dPerpSize - 1, std::vector<double>(dSize - 1, 0.0));
205 std::vector<std::vector<double>> newEValues(dPerpSize - 1, std::vector<double>(dSize - 1, 0.0));
206
207 // fill the workspace with data
208 g_log.debug() << "newYSize = " << dPerpSize << std::endl;
209 g_log.debug() << "newXSize = " << dSize << std::endl;
210 std::vector<double> dp_vec(verticalAxisRaw->getValues());
211 addTimer("fillValues", startTime, std::chrono::high_resolution_clock::now());
212
213 startTime = std::chrono::high_resolution_clock::now();
215 for (int64_t snum = 0; snum < numSpectra; ++snum) {
217 if (!spectrumInfo.isMasked(snum)) {
218 const double theta = 0.5 * spectrumInfo.twoTheta(snum);
219 const double sin_theta = sin(theta);
220 if (sin_theta == 0) {
221 throw std::runtime_error("Spectrum " + std::to_string(snum) + " has sin(theta)=0. Cannot calculate d-Spacing!");
222 }
223 if (cos(theta) <= 0) {
224 throw std::runtime_error("Spectrum " + std::to_string(snum) +
225 " has cos(theta) <= 0. Cannot calculate d-SpacingPerpendicular!");
226 }
227 const double log_cos_theta = log(cos(theta));
228 EventList &evList = m_inputWS->getSpectrum(snum);
229
230 // Switch to weighted if needed.
231 if (evList.getEventType() == TOF)
232 evList.switchTo(WEIGHTED);
233
234 std::vector<WeightedEvent> events = evList.getWeightedEvents();
235
236 for (const auto &ev : events) {
237 // find d-perpendicular bin
238 const auto dp = calcDPerp(ev.tof(), log_cos_theta);
239 const auto lowy = std::lower_bound(dp_vec.begin(), dp_vec.end(), dp);
240 if ((lowy == dp_vec.end()) || (lowy == dp_vec.begin()))
241 continue;
242 const auto dp_index = static_cast<size_t>(std::distance(dp_vec.begin(), lowy) - 1);
243
244 // find d bin
245 const auto xs = binsFromFile ? fileXbins[dp_index] : dBins;
246 const auto d = calcD(ev.tof(), sin_theta);
247 const auto lowx = std::lower_bound(xs.begin(), xs.end(), d);
248 if ((lowx == xs.end()) || lowx == xs.begin())
249 continue;
250 const auto d_index = static_cast<size_t>(std::distance(xs.begin(), lowx) - 1);
251
252 // writing to the same vectors is not thread-safe
253 PARALLEL_CRITICAL(newValues) {
254 newYValues[dp_index][d_index] += ev.weight();
255 newEValues[dp_index][d_index] += ev.errorSquared();
256 }
257 }
258 }
259 prog.report("Binning event data...");
261 }
263 addTimer("histogram", startTime, std::chrono::high_resolution_clock::now());
264
265 startTime = std::chrono::high_resolution_clock::now();
266 size_t idx = 0;
267 for (const auto &yVec : newYValues) {
268 outputWS->setCounts(idx, yVec);
269 idx++;
270 }
271 idx = 0;
272 for (auto &eVec : newEValues) {
273 std::transform(eVec.begin(), eVec.end(), eVec.begin(), static_cast<double (*)(double)>(sqrt));
274 outputWS->setCountStandardDeviations(idx, eVec);
275 idx++;
276 }
277 addTimer("setValues", startTime, std::chrono::high_resolution_clock::now());
278
279 return outputWS;
280}
281
282//----------------------------------------------------------------------------------------------
289void Bin2DPowderDiffraction::ReadBinsFromFile(std::vector<double> &Ybins,
290 std::vector<std::vector<double>> &Xbins) const {
291 const std::string beFileName = getProperty("BinEdgesFile");
292 std::ifstream file(beFileName);
293 std::string line;
294 std::string::size_type n;
295 std::string::size_type sz;
296 std::vector<double> tmp;
297 int dpno = 0;
298 while (getline(file, line)) {
299 n = line.find("dp =");
300 if (n != std::string::npos) {
301 if (!tmp.empty()) {
302 Xbins.emplace_back(tmp);
303 tmp.clear();
304 }
305 double dp1 = std::stod(line.substr(4), &sz); // 4 is needed to crop 'dp='
306 double dp2 = std::stod(line.substr(sz + 4));
307 if (dpno < 1) {
308 Ybins.emplace_back(dp1);
309 Ybins.emplace_back(dp2);
310 } else {
311 Ybins.emplace_back(dp2);
312 }
313 dpno++;
314 } else if (line.find("#") == std::string::npos) {
315 std::stringstream ss(line);
316 double d;
317 while (ss >> d) {
318 tmp.emplace_back(d);
319 }
320 }
321 }
322 Xbins.emplace_back(tmp);
323 g_log.information() << "Number of Ybins: " << Ybins.size() << std::endl;
324 g_log.information() << "Number of Xbins sets: " << Xbins.size() << std::endl;
325}
326//----------------------------------------------------------------------------------------------
336size_t Bin2DPowderDiffraction::UnifyXBins(std::vector<std::vector<double>> &Xbins) const {
337 size_t maxSize = std::accumulate(Xbins.cbegin(), Xbins.cend(), size_t(0), [](size_t currentMax, const auto &xbin) {
338 return std::max(currentMax, xbin.size());
339 });
340 // resize all vectors to maximum size, fill last vector element at the end
341 for (auto &v : Xbins) {
342 if (v.size() < maxSize)
343 v.resize(maxSize, v.back());
344 }
345 return maxSize;
346}
347
349 NumericAxis *verticalAxis = dynamic_cast<NumericAxis *>(outWS->getAxis(1));
350 const std::vector<double> &yValues = verticalAxis->getValues();
351 auto nhist = outWS->getNumberHistograms();
352 g_log.debug() << "Number of hists: " << nhist << " Length of YAxis: " << verticalAxis->length() << std::endl;
353
354 for (size_t idx = 0; idx < nhist; ++idx) {
355 double factor = 1.0 / (yValues[idx + 1] - yValues[idx]);
356 // divide by the xBinWidth
357 outWS->convertToFrequencies(idx);
358 auto &freqs = outWS->mutableY(idx);
359 using std::placeholders::_1;
360 std::transform(freqs.begin(), freqs.end(), freqs.begin(), std::bind(std::multiplies<double>(), factor, _1));
361 auto &errors = outWS->mutableE(idx);
362 std::transform(errors.begin(), errors.end(), errors.begin(), std::bind(std::multiplies<double>(), factor, _1));
363 }
364}
365
366double calcD(double wavelength, double sintheta) { return wavelength * 0.5 / sintheta; }
367
368double calcDPerp(double wavelength, double logcostheta) { return sqrt(wavelength * wavelength - 2.0 * logcostheta); }
369
370} // namespace Mantid::Algorithms
std::string name
Definition Run.cpp:60
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
gsl_vector * tmp
#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.
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
void addTimer(const std::string &name, const Kernel::time_point_ns &begin, const Kernel::time_point_ns &end)
@ OptionalLoad
to specify a file to read but the file doesn't have to exist
A validator which checks that a workspace contains histogram data (the default) or point data as requ...
A validator which checks that a workspace has a valid instrument.
Class to represent a numeric axis of a workspace.
Definition NumericAxis.h:29
virtual const std::vector< double > & getValues() const
Return a const reference to the values.
std::size_t length() const override
Get the length of the axis.
Definition NumericAxis.h:38
Helper class for reporting progress from algorithms.
Definition Progress.h:25
A validator which checks whether the input workspace has the Spectra number in the axis.
A property class for workspaces.
A validator which checks that the unit of the workspace referred to by a WorkspaceProperty is the exp...
void ReadBinsFromFile(std::vector< double > &Ybins, std::vector< std::vector< double > > &Xbins) const
Bin2DPowderDiffraction::ReadBinsFromFile.
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
void normalizeToBinArea(const API::MatrixWorkspace_sptr &outWS)
void exec() override
Execute the algorithm.
int m_numberOfSpectra
The number of spectra in the workspace.
std::map< std::string, std::string > validateInputs() override
Cross-check properties with each other.
API::MatrixWorkspace_sptr createOutputWorkspace()
Setup the output workspace.
void init() override
Initialize the algorithm's properties.
int version() const override
Algorithm's version for identification.
size_t UnifyXBins(std::vector< std::vector< double > > &Xbins) const
Bin2DPowderDiffraction::UnifyXBins unifies size of the vectors in Xbins.
DataObjects::EventWorkspace_sptr m_inputWS
Pointer to the input event workspace.
const std::string category() const override
Algorithm's category for identification.
A class for holding :
Definition EventList.h:57
Mantid::API::EventType getEventType() const override
Return the type of Event vector contained within.
void switchTo(Mantid::API::EventType newType) override
Switch the EventList to use the given EventType (TOF, WEIGHTED, or WEIGHTED_NOTIME)
std::vector< WeightedEvent > & getWeightedEvents()
Return the list of WeightedEvent contained.
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 debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
The concrete, templated class for properties.
virtual bool isDefault() const =0
Overriden function that returns if property has the same value that it was initialised with,...
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
double calcDPerp(double wavelength, double logcostheta)
double calcD(double wavelength, double sintheta)
std::size_t MANTID_KERNEL_DLL createAxisFromRebinParams(const std::vector< double > &params, std::vector< double > &xnew, const bool resize_xnew=true, const bool full_bins_only=false, const double xMinHint=std::nan(""), const double xMaxHint=std::nan(""), const bool useReverseLogarithmic=false, const double power=-1)
Creates a new output X array given a 'standard' set of rebinning parameters.
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.
STL namespace.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54