diff --git a/Framework/PythonInterface/plugins/algorithms/EnggCalibrate.py b/Framework/PythonInterface/plugins/algorithms/EnggCalibrate.py index 93aa6d2cd581606aff90e2b970b45ae655d8a96e..44cbf50b5c0d00184639aa96a1d3182db14eb945 100644 --- a/Framework/PythonInterface/plugins/algorithms/EnggCalibrate.py +++ b/Framework/PythonInterface/plugins/algorithms/EnggCalibrate.py @@ -181,7 +181,7 @@ class EnggCalibrate(PythonAlgorithm): fitted_peaks = fit_alg.getProperty('FittedPeaks').value - difc_alg = self.createChildAlgorithm('EnggFitDIFCFromPeaks') + difc_alg = self.createChildAlgorithm('EnggFitTOFFromPeaks') difc_alg.setProperty('FittedPeaks', fitted_peaks) prog.report("Performing fit") difc_alg.execute() diff --git a/Framework/PythonInterface/plugins/algorithms/EnggCalibrateFull.py b/Framework/PythonInterface/plugins/algorithms/EnggCalibrateFull.py index ca2a484fe99f53029046d69d61966ec22221e604..e79efe1213f440c028155652fae99ca8503a191b 100644 --- a/Framework/PythonInterface/plugins/algorithms/EnggCalibrateFull.py +++ b/Framework/PythonInterface/plugins/algorithms/EnggCalibrateFull.py @@ -241,7 +241,7 @@ class EnggCalibrateFull(PythonAlgorithm): @returns the DIFA, DIFC, TZERO calibration parameters of GSAS """ - alg = self.createChildAlgorithm('EnggFitDIFCFromPeaks') + alg = self.createChildAlgorithm('EnggFitTOFFromPeaks') alg.setProperty('FittedPeaks', fitted_peaks_table) alg.execute() diff --git a/Framework/PythonInterface/plugins/algorithms/EnggFitPeaks.py b/Framework/PythonInterface/plugins/algorithms/EnggFitPeaks.py index 791347d91430acabfb26edd5d97e066c9add0665..63db053d10b393327e40a62fbe0bdf4bab03b631 100644 --- a/Framework/PythonInterface/plugins/algorithms/EnggFitPeaks.py +++ b/Framework/PythonInterface/plugins/algorithms/EnggFitPeaks.py @@ -26,7 +26,7 @@ class EnggFitPeaks(PythonAlgorithm): return "Diffraction\\Engineering;Diffraction\\Fitting" def seeAlso(self): - return [ "EnggFitDIFCFromPeaks","GSASIIRefineFitPeaks","Fit" ] + return [ "EnggFitTOFFromPeaks","GSASIIRefineFitPeaks","Fit" ] def name(self): return "EnggFitPeaks" diff --git a/Framework/PythonInterface/plugins/algorithms/EnggFitDIFCFromPeaks.py b/Framework/PythonInterface/plugins/algorithms/EnggFitTOFFromPeaks.py similarity index 78% rename from Framework/PythonInterface/plugins/algorithms/EnggFitDIFCFromPeaks.py rename to Framework/PythonInterface/plugins/algorithms/EnggFitTOFFromPeaks.py index 36f781a3ef88c59bdbc1c04c5ed336647b2faa09..58e345c9228e3ead171c05b8ec14f2d34742c9f0 100644 --- a/Framework/PythonInterface/plugins/algorithms/EnggFitDIFCFromPeaks.py +++ b/Framework/PythonInterface/plugins/algorithms/EnggFitTOFFromPeaks.py @@ -9,7 +9,7 @@ from mantid.kernel import * from mantid.api import * -class EnggFitDIFCFromPeaks(PythonAlgorithm): +class EnggFitTOFFromPeaks(PythonAlgorithm): def category(self): return "Diffraction\\Engineering;Diffraction\\Fitting" @@ -35,13 +35,12 @@ class EnggFitDIFCFromPeaks(PythonAlgorithm): self.declareProperty('OutParametersTable', '', direction=Direction.Input, doc='Name for a table workspace with the fitted values calculated by ' - 'this algorithm (DIFC and TZERO calibration parameters) for GSAS. ' - 'These two parameters are added as two columns in a single row. If not given, ' + 'this algorithm (DIFA, DIFC and TZERO calibration parameters) for GSAS. ' + 'These three parameters are added as three columns in a single row. If not given, ' 'the table workspace is not created.') self.declareProperty('DIFA', 0.0, direction = Direction.Output, - doc='Fitted DIFA value. This parameter is not effectively considered and it ' - 'is always zero in this version of the algorithm.') + doc='Fitted DIFA calibration parameter') self.declareProperty('DIFC', 0.0, direction = Direction.Output, doc="Fitted DIFC calibration parameter") @@ -55,9 +54,9 @@ class EnggFitDIFCFromPeaks(PythonAlgorithm): peaks = self.getProperty('FittedPeaks').value # Better than failing to fit the linear function - if 1 == peaks.rowCount(): - errors['FittedPeaks'] = ('Only one peak was given in the input peaks table. This is not enough ' - 'to fit the output parameters difc and zero. Please check the list of ' + if peaks.rowCount() < 3: + errors['FittedPeaks'] = ('Less than three peaks were given in the input peaks table. This is not enough ' + 'to fit the output parameters difa, difc and zero. Please check the list of ' 'expected peaks given and if it is appropriate for the workspace') return errors @@ -65,7 +64,7 @@ class EnggFitDIFCFromPeaks(PythonAlgorithm): peaks = self.getProperty('FittedPeaks').value - difa, difc, tzero = self._fit_difc_tzero(peaks) + difa, difc, tzero = self._fit_dSpacingTOF(peaks) out_tbl_name = self.getPropertyValue('OutParametersTable') self._produce_outputs(difa, difc, tzero, out_tbl_name) @@ -73,25 +72,25 @@ class EnggFitDIFCFromPeaks(PythonAlgorithm): self.log().information("Fitted {0} peaks in total. DIFA: {1}, DIFC: {2}, TZERO: {3}". format(peaks.rowCount(), difa, difc, tzero)) - def _fit_difc_tzero(self, fitted_peaks_table): + def _fit_dSpacingTOF(self, fitted_peaks_table): """ Fits a linear function to the dSpacing-TOF relationship and - returns the fitted (DIFA=0), DIFC and TZERO values. If the - table passed has less than 2 peaks this raises an exception, - as it is not possible to fit the difc, zero parameters. + returns the fitted DIFA, DIFC and TZERO values. If the + table passed has less than 3 peaks this raises an exception, + as it is not possible to fit the difa, difc, zero parameters. @param fitted_peaks_table :: table with one row per fitted peak, expecting column 'dSpacing' as x values and column 'X0' as y values. - @returns DIFA, DIFC and TZERO parameters as defined in GSAS and GSAS-II. The difc and zero - parameters are obtained from fitting a linear background (in _fit_dSpacing_to_ToF) to the + @returns DIFA, DIFC and TZERO parameters as defined in GSAS and GSAS-II. The difa, difc and zero + parameters are obtained from fitting a quadratic background (in _fit_dSpacing_to_ToF) to the peaks fitted individually that have been passed in the input table """ num_peaks = fitted_peaks_table.rowCount() - if num_peaks < 2: - raise ValueError('Cannot fit a linear function with less than two peaks. Got a table of ' + + if num_peaks < 3: + raise ValueError('Cannot fit a quadratic function with less than three peaks. Got a table of ' + 'peaks with ' + str(num_peaks) + ' peaks') convert_tbl_alg = self.createChildAlgorithm('ConvertTableToMatrixWorkspace') @@ -103,7 +102,7 @@ class EnggFitDIFCFromPeaks(PythonAlgorithm): # Fit the curve to get linear coefficients of TOF <-> dSpacing relationship for the detector fit_alg = self.createChildAlgorithm('Fit') - fit_alg.setProperty('Function', 'name=LinearBackground') + fit_alg.setProperty('Function', 'name=Quadratic') fit_alg.setProperty('InputWorkspace', d_tof_conversion_ws) fit_alg.setProperty('WorkspaceIndex', 0) fit_alg.setProperty('CreateOutput', True) @@ -112,14 +111,14 @@ class EnggFitDIFCFromPeaks(PythonAlgorithm): tzero = param_table.cell('Value', 0) # A0 difc = param_table.cell('Value', 1) # A1 - difa = 0.0 # Not fitted, we may add an option for this later on + difa = param_table.cell('Value', 2) # A2 return difa, difc, tzero def _produce_outputs(self, difa, difc, tzero, tbl_name): """ Fills in the output properties as requested via the input - properties. Sets the output difz/difc/tzero values. It can + properties. Sets the output difa/difc/tzero values. It can also produces a table with these parameters if required in the inputs. @@ -143,4 +142,4 @@ class EnggFitDIFCFromPeaks(PythonAlgorithm): self.log().information("Output parameters added into a table workspace: %s" % tbl_name) -AlgorithmFactory.subscribe(EnggFitDIFCFromPeaks) +AlgorithmFactory.subscribe(EnggFitTOFFromPeaks) diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt b/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt index 35451c8fa460f067896a048019f2d3fafed7359c..9a0de80fd92ec6a26d1aea8c75c86b0b96bf3149 100644 --- a/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt +++ b/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt @@ -34,7 +34,7 @@ set(TEST_PY_FILES DSFinterpTest.py EnggCalibrateFullTest.py EnggCalibrateTest.py - EnggFitDIFCFromPeaksTest.py + EnggFitTOFFromPeaksTest.py EnggFitPeaksTest.py EnggFocusTest.py EnggSaveGSASIIFitResultsToHDF5Test.py diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/EnggFitDIFCFromPeaksTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/EnggFitTOFFromPeaksTest.py similarity index 71% rename from Framework/PythonInterface/test/python/plugins/algorithms/EnggFitDIFCFromPeaksTest.py rename to Framework/PythonInterface/test/python/plugins/algorithms/EnggFitTOFFromPeaksTest.py index baa1ee0333d7b3171c9f98939f1f86bd5061c633..729c8022bd91741e49ff60092c30960e110a3b28 100644 --- a/Framework/PythonInterface/test/python/plugins/algorithms/EnggFitDIFCFromPeaksTest.py +++ b/Framework/PythonInterface/test/python/plugins/algorithms/EnggFitTOFFromPeaksTest.py @@ -10,7 +10,8 @@ import unittest from mantid.simpleapi import * from mantid.api import * -class EnggFitDIFCFromPeaksTest(unittest.TestCase): + +class EnggFitTOFFromPeaksTest(unittest.TestCase): def test_wrong_properties(self): """ @@ -18,25 +19,27 @@ class EnggFitDIFCFromPeaksTest(unittest.TestCase): """ # No InputWorkspace property (required) self.assertRaises(RuntimeError, - EnggFitDIFCFromPeaks, + EnggFitTOFFromPeaks, OutParametersTable='param_table') table = CreateEmptyTableWorkspace(OutputWorkspace='some_tbl_name') # This property doesn't belong here self.assertRaises(RuntimeError, - EnggFitDIFCFromPeaks, + EnggFitTOFFromPeaks, FittedPeaks=table, ExpectedPeaks='0.6, 0.9') - def test_2peaks_runs_ok(self): + def test_runs_ok_3peaks(self): """ - Tests fitting DIFC/TZERO on a couple of peaks from EnggFitPeaks. + Tests fitting DIFC/TZERO on three clean peaks """ peak_def1 = "name=FlatBackground,A0=1;name=BackToBackExponential, I=15000, A=0.1, B=0.14, X0=15000, S=50" peak_def2 = "name=FlatBackground,A0=1;name=BackToBackExponential, I=6000, A=0.02, B=0.021, X0=20000, S=40" + peak_def3 = "name=FlatBackground,A0=1;name=BackToBackExponential, I=10000, A=0.1, B=0.09, X0=25000, S=60" sws = CreateSampleWorkspace(Function="User Defined", - UserDefinedFunction=peak_def1 + ";" + peak_def2, + UserDefinedFunction= + peak_def1 + ";" + peak_def2 + ";" + peak_def3, NumBanks=1, BankPixelWidth=1, XMin=5000, XMax=30000, BinWidth=25) @@ -45,45 +48,47 @@ class EnggFitDIFCFromPeaksTest(unittest.TestCase): peaksTblName = 'test_fit_peaks_table' ep1 = 0.83 ep2 = 1.09 - test_fit_peaks_table = EnggFitPeaks(sws, WorkspaceIndex=0, ExpectedPeaks=[ep1, ep2], + ep3 = 1.4 + test_fit_peaks_table = EnggFitPeaks(sws, WorkspaceIndex=0, ExpectedPeaks=[ep1, ep2, ep3], OutFittedPeaksTable=peaksTblName) - paramsTblName = 'test_difc_zero_table' - difa, difc, zero = EnggFitDIFCFromPeaks(OutParametersTable=paramsTblName, - FittedPeaks=test_fit_peaks_table) - - - self.assertEqual(difa, 0) + paramsTblName = 'test_fit_dsp_tof_table' + difa, difc, zero = EnggFitTOFFromPeaks(FittedPeaks=test_fit_peaks_table, + OutParametersTable=paramsTblName) pTable = mtd[paramsTblName] self.assertEqual(pTable.rowCount(), 1) self.assertEqual(pTable.columnCount(), 3) - self.assertEqual(test_fit_peaks_table.rowCount(), 2) + self.assertEqual(test_fit_peaks_table.rowCount(), 3) + self.assertEqual(3, len(test_fit_peaks_table.column('dSpacing'))) + self.assertEqual(3, len(test_fit_peaks_table.column('X0'))) + self.assertEqual(3, len(test_fit_peaks_table.column('A'))) + self.assertEqual(3, len(test_fit_peaks_table.column('S'))) - # fitting results on some platforms (OSX) are different by ~0.07% - expected_difc = 19229.3699679 + expected_difa = -5442.55806986 + self.assertTrue(self._approxRelErrorLessThan(difa, expected_difa, 5e-3)) + expected_difc = 29681.0554393 + # assertLess would be nices, but only available in unittest >= 2.7 self.assertTrue(self._approxRelErrorLessThan(difc, expected_difc, 5e-3)) - expected_zero = -948.449062995 + expected_zero = -5873.54719194736 self.assertTrue(self._approxRelErrorLessThan(zero, expected_zero, 5e-3)) - # values in the table should also be good within epsilon - self.assertTrue(self._approxRelErrorLessThan(pTable.cell(0,1), expected_difc, 5e-3)) - self.assertTrue(self._approxRelErrorLessThan(pTable.cell(0,2), expected_zero, 5e-3)) - - def test_runs_ok_3peaks(self): + def test_4peaks_runs_ok(self): """ - Tests fitting DIFC/TZERO on three clean peaks + Tests fitting DIFA/DIFC/TZERO on a four clean peaks from EnggFitPeaks. """ peak_def1 = "name=FlatBackground,A0=1;name=BackToBackExponential, I=15000, A=0.1, B=0.14, X0=15000, S=50" peak_def2 = "name=FlatBackground,A0=1;name=BackToBackExponential, I=6000, A=0.02, B=0.021, X0=20000, S=40" peak_def3 = "name=FlatBackground,A0=1;name=BackToBackExponential, I=10000, A=0.1, B=0.09, X0=25000, S=60" + peak_def4 = "name=FlatBackground,A0=1;name=BackToBackExponential, I=12000, A=0.02, B=0.09, X0=30000, S=50" sws = CreateSampleWorkspace(Function="User Defined", - UserDefinedFunction= - peak_def1 + ";" + peak_def2 + ";" + peak_def3, - NumBanks=1, BankPixelWidth=1, - XMin=5000, XMax=30000, + UserDefinedFunction=peak_def1 + ";" + peak_def2 + ";" + peak_def3 + ";" + peak_def4, + NumBanks=1, + BankPixelWidth=1, + XMin=5000, + XMax=40000, BinWidth=25) EditInstrumentGeometry(Workspace=sws, L2=[1.5], Polar=[90], PrimaryFlightPath=50) @@ -91,31 +96,34 @@ class EnggFitDIFCFromPeaksTest(unittest.TestCase): ep1 = 0.83 ep2 = 1.09 ep3 = 1.4 - test_fit_peaks_table = EnggFitPeaks(sws, WorkspaceIndex=0, ExpectedPeaks=[ep1, ep2, ep3], + ep4 = 1.62 + test_fit_peaks_table = EnggFitPeaks(sws, + WorkspaceIndex=0, + ExpectedPeaks=[ep1, ep2, ep3, ep4], OutFittedPeaksTable=peaksTblName) - paramsTblName = 'test_difc_zero_table' - difa, difc, zero = EnggFitDIFCFromPeaks(FittedPeaks=test_fit_peaks_table, - OutParametersTable=paramsTblName) - - self.assertEqual(difa, 0) + paramsTblName = 'test_fit_tof_table' + difa, difc, zero = EnggFitTOFFromPeaks(OutParametersTable=paramsTblName, + FittedPeaks=test_fit_peaks_table) pTable = mtd[paramsTblName] self.assertEqual(pTable.rowCount(), 1) self.assertEqual(pTable.columnCount(), 3) - self.assertEqual(test_fit_peaks_table.rowCount(), 3) - self.assertEqual(3, len(test_fit_peaks_table.column('dSpacing'))) - self.assertEqual(3, len(test_fit_peaks_table.column('X0'))) - self.assertEqual(3, len(test_fit_peaks_table.column('A'))) - self.assertEqual(3, len(test_fit_peaks_table.column('S'))) + self.assertEqual(test_fit_peaks_table.rowCount(), 4) - expected_difc = 17500.7287679 - # assertLess would be nices, but only available in unittest >= 2.7 + # fitting results on some platforms (OSX) are different by ~0.07% + expected_difa = 2369.8867804068127 + self.assertTrue(self._approxRelErrorLessThan(difa, expected_difa, 5e-3)) + expected_difc = 12736.35201894777 self.assertTrue(self._approxRelErrorLessThan(difc, expected_difc, 5e-3)) - expected_zero = 646.607522992 + expected_zero = 2943.7460510348255 self.assertTrue(self._approxRelErrorLessThan(zero, expected_zero, 5e-3)) + # values in the table should also be good within epsilon + self.assertTrue(self._approxRelErrorLessThan(pTable.cell(0, 1), expected_difc, 5e-3)) + self.assertTrue(self._approxRelErrorLessThan(pTable.cell(0, 2), expected_zero, 5e-3)) + def _approxRelErrorLessThan(self, val, ref, epsilon): """ Checks that a value 'val' does not defer from a reference value 'ref' by 'epsilon' @@ -133,8 +141,8 @@ class EnggFitDIFCFromPeaksTest(unittest.TestCase): approx_comp = (abs((ref-val)/ref) < epsilon) if not approx_comp: - print ("Failed approximate comparison between value {0} and reference value " - "{1}, with epsilon {2}".format(val, ref, epsilon)) + print("Failed approximate comparison between value {0} and reference value " + "{1}, with epsilon {2}".format(val, ref, epsilon)) return approx_comp diff --git a/docs/source/algorithms/EnggCalibrate-v1.rst b/docs/source/algorithms/EnggCalibrate-v1.rst index 0327a8c1d60ad0134c6ef5d28389fe6f80dadbc1..4f51759fb44d399d6ebc54da2d0471a767f9bf8d 100644 --- a/docs/source/algorithms/EnggCalibrate-v1.rst +++ b/docs/source/algorithms/EnggCalibrate-v1.rst @@ -37,10 +37,10 @@ as output properties as well. If a name is given in OutputParametersTableName the algorithm also produces a table workspace with that name, containing the two output parameters. Presently the DIFA parameter is always set to zero (see -:ref:`algm-EnggFitDIFCFromPeaks`). +:ref:`algm-EnggFitTOFFromPeaks`). The script EnggUtils included with Mantid can produce a GSAS -parameters file for the ENGIN-X instrument, given the DIFC and ZERO +parameters file for the ENGIN-X instrument, given the DIFA, DIFC, and ZERO parameters for the instrument banks as produced by EnggCalibrate. This can be done with the write_ENGINX_GSAS_iparam_file() function as shown in the usage example below. @@ -57,54 +57,56 @@ Usage .. include:: ../usagedata-note.txt -**Example - Calculate Difc and Zero for EnginX:** +**Example - Calculate Difa, Difc, and Zero for EnginX:** .. testcode:: ExampleCalib - out_tbl_name = 'out_params' - ws_name = 'test_engg_data' - Load('ENGINX00213855.nxs', OutputWorkspace=ws_name) - - # Using precalculated Vanadium corrections. To calculate from scrach see EnggVanadiumCorrections - van_integ_ws = Load('ENGINX_precalculated_vanadium_run000236516_integration.nxs') - van_curves_ws = Load('ENGINX_precalculated_vanadium_run000236516_bank_curves.nxs') - - difa1, difc1, tzero1, peaks1 = EnggCalibrate(InputWorkspace=ws_name, - VanIntegrationWorkspace=van_integ_ws, - VanCurvesWorkspace=van_curves_ws, - ExpectedPeaks=[1.28, 2.1], Bank='1', - OutputParametersTableName=out_tbl_name) - - difa1, difc2, tzero2, peaks2 = EnggCalibrate(InputWorkspace=ws_name, - VanIntegrationWorkspace=van_integ_ws, - VanCurvesWorkspace=van_curves_ws, - ExpectedPeaks=[1.28, 2.1], Bank='2') - - # You can produce an instrument parameters (iparam) file for GSAS. - # Note that this is very specific to ENGIN-X - GSAS_iparm_fname = 'ENGIN_X_banks.prm' - import EnggUtils - EnggUtils.write_ENGINX_GSAS_iparam_file(GSAS_iparm_fname, bank_names=['North', 'South'], - difc=[difc1, difc2], tzero=[tzero1, tzero2]) - - import math - print("DIFA1: {0}".format(difa1)) - delta = 2 - approx_difc1 = 18267 - difc1_ok = abs(difc1 - approx_difc1) <= delta - print("DIFC1 is approximately (+/- {0}) {1}: {2}".format(delta, approx_difc1, difc1_ok)) - approx_tzero1 = 277 - tzero1_ok = abs(tzero1 - approx_tzero1) <= delta - print("TZERO1 is approximately (+/- {0}) {1}: {2}".format(delta, approx_tzero1, tzero1_ok)) - tbl = mtd[out_tbl_name] - - tbl_values_ok = (abs(tbl.cell(0,1) - approx_difc1) <= delta) and (abs(tbl.cell(0,2) - approx_tzero1) <= delta) - print("The output table has {0} row(s) and its values are as expected: {1}".format(tbl.rowCount(), - tbl_values_ok)) - - import os - print("Output GSAS iparam file was written? {0}".format(os.path.exists(GSAS_iparm_fname))) - print("Number of lines of the GSAS iparam file: {0}".format(sum(1 for line in open(GSAS_iparm_fname)))) + out_tbl_name = 'out_params' + ws_name = 'test_engg_data' + Load('ENGINX00213855.nxs', OutputWorkspace=ws_name) + + # Using precalculated Vanadium corrections. To calculate from scrach see EnggVanadiumCorrections + van_integ_ws = Load('ENGINX_precalculated_vanadium_run000236516_integration.nxs') + van_curves_ws = Load('ENGINX_precalculated_vanadium_run000236516_bank_curves.nxs') + + difa1, difc1, tzero1, peaks1 = EnggCalibrate(InputWorkspace=ws_name, + VanIntegrationWorkspace=van_integ_ws, + VanCurvesWorkspace=van_curves_ws, + ExpectedPeaks=[1.09, 1.28, 2.1], Bank='1', + OutputParametersTableName=out_tbl_name) + + difa2, difc2, tzero2, peaks2 = EnggCalibrate(InputWorkspace=ws_name, + VanIntegrationWorkspace=van_integ_ws, + VanCurvesWorkspace=van_curves_ws, + ExpectedPeaks=[1.09, 1.28, 2.1], Bank='2') + + # You can produce an instrument parameters (iparam) file for GSAS. + # Note that this is very specific to ENGIN-X + GSAS_iparm_fname = 'ENGIN_X_banks.prm' + import EnggUtils + EnggUtils.write_ENGINX_GSAS_iparam_file(GSAS_iparm_fname, bank_names=['North', 'South'], + difa=[difa1, difa2], difc=[difc1, difc2], tzero=[tzero1, tzero2]) + + import math + delta = 2 + approx_difa1 = -41 + difa1_ok = abs(difa1 - approx_difa1) <= delta + print("DIFA1 is approximately (+/- {0}) {1}: {2}".format(delta, approx_difa1, difa1_ok)) + approx_difc1 = 18405 + difc1_ok = abs(difc1 - approx_difc1) <= delta + print("DIFC1 is approximately (+/- {0}) {1}: {2}".format(delta, approx_difc1, difc1_ok)) + approx_tzero1 = 167 + tzero1_ok = abs(tzero1 - approx_tzero1) <= delta + print("TZERO1 is approximately (+/- {0}) {1}: {2}".format(delta, approx_tzero1, tzero1_ok)) + tbl = mtd[out_tbl_name] + + tbl_values_ok = (abs(tbl.cell(0,1) - approx_difc1) <= delta) and (abs(tbl.cell(0,2) - approx_tzero1) <= delta) + print("The output table has {0} row(s) and its values are as expected: {1}".format(tbl.rowCount(), + tbl_values_ok)) + + import os + print("Output GSAS iparam file was written? {0}".format(os.path.exists(GSAS_iparm_fname))) + print("Number of lines of the GSAS iparam file: {0}".format(sum(1 for line in open(GSAS_iparm_fname)))) .. testcleanup:: ExampleCalib @@ -119,9 +121,9 @@ Output: .. testoutput:: ExampleCalib - DIFA1: 0.0 - DIFC1 is approximately (+/- 2) 18267: True - TZERO1 is approximately (+/- 2) 277: True + DIFA1 is approximately (+/- 2) -41: True + DIFC1 is approximately (+/- 2) 18405: True + TZERO1 is approximately (+/- 2) 167: True The output table has 1 row(s) and its values are as expected: True Output GSAS iparam file was written? True Number of lines of the GSAS iparam file: 36 diff --git a/docs/source/algorithms/EnggCalibrateFull-v1.rst b/docs/source/algorithms/EnggCalibrateFull-v1.rst index 4c0da245c686a3debc66c508f0d79c243a1f4bd6..df4179f9a99e38c3ba93f734311eb87621ce2ef3 100644 --- a/docs/source/algorithms/EnggCalibrateFull-v1.rst +++ b/docs/source/algorithms/EnggCalibrateFull-v1.rst @@ -85,14 +85,14 @@ Usage ws_name = 'ws_focussed' Load('ENGINX00213855focussed.nxs', OutputWorkspace=ws_name) - # Using precalculated Vanadium corrections. To calculate from scrach see EnggVanadiumCorrections + # Using precalculated Vanadium corrections. To calculate from scratch see EnggVanadiumCorrections van_integ_ws = Load('ENGINX_precalculated_vanadium_run000236516_integration.nxs') van_curves_ws = Load('ENGINX_precalculated_vanadium_run000236516_bank_curves.nxs') pos_table, peaks_info = EnggCalibrateFull(Workspace=ws_name, VanIntegrationWorkspace=van_integ_ws, VanCurvesWorkspace=van_curves_ws, - ExpectedPeaks=[1.097, 2.1], Bank='1') + ExpectedPeaks=[1.097, 1.28, 2.1], Bank='1') det_id = pos_table.column(0)[0] cal_pos = pos_table.column(2)[0] @@ -113,7 +113,7 @@ Output: .. testoutput:: ExCalFull Det ID: 100001 - Calibrated position: (1.506,0.000,0.002) + Calibrated position: (8.510,0.000,0.009) Is the detector position calibrated now in the original workspace instrument? True **Example - Calculate corrected positions for EngingX, saving in a file:** @@ -135,7 +135,7 @@ Output: pos_table, peaks_info = EnggCalibrateFull(Workspace=ws_name, VanIntegrationWorkspace=van_integ_ws, VanCurvesWorkspace=van_curves_ws, - ExpectedPeaks=[1.097, 2.1], Bank='1', + ExpectedPeaks=[1.097, 1.28, 2.1], Bank='1', OutDetPosFilename=pos_filename) det_id = pos_table.column(0)[0] @@ -152,9 +152,9 @@ Output: for i,row in enumerate(reader): cal_pos = pos_table.column(2)[i] detector_pos_list = row[2].strip("[]").split(',') - calibOK = calibOK and (float(detector_pos_list[0]) - cal_pos.getX()) < 1e-6 and\ - (float(detector_pos_list[1]) - cal_pos.getY()) < 1e-6 and\ - (float(detector_pos_list[2]) - cal_pos.getZ()) < 1e-6 + calibOK = calibOK and (float(detector_pos_list[0]) - cal_pos.getX()) < 2e-6 and\ + (float(detector_pos_list[1]) - cal_pos.getY()) < 2e-6 and\ + (float(detector_pos_list[2]) - cal_pos.getZ()) < 2e-6 if not calibOK: break print("Does the calibration file have the expected values? {}".format(calibOK)) @@ -171,7 +171,7 @@ Output: .. testoutput:: ExCalFullWithOutputFile Det ID: 100001 - Calibrated position: (1.506,0.000,0.002) + Calibrated position: (8.510,0.000,0.009) Got details on the peaks fitted for 1 detector(s) Was the file created? True Does the calibration file have the expected values? True diff --git a/docs/source/algorithms/EnggFitDIFCFromPeaks-v1.rst b/docs/source/algorithms/EnggFitDIFCFromPeaks-v1.rst deleted file mode 100644 index 0faff5d7519df1963ccdac5b49896362c077cd30..0000000000000000000000000000000000000000 --- a/docs/source/algorithms/EnggFitDIFCFromPeaks-v1.rst +++ /dev/null @@ -1,112 +0,0 @@ -.. algorithm:: - -.. summary:: - -.. relatedalgorithms:: - -.. properties:: - -Description ------------ - -.. warning:: - - This algorithm is being developed for a specific instrument. It - might undergo significant changes, should instrument scientists - decide to do so. - -Finds the calibration parameters DIFC and TZERO (as defined in GSAS) -from a list of peaks fitted to a diffraction pattern. The peaks can be -fitted to a Mantid workspace spectrum using the algorithm -:ref:`EnggFitPeaks <algm-EnggFitPeaks>` which produces a table with -parameter values for peaks in time-of-flight (TOF, see -:ref:`Unit Factory <Unit Factory>`). The table is the essential -input to this algorithm. - -This algorithm fits the adjusted peak time-of-fligth value positions -that are fitted against expected dSpacing values (d) according to the -expression: - -.. math:: TOF = DIFC*d + TZERO - -The calibration parameters TZERO and DIFC can then be used within the -GSAS program or in other Mantid algorithms (see :ref:`EnggCalibrate -<algm-EnggCalibrate>` and :ref:`EnggCalibrateFull -<algm-EnggCalibrateFull>`). These parameters are returned and can be -retrieved as output properties as well. The DIFA coefficient -(quadratic term on d) is not considered and is fixed to zero in this -version of the algorithm. - -If a name is given in OutParametersTable this algorithm also produces -a table workspace with that name, containing the parameters fitted -(DIFA, DIFC, TZERO). - -The parameters DIFA, DIFC, TZERO are also used in other Mantid -algorithms. For example see :ref:`AlignDetectors -<algm-AlignDetectors>` where these parameters are used to convert -units from time-of-flight to d-spacing. - -Usage ------ - -**Example - Fitting DIFC parameter with two peaks:** - -.. testcode:: ExTwoPeaks - - # Two BackB2Back exponential peaks - peak1 = "name=BackToBackExponential,I=6000,A=1,B=0.5,X0=15000,S=250" - peak2 = "name=BackToBackExponential,I=5000,A=1,B=0.7,X0=35000,S=300" - - # Create workpsace with the above peaks and a single detector pixel - ws = CreateSampleWorkspace(Function="User Defined", - UserDefinedFunction=peak1 + ";" + peak2, - NumBanks=1, - BankPixelWidth=1, - XMin=6000, - XMax=45000, - BinWidth=10) - - # Update instrument geometry to something that would allow converting to some sane dSpacing values - EditInstrumentGeometry(Workspace = ws, L2 = [1.5], Polar = [90], PrimaryFlightPath = 50) - - # Run the algorithm. Defaults are shown below. Files entered must be in .csv format and if both ExpectedPeaks and ExpectedPeaksFromFile are entered, the latter will be used. - - peaks_tbl = EnggFitPeaks(ws, 0, [0.8, 1.9]) - out_tbl_name = 'difc_from_peaks' - difa, difc, tzero = EnggFitDIFCFromPeaks(FittedPeaks=peaks_tbl, OutParametersTable=out_tbl_name) - - # Print the results - print("DIFA: %.1f" % difa) - print("DIFC: %.0f" % round(difc,-1)) - print("TZERO: %.0f" %round(tzero,-1)) - tbl = mtd[out_tbl_name] - print("The output table has %d row(s)" % tbl.rowCount()) - print("Parameters from the table, DIFA: %.1f, DIFC: %.0f, TZERO: %.0f" % (tbl.cell(0,0), round(tbl.cell(0,1),-1), round(tbl.cell(0,2),-1))) - print("Number of peaks fitted: {0}".format(peaks_tbl.rowCount())) - print("First peak expected (dSpacing): {0}".format(peaks_tbl.column('dSpacing')[0])) - print("First fitted peak center (ToF): {0:.1f}".format(peaks_tbl.column('X0')[0])) - print("Second peak expected (dSpacing): {0}".format(peaks_tbl.column('dSpacing')[1])) - print("Second fitted peak center (ToF): {0:.0f}".format(round(peaks_tbl.column('X0')[1],-1))) - -Output: - -.. testcleanup:: ExTwoPeaks - - DeleteWorkspace(out_tbl_name) - -.. testoutput:: ExTwoPeaks - - DIFA: 0.0 - DIFC: 18180 - TZERO: 460 - The output table has 1 row(s) - Parameters from the table, DIFA: 0.0, DIFC: 18180, TZERO: 460 - Number of peaks fitted: 2 - First peak expected (dSpacing): 0.8 - First fitted peak center (ToF): 15006.0 - Second peak expected (dSpacing): 1.9 - Second fitted peak center (ToF): 35010 - -.. categories:: - -.. sourcelink:: diff --git a/docs/source/algorithms/EnggFitPeaks-v1.rst b/docs/source/algorithms/EnggFitPeaks-v1.rst index b94994f858a4545391caacd06b6ba1d2974e3711..c731ca5f11bf216a0d3aac328fc1949ad4064633 100644 --- a/docs/source/algorithms/EnggFitPeaks-v1.rst +++ b/docs/source/algorithms/EnggFitPeaks-v1.rst @@ -40,7 +40,7 @@ spectrum) and has a log entry named "difc", where the GSAS DIFC parameter is expected. Otherwise the conversion of units is done as in the Mantid :ref:`ConvertUnits <algm-ConvertUnits>`. -.. seealso:: :ref:`EnggFitDIFCFromPeaks <algm-EnggFitDIFCFromPeaks>`. +.. seealso:: :ref:`EnggFitTOFFromPeaks <algm-EnggFitTOFFromPeaks>`. This algorithm currently fits (single) peaks of type :ref:`Back2BackExponential <func-BackToBackExponential>`. Other diff --git a/docs/source/algorithms/EnggFitTOFFromPeaks-v1.rst b/docs/source/algorithms/EnggFitTOFFromPeaks-v1.rst new file mode 100644 index 0000000000000000000000000000000000000000..1ac50b16bee6d4d692cf04e1cec233c22457b963 --- /dev/null +++ b/docs/source/algorithms/EnggFitTOFFromPeaks-v1.rst @@ -0,0 +1,111 @@ +.. algorithm:: + +.. summary:: + +.. relatedalgorithms:: + +.. properties:: + +Description +----------- + +.. warning:: + + This algorithm is being developed for a specific instrument. It + might undergo significant changes, should instrument scientists + decide to do so. + +Finds the calibration parameters DIFA, DIFC and TZERO (as defined in GSAS) +from a list of peaks fitted to a diffraction pattern. The peaks can be +fitted to a Mantid workspace spectrum using the algorithm +:ref:`EnggFitPeaks <algm-EnggFitPeaks>` which produces a table with +parameter values for peaks in time-of-flight (TOF, see +:ref:`Unit Factory <Unit Factory>`). The table is the essential +input to this algorithm. + +This algorithm fits the adjusted peak time-of-flight value positions +that are fitted against expected dSpacing values (d) according to the +expression: + +.. math:: TOF = DIFA*d^2 + DIFC*d + TZERO + +The calibration parameters DIFA, TZERO and DIFC can then be used within the +GSAS program or in other Mantid algorithms (see :ref:`EnggCalibrate +<algm-EnggCalibrate>` and :ref:`EnggCalibrateFull +<algm-EnggCalibrateFull>`). These parameters are returned and can be +retrieved as output properties as well. + +If a name is given in OutParametersTable this algorithm also produces +a table workspace with that name, containing the parameters fitted +(DIFA, DIFC, TZERO). + +The parameters DIFA, DIFC, TZERO are also used in other Mantid +algorithms. For example see :ref:`AlignDetectors +<algm-AlignDetectors>` where these parameters are used to convert +units from time-of-flight to d-spacing. + +Usage +----- + +**Example - Fitting DIFC parameter with two peaks:** + +.. testcode:: ExTwoPeaks + + # Three Back2Back exponential peaks + peak1 = "name=BackToBackExponential,I=6000,A=1,B=0.5,X0=15000,S=250" + peak2 = "name=BackToBackExponential,I=6000,A=1,B=0.5,X0=27500,S=250" + peak3 = "name=BackToBackExponential,I=5000,A=1,B=0.7,X0=35000,S=300" + + # Create workpsace with the above peaks and a single detector pixel + ws = CreateSampleWorkspace(Function="User Defined", + UserDefinedFunction=peak1 + ";" + peak2 + ";" + peak3, + NumBanks=1, + BankPixelWidth=1, + XMin=6000, + XMax=45000, + BinWidth=10) + + # Update instrument geometry to something that would allow converting to some sane dSpacing values + EditInstrumentGeometry(Workspace = ws, L2 = [1.5], Polar = [90], PrimaryFlightPath = 50) + + # Run the algorithm. Defaults are shown below. Files entered must be in .csv format and if both ExpectedPeaks and ExpectedPeaksFromFile are entered, the latter will be used. + + peaks_tbl = EnggFitPeaks(ws, 0, [0.8, 1.5, 1.9]) + out_tbl_name = 'difc_from_peaks' + difa, difc, tzero = EnggFitTOFFromPeaks(FittedPeaks=peaks_tbl, OutParametersTable=out_tbl_name) + + # Print the results + print("DIFA: %.1f" % difa) + print("DIFC: %.0f" % round(difc,-1)) + print("TZERO: %.0f" %round(tzero,-1)) + tbl = mtd[out_tbl_name] + print("The output table has %d row(s)" % tbl.rowCount()) + print("Parameters from the table, DIFA: %.1f, DIFC: %.0f, TZERO: %.0f" % (tbl.cell(0,0), round(tbl.cell(0,1),-1), round(tbl.cell(0,2),-1))) + print("Number of peaks fitted: {0}".format(peaks_tbl.rowCount())) + print("First peak expected (dSpacing): {0}".format(peaks_tbl.column('dSpacing')[0])) + print("First fitted peak center (ToF): {0:.1f}".format(peaks_tbl.column('X0')[0])) + print("Second peak expected (dSpacing): {0}".format(peaks_tbl.column('dSpacing')[1])) + print("Second fitted peak center (ToF): {0:.0f}".format(round(peaks_tbl.column('X0')[1],-1))) + +Output: + +.. testcleanup:: ExTwoPeaks + + DeleteWorkspace(out_tbl_name) + +.. testoutput:: ExTwoPeaks + + DIFA: 815.7 + DIFC: 15980 + TZERO: 1700 + The output table has 1 row(s) + Parameters from the table, DIFA: 815.7, DIFC: 15980, TZERO: 1700 + Number of peaks fitted: 3 + First peak expected (dSpacing): 0.8 + First fitted peak center (ToF): 15006.0 + Second peak expected (dSpacing): 1.5 + Second fitted peak center (ToF): 27510 + +.. categories:: + +.. sourcelink:: diff --git a/docs/source/interfaces/Engineering Diffraction.rst b/docs/source/interfaces/Engineering Diffraction.rst index 8bed2a18ab538e84f45e82b2a3053577930e3385..ac36fe86cb58fccd8a772c72e4f28d5fd364ac7e 100644 --- a/docs/source/interfaces/Engineering Diffraction.rst +++ b/docs/source/interfaces/Engineering Diffraction.rst @@ -81,9 +81,9 @@ vanadium curves and Ceria peaks. For Ceria peaks there will be two workspaces generated and plotted, one for each bank, whereas for a cropped calibration there will only be one workspace generated and plotted, depending on the selected bank or provided Spectrum -IDs. The workspace contains difc and tzero data which is then +IDs. The workspace contains difa, difc and tzero data which is then utilised to plot the Ceria peaks per bank, the graph will plot Peaks -Fitted and Difc/TZero Straight Line for comparison. More information +Fitted and TOF Straight Line for comparison. More information regarding the fit peaks can be found on the :ref:`EnggFitPeaks<algm-EnggFitPeaks>` documentation. diff --git a/docs/source/release/v3.7.1/diffraction.rst b/docs/source/release/v3.7.1/diffraction.rst index a8c140af8b1d7b6f737795dba3c788334ca206b4..022f7b326681efa0fe0e3874df2407fa0eda746b 100644 --- a/docs/source/release/v3.7.1/diffraction.rst +++ b/docs/source/release/v3.7.1/diffraction.rst @@ -59,7 +59,7 @@ Engineering Diffraction parameters (whole pattern refinement) and/or fit peaks. - New algorithm added: - :ref:`EnggFitDIFCFromPeaks<algm-EnggFitDIFCFromPeaks>`, which forks + :ref:`EnggFitDIFCFromPeaks<algm-EnggFitTOFFromPeaks>`, which forks from the old :ref:`EnggFitPeaks<algm-EnggFitPeaks>`. :ref:`EnggFitPeaks<algm-EnggFitPeaks>` modified to fit peaks but not calibration parameters.