Esempio n. 1
0
    def _reduction(self):
        """
        Run energy conversion for IN16B
        """
        from IndirectCommon import StartTime, EndTime

        StartTime('ILLindirect')

        if self._verbose:
            logger.notice('Input workspace : %s' % self._raw_workspace)

        idf_directory = config['instrumentDefinition.directory']
        ipf_name = self._instrument_name + '_' + self._analyser + '_' + self._reflection + '_Parameters.xml'
        ipf_path = os.path.join(idf_directory, ipf_name)

        LoadParameterFile(Workspace=self._raw_workspace, Filename=ipf_path)
        AddSampleLog(Workspace=self._raw_workspace,
                     LogName="facility",
                     LogType="String",
                     LogText="ILL")

        if self._map_file == '':
            # path name for default map file
            instrument = mtd[self._raw_workspace].getInstrument()
            if instrument.hasParameter('Workflow.GroupingFile'):
                grouping_filename = instrument.getStringParameter(
                    'Workflow.GroupingFile')[0]
                self._map_file = os.path.join(
                    config['groupingFiles.directory'], grouping_filename)
            else:
                raise ValueError(
                    "Failed to find default map file. Contact development team."
                )

        if self._verbose:
            logger.notice('Map file : %s' % self._map_file)

        grouped_ws = self._run_name + '_group'
        GroupDetectors(InputWorkspace=self._raw_workspace,
                       OutputWorkspace=grouped_ws,
                       MapFile=self._map_file,
                       Behaviour='Average')

        monitor_ws = self._run_name + '_mon'
        ExtractSingleSpectrum(InputWorkspace=self._raw_workspace,
                              OutputWorkspace=monitor_ws,
                              WorkspaceIndex=0)

        if self._use_mirror_mode:
            output_workspaces = self._run_mirror_mode(monitor_ws, grouped_ws)
        else:
            if self._verbose:
                logger.notice('Mirror sense is OFF')

            self._calculate_energy(monitor_ws, grouped_ws, self._red_workspace)
            output_workspaces = [self._red_workspace]

        EndTime('ILLindirect')
        return output_workspaces
Esempio n. 2
0
    def PyExec(self):
        from mantid import logger
        from IndirectCommon import StartTime, EndTime

        self._setup()
        StartTime('CreateCalibrationWorkspace')

        runs = []
        for in_file in self._input_files:
            (_, filename) = os.path.split(in_file)
            (root, _) = os.path.splitext(filename)
            try:
                Load(Filename=in_file,
                     OutputWorkspace=root,
                     SpectrumMin=int(self._spec_range[0]),
                     SpectrumMax=int(self._spec_range[1]),
                     LoadLogFiles=False)
                runs.append(root)
            except Exception as exc:
                logger.error('Could not load raw file "%s": %s' %
                             (in_file, str(exc)))

        calib_ws_name = 'calibration'
        if len(runs) > 1:
            MergeRuns(InputWorkspaces=",".join(runs),
                      OutputWorkspace=calib_ws_name)
            factor = 1.0 / len(runs)
            Scale(InputWorkspace=calib_ws_name,
                  OutputWorkspace=calib_ws_name,
                  Factor=factor)
        else:
            calib_ws_name = runs[0]

        CalculateFlatBackground(InputWorkspace=calib_ws_name,
                                OutputWorkspace=calib_ws_name,
                                StartX=self._back_range[0],
                                EndX=self._back_range[1],
                                Mode='Mean')

        from inelastic_indirect_reduction_steps import NormaliseToUnityStep
        ntu = NormaliseToUnityStep()
        ntu.set_factor(self._intensity_scale)
        ntu.set_peak_range(self._peak_range[0], self._peak_range[1])
        ntu.execute(None, calib_ws_name)

        RenameWorkspace(InputWorkspace=calib_ws_name,
                        OutputWorkspace=self._out_ws)

        # Remove old workspaces
        if len(runs) > 1:
            for run in runs:
                DeleteWorkspace(Workspace=run)

        self.setProperty('OutputWorkspace', self._out_ws)
        self._post_process()

        EndTime('CreateCalibrationWorkspace')
    def PyExec(self):
        from IndirectCommon import StartTime, EndTime
        self._setup()

        StartTime('IndirectTransmissionMonitor')

        ws_basename = str(self._sample_ws_in)

        self._trans_mon(ws_basename, 'Sam', self._sample_ws_in)
        self._trans_mon(ws_basename, 'Can', self._can_ws_in)

        # Generate workspace names
        sam_ws = ws_basename + '_Sam'
        can_ws = ws_basename + '_Can'
        trans_ws = ws_basename + '_Trans'

        # Divide sample and can workspaces
        Divide(LHSWorkspace=sam_ws,
               RHSWorkspace=can_ws,
               OutputWorkspace=trans_ws)
        trans = numpy.average(mtd[trans_ws].readY(0))

        AddSampleLog(Workspace=trans_ws,
                     LogName='can_workspace',
                     LogType='String',
                     LogText=self._can_ws_in)

        # Group workspaces
        group = sam_ws + ',' + can_ws + ',' + trans_ws
        GroupWorkspaces(InputWorkspaces=group, OutputWorkspace=self._out_ws)

        self.setProperty('OutputWorkspace', self._out_ws)

        if self._verbose:
            logger.notice('Transmission : ' + str(trans))

        # Save the tranmissin workspace group to a nexus file
        if self._save:
            workdir = config['defaultsave.directory']
            path = os.path.join(workdir, self._out_ws + '.nxs')
            SaveNexusProcessed(InputWorkspace=self._out_ws, Filename=path)
            if self._verbose:
                logger.notice('Output file created : ' + path)

        # Plot spectra from transmission workspace
        if self._plot:
            mtd_plot = import_mantidplot()
            mtd_plot.plotSpectrum(self._out_ws, 0)

        EndTime('IndirectTransmissionMonitor')
