Source code for beetroots.inversion.plots.readable_line_names

r"""Former version of the ``latex-ism-emission-lines`` package by L. Einig available at  https://github.com/einigl/latex-ism-emission-lines/tree/main.
"""

import re
from typing import Optional, Sequence, Tuple, Union

__all__ = ["lines_to_latex"]


# Molecular names with underscore
_underscore_prefixes = {
    "13c_o": "13co",
    "c_18o": "c-18o",
    "13c_18o": "13c-18o",
    "h2_18o": "h2-18o",
    "c_c3h2": "c-c3h2",
}

# Molecular names to latex
_molecules_to_latex = {
    "h": "H",
    "h2": "H_2",
    "hd": "HD",
    "co": "CO",
    "13co": "^{13}CO",
    "c-18o": "C^{18}O",
    "13c-18o": "^{13}C^{18}O",
    "c": "C",
    "n": "N",
    "o": "O",
    "s": "S",
    "si": "Si",
    "cs": "CS",
    "cn": "CN",
    "hcn": "HCN",
    "hnc": "HNC",
    "oh": "OH",
    "h2o": "H_2O",
    "h2-18o": "H_2^{18}O",
    "c2h": "C_2H",
    "c-c3h2": "c-C_3H_2",
    "so": "SO",
    "cp": "C^+",
    "sp": "S^+",
    "hcop": "HCO^+",
    "chp": "CH^+",
    "ohp": "OH^+",
    "shp": "SH^+",
}

# Energy to LaTeX
_energy_to_latex = {
    "j": "J",
    "v": "\\nu",
    "f": "f",
    "n": "n",
    "ka": "k_a",
    "kc": "k_c",
}


def _transition(
    name: Optional[str],
    high_lvl: Union[str, int],
    low_lvl: Union[str, int],
) -> Tuple[Union[str, Tuple[str, str]], bool]:
    """
    Returns a LaTeX string representing a non electronic transition.

    Parameters
    ----------
    name : str
        Energy name.
    high_lvl : str
        Higher energy level.
    low_lvl : str
        Lower energy level. Can be the same as `high_lvl`.

    Returns
    -------
    str or tuple of str
        If it is a transition: higher energy level and lower energy level. Else: energy level.
    bool
        True if it is a transition, else False
    """
    if name:
        if name in _energy_to_latex:
            name_latex = _energy_to_latex[name]
        else:
            name_latex = name
        if high_lvl == low_lvl:
            return "${}={}$".format(name_latex, low_lvl), False
        return (
            "${}={}$".format(name_latex, high_lvl),
            "${}={}$".format(name_latex, low_lvl),
        ), True
    if high_lvl == low_lvl:
        return "", False
    return ("${}$".format(high_lvl), "${}$".format(low_lvl)), True


def _eltransition(
    high: Union[str, int], low: Union[str, int]
) -> Tuple[Union[str, Tuple[str, str]], bool]:
    """
    Returns a LaTeX string representing an electronic transition.

    Parameters
    ----------
    high :  Union[str,int]
        Higher energy electronic configuration.
    low :  Union[str,int]
        Lower energy electronic configuration. Can be the same as `high`.

    Returns
    -------
    str or tuple of str
        If it is a transition: higher energy electronic configuration and lower energy configuration. Else: energy electronic configuration.
    bool
        True if it is a transition, else False
    """
    if high == low:
        return "${}$".format(high), False
    return ("${}$".format(high), "${}$".format(low)), True


def _sort_transitions(
    names: Sequence[str],
    high_lvls: Union[Sequence[int], Sequence[str]],
    low_lvls: Union[Sequence[int], Sequence[str]],
) -> str:
    """
    Returns a LaTeX string representing the energy transitions.
    This string first display the constant energy levels and then the energy transitions.

    Parameters
    ----------
    names : Sequence of str
        Energies names.
    high_lvls : Sequence of int
        Sequence of higher level for each energy.
    low_lvls : Sequence of int.
        Sequence of lower level for each energy.

    Returns
    -------
    str
        String representing first the constant energy levels and then the energy transitions.
    """
    if len(high_lvls) != len(names) or len(low_lvls) != len(names):
        raise ValueError("names, high_lvls and low_lvls must have the same length")

    if len(names) == 0:
        return ""
    if len(names) == 1:
        return _transition(None, high_lvls[0], low_lvls[0])

    descr_0, descr_1a, descr_1b = "", "", ""
    for name, high, low in zip(names, high_lvls, low_lvls):
        if name == "el":
            descr, istrans = _eltransition(high, low)
        else:
            descr, istrans = _transition(name, high, low)
        if istrans:
            descr_1a += descr[0] + ", "
            descr_1b += descr[1] + ", "
        else:
            descr_0 += descr + " "
    return "{} ({} $\\to$ {})".format(descr_0.strip(), descr_1a[:-2], descr_1b[:-2])


