Newer
Older
#include "MantidAlgorithms/PDFFourierTransform.h"
Federico Montesino Pouzols
committed
#include "MantidAPI/Axis.h"
Federico Montesino Pouzols
committed
#include "MantidAPI/WorkspaceFactory.h"
#include "MantidAPI/WorkspaceUnitValidator.h"
#include "MantidKernel/BoundedValidator.h"
#include "MantidKernel/ListValidator.h"
#include "MantidKernel/PhysicalConstants.h"
#include "MantidKernel/UnitFactory.h"
Federico Montesino Pouzols
committed
#include <sstream>
#include <math.h>
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
namespace Mantid {
namespace Algorithms {
using std::string;
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(PDFFourierTransform)
using namespace Mantid::Kernel;
using namespace Mantid::API;
namespace { // anonymous namespace
/// Crystalline PDF
const string BIG_G_OF_R("G(r)");
/// Liquids PDF
const string LITTLE_G_OF_R("g(r)");
/// Radial distribution function
const string RDF_OF_R("RDF(r)");
/// Normalized intensity
const string S_OF_Q("S(Q)");
/// Asymptotes to zero
const string S_OF_Q_MINUS_ONE("S(Q)-1");
/// Kernel of the Fourier transform
const string Q_S_OF_Q_MINUS_ONE("Q[S(Q)-1]");
}
//----------------------------------------------------------------------------------------------
/** Constructor
*/
PDFFourierTransform::PDFFourierTransform() {}
//----------------------------------------------------------------------------------------------
/** Destructor
*/
PDFFourierTransform::~PDFFourierTransform() {}
const std::string PDFFourierTransform::name() const {
return "PDFFourierTransform";
}
int PDFFourierTransform::version() const { return 1; }
const std::string PDFFourierTransform::category() const {
}
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
/** Initialize the algorithm's properties.
*/
void PDFFourierTransform::init() {
auto uv = boost::make_shared<API::WorkspaceUnitValidator>("MomentumTransfer");
declareProperty(make_unique<WorkspaceProperty<>>("InputWorkspace", "",
Direction::Input, uv),
S_OF_Q + ", " + S_OF_Q_MINUS_ONE + ", or " +
Q_S_OF_Q_MINUS_ONE);
declareProperty(make_unique<WorkspaceProperty<>>("OutputWorkspace", "",
Direction::Output),
"Result paired-distribution function");
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
// Set up input data type
std::vector<std::string> inputTypes;
inputTypes.push_back(S_OF_Q);
inputTypes.push_back(S_OF_Q_MINUS_ONE);
inputTypes.push_back(Q_S_OF_Q_MINUS_ONE);
declareProperty("InputSofQType", S_OF_Q,
boost::make_shared<StringListValidator>(inputTypes),
"To identify whether input function");
auto mustBePositive = boost::make_shared<BoundedValidator<double>>();
mustBePositive->setLower(0.0);
declareProperty(
"Qmin", EMPTY_DBL(), mustBePositive,
"Minimum Q in S(Q) to calculate in Fourier transform (optional).");
declareProperty(
"Qmax", EMPTY_DBL(), mustBePositive,
"Maximum Q in S(Q) to calculate in Fourier transform. (optional)");
declareProperty("Filter", false,
"Set to apply Lorch function filter to the input");
// Set up output data type
std::vector<std::string> outputTypes;
outputTypes.push_back(BIG_G_OF_R);
outputTypes.push_back(LITTLE_G_OF_R);
outputTypes.push_back(RDF_OF_R);
declareProperty("PDFType", BIG_G_OF_R,
boost::make_shared<StringListValidator>(outputTypes),
"Type of output PDF including G(r)");
declareProperty("DeltaR", EMPTY_DBL(), mustBePositive,
"Step size of r of G(r) to calculate. Default = "
":math:`\\frac{\\pi}{Q_{max}}`.");
declareProperty("Rmax", 20., mustBePositive,
"Maximum r for G(r) to calculate.");
declareProperty(
"rho0", EMPTY_DBL(), mustBePositive,
"Average number density used for g(r) and RDF(r) conversions (optional)");
string recipGroup("Reciprocal Space");
setPropertyGroup("InputSofQType", recipGroup);
setPropertyGroup("Qmin", recipGroup);
setPropertyGroup("Qmax", recipGroup);
setPropertyGroup("Filter", recipGroup);
string realGroup("Real Space");
setPropertyGroup("PDFType", realGroup);
setPropertyGroup("DeltaR", realGroup);
setPropertyGroup("Rmax", realGroup);
setPropertyGroup("rho0", realGroup);
}
std::map<string, string> PDFFourierTransform::validateInputs() {
std::map<string, string> result;
double Qmin = getProperty("Qmin");
double Qmax = getProperty("Qmax");
if ((!isEmpty(Qmin)) && (!isEmpty(Qmax)))
if (Qmax <= Qmin)
result["Qmax"] = "Must be greater than Qmin";
// check for null pointers - this is to protect against workspace groups
API::MatrixWorkspace_const_sptr inputWS = getProperty("InputWorkspace");
if (!inputWS) {
return result;
}
if (inputWS->getNumberHistograms() != 1) {
result["InputWorkspace"] = "Input workspace must have only one spectrum";
}
return result;
}
//----------------------------------------------------------------------------------------------
/** Execute the algorithm.
*/
void PDFFourierTransform::exec() {
// get input data
API::MatrixWorkspace_const_sptr inputWS = getProperty("InputWorkspace");
MantidVec inputQ = inputWS->dataX(0); // x for input
MantidVec inputDQ = inputWS->dataDx(0); // dx for input
MantidVec inputFOfQ = inputWS->dataY(0); // y for input
MantidVec inputDfOfQ = inputWS->dataE(0); // dy for input
if (inputDQ.empty())
inputDQ.assign(inputQ.size(), 0.);
// transform input data into Q/MomentumTransfer
const std::string inputXunit = inputWS->getAxis(0)->unit()->unitID();
if (inputXunit == "MomentumTransfer") {
// nothing to do
} else if (inputXunit == "dSpacing") {
// convert the x-units to Q/MomentumTransfer
std::transform(inputDQ.begin(), inputDQ.end(), inputQ.begin(),
inputDQ.begin(), std::divides<double>());
const double PI_2(2. * M_PI);
std::transform(inputQ.begin(), inputQ.end(), inputQ.begin(),
std::bind1st(std::divides<double>(), PI_2));
// reverse all of the arrays
std::reverse(inputQ.begin(), inputQ.end());
std::reverse(inputDQ.begin(), inputDQ.end());
std::reverse(inputFOfQ.begin(), inputFOfQ.end());
std::reverse(inputDfOfQ.begin(), inputDfOfQ.end());
} else {
std::stringstream msg;
msg << "Input data x-axis with unit \"" << inputXunit
<< "\" is not supported (use \"MomentumTransfer\" or \"dSpacing\")";
throw std::invalid_argument(msg.str());
}
g_log.debug() << "Input unit is " << inputXunit << "\n";
// convert from histogram to density
if (!inputWS->isHistogramData()) {
g_log.warning() << "This algorithm has not been tested on density data "
"(only on histograms)\n";
/* Don't do anything for now
double deltaQ;
for (size_t i = 0; i < inputFOfQ.size(); ++i)
deltaQ = inputQ[i+1] -inputQ[i];
inputFOfQ[i] = inputFOfQ[i]*deltaQ;
inputDfOfQ[i] = inputDfOfQ[i]*deltaQ; // TODO feels wrong
inputQ[i] += -.5*deltaQ;
inputDQ[i] += .5*(inputDQ[i] + inputDQ[i+1]); // TODO running average
inputQ.push_back(inputQ.back()+deltaQ);
inputDQ.push_back(inputDQ.back()); // copy last value
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
}
// convert to Q[S(Q)-1]
string soqType = getProperty("InputSofQType");
if (soqType == S_OF_Q) {
g_log.information() << "Subtracting one from all values\n";
// there is no error propagation for subtracting one
std::transform(inputFOfQ.begin(), inputFOfQ.end(), inputFOfQ.begin(),
std::bind2nd(std::minus<double>(), 1.));
soqType = S_OF_Q_MINUS_ONE;
}
if (soqType == S_OF_Q_MINUS_ONE) {
g_log.information() << "Multiplying all values by Q\n";
// convert the function
std::transform(inputFOfQ.begin(), inputFOfQ.end(), inputQ.begin(),
inputFOfQ.begin(), std::multiplies<double>());
soqType = Q_S_OF_Q_MINUS_ONE;
}
if (soqType != Q_S_OF_Q_MINUS_ONE) {
// should never get here
std::stringstream msg;
msg << "Do not understand InputSofQType = " << soqType;
throw std::runtime_error(msg.str());
}
// determine Q-range
double qmin = getProperty("Qmin");
double qmax = getProperty("Qmax");
if (isEmpty(qmin))
qmin = inputQ.front();
if (isEmpty(qmax))
qmax = inputQ.back();
// check Q-range and print information
if (qmin < inputQ.front()) {
g_log.information()
<< "Specified Qmin < range of data. Adjusting to data range.\n";
qmin = inputQ.front();
}
if (qmax > inputQ.back()) {
g_log.information()
<< "Specified Qmax > range of data. Adjusting to data range.\n";
}
g_log.debug() << "User specified Qmin = " << qmin
<< "Angstroms^-1 and Qmax = " << qmax << "Angstroms^-1\n";
// get pointers for the data range
size_t qmin_index;
size_t qmax_index;
{ // keep variable scope small
auto qmin_ptr = std::upper_bound(inputQ.begin(), inputQ.end(), qmin);
qmin_index = std::distance(inputQ.begin(), qmin_ptr);
if (qmin_index == 0)
qmin_index += 1; // so there doesn't have to be a check below
auto qmax_ptr = std::lower_bound(inputQ.begin(), inputQ.end(), qmax);
qmax_index = std::distance(inputQ.begin(), qmax_ptr);
}
size_t qmi_out = qmax_index;
if (qmi_out == inputQ.size())
qmi_out--; // prevent unit test problem under windows (and probably other
// hardly identified problem)
g_log.notice() << "Adjusting to data: Qmin = " << inputQ[qmin_index]
<< " Qmax = " << inputQ[qmi_out] << "\n";
// determine r axis for result
const double rmax = getProperty("RMax");
double rdelta = getProperty("DeltaR");
if (isEmpty(rdelta))
rdelta = M_PI / qmax;
size_t sizer = static_cast<size_t>(rmax / rdelta);
bool filter = getProperty("Filter");
// create the output workspace
API::MatrixWorkspace_sptr outputWS =
WorkspaceFactory::Instance().create("Workspace2D", 1, sizer, sizer);
outputWS->getAxis(0)->unit() = UnitFactory::Instance().create("Label");
Unit_sptr unit = outputWS->getAxis(0)->unit();
boost::shared_ptr<Units::Label> label =
boost::dynamic_pointer_cast<Units::Label>(unit);
label->setLabel("AtomicDistance", "Angstrom");
outputWS->setYUnitLabel("PDF");
outputWS->mutableRun().addProperty("Qmin", qmin, "Angstroms^-1", true);
outputWS->mutableRun().addProperty("Qmax", qmax, "Angstroms^-1", true);
MantidVec &outputR = outputWS->dataX(0);
for (size_t i = 0; i < sizer; i++) {
outputR[i] = rdelta * static_cast<double>(1 + i);
}
g_log.information() << "Using rmin = " << outputR.front()
<< "Angstroms and rmax = " << outputR.back()
<< "Angstroms\n";
// always calculate G(r) then convert
MantidVec &outputY = outputWS->dataY(0);
MantidVec &outputE = outputWS->dataE(0);
// do the math
for (size_t r_index = 0; r_index < sizer; r_index++) {
const double r = outputR[r_index];
double fs = 0;
double error = 0;
for (size_t q_index = qmin_index; q_index < qmax_index; q_index++) {
double q = inputQ[q_index];
double deltaq = inputQ[q_index] - inputQ[q_index - 1];
double sinus = sin(q * r) * deltaq;
// multiply by filter function sin(q*pi/qmax)/(q*pi/qmax)
if (filter && q != 0) {
sinus *= sin(q * rdelta) / (q * rdelta);
fs += sinus * inputFOfQ[q_index];
error +=
q * q * (sinus * inputDfOfQ[q_index]) * (sinus * inputDfOfQ[q_index]);
// g_log.debug() << "q[" << i << "] = " << q << " dq = " << deltaq << "
// S(q) =" << s;
// g_log.debug() << " d(gr) = " << temp << " gr = " << gr << std::endl;
// put the information into the output
outputY[r_index] = fs * 2 / M_PI;
outputE[r_index] = sqrt(error) * 2 / M_PI; // TODO: Wrong!
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
}
// convert to the correct form of PDF
string pdfType = getProperty("PDFType");
double rho0 = getProperty("rho0");
if (isEmpty(rho0)) {
const Kernel::Material &material = inputWS->sample().getMaterial();
double materialDensity = material.numberDensity();
if (!isEmpty(materialDensity) && materialDensity > 0)
rho0 = materialDensity;
else
rho0 = 1.;
// write out that it was reset if the value is coming into play
if (pdfType == LITTLE_G_OF_R || pdfType == RDF_OF_R)
g_log.information() << "Using rho0 = " << rho0 << "\n";
}
if (pdfType == BIG_G_OF_R) {
// nothing to do
} else if (pdfType == LITTLE_G_OF_R) {
const double factor = 1. / (4. * M_PI * rho0);
for (size_t i = 0; i < outputY.size(); ++i) {
// error propogation - assuming uncertainty in r = 0
outputE[i] = outputE[i] / outputR[i];
// transform the data
outputY[i] = 1. + factor * outputY[i] / outputR[i];
} else if (pdfType == RDF_OF_R) {
const double factor = 4. * M_PI * rho0;
for (size_t i = 0; i < outputY.size(); ++i) {
// error propogation - assuming uncertainty in r = 0
outputE[i] = outputE[i] * outputR[i];
// transform the data
outputY[i] = outputR[i] * outputY[i] + factor * outputR[i] * outputR[i];
}
} else {
// should never get here
std::stringstream msg;
msg << "Do not understand PDFType = " << pdfType;
throw std::runtime_error(msg.str());
}
// set property
setProperty("OutputWorkspace", outputWS);
}
} // namespace Mantid
} // namespace Algorithms