Esempio n. 4
0
    def PyExec(self):
        from IndirectCommon import StartTime, EndTime, getWSprefix
        import inelastic_indirect_reducer

        StartTime('IndirectResolution')
        self._setup()

        InelasticIndirectReduction(Instrument=self._instrument,
                                   Analyser=self._analyser,
                                   Reflection=self._reflection,
                                   Grouping='All',
                                   SumFiles=True,
                                   InputFiles=self._input_files,
                                   DetectorRange=self._detector_range,
                                   OutputWorkspace='__icon_ws_group')

        icon_ws = mtd['__icon_ws_group'].getItem(0).getName()

        if self._out_ws == "":
            self._out_ws = getWSprefix(icon_ws) + 'res'

        if self._scale_factor != 1.0:
            Scale(InputWorkspace=icon_ws,
                  OutputWorkspace=icon_ws,
                  Factor=self._scale_factor)

        CalculateFlatBackground(InputWorkspace=icon_ws,
                                OutputWorkspace=self._out_ws,
                                StartX=self._background[0],
                                EndX=self._background[1],
                                Mode='Mean',
                                OutputMode='Subtract Background')

        Rebin(InputWorkspace=self._out_ws,
              OutputWorkspace=self._out_ws,
              Params=self._rebin_string)

        if self._smooth:
            WienerSmooth(InputWorkspace=self._out_ws,
                         OutputWorkspace='__smooth_temp')
            CopyLogs(InputWorkspace=self._out_ws,
                     OutputWorkspace='__smooth_temp')
            RenameWorkspace(InputWorkspace='__smooth_temp',
                            OutputWorkspace=self._out_ws)

        self._post_process()
        self.setProperty('OutputWorkspace', self._out_ws)

        EndTime('IndirectResolution')
