예제 #1
0
 def PyExec(self):
     error = self.getProperty("Error").value
     if error:
         raise RuntimeError('Error in algorithm')
     progress = Progress(self, 0.0, 1.0, 2)
     progress.report('Half way')
     progress.report()
    def _wave_range(self):

        if self._emode != 'Elastic':
            self._fixed = math.sqrt(81.787 / self._efixed)

        if self._emode == 'Efixed':
            self._waves.append(self._fixed)
            logger.information('Efixed mode, setting lambda_fixed to {0}'.format(self._fixed))
        else:
            wave_range = '__wave_range'
            ExtractSingleSpectrum(InputWorkspace=self._sample_ws_name, OutputWorkspace=wave_range, WorkspaceIndex=0)

            Xin = mtd[wave_range].readX(0)
            wave_min = mtd[wave_range].readX(0)[0]
            wave_max = mtd[wave_range].readX(0)[len(Xin) - 1]
            number_waves = self._number_wavelengths
            wave_bin = (wave_max - wave_min) / (number_waves-1)

            self._waves = list()
            wave_prog = Progress(self, start=0.07, end = 0.10, nreports=number_waves)
            for idx in range(0, number_waves):
                wave_prog.report('Appending wave data: %i' % idx)
                self._waves.append(wave_min + idx * wave_bin)
            DeleteWorkspace(wave_range, EnableLogging = False)

            if self._emode == 'Elastic':
                self._elastic = self._waves[int(len(self._waves) / 2)]
                logger.information('Elastic lambda : %f' % self._elastic)

            logger.information('Lambda : %i values from %f to %f' % (len(self._waves), self._waves[0], self._waves[-1]))
예제 #3
0
    def PyExec(self):
        # Read the state
        state_property_manager = self.getProperty("SANSState").value
        state = create_deserialized_sans_state_from_property_manager(state_property_manager)

        # Run the appropriate SANSLoader and get the workspaces and the workspace monitors
        # Note that cache optimization is only applied to the calibration workspace since it is not available as a
        # return property and it is also something which is most likely not to change between different reductions.
        use_cached = self.getProperty("UseCached").value
        publish_to_ads = self.getProperty("PublishToCache").value

        data = state.data
        progress = self._get_progress_for_file_loading(data)

        # Get the correct SANSLoader from the SANSLoaderFactory
        load_factory = SANSLoadDataFactory()
        loader = load_factory.create_loader(state)

        workspaces, workspace_monitors = loader.execute(data_info=data, use_cached=use_cached,
                                                        publish_to_ads=publish_to_ads, progress=progress,
                                                        parent_alg=self)
        progress.report("Loaded the data.")

        progress_move = Progress(self, start=0.8, end=1.0, nreports=2)
        progress_move.report("Starting to move the workspaces.")
        self._perform_initial_move(workspaces, state)
        progress_move.report("Finished moving the workspaces.")

        # Set output workspaces
        for workspace_type, workspace in workspaces.items():
            self.set_output_for_workspaces(workspace_type, workspace)

        # Set the output monitor workspaces
        for workspace_type, workspace in workspace_monitors.items():
            self.set_output_for_monitor_workspaces(workspace_type, workspace)
    def _sample(self):
        sample_prog = Progress(self, start=0.01, end=0.03, nreports=2)
        sample_prog.report('Setting Sample Material for Sample')
        SetSampleMaterial(self._sample_ws_name , ChemicalFormula=self._sample_chemical_formula,
                          SampleNumberDensity=self._sample_number_density)
        sample = mtd[self._sample_ws_name].sample()
        sam_material = sample.getMaterial()
        # total scattering x-section
        self._sig_s = np.zeros(self._number_can)
        self._sig_s[0] = sam_material.totalScatterXSection()
        # absorption x-section
        self._sig_a = np.zeros(self._number_can)
        self._sig_a[0] = sam_material.absorbXSection()
        # density
        self._density = np.zeros(self._number_can)
        self._density[0] = self._sample_number_density

        if self._use_can:
            sample_prog.report('Setting Sample Material for Container')
            SetSampleMaterial(InputWorkspace=self._can_ws_name, ChemicalFormula=self._can_chemical_formula,
                              SampleNumberDensity=self._can_number_density)
            can_sample = mtd[self._can_ws_name].sample()
            can_material = can_sample.getMaterial()
            self._sig_s[1] = can_material.totalScatterXSection()
            self._sig_a[1] = can_material.absorbXSection()
            self._density[1] = self._can_number_density
    def _setup(self):
        setup_prog = Progress(self, start=0.00, end=0.01, nreports=2)
        setup_prog.report('Obtaining input properties')
        self._sample_ws_name = self.getPropertyValue('SampleWorkspace')
        self._sample_density_type = self.getPropertyValue('SampleDensityType')
        self._sample_density = self.getProperty('SampleDensity').value
        self._sample_inner_radius = self.getProperty('SampleInnerRadius').value
        self._sample_outer_radius = self.getProperty('SampleOuterRadius').value
        self._number_can = 1

        self._can_ws_name = self.getPropertyValue('CanWorkspace')
        self._use_can = self._can_ws_name != ''
        self._can_density_type = self.getPropertyValue('CanDensityType')
        self._can_density = self.getProperty('CanDensity').value
        self._can_outer_radius = self.getProperty('CanOuterRadius').value
        if self._use_can:
            self._number_can = 2

        self._step_size = self.getProperty('StepSize').value
        self._radii = np.zeros(self._number_can +1)
        self._radii[0] = self._sample_inner_radius
        self._radii[1] = self._sample_outer_radius
        if (self._radii[1] - self._radii[0]) < 1e-4:
            raise ValueError('Sample outer radius not > inner radius')
        else:
            logger.information('Sample : inner radius = %f ; outer radius = %f' % (self._radii[0], self._radii[1]))
            self._ms = int((self._radii[1] - self._radii[0] + 0.0001)/self._step_size)
            if self._ms < 20:
                raise ValueError('Number of steps ( %i ) should be >= 20' % self._ms)
            else:
                if self._ms < 1:
                    self._ms = 1
                logger.information('Sample : ms = %i ' % self._ms)
        if self._use_can:
            self._radii[2] = self._can_outer_radius
            if (self._radii[2] - self._radii[1]) < 1e-4:
                raise ValueError('Can outer radius not > sample outer radius')
            else:
                logger.information('Can : inner radius = %f ; outer radius = %f' % (self._radii[1], self._radii[2]))
        setup_prog.report('Obtaining beam values')
        beam_width = self.getProperty('BeamWidth').value
        beam_height = self.getProperty('BeamHeight').value
        self._beam = [beam_height,
                      0.5 * beam_width,
                      -0.5 * beam_width,
                      (beam_width / 2),
                      -(beam_width / 2),
                      0.0,
                      beam_height,
                      0.0,
                      beam_height]

        self._interpolate = self.getProperty('Interpolate').value
        self._number_wavelengths = self.getProperty('NumberWavelengths').value

        self._emode = self.getPropertyValue('Emode')
        self._efixed = self.getProperty('Efixed').value

        self._output_ws_name = self.getPropertyValue('OutputWorkspace')
    def PyExec(self):

        setup_prog = Progress(self, start=0.05, end=0.95, nreports=3)

        self._tmp_fit_name = "__fit_ws"
        self._crop_ws(self._sample_ws, self._tmp_fit_name, self._e_min, self._e_max)

        convert_to_hist_alg = self.createChildAlgorithm("ConvertToHistogram", enableLogging=False)
        convert_to_hist_alg.setProperty("InputWorkspace", self._tmp_fit_name)
        convert_to_hist_alg.setProperty("OutputWorkspace", self._tmp_fit_name)
        convert_to_hist_alg.execute()
        mtd.addOrReplace(self._tmp_fit_name, convert_to_hist_alg.getProperty("OutputWorkspace").value)

        self._convert_to_elasticQ(self._tmp_fit_name)

        num_hist = self._sample_ws.getNumberHistograms()
        if self._hist_max is None:
            self._hist_max = num_hist - 1

        setup_prog.report('Fitting 1 peak')
        self._fit(1)
        setup_prog.report('Fitting 2 peaks')
        self._fit(2)
        self._delete_ws(self._tmp_fit_name)
		
        chi_group = self._output_name + '_ChiSq'
        chi_ws1 = self._output_name + '_1L_ChiSq'
        chi_ws2 = self._output_name + '_2L_ChiSq'
        self._clone_ws(chi_ws1, chi_group)
        self._append(chi_group, chi_ws2, chi_group)
        ws = mtd[chi_group]
        ax = TextAxis.create(2)
        for i, x in enumerate(['1 peak', '2 peaks']):
            ax.setLabel(i, x)
        ws.replaceAxis(1, ax)
        self._delete_ws(chi_ws1)
        self._delete_ws(chi_ws2)

        res_group = self._output_name + '_Result'
        res_ws1 = self._output_name + '_1L_Result'
        res_ws2 = self._output_name + '_2L_Result'
        self._extract(res_ws1, res_group, 1)
        self._extract(res_ws2, '__spectrum', 1)
        self._append(res_group, '__spectrum', res_group)
        self._extract(res_ws2, '__spectrum', 3)
        self._append(res_group, '__spectrum', res_group)
        ws = mtd[res_group]
        ax = TextAxis.create(3)
        for i, x in enumerate(['fwhm.1', 'fwhm.2.1', 'fwhm.2.2']):
            ax.setLabel(i, x)
        ws.replaceAxis(1, ax)
        self._delete_ws(res_ws1)
        self._delete_ws(res_ws2)
        self._delete_ws(self._output_name + '_1L_Parameters')
        self._delete_ws(self._output_name + '_2L_Parameters')
예제 #7
0
    def PyExec(self):
        """ Main execution body
        """

        self.vanaws = self.getProperty("VanadiumWorkspace").value       # returns workspace instance
        outws_name = self.getPropertyValue("OutputWorkspace")           # returns workspace name (string)
        eppws = self.getProperty("EPPTable").value
        nhist = self.vanaws.getNumberHistograms()
        prog_reporter = Progress(self, start=0.0, end=1.0, nreports=nhist+1)

        # calculate array of Debye-Waller factors
        dwf = self.calculate_dwf()

        # for each detector: fit gaussian to get peak_centre and fwhm
        # sum data in the range [peak_centre - 3*fwhm, peak_centre + 3*fwhm]
        dataX = self.vanaws.readX(0)
        coefY = np.zeros(nhist)
        coefE = np.zeros(nhist)
        instrument = self.vanaws.getInstrument()
        detID_offset = self.get_detID_offset()
        peak_centre = eppws.column('PeakCentre')
        sigma = eppws.column('Sigma')

        for idx in range(nhist):
            prog_reporter.report("Setting %dth spectrum" % idx)
            dataY = self.vanaws.readY(idx)
            det = instrument.getDetector(idx + detID_offset)
            if np.max(dataY) == 0 or det.isMasked():
                coefY[idx] = 0.
                coefE[idx] = 0.
            else:
                dataE = self.vanaws.readE(idx)
                fwhm = sigma[idx]*2.*np.sqrt(2.*np.log(2.))
                idxmin = (np.fabs(dataX-peak_centre[idx]+3.*fwhm)).argmin()
                idxmax = (np.fabs(dataX-peak_centre[idx]-3.*fwhm)).argmin()
                coefY[idx] = dwf[idx]*sum(dataY[idxmin:idxmax+1])
                coefE[idx] = dwf[idx]*sum(dataE[idxmin:idxmax+1])

        # create X array, X data are the same for all detectors, so
        coefX = np.zeros(nhist)
        coefX.fill(dataX[0])

        create = self.createChildAlgorithm("CreateWorkspace")
        create.setPropertyValue('OutputWorkspace', outws_name)
        create.setProperty('ParentWorkspace', self.vanaws)
        create.setProperty('DataX', coefX)
        create.setProperty('DataY', coefY)
        create.setProperty('DataE', coefE)
        create.setProperty('NSpec', nhist)
        create.setProperty('UnitX', 'TOF')
        create.execute()
        outws = create.getProperty('OutputWorkspace').value

        self.setProperty("OutputWorkspace", outws)
 def _get_angles(self):
     num_hist = mtd[self._sample_ws_name].getNumberHistograms()
     angle_prog = Progress(self, start=0.03, end=0.07, nreports=num_hist)
     source_pos = mtd[self._sample_ws_name].getInstrument().getSource().getPos()
     sample_pos = mtd[self._sample_ws_name].getInstrument().getSample().getPos()
     beam_pos = sample_pos - source_pos
     self._angles = list()
     for index in range(0, num_hist):
         angle_prog.report('Obtaining data for detector angle %i' % index)
         detector = mtd[self._sample_ws_name].getDetector(index)
         two_theta = detector.getTwoTheta(sample_pos, beam_pos) * 180.0 / math.pi
         self._angles.append(two_theta)
     logger.information('Detector angles : %i from %f to %f ' % (len(self._angles), self._angles[0], self._angles[-1]))
예제 #9
0
    def PyExec(self):
        use_zero_error_free = self.getProperty("UseZeroErrorFree").value
        file_formats = self._get_file_formats()
        file_name = self.getProperty("Filename").value
        workspace = self.getProperty("InputWorkspace").value

        if use_zero_error_free:
            workspace = get_zero_error_free_workspace(workspace)
        progress = Progress(self, start=0.0, end=1.0, nreports=len(file_formats) + 1)
        for file_format in file_formats:
            progress_message = "Saving to {0}.".format(SaveType.to_string(file_format.file_format))
            progress.report(progress_message)
            save_to_file(workspace, file_format, file_name)
        progress.report("Finished saving workspace to files.")
    def PyExec(self):
        """Executes the data reduction workflow."""
        progress = Progress(self, 0.0, 1.0, 4)
        subalgLogging = False
        if self.getProperty(common.PROP_SUBALG_LOGGING).value == common.SUBALG_LOGGING_ON:
            subalgLogging = True
        wsNamePrefix = self.getProperty(common.PROP_OUTPUT_WS).valueAsStr
        cleanupMode = self.getProperty(common.PROP_CLEANUP_MODE).value
        wsNames = common.NameSource(wsNamePrefix, cleanupMode)
        wsCleanup = common.IntermediateWSCleanup(cleanupMode, subalgLogging)

        progress.report('Loading inputs')
        mainWS = self._inputWS(wsCleanup)

        progress.report('Applying self shielding corrections')
        mainWS, applied = self._applyCorrections(mainWS, wsNames, wsCleanup, subalgLogging)

        progress.report('Subtracting EC')
        mainWS, subtracted = self._subtractEC(mainWS, wsNames, wsCleanup, subalgLogging)

        if not applied and not subtracted:
            mainWS = self._cloneOnly(mainWS, wsNames, wsCleanup, subalgLogging)

        self._finalize(mainWS, wsCleanup)
        progress.report('Done')
    def _rebin_result(self):                    #apply rebinning
        rebin_prog = Progress(self, start=0.0, end=0.8, nreports=3)
        rebin_prog.report('Rebin result ')

        logger.information('Rebin option : ' + self._rebin_option)
        qrange = ''
        if mtd.doesExist(self._sofq):                  #check if S(Q) WS exists
		    logger.information('Sofq data from Workspace : %s' % self._sofq)
        else:                                    #read from nxs file
            sofq_path = FileFinder.getFullPath(self._sofq + '.nxs')
            LoadNexusProcessed(Filename=sofq_path,
                               OutputWorkspace=self._sofq,
                               EnableLogging=False)
            logger.information('Sq data from File : %s' % sofq_path)
        rebin_logs = [('rebin_option', self._rebin_option)]
        if self._rebin_option != 'None':          #rebin to be applied
            rebin_logs.append(('rebin_qrange', self._rebin_qrange))
            logger.information('Rebin qrange : %s' % self._rebin_qrange)
            if self._rebin_qrange == 'New':          #new Q range
                mtd[self._final_q].setDistribution(True)
                xs = mtd[self._final_q].readX(0)
                new_dq = float(self._rebin_qinc)       #increment in Q
                xmax = (int(xs[len(xs) -1 ] / new_dq) + 1) * new_dq    #find number of points & Q max
                qrange = '0.0, %f, %f' % (new_dq, xmax)   #create Q range
                self._rebin(self._final_q, self._final_q, qrange)
                x = mtd[self._final_q].readX(0)
                xshift = 0.5 * (x[0] - x[1])
                self._scale_x(self._final_q, self._final_q, xshift)
                logger.information('Output S(Q) rebinned for range : %s' % qrange)
            if self._rebin_qrange == 'Snap':         #use input Q range
                gR = mtd[self._sofq].getRun()      #input S(Q) WS
                stype = gR.getLogData('input_type').value
                logger.information('Rebin option : %s' % self._rebin_option)
                if stype != 'Q':             #check input was in Q
                    raise ValueError('Input type must be Q for Snap option')
                if self._rebin_option == 'Interpolate':
                    self._rebin_ws(self._final_q, self._sofq, self._final_q)
                    logger.information('Output S(Q) interpolated to input S(Q) : %s' % self._sofq)
                if self._rebin_option == 'Spline':
                    self._spline_interp(self._sofq, self._final_q, self._final_q, '', 2)
                    logger.information('Output S(Q) spline interpolated to input S(Q) :%s ' % self._sofq)
                    rebin_logs.append(('rebin_Q_file', self._sofq))
        log_names = [item[0] for item in rebin_logs]
        log_values = [item[1] for item in rebin_logs]
#        self._add_sample_log_mult(self._final_q, log_names, log_values)
        logger.information('Corrected WS created : %s' % self._final_q)
예제 #12
0
    def PyExec(self):
        self._setup()

        self._calculate_parameters()

        if not self._dry_run:
            self._transform()

            self._add_logs()

        else:
            skip_prog = Progress(self, start=0.3, end=1.0, nreports=2)
            skip_prog.report('skipping transform')
            skip_prog.report('skipping add logs')
            logger.information('Dry run, will not run TransformToIqt')

        self.setProperty('ParameterWorkspace', self._parameter_table)
        self.setProperty('OutputWorkspace', self._output_workspace)
    def _subtract_corr(self):    #subtract corrections from input to give _data_used & _result
        calc_prog = Progress(self, start=0.0, end=0.8, nreports=3)
        calc_prog.report('Subtract corrections ')

        logger.information('Subtracting corrections')
        self._data_used = self._data + '_used'
        if self._smooth:                                #select which hist to use
            index = 1
        else:
            index= 0
        self._extract(self._data, self._data_used, index)

        self._extract(self._corr, '__wcr', 0)
        wsc1 = 'S-1C'    #1 term subtracted
        self._plus(self._data_used, '__wcr', wsc1)
        wsc2 = 'S-2C'    #2 terms subtracted
        self._extract(self._corr, '__wcr', 1)
        self._plus(wsc1, '__wcr', wsc2)
        wsc3 = 'S-3C'    #3 terms subtracted
        self._extract(self._corr, '__wcr', 2)
        self._plus(wsc2, '__wcr', wsc3)
        wsc4 = 'S-4C'    #4 terms subtracted
        self._extract(self._corr, '__wcr', 3)
        self._plus(wsc3, '__wcr', wsc4)

        self._result = self._data + '_result'             #results WS
        self._clone_ws(wsc1, self._result)
        self._append(self._result, wsc2, self._result)
        self._append(self._result, wsc3, self._result)
        self._append(self._result, wsc4, self._result)

        ax = TextAxis.create(4)
        for i, x in enumerate(['S-1C', 'S-2C', 'S-3C', 'S-4C']):
            ax.setLabel(i, x)
        mtd[self._result].replaceAxis(1, ax)

        subtract_logs = [('smooth', self._smooth)]
        log_names = [item[0] for item in subtract_logs]
        log_values = [item[1] for item in subtract_logs]
        self._add_sample_log_mult(self._result, log_names, log_values)
        workspaces = ['__wcr', wsc1, wsc2, wsc3, wsc4]
        for ws in workspaces:
            self._delete_ws(ws)
        logger.information('Results in WS %s' % self._result)
    def _corr_terms(self):   #calculates the correction terms = coef*deriv as _corr
        calc_prog = Progress(self, start=0.0, end=0.8, nreports=3)
        calc_prog.report('Correction terms ')

        logger.information('Calculating Correction terms')
        self._corr = self._data + '_corr'              #corrections WS
        self._extract(self._deriv, '__temp', 0)
        self._spline_interp('__temp', self._coeff, self._coeff, '', 2)
        self._multiply(self._coeff, self._deriv, self._corr)

        ax = TextAxis.create(4)
        for i, x in enumerate(['Corr.1', 'Corr.2', 'Corr.3', 'Corr.4']):
            ax.setLabel(i, x)
        mtd[self._corr].replaceAxis(1, ax)

        self._copy_log(self._mome, self._corr, 'MergeKeepExisting')
        self._delete_ws('__temp')
        logger.information('Correction terms WS created : %s' % self._corr)
        calc_prog.report('Correction terms completed')
    def _sample(self):
        sample_prog = Progress(self, start=0.01, end=0.03, nreports=2)
        sample_prog.report('Setting Sample Material for Sample')

        sample_ws, self._sample_density = self._set_material(self._sample_ws_name,
                                                             self._set_sample_method,
                                                             self._sample_chemical_formula,
                                                             self._sample_coherent_cross_section,
                                                             self._sample_incoherent_cross_section,
                                                             self._sample_attenuation_cross_section,
                                                             self._sample_density_type,
                                                             self._sample_density,
                                                             self._sample_number_density_unit)

        sample_material = sample_ws.sample().getMaterial()
        # total scattering x-section
        self._sig_s = np.zeros(self._number_can)
        self._sig_s[0] = sample_material.totalScatterXSection()
        # absorption x-section
        self._sig_a = np.zeros(self._number_can)
        self._sig_a[0] = sample_material.absorbXSection()
        # density
        self._density = np.zeros(self._number_can)
        self._density[0] = self._sample_density

        if self._use_can:
            sample_prog.report('Setting Sample Material for Container')

            can_ws, self._can_density = self._set_material(self._can_ws_name,
                                                           self._set_can_method,
                                                           self._can_chemical_formula,
                                                           self._can_coherent_cross_section,
                                                           self._can_incoherent_cross_section,
                                                           self._can_attenuation_cross_section,
                                                           self._can_density_type,
                                                           self._can_density,
                                                           self._can_number_density_unit)

            can_material = can_ws.sample().getMaterial()
            self._sig_s[1] = can_material.totalScatterXSection()
            self._sig_a[1] = can_material.absorbXSection()
            self._density[1] = self._can_density
예제 #16
0
    def PyExec(self):
        # Read the state
        state_property_manager = self.getProperty("SANSState").value
        state = create_deserialized_sans_state_from_property_manager(state_property_manager)

        component = self._get_component()

        # Get the correct SANS masking strategy from create_masker
        workspace = self.getProperty("Workspace").value
        masker = create_masker(state, component)

        # Perform the masking
        number_of_masking_options = 7
        progress = Progress(self, start=0.0, end=1.0, nreports=number_of_masking_options)
        mask_info = state.mask
        workspace = masker.mask_workspace(mask_info, workspace, component, progress)

        append_to_sans_file_tag(workspace, "_masked")
        self.setProperty("Workspace", workspace)
        progress.report("Completed masking the workspace")
    def _cut_result(self):                  #cutoff high angle data ouput _final_theta as *_theta_corrected
        calc_prog = Progress(self, start=0.0, end=0.8, nreports=3)
        calc_prog.report('Cut result ')

        logger.information('Cutting result')
        logger.information('Number of terms used : %i' % (self._nterms))
        temp_ws = '__final'
        self._extract(self._result, temp_ws, self._nterms -1)
        self._final_theta = self._data + '_corrected'   #theta corrected WS
        final_list = ['S-1C', 'S-2C', 'S-3C', 'S-4C']   #names for hist
        icut = 0
        cut_pt = 0
        cut_logs = [('correct_terms', self._nterms), ('cutoff', self._cutoff)]
        if self._cutoff:
            xs = mtd[self._data_used].readX(0)             #S(theta) data
            ys = mtd[self._data_used].readY(0)
            es = mtd[self._data_used].readE(0)
            xf = np.array(mtd[temp_ws].readX(0))
            xfa = np.array(xf)
            yf = np.array(mtd[temp_ws].readY(0))
            ef = np.array(mtd[temp_ws].readE(0))
            icut = np.where(self._cutoff_pt > xfa)[0][-1]
            cut_pt = xf[icut]                      #x-value for cutoff
            logger.information('Corrected data cutoff at : %f' % (cut_pt))
            xnew = np.array(xf[:icut])                #start off new array
            xnew = np.append(xnew,np.array(xs[icut:]))  #append input data
            ynew = np.array(yf[:icut])                #start off new array
            ynew = np.append(ynew,np.array(ys[icut:]))  #append input data
            enew = np.array(ef[:icut])
            enew = np.append(enew,np.array(es[icut:]))
            self._create_ws(self._final_theta, xnew, ynew, enew, 1, final_list[self._nterms - 1])
            cut_logs.append(('cutoff_point', cut_pt))
            self._delete_ws(temp_ws)
        else:
            self._rename_ws(temp_ws, self._final_theta)
            logger.information('Corrected data NOT cutoff')
        self._copy_log(self._result, self._final_theta, 'MergeReplaceExisting')
        log_names = [item[0] for item in cut_logs]
        log_values = [item[1] for item in cut_logs]
        self._add_sample_log_mult(self._final_theta, log_names, log_values)
        logger.information('Corrected WS created : ' + self._final_theta)
    def _convert_result(self):                  #cutoff high angle data ouput _final_theta as *_theta_corrected
        convert_prog = Progress(self, start=0.0, end=0.8, nreports=3)
        convert_prog.report('Converting result to Q')

        #convert *_theta_corrected to *_Q_corrected
        k0 = 4.0 * math.pi / self._lambda

        # Create a copy of the theta workspace and convert to Q
        self._clone_ws(self._corr, self._final_q)
        x_q = mtd[self._final_q].dataX(0)
        x_q = k0 * np.sin(0.5 * np.radians(x_q - self._azero))    #convert to Q after applying zero angle correction
        mtd[self._final_q].setX(0, x_q)
        unitx = mtd[self._final_q].getAxis(0).setUnit("MomentumTransfer")

        self._copy_log(self._corr, self._final_q, 'MergeReplaceExisting')
        convert_logs = [('lambda_out', self._lambda), ('zero_out', self._azero)]
        logger.information('Converting : %s ; from theta to Q as : %s' % (self._corr, self._final_q))
        logger.information('lambda = %f ; zero = %f' % (self._lambda, self._azero))
        log_names = [item[0] for item in convert_logs]
        log_values = [item[1] for item in convert_logs]
        self._add_sample_log_mult(self._final_q, log_names, log_values)
예제 #19
0
    def PyExec(self):
        """ Main execution body
        """

        # returns workspace instance
        self.vanaws = self.getProperty("VanadiumWorkspace").value
        # returns workspace name (string)
        eppws = self.getProperty("EPPTable").value
        nhist = self.vanaws.getNumberHistograms()
        prog_reporter = Progress(self, start=0.8, end=1.0, nreports=3)
        integrate = self.createChildAlgorithm("IntegrateEPP", startProgress=0.0, endProgress=0.8, enableLogging=False)
        integrate.setProperty("InputWorkspace", self.vanaws)
        integrate.setProperty("OutputWorkspace", "__unused_for_child")
        integrate.setProperty("EPPWorkspace", eppws)
        width = 3. * 2. * np.sqrt(2. * np.log(2.))
        integrate.setProperty("HalfWidthInSigmas", width)
        integrate.execute()
        prog_reporter.report("Computing DWFs")
        outws = integrate.getProperty("OutputWorkspace").value
        # calculate array of Debye-Waller factors
        prog_reporter.report("Applying DWFs")
        dwf = self.calculate_dwf()
        for idx in range(nhist):
            ys = outws.dataY(idx)
            ys /= dwf[idx]
            es = outws.dataE(idx)
            es /= dwf[idx]
        prog_reporter.report("Done")
        self.setProperty("OutputWorkspace", outws)
    def PyExec(self):
        workspace = get_input_workspace_as_copy_if_not_same_as_output_workspace(self)

        progress = Progress(self, start=0.0, end=1.0, nreports=3)

        # Convert the units into wavelength
        progress.report("Converting workspace to wavelength units.")
        workspace = self._convert_units_to_wavelength(workspace)

        # Get the rebin option
        rebin_type = RebinType.from_string(self.getProperty("RebinMode").value)
        rebin_string = self._get_rebin_string(workspace)
        if rebin_type is RebinType.Rebin:
            rebin_options = {"InputWorkspace": workspace,
                             "PreserveEvents": True,
                             "Params": rebin_string}
        else:
            rebin_options = {"InputWorkspace": workspace,
                             "Params": rebin_string}

        # Perform the rebin
        progress.report("Performing rebin.")
        workspace = self._perform_rebin(rebin_type, rebin_options, workspace)

        append_to_sans_file_tag(workspace, "_toWavelength")
        self.setProperty("OutputWorkspace", workspace)
        progress.report("Finished converting to wavelength.")
예제 #21
0
    def PyExec(self):
        # Read the state
        state_property_manager = self.getProperty("SANSState").value
        state = create_deserialized_sans_state_from_property_manager(state_property_manager)

        progress = Progress(self, start=0.0, end=1.0, nreports=3)
        input_workspace = self.getProperty("InputWorkspace").value

        data_type_as_string = self.getProperty("DataType").value
        data_type = DataType.from_string(data_type_as_string)

        slicer = SliceEventFactory.create_slicer(state, input_workspace, data_type)
        slice_info = state.slice

        # Perform the slicing
        progress.report("Starting to slice the workspace.")
        sliced_workspace, slice_factor = slicer.create_slice(input_workspace, slice_info)

        # Scale the monitor accordingly
        progress.report("Scaling the monitors.")
        self.scale_monitors(slice_factor)

        # Set the outputs
        append_to_sans_file_tag(sliced_workspace, "_sliced")
        self.setProperty("OutputWorkspace", sliced_workspace)
        self.setProperty("SliceEventFactor", slice_factor)
        progress.report("Finished slicing.")
예제 #22
0
파일: SANSCrop.py 프로젝트: DanNixon/mantid
    def PyExec(self):
        # Get the correct SANS move strategy from the SANSMaskFactory
        workspace = self.getProperty("InputWorkspace").value

        # Component to crop
        component = self._get_component(workspace)

        progress = Progress(self, start=0.0, end=1.0, nreports=2)
        progress.report("Starting to crop component {0}".format(component))

        # Crop to the component
        crop_name = "CropToComponent"
        crop_options = {"InputWorkspace": workspace,
                        "OutputWorkspace": EMPTY_NAME,
                        "ComponentNames": component}
        crop_alg = create_unmanaged_algorithm(crop_name, **crop_options)
        crop_alg.execute()
        output_workspace = crop_alg.getProperty("OutputWorkspace").value

        # Change the file tag and set the output
        append_to_sans_file_tag(output_workspace, "_cropped")
        self.setProperty("OutputWorkspace", output_workspace)
        progress.report("Finished cropping")
예제 #23
0
    def PyExec(self):
        from IndirectCommon import getWSprefix
        if self._create_output:
            self._out_ws_table = self.getPropertyValue('OutputWorkspaceTable')

        # Process vanadium workspace
        van_ws = ConvertSpectrumAxis(InputWorkspace=self._van_ws,
                                     OutputWorkspace='__ResNorm_vanadium',
                                     Target='ElasticQ',
                                     EMode='Indirect')

        num_hist = van_ws.getNumberHistograms()

        v_values = van_ws.getAxis(1).extractValues()
        v_unit = van_ws.getAxis(1).getUnit().unitID()

        # Process resolution workspace
        padded_res_ws = self._process_res_ws(num_hist)
        prog_namer = Progress(self, start=0.0, end=0.02, nreports=num_hist)
        input_str = ''
        for idx in range(num_hist):
            input_str += '%s,i%d;' % (padded_res_ws, idx)
            prog_namer.report('Generating PlotPeak input string')

        out_name = getWSprefix(self._res_ws) + 'ResNorm_Fit'
        function = 'name=TabulatedFunction,Workspace=%s,Scaling=1,Shift=0,XScaling=1,ties=(Shift=0)' % self._van_ws

        plot_peaks = self.createChildAlgorithm(name='PlotPeakByLogValue', startProgress=0.02, endProgress=0.94, enableLogging=True)
        plot_peaks.setProperty('Input', input_str)
        plot_peaks.setProperty('OutputWorkspace', out_name)
        plot_peaks.setProperty('Function', function)
        plot_peaks.setProperty('FitType', 'Individual')
        plot_peaks.setProperty('PassWSIndexToFunction', True)
        plot_peaks.setProperty('CreateOutput', self._create_output)
        plot_peaks.setProperty('StartX', self._e_min)
        plot_peaks.setProperty('EndX', self._e_max)
        plot_peaks.execute()
        fit_params = plot_peaks.getProperty('OutputWorkspace').value

        params = {'XScaling':'Stretch', 'Scaling':'Intensity'}
        result_workspaces = []
        prog_process = Progress(self, start=0.94, end=1.0, nreports=3)
        for param_name, output_name in params.items():
            result_workspaces.append(self._process_fit_params(fit_params, param_name, v_values, v_unit, output_name))
            prog_process.report('Processing Fit data')

        GroupWorkspaces(InputWorkspaces=result_workspaces,
                        OutputWorkspace=self._out_ws)
        self.setProperty('OutputWorkspace', self._out_ws)

        DeleteWorkspace(van_ws)
        DeleteWorkspace(padded_res_ws)
        prog_process.report('Deleting workspaces')

        if self._create_output:
            self.setProperty('OutputWorkspaceTable', fit_params)
예제 #24
0
    def PyExec(self):
        prog = Progress(self, start=0, end=1, nreports=5)

        prog.report('Importing GSAS-II ')
        self._run_threadsafe(self._import_gsas2, self.getProperty(self.PROP_PATH_TO_GSASII).value)

        prog.report('Initializing GSAS-II ')
        gs2 = self._run_threadsafe(self._init_gs2)

        prog.report('Loading and preparing input data')
        focused_wks = self._get_focused_wks(self.PROP_INPUT_WORKSPACE, self.PROP_WORKSPACE_INDEX)

        inst_file = self.getProperty(self.PROP_INSTR_FILE).value
        try:
            (gs2_rd, limits, peaks_init, background_def) =\
                self._run_threadsafe(self._load_prepare_data_for_fit, gs2, focused_wks, inst_file)
        except RuntimeError as rexc:
            raise RuntimeError("Error in execution of GSAS-II data loading routines: "
                               "{0}.".format(str(rexc)))

        # No obvious way to provide proper progress report from inside the refinement/fitting routines
        prog.report('Running refinement. This may take some times')
        try:
            (gof_estimates, lattice_params, parm_dict) = \
                self._run_threadsafe(self._run_refinement,
                                     gs2, gs2_rd, (limits, peaks_init, background_def))
        except RuntimeError as rexc:
            raise RuntimeError("Error in execution of GSAS-II refinement routines: "
                               "{0}".format(str(rexc)))

        prog.report('Producing outputs')
        self._save_project_read_lattice(gs2, gs2_rd)
        self._produce_outputs(gof_estimates, lattice_params, parm_dict)

        import time
        time.sleep(0.1)
    def _sample(self):
        sample_prog = Progress(self, start=0.01, end=0.03, nreports=2)
        sample_prog.report('Setting Sample Material for Sample')

        sample_chemical_formula = self.getPropertyValue('SampleChemicalFormula')

        sample_ws, self._sample_density = self._set_material(self._sample_ws_name,
                                                             sample_chemical_formula,
                                                             self._sample_density_type,
                                                             self._sample_density)

        sample_material = sample_ws.sample().getMaterial()
        # total scattering x-section
        self._sig_s = np.zeros(self._number_can)
        self._sig_s[0] = sample_material.totalScatterXSection()
        # absorption x-section
        self._sig_a = np.zeros(self._number_can)
        self._sig_a[0] = sample_material.absorbXSection()
        # density
        self._density = np.zeros(self._number_can)
        self._density[0] = self._sample_density

        if self._use_can:
            sample_prog.report('Setting Sample Material for Container')

            can_chemical_formula = self.getPropertyValue('CanChemicalFormula')

            can_ws, self._can_density = self._set_material(self._can_ws_name,
                                                           can_chemical_formula,
                                                           self._can_density_type,
                                                           self._can_density)

            can_material = can_ws.sample().getMaterial()
            self._sig_s[1] = can_material.totalScatterXSection()
            self._sig_a[1] = can_material.absorbXSection()
            self._density[1] = self._can_density
    def _read_sofq(self):  #reads the structure data
        read_prog = Progress(self, start=0.0, end=0.1, nreports=3)
        read_prog.report('Reading data ')

        handle = open(self._input_path, 'r')
        asc = []
        for line in handle:                    #read lines into list 'asc'
            line = line.rstrip()
            asc.append(line)
        handle.close()
        len_asc = len(asc)
        len_head, head = self._read_header(asc)        #find header block
        self._sofq_header  = [('input_type', self._type), ('sofq_lines', len_head)] 
        for m in range(0, len_head):          #number of header lines
            self._sofq_header.append(('sofq_%i' % (m), head[m]))

        #get data from list
        x, y, e = self._read_sofq_data(asc, len_head, len_asc)
        dataX = np.array(x)
        dataY = np.array(y)
        dataE = np.array(e)
        self._temp = '__temp'
        self._create_ws(self._temp, dataX, dataY, dataE)
        log_names = [item[0] for item in self._sofq_header]
        log_values = [item[1] for item in self._sofq_header]
        self._add_sample_log_mult(self._temp, log_names, log_values)
        if self._type == 'angle':       #_input_ws = _theta_temp
            self._clone_ws(self._temp, self._theta_ws)
            unitx = mtd[self._theta_ws].getAxis(0).setUnit("Label")
            unitx.setLabel('2theta', 'deg')

        if self._type == 'Q':
            self._clone_ws(self._temp, self._q_ws)
            self._q_to_theta()       #converts _input_ws from Q to theta in _theta_tmp

        read_prog.report('Reading data completed')
    def PyExec(self):
        """Executes the data reduction workflow."""
        progress = Progress(self, 0.0, 1.0, 4)
        subalgLogging = self.getProperty(common.PROP_SUBALG_LOGGING).value == common.SUBALG_LOGGING_ON
        cleanupMode = self.getProperty(common.PROP_CLEANUP_MODE).value
        wsCleanup = common.IntermediateWSCleanup(cleanupMode, subalgLogging)

        progress.report('Loading inputs')
        mainWS = self._inputWS(wsCleanup)

        progress.report('Integrating')
        mainWS = self._integrate(mainWS, wsCleanup, subalgLogging)

        progress.report('Masking zeros')
        mainWS = self._maskZeros(mainWS, subalgLogging)

        self._finalize(mainWS, wsCleanup)
        progress.report('Done')
