Esempio n. 1
0
    def _calculate_wavelength_band(self):
        """
        Calculate the wavelength band using the monitors from the sample runs

        Consider wavelenghts with an associated intensity above a certain
        fraction of the maximum observed intensity.
        """
        _t_w = self._load_monitors('sample')
        _t_w = ConvertUnits(_t_w, Target='Wavelength', Emode='Elastic')
        l_min, l_max = 0.0, 20.0
        _t_w = CropWorkspace(_t_w, XMin=l_min, XMax=l_max)
        _t_w = OneMinusExponentialCor(_t_w,
                                      C='0.20749999999999999',
                                      C1='0.001276')
        _t_w = Scale(_t_w, Factor='1e-06', Operation='Multiply')
        _t_w = Rebin(_t_w,
                     Params=[l_min, self._wavelength_dl, l_max],
                     PreserveEvents=False)
        y = _t_w.readY(0)
        k = np.argmax(y)  # y[k] is the maximum observed intensity
        factor = 0.8  # 80% of the maximum intensity
        i_s = k - 1
        while y[i_s] > factor * y[k]:
            i_s -= 1
        i_e = k + 1
        while y[i_e] > factor * y[k]:
            i_e += 1
        x = _t_w.readX(0)
        self._wavelength_band = [x[i_s], x[i_e]]