Esempio n. 5
0
    def PyExec(self):
        from IndirectCommon import StartTime, EndTime
        from IndirectDiffractionReduction import MSGDiffractionReducer

        StartTime('MSGDiffractionReduction')

        input_files = self.getProperty('InputFiles').value
        sum_files = self.getProperty('SumFiles').value
        individual_grouping = self.getProperty('IndividualGrouping').value
        instrument_name = self.getPropertyValue('Instrument')
        mode = self.getPropertyValue('Mode')
        detector_range = self.getProperty('DetectorRange').value
        rebin_string = self.getPropertyValue('RebinParam')
        output_ws_group = self.getPropertyValue('OutputWorkspace')
        save_formats = self.getProperty('SaveFormats').value

        ipf_filename = instrument_name + '_diffraction_' + mode + '_Parameters.xml'

        reducer = MSGDiffractionReducer()
        reducer.set_instrument_name(instrument_name)
        reducer.set_detector_range(int(detector_range[0] - 1),
                                   int(detector_range[1] - 1))
        reducer.set_parameter_file(ipf_filename)
        reducer.set_sum_files(sum_files)
        reducer.set_save_formats(save_formats)

        if individual_grouping:
            reducer.set_grouping_policy('Individual')

        for in_file in input_files:
            reducer.append_data_file(in_file)

        if rebin_string != '':
            reducer.set_rebin_string(rebin_string)

        if instrument_name == 'VESUVIO':
            reducer.append_load_option('Mode', 'FoilOut')

        reducer.reduce()

        result_ws_list = reducer.get_result_workspaces()
        GroupWorkspaces(InputWorkspaces=result_ws_list,
                        OutputWorkspace=output_ws_group)
        self.setProperty('OutputWorkspace', output_ws_group)

        EndTime('MSGDiffractionReduction')
Esempio n. 6
0
def IN13Start(instr, run, ana, refl, _rejectZ, _useM, _mapPath, Plot,
              Save):  # Ascii start routine
    StartTime('IN13')
    IN13Read(instr, run, ana, refl, Plot, Save)
    EndTime('IN13')
Esempio n. 7
0
def InxStart(instr, run, ana, refl, rejectZ, useM, mapPath, Plot, Save):
    StartTime('Inx')
    workdir = config['defaultsave.directory']

    path, fname = getFilePath(run, '.inx', instr)

    logger.information('Reading file : ' + path)

    asc = loadFile(path)
    lasc = len(asc)

    val = ExtractInt(asc[0])
    lgrp = int(val[0])
    ngrp = int(val[2])
    title = asc[1]
    ltot = ngrp * lgrp
    logger.information('Number of spectra : ' + str(ngrp))
    if ltot == lasc:
        error = ''
    else:
        error = 'file ' + filext + ' should be ' + str(ltot) + ' lines'
        logger.information('ERROR *** ' + error)
        sys.exit(error)
    _Qaxis = ''
    xDat = []
    yDat = []
    eDat = []
    ns = 0
    Q = []
    tot = []
    for m in range(0, ngrp):
        Qq, nd, xd, yd, ed = ReadInxGroup(asc, m, lgrp)
        tot.append(sum(yd))
        logger.information('Spectrum ' + str(m + 1) + ' at Q= ' + str(Qq) +
                           ' ; Total counts = ' + str(tot))
        if ns != 0:
            _Qaxis += ','
        _Qaxis += str(Qq)
        Q.append(Qq)
        for n in range(0, nd):
            xDat.append(xd[n])
            yDat.append(yd[n])
            eDat.append(ed[n])
        xDat.append(2 * xd[nd - 1] - xd[nd - 2])
        ns += 1
    ascWS = fname + '_' + ana + refl + '_inx'
    outWS = fname + '_' + ana + refl + '_red'
    CreateWorkspace(OutputWorkspace=ascWS,
                    DataX=xDat,
                    DataY=yDat,
                    DataE=eDat,
                    Nspec=ns,
                    UnitX='DeltaE')
    InstrParas(ascWS, instr, ana, refl)
    efixed = RunParas(ascWS, instr, 0, title)
    pi4 = 4.0 * math.pi
    wave = 1.8 * math.sqrt(25.2429 / efixed)
    theta = []
    for n in range(0, ngrp):
        qw = wave * Q[n] / pi4
        ang = 2.0 * math.degrees(math.asin(qw))
        theta.append(ang)
    ChangeAngles(ascWS, instr, theta)
    if useM:
        map = ReadMap(mapPath)
        UseMap(ascWS, map)
    if rejectZ:
        RejectZero(ascWS, tot)
    if not useM and not rejectZ:
        CloneWorkspace(InputWorkspace=ascWS, OutputWorkspace=outWS)
    if Save:
        opath = os.path.join(workdir, outWS + '.nxs')
        SaveNexusProcessed(InputWorkspace=outWS, Filename=opath)
        logger.information('Output file : ' + opath)
    if Plot:
        plotForce(outWS, Plot)
    EndTime('Inx')
