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_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 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 _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
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_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