Commit f755fe1c authored by Peterson, Peter's avatar Peterson, Peter
Browse files

Reseting to versions from master

parent 83d12a56
......@@ -181,7 +181,8 @@ The workflow follows these step:
groups,
DReference=2.0,
Xmin=1.75,
Xmax=2.25)
Xmax=2.25,
OffsetThreshold=1.0)
print("DetID DIFC")
for detid, difc in zip(cc_diffcal.column('detid'), cc_diffcal.column('difc')):
......@@ -193,9 +194,58 @@ The workflow follows these step:
1 2208.3
2 2318.6
3 2098.0
4 2208.3
5 2161.2
6 2115.7
4 2255.4
5 2208.3
6 2163.0
.. testcode:: group_cal
# In this case, cycling through cross correlation until offset converges.
cc_diffcal = cc_calibrate_groups(ws,
groups,
DReference=2.0,
Xmin=1.75,
Xmax=2.25,
OffsetThreshold=1E-4)
print("DetID DIFC")
for detid, difc in zip(cc_diffcal.column('detid'), cc_diffcal.column('difc')):
print(f'{detid:>5} {difc:.1f}')
.. testoutput:: group_cal
DetID DIFC
1 2208.3
2 2318.7
3 2097.9
4 2253.3
5 2208.3
6 2165.0
.. testcode:: group_cal
# Turn on cross correlation cycling but skip cross correlation for group-1.
cc_diffcal = cc_calibrate_groups(ws,
groups,
DReference=2.0,
Xmin=1.75,
Xmax=2.25,
OffsetThreshold=1E-4,
SkipCrossCorrelation=[1])
print("DetID DIFC")
for detid, difc in zip(cc_diffcal.column('detid'), cc_diffcal.column('difc')):
print(f'{detid:>5} {difc:.1f}')
.. testoutput:: group_cal
DetID DIFC
1 2208.3
2 2208.3
3 2208.3
4 2253.3
5 2208.3
6 2165.0
.. testcode:: group_cal
......@@ -215,12 +265,12 @@ The workflow follows these step:
.. testoutput:: group_cal
DetID DIFC
1 2208.7
2 2319.0
3 2098.4
4 2368.8
5 2318.3
6 2269.5
1 2319.1
2 2319.1
3 2319.1
4 2365.5
5 2318.2
6 2272.7
The evolution in the calibration can be seen with
......@@ -287,7 +337,8 @@ The same complete calibration can just be run with just
cc_kwargs={
"DReference": 2.0,
"Xmin": 1.75,
"Xmax": 2.25},
"Xmax": 2.25,
"OffsetThreshold": 1.0},
pdcal_kwargs={
"PeakPositions": [1.0, 2.0, 3.0],
"PeakFunction": 'Gaussian',
......@@ -303,9 +354,9 @@ The same complete calibration can just be run with just
1 2208.7
2 2319.0
3 2098.4
4 2368.8
5 2318.3
6 2269.5
4 2367.7
5 2318.2
6 2270.7
The resulting :ref:`diffcal <DiffractionCalibrationWorkspace>` can be
saved with :ref:`SaveDiffCal <algm-SaveDiffCal>`.
......@@ -317,6 +368,192 @@ saved with :ref:`SaveDiffCal <algm-SaveDiffCal>`.
Filename='calibration.h5')
.. _calibration_tofpd_group_calibration_howto-ref:
Group calibration how-to's
~~~~~~~~~~~~~~~~~~~~~~~~~~
**Generate grouping file**
The first stage of the group calibration is to generate suitable grouping scheme
for all spectra involved. The principle is to group similar spectra together.
A natural choice for generating grouping file is to use :ref:`CreateGroupingWorkspace <algm-CreateGroupingWorkspace>`
algorithm which embodies several choices of grouping detectors according to physical geometry. A generic approach
has also been implemented into the framework of `mantidtotalscattering <https://github.com/neutrons/mantid_total_scattering>`_,
which automatically groups input spectra according to the similarity among each other, based on a unsupervised clustering algorithm.
``mantidtotalscattering`` has been deployed on SNS analysis cluster and therefore the generic grouping routine can be accessed easily
from analysis. To activate the `mantidtotalscattering` conda environment, one needs to first log into analysis cluster and the
following commands could be executed from terminal,
.. code-block:: bash
. /opt/anaconda/etc/profile.d/conda.sh
conda activate mantidtotalscattering
With the `mantidtotalscattering` conda environment active, here follows is provided a simple Python script for calling the generic
grouping routine on analysis,
.. code-block:: python
#!/usr/bin/env python
import sys
import json
from total_scattering.autogrouping.autogrouping import main
jsonfile = "/SNS/users/y8z/Temp/autogrouping_config.json"
with open(jsonfile, 'r') as jf:
config = json.load(jf)
# execute
main(config)
An example json file is presented below to control the grouping behavior,
.. code-block:: json
{
"DiamondFile": "/SNS/NOM/IPTS-24637/nexus/NOM_144974.nxs.h5",
"MaskFile": "/SNS/users/y8z/Temp/mask144974.out",
"GroupingMethod": "KMEANS_ED",
"NumberOutputGroups": "4",
"StandardScaling": false,
"FittingFunctionParameters": "Mixing,Intensity,PeakCentre,FWHM",
"FitPeaksArgs": { "PeakFunction": "PseudoVoigt",
"PeakParameterNames": "Mixing",
"PeakParameterValues": "0.6",
"HighBackground": false,
"MinimumPeakHeight": 3,
"ConstrainPeakPositions": false
},
"DiamondPeaks": "0.8920,1.0758,1.2615",
"ParameterThresholds": { "PeakCentre": "(0.01,10.0)",
"Height": "(0.0,10000.0)"
},
"FilterByChi2": { "Enable": true,
"Value": 1e4
},
"OutputGroupingFile": "./outputgrouping.xml",
"OutputMaskFile": "./outputmask.txt",
"OutputFitParamFile": "./outputfitparamtable.nxs",
"CacheDir": "./tmp/",
"Plots": { "Grouping": true,
"ED_Features": true,
"PCA": true,
"KMeans_Elbow": true,
"KMeans_Silhouette": true}
}
and description for entries in the input json file is summarized in the following table,
.. list-table::
:widths: 25 50
:header-rows: 1
* - Name
- Description
* - DiamondFile
- Full name of the input nexus file. For calibration purpose, usually a diamond measurement will be used.
* - MaskFile
- Full name of the input mask file. The file should contain a whole bunch of lines with a single integter in each line specifying the detector ID to be masked (index starting from 0).
* - GroupingMethod
- The method to be used for grouping. Valid input could be ``KMEANS_CC``, ``KMEANS_DG``, ``KMEANS_ED``, ``DBSCAN_CC``, ``DBSCAN_DG`` and ``DBSCAN_ED``. ``KMEANS`` and ``DBSCAN`` refers to the two clustering methods. The second part of those values refers to the method for calculating similarity between spectra. ``CC`` for cross-correlation, ``DG`` for De Gelder similarity and ``ED`` for Euclidean distance in parameter space.
* - NumberOutputGroups
- The number of groups to cluster all input spectra into. If using ``DBSCAN`` method, there is no need to specify this parameter.
* - StandardScaling
- Whether or not to scale the input spectra by removing the mean and scaling to unit variance before clustering.
* - WorkspaceIndexRange
- Range of workspace indeces to include in automatic grouping process.
* - FittingFunctionParameters
- If ``ED`` method is to be used for calculating similarity between spectra, this specifies the peak parameters to fit and to be used as the coordinate components in parameter space.
* - FitPeaksArgs
- Refer to the input parameters for :ref:`FitPeaks <algm-FitPeaks>` algorithm.
* - DiamondPeaks
- If ``ED`` method is to be used for calculating similarity between spectra, this specifies the diamond peaks, as specified by the nominal peak positions, to be used for peak fitting and clustering.
* - ParameterThresholds
- If ``ED`` method is to be used for calculating similarity between spectra, this specifies the threshold for relevant peak parameters. The threshold for each relevant peak parameter will be given as sub-entries.
* - FilterByChi2
- If ``ED`` method is to be used for calculating similarity between spectra, this specifies whether or not to mask out pixels based on chi square of peak fitting. Among the two sub-entries, ``Enable`` is a boolean trigger and ``Value`` is the threshold of chi square.
* - OutputGroupingFile
- Full name of the output grouping file.
* - OutputMaskFile
- Full name of the output masking file.
* - OutputFitParamFile
- If ``ED`` method is to be used for calculating similarity between spectra, this specifies the full name of the output fit parameters file.
* - CacheDir
- Cache directory.
* - Plots
- A series of boolean variables control the plotting options. ``Grouping`` for plotting the grouping of detectors. ``ED_Features`` for plotting parameters correlation features. ``KMeans_Elbow`` for plotting the elbow analysis result. ``KMeans_Silhouette`` for plotting the Silhouette score.
Here, it is worth noting that detectors may be masked out as belonging to none of the generated groups.
For example, when using the ``ED`` method for defining the similarity between spectra, detectors will be masked out at the fitting stage if the corresponding spectra cannot be fitted successfully.
Following is presented the clustering result for a NOMAD diamond measurement data,
.. figure:: /images/NOMAD_Grouping.png
:width: 400px
:align: right
.. note::
For certain instruments (e.g., POWGEN), the automatic grouping routine may not work due to special d-space coverage for detectors.
In this case, one may need to treat various ranges of detectors individually (using the input entry ``WorkspaceIndexRange`` in the input json file) and also some of the groups may need to be manually specified.
**Group calibration**
Having the grouping file (and potentially the masking file) ready, one can then open Mantid workbench interface and trigger the group calibration routine, using a simple Python script, as presented below,
.. code-block:: python
# import mantid algorithms, numpy and matplotlib
from mantid.simpleapi import *
import matplotlib.pyplot as plt
import numpy as np
from Calibration.tofpd import group_calibration
infile = "/SNS/NOM/shared/User_story_test/NOM_US-231_240/group_calib.json"
group_calibration.process_json(infile)
Here follows is presented a demo input json file,
.. code-block:: json
{
"Calibrant": "161450",
"Groups": "/SNS/NOM/shared/User_story_test/NOM_US-231_240/outputgrouping.xml",
"Mask": "/SNS/NOM/shared/User_story_test/NOM_US-231_240/outputmask.xml",
"Instrument": "NOMAD",
"Date" : "2021_07_21",
"SampleEnvironment": "shifter",
"CalDirectory": "/SNS/NOM/shared/User_story_test/NOM_US-231_240/",
"CrossCorrelate": {"Step": 0.001,
"DReference": 1.2615,
"Xmin": 1.0,
"Xmax": 3.0,
"MaxDSpaceShift": 0.25,
"OffsetThreshold": 1E-4,
"SkipCrossCorrelation": [1,2,3]},
"PDCalibration": {"TofBinning": [300,0.01,16666],
"PeakFunction": "Gaussian",
"PeakWindow": 0.1,
"PeakWidthPercent": 0.001}
}
Parameters in the input json file should be self-explaining. Here only the ``Calibrant`` and ``Groups`` entries are mandatory. For ``CrossCorrelate`` entries, one can refer to the parameters for
:ref:`CrossCorrelate <algm-CrossCorrelate>` and :ref:`GetDetectorOffsets <algm-GetDetectorOffsets>`. For ``PDCalibration`` entries, one can refer to the parameters for :ref:`PDCalibration <algm-PDCalibration>`. In the group calibration workflow, one of the crucial steps is to cross correlate spectra in a
single group. A cycling cross correlation scheme is introduced at this point to continue cross correlate spectra until the median value of the offset of all
spectra in a single group is below the preset threshold (specified by the ``OffsetThreshol`` parameter). If the ``OffsetThreshold`` is set to 1.0 or larger, that means no cycling of cross correlation will be conducted. The ``SkipCrossCorrelation`` parameter is to control the skipping of cross correlation for specified groups of spectra. For ``Xmin``, ``Xmax``, ``MaxDSpaceShift`` and ``OffsetThreshold`` parameters, they can be either provided with a single number or a list. When a single number is given, the value will apply to all groups, whereas if a list is given, each entry in the list will apply to each single group respectively.
After the group calibration is complete, one can then inspect the quality of calibration by generating various diagnostics plots as documented in :ref:`Calibration Diagnostics`.
Saving and Loading Calibration
##############################
......@@ -402,6 +639,8 @@ This approach attempts to correct the instrument component positions based on th
* At ISIS enter the resulting workspace as the calibration workspace into the DAE software when recording new runs. The calibrated workspace will be copied into the resulting NeXuS file of the run.
.. _Calibration Diagnostics:
Calibration Diagnostics
-----------------------
......
......@@ -76,7 +76,7 @@ Bugfixes
- An empty Engineering Diffraction interface is no longer saved if the user saves a project having previously had the interface open at some point in that session
- The help button on the Engineering Diffraction interface points to the correct page, having been broken in the last release
- Using the Clear button on the Workspace widget while using the Fitting tab no longer causes issues when you try to load runs back in.
- On the fitting tab of the EngDiff UI the background can be inspected whether the background subtraction box is checked or not.
Single Crystal Diffraction
--------------------------
......
......@@ -10,7 +10,10 @@ from mantid.simpleapi import (ConvertUnits, ExtractSpectra, Rebin,
DiffractionFocussing, PDCalibration,
Load, LoadMask, CombineDiffCal,
LoadDiffCal, LoadDetectorsGroupingFile,
SaveDiffCal, DeleteWorkspace, logger)
SaveDiffCal, DeleteWorkspace, logger,
RenameWorkspace, Integration, CloneWorkspace,
CreateGroupingWorkspace, CreateDetectorTable,
CreateEmptyTableWorkspace)
# Diamond peak positions in d-space
DIAMOND = (0.3117,0.3257,0.3499,0.4205,0.4645,
......@@ -27,14 +30,20 @@ def cc_calibrate_groups(data_ws,
DReference=1.2615,
Xmin=1.22,
Xmax=1.30,
MaxDSpaceShift=None):
MaxDSpaceShift=None,
OffsetThreshold=1E-4,
SkipCrossCorrelation=[]):
"""This will perform the CrossCorrelate/GetDetectorOffsets on a group
of detector pixel.
It works by looping over the different groups in the group_ws,
extracting all unmasked spectra of a group, then running
CrossCorrelate and GetDetectorOffsets on just that group, and
combinning the results at the end.
combinning the results at the end. When running a group,
CrossCorrelate and GetDetectorOffsets could be cycled until
converging of offsets is reached, given the user input offset
threshold. If offset threshold is specified to be equal to or
larger than 1.0, no cycling will be carried out.
The first unmasked spectra of the group will be used for the
ReferenceSpectra in CrossCorrelate.
......@@ -48,6 +57,8 @@ def cc_calibrate_groups(data_ws,
:param Xmin: Xmin parameter for CrossCorrelate, default 1.22
:param Xmax: Xmax parameter for CrossCorrelate, default 1.30
:param MaxDSpaceShift: MaxDSpaceShift paramter for CrossCorrelate, default None
:param OffsetThreshold: Convergence threshold for cycling cross correlation, default 1E-4
:param SkipCrossCorrelation: Skip cross correlation for specified groups.
:return: Combined DiffCal workspace from all the different groups
"""
if previous_calibration:
......@@ -57,7 +68,19 @@ def cc_calibrate_groups(data_ws,
group_list = np.unique(group_ws.extractY())
_accum_cc = None
to_skip = []
for group in group_list:
# Figure out input parameters for CrossCorrelate and GetDetectorOffset, specifically
# for those parameters for which both a single value and a list is accepted. If a
# list is given, that means different parameter setup will be used for different groups.
Xmin_group = Xmin[int(group) - 1] if type(Xmin) == list else Xmin
Xmax_group = Xmax[int(group) - 1] if type(Xmax) == list else Xmax
MDS_group = MaxDSpaceShift[int(group) - 1] if type(MaxDSpaceShift) == list else MaxDSpaceShift
DRef_group = DReference[int(group) - 1] if type(DReference) == list else DReference
OT_group = OffsetThreshold[int(group) - 1] if type(OffsetThreshold) == list else OffsetThreshold
cycling = OT_group < 1.0
indexes = np.where(group_ws.extractY().flatten() == group)[0]
sn = np.array(group_ws.getSpectrumNumbers())[indexes]
try:
......@@ -65,29 +88,97 @@ def cc_calibrate_groups(data_ws,
except RuntimeError:
# data does not contain spectrum in group
continue
if group in SkipCrossCorrelation:
to_skip.extend(ws_indexes)
ExtractSpectra(data_d, WorkspaceIndexList=ws_indexes, OutputWorkspace='_tmp_group_cc')
ExtractUnmaskedSpectra('_tmp_group_cc', OutputWorkspace='_tmp_group_cc')
if mtd['_tmp_group_cc'].getNumberHistograms() < 2:
ExtractSpectra(data_ws, WorkspaceIndexList=ws_indexes, OutputWorkspace='_tmp_group_cc_raw')
ExtractUnmaskedSpectra('_tmp_group_cc_raw', OutputWorkspace='_tmp_group_cc_raw')
num_spectra = mtd['_tmp_group_cc'].getNumberHistograms()
if num_spectra < 2:
continue
Rebin('_tmp_group_cc', Params=f'{Xmin},{Step},{Xmax}', OutputWorkspace='_tmp_group_cc')
CrossCorrelate('_tmp_group_cc',
Xmin=Xmin, XMax=Xmax,
MaxDSpaceShift=MaxDSpaceShift,
WorkspaceIndexMin=0,
WorkspaceIndexMax=mtd['_tmp_group_cc'].getNumberHistograms()-1,
OutputWorkspace='_tmp_group_cc')
bin_range = (Xmax-Xmin)/Step
GetDetectorOffsets(InputWorkspace='_tmp_group_cc',
Step=Step,
Xmin=-bin_range, XMax=bin_range,
DReference=DReference,
MaxOffset=1,
Rebin('_tmp_group_cc', Params=f'{Xmin_group},{Step},{Xmax_group}', OutputWorkspace='_tmp_group_cc')
# Figure out brightest spectra to be used as the reference for cross correlation.
CloneWorkspace('_tmp_group_cc_raw', OutputWorkspace='_tmp_group_cc_raw_tmp')
intg = Integration('_tmp_group_cc_raw_tmp',
StartWorkspaceIndex=0,
EndWorkspaceIndex=num_spectra-1,
OutputWorkspace='_tmp_group_intg')
brightest_spec_index = int(np.argmax(np.array([intg.readY(i)[0] for i in range(num_spectra)])))
# Cycling cross correlation. At each step, we will use the obtained offsets and DIFC's from
# previous step to obtain new DIFC's. In this way, spectra in group will come closer and closer
# to each other as the cycle goes. This will continue until converging criterion is reached. The
# converging criterion is set in such a way that the median value of all the non-zero offsets
# should be smaller than the threshold (user tuned parameter, default to 1E-4, meaning 0.04%
# relative offset).
num_cycle = 1
while True:
CrossCorrelate('_tmp_group_cc',
Xmin=Xmin_group, XMax=Xmax_group,
MaxDSpaceShift=MDS_group,
ReferenceSpectra=brightest_spec_index,
WorkspaceIndexMin=0,
WorkspaceIndexMax=num_spectra-1,
OutputWorkspace='_tmp_group_cc')
previous_calibration = ConvertDiffCal('_tmp_group_cc',
PreviousCalibration=previous_calibration,
OutputWorkspace=f'{output_basename}_cc_diffcal')
DeleteWorkspace('_tmp_group_cc')
bin_range = (Xmax_group-Xmin_group)/Step
GetDetectorOffsets(InputWorkspace='_tmp_group_cc',
Step=Step,
Xmin=-bin_range, XMax=bin_range,
DReference=DRef_group,
MaxOffset=1,
OutputWorkspace='_tmp_group_cc')
if group not in SkipCrossCorrelation:
offsets_tmp = []
for item in ws_indexes:
if abs(mtd['_tmp_group_cc'].readY(item)) != 0:
offsets_tmp.append(abs(mtd['_tmp_group_cc'].readY(item)))
offsets_tmp = np.array(offsets_tmp)
logger.information(f'Running group-{group}, cycle-{num_cycle}.')
logger.information(f'Median offset (no sign) = {np.median(offsets_tmp)}')
converged = np.median(offsets_tmp) < OT_group
else:
for item in ws_indexes:
mtd['_tmp_group_cc'].dataY(item)[0] = 0.0
logger.information(f'Cross correlation skipped for group-{group}.')
converged = True
if not cycling or converged:
if cycling and converged:
if group not in SkipCrossCorrelation:
logger.information(f'Cross correlation for group-{group} converged, ')
logger.information(f'with offset threshold {OT_group}.')
break
else:
previous_calibration = ConvertDiffCal('_tmp_group_cc',
PreviousCalibration=previous_calibration,
OutputWorkspace='_tmp_group_cc_diffcal')
ApplyDiffCal('_tmp_group_cc_raw', CalibrationWorkspace='_tmp_group_cc_diffcal')
ConvertUnits('_tmp_group_cc_raw', Target='dSpacing', OutputWorkspace='_tmp_group_cc')
Rebin('_tmp_group_cc', Params=f'{Xmin_group},{Step},{Xmax_group}', OutputWorkspace='_tmp_group_cc')
num_cycle += 1
if not _accum_cc:
_accum_cc = RenameWorkspace('_tmp_group_cc')
else:
_accum_cc += mtd['_tmp_group_cc']
# DeleteWorkspace('_tmp_group_cc')
previous_calibration = ConvertDiffCal('_accum_cc',
PreviousCalibration=previous_calibration,
OutputWorkspace=f'{output_basename}_cc_diffcal')
DeleteWorkspace('_accum_cc')
DeleteWorkspace('_tmp_group_cc')
DeleteWorkspace('_tmp_group_cc_raw')
if cycling:
DeleteWorkspace('_tmp_group_cc_diffcal')
return mtd[f'{output_basename}_cc_diffcal']
......@@ -95,13 +186,15 @@ def cc_calibrate_groups(data_ws,
def pdcalibration_groups(data_ws,
group_ws,
cc_diffcal,
mask=None,
output_basename="_tmp_group_pd_calibration",
previous_calibration=None,
PeakPositions=DIAMOND,
TofBinning=(300,-.001,16666.7),
PeakFunction='IkedaCarpenterPV',
PeakWindow=0.1,
PeakWidthPercent=None):
PeakWidthPercent=None,
BadCalibThreshold=100):
"""This will perform PDCalibration of the group data and combine the
results with the results of `cc_calibrate_groups`.
......@@ -126,39 +219,100 @@ def pdcalibration_groups(data_ws,
:param PeakFunction: PeakFunction parameter of PDCalibration, default 'IkedaCarpenterPV'
:param PeakWindow: PeakWindow parameter of PDCalibration, default 0.1
:param PeakWidthPercent: PeakWidthPercent parameter of PDCalibration, default None
:return: tuple of DiffCal and Mask from CrossCorrelate combined with DiffCal from PDCalibration of grouped workspace
:param BadCalibThreshold: Threshold for relative difference between calibrated DIFC and engineering value.
:return: tuple of DiffCal and Mask (both as TableWorkspace objects) holding the combined DiffCal.
"""
CreateDetectorTable(data_ws, DetectorTableWorkspace="calib_table_bak")
ApplyDiffCal(data_ws, CalibrationWorkspace=cc_diffcal)
ConvertUnits(data_ws, Target='dSpacing', OutputWorkspace='_tmp_data_aligned')
DiffractionFocussing('_tmp_data_aligned', GroupingWorkspace=group_ws, OutputWorkspace='_tmp_data_aligned')
ConvertUnits('_tmp_data_aligned', Target='TOF', OutputWorkspace='_tmp_data_aligned')
PDCalibration(InputWorkspace='_tmp_data_aligned',
TofBinning=TofBinning,
PreviousCalibrationTable=previous_calibration,
PeakFunction=PeakFunction,
PeakPositions=PeakPositions,
PeakWindow=PeakWindow,
PeakWidthPercent=PeakWidthPercent,
OutputCalibrationTable=f'{output_basename}_pd_diffcal',
DiagnosticWorkspaces=f'{output_basename}_pd_diag')
instrument = data_ws.getInstrument().getName()
if instrument != 'POWGEN':
PDCalibration(InputWorkspace='_tmp_data_aligned',
TofBinning=TofBinning,
PreviousCalibrationTable=previous_calibration,
PeakFunction=PeakFunction,
PeakPositions=PeakPositions,
PeakWindow=PeakWindow,
PeakWidthPercent=PeakWidthPercent,
OutputCalibrationTable=f'{output_basename}_pd_diffcal',
DiagnosticWorkspaces=f'{output_basename}_pd_diag')
else:
PDCalibration(InputWorkspace='_tmp_data_aligned',
TofBinning=TofBinning,
PreviousCalibrationTable=previous_calibration,
PeakFunction=PeakFunction,
PeakPositions=PeakPositions,
PeakWindow=PeakWindow,
PeakWidthPercent=PeakWidthPercent,
OutputCalibrationTable='PDCalib',
DiagnosticWorkspaces='diag')
PDCalibration(InputWorkspace='_tmp_data_aligned',
TofBinning=[TofBinning[0], TofBinning[1]/2, TofBinning[2]],
PreviousCalibrationTable='PDCalib',
PeakFunction=PeakFunction,
PeakPositions=PeakPositions,
PeakWindow=PeakWindow,
PeakWidthPercent=PeakWidthPercent/2.0,
OutputCalibrationTable='PDCalib',
DiagnosticWorkspaces='diag')
PDCalibration(InputWorkspace='_tmp_data_aligned',
TofBinning=[TofBinning[0], TofBinning[1]/2, TofBinning[2]],
PreviousCalibrationTable='PDCalib',
PeakFunction=PeakFunction,
PeakPositions=PeakPositions,
PeakWindow=PeakWindow,
PeakWidthPercent=PeakWidthPercent/2.0,
OutputCalibrationTable=f'{output_basename}_pd_diffcal',
DiagnosticWorkspaces=f'{output_basename}_pd_diag')
DeleteWorkspace('PDCalib')
DeleteWorkspace('PDCalib_mask')
DeleteWorkspace('diag')
CombineDiffCal(PixelCalibration=cc_diffcal,
GroupedCalibration=f'{output_basename}_pd_diffcal',
CalibrationWorkspace='_tmp_data_aligned',
MaskWorkspace=f'{output_basename}_pd_diffcal_mask',
OutputWorkspace=f'{output_basename}_cc_pd_diffcal')
OutputWorkspace=f'{output_basename}_cc_pd_diffcal_tmp')
DeleteWorkspace('_tmp_data_aligned')
out_table = CreateEmptyTableWorkspace(OutputWorkspace=f'{output_basename}_cc_pd_diffcal')
out_table.addColumn("int", "detid")
out_table.addColumn("double", "difc")
out_table.addColumn("double", "difa")
out_table.addColumn("double", "tzero")
num_hist = data_ws.getNumberHistograms()
for i in range(num_hist):