def test_validation_vs_specair(rtol=1e-2,
                               verbose=True,
                               plot=False,
                               *args,
                               **kwargs):
    """Test RADIS output on CO IR bands against SPECAIR

    Test is only performed on integrals of absorption coefficient

    RADIS doesnt actually match Specair exactly, but this is due to line intensity
    differences (Specair has no rovibrational specific intensities) rather than
    differences in populations calculations, as evidenced by the partition functions
    comparison in the RADIS presentation article.

    """

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

    # %% Specair calculation
    # -----------

    specair_300_300 = load_spec(
        getValidationCase(
            join(
                "test_validation_vs_specair_noneqCO_data",
                "specair_CO_IR_Tvib300_Trot300.spec",
            )),
        binary=True,
    )
    specair_300_2000 = load_spec(
        getValidationCase(
            join(
                "test_validation_vs_specair_noneqCO_data",
                "specair_CO_IR_Tvib300_Trot2000.spec",
            )),
        binary=True,
    )
    specair_2000_300 = load_spec(
        getValidationCase(
            join(
                "test_validation_vs_specair_noneqCO_data",
                "specair_CO_IR_Tvib2000_Trot300.spec",
            )),
        binary=True,
    )

    article_version = False  # just for the article

    # %% Compare with RADIS
    # ----------

    wstep = 0.002
    pl = SpectrumFactory(
        wavelength_min=4400,
        wavelength_max=4900,
        mole_fraction=1,
        pressure=0.01,  # bar
        path_length=1,  # we dont care for abscoeff anyway
        parallel=False,
        cutoff=1e-30,
        wstep=wstep,
        isotope=1,  # '1,2,3',
        medium="vacuum",
    )  # 0.2)
    pl.warnings["MissingSelfBroadeningWarning"] = "ignore"

    if article_version:
        pl.load_databank("HITEMP-CO-DUNHAM")
    else:
        pl.load_databank("HITRAN-CO-TEST")
        # Available on all systems, convenient for fast testing, but you
        # will be missing some small lines for T ~ 2000 K .
        print(
            "Using HITRAN: small lines will be missing at T ~ 2000K. Use HITEMP if you want them"
        )

    s_300_300 = pl.eq_spectrum(300, name="RADIS")

    s_2000_300 = pl.non_eq_spectrum(Tvib=2000,
                                    Trot=300,
                                    Ttrans=300,
                                    name="RADIS")

    s_300_2000 = pl.non_eq_spectrum(Tvib=300,
                                    Trot=2000,
                                    Ttrans=2000,
                                    name="RADIS")

    # %% Test

    # Compare integrals

    b1 = np.isclose(
        specair_300_300.get_integral("abscoeff"),
        s_300_300.get_integral("abscoeff"),
        rtol=rtol,
    )
    b1 *= np.isclose(
        specair_2000_300.get_integral("abscoeff"),
        s_2000_300.get_integral("abscoeff"),
        rtol=rtol,
    )
    b1 *= np.isclose(
        specair_300_2000.get_integral("abscoeff"),
        s_300_2000.get_integral("abscoeff"),
        rtol=rtol,
    )

    # Compare partition functions to hardcoded values
    b2 = np.isclose(s_2000_300.lines.Q, 139, atol=1)  # Specair: 139
    b2 *= np.isclose(s_300_2000.lines.Q, 727, atol=1)  # Specair: 727

    if verbose:
        printm(
            ">>> comparing RADIS vs SPECAIR on CO: integrals of abscoeff is are close"
            + " to within {0:.1f}%: {1} ({2:.1f}%, {3:.1f}%, {4:.1f}%)".format(
                rtol * 100,
                bool(b1),
                abs(
                    specair_300_300.get_integral("abscoeff") /
                    s_300_300.get_integral("abscoeff") - 1) * 100,
                abs(
                    specair_2000_300.get_integral("abscoeff") /
                    s_2000_300.get_integral("abscoeff") - 1) * 100,
                abs(
                    specair_300_2000.get_integral("abscoeff") /
                    s_300_2000.get_integral("abscoeff") - 1) * 100,
            ))
        printm(">>> comparing RADIS vs SPECAIR on CO: partition functions " +
               "are equal to round error: {0}".format(bool(b2)))

    if plot:
        plot_diff(
            specair_300_300,
            s_300_300,
            title=r"T$_\mathregular{vib}$ 300 K, T$_\mathregular{rot}$ 300 K",
            diff_window=int(
                0.02 // wstep
            ),  # compensate for small shifts in both codes. we're comparing intensities here.
            lw_multiplier=1,  # 0.75,
            wunit="nm_vac",
            plot_medium=True,
        )
        plt.xlim((4500, 4900))
        if article_version:
            plt.savefig(
                "out/test_validation_vs_specair_noneqCO_Tvib300_Trot300.png")
            plt.savefig(
                "out/test_validation_vs_specair_noneqCO_Tvib300_Trot300.pdf")

        plot_diff(
            specair_2000_300,
            s_2000_300,
            title=r"T$_\mathregular{vib}$ 2000 K, T$_\mathregular{rot}$ 300 K",
            diff_window=int(
                0.02 // wstep
            ),  # compensate for small shifts in both codes. we're comparing intensities here.
            lw_multiplier=1,  # 0.75,
            wunit="nm_vac",
            plot_medium=True,
        )
        plt.xlim((4500, 4900))
        if article_version:
            plt.savefig(
                "out/test_validation_vs_specair_noneqCO_Tvib2000_Trot300.png")
            plt.savefig(
                "out/test_validation_vs_specair_noneqCO_Tvib2000_Trot300.pdf")

        plot_diff(
            specair_300_2000,
            s_300_2000,
            title=r"T$_\mathregular{vib}$ 300 K, T$_\mathregular{rot}$ 2000 K",
            diff_window=int(
                0.02 // wstep
            ),  # compensate for small shifts in both codes. we're comparing intensities here.
            lw_multiplier=1,  # 0.75,
            wunit="nm_vac",
            plot_medium=True,
        )
        plt.xlim((4500, 4900))
        if article_version:
            plt.savefig(
                "out/test_validation_vs_specair_noneqCO_Tvib300_Trot2000.png")
            plt.savefig(
                "out/test_validation_vs_specair_noneqCO_Tvib300_Trot2000.pdf")

    return bool(b1 * b2)
