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"
24#include "MantidKernel/V3D.h"
25#include "MantidTypes/SpectrumDefinition.h"
26
27#include <cmath>
28
29using namespace Mantid;
30using namespace Mantid::API;
31using namespace Mantid::Geometry;
32using namespace Mantid::Kernel;
33using namespace std;
34
35namespace Mantid::Algorithms {
36
37DECLARE_ALGORITHM(EstimateResolutionDiffraction)
38
39namespace { // hide these constants
41const double MICROSEC_TO_SEC = 1.0E-6;
43const double WAVELENGTH_TO_VELOCITY = 1.0E10 * PhysicalConstants::h / PhysicalConstants::NeutronMass;
45const double WAVELENGTH_MAX = 1000.;
46
47namespace PropertyNames {
48const std::string INPUT_WKSP("InputWorkspace");
49const std::string OUTPUT_WKSP("OutputWorkspace");
50const std::string PARTIALS_WKSP_GRP("PartialResolutionWorkspaces");
51const std::string DIVERGENCE_WKSP("DivergenceWorkspace");
52const std::string WAVELENGTH("Wavelength");
53const std::string DELTA_T("DeltaTOF");
54const std::string DELTA_T_OVER_T("DeltaTOFOverTOF");
55const std::string SOURCE_DELTA_L("SourceDeltaL");
56const std::string SOURCE_DELTA_THETA("SourceDeltaTheta");
57} // namespace PropertyNames
58} // namespace
59
60const std::string EstimateResolutionDiffraction::name() const { return "EstimateResolutionDiffraction"; }
61
62const std::string EstimateResolutionDiffraction::alias() const { return "EstimatePDDetectorResolution"; }
63
64const std::string EstimateResolutionDiffraction::summary() const {
65 return "Estimate the resolution of each detector pixel for a powder "
66 "diffractometer";
67}
68
70
71const std::string EstimateResolutionDiffraction::category() const { return "Diffraction\\Utility"; }
72
75 "Name of the workspace to have detector resolution calculated");
76 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>(PropertyNames::DIVERGENCE_WKSP, "",
78 "Workspace containing the divergence");
81 "Name of the output workspace containing delta(d)/d of each "
82 "detector/spectrum");
83
84 auto positiveOrZero = std::make_shared<BoundedValidator<double>>();
85 positiveOrZero->setLower(0.);
86 positiveOrZero->setLowerExclusive(false);
88 std::make_unique<PropertyWithValue<double>>(PropertyNames::DELTA_T, 0., positiveOrZero, PropertyMode::Optional),
89 "DeltaT as the resolution of TOF with unit microsecond");
90
91 declareProperty(PropertyNames::DELTA_T_OVER_T, EMPTY_DBL(), positiveOrZero,
92 "DeltaT/T as the full term in the equation");
93
94 declareProperty(PropertyNames::SOURCE_DELTA_L, 0., positiveOrZero,
95 "Uncertainty in the path length due to the source in unit of meters");
96 declareProperty(PropertyNames::SOURCE_DELTA_THETA, 0., positiveOrZero,
97 "Uncertainty in angle due to the source in unit of radians");
98
99 auto positiveWavelength = std::make_shared<BoundedValidator<double>>();
100 positiveWavelength->setLower(0.);
101 positiveWavelength->setLowerExclusive(true);
102 declareProperty(PropertyNames::WAVELENGTH, EMPTY_DBL(), positiveWavelength,
103 "Wavelength setting in Angstroms. This overrides what is in "
104 "the dataset.");
105
107 std::make_unique<WorkspaceProperty<WorkspaceGroup>>(PropertyNames::PARTIALS_WKSP_GRP, "", Direction::Output),
108 "Workspaces created showing the various resolution terms");
109}
110
111std::map<std::string, std::string> EstimateResolutionDiffraction::validateInputs() {
112 std::map<std::string, std::string> errors;
113
114 // cannot specify both deltaTOF AND deltaTOF/TOF
115 const double deltaT = getProperty(PropertyNames::DELTA_T);
116 const bool hasDeltaT = (deltaT > 0.);
117 const bool hasDeltaTOverT = (!isDefault(PropertyNames::DELTA_T_OVER_T));
118 std::string msg;
119 if (hasDeltaT && hasDeltaTOverT) {
120 msg = "Cannot specify both " + PropertyNames::DELTA_T + " and " + PropertyNames::DELTA_T_OVER_T;
121 } else if ((!hasDeltaT) && (!hasDeltaTOverT)) {
122 msg = "Must specify either " + PropertyNames::DELTA_T + " or " + PropertyNames::DELTA_T_OVER_T;
123 }
124 if (!msg.empty()) {
125 errors[PropertyNames::DELTA_T] = msg;
126 errors[PropertyNames::DELTA_T_OVER_T] = msg;
127 }
128
129 return errors;
130}
131
134
135 // create all of the output workspaces
136 std::string partials_prefix = getPropertyValue(PropertyNames::PARTIALS_WKSP_GRP);
137 m_resTof = DataObjects::create<DataObjects::Workspace2D>(*m_inputWS, HistogramData::Points(1));
138 m_resPathLength = DataObjects::create<DataObjects::Workspace2D>(*m_inputWS, HistogramData::Points(1));
139 m_resAngle = DataObjects::create<DataObjects::Workspace2D>(*m_inputWS, HistogramData::Points(1));
140 m_outputWS = DataObjects::create<DataObjects::Workspace2D>(*m_inputWS, HistogramData::Points(1));
141
143
145
146 // put together the output group
147 auto partialsGroup = std::make_shared<WorkspaceGroup>();
148 API::AnalysisDataService::Instance().addOrReplace(partials_prefix + "_tof", m_resTof);
149 API::AnalysisDataService::Instance().addOrReplace(partials_prefix + "_length", m_resPathLength);
150 API::AnalysisDataService::Instance().addOrReplace(partials_prefix + "_angle", m_resAngle);
151 partialsGroup->addWorkspace(m_resTof);
152 partialsGroup->addWorkspace(m_resPathLength);
153 partialsGroup->addWorkspace(m_resAngle);
154 setProperty(PropertyNames::PARTIALS_WKSP_GRP, partialsGroup);
155}
156
159 m_divergenceWS = getProperty(PropertyNames::DIVERGENCE_WKSP);
160
161 // get source delta-L value and square it
162 m_sourceDeltaLMetersSq = getProperty(PropertyNames::SOURCE_DELTA_L);
164
165 // get source delta-theta value and square it
166 m_sourceDeltaThetaRadiansSq = getProperty(PropertyNames::SOURCE_DELTA_THETA);
168
169 if (isDefault(PropertyNames::DELTA_T_OVER_T)) {
170 m_deltaT = getProperty(PropertyNames::DELTA_T);
171 m_deltaT *= MICROSEC_TO_SEC; // convert to seconds
172
174 } else {
175 m_deltaTOverTOF = getProperty(PropertyNames::DELTA_T_OVER_T);
176 }
177}
178
180 double wavelength = getProperty(PropertyNames::WAVELENGTH);
181 if (!isEmpty(wavelength)) {
182 return wavelength;
183 }
184
185 Property *cwlproperty = m_inputWS->run().getProperty("LambdaRequest");
186 if (!cwlproperty)
187 throw runtime_error("Unable to locate property LambdaRequest as central wavelength. ");
188
189 auto const *cwltimeseries = dynamic_cast<TimeSeriesProperty<double> *>(cwlproperty);
190
191 if (!cwltimeseries)
192 throw runtime_error("LambdaReqeust is not a TimeSeriesProperty in double. ");
193
194 string unit = cwltimeseries->units();
195 if ((unit != "Angstrom") && (unit != "A")) {
196 throw runtime_error("Unit is not recognized: " + unit);
197 }
198
199 return m_inputWS->run().getTimeAveragedValue("LambdaRequest");
200}
201
203 double centrewavelength = getWavelength();
204 g_log.notice() << "Centre wavelength = " << centrewavelength << " Angstrom\n";
205 if (centrewavelength > WAVELENGTH_MAX) {
206 throw runtime_error("unphysical wavelength used");
207 }
208
209 // Calculate centre neutron velocity
210 m_centreVelocity = WAVELENGTH_TO_VELOCITY / centrewavelength;
211 g_log.notice() << "Centre neutron velocity = " << m_centreVelocity << "\n";
212}
213
215 const auto &spectrumInfo = m_inputWS->spectrumInfo();
216 const auto l1 = spectrumInfo.l1();
217 const auto &componentInfo = m_inputWS->componentInfo();
218 const auto &detectorInfo = m_inputWS->detectorInfo();
219 g_log.notice() << "L1 = " << l1 << "\n";
220 const auto samplepos = spectrumInfo.samplePosition();
221
222 const size_t numspec = m_inputWS->getNumberHistograms();
223
224 double mintwotheta = 2. * M_PI; // a bit more than 2*pi
225 double maxtwotheta = 0.;
226 for (std::size_t i = 0; i < detectorInfo.size(); ++i) {
227 if (!(detectorInfo.isMasked(i) || detectorInfo.isMonitor(i))) {
228 // update overall range for two-theta and term3
229 const auto twotheta = detectorInfo.twoTheta(i);
230 mintwotheta = std::min(twotheta, mintwotheta);
231 maxtwotheta = std::max(twotheta, maxtwotheta);
232 }
233 }
234
235 double minTerm3Sq = 1.;
236 double maxTerm3Sq = 0.;
237
238 g_log.information() << "Source terms: deltaL=" << sqrt(m_sourceDeltaLMetersSq)
239 << " deltaTheta=" << sqrt(m_sourceDeltaThetaRadiansSq) << "\n";
240
241 // if a spectrum has multiple pixels, add them up in quadrature
242 for (size_t specNum = 0; specNum < numspec; ++specNum) {
243 // which of the detectors are part of this spectrum
244 const auto &spectrumDefinition = spectrumInfo.spectrumDefinition(specNum);
245 // number of spectra as a double
246 const double numDet = double(spectrumDefinition.size());
247
248 // resolution in time
249 double term1Sq = m_deltaTOverTOF * m_deltaTOverTOF;
250 if (term1Sq == 0.) { // calculate per pixel
251 const auto centreVel = m_centreVelocity;
252 const auto deltaT = m_deltaT;
253 term1Sq = std::accumulate(spectrumDefinition.cbegin(), spectrumDefinition.cend(), 0.,
254 [&detectorInfo, &centreVel, &l1, &deltaT](const auto sum, const auto &index) {
255 if (detectorInfo.isMasked(index.first) || detectorInfo.isMonitor(index.first)) {
256 return sum;
257 } else {
258 // Get the distance from detector to source
259 const double l2 = detectorInfo.l2(index.first);
260 const double centraltof = (l1 + l2) / centreVel;
261 const double term = deltaT / centraltof;
262 return sum + (term * term);
263 }
264 });
265 term1Sq = term1Sq / numDet;
266 }
267
268 // resolution in length
269 double term2Sq;
270 { // reduce scope
271 const double sourceTerm = m_sourceDeltaLMetersSq;
272 term2Sq = std::accumulate(spectrumDefinition.cbegin(), spectrumDefinition.cend(), 0.,
273 [&componentInfo, &detectorInfo, &sourceTerm, &l1](const auto sum, const auto &index) {
274 if (detectorInfo.isMasked(index.first) || detectorInfo.isMonitor(index.first)) {
275 return sum;
276 } else {
277 const auto l2 = detectorInfo.l2(index.first);
278 const auto l_total = (l1 + l2);
279 // detector dimensions from the unscaled shape, as in ObjComponent::getWidth etc.
280 const auto &bbox = componentInfo.shape(index.first).getBoundingBox();
281 const auto scale = componentInfo.scaleFactor(index.first);
282 const double dx = (bbox.xMax() - bbox.xMin()) / scale.X();
283 const double dy = (bbox.yMax() - bbox.yMin()) / scale.Y();
284 const double dz = (bbox.zMax() - bbox.zMin()) / scale.Z();
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 }
290 });
291 term2Sq = term2Sq / numDet;
292 }
293
294 // resolution in angle - everything is in radians
295 double term3Sq;
296 if (m_divergenceWS) {
297 double deltathetaSq = m_divergenceWS->y(specNum)[0];
298 deltathetaSq = deltathetaSq * deltathetaSq;
299 // use the average angle for the spectrum - not right for focussed data
300 const double theta = spectrumInfo.isMonitor(specNum) ? 0.0 : 0.5 * spectrumInfo.twoTheta(specNum);
301 const double tan_theta = tan(theta);
302 term3Sq = (deltathetaSq + m_sourceDeltaThetaRadiansSq) / (tan_theta * tan_theta);
303 } else {
304 const double sourceTerm = m_sourceDeltaThetaRadiansSq;
305 const double solidAngle = std::accumulate(
306 spectrumDefinition.cbegin(), spectrumDefinition.cend(), 0.,
307 [&componentInfo, &samplepos, &detectorInfo, &sourceTerm](const auto sum, const auto &index) {
308 if (detectorInfo.isMasked(index.first) || detectorInfo.isMonitor(index.first)) {
309 return sum;
310 } else {
311 const double theta = 0.5 * detectorInfo.twoTheta(index.first); // angle of the pixel
312 const double cot_theta = 1. / tan(theta);
313 return sum + ((componentInfo.solidAngle(index.first, samplepos) + sourceTerm) * cot_theta * cot_theta);
314 }
315 });
316 term3Sq = solidAngle / numDet;
317 }
318
319 // add information to outputs
320 if (spectrumInfo.isMonitor(specNum)) {
321 m_resTof->mutableY(specNum) = 0.;
322 m_resPathLength->mutableY(specNum) = 0.;
323 m_resAngle->mutableY(specNum) = 0.;
324 m_outputWS->mutableY(specNum) = 0.;
325 } else { // not a monitor
326 const double resolution = sqrt(term1Sq + term2Sq + term3Sq);
327 m_resTof->mutableY(specNum) = sqrt(term1Sq);
328 m_resPathLength->mutableY(specNum) = sqrt(term2Sq);
329 m_resAngle->mutableY(specNum) = sqrt(term3Sq);
330 m_outputWS->mutableY(specNum) = resolution;
331 }
332
333 m_resTof->mutableX(specNum) = static_cast<double>(specNum);
334 m_resPathLength->mutableX(specNum) = static_cast<double>(specNum);
335 m_resAngle->mutableX(specNum) = static_cast<double>(specNum);
336 m_outputWS->mutableX(specNum) = static_cast<double>(specNum);
337
338 // update overall range for term3
339 minTerm3Sq = std::min(term3Sq, minTerm3Sq);
340 maxTerm3Sq = std::max(term3Sq, maxTerm3Sq);
341
342 // log extra information if level is debug (7) or greater - converting to strings is expensive
343 if (g_log.getLevelOffset() > 6) {
344 // central two-theta
345 const double twotheta = spectrumInfo.isMonitor(specNum) ? 0.0 : spectrumInfo.twoTheta(specNum);
346 // component type as reported by IDetector::type() for a single detector or a DetectorGroup
347 const std::string detType = spectrumInfo.hasUniqueDetector(specNum) ? "DetectorComponent" : "LogicalComponent";
348
349 g_log.debug() << detType << " " << specNum << "\t\t" << twotheta << "\t\tdT/T = " << sqrt(term1Sq)
350 << "\t\tdL/L = " << sqrt(term2Sq) << "\t\tdTheta*cotTheta = " << sqrt(term3Sq) << "\n";
351 }
352 }
353
354 g_log.notice() << "2theta range: " << (mintwotheta * Geometry::rad2deg) << ", " << (maxtwotheta * Geometry::rad2deg)
355 << "\n";
356 g_log.notice() << "t3 range: " << sqrt(minTerm3Sq) << ", " << sqrt(maxTerm3Sq) << "\n";
357}
358
359} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:547
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:426
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:95
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:54
@ Output
An output workspace.
Definition Property.h:55