Esempio n. 8
0
def IbackStart(instr, run, ana, refl, rejectZ, useM, mapPath, Plot,
               Save):  # Ascii start routine
    StartTime('Iback')
    workdir = config['defaultsave.directory']

    path, fname = getFilePath(run, '.asc', instr)

    logger.information('Reading file : ' + path)

    asc = loadFile(path)

    # raw head
    text = asc[1]
    run = text[:8]
    first = 5
    next, _Ival = Iblock(asc, first)
    next += 2
    title = asc[next]  # title line
    next += 1
    text = asc[next]  # user line
    next += 6  # 5 lines of text
    # back head1
    next, Fval = Fblock(asc, next)
    if instr == 'IN10':
        freq = Fval[89]
    if instr == 'IN16':
        freq = Fval[2]
        amp = Fval[3]
        wave = Fval[69]
        npt = int(Fval[6])
        nsp = int(Fval[7])
    # back head2
    next, Fval = Fblock(asc, next)
    theta = []
    for m in range(0, nsp):
        theta.append(Fval[m])
    # raw spectra
    val = ExtractInt(asc[next + 3])
    npt = val[0]
    lgrp = 5 + npt / 10
    val = ExtractInt(asc[next + 1])
    if instr == 'IN10':
        nsp = int(val[2])
    logger.information('Number of spectra : ' + str(nsp))
    # read monitor
    nmon = next + nsp * lgrp
    _nm, _xm, ym, em = ReadIbackGroup(asc, nmon)
    # monitor calcs
    imin = 0
    ymax = 0
    for m in range(0, 20):
        if ym[m] > ymax:
            imin = m
            ymax = ym[m]
    npt = len(ym)
    imax = npt
    ymax = 0
    for m in range(npt - 1, npt - 20, -1):
        if ym[m] > ymax:
            imax = m
            ymax = ym[m]
    new = imax - imin
    imid = new / 2 + 1
    if instr == 'IN10':
        DRV = 18.706  # fast drive
        vmax = freq * DRV
    if instr == 'IN16':
        vmax = 1.2992581918414711e-4 * freq * amp * 2.0 / wave  # max energy
    dele = 2.0 * vmax / new
    xMon = []
    yOut = []
    eOut = []
    for m in range(0, new + 1):
        xe = (m - imid) * dele
        mm = m + imin
        xMon.append(xe)
        yOut.append(ym[mm] / 100.0)
        eOut.append(em[mm] / 10.0)
    xMon.append(2 * xMon[new - 1] - xMon[new - 2])
    monWS = '__Mon'
    CreateWorkspace(OutputWorkspace=monWS,
                    DataX=xMon,
                    DataY=yOut,
                    DataE=eOut,
                    Nspec=1,
                    UnitX='DeltaE')
    #
    _Qaxis = ''
    xDat = []
    yDat = []
    eDat = []
    tot = []
    for n in range(0, nsp):
        next, _xd, yd, ed = ReadIbackGroup(asc, next)
        tot.append(sum(yd))
        logger.information('Spectrum ' + str(n + 1) + ' at angle ' +
                           str(theta[n]) + ' ; Total counts = ' + str(sum(yd)))
        for m in range(0, new + 1):
            mm = m + imin
            xDat.append(xMon[m])
            yDat.append(yd[mm])
            eDat.append(ed[mm])
        if n != 0:
            _Qaxis += ','
        xDat.append(2 * xDat[new - 1] - xDat[new - 2])
        _Qaxis += str(theta[n])
    ascWS = fname + '_' + ana + refl + '_asc'
    outWS = fname + '_' + ana + refl + '_red'
    CreateWorkspace(OutputWorkspace=ascWS,
                    DataX=xDat,
                    DataY=yDat,
                    DataE=eDat,
                    Nspec=nsp,
                    UnitX='DeltaE')
    Divide(LHSWorkspace=ascWS,
           RHSWorkspace=monWS,
           OutputWorkspace=ascWS,
           AllowDifferentNumberSpectra=True)
    DeleteWorkspace(monWS)  # delete monitor WS
    InstrParas(ascWS, instr, ana, refl)
    RunParas(ascWS, instr, run, title)
    ChangeAngles(ascWS, instr, theta)
    if useM:
        map = ReadMap(mapPath)
        UseMap(ascWS, map)
    if rejectZ:
        RejectZero(ascWS, tot)
    if not useM and not rejectZ:
        CloneWorkspace(InputWorkspace=ascWS, OutputWorkspace=outWS)
    if Save:
        opath = os.path.join(workdir, outWS + '.nxs')
        SaveNexusProcessed(InputWorkspace=outWS, Filename=opath)
        logger.information('Output file : ' + opath)
    if Plot:
        plotForce(outWS, Plot)
    EndTime('Iback')
