Esempio n. 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, rtol in [
        ("CO2", 1, 300, 0.9, 0.02),
        ("CO2", 1, 1000, 5.0, 0.02),
        ("CO2", 1, 3000, 2150, 0.02),
        ("CO2", 2, 300, 1.8, 0.02),
        ("CO2", 2, 1000, 25, 0.02),
        ("CO2", 2, 3000, 4962, 0.02),
        ("CO", 1, 300, 0.16, 0.02),
        ("CO", 1, 1000, 1.9, 0.02),
        ("CO", 1, 3000, 27, 0.02),
        ("CO", 2, 300, 0.31, 0.02),
        ("CO", 2, 1000, 4.1, 0.02),
        ("CO", 2, 3000, 56.9, 0.02),
        ("CO", 3, 300, 0.16, 0.02),
        ("CO", 3, 1000, 2.1, 0.02),
        ("CO", 3, 3000, 28.6, 0.02),
    ]:

        S = Molecules[molecule][iso]["X"]

        # Dont use cached: force recalculating
        db = PartFunc_Dunham(S)

        from radis.db.classes 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)
            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 test_line_broadening(rtol=1e-3, verbose=True, plot=False, *args, **kwargs):
    r"""
    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 hapi import (
        absorptionCoefficient_Voigt,
        db_begin,
        fetch,
        tableList,
        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,
                    "p": p / 1.01325,
                },  # K  # 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",
                "1",
                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,
            isotope=iso,
        )
        pl.warnings["MissingSelfBroadeningWarning"] = "ignore"
        pl.warnings["HighTemperatureWarning"] = "ignore"
        pl.fetch_databank(
            source="hitran",
            load_energies=False,
            db_use_cached=True,
        )

        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") /
        s_hapi.get_integral("transmittance_noslit") - 1)
    b = diff < rtol

    if verbose:
        printm("Integral difference ({0:.2f}%) < {1:.2f}%: {2}".format(
            diff * 100, rtol * 100, b))

    return b
Esempio n. 3
0
def fetch_astroquery(
    molecule, isotope, wmin, wmax, verbose=True, cache=True, expected_metadata={}
):
    """Download a HITRAN line database to a Pandas DataFrame.

    Wrapper to Astroquery [1]_ fetch function

    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 or ``'regen'``
        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.
        If ``'regen'``, delete existing cache file to regerenate it.
    expected_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.

    Notes
    -----
    The HITRAN module in Astroquery [1]_ is itself based on [HAPI]_

    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:
        # Cache file location in Astroquery cache
        # TODO: move full HITRAN databases in ~/radisdb cache like io/hitemp/fetch_hitemp ?
        fcache = join(
            Hitran.cache_location,
            CACHE_FILE_NAME.format(
                **{"molecule": molecule, "isotope": isotope, "wmin": wmin, "wmax": wmax}
            ),
        )
        # ... Update metadata with physical properties from the database.
        expected_metadata.update(
            {"molecule": molecule, "isotope": isotope, "wmin": wmin, "wmax": wmax}
        )
        if cache == "regen":
            if exists(fcache):
                if verbose:
                    print(f"Cache file {fcache} deleted to be regenerated")
                os.remove(fcache)
        else:
            # Load cache file if valid
            check_cache_file(
                fcache=fcache,
                use_cached=cache,
                expected_metadata=expected_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)

    # 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:
        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:
        new_metadata = {
            "molecule": molecule,
            "isotope": isotope,
            "wmin": wmin,
            "wmax": wmax,
        }
        if verbose:
            print(
                "Generating cache file {0} with metadata :\n{1}".format(
                    fcache, new_metadata
                )
            )
        try:
            save_to_hdf(
                df,
                fcache,
                metadata=new_metadata,
                version=radis.__version__,
                key="df",
                overwrite=True,
                verbose=verbose,
            )
        except PermissionError:
            if verbose:
                print(sys.exc_info())
                print("An error occured in cache file generation. Lookup access rights")
            pass

    return df