Пример #1
0
def test_broadening_DLM_noneq(verbose=True, plot=False, *args, **kwargs):
    '''
    Test Noneq version of DLM and makes sure it gives the same results as the eq
    one when used with Tvib=Trot 
    
    '''

    if plot:  # Make sure matplotlib is interactive so that test are not stuck in pytest
        plt.ion()

    setup_test_line_databases()  # add HITRAN-CO2-TEST in ~/.radis if not there

    # Conditions
    p = 1
    wstep = 0.002
    wmin = 2380  # cm-1
    wmax = 2400  # cm-1
    broadening_max_width = 10  # cm-1

    # %% Calculate with RADIS
    # ----------
    sf = SpectrumFactory(
        wavenum_min=wmin,
        wavenum_max=wmax,
        mole_fraction=1,
        path_length=1,   # doesnt change anything
        wstep=wstep,
        pressure=p,
        broadening_max_width=broadening_max_width,
        isotope='1',
        verbose=3,
        warnings={'MissingSelfBroadeningWarning':'ignore',
                  'NegativeEnergiesWarning':'ignore',
                  'HighTemperatureWarning':'ignore',
                  'GaussianBroadeningWarning':'ignore'}
        )  # 0.2)
    sf.load_databank('HITRAN-CO2-TEST')
    
    # DLM:
    sf.misc['chunksize'] = 'DLM'
    s_dlm_eq = sf.eq_spectrum(Tgas=3000)
    s_dlm_eq.name = 'DLM eq ({0:.2f}s)'.format(s_dlm_eq.conditions['calculation_time'])
    
    s_dlm_noneq = sf.non_eq_spectrum(Tvib=3000, Trot=3000)
    s_dlm_noneq.name = 'DLM noneq ({0:.2f}s)'.format(s_dlm_noneq.conditions['calculation_time'])
    
    # Compare
    res = get_residual(s_dlm_eq, s_dlm_noneq, 'radiance_noslit')
    
    if verbose:
        print('Residual:', res)

    # plot
    if plot:
        plot_diff(s_dlm_eq, s_dlm_noneq)
    
    assert res <= 4e-5 
Пример #2
0
def test_broadening_methods_different_wstep(verbose=True, plot=False, *args, **kwargs):
    '''
    Test direct Voigt broadening vs convolution of Gaussian x Lorentzian
    for different spectral grid resolution
    '''

    if plot:  # Make sure matplotlib is interactive so that test are not stuck in pytest
        plt.ion()

    setup_test_line_databases()  # add HITRAN-CO-TEST in ~/.radis if not there

    # Conditions
    T = 3000
    p = 1
    wmin = 2150  # cm-1
    wmax = 2152  # cm-1
    broadening_max_width = 10  # cm-1
    
    for i, wstep in enumerate([0.01, 0.1, 0.5]):

        # %% Calculate with RADIS
        # ----------
        sf = SpectrumFactory(
            wavenum_min=wmin,
            wavenum_max=wmax,
            mole_fraction=1,
            path_length=1,   # doesnt change anything
            wstep=wstep,
            pressure=p,
            broadening_max_width=broadening_max_width,
            isotope='1',
            verbose=False,
            warnings={'MissingSelfBroadeningWarning':'ignore',
                      'NegativeEnergiesWarning':'ignore',
                      'HighTemperatureWarning':'ignore',
                      'GaussianBroadeningWarning':'ignore'}
            )  # 0.2)
        sf.load_databank('HITRAN-CO-TEST')
    #    s = pl.non_eq_spectrum(Tvib=T, Trot=T, Ttrans=T)
        sf._broadening_method = 'voigt'
        s_voigt = sf.eq_spectrum(Tgas=T, name='direct')
        
        sf._broadening_method = 'convolve'
        s_convolve = sf.eq_spectrum(Tgas=T, name='convolve')
    
        res = get_residual(s_voigt, s_convolve, 'abscoeff')
        
        if verbose:
            print('Residual:', res)

        # plot the last one    
        if plot:
            plot_diff(s_voigt, s_convolve, 'abscoeff', nfig='test_voigt_broadening_methods'+str(i),
                      title='P {0} bar, T {1} K, wstep {2} cm-1'.format(p, T, wstep))
        
        assert res < 2e-4
Пример #3
0
def test_media_line_shift(plot=False, verbose=True, warnings=True, *args, **kwargs):
    """ See wavelength difference in air and vacuum """

    if plot:  # Make sure matplotlib is interactive so that test are not stuck in pytest
        plt.ion()

    if verbose:
        printm(">>> _test_media_line_shift")

    setup_test_line_databases()  # add HITRAN-CO-TEST in ~/.radis if not there

    sf = SpectrumFactory(
        wavelength_min=4500,
        wavelength_max=4600,
        wstep=0.001,
        cutoff=1e-30,
        path_length=0.1,
        mole_fraction=400e-6,
        isotope=[1],
        medium="vacuum",
        broadening_max_width=10,
        verbose=verbose,
    )
    sf.warnings["MissingSelfBroadeningWarning"] = "ignore"
    sf.warnings["GaussianBroadeningWarning"] = "ignore"
    sf.load_databank("HITRAN-CO-TEST")

    # Calculate a spectrum
    s = sf.eq_spectrum(2000)

    # Compare
    if plot:
        fig = plt.figure(fig_prefix + "Propagating media line shift")
        s.plot("radiance_noslit", wunit="nm_vac", nfig=fig.number, lw=2, label="Vacuum")
        plt.title("CO spectrum (2000 K)")
        s.plot(
            "radiance_noslit",
            wunit="nm",
            nfig=fig.number,
            lw=2,
            color="r",
            label="Air",
        )

    # ... there should be about ~1.25 nm shift at 4.5 µm:
    assert np.isclose(
        s.get("radiance_noslit", wunit="nm_vac")[0][0]
        - s.get("radiance_noslit", wunit="nm")[0][0],
        1.2540436086346745,
    )
Пример #4
0
def test_pathlength_units_conversion(
    input_pathlength, expected_pathlength_cm, verbose=True, *args, **kwargs
):
    setup_test_line_databases()  # add HITRAN-CO-TEST in ~/.radis if not there

    sf = SpectrumFactory(
        wavelength_min=4300,
        wavelength_max=4500,
        wstep=0.01,
        cutoff=1e-30,
        pressure=1,
        path_length=input_pathlength,
        mole_fraction=1,
        isotope=[1],
        verbose=verbose,
    )
    sf.load_databank("HITRAN-CO-TEST")
    s = sf.eq_spectrum(Tgas=300)
    assert np.isclose(s.conditions["path_length"], expected_pathlength_cm)
Пример #5
0
def test_pressure_units_conversion(
    input_pressure, expected_pressure_bar, verbose=True, *args, **kwargs
):
    setup_test_line_databases()  # add HITRAN-CO-TEST in ~/.radis if not there

    sf = SpectrumFactory(
        wavelength_min=4300,
        wavelength_max=4500,
        wstep=0.01,
        cutoff=1e-30,
        pressure=input_pressure,
        path_length=1,
        mole_fraction=1,
        isotope=[1],
        verbose=verbose,
        warnings={"AccuracyError": "ignore", "AccuracyWarning": "ignore"},
    )
    sf.load_databank("HITRAN-CO-TEST")
    s = sf.eq_spectrum(Tgas=300)
    assert np.isclose(s.conditions["pressure_mbar"], expected_pressure_bar * 1000)
