Mantid
Loading...
Searching...
No Matches
EstimateResolutionDiffraction.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 +
9#include "MantidAPI/Run.h"
23#include "MantidKernel/V3D.h"
24#include "MantidTypes/SpectrumDefinition.h"
25
26#include <cmath>
27
28using namespace Mantid;
29using namespace Mantid::API;
30using namespace Mantid::Geometry;
31using namespace Mantid::Kernel;
32using namespace std;
33
34namespace Mantid::Algorithms {
35
36DECLARE_ALGORITHM(EstimateResolutionDiffraction)
37
38namespace { // hide these constants
40const double MICROSEC_TO_SEC = 1.0E-6;
42const double WAVELENGTH_TO_VELOCITY = 1.0E10 * PhysicalConstants::h / PhysicalConstants::NeutronMass;
44const double WAVELENGTH_MAX = 1000.;
45
46namespace PropertyNames {
47const std::string INPUT_WKSP("InputWorkspace");
48const std::string OUTPUT_WKSP("OutputWorkspace");
49const std::string PARTIALS_WKSP_GRP("PartialResolutionWorkspaces");
50const std::string DIVERGENCE_WKSP("DivergenceWorkspace");
51const std::string WAVELENGTH("Wavelength");
52const std::string DELTA_T("DeltaTOF");
53const std::string DELTA_T_OVER_T("DeltaTOFOverTOF");
54const std::string SOURCE_DELTA_L("SourceDeltaL");
55const std::string SOURCE_DELTA_THETA("SourceDeltaTheta");
56} // namespace PropertyNames
57} // namespace
58
59const std::string EstimateResolutionDiffraction::name() const { return "EstimateResolutionDiffraction"; }
60
61const std::string EstimateResolutionDiffraction::alias() const { return "EstimatePDDetectorResolution"; }
62
63const std::string EstimateResolutionDiffraction::summary() const {
64 return "Estimate the resolution of each detector pixel for a powder "
65 "diffractometer";
66}
67
69
70const std::string EstimateResolutionDiffraction::category() const { return "Diffraction\\Utility"; }
71
74 "Name of the workspace to have detector resolution calculated");
75 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>(PropertyNames::DIVERGENCE_WKSP, "",
77 "Workspace containing the divergence");
80 "Name of the output workspace containing delta(d)/d of each "
81 "detector/spectrum");
82
83 auto positiveOrZero = std::make_shared<BoundedValidator<double>>();
84 positiveOrZero->setLower(0.);
85 positiveOrZero->setLowerExclusive(false);
87 std::make_unique<PropertyWithValue<double>>(PropertyNames::DELTA_T, 0., positiveOrZero, PropertyMode::Optional),
88 "DeltaT as the resolution of TOF with unit microsecond");
89
90 declareProperty(PropertyNames::DELTA_T_OVER_T, EMPTY_DBL(), positiveOrZero,
91 "DeltaT/T as the full term in the equation");
92
93 declareProperty(PropertyNames::SOURCE_DELTA_L, 0., positiveOrZero,
94 "Uncertainty in the path length due to the source in unit of meters");
95 declareProperty(PropertyNames::SOURCE_DELTA_THETA, 0., positiveOrZero,
96 "Uncertainty in angle due to the source in unit of radians");
97
98 auto positiveWavelength = std::make_shared<BoundedValidator<double>>();
99 positiveWavelength->setLower(0.);
100 positiveWavelength->setLowerExclusive(true);
101 declareProperty(PropertyNames::WAVELENGTH, EMPTY_DBL(), positiveWavelength,
102 "Wavelength setting in Angstroms. This overrides what is in "
103 "the dataset.");
104
106 std::make_unique<WorkspaceProperty<WorkspaceGroup>>(PropertyNames::PARTIALS_WKSP_GRP, "", Direction::Output),
107 "Workspaces created showing the various resolution terms");
108}
109
110std::map<std::string, std::string> EstimateResolutionDiffraction::validateInputs() {
111 std::map<std::string, std::string> errors;
112
113 // cannot specify both deltaTOF AND deltaTOF/TOF
114 const double deltaT = getProperty(PropertyNames::DELTA_T);
115 const bool hasDeltaT = (deltaT > 0.);
116 const bool hasDeltaTOverT = (!isDefault(PropertyNames::DELTA_T_OVER_T));
117 std::string msg;
118 if (hasDeltaT && hasDeltaTOverT) {
119 msg = "Cannot specify both " + PropertyNames::DELTA_T + " and " + PropertyNames::DELTA_T_OVER_T;
120 } else if ((!hasDeltaT) && (!hasDeltaTOverT)) {
121 msg = "Must specify either " + PropertyNames::DELTA_T + " or " + PropertyNames::DELTA_T_OVER_T;
122 }
123 if (!msg.empty()) {
124 errors[PropertyNames::DELTA_T] = msg;
125 errors[PropertyNames::DELTA_T_OVER_T] = msg;
126 }
127
128 return errors;
129}
130
133
134 // create all of the output workspaces
135 std::string partials_prefix = getPropertyValue(PropertyNames::PARTIALS_WKSP_GRP);
136 m_resTof = DataObjects::create<DataObjects::Workspace2D>(*m_inputWS, HistogramData::Points(1));
137 m_resPathLength = DataObjects::create<DataObjects::Workspace2D>(*m_inputWS, HistogramData::Points(1));
138 m_resAngle = DataObjects::create<DataObjects::Workspace2D>(*m_inputWS, HistogramData::Points(1));
139 m_outputWS = DataObjects::create<DataObjects::Workspace2D>(*m_inputWS, HistogramData::Points(1));
140
142
144
145 // put together the output group
146 auto partialsGroup = std::make_shared<WorkspaceGroup>();
147 API::AnalysisDataService::Instance().addOrReplace(partials_prefix + "_tof", m_resTof);
148 API::AnalysisDataService::Instance().addOrReplace(partials_prefix + "_length", m_resPathLength);
149 API::AnalysisDataService::Instance().addOrReplace(partials_prefix + "_angle", m_resAngle);
150 partialsGroup->addWorkspace(m_resTof);
151 partialsGroup->addWorkspace(m_resPathLength);
152 partialsGroup->addWorkspace(m_resAngle);
153 setProperty(PropertyNames::PARTIALS_WKSP_GRP, partialsGroup);
154}
155
158 m_divergenceWS = getProperty(PropertyNames::DIVERGENCE_WKSP);
159
160 // get source delta-L value and square it
161 m_sourceDeltaLMetersSq = getProperty(PropertyNames::SOURCE_DELTA_L);
163
164 // get source delta-theta value and square it
165 m_sourceDeltaThetaRadiansSq = getProperty(PropertyNames::SOURCE_DELTA_THETA);
167
168 if (isDefault(PropertyNames::DELTA_T_OVER_T)) {
169 m_deltaT = getProperty(PropertyNames::DELTA_T);
170 m_deltaT *= MICROSEC_TO_SEC; // convert to seconds
171
173 } else {
174 m_deltaTOverTOF = getProperty(PropertyNames::DELTA_T_OVER_T);
175 }
176}
177
179 double wavelength = getProperty(PropertyNames::WAVELENGTH);
180 if (!isEmpty(wavelength)) {
181 return wavelength;
182 }
183
184 Property *cwlproperty = m_inputWS->run().getProperty("LambdaRequest");
185 if (!cwlproperty)
186 throw runtime_error("Unable to locate property LambdaRequest as central wavelength. ");
187
188 auto const *cwltimeseries = dynamic_cast<TimeSeriesProperty<double> *>(cwlproperty);
189
190 if (!cwltimeseries)
191 throw runtime_error("LambdaReqeust is not a TimeSeriesProperty in double. ");
192
193 string unit = cwltimeseries->units();
194 if ((unit != "Angstrom") && (unit != "A")) {
195 throw runtime_error("Unit is not recognized: " + unit);
196 }
197
198 return m_inputWS->run().getTimeAveragedValue("LambdaRequest");
199}
200
202 double centrewavelength = getWavelength();
203 g_log.notice() << "Centre wavelength = " << centrewavelength << " Angstrom\n";
204 if (centrewavelength > WAVELENGTH_MAX) {
205 throw runtime_error("unphysical wavelength used");
206 }
207
208 // Calculate centre neutron velocity
209 m_centreVelocity = WAVELENGTH_TO_VELOCITY / centrewavelength;
210 g_log.notice() << "Centre neutron velocity = " << m_centreVelocity << "\n";
211}
212
214 const auto &spectrumInfo = m_inputWS->spectrumInfo();
215 const auto l1 = spectrumInfo.l1();
216 const auto &componentInfo = m_inputWS->componentInfo();
217 const auto &detectorInfo = m_inputWS->detectorInfo();
218 g_log.notice() << "L1 = " << l1 << "\n";
219 const auto samplepos = spectrumInfo.samplePosition();
220
221 const size_t numspec = m_inputWS->getNumberHistograms();
222
223 double mintwotheta = 2. * M_PI; // a bit more than 2*pi
224 double maxtwotheta = 0.;
225 for (std::size_t i = 0; i < detectorInfo.size(); ++i) {
226 if (!(detectorInfo.isMasked(i) || detectorInfo.isMonitor(i))) {
227 // update overall range for two-theta and term3
228 const auto twotheta = detectorInfo.twoTheta(i);
229 mintwotheta = std::min(twotheta, mintwotheta);
230 maxtwotheta = std::max(twotheta, maxtwotheta);
231 }
232 }
233
234 double minTerm3Sq = 1.;
235 double maxTerm3Sq = 0.;
236
237 g_log.information() << "Source terms: deltaL=" << sqrt(m_sourceDeltaLMetersSq)
238 << " deltaTheta=" << sqrt(m_sourceDeltaThetaRadiansSq) << "\n";
239
240 // if a spectrum has multiple pixels, add them up in quadrature
241 for (size_t specNum = 0; specNum < numspec; ++specNum) {
242 // which of the detectors are part of this spectrum
243 const auto &spectrumDefinition = spectrumInfo.spectrumDefinition(specNum);
244 // number of spectra as a double
245 const double numDet = double(spectrumDefinition.size());
246
247 // resolution in time
248 double term1Sq = m_deltaTOverTOF * m_deltaTOverTOF;
249 if (term1Sq == 0.) { // calculate per pixel
250 const auto centreVel = m_centreVelocity;
251 const auto deltaT = m_deltaT;
252 term1Sq = std::accumulate(spectrumDefinition.cbegin(), spectrumDefinition.cend(), 0.,
253 [&detectorInfo, &centreVel, &l1, &deltaT](const auto sum, const auto &index) {
254 if (detectorInfo.isMasked(index.first) || detectorInfo.isMonitor(index.first)) {
255 return sum;
256 } else {
257 // Get the distance from detector to source
258 const double l2 = detectorInfo.l2(index.first);
259 const double centraltof = (l1 + l2) / centreVel;
260 const double term = deltaT / centraltof;
261 return sum + (term * term);
262 }
263 });
264 term1Sq = term1Sq / numDet;
265 }
266
267 // resolution in length
268 double term2Sq;
269 { // reduce scope
270 const double sourceTerm = m_sourceDeltaLMetersSq;
271 term2Sq = std::accumulate(spectrumDefinition.cbegin(), spectrumDefinition.cend(), 0.,
272 [&detectorInfo, &sourceTerm, &l1](const auto sum, const auto &index) {
273 if (detectorInfo.isMasked(index.first) || detectorInfo.isMonitor(index.first)) {
274 return sum;
275 } else {
276 const auto &detector = detectorInfo.detector(index.first);
277 const auto realdet = dynamic_cast<const Detector *>(&detector);
278 if (realdet) {
279 const auto l2 = detectorInfo.l2(index.first);
280 const auto l_total = (l1 + l2);
281 realdet->shape();
282 const double dx = realdet->getWidth();
283 const double dy = realdet->getHeight();
284 const double dz = realdet->getDepth();
285 // not sure why divide by 4, but it has always been there
286 const double detdimSq = (dx * dx + dy * dy + dz * dz) * 0.25;
287 const double term = (detdimSq + sourceTerm) / (l_total * l_total);
288 return sum + term;
289 } else {
290 return sum;
291 }
292 }
293 });
294 term2Sq = term2Sq / numDet;
295 }
296
297 // resolution in angle - everything is in radians
298 double term3Sq;
299 if (m_divergenceWS) {
300 double deltathetaSq = m_divergenceWS->y(specNum)[0];
301 deltathetaSq = deltathetaSq * deltathetaSq;
302 // use the average angle for the spectrum - not right for focussed data
303 const double theta = spectrumInfo.isMonitor(specNum) ? 0.0 : 0.5 * spectrumInfo.twoTheta(specNum);
304 const double tan_theta = tan(theta);
305 term3Sq = (deltathetaSq + m_sourceDeltaThetaRadiansSq) / (tan_theta * tan_theta);
306 } else {
307 const double sourceTerm = m_sourceDeltaThetaRadiansSq;
308 const double solidAngle = std::accumulate(
309 spectrumDefinition.cbegin(), spectrumDefinition.cend(), 0.,
310 [&componentInfo, &samplepos, &detectorInfo, &sourceTerm](const auto sum, const auto &index) {
311 if (detectorInfo.isMasked(index.first) || detectorInfo.isMonitor(index.first)) {
312 return sum;
313 } else {
314 const double theta = 0.5 * detectorInfo.twoTheta(index.first); // angle of the pixel
315 const double cot_theta = 1. / tan(theta);
316 return sum + ((componentInfo.solidAngle(index.first, samplepos) + sourceTerm) * cot_theta * cot_theta);
317 }
318 });
319 term3Sq = solidAngle / numDet;
320 }
321
322 // add information to outputs
323 if (spectrumInfo.isMonitor(specNum)) {
324 m_resTof->mutableY(specNum) = 0.;
325 m_resPathLength->mutableY(specNum) = 0.;
326 m_resAngle->mutableY(specNum) = 0.;
327 m_outputWS->mutableY(specNum) = 0.;
328 } else { // not a monitor
329 const double resolution = sqrt(term1Sq + term2Sq + term3Sq);
330 m_resTof->mutableY(specNum) = sqrt(term1Sq);
331 m_resPathLength->mutableY(specNum) = sqrt(term2Sq);
332 m_resAngle->mutableY(specNum) = sqrt(term3Sq);
333 m_outputWS->mutableY(specNum) = resolution;
334 }
335
336 m_resTof->mutableX(specNum) = static_cast<double>(specNum);
337 m_resPathLength->mutableX(specNum) = static_cast<double>(specNum);
338 m_resAngle->mutableX(specNum) = static_cast<double>(specNum);
339 m_outputWS->mutableX(specNum) = static_cast<double>(specNum);
340
341 // update overall range for term3
342 minTerm3Sq = std::min(term3Sq, minTerm3Sq);
343 maxTerm3Sq = std::max(term3Sq, maxTerm3Sq);
344
345 // log extra information if level is debug (7) or greater - converting to strings is expensive
346 if (g_log.getLevelOffset() > 6) {
347 // central two-theta
348 const double twotheta = spectrumInfo.isMonitor(specNum) ? 0.0 : spectrumInfo.twoTheta(specNum);
349 const auto &det = spectrumInfo.detector(specNum);
350
351 g_log.debug() << det.type() << " " << specNum << "\t\t" << twotheta << "\t\tdT/T = " << sqrt(term1Sq)
352 << "\t\tdL/L = " << sqrt(term2Sq) << "\t\tdTheta*cotTheta = " << sqrt(term3Sq) << "\n";
353 }
354 }
355
356 g_log.notice() << "2theta range: " << (mintwotheta * Geometry::rad2deg) << ", " << (maxtwotheta * Geometry::rad2deg)
357 << "\n";
358 g_log.notice() << "t3 range: " << sqrt(minTerm3Sq) << ", " << sqrt(maxTerm3Sq) << "\n";
359}
360
361} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
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.
Kernel::Logger & g_log
Definition Algorithm.h:422
bool isDefault(const std::string &name) const
static bool isEmpty(const NumT toCheck)
checks that the value was not set by users, uses the value in empty double/int.
A property class for workspaces.
API::MatrixWorkspace_sptr m_resTof
workspace holding the term for just the time-of-flight portion of the resolution
API::MatrixWorkspace_sptr m_resAngle
workspace holding the term for just the angular/solid angle portion of the resolution
double getWavelength()
Returns the wavelength from either the property or the input workspace.
API::MatrixWorkspace_sptr m_outputWS
Output workspace.
void processAlgProperties()
Process input properties for algorithm.
const std::string category() const override
Algorithm's category for identification overriding a virtual method.
std::map< std::string, std::string > validateInputs() override
Method checking errors on ALL the inputs, before execution.
void exec() override
Implement abstract Algorithm methods.
void init() override
Implement abstract Algorithm methods.
const std::string alias() const override
function to return any aliases to the algorithm
API::MatrixWorkspace_sptr m_divergenceWS
Workspace with custom divergence term.
API::MatrixWorkspace_sptr m_resPathLength
workspace holding the term for just the flight path portion of the resolution
const std::string summary() const override
Summary of algorithms purpose.
int version() const override
Algorithm's version for identification overriding a virtual method.
const std::string name() const override
Algorithm's name for identification overriding a virtual method.
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
int getLevelOffset() const
Gets the Logger's log offset level.
Definition Logger.cpp:467
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
The concrete, templated class for properties.
Base class for properties.
Definition Property.h:94
A specialised Property class for holding a series of time-value pairs.
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::string const OUTPUT_WKSP("OutputWorkspace")
std::string const INPUT_WKSP("InputWorkspace")
constexpr double rad2deg
Radians to degrees conversion factor.
Definition AngleUnits.h:23
static constexpr double NeutronMass
Mass of the neutron in kg.
static constexpr double h
Planck constant in J*s.
const std::string OUTPUT_WKSP("OutputWorkspace")
const std::string INPUT_WKSP("InputWorkspace")
Helper class which provides the Collimation Length for SANS instruments.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
STL namespace.
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54