Esempio n. 9
0
    def PyExec(self):
        from mantid import config, logger
        from IndirectCommon import StartTime, EndTime
        import inelastic_indirect_reducer

        self._setup()

        StartTime('InelasticIndirectReduction')

        # Setup reducer
        reducer = inelastic_indirect_reducer.IndirectReducer()

        reducer.set_rename(True)

        reducer.set_instrument_name(self._instrument)
        reducer.set_parameter_file(self._param_file)
        try:
            reducer.set_output_path(config["defaultsave.directory"])
        except RuntimeError:
            pass # Use default

        for data_file in self._data_files:
            reducer.append_data_file(data_file)

        reducer.set_sum_files(self._sum_files)

        reducer.set_detector_range(int(self._detector_range[0]) - 1, int(self._detector_range[1]) - 1)

        self._use_calib_ws = self._calib_ws_name != ''
        if self._use_calib_ws:
            logger.information('Using calibration workspace: %s' % self._calib_ws_name)
            reducer.set_calibration_workspace(self._calib_ws_name)

        if len(self._background_range) == 2:
            logger.debug('Using background range: ' + str(self._background_range))
            reducer.set_background(float(self._background_range[0]), float(self._background_range[1]))

        # TODO: There should be a better way to do this
        self._use_detailed_balance = self._detailed_balance != -1.0
        if self._use_detailed_balance:
            logger.debug('Using detailed balance: ' + str(self._detailed_balance))
            reducer.set_detailed_balance(self._detailed_balance)

        if self._rebin_string != '':
            logger.debug('Using rebin string: ' + self._rebin_string)
            reducer.set_rebin_string(self._rebin_string)

        self._use_scale_factor = self._scale_factor != 1.0
        if self._use_scale_factor:
            logger.debug('Using scale factor: ' + str(self._scale_factor))
            reducer.set_scale_factor(self._scale_factor)

        if self._map_file != '':
            logger.debug('Using mapping file: ' + str(self._map_file))
            reducer.set_grouping_policy(self._map_file)

        reducer.set_fold_multiple_frames(self.getProperty('Fold').value)
        reducer.set_save_to_cm_1(self.getProperty('SaveCM1').value)
        reducer.set_save_formats(self._save_formats)

        # Do reduction and get result workspaces
        reducer.reduce()
        ws_list = reducer.get_result_workspaces()

        self._plot_ws = ws_list[0]

        if len(ws_list) < 1:
            logger.error('Failed to complete reduction')
            return

        # Add sample logs to output workspace(s)
        for workspace in ws_list:
            self._add_ws_logs(workspace)

        # Group output workspaces
        GroupWorkspaces(InputWorkspaces=ws_list, OutputWorkspace=self._out_ws_group)
        self.setProperty('OutputWorkspace', self._out_ws_group)

        # Do plotting
        if self._plot_type != 'none':
            self._plot()

        EndTime('InelasticIndirectReduction')
