-
Notifications
You must be signed in to change notification settings - Fork 122
/
PhaseQuadMuon.cpp
510 lines (428 loc) · 15.8 KB
/
PhaseQuadMuon.cpp
1
2
3
4
5
6
7
8
9
10
11
12
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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
195
196
197
198
199
200
201
202
203
204
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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
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
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidAlgorithms/PhaseQuadMuon.h"
#include "MantidAPI/FileProperty.h"
#include "MantidAPI/IFileLoader.h"
#include "MantidAPI/TableRow.h"
#include "MantidAPI/FrameworkManager.h"
namespace Mantid
{
namespace Algorithms
{
using namespace Kernel;
using API::Progress;
// Register the class into the algorithm factory
DECLARE_ALGORITHM(PhaseQuadMuon)
/** Initialisation method. Declares properties to be used in algorithm.
*
*/
void PhaseQuadMuon::init()
{
declareProperty(new API::WorkspaceProperty<API::MatrixWorkspace>("InputWorkspace", "", Direction::Input),
"Name of the input workspace containing the spectra");
declareProperty(new API::WorkspaceProperty<API::MatrixWorkspace>("OutputWorkspace", "", Direction::Output),
"Name of the output workspace to hold squashograms" );
declareProperty(new API::WorkspaceProperty<API::ITableWorkspace>("PhaseTable", "", Direction::Input, API::PropertyMode::Optional),
"Name of the Phase Table");
declareProperty(new PropertyWithValue<int>("PulseOver", 60, Direction::Input),
"Bin number when the pulse is over. Mandatory if PhaseTable is provided");
declareProperty(new PropertyWithValue<double>("MeanLag", 127.702, Direction::Input),
"Average Lag value over detectors");
declareProperty(new PropertyWithValue<bool>("DoublePulse", false, Direction::Input),
"If signal is double-pulse");
declareProperty(new API::FileProperty("PhaseList", "", API::FileProperty::OptionalLoad, ".INF", Direction::Input),
"A space-delimited text file with a six-row header");
}
/** Executes the algorithm
*
*/
void PhaseQuadMuon::exec()
{
// Get input workspace
API::MatrixWorkspace_sptr inputWs = getProperty("InputWorkspace");
// Get input phase table
API::ITableWorkspace_sptr phaseTable = getProperty("PhaseTable");
// Get input phase list
std::string filename = getPropertyValue("PhaseList");
// Set number of histograms
m_nHist = static_cast<int>(inputWs->getNumberHistograms());
// Set number of data points per histogram
m_nData = static_cast<int>(inputWs->getSpectrum(0)->readY().size());
// Not necessary as we will be converting back to micro secs
// Just to make sure that the code is doing exactly the same as the VAX c code
// BUT the method also shifts the spectra, and we definitely need this shift
convertToNanoSecs(inputWs);
// Set time resolution
m_res = inputWs->getSpectrum(0)->readX()[1] - inputWs->getSpectrum(0)->readX()[0];
// Create a deadTimeTable to apply deadtime corrections. It will be filled by loadPhaseTable or loadPhaseList
API::ITableWorkspace_sptr deadTimeTable = API::WorkspaceFactory::Instance().createTable();
deadTimeTable->addColumn("int","Spectrum no");
deadTimeTable->addColumn("double","Deadtime");
// Check that either phaseTable or PhaseList has been provided
if ( phaseTable )
{
loadPhaseTable(phaseTable, deadTimeTable);
// If phaseTable was supplied, get m_tPulseOver, m_meanLag
m_tPulseOver = getProperty("PulseOver");
m_meanLag = getProperty("MeanLag");
}
else if ( filename != "" )
{
loadPhaseList ( filename, deadTimeTable );
}
else
{
throw std::runtime_error("PhaseQuad: You must provide either PhaseTable or PhaseList");
}
// Check if signal is double-pulse and set m_tPulseOver accordingly
m_isDouble = getProperty("DoublePulse");
double a= m_meanLag + m_pulseTail;
if (m_isDouble)
{
a += m_pulseTwo*1000;
}
a/=m_res;
m_tPulseOver = static_cast<int>(a);
// Create temporary workspace to perform operations on it
API::MatrixWorkspace_sptr tempWs = boost::dynamic_pointer_cast<API::MatrixWorkspace>(
API::WorkspaceFactory::Instance().create("Workspace2D", m_nHist, m_nData+1, m_nData));
// Apply deadtime corrections and store results in tempWs
deadTimeCorrection(inputWs,deadTimeTable,tempWs);
// Create output workspace with two spectra (squashograms)
API::MatrixWorkspace_sptr outputWs = boost::dynamic_pointer_cast<API::MatrixWorkspace>(
API::WorkspaceFactory::Instance().create("Workspace2D", 2, m_nData+1, m_nData));
outputWs->getAxis(0)->unit() = inputWs->getAxis(0)->unit();
// Rescale detector efficiency to maximum value
normaliseAlphas(m_histData);
// Remove exponential decay and save results into tempWs
loseExponentialDecay(tempWs);
// Compute squashograms
squash (tempWs, outputWs);
// Regain exponential decay
regainExponential(outputWs);
// Convert and shift output and input workspaces
convertToMicroSecs(inputWs);
convertToMicroSecs(outputWs);
setProperty("OutputWorkspace", outputWs);
}
//----------------------------------------------------------------------------------------------
/** Convert X units from micro-secs to nano-secs and shift to start at t=0
* @param inputWs :: [input/output] workspace to convert
*/
void PhaseQuadMuon::convertToNanoSecs (API::MatrixWorkspace_sptr inputWs)
{
for (int h=0; h<inputWs->getNumberHistograms(); h++)
{
auto spec = inputWs->getSpectrum(h);
m_tMin = spec->dataX()[0];
for (int t=0; t<m_nData+1; t++)
{
spec->dataX()[t] = ( spec->dataX()[t] - m_tMin ) * 1000;
}
}
}
//----------------------------------------------------------------------------------------------
/** Convert X units from nano-secs to micro-secs and shift to start at m_tMin
* @param inputWs :: [input/output] workspace to convert
*/
void PhaseQuadMuon::convertToMicroSecs (API::MatrixWorkspace_sptr inputWs)
{
for (size_t h=0; h<inputWs->getNumberHistograms(); h++)
{
auto spec = inputWs->getSpectrum(h);
for (int t=0; t<m_nData+1; t++)
{
spec->dataX()[t] = spec->dataX()[t]/1000+m_tMin;
}
}
}
//----------------------------------------------------------------------------------------------
/** Apply dead time correction to spectra in inputWs and create temporary workspace with corrected spectra
* @param inputWs :: [input] input workspace containing spectra to correct
* @param deadTimeTable :: [input] table containing dead times
* @param tempWs :: [output] workspace containing corrected spectra
*/
void PhaseQuadMuon::deadTimeCorrection(API::MatrixWorkspace_sptr inputWs, API::ITableWorkspace_sptr deadTimeTable, API::MatrixWorkspace_sptr& tempWs)
{
// Apply correction only from t = m_tPulseOver
// To do so, we first apply corrections to the whole spectrum (ApplyDeadTimeCorr
// does not allow to select a range in the spectrum)
// Then we recover counts from 0 to m_tPulseOver
auto alg = this->createChildAlgorithm("ApplyDeadTimeCorr",-1,-1);
alg->initialize();
alg->setProperty("DeadTimeTable", deadTimeTable);
alg->setPropertyValue("InputWorkspace", inputWs->getName());
alg->setPropertyValue("OutputWorkspace", inputWs->getName()+"_deadtime");
bool sucDeadTime = alg->execute();
if (!sucDeadTime)
{
g_log.error() << "PhaseQuad: Unable to apply dead time corrections" << std::endl;
throw std::runtime_error("PhaseQuad: Unable to apply dead time corrections");
}
tempWs = alg->getProperty("OutputWorkspace");
// Now recover counts from t=0 to m_tPulseOver
// Errors are set to m_bigNumber
for (int h=0; h<m_nHist; h++)
{
auto specOld = inputWs->getSpectrum(h);
auto specNew = tempWs->getSpectrum(h);
for (int t=0; t<m_tPulseOver; t++)
{
specNew->dataY()[t] = specOld->dataY()[t];
specNew->dataE()[t] = m_bigNumber;
}
}
}
//----------------------------------------------------------------------------------------------
/** Load PhaseTable file to a vector of HistData.
* @param phaseTable :: [input] phase table containing detector info
* @param deadTimeTable :: [output] phase table containing dead times
*/
void PhaseQuadMuon::loadPhaseTable(API::ITableWorkspace_sptr phaseTable, API::ITableWorkspace_sptr deadTimeTable)
{
if ( phaseTable->rowCount() )
{
if ( phaseTable->columnCount()<4 )
{
throw std::invalid_argument("PhaseQuad: PhaseTable must contain at least four columns");
}
// Check number of histograms in inputWs match number of detectors in phase table
if (m_nHist != static_cast<int>(phaseTable->rowCount()))
{
throw std::runtime_error("PhaseQuad: Number of histograms in phase table does not match number of spectra in workspace");
}
for (size_t i=0; i<phaseTable->rowCount(); ++i)
{
API::TableRow phaseRow = phaseTable->getRow(i);
// The first three columns go to m_histData
HistData tempHist;
tempHist.detOK = phaseRow.Bool(0);
tempHist.alpha = phaseRow.Double(1);
tempHist.phi = phaseRow.Double(2);
m_histData.push_back(tempHist);
// The last column goes to deadTimeTable
API::TableRow deadRow = deadTimeTable->appendRow();
deadRow << static_cast<int>(i)+1 << phaseRow.Double(3);
}
}
else
{
throw std::invalid_argument("PhaseQuad: PhaseTable is empty");
}
}
//----------------------------------------------------------------------------------------------
/** Load PhaseList file to a vector of HistData and a deadTimeTable.
* @param filename :: [input] phase list .inf filename
* @param deadTimeTable :: [output] table containing dead times
*/
void PhaseQuadMuon::loadPhaseList(const std::string& filename, API::ITableWorkspace_sptr deadTimeTable )
{
std::ifstream input(filename.c_str(), std::ios_base::in);
if (input.is_open())
{
if ( input.eof() )
{
throw Exception::FileError("PhaseQuad: File is empty.", filename);
}
else
{
std::string line;
// Header of .INF file is as follows:
//
// Comment on the output file
// Top row of numbers are:
// #histos, typ. first good bin#, typ. bin# when pulse over, mean lag.
// Tabulated numbers are, per histogram:
// det ok, asymmetry, phase, lag, deadtime_c, deadtime_m.
//
std::getline( input, line ); // Skip first line in header
std::getline( input, line ); // Skip second line
std::getline( input, line ); // ...
std::getline( input, line );
std::getline( input, line );
// Read first useful line
int nHist;
input >> nHist >> m_tValid >> m_tPulseOver >> m_meanLag;
if (m_nHist!=nHist)
{
throw std::runtime_error("PhaseQuad: Number of histograms in phase list does not match number of spectra in workspace");
}
// Read histogram data
int cont=0;
HistData tempData;
double lag, dead, deadm;
while( input >> tempData.detOK >> tempData.alpha >> tempData.phi >> lag >> dead >> deadm )
{
m_histData.push_back (tempData);
cont++;
// Add dead time to deadTimeTable
API::TableRow row = deadTimeTable->appendRow();
row << cont << dead;
}
if ( cont != m_nHist )
{
if ( cont < m_nHist )
{
throw Exception::FileError("PhaseQuad: Lines missing in phase list", filename);
}
else
{
throw Exception::FileError("PhaseQuad: Extra lines in phase list", filename);
}
}
}
}
else
{
// Throw exception if file cannot be opened
throw std::runtime_error("PhaseQuad: Unable to open PhaseList");
}
}
//----------------------------------------------------------------------------------------------
/** Rescale detector efficiencies to maximum value.
* @param histData :: vector of HistData containing detector efficiencies
*/
void PhaseQuadMuon::normaliseAlphas (std::vector<HistData>& histData)
{
double max=0;
for (int h=0; h<m_nHist; h++)
{
if (histData[h].alpha>max)
{
max = histData[h].alpha;
}
}
if ( !max )
{
throw std::runtime_error("PhaseQuad: Could not rescale efficiencies");
}
for (int h=0; h<m_nHist; h++)
{
histData[h].alpha /= max;
}
}
//----------------------------------------------------------------------------------------------
/** Remove exponential decay from input histograms, i.e., calculate asymmetry
* @param tempWs :: workspace containing the spectra to remove exponential from
*/
void PhaseQuadMuon::loseExponentialDecay (API::MatrixWorkspace_sptr tempWs)
{
for (size_t h=0; h<tempWs->getNumberHistograms(); h++)
{
auto specIn = tempWs->getSpectrum(h);
MantidVec outX, outY, outE;
MantidVec inX, inY, inE;
inX = specIn->readX();
inY = specIn->readY();
inE = specIn->readE();
outX= specIn->readX();
outY= specIn->readY();
outE= specIn->readE();
for(int i=0; i<m_nData; i++)
{
double usey = specIn->readY()[i];
double oops = ( (usey<=0) || (specIn->readE()[i]>=m_bigNumber));
outY[i] = oops ? 0 : log(usey);
outE[i] = oops ? m_bigNumber : specIn->readE()[i]/usey;
}
double s, sx, sy, sig;
s = sx = sy =0;
for (int i=0; i<m_nData; i++)
{
sig = outE[i]*outE[i];
s += 1./sig;
sx+= outX[i]/sig;
sy+= outY[i]/sig;
}
double N0 = (sy+sx/m_muLife/1000)/s;
N0=exp(N0);
m_histData[h].n0=N0;
for (int i=0; i<m_nData; i++)
{
specIn->dataY()[i] = inY[i] - N0 *exp(-outX[i]/m_muLife/1000);
if (i<m_tPulseOver )
{
specIn->dataE()[i] = m_bigNumber;
}
else
{
specIn->dataE()[i] = ( inY[i] > m_poissonLim) ? inE[i] : sqrt(N0*exp(-outX[i]/m_muLife/1000));
}
}
} // Histogram loop
}
//----------------------------------------------------------------------------------------------
/** Compute Squashograms
* @param tempWs :: input workspace containing the asymmetry in the lab frame
* @param outputWs :: output workspace to hold squashograms
*/
void PhaseQuadMuon::squash(const API::MatrixWorkspace_sptr tempWs, API::MatrixWorkspace_sptr outputWs)
{
double sxx=0;
double syy=0;
double sxy=0;
for (int h=0; h<m_nHist; h++)
{
auto data = m_histData[h];
double X = data.n0 * data.alpha * cos(data.phi);
double Y = data.n0 * data.alpha * sin(data.phi);
sxx += X*X;
syy += Y*Y;
sxy += X*Y;
}
double lam1 = 2 * syy / (sxx*syy - sxy*sxy);
double mu1 = 2 * sxy / (sxy*sxy - sxx*syy);
double lam2 = 2 * sxy / (sxy*sxy - sxx*syy);
double mu2 = 2 * sxx / (sxx*syy - sxy*sxy);
std::vector<double> aj, bj;
for (int h=0; h<m_nHist; h++)
{
auto data = m_histData[h];
double X = data.n0 * data.alpha * cos(data.phi);
double Y = data.n0 * data.alpha * sin(data.phi);
aj.push_back( (lam1 * X + mu1 * Y)*0.5 );
bj.push_back( (lam2 * X + mu2 * Y)*0.5 );
}
std::vector<double> data1(m_nData,0), data2(m_nData,0);
std::vector<double> sigm1(m_nData,0), sigm2(m_nData,0);
for (int i=0; i<m_nData; i++)
{
for (int h=0; h<m_nHist; h++)
{
auto spec = tempWs->getSpectrum(h);
data1[i] += aj[h] * spec->readY()[i];
data2[i] += bj[h] * spec->readY()[i];
sigm1[i] += aj[h]*aj[h] * spec->readE()[i] * spec->readE()[i];
sigm2[i] += bj[h]*bj[h] * spec->readE()[i] * spec->readE()[i];
}
sigm1[i] = sqrt(sigm1[i]);
sigm2[i] = sqrt(sigm2[i]);
}
outputWs->getSpectrum(0)->dataX() = tempWs->getSpectrum(0)->readX();
outputWs->getSpectrum(0)->dataY() = data1;
outputWs->getSpectrum(0)->dataE() = sigm1;
outputWs->getSpectrum(1)->dataX() = tempWs->getSpectrum(1)->readX();
outputWs->getSpectrum(1)->dataY() = data2;
outputWs->getSpectrum(1)->dataE() = sigm2;
}
//----------------------------------------------------------------------------------------------
/** Put back in exponential decay
* @param outputWs :: output workspace with squashograms to update
*/
void PhaseQuadMuon::regainExponential(API::MatrixWorkspace_sptr outputWs)
{
auto specRe = outputWs->getSpectrum(0);
auto specIm = outputWs->getSpectrum(1);
for (int i=0; i<m_nData; i++)
{
double x = outputWs->getSpectrum(0)->readX()[i];
double e = exp(-x/m_muLife/1000);
specRe->dataY()[i] /= e;
specIm->dataY()[i] /= e;
specRe->dataE()[i] /= e;
specIm->dataE()[i] /= e;
}
}
}
}