def test_klarenaar_validation_case(verbose=True,
                                   plot=False,
                                   warnings=True,
                                   *args,
                                   **kwargs):
    """ Reproduce the Klarenaar 2018 validation case, as given in the 
    [RADIS-2018]_ article. 
    
    References
    ----------

    Klarenaar et al, "Time evolution of vibrational temperatures in a CO 2 glow 
    discharge measured with infrared absorption spectroscopy", doi 10.1088/1361-6595/aa902e,
    and the references there in.
    
    """

    setup_test_line_databases()

    try:
        # %% Data from Dang, adapted by Klarenaar
        s_exp = Spectrum.from_txt(
            getValidationCase(
                join(
                    "test_CO2_3Tvib_vs_klarenaar_data",
                    "klarenaar_2017_digitized_data.csv",
                )),
            "transmittance_noslit",
            waveunit="cm-1",
            unit="I/I0",
            delimiter=",",
            name="Klarenaar 2017",
        )

        # %% Calculate Klarenaar test case conditions

        sf = SpectrumFactory(
            2284.2,
            2284.6,
            wstep=0.001,  # cm-1
            pressure=20 * 1e-3,  # bar
            db_use_cached=True,
            cutoff=1e-25,
            molecule="CO2",
            isotope="1,2",
            path_length=10,  # cm-1
            # warning! 10% in mass fraction -> less in mole fraction
            mole_fraction=0.1 * 28.97 / 44.07,
            broadening_max_width=1,  # cm-1
            medium="vacuum",
            export_populations="vib",
        )
        sf.warnings["MissingSelfBroadeningWarning"] = "ignore"
        #        sf.load_databank('HITEMP-CO2-DUNHAM')
        sf.load_databank("HITEMP-CO2-TEST")

        # Calculate with Klarenaar fitted values
        T12 = 517
        T3 = 2641
        Trot = 491

        s = sf.non_eq_spectrum((T12, T12, T3),
                               Trot,
                               Ttrans=Trot,
                               vib_distribution="treanor",
                               name="RADIS")

        if plot:
            plot_diff(s, s_exp, "transmittance_noslit")
        #        plt.savefig('test_CO2_3Tvib_vs_klarenaar.png')

        assert get_residual(s, s_exp, "transmittance_noslit",
                            ignore_nan=True) < 0.003

        return True

    except DatabankNotFound as err:
        assert IgnoreMissingDatabase(err, __file__, warnings)
Beispiel #3
0
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import minimize
from os.path import join

# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#                 USER SECTION    (change this as you want)
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------

# %% Get Fitted Data

# Data from Dang, adapted by Klarenaar, digitized by us
s_exp = Spectrum.from_txt(getValidationCase(
    join('test_CO2_3Tvib_vs_klarenaar_data',
         'klarenaar_2017_digitized_data.csv')),
                          'transmittance_noslit',
                          waveunit='cm-1',
                          unit='',
                          delimiter=',',
                          name='Klarenaar 2017')

w_exp, T_exp = s_exp.get('transmittance_noslit', wunit='cm-1')

# %% Calculate