Esempio n. 10
0
    def PyExec(self):
        from IndirectCommon import StartTime, EndTime

        StartTime('IndirectTransmission')

        instrument_name = self.getPropertyValue('Instrument')
        analyser = self.getPropertyValue('Analyser')
        reflection = self.getPropertyValue('Reflection')
        formula = self.getPropertyValue('ChemicalFormula')
        density = self.getPropertyValue('NumberDensity')
        thickness = self.getPropertyValue('Thickness')

        # Create an empty instrument workspace
        workspace = self._create_instrument_workspace(instrument_name)

        # Do some validation on the analyser and reflection
        instrument = mtd[workspace].getInstrument()
        valid_analysers = _get_instrument_property_list(instrument, 'analysers')
        logger.debug('Valid analysers for instrument %s: %s' % (instrument_name, str(valid_analysers)))

        # Check the analyser is valid for the instrument
        if analyser not in valid_analysers:
            # Remove idf/ipf workspace
            DeleteWorkspace(workspace)
            raise RuntimeError('Analyser %s not valid for instrument %s' % (analyser, instrument_name))

        else:
            # If the analyser was valid then we can check the reflection
            reflections_param_name = 'refl-%s' % analyser
            valid_reflections = _get_instrument_property_list(instrument, reflections_param_name)
            logger.debug('Valid reflections for analyser %s: %s' % (analyser, str(valid_reflections)))

            if reflection not in valid_reflections:
                # Remove idf/ipf workspace
                DeleteWorkspace(workspace)
                raise RuntimeError('Reflection %s not valid for analyser %s on instrument %s' % (reflection, analyser, instrument_name))

        # Load instrument parameter file
        idf_directory = config['instrumentDefinition.directory']
        name_stem = instrument_name + '_' + analyser + '_' + reflection
        ipf_filename = idf_directory + name_stem + '_Parameters.xml'
        LoadParameterFile(Workspace=workspace, Filename=ipf_filename)

        # Get efixed value
        instrument = mtd[workspace].getInstrument()
        efixed = instrument.getNumberParameter('efixed-val')[0]

        logger.notice('Analyser : ' + analyser + reflection + ' with energy = ' + str(efixed))

        result = SetSampleMaterial(InputWorkspace=workspace, ChemicalFormula=formula)

        # Elastic wavelength
        wave = 1.8 * math.sqrt(25.2429 / efixed)

        absorption_x_section = result[5] * wave / 1.7982
        coherent_x_section = result[4]
        incoherent_x_section = result[3]
        scattering_s_section = incoherent_x_section + coherent_x_section

        thickness = float(thickness)
        density = float(density)

        total_x_section = absorption_x_section + scattering_s_section

        transmission = math.exp(-density * total_x_section * thickness)
        scattering = 1.0 - math.exp(-density * scattering_s_section * thickness)

        # Create table workspace to store calculations
        table_ws = self.getPropertyValue('OutputWorkspace')
        table_ws = CreateEmptyTableWorkspace(OutputWorkspace=table_ws)
        table_ws.addColumn("str", "Name")
        table_ws.addColumn("double", "Value")

        # Names for each of the output values
        output_names = ['Wavelength', 'Absorption Xsection', 'Coherent Xsection', 'Incoherent Xsection',
                        'Total scattering Xsection', 'Number density', 'Thickness', 'Transmission (abs+scatt)', 'Total scattering']

        # List of the calculated values
        output_values = [wave, absorption_x_section, coherent_x_section, incoherent_x_section,
                         scattering_s_section, density, thickness, transmission, scattering]

        # Build table of values
        for data in zip(output_names, output_values):
            table_ws.addRow(list(data))
            logger.information(': '.join(map(str, list(data))))

        # Remove idf/ipf workspace
        DeleteWorkspace(workspace)

        self.setProperty("OutputWorkspace", table_ws)

        EndTime('IndirectTransmission')
Esempio n. 11
0
def IN13Start(instr,run,ana,refl,rejectZ,useM,mapPath,Verbose,Plot,Save):      #Ascii start routine
    StartTime('IN13')
    samWS = IN13Read(instr,run,ana,refl,Verbose,Plot,Save)
    EndTime('IN13')