class CalculateEfficiencyCorrection(PythonAlgorithm):
    _input_ws = None
    _output_ws = None

    def category(self):
        return 'CorrectionFunctions\\EfficiencyCorrections'

    def name(self):
        return 'CalculateEfficiencyCorrection'

    def summary(self):
        return 'Calculate an efficiency correction using various inputs. Can be used to determine \
                an incident spectrum after correcting a measured spectrum from beam monitors \
                or vanadium measurements.'

    def seeAlso(self):
        return ["He3TubeEfficiency", "CalculateSampleTransmission",
                "DetectorEfficiencyCor", "DetectorEfficiencyCorUser",
                "CalculateEfficiency", "ComputeCalibrationCoefVan"]

    def PyInit(self):
        self.declareProperty(
            MatrixWorkspaceProperty('InputWorkspace', '',
                                    direction=Direction.Input,
                                    optional=PropertyMode.Optional,
                                    validator=CommonBinsValidator()),
            doc='Input workspace with wavelength range to calculate for the correction.')

        self.declareProperty(name='WavelengthRange', defaultValue='',
                             doc='Wavelength range to calculate efficiency for.')

        self.declareProperty(
            MatrixWorkspaceProperty('OutputWorkspace', '',
                                    direction=Direction.Output),
            doc="Output workspace for the efficiency correction. \
                 This can be applied by multiplying the OutputWorkspace \
                 to the workspace that requires the correction.")

        self.declareProperty(
            name='ChemicalFormula', defaultValue='None',
            validator=StringMandatoryValidator(),
            doc='Sample chemical formula used to determine cross-section term.')

        self.declareProperty(
            name='DensityType', defaultValue='Mass Density',
            validator=StringListValidator(['Mass Density', 'Number Density']),
            doc='Use of Mass density (g/cm^3) or Number density (atoms/Angstrom^3)')

        self.declareProperty(
            name='Density',
            defaultValue=0.0,
            validator=FloatBoundedValidator(0.0),
            doc='Mass density (g/cm^3) or Number density (atoms/Angstrom^3).')

        self.declareProperty(
            name='Thickness',
            defaultValue=1.0,
            validator=FloatBoundedValidator(0.0),
            doc='Sample thickness (cm).')

        self.declareProperty(
            name='MeasuredEfficiency',
            defaultValue=0.0,
            validator=FloatBoundedValidator(0.0, 1.0),
            doc="Directly input the efficiency measured at MeasuredEfficiencyWavelength. \
                 This is used to determine a Density*Thickness term.")

        self.declareProperty(
            name='MeasuredEfficiencyWavelength',
            defaultValue=1.7982,
            validator=FloatBoundedValidator(0.0),
            doc="The wavelength at which the MeasuredEfficiency was measured.")

        self.declareProperty(
            name='Alpha', defaultValue=0.0,
            doc="Directly input the alpha term in exponential to multiply by the wavelength. \
                 XSectionType has no effect if this is used.")

        self.declareProperty(
            name='XSectionType',
            defaultValue="AttenuationXSection",
            validator=StringListValidator(['AttenuationXSection', 'TotalXSection']),
            doc='Use either the absorption  or total cross section in exponential term. \
                   The absorption cross section is for monitor-type corrections and \
                   the total cross section is for transmission-type corrections')

    def validateInputs(self):
        issues = dict()

        # Check the inputs for wavelength
        isInWSDefault = self.getProperty('InputWorkspace').isDefault
        isWlRangeDefault = self.getProperty('WavelengthRange').isDefault

        if isInWSDefault and isWlRangeDefault:
            issues['InputWorkspace'] = "Must select either InputWorkspace and WavelengthRange as input"
            issues['WavelengthRange'] = "Must select either InputWorkspace and WavelengthRange as input"

        if isInWSDefault and isWlRangeDefault:
            issues['InputWorkspace'] = "Cannot select both InputWorkspace and WavelengthRange as input"
            issues['WavelengthRange'] = "Cannot select both InputWorkspace and WavelengthRange as input"

        # Check the different inputs for the exponential term
        isDensityDefault = self.getProperty('Density').isDefault
        isFormulaDefault = self.getProperty('ChemicalFormula').isDefault
        isEffDefault     = self.getProperty('MeasuredEfficiency').isDefault
        isAlphaDefault   = self.getProperty('Alpha').isDefault

        if not isDensityDefault:
            if isFormulaDefault:
                issues['ChemicalFormula'] = "Must specify the ChemicalFormula with Density"

        if not isEffDefault:
            if isFormulaDefault:
                issues['ChemicalFormula'] = "Must specify the ChemicalFormula with MeasuredEfficiency"

        if not isEffDefault and not isDensityDefault:
            issues['MeasuredEfficiency'] = "Cannot select both MeasuredEfficiency and Density as input"
            issues['Density'] = "Cannot select both MeasuredEfficiency and Density as input"

        if not isAlphaDefault and not isDensityDefault:
            issues['Alpha'] = "Cannot select both Alpha and Density as input"
            issues['Density'] = "Cannot select both Alpha and Density as input"

        if not isEffDefault and not isAlphaDefault:
            issues['MeasuredEfficiency'] = "Cannot select both MeasuredEfficiency and Alpha as input"
            issues['Alpha'] = "Cannot select both MeasuredEfficiency and Alpha as input"

        return issues

    def _setup(self):
        self._input_ws = self.getProperty('InputWorkspace').value
        self._bin_params = self.getPropertyValue('WavelengthRange')
        self._output_ws = self.getProperty('OutputWorkspace').valueAsStr
        self._chemical_formula = self.getPropertyValue('ChemicalFormula')
        self._density_type = self.getPropertyValue('DensityType')
        self._density = self.getProperty('Density').value
        self._thickness = self.getProperty('Thickness').value
        self._efficiency = self.getProperty('MeasuredEfficiency').value
        self._efficiency_wavelength = self.getProperty('MeasuredEfficiencyWavelength').value
        self._alpha_absXS = self.getProperty('Alpha').value
        self._alpha_scatXS = 0.0
        self._xsection_type = self.getProperty('XSectionType').value

    def PyExec(self):
        self._setup()
        if not self.getProperty("InputWorkspace").isDefault:
            self._output_ws = CloneWorkspace(Inputworkspace=self._input_ws,
                                             OutputWorkspace=self._output_ws,
                                             StoreInADS=False)
        else:
            self._output_ws = CreateWorkspace(NSpec=1, DataX=[0], DataY=[0],
                                              UnitX='Wavelength', Distribution=False,
                                              StoreInADS=False)
            self._output_ws = Rebin(InputWorkspace=self._output_ws,
                                    Params=self._bin_params,
                                    StoreInADS=False)

        if self._output_ws.isDistribution():
            ConvertFromDistribution(Workspace=self._output_ws,
                                    StoreInADS=False)

        self._output_ws = ConvertToPointData(InputWorkspace=self._output_ws,
                                             StoreInADS=False)
        self._output_ws = ConvertUnits(InputWorkspace=self._output_ws,
                                       Target='Wavelength',
                                       EMode='Elastic',
                                       StoreInADS=False)

        if self.getProperty('Alpha').isDefault:
            if self._density_type == 'Mass Density':
                SetSampleMaterial(
                    InputWorkspace=self._output_ws,
                    ChemicalFormula=self._chemical_formula,
                    SampleMassDensity=self._density,
                    StoreInADS=False)
                self._density = self._output_ws.sample().getMaterial().numberDensityEffective
            elif self._density_type == 'Number Density':
                SetSampleMaterial(
                    InputWorkspace=self._output_ws,
                    ChemicalFormula=self._chemical_formula,
                    SampleNumberDensity=self._density,
                    StoreInADS=False)
            else:
                raise RuntimeError(f'Unknown "DensityType": {self._density_type}')
            if self.getProperty('MeasuredEfficiency').isDefault:
                self._calculate_area_density_from_density()
            else:
                self._calculate_area_density_from_efficiency()
            self._calculate_alpha_absXS_term()
            if self._xsection_type == "TotalXSection":
                self._calculate_alpha_scatXS_term()

        wavelengths = self._output_ws.readX(0)
        efficiency = self._calculate_efficiency(wavelengths)
        for histo in range(self._output_ws.getNumberHistograms()):
            self._output_ws.setY(histo, efficiency)

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

    def _calculate_area_density_from_efficiency(self):
        """Calculates area density (atom/cm^2) using efficiency"""
        material = self._output_ws.sample().getMaterial()
        ref_absXS = material.absorbXSection()
        xs_term = ref_absXS * self._efficiency_wavelength / TABULATED_WAVELENGTH
        if self._xsection_type == "TotalXSection":
            xs_term += material.totalScatterXSection()
        self._area_density = - np.log(1.0 - self._efficiency) / xs_term

    def _calculate_area_density_from_density(self):
        """Calculates area density (atom/cm^2) using number density and thickness."""
        self._area_density = self._density * self._thickness

    def _calculate_alpha_absXS_term(self):
        """Calculates absorption XS alpha term = area_density * absXS / 1.7982"""
        material = self._output_ws.sample().getMaterial()
        absXS = material.absorbXSection() / TABULATED_WAVELENGTH
        self._alpha_absXS = self._area_density * absXS

    def _calculate_alpha_scatXS_term(self):
        """Calculates scattering XS alpha term = area_density * scatXS"""
        material = self._output_ws.sample().getMaterial()
        scatXS = material.totalScatterXSection()
        self._alpha_scatXS = self._area_density * scatXS

    def _calculate_efficiency(self, wavelength):
        """
        Calculates efficiency of a detector / monitor at a given wavelength.
        If using just the absorption cross section, _alpha_scatXS is == 0.

        @param wavelength Wavelength at which to calculate (in Angstroms)
        @return efficiency
        """
        efficiency = 1. / (1.0 - np.exp(-(self._alpha_scatXS + self._alpha_absXS * wavelength)))
        return efficiency