예제 #28
0
    def PyExec(self):
        # Read the state
        state_property_manager = self.getProperty("SANSState").value
        state = create_deserialized_sans_state_from_property_manager(state_property_manager)

        # Get the correct SANS move strategy from the SANSMoveFactory
        workspace = self.getProperty("Workspace").value
        move_factory = SANSMoveFactory()
        mover = move_factory.create_mover(workspace)

        # Get the selected component and the beam coordinates
        move_info = state.move
        full_component_name = self._get_full_component_name(move_info)
        coordinates = self._get_coordinates(move_info, full_component_name)

        # Get which move operation the user wants to perform on the workspace. This can be:
        # 1. Initial move: Suitable when a workspace has been freshly loaded.
        # 2. Elementary displacement: Takes the degrees of freedom of the detector into account. This is normally used
        #    for beam center finding
        # 3. Set to zero: Set the component to its zero position
        progress = Progress(self, start=0.0, end=1.0, nreports=2)
        selected_move_type = self._get_move_type()

        if selected_move_type is MoveType.ElementaryDisplacement:
            progress.report("Starting elementary displacement")
            mover.move_with_elementary_displacement(move_info, workspace, coordinates, full_component_name)
        elif selected_move_type is MoveType.InitialMove:
            is_transmission_workspace = self.getProperty("IsTransmissionWorkspace").value
            progress.report("Starting initial move.")
            mover.move_initial(move_info, workspace, coordinates, full_component_name, is_transmission_workspace)
        elif selected_move_type is MoveType.SetToZero:
            progress.report("Starting set to zero.")
            mover.set_to_zero(move_info, workspace, full_component_name)
        else:
            raise ValueError("SANSMove: The selection {0} for the  move type "
                             "is unknown".format(str(selected_move_type)))
        progress.report("Completed move.")
예제 #29
0
    def PyExec(self):

        progress = Progress(self, 0, 1, 4) # Four coarse steps

        in_ws = self.getProperty('InputWorkspace').value
        trans_ws = self.getProperty('TransmissionWorkspace').value
        bands = self.getProperty('Bands').value

        accumulated_output = self._integrate_bands(bands, in_ws)
        if trans_ws:
            accumulated_trans_output = self._integrate_bands(bands, trans_ws)
        progress.report()

        # Perform solid angle correction. Calculate solid angle then divide through.
        normalized=accumulated_output
        if self.getProperty("SolidAngleCorrection").value:
            solidAngle = self.createChildAlgorithm("SolidAngle")
            solidAngle.setProperty("InputWorkspace", accumulated_output)
            solidAngle.execute()
            solid_angle_weighting = solidAngle.getProperty("OutputWorkspace").value
            normalized = self._divide(normalized, solid_angle_weighting)
        progress.report()
        # Divide through by the transmission workspace provided
        if trans_ws:
            normalized = self._divide(normalized, accumulated_trans_output)
        # Determine the max across all spectra
        y_values = normalized.extractY()
        mean_val = np.mean(y_values)
        # Create a workspace from the single max value
        create = self.createChildAlgorithm("CreateSingleValuedWorkspace")
        create.setProperty("DataValue", mean_val)
        create.execute()
        mean_ws = create.getProperty("OutputWorkspace").value
        # Divide each entry by mean
        normalized = self._divide(normalized, mean_ws)
        progress.report()
        # Fix-up ranges
        for i in range(normalized.getNumberHistograms()):
            normalized.dataX(i)[0] = bands[0]
            normalized.dataX(i)[1] = bands[-1]

        self.setProperty('OutputWorkspace', normalized)
예제 #30
0
    def PyExec(self):
        state_property_manager = self.getProperty("SANSState").value
        state = create_deserialized_sans_state_from_property_manager(state_property_manager)

        # Get the correct SANS move strategy from the SANSMaskFactory
        workspace = self.getProperty("InputWorkspace").value

        progress = Progress(self, start=0.0, end=1.0, nreports=3)

        # Multiply by the absolute scale
        progress.report("Applying absolute scale.")
        workspace = self._multiply_by_absolute_scale(workspace, state)

        # Divide by the sample volume
        progress.report("Dividing by the sample volume.")

        workspace = self._divide_by_volume(workspace, state)

        append_to_sans_file_tag(workspace, "_scale")
        self.setProperty("OutputWorkspace", workspace)
        progress.report("Finished applying absolute scale")
예제 #31
0
    def PyExec(self):
        # Facility and database configuration
        config_new_options = {
            'default.facility': 'SNS',
            'default.instrument': 'BASIS',
            'datasearch.searcharchive': 'On'
        }

        # Find valid incoming momentum range
        self._lambda_range = np.array(self.getProperty('LambdaRange').value)
        self._momentum_range = np.sort(2 * np.pi / self._lambda_range)

        # implement with ContextDecorator after python2 is deprecated)
        with pyexec_setup(config_new_options) as self._temps:
            # Load the mask to a temporary workspace
            self._t_mask = LoadMask(
                Instrument='BASIS',
                InputFile=self.getProperty('MaskFile').value,
                OutputWorkspace='_t_mask')

            # Pre-process the background runs
            if self.getProperty('BackgroundRuns').value:
                bkg_run_numbers = self._getRuns(
                    self.getProperty('BackgroundRuns').value, doIndiv=True)
                bkg_run_numbers = \
                    list(itertools.chain.from_iterable(bkg_run_numbers))
                background_reporter = Progress(self,
                                               start=0.0,
                                               end=1.0,
                                               nreports=len(bkg_run_numbers))
                for i, run in enumerate(bkg_run_numbers):
                    if self._bkg is None:
                        self._bkg = self._mask_t0_crop(run, '_bkg')
                        self._temps.workspaces.append('_bkg')
                    else:
                        _ws = self._mask_t0_crop(run, '_ws')
                        self._bkg += _ws
                        if '_ws' not in self._temps.workspaces:
                            self._temps.workspaces.append('_ws')
                    message = 'Pre-processing background: {} of {}'.\
                        format(i+1, len(bkg_run_numbers))
                    background_reporter.report(message)
                SetGoniometer(self._bkg, Axis0='0,0,1,0,1')
                self._bkg_scale = self.getProperty('BackgroundScale').value
                background_reporter.report(len(bkg_run_numbers), 'Done')

            # Pre-process the vanadium run(s) by removing the delayed
            # emission time from the moderator and then saving to file(s)
            if self.getProperty('VanadiumRuns').value:
                run_numbers = self._getRuns(
                    self.getProperty('VanadiumRuns').value, doIndiv=True)
                run_numbers = list(itertools.chain.from_iterable(run_numbers))
                vanadium_reporter = Progress(self,
                                             start=0.0,
                                             end=1.0,
                                             nreports=len(run_numbers))
                self._vanadium_files = list()
                for i, run in enumerate(run_numbers):
                    self._vanadium_files.append(self._save_t0(run))
                    message = 'Pre-processing vanadium: {} of {}'. \
                        format(i+1, len(run_numbers))
                    vanadium_reporter.report(message)
                vanadium_reporter.report(len(run_numbers), 'Done')

            # Determination of single crystal diffraction
            self._determine_single_crystal_diffraction()
예제 #32
0
    def PyExec(self):
        self.setUp()
        sample_runs = self.getPropertyValue('Runs').split(',')
        processes = ['Cadmium', 'Empty', 'Vanadium', 'Sample']
        nReports = np.array([3, 3, 3, 3])
        nReports *= len(sample_runs)
        nReports += 1  # for the wrapping up report
        if self.masking:
            nReports += 1
        progress = Progress(self,
                            start=0.0,
                            end=1.0,
                            nreports=int(nReports[processes.index(
                                self.process)]))
        output_samples = []
        for sample_no, sample in enumerate(sample_runs):
            progress.report("Collecting data")
            ws = self._collect_data(sample,
                                    vanadium=self.process == 'Vanadium')
            if self.cadmium:
                self._subtract_cadmium(ws)
            if self.masking and sample_no == 0:  # prepares masks once, and when the instrument is known
                progress.report("Preparing masks")
                self.mask_ws = self._prepare_masks()
            if self.process == 'Vanadium':
                progress.report("Processing vanadium")
                ws_sofq, ws_softw, ws_diag, ws_integral = self._process_vanadium(
                    ws)
                current_output = [ws_sofq, ws_softw, ws_diag, ws_integral]
                progress.report("Renaming output")
                current_output = self._rename_workspaces(current_output)
                output_samples.extend(current_output)
            elif self.process == 'Sample':
                progress.report("Processing sample")
                sample_sofq, sample_softw = self._process_sample(ws, sample_no)
                current_output = np.array([sample_sofq, sample_softw])
                current_output = current_output[[
                    isinstance(elem, str) for elem in current_output
                ]]
                progress.report("Renaming output")
                current_output = self._rename_workspaces(current_output)
                output_samples.extend(current_output)
            else:  # Empty or Cadmium
                progress.report("Renaming output")
                current_output = [ws]
                current_output = self._rename_workspaces(current_output)
                self._group_detectors(current_output)
                output_samples.extend(current_output)
            if self.save_output:
                self._save_output(current_output)

        progress.report("Grouping outputs")
        GroupWorkspaces(InputWorkspaces=output_samples,
                        OutputWorkspace=self.output)
        if self.clear_cache:  # final clean up
            self._clean_up(self.to_clean)
        self._print_report()
        self.setProperty('OutputWorkspace', mtd[self.output])
예제 #33
0
    def PyExec(self):
        from IndirectCommon import getEfixed

        self._setup()

        # Set up progress reporting
        n_prog_reports = 2
        if self._can_ws_name is not None:
            n_prog_reports += 1
        prog = Progress(self, 0.0, 1.0, n_prog_reports)

        efixed = getEfixed(self._sample_ws)

        sample_wave_ws = '__sam_wave'
        ConvertUnits(InputWorkspace=self._sample_ws, OutputWorkspace=sample_wave_ws,
                     Target='Wavelength', EMode='Indirect', EFixed=efixed)

        SetSampleMaterial(sample_wave_ws, ChemicalFormula=self._sample_chemical_formula, SampleNumberDensity=self._sample_number_density)

        prog.report('Calculating sample corrections')
        FlatPlateAbsorption(InputWorkspace=sample_wave_ws,
                            OutputWorkspace=self._ass_ws,
                            SampleHeight=self._sample_height,
                            SampleWidth=self._sample_width,
                            SampleThickness=self._sample_thickness,
                            ElementSize=self._element_size,
                            EMode='Indirect',
                            EFixed=efixed,
                            NumberOfWavelengthPoints=10)

        plot_data = [self._output_ws, self._sample_ws]
        plot_corr = [self._ass_ws]
        group = self._ass_ws

        if self._can_ws_name is not None:
            can_wave_ws = '__can_wave'
            ConvertUnits(InputWorkspace=self._can_ws_name, OutputWorkspace=can_wave_ws,
                         Target='Wavelength', EMode='Indirect', EFixed=efixed)
            if self._can_scale != 1.0:
                logger.information('Scaling container by: ' + str(self._can_scale))
                Scale(InputWorkspace=can_wave_ws, OutputWorkspace=can_wave_ws, Factor=self._can_scale, Operation='Multiply')

            if self._use_can_corrections:
                prog.report('Calculating container corrections')
                Divide(LHSWorkspace=sample_wave_ws, RHSWorkspace=self._ass_ws, OutputWorkspace=sample_wave_ws)

                SetSampleMaterial(can_wave_ws, ChemicalFormula=self._can_chemical_formula, SampleNumberDensity=self._can_number_density)
                FlatPlateAbsorption(InputWorkspace=can_wave_ws,
                                    OutputWorkspace=self._acc_ws,
                                    SampleHeight=self._sample_height,
                                    SampleWidth=self._sample_width,
                                    SampleThickness=self._can_front_thickness + self._can_back_thickness,
                                    ElementSize=self._element_size,
                                    EMode='Indirect',
                                    EFixed=efixed,
                                    NumberOfWavelengthPoints=10)

                Divide(LHSWorkspace=can_wave_ws, RHSWorkspace=self._acc_ws, OutputWorkspace=can_wave_ws)
                Minus(LHSWorkspace=sample_wave_ws, RHSWorkspace=can_wave_ws, OutputWorkspace=sample_wave_ws)
                plot_corr.append(self._acc_ws)
                group += ',' + self._acc_ws

            else:
                prog.report('Calculating container scaling')
                Minus(LHSWorkspace=sample_wave_ws, RHSWorkspace=can_wave_ws, OutputWorkspace=sample_wave_ws)
                Divide(LHSWorkspace=sample_wave_ws, RHSWorkspace=self._ass_ws, OutputWorkspace=sample_wave_ws)

            DeleteWorkspace(can_wave_ws)
            plot_data.append(self._can_ws_name)

        else:
            Divide(LHSWorkspace=sample_wave_ws, RHSWorkspace=self._ass_ws, OutputWorkspace=sample_wave_ws)

        ConvertUnits(InputWorkspace=sample_wave_ws, OutputWorkspace=self._output_ws,
                     Target='DeltaE', EMode='Indirect', EFixed=efixed)
        DeleteWorkspace(sample_wave_ws)

        prog.report('Recording samle logs')
        sample_log_workspaces = [self._output_ws, self._ass_ws]
        sample_logs = [('sample_shape', 'flatplate'),
                       ('sample_filename', self._sample_ws),
                       ('sample_height', self._sample_height),
                       ('sample_width', self._sample_width),
                       ('sample_thickness', self._sample_thickness),
                       ('element_size', self._element_size)]

        if self._can_ws_name is not None:
            sample_logs.append(('container_filename', self._can_ws_name))
            sample_logs.append(('container_scale', self._can_scale))
            if self._use_can_corrections:
                sample_log_workspaces.append(self._acc_ws)
                sample_logs.append(('container_front_thickness', self. _can_front_thickness))
                sample_logs.append(('container_back_thickness', self. _can_back_thickness))

        log_names = [item[0] for item in sample_logs]
        log_values = [item[1] for item in sample_logs]

        for ws_name in sample_log_workspaces:
            AddSampleLogMultiple(Workspace=ws_name, LogNames=log_names, LogValues=log_values)

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

        # Output the Ass workspace if it is wanted, delete if not
        if self._abs_ws == '':
            DeleteWorkspace(self._ass_ws)
            if self._can_ws_name is not None and self._use_can_corrections:
                DeleteWorkspace(self._acc_ws)
        else:
            GroupWorkspaces(InputWorkspaces=group, OutputWorkspace=self._abs_ws)
            self.setProperty('CorrectionsWorkspace', self._abs_ws)

        if self._plot:
            from IndirectImport import import_mantidplot
            mantid_plot = import_mantidplot()
            mantid_plot.plotSpectrum(plot_data, 0)
            if self._abs_ws != '':
                mantid_plot.plotSpectrum(plot_corr, 0)
예제 #34
0
class IndirectILLReductionFWS(PythonAlgorithm):

    _SAMPLE = 'sample'
    _BACKGROUND = 'background'
    _CALIBRATION = 'calibration'
    _BACKCALIB = 'calibrationBackground'

    _sample_files = None
    _background_files = None
    _calibration_files = None
    _background_calib_files = None
    _observable = None
    _sortX = None
    _red_ws = None
    _back_scaling = None
    _back_calib_scaling = None
    _criteria = None
    _progress = None
    _back_option = None
    _calib_option = None
    _back_calib_option = None
    _common_args = {}
    _all_runs = None
    _discard_sds = None

    def category(self):
        return "Workflow\\MIDAS;Workflow\\Inelastic;Inelastic\\Indirect;Inelastic\\Reduction;ILL\\Indirect"

    def summary(self):
        return 'Performs fixed-window scan (FWS) multiple file reduction (both elastic and inelastic) ' \
               'for ILL indirect geometry data, instrument IN16B.'

    def seeAlso(self):
        return ["IndirectILLReductionQENS", "IndirectILLEnergyTransfer"]

    def name(self):
        return "IndirectILLReductionFWS"

    def PyInit(self):

        self.declareProperty(MultipleFileProperty('Run', extensions=['nxs']),
                             doc='Run number(s) of sample run(s).')

        self.declareProperty(
            MultipleFileProperty('BackgroundRun',
                                 action=FileAction.OptionalLoad,
                                 extensions=['nxs']),
            doc='Run number(s) of background (empty can) run(s).')

        self.declareProperty(
            MultipleFileProperty('CalibrationRun',
                                 action=FileAction.OptionalLoad,
                                 extensions=['nxs']),
            doc='Run number(s) of vanadium calibration run(s).')

        self.declareProperty(
            MultipleFileProperty('CalibrationBackgroundRun',
                                 action=FileAction.OptionalLoad,
                                 extensions=['nxs']),
            doc=
            'Run number(s) of background (empty can) run(s) for vanadium run.')

        self.declareProperty(name='Observable',
                             defaultValue='sample.temperature',
                             doc='Scanning observable, a Sample Log entry\n')

        self.declareProperty(name='SortXAxis',
                             defaultValue=False,
                             doc='Whether or not to sort the x-axis\n')

        self.declareProperty(name='BackgroundScalingFactor',
                             defaultValue=1.,
                             validator=FloatBoundedValidator(lower=0),
                             doc='Scaling factor for background subtraction')

        self.declareProperty(
            name='CalibrationBackgroundScalingFactor',
            defaultValue=1.,
            validator=FloatBoundedValidator(lower=0),
            doc=
            'Scaling factor for background subtraction for vanadium calibration'
        )

        self.declareProperty(
            name='BackgroundOption',
            defaultValue='Sum',
            validator=StringListValidator(['Sum', 'Interpolate']),
            doc='Whether to sum or interpolate the background runs.')

        self.declareProperty(
            name='CalibrationOption',
            defaultValue='Sum',
            validator=StringListValidator(['Sum', 'Interpolate']),
            doc='Whether to sum or interpolate the calibration runs.')

        self.declareProperty(
            name='CalibrationBackgroundOption',
            defaultValue='Sum',
            validator=StringListValidator(['Sum', 'Interpolate']),
            doc=
            'Whether to sum or interpolate the background run for calibration runs.'
        )

        self.declareProperty(
            FileProperty('MapFile',
                         '',
                         action=FileAction.OptionalLoad,
                         extensions=['map', 'xml']),
            doc='Filename of the detector grouping map file to use. \n'
            'By default all the pixels will be summed per each tube. \n'
            'Use .map or .xml file (see GroupDetectors documentation) '
            'only if different range is needed for each tube.')

        self.declareProperty(
            name='ManualPSDIntegrationRange',
            defaultValue=[1, 128],
            doc='Integration range of vertical pixels in each PSD tube. \n'
            'By default all the pixels will be summed per each tube. \n'
            'Use this option if the same range (other than default) '
            'is needed for all the tubes.')

        self.declareProperty(name='Analyser',
                             defaultValue='silicon',
                             validator=StringListValidator(['silicon']),
                             doc='Analyser crystal.')

        self.declareProperty(name='Reflection',
                             defaultValue='111',
                             validator=StringListValidator(['111', '311']),
                             doc='Analyser reflection.')

        self.declareProperty(WorkspaceGroupProperty(
            'OutputWorkspace', '', direction=Direction.Output),
                             doc='Output workspace group')

        self.declareProperty(name='SpectrumAxis',
                             defaultValue='SpectrumNumber',
                             validator=StringListValidator(
                                 ['SpectrumNumber', '2Theta', 'Q', 'Q2']),
                             doc='The spectrum axis conversion target.')

        self.declareProperty(
            name='DiscardSingleDetectors',
            defaultValue=False,
            doc='Whether to discard the spectra of single detectors.')

        self.declareProperty(
            name='ManualInelasticPeakChannels',
            defaultValue=[-1, -1],
            doc=
            'The channel indices for the inelastic peak positions in the beginning '
            'and in the end of the spectra; by default the maxima of the monitor '
            'spectrum will be used for this. The intensities will be integrated symmetrically '
            'around each peak.')

    def validateInputs(self):

        issues = dict()

        if self.getPropertyValue(
                'CalibrationBackgroundRun'
        ) and not self.getPropertyValue('CalibrationRun'):
            issues['CalibrationRun'] = 'Calibration runs are required, ' \
                                       'if background for calibration is given.'

        if not self.getProperty('ManualInelasticPeakChannels').isDefault:
            peaks = self.getProperty('ManualInelasticPeakChannels').value
            if len(peaks) != 2:
                issues['ManualInelasticPeakChannels'] = 'Invalid value for peak channels, ' \
                                                        'provide two comma separated positive integers.'
            elif peaks[0] >= peaks[1]:
                issues[
                    'ManualInelasticPeakChannels'] = 'First peak channel must be less than the second'
            elif peaks[0] <= 0:
                issues[
                    'ManualInelasticPeakChannels'] = 'Non negative integers are required'

        return issues

    def setUp(self):

        self._sample_files = self.getPropertyValue('Run')
        self._background_files = self.getPropertyValue('BackgroundRun')
        self._calibration_files = self.getPropertyValue('CalibrationRun')
        self._background_calib_files = self.getPropertyValue(
            'CalibrationBackgroundRun')
        self._observable = self.getPropertyValue('Observable')
        self._sortX = self.getProperty('SortXAxis').value
        self._back_scaling = self.getProperty('BackgroundScalingFactor').value
        self._back_calib_scaling = self.getProperty(
            'CalibrationBackgroundScalingFactor').value
        self._back_option = self.getPropertyValue('BackgroundOption')
        self._calib_option = self.getPropertyValue('CalibrationOption')
        self._back_calib_option = self.getPropertyValue(
            'CalibrationBackgroundOption')
        self._spectrum_axis = self.getPropertyValue('SpectrumAxis')
        self._discard_sds = self.getProperty('DiscardSingleDetectors').value

        # arguments to pass to IndirectILLEnergyTransfer
        self._common_args['MapFile'] = self.getPropertyValue('MapFile')
        self._common_args['Analyser'] = self.getPropertyValue('Analyser')
        self._common_args['Reflection'] = self.getPropertyValue('Reflection')
        self._common_args['ManualPSDIntegrationRange'] = self.getProperty(
            'ManualPSDIntegrationRange').value
        self._common_args['SpectrumAxis'] = self._spectrum_axis
        self._common_args['DiscardSingleDetectors'] = self._discard_sds

        self._red_ws = self.getPropertyValue('OutputWorkspace')

        suffix = ''
        if self._spectrum_axis == 'SpectrumNumber':
            suffix = '_red'
        elif self._spectrum_axis == '2Theta':
            suffix = '_2theta'
        elif self._spectrum_axis == 'Q':
            suffix = '_q'
        elif self._spectrum_axis == 'Q2':
            suffix = '_q2'

        self._red_ws += suffix

        # Nexus metadata criteria for FWS type of data (both EFWS and IFWS)
        self._criteria = '($/entry0/instrument/Doppler/maximum_delta_energy$ == 0. or ' \
                         '$/entry0/instrument/Doppler/velocity_profile$ == 1)'

        # force sort x-axis, if interpolation is requested
        if ((self._back_option == 'Interpolate' and self._background_files)
            or (self._calib_option == 'Interpolate' and self._calibration_files)
            or (self._back_calib_option == 'Interpolate' and self._background_calib_files)) \
                and not self._sortX:
            self.log().warning(
                'Interpolation option requested, X-axis will be sorted.')
            self._sortX = True

        # empty dictionary to keep track of all runs (ws names)
        self._all_runs = dict()

    def _filter_files(self, files, label):
        '''
        Filters the given list of files according to nexus criteria
        @param  files :: list of input files (i.e. , and + separated string)
        @param  label :: label of error message if nothing left after filtering
        @throws RuntimeError :: when nothing left after filtering
        @return :: the list of input files that passsed the criteria
        '''

        files = SelectNexusFilesByMetadata(files, self._criteria)

        if not files:
            raise RuntimeError(
                'None of the {0} runs satisfied the FWS and Observable criteria.'
                .format(label))
        else:
            self.log().information('Filtered {0} runs are: {0} \\n'.format(
                label, files.replace(',', '\\n')))

        return files

    def _ifws_peak_bins(self, ws):
        '''
        Gives the bin indices of the first and last inelastic peaks
        By default they are taken from the maxima of the monitor spectrum
        Or they can be specified manually as input parameters
        @param ws :: input workspace
        return    :: [imin,imax]
        '''
        if not self.getProperty('ManualInelasticPeakChannels').isDefault:
            peak_channels = self.getProperty(
                'ManualInelasticPeakChannels').value
            blocksize = mtd[ws].blocksize()
            if peak_channels[1] >= blocksize:
                raise RuntimeError(
                    'Manual peak channel {0} is out of range {1}'.format(
                        peak_channels[1], blocksize))
            else:
                AddSampleLogMultiple(Workspace=ws,
                                     LogNames=[
                                         'ManualInelasticLeftPeak',
                                         'ManualInelasticRightPeak'
                                     ],
                                     LogValues=str(peak_channels[0]) + ',' +
                                     str(peak_channels[1]))
                return peak_channels
        run = mtd[ws].getRun()
        if not run.hasProperty('MonitorLeftPeak') or not run.hasProperty(
                'MonitorRightPeak'):
            raise RuntimeError(
                'Unable to retrieve the monitor peak information from the sample logs.'
            )
        else:
            imin = run.getLogData('MonitorLeftPeak').value
            imax = run.getLogData('MonitorRightPeak').value
        return imin, imax

    def _ifws_integrate(self, wsgroup):
        '''
        Integrates IFWS over two peaks at the beginning and end
        @param ws :: input workspace group
        '''

        for item in mtd[wsgroup]:
            ws = item.name()
            size = item.blocksize()
            imin, imax = self._ifws_peak_bins(ws)
            x_values = item.readX(0)
            int1 = '__int1_' + ws
            int2 = '__int2_' + ws
            Integration(InputWorkspace=ws,
                        OutputWorkspace=int1,
                        RangeLower=x_values[0],
                        RangeUpper=x_values[2 * imin])
            Integration(InputWorkspace=ws,
                        OutputWorkspace=int2,
                        RangeLower=x_values[-2 * (size - imax)],
                        RangeUpper=x_values[-1])
            Plus(LHSWorkspace=int1, RHSWorkspace=int2, OutputWorkspace=ws)
            DeleteWorkspace(int1)
            DeleteWorkspace(int2)

    def _perform_unmirror(self, groupws):
        '''
        Sums the integrals of left and right for two wings, or returns the integral of one wing
        @param ws :: group workspace containing one ws for one wing, and two ws for two wing data
        '''
        if mtd[groupws].getNumberOfEntries() == 2:  # two wings, sum
            left = mtd[groupws].getItem(0).name()
            right = mtd[groupws].getItem(1).name()
            left_right_sum = '__sum_' + groupws

            left_monitor = mtd[left].getRun().getLogData(
                'MonitorIntegral').value
            right_monitor = mtd[right].getRun().getLogData(
                'MonitorIntegral').value

            if left_monitor != 0. and right_monitor != 0.:
                sum_monitor = left_monitor + right_monitor
                left_factor = left_monitor / sum_monitor
                right_factor = right_monitor / sum_monitor
                Scale(InputWorkspace=left,
                      OutputWorkspace=left,
                      Factor=left_factor)
                Scale(InputWorkspace=right,
                      OutputWorkspace=right,
                      Factor=right_factor)
            else:
                self.log().notice(
                    'Zero monitor integral has been found in one (or both) wings;'
                    ' left: {0}, right: {1}'.format(left_monitor,
                                                    right_monitor))

            Plus(LHSWorkspace=left,
                 RHSWorkspace=right,
                 OutputWorkspace=left_right_sum)

            DeleteWorkspace(left)
            DeleteWorkspace(right)

            RenameWorkspace(InputWorkspace=left_right_sum,
                            OutputWorkspace=groupws)

        else:
            RenameWorkspace(InputWorkspace=mtd[groupws].getItem(0),
                            OutputWorkspace=groupws)

    def PyExec(self):

        self.setUp()

        # total number of (unsummed) runs
        total = self._sample_files.count(',') + self._background_files.count(
            ',') + self._calibration_files.count(',')

        self._progress = Progress(self, start=0.0, end=1.0, nreports=total)

        self._reduce_multiple_runs(self._sample_files, self._SAMPLE)

        if self._background_files:

            self._reduce_multiple_runs(self._background_files,
                                       self._BACKGROUND)

            back_ws = self._red_ws + '_' + self._BACKGROUND

            Scale(InputWorkspace=back_ws,
                  Factor=self._back_scaling,
                  OutputWorkspace=back_ws)

            if self._back_option == 'Sum':
                self._integrate(self._BACKGROUND, self._SAMPLE)
            else:
                self._interpolate(self._BACKGROUND, self._SAMPLE)

            self._subtract_background(self._BACKGROUND, self._SAMPLE)

            DeleteWorkspace(back_ws)

        if self._calibration_files:

            self._reduce_multiple_runs(self._calibration_files,
                                       self._CALIBRATION)

            if self._background_calib_files:
                self._reduce_multiple_runs(self._background_calib_files,
                                           self._BACKCALIB)

                back_calib_ws = self._red_ws + '_' + self._BACKCALIB

                Scale(InputWorkspace=back_calib_ws,
                      Factor=self._back_calib_scaling,
                      OutputWorkspace=back_calib_ws)

                if self._back_calib_option == 'Sum':
                    self._integrate(self._BACKCALIB, self._CALIBRATION)
                else:
                    self._interpolate(self._BACKCALIB, self._CALIBRATION)

                self._subtract_background(self._BACKCALIB, self._CALIBRATION)

                DeleteWorkspace(back_calib_ws)

            if self._calib_option == 'Sum':
                self._integrate(self._CALIBRATION, self._SAMPLE)
            else:
                self._interpolate(self._CALIBRATION, self._SAMPLE)

            self._calibrate()

            DeleteWorkspace(self._red_ws + '_' + self._CALIBRATION)

        self.log().debug('Run files map is :' + str(self._all_runs))

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

    def _reduce_multiple_runs(self, files, label):
        '''
        Filters and reduces multiple files
        @param files :: list of run paths
        @param label :: output ws name
        '''

        files = self._filter_files(files, label)

        for run in files.split(','):
            self._reduce_run(run, label)

        self._create_matrices(label)

    def _reduce_run(self, run, label):
        '''
        Reduces the given (single or summed multiple) run
        @param run :: run path
        @param  label :: sample, background or calibration
        '''

        runs_list = run.split('+')

        runnumber = os.path.basename(runs_list[0]).split('.')[0]

        ws = '__' + runnumber

        if (len(runs_list) > 1):
            ws += '_multiple'

        ws += '_' + label

        self._progress.report("Reducing run #" + runnumber)

        IndirectILLEnergyTransfer(Run=run,
                                  OutputWorkspace=ws,
                                  **self._common_args)

        energy = round(
            mtd[ws].getItem(0).getRun().getLogData(
                'Doppler.maximum_delta_energy').value, 2)

        if energy == 0.:
            # Elastic, integrate over full energy range
            Integration(InputWorkspace=ws, OutputWorkspace=ws)
        else:
            # Inelastic, do something more complex
            self._ifws_integrate(ws)

        ConvertToPointData(InputWorkspace=ws, OutputWorkspace=ws)

        self._perform_unmirror(ws)

        self._subscribe_run(ws, energy, label)

    def _subscribe_run(self, ws, energy, label):
        '''
        Subscribes the given ws name to the map for given energy and label
        @param ws     :: workspace name
        @param energy :: energy value
        @param label  :: sample, calibration or background
        '''

        if label in self._all_runs:
            if energy in self._all_runs[label]:
                self._all_runs[label][energy].append(ws)
            else:
                self._all_runs[label][energy] = [ws]
        else:
            self._all_runs[label] = dict()
            self._all_runs[label][energy] = [ws]

    def _integrate(self, label, reference):
        '''
        Averages the background or calibration intensities over all observable points at given energy
        @param label :: calibration or background
        @param reference :: sample or calibration
        '''

        for energy in self._all_runs[reference]:
            if energy in self._all_runs[label]:
                ws = self._insert_energy_value(self._red_ws + '_' + label,
                                               energy, label)
                if mtd[ws].blocksize() > 1:
                    SortXAxis(InputWorkspace=ws, OutputWorkspace=ws)
                    axis = mtd[ws].readX(0)
                    start = axis[0]
                    end = axis[-1]
                    integration_range = end - start
                    params = [start, integration_range, end]
                    Rebin(InputWorkspace=ws, OutputWorkspace=ws, Params=params)

    def _interpolate(self, label, reference):
        '''
        Interpolates the background or calibration intensities to
        all observable points existing in sample at a given energy
        @param label  :: calibration or background
        @param reference :: to interpolate to, can be sample or calibration
        '''

        for energy in self._all_runs[reference]:
            if energy in self._all_runs[label]:

                ws = self._insert_energy_value(self._red_ws + '_' + label,
                                               energy, label)

                if reference == self._SAMPLE:
                    ref = self._insert_energy_value(self._red_ws, energy,
                                                    reference)
                else:
                    ref = self._insert_energy_value(
                        self._red_ws + '_' + reference, energy, reference)

                if mtd[ws].blocksize() > 1:
                    SplineInterpolation(WorkspaceToInterpolate=ws,
                                        WorkspaceToMatch=ref,
                                        Linear2Points=True,
                                        OutputWorkspace=ws)

    def _subtract_background(self, background, reference):
        '''
        Subtracts the background per each energy if background run is available
        @param background :: background to subtract
        @param reference :: to subtract from
        '''

        for energy in self._all_runs[reference]:
            if energy in self._all_runs[background]:

                if reference == self._SAMPLE:
                    lhs = self._insert_energy_value(self._red_ws, energy,
                                                    reference)
                else:
                    lhs = self._insert_energy_value(
                        self._red_ws + '_' + reference, energy, reference)

                rhs = self._insert_energy_value(
                    self._red_ws + '_' + background, energy, background)
                Minus(LHSWorkspace=lhs, RHSWorkspace=rhs, OutputWorkspace=lhs)
            else:
                self.log().warning(
                    'No background subtraction can be performed for doppler energy of {0} microEV, '
                    'since no background run was provided for the same energy value.'
                    .format(energy))

    def _calibrate(self):
        '''
        Performs calibration per each energy if calibration run is available
        '''

        for energy in self._all_runs[self._SAMPLE]:
            if energy in self._all_runs[self._CALIBRATION]:
                sample_ws = self._insert_energy_value(self._red_ws, energy,
                                                      self._SAMPLE)
                calib_ws = sample_ws + '_' + self._CALIBRATION
                Divide(LHSWorkspace=sample_ws,
                       RHSWorkspace=calib_ws,
                       OutputWorkspace=sample_ws)
                self._scale_calibration(sample_ws, calib_ws)
            else:
                self.log().warning(
                    'No calibration can be performed for doppler energy of {0} microEV, '
                    'since no calibration run was provided for the same energy value.'
                    .format(energy))

    def _scale_calibration(self, sample, calib):
        '''
        Scales sample workspace after calibration up by the maximum of integral intensity
        in calibration run for each observable point
        @param sample  :: sample workspace after calibration
        @param calib   :: calibration workspace
        '''

        if mtd[calib].blocksize() == 1:
            scale = np.max(mtd[calib].extractY()[:, 0])
            Scale(InputWorkspace=sample,
                  Factor=scale,
                  OutputWorkspace=sample,
                  Operation='Multiply')
        else:
            # here calib and sample have the same size already
            for column in range(mtd[sample].blocksize()):
                scale = np.max(mtd[calib].extractY()[:, column])
                for spectrum in range(mtd[sample].getNumberHistograms()):
                    mtd[sample].dataY(spectrum)[column] *= scale
                    mtd[sample].dataE(spectrum)[column] *= scale

    def _get_observable_values(self, ws_list):
        '''
        Retrieves the needed sample log values for the given list of workspaces
        @param ws_list :: list of workspaces
        @returns :: array of observable values
        @throws  :: ValueError if the log entry is not a number nor time-stamp
        '''

        result = []

        zero_time = 0

        pattern = '%Y-%m-%dT%H:%M:%S'

        for i, ws in enumerate(ws_list):

            log = mtd[ws].getRun().getLogData(self._observable)
            value = log.value

            if log.type == 'number':
                value = float(value)
            else:
                try:
                    value = time.mktime(time.strptime(value, pattern))
                except ValueError:
                    raise ValueError(
                        "Invalid observable. "
                        "Provide a numeric (sample.*, run_number, etc.) or time-stamp "
                        "like string (e.g. start_time) log.")
                if i == 0:
                    zero_time = value

                value = value - zero_time

            result.append(value)

        return result

    def _create_matrices(self, label):
        '''
        For each reduction type concatenates the workspaces putting the given sample log value as x-axis
        Creates a group workspace for the given label, that contains 2D workspaces for each distinct energy value
        @param label :: sample, background or calibration
        '''

        togroup = []

        groupname = self._red_ws

        if label != self._SAMPLE:
            groupname += '_' + label

        for energy in sorted(self._all_runs[label]):

            ws_list = self._all_runs[label][energy]

            wsname = self._insert_energy_value(groupname, energy, label)

            togroup.append(wsname)
            nspectra = mtd[ws_list[0]].getNumberHistograms()
            observable_array = self._get_observable_values(
                self._all_runs[label][energy])

            ConjoinXRuns(InputWorkspaces=ws_list, OutputWorkspace=wsname)

            mtd[wsname].setDistribution(True)

            run_list = ''  # to set to sample logs

            for ws in ws_list:
                run = mtd[ws].getRun()

                if run.hasProperty('run_number_list'):
                    run_list += run.getLogData(
                        'run_number_list').value.replace(', ', '+') + ','
                else:
                    run_list += str(run.getLogData('run_number').value) + ','

            AddSampleLog(Workspace=wsname,
                         LogName='ReducedRunsList',
                         LogText=run_list.rstrip(','))

            for spectrum in range(nspectra):

                mtd[wsname].setX(spectrum, np.array(observable_array))

            if self._sortX:
                SortXAxis(InputWorkspace=wsname, OutputWorkspace=wsname)

            self._set_x_label(wsname)

        for energy, ws_list in self._all_runs[label].items():
            for ws in ws_list:
                DeleteWorkspace(ws)

        GroupWorkspaces(InputWorkspaces=togroup, OutputWorkspace=groupname)

    def _set_x_label(self, ws):
        '''
        Sets the x-axis label
        @param ws :: input workspace
        '''

        axis = mtd[ws].getAxis(0)
        if self._observable == 'sample.temperature':
            axis.setUnit("Label").setLabel('Temperature', 'K')
        elif self._observable == 'sample.pressure':
            axis.setUnit("Label").setLabel('Pressure', 'P')
        elif 'time' in self._observable:
            axis.setUnit("Label").setLabel('Time', 'seconds')
        else:
            axis.setUnit("Label").setLabel(self._observable, '')

    def _insert_energy_value(self, ws_name, energy, label):
        '''
        Inserts the doppler's energy value in the workspace name
        in between the user input and automatic suffix
        @param ws_name : workspace name
        @param energy : energy value
        @param label : sample, background, or calibration
        @return : new name with energy value inside
        Example:
        user_input_2theta > user_input_1.5_2theta
        user_input_red_background > user_input_1.5_red_background
        '''
        suffix_pos = ws_name.rfind('_')

        if label != self._SAMPLE:
            # find second to last underscore
            suffix_pos = ws_name.rfind('_', 0, suffix_pos)

        return ws_name[:suffix_pos] + '_' + str(energy) + ws_name[suffix_pos:]
