forked from npshub/mantid
-
Notifications
You must be signed in to change notification settings - Fork 0
/
CalculateMuonAsymmetry.cpp
407 lines (356 loc) · 17.2 KB
/
CalculateMuonAsymmetry.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
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source,
// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
// SPDX - License - Identifier: GPL - 3.0 +
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidMuon/CalculateMuonAsymmetry.h"
#include "MantidMuon/MuonAsymmetryHelper.h"
#include "MantidAPI/ADSValidator.h"
#include "MantidAPI/AlgorithmManager.h"
#include "MantidAPI/AnalysisDataService.h"
#include "MantidAPI/FuncMinimizerFactory.h"
#include "MantidAPI/FunctionProperty.h"
#include "MantidAPI/IFuncMinimizer.h"
#include "MantidAPI/IFunction.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/MultiDomainFunction.h"
#include "MantidAPI/Run.h"
#include "MantidKernel/ArrayOrderedPairsValidator.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidKernel/BoundedValidator.h"
#include "MantidKernel/ListValidator.h"
#include "MantidKernel/StartsWithValidator.h"
#include "MantidMuon/MuonAlgorithmHelper.h"
#include <cmath>
#include <numeric>
#include <vector>
namespace Mantid::Algorithms {
using namespace Kernel;
using std::size_t;
// Register the class into the algorithm factory
DECLARE_ALGORITHM(CalculateMuonAsymmetry)
/** Initialisation method. Declares properties to be used in algorithm.
*
*/
void CalculateMuonAsymmetry::init() {
// norm table to update
declareProperty(std::make_unique<API::WorkspaceProperty<API::ITableWorkspace>>(
"NormalizationTable", "", Direction::Input, API::PropertyMode::Optional),
"Name of the table containing the normalizations for the asymmetries.");
// list of uNonrm workspaces to fit to
declareProperty(std::make_unique<Kernel::ArrayProperty<std::string>>("UnNormalizedWorkspaceList",
std::make_shared<API::ADSValidator>()),
"An ordered list of workspaces (to get the initial values "
"for the normalizations).");
// list of workspaces to output renormalized result to
declareProperty(std::make_unique<Kernel::ArrayProperty<std::string>>("ReNormalizedWorkspaceList",
std::make_shared<API::ADSValidator>()),
"An ordered list of workspaces (to get the initial values "
"for the normalizations).");
declareProperty("OutputFitWorkspace", "fit", "The name of the output fit workspace.");
declareProperty("StartX", 0.1, "The lower limit for calculating the asymmetry (an X value).");
declareProperty("EndX", 15.0, "The upper limit for calculating the asymmetry (an X value).");
declareProperty(
std::make_unique<ArrayProperty<double>>("Exclude", std::make_shared<ArrayOrderedPairsValidator<double>>()),
"A list of pairs of real numbers, defining the regions to "
"exclude from the fit for all spectra.");
declareProperty(std::make_unique<API::FunctionProperty>("InputFunction"), "The fitting function to be converted.");
std::vector<std::string> minimizerOptions = API::FuncMinimizerFactory::Instance().getKeys();
Kernel::IValidator_sptr minimizerValidator = std::make_shared<Kernel::StartsWithValidator>(minimizerOptions);
declareProperty("Minimizer", "Levenberg-MarquardtMD", minimizerValidator, "Minimizer to use for fitting.");
auto mustBePositive = std::make_shared<Kernel::BoundedValidator<int>>();
mustBePositive->setLower(0);
declareProperty("MaxIterations", 500, mustBePositive->clone(),
"Stop after this number of iterations if a good fit is not found");
declareProperty("OutputStatus", "", Kernel::Direction::Output);
declareProperty("ChiSquared", 0.0, Kernel::Direction::Output);
declareProperty(std::make_unique<API::FunctionProperty>("OutputFunction", Kernel::Direction::Output),
"The fitting function after fit.");
declareProperty("EnableDoublePulse", false, "Controls whether to perform a double pulse or single pulse fit.");
declareProperty("PulseOffset", 0.0, "The time offset between the two pulses");
declareProperty("FirstPulseWeight", 0.5,
"Weighting of first pulse (w_1)."
"The second pulse weighting (w_1) is set as w_2 = 1 - w_1.");
}
/*
* Validate the input parameters
* @returns map with keys corresponding to properties with errors and values
* containing the error messages.
*/
std::map<std::string, std::string> CalculateMuonAsymmetry::validateInputs() {
// create the map
std::map<std::string, std::string> validationOutput;
// check start and end times
double startX = getProperty("StartX");
double endX = getProperty("EndX");
if (startX > endX) {
validationOutput["StartX"] = "Start time is after the end time.";
} else if (startX == endX) {
validationOutput["StartX"] = "Start and end times are equal, there is no "
"data to apply the algorithm to.";
}
// check inputs
std::vector<std::string> unnormWS = getProperty("UnNormalizedWorkspaceList");
std::vector<std::string> normWS = getProperty("ReNormalizedWorkspaceList");
if (normWS.size() != unnormWS.size()) {
validationOutput["ReNormalizedWorkspaceList"] = "The ReNormalizedWorkspaceList and UnNormalizedWorkspaceList must "
"contain the same number of workspaces.";
}
API::IFunction_sptr tmp = getProperty("InputFunction");
auto function = std::dynamic_pointer_cast<API::CompositeFunction>(tmp);
if (function->getNumberDomains() != normWS.size()) {
validationOutput["InputFunction"] = "The Fitting function does not have "
"the same number of domains as the "
"number of domains to fit.";
}
// check norm table is correct -> move this to helper when move muon algs to
// muon folder
API::ITableWorkspace_const_sptr tabWS = getProperty("NormalizationTable");
if (tabWS) {
if (tabWS->columnCount() == 0) {
validationOutput["NormalizationTable"] = "Please provide a non-empty NormalizationTable.";
}
// NormalizationTable should have three columns: (norm, name, method)
if (tabWS->columnCount() != 3) {
validationOutput["NormalizationTable"] = "NormalizationTable must have three columns";
}
auto names = tabWS->getColumnNames();
int normCount = 0;
int wsNamesCount = 0;
for (const std::string &name : names) {
if (name == "norm") {
normCount += 1;
}
if (name == "name") {
wsNamesCount += 1;
}
}
if (normCount == 0) {
validationOutput["NormalizationTable"] = "NormalizationTable needs norm column";
}
if (wsNamesCount == 0) {
validationOutput["NormalizationTable"] = "NormalizationTable needs a name column";
}
if (normCount > 1) {
validationOutput["NormalizationTable"] = "NormalizationTable has " + std::to_string(normCount) + " norm columns";
}
if (wsNamesCount > 1) {
validationOutput["NormalizationTable"] =
"NormalizationTable has " + std::to_string(wsNamesCount) + " name columns";
}
}
return validationOutput;
}
/** Executes the algorithm
*
*/
void CalculateMuonAsymmetry::exec() {
const std::vector<std::string> wsNamesUnNorm = getProperty("UnNormalizedWorkspaceList");
std::vector<std::string> wsNames = getProperty("reNormalizedWorkspaceList");
// get new norm
std::vector<double> norms = getNormConstants(wsNamesUnNorm); // this will do the fit
auto containsZeros = std::any_of(norms.begin(), norms.end(), [](double value) { return value == 0.0; });
if (containsZeros) {
setProperty("OutputStatus", "Aborted, a normalization constant was zero");
g_log.error("Got a zero for the normalization, aborting algorithm.");
return;
}
// update the ws to new norm
for (size_t j = 0; j < wsNames.size(); j++) {
API::MatrixWorkspace_sptr ws =
API::AnalysisDataService::Instance().retrieveWS<API::MatrixWorkspace>(wsNamesUnNorm[j]);
API::MatrixWorkspace_sptr normWS =
API::AnalysisDataService::Instance().retrieveWS<API::MatrixWorkspace>(wsNames[j]);
normalizeWorkspace(normWS, ws, 0, norms[j]);
API::AnalysisDataService::Instance().addOrReplace(normWS->getName(), normWS);
MuonAlgorithmHelper::addSampleLog(normWS, "analysis_asymmetry_norm", std::to_string(norms[j]));
}
addNormalizedFits(wsNames.size(), norms);
// update table with new norm
std::vector<std::string> methods(wsNames.size(), "Calculated");
API::ITableWorkspace_sptr table = getProperty("NormalizationTable");
if (table) {
updateNormalizationTable(table, wsNames, norms, methods);
}
}
/**
* Adds a normalised fit as the last spectra in the fitted workspace.
* @param numberOfFits :: The number of simultaneous fits performed
* @param norms :: a vector of normalisation values
* @return normalization constants
*/
void CalculateMuonAsymmetry::addNormalizedFits(size_t numberOfFits, const std::vector<double> &norms) {
for (size_t j = 0; j < numberOfFits; j++) {
API::Workspace_sptr fitWorkspace = getProperty("OutputWorkspace");
API::MatrixWorkspace_sptr fitWorkspaceActual;
if (fitWorkspace->isGroup()) {
fitWorkspaceActual = std::dynamic_pointer_cast<API::MatrixWorkspace>(
std::static_pointer_cast<API::WorkspaceGroup>(fitWorkspace)->getItem(0));
} else {
fitWorkspaceActual = std::dynamic_pointer_cast<API::MatrixWorkspace>(fitWorkspace);
}
API::IAlgorithm_sptr extractSpectra = createChildAlgorithm("ExtractSingleSpectrum");
API::IAlgorithm_sptr appendSpectra = createChildAlgorithm("AppendSpectra");
extractSpectra->setProperty("InputWorkspace", fitWorkspaceActual);
extractSpectra->setProperty("WorkspaceIndex", 1);
extractSpectra->execute();
API::MatrixWorkspace_sptr unnormalisedFit = extractSpectra->getProperty("OutputWorkspace");
normalizeWorkspace(unnormalisedFit, fitWorkspaceActual, 1, norms[j]);
appendSpectra->setProperty("InputWorkspace1", fitWorkspaceActual);
appendSpectra->setProperty("InputWorkspace2", unnormalisedFit);
appendSpectra->execute();
API::MatrixWorkspace_sptr appendedFitWorkspace = appendSpectra->getProperty("OutputWorkspace");
if (fitWorkspace->isGroup()) {
std::string workspaceName = fitWorkspaceActual->getName();
auto fitWorkspaceGroupPointer = std::static_pointer_cast<API::WorkspaceGroup>(fitWorkspace);
fitWorkspaceGroupPointer->removeItem(0);
API::AnalysisDataService::Instance().addOrReplace(workspaceName, appendedFitWorkspace);
fitWorkspaceGroupPointer->addWorkspace(appendedFitWorkspace);
} else {
setProperty("OutputWorkspace", appendedFitWorkspace);
}
}
}
/**
* Calculate normalization constant after the exponential decay has been removed
* to a linear fitting function
* @param wsNames :: names of workspaces to fit to
* @return normalization constants
*/
std::vector<double> CalculateMuonAsymmetry::getNormConstants(const std::vector<std::string> &wsNames) {
std::vector<double> norms;
double startX = getProperty("StartX");
double endX = getProperty("EndX");
std::string exclude = getProperty("Exclude");
int maxIterations = getProperty("MaxIterations");
auto minimizer = getProperty("Minimizer");
API::IAlgorithm_sptr fit;
auto doublePulseEnabled = getProperty("EnableDoublePulse");
if (doublePulseEnabled) {
double pulseOffset = getProperty("PulseOffset");
double firstPulseWeight = getProperty("FirstPulseWeight");
fit = createChildAlgorithm("DoublePulseFit");
fit->initialize();
fit->setProperty("PulseOffset", pulseOffset);
fit->setProperty("FirstPulseWeight", firstPulseWeight);
fit->setProperty("SecondPulseWeight", 1.0 - firstPulseWeight);
} else {
fit = createChildAlgorithm("Fit");
fit->initialize();
}
API::IFunction_sptr function = getProperty("InputFunction");
fit->setProperty("Function", function);
fit->setProperty("MaxIterations", maxIterations);
fit->setPropertyValue("Minimizer", minimizer);
fit->setProperty("CreateOutput", true);
std::string output = getPropertyValue("OutputFitWorkspace");
fit->setProperty("Output", output);
fit->setProperty("InputWorkspace", wsNames[0]);
fit->setProperty("StartX", startX);
fit->setProperty("EndX", endX);
fit->setProperty("Exclude", exclude);
fit->setProperty("WorkspaceIndex", 0);
if (wsNames.size() > 1) {
for (size_t j = 1; j < wsNames.size(); j++) {
std::string suffix = boost::lexical_cast<std::string>(j);
fit->setPropertyValue("InputWorkspace_" + suffix, wsNames[j]);
fit->setProperty("WorkspaceIndex_" + suffix, 0);
fit->setProperty("StartX_" + suffix, startX);
fit->setProperty("EndX_" + suffix, endX);
}
}
fit->execute();
auto status = fit->getPropertyValue("OutputStatus");
setProperty("OutputStatus", status);
double chi2 = std::stod(fit->getPropertyValue("OutputChi2overDoF"));
setProperty("ChiSquared", chi2);
API::IFunction_sptr tmp = fit->getProperty("Function");
setProperty("OutputFunction", tmp);
API::ITableWorkspace_sptr parameterTable = fit->getProperty("OutputParameters");
API::Workspace_sptr outputWorkspace;
if (wsNames.size() > 1) {
API::WorkspaceGroup_sptr outputWorkspaceGroup = fit->getProperty("OutputWorkspace");
outputWorkspace = outputWorkspaceGroup;
} else {
API::MatrixWorkspace_sptr outputWorkspaceMatrix = fit->getProperty("OutputWorkspace");
outputWorkspace = outputWorkspaceMatrix;
}
API::ITableWorkspace_sptr outputNormalisedCovarianceMatrix = fit->getProperty("OutputNormalisedCovarianceMatrix");
declareProperty(
std::make_unique<API::WorkspaceProperty<API::ITableWorkspace>>("OutputParameters", "", Kernel::Direction::Output),
"The name of the TableWorkspace in which to store the "
"final fit parameters");
setPropertyValue("OutputParameters", output + "_Parameters");
declareProperty(
std::make_unique<API::WorkspaceProperty<API::Workspace>>("OutputWorkspace", "", Kernel::Direction::Output),
"The name of the matrix in which to store the "
"final fit results");
if (outputWorkspace->isGroup()) {
setPropertyValue("OutputWorkspace", output + "_Workspaces");
} else {
setPropertyValue("OutputWorkspace", output + "_Workspace");
}
declareProperty(std::make_unique<API::WorkspaceProperty<API::ITableWorkspace>>("OutputNormalisedCovarianceMatrix", "",
Kernel::Direction::Output),
"The name of the TableWorkspace in which to store the final covariance "
"matrix");
setPropertyValue("OutputNormalisedCovarianceMatrix", output + "_NormalisedCovarianceMatrix");
setProperty("OutputParameters", parameterTable);
setProperty("OutputWorkspace", outputWorkspace);
setProperty("OutputNormalisedCovarianceMatrix", outputNormalisedCovarianceMatrix);
try {
if (wsNames.size() == 1) {
// N(1+g) + exp
auto TFFunc = std::dynamic_pointer_cast<API::CompositeFunction>(tmp);
norms.emplace_back(getNormValue(TFFunc));
} else {
auto result = std::dynamic_pointer_cast<API::MultiDomainFunction>(tmp);
for (size_t j = 0; j < wsNames.size(); j++) {
// get domain
auto TFFunc = std::dynamic_pointer_cast<API::CompositeFunction>(result->getFunction(j));
// N(1+g) + exp
TFFunc = std::dynamic_pointer_cast<API::CompositeFunction>(TFFunc);
norms.emplace_back(getNormValue(TFFunc));
}
}
} catch (...) {
throw std::invalid_argument("The fitting function is not of the expected "
"form. Try using "
"ConvertFitFunctionForMuonTFAsymmetry");
}
return norms;
}
/**
* Gets the normalization from a fitting function
* @param func :: fittef function
* @return normalization constant
*/
double CalculateMuonAsymmetry::getNormValue(API::CompositeFunction_sptr &func) {
// getFunction(0) -> N(1+g)
auto TFFunc = std::dynamic_pointer_cast<API::CompositeFunction>(func->getFunction(0));
// getFunction(0) -> N
auto flat = TFFunc->getFunction(0);
return flat->getParameter("A0");
}
/**
* Normalize a single spectrum workpace based on a specified index from another
* workspace and a N0 value. The unormalized from here is N0(1+f) where f is the
* desired normalized function.
* @param normalizedWorkspace :: workspace to store normalised data
* @param unormalizedWorkspace :: workspace to normalise from
* @param workspaceIndex :: index of raw data in unormalizedWorkspace
* @param N0 :: normalization value
* @return normalization constant
*/
void CalculateMuonAsymmetry::normalizeWorkspace(const API::MatrixWorkspace_sptr &normalizedWorkspace,
const API::MatrixWorkspace_const_sptr &unnormalizedWorkspace,
size_t workspaceIndex, double N0) {
normalizedWorkspace->mutableY(0) = unnormalizedWorkspace->y(workspaceIndex) / N0;
normalizedWorkspace->mutableY(0) -= 1.0;
normalizedWorkspace->mutableE(0) = unnormalizedWorkspace->e(workspaceIndex) / N0;
}
} // namespace Mantid::Algorithms