class CalculateEfficiencyCorrection(PythonAlgorithm):
    _input_ws = None
    _output_ws = None

    def category(self):
        return 'CorrectionFunctions\\EfficiencyCorrections'

    def name(self):
        return 'CalculateEfficiencyCorrection'

    def summary(self):
        return 'Calculate an efficiency correction using various inputs. Can be used to determine \
                an incident spectrum after correcting a measured spectrum from beam monitors \
                or vanadium measurements.'

    def seeAlso(self):
        return [ "He3TubeEfficiency", "CalculateSampleTransmission",
                 "DetectorEfficiencyCor", "DetectorEfficiencyCorUser",
                 "CalculateEfficiency", "ComputeCalibrationCoefVan" ]

    def PyInit(self):
        self.declareProperty(
            MatrixWorkspaceProperty('InputWorkspace', '',
                                    direction=Direction.Input,
                                    optional=PropertyMode.Optional,
                                    validator=CommonBinsValidator()),
            doc='Input workspace with wavelength range to calculate for the correction.')

        self.declareProperty(name='WavelengthRange', defaultValue='',
                             doc='Wavelength range to calculate efficiency for.')

        self.declareProperty(
            MatrixWorkspaceProperty('OutputWorkspace', '',
                                    direction=Direction.Output),
            doc="Output workspace for the efficiency correction. \
                 This can be applied by multiplying the OutputWorkspace \
                 to the workspace that requires the correction.")

        self.declareProperty(
            name='ChemicalFormula', defaultValue='None',
            validator=StringMandatoryValidator(),
            doc='Sample chemical formula used to determine cross-section term.')

        self.declareProperty(
            name='DensityType', defaultValue = 'Mass Density',
            validator=StringListValidator(['Mass Density', 'Number Density']),
            doc = 'Use of Mass density (g/cm^3) or Number density (atoms/Angstrom^3)')

        self.declareProperty(
            name='Density',
            defaultValue=0.0,
            validator=FloatBoundedValidator(0.0),
            doc='Mass density (g/cm^3) or Number density (atoms/Angstrom^3).')

        self.declareProperty(
            name='Thickness',
            defaultValue=1.0,
            validator=FloatBoundedValidator(0.0),
            doc='Sample thickness (cm).')

        self.declareProperty(
            name='MeasuredEfficiency',
            defaultValue=0.0,
            validator=FloatBoundedValidator(0.0, 1.0),
            doc="Directly input the efficiency measured at MeasuredEfficiencyWavelength. \
                 This is used to determine a Density*Thickness term.")

        self.declareProperty(
            name='MeasuredEfficiencyWavelength',
            defaultValue=1.7982,
            validator=FloatBoundedValidator(0.0),
            doc="The wavelength at which the MeasuredEfficiency was measured.")

        self.declareProperty(
            name='Alpha', defaultValue=0.0,
            doc="Directly input the alpha term in exponential to multiply by the wavelength. \
                 XSectionType has no effect if this is used.")

        self.declareProperty(
            name='XSectionType',
            defaultValue="AttenuationXSection",
            validator=StringListValidator(['AttenuationXSection', 'TotalXSection']),
            doc = 'Use either the absorption  or total cross section in exponential term. \
                   The absorption cross section is for monitor-type corrections and \
                   the total cross section is for transmission-type corrections' )

    def validateInputs(self):
        issues = dict()

        # Check the inputs for wavelength
        isInWSDefault = self.getProperty('InputWorkspace').isDefault
        isWlRangeDefault = self.getProperty('WavelengthRange').isDefault

        if isInWSDefault and isWlRangeDefault:
            issues['InputWorkspace'] = "Must select either InputWorkspace and WavelengthRange as input"
            issues['WavelengthRange'] = "Must select either InputWorkspace and WavelengthRange as input"

        if isInWSDefault and isWlRangeDefault:
            issues['InputWorkspace'] = "Cannot select both InputWorkspace and WavelengthRange as input"
            issues['WavelengthRange'] = "Cannot select both InputWorkspace and WavelengthRange as input"

        # Check the different inputs for the exponential term
        isDensityDefault = self.getProperty('Density').isDefault
        isFormulaDefault = self.getProperty('ChemicalFormula').isDefault
        isEffDefault     = self.getProperty('MeasuredEfficiency').isDefault
        isAlphaDefault   = self.getProperty('Alpha').isDefault

        if not isDensityDefault:
            if isFormulaDefault:
                issues['ChemicalFormula'] = "Must specify the ChemicalFormula with Density"

        if not isEffDefault:
            if isFormulaDefault:
                issues['ChemicalFormula'] = "Must specify the ChemicalFormula with MeasuredEfficiency"

        if not isEffDefault and not isDensityDefault:
            issues['MeasuredEfficiency'] = "Cannot select both MeasuredEfficiency and Density as input"
            issues['Density'] = "Cannot select both MeasuredEfficiency and Density as input"

        if not isAlphaDefault and not isDensityDefault:
            issues['Alpha'] = "Cannot select both Alpha and Density as input"
            issues['Density'] = "Cannot select both Alpha and Density as input"

        if not isEffDefault and not isAlphaDefault:
            issues['MeasuredEfficiency'] = "Cannot select both MeasuredEfficiency and Alpha as input"
            issues['Alpha'] = "Cannot select both MeasuredEfficiency and Alpha as input"

        return issues

    def _setup(self):
        self._input_ws = self.getProperty('InputWorkspace').value
        self._bin_params = self.getPropertyValue('WavelengthRange')
        self._output_ws = self.getProperty('OutputWorkspace').valueAsStr
        self._chemical_formula = self.getPropertyValue('ChemicalFormula')
        self._density_type = self.getPropertyValue('DensityType')
        self._density = self.getProperty('Density').value
        self._thickness = self.getProperty('Thickness').value
        self._efficiency = self.getProperty('MeasuredEfficiency').value
        self._efficiency_wavelength = self.getProperty('MeasuredEfficiencyWavelength').value
        self._alpha_absXS = self.getProperty('Alpha').value
        self._alpha_scatXS = 0.0
        self._xsection_type = self.getProperty('XSectionType').value

    def PyExec(self):
        self._setup()
        if not self.getProperty("InputWorkspace").isDefault:
            self._output_ws = CloneWorkspace(Inputworkspace=self._input_ws,
                                             OutputWorkspace=self._output_ws,
                                             StoreInADS=False)
        else:
            self._output_ws = CreateWorkspace(NSpec=1, DataX=[0], DataY=[0],
                                              UnitX='Wavelength', Distribution=False,
                                              StoreInADS=False)
            self._output_ws = Rebin(InputWorkspace=self._output_ws,
                                    Params=self._bin_params,
                                    StoreInADS=False)

        if self._output_ws.isDistribution():
            ConvertFromDistribution(Workspace=self._output_ws,
                                    StoreInADS=False)

        self._output_ws = ConvertToPointData(InputWorkspace=self._output_ws,
                                             StoreInADS=False)
        self._output_ws = ConvertUnits(InputWorkspace=self._output_ws,
                                       Target='Wavelength',
                                       EMode='Elastic',
                                       StoreInADS=False)

        if self.getProperty('Alpha').isDefault:
            SetSampleMaterial(
                InputWorkspace=self._output_ws,
                ChemicalFormula=self._chemical_formula,
                SampleNumberDensity=self._density,
                StoreInADS=False)
            if self.getProperty('MeasuredEfficiency').isDefault:
                self._calculate_area_density_from_density()
            else:
                self._calculate_area_density_from_efficiency()
            self._calculate_alpha_absXS_term()
            if self._xsection_type == "TotalXSection":
                self._calculate_alpha_scatXS_term()

        wavelengths = self._output_ws.readX(0)
        efficiency = self._calculate_efficiency(wavelengths)
        for histo in range(self._output_ws.getNumberHistograms()):
            self._output_ws.setY(histo, efficiency)

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

    def _calculate_area_density_from_efficiency(self):
        """Calculates area density (atom/cm^2) using efficiency"""
        material = self._output_ws.sample().getMaterial()
        ref_absXS = material.absorbXSection()
        xs_term = ref_absXS * self._efficiency_wavelength / TABULATED_WAVELENGTH
        if self._xsection_type == "TotalXSection":
            xs_term += material.totalScatterXSection()
        self._area_density = - np.log( 1.0 - self._efficiency) / xs_term

    def _calculate_area_density_from_density(self):
        """Calculates area density (atom/cm^2) using number density and thickness."""
        if self._density_type == 'Mass Density':
            builder = MaterialBuilder().setFormula(self._chemical_formula)
            mat = builder.setMassDensity(self._density).build()
            self._density = mat.numberDensity
        self._area_density = self._density * self._thickness

    def _calculate_alpha_absXS_term(self):
        """Calculates absorption XS alpha term = area_density * absXS / 1.7982"""
        material = self._output_ws.sample().getMaterial()
        absXS = material.absorbXSection()  / TABULATED_WAVELENGTH
        self._alpha_absXS = self._area_density * absXS

    def _calculate_alpha_scatXS_term(self):
        """Calculates scattering XS alpha term = area_density * scatXS"""
        material = self._output_ws.sample().getMaterial()
        scatXS = material.totalScatterXSection()
        self._alpha_scatXS = self._area_density * scatXS

    def _calculate_efficiency(self, wavelength):
        """
        Calculates efficiency of a detector / monitor at a given wavelength.
        If using just the absorption cross section, _alpha_scatXS is == 0.

        @param wavelength Wavelength at which to calculate (in Angstroms)
        @return efficiency
        """
        efficiency = 1. / (1.0 - np.exp(-(self._alpha_scatXS + self._alpha_absXS * wavelength)))
        return efficiency