diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/IqtFitSequential.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/IqtFitSequential.py deleted file mode 100644 index 4429717507c7fa10a13723c5a4e4f7c2e7cee05e..0000000000000000000000000000000000000000 --- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/IqtFitSequential.py +++ /dev/null @@ -1,247 +0,0 @@ -from __future__ import (absolute_import, division, print_function) - -from mantid import logger, AlgorithmFactory -from mantid.api import * -from mantid.kernel import * -import mantid.simpleapi as ms - - -class IqtFitSequential(PythonAlgorithm): - _input_ws = None - _function = None - _fit_type = None - _start_x = None - _end_x = None - _spec_min = None - _spec_max = None - _intensities_constrained = None - _minimizer = None - _max_iterations = None - _result_name = None - _result_ws = None - _parameter_name = None - _fit_group_name = None - - def category(self): - return "Workflow\\MIDAS" - - def summary(self): - return "Fits an \*\_iqt file generated by I(Q,t) sequentially." - - def PyInit(self): - self.declareProperty(MatrixWorkspaceProperty('InputWorkspace', '', direction=Direction.Input), - doc='The _iqt.nxs InputWorkspace used by the algorithm') - - self.declareProperty(name='Function', defaultValue='', - doc='The function to use in fitting') - - self.declareProperty(name='FitType', defaultValue='', - doc='The type of fit being carried out') - - self.declareProperty(name='StartX', defaultValue=0.0, - validator=FloatBoundedValidator(0.0), - doc="The first value for X") - - self.declareProperty(name='EndX', defaultValue=0.2, - validator=FloatBoundedValidator(0.0), - doc="The last value for X") - - self.declareProperty(name='SpecMin', defaultValue=0, - validator=IntBoundedValidator(0), - doc='Minimum spectra in the workspace to fit') - - self.declareProperty(name='SpecMax', defaultValue=1, - validator=IntBoundedValidator(0), - doc='Maximum spectra in the workspace to fit') - - self.declareProperty(name='Minimizer', defaultValue='Levenberg-Marquardt', - doc='The minimizer to use in fitting') - - self.declareProperty(name="MaxIterations", defaultValue=500, - validator=IntBoundedValidator(0), - doc="The Maximum number of iterations for the fit") - - self.declareProperty(name='ConstrainIntensities', defaultValue=False, - doc="If the Intensities should be constrained during the fit") - - self.declareProperty(name='ExtractMembers', defaultValue=False, - doc="If true, then each member of the fit will be extracted, into their " - "own workspace") - - self.declareProperty(MatrixWorkspaceProperty('OutputResultWorkspace', '', direction=Direction.Output), - doc='The output workspace containing the results of the fit data') - - self.declareProperty(ITableWorkspaceProperty('OutputParameterWorkspace', '', direction=Direction.Output), - doc='The output workspace containing the parameters for each fit') - - self.declareProperty(WorkspaceGroupProperty('OutputWorkspaceGroup', '', direction=Direction.Output), - doc='The OutputWorkspace group Data, Calc and Diff, values for the fit of each spectra') - - def validateInputs(self): - self._get_properties() - issues = dict() - if self._start_x >= self._end_x: - issues['StartX'] = 'StartX must be more than EndX' - return issues - - def _get_properties(self): - self._input_ws = self.getProperty('InputWorkspace').value - self._function = self.getProperty('Function').value - self._fit_type = self.getProperty('FitType').value - self._start_x = self.getProperty('StartX').value - self._end_x = self.getProperty('EndX').value - self._spec_min = self.getProperty('SpecMin').value - self._spec_max = self.getProperty('SpecMax').value - self._intensities_constrained = self.getProperty('ConstrainIntensities').value - self._do_extract_members = self.getProperty('ExtractMembers').value - self._minimizer = self.getProperty('Minimizer').value - self._max_iterations = self.getProperty('MaxIterations').value - self._result_name = self.getPropertyValue('OutputResultWorkspace') - self._parameter_name = self.getPropertyValue('OutputParameterWorkspace') - self._fit_group_name = self.getPropertyValue('OutputWorkspaceGroup') - - def PyExec(self): - from IndirectCommon import (getWSprefix, convertToElasticQ) - - setup_prog = Progress(self, start=0.0, end=0.1, nreports=4) - self._fit_type = self._fit_type[:-2] - logger.information('Option: ' + self._fit_type) - logger.information(self._function) - - setup_prog.report('Cropping workspace') - tmp_fit_name = "__IqtFit_ws" - crop_alg = self.createChildAlgorithm("CropWorkspace", enableLogging=False) - crop_alg.setProperty("InputWorkspace", self._input_ws) - crop_alg.setProperty("OutputWorkspace", tmp_fit_name) - crop_alg.setProperty("XMin", self._start_x) - crop_alg.setProperty("XMax", self._end_x) - crop_alg.execute() - - num_hist = self._input_ws.getNumberHistograms() - if self._spec_max is None: - self._spec_max = num_hist - 1 - - # Name stem for generated workspace - output_workspace = '%sIqtFit_%s_s%d_to_%d' % (getWSprefix(self._input_ws.name()), - self._fit_type, self._spec_min, - self._spec_max) - - setup_prog.report('Converting to Histogram') - convert_to_hist_alg = self.createChildAlgorithm("ConvertToHistogram", enableLogging=False) - convert_to_hist_alg.setProperty("InputWorkspace", crop_alg.getProperty("OutputWorkspace").value) - convert_to_hist_alg.setProperty("OutputWorkspace", tmp_fit_name) - convert_to_hist_alg.execute() - mtd.addOrReplace(tmp_fit_name, convert_to_hist_alg.getProperty("OutputWorkspace").value) - - setup_prog.report('Convert to Elastic Q') - convertToElasticQ(tmp_fit_name) - - # Build input string for PlotPeakByLogValue - input_str = [tmp_fit_name + ',i%d' % i for i in range(self._spec_min, self._spec_max + 1)] - input_str = ';'.join(input_str) - - fit_prog = Progress(self, start=0.1, end=0.8, nreports=2) - fit_prog.report('Fitting...') - ms.PlotPeakByLogValue(Input=input_str, - OutputWorkspace=output_workspace, - Function=self._function, - Minimizer=self._minimizer, - MaxIterations=self._max_iterations, - StartX=self._start_x, - EndX=self._end_x, - FitType='Sequential', - CreateOutput=True, - OutputCompositeMembers=self._do_extract_members) - fit_prog.report('Fitting complete') - - conclusion_prog = Progress(self, start=0.8, end=1.0, nreports=5) - # Remove unused workspaces - delete_alg = self.createChildAlgorithm("DeleteWorkspace", enableLogging=False) - delete_alg.setProperty("Workspace", output_workspace + '_NormalisedCovarianceMatrices') - delete_alg.execute() - delete_alg.setProperty("Workspace", output_workspace + '_Parameters') - delete_alg.execute() - delete_alg.setProperty("Workspace", tmp_fit_name) - delete_alg.execute() - - conclusion_prog.report('Renaming workspaces') - # rename workspaces to match user input - rename_alg = self.createChildAlgorithm("RenameWorkspace", enableLogging=False) - if output_workspace + "_Workspaces" != self._fit_group_name: - rename_alg.setProperty("InputWorkspace", output_workspace + "_Workspaces") - rename_alg.setProperty("OutputWorkspace", self._fit_group_name) - rename_alg.execute() - if output_workspace != self._parameter_name: - rename_alg.setProperty("InputWorkspace", output_workspace) - rename_alg.setProperty("OutputWorkspace", self._parameter_name) - rename_alg.execute() - - # Create *_Result workspace - parameter_names = 'A0,Height,Lifetime,Stretching' - conclusion_prog.report('Processing indirect fit parameters') - pifp_alg = self.createChildAlgorithm("ProcessIndirectFitParameters") - pifp_alg.setProperty("InputWorkspace", self._parameter_name) - pifp_alg.setProperty("ColumnX", "axis-1") - pifp_alg.setProperty("XAxisUnit", "MomentumTransfer") - pifp_alg.setProperty("ParameterNames", parameter_names) - pifp_alg.setProperty("OutputWorkspace", self._result_name) - pifp_alg.execute() - self._result_ws = pifp_alg.getProperty("OutputWorkspace").value - - mtd.addOrReplace(self._result_name, self._result_ws) - - # Process generated workspaces - wsnames = mtd[self._fit_group_name].getNames() - for i, workspace in enumerate(wsnames): - output_ws = output_workspace + '_Workspace_%d' % i - rename_alg.setProperty("InputWorkspace", workspace) - rename_alg.setProperty("OutputWorkspace", output_ws) - rename_alg.execute() - - conclusion_prog.report('Copying and transferring sample logs') - self._transfer_sample_logs() - - if self._do_extract_members: - ms.ExtractQENSMembers(InputWorkspace=self._input_ws, - ResultWorkspace=self._fit_group_name, - OutputWorkspace=self._fit_group_name.rsplit('_', 1)[0] + "_Members") - - self.setProperty('OutputParameterWorkspace', self._parameter_name) - self.setProperty('OutputWorkspaceGroup', self._fit_group_name) - self.setProperty('OutputResultWorkspace', self._result_ws) - conclusion_prog.report('Algorithm complete') - - def _transfer_sample_logs(self): - """ - Copy the sample logs from the input workspace and add them to the output workspaces - """ - - sample_logs = {'start_x': self._start_x, 'end_x': self._end_x, 'fit_type': self._fit_type, - 'intensities_constrained': self._intensities_constrained, 'beta_constrained': False} - - copy_log_alg = self.createChildAlgorithm("CopyLogs", enableLogging=False) - copy_log_alg.setProperty("InputWorkspace", self._input_ws) - copy_log_alg.setProperty("OutputWorkspace", self._fit_group_name) - copy_log_alg.execute() - copy_log_alg.setProperty("InputWorkspace", self._input_ws) - copy_log_alg.setProperty("OutputWorkspace", self._result_ws.name()) - copy_log_alg.execute() - - log_names = [item for item in sample_logs] - log_values = [sample_logs[item] for item in sample_logs] - - add_sample_log_multi = self.createChildAlgorithm("AddSampleLogMultiple", enableLogging=False) - add_sample_log_multi.setProperty("Workspace", self._result_ws.name()) - add_sample_log_multi.setProperty("LogNames", log_names) - add_sample_log_multi.setProperty("LogValues", log_values) - add_sample_log_multi.execute() - add_sample_log_multi.setProperty("Workspace", self._fit_group_name) - add_sample_log_multi.setProperty("LogNames", log_names) - add_sample_log_multi.setProperty("LogValues", log_values) - add_sample_log_multi.execute() - - def _extract_members(self): - ms.ExtractMembers() - - -AlgorithmFactory.subscribe(IqtFitSequential) diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/IqtFitSequentialTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/IqtFitSequentialTest.py index a3d94632af81281dae01dc938c0df09642b2570f..98a1a4a7bb538bca98b5ad46a5e1b47acbd1ce43 100644 --- a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/IqtFitSequentialTest.py +++ b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/IqtFitSequentialTest.py @@ -73,7 +73,7 @@ class IqtFitSequentialTest(unittest.TestCase): nbins = sub_ws.blocksize() nhists = sub_ws.getNumberHistograms() self.assertEquals(nbins, 58) - self.assertEquals(nhists, 3) + self.assertEquals(nhists, 5) # Check histogram names text_axis = sub_ws.getAxis(1) @@ -100,7 +100,7 @@ class IqtFitSequentialTest(unittest.TestCase): self.assertEquals(round(row['f1.Lifetime'], 7), 0.0287491) def _validate_matrix_values(self, matrixWS): - # Check f0.A0 + # Check f1.A0 a0 = matrixWS.readY(0) self.assertEquals(round(a0[0], 7), 0.0336564) self.assertEquals(round(a0[-1],7), 0.0182411) @@ -115,7 +115,6 @@ class IqtFitSequentialTest(unittest.TestCase): self.assertEquals(round(lifetime[0], 7), 0.0287491) self.assertEquals(round(lifetime[-1],7), 0.0034427) - def _validate_group_values(self, groupWS): sub_ws = groupWS.getItem(0) # Check Data @@ -134,9 +133,6 @@ class IqtFitSequentialTest(unittest.TestCase): def _validate_sample_log_values(self, matrixWS): run = matrixWS.getRun() # Check additionally added logs - self.assertEqual(run.getProperty('fit_type').value, '1E') - self.assertEqual(run.getProperty('intensities_constrained').value, 'True') - self.assertEqual(run.getProperty('beta_constrained').value, 'False') self.assertEqual(run.getProperty('end_x').value, 0.24) self.assertEqual(run.getProperty('start_x').value, 0.0) @@ -155,12 +151,10 @@ class IqtFitSequentialTest(unittest.TestCase): """ result, params, fit_group = IqtFitSequential(InputWorkspace=self._iqt_ws, Function=self._function, - FitType='1E_s', StartX=0, EndX=0.24, SpecMin=0, - SpecMax=16, - ConstrainIntensities=True) + SpecMax=16) self._validate_output(params, result, fit_group) #----------------------------------------Failure cases------------------------------------- @@ -169,72 +163,54 @@ class IqtFitSequentialTest(unittest.TestCase): self.assertRaises(ValueError, IqtFitSequential, InputWorkspace=self._iqt_ws, Function=self._function, - FitType='1S_s', EndX=0.2, SpecMin=-1, SpecMax=16, - OutputResultWorkspace='result', + OutputWorkspace='result', OutputParameterWorkspace='table', OutputWorkspaceGroup='fit_group') def test_maximum_spectra_more_than_workspace_spectra(self): self.assertRaises(RuntimeError, IqtFitSequential, InputWorkspace=self._iqt_ws, Function=self._function, - FitType='1S_s', EndX=0.2, SpecMin=0, SpecMax=20, - OutputResultWorkspace='result', + OutputWorkspace='result', OutputParameterWorkspace='table', OutputWorkspaceGroup='fit_group') def test_minimum_spectra_more_than_maximum_spectra(self): self.assertRaises(RuntimeError, IqtFitSequential, InputWorkspace=self._iqt_ws, Function=self._function, - FitType='1S_s', EndX=0.2, SpecMin=10, SpecMax=5, - OutputResultWorkspace='result', + OutputWorkspace='result', OutputParameterWorkspace='table', OutputWorkspaceGroup='fit_group') def test_minimum_x_less_than_0(self): - self.assertRaises(ValueError, IqtFitSequential, InputWorkspace=self._iqt_ws, + self.assertRaises(RuntimeError, IqtFitSequential, InputWorkspace=self._iqt_ws, Function=self._function, - FitType='1S_s', StartX=-0.2, EndX=0.2, SpecMin=0, SpecMax=16, - OutputResultWorkspace='result', + OutputWorkspace='result', OutputParameterWorkspace='table', OutputWorkspaceGroup='fit_group') def test_maximum_x_more_than_workspace_max_x(self): self.assertRaises(RuntimeError, IqtFitSequential, InputWorkspace=self._iqt_ws, Function=self._function, - FitType='1S_s', StartX=0, EndX=0.4, SpecMin=0, SpecMax=16, - OutputResultWorkspace='result', - OutputParameterWorkspace='table', - OutputWorkspaceGroup='fit_group') - - def test_minimum_spectra_more_than_maximum_spectra(self): - self.assertRaises(RuntimeError, IqtFitSequential, InputWorkspace=self._iqt_ws, - Function=self._function, - FitType='1S_s', - StartX=0.2, - EndX=0.1, - SpecMin=16, - SpecMax=0, - OutputResultWorkspace='result', + OutputWorkspace='result', OutputParameterWorkspace='table', OutputWorkspaceGroup='fit_group') - if __name__=="__main__": unittest.main() diff --git a/Framework/WorkflowAlgorithms/CMakeLists.txt b/Framework/WorkflowAlgorithms/CMakeLists.txt index adbf431c1653738462031a5978e752d5f99ddec3..8968361ce1cc72ad693ad371208a7ada53fbe02a 100644 --- a/Framework/WorkflowAlgorithms/CMakeLists.txt +++ b/Framework/WorkflowAlgorithms/CMakeLists.txt @@ -22,6 +22,7 @@ set ( SRC_FILES src/HFIRLoad.cpp src/HFIRSANSNormalise.cpp src/IMuonAsymmetryCalculator.cpp + src/IqtFitSequential.cpp src/LoadEventAndCompress.cpp src/MuonGroupAsymmetryCalculator.cpp src/MuonGroupCalculator.cpp @@ -29,6 +30,7 @@ set ( SRC_FILES src/MuonProcess.cpp src/MuonPairAsymmetryCalculator.cpp src/ProcessIndirectFitParameters.cpp + src/QENSFitSequential.cpp src/RefRoi.cpp src/SANSBeamFinder.cpp src/SANSBeamFluxCorrection.cpp @@ -68,6 +70,7 @@ set ( INC_FILES inc/MantidWorkflowAlgorithms/HFIRLoad.h inc/MantidWorkflowAlgorithms/HFIRSANSNormalise.h inc/MantidWorkflowAlgorithms/IMuonAsymmetryCalculator.h + inc/MantidWorkflowAlgorithms/IqtFitSequential.h inc/MantidWorkflowAlgorithms/LoadEventAndCompress.h inc/MantidWorkflowAlgorithms/MuonGroupAsymmetryCalculator.h inc/MantidWorkflowAlgorithms/MuonGroupCalculator.h @@ -75,6 +78,7 @@ set ( INC_FILES inc/MantidWorkflowAlgorithms/MuonProcess.h inc/MantidWorkflowAlgorithms/MuonPairAsymmetryCalculator.h inc/MantidWorkflowAlgorithms/ProcessIndirectFitParameters.h + inc/MantidWorkflowAlgorithms/QENSFitSequential.h inc/MantidWorkflowAlgorithms/RefRoi.h inc/MantidWorkflowAlgorithms/SANSBeamFinder.h inc/MantidWorkflowAlgorithms/SANSBeamFluxCorrection.h @@ -94,6 +98,7 @@ set ( TEST_FILES LoadEventAndCompressTest.h MuonProcessTest.h ProcessIndirectFitParametersTest.h + QENSFitSequentialTest.h SANSSolidAngleCorrectionTest.h StepScanTest.h ) diff --git a/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ConvolutionFitSequential.h b/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ConvolutionFitSequential.h index 83d8bcbe27ec2a2dc2a0015f05fa1a3814779a24..54341333a84d177f01986b4ad9ecb0a01779f80d 100644 --- a/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ConvolutionFitSequential.h +++ b/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ConvolutionFitSequential.h @@ -2,10 +2,8 @@ #define MANTID_ALGORITHMS_CONVOLUTIONFITSEQUENTIAL_H_ #include "MantidAPI/Column.h" -#include "MantidAPI/DataProcessorAlgorithm.h" -#include "MantidAPI/MatrixWorkspace.h" -#include "MantidAPI/WorkspaceGroup.h" #include "MantidKernel/System.h" +#include "MantidWorkflowAlgorithms/QENSFitSequential.h" namespace Mantid { namespace Algorithms { @@ -34,31 +32,28 @@ namespace Algorithms { File change history is stored at: <https://github.com/mantidproject/mantid> Code Documentation is available at: <http://doxygen.mantidproject.org> */ -class DLLExport ConvolutionFitSequential : public API::DataProcessorAlgorithm { +class DLLExport ConvolutionFitSequential : public QENSFitSequential { public: const std::string name() const override; int version() const override; const std::string category() const override; const std::string summary() const override; + const std::vector<std::string> seeAlso() const override; + +protected: + API::ITableWorkspace_sptr performFit(const std::string &input, + const std::string &output) override; + std::map<std::string, std::string> getAdditionalLogStrings() const override; + std::map<std::string, std::string> getAdditionalLogNumbers() const override; private: - void init() override; - void exec() override; + std::map<std::string, std::string> validateInputs() override; - bool checkForTwoLorentz(const std::string &); - std::vector<std::string> findValuesFromFunction(const std::string &); - std::vector<std::string> searchForFitParams(const std::string &, - const std::vector<std::string> &); - std::vector<double> squareVector(std::vector<double>); - std::vector<double> cloneVector(const std::vector<double> &); - void convertInputToElasticQ(API::MatrixWorkspace_sptr &, const std::string &); + bool throwIfElasticQConversionFails() const override; + bool isFitParameter(const std::string &name) const override; void calculateEISF(API::ITableWorkspace_sptr &); - std::string convertBackToShort(const std::string &); - std::string convertFuncToShort(const std::string &); - void extractMembers(Mantid::API::MatrixWorkspace_sptr inputWs, - Mantid::API::WorkspaceGroup_sptr resultGroupWs, - const std::vector<std::string> &convolvedMembers, - const std::string &outputWsName); + + bool m_deltaUsed; }; } // namespace Algorithms diff --git a/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ExtractQENSMembers.h b/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ExtractQENSMembers.h index eb2cdb782917893f0ba12c8daecdb9db70a1e739..bb25ef3a851ead9e6f5774922828451a8ecb2d90 100644 --- a/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ExtractQENSMembers.h +++ b/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ExtractQENSMembers.h @@ -41,8 +41,12 @@ public: private: void init() override; void exec() override; + std::map<std::string, std::string> validateInputs() override; - std::vector<double> getQValues(Mantid::API::MatrixWorkspace_sptr workspace); + std::vector<Mantid::API::MatrixWorkspace_sptr> getInputWorkspaces() const; + + std::vector<double> + getQValues(const std::vector<Mantid::API::MatrixWorkspace_sptr> &workspaces); std::vector<std::string> getAxisLabels(Mantid::API::MatrixWorkspace_sptr workspace, diff --git a/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/IqtFitSequential.h b/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/IqtFitSequential.h new file mode 100644 index 0000000000000000000000000000000000000000..550a8abcc3dc472da49a284c80c33169bdb88359 --- /dev/null +++ b/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/IqtFitSequential.h @@ -0,0 +1,46 @@ +#ifndef MANTID_ALGORITHMS_IQTFITSEQUENTIAL_H_ +#define MANTID_ALGORITHMS_IQTFITSEQUENTIAL_H_ + +#include "MantidWorkflowAlgorithms/QENSFitSequential.h" + +namespace Mantid { +namespace Algorithms { + +/** + Copyright © 2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge + National Laboratory & European Spallation Source + This file is part of Mantid. + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +class DLLExport IqtFitSequential : public QENSFitSequential { +public: + const std::string name() const override; + int version() const override; + const std::string category() const override; + const std::string summary() const override; + const std::vector<std::string> seeAlso() const override; + +protected: + std::vector<API::MatrixWorkspace_sptr> getWorkspaces() const override; + +private: + std::map<std::string, std::string> validateInputs() override; + bool isFitParameter(const std::string &name) const override; + bool throwIfElasticQConversionFails() const override; +}; + +} // namespace Algorithms +} // namespace Mantid + +#endif diff --git a/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/QENSFitSequential.h b/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/QENSFitSequential.h new file mode 100644 index 0000000000000000000000000000000000000000..bce0351f005f59abbfeb5b22fa77ae9d046cf64b --- /dev/null +++ b/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/QENSFitSequential.h @@ -0,0 +1,92 @@ +#ifndef MANTID_ALGORITHMS_QENSFITSEQUENTIAL_H_ +#define MANTID_ALGORITHMS_QENSFITSEQUENTIAL_H_ + +#include "MantidAPI/DataProcessorAlgorithm.h" +#include "MantidAPI/ITableWorkspace.h" +#include "MantidAPI/MatrixWorkspace.h" +#include "MantidAPI/WorkspaceGroup.h" +#include "MantidKernel/IValidator.h" + +#include <set> + +namespace Mantid { +namespace Algorithms { + +/** + QENSFitSequential - Performs a sequential QENS fit + + Copyright © 2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge + National Laboratory & European Spallation Source + This file is part of Mantid. + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +class DLLExport QENSFitSequential : public API::DataProcessorAlgorithm { +public: + const std::string name() const override; + int version() const override; + const std::string category() const override; + const std::string summary() const override; + const std::vector<std::string> seeAlso() const override; + +protected: + std::map<std::string, std::string> validateInputs() override; + virtual std::vector<API::MatrixWorkspace_sptr> getWorkspaces() const; + virtual std::map<std::string, std::string> getAdditionalLogStrings() const; + virtual std::map<std::string, std::string> getAdditionalLogNumbers() const; + virtual API::ITableWorkspace_sptr performFit(const std::string &input, + const std::string &output); + +private: + void init() override; + void exec() override; + void deleteTemporaryWorkspaces(const std::string &outputBaseName); + void addAdditionalLogs(API::MatrixWorkspace_sptr result); + + virtual bool throwIfElasticQConversionFails() const; + virtual bool isFitParameter(const std::string ¶meterName) const; + std::vector<std::string> getFitParameterNames() const; + std::set<std::string> getUniqueParameterNames() const; + std::string getOutputBaseName() const; + std::string getInputString( + const std::vector<API::MatrixWorkspace_sptr> &workspaces) const; + API::MatrixWorkspace_sptr + processIndirectFitParameters(API::ITableWorkspace_sptr parameterWorkspace); + + std::vector<API::MatrixWorkspace_sptr> convertInputToElasticQ( + const std::vector<API::MatrixWorkspace_sptr> &workspaces) const; + + void renameWorkspaces(API::WorkspaceGroup_sptr outputGroup, + const std::vector<std::string> &spectra); + void renameWorkspaces(API::WorkspaceGroup_sptr outputGroup, + const std::vector<std::string> &spectra, + const std::vector<API::MatrixWorkspace_sptr> &names); + void copyLogs(API::MatrixWorkspace_sptr resultWorkspace, + const std::vector<API::MatrixWorkspace_sptr> &workspaces); + void copyLogs(API::MatrixWorkspace_sptr resultWorkspace, + API::WorkspaceGroup_sptr resultGroup); + void extractMembers(API::WorkspaceGroup_sptr resultGroupWs, + const std::vector<API::MatrixWorkspace_sptr> &workspaces, + const std::string &outputWsName); + + API::IAlgorithm_sptr + extractMembersAlgorithm(API::WorkspaceGroup_sptr resultGroupWs, + const std::string &outputWsName) const; + + std::string getTemporaryName() const; +}; + +} // namespace Algorithms +} // namespace Mantid + +#endif diff --git a/Framework/WorkflowAlgorithms/src/ConvolutionFitSequential.cpp b/Framework/WorkflowAlgorithms/src/ConvolutionFitSequential.cpp index 52f5592090eb6e3a5cee79079e9405404b24e2ce..c1fe67f69520071a29ca4bc3c11fbed6d18ec293 100644 --- a/Framework/WorkflowAlgorithms/src/ConvolutionFitSequential.cpp +++ b/Framework/WorkflowAlgorithms/src/ConvolutionFitSequential.cpp @@ -1,10 +1,9 @@ #include "MantidWorkflowAlgorithms/ConvolutionFitSequential.h" #include "MantidAPI/AlgorithmManager.h" -#include "MantidAPI/Axis.h" +#include "MantidAPI/CompositeFunction.h" #include "MantidAPI/FunctionDomain1D.h" #include "MantidAPI/FunctionFactory.h" -#include "MantidAPI/IFunction.h" #include "MantidAPI/ITableWorkspace.h" #include "MantidAPI/NumericAxis.h" #include "MantidAPI/Progress.h" @@ -19,12 +18,185 @@ #include "MantidKernel/StringContainsValidator.h" #include "MantidKernel/VectorHelper.h" +#include <algorithm> #include <cmath> namespace { Mantid::Kernel::Logger g_log("ConvolutionFitSequential"); + +using namespace Mantid::API; +using namespace Mantid::Kernel; +using Mantid::MantidVec; + +std::size_t numberOfFunctions(IFunction_sptr function, + const std::string &functionName); + +std::size_t numberOfFunctions(CompositeFunction_sptr composite, + const std::string &functionName) { + std::size_t count = 0; + for (auto i = 0u; i < composite->nFunctions(); ++i) + count += numberOfFunctions(composite->getFunction(i), functionName); + return count; +} + +std::size_t numberOfFunctions(IFunction_sptr function, + const std::string &functionName) { + const auto composite = + boost::dynamic_pointer_cast<CompositeFunction>(function); + if (composite) + return numberOfFunctions(composite, functionName); + return function->name() == functionName ? 1 : 0; +} + +bool containsFunction(IFunction_sptr function, const std::string &functionName); + +bool containsFunction(CompositeFunction_sptr composite, + const std::string &functionName) { + for (auto i = 0u; i < composite->nFunctions(); ++i) { + if (containsFunction(composite->getFunction(i), functionName)) + return true; + } + return false; +} + +bool containsFunction(IFunction_sptr function, + const std::string &functionName) { + const auto composite = + boost::dynamic_pointer_cast<CompositeFunction>(function); + if (function->name() == functionName) + return true; + else if (composite) + return containsFunction(composite, functionName); + return false; +} + +template <typename T, typename F, typename... Ts> +std::vector<T, Ts...> transformVector(const std::vector<T, Ts...> &vec, + F const &functor) { + auto target = std::vector<T, Ts...>(); + target.reserve(vec.size()); + std::transform(vec.begin(), vec.end(), std::back_inserter(target), functor); + return target; +} + +template <typename T, typename F, typename... Ts> +std::vector<T, Ts...> combineVectors(const std::vector<T, Ts...> &vec, + const std::vector<T, Ts...> &vec2, + F const &combinator) { + auto combined = std::vector<T, Ts...>(); + combined.reserve(vec.size()); + std::transform(vec.begin(), vec.end(), vec2.begin(), + std::back_inserter(combined), combinator); + return combined; } +template <typename T, typename... Ts> +std::vector<T, Ts...> divideVectors(const std::vector<T, Ts...> ÷nd, + const std::vector<T, Ts...> &divisor) { + return combineVectors(dividend, divisor, std::divides<T>()); +} + +template <typename T, typename... Ts> +std::vector<double> addVectors(const std::vector<T, Ts...> &vec, + const std::vector<T, Ts...> &vec2) { + return combineVectors(vec, vec2, std::plus<T>()); +} + +template <typename T, typename... Ts> +std::vector<double> multiplyVectors(const std::vector<T, Ts...> &vec, + const std::vector<T, Ts...> &vec2) { + return combineVectors(vec, vec2, std::multiplies<T>()); +} + +template <typename T, typename... Ts> +std::vector<T, Ts...> squareVector(const std::vector<T, Ts...> &vec) { + return transformVector(vec, VectorHelper::Squares<T>()); +} + +template <typename T, typename... Ts> +std::vector<T, Ts...> squareRootVector(const std::vector<T, Ts...> &vec) { + return transformVector(vec, static_cast<T (*)(T)>(sqrt)); +} + +IFunction_sptr extractFirstBackground(IFunction_sptr function); + +IFunction_sptr extractFirstBackground(CompositeFunction_sptr composite) { + for (auto i = 0u; i < composite->nFunctions(); ++i) { + auto background = extractFirstBackground(composite->getFunction(i)); + if (background) + return background; + } + return nullptr; +} + +IFunction_sptr extractFirstBackground(IFunction_sptr function) { + auto composite = boost::dynamic_pointer_cast<CompositeFunction>(function); + + if (composite) + return extractFirstBackground(composite); + else if (function->category() == "Background") + return function; + return nullptr; +} + +std::string extractBackgroundType(IFunction_sptr function) { + auto background = extractFirstBackground(function); + if (!background) + return "None"; + + auto backgroundType = background->name(); + auto position = backgroundType.rfind("Background"); + + if (position != std::string::npos) + backgroundType = backgroundType.substr(0, position); + + if (background->isFixed(0)) + backgroundType = "Fixed " + backgroundType; + else + backgroundType = "Fit " + backgroundType; + return backgroundType; +} + +std::vector<std::size_t> +searchForFitParameters(const std::string &suffix, + ITableWorkspace_sptr tableWorkspace) { + auto indices = std::vector<std::size_t>(); + + for (auto i = 0u; i < tableWorkspace->columnCount(); ++i) { + auto name = tableWorkspace->getColumn(i)->name(); + auto position = name.rfind(suffix); + if (position != std::string::npos && + position + suffix.size() == name.size()) + indices.emplace_back(i); + } + return indices; +} + +std::pair<MantidVec, MantidVec> +calculateEISFAndError(const MantidVec &height, const MantidVec &heightError, + const MantidVec &litude, + const MantidVec &litudeError) { + auto total = addVectors(height, amplitude); + auto eisfY = divideVectors(height, total); + + auto heightESq = squareVector(heightError); + + auto ampErrSq = squareVector(amplitudeError); + auto totalErr = addVectors(heightESq, ampErrSq); + + auto heightYSq = squareVector(height); + auto totalSq = squareVector(total); + + auto errOverTotalSq = divideVectors(totalErr, totalSq); + auto heightESqOverYSq = divideVectors(heightESq, heightYSq); + + auto sqrtESqOverYSq = squareRootVector(heightESqOverYSq); + auto eisfYSumRoot = multiplyVectors(eisfY, sqrtESqOverYSq); + + return {eisfY, addVectors(eisfYSumRoot, errOverTotalSq)}; +} +} // namespace + namespace Mantid { namespace Algorithms { @@ -54,552 +226,89 @@ const std::string ConvolutionFitSequential::summary() const { return "Performs a sequential fit for a convolution workspace"; } -//---------------------------------------------------------------------------------------------- -/** Initialize the algorithm's properties. - */ -void ConvolutionFitSequential::init() { - declareProperty( - make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input), - "The input workspace for the fit."); - - auto scv = boost::make_shared<StringContainsValidator>(); - auto requires = std::vector<std::string>{"Convolution", "Resolution"}; - scv->setRequiredStrings(requires); - - declareProperty("Function", "", scv, - "The function that describes the parameters of the fit.", - Direction::Input); - - declareProperty("PassWSIndexToFunction", false, - "For each spectrum in Input pass its workspace index to all " - "functions that have attribute WorkspaceIndex."); - - std::vector<std::string> backType{"Fixed Flat", "Fit Flat", "Fit Linear"}; - - declareProperty("BackgroundType", "Fixed Flat", - boost::make_shared<StringListValidator>(backType), - "The Type of background used in the fitting", - Direction::Input); - - declareProperty( - "StartX", EMPTY_DBL(), boost::make_shared<MandatoryValidator<double>>(), - "The start of the range for the fit function.", Direction::Input); - - declareProperty( - "EndX", EMPTY_DBL(), boost::make_shared<MandatoryValidator<double>>(), - "The end of the range for the fit function.", Direction::Input); - - auto boundedV = boost::make_shared<BoundedValidator<int>>(); - boundedV->setLower(0); - - declareProperty("SpecMin", 0, boundedV, "The first spectrum to be used in " - "the fit. Spectra values can not be " - "negative", - Direction::Input); - - declareProperty("SpecMax", 0, boundedV, "The final spectrum to be used in " - "the fit. Spectra values can not be " - "negative", - Direction::Input); - - declareProperty( - "Convolve", true, - "If true, output fitted model components will be convolved with " - "the resolution.", - Direction::Input); - - declareProperty( - "ExtractMembers", false, - "If true, then each member of the convolution fit will be extracted" - ", into their own workspace. These workspaces will have a histogram" - " for each spectrum (Q-value) and will be grouped.", - Direction::Input); - - declareProperty("Minimizer", "Levenberg-Marquardt", - boost::make_shared<MandatoryValidator<std::string>>(), - "Minimizer to use for fitting. Minimizers available are " - "'Levenberg-Marquardt', 'Simplex', 'FABADA',\n" - "'Conjugate gradient (Fletcher-Reeves imp.)', 'Conjugate " - "gradient (Polak-Ribiere imp.)' and 'BFGS'"); - - declareProperty("MaxIterations", 500, boundedV, - "The maximum number of iterations permitted", - Direction::Input); - declareProperty("PeakRadius", 0, - "A value of the peak radius the peak functions should use. A " - "peak radius defines an interval on the x axis around the " - "centre of the peak where its values are calculated. Values " - "outside the interval are not calculated and assumed zeros." - "Numerically the radius is a whole number of peak widths " - "(FWHM) that fit into the interval on each side from the " - "centre. The default value of 0 means the whole x axis."); - - declareProperty(make_unique<WorkspaceProperty<>>("OutputWorkspace", "", - Direction::Output), - "The OutputWorkspace containing the results of the fit."); -} - -//---------------------------------------------------------------------------------------------- -/** Execute the algorithm. - */ -void ConvolutionFitSequential::exec() { - // Initialise variables with properties - MatrixWorkspace_sptr inputWs = getProperty("InputWorkspace"); - const std::string function = getProperty("Function"); - const bool passIndex = getProperty("PassWSIndexToFunction"); - const std::string backType = - convertBackToShort(getProperty("backgroundType")); - const double startX = getProperty("StartX"); - const double endX = getProperty("EndX"); - const int specMin = getProperty("SpecMin"); - const int specMax = getProperty("Specmax"); - const bool convolve = getProperty("Convolve"); - const bool doExtractMembers = getProperty("ExtractMembers"); - const int maxIter = getProperty("MaxIterations"); - const std::string minimizer = getProperty("Minimizer"); - const int peakRadius = getProperty("PeakRadius"); - - // Inspect function to obtain fit Type and background - const auto functionValues = findValuesFromFunction(function); - const auto LorentzNum = functionValues[0]; - const auto funcName = functionValues[1]; - std::vector<std::string> convolvedMembers; - - // Check if a delta function is being used - auto delta = false; - std::string usingDelta = "false"; - auto pos = function.find("Delta"); - if (pos != std::string::npos) { - delta = true; - usingDelta = "true"; - convolvedMembers.emplace_back("DeltaFunction"); - } - - if (LorentzNum == "0") - convolvedMembers.emplace_back(funcName); - else if (LorentzNum == "1") - convolvedMembers.emplace_back("Lorentzian"); - else { - convolvedMembers.emplace_back("Lorentzian1"); - convolvedMembers.emplace_back("Lorentzian2"); - } - - // Log information to result log - m_log.information("Input files: " + inputWs->getName()); - m_log.information("Fit type: Delta=" + usingDelta + "; Lorentzians=" + - LorentzNum); - m_log.information("Background type: " + backType); - - // Output workspace name - auto outputWsName = inputWs->getName(); - pos = outputWsName.rfind('_'); - if (pos != std::string::npos) { - outputWsName = outputWsName.substr(0, pos + 1); - } - outputWsName += "conv_"; - if (delta) { - outputWsName += "Delta"; - } - if (LorentzNum != "0") { - outputWsName += LorentzNum + "L"; - } else { - outputWsName += convertFuncToShort(funcName); - } - outputWsName += backType + "_s"; - outputWsName += std::to_string(specMin); - - if (specMin != specMax) { - outputWsName += "_to_"; - outputWsName += std::to_string(specMax); - } - - // Convert input workspace to get Q axis - const std::string tempFitWsName = "__convfit_fit_ws"; - convertInputToElasticQ(inputWs, tempFitWsName); - - Progress plotPeakStringProg(this, 0.0, 0.05, specMax - specMin); - // Construct plotpeak string - std::string plotPeakInput; - for (int i = specMin; i < specMax + 1; i++) { - auto nextWs = tempFitWsName + ",i"; - nextWs += std::to_string(i); - plotPeakInput += nextWs + ";"; - plotPeakStringProg.report("Constructing PlotPeak name"); - } - - // Run PlotPeaksByLogValue - auto plotPeaks = createChildAlgorithm("PlotPeakByLogValue", 0.05, 0.90, true); - plotPeaks->setProperty("Input", plotPeakInput); - plotPeaks->setProperty("OutputWorkspace", outputWsName); - plotPeaks->setProperty("Function", function); - plotPeaks->setProperty("StartX", startX); - plotPeaks->setProperty("EndX", endX); - plotPeaks->setProperty("FitType", "Sequential"); - plotPeaks->setProperty("CreateOutput", true); - plotPeaks->setProperty("OutputCompositeMembers", true); - plotPeaks->setProperty("ConvolveMembers", convolve); - plotPeaks->setProperty("MaxIterations", maxIter); - plotPeaks->setProperty("Minimizer", minimizer); - plotPeaks->setProperty("PassWSIndexToFunction", passIndex); - plotPeaks->setProperty("PeakRadius", peakRadius); - plotPeaks->executeAsChildAlg(); - ITableWorkspace_sptr outputWs = plotPeaks->getProperty("OutputWorkspace"); - - // Delete workspaces - Progress deleteProgress(this, 0.90, 0.91, 2); - auto deleter = createChildAlgorithm("DeleteWorkspace", -1.0, -1.0, false); - deleter->setProperty("WorkSpace", - outputWsName + "_NormalisedCovarianceMatrices"); - deleter->executeAsChildAlg(); - deleteProgress.report("Deleting PlotPeak Output"); - - deleter->setProperty("Workspace", tempFitWsName); - deleter->executeAsChildAlg(); - - deleter->setProperty("WorkSpace", outputWsName + "_Parameters"); - deleter->executeAsChildAlg(); - deleteProgress.report("Deleting PlotPeak Output"); - - const auto paramTableName = outputWsName + "_Parameters"; - AnalysisDataService::Instance().addOrReplace(paramTableName, outputWs); - - // Construct output workspace - const auto resultWsName = outputWsName + "_Result"; - - Progress workflowProg(this, 0.91, 0.94, 4); - auto paramNames = std::vector<std::string>(); - if (funcName == "DeltaFunction") { - paramNames.emplace_back("Height"); - } else { - auto func = FunctionFactory::Instance().createFunction(funcName); - if (delta) { - paramNames.emplace_back("Height"); - } - for (size_t i = 0; i < func->nParams(); i++) { - paramNames.push_back(func->parameterName(i)); - workflowProg.report("Finding parameters to process"); - } - if (funcName == "Lorentzian") { - // remove peak centre - size_t pos = find(paramNames.begin(), paramNames.end(), "PeakCentre") - - paramNames.begin(); - paramNames.erase(paramNames.begin() + pos); - paramNames.emplace_back("EISF"); - } - } - - // Run calcEISF if Delta - if (delta) { - calculateEISF(outputWs); - } - - // Construct comma separated list for ProcessIndirectFitParameters - std::string paramNamesList; - const size_t maxNames = paramNames.size(); - for (size_t i = 0; i < maxNames; i++) { - paramNamesList += paramNames.at(i); - if (i != (maxNames - 1)) { - paramNamesList += ","; - } - workflowProg.report("Constructing indirectFitParams input"); - } - - // Run ProcessIndirectFitParameters - auto pifp = - createChildAlgorithm("ProcessIndirectFitParameters", 0.94, 0.96, true); - pifp->setProperty("InputWorkspace", outputWs); - pifp->setProperty("ColumnX", "axis-1"); - pifp->setProperty("XAxisUnit", "MomentumTransfer"); - pifp->setProperty("ParameterNames", paramNamesList); - pifp->setProperty("OutputWorkspace", resultWsName); - pifp->executeAsChildAlg(); - - MatrixWorkspace_sptr resultWs = pifp->getProperty("OutputWorkspace"); - AnalysisDataService::Instance().addOrReplace(resultWsName, resultWs); - - // Handle sample logs - auto logCopier = createChildAlgorithm("CopyLogs", -1.0, -1.0, false); - logCopier->setProperty("InputWorkspace", inputWs); - logCopier->setProperty("OutputWorkspace", resultWs); - logCopier->executeAsChildAlg(); - - resultWs = logCopier->getProperty("OutputWorkspace"); - - // Create Sample Log - auto sampleLogStrings = std::map<std::string, std::string>(); - sampleLogStrings["sample_filename"] = inputWs->getName(); - sampleLogStrings["convolve_members"] = (convolve == 1) ? "true" : "false"; - sampleLogStrings["fit_program"] = "ConvFit"; - sampleLogStrings["background"] = backType; - sampleLogStrings["delta_function"] = usingDelta; - auto sampleLogNumeric = std::map<std::string, std::string>(); - sampleLogNumeric["lorentzians"] = - boost::lexical_cast<std::string>(LorentzNum); - - Progress logAdderProg(this, 0.96, 0.97, 6); - // Add String Logs - auto logAdder = createChildAlgorithm("AddSampleLog", -1.0, -1.0, false); - for (auto &sampleLogString : sampleLogStrings) { - logAdder->setProperty("Workspace", resultWs); - logAdder->setProperty("LogName", sampleLogString.first); - logAdder->setProperty("LogText", sampleLogString.second); - logAdder->setProperty("LogType", "String"); - logAdder->executeAsChildAlg(); - logAdderProg.report("Add text logs"); - } - - // Add Numeric Logs - for (auto &logItem : sampleLogNumeric) { - logAdder->setProperty("Workspace", resultWs); - logAdder->setProperty("LogName", logItem.first); - logAdder->setProperty("LogText", logItem.second); - logAdder->setProperty("LogType", "Number"); - logAdder->executeAsChildAlg(); - logAdderProg.report("Adding Numerical logs"); - } - - // Copy Logs to GroupWorkspace - logCopier = createChildAlgorithm("CopyLogs", 0.97, 0.98, false); - logCopier->setProperty("InputWorkspace", resultWs); - std::string groupName = outputWsName + "_Workspaces"; - logCopier->setProperty("OutputWorkspace", groupName); - logCopier->executeAsChildAlg(); - - // Rename Workspaces in group - WorkspaceGroup_sptr groupWs = - AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(outputWsName + - "_Workspaces"); - const auto groupWsNames = groupWs->getNames(); - auto renamer = createChildAlgorithm("RenameWorkspace", -1.0, -1.0, false); - Progress renamerProg(this, 0.98, 1.0, specMax + 1); - for (int i = specMin; i < specMax + 1; i++) { - renamer->setProperty("InputWorkspace", groupWsNames.at(i - specMin)); - auto outName = outputWsName; - - // Check if multiple spectrum were fit. - if (specMin != specMax) { - outName += "_" + std::to_string(i); - } - outName += "_Workspace"; - renamer->setProperty("OutputWorkspace", outName); - renamer->executeAsChildAlg(); - renamerProg.report("Renaming group workspaces"); - } - - // Check whether to extract members into their own workspaces. - if (doExtractMembers) - extractMembers(inputWs, groupWs, convolvedMembers, - outputWsName + "_Members"); - - setProperty("OutputWorkspace", resultWs); +/// Algorithm's see also for related algorithms. @see Algorithm::seeAlso +const std::vector<std::string> ConvolutionFitSequential::seeAlso() const { + return {"QENSFitSequential"}; } -/** - * Check function to establish if it is for one lorentzian or Two - * @param subFunction The unchecked substring of the function - * @return true if the function is two lorentzian false if one lorentzian - */ -bool ConvolutionFitSequential::checkForTwoLorentz( - const std::string &subFunction) { - auto pos = subFunction.rfind("Lorentzian"); - return pos != std::string::npos; +std::map<std::string, std::string> ConvolutionFitSequential::validateInputs() { + auto errors = QENSFitSequential::validateInputs(); + IFunction_sptr function = getProperty("Function"); + if (!containsFunction(function, "Convolution") || + !containsFunction(function, "Resolution")) + errors["Function"] = "Function provided does not contain convolution with " + "a resolution function."; + return errors; } -/** - * Finds specific values embedded in the function supplied to the algorithm - * @param function The full function string - * @return all values of interest from the function (0 - fitType, 1 - - * functionName) - */ -std::vector<std::string> -ConvolutionFitSequential::findValuesFromFunction(const std::string &function) { - std::vector<std::string> result; - std::string fitType; - std::string functionName; - auto startPos = function.rfind("name="); - if (startPos != std::string::npos) { - fitType = function.substr(startPos, function.size()); - auto nextPos = fitType.find_first_of(','); - fitType = fitType.substr(5, nextPos - 5); - functionName = fitType; - if (fitType == "Lorentzian") { - std::string newSub = function.substr(0, startPos); - bool isTwoL = checkForTwoLorentz(newSub); - if (isTwoL) { - fitType = "2"; - } else { - fitType = "1"; - } - } else { - fitType = "0"; - } - result.push_back(fitType); - } - - result.push_back(functionName); - return result; +bool ConvolutionFitSequential::throwIfElasticQConversionFails() const { + return true; } -/** - * Searchs for a given fit parameter within the a vector of columnNames - * @param suffix - The string to search for within the columnName - * @param columns - A vector of column names to be searched through - * @return A vector of all the column names that contained the given suffix - * string - */ -std::vector<std::string> ConvolutionFitSequential::searchForFitParams( - const std::string &suffix, const std::vector<std::string> &columns) { - auto fitParams = std::vector<std::string>(); - const size_t totalColumns = columns.size(); - for (size_t i = 0; i < totalColumns; i++) { - auto pos = columns.at(i).rfind(suffix); - if (pos != std::string::npos) { - auto endCheck = pos + suffix.size(); - if (endCheck == columns.at(i).size()) { - fitParams.push_back(columns.at(i)); - } - } - } - return fitParams; +bool ConvolutionFitSequential::isFitParameter(const std::string &name) const { + bool isBackgroundParameter = name.rfind("A0") != std::string::npos || + name.rfind("A1") != std::string::npos; + return name.rfind("Centre") == std::string::npos && !isBackgroundParameter; } -/** - * Squares all the values inside a vector of type double - * @param target - The vector to be squared - * @return The vector after being squared - */ -std::vector<double> -ConvolutionFitSequential::squareVector(std::vector<double> target) { - std::transform(target.begin(), target.end(), target.begin(), - VectorHelper::Squares<double>()); - return target; +ITableWorkspace_sptr +ConvolutionFitSequential::performFit(const std::string &input, + const std::string &output) { + auto parameterWorkspace = QENSFitSequential::performFit(input, output); + IFunction_sptr function = getProperty("Function"); + m_deltaUsed = containsFunction(function, "DeltaFunction"); + if (m_deltaUsed) + calculateEISF(parameterWorkspace); + return parameterWorkspace; } -/** - * Creates a vector of type double using the values of another vector - * @param original - The original vector to be cloned - * @return A clone of the original vector - */ -std::vector<double> -ConvolutionFitSequential::cloneVector(const std::vector<double> &original) { - return std::vector<double>(original.begin(), original.end()); +std::map<std::string, std::string> +ConvolutionFitSequential::getAdditionalLogStrings() const { + IFunction_sptr function = getProperty("Function"); + auto logs = QENSFitSequential::getAdditionalLogStrings(); + logs["delta_function"] = m_deltaUsed ? "true" : "false"; + logs["background"] = extractBackgroundType(function); + return logs; } -/** - * Converts the input workspaces to spectrum axis to ElasticQ and adds it to the - * ADS to be used by PlotPeakBylogValue - * @param inputWs - The MatrixWorkspace to be converted - * @param wsName - The desired name of the output workspace - */ -void ConvolutionFitSequential::convertInputToElasticQ( - API::MatrixWorkspace_sptr &inputWs, const std::string &wsName) { - auto axis = inputWs->getAxis(1); - if (axis->isSpectra()) { - auto convSpec = createChildAlgorithm("ConvertSpectrumAxis"); - // Store in ADS to allow use by PlotPeakByLogValue - convSpec->setAlwaysStoreInADS(true); - convSpec->setProperty("InputWorkSpace", inputWs); - convSpec->setProperty("OutputWorkSpace", wsName); - convSpec->setProperty("Target", "ElasticQ"); - convSpec->setProperty("EMode", "Indirect"); - convSpec->executeAsChildAlg(); - } else if (axis->isNumeric()) { - // Check that units are Momentum Transfer - if (axis->unit()->unitID() != "MomentumTransfer") { - throw std::runtime_error("Input must have axis values of Q"); - } - auto cloneWs = createChildAlgorithm("CloneWorkspace"); - // Store in ADS to allow use by PlotPeakByLogValue - cloneWs->setAlwaysStoreInADS(true); - cloneWs->setProperty("InputWorkspace", inputWs); - cloneWs->setProperty("OutputWorkspace", wsName); - cloneWs->executeAsChildAlg(); - } else { - throw std::runtime_error( - "Input workspace must have either spectra or numeric axis."); - } +std::map<std::string, std::string> +ConvolutionFitSequential::getAdditionalLogNumbers() const { + auto logs = QENSFitSequential::getAdditionalLogNumbers(); + IFunction_sptr function = getProperty("Function"); + logs["lorentzians"] = boost::lexical_cast<std::string>( + numberOfFunctions(function, "Lorentzian")); + return logs; } /** * Calculates the EISF if the fit includes a Delta function * @param tableWs - The TableWorkspace to append the EISF calculation to */ -void ConvolutionFitSequential::calculateEISF( - API::ITableWorkspace_sptr &tableWs) { +void ConvolutionFitSequential::calculateEISF(ITableWorkspace_sptr &tableWs) { // Get height data from parameter table - const auto columns = tableWs->getColumnNames(); - const auto height = searchForFitParams("Height", columns).at(0); - const auto heightErr = searchForFitParams("Height_Err", columns).at(0); + const auto height = searchForFitParameters("Height", tableWs).at(0); + const auto heightErr = searchForFitParameters("Height_Err", tableWs).at(0); auto heightY = tableWs->getColumn(height)->numeric_fill<>(); auto heightE = tableWs->getColumn(heightErr)->numeric_fill<>(); // Get amplitude column names - const auto ampNames = searchForFitParams("Amplitude", columns); - const auto ampErrorNames = searchForFitParams("Amplitude_Err", columns); + const auto ampIndices = searchForFitParameters("Amplitude", tableWs); + const auto ampErrorIndices = searchForFitParameters("Amplitude_Err", tableWs); // For each lorentzian, calculate EISF - size_t maxSize = ampNames.size(); - if (ampErrorNames.size() > maxSize) { - maxSize = ampErrorNames.size(); - } - for (size_t i = 0; i < maxSize; i++) { + auto maxSize = ampIndices.size(); + if (ampErrorIndices.size() > maxSize) + maxSize = ampErrorIndices.size(); + + for (auto i = 0u; i < maxSize; ++i) { // Get amplitude from column in table workspace - const auto ampName = ampNames.at(i); - auto ampY = tableWs->getColumn(ampName)->numeric_fill<>(); - const auto ampErrorName = ampErrorNames.at(i); - auto ampErr = tableWs->getColumn(ampErrorName)->numeric_fill<>(); - - // Calculate EISF and EISF error - // total = heightY + ampY - auto total = cloneVector(heightY); - std::transform(total.begin(), total.end(), ampY.begin(), total.begin(), - std::plus<double>()); - // eisfY = heightY / total - auto eisfY = cloneVector(heightY); - std::transform(eisfY.begin(), eisfY.end(), total.begin(), eisfY.begin(), - std::divides<double>()); - // heightE squared - auto heightESq = cloneVector(heightE); - heightESq = squareVector(heightESq); - // ampErr squared - auto ampErrSq = cloneVector(ampErr); - ampErrSq = squareVector(ampErrSq); - // totalErr = heightE squared + ampErr squared - auto totalErr = cloneVector(heightESq); - std::transform(totalErr.begin(), totalErr.end(), ampErrSq.begin(), - totalErr.begin(), std::plus<double>()); - // heightY squared - auto heightYSq = cloneVector(heightY); - heightYSq = squareVector(heightYSq); - // total Squared - auto totalSq = cloneVector(total); - totalSq = squareVector(totalSq); - // errOverTotalSq = totalErr / total squared - auto errOverTotalSq = cloneVector(totalErr); - std::transform(errOverTotalSq.begin(), errOverTotalSq.end(), - totalSq.begin(), errOverTotalSq.begin(), - std::divides<double>()); - // heightESqOverYSq = heightESq / heightYSq - auto heightESqOverYSq = cloneVector(heightESq); - std::transform(heightESqOverYSq.begin(), heightESqOverYSq.end(), - heightYSq.begin(), heightESqOverYSq.begin(), - std::divides<double>()); - // sqrtESqOverYSq = squareRoot( heightESqOverYSq ) - auto sqrtESqOverYSq = cloneVector(heightESqOverYSq); - std::transform(sqrtESqOverYSq.begin(), sqrtESqOverYSq.end(), - sqrtESqOverYSq.begin(), - static_cast<double (*)(double)>(sqrt)); - // eisfYSumRoot = eisfY * sqrtESqOverYSq - auto eisfYSumRoot = cloneVector(eisfY); - std::transform(eisfYSumRoot.begin(), eisfYSumRoot.end(), - sqrtESqOverYSq.begin(), eisfYSumRoot.begin(), - std::multiplies<double>()); - // eisfErr = eisfYSumRoot + errOverTotalSq - auto eisfErr = cloneVector(eisfYSumRoot); - std::transform(eisfErr.begin(), eisfErr.end(), errOverTotalSq.begin(), - eisfErr.begin(), std::plus<double>()); + auto ampY = tableWs->getColumn(ampIndices[i])->numeric_fill<>(); + auto ampErr = tableWs->getColumn(ampErrorIndices[i])->numeric_fill<>(); + auto eisfAndError = calculateEISFAndError(heightY, heightE, ampY, ampErr); // Append the calculated values to the table workspace + auto ampName = tableWs->getColumn(ampIndices[i])->name(); + auto ampErrorName = tableWs->getColumn(ampErrorIndices[i])->name(); auto columnName = ampName.substr(0, (ampName.size() - std::string("Amplitude").size())); columnName += "EISF"; @@ -609,87 +318,18 @@ void ConvolutionFitSequential::calculateEISF( tableWs->addColumn("double", columnName); tableWs->addColumn("double", errorColumnName); - size_t maxEisf = eisfY.size(); - if (eisfErr.size() > maxEisf) { - maxEisf = eisfErr.size(); + auto maxEisf = eisfAndError.first.size(); + if (eisfAndError.second.size() > maxEisf) { + maxEisf = eisfAndError.second.size(); } - Column_sptr col = tableWs->getColumn(columnName); - Column_sptr errCol = tableWs->getColumn(errorColumnName); - for (size_t j = 0; j < maxEisf; j++) { - col->cell<double>(j) = eisfY.at(j); - errCol->cell<double>(j) = eisfErr.at(j); - } - } -} - -/* - * Extracts the convolution fit members from the specified result group - * workspace, given the specified input workspace used for the fit, each into a - * workspace, stored inside a group workspace of the specified name. - * - * @param inputWs The input workspace used in the convolution fit. - * @param resultGroupWs The result group workspace produced by the convolution - * fit; from which to extract the members. - * @param outputWsName The name of the output group workspace to store the - * member workspaces. - */ -void ConvolutionFitSequential::extractMembers( - MatrixWorkspace_sptr inputWs, WorkspaceGroup_sptr resultGroupWs, - const std::vector<std::string> &convolvedMembers, - const std::string &outputWsName) { - bool convolved = getProperty("Convolve"); - auto extractMembersAlg = - AlgorithmManager::Instance().create("ExtractQENSMembers"); - extractMembersAlg->setProperty("InputWorkspace", inputWs); - extractMembersAlg->setProperty("ResultWorkspace", resultGroupWs); - extractMembersAlg->setProperty("OutputWorkspace", outputWsName); - extractMembersAlg->setProperty("RenameConvolvedMembers", convolved); - extractMembersAlg->setProperty("ConvolvedMembers", convolvedMembers); - extractMembersAlg->execute(); -} - -/** - * Converts the user input for background into short hand for use in the - * workspace naming - * @param original - The original user input to the function - * @return The short hand of the users input - */ -std::string -ConvolutionFitSequential::convertBackToShort(const std::string &original) { - auto result = original.substr(0, 3); - const auto pos = original.find(' '); - if (pos != std::string::npos) { - result += original.at(pos + 1); - } - return result; -} - -/** - * Converts the user input for function into short hand for use in the workspace - * naming - * @param original - The original user input to the function - * @return The short hand of the users input - */ -std::string -ConvolutionFitSequential::convertFuncToShort(const std::string &original) { - std::string result; - if (original != "DeltaFunction") { - if (original.at(0) == 'E') { - result += "E"; - } else if (original.at(0) == 'I') { - result += "I"; - } else { - return "SFT"; - } - const auto pos = original.find("Circle"); - if (pos != std::string::npos) { - result += "DC"; - } else { - result += "DS"; + auto col = tableWs->getColumn(columnName); + auto errCol = tableWs->getColumn(errorColumnName); + for (auto j = 0u; j < maxEisf; j++) { + col->cell<double>(j) = eisfAndError.first.at(j); + errCol->cell<double>(j) = eisfAndError.second.at(j); } } - return result; } } // namespace Algorithms diff --git a/Framework/WorkflowAlgorithms/src/ExtractQENSMembers.cpp b/Framework/WorkflowAlgorithms/src/ExtractQENSMembers.cpp index 8bf115c60875d9a038c73a547187454d82fd4e8e..6c792e113c6ec46fed8c2ab2ac5dd5867ac5b1fb 100644 --- a/Framework/WorkflowAlgorithms/src/ExtractQENSMembers.cpp +++ b/Framework/WorkflowAlgorithms/src/ExtractQENSMembers.cpp @@ -1,5 +1,6 @@ #include "MantidWorkflowAlgorithms/ExtractQENSMembers.h" +#include "MantidAPI/ADSValidator.h" #include "MantidAPI/Axis.h" #include "MantidAPI/NumericAxis.h" @@ -45,8 +46,13 @@ const std::string ExtractQENSMembers::summary() const { */ void ExtractQENSMembers::init() { declareProperty( - make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input), - "The input workspace used in the fit."); + make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input, + PropertyMode::Optional), + "The input workspace used in the fit. Ignored if 'InputWorkspaces' " + "property is provided."); + declareProperty( + make_unique<ArrayProperty<std::string>>("InputWorkspaces", ""), + "List of the workspaces used in the fit."); declareProperty(make_unique<WorkspaceProperty<WorkspaceGroup>>( "ResultWorkspace", "", Direction::Input), "The result group workspace produced in a QENS fit."); @@ -63,12 +69,23 @@ void ExtractQENSMembers::init() { "The output workspace group, containing the fit members."); } +std::map<std::string, std::string> ExtractQENSMembers::validateInputs() { + std::map<std::string, std::string> errors; + std::vector<std::string> workspaceNames = getProperty("InputWorkspaces"); + MatrixWorkspace_sptr inputWorkspace = getProperty("InputWorkspace"); + + if (workspaceNames.empty() && !inputWorkspace) + errors["InputWorkspace"] = "Neither the InputWorkspace or InputWorkspaces " + "property have been defined."; + return errors; +} + void ExtractQENSMembers::exec() { - MatrixWorkspace_sptr inputWS = getProperty("InputWorkspace"); + auto inputWorkspaces = getInputWorkspaces(); WorkspaceGroup_sptr resultWS = getProperty("ResultWorkspace"); MatrixWorkspace_sptr initialWS = boost::dynamic_pointer_cast<MatrixWorkspace>(resultWS->getItem(0)); - auto qValues = getQValues(inputWS); + const auto qValues = getQValues(inputWorkspaces); auto members = getAxisLabels(initialWS, 1); bool renameConvolved = getProperty("RenameConvolvedMembers"); @@ -89,18 +106,43 @@ void ExtractQENSMembers::exec() { setProperty("OutputWorkspace", groupWorkspaces(workspaceNames)); } +std::vector<MatrixWorkspace_sptr> +ExtractQENSMembers::getInputWorkspaces() const { + std::vector<MatrixWorkspace_sptr> workspaces; + std::vector<std::string> workspaceNames = getProperty("InputWorkspaces"); + + for (const auto &name : workspaceNames) + workspaces.emplace_back( + AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(name)); + + if (!workspaces.empty()) { + workspaces.reserve(workspaceNames.size()); + for (const auto &name : workspaceNames) + workspaces.emplace_back( + AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(name)); + } else + workspaces.push_back(getProperty("InputWorkspace")); + return workspaces; +} + /** * Extracts the Q-Values from the specified workspace. * - * @param workspace The workspace whose Q-Values to extract. - * @return The extracted Q-Values. + * @param workspaces The workspaces whose Q-Values to extract. + * @return The extracted Q-Values. */ -std::vector<double> -ExtractQENSMembers::getQValues(MatrixWorkspace_sptr workspace) { - auto getQs = createChildAlgorithm("GetQsInQENSData", -1.0, -1.0, false); - getQs->setProperty("InputWorkspace", workspace); - getQs->executeAsChildAlg(); - return getQs->getProperty("Qvalues"); +std::vector<double> ExtractQENSMembers::getQValues( + const std::vector<MatrixWorkspace_sptr> &workspaces) { + std::vector<double> qValues; + + for (const auto &workspace : workspaces) { + auto getQs = createChildAlgorithm("GetQsInQENSData", -1.0, -1.0, false); + getQs->setProperty("InputWorkspace", workspace); + getQs->executeAsChildAlg(); + const std::vector<double> values = getQs->getProperty("Qvalues"); + qValues.insert(std::end(qValues), std::begin(values), std::end(values)); + } + return qValues; } /** diff --git a/Framework/WorkflowAlgorithms/src/IqtFitSequential.cpp b/Framework/WorkflowAlgorithms/src/IqtFitSequential.cpp new file mode 100644 index 0000000000000000000000000000000000000000..82c0ae4a30d3c831ff2f42034016109b03d56932 --- /dev/null +++ b/Framework/WorkflowAlgorithms/src/IqtFitSequential.cpp @@ -0,0 +1,90 @@ +#include "MantidWorkflowAlgorithms/IqtFitSequential.h" + +using namespace Mantid::API; + +namespace { +Mantid::Kernel::Logger g_log("IqtFitSequential"); + +MatrixWorkspace_sptr cropWorkspace(MatrixWorkspace_sptr workspace, + double startX, double endX) { + auto cropper = AlgorithmManager::Instance().create("CropWorkspace"); + cropper->setLogging(false); + cropper->setAlwaysStoreInADS(false); + cropper->setProperty("InputWorkspace", workspace); + cropper->setProperty("OutputWorkspace", "__cropped"); + cropper->setProperty("XMin", startX); + cropper->setProperty("XMax", endX); + cropper->execute(); + return cropper->getProperty("OutputWorkspace"); +} + +MatrixWorkspace_sptr convertToHistogram(MatrixWorkspace_sptr workspace) { + auto converter = AlgorithmManager::Instance().create("ConvertToHistogram"); + converter->setLogging(false); + converter->setAlwaysStoreInADS(false); + converter->setProperty("InputWorkspace", workspace); + converter->setProperty("OutputWorkspace", "__converted"); + converter->execute(); + return converter->getProperty("OutputWorkspace"); +} +} // namespace + +namespace Mantid { +namespace Algorithms { + +using namespace API; + +// Register the algorithm into the AlgorithmFactory +DECLARE_ALGORITHM(IqtFitSequential) + +/// Algorithms name for identification. @see Algorithm::name +const std::string IqtFitSequential::name() const { return "IqtFitSequential"; } + +/// Algorithm's version for identification. @see Algorithm::version +int IqtFitSequential::version() const { return 1; } + +/// Algorithm's category for identification. @see Algorithm::category +const std::string IqtFitSequential::category() const { + return "Workflow\\MIDAS"; +} + +/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary +const std::string IqtFitSequential::summary() const { + return "Fits an \\*\\_iqt file generated by I(Q, t) sequentially."; +} + +/// Algorithm's see also for related algorithms. @see Algorithm::seeAlso +const std::vector<std::string> IqtFitSequential::seeAlso() const { + return {"QENSFitSequential"}; +} + +std::map<std::string, std::string> IqtFitSequential::validateInputs() { + auto errors = QENSFitSequential::validateInputs(); + double startX = getProperty("StartX"); + if (startX < 0) + errors["StartX"] = "StartX must be greater than or equal to 0."; + return errors; +} + +bool IqtFitSequential::isFitParameter(const std::string &name) const { + return name.rfind("A0") != std::string::npos || + name.rfind("Height") != std::string::npos || + name.rfind("Stretching") != std::string::npos || + name.rfind("Lifetime") != std::string::npos; +} + +bool IqtFitSequential::throwIfElasticQConversionFails() const { return true; } + +std::vector<API::MatrixWorkspace_sptr> IqtFitSequential::getWorkspaces() const { + auto workspaces = QENSFitSequential::getWorkspaces(); + const double startX = getProperty("StartX"); + const double endX = getProperty("EndX"); + + for (auto i = 0u; i < workspaces.size(); ++i) + workspaces[i] = + convertToHistogram(cropWorkspace(workspaces[i], startX, endX)); + return workspaces; +} + +} // namespace Algorithms +} // namespace Mantid diff --git a/Framework/WorkflowAlgorithms/src/ProcessIndirectFitParameters.cpp b/Framework/WorkflowAlgorithms/src/ProcessIndirectFitParameters.cpp index d1aa464a75cdfdf73d0ac5981e1f4ef5a37f5c16..ec98c9067d7fdfdb14a9bbac37d50c1b46be519e 100644 --- a/Framework/WorkflowAlgorithms/src/ProcessIndirectFitParameters.cpp +++ b/Framework/WorkflowAlgorithms/src/ProcessIndirectFitParameters.cpp @@ -4,10 +4,10 @@ #include "MantidAPI/MatrixWorkspace.h" #include "MantidAPI/TextAxis.h" -#include "MantidKernel/MandatoryValidator.h" +#include "MantidKernel/ArrayProperty.h" #include "MantidKernel/ListValidator.h" +#include "MantidKernel/MandatoryValidator.h" #include "MantidKernel/UnitFactory.h" -#include "MantidKernel/VectorHelper.h" namespace Mantid { namespace Algorithms { @@ -55,10 +55,11 @@ void ProcessIndirectFitParameters::init() { "ColumnX", "", boost::make_shared<MandatoryValidator<std::string>>(), "The column in the table to use for the x values.", Direction::Input); - declareProperty("ParameterNames", "", - boost::make_shared<MandatoryValidator<std::string>>(), - "List of the parameter names to add to the workspace.", - Direction::Input); + declareProperty( + make_unique<ArrayProperty<std::string>>( + "ParameterNames", + boost::make_shared<MandatoryValidator<std::vector<std::string>>>()), + "List of the parameter names to add to the workspace."); declareProperty("XAxisUnit", "", boost::make_shared<StringListValidator>(unitOptions), @@ -76,10 +77,7 @@ void ProcessIndirectFitParameters::exec() { // Get Properties ITableWorkspace_sptr inputWs = getProperty("InputWorkspace"); std::string xColumn = getProperty("ColumnX"); - std::string parameterNamesProp = getProperty("ParameterNames"); - auto parameterNames = - Kernel::VectorHelper::splitStringIntoVector<std::string>( - parameterNamesProp); + std::vector<std::string> parameterNames = getProperty("ParameterNames"); std::string xUnit = getProperty("XAxisUnit"); MatrixWorkspace_sptr outputWs = getProperty("OutputWorkspace"); diff --git a/Framework/WorkflowAlgorithms/src/QENSFitSequential.cpp b/Framework/WorkflowAlgorithms/src/QENSFitSequential.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0adcb72039b1bdb61758c766eaa86e75b180f8eb --- /dev/null +++ b/Framework/WorkflowAlgorithms/src/QENSFitSequential.cpp @@ -0,0 +1,695 @@ +#include "MantidWorkflowAlgorithms/QENSFitSequential.h" + +#include "MantidAPI/AlgorithmManager.h" +#include "MantidAPI/Axis.h" +#include "MantidAPI/CompositeFunction.h" +#include "MantidAPI/CostFunctionFactory.h" +#include "MantidAPI/FunctionProperty.h" +#include "MantidAPI/IFunction.h" +#include "MantidKernel/BoundedValidator.h" +#include "MantidKernel/ListValidator.h" +#include "MantidKernel/MandatoryValidator.h" + +#include <boost/cast.hpp> +#include <boost/regex.hpp> + +#include <sstream> +#include <stdexcept> +#include <unordered_map> + +namespace { +using namespace Mantid::API; +using namespace Mantid::Kernel; + +MatrixWorkspace_sptr convertSpectrumAxis(MatrixWorkspace_sptr inputWorkspace, + const std::string &outputName) { + auto convSpec = AlgorithmManager::Instance().create("ConvertSpectrumAxis"); + convSpec->setLogging(false); + convSpec->setProperty("InputWorkspace", inputWorkspace); + convSpec->setProperty("OutputWorkspace", outputName); + convSpec->setProperty("Target", "ElasticQ"); + convSpec->setProperty("EMode", "Indirect"); + convSpec->execute(); + // Attempting to use getProperty("OutputWorkspace") on algorithm results in a + // nullptr being returned + return AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>( + outputName); +} + +MatrixWorkspace_sptr cloneWorkspace(MatrixWorkspace_sptr inputWorkspace, + const std::string &outputName) { + Workspace_sptr workspace = inputWorkspace->clone(); + AnalysisDataService::Instance().addOrReplace(outputName, workspace); + return boost::dynamic_pointer_cast<MatrixWorkspace>(workspace); +} + +MatrixWorkspace_sptr convertToElasticQ(MatrixWorkspace_sptr inputWorkspace, + const std::string &outputName, + bool doThrow) { + auto axis = inputWorkspace->getAxis(1); + if (axis->isSpectra()) + return convertSpectrumAxis(inputWorkspace, outputName); + else if (axis->isNumeric()) { + if (axis->unit()->unitID() != "MomentumTransfer" && doThrow) + throw std::runtime_error("Input must have axis values of Q"); + return cloneWorkspace(inputWorkspace, outputName); + } else if (doThrow) + throw std::runtime_error( + "Input workspace must have either spectra or numeric axis."); + return cloneWorkspace(inputWorkspace, outputName); +} + +struct ElasticQAppender { + explicit ElasticQAppender(std::vector<MatrixWorkspace_sptr> &elasticInput) + : m_elasticInput(elasticInput), m_converted() {} + + void operator()(MatrixWorkspace_sptr workspace, const std::string &outputBase, + bool doThrow) { + auto it = m_converted.find(workspace.get()); + if (it != m_converted.end()) + m_elasticInput.emplace_back(it->second); + else { + auto elasticQ = convertToElasticQ( + workspace, outputBase + std::to_string(m_converted.size() + 1), + doThrow); + m_elasticInput.emplace_back(elasticQ); + m_converted[workspace.get()] = elasticQ; + } + } + +private: + std::vector<MatrixWorkspace_sptr> &m_elasticInput; + std::unordered_map<MatrixWorkspace *, MatrixWorkspace_sptr> m_converted; +}; + +std::vector<MatrixWorkspace_sptr> +convertToElasticQ(const std::vector<MatrixWorkspace_sptr> &workspaces, + const std::string &outputBaseName, bool doThrow) { + std::vector<MatrixWorkspace_sptr> elasticInput; + auto appendElasticQWorkspace = ElasticQAppender(elasticInput); + appendElasticQWorkspace(workspaces[0], outputBaseName, doThrow); + + for (auto i = 1u; i < workspaces.size(); ++i) + appendElasticQWorkspace(workspaces[i], outputBaseName, doThrow); + return elasticInput; +} + +void extractFunctionNames(CompositeFunction_sptr composite, + std::vector<std::string> &names) { + for (auto i = 0u; i < composite->nFunctions(); ++i) + names.emplace_back(composite->getFunction(i)->name()); +} + +void extractFunctionNames(IFunction_sptr function, + std::vector<std::string> &names) { + auto composite = boost::dynamic_pointer_cast<CompositeFunction>(function); + if (composite) + extractFunctionNames(composite, names); + else + names.emplace_back(function->name()); +} + +void extractConvolvedNames(IFunction_sptr function, + std::vector<std::string> &names); + +void extractConvolvedNames(CompositeFunction_sptr composite, + std::vector<std::string> &names) { + for (auto i = 0u; i < composite->nFunctions(); ++i) + extractConvolvedNames(composite->getFunction(i), names); +} + +void extractConvolvedNames(IFunction_sptr function, + std::vector<std::string> &names) { + auto composite = boost::dynamic_pointer_cast<CompositeFunction>(function); + if (composite) { + if (composite->name() == "Convolution" && composite->nFunctions() > 1 && + composite->getFunction(0)->name() == "Resolution") + extractFunctionNames(composite->getFunction(1), names); + else + extractConvolvedNames(composite, names); + } +} + +std::string constructInputString(MatrixWorkspace_sptr workspace, int specMin, + int specMax) { + std::ostringstream input; + for (auto i = specMin; i < specMax + 1; ++i) + input << workspace->getName() << ",i" << std::to_string(i) << ";"; + return input.str(); +} + +std::vector<MatrixWorkspace_sptr> extractWorkspaces(const std::string &input) { + std::vector<MatrixWorkspace_sptr> workspaces; + + auto extractWorkspace = [&](const std::string &name) { + workspaces.emplace_back( + AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(name)); + }; + + boost::regex reg("([^,;]+),"); + std::for_each( + boost::sregex_token_iterator(input.begin(), input.end(), reg, 1), + boost::sregex_token_iterator(), extractWorkspace); + return workspaces; +} + +std::vector<std::string> getSpectra(const std::string &input) { + std::vector<std::string> spectra; + boost::regex reg(",[i|sp](0|[1-9][0-9]*);?"); + std::copy(boost::sregex_token_iterator(input.begin(), input.end(), reg, 1), + boost::sregex_token_iterator(), std::back_inserter(spectra)); + return spectra; +} + +std::vector<std::string> getSuffices(const std::string &input) { + std::vector<std::string> suffices; + boost::regex reg(",[i|sp](0|[1-9][0-9]*);?"); + std::copy(boost::sregex_token_iterator(input.begin(), input.end(), reg, 0), + boost::sregex_token_iterator(), std::back_inserter(suffices)); + return suffices; +} + +std::string +replaceWorkspaces(const std::string &input, + const std::vector<MatrixWorkspace_sptr> &workspaces) { + const auto suffices = getSuffices(input); + std::stringstream newInput; + for (auto i = 0u; i < workspaces.size(); ++i) + newInput << workspaces[i]->getName() << suffices[i]; + return newInput.str(); +} + +void renameWorkspace(IAlgorithm_sptr renamer, Workspace_sptr workspace, + const std::string &newName) { + renamer->setProperty("InputWorkspace", workspace); + renamer->setProperty("OutputWorkspace", newName); + renamer->executeAsChildAlg(); +} + +void deleteTemporaries(IAlgorithm_sptr deleter, const std::string &base) { + auto name = base + std::to_string(1); + std::size_t i = 2; + + while (AnalysisDataService::Instance().doesExist(name)) { + deleter->setProperty("Workspace", name); + deleter->executeAsChildAlg(); + name = base + std::to_string(i++); + } +} + +std::string shortParameterName(const std::string &longName) { + return longName.substr(longName.rfind('.') + 1, longName.size()); +} + +bool containsMultipleData(const std::vector<MatrixWorkspace_sptr> &workspaces) { + const auto &first = workspaces.front(); + for (const auto &workspace : workspaces) { + if (workspace != first) + return true; + } + return false; +} + +template <typename F, typename Renamer> +void renameWorkspacesWith(WorkspaceGroup_sptr groupWorkspace, F const &getName, + Renamer const &renamer) { + std::unordered_map<std::string, std::size_t> nameCount; + for (auto i = 0u; i < groupWorkspace->size(); ++i) { + const auto name = getName(i); + auto count = nameCount.find(name); + + if (count == nameCount.end()) { + renamer(groupWorkspace->getItem(i), name); + nameCount[name] = 1; + } else + renamer(groupWorkspace->getItem(i), + name + "(" + std::to_string(++count->second) + ")"); + } +} + +template <typename F> +void renameWorkspacesInQENSFit(Algorithm *qensFit, + IAlgorithm_sptr renameAlgorithm, + WorkspaceGroup_sptr outputGroup, + const F &getNameSuffix) { + const auto groupName = qensFit->getPropertyValue("OutputWorkspaceGroup"); + auto outputBase = groupName.substr(0, groupName.rfind("_Workspaces")); + + Progress renamerProg(qensFit, 0.98, 1.0, outputGroup->size() + 1); + renamerProg.report("Renaming group workspaces..."); + + auto getName = + [&](std::size_t i) { return outputBase + "_" + getNameSuffix(i); }; + + auto renamer = [&](Workspace_sptr workspace, const std::string &name) { + renameWorkspace(renameAlgorithm, workspace, name); + renamerProg.report("Renamed workspace in group."); + }; + renameWorkspacesWith(outputGroup, getName, renamer); + + if (outputGroup->getName() != groupName) + renameWorkspace(renameAlgorithm, outputGroup, groupName); +} +} // namespace + +namespace Mantid { +namespace Algorithms { + +using namespace API; +using namespace Kernel; + +// Register the algorithm into the AlgorithmFactory +DECLARE_ALGORITHM(QENSFitSequential) + +/// Algorithms name for identification. @see Algorithm::name +const std::string QENSFitSequential::name() const { + return "QENSFitSequential"; +} + +/// Algorithm's version for identification. @see Algorithm::version +int QENSFitSequential::version() const { return 1; } + +/// Algorithm's category for identification. @see Algorithm::category +const std::string QENSFitSequential::category() const { + return "Workflow\\MIDAS"; +} + +/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary +const std::string QENSFitSequential::summary() const { + return "Performs a sequential fit for QENS data"; +} + +/// Algorithm's see also for related algorithms. @see Algorithm::seeAlso +const std::vector<std::string> QENSFitSequential::seeAlso() const { + return {"ConvolutionFitSequential", "IqtFitSequential", "PlotPeakByLogValue"}; +} + +void QENSFitSequential::init() { + declareProperty( + make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input, + PropertyMode::Optional), + "The input workspace for the fit. This property will be ignored if " + "'Input' is provided."); + + auto boundedV = boost::make_shared<BoundedValidator<int>>(); + boundedV->setLower(0); + + declareProperty( + "SpecMin", 0, boundedV, + "The first spectrum to be used in " + "the fit. Spectra values can not be " + "negative. This property will be ignored if 'Input' is provided.", + Direction::Input); + + declareProperty( + "SpecMax", 0, boundedV, + "The final spectrum to be used in " + "the fit. Spectra values can not be " + "negative. This property will be ignored if 'Input' is provided.", + Direction::Input); + + declareProperty( + "Input", "", + "A list of sources of data to fit. \n" + "Sources can be either workspace names or file names followed optionally " + "by a list of spectra/workspace-indices \n" + "or values using the notation described in the description section of " + "the help page."); + + declareProperty(make_unique<WorkspaceProperty<MatrixWorkspace>>( + "OutputWorkspace", "", Direction::Output), + "The output result workspace"); + declareProperty(make_unique<WorkspaceProperty<ITableWorkspace>>( + "OutputParameterWorkspace", "", Direction::Output, + PropertyMode::Optional), + "The output parameter workspace"); + declareProperty(make_unique<WorkspaceProperty<WorkspaceGroup>>( + "OutputWorkspaceGroup", "", Direction::Output, + PropertyMode::Optional), + "The output group workspace"); + + declareProperty( + make_unique<FunctionProperty>("Function"), + "The fitting function, common for all workspaces in the input."); + declareProperty("LogValue", "", "Name of the log value to plot the " + "parameters against. Default: use spectra " + "numbers."); + declareProperty("StartX", EMPTY_DBL(), "A value of x in, or on the low x " + "boundary of, the first bin to " + "include in\n" + "the fit (default lowest value of x)"); + declareProperty("EndX", EMPTY_DBL(), "A value in, or on the high x boundary " + "of, the last bin the fitting range\n" + "(default the highest value of x)"); + + declareProperty("PassWSIndexToFunction", false, + "For each spectrum in Input pass its workspace index to all " + "functions that" + "have attribute WorkspaceIndex."); + + declareProperty("Minimizer", "Levenberg-Marquardt", + "Minimizer to use for fitting. Minimizers available are " + "'Levenberg-Marquardt', 'Simplex', 'FABADA',\n" + "'Conjugate gradient (Fletcher-Reeves imp.)', 'Conjugate " + "gradient (Polak-Ribiere imp.)' and 'BFGS'"); + + const std::vector<std::string> costFuncOptions = + CostFunctionFactory::Instance().getKeys(); + declareProperty("CostFunction", "Least squares", + boost::make_shared<StringListValidator>(costFuncOptions), + "Cost functions to use for fitting. Cost functions available " + "are 'Least squares' and 'Ignore positive peaks'", + Direction::InOut); + + declareProperty("MaxIterations", 500, boundedV, + "Stop after this number of iterations if a good fit is not " + "found"); + declareProperty("PeakRadius", 0, + "A value of the peak radius the peak functions should use. A " + "peak radius defines an interval on the x axis around the " + "centre of the peak where its values are calculated. Values " + "outside the interval are not calculated and assumed zeros." + "Numerically the radius is a whole number of peak widths " + "(FWHM) that fit into the interval on each side from the " + "centre. The default value of 0 means the whole x axis."); + + declareProperty( + "ExtractMembers", false, + "If true, then each member of the convolution fit will be extracted" + ", into their own workspace. These workspaces will have a histogram" + " for each spectrum (Q-value) and will be grouped.", + Direction::Input); + + declareProperty( + make_unique<Kernel::PropertyWithValue<bool>>("ConvolveMembers", false), + "If true and ExtractMembers is true members of any " + "Convolution are output convolved\n" + "with corresponding resolution"); + + const std::array<std::string, 2> evaluationTypes = { + {"CentrePoint", "Histogram"}}; + declareProperty( + "EvaluationType", "CentrePoint", + Kernel::IValidator_sptr( + new Kernel::ListValidator<std::string>(evaluationTypes)), + "The way the function is evaluated: CentrePoint or Histogram.", + Kernel::Direction::Input); +} + +std::map<std::string, std::string> QENSFitSequential::validateInputs() { + std::map<std::string, std::string> errors; + + if (getPropertyValue("Input").empty()) { + MatrixWorkspace_sptr workspace = getProperty("InputWorkspace"); + if (!workspace) + errors["InputWorkspace"] = + "No input string or input workspace was provided."; + + const int specMin = getProperty("SpecMin"); + const int specMax = getProperty("SpecMax"); + if (specMin > specMax) + errors["SpecMin"] = "SpecMin must be less than or equal to SpecMax."; + } + + const double startX = getProperty("StartX"); + const double endX = getProperty("EndX"); + if (startX >= endX) + errors["StartX"] = "StartX must be less than EndX"; + + return errors; +} + +void QENSFitSequential::exec() { + const auto outputBaseName = getOutputBaseName(); + + if (getPropertyValue("OutputParameterWorkspace").empty()) + setProperty("OutputParameterWorkspace", outputBaseName + "_Parameters"); + + if (getPropertyValue("OutputWorkspaceGroup").empty()) + setProperty("OutputWorkspaceGroup", outputBaseName + "_Workspaces"); + + const auto inputWorkspaces = getWorkspaces(); + const auto workspaces = convertInputToElasticQ(inputWorkspaces); + const auto inputString = getInputString(workspaces); + const auto spectra = getSpectra(inputString); + + if (workspaces.empty() || spectra.empty() || + (workspaces.size() > 1 && workspaces.size() != spectra.size())) + throw std::invalid_argument("A malformed input string was provided."); + + const auto outputWs = performFit(inputString, outputBaseName); + const auto resultWs = processIndirectFitParameters(outputWs); + const auto groupWs = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>( + outputBaseName + "_Workspaces"); + AnalysisDataService::Instance().addOrReplace( + getPropertyValue("OutputWorkspace"), resultWs); + + if (containsMultipleData(workspaces)) + renameWorkspaces(groupWs, spectra, inputWorkspaces); + else + renameWorkspaces(groupWs, spectra); + copyLogs(resultWs, workspaces); + + const bool doExtractMembers = getProperty("ExtractMembers"); + if (doExtractMembers) + extractMembers(groupWs, workspaces, outputBaseName + "_Members"); + + deleteTemporaryWorkspaces(outputBaseName); + + addAdditionalLogs(resultWs); + copyLogs(resultWs, groupWs); + + setProperty("OutputWorkspace", resultWs); + setProperty("OutputParameterWorkspace", outputWs); + setProperty("OutputWorkspaceGroup", groupWs); +} + +std::map<std::string, std::string> +QENSFitSequential::getAdditionalLogStrings() const { + const bool convolve = getProperty("ConvolveMembers"); + auto fitProgram = name(); + fitProgram = fitProgram.substr(0, fitProgram.rfind("Sequential")); + + auto logs = std::map<std::string, std::string>(); + logs["sample_filename"] = getPropertyValue("InputWorkspace"); + logs["convolve_members"] = convolve ? "true" : "false"; + logs["fit_program"] = fitProgram; + logs["fit_mode"] = "Sequential"; + return logs; +} + +std::map<std::string, std::string> +QENSFitSequential::getAdditionalLogNumbers() const { + std::map<std::string, std::string> logs; + logs["start_x"] = getPropertyValue("StartX"); + logs["end_x"] = getPropertyValue("EndX"); + return logs; +} + +void QENSFitSequential::addAdditionalLogs( + MatrixWorkspace_sptr resultWorkspace) { + auto logAdder = createChildAlgorithm("AddSampleLog", -1.0, -1.0, false); + logAdder->setProperty("Workspace", resultWorkspace); + + Progress logAdderProg(this, 0.99, 1.00, 6); + logAdder->setProperty("LogType", "String"); + for (const auto &log : getAdditionalLogStrings()) { + logAdder->setProperty("LogName", log.first); + logAdder->setProperty("LogText", log.second); + logAdder->executeAsChildAlg(); + logAdderProg.report("Add text logs"); + } + + logAdder->setProperty("LogType", "Number"); + for (const auto &log : getAdditionalLogNumbers()) { + logAdder->setProperty("LogName", log.first); + logAdder->setProperty("LogText", log.second); + logAdder->executeAsChildAlg(); + logAdderProg.report("Add number logs"); + } +} + +std::string QENSFitSequential::getOutputBaseName() const { + const auto base = getPropertyValue("OutputWorkspace"); + const auto position = base.rfind("_Result"); + if (position != std::string::npos) + return base.substr(0, position); + return base; +} + +bool QENSFitSequential::throwIfElasticQConversionFails() const { return false; } + +bool QENSFitSequential::isFitParameter(const std::string &) const { + return true; +} + +std::vector<std::string> QENSFitSequential::getFitParameterNames() const { + const auto uniqueParameters = getUniqueParameterNames(); + std::vector<std::string> parameters; + parameters.reserve(uniqueParameters.size()); + std::copy_if( + uniqueParameters.begin(), uniqueParameters.end(), + std::back_inserter(parameters), + [&](const std::string ¶meter) { return isFitParameter(parameter); }); + return parameters; +} + +std::set<std::string> QENSFitSequential::getUniqueParameterNames() const { + IFunction_sptr function = getProperty("Function"); + std::set<std::string> nameSet; + for (auto i = 0u; i < function->nParams(); ++i) + nameSet.insert(shortParameterName(function->parameterName(i))); + return nameSet; +} + +void QENSFitSequential::deleteTemporaryWorkspaces( + const std::string &outputBaseName) { + auto deleter = createChildAlgorithm("DeleteWorkspace", -1.0, -1.0, false); + deleter->setProperty("Workspace", + outputBaseName + "_NormalisedCovarianceMatrices"); + deleter->executeAsChildAlg(); + + deleter->setProperty("Workspace", outputBaseName + "_Parameters"); + deleter->executeAsChildAlg(); + + deleteTemporaries(deleter, getTemporaryName()); +} + +MatrixWorkspace_sptr QENSFitSequential::processIndirectFitParameters( + ITableWorkspace_sptr parameterWorkspace) { + auto pifp = + createChildAlgorithm("ProcessIndirectFitParameters", 0.91, 0.95, true); + pifp->setProperty("InputWorkspace", parameterWorkspace); + pifp->setProperty("ColumnX", "axis-1"); + pifp->setProperty("XAxisUnit", "MomentumTransfer"); + pifp->setProperty("ParameterNames", getFitParameterNames()); + pifp->setProperty("OutputWorkspace", "__Result"); + pifp->executeAsChildAlg(); + return pifp->getProperty("OutputWorkspace"); +} + +void QENSFitSequential::renameWorkspaces( + WorkspaceGroup_sptr outputGroup, const std::vector<std::string> &spectra, + const std::vector<MatrixWorkspace_sptr> &inputWorkspaces) { + auto rename = createChildAlgorithm("RenameWorkspace", -1.0, -1.0, false); + const auto getNameSuffix = [&](std::size_t i) { + return inputWorkspaces[i]->getName() + "_" + spectra[i] + "_Workspace"; + }; + return renameWorkspacesInQENSFit(this, rename, outputGroup, getNameSuffix); +} + +void QENSFitSequential::renameWorkspaces( + WorkspaceGroup_sptr outputGroup, const std::vector<std::string> &spectra) { + auto rename = createChildAlgorithm("RenameWorkspace", -1.0, -1.0, false); + auto getNameSuffix = [&](std::size_t i) { return spectra[i] + "_Workspace"; }; + return renameWorkspacesInQENSFit(this, rename, outputGroup, getNameSuffix); +} + +ITableWorkspace_sptr QENSFitSequential::performFit(const std::string &input, + const std::string &output) { + const bool convolveMembers = getProperty("ConvolveMembers"); + const bool passWsIndex = getProperty("PassWSIndexToFunction"); + + // Run PlotPeaksByLogValue + auto plotPeaks = createChildAlgorithm("PlotPeakByLogValue", 0.05, 0.90, true); + plotPeaks->setProperty("Input", input); + plotPeaks->setProperty("OutputWorkspace", output); + plotPeaks->setPropertyValue("Function", getPropertyValue("Function")); + plotPeaks->setProperty("StartX", getPropertyValue("StartX")); + plotPeaks->setProperty("EndX", getPropertyValue("EndX")); + plotPeaks->setProperty("FitType", "Sequential"); + plotPeaks->setProperty("CreateOutput", true); + plotPeaks->setProperty("OutputCompositeMembers", true); + plotPeaks->setProperty("ConvolveMembers", convolveMembers); + plotPeaks->setProperty("MaxIterations", getPropertyValue("MaxIterations")); + plotPeaks->setProperty("Minimizer", getPropertyValue("Minimizer")); + plotPeaks->setProperty("PassWSIndexToFunction", passWsIndex); + plotPeaks->setProperty("PeakRadius", getPropertyValue("PeakRadius")); + plotPeaks->setProperty("LogValue", getPropertyValue("LogValue")); + plotPeaks->setProperty("EvaluationType", getPropertyValue("EvaluationType")); + plotPeaks->setProperty("CostFunction", getPropertyValue("CostFunction")); + plotPeaks->executeAsChildAlg(); + return plotPeaks->getProperty("OutputWorkspace"); +} + +std::string QENSFitSequential::getInputString( + const std::vector<MatrixWorkspace_sptr> &workspaces) const { + const auto inputString = getPropertyValue("Input"); + if (!inputString.empty()) + return replaceWorkspaces(inputString, workspaces); + return constructInputString(workspaces[0], getProperty("SpecMin"), + getProperty("SpecMax")); +} + +std::vector<MatrixWorkspace_sptr> QENSFitSequential::getWorkspaces() const { + const auto inputString = getPropertyValue("Input"); + if (!inputString.empty()) + return extractWorkspaces(inputString); + return {getProperty("InputWorkspace")}; +} + +std::vector<MatrixWorkspace_sptr> QENSFitSequential::convertInputToElasticQ( + const std::vector<MatrixWorkspace_sptr> &workspaces) const { + return convertToElasticQ(workspaces, getTemporaryName(), + throwIfElasticQConversionFails()); +} + +void QENSFitSequential::extractMembers( + WorkspaceGroup_sptr resultGroupWs, + const std::vector<API::MatrixWorkspace_sptr> &workspaces, + const std::string &outputWsName) { + std::vector<std::string> workspaceNames; + std::transform( + workspaces.begin(), workspaces.end(), std::back_inserter(workspaceNames), + [](API::MatrixWorkspace_sptr workspace) { return workspace->getName(); }); + + auto extractAlgorithm = extractMembersAlgorithm(resultGroupWs, outputWsName); + extractAlgorithm->setProperty("InputWorkspaces", workspaceNames); + extractAlgorithm->execute(); +} + +void QENSFitSequential::copyLogs( + MatrixWorkspace_sptr resultWorkspace, + const std::vector<MatrixWorkspace_sptr> &workspaces) { + auto logCopier = createChildAlgorithm("CopyLogs", -1.0, -1.0, false); + logCopier->setProperty("OutputWorkspace", resultWorkspace->getName()); + + for (const auto &workspace : workspaces) { + logCopier->setProperty("InputWorkspace", workspace); + logCopier->executeAsChildAlg(); + } +} + +void QENSFitSequential::copyLogs(MatrixWorkspace_sptr resultWorkspace, + WorkspaceGroup_sptr resultGroup) { + auto logCopier = createChildAlgorithm("CopyLogs", -1.0, -1.0, false); + logCopier->setProperty("InputWorkspace", resultWorkspace); + logCopier->setProperty("OutputWorkspace", resultGroup->getName()); + logCopier->executeAsChildAlg(); +} + +IAlgorithm_sptr QENSFitSequential::extractMembersAlgorithm( + WorkspaceGroup_sptr resultGroupWs, const std::string &outputWsName) const { + const bool convolved = getProperty("ConvolveMembers"); + std::vector<std::string> convolvedMembers; + IFunction_sptr function = getProperty("Function"); + + if (convolved) + extractConvolvedNames(function, convolvedMembers); + + auto extractMembersAlg = + AlgorithmManager::Instance().create("ExtractQENSMembers"); + extractMembersAlg->setProperty("ResultWorkspace", resultGroupWs); + extractMembersAlg->setProperty("OutputWorkspace", outputWsName); + extractMembersAlg->setProperty("RenameConvolvedMembers", convolved); + extractMembersAlg->setProperty("ConvolvedMembers", convolvedMembers); + return extractMembersAlg; +} + +std::string QENSFitSequential::getTemporaryName() const { + return "__" + name() + "_ws"; +} + +} // namespace Algorithms +} // namespace Mantid diff --git a/Framework/WorkflowAlgorithms/test/ConvolutionFitSequentialTest.h b/Framework/WorkflowAlgorithms/test/ConvolutionFitSequentialTest.h index 298a5a43e9a8be7c12bc27d80985de7e9731e8a0..137749cde25ac70b80c94a03a318b0b89fa0933a 100644 --- a/Framework/WorkflowAlgorithms/test/ConvolutionFitSequentialTest.h +++ b/Framework/WorkflowAlgorithms/test/ConvolutionFitSequentialTest.h @@ -42,8 +42,11 @@ public: void test_fit_function_is_valid_for_convolution_fitting() { Mantid::Algorithms::ConvolutionFitSequential alg; TS_ASSERT_THROWS_NOTHING(alg.initialize()); + createConvFitResWorkspace(1, 1); TS_ASSERT_THROWS_NOTHING(alg.setProperty( - "Function", "function=test,name=Convolution,name=Resolution")); + "Function", "name=Convolution;name=Resolution,Workspace=__ConvFit_" + "Resolution,WorkspaceIndex=0;")); + AnalysisDataService::Instance().clear(); } //-------------------------- Failure cases ---------------------------- @@ -143,15 +146,15 @@ public: "WorkspaceIndex=0;((composite=ProductFunction,NumDeriv=" "false;name=Lorentzian,Amplitude=1,PeakCentre=0,FWHM=0." "0175)))"); - alg.setProperty("BackgroundType", "Fixed Flat"); alg.setProperty("StartX", 0.0); alg.setProperty("EndX", 3.0); alg.setProperty("SpecMin", 0); alg.setProperty("SpecMax", 5); - alg.setProperty("Convolve", true); + alg.setProperty("ConvolveMembers", true); alg.setProperty("Minimizer", "Levenberg-Marquardt"); alg.setProperty("MaxIterations", 500); - alg.setProperty("OutputWorkspace", "Result"); + alg.setProperty("OutputWorkspace", + "ReductionWs_conv_1LFixF_s0_to_5_Result"); TS_ASSERT_THROWS_NOTHING(alg.execute()); TS_ASSERT(alg.isExecuted()); @@ -197,12 +200,15 @@ public: // Check new Log data is present auto memberLogs = memberRun.getLogData(); - TS_ASSERT_EQUALS(memberLogs.at(2)->value(), "FixF"); - TS_ASSERT_EQUALS(memberLogs.at(3)->value(), "true"); - TS_ASSERT_EQUALS(memberLogs.at(4)->value(), "false"); - TS_ASSERT_EQUALS(memberLogs.at(5)->value(), "ConvFit"); - TS_ASSERT_EQUALS(memberLogs.at(6)->value(), "ReductionWs_"); - TS_ASSERT_EQUALS(memberLogs.at(7)->value(), "1"); + TS_ASSERT_EQUALS(memberRun.getLogData("background")->value(), + "Fixed Linear"); + TS_ASSERT_EQUALS(memberRun.getLogData("convolve_members")->value(), "true"); + TS_ASSERT_EQUALS(memberRun.getLogData("delta_function")->value(), "false"); + TS_ASSERT_EQUALS(memberRun.getLogData("fit_program")->value(), + "ConvolutionFit"); + TS_ASSERT_EQUALS(memberRun.getLogData("sample_filename")->value(), + "ReductionWs_"); + TS_ASSERT_EQUALS(memberRun.getLogData("lorentzians")->value(), "1"); AnalysisDataService::Instance().clear(); } @@ -221,15 +227,14 @@ public: "WorkspaceIndex=0;((composite=ProductFunction,NumDeriv=" "false;name=Lorentzian,Amplitude=1,PeakCentre=0,FWHM=0." "0175)))"); - alg.setProperty("BackgroundType", "Fixed Flat"); alg.setProperty("StartX", 0.0); alg.setProperty("EndX", 5.0); alg.setProperty("SpecMin", 0); alg.setProperty("SpecMax", 0); - alg.setProperty("Convolve", true); + alg.setProperty("ConvolveMembers", true); alg.setProperty("Minimizer", "Levenberg-Marquardt"); alg.setProperty("MaxIterations", 500); - alg.setProperty("OutputWorkspace", "Result"); + alg.setProperty("OutputWorkspace", "SqwWs_conv_1LFixF_s0_Result"); TS_ASSERT_THROWS_NOTHING(alg.execute()); TS_ASSERT(alg.isExecuted()); @@ -263,6 +268,9 @@ public: size_t specMin = 0; size_t specMax = 5; + auto outputName = runName + "_conv_1LFixF_s" + std::to_string(specMin) + + "_to_" + std::to_string(specMax) + "_Result"; + Mantid::Algorithms::ConvolutionFitSequential alg; TS_ASSERT_THROWS_NOTHING(alg.initialize()); alg.setProperty("InputWorkspace", redWs); @@ -273,16 +281,15 @@ public: "WorkspaceIndex=0;((composite=ProductFunction,NumDeriv=" "false;name=Lorentzian,Amplitude=1,PeakCentre=0,FWHM=0." "0175)))"); - alg.setProperty("BackgroundType", "Fixed Flat"); alg.setProperty("StartX", 0.0); alg.setProperty("EndX", 3.0); alg.setProperty("SpecMin", boost::numeric_cast<int>(specMin)); alg.setProperty("SpecMax", boost::numeric_cast<int>(specMax)); - alg.setProperty("Convolve", true); + alg.setProperty("ConvolveMembers", true); alg.setProperty("ExtractMembers", true); alg.setProperty("Minimizer", "Levenberg-Marquardt"); alg.setProperty("MaxIterations", 500); - alg.setProperty("OutputWorkspace", "Result"); + alg.setProperty("OutputWorkspace", outputName); TS_ASSERT_THROWS_NOTHING(alg.execute()); TS_ASSERT(alg.isExecuted()); diff --git a/Framework/WorkflowAlgorithms/test/QENSFitSequentialTest.h b/Framework/WorkflowAlgorithms/test/QENSFitSequentialTest.h new file mode 100644 index 0000000000000000000000000000000000000000..199d0bdc0692c8e7a0cca5cb332e75d4359f59ca --- /dev/null +++ b/Framework/WorkflowAlgorithms/test/QENSFitSequentialTest.h @@ -0,0 +1,225 @@ +#ifndef MANTID_ALGORITHMS_QENSFITSEQUENTIALTEST_H_ +#define MANTID_ALGORITHMS_QENSFITSEQUENTIALTEST_H_ + +#include <cxxtest/TestSuite.h> + +#include "MantidAPI/Axis.h" +#include "MantidAPI/FrameworkManager.h" +#include "MantidAPI/WorkspaceFactory.h" +#include "MantidAPI/WorkspaceGroup.h" + +#include "MantidDataHandling/Load.h" + +#include "MantidWorkflowAlgorithms/QENSFitSequential.h" + +#include "MantidDataObjects/Workspace2D.h" +#include "MantidKernel/TimeSeriesProperty.h" +#include "MantidTestHelpers/WorkspaceCreationHelper.h" + +using Mantid::Algorithms::QENSFitSequential; +using namespace Mantid::API; +using namespace Mantid::DataObjects; +using Mantid::Kernel::make_cow; +using Mantid::HistogramData::BinEdges; +using Mantid::HistogramData::Counts; +using Mantid::HistogramData::CountStandardDeviations; + +class QENSFitSequentialTest : public CxxTest::TestSuite { +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static QENSFitSequentialTest *createSuite() { + return new QENSFitSequentialTest(); + } + static void destroySuite(QENSFitSequentialTest *suite) { delete suite; } + + QENSFitSequentialTest() { FrameworkManager::Instance(); } + + void test_set_valid_fit_function() { + Mantid::Algorithms::QENSFitSequential alg; + TS_ASSERT_THROWS_NOTHING(alg.initialize()); + TS_ASSERT_THROWS_NOTHING(alg.setProperty( + "Function", "name=DeltaFunction,Height=1,Centre=0;name=" + "Lorentzian,Amplitude=1,PeakCentre=0,FWHM=0;")); + } + + void test_empty_function_is_not_allowed() { + Mantid::Algorithms::QENSFitSequential alg; + TS_ASSERT_THROWS_NOTHING(alg.initialize()); + + TS_ASSERT_THROWS(alg.setPropertyValue("Function", ""), + std::invalid_argument); + } + + void test_convolution_fit() { + const int totalBins = 6; + const int totalHist = 5; + auto inputWorkspace = createReducedWorkspace(totalBins, totalHist); + auto resolution = + createResolutionWorkspace(totalBins, totalHist, "__QENS_Resolution"); + + auto outputBaseName = runConvolutionFit(inputWorkspace, resolution); + testFitOutput(outputBaseName, inputWorkspace->getNumberHistograms()); + AnalysisDataService::Instance().clear(); + } + + void test_multiple_fit() { + const int totalBins = 15; + const int totalHist = 10; + + std::vector<std::string> names = {"first_red", "second_red"}; + auto outputBaseName = runMultipleFit( + createReducedWorkspaces(names, totalBins, totalHist), peakFunction()); + testFitOutput(outputBaseName, names.size() * 3); + AnalysisDataService::Instance().clear(); + } + +private: + std::string runConvolutionFit(MatrixWorkspace_sptr inputWorkspace, + MatrixWorkspace_sptr resolution) { + Mantid::Algorithms::QENSFitSequential alg; + TS_ASSERT_THROWS_NOTHING(alg.initialize()); + + alg.setProperty("InputWorkspace", inputWorkspace); + alg.setProperty("Function", convolutionFunction(resolution->getName())); + alg.setProperty("StartX", 0.0); + alg.setProperty("EndX", 3.0); + alg.setProperty("SpecMin", 0); + alg.setProperty( + "SpecMax", static_cast<int>(inputWorkspace->getNumberHistograms() - 1)); + alg.setProperty("ConvolveMembers", true); + alg.setProperty("Minimizer", "Levenberg-Marquardt"); + alg.setProperty("MaxIterations", 500); + alg.setProperty("OutputWorkspace", + "ReductionWs_conv_1LFixF_s0_to_5_Result"); + TS_ASSERT_THROWS_NOTHING(alg.execute()); + TS_ASSERT(alg.isExecuted()); + + return "ReductionWs_conv_1LFixF_s0_to_5"; + } + + std::string runMultipleFit( + const std::vector<Mantid::API::MatrixWorkspace_sptr> &workspaces, + const std::string &function) { + Mantid::Algorithms::QENSFitSequential alg; + TS_ASSERT_THROWS_NOTHING(alg.initialize()); + + alg.setProperty("Input", createMultipleFitInput(workspaces)); + alg.setProperty("Function", function); + alg.setProperty("StartX", 0.0); + alg.setProperty("EndX", 10.0); + alg.setProperty("ConvolveMembers", true); + alg.setProperty("Minimizer", "Levenberg-Marquardt"); + alg.setProperty("MaxIterations", 500); + alg.setProperty("OutputWorkspace", "MultiQENSFitSequential_Result"); + TS_ASSERT_THROWS_NOTHING(alg.execute()); + TS_ASSERT(alg.isExecuted()); + + return "MultiQENSFitSequential"; + } + + void testFitOutput(const std::string &outputBaseName, + std::size_t expectedGroupSize) { + WorkspaceGroup_sptr groupWorkspace; + + TS_ASSERT_THROWS_NOTHING( + AnalysisDataService::Instance().retrieveWS<ITableWorkspace>( + outputBaseName + "_Parameters")); + TS_ASSERT_THROWS_NOTHING( + groupWorkspace = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>( + outputBaseName + "_Workspaces")); + TS_ASSERT_THROWS_NOTHING( + AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>( + outputBaseName + "_Result")); + + TS_ASSERT_EQUALS(groupWorkspace->size(), expectedGroupSize); + } + + std::vector<Mantid::API::MatrixWorkspace_sptr> + createReducedWorkspaces(const std::vector<std::string> &names, int totalBins, + int totalHist) { + std::vector<Mantid::API::MatrixWorkspace_sptr> workspaces; + + for (const auto &name : names) { + workspaces.emplace_back(createReducedWorkspace(totalBins, totalHist)); + AnalysisDataService::Instance().addOrReplace(name, workspaces.back()); + } + return workspaces; + } + + std::string createMultipleFitInput( + const std::vector<Mantid::API::MatrixWorkspace_sptr> &workspaces) const { + std::ostringstream input; + + for (const auto &workspace : workspaces) + input << workspace->getName() << ",i0;" << workspace->getName() << ",i" + << std::to_string(workspace->getNumberHistograms() / 2) << ";" + << workspace->getName() << ",i" + << std::to_string(workspace->getNumberHistograms() - 1) << ";"; + return input.str(); + } + + std::string peakFunction() const { + return "name=LinearBackground,A0=0,A1=0;name=Lorentzian,Amplitude=1," + "PeakCentre=0,FWHM=0.0175"; + } + + std::string convolutionFunction(const std::string &resolutionName) const { + return "name=LinearBackground,A0=0,A1=0,ties=(A0=0.000000,A1=0.0);(" + "composite=Convolution,FixResolution=true,NumDeriv=true;name=" + "Resolution,Workspace=" + + resolutionName + + ",WorkspaceIndex=0;((composite=ProductFunction,NumDeriv=false;name=" + "Lorentzian,Amplitude=1,PeakCentre=0,FWHM=0.0175)))"; + } + + MatrixWorkspace_sptr createReducedWorkspace(int xlen, int ylen) const { + auto ws = WorkspaceCreationHelper::create2DWorkspaceWithFullInstrument( + xlen, xlen - 1, false, false, true, "testInst"); + ws->initialize(ylen, xlen, xlen - 1); + addBinsAndCountsToWorkspace(ws, xlen, xlen - 1, 1.0, 3.0); + + ws->getAxis(0)->setUnit("DeltaE"); + + for (int i = 0; i < xlen; ++i) + ws->setEFixed((i + 1), 0.50); + + auto &run = ws->mutableRun(); + auto timeSeries = + new Mantid::Kernel::TimeSeriesProperty<std::string>("TestTimeSeries"); + timeSeries->addValue("2010-09-14T04:20:12", "0.02"); + run.addProperty(timeSeries); + return ws; + } + + MatrixWorkspace_sptr + createResolutionWorkspace(std::size_t totalBins, std::size_t totalHist, + const std::string &name) const { + auto resolution = + createWorkspace<Workspace2D>(totalHist + 1, totalBins + 1, totalBins); + addBinsAndCountsToWorkspace(resolution, totalBins + 1, totalBins, 0.0, 3.0); + AnalysisDataService::Instance().addOrReplace(name, resolution); + return resolution; + } + + void addBinsAndCountsToWorkspace(Workspace2D_sptr workspace, + std::size_t totalBinEdges, + std::size_t totalCounts, double binValue, + double countValue) const { + BinEdges x1(totalBinEdges, binValue); + Counts y1(totalCounts, countValue); + CountStandardDeviations e1(totalCounts, sqrt(countValue)); + + int j = 0; + std::generate(begin(x1), end(x1), [&j] { return 0.5 + 0.75 * j++; }); + + for (auto i = 0u; i < workspace->getNumberHistograms(); ++i) { + workspace->setBinEdges(i, x1); + workspace->setCounts(i, y1); + workspace->setCountStandardDeviations(i, e1); + } + } +}; + +#endif /* MANTID_ALGORITHMS_CONVOLUTIONFITSEQUENTIALTEST_H_ */ diff --git a/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py b/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py index 2a68c74d136de8814fe166837ca75ba69ad5a336..c41ea24915483f42f1ac79f506047774faf1d579 100644 --- a/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py +++ b/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py @@ -750,7 +750,7 @@ class ISISIndirectInelasticIqtAndIqtFit(with_metaclass(ABCMeta, ISISIndirectInel # Load files into Mantid for sample in self.samples: LoadNexus(sample, OutputWorkspace=sample) - LoadNexus(self.resolution, OutputWorkspace=self.resolution) + LoadNexus(FileFinder.getFullPath(self.resolution), OutputWorkspace=self.resolution) _, iqt_ws = TransformToIqt(SampleWorkspace=self.samples[0], ResolutionWorkspace=self.resolution, @@ -760,12 +760,9 @@ class ISISIndirectInelasticIqtAndIqtFit(with_metaclass(ABCMeta, ISISIndirectInel DryRun=False) # Test IqtFit Sequential - iqtfitSeq_ws, params, fit_group = IqtFitSequential(iqt_ws, - self.func, - self.ftype, - self.startx, - self.endx, 0, - self.spec_max) + iqtfitSeq_ws, params, fit_group = IqtFitSequential(InputWorkspace=iqt_ws, Function=self.func, + StartX=self.startx, EndX=self.endx, + SpecMin=0, SpecMax=self.spec_max) self.result_names = [iqt_ws.name(), iqtfitSeq_ws.name()] @@ -792,8 +789,6 @@ class ISISIndirectInelasticIqtAndIqtFit(with_metaclass(ABCMeta, ISISIndirectInel raise RuntimeError("num_bins should be an int") if not isinstance(self.func, str): raise RuntimeError("Function should be a string.") - if not isinstance(self.ftype, str): - raise RuntimeError("Function type should be a string.") if not isinstance(self.startx, float): raise RuntimeError("startx should be a float") if not isinstance(self.endx, float): @@ -818,7 +813,6 @@ class OSIRISIqtAndIqtFit(ISISIndirectInelasticIqtAndIqtFit): self.func = r'composite=CompositeFunction,NumDeriv=1;name=LinearBackground,A0=0,A1=0,ties=(A1=0);'\ 'name=UserFunction,Formula=Intensity*exp(-(x/Tau)),'\ 'Intensity=0.304185,Tau=100;ties=(f1.Intensity=1-f0.A0)' - self.ftype = '1E_s' self.spec_max = 41 self.startx = 0.0 self.endx = 0.118877 @@ -847,7 +841,6 @@ class IRISIqtAndIqtFit(ISISIndirectInelasticIqtAndIqtFit): self.func = r'composite=CompositeFunction,NumDeriv=1;name=LinearBackground,A0=0,A1=0,ties=(A1=0);'\ 'name=UserFunction,Formula=Intensity*exp(-(x/Tau)),'\ 'Intensity=0.355286,Tau=100;ties=(f1.Intensity=1-f0.A0)' - self.ftype = '1E_s' self.spec_max = 50 self.startx = 0.0 self.endx = 0.169171 @@ -875,7 +868,7 @@ class ISISIndirectInelasticIqtAndIqtFitMulti(with_metaclass(ABCMeta, ISISIndirec #load files into mantid for sample in self.samples: LoadNexus(sample, OutputWorkspace=sample) - LoadNexus(self.resolution, OutputWorkspace=self.resolution) + LoadNexus(FileFinder.getFullPath(self.resolution), OutputWorkspace=self.resolution) _, iqt_ws = TransformToIqt(SampleWorkspace=self.samples[0], ResolutionWorkspace=self.resolution, @@ -999,7 +992,7 @@ class ISISIndirectInelasticConvFit(with_metaclass(ABCMeta, ISISIndirectInelastic '''Defines the workflow for the test''' self.tolerance = 1e-4 LoadNexus(self.sample, OutputWorkspace=self.sample) - LoadNexus(self.resolution, OutputWorkspace=self.resolution) + LoadNexus(FileFinder.getFullPath(self.resolution), OutputWorkspace=self.resolution) ConvolutionFitSequential( InputWorkspace=self.sample, @@ -1007,11 +1000,10 @@ class ISISIndirectInelasticConvFit(with_metaclass(ABCMeta, ISISIndirectInelastic PassWSIndexToFunction=self.passWSIndexToFunction, StartX=self.startx, EndX=self.endx, - BackgroundType=self.bg, SpecMin=self.spectra_min, SpecMax=self.spectra_max, PeakRadius=5, - OutputWorkspace='result') + OutputWorkspace=self.result_names[0]) def _validate_properties(self): '''Check the object properties are in an expected state to continue''' @@ -1022,8 +1014,6 @@ class ISISIndirectInelasticConvFit(with_metaclass(ABCMeta, ISISIndirectInelastic raise RuntimeError("Resolution should be a string.") if not isinstance(self.func, str): raise RuntimeError("Function should be a string.") - if not isinstance(self.bg, str): - raise RuntimeError("Background type should be a string.") if not isinstance(self.startx, float): raise RuntimeError("startx should be a float") if not isinstance(self.endx, float): @@ -1043,7 +1033,7 @@ class OSIRISConvFit(ISISIndirectInelasticConvFit): def __init__(self): ISISIndirectInelasticConvFit.__init__(self) self.sample = 'osi97935_graphite002_red.nxs' - self.resolution = FileFinder.getFullPath('osi97935_graphite002_res.nxs') + self.resolution = 'osi97935_graphite002_res.nxs' #ConvFit fit function self.func = 'name=LinearBackground,A0=0,A1=0;(composite=Convolution,FixResolution=true,NumDeriv=true;'\ 'name=Resolution,Workspace=\"%s\";name=Lorentzian,Amplitude=2,FWHM=0.002,ties=(PeakCentre=0)'\ @@ -1051,7 +1041,6 @@ class OSIRISConvFit(ISISIndirectInelasticConvFit): self.passWSIndexToFunction = False # osi97935_graphite002_res is single histogram self.startx = -0.2 self.endx = 0.2 - self.bg = 'Fit Linear' self.spectra_min = 0 self.spectra_max = 41 self.ties = False @@ -1070,7 +1059,7 @@ class IRISConvFit(ISISIndirectInelasticConvFit): def __init__(self): ISISIndirectInelasticConvFit.__init__(self) self.sample = 'irs53664_graphite002_red.nxs' - self.resolution = FileFinder.getFullPath('irs53664_graphite002_res.nxs') + self.resolution = 'irs53664_graphite002_res.nxs' #ConvFit fit function self.func = 'name=LinearBackground,A0=0.060623,A1=0.001343;' \ '(composite=Convolution,FixResolution=true,NumDeriv=true;' \ @@ -1079,7 +1068,6 @@ class IRISConvFit(ISISIndirectInelasticConvFit): self.passWSIndexToFunction = False # irs53664_graphite002_res is single histogram self.startx = -0.2 self.endx = 0.2 - self.bg = 'Fit Linear' self.spectra_min = 0 self.spectra_max = 50 self.ties = False diff --git a/docs/source/algorithms/ConvolutionFitSequential-v1.rst b/docs/source/algorithms/ConvolutionFitSequential-v1.rst index 92b2d66d6e97f37b7479596211d8c4c2410edfc0..66d6565736f300a5bd2ec511abdd9bd630449a59 100644 --- a/docs/source/algorithms/ConvolutionFitSequential-v1.rst +++ b/docs/source/algorithms/ConvolutionFitSequential-v1.rst @@ -10,14 +10,14 @@ Description ----------- -An algorithm designed mainly as a sequential call to PlotPeakByLogValue -but used within the ConvFit tab within the Indirect Analysis interface -to fit Convolution Functions. +Performs a sequential fit involving a convolution with a defined resolution. This algorithm is a special-case of +:ref:`QENSFitSequential <algm-QENSFitSequential>`, which calculates the elastic incoherent scattering factor when +a delta function is provided in the fitting model. Workflow -------- -.. diagram:: ConvolutionFitSequential-v1_wkflw.dot +.. diagram:: QENSFitSequential-v1_wkflw.dot Usage @@ -38,7 +38,6 @@ Usage (composite=Convolution,FixResolution=true,NumDeriv=true; name=Resolution,Workspace=resolution,WorkspaceIndex=0; name=Lorentzian,Amplitude=1,PeakCentre=0,FWHM=0.0175)""" - bgType = "Fixed Flat" startX = -0.547608 endX = 0.543217 specMin = 0 @@ -48,10 +47,13 @@ Usage maxIt = 500 # Run algorithm - result_ws = ConvolutionFitSequential(InputWorkspace=sample, - Function=function, PassWSIndexToFunction=True, BackgroundType=bgType, - StartX=startX, EndX=endX, SpecMin=specMin, SpecMax=specMax, - Convolve=convolve, Minimizer=minimizer, MaxIterations=maxIt) + result_ws, _, _ = ConvolutionFitSequential(InputWorkspace=sample, + Function=function, + PassWSIndexToFunction=True, + StartX=startX, EndX=endX, + SpecMin=specMin, SpecMax=specMax, + ConvolveMembers=convolve, + Minimizer=minimizer, MaxIterations=maxIt) print("Result has %i Spectra" %result_ws.getNumberHistograms()) diff --git a/docs/source/algorithms/ExtractQENSMembers-v1.rst b/docs/source/algorithms/ExtractQENSMembers-v1.rst index 8ea5b640408287fb97c886c03ab21660a17d0bd2..e72c473e4b1e5ccdc2cc5dfc4071e95806f07d2c 100644 --- a/docs/source/algorithms/ExtractQENSMembers-v1.rst +++ b/docs/source/algorithms/ExtractQENSMembers-v1.rst @@ -29,7 +29,6 @@ Usage (composite=Convolution,FixResolution=true,NumDeriv=true; name=Resolution,Workspace=resolution,WorkspaceIndex=0; name=Lorentzian,Amplitude=1,PeakCentre=0,FWHM=0.0175)""" - bgType = "Fixed Flat" startX = -0.547608 endX = 0.543217 specMin = 0 @@ -40,11 +39,11 @@ Usage output_ws_name = "irs26176_graphite002_conv_1LFixF_s0_to_9" # Run ConvolutionFitSequential algorithm - result_ws = ConvolutionFitSequential(InputWorkspace=sample, Function=function, - PassWSIndexToFunction=True, BackgroundType=bgType, - StartX=startX, EndX=endX, SpecMin=specMin, SpecMax=specMax, - Convolve=convolve, Minimizer=minimizer, MaxIterations=maxIt, - OutputWorkspace=output_ws_name) + ConvolutionFitSequential(InputWorkspace=sample, Function=function, + PassWSIndexToFunction=True, StartX=startX, EndX=endX, + SpecMin=specMin, SpecMax=specMax, ConvolveMembers=convolve, + Minimizer=minimizer, MaxIterations=maxIt, + OutputWorkspace=output_ws_name) # Extract members from the output of the ConvolutionFitSequential algorithm members_ws = ExtractQENSMembers(InputWorkspace=sample, ResultWorkspace=output_ws_name+"_Workspaces", @@ -59,7 +58,7 @@ Usage DeleteWorkspace(output_ws_name + "_Workspaces") DeleteWorkspace(output_ws_name + "_Parameters") DeleteWorkspace(output_ws_name + "_Members") - DeleteWorkspace(result_ws) + DeleteWorkspace(output_ws_name) DeleteWorkspace(sample) DeleteWorkspace(resolution) @@ -86,7 +85,6 @@ Output: (composite=Convolution,FixResolution=true,NumDeriv=true; name=Resolution,Workspace=resolution,WorkspaceIndex=0; name=Lorentzian,Amplitude=1,PeakCentre=0,FWHM=0.0175)""" - bgType = "Fixed Flat" startX = -0.547608 endX = 0.543217 specMin = 0 @@ -97,11 +95,10 @@ Output: output_ws_name = "irs26176_graphite002_conv_1LFixF_s0_to_9" # Run ConvolutionFitSequential algorithm with ExtractMembers property - result_ws = ConvolutionFitSequential(InputWorkspace=sample, Function=function, - PassWSIndexToFunction=True, BackgroundType=bgType, - StartX=startX, EndX=endX, SpecMin=specMin, SpecMax=specMax, - Convolve=convolve, Minimizer=minimizer, MaxIterations=maxIt, - ExtractMembers=True, OutputWorkspace=output_ws_name) + ConvolutionFitSequential(InputWorkspace=sample, Function=function, PassWSIndexToFunction=True, + StartX=startX, EndX=endX, SpecMin=specMin, SpecMax=specMax, + ConvolveMembers=convolve, Minimizer=minimizer, MaxIterations=maxIt, + ExtractMembers=True, OutputWorkspace=output_ws_name) members_ws = mtd[output_ws_name + "_Members"] @@ -113,7 +110,7 @@ Output: DeleteWorkspace(output_ws_name + "_Workspaces") DeleteWorkspace(output_ws_name + "_Parameters") DeleteWorkspace(output_ws_name + "_Members") - DeleteWorkspace(result_ws) + DeleteWorkspace(output_ws_name) DeleteWorkspace(sample) DeleteWorkspace(resolution) diff --git a/docs/source/algorithms/IqtFitSequential-v1.rst b/docs/source/algorithms/IqtFitSequential-v1.rst index 6c62230170af024a48ab16f8f35e80b56d6f88c6..89853eb7c76ac8d3cda0bdd35e0cec8b9b8dd123 100644 --- a/docs/source/algorithms/IqtFitSequential-v1.rst +++ b/docs/source/algorithms/IqtFitSequential-v1.rst @@ -9,13 +9,13 @@ Description ----------- -Fits an \*\_iqt file generated by I(Q,t) sequentially. +Fits an \*\_iqt file generated by I(Q,t) sequentially. This algorithm is a special-case of +:ref:`QENSFitSequential <algm-QENSFitSequential>` in which all input is converted into a histogram format. Workflow -------- -.. diagram:: IqtFitSequential-v1_wkflw.dot - +.. diagram:: QENSFitSequential-v1_wkflw.dot Usage ----- @@ -29,7 +29,7 @@ Usage function = r'name=LinearBackground,A0=0.027668,A1=0,ties=(A1=0);name=UserFunction,Formula=Intensity*exp(-(x/Tau)^Beta),Intensity=0.972332,Tau=0.0247558,Beta=1;ties=(f1.Intensity=1-f0.A0)' #run IqtFitSequential - result, params, fit_group = IqtFitSequential(InputWorkspace=input_ws, Function=function, FitType='1S_s', StartX=0, EndX=0.2, SpecMin=0, SpecMax=16) + result, params, fit_group = IqtFitSequential(InputWorkspace=input_ws, Function=function, StartX=0, EndX=0.2, SpecMin=0, SpecMax=16) .. categories:: diff --git a/docs/source/algorithms/QENSFitSequential-v1.rst b/docs/source/algorithms/QENSFitSequential-v1.rst new file mode 100644 index 0000000000000000000000000000000000000000..eb943a3b86b3e436fbbe1376d5547a0a523a827e --- /dev/null +++ b/docs/source/algorithms/QENSFitSequential-v1.rst @@ -0,0 +1,48 @@ + +.. algorithm:: + +.. summary:: + +.. relatedalgorithms:: + +.. properties:: + +Description +----------- +An algorithm used for fitting QENS-data sequentially and formatting the output. Uses the +:ref:`PlotPeakByLogValue <algm-PlotPeakByLogValue>` algorithm to perform the sequential fit. + +The string format expected by the "Input" property of this algorithm is outlined in +:ref:`PlotPeakByLogValue <algm-PlotPeakByLogValue>`. + +Workflow +-------- + +.. diagram:: QENSFitSequential-v1_wkflw.dot + + +Usage +----- + +**Example - QENSFitSequential** + +.. testcode:: QENSFitSequentialExample + + from __future__ import print_function + + # Load sample and resolution files + sample = Load('irs26176_graphite002_red.nxs') + resolution = Load('irs26173_graphite002_red.nxs') + + # Set up algorithm parameters + function = """name=LinearBackground,A0=0,A1=0,ties=(A0=0.000000,A1=0.0); + (composite=Convolution,FixResolution=true,NumDeriv=true; + name=Resolution,Workspace=resolution,WorkspaceIndex=0; + name=Lorentzian,Amplitude=1,PeakCentre=0,FWHM=0.0175)""" + startX = -0.547608 + endX = 0.543217 + specMin = 0 + specMax = sample.getNumberHistograms() - 1 + convolve = True # Convolve the fitted model components with the resolution + minimizer = "Levenberg-Marquardt" + maxIt = 500 \ No newline at end of file diff --git a/docs/source/diagrams/IqtFitSequential-v1_wkflw.dot b/docs/source/diagrams/IqtFitSequential-v1_wkflw.dot deleted file mode 100644 index bc7a0e846d308cf5ea5344ca2fbece7355df7414..0000000000000000000000000000000000000000 --- a/docs/source/diagrams/IqtFitSequential-v1_wkflw.dot +++ /dev/null @@ -1,72 +0,0 @@ -digraph IqtFitSequential { - label="IqtFitSequential Flowchart" - $global_style - - subgraph params { - $param_style - InputWorkspace - InputWorkspace2 [label="InputWorkspace"] - Function - StartX - EndX - Minimizer - MaxIterations - ExtractMembers - ParameterTable - ParameterNames - FitGroup - FitGroup2 [label="FitGroup"] - SampleLogs - ResultWorkspace - ResultWorkspace2 [label="ResultWorkspace"] - } - - subgraph algorithms { - $algorithm_style - CropWorkspace - PlotPeakByLogValue - ConvertToHistogram - AddSampleLogMultiple - AddSampleLogMultiple2 [label="AddSampleLogMultiple"] - CopyLogs - CopyLogs2 [label="Copy logs"] - ExtractQENSMembers - } - - subgraph decisions { - $decision_style - } - - subgraph process { - $process_style - ConvertToElasticQ - } - - InputWorkspace -> CropWorkspace - StartX -> CropWorkspace - EndX -> CropWorkspace - CropWorkspace -> ConvertToHistogram - ConvertToHistogram -> ConvertToElasticQ [label="Convert spectrum axis to ElaticQ"] - ConvertToElasticQ -> PlotPeakByLogValue - Function -> PlotPeakByLogValue - Minimizer -> PlotPeakByLogValue - MaxIterations -> PlotPeakByLogValue - PlotPeakByLogValue -> ParameterTable - PlotPeakByLogValue -> FitGroup [label="GroupWorkspace containing fitting results from each spectra"] - ParameterTable -> ProcessIndirectFitParameters - ParameterNames -> ProcessIndirectFitParameters [label="Convert parameter of interest into MatrixWorkspace containing paramNames"] - ProcessIndirectFitParameters -> ResultWorkspace [label="Outputs Matrixworkspace containing Tau, Intensity, A0 and Beta"] - InputWorkspace2 -> CopyLogs [label="Copy SampleLogs from InputWorkspace to OutputWorkspaces"] - ResultWorkspace -> CopyLogs - InputWorkspace2 -> CopyLogs2 - FitGroup -> CopyLogs2 - CopyLogs -> AddSampleLogMultiple - CopyLogs2 -> AddSampleLogMultiple2 - SampleLogs -> AddSampleLogMultiple2 - SampleLogs -> AddSampleLogMultiple [label="Additional SampleLogs based on Fit data"] - AddSampleLogMultiple -> ResultWorkspace2 [label="Outputworkspace"] - AddSampleLogMultiple2 -> FitGroup2 [label="OutputWorkspace"] - ExtractMembers -> ExtractQENSMembers [label="Extract members?"] - InputWorkspace -> ExtractQENSMembers - FitGroup -> ExtractQENSMembers -} diff --git a/docs/source/diagrams/ConvolutionFitSequential-v1_wkflw.dot b/docs/source/diagrams/QENSFitSequential-v1_wkflw.dot similarity index 76% rename from docs/source/diagrams/ConvolutionFitSequential-v1_wkflw.dot rename to docs/source/diagrams/QENSFitSequential-v1_wkflw.dot index 90e2132cdbe988da0b4c3822d26c111f0f8e5ea9..c4329aa9257dd85788141be23875fe2dd428a4c5 100644 --- a/docs/source/diagrams/ConvolutionFitSequential-v1_wkflw.dot +++ b/docs/source/diagrams/QENSFitSequential-v1_wkflw.dot @@ -1,28 +1,33 @@ -digraph ConvolutionFitSequential { - label="ConvolutionFitSequential Flowchart" +digraph QENSFitSequential { + label="QENSFitSequential Flowchart" $global_style subgraph params { $param_style InputWorkspace - FitType + Input FitParameters - BackgroundType Function - XStart - XEnd + StartX + EndX SpecMin SpecMax - Convolve + ConvolveMembers ExtractMembers + PassWSIndexToFunction Minimizer + CostFunction MaxIterations + PeakRadius + EvaluationType + LogValue ParameterTable FitWorkspaces NormalisedCovarianceWorkspace ParameterNames - ResultWorkspace OutputWorkspace + OutputParameterWorkspace + OutputWorkspaceGroup SampleLog } @@ -48,20 +53,25 @@ digraph ConvolutionFitSequential { } InputWorkspace -> FitFunction - BackgroundType -> FitFunction - FitType -> FitFunction + Input -> FitFunction Function -> FitFunction FitParameters -> FitFunction SpecMin -> FitFunction SpecMax -> FitFunction - InputWorkspace -> ConvertSpectrumAxis [label="Convert to Elastic Q"] + InputWorkspace -> ConvertSpectrumAxis [label="Attempt conversion to Elastic Q"] ConvertSpectrumAxis -> PlotPeakByLogValue [label="InputWorkspace"] + Input -> PlotPeakByLogValue FitFunction -> PlotPeakByLogValue - XStart -> PlotPeakByLogValue - XEnd -> PlotPeakByLogValue - Convolve -> PlotPeakByLogValue + StartX -> PlotPeakByLogValue + EndX -> PlotPeakByLogValue + ConvolveMembers -> PlotPeakByLogValue MaxIterations -> PlotPeakByLogValue Minimizer -> PlotPeakByLogValue + CostFunction -> PlotPeakByLogValue + PeakRadius -> PlotPeakByLogValue + LogValue -> PlotPeakByLogValue + PassWSIndexToFunction -> PlotPeakByLogValue + EvaluationType -> PlotPeakByLogValue PlotPeakByLogValue -> NormalisedCovarianceWorkspace PlotPeakByLogValue -> ParameterTable PlotPeakByLogValue -> FitWorkspaces @@ -81,4 +91,4 @@ digraph ConvolutionFitSequential { FitWorkspaces -> do_extract_members InputWorkspace -> do_extract_members do_extract_members -> ExtractQENSMembers [label="Yes"] -} +} \ No newline at end of file diff --git a/docs/source/release/v3.13.0/indirect_inelastic.rst b/docs/source/release/v3.13.0/indirect_inelastic.rst index 7eb0483dc76efdaf716da17508a35be37886f4c4..d6f5483badf14fe17106d96141894bb772ae8b87 100644 --- a/docs/source/release/v3.13.0/indirect_inelastic.rst +++ b/docs/source/release/v3.13.0/indirect_inelastic.rst @@ -10,3 +10,19 @@ Indirect Inelastic Changes improvements, followed by bug fixes. :ref:`Release 3.13.0 <v3.13.0>` + +Algorithms +---------- + +New +### + +- :ref:`algm-QENSFitSequential` can be used to perform a general QENS sequential fit, in addition providing the + functionality to fit across multiple datasets. + + +Improved +######## + +- :ref:`algm-ConvolutionFitSequential` and :ref:`algm-IqtFitSequential` can now accept multiple datasets as input, in + the same format as that of :ref:`algm-PlotPeakByLogValue`. diff --git a/qt/scientific_interfaces/Indirect/ConvFit.cpp b/qt/scientific_interfaces/Indirect/ConvFit.cpp index f84281861a466f00634e869fda41da46a04dbf12..980bdfa4dc84591451ce100b0cd4cfd99662760b 100644 --- a/qt/scientific_interfaces/Indirect/ConvFit.cpp +++ b/qt/scientific_interfaces/Indirect/ConvFit.cpp @@ -354,7 +354,6 @@ IAlgorithm_sptr ConvFit::sequentialFit(const int &specMin, auto cfs = AlgorithmManager::Instance().create("ConvolutionFitSequential"); cfs->initialize(); cfs->setProperty("PassWSIndexToFunction", true); - cfs->setProperty("BackgroundType", backgroundType().toStdString()); cfs->setProperty("SpecMin", specMin); cfs->setProperty("SpecMax", specMax); cfs->setProperty("ExtractMembers", boolSettingValue("ExtractMembers")); diff --git a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp index cc59cf21a2aa6d6a3d79f706e3d6b4ce4b2b8fcb..9524ca5bf94606c7f3012d89d3dcc177e46f8cba 100644 --- a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp +++ b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp @@ -1051,18 +1051,6 @@ MatrixWorkspace_sptr IndirectFitAnalysisTab::fitWorkspace() const { return inputWorkspace(); } -/** - * Sets the MaxIterations property of the specified algorithm, to the specified - * integer value. - * - * @param fitAlgorithm The fit algorithm whose MaxIterations property to set. - * @param maxIterations The value to set. - */ -void IndirectFitAnalysisTab::setMaxIterations(IAlgorithm_sptr fitAlgorithm, - int maxIterations) const { - setAlgorithmProperty(fitAlgorithm, "MaxIterations", maxIterations); -} - /** * Called when the 'Run' button is called in the IndirectTab. */ @@ -1090,11 +1078,17 @@ void IndirectFitAnalysisTab::runFitAlgorithm(IAlgorithm_sptr fitAlgorithm) { setAlgorithmProperty(fitAlgorithm, "EndX", m_fitPropertyBrowser->endX()); setAlgorithmProperty(fitAlgorithm, "Minimizer", m_fitPropertyBrowser->minimizer(true)); - setMaxIterations(fitAlgorithm, m_fitPropertyBrowser->maxIterations()); - setAlgorithmProperty(fitAlgorithm, "Convolve", + setAlgorithmProperty(fitAlgorithm, "MaxIterations", + m_fitPropertyBrowser->maxIterations()); + setAlgorithmProperty(fitAlgorithm, "ConvolveMembers", m_fitPropertyBrowser->convolveMembers()); setAlgorithmProperty(fitAlgorithm, "PeakRadius", m_fitPropertyBrowser->getPeakRadius()); + setAlgorithmProperty(fitAlgorithm, "CostFunction", + m_fitPropertyBrowser->costFunction()); + + if (m_fitPropertyBrowser->isHistogramFit()) + setAlgorithmProperty(fitAlgorithm, "EvaluationType", "Histogram"); auto fittingFunction = m_fitPropertyBrowser->getFittingFunction(); m_appendResults = false; diff --git a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h index 653be7a5d30757a1a69c2338d26e4fe1dbe03571..9d2dd8b75bc4a23320f27886fcf1933271a0c1fc 100644 --- a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h +++ b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h @@ -261,9 +261,6 @@ protected: void setPlotOptions(QComboBox *cbPlotType, const QSet<QString> &options) const; - virtual void setMaxIterations(Mantid::API::IAlgorithm_sptr fitAlgorithm, - int maxIterations) const; - virtual std::string createSequentialFitOutputName() const; virtual std::string createSingleFitOutputName() const = 0; diff --git a/qt/scientific_interfaces/Indirect/IqtFit.cpp b/qt/scientific_interfaces/Indirect/IqtFit.cpp index dc168ad34453bb8ac57a8c960a96d5d181fc4098..a5ec3d4b9d4685fd0a9a65b4d7cac1f33b132589 100644 --- a/qt/scientific_interfaces/Indirect/IqtFit.cpp +++ b/qt/scientific_interfaces/Indirect/IqtFit.cpp @@ -227,7 +227,6 @@ IAlgorithm_sptr IqtFit::iqtFitAlgorithm(const size_t &specMin, const size_t &specMax) const { const auto outputName = outputWorkspaceName(specMin); const bool constrainBeta = boolSettingValue("ConstrainBeta"); - const bool constrainIntens = boolSettingValue("ConstrainIntensities"); const bool extractMembers = boolSettingValue("ExtractMembers"); IAlgorithm_sptr iqtFitAlg; @@ -238,15 +237,10 @@ IAlgorithm_sptr IqtFit::iqtFitAlgorithm(const size_t &specMin, iqtFitAlg = AlgorithmManager::Instance().create("IqtFitSequential"); iqtFitAlg->initialize(); - iqtFitAlg->setProperty("FitType", fitTypeString() + "_s"); - iqtFitAlg->setProperty("SpecMin", boost::numeric_cast<long>(specMin)); - iqtFitAlg->setProperty("SpecMax", boost::numeric_cast<long>(specMax)); - iqtFitAlg->setProperty("ConstrainIntensities", constrainIntens); + iqtFitAlg->setProperty("SpecMin", static_cast<int>(specMin)); + iqtFitAlg->setProperty("SpecMax", static_cast<int>(specMax)); iqtFitAlg->setProperty("ExtractMembers", extractMembers); - iqtFitAlg->setProperty("OutputResultWorkspace", outputName + "_Result"); - iqtFitAlg->setProperty("OutputParameterWorkspace", - outputName + "_Parameters"); - iqtFitAlg->setProperty("OutputWorkspaceGroup", outputName + "_Workspaces"); + iqtFitAlg->setProperty("OutputWorkspace", outputName + "_Result"); return iqtFitAlg; } @@ -277,11 +271,6 @@ IqtFit::getParameters(IFunction_sptr function, return parameters; } -void IqtFit::setMaxIterations(IAlgorithm_sptr fitAlgorithm, - int maxIterations) const { - fitAlgorithm->setProperty("MaxIterations", static_cast<long>(maxIterations)); -} - void IqtFit::updatePlotOptions() { IndirectFitAnalysisTab::updatePlotOptions(m_uiForm->cbPlotType); } diff --git a/qt/scientific_interfaces/Indirect/IqtFit.h b/qt/scientific_interfaces/Indirect/IqtFit.h index 391db76fe4b5c7f029176e15b0ecc982d4b06c72..d642678d83753db010cc6d708032bd991472571d 100644 --- a/qt/scientific_interfaces/Indirect/IqtFit.h +++ b/qt/scientific_interfaces/Indirect/IqtFit.h @@ -43,8 +43,6 @@ protected: std::string createSequentialFitOutputName() const override; Mantid::API::IAlgorithm_sptr singleFitAlgorithm() const override; Mantid::API::IAlgorithm_sptr sequentialFitAlgorithm() const override; - void setMaxIterations(Mantid::API::IAlgorithm_sptr fitAlgorithm, - int maxIterations) const override; void enablePlotResult() override; void disablePlotResult() override; void enableSaveResult() override; diff --git a/qt/scientific_interfaces/Indirect/JumpFit.cpp b/qt/scientific_interfaces/Indirect/JumpFit.cpp index 6aa7bcd3a276fdefd27a5febe314e448c720567c..e939e726e4a249bedff0f917bfc23b913d7a6708 100644 --- a/qt/scientific_interfaces/Indirect/JumpFit.cpp +++ b/qt/scientific_interfaces/Indirect/JumpFit.cpp @@ -117,10 +117,6 @@ void JumpFit::algorithmComplete(bool error) { // Process the parameters table const auto paramWsName = outputWorkspaceName() + "_Parameters"; - const auto resultWsName = outputWorkspaceName() + "_Result"; - deleteWorkspaceAlgorithm(paramWsName)->execute(); - renameWorkspaceAlgorithm(outputWorkspaceName(), paramWsName)->execute(); - processParametersAlgorithm(paramWsName, resultWsName)->execute(); IndirectFitAnalysisTab::fitAlgorithmComplete(paramWsName); } @@ -254,49 +250,27 @@ void JumpFit::updatePlotRange() { std::string JumpFit::createSingleFitOutputName() const { auto outputName = inputWorkspace()->getName(); + auto position = outputName.rfind("_Result"); - // Remove _red - const auto cutIndex = outputName.find_last_of('_'); - if (cutIndex != std::string::npos) - outputName = outputName.substr(0, cutIndex); + if (position != std::string::npos) + outputName = outputName.substr(0, position) + + outputName.substr(position + 7, outputName.size()); return outputName + "_" + selectedFitType().toStdString() + "_JumpFit"; } IAlgorithm_sptr JumpFit::singleFitAlgorithm() const { - const auto sample = inputWorkspace()->getName(); const auto widthText = m_uiForm->cbWidth->currentText().toStdString(); const auto width = m_spectraList.at(widthText); - auto fitAlg = AlgorithmManager::Instance().create("PlotPeakByLogValue"); + auto fitAlg = AlgorithmManager::Instance().create("QENSFitSequential"); fitAlg->initialize(); - fitAlg->setProperty("Input", sample + ",i" + std::to_string(width)); - fitAlg->setProperty("OutputWorkspace", outputWorkspaceName()); - fitAlg->setProperty("CreateOutput", true); + fitAlg->setProperty("SpecMin", static_cast<int>(width)); + fitAlg->setProperty("SpecMax", static_cast<int>(width)); + fitAlg->setProperty("OutputWorkspace", + createSingleFitOutputName() + "_Result"); return fitAlg; } -/* - * Creates an algorithm for processing an output parameters workspace. - * - * @param parameterWSName The name of the parameters workspace. - * @return A processing algorithm. - */ -IAlgorithm_sptr -JumpFit::processParametersAlgorithm(const std::string ¶meterWSName, - const std::string &resultWSName) { - const auto parameterNames = - boost::algorithm::join(fitFunction()->getParameterNames(), ","); - - auto processAlg = - AlgorithmManager::Instance().create("ProcessIndirectFitParameters"); - processAlg->setProperty("InputWorkspace", parameterWSName); - processAlg->setProperty("ColumnX", "axis-1"); - processAlg->setProperty("XAxisUnit", "MomentumTransfer"); - processAlg->setProperty("ParameterNames", parameterNames); - processAlg->setProperty("OutputWorkspace", resultWSName); - return processAlg; -} - IAlgorithm_sptr JumpFit::deleteWorkspaceAlgorithm(const std::string &workspaceName) { auto deleteAlg = AlgorithmManager::Instance().create("DeleteWorkspace"); @@ -304,15 +278,6 @@ JumpFit::deleteWorkspaceAlgorithm(const std::string &workspaceName) { return deleteAlg; } -IAlgorithm_sptr -JumpFit::renameWorkspaceAlgorithm(const std::string &workspaceToRename, - const std::string &newName) { - auto renameAlg = AlgorithmManager::Instance().create("RenameWorkspace"); - renameAlg->setProperty("InputWorkspace", workspaceToRename); - renameAlg->setProperty("OutputWorkspace", newName); - return renameAlg; -} - IAlgorithm_sptr JumpFit::scaleAlgorithm(const std::string &workspaceToScale, const std::string &outputName, double scaleFactor) { diff --git a/qt/scientific_interfaces/Indirect/JumpFit.h b/qt/scientific_interfaces/Indirect/JumpFit.h index 0afdd82c388ef9b923f5fddb137647f18f15dd44..a49188be0d929049c869824bed02baa0eb342e37 100644 --- a/qt/scientific_interfaces/Indirect/JumpFit.h +++ b/qt/scientific_interfaces/Indirect/JumpFit.h @@ -51,17 +51,9 @@ protected: std::string createSingleFitOutputName() const override; Mantid::API::IAlgorithm_sptr singleFitAlgorithm() const override; - Mantid::API::IAlgorithm_sptr - processParametersAlgorithm(const std::string ¶meterWSName, - const std::string &resultWSName); - Mantid::API::IAlgorithm_sptr deleteWorkspaceAlgorithm(const std::string &workspaceName); - Mantid::API::IAlgorithm_sptr - renameWorkspaceAlgorithm(const std::string &workspaceToRename, - const std::string &newName); - Mantid::API::IAlgorithm_sptr scaleAlgorithm(const std::string &workspaceToScale, const std::string &outputName, double scaleFactor); diff --git a/qt/scientific_interfaces/Indirect/MSDFit.cpp b/qt/scientific_interfaces/Indirect/MSDFit.cpp index 63090cc4fb0a82a52b6d5a9a362782a02412ae01..e3bdde91186e938937fb6da6650133eeec74a21d 100644 --- a/qt/scientific_interfaces/Indirect/MSDFit.cpp +++ b/qt/scientific_interfaces/Indirect/MSDFit.cpp @@ -101,16 +101,14 @@ std::string MSDFit::constructBaseName() const { } IAlgorithm_sptr MSDFit::singleFitAlgorithm() const { - const auto model = selectedFitType(); const auto fitSpec = m_uiForm->spPlotSpectrum->value(); - return msdFitAlgorithm(modelToAlgorithmProperty(model), fitSpec, fitSpec); + return msdFitAlgorithm(fitSpec, fitSpec); } IAlgorithm_sptr MSDFit::sequentialFitAlgorithm() const { - const auto model = selectedFitType(); const auto specMin = m_uiForm->spSpectraMin->value(); const auto specMax = m_uiForm->spSpectraMax->value(); - return msdFitAlgorithm(modelToAlgorithmProperty(model), specMin, specMax); + return msdFitAlgorithm(specMin, specMax); } /* @@ -118,25 +116,21 @@ IAlgorithm_sptr MSDFit::sequentialFitAlgorithm() const { * specified name, to be run from the specified minimum spectrum to * the specified maximum spectrum. * - * @param model The name of the model to be used by the algorithm. * @param specMin The minimum spectrum to fit. * @param specMax The maximum spectrum to fit. * @return An MSDFit Algorithm using the specified model, which * will run across all spectrum between the specified * minimum and maximum. */ -IAlgorithm_sptr MSDFit::msdFitAlgorithm(const std::string &model, int specMin, - int specMax) const { - IAlgorithm_sptr msdAlg = AlgorithmManager::Instance().create("MSDFit"); +IAlgorithm_sptr MSDFit::msdFitAlgorithm(int specMin, int specMax) const { + IAlgorithm_sptr msdAlg = + AlgorithmManager::Instance().create("QENSFitSequential"); msdAlg->initialize(); - msdAlg->setProperty("Model", model); - msdAlg->setProperty("SpecMin", boost::numeric_cast<long>(specMin)); - msdAlg->setProperty("SpecMax", boost::numeric_cast<long>(specMax)); - msdAlg->setProperty("XStart", startX()); - msdAlg->setProperty("XEnd", endX()); + msdAlg->setProperty("SpecMin", specMin); + msdAlg->setProperty("SpecMax", specMax); msdAlg->setProperty( "OutputWorkspace", - outputWorkspaceName(boost::numeric_cast<size_t>(specMin))); + outputWorkspaceName(boost::numeric_cast<size_t>(specMin)) + "_Result"); return msdAlg; } @@ -287,25 +281,6 @@ void MSDFit::endXChanged(double endX) { rangeSelector->setMaximum(endX); } -/* - * Given the selected model in the interface, returns the name of - * the associated model to pass to the MSDFit algorithm. - * - * @param model The name of the model as displayed in the interface. - * @return The name of the model as defined in the MSDFit algorithm. - */ -std::string MSDFit::modelToAlgorithmProperty(const QString &model) const { - - if (model == "Gaussian") - return "Gauss"; - else if (model == "Peters") - return "Peters"; - else if (model == "Yi") - return "Yi"; - else - return model.toStdString(); -} - /** * Handles saving of workspace */ diff --git a/qt/scientific_interfaces/Indirect/MSDFit.h b/qt/scientific_interfaces/Indirect/MSDFit.h index 805ee9e1fdaafb88c0fe302680eceba478c2ba5c..d3fc5a24faaddfdfeb19baf725d4dbd3d8d8e04f 100644 --- a/qt/scientific_interfaces/Indirect/MSDFit.h +++ b/qt/scientific_interfaces/Indirect/MSDFit.h @@ -57,9 +57,7 @@ protected: private: void disablePlotGuess() override; void enablePlotGuess() override; - Mantid::API::IAlgorithm_sptr msdFitAlgorithm(const std::string &model, - int specMin, int specMax) const; - std::string modelToAlgorithmProperty(const QString &model) const; + Mantid::API::IAlgorithm_sptr msdFitAlgorithm(int specMin, int specMax) const; std::unique_ptr<Ui::MSDFit> m_uiForm; };