Пример #6
0
def test_wavenumber_units_conversion(
    input_wavenumbers, expected_wavenumbers_cm1, verbose=True, *args, **kwargs
):
    setup_test_line_databases()  # add HITRAN-CO-TEST in ~/.radis if not there

    wmin, wmax = input_wavenumbers
    expected_wmin, expected_wmax = expected_wavenumbers_cm1
    sf = SpectrumFactory(
        wavenum_min=wmin,
        wavenum_max=wmax,
        wstep=0.01,
        cutoff=1e-30,
        pressure=1,
        path_length=1,
        mole_fraction=1,
        isotope=[1],
        verbose=verbose,
    )
    sf.load_databank("HITRAN-CO-TEST")
    s = sf.eq_spectrum(Tgas=300)
    assert np.isclose(s.get_wavenumber().min(), expected_wmin)
    assert np.isclose(s.get_wavenumber().max(), expected_wmax)
Пример #7
0
def test_temperature_units_conversion(
    input_temperature, expected_temperature_K, verbose=True, *args, **kwargs
):
    setup_test_line_databases()  # add HITRAN-CO-TEST in ~/.radis if not there

    sf = SpectrumFactory(
        wavelength_min=4300,
        wavelength_max=4500,
        wstep=0.001,
        cutoff=1e-30,
        pressure=1,
        mole_fraction=1,
        isotope=[1],
        Tref=300 * u.K,
        verbose=verbose,
    )
    sf.load_databank("HITRAN-CO-TEST")
    s = sf.eq_spectrum(
        Tgas=input_temperature, pressure=20 * u.mbar, path_length=1 * u.mm
    )
    assert np.isclose(s.conditions["Tgas"], expected_temperature_K)
    assert np.isclose(s.conditions["path_length"], 0.1)  # cm
    assert np.isclose(s.conditions["pressure_mbar"], 20)
Пример #8
0
def test_broadening_vs_hapi(rtol=1e-2,
                            verbose=True,
                            plot=False,
                            *args,
                            **kwargs):
    """
    Test broadening against HAPI and tabulated data

    We're looking at CO(0->1) line 'R1' at 2150.86 cm-1
    """
    from hapi import absorptionCoefficient_Voigt, db_begin, fetch, tableList

    if plot:  # Make sure matplotlib is interactive so that test are not stuck in pytest
        plt.ion()

    setup_test_line_databases()  # add HITRAN-CO-TEST in ~/.radis if not there

    # Conditions
    T = 3000
    p = 0.0001
    wstep = 0.001
    wmin = 2150  # cm-1
    wmax = 2152  # cm-1
    broadening_max_width = 10  # cm-1

    # %% HITRAN calculation
    # -----------

    # Generate HAPI database locally
    hapi_data_path = join(dirname(__file__),
                          __file__.replace(".py", "_HAPIdata"))

    db_begin(hapi_data_path)
    if not "CO" in tableList():  # only if data not downloaded already
        fetch("CO", 5, 1, wmin - broadening_max_width / 2,
              wmax + broadening_max_width / 2)
        # HAPI doesnt correct for side effects

    # Calculate with HAPI
    nu, coef = absorptionCoefficient_Voigt(
        SourceTables="CO",
        Environment={
            "T": T,
            "p": p / 1.01325,
        },  # K  # atm
        WavenumberStep=wstep,
        HITRAN_units=False,
    )

    s_hapi = Spectrum.from_array(nu,
                                 coef,
                                 "abscoeff",
                                 "cm-1",
                                 "cm-1",
                                 conditions={"Tgas": T},
                                 name="HAPI")

    # %% Calculate with RADIS
    # ----------
    sf = SpectrumFactory(
        wavenum_min=wmin,
        wavenum_max=wmax,
        mole_fraction=1,
        path_length=1,  # doesnt change anything
        wstep=wstep,
        pressure=p,
        broadening_max_width=broadening_max_width,
        isotope=[1],
        warnings={
            "MissingSelfBroadeningWarning": "ignore",
            "NegativeEnergiesWarning": "ignore",
            "HighTemperatureWarning": "ignore",
            "GaussianBroadeningWarning": "ignore",
        },
    )  # 0.2)
    sf.load_databank(path=join(hapi_data_path, "CO.data"),
                     format="hitran",
                     parfuncfmt="hapi")
    #    s = pl.non_eq_spectrum(Tvib=T, Trot=T, Ttrans=T)
    s = sf.eq_spectrum(Tgas=T, name="RADIS")

    if plot:  # plot broadening of line of largest linestrength
        sf.plot_broadening(i=sf.df1.S.idxmax())

    # Plot and compare
    res = abs(get_residual_integral(s, s_hapi, "abscoeff"))
    if plot:
        plot_diff(
            s,
            s_hapi,
            var="abscoeff",
            title="{0} bar, {1} K (residual {2:.2g}%)".format(p, T, res * 100),
            show_points=False,
        )
        plt.xlim((wmin, wmax))
    if verbose:
        printm("residual:", res)
    assert res < rtol
Пример #9
0
def test_broadening_methods_different_wstep(verbose=True,
                                            plot=False,
                                            *args,
                                            **kwargs):
    """
    Test direct Voigt broadening vs convolution of Gaussian x Lorentzian
    for different spectral grid resolution
    """

    if plot:  # Make sure matplotlib is interactive so that test are not stuck in pytest
        plt.ion()

    setup_test_line_databases()  # add HITRAN-CO-TEST in ~/.radis if not there

    # Conditions
    T = 3000
    p = 1
    wmin = 2150  # cm-1
    wmax = 2152  # cm-1
    broadening_max_width = 10  # cm-1

    for i, wstep in enumerate([0.01, 0.1, 0.5]):

        # %% Calculate with RADIS
        # ----------
        sf = SpectrumFactory(
            wavenum_min=wmin,
            wavenum_max=wmax,
            mole_fraction=1,
            path_length=1,  # doesnt change anything
            wstep=wstep,
            pressure=p,
            broadening_max_width=broadening_max_width,
            isotope="1",
            optimization=None,
            verbose=False,
            warnings={
                "MissingSelfBroadeningWarning": "ignore",
                "NegativeEnergiesWarning": "ignore",
                "HighTemperatureWarning": "ignore",
                "GaussianBroadeningWarning": "ignore",
                "AccuracyError": "ignore",
                "AccuracyWarning": "ignore",
            },
        )  # 0.2)
        sf.load_databank("HITRAN-CO-TEST")
        #    s = pl.non_eq_spectrum(Tvib=T, Trot=T, Ttrans=T)
        sf.params.broadening_method = "voigt"
        s_voigt = sf.eq_spectrum(Tgas=T, name="direct")

        sf.params.broadening_method = "convolve"
        s_convolve = sf.eq_spectrum(Tgas=T, name="convolve")

        res = get_residual(s_voigt, s_convolve, "abscoeff")

        if verbose:
            print("Residual:", res)

        # plot the last one
        if plot:
            plot_diff(
                s_voigt,
                s_convolve,
                "abscoeff",
                nfig="test_voigt_broadening_methods" + str(i),
                title="P {0} bar, T {1} K, wstep {2} cm-1".format(p, T, wstep),
            )

        assert res < 2e-4
