From bc9535a7b3180beacb3ec16cff9ecd37f408145d Mon Sep 17 00:00:00 2001 From: Applin Date: Fri, 13 Sep 2024 13:10:54 +0100 Subject: [PATCH 1/5] Fix the calculation of the EISF parameter errors --- .../algorithms/WorkflowAlgorithms/BayesQuasi.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/BayesQuasi.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/BayesQuasi.py index 05ec6312b859..0382aa6b3399 100644 --- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/BayesQuasi.py +++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/BayesQuasi.py @@ -654,24 +654,24 @@ def C2Fw(self, sname): height_data, height_error = np.asarray(height_data), np.asarray(height_error) # calculate EISF and EISF error - total = height_data + amplitude_data - EISF_data = height_data / total - total_error = height_error**2 + amplitude_error**2 - EISF_error = EISF_data * np.sqrt((height_error**2 / height_data**2) + (total_error / total**2)) + eisf_data = height_data / (height_data + amplitude_data) + eisf_error = (1 / (height_data + amplitude_data) ** 2) * np.sqrt( + (amplitude_data * height_error) ** 2 + (height_data * amplitude_error) ** 2 + ) # interlace amplitudes and widths of the peaks y.append(np.asarray(height_data)) - for amp, width, EISF in zip(amplitude_data, width_data, EISF_data): + for amp, width, eisf in zip(amplitude_data, width_data, eisf_data): y.append(amp) y.append(width) - y.append(EISF) + y.append(eisf) # interlace amplitude and width errors of the peaks e.append(np.asarray(height_error)) - for amp, width, EISF in zip(amplitude_error, width_error, EISF_error): + for amp, width, eisf in zip(amplitude_error, width_error, eisf_error): e.append(amp) e.append(width) - e.append(EISF) + e.append(eisf) # create x data and axis names for each function axis_names.append("f" + str(nl) + ".f0." + "Height") From 1f854141a817f0a2000ccb8d717ce1733d41e6fc Mon Sep 17 00:00:00 2001 From: Applin Date: Fri, 13 Sep 2024 13:15:20 +0100 Subject: [PATCH 2/5] Add release note --- docs/source/release/v6.11.0/Inelastic/Bugfixes/37973.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/source/release/v6.11.0/Inelastic/Bugfixes/37973.rst diff --git a/docs/source/release/v6.11.0/Inelastic/Bugfixes/37973.rst b/docs/source/release/v6.11.0/Inelastic/Bugfixes/37973.rst new file mode 100644 index 000000000000..db1b974b7afa --- /dev/null +++ b/docs/source/release/v6.11.0/Inelastic/Bugfixes/37973.rst @@ -0,0 +1 @@ +- Fixed an invalid calculation of the EISF errors calculated on the Quasi tab of the :ref:`Inelastic Bayes fitting ` interface. \ No newline at end of file From 3253f3b76a0db9587655a557420a54f0a8edcb61 Mon Sep 17 00:00:00 2001 From: Applin Date: Fri, 13 Sep 2024 13:58:35 +0100 Subject: [PATCH 3/5] Write a unit test for calculating the EISF errors --- .../algorithms/WorkflowAlgorithms/BayesQuasi.py | 14 ++++++++++---- .../WorkflowAlgorithms/BayesQuasiTest.py | 15 ++++++++++++++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/BayesQuasi.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/BayesQuasi.py index 0382aa6b3399..d6b079fccb59 100644 --- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/BayesQuasi.py +++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/BayesQuasi.py @@ -8,6 +8,7 @@ import os import numpy as np +from typing import Tuple from mantid.api import ( AlgorithmFactory, @@ -24,6 +25,14 @@ from IndirectCommon import * +def _calculate_eisf( + height: np.ndarray, height_error: np.ndarray, amplitude: np.ndarray, amplitude_error: np.ndarray +) -> Tuple[np.ndarray, np.ndarray]: + eisf = height / (height + amplitude) + eisf_error = (1 / (height + amplitude) ** 2) * np.sqrt((amplitude * height_error) ** 2 + (height * amplitude_error) ** 2) + return eisf, eisf_error + + class BayesQuasi(PythonAlgorithm): _program = None _samWS = None @@ -654,10 +663,7 @@ def C2Fw(self, sname): height_data, height_error = np.asarray(height_data), np.asarray(height_error) # calculate EISF and EISF error - eisf_data = height_data / (height_data + amplitude_data) - eisf_error = (1 / (height_data + amplitude_data) ** 2) * np.sqrt( - (amplitude_data * height_error) ** 2 + (height_data * amplitude_error) ** 2 - ) + eisf_data, eisf_error = _calculate_eisf(height_data, height_error, amplitude_data, amplitude_error) # interlace amplitudes and widths of the peaks y.append(np.asarray(height_data)) diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/BayesQuasiTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/BayesQuasiTest.py index 57ff31b972d7..397b4f24ebc5 100644 --- a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/BayesQuasiTest.py +++ b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/BayesQuasiTest.py @@ -6,8 +6,9 @@ # SPDX - License - Identifier: GPL - 3.0 + import unittest import numpy as np -from mantid.simpleapi import * +from mantid.simpleapi import BayesQuasi, CreateWorkspace, DeleteWorkspace, Load from mantid.api import MatrixWorkspace, WorkspaceGroup +from plugins.algorithms.WorkflowAlgorithms.BayesQuasi import _calculate_eisf class BayesQuasiTest(unittest.TestCase): @@ -114,6 +115,18 @@ def test_run_with_zero_at_the_end_of_data(self): self.assertTrue(isinstance(result, MatrixWorkspace)) self.assertTrue(isinstance(prob, MatrixWorkspace)) + def test_calculate_eisf_with_positive_height_and_amplitudes(self): + height = np.array([0.2, 0.4, 0.6, 0.8, 1.0]) + height_error = np.full(5, 0.02) + amplitude = np.array([0.5, 0.4, 0.3, 0.2, 0.1]) + amplitude_error = np.full(5, 0.01) + eisf, eisf_error = _calculate_eisf(height, height_error, amplitude, amplitude_error) + + expected_eisf = [0.28571429, 0.5, 0.66666667, 0.8, 0.90909091] + expected_eisf_error = [0.02081232, 0.01397542, 0.01047566, 0.00894427, 0.00842813] + self.assertTrue(np.allclose(expected_eisf, eisf)) + self.assertTrue(np.allclose(expected_eisf_error, eisf_error)) + # --------------------------------Validate results------------------------------------------------ def _validate_QLr_shape(self, result, probability, group): From b3c23aa52b84d4ebf7ae546db0974072520825ca Mon Sep 17 00:00:00 2001 From: Applin Date: Tue, 17 Sep 2024 13:51:17 +0100 Subject: [PATCH 4/5] Use np.hstack to interlace parameter and error values --- .../WorkflowAlgorithms/BayesQuasi.py | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/BayesQuasi.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/BayesQuasi.py index d6b079fccb59..b85510412b51 100644 --- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/BayesQuasi.py +++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/BayesQuasi.py @@ -666,33 +666,23 @@ def C2Fw(self, sname): eisf_data, eisf_error = _calculate_eisf(height_data, height_error, amplitude_data, amplitude_error) # interlace amplitudes and widths of the peaks - y.append(np.asarray(height_data)) - for amp, width, eisf in zip(amplitude_data, width_data, eisf_data): - y.append(amp) - y.append(width) - y.append(eisf) + y.extend(height_data) + y.extend(np.hstack((amplitude_data, width_data, eisf_data)).flatten("F")) # interlace amplitude and width errors of the peaks - e.append(np.asarray(height_error)) - for amp, width, eisf in zip(amplitude_error, width_error, eisf_error): - e.append(amp) - e.append(width) - e.append(eisf) + e.extend(height_error) + e.extend(np.hstack((amplitude_error, width_error, eisf_error)).flatten("F")) # create x data and axis names for each function axis_names.append("f" + str(nl) + ".f0." + "Height") - x.append(x_data) + x.extend(x_data) for j in range(1, nl + 1): axis_names.append("f" + str(nl) + ".f" + str(j) + ".Amplitude") - x.append(x_data) + x.extend(x_data) axis_names.append("f" + str(nl) + ".f" + str(j) + ".FWHM") - x.append(x_data) + x.extend(x_data) axis_names.append("f" + str(nl) + ".f" + str(j) + ".EISF") - x.append(x_data) - - x = np.asarray(x).flatten() - y = np.asarray(y).flatten() - e = np.asarray(e).flatten() + x.extend(x_data) s_api.CreateWorkspace( OutputWorkspace=output_workspace, From 503801eb5a35e9b8df7504db300c2dccfcad6a99 Mon Sep 17 00:00:00 2001 From: Applin Date: Tue, 17 Sep 2024 14:00:51 +0100 Subject: [PATCH 5/5] Use absolute value for amplitude, FWHM and height errors --- .../plugins/algorithms/WorkflowAlgorithms/BayesQuasi.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/BayesQuasi.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/BayesQuasi.py index b85510412b51..db7aa78652b7 100644 --- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/BayesQuasi.py +++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/BayesQuasi.py @@ -759,12 +759,12 @@ def _read_ql_file(self, file_name, nl): # SIGIK line = next(line_pointer) amp = AMAX * math.sqrt(math.fabs(line[0]) + 1.0e-20) - block_amplitude_e.append(amp) + block_amplitude_e.append(abs(amp)) # SIGFK line = next(line_pointer) FWHM = 2.0 * HWHM * math.sqrt(math.fabs(line[0]) + 1.0e-20) - block_FWHM_e.append(FWHM) + block_FWHM_e.append(abs(FWHM)) # append data from block amp_data.append(block_amplitude) @@ -774,7 +774,7 @@ def _read_ql_file(self, file_name, nl): # append error values from block amp_error.append(block_amplitude_e) FWHM_error.append(block_FWHM_e) - height_error.append(block_height_e) + height_error.append(abs(block_height_e)) return q_data, (amp_data, FWHM_data, height_data), (amp_error, FWHM_error, height_error)