Beispiel #1
0
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
Beispiel #2
0
    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)
Beispiel #3
0
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
Beispiel #4
0
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
Beispiel #5
0
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