Пример #10
0
def test_broadening_methods_different_conditions(verbose=True,
                                                 plot=False,
                                                 *args,
                                                 **kwargs):
    """
    Test direct Voigt broadening vs convolution of Gaussian x Lorentzian
    for different spectral grid resolution

    Notes
    -----

    Reference broadening calculated manually with the HWHM formula of
    `HITRAN.org <https://hitran.org/docs/definitions-and-units/>`_
    """

    if plot:  # Make sure matplotlib is interactive so that test are not stuck in pytest
        plt.ion()

    setup_test_line_databases()  # add HITRAN-CO-TEST in ~/.radis if not there

    # Conditions
    wstep = 0.005
    wmin = 2150.4  # cm-1
    wmax = 2151.4  # cm-1
    broadening_max_width = 2  # cm-1

    for (T, p, fwhm_lorentz, fwhm_gauss) in [
            # K, bar, expected FWHM for Lotentz, gauss (cm-1)
        (3000, 1, 0.02849411, 0.01594728),
        (300, 1, 0.16023415, 0.00504297),
        (3000, 0.01, 0.00028494, 0.01594728),
    ]:

        # %% Calculate with RADIS
        # ----------
        sf = SpectrumFactory(
            wavenum_min=wmin,
            wavenum_max=wmax,
            mole_fraction=1,
            path_length=1,  # doesnt change anything
            wstep=wstep,
            pressure=p,
            broadening_max_width=broadening_max_width,
            isotope="1",
            verbose=False,
            warnings={
                "MissingSelfBroadeningWarning": "ignore",
                "NegativeEnergiesWarning": "ignore",
                "HighTemperatureWarning": "ignore",
                "OutOfRangeLinesWarning": "ignore",
                "GaussianBroadeningWarning": "ignore",
                "CollisionalBroadeningWarning": "ignore",
            },
        )
        sf.load_databank("HITRAN-CO-TEST")
        # Manually filter line database, keep one line only:
        sf.df0.drop(sf.df0[sf.df0.vu != 1].index, inplace=True)
        assert isclose(sf.df0.wav, 2150.856008)

        # Calculate spectra (different broadening methods)
        sf.params.broadening_method = "voigt"
        s_voigt = sf.eq_spectrum(Tgas=T, name="direct")

        # assert broadening FWHM are correct
        assert isclose(2 * float(sf.df1.hwhm_gauss), fwhm_gauss)
        assert isclose(2 * float(sf.df1.hwhm_lorentz), fwhm_lorentz)

        sf.params.broadening_method = "convolve"
        s_convolve = sf.eq_spectrum(Tgas=T, name="convolve")

        # assert broadening FWHM are correct
        assert isclose(2 * float(sf.df1.hwhm_gauss), fwhm_gauss)
        assert isclose(2 * float(sf.df1.hwhm_lorentz), fwhm_lorentz)

        res = get_residual(s_voigt, s_convolve, "abscoeff")

        if verbose:
            print(
                "{0} K, {1} bar: FWHM lorentz = {2:.3f} cm-1, FWHM gauss = {3:.3f} cm-1"
                .format(T, p, 2 * float(sf.df1.hwhm_lorentz),
                        2 * float(sf.df1.hwhm_gauss)))

        if plot:
            plot_diff(
                s_voigt,
                s_convolve,
                "abscoeff",
                title=
                r"T {0} K, p {1} bar: w$_\mathrm{{L}}$ {2:.3f}, w$_\mathrm{{G}}$ {3:.3f} cm$^{{-1}}$"
                .format(T, p, 2 * float(sf.df1.hwhm_lorentz),
                        float(sf.df1.hwhm_gauss)),
            )

        # assert all broadening methods match
        assert res < 2e-4
Пример #11
0
def test_resampling(rtol=1e-2,
                    verbose=True,
                    plot=True,
                    warnings=True,
                    *args,
                    **kwargs):
    """ Test what happens when a spectrum in nm or cm-1, is convolved
    with a slit function in nm. In particular, slit function is generated
    in the spectrum unit, and spectrum is resampled if not evenly spaced"""

    if verbose:
        printm("Test auto resampling")

    if plot:  # Make sure matplotlib is interactive so that test are not stuck in pytest
        plt.ion()

    try:

        setup_test_line_databases(
        )  # add HITRAN-CO-TEST in ~/.radis if not there

        plCO = SpectrumFactory(
            wavenum_min=2230,
            wavenum_max=2260,
            mole_fraction=0.02,
            path_length=100,  # cm
            broadening_max_width=20,  # cm^-1
            wstep=0.02,
            isotope=[1, 2, 3],
            verbose=verbose,
        )
        plCO.warnings["MissingSelfBroadeningWarning"] = "ignore"
        plCO.load_databank("HITRAN-CO-TEST")
        sCO = plCO.eq_spectrum(Tgas=300)

        w_nm, T_nm = sCO.get("transmittance_noslit", wunit="nm")
        w_nm, I_nm = sCO.get("radiance_noslit",
                             wunit="nm",
                             Iunit="mW/cm2/sr/nm")
        sCO_nm = transmittance_spectrum(
            w_nm, T_nm, wunit="nm")  # a new spectrum stored in nm
        # sCO_nm = theoretical_spectrum(w_nm, I_nm, wunit='nm', Iunit='mW/cm2/sr/nm') #  a new spectrum stored in nm

        if plot:
            fig = plt.figure(fig_prefix + "auto-resampling")
            sCO.plot(
                "transmittance_noslit",
                wunit="cm-1",
                nfig=fig.number,
                marker="o",
                color="k",
                lw=3,
                ms=10,
                label="(stored in cm-1)",
            )
            plt.title("No slit function")
            sCO_nm.plot(
                "transmittance_noslit",
                wunit="cm-1",
                nfig=fig.number,
                marker="o",
                color="r",
                label="(stored in nm)",
            )
            #            plt.xlim((2246.58, 2247.52))
            #            plt.ylim((0.87, 1.01))
            plt.legend()

        slit_function = 0.8
        slit_unit = "cm-1"
        sCO.apply_slit(slit_function, unit=slit_unit)
        sCO_nm.apply_slit(slit_function, unit=slit_unit)

        if plot:
            fig = plt.figure(fig_prefix +
                             "auto-resampling (after convolution)")
            sCO.plot(
                "transmittance",
                wunit="cm-1",
                nfig=fig.number,
                marker="o",
                color="k",
                lw=3,
                ms=10,
                label="(stored in cm-1)",
            )
            plt.title("Slit function: {0} {1}".format(slit_function,
                                                      slit_unit))
            sCO_nm.plot(
                "transmittance",
                wunit="cm-1",
                nfig=fig.number,
                marker="o",
                color="r",
                label="(stored in nm)",
            )

            #            plt.xlim((2246.58, 2247.52))
            #            plt.ylim((0.87, 1.01))
            plt.legend()

        w_conv, T_conv = sCO.get("transmittance", wunit="cm-1")
        w_nm_conv, T_nm_conv = sCO_nm.get("transmittance", wunit="cm-1")

        error = abs(
            (trapz(1 - T_conv, w_conv) - trapz(1 - T_nm_conv, w_nm_conv)) /
            trapz(1 - T_nm_conv, w_nm_conv))

        if verbose:
            printm("\n>>> _test_resampling\n")
        if verbose:
            printm("Error between 2 spectra ({0:.2f}%) < {1:.2f}%: {2}".format(
                error * 100, rtol * 100, bool(error < rtol)))
        assert bool(error < rtol)

    except DatabankNotFound as err:
        assert IgnoreMissingDatabase(err, __file__, warnings)
