Beispiel #1
0
def test_slit_unit_conversions_spectrum_in_nm(verbose=True, plot=True, close_plots=True, *args, **kwargs):
    ''' Test that slit is consistently applied for different units

    Assert that:

    - calculated FWHM is the one that was applied

    '''

    from radis.test.utils import getTestFile
    from radis.spectrum.spectrum import Spectrum

    if plot:    # dont get stuck with Matplotlib if executing through pytest
        plt.ion()
        if close_plots:
            plt.close('all')

    # %% Get a Spectrum (stored in nm)

    s_nm = Spectrum.from_txt(getTestFile('calc_N2C_spectrum_Trot1200_Tvib3000.txt'),
                             quantity='radiance_noslit', waveunit='nm', unit='mW/cm2/sr/µm',
                             conditions={'self_absorption': False})

    with catch_warnings():
        filterwarnings(
            'ignore', 'Condition missing to know if spectrum is at equilibrium:')
        # just because it makes better units
        s_nm.rescale_path_length(1, 0.001)

    wstep = np.diff(s_nm.get_wavelength())[0]

    assert s_nm.get_waveunit() == 'nm'       # ensures it's stored in cm-1

    for shape in ['gaussian', 'triangular']:

        # Apply slit in nm
        slit_nm = 0.5
        s_nm.name = 'Spec in nm, slit {0:.2f} nm'.format(slit_nm)
        s_nm.apply_slit(slit_nm, unit='nm', shape=shape, mode='same')
        # ... mode=same to keep same output length. It helps compare both Spectra afterwards
        # in cm-1 as that's s.get_waveunit()
        fwhm = get_FWHM(*s_nm.get_slit())
        assert np.isclose(slit_nm, fwhm, atol=2*wstep)

        # Apply slit in nm this time
        s_cm = s_nm.copy()
        w_nm = s_nm.get_wavelength(which='non_convoluted')
        slit_cm = dnm2dcm(slit_nm, w_nm[len(w_nm)//2])
        s_cm.name = 'Spec in nm, slit {0:.2f} cm-1'.format(slit_cm)
        s_cm.apply_slit(slit_cm, unit='cm-1', shape=shape, mode='same')

        plotargs = {}
        if plot:
            plotargs['title'] = 'test_slit_unit_conversions: {0} ({1} nm)'.format(
                shape, slit_nm)
        s_nm.compare_with(s_cm, spectra_only='radiance', rtol=1e-3,
                          verbose=verbose, plot=plot, **plotargs)
Beispiel #2
0
def test_convert(verbose=True, *args, **kwargs):
    """ Test conversions  """

    E = np.linspace(1, 5, 5)  # eV
    assert (J2eV(K2J(J2K(eV2J(E)))) == E).all()

    E = 2150  # cm-1
    assert J2cm(cm2J(E)) == E
    assert K2cm(cm2K(E)) == E
    if verbose:
        print("K -> cm: ~ {0:.2f} K/cm".format(cm2K(1)))

    E = 1  # eV
    assert eV2cm(E) == J2cm(eV2J(1))
    assert round(eV2K(E), 0) == 11605
    assert K2eV(eV2K(E)) == E

    E = 250  # nm
    assert isclose(nm2eV(E), cm2eV(nm2cm(E)))
    assert eV2nm(nm2eV(E)) == E
    assert hz2nm(nm2hz(E)) == E

    fwhm = 1.5  # nm
    lbd_0 = 632.8  # nm
    assert isclose(fwhm, dcm2dnm(dnm2dcm(fwhm, lbd_0), nm2cm(lbd_0)))

    fwhm = 2e-3  # 0.002 nm
    lbd_0 = 632.8
    fwhm_hz = dnm2dhz(fwhm, lbd_0)  # ~ 1.5 GHz
    if verbose:
        print(
            (
                "{0:.2g} nm broadening at {1} nm = {2:.2g} Ghz".format(
                    fwhm, lbd_0, fwhm_hz * 1e-9
                )
            )
        )
    assert isclose(fwhm_hz * 1e-9, 1.4973307983125002)
    assert isclose(dhz2dnm(fwhm_hz, nm2hz(lbd_0)), fwhm)

    assert atm2bar(1) == 1.01325
    assert isclose(torr2bar(atm2torr(bar2atm(1))), 1)
    assert isclose(torr2atm(bar2torr(atm2bar(1))), 1)

    # Hz
    assert isclose(1 / hz2cm(1e9), 30, atol=0.1)  # 1 Ghz is about 30 cm
    assert hz2cm(cm2hz(600)) == 600

    return True
Beispiel #3
0
def test_convert(verbose=True, *args, **kwargs):
    ''' Test conversions  '''

    E = np.linspace(1, 5, 5)  # eV
    assert (J2eV(K2J(J2K(eV2J(E)))) == E).all()

    E = 2150  # cm-1
    assert J2cm(cm2J(E))

    E = 1  # eV
    assert (eV2cm(E) == J2cm(eV2J(1)))
    assert (round(eV2K(E), 0) == 11605)
    assert K2eV(eV2K(E)) == E

    E = 250  # nm
    assert isclose(nm2eV(E), cm2eV(nm2cm(E)))
    assert (eV2nm(nm2eV(E)) == E)
    assert hz2nm(nm2hz(E)) == E

    fwhm = 1.5  # nm
    lbd_0 = 632.8  # nm
    assert (isclose(fwhm, dcm2dnm(dnm2dcm(fwhm, lbd_0), nm2cm(lbd_0))))

    fwhm = 2e-3  # 0.002 nm
    lbd_0 = 632.8
    fwhm_hz = dnm2dhz(fwhm, lbd_0)  #  ~ 1.5 GHz
    if verbose:
        print(('{0:.2g} nm broadening at {1} nm = {2:.2g} Ghz'.format(
            fwhm, lbd_0, fwhm_hz * 1e-9)))
    assert isclose(fwhm_hz * 1e-9, 1.4973307983125002)
    assert isclose(dhz2dnm(fwhm_hz, nm2hz(lbd_0)), fwhm)

    assert atm2bar(1) == 1.01325
    assert isclose(torr2bar(atm2torr(bar2atm(1))), 1)
    assert isclose(torr2atm(bar2torr(atm2bar(1))), 1)

    return True
Beispiel #4
0
def offset(s, offset, unit, name=None, inplace=False):
    # type: (Spectrum, float, str, str, bool) -> Spectrum
    """Offset the spectrum by a wavelength or wavenumber

    Parameters
    ----------
    s: Spectrum
        Spectrum you want to modify
    offset: float
        Constant to add to all quantities in the Spectrum.
    unit: 'nm' or 'cm-1'
        unit for ``offset``.
    name: str
        name of output spectrum
    inplace: bool
        if ``True``, modifies ``s`` directly. Else, returns a copy.
        Default ``False``

    Returns
    -------
    s : Spectrum
        Spectrum object where cst is added to intensity of s['var']
        If ``inplace=True``, ``s`` has been modified directly.

    See Also
    --------

    call as a Spectrum method directly: :py:meth:`~radis.spectrum.spectrum.Spectrum.offset`
    """

    has_var = len(s._q) > 0
    has_conv_var = len(s._q_conv) > 0

    stored_waveunit = s.get_waveunit()

    # Convert offset to correct unit:
    if stored_waveunit == "cm-1":
        if unit == "nm":
            # Note @EP: here we're offsetting by a constant value in 'nm', which is
            # not a constant value in 'cm-1'. The offset is an array
            if has_var:
                offset_q = -dnm_air2dcm(
                    offset, s.get_wavelength(
                        which="non_convoluted"))  # this is an array
            if has_conv_var:
                offset_qconv = -dnm_air2dcm(
                    offset,
                    s.get_wavelength(which="convoluted"))  # this is an array
        elif unit == "nm_vac":
            if has_var:
                offset_q = -dnm2dcm(
                    offset, s.get_wavelength(
                        which="non_convoluted"))  # this is an array
            if has_conv_var:
                offset_qconv = -dnm2dcm(
                    offset,
                    s.get_wavelength(which="convoluted"))  # this is an array
        elif unit == "cm-1":
            if has_var:
                offset_q = offset
            if has_conv_var:
                offset_qconv = offset
        else:
            raise ValueError(unit)
    elif stored_waveunit == "nm":  # wavelength air
        if unit == "nm":
            if has_var:
                offset_q = offset
            if has_conv_var:
                offset_qconv = offset
        elif unit == "nm_vac":
            # Note @EP: strictly speaking, the offset should change a little bit
            # as nm_vac > nm depends on the wavelength. Neglected here. # TODO ?
            if has_var:
                offset_q = offset
            if has_conv_var:
                offset_qconv = offset
        elif unit == "cm-1":
            if has_var:
                offset_q = -dcm2dnm_air(
                    offset, s.get_wavenumber(
                        which="non_convoluted"))  # this is an array
            if has_conv_var:
                offset_qconv = -dcm2dnm_air(
                    offset,
                    s.get_wavenumber(which="convoluted"))  # this is an array
        else:
            raise ValueError(unit)
    elif stored_waveunit == "nm_vac":  # wavelength vacuum
        if unit == "nm":
            # Note @EP: strictly speaking, the offset should change a little bit
            # as nm > nm_vac depends on the wavelength. Neglected here. # TODO ?
            if has_var:
                offset_q = offset
            if has_conv_var:
                offset_qconv = offset
        elif unit == "nm_vac":
            if has_var:
                offset_q = offset
            if has_conv_var:
                offset_qconv = offset
        elif unit == "cm-1":
            if has_var:
                offset_q = -dcm2dnm(
                    offset, s.get_wavenumber(
                        which="non_convoluted"))  # this is an array
            if has_conv_var:
                offset_qconv = -dcm2dnm(
                    offset,
                    s.get_wavenumber(which="convoluted"))  # this is an array
        else:
            raise ValueError(unit)
    else:
        raise ValueError(stored_waveunit)

    if not inplace:
        s = s.copy()

    # Update all variables
    if has_var:
        s._q["wavespace"] += offset_q
        # @dev: updates the Spectrum directly because of copy=False
    if has_conv_var:
        s._q_conv["wavespace"] += offset_qconv

    if name:
        s.name = name

    return s
Beispiel #5
0
def get_slit_function(slit_function,
                      unit='nm',
                      norm_by='area',
                      shape='triangular',
                      center_wavespace=None,
                      return_unit='same',
                      wstep=None,
                      plot=False,
                      resfactor=2,
                      *args,
                      **kwargs):
    ''' Import or generate slit function in correct wavespace
    Give a file path to import, or a float / tuple to generate arbitrary shapes

    Warning with units: read about unit and return_unit parameters.

    See :meth:`~radis.spectrum.spectrum.Spectrum.apply_slit` and 
    :func:`~radis.tools.slit.convolve_with_slit` for more info

    
    Parameters    
    ----------

    slit_function: tuple, or str
        If float:
            generate slit function with FWHM of `slit_function` (in `unit`)
        If .txt file:
            import experimental slit function (in `unit`): format must be 2-columns
            with wavelengths and intensity (doesn't have to be normalized)

    unit: 'nm' or 'cm-1'
        unit of slit_function FWHM, or unit of imported file

    norm_by: 'area', 'max', or None
        how to normalize. `area` conserves energy. With `max` the slit is normalized
        so that its maximum is one (that is what is done in Specair: it changes
        the outptut spectrum unit, e.g. from 'mW/cm2/sr/µm' to 'mW/cm2/sr')
        None doesnt normalize. Default 'area'

    shape: 'triangular', 'trapezoidal', 'gaussian'
        which shape to use when generating a slit. Default 'triangular'

    center_wavespace: float, or None
        center of slit when generated (in unit). Not used if slit is imported.

    return_unit: 'nm', 'cm-1', or 'same'
        if not 'same', slit is converted to the given wavespace.

    wstep: float
        which discretization step to use (in return_unit) when generating a slit
        function. Not used if importing


    Other Parameters
    ----------------

    resfactor: int
        resolution increase when resampling from nm to cm-1, or the other way
        round. Default 2.

    energy_threshold: float
         tolerance fraction. Only used when importing experimental slit as the
         theoretical slit functions are directly generated in spectrum wavespace
         Default 1e-3 (0.1%)
         

    Returns
    -------
    
    wslit, Islit: array
        wslit is in `return_unit` . Islit is normalized according to  `norm_by`
         
        
    Examples
    --------
    
    >>> wslit, Islit = get_slit_function(1, 'nm', shape='triangular', 
    center_wavespace=600, wstep=0.01)     
    
    Returns a triangular slit function of FWHM = 1 nm, centered on 600 nm, with
    a step of 0.01 nm
    
    >>> wslit, Islit = get_slit_function(1, 'nm', shape='triangular', 
    center_wavespace=600, return_unit='cm-1', wstep=0.01)   
    
    Returns a triangular slit function expressed in cm-1, with a FWHM = 1 nm 
    (converted in equivalent width in cm-1 at 600 nm), centered on 600 nm, with 
    a step of 0.01 cm-1 (!)   
    

    Notes
    -----

    In norm_by 'max' mode, slit is normalized by slit max. In RADIS, this is done
    in the spectrum wavespace (to avoid errors that would be caused by interpolating
    the spectrum). 
    
    A problem arise if the spectrum wavespace is different from the slit wavespace:
    typically, slit is in 'nm' but a spectrum calculated by RADIS is stored in 
    'cm-1': in that case, the convoluted spectrum is multiplied by /int(Islit*dν)
    instead of /int(Islit*dλ). The output unit is then [radiance]*[spec_unit]
    instead of [radiance]*[slit_unit], i.e, typically, [mW/cm2/sr/nm]*[cm-1]
    instead of [mW/cm2/sr/nm]*[nm]=[mW/cm2/sr]
    
    While this remains true if the units are taken into account, this is not the
    expected behaviour. For instance, Specair users are used to having a FWHM(nm) 
    factor between spectra convolved with slit normalized by max and slit normalized
    by area.
    
    The norm_by='max' behaviour adds a correction factor
    `/int(Islit*dλ)/int(Islit*dν)` to maintain an output spectrum in [radiance]*[slit_unit]
    
    See Also
    --------
    
    :meth:`~radis.spectrum.spectrum.Spectrum.apply_slit`, 
    :func:`~radis.tools.slit.convolve_with_slit`
    
    '''

    if 'waveunit' in kwargs:
        assert return_unit == 'same'  # default
        return_unit = kwargs.pop('waveunit')
        warn(DeprecationWarning('waveunit renamed return_unit'))
    if 'slit_unit' in kwargs:
        assert unit == 'nm'  # default
        unit = kwargs.pop('slit_unit')
        warn(DeprecationWarning('slit_unit renamed unit'))

    energy_threshold = kwargs.pop('energy_threshold', 1e-3)  # type: float

    # tolerance fraction
    # when resampling (only used in experimental slit as the)
    # theoretical slit functions are directly generated in
    # spectrum wavespace

    def check_input_gen():
        if center_wavespace is None:
            raise ValueError('center_wavespace has to be given when generating '+\
                             'slit function')
        if wstep is None:
            raise ValueError('wstep has to be given when generating '+\
                             'slit function')

    # Cast units
    if return_unit == 'same':
        return_unit = unit
    unit = cast_waveunit(unit)
    return_unit = cast_waveunit(return_unit)
    scale_slit = 1  # in norm_by=max mode, used to keep units in [Iunit]*return_unit in [Iunit]*unit
    # not used in norm_by=area mode

    # First get the slit in return_unit space
    if is_float(slit_function
                ):  # Generate slit function (directly in return_unit space)

        check_input_gen()

        # ... first get FWHM in return_unit  (it is in `unit` right now)
        FWHM = slit_function
        if return_unit == 'cm-1' and unit == 'nm':
            # center_wavespace ~ nm, FWHM ~ nm
            FWHM = dnm2dcm(FWHM, center_wavespace)  # wavelength > wavenumber
            center_wavespace = nm2cm(center_wavespace)
            if norm_by == 'max':
                scale_slit = slit_function / FWHM  # [unit/return_unit]
        elif return_unit == 'nm' and unit == 'cm-1':
            # center_wavespace ~ cm-1, FWHM ~ cm-1
            FWHM = dcm2dnm(FWHM, center_wavespace)  # wavenumber > wavelength
            center_wavespace = cm2nm(center_wavespace)
            if norm_by == 'max':
                scale_slit = slit_function / FWHM  # [unit/return_unit]
        else:
            pass  # correct unit already
        # Now FWHM is in 'return_unit'

        # ... now, build it (in our wavespace)
        if __debug__:
            printdbg(
                'get_slit_function: {0} FWHM {1:.2f}{2}, center {3:.2f}{2}, norm_by {4}'
                .format(shape, FWHM, return_unit, center_wavespace, norm_by))

        if shape == 'triangular':
            wslit, Islit = triangular_slit(FWHM,
                                           wstep,
                                           center=center_wavespace,
                                           bplot=plot,
                                           norm_by=norm_by,
                                           waveunit=return_unit,
                                           scale=scale_slit,
                                           *args,
                                           **kwargs)

        # Insert other slit shapes here
        # ...

        elif shape == 'gaussian':
            wslit, Islit = gaussian_slit(FWHM,
                                         wstep,
                                         center=center_wavespace,
                                         bplot=plot,
                                         norm_by=norm_by,
                                         waveunit=return_unit,
                                         scale=scale_slit,
                                         *args,
                                         **kwargs)

        elif shape == 'trapezoidal':
            raise TypeError(
                'A (top, base) tuple must be given with a trapezoidal slit')

        else:
            raise TypeError(
                'Slit function ({0}) not in known slit shapes: {1}'.format(
                    shape, SLIT_SHAPES))

    elif isinstance(slit_function, tuple):

        check_input_gen()

        try:
            top, base = slit_function
        except:
            raise TypeError(
                'Wrong format for slit function: {0}'.format(slit_function))
        if shape == 'trapezoidal':
            pass
        elif shape == 'triangular':  # it's the default
            warn(
                'Triangular slit given with a tuple: we used trapezoidal slit instead'
            )
            shape = 'trapezoidal'
        else:
            raise TypeError(
                'A (top, base) tuple must be used with a trapezoidal slit')

        # ... first get FWHM in our wavespace unit
        if return_unit == 'cm-1' and unit == 'nm':
            # center_wavespace ~ nm, FWHM ~ nm
            top = dnm2dcm(top, center_wavespace)  # wavelength > wavenumber
            base = dnm2dcm(base, center_wavespace)  # wavelength > wavenumber
            center_wavespace = nm2cm(center_wavespace)
            if norm_by == 'max':
                scale_slit = sum(slit_function) / (top + base
                                                   )  # [unit/return_unit]
        elif return_unit == 'nm' and unit == 'cm-1':
            # center_wavespace ~ cm-1, FWHM ~ cm-1
            top = dcm2dnm(top, center_wavespace)  # wavenumber > wavelength
            base = dcm2dnm(base, center_wavespace)  # wavenumber > wavelength
            center_wavespace = cm2nm(center_wavespace)
            if norm_by == 'max':
                scale_slit = sum(slit_function) / (top + base
                                                   )  # [unit/return_unit]
        else:
            pass  # correct unit already

        FWHM = (top + base) / 2

        # ... now, build it (in our wavespace)
        if __debug__:
            printdbg(
                'get_slit_function: {0}, FWHM {1:.2f}{2}, center {3:.2f}{2}, norm_by {4}'
                .format(shape, FWHM, return_unit, center_wavespace, norm_by))

        wslit, Islit = trapezoidal_slit(top,
                                        base,
                                        wstep,
                                        center=center_wavespace,
                                        bplot=plot,
                                        norm_by=norm_by,
                                        waveunit=return_unit,
                                        scale=scale_slit,
                                        *args,
                                        **kwargs)

    elif isinstance(slit_function, string_types):  # import it
        if __debug__:
            printdbg(
                'get_slit_function: {0} in {1}, norm_by {2}, return in {3}'.
                format(slit_function, unit, norm_by, return_unit))
        wslit, Islit = import_experimental_slit(
            slit_function,
            norm_by=norm_by,  # norm is done later anyway
            waveunit=unit,
            bplot=False,  # we will plot after resampling
            *args,
            **kwargs)
        # ... get unit
        # Normalize
        if norm_by == 'area':  # normalize by the area
            #        I_slit /= np.trapz(I_slit, x=w_slit)
            Iunit = '1/{0}'.format(unit)
        elif norm_by == 'max':  # set maximum to 1
            Iunit = '1'
        elif norm_by is None:
            Iunit = None
        else:
            raise ValueError(
                'Unknown normalization type: `norm_by` = {0}'.format(norm_by))

        # ... check it looks correct
        unq, counts = np.unique(wslit, return_counts=True)
        dup = counts > 1
        if dup.sum() > 0:
            raise ValueError(
                'Not all wavespace points are unique: slit function ' +
                'format may be wrong. Duplicates for w={0}'.format(unq[dup]))

        # ... resample if needed
        if return_unit == 'cm-1' and unit == 'nm':  # wavelength > wavenumber
            wold, Iold = wslit, Islit
            wslit, Islit = resample_even(nm2cm(wslit),
                                         Islit,
                                         resfactor=resfactor,
                                         energy_threshold=energy_threshold,
                                         print_conservation=True)
            scale_slit = trapz(Iold, wold) / trapz(Islit,
                                                   wslit)  # [unit/return_unit]
            renormalize = True
        elif return_unit == 'nm' and unit == 'cm-1':  # wavenumber > wavelength
            wold, Iold = wslit, Islit
            wslit, Islit = resample_even(cm2nm(wslit),
                                         Islit,
                                         resfactor=resfactor,
                                         energy_threshold=energy_threshold,
                                         print_conservation=True)
            scale_slit = trapz(Iold, wold) / trapz(Islit,
                                                   wslit)  # [unit/return_unit]
            renormalize = True
        else:  # return_unit == unit
            renormalize = False
        # Note: if wstep dont match with quantity it's alright as it gets
        # interpolated in the `convolve_with_slit` function

        # re-Normalize if needed (after changing units)
        if renormalize:
            if __debug__: printdbg('get_slit_function: renormalize')
            if norm_by == 'area':  # normalize by the area
                Islit /= abs(np.trapz(Islit, x=wslit))
                Iunit = '1/{0}'.format(return_unit)
            elif norm_by == 'max':  # set maximum to 1
                Islit /= abs(np.max(Islit))
                Islit *= scale_slit
                Iunit = '1'
                if scale_slit != 1:
                    Iunit += 'x{0}'.format(scale_slit)


#            elif norm_by == 'max2': # set maximum to 1    # removed this mode for simplification
#                Islit /= abs(np.max(Islit))
            elif norm_by is None:
                Iunit = None
            else:
                raise ValueError(
                    'Unknown normalization type: `norm_by` = {0}'.format(
                        norm_by))

        if plot:  # (plot after resampling / renormalizing)
            # Plot slit
            plot_slit(wslit, Islit, waveunit=return_unit, Iunit=Iunit)

    else:
        raise TypeError('Unexpected type for slit function: {0}'.format(
            type(slit_function)))

    return wslit, Islit
Beispiel #6
0
def offset(s, offset, unit, name=None, inplace=False):
    # type: (Spectrum, float, str, str, bool) -> Spectrum
    '''Offset the spectrum by a wavelength or wavenumber 

    Parameters    
    ----------
    s: Spectrum
        Spectrum you want to modify
    offset: float
        Constant to add to all quantities in the Spectrum.
    unit: 'nm' or 'cm-1'
        unit for ``offset``.
    name: str
        name of output spectrum
    inplace: bool
        if ``True``, modifies ``s`` directly. Else, returns a copy. 
        Default ``False``

    Returns    
    -------
    s : Spectrum
        Spectrum object where cst is added to intensity of s['var']
        If ``inplace=True``, ``s`` has been modified directly.
        
    See Also
    --------
    
    call as a Spectrum method directly: :py:meth:`~radis.spectrum.spectrum.Spectrum.offset`
    '''

    has_var = len(s._q) > 0
    has_conv_var = len(s._q_conv) > 0

    # Convert to correct unit:
    if unit == 'nm' and s.get_waveunit() == 'cm-1':
        # Note @EP: technically we should check the medium is air or vacuum...  TODO
        # Note @EP: here we're offsetting by a constant value in 'cm-1', which is
        # not a constant value in 'nm'. We end of with offset as an array
        if has_var:
            offset_q = -dnm2dcm(offset, s.get_wavelength(
                which='non_convoluted'))  # this is an array
        if has_conv_var:
            offset_qconv = -dnm2dcm(offset, s.get_wavelength(
                which='convoluted'))  # this is an array
    elif unit == 'cm-1' and s.get_waveunit() == 'nm':
        if has_var:
            offset_q = -dcm2dnm(offset, s.get_wavenumber(
                which='non_convoluted'))  # this is an array
        if has_conv_var:
            offset_qconv = -dcm2dnm(offset, s.get_wavenumber(
                which='convoluted'))  # this is an array
    else:
        assert unit == s.get_waveunit()
        if has_var: offset_q = offset
        if has_conv_var: offset_qconv = offset

    if not inplace:
        s = s.copy()

    # Update all variables
    if has_var:
        s._q['wavespace'] += offset_q
        # @dev: updates the Spectrum directly because of copy=False
    if has_conv_var:
        s._q_conv['wavespace'] += offset_qconv

    if name:
        s.name = name

    return s
Beispiel #7
0
def test_slit_unit_conversions_spectrum_in_nm(
    verbose=True, plot=True, close_plots=True, *args, **kwargs
):
    """Test that slit is consistently applied for different units

    Assert that:

    - calculated FWHM is the one that was applied

    """

    from radis.spectrum.spectrum import Spectrum
    from radis.test.utils import getTestFile

    if plot:  # dont get stuck with Matplotlib if executing through pytest
        plt.ion()
        if close_plots:
            plt.close("all")

    # %% Get a Spectrum (stored in nm)

    s_nm = Spectrum.from_txt(
        getTestFile("calc_N2C_spectrum_Trot1200_Tvib3000.txt"),
        quantity="radiance_noslit",
        waveunit="nm",
        unit="mW/cm2/sr/µm",
        conditions={"self_absorption": False},
    )

    with catch_warnings():
        filterwarnings(
            "ignore", "Condition missing to know if spectrum is at equilibrium:"
        )
        # just because it makes better units
        s_nm.rescale_path_length(1, 0.001)

    wstep = np.diff(s_nm.get_wavelength())[0]

    assert s_nm.get_waveunit() == "nm"  # ensures it's stored in cm-1

    for shape in ["gaussian", "triangular"]:

        # Apply slit in nm
        slit_nm = 0.5
        s_nm.name = "Spec in nm, slit {0:.2f} nm".format(slit_nm)
        s_nm.apply_slit(slit_nm, unit="nm", shape=shape, mode="same")
        # ... mode=same to keep same output length. It helps compare both Spectra afterwards
        # in cm-1 as that's s.get_waveunit()
        fwhm = get_FWHM(*s_nm.get_slit())
        assert np.isclose(slit_nm, fwhm, atol=2 * wstep)

        # Apply slit in nm this time
        s_cm = s_nm.copy()
        w_nm = s_nm.get_wavelength(which="non_convoluted")
        slit_cm = dnm2dcm(slit_nm, w_nm[len(w_nm) // 2])
        s_cm.name = "Spec in nm, slit {0:.2f} cm-1".format(slit_cm)
        s_cm.apply_slit(slit_cm, unit="cm-1", shape=shape, mode="same")

        plotargs = {}
        if plot:
            plotargs["title"] = "test_slit_unit_conversions: {0} ({1} nm)".format(
                shape, slit_nm
            )
        s_nm.compare_with(
            s_cm,
            spectra_only="radiance",
            rtol=1e-3,
            verbose=verbose,
            plot=plot,
            **plotargs
        )