Esempio n. 12
0
    def PyExec(self):
        from IndirectCommon import StartTime, EndTime

        StartTime('Symmetrise')
        self._setup()
        temp_ws_name = '__symm_temp'

        # The number of spectra that will actually be changed
        num_symm_spectra = self._spectra_range[1] - self._spectra_range[0] + 1

        # Find the smallest data array in the first spectra
        len_x = len(mtd[self._sample].readX(0))
        len_y = len(mtd[self._sample].readY(0))
        len_e = len(mtd[self._sample].readE(0))
        sample_array_len = min(len_x, len_y, len_e)

        sample_x = mtd[self._sample].readX(0)

        # Get slice bounds of array
        try:
            self._calculate_array_points(sample_x, sample_array_len)
        except Exception as e:
            raise RuntimeError('Failed to calculate array slice boundaries: %s' % e.message)

        max_sample_index = sample_array_len - 1
        centre_range_len = self._positive_min_index + self._negative_min_index
        positive_diff_range_len = max_sample_index - self._positive_max_index

        output_cut_index = max_sample_index - self._positive_min_index - positive_diff_range_len - 1
        new_array_len = 2 * max_sample_index - centre_range_len - 2 * positive_diff_range_len - 1

        if self._verbose:
            logger.notice('Sample array length = %d' % sample_array_len)

            logger.notice('Positive X min at i=%d, x=%f'
                          % (self._positive_min_index, sample_x[self._positive_min_index]))
            logger.notice('Negative X min at i=%d, x=%f'
                          % (self._negative_min_index, sample_x[self._negative_min_index]))

            logger.notice('Positive X max at i=%d, x=%f'
                          % (self._positive_max_index, sample_x[self._positive_max_index]))

            logger.notice('New array length = %d' % new_array_len)
            logger.notice('Output array LR split index = %d' % output_cut_index)

        x_unit = mtd[self._sample].getAxis(0).getUnit().unitID()
        v_unit = mtd[self._sample].getAxis(1).getUnit().unitID()
        v_axis_data = mtd[self._sample].getAxis(1).extractValues()

        # Take the values we need from the original vertical axis
        min_spectrum_index = mtd[self._sample].getIndexFromSpectrumNumber(int(self._spectra_range[0]))
        max_spectrum_index = mtd[self._sample].getIndexFromSpectrumNumber(int(self._spectra_range[1]))
        new_v_axis_data = v_axis_data[min_spectrum_index:max_spectrum_index + 1]

        # Create an empty workspace with enough storage for the new data
        zeros = np.zeros(new_array_len * num_symm_spectra)
        CreateWorkspace(OutputWorkspace=temp_ws_name,
                        DataX=zeros, DataY=zeros, DataE=zeros,
                        NSpec=int(num_symm_spectra),
                        VerticalAxisUnit=v_unit, VerticalAxisValues=new_v_axis_data,
                        UnitX=x_unit)

        # Copy logs and properties from sample workspace
        CopyLogs(InputWorkspace=self._sample, OutputWorkspace=temp_ws_name)
        CopyInstrumentParameters(InputWorkspace=self._sample, OutputWorkspace=temp_ws_name)

        # For each spectrum copy positive values to the negative
        output_spectrum_index = 0
        for spectrum_no in range(self._spectra_range[0], self._spectra_range[1] + 1):
            # Get index of original spectra
            spectrum_index = mtd[self._sample].getIndexFromSpectrumNumber(spectrum_no)

            # Strip any additional array cells
            x_in = mtd[self._sample].readX(spectrum_index)[:sample_array_len]
            y_in = mtd[self._sample].readY(spectrum_index)[:sample_array_len]
            e_in = mtd[self._sample].readE(spectrum_index)[:sample_array_len]

            # Get some zeroed data to overwrite with copies from sample
            x_out = np.zeros(new_array_len)
            y_out = np.zeros(new_array_len)
            e_out = np.zeros(new_array_len)

            # Left hand side (reflected)
            x_out[:output_cut_index] = -x_in[self._positive_max_index - 1:self._positive_min_index:-1]
            y_out[:output_cut_index] = y_in[self._positive_max_index - 1:self._positive_min_index:-1]
            e_out[:output_cut_index] = e_in[self._positive_max_index - 1:self._positive_min_index:-1]

            # Right hand side (copied)
            x_out[output_cut_index:] = x_in[self._negative_min_index:self._positive_max_index]
            y_out[output_cut_index:] = y_in[self._negative_min_index:self._positive_max_index]
            e_out[output_cut_index:] = e_in[self._negative_min_index:self._positive_max_index]

            # Set output spectrum data
            mtd[temp_ws_name].setX(output_spectrum_index, x_out)
            mtd[temp_ws_name].setY(output_spectrum_index, y_out)
            mtd[temp_ws_name].setE(output_spectrum_index, e_out)

            # Set output spectrum number
            mtd[temp_ws_name].getSpectrum(output_spectrum_index).setSpectrumNo(spectrum_no)
            output_spectrum_index += 1

            logger.information('Symmetrise spectrum %d' % spectrum_no)

        RenameWorkspace(InputWorkspace=temp_ws_name, OutputWorkspace=self._output_workspace)

        if self._save:
            self._save_output()

        if self._plot:
            self._plot_output()

        if self._props_output_workspace != '':
            self._generate_props_table()

        self.setProperty('OutputWorkspace', self._output_workspace)

        EndTime('Symmetrise')