{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# MHD Equilibria\n", "\n", "## Overview\n", "\n", "In Struphy, an **MHD equilibrium** is a background configuration that describes the magnetic field $\\mathbf{B}(x, y, z)$, current density $\\mathbf{J}(x, y, z)$, and plasma properties (density, pressure) in a state of force balance. These equilibria serve as the foundation for studying plasma dynamics, perturbations, and wave propagation in Struphy models.\n", "\n", "A **domain** (or mapping) is how Struphy maps from a logical unit cube $\\hat{\\Omega} = (0,1)^3$ to a physical domain $\\Omega \\subset \\mathbb{R}^3$. This enables us to work with complex geometries (cylinders, tori, tokamaks) while leveraging efficient numerical methods on the regular reference cube.\n", "\n", "Different equilibrium classes handle this mapping relationship differently:\n", "\n", "### Cartesian Equilibria (User Configures Domain)\n", "\n", "`CartesianMHDequilibrium` subclasses require the developer to specify equilibrium variables in **Cartesian coordinates** $\\mathbf{B}(x, y, z)$ and $\\mathbf{J}(x, y, z)$. This approach is flexible—the same equilibrium can work with any domain. **You, the user, must assign a domain** after instantiating the equilibrium.\n", "\n", "Examples: `ScrewPinch`, `AdhocTorus`, `EQDSKequilibrium`\n", "\n", "### Logical Equilibria (Equilibrium Provides Domain)\n", "\n", "`LogicalMHDequilibrium` subclasses provide equilibrium variables in **logical coordinates** $\\hat{\\mathbf{B}}^i(\\eta_1, \\eta_2, \\eta_3)$ along with their **built-in mapping** to Cartesian coordinates. Since the mapping is part of the equilibrium definition, **you cannot change the domain**—it is fixed.\n", "\n", "Examples: `GVECequilibrium`, `DESCequilibrium`\n", "\n", "### In This Tutorial\n", "\n", "We'll explore both types and see how to work with them:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "\n", "from struphy import equils\n", "from struphy import domains" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example 1: ScrewPinch (Cartesian Equilibrium)\n", "\n", "**ScrewPinch** is an analytic `CartesianMHDequilibrium` describing a cylindrical plasma column with a twisted magnetic field. The equilibrium is defined in Cartesian coordinates and can be used with any cylindrical domain.\n", "\n", "**Notice:** We instantiate the equilibrium, then assign a `HollowCylinder` domain to it. This tells Struphy how to map the logical cube to a hollow cylinder in physical space." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mhd_equil = equils.ScrewPinch(R0=1.0)\n", "mhd_equil.domain = domains.HollowCylinder(a1=1e-8, a2=1, Lz=2 * np.pi)\n", "mhd_equil.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example 2: AdhocTorus (Cartesian Equilibrium)\n", "\n", "**AdhocTorus** is another `CartesianMHDequilibrium`, this time describing an analytic toroidal geometry. Like ScrewPinch, it is defined in Cartesian coordinates and requires you to assign a domain.\n", "\n", "**Notice:** Here we use a `HollowTorus` domain, which maps the logical cube to a toroidal shape suitable for this equilibrium." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mhd_equil = equils.AdhocTorus()\n", "mhd_equil.domain = domains.HollowTorus(a1=1e-8, tor_period=1)\n", "mhd_equil.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example 3: EQDSK (Cartesian Equilibrium)\n", "\n", "**EQDSK** is a `CartesianMHDequilibrium` that reads tokamak equilibria from EQDSK files (a standard format in fusion research). It constructs and stores the equilibrium in Cartesian coordinates.\n", "\n", "We can assign any domain to this equilibrium, but typically we would use a `Tokamak` domain that matches the geometry of the EQDSK data." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mhd_equil = equils.EQDSKequilibrium()\n", "mhd_equil.domain = domains.Tokamak(equilibrium=mhd_equil)\n", "mhd_equil.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example 4: GVEC (Logical Equilibrium)\n", "\n", "**GVEC** (a stellarator equilibrium code) is another `LogicalMHDequilibrium`. Like EQDSK, it stores the equilibrium and geometry together. No separate domain assignment is needed.\n", "\n", "**Notice:** We instantiate and plot directly. The geometry is already embedded in the equilibrium object." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mhd_equil = equils.GVECequilibrium(use_nfp=False)\n", "mhd_equil.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example 5: DESC (Logical Equilibrium)\n", "\n", "**DESC** (Design of Equilibria with Stellarator Constraints) is a modern stellarator equilibrium optimizer. Like GVEC, it is a `LogicalMHDequilibrium` with built-in geometry.\n", "\n", "**About the `%%capture` cell above:** DESC initialization can produce verbose output. The `%%capture` magic silences it so the notebook remains clean. The `mhd_equil` object is still created and available for use." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%capture\n", "mhd_equil = equils.DESCequilibrium(use_nfp=False)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mhd_equil.show()" ] } ], "metadata": { "kernelspec": { "display_name": "env (3.12.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.12.3" } }, "nbformat": 4, "nbformat_minor": 4 }