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
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)
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
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)
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)
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)
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, )
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
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)
def test_noneq_continuum(plot=False, verbose=2, warnings=True, *args, **kwargs): ''' Test calculation with pseudo-continuum under nonequilibrium Assert results on emisscoeff dont change Notes ----- Uses HITRAN so it can deployed and tested on [Travis]_, but we should switch to HITEMP if some HITEMP files can be downloaded automatically at the execution time. ''' if plot: # Make sure matplotlib is interactive so that test are not stuck in pytest plt.ion() try: if verbose: printm('>>> test_noneq_continuum') sf = SpectrumFactory(wavelength_min=4200, wavelength_max=4500, parallel=False, bplot=False, cutoff=1e-23, molecule='CO2', isotope='1,2', db_use_cached=True, broadening_max_width=10, path_length=0.1, mole_fraction=1e-3, medium='vacuum', verbose=verbose) sf.warnings.update({ 'MissingSelfBroadeningWarning': 'ignore', 'NegativeEnergiesWarning': 'ignore', 'LinestrengthCutoffWarning': 'ignore', 'HighTemperatureWarning': 'ignore' }) sf.fetch_databank( ) # uses HITRAN: not really valid at this temperature, but runs on all machines without install # sf.load_databank('HITEMP-CO2-DUNHAM') # to take a real advantage of abscoeff continuum, should calculate with HITEMP sf._export_continuum = True # activate it # Calculate one without pseudo-continuum sf.params.pseudo_continuum_threshold = 0 s1 = sf.non_eq_spectrum(Tvib=2000, Trot=1000) s1.name = 'All lines resolved ({0}) ({1:.1f}s)'.format( s1.conditions['lines_calculated'], s1.conditions['calculation_time']) assert s1.conditions['pseudo_continuum_threshold'] == 0 # Calculate one with pseudo-continuum sf.params.pseudo_continuum_threshold = 0.05 s2 = sf.non_eq_spectrum(Tvib=2000, Trot=1000) s2.name = 'Semi-continuum + {0} lines ({1:.1f}s)'.format( s2.conditions['lines_calculated'], s2.conditions['calculation_time']) assert s2.conditions['pseudo_continuum_threshold'] == 0.05 assert 'abscoeff_continuum' in s2.get_vars() assert 'emisscoeff_continuum' in s2.get_vars() # Plot if plot: plot_diff(s1, s2, 'radiance_noslit', Iunit='µW/cm2/sr/nm', nfig='test_noneq_continuum: diff') s2.plot('emisscoeff', label=s2.name, nfig='test_noneq_continuum: show continuum') s2.plot('emisscoeff_continuum', nfig='same', label='Pseudo-continuum (aggreg. {0:g} lines)'.format( s2.conditions['lines_in_continuum']), force=True) # Compare res = get_residual(s1, s2, 'abscoeff') + get_residual( s1, s2, 'emisscoeff') if verbose: printm('residual:', res) assert res < 5e-6 except DatabankNotFound as err: assert IgnoreMissingDatabase(err, __file__, warnings)
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
def test_broadening_warnings(*args, **kwargs): """Test AccuracyWarning and AccuracyErrors are properly triggered. Inspired by https://github.com/radis/radis/issues/186 Examples -------- :: AccuracyError: Some lines are too narrow (FWHM ~ 0.0011 cm⁻¹) for the current spectral grid (wstep=0.01). Please reduce wstep to (at least) below 0.00055 cm⁻¹ or (suggested) 0.00022 cm⁻¹ """ import astropy.units as u from radis.misc.warning import AccuracyError, AccuracyWarning conditions = { "wavenum_min": 667.58 / u.cm, "wavenum_max": 667.61 / u.cm, "molecule": "CO2", "isotope": "1", "broadening_max_width": 0, # no neighbour lines in LDM method (note : with LDM method we still resolve the full lineshape!) "verbose": False, } # Try with low resolution, expect error : with pytest.raises(AccuracyError): sf = SpectrumFactory(**conditions, wstep=0.01) sf.fetch_databank("hitran") sf.eq_spectrum( Tgas=234.5, pressure=0.00646122 * u.bar, mole_fraction=400e-6, ) with pytest.warns(AccuracyWarning): sf = SpectrumFactory(**conditions, wstep=0.00055) sf.fetch_databank("hitran") sf.eq_spectrum( Tgas=234.5, pressure=0.00646122 * u.bar, mole_fraction=400e-6, )
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
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
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
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
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
def test_abscoeff_continuum(plot=False, verbose=2, warnings=True, threshold=0.1, *args, **kwargs): """ Test calculation with pseudo-continuum Assert results on abscoeff dont change Notes ----- Uses HITRAN so it can deployed and tested on [Travis]_, but we should switch to HITEMP if some HITEMP files can be downloaded automatically at the execution time. """ if plot: # Make sure matplotlib is interactive so that test are not stuck in pytest plt.ion() try: if verbose: printm(">>> test_abscoeff_continuum") sf = SpectrumFactory( wavelength_min=4200, wavelength_max=4500, parallel=False, bplot=False, cutoff=1e-23, molecule="CO2", isotope="1,2", db_use_cached=True, broadening_max_width=10, path_length=0.1, mole_fraction=1e-3, medium="vacuum", verbose=verbose, ) sf.warnings.update({ "MissingSelfBroadeningWarning": "ignore", "NegativeEnergiesWarning": "ignore", "LinestrengthCutoffWarning": "ignore", "HighTemperatureWarning": "ignore", }) sf.fetch_databank( ) # uses HITRAN: not really valid at this temperature, but runs on all machines without install # sf.load_databank('HITEMP-CO2-DUNHAM') # to take a real advantage of abscoeff continuum, should calculate with HITEMP sf._export_continuum = True # activate it # Calculate one without pseudo-continuum sf.params.pseudo_continuum_threshold = 0 s1 = sf.eq_spectrum(Tgas=2000) s1.name = "All lines resolved ({0}) ({1:.1f}s)".format( s1.conditions["lines_calculated"], s1.conditions["calculation_time"]) assert s1.conditions["pseudo_continuum_threshold"] == 0 # Calculate one with pseudo-continuum sf.params.pseudo_continuum_threshold = threshold s2 = sf.eq_spectrum(Tgas=2000) s2.name = "Semi-continuum + {0} lines ({1:.1f}s)".format( s2.conditions["lines_calculated"], s2.conditions["calculation_time"]) assert s2.conditions["pseudo_continuum_threshold"] == threshold assert "abscoeff_continuum" in s2.get_vars() # Plot if plot: plot_diff( s1, s2, "radiance_noslit", Iunit="µW/cm2/sr/nm", nfig="test_abscoeff_continuum: diff", ) s2.plot( "abscoeff", label="Full spectrum", nfig="test_abscoeff_continuum: show continuum", force=True, ) s2.plot( "abscoeff_continuum", nfig="same", label="Pseudo-continuum".format( s2.conditions["lines_in_continuum"]), force=True, ) plt.legend() # Compare res = get_residual(s1, s2, "abscoeff") if verbose: printm("residual:", res) globals().update(locals()) assert res < 1.32e-6 except DatabankNotFound as err: assert IgnoreMissingDatabase(err, __file__, warnings)
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
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
def test_noneq_continuum(plot=False, verbose=2, warnings=True, *args, **kwargs): """ Test calculation with pseudo-continuum under nonequilibrium Assert results on emisscoeff dont change Notes ----- Uses HITRAN so it can deployed and tested on `Travis CI <https://travis-ci.com/radis/radis>`_, but we should switch to HITEMP if some HITEMP files can be downloaded automatically at the execution time. """ if plot: # Make sure matplotlib is interactive so that test are not stuck in pytest plt.ion() if verbose: printm(">>> test_noneq_continuum") sf = SpectrumFactory( wavelength_min=4200, wavelength_max=4500, cutoff=1e-23, molecule="CO2", isotope="1,2", broadening_max_width=10, path_length=0.1, mole_fraction=1e-3, medium="vacuum", optimization=None, verbose=verbose, ) sf.warnings.update({ "MissingSelfBroadeningWarning": "ignore", "NegativeEnergiesWarning": "ignore", "LinestrengthCutoffWarning": "ignore", "HighTemperatureWarning": "ignore", }) sf.fetch_databank( "hitran" ) # uses HITRAN: not really valid at this temperature, but runs on all machines without install # sf.load_databank('HITEMP-CO2-DUNHAM') # to take a real advantage of abscoeff continuum, should calculate with HITEMP sf._export_continuum = True # activate it # Calculate one without pseudo-continuum sf.params.pseudo_continuum_threshold = 0 s1 = sf.non_eq_spectrum(Tvib=2000, Trot=1000) s1.name = "All lines resolved ({0}) ({1:.1f}s)".format( s1.conditions["lines_calculated"], s1.conditions["calculation_time"]) assert s1.conditions["pseudo_continuum_threshold"] == 0 # Calculate one with pseudo-continuum sf.params.pseudo_continuum_threshold = 0.05 s2 = sf.non_eq_spectrum(Tvib=2000, Trot=1000) s2.name = "Semi-continuum + {0} lines ({1:.1f}s)".format( s2.conditions["lines_calculated"], s2.conditions["calculation_time"]) assert s2.conditions["pseudo_continuum_threshold"] == 0.05 assert "abscoeff_continuum" in s2.get_vars() assert "emisscoeff_continuum" in s2.get_vars() # Plot if plot: plot_diff( s1, s2, "radiance_noslit", Iunit="µW/cm2/sr/nm", nfig="test_noneq_continuum: diff", ) plt.figure("test_noneq_continuum: show continuum").clear() s2.plot("emisscoeff", label=s2.name, nfig="test_noneq_continuum: show continuum") s2.plot( "emisscoeff_continuum", nfig="same", label="Pseudo-continuum (aggreg. {0:g} lines)".format( s2.conditions["lines_in_continuum"]), force=True, ) # Compare res = get_residual(s1, s2, "abscoeff") + get_residual(s1, s2, "emisscoeff") if verbose: printm("residual:", res) assert res < 5.2e-6
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