sf = SpectrumFactory(
    2284.2,
    2284.6,
    wstep=0.001,  # cm-1
def test_compare_torch_CO2(verbose=True,
                           plot=False,
                           save=False,
                           warnings=True,
                           use_cache=True,
                           *args,
                           **kwargs):
    ''' Reproduce the plasma torch experiment of Packan 2003 in in atmospheric air 

    Notes
    -----
    
    Thresholds parameters are reduced for a faster calculation time. For instance:
    
    - broadening_max_width should be 50 rather than 20
    - cutoff should be 1e-27 rather than 1e-25
    - wstep should be 0.01 or even 0.008 rather than 0.1

    Performance:
        
    - neq==0.9.20: Finished test_compare_torch_CO2 in 758.640324s

    - neq==0.9.21: Finished test_compare_torch_CO2 in 672s

    - neq==0.9.21*: (with ParallelFactory) Finished test_compare_torch_CO2 in 298s

    - neq==0.9.22: (Parallel + continuum) Finished in 65s
      RADIS 1.0.0 == neq 0.9.24

    Reference
    --------

    Packan et al 2003 "Measurement and Modeling of OH, NO, and CO Infrared Radiation 
    at 3400 K", JTHT

    '''

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

    t0 = time()

    try:

        # %% Conditions

        wlmin = 4100
        wlmax = 4900

        wmin = nm2cm(wlmax)
        wmax = nm2cm(wlmin)
        wstep = 0.1

        # Load concentration profile
        conds = pd.read_csv(getValidationCase(
            'test_compare_torch_CO2_data/test_compare_torch_CO2_conditions_JTHT2003.dat'
        ),
                            comment='#',
                            delim_whitespace=True)
        slab_width = 0.05  # cm,  WARNING. Hardcoded (look up the table)

        # %% Calculate slabs

        # CO2
        sf = ParallelFactory(
            wmin,
            wmax,
            mole_fraction=None,
            path_length=None,
            molecule='CO2',
            isotope='1,2',
            parallel=False,
            wstep=wstep,
            db_use_cached=use_cache,
            export_lines=False,  # saves some memory
            export_populations=False,  # saves some memory
            cutoff=1e-25,
            pseudo_continuum_threshold=0.01,
            Nprocs=cpu_count() - 1,  # warning with memory if too many procs
            broadening_max_width=20,
            verbose=False)
        sf.warnings['MissingSelfBroadeningWarning'] = 'ignore'
        sf.warnings['NegativeEnergiesWarning'] = 'ignore'
        #        # Init database: only if we want to save all spectra being calculated
        # (discarded for the moment)
        #        if use_cache:
        #            sf.init_database('test_compare_torch_CO2_SpectrumDatabase_HITEMP_1e25',
        #                             add_info=['Tgas'])
        sf.init_databank('HITEMP-CO2-DUNHAM',
                         load_energies=False)  # saves some memory
        #    sf.init_databank('CDSD-4000')

        # CO
        sfco = ParallelFactory(
            wmin,
            wmax,
            mole_fraction=None,
            path_length=None,
            molecule='CO',
            isotope='1,2',
            parallel=False,
            wstep=wstep,
            db_use_cached=use_cache,
            export_lines=False,  # saves some memory
            export_populations=False,  # saves some memory
            cutoff=1e-25,
            Nprocs=cpu_count() - 1,
            broadening_max_width=20,
            verbose=False)
        sfco.warnings['MissingSelfBroadeningWarning'] = 'ignore'
        # Init database: only if we want to save all spectra being calculated
        # (discarded for the moment)
        #        if use_cache:
        #            sfco.init_database(
        #                'test_compare_torch_CO_SpectrumDatabase_HITEMP_1e25', add_info=['Tgas'])
        sfco.init_databank('HITEMP-CO-DUNHAM')

        # Calculate all slabs
        # .. CO2 at equilibrium
        slabsco2 = sf.eq_spectrum(list(conds.T_K),
                                  mole_fraction=list(conds.CO2),
                                  path_length=slab_width)
        # .. CO at equilibrium, but with non_eq to calculate PartitionFunctions instead of using
        # ... tabulated ones (which are limited to 3000 K in HAPI)
        slabsco = sfco.non_eq_spectrum(list(conds.T_K),
                                       list(conds.T_K),
                                       mole_fraction=list(conds.CO),
                                       path_length=slab_width)

        # Room absorption
        with pytest.warns(
                UserWarning
        ):  # we expect a "using ParallelFactory for single case" warning
            s0 = sf.eq_spectrum(Tgas=300,
                                mole_fraction=330e-6,
                                path_length=600)[0]
        # Warning: This slab includes the CO2 emission from the 300 K air
        # within the spectrometer. But experimentally it is substracted
        # by there the chopper (this is corrected below)
        # ... see RADIS Article for more details

        # Add radiance for everyone if not calculated (ex: loaded from database)
        for s in slabsco2 + slabsco + [s0]:
            s.update()

        # Merge CO + CO2 for each slab
        slabstot = [MergeSlabs(s, sco) for (s, sco) in zip(slabsco2, slabsco)]

        # %% Line-of-sight

        # Solve ETR along line of sight
        # --------
        # two semi profile, + room absortion
        line_of_sight = slabstot[1:][::-1] + slabstot + [s0]
        stot = SerialSlabs(*line_of_sight)
        #        stot = SerialSlabs(*slabstot[1:][::-1], *slabstot, s0)  # Python 3 syntax only

        # Also calculate the contribution of pure CO
        # ---------
        s0tr = s0.copy()  # transmittance only
        s0tr.conditions['thermal_equilibrium'] = False
        s0tr._q['radiance_noslit'] *= 0  # hack to remove CO2 emission
        s0tr._q['emisscoeff'] *= 0  # hack to remove CO2 emission
        line_of_sight = slabsco[1:][::-1] + slabsco + [s0tr]
        sco = SerialSlabs(*line_of_sight)
        #        sco = SerialSlabs(*slabsco[1:][::-1], *slabsco, s0tr)  # Python 3 syntax only

        # Generate Slit
        # ------
        disp = 4  # dispersion conversion function (nm/mm)
        s_in = 1  # entrance slit (mm)
        s_out = 2.8  # exit slit (mm)
        top = (s_out - s_in) * disp
        base = (s_out + s_in) * disp
        slit_function = (top, base)  # (nm)

        # Final plot unit
        unit = 'µW/cm2/sr'
        norm_by = 'max'  # should be 'max' if unit ~ 'µW/cm2/sr',
        # 'area' if unit ~ 'µW/cm2/sr/nm'

        # Convolve with Slit
        # -------
        sco.apply_slit(slit_function,
                       unit='nm',
                       norm_by=norm_by,
                       shape='trapezoidal')
        stot.apply_slit(slit_function,
                        unit='nm',
                        norm_by=norm_by,
                        shape='trapezoidal')

        # Remove emission from within the spectrometer (substracted
        # by the chopper experimentaly)
        # -------
        s0spectro = s0.copy()
        s0spectro.rescale_path_length(75 * 4)  # spectrometer length
        s0spectro.apply_slit(slit_function,
                             unit='nm',
                             norm_by=norm_by,
                             shape='trapezoidal')
        _, I0spectro = s0spectro.get('radiance', Iunit=unit)

        wtot, Itot = stot.get('radiance', Iunit=unit)
        stot_corr = experimental_spectrum(
            wtot,
            Itot - I0spectro,  # hack to remove
            # emission from within
            # spectrometer
            Iunit=unit,
            conditions={'medium': 'air'})

        # %% Compare with experiment data

        # Plot experimental data
        # ------
        exp = pd.read_csv(getValidationCase(
            'test_compare_torch_CO2_data/test_compare_torch_CO2_spectrum_JTHT2003.dat'
        ),
                          skiprows=5,
                          delim_whitespace=True)
        exp.w /= 10  # Angstrom to nm
        exp = exp[(exp.w > wlmin) & (exp.w < wlmax)]
        sexp = experimental_spectrum(exp.w,
                                     exp.I,
                                     Iunit='mW/cm2/sr',
                                     conditions={'medium': 'air'})

        if plot:

            sexp.plot('radiance',
                      wunit='nm',
                      Iunit=unit,
                      lw=2,
                      label='Packan 2003')

            # Plot calculated
            # ----------

            stot.plot('radiance',
                      wunit='nm',
                      Iunit=unit,
                      nfig='same',
                      color='r',
                      ls='--',
                      label='All: CO$_2$+CO')

            # Plot internal spectrometer emission
            s0spectro.plot('radiance',
                           wunit='nm',
                           Iunit=unit,
                           nfig='same',
                           color='b',
                           label='CO$_2$ in Spectrometer')

            stot_corr.plot('radiance',
                           wunit='nm',
                           Iunit=unit,
                           nfig='same',
                           color='r',
                           lw=2,
                           label='All-CO$_2$ in Spectro',
                           zorder=10)

            sco.plot('radiance',
                     wunit='nm',
                     Iunit=unit,
                     nfig='same',
                     color='g',
                     ls='-',
                     label='CO')

            plt.ylim((0, 9))
            plt.legend(loc='upper right')

            if save:
                plt.savefig('test_compare_torch_CO2_brd20.png')
                plt.savefig('test_compare_torch_CO2_brd20k.pdf')

        assert get_residual(stot_corr, sexp, 'radiance') < 0.02

    except DatabankNotFound as err:
        assert IgnoreMissingDatabase(err, __file__, warnings)

    if verbose:
        print(('Finished test_compare_torch_CO2 in {0:.0f}s'.format(time() -
                                                                    t0)))