
.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "gallery/03_mhd_io/p01_quickstart.py"
.. LINE NUMBERS ARE GIVEN BELOW.

.. only:: html

    .. note::
        :class: sphx-glr-download-link-note

        :ref:`Go to the end <sphx_glr_download_gallery_03_mhd_io_p01_quickstart.py>`
        to download the full example code.

.. rst-class:: sphx-glr-example-title

.. _sphx_glr_gallery_03_mhd_io_p01_quickstart.py:


Getting Started with PsiData
============================

This example is intended as a quick reference for interacting with the :func:`~psi_io.mhd_io.PsiData`
lazy reader. The nuances of this API's design and full capabilities is explored in the subsequent
examples.

.. seealso::
   :ref:`sphx_glr_gallery_03_mhd_io_p02_psidata_reading.py` for the full
   read/vslice API, and
   :ref:`sphx_glr_gallery_01_reading_files_p01_reading_file_meta_data.py`
   for metadata inspection.

.. GENERATED FROM PYTHON SOURCE LINES 15-22

.. code-block:: Python

    import warnings

    from psi_io import PsiData
    from psi_data import fetch_mas_data, fetch_pot3d_data

    from psi_io.mhd_io import MetaDataWarning








.. GENERATED FROM PYTHON SOURCE LINES 23-30

Fetching Data
-------------

For the purposes of this example, the :func:`~psi_data.fetch_mas_data` and
:func:`~psi_data.fetch_pot3d_data` functions, found in the :mod:`psi_data` package, are
used to retrieve example MAS and POT3D HDF files. These functions return a simple namespace of
filepaths for each variable.

.. GENERATED FROM PYTHON SOURCE LINES 30-38

.. code-block:: Python


    mas_files= fetch_mas_data(domains='cor', variables='br,vr')
    pot3d_file = fetch_pot3d_data(variables='br')

    print(f"Example MAS Br file        →    .../{mas_files.cor_br.name}")
    print(f"Example MAS Vr file        →    .../{mas_files.cor_vr.name}")
    print(f"Example POT3D Br file      →    .../{pot3d_file.br.name}")





.. rst-class:: sphx-glr-script-out

 .. code-block:: none

      0%|                                              | 0.00/43.6M [00:00<?, ?B/s]      0%|▏                                     | 180k/43.6M [00:00<00:27, 1.60MB/s]      1%|▍                                     | 557k/43.6M [00:00<00:16, 2.65MB/s]      4%|█▍                                   | 1.75M/43.6M [00:00<00:06, 6.29MB/s]     11%|███▉                                 | 4.59M/43.6M [00:00<00:02, 13.7MB/s]     24%|████████▉                            | 10.6M/43.6M [00:00<00:01, 29.1MB/s]     43%|████████████████                     | 18.9M/43.6M [00:00<00:00, 46.4MB/s]     62%|██████████████████████▉              | 27.1M/43.6M [00:00<00:00, 57.7MB/s]     81%|██████████████████████████████       | 35.5M/43.6M [00:00<00:00, 65.8MB/s]      0%|                                              | 0.00/43.6M [00:00<?, ?B/s]    100%|██████████████████████████████████████| 43.6M/43.6M [00:00<00:00, 197GB/s]
      0%|                                              | 0.00/2.78M [00:00<?, ?B/s]      6%|██▏                                   | 164k/2.78M [00:00<00:01, 1.51MB/s]     20%|███████▋                              | 557k/2.78M [00:00<00:00, 2.73MB/s]     53%|███████████████████▍                 | 1.46M/2.78M [00:00<00:00, 5.25MB/s]      0%|                                              | 0.00/2.78M [00:00<?, ?B/s]    100%|█████████████████████████████████████| 2.78M/2.78M [00:00<00:00, 13.4GB/s]
    Example MAS Br file        →    .../br002.h5
    Example MAS Vr file        →    .../vr002.h5
    Example POT3D Br file      →    .../br.h5




.. GENERATED FROM PYTHON SOURCE LINES 39-47

Creating a Reader
-----------------