[docs] def lines_to_latex(line_name: str) -> str: """ Returns a well displayed version of the formatted line ``line_name``. Parameters ---------- line_name : str Formatted line. Returns ------- str LaTeX string representing ``line_name``. """ # Check if the input is in the good format if not "_" in line_name: return line_name line_name = line_name.lower().strip() # # Replace the underscore in molecular names containing one # for pref in _underscore_prefixes: # if pref in line_name: # line_name = line_name.replace(pref, "-", 1) # break # Replace the underscore in molecular names containing one for pref, new_pref in _underscore_prefixes.items(): if line_name.startswith(pref): line_name = line_name.replace(pref, f"{new_pref}", 1) break # Split the line name in two parts prefix, suffix = line_name.split("_", maxsplit=1) # Convert the prefix in LaTeX if prefix in _molecules_to_latex: latex_prefix = "${}$".format(_molecules_to_latex[prefix]) else: latex_prefix = prefix # Convert the suffix in LaTeX if re.match("\Aj\d*__j\d*\Z", suffix): # ??__j{}__j{} res = re.search("\Aj(.*?)__j(.*?)\Z", suffix) if res: latex_suffix = "$(J={} \\to J={})$".format(*res.group(1, 2)) else: raise ValueError("Should never been here") elif re.match("\Av\d*_j\d*__v\d*_j\d*\Z", suffix): # ??_v{}_j{}__v{}_j{} res = re.search("\Av(.*?)_j(.*?)__v(.*?)_j(.*?)\Z", suffix) if res: names = ["v", "j"] high_lvls, low_lvls = res.group(1, 2), res.group(3, 4) latex_suffix = _sort_transitions(names, high_lvls, low_lvls) else: raise ValueError("Should never been here") elif re.match("\An\d*_j\d*__n\d*_j\d*\Z", suffix): # ??_n{}_j{}__n{}_j{} res = re.search("\An(.*?)_j(.*?)__n(.*?)_j(.*?)\Z", suffix) if res: names = ["n", "j"] high_lvls, low_lvls = res.group(1, 2), res.group(3, 4) latex_suffix = _sort_transitions(names, high_lvls, low_lvls) else: raise ValueError("Should never been here") elif re.match("\Aj\d*_f\d*__j\d*_f\d*\Z", suffix): # ??_j{}_f{}__j{}_f{} res = re.search("\Aj(.*?)_f(.*?)__j(.*?)_f(.*?)\Z", suffix) if res: names = ["j", "f"] high_lvls, low_lvls = res.group(1, 2), res.group(3, 4) latex_suffix = _sort_transitions(names, high_lvls, low_lvls) else: raise ValueError("Should never been here") elif re.match("\Aj\d*_ka\d*_kc\d*__j\d*_ka\d*_kc\d*\Z", suffix): # ??_j{}_ka{}_kc{}__j{}_ka{}_kc{} res = re.search("\Aj(.*?)_ka(.*?)_kc(.*?)__j(.*?)_ka(.*?)_kc(.*?)\Z", suffix) if res: names = ["j", "ka", "kc"] high_lvls, low_lvls = res.group(1, 2, 3), res.group(4, 5, 6) latex_suffix = _sort_transitions(names, high_lvls, low_lvls) else: raise ValueError("Should never been here") elif re.match("\Ael\d*\w_j\d*__el\d*\w_j\d*\Z", suffix): # ??_el{int+char}_j{int}__el{int+char}_j{int} res = re.search("\Ael(.*?)_j(.*?)__el(.*?)_j(.*?)\Z", suffix) if res: names = ["el", "j"] high_lvls, low_lvls = res.group(1, 2), res.group(3, 4) latex_suffix = _sort_transitions(names, high_lvls, low_lvls) elif re.match("\Ael\d*\w_j\d*_2__el\d*\w_j\d*_2\Z", suffix): # ??_el{int+char}_j{int}_2__el{int+char}_j{int}_2 res = re.search("\Ael(.*?)_j(.*?)_2__el(.*?)_j(.*?)_2\Z", suffix) if res: names = ["el", "j"] high_lvls = res.group(1), r"\frac{" + f"{res.group(2)}" + r"}{2}" low_lvls = res.group(3), r"\frac{" + f"{res.group(4)}" + r"}{2}" latex_suffix = _sort_transitions(names, high_lvls, low_lvls) else: # Not adressed formats # oh_j5_2_pp_fif2__j3_2_pm_fif2 # c2h_n1d0_j1d5_f1d0__n0d0_j0d5_f1d0 # cn_n1_j0d5__n0_j0d5 # ohp_n1_j0_f0d5__n0_j1_f0d5 # shp_n1_j0_f0d5__n0_j1_f0d5 latex_suffix = suffix out = latex_prefix + " " + latex_suffix out = out.replace(" ", " ") return out