Пример #12
0
def test_broadening(rtol=1e-2, verbose=True, plot=False, *args, **kwargs):
    '''
    Test broadening against HAPI and tabulated data

    We're looking at CO(0->1) line 'R1' at 2150.86 cm-1
    '''
    from radis.io.hapi import db_begin, fetch, tableList, absorptionCoefficient_Voigt

    if plot:  # Make sure matplotlib is interactive so that test are not stuck in pytest
        plt.ion()

    setup_test_line_databases()  # add HITRAN-CO-TEST in ~/.radis if not there

    # Conditions
    T = 3000
    p = 0.0001
    wstep = 0.001
    wmin = 2150  # cm-1
    wmax = 2152  # cm-1
    broadening_max_width = 10  # cm-1

    # %% HITRAN calculation
    # -----------

    # Generate HAPI database locally
    db_begin(join(dirname(__file__), __file__.replace('.py', '_HAPIdata')))
    if not 'CO' in tableList():  # only if data not downloaded already
        fetch('CO', 5, 1, wmin - broadening_max_width / 2,
              wmax + broadening_max_width / 2)
        # HAPI doesnt correct for side effects

    # Calculate with HAPI
    nu, coef = absorptionCoefficient_Voigt(
        SourceTables='CO',
        Environment={
            'T': T,  # K
            'p': p / 1.01325,  # atm
        },
        WavenumberStep=wstep,
        HITRAN_units=False)

    s_hapi = Spectrum.from_array(nu,
                                 coef,
                                 'abscoeff',
                                 'cm-1',
                                 'cm_1',
                                 conditions={'Tgas': T},
                                 name='HAPI')

    # %% Calculate with RADIS
    # ----------
    sf = SpectrumFactory(
        wavenum_min=wmin,
        wavenum_max=wmax,
        mole_fraction=1,
        path_length=1,  # doesnt change anything
        wstep=wstep,
        pressure=p,
        broadening_max_width=broadening_max_width,
        isotope=[1],
        warnings={
            'MissingSelfBroadeningWarning': 'ignore',
            'NegativeEnergiesWarning': 'ignore',
            'HighTemperatureWarning': 'ignore',
            'GaussianBroadeningWarning': 'ignore'
        })  # 0.2)
    sf.load_databank('HITRAN-CO-TEST')
    #    s = pl.non_eq_spectrum(Tvib=T, Trot=T, Ttrans=T)
    s = sf.eq_spectrum(Tgas=T, name='RADIS')

    if plot:  # plot broadening of line of largest linestrength
        sf.plot_broadening(i=sf.df1.S.idxmax())

    # Plot and compare
    res = abs(get_residual_integral(s, s_hapi, 'abscoeff'))
    if plot:
        plot_diff(s,
                  s_hapi,
                  var='abscoeff',
                  title='{0} bar, {1} K (residual {2:.2g}%)'.format(
                      p, T, res * 100),
                  show_points=False)
        plt.xlim((wmin, wmax))
    if verbose:
        printm('residual:', res)
    assert res < rtol
Пример #13
0
def _calc_spectrum(
    wavenum_min,
    wavenum_max,
    wavelength_min,
    wavelength_max,
    Tgas,
    Tvib,
    Trot,
    pressure,
    overpopulation,
    molecule,
    isotope,
    mole_fraction,
    path_length,
    databank,
    medium,
    wstep,
    broadening_max_width,
    cutoff,
    optimization,
    name,
    use_cached,
    verbose,
    mode,
    **kwargs
):
    """See :py:func:`~radis.lbl.calc.calc_spectrum`"""

    # Check inputs

    # ... wavelengths / wavenumbers
    if (wavelength_min is not None or wavelength_max is not None) and (
        wavenum_min is not None or wavenum_max is not None
    ):
        raise ValueError("Wavenumber and Wavelength both given... it's time to choose!")

    if wavenum_min is None and wavenum_max is None:
        assert wavelength_max is not None
        assert wavelength_min is not None
        wavenum_min = nm2cm(wavelength_max)
        wavenum_max = nm2cm(wavelength_min)
    else:
        assert wavenum_min is not None
        assert wavenum_max is not None

    # ... temperatures

    if Tgas is None and Trot is None:
        raise ValueError(
            "Choose either Tgas (equilibrium) or Tvib / Trot (non equilibrium)"
        )

    if Tvib is None and Trot is not None or Tvib is not None and Trot is None:
        raise ValueError("Choose both Tvib and Trot")

    # ... others
    if databank is None:
        raise ValueError("Give a databank name")

    if not "save_memory" in kwargs:
        # no need to save intermediary results as
        # factory is used once only
        kwargs["save_memory"] = True

    if "chunksize" in kwargs:
        raise DeprecationWarning("use optimization= instead of chunksize=")

    def _is_at_equilibrium():
        try:
            assert Tvib is None or Tvib == Tgas
            assert Trot is None or Trot == Tgas
            assert overpopulation is None
            if "self_absorption" in kwargs:
                assert kwargs["self_absorption"]  # == True
            return True
        except AssertionError:
            return False

    _equilibrium = _is_at_equilibrium()

    # which columns to keep when loading line database
    if kwargs["save_memory"] >= 2 and _equilibrium:
        drop_columns = "all"
    else:
        drop_columns = "auto"

    # Run calculations
    sf = SpectrumFactory(
        wavenum_min,
        wavenum_max,
        medium=medium,
        molecule=molecule,
        isotope=isotope,
        pressure=pressure,
        wstep=wstep,
        broadening_max_width=broadening_max_width,
        cutoff=cutoff,
        verbose=verbose,
        optimization=optimization,
        **kwargs
    )
    if databank in [
        "fetch",
        "hitran",
        "hitemp",
    ]:  # mode to get databank without relying on  Line databases
        # Line database :
        if databank in ["fetch", "hitran"]:
            conditions = {"source": "hitran"}
        elif databank in ["hitemp"]:
            conditions = {"source": "hitemp"}
        # Partition functions :
        conditions.update(
            **{
                "parfuncfmt": "hapi",  # use HAPI (TIPS) partition functions for equilibrium
                "levelsfmt": None,  # no need to load energies by default
                "db_use_cached": use_cached,
            }
        )
        # Rovibrational energies :
        if not _equilibrium:
            # calculate partition functions with energy levels from built-in
            # constants (not all molecules are supported!)
            conditions["levelsfmt"] = "radis"
            conditions["lvl_use_cached"] = use_cached
        sf.fetch_databank(**conditions)
    elif exists(databank):
        conditions = {
            "path": databank,
            "drop_columns": drop_columns,
            "parfuncfmt": "hapi",  # use HAPI (TIPS) partition functions for equilibrium
            "levelsfmt": None,  # no need to load energies by default
            "db_use_cached": use_cached,
        }
        # Guess format
        if databank.endswith(".par"):
            if verbose:
                print("Infered {0} is a HITRAN-format file.".format(databank))
            conditions["format"] = "hitran"
            # If non-equilibrium we'll also need to load the energy levels.
            if not _equilibrium:
                # calculate partition functions with energy levels from built-in
                # constants (not all molecules are supported!)
                conditions["levelsfmt"] = "radis"
                conditions["lvl_use_cached"] = use_cached
        elif databank.endswith(".h5"):
            conditions["format"] = "hdf5"
            if not _equilibrium:
                conditions["levelsfmt"] = "radis"
        else:
            raise ValueError(
                "Couldnt infer the format of the line database file: {0}. ".format(
                    databank
                )
                + "Create a user-defined database in your ~/.radis file "
                + "and define the format there. More information on "
                + "https://radis.readthedocs.io/en/latest/lbl/lbl.html#configuration-file"
            )

        sf.load_databank(**conditions)

    else:  # manual mode: get from user-defined line databases defined in ~/.radis
        sf.load_databank(
            databank,
            load_energies=not _equilibrium,  # no need to load/calculate energies at eq.
            drop_columns=drop_columns,
        )

    #    # Get optimisation strategies
    #    if lineshape_optimization == 'auto':        # NotImplemented: finally we use DLM all the time as default.
    #        if len(sf.df0) > 1e5:
    #            lineshape_optimization = 'DLM'
    #        else:
    #            lineshape_optimization = None
    #        sf.params['chunksize'] = lineshape_optimization

    # Use the standard eq_spectrum / non_eq_spectrum functions
    if _equilibrium:
        if mode == "cpu":
            s = sf.eq_spectrum(
                Tgas=Tgas,
                mole_fraction=mole_fraction,
                path_length=path_length,
                name=name,
            )
        else:
            s = sf.eq_spectrum_gpu(
                Tgas=Tgas,
                mole_fraction=mole_fraction,
                pressure=pressure,
                path_length=path_length,
                name=name,
            )
    else:
        s = sf.non_eq_spectrum(
            Tvib=Tvib,
            Trot=Trot,
            Ttrans=Tgas,
            overpopulation=overpopulation,
            mole_fraction=mole_fraction,
            path_length=path_length,
            name=name,
        )

    return s
