diff --git a/docs/getting_started.rst b/docs/getting_started.rst new file mode 100644 index 0000000..5f10f4f --- /dev/null +++ b/docs/getting_started.rst @@ -0,0 +1,33 @@ + +Getting started +=============== + +.. code-block:: python + + import opacity + import matplotlib.pyplot as plt + + dataset = opacity.open_dataset('VO') + + select = dict( + temperature=750, # [K] + pressure=1e-5 # [bar] + ) + cross_section = dataset.cross_section.sel(select) + metadata = cross_section.attrs + + fig, ax = plt.subplots(figsize=(10, 5), dpi=300) + ax.loglog(ds.wavenumber, cross_section) + + title = ( + f"{metadata['molecule']}, " + f"T={select['temperature']} {cross_section.temperature.unit}, " + f"P={select['pressure']} {cross_section.pressure.unit}" + ) + + ax.set( + ylim=(1e-40, 1e-10), + xlabel=f"Wavenumber [{cross_section.wavenumber.attrs['unit']}]", + ylabel=f"Cross section [{metadata['unit']}]", + title=title + ) \ No newline at end of file diff --git a/opacity/__init__.py b/opacity/__init__.py index 7036eaf..c3f8b2a 100644 --- a/opacity/__init__.py +++ b/opacity/__init__.py @@ -4,3 +4,4 @@ from opacity.dataset import * from opacity.filesystem import * from opacity.resolve_species import * +from opacity.metadata import * diff --git a/opacity/data/metadata_descriptions.json b/opacity/data/metadata_descriptions.json new file mode 100644 index 0000000..39d1564 --- /dev/null +++ b/opacity/data/metadata_descriptions.json @@ -0,0 +1,85 @@ +{ + "calculation": { + "entries": { + "id": "int", + "mol": "str", + "iso_mol": "str", + "iso_abundance": "dict", + "ptid": "int", + "temperature": "float", + "pressure": "float", + "intensity_cutoff": "float", + "wing_cutoff": "float", + "line_profile": "float", + "code": "str", + "start_wno": "float", + "delta_wno": "float", + "doi": "str", + "date": "str" + } + }, + "broadening": { + "calculation_options": { + "P_bro(J)": { + "full_name": "Used one equation for all transitions", + "description": "A single equation for the all lines which shows the behavior of P-broadening with quantum number J. This assumes the impact of molecular symmetry, other quantum numbers, and vibrational quantas are ignored in the general (P_{bro}(J)) equation form. " + }, + "P_bro(J,Vib)": { + "full_name": "Used N equations for a molecule with N vibrational quanta", + "description": "N equations to describe the behavior of pressure-broadening coefficients for all lines based on their vibrational quanta and J quantum numbers. Therefore, for H2O molecule, we expect to have one equation for all Js, and multiple equations for each vibrational bands. This assumes the impact of other quantum numbers, and molecular symmetry are ignored in this (P_{bro}(J, vib)) equation form. " + }, + "P_bro(J,sym)": { + "full_name": "Used N equations for a molecule with N symmetries", + "description": "N equations to describe the behavior of pressure-broadening coefficients for all lines based on their molecular symmetry and J quantum number.For example, (P_{bro}(J,Sym_1)= m_0 + (m_1*J) + (m_2*J^2)); (P_{bro}(J,Sym_2)= m_3 + (m_4*J) + (m_5*J^2)); etc. Assumptions: The impact of other quantum numbers and vibrational quantas are ignored in this (P_{bro}(J, Sym)) equation form. " + }, + "P_bro(J,K,sym)": { + "full_name": "Used N equations for a molecule with N symmetries, and K molecular symmetries", + "description": "N equations to describe the behavior of pressure-broadening coefficient for all lines based on their symmetry, J and K quantum numbers. For example, (P_{bro}(J,Sym_1)=m_0+(m_1*J)+(m_2*J^2)+(n_1*K)+(n_2*K^2)), and (P_{bro}(J,K, Sym_2)=m_3+(m_4*J)+(m_5*J^2)+(n_3*K)+(n_4*K^2)), etc. Assumptions: The impact of vibrational quantas is ignored in this (P_{bro}(J,K, Sym)) equation form. " + }, + "P_bro(J,K,sym,Vib)": { + "full_name": "Used N*M equations for a molecule with N symmetries, and M vibrational bands", + "description": "This equation accounts for the dependency of P-broadening on the quantum numbers J and K, molecular symmetries, and also vibrational bands. For example: (P_{bro}(J,K, Sym_1, Vib_1)=m_0+(m_1*J)+(m_2*J^2)+(n_1*K)+(n_2*K^2)), and (P_{bro}(J,K, Sym_2, Vib_2)=m_3+(m_4*J)+(m_5*J^2)+(n_3*K)+(n_4*K^2))." + }, + "P_bro(lab,theory)": { + "full_name": "Used laboratory data and theoretical equations", + "description": "A set of laboratory measurmed coefficients and theoretical calculated P-broadening data just as they are." + } + }, + "usr_warnings": { + "1":"Laboratory or theoretical broadener information not available at all.", + "2":"Theoretical broadener information used is for 296 K. Functional extrapolation used.", + "3":"Broadener data for an air mixture was used in place of this broadener gas.", + "4":"Broadener data is incomplete for high values of J", + "5":"Broadener data for self broadening was used in place of this broadener gas.", + "6":"Average values (non J-dependent) for the line broadening parameters are used." + }, + "entries": { + "broadened_by": "str", + "abundance": "float", + "calculation_type": "str", + "functional_form": "str", + "inputs": "list", + "bibid": "str", + "usr_warning": "int", + "usr_note": "str", + "todo": "str" + } + }, + "line_list": { + "entries": { + "wno_min": "float", + "wno_max": "float", + "temp_min":"float", + "temp_max":"float", + "db_name": "str", + "bibid": "str", + "usr_warning": "str", + "usr_note": "str", + "todo": "str" + }, + "usr_warnings": { + "1": "Line list used is not suitable for this temperature regime.", + "2": "The line position accuracy is suitable for JWST-quality spectra only (R=100-3000)." + } + } +} \ No newline at end of file diff --git a/opacity/metadata.py b/opacity/metadata.py new file mode 100644 index 0000000..792840b --- /dev/null +++ b/opacity/metadata.py @@ -0,0 +1,152 @@ +import os +import re +import json +from pathlib import Path + +import ipywidgets +from IPython.display import display, Javascript +import solara + +__all__ = ['Metadata'] + +tab_style = { + 'text-transform': 'capitalize' +} +value_style = { + 'padding-left': '8em', + 'padding-top': '0.5em', + 'padding-bottom': '0.5em', +} + + +metadata_desc_path = os.path.join( + os.path.dirname(__file__), + 'data', + 'metadata_descriptions.json' +) + +with open(metadata_desc_path, 'r') as meta: + metadata_desc = json.load(meta) + + +def copy_button(text): + output = ipywidgets.Output() + + def on_button_click(_, text=text): + with output: + output.clear_output() + display(Javascript(f'navigator.clipboard.writeText("{text}")')) + + button = ipywidgets.Button(description="", icon="copy") + button.on_click(on_button_click) + button.layout.width = '30pt' + button.layout.font_size = '1pt' + button.style.button_color = "transparent" + button.style.text_color = "#037ffc" + + return ipywidgets.HBox([button, output]) + + +def unpack_dict(d): + tab_order = [ + 'linelist', 'calculation', 'broadener_H2', + 'broadener_He', 'compression', 'source', + ] + for tab_key, tab_value in d.items(): + if tab_key in ['molecule', 'unit']: + continue + + if tab_key in tab_order: + with solara.lab.Tab(f"{tab_key}", style=tab_style): + with solara.Div(style={'padding-left': '3em'}): + unpack_dict(tab_value) + continue + + else: + if tab_key == 'todo' or tab_value == '': + continue + + solara.Markdown(f"#### {tab_key}") + + if tab_key.startswith('calculation_type'): + desc = metadata_desc['broadening']['calculation_options'][tab_value]['description'] + solara.Markdown(f"{desc}", style={'color': '#5A5A5A'}) + + with solara.Row(margin=0): + + if tab_key.startswith('usr_warning'): + if tab_value == 0: + solara.Markdown("No warnings.", style=value_style) + else: + desc = metadata_desc['broadening']['usr_warnings'][str(tab_value)] + solara.Markdown(f"{desc} ({tab_value})", style=value_style) + else: + value_markdown, copy_text = to_markdown(tab_value) + solara.Markdown( + value_markdown, + style=value_style + ) + if copy_text: + # with solara.Tooltip("Markdown", color="white"): + solara.display(copy_button(copy_text)) + + +def to_markdown(text): + text_href_markdown, href_url = latex_href_to_markdown(str(text)) + text_markdown, doi_url = doi_to_markdown(text_href_markdown) + + if href_url is not None: + copy_text = href_url + elif doi_url is not None: + copy_text = doi_url + elif '$' in text_markdown or text_markdown.startswith('['): + copy_text = text_markdown + else: + copy_text = None + + return str(text_markdown), copy_text + + +def latex_href_to_markdown(text): + if not isinstance(text, str) or 'href' not in text: + return text, None + + pattern = r"\\href\{([^}]*)\}\{([^}]*)\}" + replacement_html = r'\2' + replacement_markdown = r"[\2](\1)" + return ( + re.sub(pattern, replacement_html, text), + re.sub(pattern, replacement_markdown, text) + ) + + +def doi_to_markdown(text): + if not isinstance(text, str) or 'DOI' not in text: + return text, None + + pattern = r"DOI=\[(.*?)\]" + replacement_html = r'\1' + replacement_markdown = r'[\1](https://doi.org/\1)' + return ( + re.sub(pattern, replacement_html, text), + re.sub(pattern, replacement_markdown, text) + ) + + +@solara.component +def Metadata(cross_section): + solara.Markdown(f"## Metadata: {cross_section.attrs['molecule']}") + with solara.lab.Tabs( + vertical=True, lazy=True, color="#C5F8FD", + dark=True, background_color="#13457B", + slider_color="#A0F7FF" + ): + tab_order = [ + 'linelist', 'calculation', 'broadener_H2', + 'broadener_He', 'compression', 'source', + ] + dictionary = { + ordered_key: cross_section.attrs[ordered_key] + for ordered_key in tab_order + } + unpack_dict(dictionary) diff --git a/pyproject.toml b/pyproject.toml index 98577c5..9076fea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,9 @@ dependencies = [ "s3fs", "xarray", "zarr", + "ipywidgets", + "IPython", + "solara", ] dynamic = ["version"]