예제 #35
0
    def PyExec(self):
        self._eulerConvention = self.getProperty('EulerConvention').value
        calWS = self.getProperty('CalibrationTable').value
        calWS = api.SortTableWorkspace(calWS, Columns='detid')
        maskWS = self.getProperty("MaskWorkspace").value

        if maskWS is not None:
            self._masking = True
            mask = maskWS.extractY().flatten()

        difc = calWS.column('difc')
        if self._masking:
            difc = np.ma.masked_array(difc, mask)

        detID = calWS.column('detid')

        if self.getProperty("Workspace").value is not None:
            wks_name = self.getProperty("Workspace").value.getName()
        else:
            wks_name = "alignedWorkspace"
            api.LoadEmptyInstrument(
                Filename=self.getProperty("InstrumentFilename").value,
                OutputWorkspace=wks_name)

        # Make a dictionary of what options are being refined for sample/source. No rotation.
        for opt in self._optionsList[:3]:
            self._optionsDict[opt] = self.getProperty(opt).value
        for opt in self._optionsList[3:]:
            self._optionsDict[opt] = False

        # First fit L1 if selected for Source and/or Sample
        for component in "Source", "Sample":
            if self.getProperty("Fit" + component + "Position").value:
                self._move = True
                if component == "Sample":
                    comp = api.mtd[wks_name].getInstrument().getSample()
                else:
                    comp = api.mtd[wks_name].getInstrument().getSource()
                componentName = comp.getFullName()
                logger.notice("Working on " + componentName +
                              " Starting position is " + str(comp.getPos()))
                firstIndex = 0
                lastIndex = len(difc)
                if self._masking:
                    mask_out = mask[firstIndex:lastIndex + 1]
                else:
                    mask_out = None

                self._initialPos = [
                    comp.getPos().getX(),
                    comp.getPos().getY(),
                    comp.getPos().getZ(), 0, 0, 0
                ]

                # Set up x0 and bounds lists
                x0List = []
                boundsList = []
                if self._optionsDict["Xposition"]:
                    x0List.append(self._initialPos[0])
                    boundsList.append((self._initialPos[0] +
                                       self.getProperty("MinXposition").value,
                                       self._initialPos[0] +
                                       self.getProperty("MaxXposition").value))
                if self._optionsDict["Yposition"]:
                    x0List.append(self._initialPos[1])
                    boundsList.append((self._initialPos[1] +
                                       self.getProperty("MinYposition").value,
                                       self._initialPos[1] +
                                       self.getProperty("MaxYposition").value))
                if self._optionsDict["Zposition"]:
                    x0List.append(self._initialPos[2])
                    boundsList.append((self._initialPos[2] +
                                       self.getProperty("MinZposition").value,
                                       self._initialPos[2] +
                                       self.getProperty("MaxZposition").value))

                results = minimize(self._minimisation_func,
                                   x0=x0List,
                                   method='L-BFGS-B',
                                   args=(wks_name, componentName, firstIndex,
                                         lastIndex, difc[firstIndex:lastIndex +
                                                         1], mask_out),
                                   bounds=boundsList)

                # Apply the results to the output workspace
                xmap = self._mapOptions(results.x)

                # Need to grab the component again, as things have changed
                api.MoveInstrumentComponent(wks_name,
                                            componentName,
                                            X=xmap[0],
                                            Y=xmap[1],
                                            Z=xmap[2],
                                            RelativePosition=False)
                comp = api.mtd[wks_name].getInstrument().getComponentByName(
                    componentName)
                logger.notice("Finished " + componentName +
                              " Final position is " + str(comp.getPos()))
                self._move = False

        # Now fit all the components if any
        components = self.getProperty("ComponentList").value

        # Make a dictionary of what options are being refined.
        for opt in self._optionsList:
            self._optionsDict[opt] = self.getProperty(opt).value

        if self._optionsDict["Xposition"] or self._optionsDict[
                "Yposition"] or self._optionsDict["Zposition"]:
            self._move = True

        if self._optionsDict["AlphaRotation"] or self._optionsDict[
                "BetaRotation"] or self._optionsDict["GammaRotation"]:
            self._rotate = True

        prog = Progress(self, start=0, end=1, nreports=len(components))
        for component in components:
            comp = api.mtd[wks_name].getInstrument().getComponentByName(
                component)
            firstDetID = self._getFirstDetID(comp)
            firstIndex = detID.index(firstDetID)
            lastDetID = self._getLastDetID(comp)
            lastIndex = detID.index(lastDetID)
            if lastDetID - firstDetID != lastIndex - firstIndex:
                raise RuntimeError(
                    "Calibration detid doesn't match instrument")

            eulerAngles = comp.getRotation().getEulerAngles(
                self._eulerConvention)

            logger.notice("Working on " + comp.getFullName() +
                          " Starting position is " + str(comp.getPos()) +
                          " Starting rotation is " + str(eulerAngles))

            x0List = []
            self._initialPos = [
                comp.getPos().getX(),
                comp.getPos().getY(),
                comp.getPos().getZ(), eulerAngles[0], eulerAngles[1],
                eulerAngles[2]
            ]

            boundsList = []

            if self._masking:
                mask_out = mask[firstIndex:lastIndex + 1]
                if mask_out.sum() == mask_out.size:
                    self.log().warning(
                        "All pixels in '%s' are masked. Skipping calibration."
                        % component)
                    continue
            else:
                mask_out = None

            if self._optionsDict["Xposition"]:
                x0List.append(self._initialPos[0])
                boundsList.append((self._initialPos[0] +
                                   self.getProperty("MinXposition").value,
                                   self._initialPos[0] +
                                   self.getProperty("MaxXposition").value))
            if self._optionsDict["Yposition"]:
                x0List.append(self._initialPos[1])
                boundsList.append((self._initialPos[1] +
                                   self.getProperty("MinYposition").value,
                                   self._initialPos[1] +
                                   self.getProperty("MaxYposition").value))
            if self._optionsDict["Zposition"]:
                x0List.append(self._initialPos[2])
                boundsList.append((self._initialPos[2] +
                                   self.getProperty("MinZposition").value,
                                   self._initialPos[2] +
                                   self.getProperty("MaxZposition").value))
            if self._optionsDict["AlphaRotation"]:
                x0List.append(self._initialPos[3])
                boundsList.append((self._initialPos[3] +
                                   self.getProperty("MinAlphaRotation").value,
                                   self._initialPos[3] +
                                   self.getProperty("MaxAlphaRotation").value))
            if self._optionsDict["BetaRotation"]:
                x0List.append(self._initialPos[4])
                boundsList.append((self._initialPos[4] +
                                   self.getProperty("MinBetaRotation").value,
                                   self._initialPos[4] +
                                   self.getProperty("MaxBetaRotation").value))
            if self._optionsDict["GammaRotation"]:
                x0List.append(self._initialPos[5])
                boundsList.append((self._initialPos[5] +
                                   self.getProperty("MinGammaRotation").value,
                                   self._initialPos[5] +
                                   self.getProperty("MaxGammaRotation").value))

            results = minimize(self._minimisation_func,
                               x0=x0List,
                               method='L-BFGS-B',
                               args=(wks_name, component, firstIndex,
                                     lastIndex, difc[firstIndex:lastIndex + 1],
                                     mask_out),
                               bounds=boundsList)

            # Apply the results to the output workspace
            xmap = self._mapOptions(results.x)

            if self._move:
                api.MoveInstrumentComponent(wks_name,
                                            component,
                                            X=xmap[0],
                                            Y=xmap[1],
                                            Z=xmap[2],
                                            RelativePosition=False)

            if self._rotate:
                (rotw, rotx, roty,
                 rotz) = self._eulerToAngleAxis(xmap[3], xmap[4], xmap[5],
                                                self._eulerConvention)
                api.RotateInstrumentComponent(wks_name,
                                              component,
                                              X=rotx,
                                              Y=roty,
                                              Z=rotz,
                                              Angle=rotw,
                                              RelativeRotation=False)

            # Need to grab the component again, as things have changed
            comp = api.mtd[wks_name].getInstrument().getComponentByName(
                component)
            logger.notice(
                "Finshed " + comp.getFullName() + " Final position is " +
                str(comp.getPos()) + " Final rotation is " +
                str(comp.getRotation().getEulerAngles(self._eulerConvention)))

            prog.report()
        logger.notice("Results applied to workspace " + wks_name)
예제 #36
0
    def PyExec(self):
        in_Runs = self.getProperty("RunNumbers").value
        maskWSname = self._getMaskWSname()
        progress = Progress(self, 0., .25, 3)

        # default arguments for AlignAndFocusPowder
        alignAndFocusArgs = {
            'TMax': 50000,
            'RemovePromptPulseWidth': 1600,
            'PreserveEvents': False,
            'Dspacing': True,  # binning parameters in d-space
            'Params': self.getProperty("Binning").value
        }

        # workspace for loading metadata only to be used in LoadDiffCal and
        # CreateGroupingWorkspace
        metaWS = None

        # either type of file-based calibration is stored in the same variable
        calib = self.getProperty("Calibration").value
        detcalFile = None
        if calib == "Calibration File":
            metaWS = self._loadMetaWS(in_Runs[0])
            LoadDiffCal(Filename=self.getPropertyValue("CalibrationFilename"),
                        WorkspaceName='SNAP',
                        InputWorkspace=metaWS,
                        MakeGroupingWorkspace=False,
                        MakeMaskWorkspace=False)
            alignAndFocusArgs['CalibrationWorkspace'] = 'SNAP_cal'
        elif calib == 'DetCal File':
            detcalFile = ','.join(self.getProperty('DetCalFilename').value)
        progress.report('loaded calibration')

        norm = self.getProperty("Normalization").value

        if norm == "From Processed Nexus":
            norm_File = self.getProperty("NormalizationFilename").value
            normalizationWS = 'normWS'
            LoadNexusProcessed(Filename=norm_File,
                               OutputWorkspace=normalizationWS)
            progress.report('loaded normalization')
        elif norm == "From Workspace":
            normalizationWS = str(
                self.getProperty("NormalizationWorkspace").value)
            progress.report('')
        else:
            normalizationWS = None
            progress.report('')

        group = self._generateGrouping(in_Runs[0], metaWS, progress)

        if metaWS is not None:
            DeleteWorkspace(Workspace=metaWS)

        Process_Mode = self.getProperty("ProcessingMode").value

        prefix = self.getProperty("OptionalPrefix").value

        # --------------------------- REDUCE DATA -----------------------------

        Tag = 'SNAP'
        if self.getProperty("LiveData").value:
            Tag = 'Live'

        progStart = .25
        progDelta = (1. - progStart) / len(in_Runs)
        for i, runnumber in enumerate(in_Runs):
            self.log().notice("processing run %s" % runnumber)
            self.log().information(str(self.get_IPTS_Local(runnumber)))

            # put together output names
            new_Tag = Tag
            if len(prefix) > 0:
                new_Tag += '_' + prefix
            basename = '%s_%s_%s' % (new_Tag, runnumber, group)

            if self.getProperty("LiveData").value:
                raise RuntimeError('Live data is not currently supported')
            else:
                Load(Filename='SNAP' + str(runnumber),
                     OutputWorkspace=basename + '_red',
                     startProgress=progStart,
                     endProgress=progStart + .25 * progDelta)
                progStart += .25 * progDelta
            redWS = basename + '_red'

            # overwrite geometry with detcal files
            if calib == 'DetCal File':
                LoadIsawDetCal(InputWorkspace=redWS, Filename=detcalFile)

            # create unfocussed data if in set-up mode
            if Process_Mode == "Set-Up":
                unfocussedWksp = '{}_{}_d'.format(new_Tag, runnumber)
            else:
                unfocussedWksp = ''

            AlignAndFocusPowder(
                InputWorkspace=redWS,
                OutputWorkspace=redWS,
                MaskWorkspace=maskWSname,  # can be empty string
                GroupingWorkspace=group,
                UnfocussedWorkspace=unfocussedWksp,  # can be empty string
                startProgress=progStart,
                endProgress=progStart + .5 * progDelta,
                **alignAndFocusArgs)
            progStart += .5 * progDelta

            # the rest takes up .25 percent of the run processing
            progress = Progress(self, progStart, progStart + .25 * progDelta,
                                2)

            # AlignAndFocusPowder leaves the data in time-of-flight
            ConvertUnits(InputWorkspace=redWS,
                         OutputWorkspace=redWS,
                         Target='dSpacing',
                         EMode='Elastic')

            # Edit instrument geometry to make final workspace smaller on disk
            det_table = PreprocessDetectorsToMD(
                Inputworkspace=redWS, OutputWorkspace='__SNAP_det_table')
            polar = np.degrees(det_table.column('TwoTheta'))
            azi = np.degrees(det_table.column('Azimuthal'))
            EditInstrumentGeometry(Workspace=redWS,
                                   L2=det_table.column('L2'),
                                   Polar=polar,
                                   Azimuthal=azi)
            mtd.remove('__SNAP_det_table')
            progress.report('simplify geometry')

            # AlignAndFocus doesn't necessarily rebin the data correctly
            if Process_Mode == "Set-Up":
                Rebin(InputWorkspace=unfocussedWksp,
                      Params=alignAndFocusArgs['Params'],
                      Outputworkspace=unfocussedWksp)

            NormaliseByCurrent(InputWorkspace=redWS, OutputWorkspace=redWS)

            # normalize the data as requested
            normalizationWS = self._generateNormalization(
                redWS, norm, normalizationWS)
            normalizedWS = None
            if normalizationWS is not None:
                normalizedWS = basename + '_nor'
                Divide(LHSWorkspace=redWS,
                       RHSWorkspace=normalizationWS,
                       OutputWorkspace=normalizedWS)
                ReplaceSpecialValues(Inputworkspace=normalizedWS,
                                     OutputWorkspace=normalizedWS,
                                     NaNValue='0',
                                     NaNError='0',
                                     InfinityValue='0',
                                     InfinityError='0')
                progress.report('normalized')
            else:
                progress.report()

            # rename everything as appropriate and determine output workspace name
            if normalizedWS is None:
                outputWksp = redWS
            else:
                outputWksp = normalizedWS

                if norm == "Extracted from Data" and Process_Mode == "Production":
                    DeleteWorkspace(Workspace=redWS)
                    DeleteWorkspace(Workspace=normalizationWS)

            # Save requested formats
            saveDir = self.getPropertyValue("OutputDirectory").strip()
            if len(saveDir) <= 0:
                self.log().notice('Using default save location')
                saveDir = os.path.join(self.get_IPTS_Local(runnumber),
                                       'shared', 'data')
            self._save(saveDir, basename, outputWksp)

            # set workspace as an output so it gets history
            propertyName = 'OutputWorkspace_' + str(outputWksp)
            self.declareProperty(
                WorkspaceProperty(propertyName, outputWksp, Direction.Output))
            self.setProperty(propertyName, outputWksp)

            # declare some things as extra outputs in set-up
            if Process_Mode != "Production":
                prefix = 'OuputWorkspace_{:d}_'.format(i)
                propNames = [prefix + it for it in ['d', 'norm', 'normalizer']]
                wkspNames = [
                    '%s_%s_d' % (new_Tag, runnumber), basename + '_red',
                    '%s_%s_normalizer' % (new_Tag, runnumber)
                ]
                for (propName, wkspName) in zip(propNames, wkspNames):
                    if mtd.doesExist(wkspName):
                        self.declareProperty(
                            WorkspaceProperty(propName, wkspName,
                                              Direction.Output))
                        self.setProperty(propName, wkspName)
예제 #37
0
    def PyExec(self):
        from IndirectCommon import getEfixed

        self._setup()

        # Set up progress reporting
        n_prog_reports = 2
        if self._can_ws_name is not None:
            n_prog_reports += 1
        prog = Progress(self, 0.0, 1.0, n_prog_reports)

        efixed = getEfixed(self._sample_ws_name)

        sample_wave_ws = '__sam_wave'
        ConvertUnits(InputWorkspace=self._sample_ws_name,
                     OutputWorkspace=sample_wave_ws,
                     Target='Wavelength',
                     EMode='Indirect',
                     EFixed=efixed,
                     EnableLogging=False)

        prog.report('Calculating sample corrections')
        if self._sample_density_type == 'Mass Density':
            builder = MaterialBuilder()
            mat = builder.setFormula(
                self._sample_chemical_formula).setMassDensity(
                    self._sample_density).build()
            self._sample_density = mat.numberDensity
        SetSampleMaterial(sample_wave_ws,
                          ChemicalFormula=self._sample_chemical_formula,
                          SampleNumberDensity=self._sample_density)
        prog.report('Calculating sample corrections')
        CylinderAbsorption(InputWorkspace=sample_wave_ws,
                           OutputWorkspace=self._ass_ws,
                           SampleNumberDensity=self._sample_density,
                           NumberOfWavelengthPoints=10,
                           CylinderSampleHeight=3.0,
                           CylinderSampleRadius=self._sample_radius,
                           NumberOfSlices=1,
                           NumberOfAnnuli=10)

        group = self._ass_ws

        if self._can_ws_name is not None:
            can_wave_ws = '__can_wave'
            ConvertUnits(InputWorkspace=self._can_ws_name,
                         OutputWorkspace=can_wave_ws,
                         Target='Wavelength',
                         EMode='Indirect',
                         EFixed=efixed,
                         EnableLogging=False)
            if self._can_scale != 1.0:
                logger.information('Scaling can by: ' + str(self._can_scale))
                Scale(InputWorkspace=can_wave_ws,
                      OutputWorkspace=can_wave_ws,
                      Factor=self._can_scale,
                      Operation='Multiply')

            can_thickness = self._can_radius - self._sample_radius
            logger.information('Container thickness: ' + str(can_thickness))

            if self._use_can_corrections:
                # Doing can corrections
                prog.report('Calculating container corrections')
                Divide(LHSWorkspace=sample_wave_ws,
                       RHSWorkspace=self._ass_ws,
                       OutputWorkspace=sample_wave_ws)

                if self._sample_density_type == 'Mass Density':
                    builder = MaterialBuilder()
                    mat = builder.setFormula(
                        self._can_chemical_formula).setMassDensity(
                            self._can_density).build()
                    self._can_density = mat.numberDensity
                SetSampleMaterial(can_wave_ws,
                                  ChemicalFormula=self._can_chemical_formula,
                                  SampleNumberDensity=self._can_density)

                AnnularRingAbsorption(
                    InputWorkspace=can_wave_ws,
                    OutputWorkspace=self._acc_ws,
                    SampleHeight=3.0,
                    SampleThickness=can_thickness,
                    CanInnerRadius=0.9 * self._sample_radius,
                    CanOuterRadius=1.1 * self._can_radius,
                    SampleChemicalFormula=self._can_chemical_formula,
                    SampleNumberDensity=self._can_density,
                    NumberOfWavelengthPoints=10,
                    EventsPerPoint=self._events)

                Divide(LHSWorkspace=can_wave_ws,
                       RHSWorkspace=self._acc_ws,
                       OutputWorkspace=can_wave_ws)
                Minus(LHSWorkspace=sample_wave_ws,
                      RHSWorkspace=can_wave_ws,
                      OutputWorkspace=sample_wave_ws)
                group += ',' + self._acc_ws

            else:
                # Doing simple can subtraction
                prog.report('Calculating container scaling')
                Minus(LHSWorkspace=sample_wave_ws,
                      RHSWorkspace=can_wave_ws,
                      OutputWorkspace=sample_wave_ws)
                Divide(LHSWorkspace=sample_wave_ws,
                       RHSWorkspace=self._ass_ws,
                       OutputWorkspace=sample_wave_ws)

            DeleteWorkspace(can_wave_ws, EnableLogging=False)

        else:
            Divide(LHSWorkspace=sample_wave_ws,
                   RHSWorkspace=self._ass_ws,
                   OutputWorkspace=sample_wave_ws)

        ConvertUnits(InputWorkspace=sample_wave_ws,
                     OutputWorkspace=self._output_ws,
                     Target='DeltaE',
                     EMode='Indirect',
                     EFixed=efixed,
                     EnableLogging=False)
        DeleteWorkspace(sample_wave_ws, EnableLogging=False)

        # Record sample logs
        prog.report('Recording sample logs')
        sample_log_workspaces = [self._output_ws, self._ass_ws]
        sample_logs = [('sample_shape', 'cylinder'),
                       ('sample_filename', self._sample_ws_name),
                       ('sample_radius', self._sample_radius)]

        if self._can_ws_name is not None:
            sample_logs.append(('container_filename', self._can_ws_name))
            sample_logs.append(('container_scale', self._can_scale))
            if self._use_can_corrections:
                sample_log_workspaces.append(self._acc_ws)
                sample_logs.append(('container_thickness', can_thickness))

        log_names = [item[0] for item in sample_logs]
        log_values = [item[1] for item in sample_logs]

        for ws_name in sample_log_workspaces:
            AddSampleLogMultiple(Workspace=ws_name,
                                 LogNames=log_names,
                                 LogValues=log_values,
                                 EnableLogging=False)

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

        # Output the Abs group workspace if it is wanted, delete if not
        if self._abs_ws == '':
            DeleteWorkspace(self._ass_ws, EnableLogging=False)
            if self._can_ws_name is not None and self._use_can_corrections:
                DeleteWorkspace(self._acc_ws, EnableLogging=False)

        else:
            GroupWorkspaces(InputWorkspaces=group,
                            OutputWorkspace=self._abs_ws,
                            EnableLogging=False)
            self.setProperty('CorrectionsWorkspace', self._abs_ws)
class IndirectILLReductionQENS(PythonAlgorithm):

    _sample_files = None
    _alignment_files = None
    _background_files = None
    _calibration_files = None
    _background_calib_files = None
    _sum_all_runs = None
    _unmirror_option = None
    _back_scaling = None
    _back_calib_scaling = None
    _criteria = None
    _progress = None
    _red_ws = None
    _common_args = {}
    _peak_range = []
    _runs = None
    _spectrum_axis = None
    _discard_sds = None
    _group_detectors = None

    def category(self):
        return "Workflow\\MIDAS;Workflow\\Inelastic;Inelastic\\Indirect;Inelastic\\Reduction;ILL\\Indirect"

    def summary(self):
        return 'Performs quasi-elastic neutron scattering (QENS) multiple file reduction ' \
               'for ILL indirect geometry data, instrument IN16B.'

    def seeAlso(self):
        return [ "IndirectILLReductionFWS","IndirectILLEnergyTransfer" ]

    def name(self):
        return "IndirectILLReductionQENS"

    def PyInit(self):

        self.declareProperty(MultipleFileProperty('Run', extensions=['nxs']),
                             doc='Run number(s) of sample run(s).')

        self.declareProperty(MultipleFileProperty('BackgroundRun',
                                                  action=FileAction.OptionalLoad,
                                                  extensions=['nxs']),
                             doc='Run number(s) of background (empty can) run(s).')

        self.declareProperty(MultipleFileProperty('CalibrationRun',
                                                  action=FileAction.OptionalLoad,
                                                  extensions=['nxs']),
                             doc='Run number(s) of vanadium calibration run(s).')

        self.declareProperty(MultipleFileProperty('CalibrationBackgroundRun',
                                                  action=FileAction.OptionalLoad,
                                                  extensions=['nxs']),
                             doc='Run number(s) of background (empty can) run(s) for vanadium run.')

        self.declareProperty(MultipleFileProperty('AlignmentRun',
                                                  action=FileAction.OptionalLoad,
                                                  extensions=['nxs']),
                             doc='Run number(s) of vanadium run(s) used for '
                                 'peak alignment for UnmirrorOption=[5, 7]')

        self.declareProperty(name='SumRuns',
                             defaultValue=False,
                             doc='Whether to sum all the input runs.')

        self.declareProperty(name='CropDeadMonitorChannels', defaultValue=False,
                             doc='Whether or not to exclude the first and last few channels '
                                 'with 0 monitor count in the energy transfer formula.')

        self.declareProperty(name='UnmirrorOption', defaultValue=6,
                             validator=IntBoundedValidator(lower=0, upper=7),
                             doc='Unmirroring options : \n'
                                 '0 no unmirroring\n'
                                 '1 sum of left and right\n'
                                 '2 left\n'
                                 '3 right\n'
                                 '4 shift right according to left and sum\n'
                                 '5 like 4, but use alignment run for peak positions\n'
                                 '6 center both left and right at zero and sum\n'
                                 '7 like 6, but use alignment run for peak positions')

        self.declareProperty(name='BackgroundScalingFactor', defaultValue=1.,
                             validator=FloatBoundedValidator(lower=0),
                             doc='Scaling factor for background subtraction')

        self.declareProperty(name='CalibrationBackgroundScalingFactor', defaultValue=1.,
                             validator=FloatBoundedValidator(lower=0),
                             doc='Scaling factor for background subtraction for vanadium calibration')

        self.declareProperty(name='CalibrationPeakRange', defaultValue=[-0.003,0.003],
                             validator=FloatArrayMandatoryValidator(),
                             doc='Peak range for integration over calibration file peak (in mev)')

        self.declareProperty(FileProperty('MapFile', '',
                                          action=FileAction.OptionalLoad,
                                          extensions=['map','xml']),
                             doc='Filename of the detector grouping map file to use. \n'
                                 'By default all the pixels will be summed per each tube. \n'
                                 'Use .map or .xml file (see GroupDetectors documentation) '
                                 'only if different range is needed for each tube.')

        self.declareProperty(name='ManualPSDIntegrationRange',defaultValue=[1,128],
                             doc='Integration range of vertical pixels in each PSD tube. \n'
                                 'By default all the pixels will be summed per each tube. \n'
                                 'Use this option if the same range (other than default) '
                                 'is needed for all the tubes.')

        self.declareProperty(name='Analyser',
                             defaultValue='silicon',
                             validator=StringListValidator(['silicon']),
                             doc='Analyser crystal.')

        self.declareProperty(name='Reflection',
                             defaultValue='111',
                             validator=StringListValidator(['111', '311']),
                             doc='Analyser reflection.')

        self.declareProperty(WorkspaceGroupProperty('OutputWorkspace', '',
                                                    direction=Direction.Output),
                             doc='Group name for the reduced workspace(s).')

        self.declareProperty(name='SpectrumAxis', defaultValue='SpectrumNumber',
                             validator=StringListValidator(['SpectrumNumber', '2Theta', 'Q', 'Q2']),
                             doc='The spectrum axis conversion target.')

        self.declareProperty(name='DiscardSingleDetectors', defaultValue=False,
                             doc='Whether to discard the spectra of single detectors.')

        self.declareProperty(name='GroupDetectors', defaultValue=True,
                             doc='Group the pixels using the range, tube-by-tube (default) or in a custom way; \n'
                                 'it is not recommended to group the detectors at this stage, \n'
                                 'in order to get absorption corrections right, \n'
                                 'however the default value is True for backwards compatibility.')

    def validateInputs(self):

        issues = dict()

        uo = self.getProperty('UnmirrorOption').value

        if (uo == 5 or uo == 7) and not self.getPropertyValue('AlignmentRun'):
            issues['AlignmentRun'] = 'Given UnmirrorOption requires alignment run to be set'

        if (uo != 0) and not self.getProperty('GroupDetectors').value:
            issues['UnmirrorOption'] = 'Currently UnmirrorOption=0 is required when GroupDetectors is switched off'

        if self.getPropertyValue('CalibrationRun'):
            range = self.getProperty('CalibrationPeakRange').value
            if len(range) != 2:
                issues['CalibrationPeakRange'] = 'Please provide valid calibration range ' \
                                                 '(comma separated 2 energy values).'
            elif range[0] >= range[1]:
                issues['CalibrationPeakRange'] = 'Please provide valid calibration range. ' \
                                                 'Start energy is bigger than end energy.'

        if self.getPropertyValue('CalibrationBackgroundRun') and not self.getPropertyValue('CalibrationRun'):
            issues['CalibrationRun'] = 'Calibration run is required when calibration background is given.'

        return issues

    def setUp(self):

        self._sample_file = self.getPropertyValue('Run')
        self._alignment_file = self.getPropertyValue('AlignmentRun').replace(',', '+') # automatic summing
        self._background_file = self.getPropertyValue('BackgroundRun').replace(',', '+') # automatic summing
        self._calibration_file = self.getPropertyValue('CalibrationRun').replace(',', '+') # automatic summing
        self._background_calib_files = self.getPropertyValue('CalibrationBackgroundRun').replace(',', '+') # automatic summing
        self._sum_all_runs = self.getProperty('SumRuns').value
        self._unmirror_option = self.getProperty('UnmirrorOption').value
        self._back_scaling = self.getProperty('BackgroundScalingFactor').value
        self._back_calib_scaling = self.getProperty('CalibrationBackgroundScalingFactor').value
        self._peak_range = self.getProperty('CalibrationPeakRange').value
        self._spectrum_axis = self.getPropertyValue('SpectrumAxis')
        self._discard_sds = self.getProperty('DiscardSingleDetectors').value
        self._group_detectors = self.getProperty('GroupDetectors').value
        self._red_ws = self.getPropertyValue('OutputWorkspace')

        suffix = ''
        if self._spectrum_axis == 'SpectrumNumber':
            suffix = '_red'
        elif self._spectrum_axis == '2Theta':
            suffix = '_2theta'
        elif self._spectrum_axis == 'Q':
            suffix = '_q'
        elif self._spectrum_axis == 'Q2':
            suffix = '_q2'

        self._red_ws += suffix

        # arguments to pass to IndirectILLEnergyTransfer
        self._common_args['MapFile'] = self.getPropertyValue('MapFile')
        self._common_args['Analyser'] = self.getPropertyValue('Analyser')
        self._common_args['Reflection'] = self.getPropertyValue('Reflection')
        self._common_args['ManualPSDIntegrationRange'] = self.getProperty('ManualPSDIntegrationRange').value
        self._common_args['CropDeadMonitorChannels'] = self.getProperty('CropDeadMonitorChannels').value
        self._common_args['SpectrumAxis'] = self._spectrum_axis
        self._common_args['DiscardSingleDetectors'] = self._discard_sds
        self._common_args['GroupDetectors'] = self._group_detectors

        if self._sum_all_runs is True:
            self.log().notice('All the sample runs will be summed')
            self._sample_file = self._sample_file.replace(',', '+')

        # Nexus metadata criteria for QENS type of data
        self._criteria = '$/entry0/instrument/Doppler/maximum_delta_energy$ != 0. and ' \
                         '$/entry0/instrument/Doppler/velocity_profile$ == 0'

        # empty list to store all final workspaces to group
        self._ws_list = []

    def _mask(self, ws, xstart, xend):
        """
        Masks the first and last bins
        @param   ws           :: input workspace name
        @param   xstart       :: MaskBins between x[0] and x[xstart]
        @param   xend         :: MaskBins between x[xend] and x[-1]
        """
        x_values = mtd[ws].readX(0)

        if xstart > 0:
            self.log().debug('Mask bins smaller than {0}'.format(xstart))
            MaskBins(InputWorkspace=ws, OutputWorkspace=ws, XMin=x_values[0], XMax=x_values[int(xstart)])

        if xend < len(x_values) - 1:
            self.log().debug('Mask bins larger than {0}'.format(xend))
            MaskBins(InputWorkspace=ws, OutputWorkspace=ws, XMin=x_values[int(xend) + 1], XMax=x_values[-1])

    def _filter_files(self, files, label):
        '''
        Filters the given list of files according to nexus criteria
        @param  files :: list of input files (i.e. , and + separated string)
        @param  label :: label of error message if nothing left after filtering
        @throws RuntimeError :: when nothing left after filtering
        @return :: the list of input files that passsed the criteria
        '''

        files = SelectNexusFilesByMetadata(files, self._criteria)

        if not files:
            raise RuntimeError('None of the {0} runs are of QENS type.'
                               'Check the files or reduction type.'.format(label))
        else:
            self.log().information('Filtered {0} runs are: {0} \\n'.format(label,files.replace(',','\\n')))

        return files

    def _filter_all_input_files(self):
        '''
        Filters all the lists of input files needed for the reduction.
        '''

        self._sample_file = self._filter_files(self._sample_file,'sample')

        if self._background_file:
            self._background_file = self._filter_files(self._background_file, 'background')

        if self._calibration_file:
            self._calibration_file = self._filter_files(self._calibration_file, 'calibration')

        if self._background_calib_files:
            self._background_calib_files = self._filter_files(self._background_calib_files, 'calibration background')

        if self._alignment_file:
            self._alignment_file = self._filter_files(self._alignment_file, 'alignment')

    def _warn_negative_integral(self, ws, message):
        '''
        Raises an error if an integral of the given workspace is <= 0
        @param ws :: input workspace name
        @param message :: message suffix for the error
        @throws RuntimeError :: on non-positive integral found
        '''

        tmp_int = '__tmp_int'+ws
        Integration(InputWorkspace=ws,OutputWorkspace=tmp_int)

        for item in mtd[tmp_int]:
            for index in range(item.getNumberHistograms()):
                if item.readY(index)[0] <= 0:
                    self.log().warning('Negative or 0 integral in spectrum #{0} {1}'.format(index,message))

        DeleteWorkspace(tmp_int)

    def PyExec(self):

        self.setUp()

        self._filter_all_input_files()

        if self._background_file:
            background = '__background_'+self._red_ws
            IndirectILLEnergyTransfer(Run = self._background_file, OutputWorkspace = background, **self._common_args)
            Scale(InputWorkspace=background ,Factor=self._back_scaling,OutputWorkspace=background)

        if self._calibration_file:
            calibration = '__calibration_'+self._red_ws
            IndirectILLEnergyTransfer(Run = self._calibration_file, OutputWorkspace = calibration, **self._common_args)

            if self._background_calib_files:
                back_calibration = '__calibration_back_'+self._red_ws
                IndirectILLEnergyTransfer(Run = self._background_calib_files, OutputWorkspace = back_calibration, **self._common_args)
                Scale(InputWorkspace=back_calibration, Factor=self._back_calib_scaling, OutputWorkspace=back_calibration)
                Minus(LHSWorkspace=calibration, RHSWorkspace=back_calibration, OutputWorkspace=calibration)

            # MatchPeaks does not play nicely with the ws groups
            for ws in mtd[calibration]:
                MatchPeaks(InputWorkspace=ws.getName(), OutputWorkspace=ws.getName(), MaskBins=True, BinRangeTable = '')

            Integration(InputWorkspace=calibration,RangeLower=self._peak_range[0],RangeUpper=self._peak_range[1],
                        OutputWorkspace=calibration)
            self._warn_negative_integral(calibration,'in calibration run.')

        if self._unmirror_option == 5 or self._unmirror_option == 7:
            alignment = '__alignment_'+self._red_ws
            IndirectILLEnergyTransfer(Run = self._alignment_file, OutputWorkspace = alignment, **self._common_args)

        runs = self._sample_file.split(',')

        self._progress = Progress(self, start=0.0, end=1.0, nreports=len(runs))

        for run in runs:
            self._reduce_run(run)

        if self._background_file:
            DeleteWorkspace(background)

        if self._calibration_file:
            DeleteWorkspace(calibration)

        if self._background_calib_files:
            DeleteWorkspace(back_calibration)

        if self._unmirror_option == 5 or self._unmirror_option == 7:
            DeleteWorkspace(alignment)

        GroupWorkspaces(InputWorkspaces=self._ws_list,OutputWorkspace=self._red_ws)

        for ws in mtd[self._red_ws]:
            if ws.getRun().hasProperty("NormalisedTo"):
                if ws.getRun().getLogData("NormalisedTo").value == "Monitor":
                    ws.setDistribution(True)
            # unhide the final workspaces, i.e. remove __ prefix
            RenameWorkspace(InputWorkspace=ws,OutputWorkspace=ws.getName()[2:])

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

    def _reduce_run(self,run):
        '''
        Reduces the given (single or summed multiple) run
        @param run :: run path
        @throws RuntimeError : if inconsistent mirror sense is found in container or calibration run
        '''

        runs_list = run.split('+')

        runnumber = os.path.basename(runs_list[0]).split('.')[0]

        self._progress.report("Reducing run #" + runnumber)

        ws = '__' + runnumber

        if (len(runs_list) > 1):
            ws += '_multiple'

        ws += '_' + self._red_ws

        back_ws = '__background_'+self._red_ws

        calib_ws = '__calibration_'+self._red_ws

        IndirectILLEnergyTransfer(Run = run, OutputWorkspace = ws, **self._common_args)

        wings = mtd[ws].getNumberOfEntries()

        if self._background_file:
            if wings == mtd[back_ws].getNumberOfEntries():
                Minus(LHSWorkspace=ws, RHSWorkspace=back_ws, OutputWorkspace=ws)
                self._warn_negative_integral(ws,'after background subtraction.')
            else:
                raise RuntimeError('Inconsistent mirror sense in background run. Unable to perform subtraction.')

        if self._calibration_file:
            if wings == mtd[calib_ws].getNumberOfEntries():
                Divide(LHSWorkspace=ws, RHSWorkspace=calib_ws, OutputWorkspace=ws)
                self._scale_calibration(ws, calib_ws)
            else:
                raise RuntimeError('Inconsistent mirror sense in calibration run. Unable to perform calibration.')

        self._perform_unmirror(ws,runnumber)

        # register to reduced runs list
        self._ws_list.append(ws)

    def _scale_calibration(self, ws, calib_ws):
        '''
        Scales the wings of calibrated sample ws with the maximum
        of the integrated intensities in each wing of calib ws
        @param ws       :: calibrated sample workspace
        @param calib_ws :: calibration workspace
        '''

        # number of wings are checked to be the same in ws and calib_ws here already

        for wing in range(mtd[ws].getNumberOfEntries()):
            sample = mtd[ws].getItem(wing).getName()
            integral = mtd[calib_ws].getItem(wing).getName()
            scale = numpy.max(mtd[integral].extractY()[:,0])
            self.log().information("Wing {0} will be scaled up with {1} after calibration"
                                   .format(wing,scale))
            Scale(InputWorkspace=sample,Factor=scale,OutputWorkspace=sample,Operation='Multiply')

    def _perform_unmirror(self, ws, run):
        '''
        Performs unmirroring, i.e. summing of left and right wings
        for two-wing data or centering the one wing data
        @param ws  :: workspace
        @param run :: runnumber
        @throws RuntimeError : if the size of the left and right wings do not match in 2-wings case
                               and the unmirror option is 1 or >3
        @throws RuntimeError : if the mirros sense in the alignment run is inconsistent
        '''

        outname = ws + '_tmp'

        wings = mtd[ws].getNumberOfEntries()

        self.log().information('Unmirroring workspace {0} with option {1}'
                               .format(ws,self._unmirror_option))

        alignment = '__alignment_'+self._red_ws

        # make sure the sample and alignment runs have the same mirror sense for unmirror 5,7
        if self._unmirror_option == 5 or self._unmirror_option == 7:
            if wings != mtd[alignment].getNumberOfEntries():
                raise RuntimeError('Inconsistent mirror sense in alignment run. Unable to perform unmirror.')

        if wings == 1:   # one wing

            name = mtd[ws].getItem(0).getName()

            if self._unmirror_option < 6:  # do unmirror 0, i.e. nothing
                CloneWorkspace(InputWorkspace = name, OutputWorkspace = outname)
            elif self._unmirror_option == 6:
                MatchPeaks(InputWorkspace = name, OutputWorkspace = outname, MaskBins = True, BinRangeTable = '')
            elif self._unmirror_option == 7:
                MatchPeaks(InputWorkspace = name, InputWorkspace2 = mtd[alignment].getItem(0).getName(),
                           MatchInput2ToCenter = True, OutputWorkspace = outname, MaskBins = True, BinRangeTable = '')

        elif wings == 2:  # two wing

            left = mtd[ws].getItem(0).getName()
            right = mtd[ws].getItem(1).getName()

            mask_min = 0
            mask_max = mtd[left].blocksize()

            if (self._common_args['CropDeadMonitorChannels']
                    and (self._unmirror_option == 1 or self._unmirror_option > 3)
                    and mtd[left].blocksize() != mtd[right].blocksize()):
                raise RuntimeError("Different number of bins found in the left and right wings"
                                   " after cropping the dead monitor channels. "
                                   "Unable to perform the requested unmirror option, consider using option "
                                   "0, 2 or 3 or switch off the CropDeadMonitorChannels.")

            if self._unmirror_option == 0:
                left_out = '__'+run+'_'+self._red_ws+'_left'
                right_out = '__'+run+'_'+self._red_ws+'_right'
                CloneWorkspace(InputWorkspace=left, OutputWorkspace=left_out)
                CloneWorkspace(InputWorkspace=right, OutputWorkspace=right_out)
                GroupWorkspaces(InputWorkspaces=[left_out,right_out],OutputWorkspace=outname)
            elif self._unmirror_option == 1:
                Plus(LHSWorkspace=left, RHSWorkspace=right, OutputWorkspace=outname)
                Scale(InputWorkspace=outname, OutputWorkspace=outname, Factor=0.5)
            elif self._unmirror_option == 2:
                CloneWorkspace(InputWorkspace=left, OutputWorkspace=outname)
            elif self._unmirror_option == 3:
                CloneWorkspace(InputWorkspace=right, OutputWorkspace=outname)
            elif self._unmirror_option == 4:
                bin_range_table = '__um4_'+right
                MatchPeaks(InputWorkspace=right, InputWorkspace2=left, OutputWorkspace=right,
                           MaskBins = True, BinRangeTable = bin_range_table)
                mask_min = mtd[bin_range_table].row(0)['MinBin']
                mask_max = mtd[bin_range_table].row(0)['MaxBin']
                DeleteWorkspace(bin_range_table)
            elif self._unmirror_option == 5:
                bin_range_table = '__um5_' + right
                MatchPeaks(InputWorkspace=right, InputWorkspace2=mtd[alignment].getItem(0).getName(),
                           InputWorkspace3=mtd[alignment].getItem(1).getName(), OutputWorkspace=right,
                           MaskBins = True, BinRangeTable = bin_range_table)
                mask_min = mtd[bin_range_table].row(0)['MinBin']
                mask_max = mtd[bin_range_table].row(0)['MaxBin']
                DeleteWorkspace(bin_range_table)
            elif self._unmirror_option == 6:
                bin_range_table_left = '__um6_' + left
                bin_range_table_right = '__um6_' + right
                MatchPeaks(InputWorkspace=left, OutputWorkspace=left, MaskBins = True,
                           BinRangeTable = bin_range_table_left)
                MatchPeaks(InputWorkspace=right, OutputWorkspace=right, MaskBins = True,
                           BinRangeTable=bin_range_table_right)
                mask_min = max(mtd[bin_range_table_left].row(0)['MinBin'],mtd[bin_range_table_right].row(0)['MinBin'])
                mask_max = min(mtd[bin_range_table_left].row(0)['MaxBin'],mtd[bin_range_table_right].row(0)['MaxBin'])
                DeleteWorkspace(bin_range_table_left)
                DeleteWorkspace(bin_range_table_right)
            elif self._unmirror_option == 7:
                bin_range_table_left = '__um7_' + left
                bin_range_table_right = '__um7_' + right
                MatchPeaks(InputWorkspace=left, InputWorkspace2=mtd[alignment].getItem(0).getName(),
                           OutputWorkspace=left,MatchInput2ToCenter=True,
                           MaskBins = True, BinRangeTable=bin_range_table_left)
                MatchPeaks(InputWorkspace=right, InputWorkspace2=mtd[alignment].getItem(1).getName(),
                           OutputWorkspace=right, MatchInput2ToCenter=True,
                           MaskBins = True, BinRangeTable=bin_range_table_right)
                mask_min = max(mtd[bin_range_table_left].row(0)['MinBin'], mtd[bin_range_table_right].row(0)['MinBin'])
                mask_max = min(mtd[bin_range_table_left].row(0)['MaxBin'], mtd[bin_range_table_right].row(0)['MaxBin'])
                DeleteWorkspace(bin_range_table_left)
                DeleteWorkspace(bin_range_table_right)

            if self._unmirror_option > 3:
                Plus(LHSWorkspace=left, RHSWorkspace=right, OutputWorkspace=outname)
                Scale(InputWorkspace=outname, OutputWorkspace=outname, Factor=0.5)
                self._mask(outname, mask_min, mask_max)

        DeleteWorkspace(ws)
        RenameWorkspace(InputWorkspace=outname,OutputWorkspace=ws)
