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 const auto &oldXEdges = m_inputWS->x(0);
156 BinEdges dBins(oldXEdges.size());
157 BinEdges dPerpBins(oldXEdges.size());
158
159 auto &dPerp = dPerpBins.mutableRawData();
160 std::vector<std::vector<double>> fileXbins;
161
162 // First create the output Workspace filled with zeros
163 auto startTime = std::chrono::high_resolution_clock::now();
164 if (binsFromFile) {
165 dPerp.clear();
166 ReadBinsFromFile(dPerp, fileXbins);
167 dPerpSize = dPerp.size();
168 // unify xbins
169 dSize = UnifyXBins(fileXbins);
170 g_log.debug() << "Maximal size of Xbins = " << dSize;
171 outputWS = WorkspaceFactory::Instance().create(m_inputWS, dPerpSize - 1, dSize, dSize - 1);
172 g_log.debug() << "Outws has " << outputWS->getNumberHistograms() << " histograms and " << outputWS->blocksize()
173 << " bins." << std::endl;
174
175 size_t idx = 0;
176 for (const auto &Xbins : fileXbins) {
177 g_log.debug() << "Xbins size: " << Xbins.size() << std::endl;
178 BinEdges binEdges(Xbins);
179 outputWS->setBinEdges(idx, binEdges);
180 idx++;
181 }
182
183 } else {
184 static_cast<void>(createAxisFromRebinParams(getProperty("dSpaceBinning"), dBins.mutableRawData()));
185 HistogramData::BinEdges binEdges(dBins);
186 dPerpSize = createAxisFromRebinParams(getProperty("dPerpendicularBinning"), dPerp);
187 dSize = binEdges.size();
188 outputWS = WorkspaceFactory::Instance().create(m_inputWS, dPerpSize - 1, dSize, dSize - 1);
189 for (size_t idx = 0; idx < dPerpSize - 1; idx++)
190 outputWS->setBinEdges(idx, binEdges);
191 auto abscissa = std::make_unique<BinEdgeAxis>(dBins.mutableRawData());
192 outputWS->replaceAxis(0, std::move(abscissa));
193 }
194 addTimer("createWorkspace", startTime, std::chrono::high_resolution_clock::now());
195
196 startTime = std::chrono::high_resolution_clock::now();
197 outputWS->getAxis(0)->unit() = UnitFactory::Instance().create("dSpacing");
198
199 auto verticalAxis = std::make_unique<BinEdgeAxis>(dPerp);
200 auto verticalAxisRaw = verticalAxis.get();
201 // Meta data
202 verticalAxis->unit() = UnitFactory::Instance().create("dSpacingPerpendicular");
203 verticalAxis->title() = "d_p";
204 outputWS->replaceAxis(1, std::move(verticalAxis));
205
206 Progress prog(this, 0.0, 1.0, m_numberOfSpectra);
207 auto numSpectra = static_cast<int64_t>(m_numberOfSpectra);
208 std::vector<std::vector<double>> newYValues(dPerpSize - 1, std::vector<double>(dSize - 1, 0.0));
209 std::vector<std::vector<double>> newEValues(dPerpSize - 1, std::vector<double>(dSize - 1, 0.0));
210
211 // fill the workspace with data
212 g_log.debug() << "newYSize = " << dPerpSize << std::endl;
213 g_log.debug() << "newXSize = " << dSize << std::endl;
214 std::vector<double> dp_vec(verticalAxisRaw->getValues());
215 addTimer("fillValues", startTime, std::chrono::high_resolution_clock::now());
216
217 startTime = std::chrono::high_resolution_clock::now();
219 for (int64_t snum = 0; snum < numSpectra; ++snum) {
221 if (!spectrumInfo.isMasked(snum)) {
222 const double theta = 0.5 * spectrumInfo.twoTheta(snum);
223 const double sin_theta = sin(theta);
224 if (sin_theta == 0) {
225 throw std::runtime_error("Spectrum " + std::to_string(snum) + " has sin(theta)=0. Cannot calculate d-Spacing!");
226 }
227 if (cos(theta) <= 0) {
228 throw std::runtime_error("Spectrum " + std::to_string(snum) +
229 " has cos(theta) <= 0. Cannot calculate d-SpacingPerpendicular!");
230 }
231 const double log_cos_theta = log(cos(theta));
232 EventList &evList = m_inputWS->getSpectrum(snum);
233
234 // Switch to weighted if needed.
235 if (evList.getEventType() == TOF)
236 evList.switchTo(WEIGHTED);
237
238 std::vector<WeightedEvent> events = evList.getWeightedEvents();
239
240 for (const auto &ev : events) {
241 // find d-perpendicular bin
242 const auto dp = calcDPerp(ev.tof(), log_cos_theta);
243 const auto lowy = std::lower_bound(dp_vec.begin(), dp_vec.end(), dp);
244 if ((lowy == dp_vec.end()) || (lowy == dp_vec.begin()))
245 continue;
246 const auto dp_index = static_cast<size_t>(std::distance(dp_vec.begin(), lowy) - 1);
247
248 // find d bin
249 const auto xs = binsFromFile ? fileXbins[dp_index] : dBins.rawData();
250 const auto d = calcD(ev.tof(), sin_theta);
251 const auto lowx = std::lower_bound(xs.begin(), xs.end(), d);
252 if ((lowx == xs.end()) || lowx == xs.begin())
253 continue;
254 const auto d_index = static_cast<size_t>(std::distance(xs.begin(), lowx) - 1);
255
256 // writing to the same vectors is not thread-safe
257 PARALLEL_CRITICAL(newValues) {
258 newYValues[dp_index][d_index] += ev.weight();
259 newEValues[dp_index][d_index] += ev.errorSquared();
260 }
261 }
262 }
263 prog.report("Binning event data...");
265 }
267 addTimer("histogram", startTime, std::chrono::high_resolution_clock::now());
268
269 startTime = std::chrono::high_resolution_clock::now();
270 size_t idx = 0;
271 for (const auto &yVec : newYValues) {
272 outputWS->setCounts(idx, yVec);
273 idx++;
274 }
275 idx = 0;
276 for (auto &eVec : newEValues) {
277 std::transform(eVec.begin(), eVec.end(), eVec.begin(), static_cast<double (*)(double)>(sqrt));
278 outputWS->setCountStandardDeviations(idx, eVec);
279 idx++;
280 }
281 addTimer("setValues", startTime, std::chrono::high_resolution_clock::now());
282
283 return outputWS;
284}
285
286//----------------------------------------------------------------------------------------------
293void Bin2DPowderDiffraction::ReadBinsFromFile(std::vector<double> &Ybins,
294 std::vector<std::vector<double>> &Xbins) const {
295 const std::string beFileName = getProperty("BinEdgesFile");
296 std::ifstream file(beFileName);
297 std::string line;
298 std::string::size_type n;
299 std::string::size_type sz;
300 std::vector<double> tmp;
301 int dpno = 0;
302 while (getline(file, line)) {
303 n = line.find("dp =");
304 if (n != std::string::npos) {
305 if (!tmp.empty()) {
306 Xbins.emplace_back(tmp);
307 tmp.clear();
308 }
309 double dp1 = std::stod(line.substr(4), &sz); // 4 is needed to crop 'dp='
310 double dp2 = std::stod(line.substr(sz + 4));
311 if (dpno < 1) {
312 Ybins.emplace_back(dp1);
313 Ybins.emplace_back(dp2);
314 } else {
315 Ybins.emplace_back(dp2);
316 }
317 dpno++;
318 } else if (line.find("#") == std::string::npos) {
319 std::stringstream ss(line);
320 double d;
321 while (ss >> d) {
322 tmp.emplace_back(d);
323 }
324 }
325 }
326 Xbins.emplace_back(tmp);
327 g_log.information() << "Number of Ybins: " << Ybins.size() << std::endl;
328 g_log.information() << "Number of Xbins sets: " << Xbins.size() << std::endl;
329}
330//----------------------------------------------------------------------------------------------
340size_t Bin2DPowderDiffraction::UnifyXBins(std::vector<std::vector<double>> &Xbins) const {
341 size_t maxSize = std::accumulate(Xbins.cbegin(), Xbins.cend(), size_t(0), [](size_t currentMax, const auto &xbin) {
342 return std::max(currentMax, xbin.size());
343 });
344 // resize all vectors to maximum size, fill last vector element at the end
345 for (auto &v : Xbins) {
346 if (v.size() < maxSize)
347 v.resize(maxSize, v.back());
348 }
349 return maxSize;
350}
351
353 NumericAxis *verticalAxis = dynamic_cast<NumericAxis *>(outWS->getAxis(1));
354 const std::vector<double> &yValues = verticalAxis->getValues();
355 auto nhist = outWS->getNumberHistograms();
356 g_log.debug() << "Number of hists: " << nhist << " Length of YAxis: " << verticalAxis->length() << std::endl;
357
358 for (size_t idx = 0; idx < nhist; ++idx) {
359 double factor = 1.0 / (yValues[idx + 1] - yValues[idx]);
360 // divide by the xBinWidth
361 outWS->convertToFrequencies(idx);
362 auto &freqs = outWS->mutableY(idx);
363 using std::placeholders::_1;
364 std::transform(freqs.begin(), freqs.end(), freqs.begin(), std::bind(std::multiplies<double>(), factor, _1));
365 auto &errors = outWS->mutableE(idx);
366 std::transform(errors.begin(), errors.end(), errors.begin(), std::bind(std::multiplies<double>(), factor, _1));
367 }
368}
369
370double calcD(double wavelength, double sintheta) { return wavelength * 0.5 / sintheta; }
371
372double calcDPerp(double wavelength, double logcostheta) { return sqrt(wavelength * wavelength - 2.0 * logcostheta); }
373
374} // 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)
int 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