Repository: hiddenSymmetries/simsopt
Pull request #508: [skip-ci] Added SPEC and VMEC high-level descriptions
Author: login: smiet
Created: 2025-05-08T08:26:12Z | Merged: 2025-05-13T18:59:58Z

Added documentation on how `simsopt` interfaces with the MHD codes `SPEC` and `VMEC`. 


Diff:
diff --git a/docs/source/example_islands.rst b/docs/source/example_islands.rst
index 674777ff0..2fb315bef 100644
--- a/docs/source/example_islands.rst
+++ b/docs/source/example_islands.rst
@@ -1,3 +1,5 @@
+.. _eliminating-islands:
+
 Eliminating magnetic islands
 ============================
 
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 53af6ad14..f89508f67 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -69,6 +69,7 @@ optimization.  Others include `STELLOPT
    optimizable
    geo
    fields
+   mhd
    tracing
    mpi
    testing
diff --git a/docs/source/mhd.rst b/docs/source/mhd.rst
new file mode 100644
index 000000000..f021206fd
--- /dev/null
+++ b/docs/source/mhd.rst
@@ -0,0 +1,110 @@
+Magnetohydrodynamic codes
+-------------------------
+
+The :obj:`simsopt.mhd` module contains a collection of interfaces to magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.Vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.Spec` module.
+
+The equilibrium codes must be installed separately with python bindings provided by f90wrap. 
+See the installation instructions for `VMEC2000 <https://github.com/hiddenSymmetries/vmec2000>`_ and for `SPEC <https://github.com/PrincetonUniversity/SPEC/blob/master/compilation_instructions.md>`_ in their respective repositories.
+
+The ``Vmec`` and ``Spec`` classes instantiate the MHD solvers as ``Optimizable`` objects that can depend on other :obj:`Optimizable` objects, such as a :obj:`simsopt.mhd.Profile` for the pressure, current and rotational transform profiles, as well as :obj:`simsopt.mhd.Surface` for boundary and internal surfaces
+This allows for direct handling of profiles parameters, surface degrees of freedom, and solver options through the python class.
+
+MHD codes are often run in two different modes: fixed boundary and free boundary.
+In fixed boundary mode the equilibrium depends on the boundary (accessed through ``.boundary``), which is a :obj:`simsopt.geo.SurfaceRZFourier`. 
+In free-boundary mode however, the equilibrium on an external field (which can be provided by any :obj:`simsopt.field.MagneticField`) and its :obj:`boundary` is a child, whose properties (like aspect ratio, volume, etc) can be optimized for, by varying the degrees-of-freedom of the :obj:`MagneticField`.  
+One can even combine different solvers such that the equilibrium objects depend on the same :obj:`Surface`, or where one equilibrium solves for the boundary, and metrics related to it are computed using the fixed-boundary
+solutions provided by the other solver. 
+
+.. warning::
+    ``f90wrapped`` code creates singleton classes. You cannot create multiple 
+    ``Vmec`` or ``Spec`` objects in the same kernel. Code like:
+
+    :: 
+
+        from simsopt.mhd import vmec
+        myvmec1 = vmec.Vmec()
+        myvmec2 = vmec.Vmec()
+
+    will have both objects accessing the same ``Fortran`` memory locations
+    and lead to crashes and undefined behavior.
+
+
+The Evaluating an MHD code is done by their ``.run()`` methods. 
+This is often a computationally expensive operation, and will only 
+be performed if the :obj:`recompute_bell()` method has been called 
+(as is done if any of its parents' degrees-of-freedom have changed).
+If you manually change parameters this not always triggered, and you
+have to call the :obj:`recompute_bell()` method
+yourself. 
+
+One can optimize the result of an MHD calculation, by changing
+the degrees-of-freedom of the equilibrium object. 
+``VMEC`` and ``SPEC`` do not provide analytical derivatives. 
+As such, optimization can be done using derivative-free methods, or 
+using finite-difference. 
+
+Profiles
+~~~~~~~~
+
+An equilibrium depends on a number of profiles, for example the pressure, current and rotational transform profiles. 
+These are separate :obj:`Optimizable` objects, on which the equilibrium can depend. 
+Because SPEC and VMEC have very different representations, specialized classes
+are provided for each code. 
+
+If not explicitly set, most profiles are handled by the equilibrium code 
+internally, and not exposed to the user.
+
+The :ref:`running-vmec` tutorial contains more detailed information about profiles and using them with ``VMEC``.
+
+
+VMEC
+~~~~
+VMEC is one of the most widely used codes for calculating 3D MHD equilibria. 
+As such, it provides a very large number of diagnostics and outputs and has 
+couplings to other codes providing further metrics that can be used in 
+optimization. 
+VMEC assumes nested flux surfaces. 
+The :obj:`simsopt.mhd.Vmec` class provides the interface, and can be instantiated from the same input file as is usually used for running VMEC (an ``input.<name`` or ``wout_<name>.nc`` file): 
+
+See :ref:`running_vmec` for a more in-depth tutorial on running ``VMEC`` in ``simsopt``.
+
+
+Vmec diagnostics
+^^^^^^^^^^^^^^^^
+
+There are many useful diagnostics available that depend on a :obj:`Vmec` object which provide target functions for optimization. 
+These include:
+
+* :obj:`QuasisymmetryRatioResidual`: Deviation from quasisymmetry
+* :obj:`IotaTargetMetric`: Difference between the rotational transform and a provided target
+* :obj:`IotaWeighted`: Weighted average of the rotational transform
+* :obj:`WellWeighted`: Measure for the magnetic well. 
+* :obj:`Quasisymmetry`: Measure of the quasisymmetry using the boozer spectrum.
+* :obj:`VmecRedlBootstrapMismatch`: the mismatch between the VMEC bootstrap and that provided by a recent calculation by Redl (for obtaining self-consistent bootstrap current).
+
+
+
+SPEC
+~~~~~
+
+The Stepped Pressure Equilibrium Code (SPEC) computes equilibria using the Multi-region relaxed MHD (MRxMHD) formulation. 
+This models the plasma equilibrium as a finite number of ideal interfaces between which the magnetic field is relaxed to a force-free solution. 
+The :obj:`simsopt.mhd.Spec` class provides the interface, and can be instantiated from the same input file as is usually used for running SPEC (an ``<name>.sp`` file). 
+
+SPEC equilibria can contain magnetic islands and regions of magnetic chaos,
+making it possible to check for and optimize such features. 
+
+All ideal interfaces in spec are available as :obj:`SurfaceRZFourier` objects. 
+
+
+Greene's residue
+^^^^^^^^^^^^^^^
+
+Islands in a SPEC equilibrium can be optimized for using Cary and Hansons' method of minimizing Greene's residue. 
+The fixed points of the islands are found, and their residue is calculated using
+``pyoculus`` through the :obj:`simsopt.mhd.GreenesResidue` that depends on the :obj:`simsopt.mhd.spec.Spec` object, and needs the poloidal and toroidal mode number of the island provided. 
+
+See :ref:`eliminating-islands` for a tutorial on eliminating islands using Greene's residue minimization.
+


Reviews:
user: login: mishapadidar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T17:17:33Z
user: login: mishapadidar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T17:20:42Z
user: login: mishapadidar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T17:22:54Z
user: login: mishapadidar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T17:25:38Z
user: login: mishapadidar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T17:32:56Z
user: login: mishapadidar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T17:42:33Z
user: login: mishapadidar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T17:43:39Z
user: login: mishapadidar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T17:44:27Z
user: login: mishapadidar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T17:44:52Z
user: login: mishapadidar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T17:45:28Z
user: login: mishapadidar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T17:48:12Z
user: login: mishapadidar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T17:48:51Z
user: login: mishapadidar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T17:49:33Z
user: login: mishapadidar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T17:50:06Z
user: login: mishapadidar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T17:52:12Z
user: login: mishapadidar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T17:54:47Z
user: login: mishapadidar
body: I like the idea of having a high level description of MHD codes. It would be useful to link to tutorials and other relevant places in the codes.
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T17:55:52Z
user: login: smiet
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T15:36:33Z
user: login: smiet
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T15:39:02Z
user: login: smiet
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T15:41:15Z
user: login: smiet
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T15:43:05Z
user: login: mbkumar
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-13T17:24:13Z

Review comments:
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
path: docs/source/mhd.rst
user: login: mishapadidar
body: capitalize `The`
created_at: 2025-05-08T17:17:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.spec` module.
+
+The equilibrium codes must be installed separately and with python bindings provided by f90wrap. 
+The :obj:`simsopt.mhd` module provides them :obj:`Optimizable` objects that can depend on other :obj:`Optimizable` objects or have other :obj:`Optimizable` objects depend on them.
+This includes :obj:`simsopt.mhd.Profile` for the pressure, current and rotational transform profiles, as well as :obj:`simsopt.mhd.Surface` for boundary and internal surfaces. 
+
+In fixed boundary mode the equilibrium depends on the boundary (accessed through :obj:`.boundary`), which is a :obj:`SurfaceRZFourier`. 
path: docs/source/mhd.rst
user: login: mishapadidar
body: both instances of `boundary` dont compile with a link. This generates the following error when building the documentation:
```
/Users/mpadidar/code/fix_it_week/simsopt/docs/source/mhd.rst:12: WARNING: more than one target found for cross-reference 'boundary': simsopt.mhd.spec.Spec.boundary, simsopt.mhd.vmec.Vmec.boundary, simsopt.mhd.Spec.boundary, simsopt.mhd.Vmec.boundary [ref.python]
```
The fix is to point to a specific `boundary` i.e. `~simsopt.geo.SurfaceRZFourier.boundary`
created_at: 2025-05-08T17:20:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.spec` module.
+
+The equilibrium codes must be installed separately and with python bindings provided by f90wrap. 
path: docs/source/mhd.rst
user: login: mishapadidar
body: Change to something like, 
```
The equilibrium codes must be installed separately with python bindings provided by f90wrap. See the installation [link to the installation page] page for instructions.
```
created_at: 2025-05-08T17:22:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.spec` module.
+
+The equilibrium codes must be installed separately and with python bindings provided by f90wrap. 
+The :obj:`simsopt.mhd` module provides them :obj:`Optimizable` objects that can depend on other :obj:`Optimizable` objects or have other :obj:`Optimizable` objects depend on them.
path: docs/source/mhd.rst
user: login: mishapadidar
body: The `Vmec` and `Spec` classes instantiate the MHD solvers as `Optimizable` objects that can depend on other :obj:`Optimizable` objects,  such as a :obj:`simsopt.mhd.Profile` for the pressure, current and rotational transform profiles, as well as :obj:`simsopt.mhd.Surface` for boundary and internal surfaces
created_at: 2025-05-08T17:25:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.spec` module.
+
+The equilibrium codes must be installed separately and with python bindings provided by f90wrap. 
+The :obj:`simsopt.mhd` module provides them :obj:`Optimizable` objects that can depend on other :obj:`Optimizable` objects or have other :obj:`Optimizable` objects depend on them.
+This includes :obj:`simsopt.mhd.Profile` for the pressure, current and rotational transform profiles, as well as :obj:`simsopt.mhd.Surface` for boundary and internal surfaces. 
path: docs/source/mhd.rst
user: login: mishapadidar
body: This allows for direct handling of profiles parameters, surface degrees of freedom, and solver options through the python class.
created_at: 2025-05-08T17:32:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.spec` module.
+
+The equilibrium codes must be installed separately and with python bindings provided by f90wrap. 
+The :obj:`simsopt.mhd` module provides them :obj:`Optimizable` objects that can depend on other :obj:`Optimizable` objects or have other :obj:`Optimizable` objects depend on them.
+This includes :obj:`simsopt.mhd.Profile` for the pressure, current and rotational transform profiles, as well as :obj:`simsopt.mhd.Surface` for boundary and internal surfaces. 
+
+In fixed boundary mode the equilibrium depends on the boundary (accessed through :obj:`.boundary`), which is a :obj:`SurfaceRZFourier`. 
+In free-boundary mode however, the equilibrium on an external field (which can be provided by any :obj:`simsopt.field.MagneticField`) and its :obj:`boundary` is a child, whose properties (like aspect ratio, volume, etc) can be optimized for, by varying the degrees-of-freedom of the :obj:`MagneticField`(s).  
path: docs/source/mhd.rst
user: login: mishapadidar
body: is free boundary VMEC specific?
created_at: 2025-05-08T17:42:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.spec` module.
+
+The equilibrium codes must be installed separately and with python bindings provided by f90wrap. 
+The :obj:`simsopt.mhd` module provides them :obj:`Optimizable` objects that can depend on other :obj:`Optimizable` objects or have other :obj:`Optimizable` objects depend on them.
+This includes :obj:`simsopt.mhd.Profile` for the pressure, current and rotational transform profiles, as well as :obj:`simsopt.mhd.Surface` for boundary and internal surfaces. 
+
+In fixed boundary mode the equilibrium depends on the boundary (accessed through :obj:`.boundary`), which is a :obj:`SurfaceRZFourier`. 
+In free-boundary mode however, the equilibrium on an external field (which can be provided by any :obj:`simsopt.field.MagneticField`) and its :obj:`boundary` is a child, whose properties (like aspect ratio, volume, etc) can be optimized for, by varying the degrees-of-freedom of the :obj:`MagneticField`(s).  
+One can even combine different solvers such that the equilibrium objects depend on the same :obj:`Surface`, or where one equilibrium solves for the boundary, and metrics related to it are computed using the fixed-boundary
+soltions provided by the other solver. 
+
+.. warning::
+    ``f90wrapped`` code is not re-entrant. You cannot have more than 
+    one :obj:`Vmec` or :obj:`Spec` object active at a time. Code like:
path: docs/source/mhd.rst
user: login: mishapadidar
body: `unless you are using distributed memory like MPI`
created_at: 2025-05-08T17:43:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.spec` module.
+
+The equilibrium codes must be installed separately and with python bindings provided by f90wrap. 
+The :obj:`simsopt.mhd` module provides them :obj:`Optimizable` objects that can depend on other :obj:`Optimizable` objects or have other :obj:`Optimizable` objects depend on them.
+This includes :obj:`simsopt.mhd.Profile` for the pressure, current and rotational transform profiles, as well as :obj:`simsopt.mhd.Surface` for boundary and internal surfaces. 
+
+In fixed boundary mode the equilibrium depends on the boundary (accessed through :obj:`.boundary`), which is a :obj:`SurfaceRZFourier`. 
+In free-boundary mode however, the equilibrium on an external field (which can be provided by any :obj:`simsopt.field.MagneticField`) and its :obj:`boundary` is a child, whose properties (like aspect ratio, volume, etc) can be optimized for, by varying the degrees-of-freedom of the :obj:`MagneticField`(s).  
+One can even combine different solvers such that the equilibrium objects depend on the same :obj:`Surface`, or where one equilibrium solves for the boundary, and metrics related to it are computed using the fixed-boundary
+soltions provided by the other solver. 
+
+.. warning::
+    ``f90wrapped`` code is not re-entrant. You cannot have more than 
+    one :obj:`Vmec` or :obj:`Spec` object active at a time. Code like:
+
+    :: 
+
+        from simsopt.mhd import vmec
+        myvmec1 = vmec.Vmec()
+        myvmec2 = vmec.Vmec()
+
+    will have both objects accessing the same ``Fortran`` memory locations
+    and lead to crashes and undefined behavior.
+
+.. note::
path: docs/source/mhd.rst
user: login: mishapadidar
body: drop the note formatting and include this as part of the main text.
created_at: 2025-05-08T17:44:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.spec` module.
+
+The equilibrium codes must be installed separately and with python bindings provided by f90wrap. 
+The :obj:`simsopt.mhd` module provides them :obj:`Optimizable` objects that can depend on other :obj:`Optimizable` objects or have other :obj:`Optimizable` objects depend on them.
+This includes :obj:`simsopt.mhd.Profile` for the pressure, current and rotational transform profiles, as well as :obj:`simsopt.mhd.Surface` for boundary and internal surfaces. 
+
+In fixed boundary mode the equilibrium depends on the boundary (accessed through :obj:`.boundary`), which is a :obj:`SurfaceRZFourier`. 
+In free-boundary mode however, the equilibrium on an external field (which can be provided by any :obj:`simsopt.field.MagneticField`) and its :obj:`boundary` is a child, whose properties (like aspect ratio, volume, etc) can be optimized for, by varying the degrees-of-freedom of the :obj:`MagneticField`(s).  
+One can even combine different solvers such that the equilibrium objects depend on the same :obj:`Surface`, or where one equilibrium solves for the boundary, and metrics related to it are computed using the fixed-boundary
+soltions provided by the other solver. 
+
+.. warning::
+    ``f90wrapped`` code is not re-entrant. You cannot have more than 
+    one :obj:`Vmec` or :obj:`Spec` object active at a time. Code like:
+
+    :: 
+
+        from simsopt.mhd import vmec
+        myvmec1 = vmec.Vmec()
+        myvmec2 = vmec.Vmec()
+
+    will have both objects accessing the same ``Fortran`` memory locations
+    and lead to crashes and undefined behavior.
+
+.. note::
+    The Evaluating an MHD code is done by their :obj:`.run()` methods. 
+    This is often a computationally expensive operation, and will only 
+    be performed if the :obj:`recompute_bell()` method has been called 
+    (as is done if any of its parents' degrees-of-freedom have changed).
+    If you manually change parameters this not always triggered, and you
+    have to call the :obj:`recompute_bell()` method
+    yourself. 
+
+``VMEC`` and ``SPEC`` do not provide analytical derivatives. 
+As such, optimization can be done using derivative-free methods, or 
+using finite-difference. 
+
+VMEC
+~~~~
+VMEC is one of the most widely used codes for calculating 3d MHD equilibria. 
+As such, it provices a very large number of diagnostics and outputs and has 
+couplings to other codes providing further metrics that can be used in 
+optimisation. 
+VMEC assumes nested flux surfaces. 
+The :obj:`simsopt.mhd.vmec` module provides the interface, and can be instantiated from the same input file as is usually used for running VMEC (an ``input.<name`` or ``wout_<name>.nc`` file): 
+
+Example::
+    from simsopt.mhd import vmec
path: docs/source/mhd.rst
user: login: mishapadidar
body: example code does not compile. 
In addition, if @ejpaul is writing the tutorial, we can probably remove the code from this section.
created_at: 2025-05-08T17:44:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.spec` module.
+
+The equilibrium codes must be installed separately and with python bindings provided by f90wrap. 
+The :obj:`simsopt.mhd` module provides them :obj:`Optimizable` objects that can depend on other :obj:`Optimizable` objects or have other :obj:`Optimizable` objects depend on them.
+This includes :obj:`simsopt.mhd.Profile` for the pressure, current and rotational transform profiles, as well as :obj:`simsopt.mhd.Surface` for boundary and internal surfaces. 
+
+In fixed boundary mode the equilibrium depends on the boundary (accessed through :obj:`.boundary`), which is a :obj:`SurfaceRZFourier`. 
+In free-boundary mode however, the equilibrium on an external field (which can be provided by any :obj:`simsopt.field.MagneticField`) and its :obj:`boundary` is a child, whose properties (like aspect ratio, volume, etc) can be optimized for, by varying the degrees-of-freedom of the :obj:`MagneticField`(s).  
+One can even combine different solvers such that the equilibrium objects depend on the same :obj:`Surface`, or where one equilibrium solves for the boundary, and metrics related to it are computed using the fixed-boundary
+soltions provided by the other solver. 
+
+.. warning::
+    ``f90wrapped`` code is not re-entrant. You cannot have more than 
+    one :obj:`Vmec` or :obj:`Spec` object active at a time. Code like:
+
+    :: 
+
+        from simsopt.mhd import vmec
+        myvmec1 = vmec.Vmec()
+        myvmec2 = vmec.Vmec()
+
+    will have both objects accessing the same ``Fortran`` memory locations
+    and lead to crashes and undefined behavior.
+
+.. note::
+    The Evaluating an MHD code is done by their :obj:`.run()` methods. 
+    This is often a computationally expensive operation, and will only 
+    be performed if the :obj:`recompute_bell()` method has been called 
+    (as is done if any of its parents' degrees-of-freedom have changed).
+    If you manually change parameters this not always triggered, and you
+    have to call the :obj:`recompute_bell()` method
+    yourself. 
+
+``VMEC`` and ``SPEC`` do not provide analytical derivatives. 
+As such, optimization can be done using derivative-free methods, or 
+using finite-difference. 
+
+VMEC
+~~~~
path: docs/source/mhd.rst
user: login: mishapadidar
body: @ejpaul is writing a VMEC Tutorial. We should link users to that.
created_at: 2025-05-08T17:45:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.spec` module.
+
+The equilibrium codes must be installed separately and with python bindings provided by f90wrap. 
+The :obj:`simsopt.mhd` module provides them :obj:`Optimizable` objects that can depend on other :obj:`Optimizable` objects or have other :obj:`Optimizable` objects depend on them.
+This includes :obj:`simsopt.mhd.Profile` for the pressure, current and rotational transform profiles, as well as :obj:`simsopt.mhd.Surface` for boundary and internal surfaces. 
+
+In fixed boundary mode the equilibrium depends on the boundary (accessed through :obj:`.boundary`), which is a :obj:`SurfaceRZFourier`. 
+In free-boundary mode however, the equilibrium on an external field (which can be provided by any :obj:`simsopt.field.MagneticField`) and its :obj:`boundary` is a child, whose properties (like aspect ratio, volume, etc) can be optimized for, by varying the degrees-of-freedom of the :obj:`MagneticField`(s).  
+One can even combine different solvers such that the equilibrium objects depend on the same :obj:`Surface`, or where one equilibrium solves for the boundary, and metrics related to it are computed using the fixed-boundary
path: docs/source/mhd.rst
user: login: mishapadidar
body: something broke with the formatting here, so that we see the following block as code

```MagneticField(s) One can even combine different solvers such that the equilibrium objects depend on the same Surface,```
created_at: 2025-05-08T17:48:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.spec` module.
+
+The equilibrium codes must be installed separately and with python bindings provided by f90wrap. 
+The :obj:`simsopt.mhd` module provides them :obj:`Optimizable` objects that can depend on other :obj:`Optimizable` objects or have other :obj:`Optimizable` objects depend on them.
+This includes :obj:`simsopt.mhd.Profile` for the pressure, current and rotational transform profiles, as well as :obj:`simsopt.mhd.Surface` for boundary and internal surfaces. 
+
+In fixed boundary mode the equilibrium depends on the boundary (accessed through :obj:`.boundary`), which is a :obj:`SurfaceRZFourier`. 
+In free-boundary mode however, the equilibrium on an external field (which can be provided by any :obj:`simsopt.field.MagneticField`) and its :obj:`boundary` is a child, whose properties (like aspect ratio, volume, etc) can be optimized for, by varying the degrees-of-freedom of the :obj:`MagneticField`(s).  
path: docs/source/mhd.rst
user: login: mishapadidar
body: Perhaps say that MHD codes can often be run in two modes: free boundary and fixed boundary.
created_at: 2025-05-08T17:48:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.spec` module.
+
+The equilibrium codes must be installed separately and with python bindings provided by f90wrap. 
+The :obj:`simsopt.mhd` module provides them :obj:`Optimizable` objects that can depend on other :obj:`Optimizable` objects or have other :obj:`Optimizable` objects depend on them.
+This includes :obj:`simsopt.mhd.Profile` for the pressure, current and rotational transform profiles, as well as :obj:`simsopt.mhd.Surface` for boundary and internal surfaces. 
+
+In fixed boundary mode the equilibrium depends on the boundary (accessed through :obj:`.boundary`), which is a :obj:`SurfaceRZFourier`. 
+In free-boundary mode however, the equilibrium on an external field (which can be provided by any :obj:`simsopt.field.MagneticField`) and its :obj:`boundary` is a child, whose properties (like aspect ratio, volume, etc) can be optimized for, by varying the degrees-of-freedom of the :obj:`MagneticField`(s).  
+One can even combine different solvers such that the equilibrium objects depend on the same :obj:`Surface`, or where one equilibrium solves for the boundary, and metrics related to it are computed using the fixed-boundary
+soltions provided by the other solver. 
+
+.. warning::
+    ``f90wrapped`` code is not re-entrant. You cannot have more than 
+    one :obj:`Vmec` or :obj:`Spec` object active at a time. Code like:
+
+    :: 
+
+        from simsopt.mhd import vmec
+        myvmec1 = vmec.Vmec()
+        myvmec2 = vmec.Vmec()
+
+    will have both objects accessing the same ``Fortran`` memory locations
+    and lead to crashes and undefined behavior.
+
+.. note::
+    The Evaluating an MHD code is done by their :obj:`.run()` methods. 
+    This is often a computationally expensive operation, and will only 
+    be performed if the :obj:`recompute_bell()` method has been called 
+    (as is done if any of its parents' degrees-of-freedom have changed).
+    If you manually change parameters this not always triggered, and you
+    have to call the :obj:`recompute_bell()` method
+    yourself. 
+
+``VMEC`` and ``SPEC`` do not provide analytical derivatives. 
path: docs/source/mhd.rst
user: login: mishapadidar
body: Perhaps introduce the idea that these codes can be used for optimization.
created_at: 2025-05-08T17:49:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.spec` module.
+
+The equilibrium codes must be installed separately and with python bindings provided by f90wrap. 
+The :obj:`simsopt.mhd` module provides them :obj:`Optimizable` objects that can depend on other :obj:`Optimizable` objects or have other :obj:`Optimizable` objects depend on them.
+This includes :obj:`simsopt.mhd.Profile` for the pressure, current and rotational transform profiles, as well as :obj:`simsopt.mhd.Surface` for boundary and internal surfaces. 
+
+In fixed boundary mode the equilibrium depends on the boundary (accessed through :obj:`.boundary`), which is a :obj:`SurfaceRZFourier`. 
+In free-boundary mode however, the equilibrium on an external field (which can be provided by any :obj:`simsopt.field.MagneticField`) and its :obj:`boundary` is a child, whose properties (like aspect ratio, volume, etc) can be optimized for, by varying the degrees-of-freedom of the :obj:`MagneticField`(s).  
+One can even combine different solvers such that the equilibrium objects depend on the same :obj:`Surface`, or where one equilibrium solves for the boundary, and metrics related to it are computed using the fixed-boundary
+soltions provided by the other solver. 
+
+.. warning::
+    ``f90wrapped`` code is not re-entrant. You cannot have more than 
+    one :obj:`Vmec` or :obj:`Spec` object active at a time. Code like:
+
+    :: 
+
+        from simsopt.mhd import vmec
+        myvmec1 = vmec.Vmec()
+        myvmec2 = vmec.Vmec()
+
+    will have both objects accessing the same ``Fortran`` memory locations
+    and lead to crashes and undefined behavior.
+
+.. note::
+    The Evaluating an MHD code is done by their :obj:`.run()` methods. 
+    This is often a computationally expensive operation, and will only 
+    be performed if the :obj:`recompute_bell()` method has been called 
+    (as is done if any of its parents' degrees-of-freedom have changed).
+    If you manually change parameters this not always triggered, and you
+    have to call the :obj:`recompute_bell()` method
+    yourself. 
+
+``VMEC`` and ``SPEC`` do not provide analytical derivatives. 
+As such, optimization can be done using derivative-free methods, or 
+using finite-difference. 
+
+VMEC
+~~~~
+VMEC is one of the most widely used codes for calculating 3d MHD equilibria. 
path: docs/source/mhd.rst
user: login: mishapadidar
body: change 3d to 3D or three dimensional
created_at: 2025-05-08T17:50:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.spec` module.
+
+The equilibrium codes must be installed separately and with python bindings provided by f90wrap. 
+The :obj:`simsopt.mhd` module provides them :obj:`Optimizable` objects that can depend on other :obj:`Optimizable` objects or have other :obj:`Optimizable` objects depend on them.
+This includes :obj:`simsopt.mhd.Profile` for the pressure, current and rotational transform profiles, as well as :obj:`simsopt.mhd.Surface` for boundary and internal surfaces. 
+
+In fixed boundary mode the equilibrium depends on the boundary (accessed through :obj:`.boundary`), which is a :obj:`SurfaceRZFourier`. 
+In free-boundary mode however, the equilibrium on an external field (which can be provided by any :obj:`simsopt.field.MagneticField`) and its :obj:`boundary` is a child, whose properties (like aspect ratio, volume, etc) can be optimized for, by varying the degrees-of-freedom of the :obj:`MagneticField`(s).  
+One can even combine different solvers such that the equilibrium objects depend on the same :obj:`Surface`, or where one equilibrium solves for the boundary, and metrics related to it are computed using the fixed-boundary
+soltions provided by the other solver. 
+
+.. warning::
+    ``f90wrapped`` code is not re-entrant. You cannot have more than 
+    one :obj:`Vmec` or :obj:`Spec` object active at a time. Code like:
+
+    :: 
+
+        from simsopt.mhd import vmec
+        myvmec1 = vmec.Vmec()
+        myvmec2 = vmec.Vmec()
+
+    will have both objects accessing the same ``Fortran`` memory locations
+    and lead to crashes and undefined behavior.
+
+.. note::
+    The Evaluating an MHD code is done by their :obj:`.run()` methods. 
+    This is often a computationally expensive operation, and will only 
+    be performed if the :obj:`recompute_bell()` method has been called 
+    (as is done if any of its parents' degrees-of-freedom have changed).
+    If you manually change parameters this not always triggered, and you
+    have to call the :obj:`recompute_bell()` method
+    yourself. 
+
+``VMEC`` and ``SPEC`` do not provide analytical derivatives. 
+As such, optimization can be done using derivative-free methods, or 
+using finite-difference. 
+
+VMEC
+~~~~
+VMEC is one of the most widely used codes for calculating 3d MHD equilibria. 
+As such, it provices a very large number of diagnostics and outputs and has 
+couplings to other codes providing further metrics that can be used in 
+optimisation. 
+VMEC assumes nested flux surfaces. 
+The :obj:`simsopt.mhd.vmec` module provides the interface, and can be instantiated from the same input file as is usually used for running VMEC (an ``input.<name`` or ``wout_<name>.nc`` file): 
+
+Example::
+    from simsopt.mhd import vmec
+    from simsopt.objectives imoprt least_squares_problem
+    from simsopt.solve import least_squares_serial_solve
+
+    myvmec = vmec.Vmec() # reads the default file
+    myvmec.boundary.aspect()  # evaluate attributes of the equilibrium, aspect=10 initially
+
+    prob = least_squares_problem(11, 1, myvmec.aspect) # create an optimization problem
+    least_squares_serial_solve(prob) # solve the least squares problem
+
+
+
+Vmec diagnostics
+^^^^^^^^^^^^^^^^
+
+There are many useful diagnostics available that depend on a :obj:`Vmec` object which provide target functions for optimization. 
+These include:
+- :obj:`QuasisymmetryRatioResidual`: Deviation from quasisymmetry
+- :obj:`IotaTargetMetric`: Difference between the rotational transform and a provided target
+- :obj:`IotaWeighted`: Weighted average of the rotational transorm
+- :obj:`WellWeighted`: Measure for the magnetic well. 
+- :obj:`Quasisymmetry`: Measure of the quasisymmetry using the boozer spectrum.
+- :obj:`VmecRedlBootstrapMismatch`: the mismatch between the Vmec bootstrap and that provided by a recent calculation by Redl (for obtaining self-consistent bootstrap current).
+
+
+
+SPEC
+~~~~~
+
+The Stepped Pressure Equilibrium Code (SPEC) computes equilibria using the Multi-region relaxed MHD (MRxMHD) formulation. 
+This models the plasma equilibrium as a finite number of ideal interfaces between which the magnetic field is relaxed to a force-free solution. 
+The :obj:`simsopt.mhd.spec` module provides the interface, and can be instantiated from the same input file as is usually used for running SPEC (an ``<name>.sp`` file). 
+
+SPEC equilibria can contain magnetic islands and regions of magnetic chaos,
+making it possible to check for and optimize such fieatures. 
+
+All ideal interfaces in spec are available as :obj:`SurfaceRZFourier` objects. 
+
+
+Greenes residue
path: docs/source/mhd.rst
user: login: mishapadidar
body: You could give a description of what quantities could be optimized with SPEC and then point to relevant tutorials or functions. For example, you could say
```Islands in a SPEC equilibrium can be optimized for using Cary and Hansons' method of Greenes residue minimization.```
then point to the Green's residue optimization tutorial.
created_at: 2025-05-08T17:52:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.spec` module.
+
+The equilibrium codes must be installed separately and with python bindings provided by f90wrap. 
+The :obj:`simsopt.mhd` module provides them :obj:`Optimizable` objects that can depend on other :obj:`Optimizable` objects or have other :obj:`Optimizable` objects depend on them.
+This includes :obj:`simsopt.mhd.Profile` for the pressure, current and rotational transform profiles, as well as :obj:`simsopt.mhd.Surface` for boundary and internal surfaces. 
+
+In fixed boundary mode the equilibrium depends on the boundary (accessed through :obj:`.boundary`), which is a :obj:`SurfaceRZFourier`. 
+In free-boundary mode however, the equilibrium on an external field (which can be provided by any :obj:`simsopt.field.MagneticField`) and its :obj:`boundary` is a child, whose properties (like aspect ratio, volume, etc) can be optimized for, by varying the degrees-of-freedom of the :obj:`MagneticField`(s).  
+One can even combine different solvers such that the equilibrium objects depend on the same :obj:`Surface`, or where one equilibrium solves for the boundary, and metrics related to it are computed using the fixed-boundary
+soltions provided by the other solver. 
+
+.. warning::
+    ``f90wrapped`` code is not re-entrant. You cannot have more than 
+    one :obj:`Vmec` or :obj:`Spec` object active at a time. Code like:
+
+    :: 
+
+        from simsopt.mhd import vmec
+        myvmec1 = vmec.Vmec()
+        myvmec2 = vmec.Vmec()
+
+    will have both objects accessing the same ``Fortran`` memory locations
+    and lead to crashes and undefined behavior.
+
+.. note::
+    The Evaluating an MHD code is done by their :obj:`.run()` methods. 
+    This is often a computationally expensive operation, and will only 
+    be performed if the :obj:`recompute_bell()` method has been called 
+    (as is done if any of its parents' degrees-of-freedom have changed).
+    If you manually change parameters this not always triggered, and you
+    have to call the :obj:`recompute_bell()` method
+    yourself. 
+
+``VMEC`` and ``SPEC`` do not provide analytical derivatives. 
+As such, optimization can be done using derivative-free methods, or 
+using finite-difference. 
+
+VMEC
+~~~~
+VMEC is one of the most widely used codes for calculating 3d MHD equilibria. 
+As such, it provices a very large number of diagnostics and outputs and has 
+couplings to other codes providing further metrics that can be used in 
+optimisation. 
+VMEC assumes nested flux surfaces. 
+The :obj:`simsopt.mhd.vmec` module provides the interface, and can be instantiated from the same input file as is usually used for running VMEC (an ``input.<name`` or ``wout_<name>.nc`` file): 
+
+Example::
+    from simsopt.mhd import vmec
+    from simsopt.objectives imoprt least_squares_problem
+    from simsopt.solve import least_squares_serial_solve
+
+    myvmec = vmec.Vmec() # reads the default file
+    myvmec.boundary.aspect()  # evaluate attributes of the equilibrium, aspect=10 initially
+
+    prob = least_squares_problem(11, 1, myvmec.aspect) # create an optimization problem
+    least_squares_serial_solve(prob) # solve the least squares problem
+
+
+
+Vmec diagnostics
+^^^^^^^^^^^^^^^^
+
+There are many useful diagnostics available that depend on a :obj:`Vmec` object which provide target functions for optimization. 
+These include:
+- :obj:`QuasisymmetryRatioResidual`: Deviation from quasisymmetry
+- :obj:`IotaTargetMetric`: Difference between the rotational transform and a provided target
+- :obj:`IotaWeighted`: Weighted average of the rotational transorm
+- :obj:`WellWeighted`: Measure for the magnetic well. 
+- :obj:`Quasisymmetry`: Measure of the quasisymmetry using the boozer spectrum.
+- :obj:`VmecRedlBootstrapMismatch`: the mismatch between the Vmec bootstrap and that provided by a recent calculation by Redl (for obtaining self-consistent bootstrap current).
+
+
+
+SPEC
+~~~~~
+
+The Stepped Pressure Equilibrium Code (SPEC) computes equilibria using the Multi-region relaxed MHD (MRxMHD) formulation. 
+This models the plasma equilibrium as a finite number of ideal interfaces between which the magnetic field is relaxed to a force-free solution. 
+The :obj:`simsopt.mhd.spec` module provides the interface, and can be instantiated from the same input file as is usually used for running SPEC (an ``<name>.sp`` file). 
+
+SPEC equilibria can contain magnetic islands and regions of magnetic chaos,
+making it possible to check for and optimize such fieatures. 
+
+All ideal interfaces in spec are available as :obj:`SurfaceRZFourier` objects. 
+
+
+Greenes residue
+^^^^^^^^^^^^^^^
+
+Islands in a SPEC equilibrium can be optimized for using Cary and Hansons' method of Greenes residue minimization. 
+The fixed points of the islands are found, and their residue is calculated using
+``pyoculus`` through the :obj:`simsopt.mhd.GreenesResidue` that depends on the :obj:`simsopt.mhd.spec.Spec` object, and needs the poloidal and toroidal mode number of the island provided. 
+
+
+Profiles
path: docs/source/mhd.rst
user: login: mishapadidar
body: The discussion of profiles could potentially go before the discussion of specific solvers since profiles are common to all solvers. It would fit nicely with your dicussion of boundaries.
created_at: 2025-05-08T17:54:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.spec` module.
+
+The equilibrium codes must be installed separately and with python bindings provided by f90wrap. 
+The :obj:`simsopt.mhd` module provides them :obj:`Optimizable` objects that can depend on other :obj:`Optimizable` objects or have other :obj:`Optimizable` objects depend on them.
+This includes :obj:`simsopt.mhd.Profile` for the pressure, current and rotational transform profiles, as well as :obj:`simsopt.mhd.Surface` for boundary and internal surfaces. 
+
+In fixed boundary mode the equilibrium depends on the boundary (accessed through :obj:`.boundary`), which is a :obj:`SurfaceRZFourier`. 
+In free-boundary mode however, the equilibrium on an external field (which can be provided by any :obj:`simsopt.field.MagneticField`) and its :obj:`boundary` is a child, whose properties (like aspect ratio, volume, etc) can be optimized for, by varying the degrees-of-freedom of the :obj:`MagneticField`(s).  
+One can even combine different solvers such that the equilibrium objects depend on the same :obj:`Surface`, or where one equilibrium solves for the boundary, and metrics related to it are computed using the fixed-boundary
+soltions provided by the other solver. 
+
+.. warning::
+    ``f90wrapped`` code is not re-entrant. You cannot have more than 
+    one :obj:`Vmec` or :obj:`Spec` object active at a time. Code like:
+
+    :: 
+
+        from simsopt.mhd import vmec
+        myvmec1 = vmec.Vmec()
+        myvmec2 = vmec.Vmec()
+
+    will have both objects accessing the same ``Fortran`` memory locations
+    and lead to crashes and undefined behavior.
+
+.. note::
+    The Evaluating an MHD code is done by their :obj:`.run()` methods. 
+    This is often a computationally expensive operation, and will only 
+    be performed if the :obj:`recompute_bell()` method has been called 
+    (as is done if any of its parents' degrees-of-freedom have changed).
+    If you manually change parameters this not always triggered, and you
+    have to call the :obj:`recompute_bell()` method
+    yourself. 
+
+``VMEC`` and ``SPEC`` do not provide analytical derivatives. 
+As such, optimization can be done using derivative-free methods, or 
+using finite-difference. 
+
+VMEC
+~~~~
+VMEC is one of the most widely used codes for calculating 3d MHD equilibria. 
+As such, it provices a very large number of diagnostics and outputs and has 
+couplings to other codes providing further metrics that can be used in 
+optimisation. 
+VMEC assumes nested flux surfaces. 
+The :obj:`simsopt.mhd.vmec` module provides the interface, and can be instantiated from the same input file as is usually used for running VMEC (an ``input.<name`` or ``wout_<name>.nc`` file): 
+
+Example::
+    from simsopt.mhd import vmec
path: docs/source/mhd.rst
user: login: smiet
body: code removed

created_at: 2025-05-09T15:36:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.spec` module.
+
+The equilibrium codes must be installed separately and with python bindings provided by f90wrap. 
+The :obj:`simsopt.mhd` module provides them :obj:`Optimizable` objects that can depend on other :obj:`Optimizable` objects or have other :obj:`Optimizable` objects depend on them.
+This includes :obj:`simsopt.mhd.Profile` for the pressure, current and rotational transform profiles, as well as :obj:`simsopt.mhd.Surface` for boundary and internal surfaces. 
+
+In fixed boundary mode the equilibrium depends on the boundary (accessed through :obj:`.boundary`), which is a :obj:`SurfaceRZFourier`. 
+In free-boundary mode however, the equilibrium on an external field (which can be provided by any :obj:`simsopt.field.MagneticField`) and its :obj:`boundary` is a child, whose properties (like aspect ratio, volume, etc) can be optimized for, by varying the degrees-of-freedom of the :obj:`MagneticField`(s).  
+One can even combine different solvers such that the equilibrium objects depend on the same :obj:`Surface`, or where one equilibrium solves for the boundary, and metrics related to it are computed using the fixed-boundary
+soltions provided by the other solver. 
+
+.. warning::
+    ``f90wrapped`` code is not re-entrant. You cannot have more than 
+    one :obj:`Vmec` or :obj:`Spec` object active at a time. Code like:
+
+    :: 
+
+        from simsopt.mhd import vmec
+        myvmec1 = vmec.Vmec()
+        myvmec2 = vmec.Vmec()
+
+    will have both objects accessing the same ``Fortran`` memory locations
+    and lead to crashes and undefined behavior.
+
+.. note::
+    The Evaluating an MHD code is done by their :obj:`.run()` methods. 
+    This is often a computationally expensive operation, and will only 
+    be performed if the :obj:`recompute_bell()` method has been called 
+    (as is done if any of its parents' degrees-of-freedom have changed).
+    If you manually change parameters this not always triggered, and you
+    have to call the :obj:`recompute_bell()` method
+    yourself. 
+
+``VMEC`` and ``SPEC`` do not provide analytical derivatives. 
+As such, optimization can be done using derivative-free methods, or 
+using finite-difference. 
+
+VMEC
+~~~~
path: docs/source/mhd.rst
user: login: smiet
body: reference added (but will not edit @ejpaul 's code, could you add `.. running-vmec:` to the top of example_vmec.rst?
created_at: 2025-05-09T15:39:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.spec` module.
+
+The equilibrium codes must be installed separately and with python bindings provided by f90wrap. 
+The :obj:`simsopt.mhd` module provides them :obj:`Optimizable` objects that can depend on other :obj:`Optimizable` objects or have other :obj:`Optimizable` objects depend on them.
+This includes :obj:`simsopt.mhd.Profile` for the pressure, current and rotational transform profiles, as well as :obj:`simsopt.mhd.Surface` for boundary and internal surfaces. 
+
+In fixed boundary mode the equilibrium depends on the boundary (accessed through :obj:`.boundary`), which is a :obj:`SurfaceRZFourier`. 
+In free-boundary mode however, the equilibrium on an external field (which can be provided by any :obj:`simsopt.field.MagneticField`) and its :obj:`boundary` is a child, whose properties (like aspect ratio, volume, etc) can be optimized for, by varying the degrees-of-freedom of the :obj:`MagneticField`(s).  
+One can even combine different solvers such that the equilibrium objects depend on the same :obj:`Surface`, or where one equilibrium solves for the boundary, and metrics related to it are computed using the fixed-boundary
+soltions provided by the other solver. 
+
+.. warning::
+    ``f90wrapped`` code is not re-entrant. You cannot have more than 
+    one :obj:`Vmec` or :obj:`Spec` object active at a time. Code like:
+
+    :: 
+
+        from simsopt.mhd import vmec
+        myvmec1 = vmec.Vmec()
+        myvmec2 = vmec.Vmec()
+
+    will have both objects accessing the same ``Fortran`` memory locations
+    and lead to crashes and undefined behavior.
+
+.. note::
+    The Evaluating an MHD code is done by their :obj:`.run()` methods. 
+    This is often a computationally expensive operation, and will only 
+    be performed if the :obj:`recompute_bell()` method has been called 
+    (as is done if any of its parents' degrees-of-freedom have changed).
+    If you manually change parameters this not always triggered, and you
+    have to call the :obj:`recompute_bell()` method
+    yourself. 
+
+``VMEC`` and ``SPEC`` do not provide analytical derivatives. 
+As such, optimization can be done using derivative-free methods, or 
+using finite-difference. 
+
+VMEC
+~~~~
+VMEC is one of the most widely used codes for calculating 3d MHD equilibria. 
path: docs/source/mhd.rst
user: login: smiet
body: done
created_at: 2025-05-09T15:41:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,109 @@
+Magnetohydrodynamic codes
+-------------------------
+
+the :obj:`simsopt.mhd` module contains a collection of interfaces to Magnetohydrodynamic codes. 
+Currently it contains an interface to the Variational Moments Equilibrium Code (VMEC) through the :obj:`simsopt.mhd.vmec` module and an interface to the Stepped
+Pressure Equilibrium Code (SPEC) through the :obj:`simsopt.mhd.spec` module.
+
+The equilibrium codes must be installed separately and with python bindings provided by f90wrap. 
+The :obj:`simsopt.mhd` module provides them :obj:`Optimizable` objects that can depend on other :obj:`Optimizable` objects or have other :obj:`Optimizable` objects depend on them.
+This includes :obj:`simsopt.mhd.Profile` for the pressure, current and rotational transform profiles, as well as :obj:`simsopt.mhd.Surface` for boundary and internal surfaces. 
+
+In fixed boundary mode the equilibrium depends on the boundary (accessed through :obj:`.boundary`), which is a :obj:`SurfaceRZFourier`. 
+In free-boundary mode however, the equilibrium on an external field (which can be provided by any :obj:`simsopt.field.MagneticField`) and its :obj:`boundary` is a child, whose properties (like aspect ratio, volume, etc) can be optimized for, by varying the degrees-of-freedom of the :obj:`MagneticField`(s).  
+One can even combine different solvers such that the equilibrium objects depend on the same :obj:`Surface`, or where one equilibrium solves for the boundary, and metrics related to it are computed using the fixed-boundary
+soltions provided by the other solver. 
+
+.. warning::
+    ``f90wrapped`` code is not re-entrant. You cannot have more than 
+    one :obj:`Vmec` or :obj:`Spec` object active at a time. Code like:
+
+    :: 
+
+        from simsopt.mhd import vmec
+        myvmec1 = vmec.Vmec()
+        myvmec2 = vmec.Vmec()
+
+    will have both objects accessing the same ``Fortran`` memory locations
+    and lead to crashes and undefined behavior.
+
+.. note::
+    The Evaluating an MHD code is done by their :obj:`.run()` methods. 
+    This is often a computationally expensive operation, and will only 
+    be performed if the :obj:`recompute_bell()` method has been called 
+    (as is done if any of its parents' degrees-of-freedom have changed).
+    If you manually change parameters this not always triggered, and you
+    have to call the :obj:`recompute_bell()` method
+    yourself. 
+
+``VMEC`` and ``SPEC`` do not provide analytical derivatives. 
+As such, optimization can be done using derivative-free methods, or 
+using finite-difference. 
+
+VMEC
+~~~~
+VMEC is one of the most widely used codes for calculating 3d MHD equilibria. 
+As such, it provices a very large number of diagnostics and outputs and has 
+couplings to other codes providing further metrics that can be used in 
+optimisation. 
+VMEC assumes nested flux surfaces. 
+The :obj:`simsopt.mhd.vmec` module provides the interface, and can be instantiated from the same input file as is usually used for running VMEC (an ``input.<name`` or ``wout_<name>.nc`` file): 
+
+Example::
+    from simsopt.mhd import vmec
+    from simsopt.objectives imoprt least_squares_problem
+    from simsopt.solve import least_squares_serial_solve
+
+    myvmec = vmec.Vmec() # reads the default file
+    myvmec.boundary.aspect()  # evaluate attributes of the equilibrium, aspect=10 initially
+
+    prob = least_squares_problem(11, 1, myvmec.aspect) # create an optimization problem
+    least_squares_serial_solve(prob) # solve the least squares problem
+
+
+
+Vmec diagnostics
+^^^^^^^^^^^^^^^^
+
+There are many useful diagnostics available that depend on a :obj:`Vmec` object which provide target functions for optimization. 
+These include:
+- :obj:`QuasisymmetryRatioResidual`: Deviation from quasisymmetry
+- :obj:`IotaTargetMetric`: Difference between the rotational transform and a provided target
+- :obj:`IotaWeighted`: Weighted average of the rotational transorm
+- :obj:`WellWeighted`: Measure for the magnetic well. 
+- :obj:`Quasisymmetry`: Measure of the quasisymmetry using the boozer spectrum.
+- :obj:`VmecRedlBootstrapMismatch`: the mismatch between the Vmec bootstrap and that provided by a recent calculation by Redl (for obtaining self-consistent bootstrap current).
+
+
+
+SPEC
+~~~~~
+
+The Stepped Pressure Equilibrium Code (SPEC) computes equilibria using the Multi-region relaxed MHD (MRxMHD) formulation. 
+This models the plasma equilibrium as a finite number of ideal interfaces between which the magnetic field is relaxed to a force-free solution. 
+The :obj:`simsopt.mhd.spec` module provides the interface, and can be instantiated from the same input file as is usually used for running SPEC (an ``<name>.sp`` file). 
+
+SPEC equilibria can contain magnetic islands and regions of magnetic chaos,
+making it possible to check for and optimize such fieatures. 
+
+All ideal interfaces in spec are available as :obj:`SurfaceRZFourier` objects. 
+
+
+Greenes residue
+^^^^^^^^^^^^^^^
+
+Islands in a SPEC equilibrium can be optimized for using Cary and Hansons' method of Greenes residue minimization. 
+The fixed points of the islands are found, and their residue is calculated using
+``pyoculus`` through the :obj:`simsopt.mhd.GreenesResidue` that depends on the :obj:`simsopt.mhd.spec.Spec` object, and needs the poloidal and toroidal mode number of the island provided. 
+
+
+Profiles
path: docs/source/mhd.rst
user: login: smiet
body: moved to top and linking to @ejpaul  VMEC tutorial 
created_at: 2025-05-09T15:43:05Z
author_association: CONTRIBUTOR

PR comments:
user: login: codecov[bot]
created_at: 2025-05-08T09:26:39Z
body: ## [Codecov](https://app.codecov.io/gh/hiddenSymmetries/simsopt/pull/508?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiddenSymmetries) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 91.98%. Comparing base [(`f0f0ec4`)](https://app.codecov.io/gh/hiddenSymmetries/simsopt/commit/f0f0ec45fe518e46567f28c9912a505dcdc5ec0f?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiddenSymmetries) to head [(`d399e87`)](https://app.codecov.io/gh/hiddenSymmetries/simsopt/commit/d399e870a0fec5857d6e926cbbc35a18231f1736?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiddenSymmetries).
> Report is 19 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #508      +/-   ##
==========================================
- Coverage   92.25%   91.98%   -0.28%     
==========================================
  Files          82       82              
  Lines       16015    16015              
==========================================
- Hits        14775    14731      -44     
- Misses       1240     1284      +44     
```

| [Flag](https://app.codecov.io/gh/hiddenSymmetries/simsopt/pull/508/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiddenSymmetries) | Coverage Δ | |
|---|---|---|
| [unittests](https://app.codecov.io/gh/hiddenSymmetries/simsopt/pull/508/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiddenSymmetries) | `91.98% <ø> (-0.28%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiddenSymmetries#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/hiddenSymmetries/simsopt/pull/508?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiddenSymmetries).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiddenSymmetries).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: smiet
created_at: 2025-05-09T15:46:03Z
body: @mishapadidar  Just addressed all your points. Thanks for your time! 
author_association: CONTRIBUTOR
user: login: mbkumar
created_at: 2025-05-09T16:14:49Z
body: The generated doc is at https://simsopt.readthedocs.io/fixit_spec_documentation/mhd.html
@smiet The document needs a revision. Please fix the VMEC and SPEC links so they are not explicit. Additionally check "simsopt.geo.SurfaceRZFourier`", which is hard to catch. Instead of saying "f90wrapped code is not re-entrant.", I'd say "f90wrap generates singleton classes". Please go through the docs one more time and fix small typos.
author_association: COLLABORATOR
user: login: smiet
created_at: 2025-05-13T15:25:21Z
body: @mbkumar Should have addressed your issues now, sorry first time working with the .rst format. 
author_association: CONTRIBUTOR