Пример #14
0
def test_spec_generation(
    plot=True,
    verbose=2,
    warnings=True,
    update_reference_spectrum=False,
    *args,
    **kwargs
):
    """Test spectrum generation
    Can be used as a base to generate spectra in your codes

    Non-regression test: compare with past version (see conditions below)

    Compare results from a reference case to results calculated on 30/12/2017
    This is not a validation case (30/12/2017 results are not a physically validated
    case), but it makes sure results dont change over time

    Conditions (30/12/2017)::

        Physical Conditions
        ----------------------------------------
           Tgas                 300 K
           Trot                 300 K
           Tvib                 300 K
           pressure             1.01325 bar
           isotope              1,2
           mole_fraction        1
           molecule             CO2
           path_length          1 cm
           wavelength_max       4400.0 nm
           wavelength_min       4150.0 nm
           wavenum_max          2409.6385542168673 cm-1
           wavenum_min          2272.7272727272725 cm-1
        Computation Parameters
        ----------------------------------------
           Tref                 296 K
           broadening_max_width  10 cm-1
           cutoff               1e-25 cm-1/(#.cm-2)
           db_assumed_sorted    True
           db_use_cached        True
           dbformat             cdsd
           dbpath               # USER-DEPENDANT: CDSD-HITEMP
           fillmissinglevelswithzero  False
           levelsfmt            cdsd
           levelspath           # USER-DEPENDANT: CDSD-4000
           medium               vacuum
           parfuncfmt           cdsd
           parfuncpath          # USER-DEPENDANT: CDSD-4000
           rot_distribution     boltzmann
           self_absorption      True
           vib_distribution     boltzmann
           wavenum_max_calc     2414.6385542168673 cm-1
           wavenum_min_calc     2267.7272727272725 cm-1
           waveunit             cm-1
           wstep                0.01 cm-1
        ----------------------------------------

    Notes
    -----

    Performance test. How long it tooks to calculate this Spectrum?
    Test with cutoff 1e-25, broadening_max_width=10

    - 0.9.15: >>> 33s

    - 0.9.16*: (replaced groupby().apply() with iteration over indexes) >>> 32s
            [but large impact expected on big files]

    - 0.9.16*: (upgraded cache files to h5) >>> 25s

    - 0.9.16*: (also added h5 cache file for levels) >>> 21s

    - 0.9.16*: (with Whiting slit voigt function) >>> 5.8s

    Test with cutoff 1e-27, broadening_max_width=50 :
    ("Spectrum calculated in ... ", including database loading time)

    - 0.9.16*: (same code as last) >>> 12.5s including 7.6s of broadening

    - 0.9.16**: (with pseudo_continuum_threshold=0.01) >>> 7.8s including 2.3s of broadening

    - 0.9.18 (normal code, no pseudo continuum). >>> ?

    - 0.9.21 (normal code) >>> 13.7s, including 8.7s of broadening
             (with pseudo_continuum_threshold=0.01) >>> 4.3s, including 2.6s of broadening

    - 0.9.21*              >>> 14.0s  (added the manual lineshape normalization instead of
                                       Whitings's polynomial)

    - 0.9.22 (normal code) >>> 11.3s   (without energy level lookup, for eq. calculations)
             (with pseudo_continuum_threshold=0.01) >>> 5.9s

    - 0.9.23 (normal code) >>> 7.2s   (added jit in Voigt broadening)
                           >>> 7.1s   (chunksize = None)  (and much faster for more lines)
             (with pseudo_continuum_threshold=0.01) >>> 4.9s

    RADIS:

    - 0.9.19 (normal code) >>> 6.3 s

    - 0.9.20 (normal code) >>> 6.3 s
             (with pseudo_continuum_threshold=0.01) >>> ???
             (with DLM) >>> 2.3 s

    - 0.9.26 (normal code) >>> 7.6 s
             (with pseudo_continuum_threshold=0.01) >>> 2.73s
             (with DLM) >>> 0.25 s

    """

    if plot:  # Make sure matplotlib is interactive so that test are not stuck in pytest
        plt.ion()

    from time import time

    t0 = time()
    if verbose:
        printm(">>> _test_spec_generation")

    # This is how you get a spectrum (see calc.py for front-end functions
    # that do just that)
    sf = SpectrumFactory(
        wavelength_min=4150,
        wavelength_max=4400,
        cutoff=1e-27,
        isotope="1,2",
        broadening_max_width=50,
        optimization=None,
        # optimization="min-RMS",
        # pseudo_continuum_threshold=0.01,
        medium="vacuum",
        verbose=verbose,
    )
    sf.warnings["MissingSelfBroadeningWarning"] = "ignore"
    sf.warnings["NegativeEnergiesWarning"] = "ignore"
    sf.load_databank(
        "HITEMP-CO2-DUNHAM",
        load_energies=False,  # no need to load energies at equilibrium
    )
    s = sf.eq_spectrum(Tgas=300, name="test_spec_generation")
    if verbose:
        printm(
            ">>> _test_spec_generation: Spectrum calculated in {0:.2f}s".format(
                time() - t0
            )
        )

    if plot:
        plt.figure(fig_prefix + "Reference spectrum CDSD-HITEMP (radiance)")
        # Iunit is arbitrary. Use whatever makes sense
        s.plot("radiance_noslit", Iunit="µW/cm2/sr/nm", nfig="same")
    s.rescale_path_length(0.01)

    # Here we get some extra informations:
    if plot:
        sf.plot_broadening(i=0)  # show broadening of one line
        plt.xlim((2267.20, 2268.30))

    # Compare with harcoded results
    # ... code previously used to export hardcoded results:
    # ... and header contains all input conditions:
    #        np.savetxt('output.txt', np.vstack(s.get('abscoeff', wunit='nm')).T[::10])
    #        print(s)
    # ................
    from radis import get_version
    from radis.test.utils import getTestFile

    wref, Iref = np.loadtxt(getTestFile("CO2abscoeff_300K_4150_4400nm.txt")).T
    match_reference = np.allclose(s.get("abscoeff", wunit="nm")[1][::10], Iref)
    if not match_reference:
        # give some more information before raising error
        printm(
            "Error: {0:.2f}%".format(
                np.mean(abs(s.get("abscoeff", wunit="nm")[1][::10] / Iref - 1)) * 100
            )
        )
        # Store the faulty spectrum
        s.store(
            "test_factory_failed_{0}.spec".format(radis.get_version()),
            if_exists_then="replace",
        )

    # Use "update_reference_spectrum=True" to update reference case :
    if update_reference_spectrum:
        wsave, Isave = s.get("abscoeff", wunit="nm")
        import io
        from contextlib import redirect_stdout

        with io.StringIO() as buf, redirect_stdout(buf):
            print(s)
            s_details = buf.getvalue()
        np.savetxt(
            getTestFile("CO2abscoeff_300K_4150_4400nm.txt"),
            np.vstack((wsave[::10], Isave[::10])).T,
            header="RADIS {0}\n\n{1}".format(
                get_version(add_git_number=False), s_details
            ),
        )

    # Plot comparison
    if plot:
        plt.figure(fig_prefix + "Reference spectrum (abscoeff)")
        # , show_points=True)  # show_points to have an
        s.plot(
            "abscoeff",
            wunit="nm",
            nfig="same",
            lw=3,
            label="RADIS, this version",
        )
        # idea of the resolution
        plt.plot(wref, Iref, "or", ms=3, label="version RADIS 0.9.26 (13/12/20)")
        plt.legend()
        plt.title("All close: {0}".format(match_reference))
        plt.tight_layout()

    # Another example, at higher temperature.
    # Removed because no test is associated with it and it takes time for
    # nothing
    #        s2 = sf.non_eq_spectrum(Tvib=1000, Trot=300)
    #        if plot: s2.plot('abscoeff', wunit='nm')

    if verbose:
        printm(
            "Spectrum calculation (no database loading) took {0:.1f}s\n".format(
                s.conditions["calculation_time"]
            )
        )
        printm("_test_spec_generation finished in {0:.1f}s\n".format(time() - t0))

    assert match_reference