The :func:`~psi_io.mhd_io.PsiData` reader API requires a filepath and ``model`` name; if the
``model`` name is not recognized, *i.e.* not ``mas`` or ``pot3d``, metadata cannot be inferred
and must be supplied explicitly (either as keyword arguments to :func:`~psi_io.mhd_io.PsiData`
or as attributes written to the HDF dataset itself - see :func:`~psi_io.psi_io.write_hdf_meta`
for more information on how to write metadata to HDF datasets.

.. GENERATED FROM PYTHON SOURCE LINES 47-57

.. code-block:: Python


    mas_br = PsiData(mas_files.cor_br, model='mas')
    print(f"{mas_br!r}")

    with warnings.catch_warnings(record=True) as w:
        pot3d_br = PsiData(pot3d_file.br, model='pot3d')
        print(f"METADATA WARNING: {w[-1].message}")

    print(f"{pot3d_br!r}")





.. rst-class:: sphx-glr-script-out

 .. code-block:: none

    H5Data(name='br' [MAS Magnetic Field (Radial Component)], order='F', shape=(255, 142, 299), unit=Unit("MAS_b"), mesh=Mesh(HALF, MAIN, MAIN), cached=False)
    METADATA WARNING: H5Data(br) has a dimensionless unit. Ensure the correct unit is declared at instantiation or written to the HDF dataset's attribute mapping.
    H5Data(name='br' [POT3D Magnetic Field (Radial Component)], order='F', shape=(50, 76, 182), unit=Unit("POT3D_b"), mesh=Mesh(MAIN, HALF, HALF), cached=False)




.. GENERATED FROM PYTHON SOURCE LINES 58-66

.. warning::

   POT3D applies no normalization to its output. The stored values are in whatever physical
   unit the input photospheric magnetogram used — most commonly Gauss, but this is not encoded
   in the file. The fallback unit for POT3D is dimensionless_unscaled (scale factor 1).
   As a result, it is incumbent upon the user to pass in the correct unit at instantiation
   time, or to set it manually after the fact, to ensure accurate physical interpretation and
   unit-aware computations.

.. GENERATED FROM PYTHON SOURCE LINES 66-70

.. code-block:: Python


    pot3d_br.unit = 'Gauss'  # manually set the unit to Gauss for accurate physical interpretation
    print(f"Updated POT3D Br unit: {pot3d_br.unit}")





.. rst-class:: sphx-glr-script-out

 .. code-block:: none

    Updated POT3D Br unit: G




.. GENERATED FROM PYTHON SOURCE LINES 71-83

Passing the Wrong Model or Mesh
-------------------------------

:func:`~psi_io.mhd_io.PsiData` does **not** verify that the declared ``model`` actually
matches the file — it trusts ``model`` and infers every piece of metadata (quantity name and
description, unit normalization, and mesh staggering) from that model's mapping together with
the filename. Declaring the wrong model therefore *silently* attaches the wrong metadata.

Below we deliberately open the POT3D ``br`` file as a MAS quantity. The reader happily
constructs, but the inferred unit is now ``MAS_b`` instead of ``POT3D_b`` — a different
normalization scale factor that would corrupt any physical-unit computation — and the mesh
stagger is taken from MAS' ``br`` descriptor rather than POT3D's:

.. GENERATED FROM PYTHON SOURCE LINES 83-94

.. code-block:: Python


    with warnings.catch_warnings(record=True) as w:
        warnings.simplefilter("always")
        err_reader = PsiData(pot3d_file.br, model='mas')

    print(f"Inferred unit (wrong) : {err_reader.unit}               (should be POT3D_b)")
    print(f"Inferred mesh (wrong) : {err_reader.mesh}    (should be MAIN, HALF, HALF)")
    for warning in w:
        if issubclass(warning.category, MetaDataWarning):
            print(f"METADATA WARNING: {warning.message}")





.. rst-class:: sphx-glr-script-out

 .. code-block:: none

    Inferred unit (wrong) : MAS_b               (should be POT3D_b)
    Inferred mesh (wrong) : HALF, MAIN, MAIN    (should be MAIN, HALF, HALF)
    METADATA WARNING: H5Scale(t) has a main-mesh stagger but non-zero values at the inner boundary. Check that the correct mesh code is declared at instantiation or written to the HDF dataset's attribute mapping.
    METADATA WARNING: H5Scale(p) has a main-mesh stagger but non-zero values at the inner boundary. Check that the correct mesh code is declared at instantiation or written to the HDF dataset's attribute mapping.




.. GENERATED FROM PYTHON SOURCE LINES 95-114

.. warning::

   **A model/mesh mismatch is mostly silent.** The wrong *unit* is applied with no warning at
   all, so values look numerically fine but are scaled incorrectly. The only automatic signal
   is a best-effort :class:`~psi_io.mhd_io.MetaDataWarning` raised during *scale* validation: a
   ``t`` or ``p`` coordinate declared on the *main* mesh but holding non-zero values at the
   inner boundary carries the tell-tale offset of a *half*-mesh axis, which is what trips the
   warnings above. This heuristic catches some — but not all — staggering mistakes, and it
   never inspects the data unit.

   The mesh code is not cosmetic: it drives the cell-averaging performed by
   ``read(mesh=...)`` and the half-/main-mesh offsets used to bracket coordinates in
   :meth:`~psi_io.mhd_io.PsiData.vslice`. A wrong code averages along the wrong axis or shifts
   the coordinate–data alignment by up to half a grid cell, again without raising an error.

   When the model cannot be inferred — or is wrong — supply the correct metadata explicitly via
   the ``unit`` and ``mesh`` keywords at instantiation (or set them afterward, as with the unit
   above), or write the correct attributes into the HDF dataset with
   :func:`~psi_io.psi_io.write_hdf_meta`.

.. GENERATED FROM PYTHON SOURCE LINES 116-131

Putting It Together: Reading on the Main Mesh in Physical Units
--------------------------------------------------------------

The most common end-to-end task is simply *read the whole field, on the main mesh, in physical
units*. Passing ``mesh='main'`` averages any half-mesh axis onto the main mesh, and
``unit='physical'`` decomposes the data to CGS base units. :meth:`~psi_io.mhd_io.PsiData.read`
returns the data array together with the (remeshed) ``r``, ``t``, ``p`` coordinate scales.

We also use the reader as a **context manager**. ``PsiData`` holds an open OS-level handle to
the underlying HDF file (opened when the reader is constructed). Using it in a ``with`` block
guarantees that handle is closed *deterministically* the moment the block exits — including if
an exception is raised mid-read — rather than lingering until the object is garbage-collected.
This matters when looping over many files, where leaked handles can otherwise exhaust the
process's file-descriptor limit or keep files locked. ``__enter__`` (re)opens the handle and
returns the reader; ``__exit__`` closes it.

.. GENERATED FROM PYTHON SOURCE LINES 131-142

.. code-block:: Python


    with PsiData(mas_files.cor_vr, model='mas') as reader:
        data, r, t, p = reader.read(mesh='main', unit='physical')
        print(f"quantity   : {reader.name} [{reader.desc}]")
        print(f"data       : shape={data.shape}, unit={data.unit}")
        print(f"r / t / p  : {r.shape[0]} × {t.shape[0]} × {p.shape[0]} points")

    # Outside the ``with`` block the underlying HDF handle has been closed, but the data we read is
    # an ordinary in-memory array and remains fully usable:
    print(f"max |Vr|   : {abs(data).max():.3e}")





.. rst-class:: sphx-glr-script-out

 .. code-block:: none

    quantity   : vr [MAS Velocity (Radial Component)]
    data       : shape=(299, 142, 254), unit=cm / s
    r / t / p  : 254 × 142 × 299 points
    max |Vr|   : 7.675e+07 cm / s





.. rst-class:: sphx-glr-timing

   **Total running time of the script:** (0 minutes 1.933 seconds)


.. _sphx_glr_download_gallery_03_mhd_io_p01_quickstart.py:

.. only:: html

  .. container:: sphx-glr-footer sphx-glr-footer-example

    .. container:: sphx-glr-download sphx-glr-download-jupyter

      :download:`Download Jupyter notebook: p01_quickstart.ipynb <p01_quickstart.ipynb>`

    .. container:: sphx-glr-download sphx-glr-download-python

      :download:`Download Python source code: p01_quickstart.py <p01_quickstart.py>`

    .. container:: sphx-glr-download sphx-glr-download-zip

      :download:`Download zipped: p01_quickstart.zip <p01_quickstart.zip>`


.. only:: html

 .. rst-class:: sphx-glr-signature

    `Gallery generated by Sphinx-Gallery <https://sphinx-gallery.github.io>`_