예제 #39
0
    def PyExec(self):
        # remove possible old temp workspaces
        [
            DeleteWorkspace(ws) for ws in self.temp_workspace_list
            if mtd.doesExist(ws)
        ]

        _background = bool(self.getProperty("Background").value)
        _load_inst = bool(self.getProperty("LoadInstrument").value)
        _detcal = bool(self.getProperty("DetCal").value)
        _masking = bool(self.getProperty("MaskFile").value)
        _outWS_name = self.getPropertyValue("OutputWorkspace")

        UBList = self._generate_UBList()

        dim0_min, dim0_max, dim0_bins = self.getProperty('BinningDim0').value
        dim1_min, dim1_max, dim1_bins = self.getProperty('BinningDim1').value
        dim2_min, dim2_max, dim2_bins = self.getProperty('BinningDim2').value
        MinValues = "{},{},{}".format(dim0_min, dim1_min, dim2_min)
        MaxValues = "{},{},{}".format(dim0_max, dim1_max, dim2_max)
        AlignedDim0 = ",{},{},{}".format(dim0_min, dim0_max, int(dim0_bins))
        AlignedDim1 = ",{},{},{}".format(dim1_min, dim1_max, int(dim1_bins))
        AlignedDim2 = ",{},{},{}".format(dim2_min, dim2_max, int(dim2_bins))

        LoadNexus(Filename=self.getProperty("SolidAngle").value,
                  OutputWorkspace='__sa')
        LoadNexus(Filename=self.getProperty("Flux").value,
                  OutputWorkspace='__flux')

        if _masking:
            LoadMask(Instrument=mtd['__sa'].getInstrument().getName(),
                     InputFile=self.getProperty("MaskFile").value,
                     OutputWorkspace='__mask')
            MaskDetectors(Workspace='__sa', MaskedWorkspace='__mask')
            DeleteWorkspace('__mask')

        XMin = mtd['__sa'].getXDimension().getMinimum()
        XMax = mtd['__sa'].getXDimension().getMaximum()

        if _background:
            Load(Filename=self.getProperty("Background").value,
                 OutputWorkspace='__bkg',
                 FilterByTofMin=self.getProperty("FilterByTofMin").value,
                 FilterByTofMax=self.getProperty("FilterByTofMax").value)
            if _load_inst:
                LoadInstrument(
                    Workspace='__bkg',
                    Filename=self.getProperty("LoadInstrument").value,
                    RewriteSpectraMap=False)
            if _detcal:
                LoadIsawDetCal(InputWorkspace='__bkg',
                               Filename=self.getProperty("DetCal").value)
            MaskDetectors(Workspace='__bkg', MaskedWorkspace='__sa')
            ConvertUnits(InputWorkspace='__bkg',
                         OutputWorkspace='__bkg',
                         Target='Momentum')
            CropWorkspace(InputWorkspace='__bkg',
                          OutputWorkspace='__bkg',
                          XMin=XMin,
                          XMax=XMax)

        progress = Progress(
            self, 0.0, 1.0,
            len(UBList) * len(self.getProperty("Filename").value))

        for run in self.getProperty("Filename").value:
            logger.notice("Working on " + run)

            Load(Filename=run,
                 OutputWorkspace='__run',
                 FilterByTofMin=self.getProperty("FilterByTofMin").value,
                 FilterByTofMax=self.getProperty("FilterByTofMax").value)
            if _load_inst:
                LoadInstrument(
                    Workspace='__run',
                    Filename=self.getProperty("LoadInstrument").value,
                    RewriteSpectraMap=False)
            if _detcal:
                LoadIsawDetCal(InputWorkspace='__run',
                               Filename=self.getProperty("DetCal").value)
            MaskDetectors(Workspace='__run', MaskedWorkspace='__sa')
            ConvertUnits(InputWorkspace='__run',
                         OutputWorkspace='__run',
                         Target='Momentum')
            CropWorkspace(InputWorkspace='__run',
                          OutputWorkspace='__run',
                          XMin=XMin,
                          XMax=XMax)

            if self.getProperty('SetGoniometer').value:
                SetGoniometer(
                    Workspace='__run',
                    Goniometers=self.getProperty('Goniometers').value,
                    Axis0=self.getProperty('Axis0').value,
                    Axis1=self.getProperty('Axis1').value,
                    Axis2=self.getProperty('Axis2').value)

            # Set background Goniometer to be the same as data
            if _background:
                mtd['__bkg'].run().getGoniometer().setR(
                    mtd['__run'].run().getGoniometer().getR())

            for ub in UBList:
                SetUB(Workspace='__run', UB=ub)
                ConvertToMD(InputWorkspace='__run',
                            OutputWorkspace='__md',
                            QDimensions='Q3D',
                            dEAnalysisMode='Elastic',
                            Q3DFrames='HKL',
                            QConversionScales='HKL',
                            Uproj=self.getProperty('Uproj').value,
                            Vproj=self.getProperty('Vproj').value,
                            Wproj=self.getProperty('wproj').value,
                            MinValues=MinValues,
                            MaxValues=MaxValues)
                MDNormSCD(
                    InputWorkspace=mtd['__md'],
                    FluxWorkspace='__flux',
                    SolidAngleWorkspace='__sa',
                    OutputWorkspace='__data',
                    SkipSafetyCheck=True,
                    TemporaryDataWorkspace='__data'
                    if mtd.doesExist('__data') else None,
                    OutputNormalizationWorkspace='__norm',
                    TemporaryNormalizationWorkspace='__norm'
                    if mtd.doesExist('__norm') else None,
                    AlignedDim0=mtd['__md'].getDimension(0).name + AlignedDim0,
                    AlignedDim1=mtd['__md'].getDimension(1).name + AlignedDim1,
                    AlignedDim2=mtd['__md'].getDimension(2).name + AlignedDim2)
                DeleteWorkspace('__md')

                if _background:
                    SetUB(Workspace='__bkg', UB=ub)
                    ConvertToMD(InputWorkspace='__bkg',
                                OutputWorkspace='__bkg_md',
                                QDimensions='Q3D',
                                dEAnalysisMode='Elastic',
                                Q3DFrames='HKL',
                                QConversionScales='HKL',
                                Uproj=self.getProperty('Uproj').value,
                                Vproj=self.getProperty('Vproj').value,
                                Wproj=self.getProperty('Wproj').value,
                                MinValues=MinValues,
                                MaxValues=MaxValues)
                    MDNormSCD(
                        InputWorkspace='__bkg_md',
                        FluxWorkspace='__flux',
                        SolidAngleWorkspace='__sa',
                        SkipSafetyCheck=True,
                        OutputWorkspace='__bkg_data',
                        TemporaryDataWorkspace='__bkg_data'
                        if mtd.doesExist('__bkg_data') else None,
                        OutputNormalizationWorkspace='__bkg_norm',
                        TemporaryNormalizationWorkspace='__bkg_norm'
                        if mtd.doesExist('__bkg_norm') else None,
                        AlignedDim0=mtd['__bkg_md'].getDimension(0).name +
                        AlignedDim0,
                        AlignedDim1=mtd['__bkg_md'].getDimension(1).name +
                        AlignedDim1,
                        AlignedDim2=mtd['__bkg_md'].getDimension(2).name +
                        AlignedDim2)
                    DeleteWorkspace('__bkg_md')
                progress.report()
            DeleteWorkspace('__run')

        if _background:
            # outWS = data / norm - bkg_data / bkg_norm * BackgroundScale
            DivideMD(LHSWorkspace='__data',
                     RHSWorkspace='__norm',
                     OutputWorkspace=_outWS_name + '_normalizedData')
            DivideMD(LHSWorkspace='__bkg_data',
                     RHSWorkspace='__bkg_norm',
                     OutputWorkspace=_outWS_name + '_normalizedBackground')
            CreateSingleValuedWorkspace(
                OutputWorkspace='__scale',
                DataValue=self.getProperty('BackgroundScale').value)
            MultiplyMD(LHSWorkspace=_outWS_name + '_normalizedBackground',
                       RHSWorkspace='__scale',
                       OutputWorkspace='__scaled_background')
            DeleteWorkspace('__scale')
            MinusMD(LHSWorkspace=_outWS_name + '_normalizedData',
                    RHSWorkspace='__scaled_background',
                    OutputWorkspace=_outWS_name)
            if self.getProperty('KeepTemporaryWorkspaces').value:
                RenameWorkspaces(InputWorkspaces=[
                    '__data', '__norm', '__bkg_data', '__bkg_norm'
                ],
                                 WorkspaceNames=[
                                     _outWS_name + '_data',
                                     _outWS_name + '_normalization',
                                     _outWS_name + '_background_data',
                                     _outWS_name + '_background_normalization'
                                 ])
        else:
            # outWS = data / norm
            DivideMD(LHSWorkspace='__data',
                     RHSWorkspace='__norm',
                     OutputWorkspace=_outWS_name)
            if self.getProperty('KeepTemporaryWorkspaces').value:
                RenameWorkspaces(InputWorkspaces=['__data', '__norm'],
                                 WorkspaceNames=[
                                     _outWS_name + '_data',
                                     _outWS_name + '_normalization'
                                 ])

        self.setProperty("OutputWorkspace", mtd[_outWS_name])

        # remove temp workspaces
        [
            DeleteWorkspace(ws) for ws in self.temp_workspace_list
            if mtd.doesExist(ws)
        ]
예제 #40
0
    def PyExec(self):
        from IndirectCommon import (convertToElasticQ,
                                    transposeFitParametersTable)

        setup_prog = Progress(self, start=0.0, end=0.1, nreports=4)
        setup_prog.report('generating output name')
        output_workspace = self._fit_group_name
        # check if the naming convention used is alreay correct
        chopped_name = self._fit_group_name.split('_')
        if 'WORKSPACE' in chopped_name[-1].upper():
            output_workspace = ('_').join(chopped_name[:-1])

        option = self._fit_type[:-2]
        logger.information('Option: ' + option)
        logger.information('Function: ' + self._function)

        setup_prog.report('Cropping workspace')
        #prepare input workspace for fitting
        tmp_fit_workspace = "__Iqtfit_fit_ws"
        if self._spec_max is None:
            CropWorkspace(InputWorkspace=self._input_ws,
                          OutputWorkspace=tmp_fit_workspace,
                          XMin=self._start_x,
                          XMax=self._end_x,
                          StartWorkspaceIndex=self._spec_min)
        else:
            CropWorkspace(InputWorkspace=self._input_ws,
                          OutputWorkspace=tmp_fit_workspace,
                          XMin=self._start_x,
                          XMax=self._end_x,
                          StartWorkspaceIndex=self._spec_min,
                          EndWorkspaceIndex=self._spec_max)

        setup_prog.report('Converting to Histogram')
        ConvertToHistogram(tmp_fit_workspace,
                           OutputWorkspace=tmp_fit_workspace)
        setup_prog.report('Convert to Elastic Q')
        convertToElasticQ(tmp_fit_workspace)

        #fit multi-domian functino to workspace
        fit_prog = Progress(self, start=0.1, end=0.8, nreports=2)
        multi_domain_func, kwargs = self._create_mutli_domain_func(
            self._function, tmp_fit_workspace)
        fit_prog.report('Fitting...')
        Fit(Function=multi_domain_func,
            InputWorkspace=tmp_fit_workspace,
            WorkspaceIndex=0,
            Output=output_workspace,
            CreateOutput=True,
            Minimizer=self._minimizer,
            MaxIterations=self._max_iterations,
            **kwargs)
        fit_prog.report('Fitting complete')

        conclusion_prog = Progress(self, start=0.8, end=1.0, nreports=5)
        conclusion_prog.report('Renaming workspaces')
        # rename workspaces to match user input
        if output_workspace + "_Workspaces" != self._fit_group_name:
            RenameWorkspace(InputWorkspace=output_workspace + "_Workspaces",
                            OutputWorkspace=self._fit_group_name)
        if output_workspace + "_Parameters" != self._parameter_name:
            RenameWorkspace(InputWorkspace=output_workspace + "_Parameters",
                            OutputWorkspace=self._parameter_name)

        conclusion_prog.report('Tansposing parameter table')
        transposeFitParametersTable(self._parameter_name)

        #set first column of parameter table to be axis values
        x_axis = mtd[tmp_fit_workspace].getAxis(1)
        axis_values = x_axis.extractValues()
        for i, value in enumerate(axis_values):
            mtd[self._parameter_name].setCell('axis-1', i, value)

        #convert parameters to matrix workspace
        parameter_names = 'A0,Intensity,Tau,Beta'
        conclusion_prog.report('Processing indirect fit parameters')
        result_workspace = ProcessIndirectFitParameters(
            InputWorkspace=self._parameter_name,
            ColumnX="axis-1",
            XAxisUnit="MomentumTransfer",
            ParameterNames=parameter_names,
            OutputWorkspace=self._result_name)

        # create and add sample logs
        sample_logs = {
            'start_x': self._start_x,
            'end_x': self._end_x,
            'fit_type': self._fit_type[:-2],
            'intensities_constrained': self._intensities_constrained,
            'beta_constrained': True
        }

        conclusion_prog.report('Copying sample logs')
        CopyLogs(InputWorkspace=self._input_ws,
                 OutputWorkspace=result_workspace)
        CopyLogs(InputWorkspace=self._input_ws,
                 OutputWorkspace=self._fit_group_name)

        log_names = [item for item in sample_logs]
        log_values = [sample_logs[item] for item in sample_logs]
        conclusion_prog.report('Adding sample logs')
        AddSampleLogMultiple(Workspace=result_workspace,
                             LogNames=log_names,
                             LogValues=log_values)
        AddSampleLogMultiple(Workspace=self._fit_group_name,
                             LogNames=log_names,
                             LogValues=log_values)

        DeleteWorkspace(tmp_fit_workspace)

        self.setProperty('OutputResultWorkspace', result_workspace)
        self.setProperty('OutputParameterWorkspace', self._parameter_name)
        self.setProperty('OutputWorkspaceGroup', self._fit_group_name)
        conclusion_prog.report('Algorithm complete')
예제 #41
0
class PowderILLDetectorScan(DataProcessorAlgorithm):
    _progress = None
    _height_range = ''
    _mirror = None
    _crop_negative = None
    _out_ws_name = None
    _final_mask = None

    def category(self):
        return "ILL\\Diffraction;Diffraction\\Reduction"

    def summary(self):
        return 'Performs powder diffraction data reduction for D2B and D20 (when doing a detector scan).'

    def seeAlso(self):
        return ["PowderILLParameterScan", "PowderILLEfficiency"]

    def name(self):
        return "PowderILLDetectorScan"

    def validateInputs(self):
        issues = dict()

        if not (self.getProperty("Output2DTubes").value
                or self.getProperty("Output2D").value
                or self.getProperty("Output1D").value):
            issues['Output2DTubes'] = 'No output chosen'
            issues['Output2D'] = 'No output chosen'
            issues['Output1D'] = 'No output chosen'

        if self.getPropertyValue("ComponentsToReduce") and self.getProperty(
                "CropNegativeScatteringAngles").value:
            issues[
                'CropNegativeScatteringAngles'] = 'For component-wise reduction, this has to be unchecked.'

        return issues

    def PyInit(self):

        self.declareProperty(MultipleFileProperty('Run', extensions=['nxs']),
                             doc='File path of run(s).')

        self.declareProperty(
            name='NormaliseTo',
            defaultValue='Monitor',
            validator=StringListValidator(['None', 'Monitor']),
            doc='Normalise to monitor, or skip normalisation.')

        self.declareProperty(FileProperty('CalibrationFile',
                                          '',
                                          action=FileAction.OptionalLoad,
                                          extensions=['nxs']),
                             doc='File containing the detector efficiencies.')

        self.declareProperty(
            name='UseCalibratedData',
            defaultValue=True,
            doc='Whether or not to use the calibrated data in the NeXus files.'
        )

        self.declareProperty(
            name='Output2DTubes',
            defaultValue=False,
            doc=
            'Output a 2D workspace of height along tube against tube scattering angle.'
        )

        self.declareProperty(
            name='Output2D',
            defaultValue=False,
            doc=
            'Output a 2D workspace of height along tube against the real scattering angle.'
        )

        self.declareProperty(
            name='Output1D',
            defaultValue=True,
            doc='Output a 1D workspace with counts against scattering angle.')

        self.declareProperty(
            name='CropNegativeScatteringAngles',
            defaultValue=True,
            doc='Whether or not to crop the negative scattering angles.')

        self.declareProperty(
            FloatArrayProperty(name='HeightRange',
                               values=[],
                               validator=CompositeValidator([
                                   FloatArrayOrderedPairsValidator(),
                                   FloatArrayLengthValidator(0, 2)
                               ])),
            doc=
            'A pair of values, comma separated, to give the minimum and maximum height range (in m). If not specified '
            'the full height range is used.')

        self.declareProperty(
            WorkspaceGroupProperty('OutputWorkspace',
                                   '',
                                   direction=Direction.Output),
            doc='Output workspace containing the reduced data.')

        self.declareProperty(
            name='InitialMask',
            defaultValue=20,
            validator=IntBoundedValidator(lower=0, upper=64),
            doc=
            'Number of pixels to mask from the bottom and the top of each tube before superposition.'
        )

        self.declareProperty(
            name='FinalMask',
            defaultValue=30,
            validator=IntBoundedValidator(lower=0, upper=70),
            doc=
            'Number of spectra to mask from the bottom and the top of the result of 2D options.'
        )

        self.declareProperty(
            name='ComponentsToMask',
            defaultValue='',
            doc=
            'Comma separated list of component names to mask, for instance: tube_1, tube_2'
        )

        self.declareProperty(
            name='ComponentsToReduce',
            defaultValue='',
            doc=
            'Comma separated list of component names to output the reduced data for; for example tube_1'
        )

        self.declareProperty(
            name='AlignTubes',
            defaultValue=True,
            doc='Align the tubes vertically and horizontally according to IPF.'
        )

    def _generate_mask(self, n_pix, instrument):
        """
        Generates the DetectorList input for MaskDetectors
        Masks the bottom and top n_pix pixels in each tube
        @param n_pix : Number of pixels to mask from top and bottom of each tube
        @param instrument : Instrument
        @return the DetectorList string
        """
        mask = ''
        det = instrument.getComponentByName('detectors')
        tube = instrument.getComponentByName('tube_1')
        n_tubes = det.nelements()
        n_pixels = tube.nelements()
        for tube in range(n_tubes):
            start_bottom = tube * n_pixels + 1
            end_bottom = start_bottom + n_pix - 1
            start_top = (tube + 1) * n_pixels - n_pix + 1
            end_top = start_top + n_pix - 1
            mask += str(start_bottom) + '-' + str(end_bottom) + ','
            mask += str(start_top) + '-' + str(end_top) + ','
        self.log().debug('Preparing to mask with DetectorList=' + mask[:-1])
        return mask[:-1]

    def _validate_instrument(self, instrument_name):

        supported_instruments = ['D2B', 'D20']
        if instrument_name not in supported_instruments:
            self.log.warning(
                'Running for unsupported instrument, use with caution. Supported instruments are: '
                + str(supported_instruments))
        if instrument_name == 'D20':
            if self.getProperty('Output2DTubes').value:
                raise RuntimeError(
                    'Output2DTubes is not supported for D20 (1D detector)')
            if self.getProperty('Output2D').value:
                raise RuntimeError(
                    'Output2D is not supported for D20 (1D detector)')

    def _reduce_1D(self, input_group):
        output1D = SumOverlappingTubes(
            InputWorkspaces=input_group,
            OutputType='1D',
            HeightAxis=self._height_range,
            MirrorScatteringAngles=self._mirror,
            CropNegativeScatteringAngles=self._crop_negative,
            OutputWorkspace=self._out_ws_name + '_1D')
        return output1D

    def _reduce_2DTubes(self, input_group):
        output2DtubesName = self._out_ws_name + '_2DTubes'
        output2DTubes = SumOverlappingTubes(
            InputWorkspaces=input_group,
            OutputType='2DTubes',
            HeightAxis=self._height_range,
            MirrorScatteringAngles=self._mirror,
            CropNegativeScatteringAngles=self._crop_negative,
            OutputWorkspace=output2DtubesName)
        if self._final_mask != 0:
            nSpec = mtd[output2DtubesName].getNumberHistograms()
            mask_list = '0-{0},{1}-{2}'.format(self._final_mask,
                                               nSpec - self._final_mask,
                                               nSpec - 1)
            MaskDetectors(Workspace=output2DtubesName,
                          WorkspaceIndexList=mask_list)

        return output2DTubes

    def _reduce_2D(self, input_group):
        output2DName = self._out_ws_name + '_2D'
        output2D = SumOverlappingTubes(
            InputWorkspaces=input_group,
            OutputType='2D',
            HeightAxis=self._height_range,
            MirrorScatteringAngles=self._mirror,
            CropNegativeScatteringAngles=self._crop_negative,
            OutputWorkspace=output2DName)
        if self._final_mask != 0:
            nSpec = mtd[output2DName].getNumberHistograms()
            mask_list = '0-{0},{1}-{2}'.format(self._final_mask,
                                               nSpec - self._final_mask,
                                               nSpec - 1)
            MaskDetectors(Workspace=output2DName, WorkspaceIndexList=mask_list)

        return output2D

    def PyExec(self):
        data_type = 'Raw'
        if self.getProperty('UseCalibratedData').value:
            data_type = 'Calibrated'
        align_tubes = self.getProperty('AlignTubes').value

        self._progress = Progress(self, start=0.0, end=1.0, nreports=6)
        self._progress.report('Loading data')
        # Do not merge the runs yet, since it will break the calibration
        # Load and calibrate separately, then SumOverlappingTubes will merge correctly
        # Besides + here does not make sense, and it will also slow down D2B a lot
        input_workspace = LoadAndMerge(
            Filename=self.getPropertyValue('Run').replace('+', ','),
            LoaderName='LoadILLDiffraction',
            LoaderOptions={
                'DataType': data_type,
                'AlignTubes': align_tubes
            })
        # We might already have a group, but group just in case
        input_group = GroupWorkspaces(InputWorkspaces=input_workspace)

        instrument = input_group[0].getInstrument()
        instrument_name = instrument.getName()
        self._validate_instrument(instrument_name)

        self._progress.report('Normalising to monitor')
        if self.getPropertyValue('NormaliseTo') == 'Monitor':
            input_group = NormaliseToMonitor(InputWorkspace=input_group,
                                             MonitorID=0)
            if instrument_name == 'D2B':
                input_group = Scale(InputWorkspace=input_group, Factor=1e+6)

        calib_file = self.getPropertyValue('CalibrationFile')
        if calib_file:
            self._progress.report('Applying detector efficiencies')
            LoadNexusProcessed(Filename=calib_file,
                               OutputWorkspace='__det_eff')
            for ws in input_group:
                name = ws.getName()
                ExtractMonitors(InputWorkspace=name, DetectorWorkspace=name)
                ApplyDetectorScanEffCorr(
                    InputWorkspace=name,
                    DetectorEfficiencyWorkspace='__det_eff',
                    OutputWorkspace=name)

        pixels_to_mask = self.getProperty('InitialMask').value
        if pixels_to_mask != 0:
            mask = self._generate_mask(pixels_to_mask,
                                       input_group[0].getInstrument())
            for ws in input_group:
                MaskDetectors(Workspace=ws, DetectorList=mask)
        components_to_mask = self.getPropertyValue('ComponentsToMask')
        if components_to_mask:
            for ws in input_group:
                MaskDetectors(Workspace=ws, ComponentList=components_to_mask)

        height_range_prop = self.getProperty('HeightRange').value
        if len(height_range_prop) == 0:
            run = mtd["input_group"].getItem(0).getRun()
            if run.hasProperty("PixelHeight") and run.hasProperty("MaxHeight"):
                pixelHeight = run.getLogData("PixelHeight").value
                maxHeight = run.getLogData("MaxHeight").value
                self._height_range = str(-maxHeight) + ',' + str(
                    pixelHeight) + ',' + str(maxHeight)
        elif len(height_range_prop) == 2:
            self._height_range = str(height_range_prop[0]) + ', ' + str(
                height_range_prop[1])
        output_workspaces = []
        self._out_ws_name = self.getPropertyValue('OutputWorkspace')
        self._mirror = False
        self._crop_negative = self.getProperty(
            'CropNegativeScatteringAngles').value
        if instrument.hasParameter("mirror_scattering_angles"):
            self._mirror = instrument.getBoolParameter(
                "mirror_scattering_angles")[0]
        self._final_mask = self.getProperty('FinalMask').value

        components = self.getPropertyValue('ComponentsToReduce')
        if components:
            for ws in input_group:
                CropToComponent(InputWorkspace=ws.getName(),
                                OutputWorkspace=ws.getName(),
                                ComponentNames=components)

        self._progress.report('Doing Output2DTubes Option')
        if self.getProperty('Output2DTubes').value:
            output2DTubes = self._reduce_2DTubes(input_group)
            output_workspaces.append(output2DTubes)

        self._progress.report('Doing Output2D Option')
        if self.getProperty('Output2D').value:
            output2D = self._reduce_2D(input_group)
            output_workspaces.append(output2D)

        self._progress.report('Doing Output1D Option')
        if self.getProperty('Output1D').value:
            output1D = self._reduce_1D(input_group)
            output_workspaces.append(output1D)

        self._progress.report('Finishing up...')
        DeleteWorkspace('input_group')
        GroupWorkspaces(InputWorkspaces=output_workspaces,
                        OutputWorkspace=self._out_ws_name)
        self.setProperty('OutputWorkspace', self._out_ws_name)
예제 #42
0
    def PyExec(self):

        # 0) Create reporter to report progress
        steps = 9
        begin = 0
        end = 1.0
        prog_reporter = Progress(self, begin, end, steps)

        # 1) get input parameters from a user
        self._get_properties()
        prog_reporter.report("Input data from the user has been collected.")

        # 2) read ab initio data
        ab_initio_loaders = {
            "CASTEP": AbinsModules.LoadCASTEP,
            "CRYSTAL": AbinsModules.LoadCRYSTAL,
            "DMOL3": AbinsModules.LoadDMOL3,
            "GAUSSIAN": AbinsModules.LoadGAUSSIAN
        }
        rdr = ab_initio_loaders[self._ab_initio_program](
            input_ab_initio_filename=self._vibrational_or_phonon_data_file)
        ab_initio_data = rdr.get_formatted_data()
        prog_reporter.report("Vibrational/phonon data has been read.")

        # 3) calculate S
        s_calculator = AbinsModules.CalculateS.init(
            filename=self._vibrational_or_phonon_data_file,
            temperature=self._temperature,
            sample_form=self._sample_form,
            abins_data=ab_initio_data,
            instrument=self._instrument,
            quantum_order_num=self._num_quantum_order_events,
            bin_width=self._bin_width)
        s_data = s_calculator.get_formatted_data()
        prog_reporter.report(
            "Dynamical structure factors have been determined.")

        # 4) get atoms for which S should be plotted
        self._extracted_ab_initio_data = ab_initio_data.get_atoms_data(
        ).extract()
        num_atoms = len(self._extracted_ab_initio_data)
        all_atms_smbls = list(
            set([
                self._extracted_ab_initio_data["atom_%s" % atom]["symbol"]
                for atom in range(num_atoms)
            ]))
        all_atms_smbls.sort()

        if len(self._atoms) == 0:  # case: all atoms
            atoms_symbol = all_atms_smbls
        else:  # case selected atoms
            if len(self._atoms) != len(set(
                    self._atoms)):  # only different types
                raise ValueError("Not all user defined atoms are unique.")

            for atom_symbol in self._atoms:
                if atom_symbol not in all_atms_smbls:
                    raise ValueError(
                        "User defined atom not present in the system.")
            atoms_symbol = self._atoms
        prog_reporter.report(
            "Atoms, for which dynamical structure factors should be plotted, have been determined."
        )

        # at the moment only types of atom, e.g, for  benzene three options -> 1) C, H;  2) C; 3) H
        # 5) create workspaces for atoms in interest
        workspaces = []
        if self._sample_form == "Powder":
            workspaces.extend(
                self._create_partial_s_per_type_workspaces(
                    atoms_symbols=atoms_symbol, s_data=s_data))
        prog_reporter.report(
            "Workspaces with partial dynamical structure factors have been constructed."
        )

        # 6) Create a workspace with sum of all atoms if required
        if self._sum_contributions:
            total_atom_workspaces = []
            for ws in workspaces:
                if "total" in ws:
                    total_atom_workspaces.append(ws)
            total_workspace = self._create_total_workspace(
                partial_workspaces=total_atom_workspaces)
            workspaces.insert(0, total_workspace)
            prog_reporter.report(
                "Workspace with total S  has been constructed.")

        # 7) add experimental data if available to the collection of workspaces
        if self._experimental_file != "":
            workspaces.insert(
                0,
                self._create_experimental_data_workspace().name())
            prog_reporter.report(
                "Workspace with the experimental data has been constructed.")

        GroupWorkspaces(InputWorkspaces=workspaces,
                        OutputWorkspace=self._out_ws_name)

        # 8) save workspaces to ascii_file
        num_workspaces = mtd[self._out_ws_name].getNumberOfEntries()
        for wrk_num in range(num_workspaces):
            wrk = mtd[self._out_ws_name].getItem(wrk_num)
            SaveAscii(InputWorkspace=Scale(wrk, 1.0 / self._bin_width,
                                           "Multiply"),
                      Filename=wrk.name() + ".dat",
                      Separator="Space",
                      WriteSpectrumID=False)
        prog_reporter.report("All workspaces have been saved to ASCII files.")

        # 9) set  OutputWorkspace
        self.setProperty('OutputWorkspace', self._out_ws_name)
        prog_reporter.report(
            "Group workspace with all required  dynamical structure factors has been constructed."
        )