Пример #15
0
def test_power_integral(verbose=True, warnings=True, *args, **kwargs):
    """Test direct calculation of power integral from Einstein coefficients
    matches integration of broadened spectrum in the optically thin case

    We compare:

    - direct calculation of power integral with equilibrium code
        :meth:`~radis.lbl.SpectrumFactory.optically_thin_power` (T)
    - direct calculation of power integral with non equilibrium code
        :meth:`~radis.lbl.SpectrumFactory.optically_thin_power` (T,T)
    - numerical integration of non equilibrium spectrum under optically thin conditions:
        :meth:`~radis.spectrum.spectrum.Spectrum.get_power`

    Test passes if error < 0.5%

    """

    if verbose:
        printm(">>> _test_power_integral")

    setup_test_line_databases()  # add HITRAN-CO-TEST in ~/.radis if not there

    sf = SpectrumFactory(
        wavelength_min=4300,
        wavelength_max=4666,
        wstep=0.001,
        cutoff=1e-30,
        path_length=10,
        mole_fraction=400e-6,
        isotope=[1],
        broadening_max_width=10,
        verbose=verbose,
    )
    sf.warnings.update(
        {
            "MissingSelfBroadeningWarning": "ignore",
            "OutOfRangeLinesWarning": "ignore",
            "HighTemperatureWarning": "ignore",
        }
    )
    sf.load_databank("HITRAN-CO-TEST", db_use_cached=True)
    unit = "µW/sr/cm2"
    T = 600

    # Calculate:

    # ... direct calculation of power integral with equilibrium code
    Peq = sf.optically_thin_power(Tgas=T, unit=unit)

    # ... direct calculation of power integral with non equilibrium code
    Pneq = sf.optically_thin_power(Tvib=T, Trot=T, unit=unit)

    # ... numerical integration of non equilibrium spectrum under optically thin
    # ... conditions
    sf.input.self_absorption = False
    s = sf.non_eq_spectrum(T, T)

    assert s.conditions["self_absorption"] == False

    # Compare
    err = abs(Peq - s.get_power(unit=unit)) / Peq
    if verbose:
        printm("Emission integral:\t{0:.4g}".format(Peq), unit)
        printm("Emission (noneq code):\t{0:.4g}".format(Pneq), unit)
        printm("Integrated spectrum:\t{0:.4g}".format(s.get_power(unit=unit)), unit)
        printm("Error: {0:.2f}%".format(err * 100))

    assert err < 0.005
Пример #16
0
def test_broadening_DLM(verbose=True, plot=False, *args, **kwargs):
    """
    Test use of lineshape template for broadening calculation.

    Ensures that results are the same with and without DLM.
    """

    if plot:  # Make sure matplotlib is interactive so that test are not stuck in pytest
        plt.ion()

    setup_test_line_databases()  # add HITRAN-CO-TEST in ~/.radis if not there

    # Conditions
    T = 3000
    p = 1
    wstep = 0.002
    wmin = 2150  # cm-1
    wmax = 2152  # cm-1
    broadening_max_width = 10  # cm-1

    # %% Calculate with RADIS
    # ----------
    sf = SpectrumFactory(
        wavenum_min=wmin,
        wavenum_max=wmax,
        mole_fraction=1,
        path_length=1,  # doesnt change anything
        wstep=wstep,
        pressure=p,
        broadening_max_width=broadening_max_width,
        isotope="1",
        verbose=False,
        warnings={
            "MissingSelfBroadeningWarning": "ignore",
            "NegativeEnergiesWarning": "ignore",
            "HighTemperatureWarning": "ignore",
            "GaussianBroadeningWarning": "ignore",
        },
    )  # 0.2)
    sf.load_databank("HITRAN-CO-TEST")

    # Reference: calculate without DLM
    assert sf.misc["chunksize"] is None
    s_ref = sf.eq_spectrum(Tgas=T)
    s_ref.name = "Reference ({0:.2f}s)".format(
        s_ref.conditions["calculation_time"])

    # DLM:
    sf.misc["chunksize"] = "DLM"
    sf.params.broadening_method = "convolve"
    s_dlm = sf.eq_spectrum(Tgas=T)
    s_dlm.name = "DLM ({0:.2f}s)".format(s_dlm.conditions["calculation_time"])
    # DLM Voigt with Whiting approximation:
    sf.params.broadening_method = "voigt"
    s_dlm_voigt = sf.eq_spectrum(Tgas=T)
    s_dlm_voigt.name = "DLM Whiting ({0:.2f}s)".format(
        s_dlm_voigt.conditions["calculation_time"])

    # Compare
    res = get_residual(s_ref, s_dlm, "abscoeff")
    res_voigt = get_residual(s_dlm, s_dlm_voigt, "abscoeff")

    if verbose:
        print("Residual:", res)

    # plot the last one
    if plot:
        plot_diff(s_ref, s_dlm, "abscoeff")
        plt.legend()
        plot_diff(s_dlm, s_dlm_voigt, "abscoeff")

    assert res < 1.2e-5
    assert res_voigt < 1e-5
Пример #17
0
def test_broadening_DLM_FT(verbose=True, plot=False, *args, **kwargs):
    """
    Test use of DLM with and without Fourier Transform

    Ensures that results are the same, and compare calculation times.
    """

    if plot:  # Make sure matplotlib is interactive so that test are not stuck in pytest
        plt.ion()

    setup_test_line_databases()  # add HITRAN-CO-TEST in ~/.radis if not there

    # Conditions
    T = 3000
    p = 1
    wstep = 0.002
    wmin = 2000  # cm-1
    wmax = 2300  # cm-1
    broadening_max_width = 10  # cm-1

    # %% Calculate with RADIS
    # ----------
    sf = SpectrumFactory(
        wavenum_min=wmin,
        wavenum_max=wmax,
        mole_fraction=1,
        path_length=1,  # doesnt change anything
        wstep=wstep,
        pressure=p,
        broadening_max_width=broadening_max_width,
        isotope="1",
        verbose=verbose,
        chunksize="DLM",
        warnings={
            "MissingSelfBroadeningWarning": "ignore",
            "NegativeEnergiesWarning": "ignore",
            "HighTemperatureWarning": "ignore",
            "GaussianBroadeningWarning": "ignore",
        },
    )  # 0.2)
    sf.load_databank("HITRAN-CO-TEST")

    # DLM, real space
    if verbose:
        print("\nConvolve version \n")
    sf._broadening_method = "convolve"
    s_dlm = sf.eq_spectrum(Tgas=T)
    s_dlm.name = "DLM ({0:.2f}s)".format(s_dlm.conditions["calculation_time"])

    # DLM , with Fourier
    if verbose:
        print("\nFFT version \n")
    sf.params.broadening_method = "fft"
    s_dlm_fft = sf.eq_spectrum(Tgas=T)
    s_dlm_fft.name = "DLM FFT ({0:.2f}s)".format(
        s_dlm_fft.conditions["calculation_time"])

    # Compare
    res = get_residual(s_dlm, s_dlm_fft, "abscoeff")

    if verbose:
        print("Residual:", res)

    # plot
    if plot:
        plot_diff(s_dlm, s_dlm_fft, "abscoeff")
        plt.legend()

    assert res < 5e-6
