{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Constructing a SphericalMesh\n\nThis example demonstrates the three ways to initialise a\n:class:`~pyvisual.core.mesh3d.SphericalMesh`:\n\n1. **From an HDF file path** \u2014 pass the path directly; the constructor calls\n   :func:`~psi_io.psi_io.read_hdf_by_index` internally.  Additional positional\n   arguments after the path are forwarded as index arguments to the file reader.\n2. **From data arrays** \u2014 read the file manually with\n   :func:`~psi_io.psi_io.read_hdf_by_index`, then pass the coordinate arrays and data\n   to the constructor.\n3. **From an existing** :class:`~pyvisual.core.mesh3d.SphericalMesh` \u2014 pass\n   another mesh instance to produce a shallow copy.\n\nAll three routes produce an equivalent mesh; the choice depends on how much\ncontrol over the read step you need.  Real coronal magnetic field data\n$B_r$ from a PSI Thermo 2 run for Carrington Rotation 2282 (CR 2282) is\nused throughout.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from psi_io import read_hdf_by_index\nfrom pyvisual import Plot3d\nfrom pyvisual.core.mesh3d import SphericalMesh\nfrom pyvisual.utils.data import fetch_datasets\n\nbr_file = fetch_datasets(\"cor\", \"br\").cor_br"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## From an HDF File Path\n\nPassing a file path as the first argument triggers the file-path dispatch\npath: the constructor calls :func:`~psi_io.psi_io.read_hdf_by_index` on the path,\nloading both the scalar data and the three coordinate grids.  Positional\narguments after the path are forwarded to :func:`~psi_io.psi_io.read_hdf_by_index`\nas index arguments, controlling which portion of the grid is loaded\n(see the function documentation for details).\n\nHere no index arguments are supplied, so the full 3-D coronal domain is loaded\n($r \\times \\theta \\times \\phi$).\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "mesh_from_path = SphericalMesh(br_file)\n\nprint(f\"dimensions : {mesh_from_path.dimensions}\")\nprint(f\"r range    : [{mesh_from_path.r.min():.2f}, {mesh_from_path.r.max():.2f}] R_sun\")\nprint(f\"t range    : [{mesh_from_path.t.min():.4f}, {mesh_from_path.t.max():.4f}] rad\")\nprint(f\"p range    : [{mesh_from_path.p.min():.4f}, {mesh_from_path.p.max():.4f}] rad\")\nprint(f\"data range : [{mesh_from_path.data.min():.4f}, {mesh_from_path.data.max():.4f}] MAS Units\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## From Data Arrays\n\nWhen you need to pre-process the arrays before constructing the mesh \u2014\nfor example to apply a coordinate transform or inspect the raw values \u2014\ncall :func:`~psi_io.psi_io.read_hdf_by_index` yourself and pass the results\ndirectly to the constructor.  The coordinate arrays go in as the first three\npositional arguments (``r``, ``t``, ``p``); the scalar values are supplied\nvia the ``data`` keyword.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "data, r, t, p = read_hdf_by_index(br_file)\n\nmesh_from_arrays = SphericalMesh(r, t, p, data=data, dataid='Br')\n\n# Dimensions and data range are identical to the file-path route.\nprint(f\"dimensions match : {mesh_from_arrays.dimensions == mesh_from_path.dimensions}\")\nprint(f\"data allclose    : {(mesh_from_arrays.data == mesh_from_path.data).all()}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## From an Existing SphericalMesh\n\nPassing an existing :class:`~pyvisual.core.mesh3d.SphericalMesh` (or any\n:class:`pyvista.DataSet`) produces a shallow copy \u2014 both objects share the\nsame underlying data buffers.  Pass ``deep=True`` for an independent copy.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "mesh_from_mesh = SphericalMesh(mesh_from_path)\n\nprint(f\"dimensions match : {mesh_from_mesh.dimensions == mesh_from_path.dimensions}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Visualising the Mesh\n\nAll three meshes are equivalent.  Here the equatorial plane is extracted\nby slicing the theta axis ($\\theta_{71} \\approx \\pi/2$) and rendered\nas a 2-D surface colored by $B_r$.  The ``MESH_FRAME`` tag stored in\n``user_dict`` tells :class:`~pyvisual.core.plot3d.Plot3d` to convert\nspherical coordinates to Cartesian automatically before rendering.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "equatorial = mesh_from_path[:, 71, :]\n\nplotter = Plot3d()\nplotter.show_axes()\nplotter.add_sun()\nplotter.add_mesh(equatorial, cmap='seismic', clim=(-1, 1), show_scalar_bar=True)\nplotter.show()"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.13.12"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}