예제 #43
0
    def PyExec(self):
        if not self._use_corrections:
            logger.information('Not using corrections')
        if not self._use_can:
            logger.information('Not using container')

        prog_container = Progress(self, start=0.0, end=0.2, nreports=4)
        prog_container.report('Starting algorithm')

        # Units should be wavelength
        sample_unit = self._sample_workspace.getAxis(0).getUnit().unitID()
        sample_ws_wavelength = self._convert_units_wavelength(
            self._sample_workspace)

        container_ws_wavelength = (self._process_container_workspace(
            self._container_workspace, prog_container)
                                   if self._use_can else None)

        prog_corr = Progress(self, start=0.2, end=0.6, nreports=2)
        if self._use_corrections:
            prog_corr.report('Preprocessing corrections')

            if self._use_can:
                # Use container factors
                prog_corr.report('Correcting sample and container')
                factor_workspaces = self._get_factor_workspaces()
                output_workspace = self._correct_sample_can(
                    sample_ws_wavelength, container_ws_wavelength,
                    factor_workspaces)
                correction_type = 'sample_and_can_corrections'
            else:
                # Use sample factor only
                output_workspace = self._correct_sample(
                    sample_ws_wavelength, self._corrections_workspace[0])
                correction_type = 'sample_corrections_only'
                # Add corrections filename to log values
                prog_corr.report('Correcting sample')
                s_api.AddSampleLog(Workspace=output_workspace,
                                   LogName='corrections_filename',
                                   LogType='String',
                                   LogText=self._corrections_ws_name)
        else:
            # Do simple subtraction
            output_workspace = self._subtract(sample_ws_wavelength,
                                              container_ws_wavelength)
            correction_type = 'can_subtraction'
            # Add container filename to log values
            can_base = self.getPropertyValue("CanWorkspace")
            can_base = can_base[:can_base.index('_')]
            prog_corr.report('Adding container filename')
            s_api.AddSampleLog(Workspace=output_workspace,
                               LogName='container_filename',
                               LogType='String',
                               LogText=can_base)

        prog_wrkflow = Progress(self, 0.6, 1.0, nreports=5)
        # Record the container scale factor
        if self._use_can and self._scale_can:
            prog_wrkflow.report('Adding container scaling')
            s_api.AddSampleLog(Workspace=output_workspace,
                               LogName='container_scale',
                               LogType='Number',
                               LogText=str(self._can_scale_factor))

        # Record the container shift amount
        if self._use_can and self._shift_can:
            prog_wrkflow.report('Adding container shift')
            s_api.AddSampleLog(Workspace=output_workspace,
                               LogName='container_shift',
                               LogType='Number',
                               LogText=str(self._can_shift_factor))

        # Record the type of corrections applied
        prog_wrkflow.report('Adding correction type')
        s_api.AddSampleLog(Workspace=output_workspace,
                           LogName='corrections_type',
                           LogType='String',
                           LogText=correction_type)

        # Add original sample as log entry
        sam_base = self.getPropertyValue("SampleWorkspace")

        if '_' in sam_base:
            sam_base = sam_base[:sam_base.index('_')]
            prog_wrkflow.report('Adding sample filename')
            s_api.AddSampleLog(Workspace=output_workspace,
                               LogName='sample_filename',
                               LogType='String',
                               LogText=sam_base)

        # Convert Units back to original
        emode = str(output_workspace.getEMode())
        efixed = 0.0
        if emode == "Indirect":
            efixed = self._get_e_fixed(output_workspace)
        output_workspace = self._convert_units(output_workspace, sample_unit,
                                               emode, efixed)

        self.setProperty('OutputWorkspace', output_workspace)
        prog_wrkflow.report('Algorithm Complete')
    def PyExec(self):

        # Set up progress reporting
        n_prog_reports = 2
        if self._can_ws_name is not None:
            n_prog_reports += 1
        prog = Progress(self, 0.0, 1.0, n_prog_reports)

        sample_wave_ws = '__sam_wave'
        convert_unit_alg = self.createChildAlgorithm("ConvertUnits",
                                                     enableLogging=False)
        convert_unit_alg.setProperty("InputWorkspace", self._sample_ws_name)
        convert_unit_alg.setProperty("OutputWorkspace", sample_wave_ws)
        convert_unit_alg.setProperty("Target", 'Wavelength')
        convert_unit_alg.setProperty("EMode", self._emode)
        convert_unit_alg.setProperty("EFixed", self._efixed)
        convert_unit_alg.execute()
        mtd.addOrReplace(sample_wave_ws,
                         convert_unit_alg.getProperty("OutputWorkspace").value)

        prog.report('Calculating sample corrections')
        SetBeam(sample_wave_ws,
                Geometry={
                    'Shape': 'Slit',
                    'Width': self._beam_width,
                    'Height': self._beam_height
                })

        if self._sample_density_type == 'Mass Density':
            sample_mat_list = {
                'ChemicalFormula': self._sample_chemical_formula,
                'SampleMassDensity': self._sample_density
            }
        if self._sample_density_type == 'Number Density':
            sample_mat_list = {
                'ChemicalFormula': self._sample_chemical_formula,
                'SampleNumberDensity': self._sample_density
            }

        SetSample(sample_wave_ws,
                  Geometry={
                      'Shape': 'Cylinder',
                      'Height': self._sample_height,
                      'Radius': self._sample_radius,
                      'Center': [0., 0., 0.]
                  },
                  Material=sample_mat_list)

        prog.report('Calculating sample corrections')
        MonteCarloAbsorption(InputWorkspace=sample_wave_ws,
                             OutputWorkspace=self._ass_ws,
                             EventsPerPoint=self._events,
                             NumberOfWavelengthPoints=self._number_wavelengths,
                             Interpolation='CSpline')

        group = self._ass_ws

        delete_alg = self.createChildAlgorithm("DeleteWorkspace",
                                               enableLogging=False)
        divide_alg = self.createChildAlgorithm("Divide", enableLogging=False)
        minus_alg = self.createChildAlgorithm("Minus", enableLogging=False)

        if self._can_ws_name is not None:
            can_wave_ws = '__can_wave'
            convert_unit_alg.setProperty("InputWorkspace", self._can_ws_name)
            convert_unit_alg.setProperty("OutputWorkspace", can_wave_ws)
            convert_unit_alg.setProperty("Target", 'Wavelength')
            convert_unit_alg.setProperty("EMode", self._emode)
            convert_unit_alg.setProperty("EFixed", self._efixed)
            convert_unit_alg.execute()
            mtd.addOrReplace(
                can_wave_ws,
                convert_unit_alg.getProperty("OutputWorkspace").value)

            if self._can_scale != 1.0:
                logger.information('Scaling can by: %s' % self._can_scale)
                scale_alg = self.createChildAlgorithm("Scale",
                                                      enableLogging=False)
                scale_alg.setProperty("InputWorkspace", can_wave_ws)
                scale_alg.setProperty("OutputWorkspace", can_wave_ws)
                scale_alg.setProperty("Factor", self._can_scale)
                scale_alg.setProperty("Operation", 'Multiply')
                scale_alg.execute()

            can_thickness = self._can_radius - self._sample_radius
            logger.information('Container thickness: ' + str(can_thickness))

            if self._use_can_corrections:
                # Doing can corrections
                prog.report('Calculating container corrections')
                divide_alg.setProperty("LHSWorkspace", sample_wave_ws)
                divide_alg.setProperty("RHSWorkspace", self._ass_ws)
                divide_alg.setProperty("OutputWorkspace", sample_wave_ws)
                divide_alg.execute()

                if self._sample_density_type == 'Mass Density':
                    container_mat_list = {
                        'ChemicalFormula': self._can_chemical_formula,
                        'SampleMassDensity': self._can_density
                    }
                if self._sample_density_type == 'Number Density':
                    container_mat_list = {
                        'ChemicalFormula': self._can_chemical_formula,
                        'SampleNumberDensity': self._can_density
                    }

                SetSample(can_wave_ws,
                          Geometry={
                              'Shape': 'HollowCylinder',
                              'Height': self._sample_height,
                              'InnerRadius': self._sample_radius,
                              'OuterRadius': self._can_radius,
                              'Center': [0., 0., 0.]
                          },
                          Material=container_mat_list)

                MonteCarloAbsorption(
                    InputWorkspace=can_wave_ws,
                    OutputWorkspace=self._acc_ws,
                    EventsPerPoint=self._events,
                    NumberOfWavelengthPoints=self._number_wavelengths,
                    Interpolation='CSpline')

                divide_alg.setProperty("LHSWorkspace", can_wave_ws)
                divide_alg.setProperty("RHSWorkspace", self._acc_ws)
                divide_alg.setProperty("OutputWorkspace", can_wave_ws)
                divide_alg.execute()
                minus_alg.setProperty("LHSWorkspace", sample_wave_ws)
                minus_alg.setProperty("RHSWorkspace", can_wave_ws)
                minus_alg.setProperty("OutputWorkspace", sample_wave_ws)
                minus_alg.execute()
                group += ',' + self._acc_ws

            else:
                # Doing simple can subtraction
                prog.report('Calculating container scaling')
                minus_alg.setProperty("LHSWorkspace", sample_wave_ws)
                minus_alg.setProperty("RHSWorkspace", can_wave_ws)
                minus_alg.setProperty("OutputWorkspace", sample_wave_ws)
                minus_alg.execute()
                divide_alg.setProperty("LHSWorkspace", sample_wave_ws)
                divide_alg.setProperty("RHSWorkspace", self._ass_ws)
                divide_alg.setProperty("OutputWorkspace", sample_wave_ws)
                divide_alg.execute()

            delete_alg.setProperty("Workspace", can_wave_ws)
            delete_alg.execute()

        else:
            divide_alg.setProperty("LHSWorkspace", sample_wave_ws)
            divide_alg.setProperty("RHSWorkspace", self._ass_ws)
            divide_alg.setProperty("OutputWorkspace", sample_wave_ws)
            divide_alg.execute()

        convert_unit_alg.setProperty("InputWorkspace", sample_wave_ws)
        convert_unit_alg.setProperty("OutputWorkspace", self._output_ws)
        convert_unit_alg.setProperty("Target", 'DeltaE')
        convert_unit_alg.setProperty("EMode", self._emode)
        convert_unit_alg.setProperty("EFixed", self._efixed)
        convert_unit_alg.execute()
        mtd.addOrReplace(self._output_ws,
                         convert_unit_alg.getProperty("OutputWorkspace").value)
        delete_alg.setProperty("Workspace", sample_wave_ws)
        delete_alg.execute()

        # Record sample logs
        prog.report('Recording sample logs')
        sample_log_workspaces = [self._output_ws, self._ass_ws]
        sample_logs = [('sample_shape', 'cylinder'),
                       ('sample_filename', self._sample_ws_name),
                       ('sample_radius', self._sample_radius)]

        if self._can_ws_name is not None:
            sample_logs.append(('container_filename', self._can_ws_name))
            sample_logs.append(('container_scale', self._can_scale))
            if self._use_can_corrections:
                sample_log_workspaces.append(self._acc_ws)
                sample_logs.append(('container_thickness', can_thickness))

        log_names = [item[0] for item in sample_logs]
        log_values = [item[1] for item in sample_logs]

        add_sample_log_alg = self.createChildAlgorithm("AddSampleLogMultiple",
                                                       enableLogging=False)
        for ws_name in sample_log_workspaces:
            add_sample_log_alg.setProperty("Workspace", ws_name)
            add_sample_log_alg.setProperty("LogNames", log_names)
            add_sample_log_alg.setProperty("LogValues", log_values)
            add_sample_log_alg.execute()

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

        # Output the Abs group workspace if it is wanted, delete if not
        if self._abs_ws == '':
            delete_alg.setProperty("Workspace", self._ass_ws)
            delete_alg.execute()
            if self._can_ws_name is not None and self._use_can_corrections:
                delete_alg.setProperty("Workspace", self._acc_ws)
                delete_alg.execute()

        else:
            GroupWorkspaces(InputWorkspaces=group,
                            OutputWorkspace=self._abs_ws,
                            EnableLogging=False)
            self.setProperty('CorrectionsWorkspace', self._abs_ws)
예제 #45
0
    def _determine_single_crystal_diffraction(self):
        """
        All work related to the determination of the diffraction pattern
        """

        a, b, c = self.getProperty('LatticeSizes').value
        alpha, beta, gamma = self.getProperty('LatticeAngles').value

        u = self.getProperty('VectorU').value
        v = self.getProperty('VectorV').value

        uproj = self.getProperty('Uproj').value
        vproj = self.getProperty('Vproj').value
        wproj = self.getProperty('Wproj').value

        n_bins = self.getProperty('NBins').value
        self._n_bins = (n_bins, n_bins, 1)

        axis0 = '{},0,1,0,1'.format(self.getProperty('PsiAngleLog').value)
        axis1 = '{},0,1,0,1'.format(self.getProperty('PsiOffset').value)

        # Options for SetUB independent of run
        ub_args = dict(a=a,
                       b=b,
                       c=c,
                       alpha=alpha,
                       beta=beta,
                       gamma=gamma,
                       u=u,
                       v=v)
        min_values = None
        # Options for algorithm ConvertToMD independent of run
        convert_to_md_kwargs = dict(QDimensions='Q3D',
                                    dEAnalysisMode='Elastic',
                                    Q3DFrames='HKL',
                                    QConversionScales='HKL',
                                    Uproj=uproj,
                                    Vproj=vproj,
                                    Wproj=wproj)
        md_norm_scd_kwargs = None  # Options for algorithm MDNormSCD

        # Find solid angle and flux
        if self._vanadium_files:
            kwargs = dict(Filename='+'.join(self._vanadium_files),
                          MaskFile=self.getProperty("MaskFile").value,
                          MomentumMin=self._momentum_range[0],
                          MomentumMax=self._momentum_range[1])
            _t_solid_angle, _t_int_flux = \
                MDNormSCDPreprocessIncoherent(**kwargs)
        else:
            _t_solid_angle = self.nominal_solid_angle('_t_solid_angle')
            _t_int_flux = self.nominal_integrated_flux('_t_int_flux')

        # Process a sample at a time
        run_numbers = self._getRuns(self.getProperty("RunNumbers").value,
                                    doIndiv=True)
        run_numbers = list(itertools.chain.from_iterable(run_numbers))
        diffraction_reporter = Progress(self,
                                        start=0.0,
                                        end=1.0,
                                        nreports=len(run_numbers))
        for i_run, run in enumerate(run_numbers):
            _t_sample = self._mask_t0_crop(run, '_t_sample')

            # Set Goniometer and UB matrix
            SetGoniometer(_t_sample, Axis0=axis0, Axis1=axis1)
            SetUB(_t_sample, **ub_args)
            if self._bkg:
                self._bkg.run().getGoniometer().\
                    setR(_t_sample.run().getGoniometer().getR())
                SetUB(self._bkg, **ub_args)
            # Determine limits for momentum transfer in HKL space. Needs to be
            # done only once. We use the first run.
            if min_values is None:
                kwargs = dict(QDimensions='Q3D',
                              dEAnalysisMode='Elastic',
                              Q3DFrames='HKL')
                min_values, max_values = ConvertToMDMinMaxGlobal(
                    _t_sample, **kwargs)
                convert_to_md_kwargs.update({
                    'MinValues': min_values,
                    'MaxValues': max_values
                })

            # Convert to MD
            _t_md = ConvertToMD(_t_sample,
                                OutputWorkspace='_t_md',
                                **convert_to_md_kwargs)
            if self._bkg:
                _t_bkg_md = ConvertToMD(self._bkg,
                                        OutputWorkspace='_t_bkg_md',
                                        **convert_to_md_kwargs)

            # Determine aligned dimensions. Need to be done only once
            if md_norm_scd_kwargs is None:
                aligned = list()
                for i_dim in range(3):
                    kwargs = {
                        'name': _t_md.getDimension(i_dim).name,
                        'min': min_values[i_dim],
                        'max': max_values[i_dim],
                        'n_bins': self._n_bins[i_dim]
                    }
                    aligned.append(
                        '{name},{min},{max},{n_bins}'.format(**kwargs))
                md_norm_scd_kwargs = dict(AlignedDim0=aligned[0],
                                          AlignedDim1=aligned[1],
                                          AlignedDim2=aligned[2],
                                          FluxWorkspace=_t_int_flux,
                                          SolidAngleWorkspace=_t_solid_angle,
                                          SkipSafetyCheck=True)

            # Normalize sample by solid angle and integrated flux;
            # Accumulate runs into the temporary workspaces
            MDNormSCD(_t_md,
                      OutputWorkspace='_t_data',
                      OutputNormalizationWorkspace='_t_norm',
                      TemporaryDataWorkspace='_t_data'
                      if mtd.doesExist('_t_data') else None,
                      TemporaryNormalizationWorkspace='_t_norm'
                      if mtd.doesExist('_t_norm') else None,
                      **md_norm_scd_kwargs)
            if self._bkg:
                MDNormSCD(_t_bkg_md,
                          OutputWorkspace='_t_bkg_data',
                          OutputNormalizationWorkspace='_t_bkg_norm',
                          TemporaryDataWorkspace='_t_bkg_data'
                          if mtd.doesExist('_t_bkg_data') else None,
                          TemporaryNormalizationWorkspace='_t_bkg_norm'
                          if mtd.doesExist('_t_bkg_norm') else None,
                          **md_norm_scd_kwargs)
            message = 'Processing sample {} of {}'.\
                format(i_run+1, len(run_numbers))
            diffraction_reporter.report(message)
        self._temps.workspaces.append('PreprocessedDetectorsWS')  # to remove
        # Iteration over the sample runs is done.

        # Division by vanadium, subtract background, and rename workspaces
        name = self.getPropertyValue("OutputWorkspace")
        _t_data = DivideMD(LHSWorkspace='_t_data', RHSWorkspace='_t_norm')
        if self._bkg:
            _t_bkg_data = DivideMD(LHSWorkspace='_t_bkg_data',
                                   RHSWorkspace='_t_bkg_norm')
            _t_scale = CreateSingleValuedWorkspace(DataValue=self._bkg_scale)
            _t_bkg_data = MultiplyMD(_t_bkg_data, _t_scale)
            ws = MinusMD(_t_data, _t_bkg_data)
            RenameWorkspace(_t_data, OutputWorkspace=name + '_dat')
            RenameWorkspace(_t_bkg_data, OutputWorkspace=name + '_bkg')
        else:
            ws = _t_data
        RenameWorkspace(ws, OutputWorkspace=name)
        self.setProperty("OutputWorkspace", ws)
        diffraction_reporter.report(len(run_numbers), 'Done')
예제 #46
0
    def PyExec(self):
        """Executes the data reduction workflow."""
        progress = Progress(self, 0.0, 1.0, 7)
        report = common.Report()
        subalgLogging = self.getProperty(common.PROP_SUBALG_LOGGING).value == common.SUBALG_LOGGING_ON
        wsNamePrefix = self.getProperty(common.PROP_OUTPUT_WS).valueAsStr
        cleanupMode = self.getProperty(common.PROP_CLEANUP_MODE).value
        wsNames = common.NameSource(wsNamePrefix, cleanupMode)
        wsCleanup = common.IntermediateWSCleanup(cleanupMode, subalgLogging)

        progress.report('Loading inputs')
        mainWS = self._inputWS(wsNames, wsCleanup, subalgLogging)

        maskWSName = wsNames.withSuffix('combined_mask')
        maskWS = _createMaskWS(mainWS, maskWSName, subalgLogging)

        reportWS = None
        if not self.getProperty(common.PROP_OUTPUT_DIAGNOSTICS_REPORT_WS).isDefault:
            reportWSName = self.getProperty(common.PROP_OUTPUT_DIAGNOSTICS_REPORT_WS).valueAsStr
            reportWS = _createDiagnosticsReportTable(reportWSName, mainWS.getNumberHistograms(), subalgLogging)

        progress.report('Loading default mask')
        defaultMaskWS = self._defaultMask(mainWS, wsNames, wsCleanup, report, subalgLogging)
        defaultMaskedSpectra = list()
        if defaultMaskWS is not None:
            defaultMaskedSpectra = _reportDefaultMask(reportWS, defaultMaskWS)
            maskWS = Plus(LHSWorkspace=maskWS,
                          RHSWorkspace=defaultMaskWS,
                          EnableLogging=subalgLogging)
            wsCleanup.cleanup(defaultMaskWS)

        progress.report('User-defined mask')
        userMaskWS = self._userMask(mainWS, wsNames, wsCleanup, subalgLogging)
        userMaskedSpectra = _reportUserMask(reportWS, userMaskWS)
        maskWS = Plus(LHSWorkspace=maskWS,
                      RHSWorkspace=userMaskWS,
                      EnableLogging=subalgLogging)
        wsCleanup.cleanup(userMaskWS)

        beamStopMaskedSpectra = list()
        if self._beamStopDiagnosticsEnabled(mainWS, report):
            progress.report('Diagnosing beam stop')
            beamStopMaskWS = self._beamStopDiagnostics(mainWS, wsNames, wsCleanup, report, subalgLogging)
            beamStopMaskedSpectra = _reportBeamStopMask(reportWS, beamStopMaskWS)
            maskWS = Plus(LHSWorkspace=maskWS,
                          RHSWorkspace=beamStopMaskWS,
                          EnableLogging=subalgLogging)
            wsCleanup.cleanup(beamStopMaskWS)

        bkgMaskedSpectra = list()
        if self._bkgDiagnosticsEnabled(mainWS, report):
            progress.report('Diagnosing backgrounds')
            bkgMaskWS, bkgWS = self._bkgDiagnostics(mainWS, maskWS, wsNames, wsCleanup, report, subalgLogging)
            bkgMaskedSpectra = _reportBkgDiagnostics(reportWS, bkgWS, bkgMaskWS)
            maskWS = Plus(LHSWorkspace=maskWS,
                          RHSWorkspace=bkgMaskWS,
                          EnableLogging=subalgLogging)
            wsCleanup.cleanup(bkgMaskWS)
            wsCleanup.cleanup(bkgWS)

        peakMaskedSpectra = list()
        if self._peakDiagnosticsEnabled(mainWS, report):
            progress.report('Diagnosing peaks')
            peakMaskWS, peakIntensityWS = self._peakDiagnostics(mainWS, maskWS, wsNames, wsCleanup, report, subalgLogging)
            peakMaskedSpectra = _reportPeakDiagnostics(reportWS, peakIntensityWS, peakMaskWS)
            maskWS = Plus(LHSWorkspace=maskWS,
                          RHSWorkspace=peakMaskWS,
                          EnableLogging=subalgLogging)
            wsCleanup.cleanup(peakMaskWS)
            wsCleanup.cleanup(peakIntensityWS)

        self._outputReports(reportWS, defaultMaskedSpectra, userMaskedSpectra, beamStopMaskedSpectra,
                            peakMaskedSpectra, bkgMaskedSpectra)

        self._finalize(maskWS, wsCleanup, report)
        progress.report('Done')