Пример #18
0
def calc_spectrum(
    wavenum_min=None,
    wavenum_max=None,
    wavelength_min=None,
    wavelength_max=None,
    Tgas=None,
    Tvib=None,
    Trot=None,
    pressure=1.01325,
    overpopulation=None,
    molecule="",
    isotope="all",
    mole_fraction=1,
    path_length=1,
    databank="fetch",
    medium="air",
    wstep=0.01,
    broadening_max_width=10,
    lineshape_optimization="DLM",
    name=None,
    use_cached=True,
    verbose=True,
    **kwargs
):
    """ Multipurpose function to calculate :class:`~radis.spectrum.spectrum.Spectrum`
    under equilibrium, or non-equilibrium, with or without overpopulation. 
    It's a wrapper to :class:`~radis.lbl.factory.SpectrumFactory` class. 
    For advanced used, please refer to the aforementionned class. 

    Parameters
    ----------

    wavenum_min: float(cm^-1) or `~astropy.units.quantity.Quantity`
        minimum wavenumber.
    wavenum_max: float(cm^-1) or `~astropy.units.quantity.Quantity`
        maximum wavenumber.

    wavelength_min: float(nm) or `~astropy.units.quantity.Quantity`
        minimum wavelength. Wavelength in ``'air'`` or 
        ``'vacuum'`` depending of the value of the parameter ``'medium='``
    wavelength_max: float(nm) or `~astropy.units.quantity.Quantity`
        maximum wavelength. Wavelength in ``'air'`` or 
        ``'vacuum'`` depending of the value of the parameter ``'medium='``

    Tgas: float(K) or `~astropy.units.quantity.Quantity`
        Gas temperature. If non equilibrium, is used for Ttranslational. 
        Default ``300`` K

    Tvib: float(K) or `~astropy.units.quantity.Quantity`
        Vibrational temperature. If ``None``, equilibrium calculation is run with Tgas

    Trot: float(K) or `~astropy.units.quantity.Quantity`
        Rotational temperature. If ``None``, equilibrium calculation is run with Tgas

    pressure: float(bar) or `~astropy.units.quantity.Quantity`
        partial pressure of gas. Default ``1.01325`` (1 atm)

    overpopulation: dict
        dictionary of overpopulation compared to the given vibrational temperature. 
        Ex with CO2::

            overpopulation = {
            '(00`0`0)->(00`0`1)': 2.5,
                '(00`0`1)->(00`0`2)': 1,
                '(01`1`0)->(01`1`1)': 1,
                '(01`1`1)->(01`1`2)': 1,
                }

    molecule: int, str, or ``None``
        molecule id (HITRAN format) or name. If ``None``, the molecule can be infered
        from the database files being loaded. See the list of supported molecules 
        in :py:data:`~radis.io.MOLECULES_LIST_EQUILIBRIUM`
        and :py:data:`~radis.io.MOLECULES_LIST_NONEQUILIBRIUM`. 
        Default ``None``. 

    isotope: int, list, str of the form ``'1,2'``, or ``'all'``
        isotope id (sorted by relative density: (eg: 1: CO2-626, 2: CO2-636 for CO2).
        See [HITRAN-2016]_ documentation for isotope list for all species. If ``'all'``,
        all isotopes in database are used (this may result in larger computation
        times!). Default ``'all'``

    mole_fraction: float
        database species mole fraction. Default ``1``

    path_length: float(cm) or `~astropy.units.quantity.Quantity`
        slab size. Default ``1`` cm.

    databank: str
        can be either: 

        - ``'fetch'``, to fetch automatically from [HITRAN-2016]_ through astroquery. 
        
        .. warning::
            
            [HITRAN-2016]_ is valid for low temperatures (typically < 700 K). For higher
            temperatures you may need [HITEMP-2010]_ 

        - the path to a valid database file, in which case the format is inferred. 
          For instance, ``.par`` is recognized as ``hitran/hitemp`` format. 

        - the name of a spectral database registered in your ``~/.radis`` 
          configuration file. This allows to use multiple database files.
          See :ref:`Configuration file <label_lbl_config_file>`.

        Default ``'fetch'``. See :class:`~radis.lbl.loader.DatabankLoader` for more 
        information on line databases, and :data:`~radis.misc.config.DBFORMAT` for 
        your ``~/.radis`` file format 
        
        Example::
        
            databank='fetch'     # automatic download 
            databank='PATH/TO/05_HITEMP2019.par'    # path to a file 
            databank='HITEMP-2019-CO'   # user-defined database in Configuration file 

    medium: ``'air'``, ``'vacuum'``
        propagating medium when giving inputs with ``'wavenum_min'``, ``'wavenum_max'``. 
        Does not change anything when giving inputs in wavenumber. Default ``'air'``

    wstep: float (cm-1)
        Spacing of calculated spectrum. Default ``0.01`` cm-1.

    broadening_max_width: float (cm-1)
        Full width over which to compute the broadening. Large values will create
        a huge performance drop (scales as ~broadening_width^2 without DLM)
        The calculated spectral range is increased (by broadening_max_width/2
        on each side) to take into account overlaps from out-of-range lines.
        Default ``10`` cm-1.

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

    lineshape_optimization: int, ``None``, ``'DLM'``, or ``'auto'``.
        Optimizations for the calculation of the lineshapes:
        
            - If ``None``, all lineshapes are calculated at the same time (can 
              create memory errors). 
            - If ``int``, is given as the ``chunksize`` parameter of 
              :py:class:`~radis.lbl.factory.SpectrumFactory`` to split the line database 
              in several parts so that the number of ``lines * spectral grid points`` is 
              less than ``chunksize`` (reduces memory consumption). Typical values: 
              ``lineshape_optimization=1e6``.
            - If ``'DLM'``, only typical lineshapes are calculated. This can 
              result of speedups of orders of magnitude.  See more about DLM in 
              :ref:`Performance <label_lbl_performance>`. 
              
        Default ``'DLM'``.

    slit: float, str, or ``None``
        if float, FWHM of a triangular slit function. If str, path to an 
        experimental slit function. If None, no slit is applied. Default ``None``.

    plot: str
        any parameter such as 'radiance' (if slit is given), 'radiance_noslit', 
        'absorbance', etc...   Default ``None``

    name: str
        name of the case. If None, a unique ID is generated. Default ``None``

    use_cached: boolean
        use cached files for line database and energy database. Default ``True``

    verbose: boolean, or int
        If ``False``, stays quiet. If ``True``, tells what is going on. 
        If ``>=2``, gives more detailed messages (for instance, details of 
        calculation times). Default ``True``. 

    **kwargs: other inputs forwarded to SpectrumFactory
        For instance: ``warnings``. 
        See :class:`~radis.lbl.factory.SpectrumFactory` documentation for more 
        details on input. 
        For instance:

    pseudo_continuum_threshold: float
        if not 0, first calculate a rough approximation of the spectrum, then
        moves all lines whose linestrength intensity is less than this threshold
        of the maximum in a semi-continuum. Values above 0.01 can yield significant
        errors, mostly in highly populated areas. 80% of the lines can typically
        be moved in a continuum, resulting in 5 times faster spectra. If 0,
        no semi-continuum is used. Default 0.

    Returns
    -------
    
    s: :class:`~radis.spectrum.spectrum.Spectrum`
        Output spectrum.

        Use the :py:meth:`~radis.spectrum.spectrum.Spectrum.get` method to retrieve a 
        spectral quantity (``'radiance'``, ``'radiance_noslit'``, ``'absorbance'``, etc...)

        Or the :py:meth:`~radis.spectrum.spectrum.Spectrum.plot` method to plot it
        directly.

        See [1]_ to get an overview of all Spectrum methods

    References
    ----------

    .. [1] RADIS doc: `Spectrum how to? <https://radis.readthedocs.io/en/latest/spectrum/spectrum.html#label-spectrum>`__


    Examples
    --------

    Calculate a CO spectrum from the HITRAN database::

        s = calc_spectrum(1900, 2300,         # cm-1
                          molecule='CO',
                          isotope='1,2,3',
                          pressure=1.01325,   # bar
                          Tgas=1000, 
                          mole_fraction=0.1, 
                          )
        s.apply_slit(0.5, 'nm')
        s.plot('radiance')
        
    This example uses the :py:meth:`~radis.spectrum.spectrum.Spectrum.apply_slit` 
    and :py:meth:`~radis.spectrum.spectrum.Spectrum.plot` methods. See also
    :py:meth:`~radis.spectrum.spectrum.Spectrum.line_survey`:: 
        
        s.line_survey(overlay='radiance')

    Refer to the online :ref:`Examples <label_examples>` for more cases, and to 
    the :ref:`Spectrum page <label_spectrum>` for details on post-processing methods. 

    See Also
    --------
    
    :class:`~radis.lbl.factory.SpectrumFactory`, 
    the :ref:`Spectrum page <label_spectrum>`
    """

    # Check inputs

    # ... wavelengths / wavenumbers
    if (wavelength_min is not None or wavelength_max is not None) and (
        wavenum_min is not None or wavenum_max is not None
    ):
        raise ValueError("Wavenumber and Wavelength both given... it's time to choose!")

    if wavenum_min is None and wavenum_max is None:
        assert wavelength_max is not None
        assert wavelength_min is not None
        wavenum_min = nm2cm(wavelength_max)
        wavenum_max = nm2cm(wavelength_min)
    else:
        assert wavenum_min is not None
        assert wavenum_max is not None

    # ... temperatures

    if Tgas is None and Trot is None:
        raise ValueError(
            "Choose either Tgas (equilibrium) or Tvib / Trot (non equilibrium)"
        )

    if Tvib is None and Trot is not None or Tvib is not None and Trot is None:
        raise ValueError("Choose both Tvib and Trot")

    # ... others
    if databank is None:
        raise ValueError("Give a databank name")

    if not "save_memory" in kwargs:
        # no need to save intermediary results as
        # factory is used once only
        kwargs["save_memory"] = True

    if "chunksize" in kwargs:
        raise DeprecationWarning("use lineshape_optimization= instead of chunksize=")

    def _is_at_equilibrium():
        try:
            assert Tvib is None or Tvib == Tgas
            assert Trot is None or Trot == Tgas
            assert overpopulation is None
            if "self_absorption" in kwargs:
                assert kwargs["self_absorption"]  # == True
            return True
        except AssertionError:
            return False

    _equilibrium = _is_at_equilibrium()

    # which columns to keep when loading line database
    if kwargs["save_memory"] >= 2 and _equilibrium:
        drop_columns = "all"
    else:
        drop_columns = "auto"

    # Run calculations
    sf = SpectrumFactory(
        wavenum_min,
        wavenum_max,
        medium=medium,
        molecule=molecule,
        isotope=isotope,
        pressure=pressure,
        wstep=wstep,
        broadening_max_width=broadening_max_width,
        db_use_cached=use_cached,
        verbose=verbose,
        chunksize=lineshape_optimization,  #  if lineshape_optimization != 'auto' else None, #@EP: NotImplemented. DLM use all the time by default
        **kwargs
    )
    if databank == "fetch":  # mode to get databank without relying on  Line databases
        if _equilibrium:
            # Get line database from HITRAN
            # and partition functions from HAPI
            sf.fetch_databank(
                source="astroquery",
                format="hitran",
                parfuncfmt="hapi",  # use HAPI partition functions for equilibrium
                levelsfmt=None,  # no need to load energies
            )
        else:
            # Also get line database from HITRAN, and calculate partition functions
            # with energy levels from built-in constants (not all molecules
            # are supported!)
            sf.fetch_databank(
                source="astroquery",
                format="hitran",
                parfuncfmt="hapi",  # use HAPI partition functions for equilibrium
                levelsfmt="radis",  # built-in spectroscopic constants
            )
    elif exists(databank):
        # Guess format
        if databank.endswith(".par"):
            if verbose:
                print("Infered {0} is a HITRAN-format file.".format(databank))
            # If non-equilibrium we'll also need to load the energy levels.
            if _equilibrium:
                # Get partition functions from HAPI
                sf.load_databank(
                    path=databank,
                    format="hitran",
                    parfuncfmt="hapi",  # use HAPI partition functions for equilibrium
                    levelsfmt=None,  # no need to load energies
                    drop_columns=drop_columns,
                )
            else:
                # calculate partition functions with energy levels from built-in
                # constants (not all molecules are supported!)
                sf.load_databank(
                    path=databank,
                    format="hitran",
                    parfuncfmt="hapi",  # use HAPI partition functions for equilibrium
                    levelsfmt="radis",  # built-in spectroscopic constants
                    drop_columns=drop_columns,
                )
        else:
            raise ValueError(
                "Couldnt infer the format of the line database file: {0}. ".format(
                    databank
                )
                + "Create a user-defined database in your ~/.radis file "
                + "and define the format there. More information on "
                + "https://radis.readthedocs.io/en/latest/lbl/lbl.html#configuration-file"
            )

    else:  # manual mode: get from user-defined line databases defined in ~/.radis
        sf.load_databank(
            databank,
            load_energies=not _equilibrium,  # no need to load/calculate energies at eq.
            drop_columns=drop_columns,
        )

    #    # Get optimisation strategies
    #    if lineshape_optimization == 'auto':        # NotImplemented: finally we use DLM all the time as default.
    #        if len(sf.df0) > 1e5:
    #            lineshape_optimization = 'DLM'
    #        else:
    #            lineshape_optimization = None
    #        sf.params['chunksize'] = lineshape_optimization

    # Use the standard eq_spectrum / non_eq_spectrum functions
    if _equilibrium:
        s = sf.eq_spectrum(
            Tgas=Tgas, mole_fraction=mole_fraction, path_length=path_length, name=name
        )
    else:
        s = sf.non_eq_spectrum(
            Tvib=Tvib,
            Trot=Trot,
            Ttrans=Tgas,
            overpopulation=overpopulation,
            mole_fraction=mole_fraction,
            path_length=path_length,
            name=name,
        )

    return s