parameters: (data_ws,
                          group_ws,
                          output_basename="_tmp_group_cc_calibration",
                          previous_calibration=None,
                          Step=0.001,
                          DReference=1.2615,
                          Xmin=1.22,
                          Xmax=1.30,
                          MaxDSpaceShift=None,
                          OffsetThreshold=1E-4,
                          SkipCrossCorrelation=[],
                          PeakFunction="Gaussian",
                          SmoothNPoints=0)
return_statement: return mtd[f'{output_basename}_cc_diffcal'], to_skip
docstring: 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. 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.
  
      :param data_ws: Input calibration raw data (in TOF), assumed to already be correctly masked
      :param group_ws: grouping workspace, e.g. output from LoadDetectorsGroupingFile
      :param output_basename: Optional name to use for temporay and output workspace
      :param previous_calibration: Optional previous diffcal workspace
      :param Step: step size for binning of data and input for GetDetectorOffsets, default 0.001
      :param DReference: Derefernce parameter for GetDetectorOffsets, default 1.2615
      :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
      :param PeakFunction: Peak function to use for extracting the offset
      :param SmoothNPoints: Number of points for smoothing spectra, for cross correlation ONLY
      :return: Combined DiffCal workspace from all the different groups
docstring_summary: This will perform the CrossCorrelate/GetDetectorOffsets on a group
      of detector pixel.
docstring_tokens: This will perform the CrossCorrelate / GetDetectorOffsets on a group of detector pixel .
function: def cc_calibrate_groups(data_ws,
                          group_ws,
                          output_basename="_tmp_group_cc_calibration",
                          previous_calibration=None,
                          Step=0.001,
                          DReference=1.2615,
                          Xmin=1.22,
                          Xmax=1.30,
                          MaxDSpaceShift=None,
                          OffsetThreshold=1E-4,
                          SkipCrossCorrelation=[],
                          PeakFunction="Gaussian",
                          SmoothNPoints=0):
      """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. 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.
  
      :param data_ws: Input calibration raw data (in TOF), assumed to already be correctly masked
      :param group_ws: grouping workspace, e.g. output from LoadDetectorsGroupingFile
      :param output_basename: Optional name to use for temporay and output workspace
      :param previous_calibration: Optional previous diffcal workspace
      :param Step: step size for binning of data and input for GetDetectorOffsets, default 0.001
      :param DReference: Derefernce parameter for GetDetectorOffsets, default 1.2615
      :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
      :param PeakFunction: Peak function to use for extracting the offset
      :param SmoothNPoints: Number of points for smoothing spectra, for cross correlation ONLY
      :return: Combined DiffCal workspace from all the different groups
      """
      if previous_calibration:
          ApplyDiffCal(data_ws, CalibrationWorkspace=previous_calibration)
  
      data_d = ConvertUnits(data_ws, Target='dSpacing', OutputWorkspace='data_d')
  
      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
          pf_group = PeakFunction[int(group) - 1] if type(PeakFunction) == list else PeakFunction
          snpts_group = SmoothNPoints[int(group) - 1] if type(SmoothNPoints) == list else SmoothNPoints
          cycling = OT_group < 1.0
  
          indexes = np.where(group_ws.extractY().flatten() == group)[0]
          sn = np.array(group_ws.getSpectrumNumbers())[indexes]
          try:
              ws_indexes = [data_d.getIndexFromSpectrumNumber(int(i)) for i in sn]
          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')
          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_group},{Step},{Xmax_group}', OutputWorkspace='_tmp_group_cc')
          if snpts_group >= 3:
              SmoothData('_tmp_group_cc', NPoints=snpts_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')
  
              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,
                                 PeakFunction=pf_group,
                                 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.notice(f'Running group-{group}, cycle-{num_cycle}.')
                  logger.notice(f'Median offset (no sign) = {np.median(offsets_tmp)}')
                  logger.notice(f'Running group-{group}, cycle-{num_cycle}.')
                  logger.notice(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.notice(f'Cross correlation skipped for group-{group}.')
                  converged = True
  
              if not cycling or converged:
                  if cycling and converged:
                      if group not in SkipCrossCorrelation:
                          logger.notice(f'Cross correlation for group-{group} converged, ')
                          logger.notice(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 and '_tmp_group_cc_diffcal' in mtd:
          DeleteWorkspace('_tmp_group_cc_diffcal')
  
      return mtd[f'{output_basename}_cc_diffcal'], to_skip
function_tokens: def cc_calibrate_groups ( data_ws , group_ws , output_basename = "_tmp_group_cc_calibration" , previous_calibration = None , Step = 0.001 , DReference = 1.2615 , Xmin = 1.22 , Xmax = 1.30 , MaxDSpaceShift = None , OffsetThreshold = 1E-4 , SkipCrossCorrelation = [ ] , PeakFunction = "Gaussian" , SmoothNPoints = 0 ) : if previous_calibration : ApplyDiffCal ( data_ws , CalibrationWorkspace = previous_calibration ) data_d = ConvertUnits ( data_ws , Target = 'dSpacing' , OutputWorkspace = 'data_d' ) 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 pf_group = PeakFunction [ int ( group ) - 1 ] if type ( PeakFunction ) == list else PeakFunction snpts_group = SmoothNPoints [ int ( group ) - 1 ] if type ( SmoothNPoints ) == list else SmoothNPoints cycling = OT_group < 1.0 indexes = np . where ( group_ws . extractY ( ) . flatten ( ) == group ) [ 0 ] sn = np . array ( group_ws . getSpectrumNumbers ( ) ) [ indexes ] try : ws_indexes = [ data_d . getIndexFromSpectrumNumber ( int ( i ) ) for i in sn ] 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' ) 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_group},{Step},{Xmax_group}' , OutputWorkspace = '_tmp_group_cc' ) if snpts_group >= 3 : SmoothData ( '_tmp_group_cc' , NPoints = snpts_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' ) 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 , PeakFunction = pf_group , 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 . notice ( f'Running group-{group}, cycle-{num_cycle}.' ) logger . notice ( f'Median offset (no sign) = {np.median(offsets_tmp)}' ) logger . notice ( f'Running group-{group}, cycle-{num_cycle}.' ) logger . notice ( 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 . notice ( f'Cross correlation skipped for group-{group}.' ) converged = True if not cycling or converged : if cycling and converged : if group not in SkipCrossCorrelation : logger . notice ( f'Cross correlation for group-{group} converged, ' ) logger . notice ( 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 and '_tmp_group_cc_diffcal' in mtd : DeleteWorkspace ( '_tmp_group_cc_diffcal' ) return mtd [ f'{output_basename}_cc_diffcal' ] , to_skip