예제 #47
0
    def PyExec(self):
        workflow_prog = Progress(self, start=0.0, end=0.3, nreports=4)
        workflow_prog.report('Setting up algorithm')
        self._setup()

        input_ws = mtd[self._sample]

        min_spectrum_index = input_ws.getIndexFromSpectrumNumber(int(self._spectra_range[0]))
        max_spectrum_index = input_ws.getIndexFromSpectrumNumber(int(self._spectra_range[1]))

        # Crop to the required spectra range
        workflow_prog.report('Cropping Workspace')
        cropped_input = ms.CropWorkspace(InputWorkspace=input_ws,
                                         OutputWorkspace='__symm',
                                         StartWorkspaceIndex=min_spectrum_index,
                                         EndWorkspaceIndex=max_spectrum_index)

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

        sample_x = cropped_input.readX(0)

        # Get slice bounds of array
        try:
            workflow_prog.report('Calculating array points')
            self._calculate_array_points(sample_x, sample_array_len)
        except Exception as exc:
            raise RuntimeError('Failed to calculate array slice boundaries: %s' % exc.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

        logger.information('Sample array length = %d' % sample_array_len)

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

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

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

        # Create an empty workspace with enough storage for the new data
        workflow_prog.report('Creating OutputWorkspace')
        out_ws = WorkspaceFactory.Instance().create(cropped_input, cropped_input.getNumberHistograms(),
                                                    int(new_array_len), int(new_array_len))

        # For each spectrum copy positive values to the negative
        pop_prog = Progress(self, start=0.3, end=0.95, nreports=out_ws.getNumberHistograms())
        for idx in range(out_ws.getNumberHistograms()):
            pop_prog.report('Populating data in workspace %i' % idx)
            # Strip any additional array cells
            x_in = cropped_input.readX(idx)[:sample_array_len]
            y_in = cropped_input.readY(idx)[:sample_array_len]
            e_in = cropped_input.readE(idx)[: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
            out_ws.setX(idx, x_out)
            out_ws.setY(idx, y_out)
            out_ws.setE(idx, e_out)

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

        end_prog = Progress(self, start=0.95, end=1.0, nreports=3)
        end_prog.report('Deleting temp workspaces')
        ms.DeleteWorkspace(cropped_input)

        if self._props_output_workspace != '':
            end_prog.report('Generating property table')
            self._generate_props_table()

        self.setProperty('OutputWorkspace', out_ws)
        end_prog.report('Algorithm Complete')
예제 #48
0
    def load_and_group(self, runs: List[str]) -> IMDHistoWorkspace:
        """
        Load the data with given grouping
        """
        # grouping config
        grouping = self.getProperty("Grouping").value
        if grouping == 'None':
            grouping = 1
        else:
            grouping = 2 if grouping == '2x2' else 4
        number_of_runs = len(runs)

        x_dim = 480 * 8 // grouping
        y_dim = 512 // grouping

        data_array = np.empty((number_of_runs, x_dim, y_dim), dtype=np.float64)

        s1_array = []
        duration_array = []
        run_number_array = []
        monitor_count_array = []

        progress = Progress(self, 0.0, 1.0, number_of_runs + 3)

        for n, run in enumerate(runs):
            progress.report('Loading: ' + run)
            with h5py.File(run, 'r') as f:
                bc = np.zeros((512 * 480 * 8), dtype=np.int64)
                for b in range(8):
                    bc += np.bincount(f['/entry/bank' + str(b + 1) +
                                        '_events/event_id'].value,
                                      minlength=512 * 480 * 8)
                bc = bc.reshape((480 * 8, 512))
                if grouping == 2:
                    bc = bc[::2, ::2] + bc[1::2, ::2] + bc[::2,
                                                           1::2] + bc[1::2,
                                                                      1::2]
                elif grouping == 4:
                    bc = bc[::4, ::4] + bc[1::4, ::4] + bc[2::4, ::4] + bc[3::4, ::4] + bc[::4, 1::4] + bc[1::4, 1::4] + bc[2::4, 1::4] + \
                         bc[3::4, 1::4] + bc[::4, 2::4] + bc[1::4, 2::4] + bc[2::4, 2::4] + bc[3::4, 2::4] + bc[::4, 3::4] + \
                         bc[1::4, 3::4] + bc[2::4, 3::4] + bc[3::4, 3::4]
                data_array[n] = bc
                s1_array.append(
                    f['/entry/DASlogs/HB2C:Mot:s1.RBV/average_value'].value[0])
                duration_array.append(float(f['/entry/duration'].value[0]))
                run_number_array.append(float(f['/entry/run_number'].value[0]))
                monitor_count_array.append(
                    float(f['/entry/monitor1/total_counts'].value[0]))

        progress.report('Creating MDHistoWorkspace')
        createWS_alg = self.createChildAlgorithm("CreateMDHistoWorkspace",
                                                 enableLogging=False)
        createWS_alg.setProperty("SignalInput", data_array)
        createWS_alg.setProperty("ErrorInput", np.sqrt(data_array))
        createWS_alg.setProperty("Dimensionality", 3)
        createWS_alg.setProperty(
            "Extents", '0.5,{},0.5,{},0.5,{}'.format(y_dim + 0.5, x_dim + 0.5,
                                                     number_of_runs + 0.5))
        createWS_alg.setProperty(
            "NumberOfBins", '{},{},{}'.format(y_dim, x_dim, number_of_runs))
        createWS_alg.setProperty("Names", 'y,x,scanIndex')
        createWS_alg.setProperty("Units", 'bin,bin,number')
        createWS_alg.execute()
        outWS = createWS_alg.getProperty("OutputWorkspace").value

        progress.report('Getting IDF')
        # Get the instrument and some logs from the first file; assume the rest are the same
        _tmp_ws = LoadEventNexus(runs[0],
                                 MetaDataOnly=True,
                                 EnableLogging=False)
        # The following logs should be the same for all runs
        RemoveLogs(
            _tmp_ws,
            KeepLogs=
            'HB2C:Mot:detz,HB2C:Mot:detz.RBV,HB2C:Mot:s2,HB2C:Mot:s2.RBV,'
            'HB2C:Mot:sgl,HB2C:Mot:sgl.RBV,HB2C:Mot:sgu,HB2C:Mot:sgu.RBV,'
            'run_title,start_time,experiment_identifier,HB2C:CS:CrystalAlign:UBMatrix',
            EnableLogging=False)

        time_ns_array = _tmp_ws.run().startTime().totalNanoseconds(
        ) + np.append(0,
                      np.cumsum(duration_array) * 1e9)[:-1]

        try:
            ub = np.array(re.findall(
                r'-?\d+\.*\d*',
                _tmp_ws.run().getProperty(
                    'HB2C:CS:CrystalAlign:UBMatrix').value[0]),
                          dtype=float).reshape(3, 3)
            sgl = np.deg2rad(_tmp_ws.run().getProperty(
                'HB2C:Mot:sgl.RBV').value[0])  # 'HB2C:Mot:sgl.RBV,1,0,0,-1'
            sgu = np.deg2rad(_tmp_ws.run().getProperty(
                'HB2C:Mot:sgu.RBV').value[0])  # 'HB2C:Mot:sgu.RBV,0,0,1,-1'
            sgl_a = np.array([[1, 0, 0], [0, np.cos(sgl),
                                          np.sin(sgl)],
                              [0, -np.sin(sgl), np.cos(sgl)]])
            sgu_a = np.array([[np.cos(sgu), np.sin(sgu), 0],
                              [-np.sin(sgu), np.cos(sgu), 0], [0, 0, 1]])
            UB = sgl_a.dot(sgu_a).dot(
                ub)  # Apply the Goniometer tilts to the UB matrix
            SetUB(_tmp_ws, UB=UB, EnableLogging=False)
        except (RuntimeError, ValueError):
            SetUB(_tmp_ws, EnableLogging=False)

        if grouping > 1:
            _tmp_group, _, _ = CreateGroupingWorkspace(InputWorkspace=_tmp_ws,
                                                       EnableLogging=False)

            group_number = 0
            for x in range(0, 480 * 8, grouping):
                for y in range(0, 512, grouping):
                    group_number += 1
                    for j in range(grouping):
                        for i in range(grouping):
                            _tmp_group.dataY(y + i +
                                             (x + j) * 512)[0] = group_number

            _tmp_ws = GroupDetectors(InputWorkspace=_tmp_ws,
                                     CopyGroupingFromWorkspace=_tmp_group,
                                     EnableLogging=False)
            DeleteWorkspace(_tmp_group, EnableLogging=False)

        progress.report('Adding logs')

        # Hack: ConvertToMD is needed so that a deep copy of the ExperimentInfo can happen
        # outWS.addExperimentInfo(_tmp_ws) # This doesn't work but should, when you delete `ws` `outWS` also loses it's ExperimentInfo
        _tmp_ws = Rebin(_tmp_ws, '0,1,2', EnableLogging=False)
        _tmp_ws = ConvertToMD(_tmp_ws,
                              dEAnalysisMode='Elastic',
                              EnableLogging=False,
                              PreprocDetectorsWS='__PreprocessedDetectorsWS')

        preprocWS = mtd['__PreprocessedDetectorsWS']
        twotheta = preprocWS.column(2)
        azimuthal = preprocWS.column(3)

        outWS.copyExperimentInfos(_tmp_ws)
        DeleteWorkspace(_tmp_ws, EnableLogging=False)
        DeleteWorkspace('__PreprocessedDetectorsWS', EnableLogging=False)
        # end Hack

        add_time_series_property('s1',
                                 outWS.getExperimentInfo(0).run(),
                                 time_ns_array, s1_array)
        outWS.getExperimentInfo(0).run().getProperty('s1').units = 'deg'
        add_time_series_property('duration',
                                 outWS.getExperimentInfo(0).run(),
                                 time_ns_array, duration_array)
        outWS.getExperimentInfo(0).run().getProperty(
            'duration').units = 'second'
        outWS.getExperimentInfo(0).run().addProperty('run_number',
                                                     run_number_array, True)
        add_time_series_property('monitor_count',
                                 outWS.getExperimentInfo(0).run(),
                                 time_ns_array, monitor_count_array)
        outWS.getExperimentInfo(0).run().addProperty('twotheta', twotheta,
                                                     True)
        outWS.getExperimentInfo(0).run().addProperty('azimuthal', azimuthal,
                                                     True)

        setGoniometer_alg = self.createChildAlgorithm("SetGoniometer",
                                                      enableLogging=False)
        setGoniometer_alg.setProperty("Workspace", outWS)
        setGoniometer_alg.setProperty("Axis0", 's1,0,1,0,1')
        setGoniometer_alg.setProperty("Average", False)
        setGoniometer_alg.execute()

        return outWS
예제 #49
0
    def PyExec(self):
        self._setup()
        self._wave_range()

        setup_prog = Progress(self, start=0.0, end=0.2, nreports=2)
        # Set sample material form chemical formula
        setup_prog.report('Set sample material')
        self._sample_density = self._set_material(self._sample_ws_name,
                                                  self._set_sample_method,
                                                  self._sample_chemical_formula,
                                                  self._sample_coherent_cross_section,
                                                  self._sample_incoherent_cross_section,
                                                  self._sample_attenuation_cross_section,
                                                  self._sample_density_type,
                                                  self._sample_density,
                                                  self._sample_number_density_unit)

        # If using a can, set sample material using chemical formula
        if self._use_can:
            setup_prog.report('Set container sample material')
            self._can_density = self._set_material(self._can_ws_name,
                                                   self._set_can_method,
                                                   self._can_chemical_formula,
                                                   self._can_coherent_cross_section,
                                                   self._can_incoherent_cross_section,
                                                   self._can_attenuation_cross_section,
                                                   self._can_density_type,
                                                   self._can_density,
                                                   self._can_number_density_unit)

        # Holders for the corrected data
        data_ass = []
        data_assc = []
        data_acsc = []
        data_acc = []

        self._get_angles()
        num_angles = len(self._angles)
        workflow_prog = Progress(self, start=0.2, end=0.8, nreports=num_angles * 2)

        # Check sample input
        sam_material = mtd[self._sample_ws_name].sample().getMaterial()
        self._has_sample_in = \
            bool(self._sample_density and self._sample_thickness and (sam_material.totalScatterXSection() + sam_material.absorbXSection()))
        if not self._has_sample_in:
            logger.warning("The sample has not been given, or the information is incomplete. Continuing but no absorption for sample will "
                           "be computed.")

        # Check can input
        if self._use_can:
            can_material = mtd[self._can_ws_name].sample().getMaterial()
            if self._can_density and (can_material.totalScatterXSection() + can_material.absorbXSection()):
                self._has_can_front_in = bool(self._can_front_thickness)
                self._has_can_back_in = bool(self._can_back_thickness)
            else:
                logger.warning(
                    "A can workspace was given but the can information is incomplete. Continuing but no absorption for the can will "
                    "be computed.")

            if not self._has_can_front_in:
                logger.warning(
                    "A can workspace was given but the can front thickness was not given. Continuing but no absorption for can front"
                    " will be computed.")
            if not self._has_can_back_in:
                logger.warning(
                    "A can workspace was given but the can back thickness was not given. Continuing but no absorption for can back"
                    " will be computed.")

        for angle_idx in range(num_angles):
            workflow_prog.report('Running flat correction for angle %s' % angle_idx)
            angle = self._angles[angle_idx]
            (ass, assc, acsc, acc) = self._flat_abs(angle)

            logger.information('Angle %d: %f successful' % (angle_idx + 1, self._angles[angle_idx]))
            workflow_prog.report('Appending data for angle %s' % angle_idx)
            data_ass = np.append(data_ass, ass)
            data_assc = np.append(data_assc, assc)
            data_acsc = np.append(data_acsc, acsc)
            data_acc = np.append(data_acc, acc)

        log_prog = Progress(self, start=0.8, end=1.0, nreports=8)

        sample_logs = {'sample_shape': 'flatplate', 'sample_filename': self._sample_ws_name,
                       'sample_thickness': self._sample_thickness, 'sample_angle': self._sample_angle,
                       'emode': self._emode, 'efixed': self._efixed}
        dataX = self._wavelengths * num_angles

        # Create the output workspaces
        ass_ws = self._output_ws_name + '_ass'
        log_prog.report('Creating ass output Workspace')
        CreateWorkspace(OutputWorkspace=ass_ws,
                        DataX=dataX,
                        DataY=data_ass,
                        NSpec=num_angles,
                        UnitX='Wavelength',
                        VerticalAxisUnit='SpectraNumber',
                        ParentWorkspace=self._sample_ws_name,
                        EnableLogging=False)
        log_prog.report('Adding sample logs')
        self._add_sample_logs(ass_ws, sample_logs)

        workspaces = [ass_ws]

        if self._use_can:
            log_prog.report('Adding can sample logs')
            AddSampleLog(Workspace=ass_ws, LogName='can_filename', LogType='String', LogText=str(self._can_ws_name), EnableLogging=False)

            assc_ws = self._output_ws_name + '_assc'
            workspaces.append(assc_ws)
            log_prog.report('Creating assc output workspace')
            CreateWorkspace(OutputWorkspace=assc_ws,
                            DataX=dataX,
                            DataY=data_assc,
                            NSpec=num_angles,
                            UnitX='Wavelength',
                            VerticalAxisUnit='SpectraNumber',
                            ParentWorkspace=self._sample_ws_name,
                            EnableLogging=False)
            log_prog.report('Adding assc sample logs')
            self._add_sample_logs(assc_ws, sample_logs)
            AddSampleLog(Workspace=assc_ws, LogName='can_filename', LogType='String', LogText=str(self._can_ws_name), EnableLogging=False)

            acsc_ws = self._output_ws_name + '_acsc'
            workspaces.append(acsc_ws)
            log_prog.report('Creating acsc outputworkspace')
            CreateWorkspace(OutputWorkspace=acsc_ws,
                            DataX=dataX,
                            DataY=data_acsc,
                            NSpec=num_angles,
                            UnitX='Wavelength',
                            VerticalAxisUnit='SpectraNumber',
                            ParentWorkspace=self._sample_ws_name,
                            EnableLogging=False)
            log_prog.report('Adding acsc sample logs')
            self._add_sample_logs(acsc_ws, sample_logs)
            AddSampleLog(Workspace=acsc_ws, LogName='can_filename', LogType='String', LogText=str(self._can_ws_name), EnableLogging=False)

            acc_ws = self._output_ws_name + '_acc'
            workspaces.append(acc_ws)
            log_prog.report('Creating acc workspace')
            CreateWorkspace(OutputWorkspace=acc_ws,
                            DataX=dataX,
                            DataY=data_acc,
                            NSpec=num_angles,
                            UnitX='Wavelength',
                            VerticalAxisUnit='SpectraNumber',
                            ParentWorkspace=self._sample_ws_name,
                            EnableLogging=False)
            log_prog.report('Adding acc sample logs')
            self._add_sample_logs(acc_ws, sample_logs)
            AddSampleLog(Workspace=acc_ws, LogName='can_filename', LogType='String', LogText=str(self._can_ws_name), EnableLogging=False)

        if self._interpolate:
            self._interpolate_corrections(workspaces)
        log_prog.report('Grouping Output Workspaces')
        GroupWorkspaces(InputWorkspaces=','.join(workspaces), OutputWorkspace=self._output_ws_name, EnableLogging=False)
        self.setPropertyValue('OutputWorkspace', self._output_ws_name)
예제 #50
0
    def PyExec(self):  # noqa
        progress = Progress(self, 0.0, 1.0, 5)
        inWS = self.getProperty("InputWorkspace").value
        signal = inWS.getSignalArray().copy()

        dimX = inWS.getXDimension()
        dimY = inWS.getYDimension()
        dimZ = inWS.getZDimension()

        Xmin = dimX.getMinimum()
        Ymin = dimY.getMinimum()
        Zmin = dimZ.getMinimum()
        Xmax = dimX.getMaximum()
        Ymax = dimY.getMaximum()
        Zmax = dimZ.getMaximum()
        Xbins = dimX.getNBins()
        Ybins = dimY.getNBins()
        Zbins = dimZ.getNBins()
        Xwidth = dimX.getBinWidth()
        Ywidth = dimY.getBinWidth()
        Zwidth = dimZ.getBinWidth()

        X = np.linspace(Xmin, Xmax, Xbins + 1)
        Y = np.linspace(Ymin, Ymax, Ybins + 1)
        Z = np.linspace(Zmin, Zmax, Zbins + 1)

        X, Y, Z = np.ogrid[(dimX.getX(0) + dimX.getX(1)) /
                           2:(dimX.getX(Xbins) + dimX.getX(Xbins - 1)) /
                           2:Xbins * 1j, (dimY.getX(0) + dimY.getX(1)) /
                           2:(dimY.getX(Ybins) + dimY.getX(Ybins - 1)) /
                           2:Ybins * 1j, (dimZ.getX(0) + dimZ.getX(1)) /
                           2:(dimZ.getX(Zbins) + dimZ.getX(Zbins - 1)) /
                           2:Zbins * 1j]

        if self.getProperty("RemoveReflections").value:
            progress.report("Removing Reflections")
            size = self.getProperty("Size").value
            if len(size) == 1:
                size = np.repeat(size, 3)
            size /= 2.0  # We want radii or half box width
            cut_shape = self.getProperty("Shape").value
            space_group = self.getProperty("SpaceGroup").value
            if space_group:
                check_space_group = True
                try:
                    space_group = SpaceGroupFactory.subscribedSpaceGroupSymbols(
                        int(space_group))[0]
                except ValueError:
                    pass
                logger.information('Using space group: ' + space_group)
                sg = SpaceGroupFactory.createSpaceGroup(space_group)
            else:
                check_space_group = False

            if cut_shape == 'cube':
                for h in range(int(np.ceil(Xmin)), int(Xmax) + 1):
                    for k in range(int(np.ceil(Ymin)), int(Ymax) + 1):
                        for l in range(int(np.ceil(Zmin)), int(Zmax) + 1):
                            if not check_space_group or sg.isAllowedReflection(
                                [h, k, l]):
                                signal[int((h - size[0] - Xmin) / Xwidth +
                                           1):int((h + size[0] - Xmin) /
                                                  Xwidth),
                                       int((k - size[1] - Ymin) / Ywidth +
                                           1):int((k + size[1] - Ymin) /
                                                  Ywidth),
                                       int((l - size[2] - Zmin) / Zwidth +
                                           1):int((l + size[2] - Zmin) /
                                                  Zwidth)] = np.nan
            else:  # sphere
                mask = ((X - np.round(X))**2 / size[0]**2 +
                        (Y - np.round(Y))**2 / size[1]**2 +
                        (Z - np.round(Z))**2 / size[2]**2 < 1)

                # Unmask invalid reflections
                if check_space_group:
                    for h in range(int(np.ceil(Xmin)), int(Xmax) + 1):
                        for k in range(int(np.ceil(Ymin)), int(Ymax) + 1):
                            for l in range(int(np.ceil(Zmin)), int(Zmax) + 1):
                                if not sg.isAllowedReflection([h, k, l]):
                                    mask[int((h - 0.5 - Xmin) / Xwidth +
                                             1):int((h + 0.5 - Xmin) / Xwidth),
                                         int((k - 0.5 - Ymin) / Ywidth +
                                             1):int((k + 0.5 - Ymin) / Ywidth),
                                         int((l - 0.5 - Zmin) / Zwidth +
                                             1):int((l + 0.5 - Zmin) /
                                                    Zwidth)] = False

                signal[mask] = np.nan

        if self.getProperty("CropSphere").value:
            progress.report("Cropping to sphere")
            sphereMin = self.getProperty("SphereMin").value

            if sphereMin[0] < Property.EMPTY_DBL:
                if len(sphereMin) == 1:
                    sphereMin = np.repeat(sphereMin, 3)
                signal[X**2 / sphereMin[0]**2 + Y**2 / sphereMin[1]**2 +
                       Z**2 / sphereMin[2]**2 < 1] = np.nan

            sphereMax = self.getProperty("SphereMax").value

            if sphereMax[0] < Property.EMPTY_DBL:
                if len(sphereMax) == 1:
                    sphereMax = np.repeat(sphereMax, 3)
                if self.getProperty("FillValue").value == Property.EMPTY_DBL:
                    fill_value = np.nan
                else:
                    fill_value = self.getProperty("FillValue").value
                signal[X**2 / sphereMax[0]**2 + Y**2 / sphereMax[1]**2 +
                       Z**2 / sphereMax[2]**2 > 1] = fill_value

        if self.getProperty("Convolution").value:
            progress.report("Convoluting signal")
            signal = self._convolution(signal)

        if self.getPropertyValue("IntermediateWorkspace"):
            cloneWS_alg = self.createChildAlgorithm("CloneMDWorkspace",
                                                    enableLogging=False)
            cloneWS_alg.setProperty("InputWorkspace", inWS)
            cloneWS_alg.execute()
            signalOutWS = cloneWS_alg.getProperty("OutputWorkspace").value
            signalOutWS.setSignalArray(signal)
            self.setProperty("IntermediateWorkspace", signalOutWS)

        # Do FFT
        progress.report("Running FFT")
        # Replace any remaining nan's or inf's with 0
        # Otherwise you end up with a lot of nan's
        signal[np.isnan(signal)] = 0
        signal[np.isinf(signal)] = 0

        signal = np.fft.fftshift(np.fft.fftn(np.fft.ifftshift(signal)))
        number_of_bins = signal.shape

        # Do deconvolution
        if self.getProperty("Convolution").value and self.getProperty(
                "Deconvolution").value:
            signal /= self._deconvolution(np.array(signal.shape))

        # CreateMDHistoWorkspace expects Fortan `column-major` ordering
        signal = signal.real.flatten('F')

        createWS_alg = self.createChildAlgorithm("CreateMDHistoWorkspace",
                                                 enableLogging=False)
        createWS_alg.setProperty("SignalInput", signal)
        createWS_alg.setProperty("ErrorInput", signal**2)
        createWS_alg.setProperty("Dimensionality", 3)
        createWS_alg.setProperty("Extents", self._calc_new_extents(inWS))
        createWS_alg.setProperty("NumberOfBins", number_of_bins)
        createWS_alg.setProperty("Names", 'x,y,z')
        createWS_alg.setProperty("Units", 'a,b,c')
        createWS_alg.execute()
        outWS = createWS_alg.getProperty("OutputWorkspace").value

        # Copy first experiment info
        if inWS.getNumExperimentInfo() > 0:
            outWS.copyExperimentInfos(inWS)

        progress.report()

        self.setProperty("OutputWorkspace", outWS)
예제 #51
0
 def PyExec(self):
     process = self.getPropertyValue('ProcessAs')
     processes = ['Absorber', 'Beam', 'Transmission', 'Container', 'Sample']
     progress = Progress(self,
                         start=0.0,
                         end=1.0,
                         nreports=processes.index(process) + 1)
     ws = '__' + self.getPropertyValue('OutputWorkspace')
     if self.getPropertyValue('Run'):
         LoadAndMerge(Filename=self.getPropertyValue('Run').replace(
             '+', ','),
                      LoaderName='LoadILLSANS',
                      OutputWorkspace=ws)
         if isinstance(mtd[ws], WorkspaceGroup):
             # we do not want the summing done by LoadAndMerge since it will be pair-wise and slow
             # instead we load and list, and merge once with merge runs
             tmp = '__tmp' + ws
             MergeRuns(InputWorkspaces=ws, OutputWorkspace=tmp)
             DeleteWorkspaces(ws)
             RenameWorkspace(InputWorkspace=tmp, OutputWorkspace=ws)
     else:
         in_ws = self.getPropertyValue('InputWorkspace')
         CloneWorkspace(InputWorkspace=in_ws, OutputWorkspace=ws)
     self._instrument = mtd[ws].getInstrument().getName()
     self._normalise(ws)
     run = mtd[ws].getRun()
     if run.hasProperty('tof_mode'):
         if run.getLogData('tof_mode').value == 'TOF':
             self._mode = 'TOF'
     progress.report()
     if process in ['Beam', 'Transmission', 'Container', 'Sample']:
         absorber_ws = self.getProperty('AbsorberInputWorkspace').value
         if absorber_ws:
             self._apply_absorber(ws, absorber_ws)
         if process == 'Beam':
             self._process_beam(ws)
             progress.report()
         else:
             beam_ws = self.getProperty('BeamInputWorkspace').value
             if beam_ws:
                 self._apply_beam(ws, beam_ws)
             if process == 'Transmission':
                 self._process_transmission(ws, beam_ws)
                 progress.report()
             else:
                 transmission_ws = self.getProperty(
                     'TransmissionInputWorkspace').value
                 if transmission_ws:
                     self._apply_transmission(ws, transmission_ws)
                 solid_angle = self._make_solid_angle_name(ws)
                 cache = self.getProperty('CacheSolidAngle').value
                 if (cache and not mtd.doesExist(solid_angle)) or not cache:
                     if self._instrument == "D16":
                         run = mtd[ws].getRun()
                         distance = run.getLogData('L2').value
                         CloneWorkspace(InputWorkspace=ws,
                                        OutputWorkspace=solid_angle)
                         MoveInstrumentComponent(Workspace=solid_angle,
                                                 X=0,
                                                 Y=0,
                                                 Z=distance,
                                                 RelativePosition=False,
                                                 ComponentName="detector")
                         RotateInstrumentComponent(Workspace=solid_angle,
                                                   X=0,
                                                   Y=1,
                                                   Z=0,
                                                   angle=0,
                                                   RelativeRotation=False,
                                                   ComponentName="detector")
                         input_solid = solid_angle
                     else:
                         input_solid = ws
                     SolidAngle(InputWorkspace=input_solid,
                                OutputWorkspace=solid_angle,
                                Method="Rectangle")
                 Divide(LHSWorkspace=ws,
                        RHSWorkspace=solid_angle,
                        OutputWorkspace=ws,
                        WarnOnZeroDivide=False)
                 if not cache:
                     DeleteWorkspace(solid_angle)
                 progress.report()
                 if process == 'Sample':
                     container_ws = self.getProperty(
                         'ContainerInputWorkspace').value
                     if container_ws:
                         self._apply_container(ws, container_ws)
                     self._apply_masks(ws)
                     self._apply_thickness(ws)
                     # parallax (gondola) effect
                     if self._instrument in [
                             'D22', 'D22lr', 'D33', 'D11B', 'D22B'
                     ]:
                         self._apply_parallax(ws)
                     progress.report()
                     sensitivity_out = self.getPropertyValue(
                         'SensitivityOutputWorkspace')
                     if sensitivity_out:
                         self._process_sensitivity(ws, sensitivity_out)
                     self._process_sample(ws)
                     self._set_sample_title(ws)
                     progress.report()
     self._finalize(ws, process)
예제 #52
0
    def PyExec(self):

        # Check for platform support
        if not is_supported_f2py_platform():
            unsupported_msg = "This algorithm can only be run on valid platforms." \
                              + " please view the algorithm documentation to see" \
                              + " what platforms are currently supported"
            raise RuntimeError(unsupported_msg)

        from IndirectBayes import (CalcErange, GetXYE)
        setup_prog = Progress(self, start=0.0, end=0.3, nreports=5)
        self.log().information('BayesQuasi input')

        erange = [self._e_min, self._e_max]
        nbins = [self._sam_bins, self._res_bins]
        setup_prog.report('Converting to binary for Fortran')
        #convert true/false to 1/0 for fortran
        o_el = 1 if self._elastic else 0
        o_w1 = 1 if self._width else 0
        o_res = 1 if self._res_norm else 0

        #fortran code uses background choices defined using the following numbers
        setup_prog.report('Encoding input options')
        if self._background == 'Sloping':
            o_bgd = 2
        elif self._background == 'Flat':
            o_bgd = 1
        elif self._background == 'Zero':
            o_bgd = 0

        fitOp = [o_el, o_bgd, o_w1, o_res]

        setup_prog.report('Establishing save path')
        workdir = config['defaultsave.directory']
        if not os.path.isdir(workdir):
            workdir = os.getcwd()
            logger.information(
                'Default Save directory is not set. Defaulting to current working Directory: '
                + workdir)

        array_len = 4096  # length of array in Fortran
        setup_prog.report('Checking X Range')
        CheckXrange(erange, 'Energy')

        nbin, nrbin = nbins[0], nbins[1]

        logger.information('Sample is ' + self._samWS)
        logger.information('Resolution is ' + self._resWS)

        # Check for trailing and leading zeros in data
        setup_prog.report(
            'Checking for leading and trailing zeros in the data')
        first_data_point, last_data_point = IndentifyDataBoundaries(
            self._samWS)
        if first_data_point > self._e_min:
            logger.warning(
                "Sample workspace contains leading zeros within the energy range."
            )
            logger.warning("Updating eMin: eMin = " + str(first_data_point))
            self._e_min = first_data_point
        if last_data_point < self._e_max:
            logger.warning(
                "Sample workspace contains trailing zeros within the energy range."
            )
            logger.warning("Updating eMax: eMax = " + str(last_data_point))
            self._e_max = last_data_point

        # update erange with new values
        erange = [self._e_min, self._e_max]

        setup_prog.report('Checking Analysers')
        CheckAnalysers(self._samWS, self._resWS)
        setup_prog.report('Obtaining EFixed, theta and Q')
        efix = getEfixed(self._samWS)
        theta, Q = GetThetaQ(self._samWS)

        nsam, ntc = CheckHistZero(self._samWS)

        totalNoSam = nsam

        #check if we're performing a sequential fit
        if not self._loop:
            nsam = 1

        nres = CheckHistZero(self._resWS)[0]

        setup_prog.report('Checking Histograms')
        if self._program == 'QL':
            if nres == 1:
                prog = 'QLr'  # res file
            else:
                prog = 'QLd'  # data file
                CheckHistSame(self._samWS, 'Sample', self._resWS, 'Resolution')
        elif self._program == 'QSe':
            if nres == 1:
                prog = 'QSe'  # res file
            else:
                raise ValueError('Stretched Exp ONLY works with RES file')

        logger.information('Version is ' + prog)
        logger.information(' Number of spectra = ' + str(nsam))
        logger.information(' Erange : ' + str(erange[0]) + ' to ' +
                           str(erange[1]))

        setup_prog.report('Reading files')
        Wy, We = self._read_width_file(self._width, self._wfile, totalNoSam)
        dtn, xsc = self._read_norm_file(self._res_norm, self._resnormWS,
                                        totalNoSam)

        setup_prog.report('Establishing output workspace name')
        fname = self._samWS[:-4] + '_' + prog
        probWS = fname + '_Prob'
        fitWS = fname + '_Fit'
        wrks = os.path.join(workdir, self._samWS[:-4])
        logger.information(' lptfile : ' + wrks + '_' + prog + '.lpt')
        lwrk = len(wrks)
        wrks.ljust(140, ' ')
        wrkr = self._resWS
        wrkr.ljust(140, ' ')

        setup_prog.report('Initialising probability list')
        # initialise probability list
        if self._program == 'QL':
            prob0, prob1, prob2 = [], [], []
        xQ = np.array([Q[0]])
        for m in range(1, nsam):
            xQ = np.append(xQ, Q[m])
        xProb = xQ
        xProb = np.append(xProb, xQ)
        xProb = np.append(xProb, xQ)
        eProb = np.zeros(3 * nsam)

        group = ''
        workflow_prog = Progress(self, start=0.3, end=0.7, nreports=nsam * 3)
        for spectrum in range(0, nsam):
            logger.information('Group ' + str(spectrum) + ' at angle ' +
                               str(theta[spectrum]))
            nsp = spectrum + 1

            nout, bnorm, Xdat, Xv, Yv, Ev = CalcErange(self._samWS, spectrum,
                                                       erange, nbin)
            Ndat = nout[0]
            Imin = nout[1]
            Imax = nout[2]
            if prog == 'QLd':
                mm = spectrum
            else:
                mm = 0
            Nb, Xb, Yb, Eb = GetXYE(self._resWS, mm,
                                    array_len)  # get resolution data
            numb = [nsam, nsp, ntc, Ndat, nbin, Imin, Imax, Nb, nrbin]
            rscl = 1.0
            reals = [efix, theta[spectrum], rscl, bnorm]

            if prog == 'QLr':
                workflow_prog.report(
                    'Processing Sample number %i as Lorentzian' % spectrum)
                nd, xout, yout, eout, yfit, yprob = QLr.qlres(
                    numb, Xv, Yv, Ev, reals, fitOp, Xdat, Xb, Yb, Wy, We, dtn,
                    xsc, wrks, wrkr, lwrk)
                message = ' Log(prob) : ' + str(yprob[0]) + ' ' + str(
                    yprob[1]) + ' ' + str(yprob[2]) + ' ' + str(yprob[3])
                logger.information(message)
            if prog == 'QLd':
                workflow_prog.report('Processing Sample number %i' % spectrum)
                nd, xout, yout, eout, yfit, yprob = QLd.qldata(
                    numb, Xv, Yv, Ev, reals, fitOp, Xdat, Xb, Yb, Eb, Wy, We,
                    wrks, wrkr, lwrk)
                message = ' Log(prob) : ' + str(yprob[0]) + ' ' + str(
                    yprob[1]) + ' ' + str(yprob[2]) + ' ' + str(yprob[3])
                logger.information(message)
            if prog == 'QSe':
                workflow_prog.report(
                    'Processing Sample number %i as Stretched Exp' % spectrum)
                nd, xout, yout, eout, yfit, yprob = Qse.qlstexp(
                    numb, Xv, Yv, Ev, reals, fitOp, Xdat, Xb, Yb, Wy, We, dtn,
                    xsc, wrks, wrkr, lwrk)
            dataX = xout[:nd]
            dataX = np.append(dataX, 2 * xout[nd - 1] - xout[nd - 2])
            yfit_list = np.split(yfit[:4 * nd], 4)
            dataF1 = yfit_list[1]
            if self._program == 'QL':
                dataF2 = yfit_list[2]
            workflow_prog.report('Processing data')
            dataG = np.zeros(nd)
            datX = dataX
            datY = yout[:nd]
            datE = eout[:nd]
            datX = np.append(datX, dataX)
            datY = np.append(datY, dataF1[:nd])
            datE = np.append(datE, dataG)
            res1 = dataF1[:nd] - yout[:nd]
            datX = np.append(datX, dataX)
            datY = np.append(datY, res1)
            datE = np.append(datE, dataG)
            nsp = 3
            names = 'data,fit.1,diff.1'
            res_plot = [0, 1, 2]
            if self._program == 'QL':
                workflow_prog.report('Processing Lorentzian result data')
                datX = np.append(datX, dataX)
                datY = np.append(datY, dataF2[:nd])
                datE = np.append(datE, dataG)
                res2 = dataF2[:nd] - yout[:nd]
                datX = np.append(datX, dataX)
                datY = np.append(datY, res2)
                datE = np.append(datE, dataG)
                nsp += 2
                names += ',fit.2,diff.2'
                res_plot.append(4)
                prob0.append(yprob[0])
                prob1.append(yprob[1])
                prob2.append(yprob[2])

            # create result workspace
            fitWS = fname + '_Workspaces'
            fout = fname + '_Workspace_' + str(spectrum)

            workflow_prog.report('Creating OutputWorkspace')
            s_api.CreateWorkspace(OutputWorkspace=fout,
                                  DataX=datX,
                                  DataY=datY,
                                  DataE=datE,
                                  Nspec=nsp,
                                  UnitX='DeltaE',
                                  VerticalAxisUnit='Text',
                                  VerticalAxisValues=names)

            # append workspace to list of results
            group += fout + ','

        comp_prog = Progress(self, start=0.7, end=0.8, nreports=2)
        comp_prog.report('Creating Group Workspace')
        s_api.GroupWorkspaces(InputWorkspaces=group, OutputWorkspace=fitWS)

        if self._program == 'QL':
            comp_prog.report('Processing Lorentzian probability data')
            yPr0 = np.array([prob0[0]])
            yPr1 = np.array([prob1[0]])
            yPr2 = np.array([prob2[0]])
            for m in range(1, nsam):
                yPr0 = np.append(yPr0, prob0[m])
                yPr1 = np.append(yPr1, prob1[m])
                yPr2 = np.append(yPr2, prob2[m])
            yProb = yPr0
            yProb = np.append(yProb, yPr1)
            yProb = np.append(yProb, yPr2)
            s_api.CreateWorkspace(OutputWorkspace=probWS,
                                  DataX=xProb,
                                  DataY=yProb,
                                  DataE=eProb,
                                  Nspec=3,
                                  UnitX='MomentumTransfer')
            outWS = self.C2Fw(fname)
        if self._program == 'QSe':
            comp_prog.report('Runnning C2Se')
            outWS = self.C2Se(fname)

        log_prog = Progress(self, start=0.8, end=1.0, nreports=8)
        #Add some sample logs to the output workspaces
        log_prog.report('Copying Logs to outputWorkspace')
        s_api.CopyLogs(InputWorkspace=self._samWS, OutputWorkspace=outWS)
        log_prog.report('Adding Sample logs to Output workspace')
        self._add_sample_logs(outWS, prog, erange, nbins)
        log_prog.report('Copying logs to fit Workspace')
        s_api.CopyLogs(InputWorkspace=self._samWS, OutputWorkspace=fitWS)
        log_prog.report('Adding sample logs to Fit workspace')
        self._add_sample_logs(fitWS, prog, erange, nbins)
        log_prog.report('Finialising log copying')

        self.setProperty('OutputWorkspaceFit', fitWS)
        self.setProperty('OutputWorkspaceResult', outWS)
        log_prog.report('Setting workspace properties')

        if self._program == 'QL':
            self.setProperty('OutputWorkspaceProb', probWS)
예제 #53
0
class PowderILLParameterScan(PythonAlgorithm):

    _calibration_file = None
    _roc_file = None
    _normalise_option = None
    _observable = None
    _sort_x_axis = None
    _unit = None
    _out_name = None
    _progress = None
    _crop_negative = None
    _zero_counting_option = None
    _rebin_width = None
    _region_of_interest = []
    _zero_cells = []

    def _hide(self, name):
        return '__' + self._out_name + '_' + name

    def _hide_run(selfs, runnumber):
        return '__' + runnumber

    def category(self):
        return "ILL\\Diffraction;Diffraction\\Reduction"

    def summary(self):
        return 'Performs powder diffraction data reduction for ILL instrument D20.'

    def seeAlso(self):
        return ["PowderILLDetectorScan", "PowderILLEfficiency"]

    def name(self):
        return "PowderILLParameterScan"

    def validateInputs(self):
        issues = dict()
        rebin = self.getProperty('ScanAxisBinWidth').value
        sort = self.getProperty('SortObservableAxis').value
        if rebin != 0 and not sort:
            issues[
                'SortObservableAxis'] = 'Axis must be sorted if rebin is requested.'
        return issues

    def PyInit(self):

        self.declareProperty(MultipleFileProperty('Run', extensions=['nxs']),
                             doc='File path of run(s).')

        self.declareProperty(FileProperty('CalibrationFile',
                                          '',
                                          action=FileAction.OptionalLoad,
                                          extensions=['nxs']),
                             doc='File containing the detector efficiencies.')

        self.declareProperty(
            FileProperty('ROCCorrectionFile',
                         '',
                         action=FileAction.OptionalLoad,
                         extensions=['nxs']),
            doc=
            'File containing the radial oscillating collimator (ROC) corrections.'
        )

        self.declareProperty(name='NormaliseTo',
                             defaultValue='None',
                             validator=StringListValidator(
                                 ['None', 'Time', 'Monitor', 'ROI']),
                             doc='Normalise to time, monitor or ROI counts.')

        thetaRangeValidator = FloatArrayOrderedPairsValidator()

        self.declareProperty(
            FloatArrayProperty(name='ROI',
                               values=[0, 153.6],
                               validator=thetaRangeValidator),
            doc=
            'Regions of interest for normalisation [in scattering angle in degrees].'
        )

        normaliseToROI = VisibleWhenProperty('NormaliseTo',
                                             PropertyCriterion.IsEqualTo,
                                             'ROI')
        self.setPropertySettings('ROI', normaliseToROI)

        self.declareProperty(name='Observable',
                             defaultValue='sample.temperature',
                             doc='Scanning observable, a Sample Log entry.')

        self.declareProperty(
            name='SortObservableAxis',
            defaultValue=False,
            doc='Whether or not to sort the scanning observable axis.')

        self.declareProperty(
            name='ScanAxisBinWidth',
            defaultValue=0.,
            validator=FloatBoundedValidator(lower=0.),
            doc=
            'Rebin the observable axis to this width. Default is to not rebin.'
        )

        self.declareProperty(
            name='CropNegative2Theta',
            defaultValue=True,
            doc=
            'Whether or not to crop out the bins corresponding to negative scattering angle.'
        )

        self.declareProperty(
            name='ZeroCountingCells',
            defaultValue='Interpolate',
            validator=StringListValidator(['Crop', 'Interpolate', 'Leave']),
            doc=
            'Crop out the zero counting cells or interpolate the counts from the neighbours.'
        )

        self.declareProperty(name='Unit',
                             defaultValue='ScatteringAngle',
                             validator=StringListValidator([
                                 'ScatteringAngle', 'MomentumTransfer',
                                 'dSpacing'
                             ]),
                             doc='The unit of the reduced diffractogram.')

        self.declareProperty(
            MatrixWorkspaceProperty('OutputWorkspace',
                                    '',
                                    direction=Direction.Output),
            doc='Output workspace containing the reduced data.')

    def PyExec(self):

        self._progress = Progress(self, start=0.0, end=1.0, nreports=4)

        self._configure()
        temp_ws = self._hide('temp')
        joined_ws = self._hide('joined')
        mon_ws = self._hide('mon')

        self._progress.report('Loading the data')
        LoadAndMerge(Filename=self.getPropertyValue('Run'),
                     LoaderName='LoadILLDiffraction',
                     OutputWorkspace=temp_ws)

        self._progress.report('Normalising and merging')
        if self._normalise_option == 'Time':
            if isinstance(mtd[temp_ws], WorkspaceGroup):
                for ws in mtd[temp_ws]:
                    # normalise to time here, before joining, since the duration is in sample logs
                    duration = ws.getRun().getLogData('duration').value
                    Scale(InputWorkspace=ws,
                          OutputWorkspace=ws,
                          Factor=1. / duration)
            else:
                duration = mtd[temp_ws].getRun().getLogData('duration').value
                Scale(InputWorkspace=temp_ws,
                      OutputWorkspace=temp_ws,
                      Factor=1. / duration)

        try:
            ConjoinXRuns(InputWorkspaces=temp_ws,
                         SampleLogAsXAxis=self._observable,
                         OutputWorkspace=joined_ws)
        except RuntimeError:
            raise ValueError('Invalid scanning observable')

        DeleteWorkspace(temp_ws)

        ExtractMonitors(InputWorkspace=joined_ws,
                        DetectorWorkspace=joined_ws,
                        MonitorWorkspace=mon_ws)

        if self._normalise_option == 'Monitor':
            Divide(LHSWorkspace=joined_ws,
                   RHSWorkspace=mon_ws,
                   OutputWorkspace=joined_ws)
        elif self._normalise_option == 'ROI':
            self._normalise_to_roi(joined_ws)

        DeleteWorkspace(mon_ws)

        self._progress.report('Applying calibration or ROC if needed')
        if self._calibration_file:
            calib_ws = self._hide('calib')
            LoadNexusProcessed(Filename=self._calibration_file,
                               OutputWorkspace=calib_ws)
            Multiply(LHSWorkspace=joined_ws,
                     RHSWorkspace=calib_ws,
                     OutputWorkspace=joined_ws)
            DeleteWorkspace(calib_ws)

        if self._roc_file:
            roc_ws = self._hide('roc')
            LoadNexusProcessed(Filename=self._roc_file, OutputWorkspace=roc_ws)
            Multiply(LHSWorkspace=joined_ws,
                     RHSWorkspace=roc_ws,
                     OutputWorkspace=joined_ws)
            DeleteWorkspace(roc_ws)

        if self._sort_x_axis:
            SortXAxis(InputWorkspace=joined_ws, OutputWorkspace=joined_ws)

        theta_ws = self._hide('theta')
        ConvertSpectrumAxis(InputWorkspace=joined_ws,
                            OutputWorkspace=theta_ws,
                            Target='SignedTheta',
                            OrderAxis=False)
        theta_axis = mtd[theta_ws].getAxis(1).extractValues()
        DeleteWorkspace(theta_ws)
        first_positive_theta = int(np.where(theta_axis > 0)[0][0])

        if self._crop_negative:
            self.log().information(
                'First positive 2theta at workspace index: ' +
                str(first_positive_theta))
            CropWorkspace(InputWorkspace=joined_ws,
                          OutputWorkspace=joined_ws,
                          StartWorkspaceIndex=first_positive_theta)

        self._progress.report('Treating the zero counting cells')
        self._find_zero_cells(joined_ws)

        if self._zero_counting_option == 'Crop':
            self._crop_zero_cells(joined_ws, self._zero_cells)
        elif self._zero_counting_option == 'Interpolate':
            self._interpolate_zero_cells(joined_ws, theta_axis)

        target = 'SignedTheta'
        if self._unit == 'MomentumTransfer':
            target = 'ElasticQ'
        elif self._unit == 'dSpacing':
            target = 'ElasticDSpacing'

        ConvertSpectrumAxis(InputWorkspace=joined_ws,
                            OutputWorkspace=joined_ws,
                            Target=target)
        Transpose(InputWorkspace=joined_ws, OutputWorkspace=joined_ws)

        if self._rebin_width > 0:
            self._group_spectra(joined_ws)

        RenameWorkspace(InputWorkspace=joined_ws,
                        OutputWorkspace=self._out_name)
        self.setProperty('OutputWorkspace', self._out_name)

    def _configure(self):
        """
            Configures the input properties
        """
        self._out_name = self.getPropertyValue('OutputWorkspace')
        self._observable = self.getPropertyValue('Observable')
        self._sort_x_axis = self.getProperty('SortObservableAxis').value
        self._normalise_option = self.getPropertyValue('NormaliseTo')
        self._calibration_file = self.getPropertyValue('CalibrationFile')
        self._roc_file = self.getPropertyValue('ROCCorrectionFile')
        self._unit = self.getPropertyValue('Unit')
        self._crop_negative = self.getProperty('CropNegative2Theta').value
        self._zero_counting_option = self.getPropertyValue('ZeroCountingCells')
        self._rebin_width = self.getProperty('ScanAxisBinWidth').value
        if self._normalise_option == 'ROI':
            self._region_of_interest = self.getProperty('ROI').value

    def _find_zero_cells(self, ws):
        """
            Finds the cells counting zeros
            @param ws: the input workspace
        """
        self._zero_cells = []
        size = mtd[ws].blocksize()
        for spectrum in range(mtd[ws].getNumberHistograms()):
            counts = mtd[ws].readY(spectrum)
            if np.count_nonzero(counts) < size / 5:
                self._zero_cells.append(spectrum)
        self._zero_cells.sort()
        self.log().information('Found zero counting cells at indices: ' +
                               str(self._zero_cells))

    def _crop_zero_cells(self, ws, wsIndexList):
        """
            Crops out the spectra corresponding to zero counting pixels
            @param ws: the input workspace
            @param wsIndexList: list of workspace indices to crop out
        """
        MaskDetectors(Workspace=ws, WorkspaceIndexList=wsIndexList)
        ExtractUnmaskedSpectra(InputWorkspace=ws, OutputWorkspace=ws)

    def _interpolate_zero_cells(self, ws, theta_axis):
        """
            Interpolates the counts of zero counting cells linearly from the
            nearest non-zero neighbour cells
            @param ws: the input workspace
            @param theta_axis: the unordered signed 2theta axis
        """
        unable_to_interpolate = []
        for cell in self._zero_cells:
            prev_cell = cell - 1
            next_cell = cell + 1

            while prev_cell in self._zero_cells:
                prev_cell -= 1
            while next_cell in self._zero_cells:
                next_cell += 1

            if prev_cell == -1:
                self.log().notice(
                    'Unable to interpolate for cell #' + str(cell) +
                    ': no non-zero neighbour cell was found on the left side. Bin will be cropped.'
                )
                unable_to_interpolate.append(cell)
            if next_cell == mtd[ws].getNumberHistograms():
                self.log().notice(
                    'Unable to interpolate for cell #' + str(cell) +
                    ': no non-zero neighbour cell was found on the right side. Bin will be cropped.'
                )
                unable_to_interpolate.append(cell)

            if prev_cell >= 0 and next_cell < mtd[ws].getNumberHistograms():
                theta_prev = theta_axis[prev_cell]
                theta = theta_axis[cell]
                theta_next = theta_axis[next_cell]
                counts_prev = mtd[ws].readY(prev_cell)
                errors_prev = mtd[ws].readE(prev_cell)
                counts_next = mtd[ws].readY(next_cell)
                errors_next = mtd[ws].readE(next_cell)
                coefficient = (theta - theta_prev) / (theta_next - theta_prev)
                counts = counts_prev + coefficient * (counts_next -
                                                      counts_prev)
                errors = errors_prev + coefficient * (errors_next -
                                                      errors_prev)
                mtd[ws].setY(cell, counts)
                mtd[ws].setE(cell, errors)

        self._crop_zero_cells(ws, unable_to_interpolate)

    def _normalise_to_roi(self, ws):
        """
            Normalises counts to the sum of counts in the region-of-interest
            @param ws : input workspace with raw spectrum axis
        """
        roi_ws = self._hide('roi')
        theta_ws = self._hide('theta_ROI')
        ConvertSpectrumAxis(InputWorkspace=ws,
                            OutputWorkspace=theta_ws,
                            Target='SignedTheta')
        roi_pattern = self._parse_roi(theta_ws)
        SumSpectra(InputWorkspace=ws,
                   OutputWorkspace=roi_ws,
                   ListOfWorkspaceIndices=roi_pattern)
        SumSpectra(InputWorkspace=roi_ws, OutputWorkspace=roi_ws)
        Divide(LHSWorkspace=ws, RHSWorkspace=roi_ws, OutputWorkspace=ws)
        DeleteWorkspace(roi_ws)
        DeleteWorkspace(theta_ws)

    def _parse_roi(self, ws):
        """
            Parses the regions of interest string from 2theta ranges to workspace indices
            @param ws : input workspace with 2theta as spectrum axis
            @returns: roi as workspace indices, e.g. 7-20,100-123
        """
        result = ''
        axis = mtd[ws].getAxis(1).extractValues()
        index = 0
        while index < len(self._region_of_interest):
            start = self._region_of_interest[index]
            end = self._region_of_interest[index + 1]
            start_index = np.argwhere(axis > start)
            end_index = np.argwhere(axis < end)
            result += str(start_index[0][0]) + '-' + str(end_index[-1][0])
            result += ','
            index += 2
        self.log().information('ROI summing pattern is ' + result[:-1])
        return result[:-1]

    def _group_spectra(self, ws):
        """
            Groups the spectrum axis by summing spectra
            @param ws : the input workspace
        """
        new_axis = []
        start_index = 0
        axis = mtd[ws].getAxis(1).extractValues()
        grouped = self._hide('grouped')
        name = grouped
        while start_index < len(axis):
            end = axis[start_index] + self._rebin_width
            end_index = np.argwhere(axis < end)[-1][0]
            SumSpectra(InputWorkspace=ws,
                       OutputWorkspace=name,
                       StartWorkspaceIndex=int(start_index),
                       EndWorkspaceIndex=int(end_index))
            count = end_index - start_index + 1
            Scale(InputWorkspace=name, OutputWorkspace=name, Factor=1. / count)
            new_axis.append(np.sum(axis[start_index:end_index + 1]) / count)
            if name != grouped:
                AppendSpectra(InputWorkspace1=grouped,
                              InputWorkspace2=name,
                              OutputWorkspace=grouped)
                DeleteWorkspace(name)
            start_index = end_index + 1
            name = self._hide('ws_{0}'.format(start_index))
        spectrum_axis = NumericAxis.create(len(new_axis))
        for i in range(len(new_axis)):
            spectrum_axis.setValue(i, new_axis[i])
        mtd[grouped].replaceAxis(1, spectrum_axis)
        RenameWorkspace(InputWorkspace=grouped, OutputWorkspace=ws)
예제 #54
0
    def PyExec(self):
        run_f2py_compatibility_test()

        from IndirectBayes import (CalcErange, GetXYE)
        from IndirectCommon import (CheckXrange, CheckAnalysersOrEFixed,
                                    getEfixed, GetThetaQ, CheckHistZero)
        setup_prog = Progress(self, start=0.0, end=0.3, nreports=5)
        logger.information('BayesStretch input')
        logger.information('Sample is %s' % self._sam_name)
        logger.information('Resolution is %s' % self._res_name)

        setup_prog.report('Converting to binary for Fortran')
        fitOp = self._encode_fit_ops(self._elastic, self._background)

        setup_prog.report('Establishing save path')
        workdir = self._establish_save_path()

        setup_prog.report('Checking X Range')
        CheckXrange(self._erange, 'Energy')

        setup_prog.report('Checking Analysers')
        CheckAnalysersOrEFixed(self._sam_name, self._res_name)
        setup_prog.report('Obtaining EFixed, theta and Q')
        efix = getEfixed(self._sam_name)
        theta, Q = GetThetaQ(self._sam_name)

        setup_prog.report('Checking Histograms')
        nsam, ntc = CheckHistZero(self._sam_name)

        # check if we're performing a sequential fit
        if not self._loop:
            nsam = 1

        logger.information('Version is Stretch')
        logger.information('Number of spectra = %s ' % nsam)
        logger.information('Erange : %f to %f ' %
                           (self._erange[0], self._erange[1]))

        setup_prog.report('Creating FORTRAN Input')
        fname = self._sam_name[:-4] + '_Stretch'
        wrks = os.path.join(workdir, self._sam_name[:-4])
        logger.information('lptfile : %s_Qst.lpt' % wrks)
        lwrk = len(wrks)
        wrks.ljust(140, ' ')
        wrkr = self._res_name
        wrkr.ljust(140, ' ')
        eBet0 = np.zeros(self._nbet)  # set errors to zero
        eSig0 = np.zeros(self._nsig)  # set errors to zero
        rscl = 1.0
        Qaxis = ''

        workflow_prog = Progress(self, start=0.3, end=0.7, nreports=nsam * 3)

        # Empty arrays to hold Sigma and Bet x,y,e values
        xSig, ySig, eSig = [], [], []
        xBet, yBet, eBet = [], [], []

        for m in range(nsam):
            logger.information('Group %i at angle %f' % (m, theta[m]))
            nsp = m + 1
            nout, bnorm, Xdat, Xv, Yv, Ev = CalcErange(self._sam_name, m,
                                                       self._erange,
                                                       self._nbins[0])
            Ndat = nout[0]
            Imin = nout[1]
            Imax = nout[2]

            # get resolution data (4096 = FORTRAN array length)
            Nb, Xb, Yb, _ = GetXYE(self._res_name, 0, 4096)
            numb = [
                nsam, nsp, ntc, Ndat, self._nbins[0], Imin, Imax, Nb,
                self._nbins[1], self._nbet, self._nsig
            ]
            reals = [efix, theta[m], rscl, bnorm]

            workflow_prog.report('Processing spectrum number %i' % m)
            xsout, ysout, xbout, ybout, zpout = Que.quest(
                numb, Xv, Yv, Ev, reals, fitOp, Xdat, Xb, Yb, wrks, wrkr, lwrk)
            dataXs = xsout[:self._nsig]  # reduce from fixed FORTRAN array
            dataYs = ysout[:self._nsig]
            dataXb = xbout[:self._nbet]
            dataYb = ybout[:self._nbet]
            zpWS = fname + '_Zp' + str(m)
            if m > 0:
                Qaxis += ','
            Qaxis += str(Q[m])

            dataXz = []
            dataYz = []
            dataEz = []

            for n in range(self._nsig):
                yfit_list = np.split(zpout[:self._nsig * self._nbet],
                                     self._nsig)
                dataYzp = yfit_list[n]

                dataXz = np.append(dataXz, xbout[:self._nbet])
                dataYz = np.append(dataYz, dataYzp[:self._nbet])
                dataEz = np.append(dataEz, eBet0)

            zpWS = fname + '_Zp' + str(m)
            self._create_workspace(zpWS, [dataXz, dataYz, dataEz], self._nsig,
                                   dataXs, True)

            xSig = np.append(xSig, dataXs)
            ySig = np.append(ySig, dataYs)
            eSig = np.append(eSig, eSig0)
            xBet = np.append(xBet, dataXb)
            yBet = np.append(yBet, dataYb)
            eBet = np.append(eBet, eBet0)

            if m == 0:
                groupZ = zpWS
            else:
                groupZ = groupZ + ',' + zpWS

        # create workspaces for sigma and beta
        workflow_prog.report('Creating OutputWorkspace')
        self._create_workspace(fname + '_Sigma', [xSig, ySig, eSig], nsam,
                               Qaxis)
        self._create_workspace(fname + '_Beta', [xBet, yBet, eBet], nsam,
                               Qaxis)

        group = fname + '_Sigma,' + fname + '_Beta'
        fit_ws = fname + '_Fit'
        s_api.GroupWorkspaces(InputWorkspaces=group, OutputWorkspace=fit_ws)
        contour_ws = fname + '_Contour'
        s_api.GroupWorkspaces(InputWorkspaces=groupZ,
                              OutputWorkspace=contour_ws)

        # Add some sample logs to the output workspaces
        log_prog = Progress(self, start=0.8, end=1.0, nreports=6)
        log_prog.report('Copying Logs to Fit workspace')
        copy_log_alg = self.createChildAlgorithm('CopyLogs',
                                                 enableLogging=False)
        copy_log_alg.setProperty('InputWorkspace', self._sam_name)
        copy_log_alg.setProperty('OutputWorkspace', fit_ws)
        copy_log_alg.execute()

        log_prog.report('Adding Sample logs to Fit workspace')
        self._add_sample_logs(fit_ws, self._erange, self._nbins[0])

        log_prog.report('Copying logs to Contour workspace')
        copy_log_alg.setProperty('InputWorkspace', self._sam_name)
        copy_log_alg.setProperty('OutputWorkspace', contour_ws)
        copy_log_alg.execute()

        log_prog.report('Adding sample logs to Contour workspace')
        self._add_sample_logs(contour_ws, self._erange, self._nbins[0])
        log_prog.report('Finialising log copying')

        # sort x axis
        s_api.SortXAxis(InputWorkspace=fit_ws,
                        OutputWorkspace=fit_ws,
                        EnableLogging=False)
        s_api.SortXAxis(InputWorkspace=contour_ws,
                        OutputWorkspace=contour_ws,
                        EnableLogging=False)

        self.setProperty('OutputWorkspaceFit', fit_ws)
        self.setProperty('OutputWorkspaceContour', contour_ws)
        log_prog.report('Setting workspace properties')
예제 #55
0
    def PyExec(self):
        """Executes the data reduction workflow."""
        progress = Progress(self, 0.0, 1.0, 9)
        self._report = utils.Report()
        self._subalgLogging = self.getProperty(
            common.PROP_SUBALG_LOGGING).value == common.SUBALG_LOGGING_ON
        wsNamePrefix = self.getProperty(common.PROP_OUTPUT_WS).valueAsStr
        cleanupMode = self.getProperty(common.PROP_CLEANUP_MODE).value
        self._names = utils.NameSource(wsNamePrefix, cleanupMode)
        self._cleanup = utils.Cleanup(cleanupMode, self._subalgLogging)

        # The variables 'mainWS' and 'monWS shall hold the current main
        # data throughout the algorithm.

        # Get input workspace.
        progress.report('Loading inputs')
        mainWS = self._inputWS()

        progress.report('Applying diagnostics')
        mainWS = self._applyDiagnostics(mainWS)

        # Vanadium normalization.
        progress.report('Normalising to vanadium')
        mainWS = self._normalizeToVana(mainWS)

        # Convert units from TOF to energy.
        progress.report('Converting to energy')
        mainWS = self._convertTOFToDeltaE(mainWS)

        # KiKf conversion.
        mainWS = self._correctByKiKf(mainWS)

        # Rebinning.
        progress.report('Rebinning in energy')
        mainWS = self._rebinInW(mainWS)

        # Divide the energy transfer workspace by bin widths.
        mainWS = self._convertToDistribution(mainWS)

        # Detector efficiency correction.
        progress.report('Correcting detector efficiency')
        mainWS = self._correctByDetectorEfficiency(mainWS)

        progress.report('Grouping detectors')
        mainWS = self._groupDetectors(mainWS)

        self._outputWSConvertedToTheta(mainWS)

        progress.report('Converting to q')
        mainWS = self._sOfQW(mainWS)
        mainWS = self._transpose(mainWS)
        self._finalize(mainWS)
        progress.report('Done')
예제 #56
0
    def PyExec(self):

        # 0) Create reporter to report progress
        steps = 9
        begin = 0
        end = 1.0
        prog_reporter = Progress(self, begin, end, steps)

        # 1) get input parameters from a user
        self._get_properties()
        prog_reporter.report("Input data from the user has been collected.")

        # 2) read ab initio data
        ab_initio_data = abins.AbinsData.from_calculation_data(
            self._vibrational_or_phonon_data_file, self._ab_initio_program)
        prog_reporter.report("Vibrational/phonon data has been read.")

        # 3) calculate S
        s_calculator = abins.SCalculatorFactory.init(
            filename=self._vibrational_or_phonon_data_file,
            temperature=self._temperature,
            sample_form=self._sample_form,
            abins_data=ab_initio_data,
            instrument=self._instrument,
            quantum_order_num=self._num_quantum_order_events,
            bin_width=self._bin_width)
        s_data = s_calculator.get_formatted_data()
        prog_reporter.report(
            "Dynamical structure factors have been determined.")

        # 4) get atoms for which S should be plotted
        self._extracted_ab_initio_data = ab_initio_data.get_atoms_data(
        ).extract()
        num_atoms = len(self._extracted_ab_initio_data)
        all_atms_smbls = list(
            set([
                self._extracted_ab_initio_data["atom_%s" % atom]["symbol"]
                for atom in range(num_atoms)
            ]))
        all_atms_smbls.sort()

        if len(self._atoms) == 0:  # case: all atoms
            atom_symbols = all_atms_smbls
            atom_numbers = []
        else:  # case selected atoms
            # Specific atoms are identified with prefix and integer index, e.g 'atom_5'. Other items are element symbols
            # A regular expression match is used to make the underscore separator optional and check the index format
            prefix = abins.constants.ATOM_PREFIX
            atom_symbols = [
                item for item in self._atoms if item[:len(prefix)] != prefix
            ]
            if len(atom_symbols) != len(
                    set(atom_symbols)):  # only different types
                raise ValueError(
                    "User atom selection (by symbol) contains repeated species. This is not permitted as "
                    "Abins cannot create multiple workspaces with the same name."
                )

            numbered_atom_test = re.compile('^' + prefix + r'_?(\d+)$')
            atom_numbers = [
                numbered_atom_test.findall(item) for item in self._atoms
            ]  # Matches will be lists of str
            atom_numbers = [int(match[0]) for match in atom_numbers
                            if match]  # Remove empty matches, cast rest to int

            if len(atom_numbers) != len(set(atom_numbers)):
                raise ValueError(
                    "User atom selection (by number) contains repeated atom. This is not permitted as Abins"
                    " cannot create multiple workspaces with the same name.")

            for atom_symbol in atom_symbols:
                if atom_symbol not in all_atms_smbls:
                    raise ValueError(
                        "User defined atom selection (by element) '%s': not present in the system."
                        % atom_symbol)

            for atom_number in atom_numbers:
                if atom_number < 1 or atom_number > num_atoms:
                    raise ValueError(
                        "Invalid user atom selection (by number) '%s%s': out of range (%s - %s)"
                        % (prefix, atom_number, 1, num_atoms))

            # Final sanity check that everything in "atoms" field was understood
            if len(atom_symbols) + len(atom_numbers) < len(self._atoms):
                elements_report = " Symbols: " + ", ".join(
                    atom_symbols) if len(atom_symbols) else ""
                numbers_report = " Numbers: " + ", ".join(atom_numbers) if len(
                    atom_numbers) else ""
                raise ValueError(
                    "Not all user atom selections ('atoms' option) were understood."
                    + elements_report + numbers_report)

        prog_reporter.report(
            "Atoms, for which dynamical structure factors should be plotted, have been determined."
        )

        # at the moment only types of atom, e.g, for  benzene three options -> 1) C, H;  2) C; 3) H
        # 5) create workspaces for atoms in interest
        workspaces = []
        if self._sample_form == "Powder":
            workspaces.extend(
                self._create_partial_s_per_type_workspaces(
                    atoms_symbols=atom_symbols, s_data=s_data))
            workspaces.extend(
                self._create_partial_s_per_type_workspaces(
                    atom_numbers=atom_numbers, s_data=s_data))
        prog_reporter.report(
            "Workspaces with partial dynamical structure factors have been constructed."
        )

        # 6) Create a workspace with sum of all atoms if required
        if self._sum_contributions:
            total_atom_workspaces = []
            for ws in workspaces:
                if "total" in ws:
                    total_atom_workspaces.append(ws)
            total_workspace = self._create_total_workspace(
                partial_workspaces=total_atom_workspaces)
            workspaces.insert(0, total_workspace)
            prog_reporter.report(
                "Workspace with total S has been constructed.")

        # 7) add experimental data if available to the collection of workspaces
        if self._experimental_file != "":
            workspaces.insert(
                0,
                self._create_experimental_data_workspace().name())
            prog_reporter.report(
                "Workspace with the experimental data has been constructed.")

        GroupWorkspaces(InputWorkspaces=workspaces,
                        OutputWorkspace=self._out_ws_name)

        # 8) save workspaces to ascii_file
        num_workspaces = mtd[self._out_ws_name].getNumberOfEntries()
        for wrk_num in range(num_workspaces):
            wrk = mtd[self._out_ws_name].getItem(wrk_num)
            SaveAscii(InputWorkspace=Scale(wrk, 1.0 / self._bin_width,
                                           "Multiply"),
                      Filename=wrk.name() + ".dat",
                      Separator="Space",
                      WriteSpectrumID=False)
        prog_reporter.report("All workspaces have been saved to ASCII files.")

        # 9) set  OutputWorkspace
        self.setProperty('OutputWorkspace', self._out_ws_name)
        prog_reporter.report(
            "Group workspace with all required  dynamical structure factors has been constructed."
        )
예제 #57
0
    def PyExec(self):
        """Execute the data collection workflow."""
        progress = Progress(self, 0.0, 1.0, 9)
        self._report = utils.Report()
        self._subalgLogging = self.getProperty(
            common.PROP_SUBALG_LOGGING).value == common.SUBALG_LOGGING_ON
        namePrefix = self.getProperty(common.PROP_OUTPUT_WS).valueAsStr
        cleanupMode = self.getProperty(common.PROP_CLEANUP_MODE).value
        self._cleanup = utils.Cleanup(cleanupMode, self._subalgLogging)
        self._names = utils.NameSource(namePrefix, cleanupMode)

        # The variables 'mainWS' and 'monWS shall hold the current main
        # data throughout the algorithm.

        # Get input workspace.
        progress.report('Loading inputs')
        mainWS = self._inputWS()

        # Extract monitors to a separate workspace.
        progress.report('Extracting monitors')
        mainWS, monWS = self._separateMons(mainWS)

        # Save the main workspace for later use, if needed.
        rawWS = None
        if not self.getProperty(common.PROP_OUTPUT_RAW_WS).isDefault:
            rawWS = mainWS
            self._cleanup.protect(rawWS)

        # Normalisation to monitor/time, if requested.
        progress.report('Normalising to monitor/time')
        monWS = self._flatBkgMon(monWS)
        monEPPWS = self._createEPPWSMon(monWS)
        mainWS = self._normalize(mainWS, monWS, monEPPWS)

        # Time-independent background.
        progress.report('Calculating backgrounds')
        mainWS = self._flatBkgDet(mainWS)

        # Calibrate incident energy, if requested.
        progress.report('Calibrating incident energy')
        mainWS, monWS = self._calibrateEi(mainWS, monWS, monEPPWS)
        self._cleanup.cleanup(monWS, monEPPWS)

        # Add the Ei as Efixed instrument parameter
        _addEfixedInstrumentParameter(mainWS)

        progress.report('Correcting TOF')
        mainWS = self._correctTOFAxis(mainWS)
        self._outputRaw(mainWS, rawWS)

        # Find elastic peak positions.
        progress.report('Calculating EPPs')
        self._outputDetEPPWS(mainWS)

        self._finalize(mainWS)
        progress.report('Done')
예제 #58
0
    def PyExec(self):  # noqa C901
        inWS = self.getProperty("InputWorkspace").value
        normWS = self.getProperty("NormalisationWorkspace").value
        _norm = bool(normWS)

        instrument = inWS.getExperimentInfo(0).getInstrument().getName()

        dim0_min, dim0_max, dim0_bins = self.getProperty('BinningDim0').value
        dim1_min, dim1_max, dim1_bins = self.getProperty('BinningDim1').value
        dim2_min, dim2_max, dim2_bins = self.getProperty('BinningDim2').value
        dim0_bins = int(dim0_bins)
        dim1_bins = int(dim1_bins)
        dim2_bins = int(dim2_bins)
        dim0_bin_size = (dim0_max - dim0_min) / dim0_bins
        dim1_bin_size = (dim1_max - dim1_min) / dim1_bins
        dim2_bin_size = (dim2_max - dim2_min) / dim2_bins

        data_array = inWS.getSignalArray(
        )  # getSignalArray returns a F_CONTIGUOUS view of the signal array

        number_of_runs = data_array.shape[2]

        progress = Progress(self, 0.0, 1.0, number_of_runs + 4)

        # Get rotation array
        if instrument == "HB3A":
            omega = np.deg2rad(
                inWS.getExperimentInfo(0).run().getProperty('omega').value)
            chi = np.deg2rad(
                inWS.getExperimentInfo(0).run().getProperty('chi').value)
            phi = np.deg2rad(
                inWS.getExperimentInfo(0).run().getProperty('phi').value)
        else:
            s1 = np.deg2rad(
                inWS.getExperimentInfo(0).run().getProperty('s1').value
            ) + np.deg2rad(self.getProperty("S1Offset").value)

        normaliseBy = self.getProperty("NormaliseBy").value
        if normaliseBy == "Monitor":
            if instrument == "HB3A":
                scale = np.asarray(
                    inWS.getExperimentInfo(0).run().getProperty(
                        'monitor').value)
            else:
                scale = np.asarray(
                    inWS.getExperimentInfo(0).run().getProperty(
                        'monitor_count').value)
        elif normaliseBy == "Time":
            if instrument == "HB3A":
                scale = np.asarray(
                    inWS.getExperimentInfo(0).run().getProperty('time').value)
            else:
                scale = np.asarray(
                    inWS.getExperimentInfo(0).run().getProperty(
                        'duration').value)
        else:
            scale = np.ones(number_of_runs)

        if _norm:
            if normaliseBy == "Monitor":
                if instrument == "HB3A":
                    norm_scale = np.sum(
                        normWS.getExperimentInfo(0).run().getProperty(
                            'monitor').value)
                else:
                    norm_scale = np.sum(
                        normWS.getExperimentInfo(0).run().getProperty(
                            'monitor_count').value)
            elif normaliseBy == "Time":
                if instrument == "HB3A":
                    norm_scale = np.sum(
                        normWS.getExperimentInfo(0).run().getProperty(
                            'time').value)
                else:
                    norm_scale = np.sum(
                        normWS.getExperimentInfo(0).run().getProperty(
                            'duration').value)
            else:
                norm_scale = 1.
            norm_array = normWS.getSignalArray().sum(axis=2)

        W = np.eye(3)
        UBW = np.eye(3)
        if self.getProperty("Frame").value == 'HKL':
            W[:, 0] = self.getProperty('Uproj').value
            W[:, 1] = self.getProperty('Vproj').value
            W[:, 2] = self.getProperty('Wproj').value
            ubWS = self.getProperty("UBWorkspace").value
            if ubWS:
                try:
                    ol = ubWS.sample().getOrientedLattice()
                except AttributeError:
                    ol = ubWS.getExperimentInfo(
                        0).sample().getOrientedLattice()
                logger.notice("Using UB matrix from {} with {}".format(
                    ubWS.name(), ol))
            else:
                ol = inWS.getExperimentInfo(0).sample().getOrientedLattice()
                logger.notice("Using UB matrix from {} with {}".format(
                    inWS.name(), ol))
            UB = ol.getUB()
            UBW = np.dot(UB, W)
            char_dict = {0: '0', 1: '{1}', -1: '-{1}'}
            chars = ['H', 'K', 'L']
            names = [
                '[' + ','.join(
                    char_dict.get(j, '{0}{1}').format(
                        j, chars[np.argmax(np.abs(W[:, i]))])
                    for j in W[:, i]) + ']' for i in range(3)
            ]
            units = 'in {:.3f} A^-1,in {:.3f} A^-1,in {:.3f} A^-1'.format(
                ol.qFromHKL(W[0]).norm(),
                ol.qFromHKL(W[1]).norm(),
                ol.qFromHKL(W[2]).norm())
            frames = 'HKL,HKL,HKL'
            k = 1 / self.getProperty(
                "Wavelength"
            ).value  # Not 2pi/wavelength to save dividing by 2pi later
        else:
            names = 'Q_sample_x,Q_sample_y,Q_sample_z'
            units = 'Angstrom^-1,Angstrom^-1,Angstrom^-1'
            frames = 'QSample,QSample,QSample'
            k = 2 * np.pi / self.getProperty("Wavelength").value

        progress.report('Calculating Qlab for each pixel')
        if inWS.getExperimentInfo(0).run().hasProperty('twotheta'):
            polar = np.array(
                inWS.getExperimentInfo(0).run().getProperty('twotheta').value)
        else:
            di = inWS.getExperimentInfo(0).detectorInfo()
            polar = np.array([
                di.twoTheta(i) for i in range(di.size()) if not di.isMonitor(i)
            ])
            if inWS.getExperimentInfo(0).getInstrument().getName() == 'HB3A':
                polar = polar.reshape(512 * 3, 512).T.flatten()

        if inWS.getExperimentInfo(0).run().hasProperty('twotheta'):
            azim = np.array(
                inWS.getExperimentInfo(0).run().getProperty('azimuthal').value)
        else:
            di = inWS.getExperimentInfo(0).detectorInfo()
            azim = np.array([
                di.azimuthal(i) for i in range(di.size())
                if not di.isMonitor(i)
            ])
            if inWS.getExperimentInfo(0).getInstrument().getName() == 'HB3A':
                azim = azim.reshape(512 * 3, 512).T.flatten()

        qlab = np.vstack(
            (np.sin(polar) * np.cos(azim), np.sin(polar) * np.sin(azim),
             np.cos(polar) - 1)).T * -k  # Kf - Ki(0,0,1)

        progress.report('Calculating Q volume')

        output = np.zeros((dim0_bins + 2, dim1_bins + 2, dim2_bins + 2))
        outputr = output.ravel()

        output_scale = np.zeros_like(output)
        output_scaler = output_scale.ravel()

        if _norm:
            output_norm = np.zeros_like(output)
            output_normr = output_norm.ravel()
            output_norm_scale = np.zeros_like(output)
            output_norm_scaler = output_norm_scale.ravel()

        bin_size = np.array([[dim0_bin_size], [dim1_bin_size],
                             [dim2_bin_size]])

        offset = np.array([[dim0_min / dim0_bin_size],
                           [dim1_min / dim1_bin_size],
                           [dim2_min / dim2_bin_size]]) - 0.5

        assert not data_array[:, :, 0].flags.owndata
        assert not data_array[:, :, 0].ravel('F').flags.owndata
        assert data_array[:, :, 0].flags.fnc

        for n in range(number_of_runs):
            if instrument == "HB3A":
                R1 = np.array([
                    [np.cos(omega[n]), 0, -np.sin(omega[n])],  # omega 0,1,0,-1
                    [0, 1, 0],
                    [np.sin(omega[n]), 0,
                     np.cos(omega[n])]
                ])
                R2 = np.array([
                    [np.cos(chi[n]), np.sin(chi[n]), 0],  # chi 0,0,1,-1
                    [-np.sin(chi[n]), np.cos(chi[n]), 0],
                    [0, 0, 1]
                ])
                R3 = np.array([
                    [np.cos(phi[n]), 0, -np.sin(phi[n])],  # phi 0,1,0,-1
                    [0, 1, 0],
                    [np.sin(phi[n]), 0, np.cos(phi[n])]
                ])
                R = np.dot(np.dot(R1, R2), R3)
            else:
                R = np.array([
                    [np.cos(s1[n]), 0, np.sin(s1[n])],  # s1 0,1,0,1
                    [0, 1, 0],
                    [-np.sin(s1[n]), 0, np.cos(s1[n])]
                ])
            RUBW = np.dot(R, UBW)
            q = np.round(
                np.dot(np.linalg.inv(RUBW), qlab.T) / bin_size -
                offset).astype(np.int)
            q_index = np.ravel_multi_index(
                q, (dim0_bins + 2, dim1_bins + 2, dim2_bins + 2), mode='clip')
            q_uniq, inverse = np.unique(q_index, return_inverse=True)
            outputr[q_uniq] += np.bincount(inverse, data_array[:, :,
                                                               n].ravel('F'))
            output_scaler[q_uniq] += np.bincount(inverse) * scale[n]
            if _norm:
                output_normr[q_uniq] += np.bincount(inverse,
                                                    norm_array.ravel('F'))
                output_norm_scaler[q_uniq] += np.bincount(inverse)

            progress.report()

        if _norm:
            output *= output_norm_scale * norm_scale
            output_norm *= output_scale
        else:
            output_norm = output_scale

        if self.getProperty('KeepTemporaryWorkspaces').value:
            # Create data workspace
            progress.report('Creating data MDHistoWorkspace')
            createWS_alg = self.createChildAlgorithm("CreateMDHistoWorkspace",
                                                     enableLogging=False)
            createWS_alg.setProperty("SignalInput", output[1:-1, 1:-1,
                                                           1:-1].ravel('F'))
            createWS_alg.setProperty(
                "ErrorInput", np.sqrt(output[1:-1, 1:-1, 1:-1].ravel('F')))
            createWS_alg.setProperty("Dimensionality", 3)
            createWS_alg.setProperty(
                "Extents",
                '{},{},{},{},{},{}'.format(dim0_min, dim0_max, dim1_min,
                                           dim1_max, dim2_min, dim2_max))
            createWS_alg.setProperty(
                "NumberOfBins", '{},{},{}'.format(dim0_bins, dim1_bins,
                                                  dim2_bins))
            createWS_alg.setProperty("Names", names)
            createWS_alg.setProperty("Units", units)
            createWS_alg.setProperty("Frames", frames)
            createWS_alg.execute()
            outWS_data = createWS_alg.getProperty("OutputWorkspace").value
            mtd.addOrReplace(
                self.getPropertyValue("OutputWorkspace") + '_data', outWS_data)

            # Create normalisation workspace
            progress.report('Creating norm MDHistoWorkspace')
            createWS_alg = self.createChildAlgorithm("CreateMDHistoWorkspace",
                                                     enableLogging=False)
            createWS_alg.setProperty("SignalInput",
                                     output_norm[1:-1, 1:-1, 1:-1].ravel('F'))
            createWS_alg.setProperty(
                "ErrorInput", np.sqrt(output_norm[1:-1, 1:-1,
                                                  1:-1].ravel('F')))
            createWS_alg.setProperty("Dimensionality", 3)
            createWS_alg.setProperty(
                "Extents",
                '{},{},{},{},{},{}'.format(dim0_min, dim0_max, dim1_min,
                                           dim1_max, dim2_min, dim2_max))
            createWS_alg.setProperty(
                "NumberOfBins", '{},{},{}'.format(dim0_bins, dim1_bins,
                                                  dim2_bins))
            createWS_alg.setProperty("Names", names)
            createWS_alg.setProperty("Units", units)
            createWS_alg.setProperty("Frames", frames)
            createWS_alg.execute()
            mtd.addOrReplace(
                self.getPropertyValue("OutputWorkspace") + '_normalization',
                createWS_alg.getProperty("OutputWorkspace").value)

        old_settings = np.seterr(
            divide='ignore', invalid='ignore'
        )  # Ignore RuntimeWarning: invalid value encountered in true_divide
        output /= output_norm  # We often divide by zero here and we get NaN's, this is desired behaviour
        np.seterr(**old_settings)

        progress.report('Creating MDHistoWorkspace')
        createWS_alg = self.createChildAlgorithm("CreateMDHistoWorkspace",
                                                 enableLogging=False)
        createWS_alg.setProperty("SignalInput", output[1:-1, 1:-1,
                                                       1:-1].ravel('F'))
        createWS_alg.setProperty("ErrorInput",
                                 np.sqrt(output[1:-1, 1:-1, 1:-1].ravel('F')))
        createWS_alg.setProperty("Dimensionality", 3)
        createWS_alg.setProperty(
            "Extents",
            '{},{},{},{},{},{}'.format(dim0_min, dim0_max, dim1_min, dim1_max,
                                       dim2_min, dim2_max))
        createWS_alg.setProperty(
            "NumberOfBins", '{},{},{}'.format(dim0_bins, dim1_bins, dim2_bins))
        createWS_alg.setProperty("Names", names)
        createWS_alg.setProperty("Units", units)
        createWS_alg.setProperty("Frames", frames)
        createWS_alg.execute()
        outWS = createWS_alg.getProperty("OutputWorkspace").value

        # Copy experiment infos
        if inWS.getNumExperimentInfo() > 0:
            outWS.copyExperimentInfos(inWS)

        outWS.getExperimentInfo(0).run().addProperty('RUBW_MATRIX',
                                                     list(UBW.flatten()), True)
        outWS.getExperimentInfo(0).run().addProperty('W_MATRIX',
                                                     list(W.flatten()), True)
        try:
            if outWS.getExperimentInfo(0).sample().hasOrientedLattice():
                outWS.getExperimentInfo(0).sample().getOrientedLattice().setUB(
                    UB)
        except NameError:
            pass

        if self.getProperty('KeepTemporaryWorkspaces').value:
            outWS_data.copyExperimentInfos(outWS)

        progress.report()
        self.setProperty("OutputWorkspace", outWS)
예제 #59
0
    def PyExec(self):
        # 0) Create reporter to report progress
        # Before calculating S, we use 10% of the bar for two steps
        begin, end, steps = 0, 0.1, 2
        prog_reporter = Progress(self, begin, end, steps)

        # 1) get input parameters from a user
        self._get_properties()
        prog_reporter.report("Input data from the user has been collected.")

        # 2) read ab initio data
        ab_initio_data = abins.AbinsData.from_calculation_data(
            self._vibrational_or_phonon_data_file, self._ab_initio_program)
        prog_reporter.report("Vibrational/phonon data has been read.")

        # 3) calculate S
        # Reset reporter to span range 10%-80%; s_calculator will decide how many steps are appropriate
        # so insert placeholder "1" for now.
        prog_reporter.resetNumSteps(1, 0.1, 0.8)

        s_calculator = abins.SCalculatorFactory.init(
            filename=self._vibrational_or_phonon_data_file,
            temperature=self._temperature,
            sample_form=self._sample_form,
            abins_data=ab_initio_data,
            instrument=self._instrument,
            quantum_order_num=self._num_quantum_order_events,
            autoconvolution=self._autoconvolution)
        s_calculator.progress_reporter = prog_reporter
        s_data = s_calculator.get_formatted_data()

        # Hold reporter at 80% for this message
        prog_reporter.resetNumSteps(1, 0.8, 0.80000001)
        prog_reporter.report(
            "Dynamical structure factors have been determined.")
        # Now determine number of remaining messages and set reporter for rest of run:
        n_messages = 3 + bool(self._sum_contributions) + bool(
            self._experimental_file) + bool(self._save_ascii)
        prog_reporter.resetNumSteps(n_messages, 0.8, 1)

        # 4) get atoms for which S should be plotted
        atoms_data = ab_initio_data.get_atoms_data()
        atom_numbers, atom_symbols = self.get_atom_selection(
            atoms_data=atoms_data, selection=self._atoms)
        prog_reporter.report(
            "Atoms, for which dynamical structure factors should be plotted, have been determined."
        )

        # 5) create workspaces for atoms in interest
        workspaces = []

        workspaces.extend(
            self.create_workspaces(atoms_symbols=atom_symbols,
                                   s_data=s_data,
                                   atoms_data=atoms_data,
                                   max_quantum_order=self._max_event_order))
        workspaces.extend(
            self.create_workspaces(atom_numbers=atom_numbers,
                                   s_data=s_data,
                                   atoms_data=atoms_data,
                                   max_quantum_order=self._max_event_order))
        prog_reporter.report(
            "Workspaces with partial dynamical structure factors have been constructed."
        )

        # 6) Create a workspace with sum of all atoms if required
        if self._sum_contributions:
            self.create_total_workspace(workspaces)
            prog_reporter.report(
                "Workspace with total S has been constructed.")

        # 7) add experimental data if available to the collection of workspaces
        if self._experimental_file != "":
            workspaces.insert(
                0,
                self._create_experimental_data_workspace().name())
            prog_reporter.report(
                "Workspace with the experimental data has been constructed.")

        gws = GroupWorkspaces(InputWorkspaces=workspaces,
                              OutputWorkspace=self._out_ws_name)

        # 7b) Convert units
        if self._energy_units == 'meV':
            ConvertUnits(InputWorkspace=gws,
                         OutputWorkspace=gws,
                         EMode='Indirect',
                         Target='DeltaE')

        # 8) save workspaces to ascii_file
        if self._save_ascii:
            self.write_workspaces_to_ascii(ws_name=self._out_ws_name,
                                           scale=(1.0 / self._bin_width))
            prog_reporter.report(
                "All workspaces have been saved to ASCII files.")

        # 9) set  OutputWorkspace
        self.setProperty('OutputWorkspace', self._out_ws_name)
        prog_reporter.report(
            "Group workspace with all required  dynamical structure factors has been constructed."
        )
예제 #60
0
class PowderILLEfficiency(PythonAlgorithm):

    _out_name = None  # the name of the output workspace
    _input_files = None  # input files (numor), must be detector scans (to list for D2B, to merge for D20)
    _calib_file = None  # file containing previously derived calibration constants
    _progress = None  # progress tracking
    _method = None  # calibration method
    _scan_points = None  # number of scan points (time indices)
    _out_response = None  # the name of the second output workspace with merged response
    _bin_offset = None  # this holds int(scan step / pixel size)
    _n_det = None  # number of detector pixels for D20 (=3072)
    _normalise_to = None  # normalisation option
    _pixel_range = None  # range of the pixels to derive calibration for D20, e.g. 65,3072
    _regions_of_interest = None  # ROI to normalise to, e.g. 10,50,70,100, typically just one range, used for D20
    _interpolate = None  # whether to interpolate 2thetas before taking relative ratios (D20)
    _excluded_ranges = None  # 2theta ranges to exclude when deriving the calibration factor, e.g. -20,0,40,50
    _live_pixels = None  # holds the list of cells that are not zero counting
    _derivation_method = ''  # sequential reference (D20) or global reference (D2B)
    _n_scan_files = None  # number of standard scan files for D2B (~30)
    _n_scans_per_file = None  # number of scan points in a standard scan for D2B (=25)
    _n_tubes = None  # number of tubes in D2B (=128)
    _n_pixels_per_tube = None  # number of pixels per tube in D2B (=128)
    _n_iterations = None  # number of iterations (=1); used for D2B
    _pixels_to_trim = None  # number of pixels to trim from top and bottom of tubes for chi2 calculation (D2B)
    _mask_criterion = None  # the range of efficiency constant values, outside of which they should be set to 0

    def _hide(self, name):
        return '__' + self._out_name + '_' + name

    def category(self):
        return "ILL\\Diffraction;Diffraction\\Reduction;Diffraction\\Calibration"

    def summary(self):
        return "Performs detector efficiency correction calculation for scanning " \
               "monochromatic powder diffraction instruments D20 and D2B at ILL."

    def seeAlso(self):
        return ["PowderILLDetectorScan", "PowderILLParameterScan"]

    def name(self):
        return "PowderILLEfficiency"

    def PyInit(self):
        self.declareProperty(
            MultipleFileProperty('CalibrationRun',
                                 action=FileAction.Load,
                                 extensions=['nxs']),
            doc=
            'File path of calibration runs (numors). Must be detector scans.')

        self.declareProperty(
            FileProperty('CalibrationFile',
                         '',
                         action=FileAction.OptionalLoad,
                         extensions=['nxs']),
            doc='Optional file containing previous calibration constants.')

        self.declareProperty(
            name='CalibrationMethod',
            defaultValue='Median',
            validator=StringListValidator(['Median', 'Mean',
                                           'MostLikelyMean']),
            doc='The method of how the calibration constant of a pixel '
            'is derived from the distribution of ratios.')

        self.declareProperty(
            name='DerivationMethod',
            defaultValue='SequentialSummedReference1D',
            validator=StringListValidator(
                ['SequentialSummedReference1D', 'GlobalSummedReference2D']),
            doc=
            'Choose sequential for D20 (1D detector), global for D2B (2D detector).'
        )

        self.declareProperty(
            name='InterpolateOverlappingAngles',
            defaultValue=False,
            doc=
            'Whether to interpolate scattering angle values in overlapping regions (D20 only).'
        )

        self.declareProperty(
            name='NormaliseTo',
            defaultValue='None',
            validator=StringListValidator(['None', 'Monitor', 'ROI']),
            doc=
            'Normalise to monitor or ROI counts before deriving the calibration.'
        )

        thetaRangeValidator = FloatArrayOrderedPairsValidator()

        self.declareProperty(
            FloatArrayProperty(name='ROI',
                               values=[0, 100.],
                               validator=thetaRangeValidator),
            doc=
            'Scattering angle regions of interest for normalisation [degrees].'
        )

        normaliseToROI = VisibleWhenProperty('NormaliseTo',
                                             PropertyCriterion.IsEqualTo,
                                             'ROI')
        self.setPropertySettings('ROI', normaliseToROI)

        self.declareProperty(
            FloatArrayProperty(name='ExcludedRange',
                               values=[],
                               validator=thetaRangeValidator),
            doc='Scattering angle regions to exclude from the computation of '
            'relative calibration constants; for example, the beam stop [degrees]. '
        )

        pixelRangeValidator = CompositeValidator()
        greaterThanOne = IntArrayBoundedValidator(lower=1)
        lengthTwo = IntArrayLengthValidator()
        lengthTwo.setLength(2)
        orderedPairsValidator = IntArrayOrderedPairsValidator()
        pixelRangeValidator.add(greaterThanOne)
        pixelRangeValidator.add(lengthTwo)
        pixelRangeValidator.add(orderedPairsValidator)

        self.declareProperty(
            IntArrayProperty(name='PixelRange',
                             values=[1, 3072],
                             validator=pixelRangeValidator),
            doc=
            'Range of the pixel numbers to compute the calibration factors for (D20 only); '
            'for the other pixels outside the range, the factor will be set to 1.'
        )

        self.declareProperty(
            MatrixWorkspaceProperty('OutputResponseWorkspace',
                                    '',
                                    optional=PropertyMode.Optional,
                                    direction=Direction.Output),
            doc=
            'Output workspace containing the summed diffraction patterns of all the overlapping pixels.'
        )

        self.declareProperty(
            MatrixWorkspaceProperty('OutputWorkspace',
                                    '',
                                    direction=Direction.Output),
            doc=
            'Output workspace containing the calibration constants (inverse of efficiency) for each pixel.'
        )

        self.declareProperty(
            name='NumberOfIterations',
            defaultValue=1,
            validator=IntBoundedValidator(lower=0, upper=10),
            doc=
            'Number of iterations to perform (D2B only): 0 means auto; that is, the '
            'iterations will terminate after reaching some Chi2/NdoF.')

        maskCriterionValidator = CompositeValidator()
        arrayLengthTwo = FloatArrayLengthValidator()
        arrayLengthTwo.setLengthMax(2)
        orderedPairs = FloatArrayOrderedPairsValidator()
        maskCriterionValidator.add(arrayLengthTwo)
        maskCriterionValidator.add(orderedPairs)

        self.declareProperty(
            FloatArrayProperty(name='MaskCriterion',
                               values=[],
                               validator=maskCriterionValidator),
            doc='Efficiency constants outside this range will be set to zero.')

        self.declareProperty(
            name='UseCalibratedData',
            defaultValue=False,
            doc=
            'Whether or not to use the calibrated data in the NeXus files (D2B only).'
        )

    def validateInputs(self):
        issues = dict()

        if self.getPropertyValue(
                "DerivationMethod") == "GlobalSummedReference2D":
            if self.getProperty("InterpolateOverlappingAngles").value:
                issues[
                    "InterpolateOverlappingAngles"] = "Interpolation option is not supported for global method"
            if self.getPropertyValue("NormaliseTo") == "ROI":
                issues[
                    "NormaliseTo"] = "ROI normalisation is not supported for global method"
            method = self.getPropertyValue("CalibrationMethod")
            if method == "MostLikelyMean" or method == "Mean":
                issues[
                    "CalibrationMethod"] = method + " is not supported for global reference method"

        if self.getPropertyValue(
                "DerivationMethod") == "SequentialSummedReference1D":
            if self.getProperty("NumberOfIterations").value != 1:
                issues[
                    "NumberOfIterations"] = "NumberOfIterations is not supported for sequential method"

        return issues

    def _update_reference(self, ws, cropped_ws, ref_ws, factor):
        """
            Merges the response of the current pixel with the current combined reference in the
            overlapping region, taking into account the relative scale factor.
            Updates the reference workspace to contain the weighted sum of the two, or the clone
            of one or the other if the scale factor is pathological.
            @param ws: input workspace containing data from the current pixel
            @param cropped_ws: same as ws, but last bins cropped to match the size of the reference
            @param ref_ws: current reference workspace
            @param factor: relative efficiency factor for the current pixel
        """
        x = mtd[ws].readX(0)[-self._bin_offset]
        last_bins = ws + '_last_bins'
        CropWorkspace(InputWorkspace=ws, XMin=x, OutputWorkspace=last_bins)

        if factor == 0.:
            CloneWorkspace(InputWorkspace=cropped_ws, OutputWorkspace=ref_ws)
        elif str(factor) != 'inf' and str(factor) != 'nan':
            Scale(InputWorkspace=cropped_ws,
                  OutputWorkspace=cropped_ws,
                  Factor=factor)
            Scale(InputWorkspace=last_bins,
                  OutputWorkspace=last_bins,
                  Factor=factor)
            WeightedMean(InputWorkspace1=ref_ws,
                         InputWorkspace2=cropped_ws,
                         OutputWorkspace=ref_ws)

        ConjoinXRuns(InputWorkspaces=[ref_ws, last_bins],
                     OutputWorkspace=ref_ws)
        x = mtd[ref_ws].readX(0)[self._bin_offset]
        CropWorkspace(InputWorkspace=ref_ws, XMin=x, OutputWorkspace=ref_ws)
        DeleteWorkspace(last_bins)

    def _exclude_ranges(self, ratio_ws):
        """
            Excludes 2theta ranges from the ratio workspace
            @param ratio_ws : the name of the ratio workspace
        """
        ConvertToHistogram(InputWorkspace=ratio_ws, OutputWorkspace=ratio_ws)
        equator = int(mtd[ratio_ws].getNumberHistograms() / 2)
        x = mtd[ratio_ws].readX(equator)
        xmin = x[0]
        xmax = x[-1]
        for excluded_range in self._excluded_ranges:
            if excluded_range[1] > xmin and excluded_range[0] < xmax:
                if excluded_range[0] > xmin:
                    xmin = excluded_range[0]
                if excluded_range[1] < xmax:
                    xmax = excluded_range[1]
                MaskBins(InputWorkspace=ratio_ws,
                         OutputWorkspace=ratio_ws,
                         XMin=xmin,
                         XMax=xmax)
        ConvertToPointData(InputWorkspace=ratio_ws, OutputWorkspace=ratio_ws)

    def _compute_relative_factor_1D(self, ratio_ws):
        """
            Calculates the relative detector efficiency from the workspace containing response ratios.
            Implements mean, median and most likely mean methods.
            @param ratio_ws: input workspace containing response ratios
            @returns: relative calibration factor (scalar)
        """
        if len(self._excluded_ranges) != 0:
            self._exclude_ranges(ratio_ws)
        ratios = mtd[ratio_ws].extractY()
        ratios = ratios[np.nonzero(ratios)]
        factor = 1.
        if ratios.any():
            if self._method == 'Median':
                factor = np.median(ratios)
            elif self._method == 'Mean':
                factor = np.mean(ratios)
            elif self._method == 'MostLikelyMean':
                factor = MostLikelyMean(ratios)
        return factor

    def _compute_relative_factor_2D(self, ratio_ws, tube_index):
        """
            Calculates the relative detector efficiency from the workspace containing response ratios.
            Implements mean, median and most likely mean methods.
            @param ratio_ws: input workspace containing response ratios
            @returns: relative calibration factor (1D array, factor per pixel in the tube)
        """
        if len(self._excluded_ranges) != 0:
            self._exclude_ranges(ratio_ws)
        ratios = mtd[ratio_ws].extractY()
        if tube_index == 0:
            ratios = ratios[:, 0:-self._n_scans_per_file]
        elif tube_index == self._n_tubes - 1:
            ratios = ratios[:, self._n_scans_per_file:]
        factors = np.ones(ratios.shape[0])
        ratios = ma.masked_array(ratios, mask=[ratios == 0])
        ratios = ma.masked_invalid(ratios)
        if self._method == 'Median':
            factors = np.array(ma.median(ratios, axis=1))
        elif self._method == 'Mean':
            factors = np.array(ma.mean(ratios, axis=1))
        return factors

    def _validate_scan(self, scan_ws):
        """
            Ensures that the input workspace corresponds to a detector scan
            @param scan_ws: input detector scan workspace
            @throws: RuntimeError if the workspace is not a detector scan
        """
        is_scanned = False
        try:
            mtd[scan_ws].detectorInfo().isMasked(0)
        except RuntimeError:
            is_scanned = True
        if not is_scanned:
            raise RuntimeError('The input run is not a detector scan.')

    def _reshape(self, raw_ws, ws_2d):
        """
            Reshapes the single column detector scan workspace to a 2D workspace
            with n_det+1 rows (including the monitor) and n_scan_points columns
            Sets the signed 2theta as x-values. The output is a ragged workspace.
            Sample logs are copied over, but the instrument is lost on purpose.
            @param raw_ws : raw detector scan workspace
            @param ws_2d : the name of the returned workspace
        """
        y = mtd[raw_ws].extractY()
        e = mtd[raw_ws].extractE()
        x = mtd[raw_ws].getAxis(1).extractValues()
        shape = [self._n_det + 1, self._scan_points]
        y_2d = np.reshape(y, shape)
        e_2d = np.reshape(e, shape)
        x_2d = np.reshape(x, shape)
        CreateWorkspace(DataX=x_2d,
                        DataY=y_2d,
                        DataE=e_2d,
                        NSpec=self._n_det + 1,
                        OutputWorkspace=ws_2d)
        CopyLogs(InputWorkspace=raw_ws, OutputWorkspace=ws_2d)

    def _chi_squared(self, calib_current):
        """
            Calculates the termination parameter for automatic iterations for global method (D2B)
            @param calib_current : the residual calibration map at the current iteration
            @return : chi2/NdoF
        """
        start = self._pixels_to_trim
        end = self._n_pixels_per_tube - self._pixels_to_trim
        y = mtd[calib_current].extractY()[:, start:end]
        diff = (y - 1)**2
        chi2 = np.sum(diff)
        ndof = (self._n_pixels_per_tube -
                2 * self._pixels_to_trim) * self._n_tubes
        return chi2 / ndof

    def _set_input_properties(self):
        """
            Sets up the input properties of the algorithm
        """
        self._input_files = self.getPropertyValue('CalibrationRun')
        self._calib_file = self.getPropertyValue('CalibrationFile')
        self._method = self.getPropertyValue('CalibrationMethod')
        self._derivation_method = self.getPropertyValue('DerivationMethod')
        self._normalise_to = self.getPropertyValue('NormaliseTo')
        self._regions_of_interest = self.getProperty('ROI').value
        self._excluded_ranges = self.getProperty('ExcludedRange').value
        self._interpolate = self.getProperty(
            'InterpolateOverlappingAngles').value
        self._pixel_range = self.getProperty('PixelRange').value
        self._out_response = self.getPropertyValue('OutputResponseWorkspace')
        self._out_name = self.getPropertyValue('OutputWorkspace')
        self._n_iterations = self.getProperty('NumberOfIterations').value
        self._mask_criterion = self.getProperty('MaskCriterion').value

    def _configure_sequential(self, raw_ws):
        """
            Configures the calibration with SequentialSummedReference1D method (D20)
            @param : the name of the raw detector scan (merged) workspace
        """
        self._scan_points = mtd[raw_ws].getRun().getLogData('ScanSteps').value
        self.log().information('Number of scan steps is: ' +
                               str(self._scan_points))
        self._n_det = mtd[raw_ws].detectorInfo().size() - 1
        self.log().information('Number of detector pixels is: ' +
                               str(self._n_det))
        pixel_size = mtd[raw_ws].getRun().getLogData('PixelSize').value
        theta_zeros = mtd[raw_ws].getRun().getLogData('2theta.Position')
        scan_step_in_pixel_numbers = (theta_zeros.nthValue(1) -
                                      theta_zeros.nthValue(0)) / pixel_size
        self._bin_offset = int(math.ceil(scan_step_in_pixel_numbers))
        self.log().information('Bin offset is: ' + str(self._bin_offset))
        if (abs(self._bin_offset - scan_step_in_pixel_numbers) > 0.1
                and not self._interpolate):
            self.log().warning(
                'Scan step is not an integer multiple of the pixel size. '
                'Consider checking the option InterpolateOverlappingAngles.')
        if self._pixel_range[1] > self._n_det:
            self.log().warning(
                'Last pixel number provided is larger than total number of pixels. '
                'Taking the last existing pixel.')
            self._pixel_range[1] = self._n_det
        if self._excluded_ranges.any():
            n_excluded_ranges = int(len(self._excluded_ranges) / 2)
            self._excluded_ranges = np.split(self._excluded_ranges,
                                             n_excluded_ranges)

    def _configure_global(self, raw_ws):
        """
            Configures the calibration with GlobalSummedReference2D method (D2B)
            @param : first raw ws name in the list
        """
        inst = mtd[raw_ws].getInstrument()
        self._n_tubes = inst.getComponentByName('detectors').nelements()
        self._n_pixels_per_tube = inst.getComponentByName(
            'detectors/tube_1').nelements()
        #self._n_scans_per_file = mtd[raw_ws].getRun().getLogData('ScanSteps').value
        self._n_scans_per_file = 25  # TODO: In v2 this should be freely variable
        self._scan_points = self._n_scans_per_file * self._n_scan_files
        self.log().information('Number of scan steps is: ' +
                               str(self._scan_points))
        if self._excluded_ranges.any():
            n_excluded_ranges = int(len(self._excluded_ranges) / 2)
            self._excluded_ranges = np.split(self._excluded_ranges,
                                             n_excluded_ranges)

    def _validate_roi(self, ws_2d):
        """
            ROI has to be fully within the aperture of the detector at any time index.
            Example:
            time index : detector span (degrees)
            ------------------------------------
            first      : -30 -> 120
            last       :  10 -> 160
            ROI can not be wider than [10,120]
            @param ws_2d : 2D input workspace
            @throws : ValueError if ROI is not fully within the detector span at any time index
        """
        roi_min = np.min(self._regions_of_interest)
        roi_max = np.max(self._regions_of_interest)
        first_cell_last_time_theta = mtd[ws_2d].readX(1)[-1]
        last_cell_first_time_theta = mtd[ws_2d].readX(self._n_det)[0]
        if roi_min < first_cell_last_time_theta or roi_max > last_cell_first_time_theta:
            raise ValueError(
                'Invalid ROI. The region must be fully contained within the detector at any time index. '
                'For the given scan configuration, ROI can be within {0} and {1} degrees.'
                .format(first_cell_last_time_theta,
                        last_cell_first_time_theta))

    def _normalise_roi(self, ws_2d):
        """
            Normalises to the regions of interests (ROI)
            @param ws_2d : 2D input workspace
        """
        y = mtd[ws_2d].extractY()
        x = mtd[ws_2d].extractX()
        roi_counts_arr = np.ones(self._scan_points)
        # typically should be number_rois = 1
        number_rois = int(len(self._regions_of_interest) / 2)
        starts = self._regions_of_interest[0::2]
        ends = self._regions_of_interest[1::2]
        first_cells = []
        last_cells = []
        for roi in range(number_rois):
            first_cell = np.argmax(x[..., 0] > starts[roi])
            first_cells.append(first_cell)
            last_cell = np.argmin(x[..., 0] < ends[roi])
            last_cells.append(last_cell)
        for time_index in range(self._scan_points):
            roi_counts = 0
            counts = y[..., time_index]
            for roi in range(number_rois):
                first_cell = first_cells[roi] - self._bin_offset * time_index
                last_cell = last_cells[roi] - self._bin_offset * time_index
                roi_counts += np.sum(counts[first_cell:last_cell])
            roi_counts_arr[time_index] = roi_counts
        roi_ws = self._hide('roi')
        ExtractSingleSpectrum(InputWorkspace=ws_2d,
                              WorkspaceIndex=0,
                              OutputWorkspace=roi_ws)
        mtd[roi_ws].setY(0, roi_counts_arr)
        mtd[roi_ws].setE(0, np.sqrt(roi_counts_arr))
        Divide(LHSWorkspace=ws_2d, RHSWorkspace=roi_ws, OutputWorkspace=ws_2d)
        DeleteWorkspace(roi_ws)

    def _prepare_response_workspace(self, ws_2d, response_ws):
        """
            Prepares the response workspace with x-axis set as 2theta values
            @param ws_2d : 2D input workspace
            @param response_ws : the name of the output response workspace
        """
        size = (self._n_det - 1) * self._bin_offset + self._scan_points
        y = np.zeros(size)
        e = np.zeros(size)
        x = np.zeros(size)
        x_2d = mtd[ws_2d].extractX()
        x[0:self._scan_points] = x_2d[0, ...]
        index = self._scan_points
        for pixel in range(0, self._n_det):
            x[index:(index + self._bin_offset)] = x_2d[pixel,
                                                       -self._bin_offset:]
            index += self._bin_offset
        CreateWorkspace(DataX=x,
                        DataY=y,
                        DataE=e,
                        NSpec=1,
                        UnitX='Degrees',
                        OutputWorkspace=response_ws)

    def _perform_absolute_normalisation(self, constants_ws):
        """
            Performs the absolute normalisation
            Find the median of already derived calibration constants, excluding the dead pixels
            Divides all the constants by the median, for zero pixels sets the constants to 1.
            @param constants_ws : the name of the constants workspace
        """
        constants = mtd[constants_ws].extractY()
        absolute_norm = np.median(constants[self._live_pixels])
        self.log().information('Absolute normalisation constant is: ' +
                               str(absolute_norm))
        Scale(InputWorkspace=constants_ws,
              Factor=1. / absolute_norm,
              OutputWorkspace=constants_ws)
        for pixel in range(mtd[constants_ws].getNumberHistograms()):
            if not self._live_pixels[pixel]:
                mtd[constants_ws].dataY(pixel)[0] = 1.
                mtd[constants_ws].dataE(pixel)[0] = 0.

    def _derive_calibration_sequential(self, ws_2d, constants_ws, response_ws):
        """
            Computes the relative calibration factors sequentailly for all the pixels.
            This is the main calculation, the performance is critical
            @param : 2D input workspace
            @param : the output workspace name containing the calibration constants
            @param : the output workspace name containing the combined response
        """
        ref_ws = self._hide('ref')
        zeros = np.zeros(self._n_det)
        constants = np.ones(self._n_det)
        self._live_pixels = np.zeros(self._n_det, dtype=bool)
        CreateWorkspace(DataX=zeros,
                        DataY=constants,
                        DataE=zeros,
                        NSpec=self._n_det,
                        OutputWorkspace=constants_ws)

        # loop over all the requested pixels to derive the calibration sequentially
        # this is a serial loop of about 3K iterations, so performance is critical
        nreports = int(self._pixel_range[1] - self._pixel_range[0] + 1)
        self._progress = Progress(self, start=0.0, end=1.0, nreports=nreports)
        for det in range(self._pixel_range[0] - 1, self._pixel_range[1]):
            self._progress.report(
                'Computing the relative calibration factor for pixel #' +
                str(det))
            ws = '__det_' + str(det)
            ExtractSingleSpectrum(InputWorkspace=ws_2d,
                                  WorkspaceIndex=det,
                                  OutputWorkspace=ws)
            y = mtd[ws].readY(0)
            x = mtd[ws].readX(0)
            # keep track of dead pixels
            if np.count_nonzero(y) > self._scan_points / 5:
                self._live_pixels[det] = True

            if det == self._pixel_range[0] - 1:
                CropWorkspace(InputWorkspace=ws,
                              OutputWorkspace=ref_ws,
                              XMin=x[self._bin_offset])
            else:
                ratio_ws = ws + '_ratio'
                cropped_ws = ws + '_cropped'
                CropWorkspace(InputWorkspace=ws,
                              OutputWorkspace=cropped_ws,
                              XMax=x[-(self._bin_offset + 1)])

                if self._interpolate and self._live_pixels[det]:
                    # SplineInterpolation invalidates the errors, so we need to copy them over
                    interp_ws = ws + '_interp'
                    SplineInterpolation(WorkspaceToInterpolate=cropped_ws,
                                        WorkspaceToMatch=ref_ws,
                                        OutputWorkspace=interp_ws,
                                        OutputWorkspaceDeriv="",
                                        EnableLogging=False)
                    mtd[interp_ws].setE(0, mtd[cropped_ws].readE(0))
                    RenameWorkspace(InputWorkspace=interp_ws,
                                    OutputWorkspace=cropped_ws)
                else:
                    # here we need to effectively clone the x-axis
                    cloned_ref_ws = ws + '_cloned'
                    CloneWorkspace(InputWorkspace=ref_ws,
                                   OutputWorkspace=cloned_ref_ws)
                    mtd[cloned_ref_ws].setY(0, mtd[cropped_ws].readY(0))
                    mtd[cloned_ref_ws].setE(0, mtd[cropped_ws].readE(0))
                    RenameWorkspace(InputWorkspace=cloned_ref_ws,
                                    OutputWorkspace=cropped_ws)

                Divide(LHSWorkspace=ref_ws,
                       RHSWorkspace=cropped_ws,
                       OutputWorkspace=ratio_ws,
                       EnableLogging=False)
                factor = self._compute_relative_factor_1D(ratio_ws)
                DeleteWorkspace(ratio_ws)

                if str(factor) == 'nan' or str(
                        factor) == 'inf' or factor == 0.:
                    # pixel numbers start from 1
                    self.log().warning('Factor is ' + str(factor) +
                                       ' for pixel #' + str(det + 1))
                else:
                    self.log().debug('Factor derived for detector pixel #' +
                                     str(det + 1) + ' is ' + str(factor))
                    mtd[constants_ws].dataY(det)[0] = factor

                self._update_reference(ws, cropped_ws, ref_ws, factor)
                DeleteWorkspace(cropped_ws)

            if self._out_response:
                # take care of combined response
                end = self._bin_offset
                response = mtd[response_ws]
                responseBlockSize = response.blocksize()
                if det == self._pixel_range[1] - 1:
                    end = self._scan_points - self._bin_offset
                    for scan_point in range(0, self._bin_offset):
                        index = responseBlockSize - self._bin_offset + scan_point
                        response.dataY(0)[index] = mtd[ws].readY(0)[end +
                                                                    scan_point]
                        response.dataE(0)[index] = mtd[ws].readE(0)[end +
                                                                    scan_point]

                for scan_point in range(0, end):
                    index = det * self._bin_offset + scan_point
                    response.dataY(0)[index] = mtd[ref_ws].readY(0)[scan_point]
                    response.dataE(0)[index] = mtd[ref_ws].readE(0)[scan_point]

            DeleteWorkspace(ws)
        # end of loop over pixels
        DeleteWorkspace(ref_ws)

    def _process_sequential(self):
        """
            Performs the sequential derivation for D20 with the following logic:
            1. Take first cell as the reference
            2. Compute the coefficient for the second cell wrt reference
            3. Scale the response of the second cell with the obtained factor
            4. Merge the responses of first and second cells and set it as the new reference
            5. Back to Step 2 for the third pixel and so on...
        """
        raw_ws = self._hide('raw')
        mon_ws = self._hide('mon')
        ws_2d = self._hide('2d')
        response_ws = self._hide('resp')
        constants_ws = self._hide('constants')
        calib_ws = self._hide('calib')

        ConvertSpectrumAxis(InputWorkspace=raw_ws,
                            OutputWorkspace=raw_ws,
                            Target='SignedTheta',
                            OrderAxis=False)
        self._reshape(raw_ws, ws_2d)
        DeleteWorkspace(raw_ws)
        # extract the monitor spectrum
        ExtractSingleSpectrum(InputWorkspace=ws_2d,
                              WorkspaceIndex=0,
                              OutputWorkspace=mon_ws)
        if self._normalise_to == 'Monitor':
            Divide(LHSWorkspace=ws_2d,
                   RHSWorkspace=mon_ws,
                   OutputWorkspace=ws_2d)
        elif self._normalise_to == 'ROI':
            self._validate_roi(ws_2d)
            self._normalise_roi(ws_2d)
        DeleteWorkspace(mon_ws)
        # only now crop out the monitor spectrum
        CropWorkspace(InputWorkspace=ws_2d,
                      StartWorkspaceIndex=1,
                      OutputWorkspace=ws_2d)
        ReplaceSpecialValues(InputWorkspace=ws_2d,
                             OutputWorkspace=ws_2d,
                             NaNValue=0,
                             NaNError=0,
                             InfinityValue=0,
                             InfinityError=0)

        if self._calib_file:
            Multiply(LHSWorkspace=ws_2d,
                     RHSWorkspace=calib_ws,
                     OutputWorkspace=ws_2d)
            DeleteWorkspace(calib_ws)

        if self._out_response:
            self._prepare_response_workspace(ws_2d, response_ws)

        # this is the main calculation
        self._derive_calibration_sequential(ws_2d, constants_ws, response_ws)
        DeleteWorkspace(ws_2d)
        self._perform_absolute_normalisation(constants_ws)
        mtd[constants_ws].getAxis(1).setUnit('Label').setLabel('Cell #', '')
        mtd[constants_ws].setYUnitLabel('Calibration constant')

    def _crop_last_time_index(self, ws, n_scan_points):
        ws_index_list = ""
        for pixel in range(self._n_tubes * self._n_pixels_per_tube):
            start = n_scan_points * pixel
            end = n_scan_points * (pixel + 1) - 2
            index_range = str(start) + "-" + str(end) + ","
            ws_index_list += index_range
        ws_index_list = ws_index_list[:-1]
        ExtractSpectra(InputWorkspace=ws,
                       OutputWorkspace=ws,
                       WorkspaceIndexList=ws_index_list)

    def _process_global(self):
        """
            Performs the global derivation for D2B following the logic:
            1. SumOverlappingTubes with 2D option to obtain the reference
            2. Loop over tubes, make ratios wrt reference, obtain constants
            3. Apply the constants, and iterate over if requested
        """
        data_type = 'Raw'
        if self.getProperty('UseCalibratedData').value:
            data_type = 'Calibrated'
        constants_ws = self._hide('constants')
        response_ws = self._hide('resp')
        calib_ws = self._hide('calib')
        ref_ws = self._hide('ref')
        numors = []
        self._progress = Progress(self,
                                  start=0.0,
                                  end=1.0,
                                  nreports=self._n_scan_files)

        for index, numor in enumerate(self._input_files.split(',')):
            self._progress.report('Pre-processing detector scan ' +
                                  numor[-10:-4])
            ws_name = '__raw_' + str(index)
            numors.append(ws_name)
            LoadILLDiffraction(Filename=numor,
                               OutputWorkspace=ws_name,
                               DataType=data_type)
            self._validate_scan(ws_name)
            if index == 0:
                if mtd[ws_name].getInstrument().getName() != 'D2B':
                    raise RuntimeError(
                        'Global reference method is not supported for the instrument given'
                    )
                self._configure_global(ws_name)
            if self._normalise_to == 'Monitor':
                NormaliseToMonitor(InputWorkspace=ws_name,
                                   OutputWorkspace=ws_name,
                                   MonitorID=0)
            ExtractMonitors(InputWorkspace=ws_name, DetectorWorkspace=ws_name)
            ConvertSpectrumAxis(InputWorkspace=ws_name,
                                OrderAxis=False,
                                Target="SignedTheta",
                                OutputWorkspace=ws_name)
            if self._calib_file:
                ApplyDetectorScanEffCorr(InputWorkspace=ws_name,
                                         DetectorEfficiencyWorkspace=calib_ws,
                                         OutputWorkspace=ws_name)

            n_scan_steps = mtd[ws_name].getRun().getLogData("ScanSteps").value
            if n_scan_steps != self._n_scans_per_file:
                self.log().warning(
                    "Run {0} has {1} scan points instead of {2}.".format(
                        numor[-10:-4], n_scan_steps, self._n_scans_per_file))
                self._crop_last_time_index(ws_name, n_scan_steps)

        if self._calib_file:
            DeleteWorkspace(calib_ws)

        constants = np.ones([self._n_pixels_per_tube, self._n_tubes])
        x = np.arange(self._n_tubes)
        e = np.zeros([self._n_pixels_per_tube, self._n_tubes])
        CreateWorkspace(DataX=np.tile(x, self._n_pixels_per_tube),
                        DataY=constants,
                        DataE=e,
                        NSpec=self._n_pixels_per_tube,
                        OutputWorkspace=constants_ws)
        calib_current = self._hide('current')
        CloneWorkspace(InputWorkspace=constants_ws,
                       OutputWorkspace=calib_current)

        iteration = 0
        chi2_ndof = np.inf  # set a large number to start with
        self._pixels_to_trim = 28
        chi2_ndof_threshold = 1.
        inst = mtd[numors[0]].getInstrument()
        if inst.hasParameter('pixels_to_trim'):
            self._pixels_to_trim = inst.getIntParameter('pixels_to_trim')[0]
        if inst.hasParameter('chi2_ndof'):
            chi2_ndof_threshold = inst.getNumberParameter('chi2_ndof')[0]

        while iteration < self._n_iterations or (
                self._n_iterations == 0 and chi2_ndof > chi2_ndof_threshold):
            self._progress = Progress(self, start=0.0, end=1.0, nreports=5)
            self._progress.report('Starting iteration #' + str(iteration))
            self._derive_calibration_global(numors)
            Multiply(LHSWorkspace=constants_ws,
                     RHSWorkspace=calib_current,
                     OutputWorkspace=constants_ws)
            chi2_ndof = self._chi_squared(calib_current)
            if iteration != 0:
                self.log().warning(
                    'Iteration {0}: Chi2/NdoF={1} (termination criterion: < {2})'
                    .format(iteration, chi2_ndof, chi2_ndof_threshold))
            iteration += 1

        if self._out_response:
            for index in range(self._n_scan_files):
                ws_name = '__raw_' + str(index)
                ApplyDetectorScanEffCorr(
                    InputWorkspace=ws_name,
                    DetectorEfficiencyWorkspace=calib_current,
                    OutputWorkspace=ws_name)
            SumOverlappingTubes(InputWorkspaces=numors,
                                OutputWorkspace=response_ws,
                                MirrorScatteringAngles=False,
                                CropNegativeScatteringAngles=False,
                                Normalise=True,
                                OutputType="2DTubes")

        DeleteWorkspace(ref_ws)
        DeleteWorkspaces(numors)
        DeleteWorkspace(calib_current)
        mtd[constants_ws].getAxis(0).setUnit('Label').setLabel('Tube #', '')
        mtd[constants_ws].getAxis(1).setUnit('Label').setLabel('Pixel #', '')
        mtd[constants_ws].setYUnitLabel('Calibration constant')

    def _derive_calibration_global(self, numors):
        """
            Derives one iteration of calibration with the global reference method (D2B)
            This is the main calculation, so the performance is critical
            @param numors : list of workspace names
        """
        y_tubes = []
        x_tubes = []
        e_tubes = []
        calib_current = self._hide('current')
        ref_ws = self._hide('ref')
        tubes_group = self._hide('tubes')
        ratios_group = self._hide('ratios')
        shape = [
            self._n_tubes, self._n_pixels_per_tube, self._n_scans_per_file
        ]
        for i in range(self._n_tubes):
            y_tubes.append([])
            x_tubes.append([])
            e_tubes.append([])

        for index in range(self._n_scan_files):
            ws_name = '__raw_' + str(index)
            ApplyDetectorScanEffCorr(InputWorkspace=ws_name,
                                     DetectorEfficiencyWorkspace=calib_current,
                                     OutputWorkspace=ws_name)
            y = mtd[ws_name].extractY()
            e = mtd[ws_name].extractE()
            x = mtd[ws_name].getAxis(1).extractValues()
            y_3d = np.reshape(y, shape)
            x_3d = np.reshape(x, shape)
            e_3d = np.reshape(e, shape)
            for tube in range(self._n_tubes):
                y_tubes[tube].append(y_3d[tube, :, :])
                x_tubes[tube].append(x_3d[tube, :, :])
                e_tubes[tube].append(e_3d[tube, :, :])

        self._progress.report('Constructing the global reference')
        SumOverlappingTubes(InputWorkspaces=numors,
                            OutputWorkspace=ref_ws,
                            MirrorScatteringAngles=False,
                            CropNegativeScatteringAngles=False,
                            Normalise=True,
                            OutputType="2DTubes")

        to_group = []
        self._progress.report('Preparing the tube responses')
        for tube in range(self._n_tubes):
            y_tube = np.concatenate(y_tubes[tube], axis=1)
            x_tube = np.concatenate(x_tubes[tube], axis=1)
            e_tube = np.concatenate(e_tubes[tube], axis=1)
            ws_name = "__tube" + str(tube)
            CreateWorkspace(DataX=x_tube,
                            DataY=y_tube,
                            DataE=e_tube,
                            NSpec=self._n_pixels_per_tube,
                            OutputWorkspace=ws_name)
            SortXAxis(InputWorkspace=ws_name, OutputWorkspace=ws_name)
            to_group.append(ws_name)
        GroupWorkspaces(InputWorkspaces=to_group, OutputWorkspace=tubes_group)

        ratios = []
        self._progress.report('Constructing response ratios')
        for tube in reversed(range(self._n_tubes)):
            itube = self._n_tubes - tube - 1
            ratio_ws = '__ratio' + str(tube)
            _crop_bins(ref_ws, itube * self._n_scans_per_file,
                       itube * self._n_scans_per_file + self._scan_points,
                       '__cropped_ref')
            _divide_friendly('__cropped_ref', '__tube' + str(tube), ratio_ws)
            ratios.append(ratio_ws)
            DeleteWorkspace('__cropped_ref')
        GroupWorkspaces(InputWorkspaces=ratios, OutputWorkspace=ratios_group)
        DeleteWorkspace(tubes_group)

        self._progress.report('Computing the calibration constants')
        Transpose(InputWorkspace=calib_current, OutputWorkspace=calib_current)
        for tube in range(self._n_tubes):
            coeff = self._compute_relative_factor_2D('__ratio' + str(tube),
                                                     tube)
            mtd[calib_current].setY(tube, coeff)
        Transpose(InputWorkspace=calib_current, OutputWorkspace=calib_current)
        DeleteWorkspace(ratios_group)

        ReplaceSpecialValues(InputWorkspace=calib_current,
                             OutputWorkspace=calib_current,
                             NaNValue=1,
                             InfinityValue=1,
                             SmallNumberThreshold=0.00001,
                             SmallNumberValue=1)

    def PyExec(self):
        self._set_input_properties()

        raw_ws = self._hide('raw')
        response_ws = self._hide('resp')
        constants_ws = self._hide('constants')
        calib_ws = self._hide('calib')

        if self._calib_file:
            LoadNexusProcessed(Filename=self._calib_file,
                               OutputWorkspace=calib_ws)

        if self._derivation_method == 'SequentialSummedReference1D':  # D20
            self._input_files = self._input_files.replace(',', '+')
            LoadAndMerge(Filename=self._input_files,
                         OutputWorkspace=raw_ws,
                         LoaderName='LoadILLDiffraction')
            if not mtd[raw_ws].getInstrument().getName().startswith('D20'):
                DeleteWorkspace(raw_ws)
                raise RuntimeError(
                    'Sequential reference method is not supported for the instrument given'
                )
            self._validate_scan(raw_ws)
            self._configure_sequential(raw_ws)
            self._process_sequential()
        elif self._derivation_method == 'GlobalSummedReference2D':  # D2B
            self._input_files = self._input_files.replace('+', ',')
            self._n_scan_files = self._input_files.count(',') + 1
            if self._n_scan_files < 2:
                raise RuntimeError(
                    'At least two overlapping scan files needed for the global method'
                )
            self._process_global()

        if self._mask_criterion.any():
            MaskBinsIf(InputWorkspace=constants_ws,
                       OutputWorkspace=constants_ws,
                       Criterion='y<' + str(self._mask_criterion[0]) + '||y>' +
                       str(self._mask_criterion[1]))

        # set output workspace[s]
        RenameWorkspace(InputWorkspace=constants_ws,
                        OutputWorkspace=self._out_name)
        self.setProperty('OutputWorkspace', self._out_name)
        if self._out_response:
            RenameWorkspace(InputWorkspace=response_ws,
                            OutputWorkspace=self._out_response)
            self.setProperty('OutputResponseWorkspace', self._out_response)