def test_calculatedQ_match_HAPI(plot=False, verbose=True, *args, **kwargs): """Tested that Q ab_initio (Dunham) match HAPI for different molecules and isotopes""" # molecule, isotope, temperature, absolute tolerance for molecule, iso, T, atol in [ ("CO2", 1, 300, 0.9), ("CO2", 1, 1000, 2.0), ("CO2", 1, 3000, 2000), ("CO2", 2, 300, 1.8), ("CO2", 2, 1000, 25), ("CO2", 2, 3000, 4962), ("CO", 1, 300, 0.16), ("CO", 1, 1000, 1.9), ("CO", 1, 3000, 27), ("CO", 2, 300, 0.31), ("CO", 2, 1000, 4.1), ("CO", 2, 3000, 56.9), ("CO", 3, 300, 0.16), ("CO", 3, 1000, 2.1), ("CO", 3, 3000, 28.6), ]: S = Molecules[molecule][iso]["X"] # Dont use cached: force recalculating db = PartFunc_Dunham(S) from radis.io.hitran import get_molecule_identifier hapi = PartFuncHAPI(M=get_molecule_identifier(molecule), I=iso) Q_radis = db.at(T) Q_hapi = hapi.at(T) if verbose: print("Q({0},iso={1},{2}K)\tRADIS: {3:.2f}\tHAPI={4:.2f}".format( molecule, iso, T, Q_radis, Q_hapi)) try: assert np.isclose(Q_radis, Q_hapi, atol=atol) except AssertionError: raise AssertionError( "Partition function for {0}, iso={1} ".format(molecule, iso) + "at {0}K doesnt match in RADIS ".format(T) + "({0:.4f}) and HAPI ({1:.4f})".format(Q_radis, Q_hapi)) if verbose: printm("Tested Q ab_initio (Dunham) matches HAPI: OK") return True
def __init__(self, name, verbose=True): # @dev: no optional kwargs here (final stop) self.verbose = verbose # Get name and integer id if isinstance(name, string_types): self.name = name # Get name without parenthesis (without state) for HITRAN identification filtername = re.sub(r"[\(\[].*?[\)\]]", "", name) try: self.id = get_molecule_identifier(filtername) except KeyError: # Not an HITRAN molecule self.id = None elif type(name) == int: self.id = name self.name = get_molecule(name) else: raise ValueError('Wrong name type:', name)
def fetch_astroquery(molecule, isotope, wmin, wmax, verbose=True): ''' Wrapper to Astroquery [1]_ fetch function to download a line database Notes ----- Astroquery [1]_ is itself based on [HAPI]_ Parameters ---------- molecule: str, or int molecule name or identifier isotope: int isotope number wmin, wmax: float (cm-1) wavenumber min and max Other Parameters ---------------- verbose: boolean Default ``True`` References ---------- .. [1] `Astroquery <https://astroquery.readthedocs.io>`_ See Also -------- :func:`astroquery.hitran.reader.download_hitran`, :func:`astroquery.hitran.reader.read_hitran_file` ''' # Check input if not is_float(molecule): mol_id = get_molecule_identifier(molecule) else: mol_id = molecule molecule = get_molecule(mol_id) assert is_float(isotope) empty_range = False # tbl = Hitran.query_lines_async(molecule_number=mol_id, # isotopologue_number=isotope, # min_frequency=wmin / u.cm, # max_frequency=wmax / u.cm) # # Download using the astroquery library response = Hitran.query_lines_async(molecule_number=mol_id, isotopologue_number=isotope, min_frequency=wmin / u.cm, max_frequency=wmax / u.cm) if response.status_code == 404: # Maybe there are just no lines for this species in this range # In that case we usually end up with errors like: # (<class 'Exception'>, Exception('Query failed: 404 Client Error: # Not Found for url: http://hitran.org/lbl/api?numax=25000&numin=19000&iso_ids_list=69\n',), # <traceback object at 0x7f0967c91708>) if response.reason == 'Not Found': # Let's bet it's just that there are no lines in this range empty_range = True if verbose: print(( 'No lines for {0} (id={1}), iso={2} in range {3:.2f}-{4:.2f}cm-1. ' .format(molecule, mol_id, isotope, wmin, wmax))) else: raise ValueError( 'An error occured during the download of HITRAN files ' + 'for {0} (id={1}), iso={2} between {3:.2f}-{4:.2f}cm-1. '. format(molecule, mol_id, isotope, wmin, wmax) + 'Are you online?\n' + 'See details of the error below:\n\n {0}'.format( response.reason)) # Rename columns from Astroquery to RADIS format rename_columns = { 'molec_id': 'id', 'local_iso_id': 'iso', 'nu': 'wav', 'sw': 'int', 'a': 'A', 'gamma_air': 'airbrd', 'gamma_self': 'selbrd', 'elower': 'El', 'n_air': 'Tdpair', 'delta_air': 'Pshft', 'global_upper_quanta': 'globu', 'global_lower_quanta': 'globl', 'local_upper_quanta': 'locu', 'local_lower_quanta': 'locl', 'line_mixing_flag': 'lmix', 'gp': 'gp', 'gpp': 'gpp', } if not empty_range: # _fix_astroquery_file_format(filename) # Note: as of 0.9.16 we're not fixing astroquery_file_format anymore. # maybe we should. tbl = Hitran._parse_result(response) df = tbl.to_pandas() df = df.rename(columns=rename_columns) else: df = pd.DataFrame(columns=list(rename_columns.values())) # Cast type to float64 cast_type = { 'wav': np.float64, 'int': np.float64, 'A': np.float64, 'airbrd': np.float64, 'selbrd': np.float64, 'El': np.float64, 'Tdpair': np.float64, 'Pshft': np.float64, } for c, typ in cast_type.items(): df[c] = df[c].astype(typ) return df
def fetch_astroquery(molecule, isotope, wmin, wmax, verbose=True): ''' Wrapper to Astroquery [1]_ fetch function to download a line database Notes ----- Astroquery [1]_ is itself based on HAPI [2]_ Parameters ---------- molecule: str, or int molecule name or identifier isotope: int isotope number wmin, wmax: float (cm-1) wavenumber min and max Other Parameters ---------------- verbose: boolean Default True References ---------- .. [1] `Astroquery <https://astroquery.readthedocs.io>`_ .. [2] `HAPI: The HITRAN Application Programming Interface <http://hitran.org/hapi>`_ ''' # Check input if not is_float(molecule): mol_id = get_molecule_identifier(molecule) else: mol_id = molecule molecule = get_molecule(mol_id) assert is_float(isotope) empty_range = False # Download using the astroquery library try: download_hitran(mol_id, isotope, wmin, wmax) except: # Maybe there are just no lines for this species in this range # In that case we usually end up with errors like: # (<class 'Exception'>, Exception('Query failed: 404 Client Error: # Not Found for url: http://hitran.org/lbl/api?numax=25000&numin=19000&iso_ids_list=69\n',), # <traceback object at 0x7f0967c91708>) import sys _err_class, _err_details, _err_obj = sys.exc_info() if 'Not Found for url:' in str(_err_details): # Let's bet it's just that there are no lines in this range empty_range = True if verbose: print(( 'Not lines for {0} (id={1}), iso={2} between {3:.2f}-{4:.2f}cm-1. ' .format(molecule, mol_id, isotope, wmin, wmax))) else: raise ValueError('An error occured during the download of HITRAN files '+\ 'for {0} (id={1}), iso={2} between {3:.2f}-{4:.2f}cm-1. '.format( molecule, mol_id, isotope, wmin, wmax)+\ 'See details of the error below: {0}'.format(_err_details)) # Rename columns from Astroquery to RADIS format rename_columns = { 'molec_id': 'id', 'local_iso_id': 'iso', 'nu': 'wav', 'sw': 'int', 'a': 'A', 'gamma_air': 'airbrd', 'gamma_self': 'selbrd', 'elower': 'El', 'n_air': 'Tdpair', 'delta_air': 'Pshft', 'global_upper_quanta': 'globu', 'global_lower_quanta': 'globl', 'local_upper_quanta': 'locu', 'local_lower_quanta': 'locl', 'line_mixing_flag': 'lmix', 'gp': 'gp', 'gpp': 'gpp', } if not empty_range: tbl = read_hitran_file(join(cache_location, molecule + '.data')) df = tbl.to_pandas() df = df.rename(columns=rename_columns) else: df = pd.DataFrame(columns=list(rename_columns.values())) # Cast type to float64 cast_type = { 'wav': np.float64, 'int': np.float64, 'A': np.float64, 'airbrd': np.float64, 'selbrd': np.float64, 'El': np.float64, 'Tdpair': np.float64, 'Pshft': np.float64, } for c, typ in cast_type.items(): df[c] = df[c].astype(typ) return df
def fetch_astroquery(molecule, isotope, wmin, wmax, verbose=True, cache=True, metadata={}): ''' Wrapper to Astroquery [1]_ fetch function to download a line database Notes ----- Astroquery [1]_ is itself based on [HAPI]_ Parameters ---------- molecule: str, or int molecule name or identifier isotope: int isotope number wmin, wmax: float (cm-1) wavenumber min and max Other Parameters ---------------- verbose: boolean Default ``True`` cache: boolean if ``True``, tries to find a ``.h5`` cache file in the Astroquery :py:attr:`~astroquery.query.BaseQuery.cache_location`, that would match the requirements. If not found, downloads it and saves the line dataframe as a ``.h5`` file in the Astroquery. metadata: dict if ``cache=True``, check that the metadata in the cache file correspond to these attributes. Arguments ``molecule``, ``isotope``, ``wmin``, ``wmax`` are already added by default. References ---------- .. [1] `Astroquery <https://astroquery.readthedocs.io>`_ See Also -------- :py:func:`astroquery.hitran.reader.download_hitran`, :py:func:`astroquery.hitran.reader.read_hitran_file`, :py:attr:`~astroquery.query.BaseQuery.cache_location` ''' # Check input if not is_float(molecule): mol_id = get_molecule_identifier(molecule) else: mol_id = molecule molecule = get_molecule(mol_id) assert is_float(isotope) empty_range = False # If cache, tries to find from Astroquery: if cache: # Update metadata with physical properties from the database. metadata.update({ 'molecule': molecule, 'isotope': isotope, 'wmin': wmin, 'wmax': wmax }) fcache = join( Hitran.cache_location, CACHE_FILE_NAME.format( **{ 'molecule': molecule, 'isotope': isotope, 'wmin': wmin, 'wmax': wmax })) check_cache_file(fcache=fcache, use_cached=cache, metadata=metadata, verbose=verbose) if exists(fcache): try: return get_cache_file(fcache, verbose=verbose) except Exception as err: if verbose: printr( 'Problem reading cache file {0}:\n{1}\nDeleting it!'. format(fcache, str(err))) os.remove(fcache) # tbl = Hitran.query_lines_async(molecule_number=mol_id, # isotopologue_number=isotope, # min_frequency=wmin / u.cm, # max_frequency=wmax / u.cm) # # Download using the astroquery library try: response = Hitran.query_lines_async(molecule_number=mol_id, isotopologue_number=isotope, min_frequency=wmin / u.cm, max_frequency=wmax / u.cm) except KeyError as err: raise KeyError(str(err)+' <<w this error occured in Astroquery. Maybe these molecule '+\ '({0}) and isotope ({1}) are not supported'.format(molecule, isotope)) from err # Deal with usual errors if response.status_code == 404: # Maybe there are just no lines for this species in this range # In that case we usually end up with errors like: # (<class 'Exception'>, Exception('Query failed: 404 Client Error: # Not Found for url: http://hitran.org/lbl/api?numax=25000&numin=19000&iso_ids_list=69\n',), # <traceback object at 0x7f0967c91708>) if response.reason == 'Not Found': # Let's bet it's just that there are no lines in this range empty_range = True if verbose: print(( 'No lines for {0} (id={1}), iso={2} in range {3:.2f}-{4:.2f}cm-1. ' .format(molecule, mol_id, isotope, wmin, wmax))) else: raise ValueError( 'An error occured during the download of HITRAN files ' + 'for {0} (id={1}), iso={2} between {3:.2f}-{4:.2f}cm-1. '. format(molecule, mol_id, isotope, wmin, wmax) + 'Are you online?\n' + 'See details of the error below:\n\n {0}'.format( response.reason)) elif response.status_code == 500: raise ValueError('{0} while querying the HITRAN server: '.format(response.status_code)+\ '\n\n{0}'.format(response.text)) # Process response # Rename columns from Astroquery to RADIS format rename_columns = { 'molec_id': 'id', 'local_iso_id': 'iso', 'nu': 'wav', 'sw': 'int', 'a': 'A', 'gamma_air': 'airbrd', 'gamma_self': 'selbrd', 'elower': 'El', 'n_air': 'Tdpair', 'delta_air': 'Pshft', 'global_upper_quanta': 'globu', 'global_lower_quanta': 'globl', 'local_upper_quanta': 'locu', 'local_lower_quanta': 'locl', 'line_mixing_flag': 'lmix', 'gp': 'gp', 'gpp': 'gpp', } if not empty_range: # _fix_astroquery_file_format(filename) # Note: as of 0.9.16 we're not fixing astroquery_file_format anymore. # maybe we should. tbl = Hitran._parse_result(response) df = tbl.to_pandas() df = df.rename(columns=rename_columns) else: df = pd.DataFrame(columns=list(rename_columns.values())) # Cast type to float64 cast_type = { 'wav': np.float64, 'int': np.float64, 'A': np.float64, 'airbrd': np.float64, 'selbrd': np.float64, 'El': np.float64, 'Tdpair': np.float64, 'Pshft': np.float64, } for c, typ in cast_type.items(): df[c] = df[c].astype(typ) # cached file mode but cached file doesn't exist yet (else we had returned) if cache: if verbose: print('Generating cached file: {0}'.format(fcache)) try: save_to_hdf(df, fcache, metadata=metadata, version=radis.__version__, key='df', overwrite=True, verbose=verbose) except: if verbose: print(sys.exc_info()) print( 'An error occured in cache file generation. Lookup access rights' ) pass return df
def test_line_broadening(rtol=1e-3, verbose=True, plot=False, *args, **kwargs): ''' Plot absorption coefficient (cm-1) of CO at high temperature (2000 K) with RADIS, and compare with calculations from HAPI using the HITRAN database Notes ----- In this example no data is needed. Everything is downloaded from the HITRAN database directly using either the HAPI ``fetch`` function, or the RADIS :meth:`~neq.spec.factory.fetch_databank` method. ''' from radis.io.hapi import (db_begin, fetch, tableList, absorptionCoefficient_Voigt, transmittanceSpectrum) setup_test_line_databases() # add HITRAN-CO-TEST in ~/.radis if not there # Conditions molecule = 'CO2' mol_id = get_molecule_identifier(molecule) iso = 1 T = 1500 p = 0.1 L = 0.1 # M = 0.001 # mole fraction (dont know where to put that ) dnu = 0.0001 wmin = nm2cm(4372.69 + 0.2) # cm-1 wmax = nm2cm(4372.69 - 0.2) # cm-1 # broadening_max_width = 6 # cm-1 broadening_max_width = 0.5 # cm-1 # %% HITRAN calculation # ----------- # Generate HAPI database locally HAPIdb = join(dirname(__file__), __file__.replace('.py', '_HAPIdata')) def calc_hapi(): ''' Calc spectrum under HAPI ''' clean_after_run = not exists(HAPIdb) and False try: db_begin(HAPIdb) if not molecule in tableList( ): # only if data not downloaded already fetch(molecule, mol_id, iso, wmin - broadening_max_width / 2, wmax + broadening_max_width / 2) # HAPI doesnt correct for side effects # Calculate with HAPI nu, coef = absorptionCoefficient_Voigt( SourceTables='CO2', Environment={ 'T': T, # K 'p': p / 1.01325, # atm }, WavenumberStep=dnu, HITRAN_units=False, GammaL='gamma_self') nu, trans = transmittanceSpectrum( nu, coef, Environment={ 'l': L, # cm }) s_hapi = Spectrum.from_array(nu, trans, 'transmittance_noslit', 'cm-1', 'I/I0', conditions={'Tgas': T}, name='HAPI') except: raise finally: if clean_after_run: shutil.rmtree(HAPIdb) return s_hapi s_hapi = calc_hapi() def calc_radis(): # %% Calculate with RADIS # ---------- pl = SpectrumFactory(wavenum_min=wmin, wavenum_max=wmax, mole_fraction=1, path_length=L, wstep=dnu, molecule=molecule, pressure=p, broadening_max_width=broadening_max_width, cutoff=1e-23, db_use_cached=True, isotope=iso) # 0.2) pl.warnings['MissingSelfBroadeningWarning'] = 'ignore' pl.warnings['HighTemperatureWarning'] = 'ignore' pl.fetch_databank(format='hitran', load_energies=False) s = pl.eq_spectrum(Tgas=T) # , Ttrans=300) s.name = 'RADIS' if plot: pl.plot_broadening() return s s = calc_radis() # %% Compare # also shrinks HAPI range to the valid one s_hapi.resample(s.get_wavenumber(), unit='cm-1', energy_threshold=0.1) save = False # just used in the article if plot or save: from radis import plot_diff # title = '{0} bar, {1} K, {2} cm'.format(p, T, L) if save else None fig, [ax0, ax1] = plot_diff(s, s_hapi, var='transmittance_noslit', method='ratio', show=plot) ax0.annotate( r'[P64](00$^\mathregular{0}$0)$\rightarrow $(00$^\mathregular{0}$1)', (2286.945, 0.76), (2286.94, 0.8), arrowprops=dict(arrowstyle="->", facecolor='black')) ax0.annotate( r'[P53](01$^\mathregular{1}$0)$\rightarrow $(01$^\mathregular{1}$1)', (2286.9, 0.78), (2286.9, 0.82), arrowprops=dict(arrowstyle="->", facecolor='black'), horizontalalignment='right') ax1.set_ylim(0.95, 1.05) if save: fig.savefig('out/test_RADIS_vs_HAPI_line_broadening.pdf') # Compare integrals diff = abs( s.get_integral('transmittance_noslit', medium='vacuum') / s_hapi.get_integral('transmittance_noslit', medium='vacuum') - 1) b = diff < rtol if verbose: printm('Integral difference ({0:.2f}%) < {1:.2f}%: {2}'.format( diff * 100, rtol * 100, b)) return b