diff --git a/.gitignore b/.gitignore index abd406ae..0109c4b2 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ docs/_build/ docs/examples/*/gal_solution.dat docs/examples/*/delta_gw.dat docs/examples/*/gal_solution_cut.dat +docs/examples/DIIID_ideal_example/*.py deps/bin/* deps/hdf5 @@ -53,3 +54,16 @@ sum/sum xdraw/pspack xdraw/xdraw slayer/slayer + +*.aux +*.blg +*.bbl +*.fdb_latexmk +*.fls +*.log +*.synctex.gz +*.pdf +*.png + +# keep the figure included by docs/tex/recon/main.tex +!docs/tex/recon/QvsC_quant_n1.png diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 428772c6..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1,264 +0,0 @@ -# GPEC - Generalized Perturbed Equilibrium Code - -## Overview - -GPEC is a comprehensive suite of nonaxisymmetric stability and perturbed equilibrium codes for tokamak plasma analysis. It calculates stability of tokamak plasmas to nonaxisymmetric modes and performs nonaxisymmetric force balance calculations for stable configurations. - -## Primary Languages - -- **Fortran 90** (primary) - Core physics codes with modern features (modules, derived types, dynamic allocation) -- **Python** - PyPEC package for postprocessing, running simulations, and visualization - -## Key Components/Codes - -| Code | Purpose | -|------|---------| -| **DCON** | Direct Criterion of Newcomb - ideal MHD stability analysis | -| **STRIDE** | Optimized DCON with parallel computation support | -| **GPEC** | Generalized Perturbed Equilibrium Code - adds forcing terms from external 3D fields | -| **RDCON** | Resistive DCON - resistive MHD stability | -| **PENTRC** | Kinetic effects analysis (torque matrix calculations) | -| **RMATCH** | Resistive matching for inner-layer solutions | -| **ORBIT** | Full-orbit charged particle tracking | -| **VACUUM** | Vacuum response calculations | -| **SLAYER** | Slab layer model based on linear drift MHD | - -## Directory Structure - -``` -gpec/ -├── bin/ # Compiled executables -├── coil/ # Coil geometry and magnetic field calculations -├── dcon/ # Direct Criterion of Newcomb (ideal MHD) -├── equil/ # Equilibrium data processing (EFIT, CHEASE, Miller, etc.) -├── gpec/ # Main GPEC code for 3D perturbations -├── docs/ # Sphinx documentation and examples -├── input/ # Template input namelists -├── install/ # Build configuration and Makefile -├── lib/ # Compiled library archives -├── lsode/ # LSODE ODE solver -├── match/ # Resistive matching -├── multi/ # Multiple run processing -├── orbit/ # Particle orbit tracking -├── pentrc/ # Kinetic effects/torque matrix -├── pypec/ # Python analysis package -├── rdcon/ # Resistive DCON -├── rmatch/ # Resistive matching extended -├── slayer/ # Slab layer MHD model -├── stride/ # STRIDE stability code -├── sum/ # Data extraction from MULTI runs -├── vacuum/ # Vacuum response calculations -├── xdraw/ # X11 graphics visualization -├── zlange/ # Complex linear algebra -└── zvode/ # Complex ODE solver -``` - -## Building - -```bash -cd install -make [FFLAGS="flags"] [CC="compiler"] [FC="fortran_compiler"] -``` - -### Dependencies -- Fortran 90 compiler (ifort, gfortran, pgfortran) -- LAPACK/BLAS (or Intel MKL) -- NetCDF (Fortran and C) -- X11 (for XDRAW graphics) - -### Supported Platforms -- PPPL (portal.pppl.gov) -- GA (iris.gat.com) -- NERSC Perlmutter -- macOS (Intel and ARM with gfortran) -- Linux (gfortran, Ubuntu/WSL) - -## Key Input Files (Fortran Namelists) - -| File | Purpose | -|------|---------| -| `equil.in` | Equilibrium control (format, grid, coordinates) | -| `dcon.in` | DCON/STRIDE stability analysis parameters | -| `vac.in` | Vacuum response calculation settings | -| `gpec.in` | Nonaxisymmetric perturbation configuration | -| `rdcon.in` | Resistive analysis parameters | -| `pentrc.in` | Kinetic torque calculations | -| `coil.in` | Coil geometry specifications | - -## Equilibrium Formats Supported - -- EFIT (GA code) - g-files -- CHEASE (Lausanne) -- Miller (inverse) -- TRANSP data -- JSOLVER -- Analytical (LAR, Soloviev) - -## Coordinate Systems - -- Hamada (default) -- PEST -- Boozer -- Equal-arc - -## PyPEC (Python Package) - -Located in `pypec/` directory: -- `gpec.py` - Main runner wrapper -- `data.py` - Data file reading -- `namelist.py` - Fortran namelist handling -- `modplot.py` - Plotting utilities -- `gui.py` - GUI interface (Enthought Traits-based) -- `synthetics.py` - Synthetic data generation - -## Common Workflows - -### Running DCON/STRIDE (Ideal MHD Stability) -1. Prepare equilibrium file and `equil.in` -2. Configure `dcon.in` with mode numbers and flags -3. Run `dcon` or `stride` executable -4. View results with `xdraw` or analyze NetCDF outputs - -### Running GPEC (3D Perturbations) -1. First run DCON/STRIDE to generate `euler.bin` -2. Configure `gpec.in` with 3D field source (coil, harmonic, or data file) -3. Run `gpec` executable -4. Analyze outputs for torque, stability metrics - -### Running Resistive Analysis -1. Run DCON/STRIDE first -2. Configure `rdcon.in` -3. Run `rdcon`, then `rmatch` if needed - -## Key Output Files - -- `*.bin` - Binary files viewable with XDRAW -- `*.nc` - NetCDF files for analysis -- `euler.bin` - Orthonormal basis from DCON (input to GPEC) -- `globalsol.bin` - Resistive solution from RMATCH - -## Module System Usage - -```bash -module load gpec # Default release -module load gpec/0.0 # Development branch -``` - -Sets `$GPECHOME` environment variable. - -## Documentation - -Sphinx documentation in `docs/`: -- `docs/index.rst` - Main documentation index -- `docs/outputs.rst` - Output specification guide -- `docs/releases.rst` - Version history -- `docs/examples/` - Example cases and tutorials - -Build docs: `cd docs && make html` - -## Physics Features - -- Mercier criterion (D_I, D_R) for interchange modes -- Ballooning criterion (C_A) for ideal MHD -- Newcomb criterion for low-n modes -- Plasma, vacuum, and total response matrices -- Resistive instability detection -- Kinetic effects (torque matrices) -- Full-orbit and guiding-center particle tracking - - - -## Claude Coding Guidelines for GPEC - -### Fortran Fixed-Format (.f files) Requirements - -**CRITICAL: Line Width Limit** -- Fixed-format Fortran files (.f, NOT .f90) have a **72-character line limit** -- Lines exceeding 72 characters will cause compilation errors or be silently truncated -- **ALWAYS** check line length before making edits to .f files - -#### Line Format Rules -- Columns 1-5: Statement labels (optional) -- Column 6: Continuation character (use `$` or `&` for continuation lines) -- Columns 7-72: Fortran statements -- Columns 73-80: Ignored (historically used for sequence numbers) - -#### Continuation Lines -When a statement exceeds 72 characters, split it across multiple lines: -```fortran - variable_name = very_long_expression_that_would_exceed_limit - $ + more_of_the_expression -``` - -#### String Splitting -For long strings, use Fortran string concatenation: -```fortran - WRITE(*,*) "This is a very long warning message that "// - $ "needs to be split across multiple lines" -``` - -### Type Precision - -**REAL Variables:** -- Use `0.0_r8` instead of `0.0` for REAL(r8) variables -- Use `1.0_r8` instead of `1.0` for REAL(r8) variables -- Type suffix `_r8` ensures proper precision matching - -### Variable Naming Conventions - -Use consistent naming patterns within subroutines: -- `hw_*` prefix for half-widths (e.g., `hw_isl`, `hw_v`, `hw_v_crit`, `hw_sat`, `hw_min`) -- Avoid mixing naming styles within the same subroutine - -### Safety Checks - -**Array Access:** -- Always check if allocatable arrays are ALLOCATED before accessing them: -```fortran - IF (ALLOCATED(array_name)) THEN - ! Use array - ELSE - ! Handle unallocated case - ENDIF -``` - -**Division:** -- Guard against division by zero for potentially small values -- Check bounds for ACOS, ASIN arguments (must be in [-1, 1]) -- Check that square root arguments are non-negative - -### Before Committing - -**Pre-commit Checklist:** -1. Check all modified .f files for lines exceeding 72 characters -2. Verify type suffixes match variable declarations (_r8 for REAL(r8)) -3. Ensure continuation lines use proper column 6 markers ($) -4. Test compilation before committing -5. Ask user permission before committing changes - -### Verification Commands - -Check for lines exceeding 72 characters: -```bash -awk 'length > 72 {print NR": "length" chars"}' filename.f -``` - -Count characters in a specific line: -```bash -sed -n 'NUMp' filename.f | wc -c -``` - -## Sign Conventions - -GPEC uses right-handed magnetic coordinates (psi, theta, zeta) with Fourier -decomposition exp(im*theta - in*phi). A comprehensive reference is in -`docs/sign_conventions.rst`. Key points: - -- **helicity** = ipd * btd (+1 = RH, -1 = LH), computed in `gpec_main` (`gpec/gpec.f`) -- **nn** (toroidal mode number) is always positive; resonant **m** is always positive -- **F = R*Bt** is forced positive via ABS() in `read_eq_efit` (`equil/read_eq.f`) -- **q** is recomputed by field-line integration (`direct_run` in `equil/direct.f`; `inverse_run` in `equil/inverse.f` for inverse formats); the input file's q profile is unused, and the recomputed q is always positive -- **omega_E** is positive for rotation in the direction of the toroidal coordinate zeta -- The code does NOT use the COCOS standard -- For SURFMN interface: `m_surfmn = helicity * m_gpec` -- For real-space output: RH configs take complex conjugate (`-helicity*AIMAG(...)` in `gpec/gpout.f`) diff --git a/docs/examples/DIIID_ideal_example/gpec.in b/docs/examples/DIIID_ideal_example/gpec.in index ece94a5c..abeb532b 100644 --- a/docs/examples/DIIID_ideal_example/gpec.in +++ b/docs/examples/DIIID_ideal_example/gpec.in @@ -87,4 +87,4 @@ &GPEC_DIAGNOSE timeit=t ! Print timer splits for major subroutines radvar_flag=f ! Map various radial variables (rho,psi_tor) on psi_n grid -/ +/ \ No newline at end of file diff --git a/docs/examples/solovev_ideal_example/gpec.in b/docs/examples/solovev_ideal_example/gpec.in index 282fb4e1..40686d29 100644 --- a/docs/examples/solovev_ideal_example/gpec.in +++ b/docs/examples/solovev_ideal_example/gpec.in @@ -1,13 +1,13 @@ &GPEC_INPUT - dcon_dir = './' ! Path to DCON outputs. Individual file paths are appended to this + dcon_dir = '' ! Path to DCON outputs. Individual file paths are appended to this idconfile = 'euler.bin' ! Eigenfunctions output by DCON run ieqfile = 'psi_in.bin' ! Flux surfaces output by DCON run rdconfile = 'globalsol.bin' ! Galerkin eigenfunctions output by resistive DCON run gal_flag = f ! Use the Galerkin eigenfunctions from resistive DCON instead of the ideal DCON shooting method solutions - jac_in="pest" ! Coordinate system of external perturbation (affects data_flag and harmonic_flag). Options include: pest,boozer,hamada - jsurf_in=0 ! True(1) if external perturbation is area weighted (i.e. flux) + jac_in="hamada" ! Coordinate system of external perturbation (affects data_flag and harmonic_flag). Options include: pest,boozer,hamada + jsurf_in=1 ! True(1) if external perturbation is area weighted (i.e. flux) tmag_in =0 ! True(1) if external perturbation has toroidal angle defined by jac_in. False(0) if machine angle. mthsurf =0 ! Number of poloidal angle gridpoints. Minimum of twice the Nyquist condition from DCON m limits is enforced. @@ -28,7 +28,7 @@ displacement_flag=f ! Perturbations are displacement (m). Default is field (T). fixed_boundary_flag=f ! Total perturbation = external perturbation - mode_flag = t ! Return only a single DCON eigenfunction defined by mode (ignores external pert.) + mode_flag = .T. ! Return only a single DCON eigenfunction defined by mode (ignores external pert.) mode = 1 ! DCON eigen function index (1 is least stable) filter_types = 'w' ! Isolate the filter_modes of energy (w), reluctance (r), permiability (p) or singular coupling (s) eigenvectors/singular-vectors filter_modes = 0 ! Number of modes isolated, negative values isolate backwards from the last mode @@ -47,28 +47,29 @@ use_classic_splines = f ! Use a classical cubic spline instead of tri-diagonal solution for splines with extrapolation boundary conditions / &GPEC_OUTPUT - jac_out="pest" ! Coordinate system of outputs + jac_out="hamada" ! Coordinate system of outputs jsurf_out=0 ! True(1) for area weighted outputs (only appends standard gpec_control output) tmag_out=1 ! True(1) for magnetic toroidal angle outputs (affects singcoup and singfld, appends gpec_control) mlim_out=64 ! If positive, Fourier decomposed outputs will span -mlim_out<=m<=mlim_out. Default (<=0) outputs on internal m range. - resp_flag=t ! Output energy, reluctance, inductance, and permeability eigenvalues, eigenvectors, and matrices - filter_flag=t ! Outputs energy, reluctance, permeability, and (optionally) the singular-coupling eigenmodes on the control surface + resp_flag=f ! Output energy, reluctance, inductance, and permeability eigenvalues, eigenvectors, and matrices + filter_flag=f ! Outputs energy, reluctance, pereability, and (optionally) the singular-coupling eigenmodes on the control surface singcoup_flag=f ! Calculate coupling of each m to resonant surfaces - singfld_flag=t ! Output of resonant surface quantities (flux,current,etc.) + singfld_flag=f ! Output of resonant surface quantities (flux,current,etc.) vsingfld_flag=f ! Output of vacuum resonant surface quantities + singthresh_flag=f ! Calculate Callen resonant field penetration threshold (requires kinetic profiles from pentrc.in) pmodb_flag=f ! Outputs delta-B_Lagrangian - xbnormal_flag=t ! Outputs normal displacement and field in plasma + xbnormal_flag=f ! Outputs normal displacement and field in plasma vbnormal_flag=f ! Outputs vacuum normal displacement and field in plasma - xclebsch_flag=t ! Outputs clebsch coordinate displacement for PENT + xclebsch_flag=f ! Outputs clebsch coordinate displacement for PENT dw_flag=f ! Outputs self-consistent energy and torque profiles (when kin_flag=t from DCON) eqbrzphi_flag=f ! Outputs equilibrium field on regular (r,z) grid - brzphi_flag=t ! Outputs perturbed field on regular (r,z) grid + brzphi_flag=f ! Outputs perturbed field on regular (r,z) grid xrzphi_flag=f ! Outputs displacement on regular (r,z) grid vbrzphi_flag=f ! Outputs field on regular (r,z) grid due to total boundary surface current (not a real field) - vvbrzphi_flag=f ! Outputs field on regular (r,z) grid due to external perturbation surface current (not a real field) + vvbrzphi_flag=f ! Outputs field on regular (r,z) grid due to exeternal perturbation surface current (not a real field) vsbrzphi_flag=f ! Outputs the field on regular (r,z) grid due to singular surface currents ss_flag(3)=f ! Outputs the field on regular (r,z) grid due to the 3rd singular surface current, etc. xbrzphifun_flag=f ! Outputs r,z, and perturbation vectors on psi,theta grid @@ -82,8 +83,11 @@ bin_2d_flag=f ! Write 2D binary outputs for use with xdraw max_linesout=0 ! Maximum length of ascii data tables enforced by increasing radial step size (iff .gt. 0). verbose=t ! Print run log to terminal + + recon_flag=t + netcdf_flag=t / &GPEC_DIAGNOSE - timeit=f ! Print timer splits for major subroutines + timeit=t ! Print timer splits for major subroutines radvar_flag=f ! Map various radial variables (rho,psi_tor) on psi_n grid / diff --git a/docs/examples/solovev_ideal_example/plot_K.py b/docs/examples/solovev_ideal_example/plot_K.py new file mode 100644 index 00000000..39e046ae --- /dev/null +++ b/docs/examples/solovev_ideal_example/plot_K.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +""" +Plot K diagnostics on the r-z plane. + +Running this script produces two figures: +1. gpec_recon_k_sol1.png +2. gpec_recon_k-term_sol1.png +""" + +import glob +import os +import re +import sys + +import matplotlib.pyplot as plt +import numpy as np +from matplotlib.colors import TwoSlopeNorm + + +def find_required_file(pattern): + """Find a reconstruction output file by wildcard pattern.""" + matches = sorted(glob.glob(pattern)) + if not matches: + print(f"Error: Could not find {pattern}") + sys.exit(1) + return matches[0] + + +def extract_suffix(path): + """Extract the solution suffix such as sol1 from the filename.""" + name = os.path.basename(path) + match = re.search(r"(sol\d+)\.out$", name) + if not match: + print(f"Error: Could not extract solution suffix from {name}") + sys.exit(1) + return match.group(1) + + +def make_norm(values): + """Build a diverging normalization centered at zero.""" + vmin_temp, vmax_temp = np.percentile(values, [2, 98]) + vmax = max(abs(vmin_temp), abs(vmax_temp)) + return TwoSlopeNorm(vmin=-vmax, vcenter=0.0, vmax=vmax) + + +def style_axis(ax, title): + """Apply common axis styling.""" + ax.set_xlabel("R (m)", fontsize=11) + ax.set_ylabel("Z (m)", fontsize=11) + ax.set_title(title, fontsize=12, fontweight="bold") + ax.grid(True, alpha=0.3) + ax.set_aspect("equal") + + +def scatter_panel(ax, r, z, values, title, cbar_label): + """Create a single scatter panel with symmetric color scaling.""" + scatter = ax.scatter( + r, + z, + c=values, + cmap="RdBu_r", + s=20, + alpha=0.6, + norm=make_norm(values), + ) + style_axis(ax, title) + cbar = plt.colorbar(scatter, ax=ax) + cbar.set_label(cbar_label, fontsize=10) + + +def main(): + """Main plotting routine.""" + print("=" * 70) + print("GPEC K Diagnostic Plotter") + print("=" * 70) + + k_file = find_required_file("gpec_recon_k_sol*.out") + suffix = extract_suffix(k_file) + + print(f"Reading K data from: {k_file}") + + k_data = np.loadtxt(k_file, skiprows=1) + + r = k_data[:, 2] + z = k_data[:, 3] + + k_total = k_data[:, 4] + t1 = k_data[:, 6] + t2 = k_data[:, 7] + t3 = k_data[:, 8] + + # Figure 1: total K + fig_k, ax_k = plt.subplots(figsize=(7, 8)) + scatter_panel(ax_k, r, z, k_total, "K (Total)", "K") + plt.tight_layout() + k_png = f"gpec_recon_k_{suffix}.png" + fig_k.savefig(k_png, dpi=150, bbox_inches="tight") + + # Figure 2: three terms in one row + fig_terms, axes = plt.subplots(1, 3, figsize=(18, 5.5)) + term_specs = [ + (t1, r"Term1: $|\nabla\psi_{\mathrm{dcon}}|^2 \sigma S_{\mathrm{dcon}}$", "T1"), + (t2, r"Term2: $B^2 \sigma^2$", "T2"), + (t3, r"Term3: $2 P' \kappa^\psi$", "T3"), + ] + + for ax, (values, title, label) in zip(axes, term_specs): + scatter_panel(ax, r, z, values, title, label) + + plt.tight_layout() + terms_png = f"gpec_recon_k-term_{suffix}.png" + fig_terms.savefig(terms_png, dpi=150, bbox_inches="tight") + + print("Saved:") + print(f" {k_png}") + print(f" {terms_png}") + + +if __name__ == "__main__": + main() diff --git a/docs/tex/recon/QvsC_quant_n1.png b/docs/tex/recon/QvsC_quant_n1.png new file mode 100644 index 00000000..bb697a4b Binary files /dev/null and b/docs/tex/recon/QvsC_quant_n1.png differ diff --git a/docs/tex/recon/main.tex b/docs/tex/recon/main.tex new file mode 100644 index 00000000..fd717c46 --- /dev/null +++ b/docs/tex/recon/main.tex @@ -0,0 +1,3906 @@ +\documentclass[10pt,a4paper]{article} + +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{lmodern} +\usepackage{geometry} +\geometry{margin=1in} +\usepackage{graphicx} +\usepackage{float} +\usepackage{amsmath, amssymb} +\usepackage{array} +\usepackage{hyperref} +\usepackage{cancel} +\usepackage{color} +\usepackage{pdflscape} +\numberwithin{equation}{section} + +\title{DCON \texttt{recon\_flag} document} +\author{Sunjae Lee} +\date{\today} + +\begin{document} + +\maketitle + +% \begin{abstract} +% This is the abstract of the document. Briefly describe the purpose and content here. +% \end{abstract} + +\section{Definition of covariant and contravariant basis} +The dcon context will be in black and \color{blue}the reference\cite{chance1992mhd} would be in blue. +\color{black}\subsection{Jacobian-scaled basis $(\vec{v}_i = \vec{e}_i/\mathcal{J})$} + +The $\vec{v}_i$ are \emph{not} the true covariant tangent basis $\vec{e}_i \equiv \partial\vec{r}/\partial q^i$, but rather its Jacobian-scaled counterpart: +\begin{equation} + \vec{v}_i = \frac{\vec{e}_i}{\mathcal{J}}, \qquad + \mathcal{J}^{-1} \equiv \vec{\nabla}\psi\cdot(\vec{\nabla}\theta\times\vec{\nabla}\zeta). +\end{equation} +Explicitly, +\begin{equation} + \begin{aligned} + \vec{v}_1 &= \vec{\nabla}\theta \times \vec{\nabla}\zeta, & + \vec{v}_2 &= \vec{\nabla}\zeta \times \vec{\nabla}\psi, & + \vec{v}_3 &= \vec{\nabla}\psi \times \vec{\nabla}\theta. + \end{aligned} +\end{equation} + +\begin{equation} + \vec{v}_i = v_{ij}\hat{e}_j. +\end{equation} + + +\begin{equation} +\begin{aligned} +v_{11} &= \vec{v}_1 \cdot \hat{e}_1 = \frac{1}{2r\mathcal{J}}\frac{\partial r^2}{\partial \psi}, \\[6pt] +v_{12} &= \vec{v}_1 \cdot \hat{e}_2 = \frac{2\pi r}{\mathcal{J}}\frac{\partial \eta}{\partial \psi}, \\[6pt] +v_{13} &= \vec{v}_1 \cdot \hat{e}_3 = \frac{R}{\mathcal{J}}\frac{\partial \phi}{\partial \psi}, \\[6pt] +\end{aligned} +\end{equation} + +\begin{equation} +\begin{aligned} +v_{21} &= \vec{v}_2 \cdot \hat{e}_1 = \frac{1}{2r\mathcal{J}}\frac{\partial r^2}{\partial \theta}, \\[6pt] +v_{22} &= \vec{v}_2 \cdot \hat{e}_2 = \frac{2\pi r}{\mathcal{J}}\left(1+\frac{\partial \eta}{\partial \theta}\right), \\[6pt] +v_{23} &= \vec{v}_2 \cdot \hat{e}_3 = \frac{R}{\mathcal{J}}\frac{\partial \phi}{\partial \theta}, \\[6pt] +\end{aligned} +\end{equation} + +\begin{equation} +\begin{aligned} +v_{31} &= \vec{v}_3 \cdot \hat{e}_1 = \frac{1}{2r\mathcal{J}}\frac{\partial r^2}{\partial \zeta} = 0, \\[6pt] +v_{32} &= \vec{v}_3 \cdot \hat{e}_2 = \frac{2\pi r}{\mathcal{J}}\frac{\partial \eta}{\partial \zeta} = 0, \\[6pt] +v_{33} &= \vec{v}_3 \cdot \hat{e}_3 = \frac{R}{\mathcal{J}}\frac{\partial \phi}{\partial \zeta} = \frac{2\pi R}{\mathcal{J}} +\end{aligned} +\end{equation} + + +\subsection{Gradient basis $(\vec w_i=\nabla q^i)$} +\begin{equation} +\begin{aligned} +\vec{w}_1 &= \vec{\nabla}\psi, & +\vec{w}_2 &= \vec{\nabla}\theta, & +\vec{w}_3 &= \vec{\nabla}\zeta, +\end{aligned} +\end{equation} + +\begin{equation} + \begin{aligned} + \vec{w}_i &= w_{ij}\hat{e}_j, \qquad & + \mathcal{J}\,\vec{v}_i \cdot \vec{w}_j &= \delta_{ij}. + \end{aligned} +\end{equation} + +% ==== All w_{ij}: cross-form -> v-components ==== +% Conventions: +% v_1 = ∇θ × ∇ζ, v_2 = ∇ζ × ∇ψ, v_3 = ∇ψ × ∇θ +% w_1 = ∇ψ, w_2 = ∇θ, w_3 = ∇ζ +% v_i = v_{ij} \hat e_j, w_i = w_{ij} \hat e_j +% General identity: w_{i\alpha} = J\, \varepsilon_{\alpha\beta\gamma}\, v_{j\beta} v_{k\gamma} +% with (i,j,k) cyclic: (1,2,3), (2,3,1), (3,1,2) +\begin{equation} + \begin{aligned} + % i = 1 + w_{11} + &= \vec\nabla\psi \cdot \hat e_1 + = \mathcal{J}\,\Big[\,(\vec\nabla\zeta \times \vec\nabla\psi)\times(\vec\nabla\psi \times \vec\nabla\theta)\,\Big]\!\cdot\hat e_1 + = \mathcal{J}\,(v_{22}v_{33}-v_{23}v_{32}) + = \frac{4\pi^{2} r R}{\mathcal{J}}\left(1+\frac{\partial \eta}{\partial \theta}\right), \\[6pt] + w_{12} + &= \vec\nabla\psi \cdot \hat e_2 + = \mathcal{J}\,\Big[\,(\vec\nabla\zeta \times \vec\nabla\psi)\times(\vec\nabla\psi \times \vec\nabla\theta)\,\Big]\!\cdot\hat e_2 + = \mathcal{J}\,(v_{23}v_{31}-v_{21}v_{33}) + = -\,\frac{\pi R}{r\,\mathcal{J}}\frac{\partial r^{2}}{\partial \theta}, \\[6pt] + w_{13} + &= \vec\nabla\psi \cdot \hat e_3 + = \mathcal{J}\,\Big[\,(\vec\nabla\zeta \times \vec\nabla\psi)\times(\vec\nabla\psi \times \vec\nabla\theta)\,\Big]\!\cdot\hat e_3 + = \mathcal{J}\,(v_{21}v_{32}-v_{22}v_{31}) + = 0 + \end{aligned} +\end{equation} + +\begin{equation} + \begin{aligned} + w_{21} + &= \vec\nabla\theta \cdot \hat e_1 + = \mathcal{J}\,\Big[\,(\vec\nabla\psi \times \vec\nabla\theta)\times(\vec\nabla\theta \times \vec\nabla\zeta)\,\Big]\!\cdot\hat e_1 + = \mathcal{J}\,(v_{32}v_{13}-v_{33}v_{12}) + = -\,\frac{4\pi^{2} r R}{\mathcal{J}}\frac{\partial \eta}{\partial \psi}, \\[6pt] + w_{22} + &= \vec\nabla\theta \cdot \hat e_2 + = \mathcal{J}\,\Big[\,(\vec\nabla\psi \times \vec\nabla\theta)\times(\vec\nabla\theta \times \vec\nabla\zeta)\,\Big]\!\cdot\hat e_2 + = \mathcal{J}\,(v_{33}v_{11}-v_{31}v_{13}) + = \frac{\pi R}{r\,\mathcal{J}}\frac{\partial r^{2}}{\partial \psi}, \\[6pt] + w_{23} + &= \vec\nabla\theta \cdot \hat e_3 + = \mathcal{J}\,\Big[\,(\vec\nabla\psi \times \vec\nabla\theta)\times(\vec\nabla\theta \times \vec\nabla\zeta)\,\Big]\!\cdot\hat e_3 + = \mathcal{J}\,(v_{31}v_{12}-v_{32}v_{11}) + = 0 + \end{aligned} +\end{equation} + +\begin{equation} + \begin{aligned} + w_{31} + &= \vec\nabla\zeta \cdot \hat e_1 + = \mathcal{J}\,\Big[\,(\vec\nabla\theta \times \vec\nabla\zeta)\times(\vec\nabla\zeta \times \vec\nabla\psi)\,\Big]\!\cdot\hat e_1 + = \mathcal{J}\,(v_{12}v_{23}-v_{13}v_{22}) + = \frac{2\pi r R}{\mathcal{J}}\!\left( + \frac{\partial \eta}{\partial \psi}\frac{\partial \phi}{\partial \theta} + - \frac{\partial \phi}{\partial \psi}\left(1+\frac{\partial \eta}{\partial \theta}\right) + \right), \\[6pt] + w_{32} + &= \vec\nabla\zeta \cdot \hat e_2 + = \mathcal{J}\,\Big[\,(\vec\nabla\theta \times \vec\nabla\zeta)\times(\vec\nabla\zeta \times \vec\nabla\psi)\,\Big]\!\cdot\hat e_2 + = \mathcal{J}\,(v_{13}v_{21}-v_{11}v_{23}) + = \frac{R}{2r\,\mathcal{J}}\!\left( + \frac{\partial \phi}{\partial \psi}\frac{\partial r^{2}}{\partial \theta} + - \frac{\partial r^{2}}{\partial \psi}\frac{\partial \phi}{\partial \theta} + \right), \\[6pt] + w_{33} + &= \vec\nabla\zeta \cdot \hat e_3 + = \mathcal{J}\,\Big[\,(\vec\nabla\theta \times \vec\nabla\zeta)\times(\vec\nabla\zeta \times \vec\nabla\psi)\,\Big]\!\cdot\hat e_3 + = \mathcal{J}\,(v_{11}v_{22}-v_{12}v_{21}) + = \frac{1}{2\pi R}. + \end{aligned} +\end{equation} + +\section{Coordinate system} + +The coordinate system comparing between DCON and reference\cite{chance1992mhd}. +In reference \cite{chance1992mhd}, $2 \pi \psi$ is poloidal flux +\begin{equation} +\Psi_p(\psi) = 2\pi \psi += \int_{0}^{2\pi} d\phi + \int_{\text{axis}}^{\psi} + \mathbf{B}_p \cdot d\mathbf{S} +\end{equation} + +\color{red}In DCON, $\psi_{dcon}$, $\theta_{dcon}$, $\zeta_{dcon}$ are all normalized in [0,1]. $\chi_{dcon}$ representing the poloidal flux function and $\alpha_{dcon}$ for SFL angle. Also to scale the actual poloidal flux $\chi\prime_{dcon}$ is used.($\chi\prime_{dcon} = 2\pi(ssibry-ssimag)$) +\color{black}\begin{align} +\mathbf{B} &= \nabla \chi_{dcon} \times \nabla \alpha_{dcon}\\ +% +&= \chi\prime_{dcon} \nabla \psi_{dcon} \times \nabla \alpha_{dcon}\\ +% +&= \chi\prime_{dcon} \nabla \psi_{dcon} \times (q\nabla \theta - \nabla \zeta)\\ +% +&= f_{dcon} \nabla \phi + \nabla \phi \times \frac{\chi\prime_{dcon}}{2\pi} \nabla \psi_{dcon} \\ +% +&= \color{blue} \nabla \psi \times (- \nabla \alpha)\\ +% +&= \color{blue} g(\psi) \nabla \phi + \nabla \phi \times \nabla \psi +\end{align} + +\begin{equation} + \alpha_{dcon} = \color{blue} {- \alpha / 2 \pi} \color{black}= q(\psi)\theta - \zeta +\end{equation} +\begin{equation} +g(\psi) \leftrightarrow \text{(toroidal field functions)} \leftrightarrow f_{dcon} +\end{equation} + +In the following, the blue-side variable $\psi$ is Chance's flux +coordinate. Its relation to the DCON normalized flux is +\[ +\color{blue}{\psi} +\;\;\longleftrightarrow\;\; +\color{black}\frac{\chi_{\mathrm{dcon}}}{2\pi} +\] +% +\[ +\color{blue}{\psi} += \color{black}\frac{\chi'_{\mathrm{dcon}}}{2\pi}\, +\psi_{\mathrm{dcon}}. +\] +Therefore +\[ +{\chi}_{\mathrm{dcon}} = \color{black}\chi'_{\mathrm{dcon}} \psi_{\mathrm{dcon}}. +\] + + +\subsection{Derivation of Eq.~(29) in GPEC note} + +\begin{equation} + \begin{aligned} + \mathbf{B} + &= \chi\prime \nabla \psi \times (q\nabla \theta - \nabla \zeta) = \chi\prime q \vec{v}_3 + \chi\prime \vec{v}_2 \\ + &= \mathcal{J}(B^\psi \vec{v}_1 + B^\theta \vec{v}_2 + B^\zeta \vec{v}_3) \\ + &= (B_\psi \vec{w}_1 + B_\theta \vec{w}_2 + B_\zeta \vec{w}_3) +\end{aligned} +\end{equation} + +% \begin{equation} +% S \;=\; -\,\frac{\vec{B} \times \vec{\nabla}\psi}{\lvert \vec{\nabla}\psi \rvert^{2}} +% \;\cdot\; +% \left(\vec{\nabla} \times +% \frac{\vec{B} \times \vec{\nabla} \psi}{\lvert \vec{\nabla} \psi \rvert^{2}}\right) +% \end{equation} + +\begin{equation} +\begin{aligned} +B^\theta &\equiv \vec{B}\cdot \vec{w}_2 = \frac{\chi'}{\mathcal{J}}, +\qquad +B^\zeta \equiv \vec{B}\cdot \vec{w}_3 = \frac{q\,\chi'}{\mathcal{J}}, \\[4pt] +\vec{B} \times \vec{w}_1 +&= \vec{B}\times\nabla\psi += \chi'\big(g^{\psi\zeta}-q\,g^{\psi\theta}\big)\,\nabla\psi ++ q\,\chi'\,g^{\psi\psi}\,\nabla\theta +- \chi'\,g^{\psi\psi}\,\nabla\zeta \\ +&= \chi'\big(g^{\psi\zeta}-q\,g^{\psi\theta}\big)\,\vec{w}_1 ++ q\,\chi'\,g^{\psi\psi}\,\vec{w}_2 +- \chi'\,g^{\psi\psi}\,\vec{w}_3, +\end{aligned} +\end{equation} + +\begin{equation} +(\vec{B} \times \vec{w}_1)^\psi = 0, \qquad +(\vec{B} \times \vec{w}_1)^\theta = \frac{\chi'\,(g_{23}+q\,g_{33})}{\mathcal{J}^{2}}, \qquad +(\vec{B} \times \vec{w}_1)^\zeta = -\,\frac{\chi'\,(g_{22}+q\,g_{23})}{\mathcal{J}^{2}}, +\end{equation} +where $g_{ij}=\mathcal J^2(\vec v_i\cdot\vec v_j)$ is the true covariant +metric. The important distinction is that $\mathbf B\times\nabla\psi$ is most +naturally written first in the gradient basis $\{\vec w_i\}$ and only then, if +needed, converted to contravariant components in the $\{\vec v_i\}$ basis. + +\subsection{Metric Tensor and Index Operations} + +Throughout this section we distinguish carefully between the true tangent basis +$\vec e_i=\partial\vec r/\partial q^i=\mathcal J\vec v_i$ and the dual gradient +basis $\vec e^{\,i}=\vec w_i=\nabla q^i$. The vector $\vec B$ is expanded as +\begin{equation} + \vec{B} = \mathcal{J}\,B^j\,\vec{v}_j = B^j\,\vec{e}_j + \qquad\text{(contravariant expansion)}, +\end{equation} +\begin{equation} + \vec{B} = B_j\,\vec{w}_j = B_j\,\vec{e}^j + \qquad\text{(covariant expansion)}, +\end{equation} +where the contravariant components satisfy +$B^i = \vec{B}\cdot\vec{w}_i$ and the covariant components satisfy +$B_i = \vec{B}\cdot\vec{e}_i$. These definitions are consistent because +\begin{equation} +\vec{B}\cdot\vec{w}_i += \mathcal{J} B^j \vec{v}_j\cdot\vec{w}_i += \mathcal{J} B^j \frac{\delta_{ji}}{\mathcal J} += B^i. +\end{equation} + +\subsubsection{Lowering Operator ($B^j \to B_i$)} +To transform contravariant components $B^j$ into covariant components $B_i$, we use the true covariant tangent basis $\vec{e}_i = \mathcal{J}\vec{v}_i$. +Since $B_i \equiv \vec{B}\cdot\vec{e}_i$ and $\vec{B} = \mathcal{J}B^j\vec{v}_j$: + +Using the definition $\vec{B} = \mathcal{J} B^j \vec{v}_j$ and taking the dot product with $\vec{e}_i = \mathcal{J}\vec{v}_i$: +\begin{equation} + \begin{aligned} + B_i &= \vec{B} \cdot \vec{e}_i \\ + &= (\mathcal{J} B^j \vec{v}_j) \cdot (\mathcal{J} \vec{v}_i) \\ + &= \mathcal{J}^2 (\vec{v}_i \cdot \vec{v}_j) B^j. + \end{aligned} +\end{equation} +Thus, the covariant metric tensor $g_{ij}$ must include the square of the Jacobian: +\begin{equation} + g_{ij} \equiv \mathcal{J}^2 (\vec{v}_i \cdot \vec{v}_j). +\end{equation} + +\subsubsection{Raising Operator ($B_j \to B^i$)} +To transform covariant components $B_j$ into contravariant components $B^i$, we use the gradient basis $\vec{w}_i$, which corresponds to the dual basis $\vec{e}^i$ (i.e., $\vec{e}^1 = \vec{w}_1 = \vec{\nabla}\psi$, etc.). + +Using the definition $\vec{B} = B_j \vec{w}_j$ and taking the dot product with $\vec{w}_i$: +\begin{equation} + \begin{aligned} + B^i &= \vec{B} \cdot \vec{w}_i \\ + &= (B_j \vec{w}_j) \cdot \vec{w}_i \\ + &= (\vec{w}_i \cdot \vec{w}_j) B_j. + \end{aligned} +\end{equation} +Unlike the lowering operator, the contravariant metric tensor $g^{ij}$ does not require the Jacobian: +\begin{equation} + g^{ij} \equiv \vec{w}_i \cdot \vec{w}_j. +\end{equation} + +\subsection{Derivation of Eq. (35) in gpec note} + +In accordance with the actual code implementation, $w_{11}$ and $w_{12}$ include the full +phase derivative $(1 + \partial_\theta\eta)$: +\[ +w_{11} = \frac{(2\pi)^2 r R}{\mathcal{J}} \left(1 + \frac{\partial \eta}{\partial \theta}\right), +\quad +v_{22} = \frac{2 \pi r}{\mathcal{J}} \left(1 + \frac{\partial \eta}{\partial \theta}\right), +\] +\[ +w_{12} = \frac{-\pi R}{r \mathcal{J}} \frac{\partial r^2}{\partial \theta}, +\quad +v_{21} = \frac{1}{2r \mathcal{J}} \frac{\partial r^2}{\partial \theta} +\] + +Note that $w_{11} = 2 \pi R v_{22}$ remains true with this definition, and +$w_{12} = -2 \pi R v_{21}$ is unchanged. + + +\[ +q\bigl({\vec{\nabla}} \psi \cdot {\vec{\nabla}} \theta \bigr) +- {\vec{\nabla}} \psi \cdot {\vec{\nabla}} \zeta +\] +\[ += q\,(w_{11} w_{21} + w_{12} w_{22}) + - (w_{11} w_{31} + w_{12} w_{32}) +\] +\[ += 2\pi R \mathcal{J} \Bigl[ + - q v_{33} (v_{22} v_{12} + v_{21} v_{11}) + + v_{13}(v_{22}^2 + v_{21}^2) + - v_{23}(v_{22} v_{12} + v_{11} v_{21}) +\Bigr] +\] +\[ += 2\pi R \mathcal{J} \Bigl[ + - (v_{22} v_{12} + v_{21} v_{11})(v_{23}+q v_{33}) + + v_{13}(v_{22}^2 + v_{21}^2) +\Bigr] +\] + +\section{Energy principle\cite{RevModPhys.76.1071}\cite{Park2009Ideal}} +\begin{align} +\delta W_p &= \frac{1}{2\mu_0} \int d\tau_p +\Big\{ |\mathbf{Q}|^2 - \mu_0\,\mathbf{j} \cdot \, \mathbf{Q} \times \boldsymbol{\xi}^* ++ \mu_0 \gamma p |\nabla \cdot \boldsymbol{\xi}|^2 ++ \mu_0 (\nabla \cdot \boldsymbol{\xi})^* \boldsymbol{\xi} \cdot \nabla p \Big\} \label{eq:ep_first}\\ +&= \frac{1}{2\mu_0} \int d\tau_p +\Big\{ |\mathbf{Q}_\perp|^2 ++ \frac{1}{B^2} |\mathbf{Q} \cdot \mathbf{B} - \mu_0\,\boldsymbol{\xi} \cdot \nabla p|^2 ++ \mu_0\,\sigma\, \mathbf{B} \times \boldsymbol{\xi}^* \cdot \mathbf{Q} ++ \mu_0 \gamma p |\nabla \cdot \boldsymbol{\xi}|^2 +- 2 \mu_0\,\boldsymbol{\xi} \cdot \nabla p\, \boldsymbol{\xi}^* \cdot \boldsymbol{\kappa} +\Big\} \label{eq:ep_intuitive}\\ +&= \frac{1}{2\mu_0} \int d\tau_p +\Big\{ |\mathbf{Q} + \xi_n (\mu_0\mathbf{j} \times \mathbf{n})|^2 ++ \mu_0 \gamma p |\nabla \cdot \boldsymbol{\xi}|^2 +- 2\,\mu_0\mathbf{j} \times \mathbf{n} \cdot (\mathbf{B} \cdot \nabla \mathbf{n})\, |\xi_n|^2 +\Big\}\label{eq:ep_bernstein}\\ +&= \frac{1}{2\mu_0} \int d\tau_p +\Big\{ |\mathbf{Q} + \xi_n (\mu_0 \mathbf{j} \times \mathbf{n})|^2 ++ \mu_0 \gamma p |\nabla \cdot \boldsymbol{\xi}|^2 +- \mu_0 K\, |\xi_n|^2 +\Big\}\label{eq:ep_bernsteinK} +\end{align} + +The derivation of \eqref{eq:ep_intuitive} can be found in \cite{RevModPhys.76.1071} and the original derivation of \eqref{eq:ep_bernstein} is in \cite{Bernstein1983}. +The $K$ term in \eqref{eq:ep_bernsteinK} is defined in \cite{chance1992mhd}. + +\subsection{Transformation of $\delta W_F$ into the Bernstein form (modified SI/DCON units)} +\label{sec:bernstein_transform} + +We follow the derivation of \cite{Bernstein1983}, while keeping the present SI/DCON conventions. +Throughout this subsection, we employ the following definitions: +\begin{equation} +\sigma \equiv \frac{\mathbf{j}\cdot\mathbf{B}}{B^2}, \qquad +\mathbf Q \equiv \nabla\times(\boldsymbol{\xi}\times\mathbf B), \qquad +\mu_0\mathbf j = \nabla\times\mathbf B, +\end{equation} +\begin{equation} +\hat{\mathbf n}\equiv \frac{\nabla\psi}{|\nabla\psi|}, \qquad +\xi_n \equiv \hat{\mathbf n}\cdot\boldsymbol{\xi}, \qquad +\delta\mathbf B = \mathbf Q, +\end{equation} +with \(p=p(\psi)\). + +We begin from +\begin{equation} +2\mu_0 \delta W_F += +\int d^3r\left\{ +|\mathbf Q|^2 ++\mu_0\gamma p\,|\nabla\cdot\boldsymbol{\xi}|^2 ++\mu_0(\nabla\cdot\boldsymbol{\xi})^*\,\boldsymbol{\xi}\cdot\nabla p ++(\boldsymbol{\xi}^*\times\mathbf Q)\cdot(\nabla\times\mathbf B) +\right\}. +\end{equation} + +The vectors $\mathbf{B}$, $\nabla p$, and $\nabla\times\mathbf{B}$ form a local +basis away from degenerate points where \(p'=0\) or where \(\mathbf B\) and +\(\nabla\times\mathbf B\) become linearly dependent. Here $\nabla p$ is +orthogonal to both $\mathbf B$ and $\nabla\times\mathbf B$ (since +$\mathbf{B}\cdot\nabla p = (\nabla\times\mathbf{B})\cdot\nabla p = 0$, i.e. +$\mathbf B$ and $\mathbf j$ lie in the flux surface), while $\mathbf B$ and +$\nabla\times\mathbf B$ are independent but not orthogonal +($\mathbf{B}\cdot(\nabla\times\mathbf{B})=\mu_0 j_\parallel B\neq0$). We thus +introduce +\begin{equation} +\boldsymbol{\xi} += +a\,\nabla p + b\,\nabla\times\mathbf B + c\,\mathbf B. +\end{equation} +Since \(p=p(\psi)\), +\begin{equation} +\nabla p = p'(\psi)\nabla\psi = p'(\psi)|\nabla\psi|\,\hat{\mathbf n}, +\qquad +\xi_n = \hat{\mathbf n}\cdot\boldsymbol{\xi} = a\,p'(\psi)|\nabla\psi|, +\end{equation} +so that +\begin{equation} +a\,\nabla p = \xi_n\,\hat{\mathbf n}. +\end{equation} +Also, because \(\mathbf B\cdot\nabla p=0\) and \((\nabla\times\mathbf B)\cdot\nabla p=0\), +\begin{align} +\boldsymbol{\xi}\times\mathbf B +&= +-a\,\mathbf B\times\nabla p ++b\,(\nabla\times\mathbf B)\times\mathbf B += +-a\,\mathbf B\times\nabla p+\mu_0 b\,\nabla p, \\ +\boldsymbol{\xi}\times(\nabla\times\mathbf B) +&= +-a\,(\nabla\times\mathbf B)\times\nabla p ++c\,\mathbf B\times(\nabla\times\mathbf B) += +-a\,(\nabla\times\mathbf B)\times\nabla p-\mu_0 c\,\nabla p. +\end{align} +Hence +\begin{equation} +\mathbf Q += +\nabla\times\bigl(-a\,\mathbf B\times\nabla p+\mu_0 b\,\nabla p\bigr). +\end{equation} + +Substituting $\boldsymbol{\xi} = a\,\nabla p + b\,\nabla\times\mathbf B + c\,\mathbf B$: +\begin{align} +\nabla\cdot\boldsymbol{\xi} &= \nabla\cdot(a\nabla p)+\nabla\cdot(b\nabla\times\mathbf B+c\mathbf B), \quad \boldsymbol{\xi}\cdot\nabla p = a(\nabla p)^2. +\end{align} +By the product rule: +\begin{equation} +a(\nabla p)^2\nabla\cdot(b\nabla\times\mathbf B+c\mathbf B) = +\nabla\cdot\Bigl[a(\nabla p)^2(b\nabla\times\mathbf B+c\mathbf B)\Bigr] +- (b\nabla\times\mathbf B+c\mathbf B)\cdot\nabla\bigl[a(\nabla p)^2\bigr]. +\end{equation} +After integration over a volume bounded by flux surfaces, the divergence term +has no surface contribution, giving +\begin{align} +a(\nabla p)^2\nabla\cdot\bigl(b\nabla\times\mathbf B+c\mathbf B\bigr) +&= +-\bigl(b\nabla\times\mathbf B+c\mathbf B\bigr)\cdot\nabla\!\bigl[a(\nabla p)^2\bigr] +\nonumber\\ +&\quad ++\nabla\cdot\!\Bigl[a(\nabla p)^2\bigl(b\nabla\times\mathbf B+c\mathbf B\bigr)\Bigr]. +\end{align} +For a volume bounded by flux surfaces, \(\hat{\mathbf n}\cdot\mathbf B=0\) and \(\hat{\mathbf n}\cdot(\nabla\times\mathbf B)=0\), so the surface integral of the divergence term vanishes. Using also \(\nabla\cdot\mathbf B=0\) and \(\nabla\cdot(\nabla\times\mathbf B)=0\), one obtains +\begin{align} +2\mu_0 \delta W_F += +\int d^3r\Bigl\{ +&\mu_0\gamma p\,|\nabla\cdot\boldsymbol{\xi}|^2 ++\Bigl|\mathbf Q+a(\nabla\times\mathbf B)\times\nabla p\Bigr|^2 +\nonumber\\ +&\quad +-a^2\Bigl|(\nabla\times\mathbf B)\times\nabla p\Bigr|^2 ++a(\nabla\times\mathbf B)\times\nabla p\cdot\nabla\times(a\,\mathbf B\times\nabla p) +\nonumber\\ +&\quad ++\mu_0 a(\nabla p)^2\nabla\cdot(a\nabla p) +\Bigr\}. +\end{align} + +Using $\mu_0\nabla p = (\nabla\times\mathbf B)\times\mathbf B$ and $\hat{\mathbf n}\cdot\mathbf B=0$, the last three scalar terms combine to: +\begin{align} +&-a^2\Bigl|(\nabla\times\mathbf B)\times\nabla p\Bigr|^2 +a(\nabla\times\mathbf B)\times\nabla p\cdot\nabla\times(a\,\mathbf B\times\nabla p) +\mu_0 a(\nabla p)^2\nabla\cdot(a\nabla p) \nonumber\\ +&= a^2(\nabla p)^2 \Bigl[-|\nabla\times\mathbf B|^2 +(\nabla\times\mathbf B)\times\hat{\mathbf n}\cdot\Bigl((\hat{\mathbf n}\cdot\nabla)\mathbf B-(\mathbf B\cdot\nabla)\hat{\mathbf n}\Bigr)\Bigr]. +\end{align} + +Now, since \(\hat{\mathbf n}\cdot\mathbf B=0\), +\begin{equation} +0=\nabla(\hat{\mathbf n}\cdot\mathbf B) += +(\hat{\mathbf n}\cdot\nabla)\mathbf B ++(\mathbf B\cdot\nabla)\hat{\mathbf n} ++\hat{\mathbf n}\times(\nabla\times\mathbf B) ++\mathbf B\times(\nabla\times\hat{\mathbf n}). +\end{equation} +Moreover, +\begin{equation} +(\nabla\times\mathbf B)\cdot\hat{\mathbf n}=0, +\end{equation} +and, since \(\hat{\mathbf n}=\lambda\nabla\psi\) with \(\lambda=|\nabla\psi|^{-1}\), +\begin{equation} +\nabla\times\hat{\mathbf n} += +\nabla\lambda\times\nabla\psi, +\qquad +\hat{\mathbf n}\cdot(\nabla\times\hat{\mathbf n})=0. +\end{equation} +Therefore, +\begin{equation} +-|\nabla\times\mathbf B|^2 ++(\nabla\times\mathbf B)\times\hat{\mathbf n}\cdot +\Bigl((\hat{\mathbf n}\cdot\nabla)\mathbf B-(\mathbf B\cdot\nabla)\hat{\mathbf n}\Bigr) += +-2\,(\nabla\times\mathbf B)\times\hat{\mathbf n}\cdot(\mathbf B\cdot\nabla\hat{\mathbf n}). +\end{equation} + +Using \(a\nabla p=\xi_n\hat{\mathbf n}\) and \(\mu_0\mathbf j=\nabla\times\mathbf B\), we finally obtain +\begin{equation} +2\mu_0 \delta W_F += +\int d^3r\left\{ +\Bigl|\mathbf Q+\xi_n(\mu_0\mathbf j\times\hat{\mathbf n})\Bigr|^2 ++\mu_0\gamma p\,|\nabla\cdot\boldsymbol{\xi}|^2 +-2\,|\xi_n|^2\,(\mu_0\mathbf j\times\hat{\mathbf n})\cdot(\mathbf B\cdot\nabla\hat{\mathbf n}) +\right\}. +\end{equation} +Defining +\begin{equation} +K \equiv 2\,(\mathbf j\times\hat{\mathbf n})\cdot(\mathbf B\cdot\nabla\hat{\mathbf n}), +\end{equation} +the Bernstein form becomes +\begin{equation} +\delta W_F += +\frac{1}{2\mu_0}\int d^3r\left\{ +\Bigl|\mathbf Q+\xi_n(\mu_0\mathbf j\times\hat{\mathbf n})\Bigr|^2 ++\mu_0\gamma p\,|\nabla\cdot\boldsymbol{\xi}|^2 +-\mu_0 K\,|\xi_n|^2 +\right\}. +\end{equation} + +\subsection{Derivation of $K$ with explicit $\mu_0$} + +We start from +\begin{equation} +\color{blue} K \equiv 2(\mathbf j\times \hat{\mathbf n})\cdot(\mathbf B\cdot\nabla)\hat{\mathbf n}. +\end{equation} +Since +\color{blue}\begin{align} +(\mathbf B\cdot\nabla)\hat{\mathbf n} &= (\mathbf B\cdot\nabla)\left(\frac{\nabla\psi}{|\nabla\psi|}\right), +(\mathbf j\times \hat{\mathbf n})\cdot \nabla\psi = 0, +\end{align} +\color{black} +we have +\begin{equation} +\color{blue}K +\equiv 2(\mathbf j\times \hat{\mathbf n})\cdot(\mathbf B\cdot\nabla)\hat{\mathbf n} += +\frac{2}{|\nabla\psi|^2} +(\mathbf j\times\nabla\psi)\cdot(\mathbf B\cdot\nabla)\nabla\psi. +\end{equation} + +Now use $\mathbf B\cdot\nabla\psi=0$: +\begin{equation} +0=\nabla(\mathbf B\cdot\nabla\psi) = (\mathbf B\cdot\nabla)\nabla\psi + (\nabla\psi\cdot\nabla)\mathbf B + \nabla\psi\times(\nabla\times\mathbf B), +\end{equation} +thus +\color{blue}\begin{align} +K &= 2\mu_0 j^2 - \frac{2}{|\nabla\psi|^2}(\mathbf j\times\nabla\psi)\cdot(\nabla\psi\cdot\nabla)\mathbf B. +\end{align} +\color{black} + +Decompose $\mathbf j = \sigma\mathbf B + \frac{p'}{B^2}\mathbf B\times\nabla\psi \equiv \mathbf j_{\parallel}+\mathbf j_{\perp}$: +\begin{equation} +\color{blue}K = K_{\parallel}+K_{\perp}, +\end{equation} +\color{blue}\begin{align} +K_{\parallel} +&= +2\mu_0 j_{\parallel}^2 +-\frac{2}{|\nabla\psi|^2} +(\mathbf j_{\parallel}\times\nabla\psi)\cdot(\nabla\psi\cdot\nabla)\mathbf B, +\\ +K_{\perp} +&= +2\mu_0 j_{\perp}^2 +-\frac{2}{|\nabla\psi|^2} +(\mathbf j_{\perp}\times\nabla\psi)\cdot(\nabla\psi\cdot\nabla)\mathbf B. +\end{align} + +\color{black} +\paragraph{1. Perpendicular part $K_{\perp}$} +With $\mathbf j_{\perp}=\frac{p'}{B^2}\mathbf B\times\nabla\psi$: +\begin{equation} +j_{\perp}^{2} = \frac{p'^{\,2}}{B^{2}}|\nabla\psi|^{2}, +\end{equation} +where $\mathbf B\cdot\nabla\psi=0$. Also, +\begin{equation} +\mathbf j_{\perp}\times\nabla\psi = -\frac{p'}{B^{2}}\mathbf B|\nabla\psi|^{2}. +\end{equation} +Hence +\begin{equation} +K_{\perp} = \frac{2\mu_{0}p'^{\,2}}{B^{2}}|\nabla\psi|^{2} + \frac{2p'}{B^{2}}\mathbf B\cdot(\nabla\psi\cdot\nabla)\mathbf B. +\end{equation} + +To simplify, use $0=\nabla(\mathbf B\cdot\nabla\psi) = (\mathbf B\cdot\nabla)\nabla\psi + (\nabla\psi\cdot\nabla)\mathbf B + \nabla\psi\times(\nabla\times\mathbf B)$. +Dotting with $\mathbf B$ and using $\nabla\times\mathbf B=\mu_{0}\mathbf j$: +\begin{equation} +\mathbf B\cdot(\nabla\psi\cdot\nabla)\mathbf B = -\mathbf B\cdot(\mathbf B\cdot\nabla)\nabla\psi - \mu_{0}\mathbf B\cdot(\nabla\psi\times\mathbf j). +\end{equation} +From $(\mathbf B\cdot\nabla)(\mathbf B\cdot\nabla\psi)=0$: +\begin{equation} +\mathbf B\cdot(\mathbf B\cdot\nabla)\nabla\psi = -((\mathbf B\cdot\nabla)\mathbf B)\cdot\nabla\psi. +\end{equation} +From equilibrium $\mathbf j\times\mathbf B=p'\nabla\psi$: +\begin{equation} +\mathbf B\cdot(\nabla\psi\times\mathbf j) = p'|\nabla\psi|^{2}. +\end{equation} +Therefore +\begin{equation} +\mathbf B\cdot(\nabla\psi\cdot\nabla)\mathbf B = ((\mathbf B\cdot\nabla)\mathbf B)\cdot\nabla\psi - \mu_{0}p'|\nabla\psi|^{2}. +\end{equation} + +Substituting: +\begin{align} +K_{\perp} &= \frac{2\mu_{0}p'^{\,2}}{B^{2}}|\nabla\psi|^{2} + \frac{2p'}{B^{2}}\left[((\mathbf B\cdot\nabla)\mathbf B)\cdot\nabla\psi - \mu_{0}p'|\nabla\psi|^{2}\right] \\ +&= \frac{2p'}{B^{2}}((\mathbf B\cdot\nabla)\mathbf B)\cdot\nabla\psi. +\end{align} + +With $\mathbf B=B\mathbf b$ and $\mathbf b\cdot\nabla\psi=0$: +\begin{equation} +((\mathbf B\cdot\nabla)\mathbf B)\cdot\nabla\psi = B^2\,\boldsymbol{\kappa}\cdot\nabla\psi = B^2\kappa_\psi. +\end{equation} +Thus, $\boxed{K_{\perp}=2p'\kappa_\psi}$. + +\paragraph{2. Parallel part $K_{\parallel}$} +With $\mathbf j_{\parallel}=\sigma\mathbf B$: +\begin{equation} +K_{\parallel} = 2\mu_0\sigma^2B^2 - \frac{2\sigma}{|\nabla\psi|^2}(\mathbf B\times\nabla\psi)\cdot(\nabla\psi\cdot\nabla)\mathbf B. +\end{equation} + +Define $\mathbf A \equiv \mathbf B\times\nabla\psi$. Using +\begin{equation} +\nabla\times(\mathbf B\times\nabla\psi) = \mathbf B\nabla^2\psi + (\nabla\psi\cdot\nabla)\mathbf B - (\mathbf B\cdot\nabla)\nabla\psi, +\end{equation} +and from $0=\nabla(\mathbf B\cdot\nabla\psi) = (\mathbf B\cdot\nabla)\nabla\psi + (\nabla\psi\cdot\nabla)\mathbf B + \nabla\psi\times(\nabla\times\mathbf B)$ with $\nabla\times\mathbf B=\mu_0\mathbf j$: +\begin{equation} +(\mathbf B\cdot\nabla)\nabla\psi = -(\nabla\psi\cdot\nabla)\mathbf B - \mu_0\nabla\psi\times\mathbf j. +\end{equation} +Therefore +\begin{equation} +\nabla\times(\mathbf B\times\nabla\psi) = \mathbf B\nabla^2\psi + 2(\nabla\psi\cdot\nabla)\mathbf B + \mu_0\nabla\psi\times\mathbf j. +\end{equation} + +Dotting with $\mathbf A=\mathbf B\times\nabla\psi$ (note: $\mathbf A\cdot\mathbf B=0$): +\begin{equation} +\mathbf A\cdot\nabla\times(\mathbf B\times\nabla\psi) = 2\mathbf A\cdot(\nabla\psi\cdot\nabla)\mathbf B + \mu_0\mathbf A\cdot(\nabla\psi\times\mathbf j), +\end{equation} +giving +\begin{equation} +\mathbf A\cdot(\nabla\psi\cdot\nabla)\mathbf B = \frac{1}{2}\left[\mathbf A\cdot\nabla\times(\mathbf B\times\nabla\psi) - \mu_0\mathbf A\cdot(\nabla\psi\times\mathbf j)\right]. +\end{equation} + +Substituting into $K_{\parallel}$: +\begin{equation} +K_{\parallel} = 2\mu_0\sigma^2B^2 - \frac{\sigma}{|\nabla\psi|^2}\mathbf A\cdot\nabla\times(\mathbf B\times\nabla\psi) + \frac{\mu_0\sigma}{|\nabla\psi|^2}\mathbf A\cdot(\nabla\psi\times\mathbf j). +\end{equation} + +For the last term, using the vector identity $(\mathbf a\times\mathbf b)\cdot(\mathbf c\times\mathbf d) = (\mathbf a\cdot\mathbf c)(\mathbf b\cdot\mathbf d) - (\mathbf a\cdot\mathbf d)(\mathbf b\cdot\mathbf c)$: +\begin{equation} +(\mathbf B\times\nabla\psi)\cdot(\nabla\psi\times\mathbf j) = (\mathbf B\cdot\nabla\psi)(\nabla\psi\cdot\mathbf j) - (\mathbf B\cdot\mathbf j)|\nabla\psi|^2 = -\sigma B^2|\nabla\psi|^2, +\end{equation} +where $\mathbf B\cdot\nabla\psi=0$ and $\mathbf B\cdot\mathbf j=\sigma B^2$ were used. Therefore +\begin{align} +K_{\parallel} &= 2\mu_0\sigma^2B^2 - \frac{\sigma}{|\nabla\psi|^2}(\mathbf B\times\nabla\psi)\cdot\nabla\times(\mathbf B\times\nabla\psi) - \mu_0\sigma^2B^2 \\ +&= \mu_0\sigma^2B^2 - \frac{\sigma}{|\nabla\psi|^2}(\mathbf B\times\nabla\psi)\cdot\nabla\times(\mathbf B\times\nabla\psi). +\end{align} + +Define +\begin{equation} +S \equiv -\frac{\mathbf B\times\nabla\psi}{|\nabla\psi|^2}\cdot\nabla\times\left(\frac{\mathbf B\times\nabla\psi}{|\nabla\psi|^2}\right), +\end{equation} +which is equivalent to $S = -\frac{1}{|\nabla\psi|^4}(\mathbf B\times\nabla\psi)\cdot\nabla\times(\mathbf B\times\nabla\psi)$ by the product rule for curl. +Thus +\begin{equation} +\boxed{K_{\parallel} = \mu_0\sigma^2B^2 + \sigma|\nabla\psi|^2 S}. +\end{equation} + +\paragraph{3. Final expression} +\begin{equation} +\boxed{ +K = |\nabla\psi|^2 \sigma S + \mu_0\sigma^2B^2 + 2p'\kappa_\psi +} +\end{equation} + +\subsection{Expression of $K$ in DCON coordinates} + +Following the Bernstein formulation from §3.1-3.2, the energy principle yields +\color{blue} +\begin{equation} +K = |\nabla\psi|^2 \sigma S + \mu_0 \sigma^2 B^2 + 2p'\kappa_\psi, \quad +\sigma \equiv \frac{\mathbf j\cdot\mathbf B}{B^2}, \quad +\mathbf j \equiv \frac{\nabla\times\mathbf B}{\mu_0}. +\end{equation} +\color{black} +In §3.2, this expression was obtained in a general flux-coordinate form. We now rewrite +it so that each ingredient can be evaluated directly from the DCON equilibrium data. From +this point on, $\psi$ denotes the DCON normalized flux coordinate, $\psi\in[0,1]$, and +$p' \equiv dp/d\psi$ is the derivative with respect to this normalized flux label. + +In DCON coordinates $(\psi,\theta,\zeta)$, +\begin{equation} +\mathbf B = \chi'(\psi)\,\nabla\psi\times\nabla\alpha, \quad +\alpha \equiv q(\psi)\theta-\zeta, \quad +\chi' \equiv \frac{d\chi}{d\psi}, \quad +\mathcal J^{-1} \equiv \nabla\psi\cdot(\nabla\theta\times\nabla\zeta). +\end{equation} + +We use the contravariant metric elements +\begin{equation} +g^{ij}\equiv \nabla q^i\cdot\nabla q^j, +\qquad +(q^1,q^2,q^3) \equiv (\psi,\theta,\zeta), +\end{equation} +with in particular +\begin{equation} +g^{\psi\psi}\equiv \nabla\psi\cdot\nabla\psi, \quad +g^{\psi\theta}\equiv \nabla\psi\cdot\nabla\theta, \quad +g^{\psi\zeta}\equiv \nabla\psi\cdot\nabla\zeta. +\end{equation} +The combinations involving $\alpha=q\theta-\zeta$ are +\begin{equation} +g^{\psi\alpha}\equiv \nabla\psi\cdot\nabla\alpha += q\,g^{\psi\theta}-g^{\psi\zeta}, +\end{equation} +\begin{equation} +g^{\alpha\alpha}\equiv \nabla\alpha\cdot\nabla\alpha += q^2 g^{\theta\theta}-2q\,g^{\theta\zeta}+g^{\zeta\zeta}. +\end{equation} +Accordingly, the parallel derivative operator becomes +\begin{equation} +\mathbf B\cdot\nabla += +\frac{\chi'}{\mathcal J} +\left( +\frac{\partial}{\partial\theta} ++ +q\frac{\partial}{\partial\zeta} +\right), +\end{equation} +and the field strength is written as +\begin{equation} +\boxed{ +B^2 += +\chi'^2|\nabla\psi\times\nabla\alpha|^2 += +\chi'^2\left(g^{\psi\psi}g^{\alpha\alpha}-(g^{\psi\alpha})^2\right). +} +\end{equation} +These relations provide the DCON input form used below for the explicit evaluation of +$S$, $\xi_\psi$, $\kappa_\psi$, and finally $K$. + +\subsubsection{Shear parameter $S$ in DCON} + +\color{blue} +For any flux label $\lambda$, the shear parameter associated with that label is +\begin{equation} +S_{\lambda} +\equiv +-\, +\frac{\mathbf B\times\nabla\lambda}{|\nabla\lambda|^2} +\cdot +\nabla\times +\left( +\frac{\mathbf B\times\nabla\lambda}{|\nabla\lambda|^2} +\right), +\end{equation} +and the shear contribution to $K$ is $|\nabla\lambda|^2\sigma S_{\lambda}$. + +\color{black} +In this subsection we take $\lambda=\psi$, where $\psi$ is the DCON normalized flux label. + +Introduce +\begin{equation} +\boldsymbol{\Lambda}_\psi +\equiv +\frac{\mathbf B\times\nabla\psi}{|\nabla\psi|^2}. +\end{equation} +Using $\mathbf B=\chi'\nabla\psi\times\nabla\alpha$, we obtain +\begin{align} +\boldsymbol{\Lambda}_\psi +&= +\chi' +\frac{(\nabla\psi\times\nabla\alpha)\times\nabla\psi}{|\nabla\psi|^2} +\\ +&= +\chi' +\left[ +\nabla\alpha +- +\frac{\nabla\psi\cdot\nabla\alpha}{|\nabla\psi|^2}\nabla\psi +\right]. +\end{align} + +Define +\begin{equation} +\Gamma_\alpha +\equiv +\frac{\nabla\psi\cdot\nabla\alpha}{|\nabla\psi|^2}. +\end{equation} +Then +\begin{equation} +\boldsymbol{\Lambda}_\psi += +\chi'(\nabla\alpha-\Gamma_\alpha \nabla\psi). +\end{equation} + +Its curl is +\begin{align} +\nabla\times\boldsymbol{\Lambda}_\psi +&= +\nabla\chi' \times (\nabla\alpha-\Gamma_\alpha\nabla\psi) ++ +\chi'\nabla\times(\nabla\alpha-\Gamma_\alpha\nabla\psi) +\\ +&= +\nabla\chi' \times (\nabla\alpha-\Gamma_\alpha\nabla\psi) +- +\chi'\,\nabla\Gamma_\alpha\times\nabla\psi, +\end{align} +since $\nabla\times\nabla\alpha=0$ and $\nabla\times\nabla\psi=0$. + +Now, because $\nabla\chi' \parallel \nabla\psi$, the first term vanishes when contracted +with $\boldsymbol{\Lambda}_\psi$, so +\begin{align} +S_{\psi} +&= +-\boldsymbol{\Lambda}_\psi\cdot(\nabla\times\boldsymbol{\Lambda}_\psi) +\\ +&= +\chi'^2 +(\nabla\alpha-\Gamma_\alpha\nabla\psi)\cdot +(\nabla\Gamma_\alpha\times\nabla\psi) +\\ +&= +\chi'^2\,\nabla\alpha\cdot(\nabla\Gamma_\alpha\times\nabla\psi) +\\ +&= +\chi'^2\,(\nabla\psi\times\nabla\alpha)\cdot\nabla\Gamma_\alpha +\\ +&= +\chi'\,\mathbf B\cdot\nabla\Gamma_\alpha. +\end{align} +Therefore, +\begin{equation} +\boxed{ +S_{\psi} += +\chi'\,\mathbf B\cdot\nabla +\left( +\frac{\nabla\psi\cdot\nabla\alpha}{\nabla\psi\cdot\nabla\psi} +\right). +} +\end{equation} +Here $S_{\psi}$ denotes the shear parameter associated with the DCON normalized flux +label $\psi$, not the $\chi/(2\pi)$-based shear used in the GPEC note. + +Next, +\begin{align} +\nabla\psi\cdot\nabla\alpha +&= +\nabla\psi\cdot\nabla\big(q(\psi)\theta-\zeta\big) +\\ +&= +q'(\nabla\psi\cdot\nabla\psi)\,\theta ++ +q(\nabla\psi\cdot\nabla\theta) +- +(\nabla\psi\cdot\nabla\zeta), +\end{align} +so that +\begin{equation} +\Gamma_\alpha += +q'\theta ++ +\frac{q\,g^{\psi\theta}-g^{\psi\zeta}}{g^{\psi\psi}}. +\end{equation} +The \(q'\theta\) piece is a gauge term coming from +\(\alpha=q(\psi)\theta-\zeta\); it is not itself periodic in \(\theta\). In the +following expressions only its derivative, \(\partial_\theta(q'\theta)=q'\), +enters the physical scalar \(S_\psi\), while integrations by parts must be +applied only to the resulting periodic combinations. + +For an axisymmetric tokamak equilibrium, all equilibrium scalars and metric elements are +independent of $\zeta$, hence $\partial\Gamma_\alpha/\partial\zeta=0$. Using +\begin{equation} +\mathbf B\cdot\nabla += +\frac{\chi'}{\mathcal J} +\left( +\frac{\partial}{\partial\theta} ++ +q\frac{\partial}{\partial\zeta} +\right), +\end{equation} +we obtain +\begin{align} +S_{\psi} +&= +\frac{\chi'^2}{\mathcal J} +\frac{\partial}{\partial\theta} +\left[ +q'\theta ++ +\frac{q\,g^{\psi\theta}-g^{\psi\zeta}}{g^{\psi\psi}} +\right] +\\ +&= +\boxed{ +\frac{\chi'^2}{\mathcal J} +\left[ +q' ++ +\frac{\partial}{\partial\theta} +\left( +\frac{q\,g^{\psi\theta}-g^{\psi\zeta}}{g^{\psi\psi}} +\right) +\right]. +} +\end{align} +Therefore, in DCON coordinates the shear contribution to $K$ is written as +\begin{equation} +\boxed{ +K_{\mathrm{shear}}^{DCON} += +g^{\psi\psi}\sigma S_{\psi} += +g^{\psi\psi}\sigma +\frac{\chi'^2}{\mathcal J} +\left[ +q' ++ +\frac{\partial}{\partial\theta} +\left( +\frac{q\,g^{\psi\theta}-g^{\psi\zeta}}{g^{\psi\psi}} +\right) +\right]. +} +\end{equation} + +\noindent\textbf{Relation to the GPEC note.} +If one instead uses the flux label +\begin{equation} +\lambda = \frac{\chi(\psi)}{2\pi}, +\end{equation} +then \(\nabla\lambda=(\chi'/2\pi)\nabla\psi\) on each flux surface and +\begin{equation} +S_{\lambda} += +\left(\frac{2\pi}{\chi'}\right)^2 S_{\psi} += +\frac{(2\pi)^2}{\mathcal J} +\left[ +q' ++ +\frac{\partial}{\partial\theta} +\left( +\frac{q\,g^{\psi\theta}-g^{\psi\zeta}}{g^{\psi\psi}} +\right) +\right]. +\end{equation} +This is the shear normalization used in the GPEC note. The first term of $K$ is the +same only when the corresponding gradient factor is used consistently: +\begin{equation} +\left|\nabla\left(\frac{\chi}{2\pi}\right)\right|^2\sigma S_{\lambda} += +g^{\psi\psi}\sigma S_{\psi}. +\end{equation} + +\subsubsection{Normal displacement $\xi_\psi$} + +In DCON coordinates, the flux-normal displacement variable is +\begin{equation} +\xi_\psi +\equiv +\boldsymbol{\xi}\cdot\nabla\psi += +\xi^\psi_{\rm tang}, +\qquad +\hat{\mathbf n} += +\frac{\nabla\psi}{|\nabla\psi|}. +\end{equation} +Here \(\xi^\psi_{\rm tang}\) denotes the contravariant component in the true +tangent basis \(\vec e_i=\partial\vec r/\partial q^i\). It should not be +confused with a covariant component in the gradient basis. +Therefore, +\begin{equation} +\boxed{ +\boldsymbol{\xi}_\perp\cdot\hat{\mathbf n} += +\frac{\boldsymbol{\xi}\cdot\nabla\psi}{|\nabla\psi|} += +\frac{\xi_\psi}{|\nabla\psi|}. +} +\end{equation} +The variable $\xi_\psi$ itself depends on the chosen flux label, but the ratio +$\xi_\psi/|\nabla\psi|$ does not. + +Using the DCON Fourier convention, +\begin{equation} +\xi_\psi(\psi,\theta,\zeta) += +\sum_{m,n} +v_{m,n}(\psi)\, +e^{2\pi i(m\theta-n\zeta)}, +\end{equation} +we obtain +\begin{equation} +\boxed{ +\boldsymbol{\xi}_\perp\cdot\hat{\mathbf n} += +\frac{1}{|\nabla\psi|} +\sum_{m,n} +v_{m,n}(\psi)\, +e^{2\pi i(m\theta-n\zeta)}. +} +\end{equation} + +For the quadratic form in the energy principle, the magnitude squared is: +\begin{equation} +\boxed{ +(\boldsymbol{\xi}_\perp\cdot\hat{\mathbf n}) +(\boldsymbol{\xi}_\perp\cdot\hat{\mathbf n})^* += +\frac{\xi_\psi(\xi_\psi)^*}{g^{\psi\psi}}, +\qquad +g^{\psi\psi}=|\nabla\psi|^2. +} +\end{equation} +Since $\xi_\psi$ is represented as a complex Fourier expansion, the product with its +complex conjugate gives the magnitude squared. Therefore the Bernstein scalar term is +evaluated in DCON as +\begin{equation} +\boxed{ +K\left|\boldsymbol{\xi}_\perp\cdot\hat{\mathbf n}\right|^2 += +\frac{K}{g^{\psi\psi}}|\xi_\psi|^2. +} +\end{equation} + +\subsubsection{Curvature $\kappa_\psi$} + +\color{blue} +For any flux label $\lambda$, define +\begin{equation} +\kappa_\lambda +\equiv +\boldsymbol{\kappa}\cdot\nabla\lambda, +\qquad +\boldsymbol{\kappa}\equiv(\mathbf b\cdot\nabla)\mathbf b, +\qquad +\mathbf b\equiv\frac{\mathbf B}{B}. +\end{equation} +This is the magnetic-field-line curvature projected along the chosen flux-gradient +direction. + +\color{black} +In DCON coordinates we take $\lambda=\psi$, so +\begin{equation} +\kappa_\psi\equiv\boldsymbol{\kappa}\cdot\nabla\psi, +\qquad +p'\equiv\frac{dp}{d\psi}. +\end{equation} +Both $p'$ and $\kappa_\psi$ depend on this flux-label normalization, while their product +$p'\kappa_\psi$ is the invariant pressure-curvature combination entering $K$. + +Using the equilibrium force balance +\begin{equation} +(\nabla\times\mathbf B)\times\mathbf B += +\mu_0\nabla p, +\qquad +\nabla p = p'(\psi)\nabla\psi, +\end{equation} +together with +\begin{equation} +(\nabla\times\mathbf B)\times\mathbf B += +(\mathbf B\cdot\nabla)\mathbf B +- +\nabla\left(\frac{B^2}{2}\right), +\end{equation} +we obtain +\begin{equation} +(\mathbf B\cdot\nabla)\mathbf B += +\mu_0\nabla p ++ +\nabla\left(\frac{B^2}{2}\right). +\end{equation} + +Taking the dot product with $\nabla\psi$ gives +\begin{equation} +B^2\kappa_\psi += +\mu_0 p' |\nabla\psi|^2 ++ +\frac{1}{2}\,\nabla B^2\cdot\nabla\psi. +\end{equation} + +Now expand +\begin{equation} +\nabla B^2 += +\frac{\partial B^2}{\partial\psi}\nabla\psi ++ +\frac{\partial B^2}{\partial\theta}\nabla\theta ++ +\frac{\partial B^2}{\partial\zeta}\nabla\zeta. +\end{equation} +For an axisymmetric tokamak equilibrium, $\partial B^2/\partial\zeta=0$, so +\begin{equation} +\nabla B^2\cdot\nabla\psi += +\frac{\partial B^2}{\partial\psi}g^{\psi\psi} ++ +\frac{\partial B^2}{\partial\theta}g^{\psi\theta}. +\end{equation} +Hence +\begin{equation} +\boxed{ +\kappa_\psi += +\frac{1}{B^2} +\left[ +\mu_0 p' g^{\psi\psi} ++ +\frac{1}{2}\frac{\partial B^2}{\partial\psi}g^{\psi\psi} ++ +\frac{1}{2}\frac{\partial B^2}{\partial\theta}g^{\psi\theta} +\right]. +} +\end{equation} +Equivalently, +\begin{equation} +\boxed{ +\kappa_\psi += +\frac{g^{\psi\psi}}{B^2} +\left[ +\mu_0 p' ++ +\frac{1}{2}\frac{\partial B^2}{\partial\psi} ++ +\frac{1}{2}\frac{\partial B^2}{\partial\theta} +\frac{g^{\psi\theta}}{g^{\psi\psi}} +\right]. +} +\end{equation} +Here $B^2$, $\partial B^2/\partial\psi$, and $\partial B^2/\partial\theta$ are evaluated +from the DCON field-strength expression in §3.3.1. + +\subsubsection{Final expression} + +\color{black} +Combining the DCON expressions above, we obtain +\begin{equation} +\boxed{ +K_{DCON} += +g^{\psi\psi}\sigma S_{\psi} ++\mu_0\sigma^2B^2 ++2p'\kappa_\psi. +} +\end{equation} +Here each flux-label dependent quantity is evaluated with the DCON normalized coordinate +$\psi$. Explicitly: +\begin{align} +K_{DCON}(\psi,\theta) +&= +g^{\psi\psi}\sigma +\frac{\chi'^2}{\mathcal J} +\left[ +q' ++ +\frac{\partial}{\partial\theta} +\left( +\frac{q\,g^{\psi\theta}-g^{\psi\zeta}}{g^{\psi\psi}} +\right) +\right] ++\mu_0\sigma^2B^2 +\\ +&\quad ++\frac{2p'}{B^2} +\left[ +\mu_0 p' g^{\psi\psi} ++ +\frac{1}{2}\frac{\partial B^2}{\partial\psi}g^{\psi\psi} ++ +\frac{1}{2}\frac{\partial B^2}{\partial\theta}g^{\psi\theta} +\right]. +\end{align} + +\section{Reconstruction of energy principle variables in DCON} + +\begin{equation} +\begin{aligned} +2\mu_0\delta W +&= \int \big[|\vec{C}|^2- \mu_0K(\vec{\xi} \cdot \hat{n})^2 +\gamma p \mu_0|\vec{\nabla} \cdot \vec{\xi}|^2 \big] dx^3\\ +&= \int d\psi d\theta d\zeta \mathcal{J} \Big\{ [ \nabla \xi_s \times \nabla \psi ++ (\nabla \theta \times \nabla \zeta)\,\mathcal{J}\,\mathbf{B}\cdot\nabla \xi_\psi +- \mathbf{B}\,\xi_\psi' \\ +&- +\left( + \chi'' \nabla \zeta \times \nabla \psi + + (q\chi')' \nabla \psi \times \nabla \theta +\right)\xi_\psi \\ +&+\frac{\mathcal J\xi_\psi}{|\nabla \psi|^2} [ +\mu_0j^\theta +\big( +\nabla\psi(\nabla\zeta\cdot\nabla\psi) +- +\nabla\zeta|\nabla\psi|^2 +\big)+\mu_0j^\zeta +\big( +\nabla\theta|\nabla\psi|^2 +- +\nabla\psi(\nabla\theta\cdot\nabla\psi) +\big)]]^2 \\ +&-\mu_0 K(\vec{\xi} \cdot \hat{n})^2 +\gamma p \mu_0|\vec{\nabla} \cdot \vec{\xi}|^2\} +\end{aligned} +\end{equation} + +\subsection{Reconstruction v1: real-space reconstruction on the $(\psi,\theta)$ grid} +\begin{align} +\xi_\psi = \sum_{m}^{} \sum_{n}^{} v_{m,n}(\psi) \cdot +\exp\!\bigl[2\pi i(m\theta -n\zeta)\bigr] \\ +\xi_s = \sum_{m}^{} \sum_{n}^{} u_{m,n}(\psi) \cdot +\exp\!\bigl[2\pi i(m\theta -n\zeta)\bigr] +\end{align} +Here $\xi_\psi\equiv\vec\xi\cdot\nabla\psi$ is the normal displacement amplitude used by DCON, and $\xi_s$ is the surface displacement variable. +\begin{align} +\vec{Q} &= \mathcal{J} (Q^{\psi} \nabla \theta \times \nabla \zeta + Q^{\theta} \nabla \zeta \times \nabla \psi + Q^{\zeta} \nabla \psi \times \nabla \theta) &= \mathcal{J} ( Q^{\psi} \vec{v_1} + Q^{\theta} \vec{v_2} + Q^{\zeta} \vec{v_3}) \\ +&= Q_1 \nabla \psi + Q_2 \nabla \theta + Q_3 \nabla \zeta +\end{align} + +\begin{equation} +Q^{\psi} = \frac{\chi^\prime}{\mathcal{J}} (\frac{\partial}{\partial \theta} + q \frac{\partial}{\partial \zeta} )\xi_\psi = \frac{\chi^\prime}{\mathcal{J}} \sum_{m}^{} \sum_{n}^{} v_{m,n}(\psi) \cdot \exp\!\bigl[2 \pi i(m\theta -n\zeta)\bigr]\cdot 2 \pi i (m-nq) +\end{equation} + +\begin{align} +Q^{\theta} &= - \frac{1}{\mathcal{J}} \frac{\partial}{\partial \psi}(\chi ^ \prime \xi_\psi) + \frac{1}{\mathcal{J}} \frac{\partial \xi_s}{\partial \zeta} \\ +&= - \frac{1}{\mathcal{J}} \sum_{m}^{} \sum_{n}^{} \left(\chi ^ \prime \frac{\partial}{\partial \psi}(v_{m,n}(\psi)) + \chi^{\prime\prime}v_{m,n}(\psi) + 2\pi i n u_{m,n}(\psi) \right) \cdot \exp\!\bigl[2\pi i(m\theta -n\zeta)\bigr] +\end{align} + +\begin{align} +Q^{\zeta} +&= - \frac{1}{\mathcal{J}} \frac{\partial}{\partial \psi} (\chi^\prime q \xi_\psi) + -\frac{1}{\mathcal{J}} \frac{\partial \xi_s}{\partial \theta} \\ +&= - \frac{1}{\mathcal{J}} \sum_{m} \sum_{n} + \left( \chi ^ \prime q \frac{\partial v_{m,n}(\psi)}{\partial \psi} + + (\chi^{\prime\prime}q+\chi^\prime q^\prime) v_{m,n}(\psi) + + 2\pi i m u_{m,n}(\psi) \right) + \exp\!\bigl(2 \pi i(m\theta - n\zeta)\bigr). +\end{align} + +Then the current vector can be reconstructed by the Grad-Shafranov equation: +\begin{align} +\mu_0 \vec{j} &= \mathcal{J} (\mu_0 j^{\psi} \nabla \theta \times \nabla \zeta + \mu_0 j^{\theta} \nabla \zeta \times \nabla \psi + \mu_0 j^{\zeta} \nabla \psi \times \nabla \theta) \\ +&= \mathcal{J} (\mu_0 j^{\psi} \vec{v_1} + \mu_0 j^{\theta} \vec{v_2} + \mu_0 j^{\zeta} \vec{v_3})\\ +&=\mathcal{J} (\mu_0 j^{\theta} \nabla \zeta \times \nabla \psi + \mu_0 j^{\zeta} \nabla \psi \times \nabla \theta) +\end{align} +\begin{equation} + \mu_0 j^\theta = \mu_0 j \cdot \nabla \theta = - 2 \pi f^\prime / \mathcal{J}, \mu_0 j^\zeta = \mu_0 j \cdot \nabla \zeta = - \mu_0 p^\prime / \chi^\prime - 2 \pi f^\prime q / \mathcal{J} +\end{equation} +Now we can reconstruct the cross term in the energy principle: +\begin{align} +\vec{C} &\equiv \vec{Q} + (\vec{\xi} \cdot \hat{n} ) (\mu_0 \vec{j} \times \hat{n}) \\ +&= \mathcal{J} ( C^\psi \vec{v}_1 + C^\theta \vec{v}_2 + C^\zeta \vec{v}_3 )\\ +&=C_1 \nabla\psi + C_2 \nabla\theta + C_3 \nabla\zeta +\end{align} +\begin{equation} +\begin{aligned} +C^\psi &= \vec{C}\cdot\nabla\psi = Q^\psi +&\qquad +C_1 &= Q_1 + \frac{\mathcal{J}\xi_\psi}{|\nabla\psi|^2} +\bigl[ \mu_0 j^\theta (\nabla\psi\cdot\nabla\zeta) +- \mu_0 j^\zeta (\nabla\psi\cdot\nabla\theta) \bigr] \\ +C^\theta &= \vec{C}\cdot\nabla\theta = Q^\theta + \xi_\psi \frac{1}{|\nabla\psi|^2 \mathcal{J}}(\mu_0j^\theta g_{23} + \mu_0j^\zeta g_{33}) +&\qquad +C_2 &= Q_2 + \mathcal{J}\xi_\psi \mu_0 j^\zeta \\ +C^\zeta &= \vec{C}\cdot\nabla\zeta = Q^\zeta - \xi_\psi \frac{1}{|\nabla\psi|^2 \mathcal{J}}(\mu_0j^\theta g_{22} + \mu_0j^\zeta g_{23}) +&\qquad +C_3 &= Q_3 - \mathcal{J}\xi_\psi \mu_0 j^\theta +\end{aligned} +\end{equation} + +The correction term that turns the bare perturbed field $\vec Q$ into the effective field +$\vec C$, namely $\vec V\equiv\vec C-\vec Q=(\vec\xi\cdot\hat n)(\mu_0\vec j\times\hat n)$, has +contravariant and covariant components ($\vec V=V_i\nabla x^i$): +\begin{equation} +\begin{aligned} +V^\psi &= 0 +&\qquad +V_\psi &= \frac{\mathcal{J}\xi_\psi}{|\nabla\psi|^2} +\bigl[ \mu_0 j^\theta (\nabla\psi\cdot\nabla\zeta) +- \mu_0 j^\zeta (\nabla\psi\cdot\nabla\theta) \bigr] \\ +V^\theta &= \frac{\xi_\psi}{|\nabla\psi|^2 \mathcal{J}} +(\mu_0j^\theta g_{23} + \mu_0j^\zeta g_{33}) +&\qquad +V_\theta &= \mathcal{J}\xi_\psi\,\mu_0 j^\zeta \\ +V^\zeta &= -\frac{\xi_\psi}{|\nabla\psi|^2 \mathcal{J}} +(\mu_0j^\theta g_{22} + \mu_0j^\zeta g_{23}) +&\qquad +V_\zeta &= -\mathcal{J}\xi_\psi\,\mu_0 j^\theta +\end{aligned} +\label{eq:Vcomp} +\end{equation} + +For direct reconstruction on the $(\psi,\theta)$ grid, the energy density is closed by the metric contraction +\begin{equation} +|\vec C|^2 += +g_{ij}C^{i*}C^j += +C_i^*C^i, +\qquad +C_i=g_{ij}C^j, +\qquad +i,j\in\{\psi,\theta,\zeta\}. +\end{equation} +Equivalently, +\begin{equation} +\begin{aligned} +|\vec C|^2 +&= +g_{\psi\psi}|C^\psi|^2 ++g_{\theta\theta}|C^\theta|^2 ++g_{\zeta\zeta}|C^\zeta|^2 \\ +&\quad ++2\operatorname{Re}\!\left[ +g_{\psi\theta}C^{\psi*}C^\theta ++g_{\psi\zeta}C^{\psi*}C^\zeta ++g_{\theta\zeta}C^{\theta*}C^\zeta +\right]. +\end{aligned} +\end{equation} + +\paragraph{Verification: $\vec C$ as an effective field and the $\nabla\times\vec C$ check.} +The reconstruction is closed by a self-consistency test of the condition +$(\nabla\times\vec C)\cdot\nabla\psi=0$, which holds on every flux surface and admits both a +variational and a physical reading. \emph{Variationally}, it is the Euler--Lagrange +equation obtained when the surface displacement $\xi_s$ is eliminated by minimizing +$\delta W$ at fixed $\xi_\psi$: because $\xi_s$ enters the energy only through +$\nabla\xi_s\times\nabla\psi$ in $|\vec C|^2$---the compressional term +$\gamma p\,|\nabla\cdot\vec\xi|^2$ being carried by a separate (parallel) displacement +component---the stationarity condition is $\nabla\cdot(\vec C\times\nabla\psi)=0$, i.e.\ +$(\nabla\times\vec C)\cdot\nabla\psi=0$. + +\emph{Physically}, in analogy with Amp\`ere's law the curl of $\vec C$ defines an effective +perturbed current, +\begin{equation} +\nabla\times\vec B=\mu_0\vec j +\quad\Longrightarrow\quad +\vec j_{\mathrm{eff}}\equiv\frac{\nabla\times\vec C}{\mu_0}, +\end{equation} +so the reconstruction condition states exactly that this effective current is tangent to the +flux surface, $(\nabla\times\vec C)\cdot\nabla\psi=0\Leftrightarrow\vec j_{\mathrm{eff}}\cdot\nabla\psi=0$. In the equilibrium the current +lies in flux surfaces, $\vec j\cdot\nabla\psi=0$. Under an ideal-MHD displacement the flux +is frozen into the fluid, so the equilibrium current is convected with the displaced surface +$\Psi=\psi-\vec\xi\cdot\nabla\psi$ (the Lagrangian picture) and keeps no component through it. +Referred instead to the \emph{fixed} (Eulerian) surface $\psi$ that DCON uses, this convected +current acquires an \emph{apparent} normal component; with $\vec Q=\delta\vec B$, +\begin{equation} +\frac{1}{\mu_0}(\nabla\times\vec Q)\cdot\nabla\psi +=\nabla\cdot\!\big[(\vec\xi\cdot\nabla\psi)\,\vec j\big] +=-\,\nabla\psi\cdot\nabla\times\!\big[\xi_n(\vec j\times\hat n)\big]\neq0, +\end{equation} +where the middle equality uses $\nabla\cdot(a\vec j)=\vec j\cdot\nabla a$ (as +$\nabla\cdot\vec j=0$), and the last uses $\nabla\psi\cdot\nabla\times\vec G=\nabla\cdot(\vec G\times\nabla\psi)$ +(valid since $\nabla\times\nabla\psi=0$) together with $\nabla\psi=|\nabla\psi|\hat n$ and the +triple product $(\vec j\times\hat n)\times\hat n=(\vec j\cdot\hat n)\hat n-\vec j=-\vec j$ +(because $\vec j\cdot\hat n=0$), giving +$\nabla\cdot[(\vec\xi\cdot\nabla\psi)\vec j]=-\nabla\psi\cdot\nabla\times[\xi_n(\vec j\times\hat n)]$. +This apparent normal is a frame effect---the frozen current viewed against the unperturbed +surface. The cross term $\xi_n(\mu_0\vec j\times\hat n)$ in the definition of $\vec C$ is +exactly this convective (Lagrangian$\leftrightarrow$Eulerian) contribution and cancels it, +\begin{equation} +(\nabla\times\vec C)\cdot\nabla\psi +=(\nabla\times\vec Q)\cdot\nabla\psi ++\mu_0\,\nabla\psi\cdot\nabla\times\!\big[\xi_n(\vec j\times\hat n)\big]=0. +\end{equation} +Hence $\vec C$ is an \emph{effective perturbed magnetic field}---$\vec Q$ with the +frozen-current artifact removed, so that its curl is a genuine surface current on the +\emph{fixed} (Eulerian) flux surface (Fig.~\ref{fig:C_frame}); where $\vec j=0$ (e.g.\ outside the plasma) the +correction drops and $\vec C=\vec Q$. (See Boozer~\cite{RevModPhys.76.1071}; +Park~\cite{Park2009Ideal}, \S2.2.) + +Table~\ref{tab:QvsC} contrasts the bare perturbed field with the effective field. + +\begin{figure} + \centering + \includegraphics[width=0.8\linewidth]{QvsC_quant_n1.png} + \caption{Quantitative check of $(\nabla\times\vec C)\cdot\nabla\psi=0$ on the actual run + (DIII-D \#147131, $n=1$). The real normal effective-current density + $(\nabla\times\,\cdot\,)\cdot\nabla\psi$ (at $\phi=0$) on the equilibrium flux grid for + (a)~the bare perturbed field $\vec Q=\delta\vec B$ and (b)~the effective field + $\vec C=\vec Q+\vec V$, \emph{both in the fixed (Eulerian) frame}. $\vec Q$'s effective + current pierces the flux surface (RMS $=2.1\times10^{-3}$) while $\vec C$'s is tangent to + numerical roundoff (RMS $=2.1\times10^{-12}$). Computed spectrally from the code's + covariant field components ($Q_i,C_i$) and validated against the \texttt{C verify} diagnostic; the + near-axis speckle is a $1/|\nabla\psi|$ reconstruction artifact.} + \label{fig:C_frame} +\end{figure} + +\begin{table}[ht] +\centering +\renewcommand{\arraystretch}{1.3} +\begin{tabular}{l >{\raggedright\arraybackslash}p{0.32\linewidth} >{\raggedright\arraybackslash}p{0.38\linewidth}} +\hline\hline + & $\vec Q=\delta\vec B$ & $\vec C=\vec Q+\vec V$ \\ +\hline +Name & perturbed field (Eulerian $\delta\vec B$) & effective perturbed field ($\delta\vec B+\text{correction}$) \\ +Normal field (flux) & $b_n$ & $b_n$\quad\emph{(identical)} \\ +$\nabla\times$ on fixed $\psi$ & not tangent --- apparent normal current & tangent: $(\nabla\times\vec C)\cdot\nabla\psi=0$ \\ +\hline\hline +\end{tabular} +\caption{The bare perturbed field $\vec Q=\delta\vec B$ versus the effective perturbed +field $\vec C=\vec Q+\vec V$. Both carry the \emph{same} normal field (flux) $b_n$ and differ +only in their tangential (surface-current) part, so that only $\nabla\times\vec C$ is tangent +to the fixed flux surface ($\vec j_{\mathrm{eff}}\cdot\nabla\psi=0$).} +\label{tab:QvsC} +\end{table} + +The code's \texttt{C verify} check tests this condition on the reconstructed components +through its flux-coordinate form +\begin{equation} +(\nabla\times \vec C)\cdot \nabla \psi += +\frac{1}{\mathcal{J}} +\left( +\frac{\partial C_\zeta}{\partial \theta} +- +\frac{\partial C_\theta}{\partial \zeta} +\right), +\end{equation} +with $C_\theta,C_\zeta$ the covariant components of +$\vec C=C_\psi\nabla\psi+C_\theta\nabla\theta+C_\zeta\nabla\zeta$; it thus verifies that the +reconstructed components satisfy this cancellation to numerical roundoff on each flux +surface. + +\subsection{Reconstruction v2: radial matrix representation of $\delta W$} +In this part, we show that the reconstructed Bernstein form can be written in the same radial matrix structure as the DCON/Newcomb formulation. +\begin{equation} +\boxed{\;\vec v_i\cdot\vec v_j=\frac{g_{ij}}{\mathcal{J}^2}\;} +\end{equation} +Here $g_{ij}$ denotes the true covariant metric used in the previous sections. +The DCON matrix coefficients use the Fourier projection of $\vec v_i\cdot\vec v_j$, not of $g_{ij}$ itself. + +\begin{equation} +\boxed{ +\left\langle F \right\rangle_{mm'} +\equiv \int_{0}^{1} d\theta\; +\mathcal{J}(\psi,\theta)\, +\exp\!\bigl(2\pi i(m'-m)\theta\bigr)\, +F(\psi,\theta) +} +\end{equation} + +\begin{equation} +\boxed{ +(G_{ij})_{mm'} \equiv \left\langle \frac{g_{ij}}{\mathcal{J}^2} \right\rangle_{mm'} +} +\end{equation} +where $G_{ij}$ is the DCON matrix representation of $\vec v_i\cdot\vec v_j$ in Fourier space. + +The reconstructed Bernstein form is written in the DCON/Newcomb radial matrix +form as +\begin{equation} +\boxed{ +\begin{aligned} +2\mu_0\delta W +&= +\int d\psi\, +\Big[ +\Xi_s^\dagger \mathbf A \Xi_s ++\Xi_s^\dagger(\mathbf B\Xi_\psi' + \mathbf C\Xi_\psi) ++(\Xi_\psi^{\prime\dagger}\mathbf B^\dagger+\Xi_\psi^\dagger\mathbf C^\dagger)\Xi_s \\ +&\qquad ++\Xi_\psi^{\prime\dagger}\mathbf D\Xi_\psi' ++\Xi_\psi^{\prime\dagger}\mathbf E\Xi_\psi ++\Xi_\psi^\dagger\mathbf E^\dagger\Xi_\psi' ++\Xi_\psi^\dagger\mathbf H\Xi_\psi +\Big]. +\end{aligned} +} +\end{equation} +Here +\begin{equation} +\Xi_s\equiv\{u_m\}, +\qquad +\Xi_\psi\equiv\{v_m\}, +\qquad +\mathbf M_{mm'}=m\delta_{mm'}, +\qquad +\mathbf Q_{mm'}=(m-nq)\delta_{mm'}. +\end{equation} +We also use +\begin{equation} +\mathbf J\equiv\langle 1\rangle, +\qquad +\mathbf J'\equiv\left\langle \frac{\mathcal J'}{\mathcal J}\right\rangle, +\qquad +\mathbf I_{mm'}=\delta_{mm'}. +\end{equation} +With the DCON-normalized flux scale $v'\leftrightarrow\chi'$ and $P'\leftrightarrow\mu_0p'$, the coefficient matrices are +\begin{equation} +\boxed{ +\begin{aligned} +\mathbf A +&=(2\pi)^2 +\left[ +n(n\mathbf G_{22}+\mathbf G_{23}\mathbf M) ++\mathbf M(n\mathbf G_{23}+\mathbf G_{33}\mathbf M) +\right],\\ +\mathbf B +&=-2\pi i\chi' +\left[ +n(\mathbf G_{22}+q\mathbf G_{23}) ++\mathbf M(\mathbf G_{23}+q\mathbf G_{33}) +\right],\\ +\mathbf C +&=-2\pi i +\left[ +\chi''(n\mathbf G_{22}+\mathbf M\mathbf G_{23}) ++(q\chi')'(n\mathbf G_{23}+\mathbf M\mathbf G_{33}) +\right] \\ +&\quad +-(2\pi)^2\chi'(n\mathbf G_{12}+\mathbf M\mathbf G_{31})\mathbf Q ++2\pi i +\left( +2\pi f'\mathbf Q +-n\frac{\mu_0p'}{\chi'}\mathbf J +\right),\\ +\mathbf D +&=\chi'^2 +\left[ +(\mathbf G_{22}+q\mathbf G_{23}) ++q(\mathbf G_{23}+q\mathbf G_{33}) +\right],\\ +\mathbf E +&=\chi' +\left[ +\chi''(\mathbf G_{22}+q\mathbf G_{23}) ++(q\chi')'(\mathbf G_{23}+q\mathbf G_{33}) +\right] \\ +&\quad +-2\pi i\chi'^2(\mathbf G_{12}+q\mathbf G_{31})\mathbf Q ++\mu_0p'\mathbf J,\\ +\mathbf H +&= +\chi'' +\left[ +\chi''\mathbf G_{22}+(q\chi')'\mathbf G_{23} +\right] ++(q\chi')' +\left[ +\chi''\mathbf G_{23}+(q\chi')'\mathbf G_{33} +\right] \\ +&\quad ++2\pi i\chi' +\left[ +\chi''(\mathbf M\mathbf G_{12}-\mathbf G_{12}\mathbf M) ++(q\chi')'(\mathbf M\mathbf G_{31}-\mathbf G_{31}\mathbf M) +\right] \\ +&\quad ++(2\pi\chi')^2\mathbf Q\mathbf G_{11}\mathbf Q ++\left[ +\mu_0p'\left(\frac{\chi''}{\chi'}\mathbf J+\mathbf J'\right) +-2\pi f'q'\chi'\mathbf I +\right]. +\end{aligned} +} +\end{equation} + +Decompose the $\vec C$ term as +\begin{equation} +\begin{aligned} +&\mathbf X \equiv \nabla \xi_s \times \nabla \psi,\\ +&\mathbf Y \equiv (\nabla \theta \times \nabla \zeta)\, \mathcal J \, \mathbf B \cdot \nabla \xi_\psi,\\ +&\mathbf Z \equiv -\mathbf B\, \xi_\psi',\\ +&\mathbf U \equiv -\left( +\chi'' \nabla \zeta \times \nabla \psi ++ +(q\chi')' +\nabla \psi \times \nabla \theta +\right)\xi_\psi,\\ +&\mathbf V \equiv +\frac{\xi_\psi}{|\nabla \psi|^2} +\, \mu_0 \mathbf j \times \nabla \psi . +\end{aligned} +\end{equation} + +\noindent Define +\begin{equation} +\mathbf Q \equiv \mathbf X+\mathbf Y+\mathbf Z+\mathbf U, +\qquad +\vec C=\mathbf Q+\mathbf V . +\label{eq:CeqQplusV} +\end{equation} +Then the Bernstein part of the energy is +\begin{equation} +\begin{aligned} +\int d^3x\left(|\vec C|^2-\mu_0K\frac{|\xi_\psi|^2}{|\nabla\psi|^2}\right) +&= +\int d\psi d\theta d\zeta\,\mathcal J +\Big[ +|\mathbf Q|^2 ++\mathbf Q^*\cdot\mathbf V+\mathbf V^*\cdot\mathbf Q \\ +&\qquad ++|\mathbf V|^2 +-\mu_0K\frac{|\xi_\psi|^2}{|\nabla\psi|^2} +\Big]. +\end{aligned} +\end{equation} +Using \(e_m\equiv e^{2\pi i(m\theta-n\zeta)}\), the four $\mathbf Q$ pieces are +\begin{equation} +\begin{aligned} +\mathbf X +&= +-2\pi i\sum_m u_m e_m\,(n\vec v_2+m\vec v_3),\\ +\mathbf Y +&= +2\pi i\chi'\sum_m (m-nq)v_m e_m\,\vec v_1,\\ +\mathbf Z +&= +-\chi'\sum_m v_m' e_m\,(\vec v_2+q\vec v_3),\\ +\mathbf U +&= +-\sum_m v_m e_m\, +\left[\chi''\vec v_2+(q\chi')'\vec v_3\right]. +\end{aligned} +\end{equation} +Therefore each bilinear product has a fixed matrix block: +\begin{equation} +\begin{array}{c|c|c} +\text{product} & \text{radial amplitudes} & \text{matrix block} \\ \hline +\mathbf X^*\!\cdot\mathbf X & \Xi_s^\dagger\Xi_s & \mathbf A\\ +\mathbf X^*\!\cdot\mathbf Z+\mathbf Z^*\!\cdot\mathbf X +& \Xi_s^\dagger\Xi_\psi' + \Xi_\psi^{\prime\dagger}\Xi_s & \mathbf B\\ +\mathbf X^*\!\cdot(\mathbf Y+\mathbf U+\mathbf V) ++(\mathbf Y+\mathbf U+\mathbf V)^*\!\cdot\mathbf X +& \Xi_s^\dagger\Xi_\psi + \Xi_\psi^\dagger\Xi_s & \mathbf C\\ +\mathbf Z^*\!\cdot\mathbf Z & \Xi_\psi^{\prime\dagger}\Xi_\psi' & \mathbf D\\ +\mathbf Z^*\!\cdot(\mathbf Y+\mathbf U+\mathbf V) ++(\mathbf Y+\mathbf U+\mathbf V)^*\!\cdot\mathbf Z +& \Xi_\psi^{\prime\dagger}\Xi_\psi + \Xi_\psi^\dagger\Xi_\psi' & \mathbf E\\ +\mathbf Y,\mathbf U,\mathbf V,K\ \text{pure terms} +& \Xi_\psi^\dagger\Xi_\psi & \mathbf H . +\end{array} +\end{equation} + +This is the actual route from the real-space Bernstein expression to the radial matrix form. The factors \(m\) and \(m-nq\) are then written as the diagonal matrices \(\mathbf M\) and \(\mathbf Q\). +For instance, +\begin{equation} +\begin{aligned} +\int d\theta\,\mathcal J\,\mathbf X^*\cdot\mathbf X +&= +\Xi_s^\dagger +(2\pi)^2 +\left[ +n^2\mathbf G_{22} +{}+n\mathbf G_{23}\mathbf M +{}+n\mathbf M\mathbf G_{23} +{}+\mathbf M\mathbf G_{33}\mathbf M +\right]\Xi_s,\\ +\int d\theta\,\mathcal J\,\mathbf X^*\cdot\mathbf Z +&= +\Xi_s^\dagger +\left\{ +-2\pi i\chi' +\left[ +n(\mathbf G_{22}+q\mathbf G_{23}) +{}+\mathbf M(\mathbf G_{23}+q\mathbf G_{33}) +\right] +\right\} +\Xi_\psi'. +\end{aligned} +\end{equation} +The same projection rule gives \(\mathbf C_1,\mathbf C_2,\mathbf D,\mathbf E_1,\mathbf E_2,\mathbf H_1,\mathbf H_2,\mathbf H_3\) below. The only nontrivial part is the current term \(\mathbf V\), because its pure-\(\Xi_\psi\) pieces must be recombined with the scalar Bernstein term involving \(K\). + +\begin{equation} +\begin{aligned} +2\mu_0\delta W_{\mathrm{term1}} & = \int d\psi d\theta d\zeta \mathcal{J} [|\vec{C}|^2]\\ +&= \int d\psi d\theta d\zeta \mathcal{J}\{ |\mathbf Q|^2+ +\left[\mathbf Q\cdot\mathbf V^* + \mathbf V \cdot \mathbf Q^* +\right]+|\mathbf V|^2\} . +\end{aligned} +\end{equation} + +\paragraph{Correction of the $\mu_0\vec j\times\nabla\psi$ decomposition} + +Before continuing, one algebraic correction is needed. +From +\begin{equation} +\vec v_2 \times \nabla\psi += +\frac{1}{\mathcal J}(g_{23}\vec v_2-g_{22}\vec v_3), +\end{equation} +and +\begin{align} +\vec v_3 \times \nabla\psi +&= +\vec v_3\times \mathcal J(\vec v_2\times \vec v_3) \\ +&= +\mathcal J\big[\vec v_2(\vec v_3\cdot \vec v_3)-\vec v_3(\vec v_3\cdot \vec v_2)\big] \\ +&= +\mathcal J\left[ +\vec v_2\left(\frac{g_{33}}{\mathcal J^2}\right) +- +\vec v_3\left(\frac{g_{23}}{\mathcal J^2}\right) +\right] \\ +&= +\frac{1}{\mathcal J}(g_{33}\vec v_2-g_{23}\vec v_3), +\end{align} +we obtain +\begin{align} +\mu_0\vec j\times \nabla\psi +&= +\mathcal J\mu_0j^\theta(\vec v_2\times \nabla\psi) ++\mathcal J\mu_0j^\zeta(\vec v_3\times \nabla\psi) \\ +&= +(\mu_0j^\theta g_{23}+\mu_0j^\zeta g_{33})\vec v_2 +- +(\mu_0j^\theta g_{22}+\mu_0j^\zeta g_{23})\vec v_3. +\end{align} + +Therefore +\begin{equation} +\boxed{ +\mathbf V += +\frac{\xi_\psi}{|\nabla\psi|^2} +\left[ +(\mu_0j^\theta g_{23}+\mu_0j^\zeta g_{33})\vec v_2 +- +(\mu_0j^\theta g_{22}+\mu_0j^\zeta g_{23})\vec v_3 +\right]. +} +\end{equation} + +For compactness, define +\begin{equation} +\mathcal A_2 \equiv \mu_0j^\theta g_{23}+\mu_0j^\zeta g_{33}, +\qquad +\mathcal A_3 \equiv \mu_0j^\theta g_{22}+\mu_0j^\zeta g_{23}, +\end{equation} +so that +\begin{equation} +\mathbf V += +\frac{\xi_\psi}{|\nabla\psi|^2} +\left( +\mathcal A_2 \vec v_2-\mathcal A_3 \vec v_3 +\right). +\end{equation} + +We will also use the cofactor identities of the covariant metric: +\begin{equation} +\boxed{ +g_{22}g_{33}-g_{23}^2=\mathcal J^2|\nabla\psi|^2, +} +\end{equation} +\begin{equation} +\boxed{ +g_{12}g_{23}-g_{31}g_{22} += +\mathcal J^2(\nabla\zeta\cdot\nabla\psi), +} +\end{equation} +\begin{equation} +\boxed{ +g_{12}g_{33}-g_{31}g_{23} += +-\mathcal J^2(\nabla\theta\cdot\nabla\psi). +} +\end{equation} + +\paragraph{Blocks from \(\mathbf Q^*\cdot\mathbf Q\)} + +Applying the projection rule above to the products that do not contain \(\mathbf V\) gives the following blocks: +\begin{equation} +|\mathbf X|^2 \leadsto \Xi_s^\dagger \mathbf A \Xi_s, +\qquad +\mathbf A=(2\pi)^2\left[n^2 \mathbf G_{22}+n \mathbf G_{23}\mathbf M+n \mathbf M \mathbf G_{23}+\mathbf M \mathbf G_{33}\mathbf M\right]. +\end{equation} +\begin{equation} +\mathbf X^* \cdot \mathbf Z + \mathbf Z^* \cdot \mathbf X +\leadsto +\Xi_s^\dagger \mathbf B \Xi_\psi^\prime + \Xi_\psi^{\prime\dagger} \mathbf B^\dagger \Xi_s, +\qquad +\mathbf B=-2\pi i\chi' \left[n(\mathbf G_{22}+q\mathbf G_{23})+\mathbf M(\mathbf G_{23}+q\mathbf G_{33})\right]. +\end{equation} +\begin{equation} +\mathbf X^* \cdot \mathbf Y + \mathbf Y^* \cdot \mathbf X +\leadsto +\Xi_s^\dagger \mathbf C_1 \Xi_\psi + \Xi_\psi^\dagger \mathbf C_1^\dagger \Xi_s, +\qquad +\mathbf C_1=-(2\pi)^2\chi'(\mathbf M \mathbf G_{31}+n\mathbf G_{12})\mathbf Q. +\end{equation} +\begin{equation} +\mathbf X^* \cdot \mathbf U + \mathbf U^* \cdot \mathbf X +\leadsto +\Xi_s^\dagger \mathbf C_2 \Xi_\psi + \Xi_\psi^\dagger \mathbf C_2^\dagger \Xi_s, +\qquad +\mathbf C_2=-2\pi i \left[\chi''(n\mathbf G_{22}+\mathbf M \mathbf G_{23})+(q\chi')'(n\mathbf G_{23}+\mathbf M \mathbf G_{33})\right]. +\end{equation} +\begin{equation} +|\mathbf Y|^2 \leadsto \Xi_\psi^\dagger \mathbf H_1 \Xi_\psi, +\qquad +\mathbf H_1=(2\pi\chi')^2\mathbf Q\mathbf G_{11}\mathbf Q. +\end{equation} +\begin{equation} +\mathbf Y^* \cdot \mathbf Z + \mathbf Z^* \cdot \mathbf Y +\leadsto +\Xi_\psi^{\prime\dagger}\mathbf E_1 \Xi_\psi + \Xi_\psi^\dagger \mathbf E_1^\dagger \Xi_\psi^\prime, +\qquad +\mathbf E_1=-2\pi i\chi'^2(\mathbf G_{12}+q\mathbf G_{31})\mathbf Q. +\end{equation} +\begin{equation} +\mathbf Y^* \cdot \mathbf U + \mathbf U^* \cdot \mathbf Y +\leadsto +\Xi_\psi^\dagger \mathbf H_2 \Xi_\psi, +\qquad +\mathbf H_2=2\pi i\chi'\left[\chi''(\mathbf M \mathbf G_{12}-\mathbf G_{12}\mathbf M)+(q\chi')'(\mathbf M \mathbf G_{31}-\mathbf G_{31}\mathbf M)\right]. +\end{equation} +\begin{equation} +|\mathbf Z|^2 \leadsto \Xi_\psi^{\prime\dagger} \mathbf D \Xi_\psi^\prime, +\qquad +\mathbf D=\chi'^2\left[(\mathbf{G}_{22}+q\,\mathbf{G}_{23})+q(\mathbf{G}_{23}+q\,\mathbf{G}_{33})\right]. +\end{equation} +\begin{equation} +\mathbf Z^* \cdot \mathbf U + \mathbf U^* \cdot \mathbf Z +\leadsto +\Xi_\psi^{\prime\dagger}\mathbf E_2 \Xi_\psi + \Xi_\psi^\dagger \mathbf E_2^\dagger \Xi_\psi^\prime, +\qquad +\mathbf E_2=\chi'\left[\chi''(\mathbf{G}_{22}+q\,\mathbf{G}_{23})+(q\chi')'(\mathbf{G}_{23}+q\,\mathbf{G}_{33})\right]. +\end{equation} + +Finally, +\begin{equation} +|\mathbf U|^2 \leadsto \Xi_\psi^\dagger \mathbf H_3 \Xi_\psi, +\qquad +\mathbf{H}_3 += +\chi'' +\left[ +\chi''\,\mathbf{G}_{22} ++ +(q\chi')'\,\mathbf{G}_{23} +\right] ++ +(q\chi')' +\left[ +\chi''\,\mathbf{G}_{23} ++ +(q\chi')'\,\mathbf{G}_{33} +\right] +\end{equation} + +\paragraph{Bernstein-current correction terms} + +The vector $\mathbf V$ is not an additional physical term. It appears only because the Bernstein form rewrites the original current--pressure contribution into +\[ +\vec C=\vec Q+\frac{\xi_\psi}{|\nabla\psi|^2}\mu_0\vec j\times\nabla\psi +=\mathbf X+\mathbf Y+\mathbf Z+\mathbf U+\mathbf V. +\] +Therefore the $\mathbf V$-dependent pieces from $|\vec C|^2$ must be combined with the scalar term +$-\mu_0K|\xi_\psi|^2/|\nabla\psi|^2$. After this recombination, the same $\mathbf C$, $\mathbf E$, and $\mathbf H$ blocks in the DCON matrix form are obtained. + +The cross term $\mathbf X^* \cdot \mathbf V + \mathbf V^* \cdot \mathbf X$ produces the current part of the $\mathbf C$ block: +\[ +\Xi_s^\dagger \mathbf C_3 \Xi_\psi + \Xi_\psi^\dagger \mathbf C_3^\dagger \Xi_s, +\] +where $\mathbf C_3$ is obtained as follows. + +\begin{align} +\nabla \xi_s +&=\sum_m +\left[ +u_m' \nabla\psi ++ +2\pi i\,m\,u_m \nabla\theta +- +2\pi i\,n\,u_m \nabla\zeta +\right]e^{2\pi i(m\theta-n\zeta)} +\end{align} +\begin{align} +\mathbf X +&= +\nabla\xi_s\times\nabla\psi \\ +&= +\sum_m +2\pi i\,u_m e^{2\pi i(m\theta-n\zeta)} +\left( +m\,\nabla\theta\times\nabla\psi +- +n\,\nabla\zeta\times\nabla\psi +\right)\\ +\mathbf X^* +&= +\sum_m +(-2\pi i)\,u_m^* e^{-2\pi i(m\theta-n\zeta)} +\left( +m\,\nabla\theta\times\nabla\psi +- +n\,\nabla\zeta\times\nabla\psi +\right) \\ +\mathbf V +&= +\frac{\xi_\psi}{|\nabla\psi|^2}\,\mu_0\mathbf j\times\nabla\psi += +\sum_{m'} +v_{m'} e^{2\pi i(m'\theta-n\zeta)} +\frac{1}{|\nabla\psi|^2} +\mu_0\mathbf j\times\nabla\psi\\ +\mathbf V^* +&= +\frac{\xi_\psi^*}{|\nabla\psi|^2}\,\mu_0\mathbf j\times\nabla\psi += +\sum_{m'} +v_{m'}^* e^{-2\pi i(m'\theta-n\zeta)} +\frac{1}{|\nabla\psi|^2} +\mu_0\mathbf j\times\nabla\psi +\end{align} + +Therefore +\begin{align} +\mathbf X^*\cdot \mathbf V +&= +\sum_{m,m'} +(-2\pi i)\, +u_m^* v_{m'}\, +e^{2\pi i(m'-m)\theta} +\frac{\mu_0}{|\nabla\psi|^2} +\Big[ +m(\nabla\theta\times\nabla\psi)\cdot(\mathbf j\times\nabla\psi) +- +n(\nabla\zeta\times\nabla\psi)\cdot(\mathbf j\times\nabla\psi) +\Big] +\end{align} + +Now use +$(\mathbf a\times\mathbf c)\cdot(\mathbf b\times\mathbf c)=(\mathbf a\cdot\mathbf b)|\mathbf c|^2-(\mathbf a\cdot\mathbf c)(\mathbf b\cdot\mathbf c)$ +with $\mathbf c=\nabla\psi$, together with $\mathbf j\cdot\nabla\psi=0$. Then +\begin{align} +(\nabla\theta\times\nabla\psi)\cdot(\mathbf j\times\nabla\psi) +&=(\nabla\theta\cdot\mathbf j)|\nabla\psi|^2-(\nabla\theta\cdot\nabla\psi)(\mathbf j\cdot\nabla\psi) &=j^\theta |\nabla\psi|^2\\ +(\nabla\zeta\times\nabla\psi)\cdot(\mathbf j\times\nabla\psi)&=j^\zeta |\nabla\psi|^2 +\end{align} + +Hence +\begin{align} +\mathbf X^*\cdot \mathbf V +&= +\sum_{m,m'} +(-2\pi i)\, +u_m^* v_{m'}\, +e^{2\pi i(m'-m)\theta}\, +\mu_0\left(mj^\theta-nj^\zeta\right) +\end{align} + +Now substitute the equilibrium current components. In the normalization used here, +\begin{equation} +\mu_0 j^\theta = -\frac{2\pi f'}{\mathcal J}, +\qquad +\mu_0 j^\zeta = -\frac{\mu_0 p'}{\chi'}-\frac{2\pi f' q}{\mathcal J} +\end{equation} +\begin{align} +\mu_0(mj^\theta-nj^\zeta) +&= +-\frac{2\pi f'}{\mathcal J}m ++ +n\left( +\frac{\mu_0 p'}{\chi'} ++ +\frac{2\pi f'q}{\mathcal J} +\right) \\ +&= +-\frac{2\pi f'}{\mathcal J}(m-nq) ++ +n\frac{\mu_0 p'}{\chi'} +\end{align} +Therefore +\begin{align} +(\mathbf C_3)_{mm'} +&= +\int_0^1 d\theta\; +\mathcal J\,e^{2\pi i(m'-m)\theta} +(-2\pi i) +\left[ +-\frac{2\pi f'}{\mathcal J}(m-nq) ++ +n\frac{\mu_0 p'}{\chi'} +\right] \\ +&= +\int_0^1 d\theta\; +e^{2\pi i(m'-m)\theta} +\left[ +(2\pi i)(2\pi f')(m-nq) +- +(2\pi i)n\frac{\mu_0 p'}{\chi'}\mathcal J +\right] +\end{align} + +\begin{equation} +\boxed{ +\mathbf C_3 += +2\pi i +\left( +2\pi f' \mathbf Q +- +n\frac{\mu_0 p'}{\chi'}\mathbf J +\right) +} +\end{equation} + +\medskip +\noindent\rule{\linewidth}{0.4pt} +\medskip + +Next, the $\mathbf Z$--$\mathbf V$ block produces the pressure part of the $\mathbf E$ block: +\[ +\Xi_\psi^{\prime\dagger}\mathbf E_3 \Xi_\psi + \Xi_\psi^\dagger \mathbf E_3^\dagger \Xi_\psi^\prime. +\] +\begin{align} +\mathbf Z^*\cdot \mathbf V +&= +\sum_{m,m'} +v_m^{\prime *}v_{m'}\,e^{2\pi i(m'-m)\theta} +\left[ +-\chi' +\frac{ +\mathcal A_2(g_{22}+qg_{23})-\mathcal A_3(g_{23}+qg_{33}) +}{ +\mathcal J^2|\nabla\psi|^2 +} +\right] +\end{align} +\begin{align} +\mathcal A_2(g_{22}+qg_{23})-\mathcal A_3(g_{23}+qg_{33}) +&= +(\mu_0j^\theta g_{23}+\mu_0j^\zeta g_{33})(g_{22}+qg_{23}) +- +(\mu_0j^\theta g_{22}+\mu_0j^\zeta g_{23})(g_{23}+qg_{33}) \\ +&= +(\mu_0j^\zeta-q\mu_0j^\theta)(g_{22}g_{33}-g_{23}^2) \\ +&= +(\mu_0j^\zeta-q\mu_0j^\theta)\mathcal J^2|\nabla\psi|^2 . +\end{align} +\begin{align} +\mathbf Z^*\cdot \mathbf V +&= +-\chi'(\mu_0j^\zeta-q\mu_0j^\theta) +\sum_{m,m'}v_m^{\prime *}v_{m'}\,e^{2\pi i(m'-m)\theta} \\ +&= +\mu_0 p'\sum_{m,m'}v_m^{\prime *}v_{m'}\,e^{2\pi i(m'-m)\theta}. +\end{align} +\begin{equation} +\boxed{ +\begin{aligned} +\mathbf Z^*\cdot \mathbf V+\mathbf V^*\cdot\mathbf Z +\leadsto\; +\Xi_\psi^{\prime\dagger}\mathbf E_3 \Xi_\psi ++ +\Xi_\psi^\dagger \mathbf E_3^\dagger \Xi_\psi^\prime, \\ +\qquad +\mathbf E_3 +&= +\mu_0p'\mathbf J . +\end{aligned} +} +\end{equation} + +\medskip +\noindent\rule{\linewidth}{0.4pt} +\medskip + +The term $\mathbf Y^*\cdot\mathbf V+\mathbf V^*\cdot\mathbf Y$ is also a +pure-$\Xi_\psi$ contribution, but it is not closed as an independent DCON +block. It must be combined with the other pure scalar current--pressure terms +before the final $\mathbf H_4$ block is obtained. + +\begin{align} +\mathbf Y +&= +(\nabla\theta\times\nabla\zeta)\,\mathcal J\,\mathbf B\cdot\nabla\xi_\psi= +2\pi i\,\chi' +\sum_m (m-nq)\,v_m\,e^{2\pi i(m\theta-n\zeta)}\,\vec v_1 \\ +\mathbf Y^*&= +(-2\pi i)\chi' +\sum_m (m-nq)\,v_m^*\,e^{-2\pi i(m\theta-n\zeta)}\,\vec v_1 +\end{align} + +Using the corrected form of $\mathbf V$, +\begin{align} +\mathbf Y^*\cdot\mathbf V +&= +(-2\pi i)\chi' +\sum_{m,m'} +v_m^*(m-nq)v_{m'} +e^{2\pi i(m'-m)\theta} +\; +\frac{1}{|\nabla\psi|^2}\, +\vec v_1\cdot(\mathcal A_2\vec v_2-\mathcal A_3\vec v_3) \\ +&= +(-2\pi i)\chi' +\sum_{m,m'} +v_m^*(m-nq)v_{m'} +e^{2\pi i(m'-m)\theta} +\; +\frac{1}{\mathcal J^2|\nabla\psi|^2} +\big( +\mathcal A_2 g_{12}-\mathcal A_3 g_{31} +\big) +\end{align} + +Now expand the bracket: +\begin{align} +\mathcal A_2 g_{12}-\mathcal A_3 g_{31} +&= +(\mu_0j^\theta g_{23}+\mu_0j^\zeta g_{33})g_{12} +- +(\mu_0j^\theta g_{22}+\mu_0j^\zeta g_{23})g_{31} \\ +&= +\mu_0j^\theta(g_{12}g_{23}-g_{31}g_{22}) ++ +\mu_0j^\zeta(g_{12}g_{33}-g_{31}g_{23}) \\ +&= +\mathcal J^2 +\left[ +\mu_0j^\theta(\nabla\zeta\cdot\nabla\psi) +- +\mu_0j^\zeta(\nabla\theta\cdot\nabla\psi) +\right] +\end{align} +\begin{equation} +\boxed{ +\begin{aligned} +\mathbf Y^*\cdot\mathbf V +&= +(-2\pi i)\chi' +\sum_{m,m'} +v_m^*(m-nq)v_{m'}e^{2\pi i(m'-m)\theta} \\ +&\qquad\times +\mu_0 +\frac{ +j^\theta(\nabla\zeta\cdot\nabla\psi) +- +j^\zeta(\nabla\theta\cdot\nabla\psi) +}{ +|\nabla\psi|^2 +}. +\end{aligned} +} +\end{equation} + +It is more useful to rewrite this in field form. Since +\begin{equation} +\mathcal J\,\mathbf B\cdot\nabla += +\chi'\left(\partial_\theta+q\partial_\zeta\right), +\end{equation} +we may write +\begin{align} +\mathcal J(\mathbf Y^*\cdot\mathbf V+\mathbf V^*\cdot\mathbf Y) +&= +\chi' +\left[ +(\partial_\theta+q\partial_\zeta)\xi_\psi^* +\right] +\xi_\psi\, +\mathcal F ++ +\chi'\, +\xi_\psi^* +\left[ +(\partial_\theta+q\partial_\zeta)\xi_\psi +\right]\mathcal F, +\end{align} +where +\begin{equation} +\mathcal F +\equiv +\mathcal J +\frac{ +(\mu_0 j^\theta)(\nabla\zeta\cdot\nabla\psi) +- +(\mu_0 j^\zeta)(\nabla\theta\cdot\nabla\psi) +}{ +|\nabla\psi|^2 +}. +\end{equation} + +Because the equilibrium is axisymmetric and periodic in $\theta,\zeta$, we may integrate by parts in $\theta$ and drop the boundary term: +\begin{align} +\int d\theta\,d\zeta\;\mathcal J(\mathbf Y^*\cdot\mathbf V+\mathbf V^*\cdot\mathbf Y) +&= +\chi'\int d\theta\,d\zeta\; +\partial_\theta(|\xi_\psi|^2)\,\mathcal F \\ +&= +-\chi' \int d\theta\,d\zeta\; +|\xi_\psi|^2\,\partial_\theta \mathcal F. +\end{align} + +Substituting $\mu_0 j^\theta$ and $\mu_0 j^\zeta$, +\begin{align} +\mathcal F +&= +\mathcal J +\left[ +-\frac{2\pi f'}{\mathcal J} +\frac{\nabla\zeta\cdot\nabla\psi}{|\nabla\psi|^2} ++ +\left( +\frac{\mu_0p'}{\chi'} ++ +\frac{2\pi f'q}{\mathcal J} +\right) +\frac{\nabla\theta\cdot\nabla\psi}{|\nabla\psi|^2} +\right] \\ +&= +2\pi f' +\frac{ +q(\nabla\psi\cdot\nabla\theta)-(\nabla\psi\cdot\nabla\zeta) +}{ +|\nabla\psi|^2 +} ++ +\frac{\mu_0p'}{\chi'}\, +\mathcal J\, +\frac{\nabla\psi\cdot\nabla\theta}{|\nabla\psi|^2}. +\end{align} + +Therefore +\begin{align} +\partial_\theta \mathcal F +&= +2\pi f'\, +\partial_\theta +\left[ +\frac{ +q(\nabla\psi\cdot\nabla\theta)-(\nabla\psi\cdot\nabla\zeta) +}{ +|\nabla\psi|^2 +} +\right] ++ +\frac{\mu_0p'}{\chi'}\, +\partial_\theta +\left[ +\mathcal J\frac{\nabla\psi\cdot\nabla\theta}{|\nabla\psi|^2} +\right]. +\end{align} + +Using the DCON-normalized shear parameter, +\begin{equation} +S_\psi += +\frac{\chi'^2}{\mathcal J} +\left[ +q' ++ +\partial_\theta +\left( +\frac{ +q(\nabla\psi\cdot\nabla\theta)-(\nabla\psi\cdot\nabla\zeta) +}{ +|\nabla\psi|^2 +} +\right) +\right], +\end{equation} +we obtain +\begin{equation} +\partial_\theta +\left[ +\frac{ +q(\nabla\psi\cdot\nabla\theta)-(\nabla\psi\cdot\nabla\zeta) +}{ +|\nabla\psi|^2 +} +\right] += +\frac{\mathcal J}{\chi'^2}S_\psi-q'. +\end{equation} + +Hence +\begin{align} +\partial_\theta\mathcal F +&= +\frac{2\pi f'}{\chi'^2}\,\mathcal J\,S_\psi +- +2\pi f'q' ++ +\frac{\mu_0p'}{\chi'}\, +\partial_\theta +\left[ +\mathcal J\frac{\nabla\psi\cdot\nabla\theta}{|\nabla\psi|^2} +\right]. +\end{align} + +The last derivative, +\begin{equation} +\partial_\theta +\left[ +\mathcal J\frac{\nabla\psi\cdot\nabla\theta}{|\nabla\psi|^2} +\right], +\end{equation} +is not a general coordinate identity and is not reduced here as a standalone +matrix block. Therefore +$\mathbf Y^*\cdot\mathbf V+\mathbf V^*\cdot\mathbf Y$ is kept as an +intermediate pure-$\Xi_\psi$ Bernstein scalar term. The closed DCON block is +obtained only after this term is combined below with +$\mathbf U^*\cdot\mathbf V+\mathbf V^*\cdot\mathbf U$, $|\mathbf V|^2$, and +the scalar $-\mu_0K|\xi_\psi|^2/|\nabla\psi|^2$ contribution. + +\medskip +\noindent\rule{\linewidth}{0.4pt} +\medskip + +\paragraph{The term $\mathbf U^*\cdot\mathbf V+\mathbf V^*\cdot\mathbf U$.} + +Since both $\mathbf U$ and $\mathbf V$ are proportional to $\xi_\psi$, this contribution is a pure-$\Xi_\psi$ block and can be written as +\[ +\Xi_\psi^\dagger \mathbf H_{UV} \Xi_\psi. +\] + +Recall +\begin{equation} +\mathbf U += +-\left( +\chi''\vec v_2+(q\chi')'\vec v_3 +\right)\xi_\psi. +\end{equation} +Hence +\begin{align} +\mathbf U^*\cdot\mathbf V +&= +-\frac{|\xi_\psi|^2}{|\nabla\psi|^2} +\left( +\chi''\vec v_2+(q\chi')'\vec v_3 +\right)\cdot +\left( +\mathcal A_2\vec v_2-\mathcal A_3\vec v_3 +\right) \\ +&= +-\frac{|\xi_\psi|^2}{\mathcal J^2|\nabla\psi|^2} +\left[ +\chi''(\mathcal A_2 g_{22}-\mathcal A_3 g_{23}) ++ +(q\chi')'(\mathcal A_2 g_{23}-\mathcal A_3 g_{33}) +\right]. +\end{align} + +Now simplify each bracket: +\begin{align} +\mathcal A_2 g_{22}-\mathcal A_3 g_{23} +&= +(\mu_0j^\theta g_{23}+\mu_0j^\zeta g_{33})g_{22} +- +(\mu_0j^\theta g_{22}+\mu_0j^\zeta g_{23})g_{23} \\ +&= +\mu_0j^\zeta(g_{22}g_{33}-g_{23}^2) \\ +&= +\mu_0j^\zeta \mathcal J^2 |\nabla\psi|^2, +\end{align} +and +\begin{align} +\mathcal A_2 g_{23}-\mathcal A_3 g_{33} +&= +(\mu_0j^\theta g_{23}+\mu_0j^\zeta g_{33})g_{23} +- +(\mu_0j^\theta g_{22}+\mu_0j^\zeta g_{23})g_{33} \\ +&= +-\mu_0j^\theta(g_{22}g_{33}-g_{23}^2) \\ +&= +-\mu_0j^\theta \mathcal J^2 |\nabla\psi|^2. +\end{align} +Therefore +\begin{align} +\mathbf U^*\cdot\mathbf V +&= +-|\xi_\psi|^2 +\left[ +\mu_0j^\zeta\chi'' +- +\mu_0j^\theta(q\chi')' +\right]. +\end{align} + +Substituting $j^\theta$ and $j^\zeta$, +\begin{align} +\mathbf U^*\cdot\mathbf V +&= +-|\xi_\psi|^2 +\left[ +\left( +-\frac{\mu_0p'}{\chi'}-\frac{2\pi f'q}{\mathcal J} +\right)\chi'' +- +\left( +-\frac{2\pi f'}{\mathcal J} +\right)(q\chi')' +\right] \\ +&= +|\xi_\psi|^2 +\left[ +\mu_0p'\frac{\chi''}{\chi'} ++ +\frac{2\pi f'}{\mathcal J} +\left( +q\chi''-(q\chi')' +\right) +\right]. +\end{align} +Using +\begin{equation} +(q\chi')'=q'\chi'+q\chi'', +\end{equation} +we obtain +\begin{equation} +\boxed{ +\mathbf U^*\cdot\mathbf V += +|\xi_\psi|^2 +\left[ +\mu_0p'\frac{\chi''}{\chi'} +- +\frac{2\pi f' q'\chi'}{\mathcal J} +\right]. +} +\end{equation} + +\begin{equation} +\boxed{ +\begin{aligned} +\mathbf U^*\cdot\mathbf V+\mathbf V^*\cdot\mathbf U +\leadsto\; +\Xi_\psi^\dagger \mathbf H_{UV}\Xi_\psi, \\ +\qquad +\mathbf H_{UV} +&= +2\mu_0p'\frac{\chi''}{\chi'}\,\mathbf J +-4\pi f'q'\chi'\,\mathbf I. +\end{aligned} +} +\end{equation} + +\medskip +\noindent\rule{\linewidth}{0.4pt} +\medskip + +\paragraph{The term $|\mathbf V|^2$.} + +This is again a pure-$\Xi_\psi$ contribution, so we write +\[ +\Xi_\psi^\dagger \mathbf H_V \Xi_\psi. +\] + +From +\begin{equation} +\mathbf V += +\frac{\xi_\psi}{|\nabla\psi|^2} +(\mathcal A_2\vec v_2-\mathcal A_3\vec v_3), +\end{equation} +we have +\begin{align} +|\mathbf V|^2 +&= +\frac{|\xi_\psi|^2}{|\nabla\psi|^4} +(\mathcal A_2\vec v_2-\mathcal A_3\vec v_3)\cdot +(\mathcal A_2\vec v_2-\mathcal A_3\vec v_3) \\ +&= +\frac{|\xi_\psi|^2}{\mathcal J^2|\nabla\psi|^4} +\left[ +\mathcal A_2^2 g_{22} +-2\mathcal A_2\mathcal A_3 g_{23} ++\mathcal A_3^2 g_{33} +\right]. +\end{align} + +Now expand the quadratic form: +\begin{align} +&\mathcal A_2^2 g_{22} +-2\mathcal A_2\mathcal A_3 g_{23} ++\mathcal A_3^2 g_{33} \\ +&= +(\mu_0j^\theta g_{23}+\mu_0j^\zeta g_{33})^2 g_{22} +-2(\mu_0j^\theta g_{23}+\mu_0j^\zeta g_{33}) +(\mu_0j^\theta g_{22}+\mu_0j^\zeta g_{23})g_{23} \\ +&\qquad ++(\mu_0j^\theta g_{22}+\mu_0j^\zeta g_{23})^2 g_{33} \\ +&= +(g_{22}g_{33}-g_{23}^2) +\left[ +\mu_0^2g_{22}(j^\theta)^2 ++ +2\mu_0^2g_{23}j^\theta j^\zeta ++ +\mu_0^2g_{33}(j^\zeta)^2 +\right]. +\end{align} +Therefore +\begin{align} +|\mathbf V|^2 +&= +\frac{|\xi_\psi|^2}{\mathcal J^2|\nabla\psi|^4} +(g_{22}g_{33}-g_{23}^2) +\left[ +\mu_0^2g_{22}(j^\theta)^2 ++ +2\mu_0^2g_{23}j^\theta j^\zeta ++ +\mu_0^2g_{33}(j^\zeta)^2 +\right] \\ +&= +\frac{|\xi_\psi|^2}{|\nabla\psi|^2} +\left[ +\mu_0^2g_{22}(j^\theta)^2 ++ +2\mu_0^2g_{23}j^\theta j^\zeta ++ +\mu_0^2g_{33}(j^\zeta)^2 +\right]. +\end{align} + +This is the completely explicit covariant-form result. + +It is also useful to rewrite it invariantly. Since +\begin{equation} +\mathbf j += +\sigma \mathbf B ++\frac{p'}{B^2}\mathbf B\times \nabla\psi, +\qquad +\sigma\equiv \frac{\mathbf j\cdot\mathbf B}{B^2}, +\end{equation} +one finds +\begin{equation} +\frac{|\mu_0\mathbf j\times\nabla\psi|^2}{|\nabla\psi|^2} += +\mu_0\sigma\,(\mu_0\mathbf j\cdot\mathbf B) ++ +\frac{(\mu_0p')^2}{B^2}|\nabla\psi|^2. +\end{equation} +Hence +\begin{equation} +\boxed{ +|\mathbf V|^2 += +|\xi_\psi|^2 +\left[ +\frac{\mu_0\sigma\,(\mu_0\mathbf j\cdot\mathbf B)}{|\nabla\psi|^2} ++ +\frac{(\mu_0p')^2}{B^2} +\right]. +} +\end{equation} + +\begin{equation} +\boxed{ +\begin{aligned} +|\mathbf V|^2 +\leadsto\; +\Xi_\psi^\dagger \mathbf H_V \Xi_\psi, \\ +\qquad +(\mathbf H_V)_{mm'} +&\equiv +\left\langle +\frac{\mu_0\sigma\,(\mu_0\mathbf j\cdot\mathbf B)}{|\nabla\psi|^2} ++ +\frac{(\mu_0p')^2}{B^2} +\right\rangle_{mm'}. +\end{aligned} +} +\end{equation} + +\medskip +\noindent\rule{\linewidth}{0.4pt} +\paragraph{Closed form of the $\mathbf V$-sector ($\mathbf H_4$).} + +The four pure-$\Xi_\psi$ contributions assemble into +\begin{equation} +\boxed{\;\mathbf H_4=\mathbf H_{YV}+\mathbf H_{UV}+\mathbf H_V+\mathbf H_K\;,} +\end{equation} +equal, term by term, to the $\mathbf V$-sector of the Bernstein form +\eqref{eq:ep_bernsteinK} without invoking any equilibrium identity. The +Grad--Shafranov equation reduces it to the compact block +\begin{equation} +\boxed{\; +\mathbf H_4=\mu_0p'\left(\frac{\chi''}{\chi'}\mathbf J+\mathbf J'\right) +-2\pi f'q'\chi'\,\mathbf I\;,} +\end{equation} +as proved in Appendix~\ref{app:h4}. + +The complete term-to-matrix assignment is therefore +\begin{equation} +\boxed{ +\begin{aligned} +\mathbf A &: \quad \mathbf X^*\!\cdot\mathbf X,\\ +\mathbf B &: \quad \mathbf X^*\!\cdot\mathbf Z,\\ +\mathbf C &: \quad +\mathbf X^*\!\cdot\mathbf Y\to\mathbf C_1,\quad +\mathbf X^*\!\cdot\mathbf U\to\mathbf C_2,\quad +\mathbf X^*\!\cdot\mathbf V\to\mathbf C_3,\\ +\mathbf D &: \quad \mathbf Z^*\!\cdot\mathbf Z,\\ +\mathbf E &: \quad +\mathbf Z^*\!\cdot\mathbf Y\to\mathbf E_1,\quad +\mathbf Z^*\!\cdot\mathbf U\to\mathbf E_2,\quad +\mathbf Z^*\!\cdot\mathbf V\to\mathbf E_3,\\ +\mathbf H &: \quad +\mathbf Y^*\!\cdot\mathbf Y\to\mathbf H_1,\quad +\mathbf Y^*\!\cdot\mathbf U+\mathbf U^*\!\cdot\mathbf Y\to\mathbf H_2,\quad +\mathbf U^*\!\cdot\mathbf U\to\mathbf H_3,\\ +&\quad +\mathbf Y^*\!\cdot\mathbf V+\mathbf V^*\!\cdot\mathbf Y ++\mathbf U^*\!\cdot\mathbf V+\mathbf V^*\!\cdot\mathbf U ++|\mathbf V|^2 +-\mu_0K\frac{|\xi_\psi|^2}{|\nabla\psi|^2} +\to\mathbf H_4 . +\end{aligned} +} +\end{equation} +Thus the final coefficient matrices are +\begin{equation} +\boxed{ +\begin{aligned} +\mathbf C&=\mathbf C_1+\mathbf C_2+\mathbf C_3, \\ +\mathbf E&=\mathbf E_1+\mathbf E_2+\mathbf E_3, \\ +\mathbf H&=\mathbf H_1+\mathbf H_2+\mathbf H_3+\mathbf H_4. +\end{aligned} +} +\end{equation} +Subject to the boundary and reduced-compressional assumptions stated above, +this is the DCON-normalized version of the matrix structure in Eq.~(35) of +\texttt{dcon.tex}: the Bernstein form and the original DCON current--pressure +form yield the same radial matrix functional, the $\mathbf V$-dependent pieces +being purely intermediate recombination terms. +\section{Variable Naming Convention and Mathematical Symbols} +\label{sec:naming} + +This section summarizes the code implementation of the $\vec C$ and $\vec Q$ vectors and the tensor transformations between their contravariant and covariant representations. + +\subsection{Fundamental Transformation Laws} + +The C and Q vectors are defined using a dual gradient basis and a tangent basis: + +\textbf{Gradient basis (covariant one-forms):} +$\vec{w}_i \equiv \nabla \psi, \nabla \theta, \nabla \zeta$ \\ +\textbf{Jacobian-scaled tangent basis:} +$\vec{v}_i \equiv \nabla \theta \times \nabla \zeta, \nabla \zeta \times \nabla \psi, \nabla \psi \times \nabla \theta$, +so that the true tangent basis is \(\vec e_i=\mathcal J\vec v_i\). + +\begin{equation} +\boxed{ +\begin{aligned} +\text{Contravariant} \quad &Q^i = \frac{b^i}{\mathcal{J}} \\ +\text{Covariant} \quad &Q_i = g_{ij} Q^j = g_{ij} \frac{b^j}{\mathcal{J}} \\ +\text{Physics method} \quad &C_i = Q_i + \left[\xi_n(\mu_0 \mathbf{j} \times \hat{n})\right]_i +\end{aligned} +} +\end{equation} + +where $\mathcal{J}$ is the Jacobian, \(g_{ij}\) is the true covariant tangent +metric, \(\xi_n=\boldsymbol\xi\cdot\hat n\), and \(b^i\equiv\mathcal J Q^i\) +denotes the Jacobian-weighted contravariant arrays stored by the code +(\texttt{bwp}, \texttt{bwt}, \texttt{bwz}). +\subsection{Code Variable Mapping Table} + +\subsubsection*{C Vector - Contravariant components in the tangent basis ($C^i$)} +\begin{table}[h!] +\centering +\begin{tabular}{|l|l|} +\hline +\textbf{Variable} & \textbf{Symbol/Definition} \\ +\hline +\texttt{cwp\_tmp, cwp\_fun, cwp\_mn} & $\mathcal{J}C^\psi$ (contravariant component) \\ +\texttt{cwt\_tmp, cwt\_fun, cwt\_mn} & $\mathcal{J}C^\theta$ (contravariant component) \\ +\texttt{cwz\_tmp, cwz\_fun, cwz\_mn} & $\mathcal{J}C^\zeta$ (contravariant component) \\ +\hline +\end{tabular} +\end{table} + +\subsubsection*{C Vector - Covariant components in the gradient basis ($C_i$)} +\begin{table}[h!] +\centering +\begin{tabular}{|l|l|} +\hline +\textbf{Variable} & \textbf{Symbol/Definition} \\ +\hline +\texttt{cvp\_tmp, cvp\_fun} & $C_\psi$ reconstructed directly from $Q_\psi+(\xi\cdot\hat n)(\mu_0\mathbf j\times\hat n)_\psi$ \\ +\texttt{cvt\_tmp, cvt\_fun} & $C_\theta$ reconstructed directly from $Q_\theta+\mathcal J\xi_\psi\mu_0j^\zeta$ \\ +\texttt{cvz\_tmp, cvz\_fun} & $C_\zeta$ reconstructed directly from $Q_\zeta-\mathcal J\xi_\psi\mu_0j^\theta$ \\ +\texttt{c2vp\_mn, cv2p\_fun} & $g_{\psi j}C^j$ from metric lowering of the reconstructed upper components \\ +\texttt{c2vt\_mn, cv2t\_fun} & $g_{\theta j}C^j$ from metric lowering of the reconstructed upper components \\ +\texttt{c2vz\_mn, cv2z\_fun} & $g_{\zeta j}C^j$ from metric lowering of the reconstructed upper components \\ +\hline +\end{tabular} +\end{table} + +\section{Log-output quantities and their exact integrands} +\label{sec:logmap} + +This section gives the precise mathematical definition of every scalar +printed to the terminal and to \texttt{gpec.log} by the three reconstruction +diagnostics. The three blocks correspond to the input flags +\texttt{recon\_flag} (\texttt{GPEC\_RECON}, driver \texttt{gpout\_recon}), +\texttt{recon\_flag2} (\texttt{GPEC\_RECON2}, driver \texttt{gpout\_recon2} +$\to$ \texttt{gpeq\_firstform}), and \texttt{recon\_flag3} +(\texttt{GPEC\_RECON3}, driver \texttt{gpout\_recon3} $\to$ +\texttt{gpeq\_recon3}). + +The DCON normalization factor that converts the GPEC energy to the +DCON-normalized $\delta W$, used in the normalized outputs below, is +\begin{equation} +\texttt{dcon\_fac} +=\frac{2\mu_0}{\psi_o^{2}\,(10^{-3}\chi')^{2}}, +\qquad +\chi'=2\pi\psi_o . +\end{equation} + +\subsection{\texttt{GPEC\_RECON} (\texttt{recon\_flag})} + +This route reconstructs the closed Bernstein form \eqref{eq:ep_bernsteinK}, +\begin{equation} +\delta W_p=\frac{1}{2\mu_0}\int d\tau_p +\Big\{\,|\vec C|^2 ++\mu_0\gamma p\,|\nabla\!\cdot\!\vec\xi|^2 +-\mu_0 K\,|\xi_n|^2\,\Big\}, +\qquad +\vec C\equiv\vec Q+\xi_n\,(\mu_0\vec j\times\hat n), +\label{eq:dWp_recon1} +\end{equation} +For the reduced diagnostic used here the positive compressional norm +\(\gamma p\,|\nabla\!\cdot\!\vec\xi|^2\) is omitted; this is a reconstruction +assumption, not a general ideal-MHD identity. The reconstructed energy is then +\(\delta W_p=\tfrac{1}{2\mu_0}\int(|\vec C|^2-\mu_0K|\xi_n|^2)\,d\tau_p\). +Here the symbols are +\begin{itemize} +\item $\vec Q=\nabla\times(\vec\xi\times\vec B)$ is the perturbed field; +\item $\xi_n\equiv\vec\xi\cdot\hat n$ is the normal displacement, $\hat + n=\nabla\psi/|\nabla\psi|$ (code \texttt{xno\_mn}); +\item $\vec C=\vec Q+\xi_n(\mu_0\vec j\times\hat n)$ is the cross vector, + whose squared norm is closed by the metric contraction + $|\vec C|^2=g_{ij}C^{i*}C^{j}=C_i^*C^i$ (routine \texttt{gpeq\_epf}); +\item $K=K_1+K_2+K_3$ is the destabilizing curvature kernel (routine + \texttt{gpeq\_K}) integrated against $|\xi_n|^2$ (routine + \texttt{gpeq\_dst}). +\end{itemize} +The printed scalars are the radial/poloidal integrals of these pieces: + +\begin{align} +\texttt{C2\_psi/mu0} +&=\int\! d\psi\,\frac{1}{\mu_0}\oint + \mathrm{Re}\!\big[(C^\psi)^*\,C_\psi\big]\,\mathcal J\,d\theta,\\ +\texttt{C2\_theta/mu0} +&=\int\! d\psi\,\frac{1}{\mu_0}\oint + \mathrm{Re}\!\big[(C^\theta)^*\,C_\theta\big]\,\mathcal J\,d\theta,\\ +\texttt{C2\_zeta/mu0} +&=\int\! d\psi\,\frac{1}{\mu_0}\oint + \mathrm{Re}\!\big[(C^\zeta)^*\,C_\zeta\big]\,\mathcal J\,d\theta,\\ +\texttt{int\_J\_C2\_over\_mu0} +&=\texttt{C2\_psi/mu0}+\texttt{C2\_theta/mu0}+\texttt{C2\_zeta/mu0} + =\int\! d\psi\,\frac{1}{\mu_0}\oint |\vec C|^2\,\mathcal J\,d\theta. +\end{align} +The curvature ($K$) pieces, with +$K_1=|\nabla\psi|^2\,\sigma\,S$ (shear $S$), +$K_2=\mu_0 B^2\sigma^2$ (parallel current), and +$K_3=2p'\kappa_\psi$ (curvature projection +\(\kappa_\psi=\boldsymbol\kappa\cdot\nabla\psi\)), give +\begin{align} +\texttt{Ki\_xin2} +&=\int\! d\psi\oint K_i\,|\xi_n|^2\,\mathcal J\,d\theta, +\qquad i=1,2,3,\\ +\texttt{int\_J\_K\_xin2} +&=\sum_{i=1}^{3}\texttt{Ki\_xin2} + =\int\! d\psi\oint K\,|\xi_n|^2\,\mathcal J\,d\theta. +\end{align} +The reconstructed potential energy and its DCON-normalized value are +\begin{align} +\texttt{dW\_p(recon)} +&=\tfrac12\bigl(\texttt{int\_J\_C2\_over\_mu0}-\texttt{int\_J\_K\_xin2}\bigr) + =\tfrac12\!\int\!\Big[\tfrac{|\vec C|^2}{\mu_0}-K|\xi_n|^2\Big] + \mathcal J\,d\psi\,d\theta,\\ +\texttt{dW\_p(recon-normalize)} +&=\texttt{dcon\_fac}\times\texttt{dW\_p(recon)} . +\end{align} +The \texttt{C verify} block reports $\max$/$\mathrm{rms}$ of +$(\nabla\times\vec C)\cdot\nabla\psi=\tfrac1{\mathcal J} +(\partial_\theta C_\zeta-\partial_\zeta C_\theta)$, which must vanish to +\texttt{tol}. + +\subsection{\texttt{GPEC\_RECON2} (\texttt{recon\_flag2})} + +This route reconstructs the original ``first form'' of $\delta W_p$, Eq.~\eqref{eq:ep_first}, +\begin{equation} +\delta W_p=\frac{1}{2\mu_0}\int d\tau_p +\Big\{\,|\vec Q|^2 +-\mu_0\,\vec j\cdot\vec Q\times\vec\xi^{*} ++\mu_0\gamma p\,|\nabla\!\cdot\!\vec\xi|^2 ++\mu_0(\nabla\!\cdot\!\vec\xi)^{*}\,\vec\xi\cdot\nabla p\,\Big\}, +\label{eq:dWp_recon2} +\end{equation} +The reduced diagnostic omits only the positive +\(\gamma p|\nabla\!\cdot\!\vec\xi|^2\) norm; the pressure--compression coupling +term is retained. The three retained groups map one-to-one onto the printed +scalars, with +\begin{itemize} +\item $|\vec Q|^2$ $\to$ \texttt{int\_J\_Q2\_over\_mu0}; +\item the current cross term $-\vec j\cdot\vec Q\times\vec\xi^{*}$ $\to$ + \texttt{int\_J\_jQxixi\_over\_mu0} (evaluated as a determinant of the + Jacobian-weighted contravariant $Q$ and $\xi$ components against + $\mu_0 j^\theta,\mu_0 j^\zeta$); +\item the pressure--compression term + $(\nabla\!\cdot\!\vec\xi)^{*}\,\vec\xi\cdot\nabla p$ $\to$ + \texttt{int\_J\_pdiv\_term}. +\end{itemize} +The route (routine \texttt{gpeq\_firstform}) evaluates these groups pointwise +in $\theta$ and integrates in $\psi$. Writing +$\tilde Q^i\equiv\mathcal JQ^i$ and $\tilde\xi^i\equiv\mathcal J\xi^i$ for the +Jacobian-weighted contravariant components, +\begin{align} +\texttt{int\_J\_Q2\_over\_mu0} +&=\int\! d\psi\,\frac{1}{\mu_0}\oint + \mathrm{Re}\!\big[Q_\psi^*Q^\psi+Q_\theta^*Q^\theta+Q_\zeta^*Q^\zeta\big] + \mathcal J\,d\theta + =\int\! d\psi\,\frac{1}{\mu_0}\oint|\vec Q|^2\,\mathcal J\,d\theta,\\ +\texttt{int\_J\_jQxixi\_over\_mu0} +&=\int\! d\psi\,\frac{1}{\mu_0}\oint\frac{1}{\mathcal J} + \Big[-\mu_0 j^\theta\bigl(\tilde Q^\psi\,\tilde\xi^{\zeta*}-\tilde Q^\zeta\,\tilde\xi^{\psi*}\bigr) + +\mu_0 j^\zeta\bigl(\tilde Q^\psi\,\tilde\xi^{\theta*}-\tilde Q^\theta\,\tilde\xi^{\psi*}\bigr) + \Big]\mathcal J\,d\theta + =\int\! d\psi\oint \vec j\cdot(\vec Q\times\vec\xi^{\,*})\,\mathcal J\,d\theta,\\ +\texttt{int\_J\_pdiv\_term} +&=\int\! d\psi\oint + (\nabla\!\cdot\!\vec\xi)^{*}\,p'\,\xi^{\psi}\, + \mathcal J\,d\theta,\\ +\texttt{int\_J\_total} +&=\texttt{int\_J\_Q2\_over\_mu0} + -\texttt{int\_J\_jQxixi\_over\_mu0} + +\texttt{int\_J\_pdiv\_term}. +\end{align} +Here $\tilde Q^i=\mathcal JQ^i$ and $\tilde\xi^i=\mathcal J\xi^i$ are the +Jacobian-weighted \emph{contravariant} components (code variables +\texttt{bwp\_fun}, \texttt{bmt\_fun}, \texttt{bmz\_fun} and +\texttt{xwp\_fun}, \texttt{xmt\_fun}, \texttt{xmz\_fun}), and +$\nabla\!\cdot\!\vec\xi$ is the compressional density assembled in +\texttt{divxi\_density}. The reconstructed energy is +\begin{equation} +\texttt{dW\_p(recon2)}=\tfrac12\,\texttt{int\_J\_total}, +\qquad +\texttt{dW\_p(recon2-normalize)}=\texttt{dcon\_fac}\times\texttt{dW\_p(recon2)} . +\end{equation} +Each line is printed as a complex pair (real, imaginary); the imaginary part +is a roundoff diagnostic and should vanish. + +\subsection{\texttt{GPEC\_RECON3} (\texttt{recon\_flag3})} + +This route keeps the same closed form as recon1, Eq.~\eqref{eq:dWp_recon1}, +but expands the cross-energy density $|\vec C|^2$ term by term using +$\vec C=\vec Q+\vec V$, Eq.~\eqref{eq:CeqQplusV}, +\begin{equation} +|\vec C|^2=|\vec Q|^2+2\,\mathrm{Re}\bigl(\vec V^{*}\!\cdot\vec Q\bigr) ++|\vec V|^2 . +\label{eq:Cdecomp} +\end{equation} +Crucially, $\vec V$ is evaluated \emph{directly} from its explicit components +in Eq.~\eqref{eq:Vcomp} (equilibrium current, metric, and $\xi_\psi$), not as +$\vec C-\vec Q$, so the $B$ term below does not depend on the reconstructed +$\vec C$. Using that $\vec j$ is tangent to the flux surface ($\vec +j\cdot\hat n=0$), the last piece separates into a parallel-current and a +perpendicular (Pfirsch--Schl\"uter) contribution, +\begin{equation} +|\vec V|^2=\mu_0^2\,|\xi_n|^2\Bigl(\sigma^2B^2 ++\frac{p'^2|\nabla\psi|^2}{B^2}\Bigr). +\end{equation} +The five printed scalars are the $\psi,\theta$ integrals of the four density +pieces of Eq.~\eqref{eq:Cdecomp} plus the full $|\vec C|^2$: +\begin{align} +\texttt{A\_total} +&=\int\! d\psi\,\frac{1}{\mu_0}\oint|\vec Q|^2\,\mathcal J\,d\theta + \;\;(=\texttt{int\_J\_Q2\_over\_mu0}\ \text{of recon2}),\\ +\texttt{B\_total} +&=\int\! d\psi\,\frac{2}{\mu_0}\oint + \mathrm{Re}\!\big[V_i^{*}\,Q^{i}\big]\,\mathcal J\,d\theta + \;\;\bigl(=\tfrac{2}{\mu_0}\!\int\!\mathrm{Re}(\vec V^{*}\!\cdot\vec Q)\bigr),\\ +\texttt{Cpar\_total} +&=\int\! d\psi\oint \mu_0\,\sigma^2 B^2\,|\xi_n|^2\,\mathcal J\,d\theta + \;\;(=\texttt{K2\_xin2}\ \text{of recon1}=dst_2),\\ +\texttt{Iperp\_total} +&=\int\! d\psi\oint \frac{\mu_0\,(p')^2\,|\nabla\psi|^2}{B^2}\,|\xi_n|^2\, + \mathcal J\,d\theta,\\ +\texttt{epf\_total} +&=\int\! d\psi\,\frac{1}{\mu_0}\oint|\vec C|^2\,\mathcal J\,d\theta + \;\;(=\texttt{int\_J\_C2\_over\_mu0}\ \text{of recon1}). +\end{align} +The closure of the decomposition is reported as +\begin{equation} +\texttt{identity\_res} +=\texttt{epf\_total} +-\bigl(\texttt{A\_total}+\texttt{B\_total} + +\texttt{Cpar\_total}+\texttt{Iperp\_total}\bigr), +\end{equation} +which must vanish to roundoff since +$|\vec C|^2=|\vec Q|^2+2\,\mathrm{Re}(\vec V^{*}\!\cdot\vec Q)+|\vec V|^2$. + +\subsubsection{Combined $\delta W_p$ and the $K_2$ cancellation} +\label{sec:recon3_combined} + +The recon3 outputs $(A,B,I_\perp)$ can be combined with the recon1 +destabilizing pieces $\mathrm{dst}_1,\mathrm{dst}_3$ to write $\delta W_p$ in a +form in which the parallel-current contribution cancels identically. Start +from the recon1 form, Eq.~\eqref{eq:dWp_recon1}, +\begin{equation} +\delta W_p=\frac12\Bigl[\int\mathcal J\,\frac{|\vec C|^2}{\mu_0}\, +-\int\mathcal J\,K\,|\xi_n|^2\Bigr]. +\end{equation} +The recon3 decomposition of the stabilizing density reads +\begin{equation} +\int\mathcal J\,\frac{|\vec C|^2}{\mu_0} +=A+B+C_\parallel+C_\perp, +\qquad +C_\parallel=\mathrm{dst}_2\ (=K_2|\xi_n|^2\ \text{integral}),\quad +C_\perp=I_\perp, +\end{equation} +while the curvature kernel splits as +$\int\mathcal J\,K\,|\xi_n|^2=\mathrm{dst}_1+\mathrm{dst}_2+\mathrm{dst}_3$. +Substituting, +\begin{equation} +2\,\delta W_p +=(A+B+\mathrm{dst}_2+I_\perp) +-(\mathrm{dst}_1+\mathrm{dst}_2+\mathrm{dst}_3). +\end{equation} +The term $\mathrm{dst}_2$ ($=K_2|\xi_n|^2$, the parallel-current$^2$ piece) +enters $|\vec C|^2$ and $K$ with \emph{opposite} sign and cancels exactly, +leaving +\begin{equation} +\boxed{\;2\,\delta W_p=A+B+I_\perp-\mathrm{dst}_1-\mathrm{dst}_3\;.} +\label{eq:dWp_combined} +\end{equation} +Written out term by term, +\begin{equation} +\delta W_p +=\frac12\!\int\!\mathcal J\,d\psi\,d\theta\, +\Bigl[ +\underbrace{\tfrac{|\vec Q|^2}{\mu_0}}_{A_{\text{density}}} +\;+\; +\underbrace{\tfrac{2\,\mathrm{Re}(\vec V^{*}\!\cdot\vec Q)}{\mu_0}}_{B_{\text{density}}} +\;+\; +\underbrace{\tfrac{\mu_0\,p'^2\,|\nabla\psi|^2}{B^2}\,|\xi_n|^2}_{I_{\perp,\text{density}}} +\;-\; +\underbrace{|\nabla\psi|^2\,\sigma S\,|\xi_n|^2}_{K_1|\xi_n|^2\,(\mathrm{dst}_1)} +\;-\; +\underbrace{2p'\kappa_\psi\,|\xi_n|^2}_{K_3|\xi_n|^2\,(\mathrm{dst}_3)} +\Bigr], +\end{equation} +with $\vec V=\xi_n\,\mu_0\,\vec j\times\hat n$ and +$\sigma=\vec j\cdot\vec B/B^2$. In the DIII-D example +($n=1$, \texttt{gpec.log}) this gives +$2\,\delta W_p=0.81768+(-0.99951)+0.04056-(-0.03096)-0.12767=-0.23798$, +i.e. $\delta W_p=-0.11899$, matching \texttt{dW\_p(recon)} to roundoff. + +\paragraph{Physical role of each term.} +\begin{itemize} +\item $+A$ (sign $+$, stabilizing): magnetic energy of the perturbed field + $\vec Q$ itself (field-line bending). +\item $+B$ (sign-indefinite): cross-coupling of $\vec Q$ with the equilibrium + current, $2\,\mathrm{Re}(\vec V^{*}\!\cdot\vec Q)$ --- acts as a kink + driver or suppressor depending on phase. +\item $+I_\perp$ (sign $+$): Pfirsch--Schl\"uter perpendicular-current$^2$ + contribution $\propto p'^2|\nabla\psi|^2|\xi_n|^2/B^2$, positive definite. +\item $-\mathrm{dst}_1$ (sign-indefinite): shear--current coupling + $|\nabla\psi|^2\sigma S\,|\xi_n|^2$; stabilizing or destabilizing with the + sign of $K_1$. +\item $-\mathrm{dst}_3$ (sign-indefinite): pressure--curvature term + $2p'\kappa_\psi\,|\xi_n|^2$; a ballooning drive when $p'\kappa_\psi<0$. +\end{itemize} + +\paragraph{Meaning of the vanishing $K_2$.} +The cancellation does \emph{not} mean the parallel current $j_\parallel^2$ is +absent from $\delta W$. The same quantity $C_\parallel=K_2|\xi_n|^2$ enters the +stabilizing $|\vec C|^2$ and the destabilizing $K$ with opposite sign, so the +explicit $j_\parallel^2$ piece cancels in the energy balance. The residual +effect of $j_\parallel$ survives only indirectly --- through the shear term +$K_1$ (its dependence on $\sigma S$) and through the cross term $B$. + +\subsection{Cross-checks between the three routes} + +The identities below are exact and are confirmed at roundoff in the DIII-D +example ($n=1$, \texttt{gpec.log}): +\begin{align} +\texttt{recon3:A\_total} &= \texttt{recon2:int\_J\_Q2\_over\_mu0} + &&(0.817676),\\ +\texttt{recon3:Cpar\_total} &= \texttt{recon1:K2\_xin2} + = \texttt{gpeq\_dst}:dst_2 &&(1.082617),\\ +\texttt{recon3:epf\_total} &= \texttt{recon1:int\_J\_C2\_over\_mu0} + &&(0.941346),\\ +\texttt{recon1:dW\_p} &\approx \texttt{recon2:dW\_p} + &&(-0.1190\ \text{vs}\ -0.1198). +\end{align} + + +\clearpage +\appendix + +\section{Full reduction of the $\mathbf H_4$ ($\mathbf V$-sector) block}\label{app:h4} + +This appendix proves the $\mathbf H_4$ ($\mathbf V$-sector) identity quoted in +the main text at two levels: (i)~the equilibrium-free Bernstein form +$\mathbf H_4=\mathbf H_{YV}+\mathbf H_{UV}+\mathbf H_V+\mathbf H_K$, equal term by +term to \eqref{eq:ep_bernsteinK}; and (ii)~its collapse to the compact block +$\mathbf H_4=\mu_0p'\!\left(\tfrac{\chi''}{\chi'}\mathbf J+\mathbf J'\right)-2\pi +f'q'\chi'\,\mathbf I$, whose only equilibrium input is the Grad--Shafranov +equation. The steps that invoke an equilibrium relation (force balance, the +magnetic-surface condition $\mathbf B\cdot\nabla\psi=0$, Grad--Shafranov) are +noted where they occur. + +\subsection{Setup: the $\mathbf V$-sector pieces and the single geometric rule} + +We work in DCON straight-field-line flux coordinates $(\psi,\theta,\zeta)$, all +normalized to $[0,1]$, with $e_m\equiv e^{2\pi i(m\theta-n\zeta)}$ and the +Jacobian-scaled basis $\vec v_i=\vec e_i/\mathcal J$, so that +$\vec v_i\cdot\vec v_j=g_{ij}/\mathcal J^2$. We start from +\begin{equation} +\mathbf Q \equiv \mathbf X+\mathbf Y+\mathbf Z+\mathbf U, +\qquad +\vec C = \mathbf Q+\mathbf V, +\end{equation} +so that $|\vec C|^2$ expands into $5$ diagonal and $10$ cross products. The scalar +Bernstein term $-\mu_0K|\xi_\psi|^2/|\nabla\psi|^2$ is a \emph{separate} piece of the energy +$2\mu_0\delta W=\int[\,|\vec C|^2+\mu_0\gamma p|\nabla\cdot\vec\xi|^2-\mu_0K|\xi_\psi|^2/|\nabla\psi|^2\,]$; +the purpose of this appendix is to show that the $\mathbf H_4$ part of the +$\mathbf V$-sector --- the products +$2\,\mathrm{Re}(\mathbf V^{*}\!\cdot\mathbf Y) + +2\,\mathrm{Re}(\mathbf V^{*}\!\cdot\mathbf U)+|\mathbf V|^2$, +together with that scalar --- collapses to the compact block +$\mathbf H_4=\mu_0p'(\tfrac{\chi''}{\chi'}\mathbf J+\mathbf J')-2\pi f'q'\chi'\mathbf I$. +(The other two $\mathbf V$-cross products, $\mathbf V^{*}\!\cdot\mathbf X$ and +$\mathbf V^{*}\!\cdot\mathbf Z$, are \emph{not} part of $\mathbf H_4$; see below.) +The field pieces are +\begin{equation} +\begin{aligned} +\mathbf X &= -2\pi i\sum_m u_m e_m\,(n\vec v_2+m\vec v_3), +& +\mathbf Y &= 2\pi i\chi'\sum_m (m-nq)v_m e_m\,\vec v_1,\\ +\mathbf Z &= -\chi'\sum_m v_m' e_m\,(\vec v_2+q\vec v_3), +& +\mathbf U &= -\sum_m v_m e_m\left[\chi''\vec v_2+(q\chi')'\vec v_3\right],\\ +\mathbf V &= \frac{\xi_\psi}{|\nabla\psi|^2}\left(\mathcal A_2\vec v_2-\mathcal A_3\vec v_3\right), +\end{aligned} +\end{equation} +with +\begin{equation} +\mathcal A_2 \equiv \mu_0j^\theta g_{23}+\mu_0j^\zeta g_{33}, +\qquad +\mathcal A_3 \equiv \mu_0j^\theta g_{22}+\mu_0j^\zeta g_{23}. +\end{equation} + +Physically $\mathbf V$ is simply the edge current rotated into the flux surface, +$\mathbf V=\frac{\xi_\psi}{|\nabla\psi|^2}\,\mu_0\mathbf j\times\nabla\psi$ --- a +concrete field on the $(\psi,\theta)$ grid --- and the components +$\mathcal A_2=\mathcal J(\mu_0\mathbf j\times\nabla\psi)^{\theta}$, +$\mathcal A_3=-\mathcal J(\mu_0\mathbf j\times\nabla\psi)^{\zeta}$ are just its +Jacobian-weighted contravariant entries (the expansion is in the main text). +Because $\mathbf V$ is a cross product, every \emph{in-surface} product with it +collapses in one line by a scalar triple product: for any surface vector +$\mathbf W=a\vec v_2+b\vec v_3$, +\begin{equation} +\mathbf W\cdot\mathbf V +=\frac{\xi_\psi}{|\nabla\psi|^2}\,\mu_0\,\nabla\psi\cdot(\mathbf W\times\mathbf j) +=\xi_\psi\,(a\,\mu_0 j^\zeta-b\,\mu_0 j^\theta), +\label{eq:app_Vrule} +\end{equation} +since $\mathbf W\times\mathbf j=(a\,j^\zeta-b\,j^\theta)\nabla\psi$ for two +in-surface vectors. This is the cofactor identity +$g_{22}g_{33}-g_{23}^2=\mathcal J^2|\nabla\psi|^2$ in coordinate-free form, and is +the only tool needed for $\mathbf X^*\!\cdot\mathbf V$, $\mathbf Z^*\!\cdot\mathbf V$, +and $\mathbf U^*\!\cdot\mathbf V$ below; likewise +$|\mathbf V|^2=|\xi_\psi|^2\mu_0^2|\mathbf j|^2/|\nabla\psi|^2$ follows from +$|\mu_0\mathbf j\times\nabla\psi|^2=\mu_0^2|\mathbf j|^2|\nabla\psi|^2$ +($\mathbf j\cdot\nabla\psi=0$). +All dot products are reduced with the single geometric rule +\begin{equation} +\vec v_i\cdot\vec v_j=\frac{g_{ij}}{\mathcal J^2}, +\qquad +e_m^*e_{m'}=e^{2\pi i(m'-m)\theta}. +\end{equation} + +Throughout, the only current data are the DCON contravariant current densities +\begin{equation} +\mu_0 j^\theta=-\frac{2\pi f'}{\mathcal J}, +\qquad +\mu_0 j^\zeta=-\frac{\mu_0p'}{\chi'}-\frac{2\pi f'q}{\mathcal J}, +\label{eq:app_dconcurrents} +\end{equation} +and the equilibrium is a magnetic surface, $\mathbf B\cdot\nabla\psi=0$, so that +$\mathbf B=\chi'\,(q\,\nabla\psi\times\nabla\theta-\nabla\psi\times\nabla\zeta)$ +lies in the surface and $\mu_0\mathbf j\cdot\mathbf B=\mu_0\sigma B^2$ with +$\sigma\equiv\mathbf j\cdot\mathbf B/B^2$. Because the perturbation carries a +single toroidal mode number $n$, +$|\xi_\psi|^2=\sum_{m,m'}v_m^*v_{m'}\,e^{2\pi i(m'-m)\theta}$ is independent of +$\zeta$; this is used in Sec.~\ref{app:assembly}. + +\subsection{The five diagonal and ten cross products} + +Every $|\mathbf C|^2$ product is reduced with +$\vec v_i\cdot\vec v_j=g_{ij}/\mathcal J^2$ and the cofactor identity +$g_{22}g_{33}-g_{23}^2=\mathcal J^2|\nabla\psi|^2$. Of the $\mathbf V$-touching +products, only $\mathbf Y^*\!\cdot\mathbf V$, $\mathbf U^*\!\cdot\mathbf V$, and +$|\mathbf V|^2$ --- together with the scalar $-\mu_0K$ term --- enter +$\mathbf H_4$. The other two route elsewhere: +$\mathbf X^*\!\cdot\mathbf V$ (a $\xi_s$--$\xi_\psi$ coupling) feeds the +$\mathbf C$ block and +$\mathbf Z^*\!\cdot\mathbf V=\mu_0p'\,\xi_\psi^{\prime *}\xi_\psi$ (a +$\xi_\psi'$--$\xi_\psi$ coupling) the $\mathbf E$ block; the remaining +$\mathbf Q$-sector diagonals and cross products are listed for completeness and +feed the blocks +$\mathbf A,\mathbf B,\mathbf D,\mathbf H_{1\text{--}3}$ of the main text. + +\medskip\noindent\emph{Diagonal products.} + +\begin{equation} +|\mathbf X|^2 += +\frac{(2\pi)^2}{\mathcal J^2} +\sum_{m,m'} u_m^* u_{m'}\,e^{2\pi i(m'-m)\theta} +\left[ +n^2 g_{22}+n(m+m')g_{23}+m m' g_{33} +\right], +\end{equation} + +\begin{equation} +|\mathbf Y|^2 += +\frac{(2\pi\chi')^2}{\mathcal J^2} +\sum_{m,m'} (m-nq)(m'-nq)\,v_m^* v_{m'}\,e^{2\pi i(m'-m)\theta}\, +g_{11}, +\end{equation} + +\begin{equation} +|\mathbf Z|^2 += +\frac{\chi'^2}{\mathcal J^2} +\sum_{m,m'} v_m^{\prime *} v_{m'}'\,e^{2\pi i(m'-m)\theta} +\left[ +g_{22}+2q\,g_{23}+q^2 g_{33} +\right], +\end{equation} + +\begin{equation} +|\mathbf U|^2 += +\frac{1}{\mathcal J^2} +\sum_{m,m'} v_m^* v_{m'}\,e^{2\pi i(m'-m)\theta} +\left[ +\chi''^2 g_{22}+2\chi''(q\chi')' g_{23}+(q\chi')'^2 g_{33} +\right], +\end{equation} + +\begin{equation} +\begin{aligned} +|\mathbf V|^2 +&= +\frac{|\xi_\psi|^2}{|\nabla\psi|^2} +\left[ +\mu_0^2 g_{22}(j^\theta)^2 ++2\mu_0^2 g_{23}\,j^\theta j^\zeta ++\mu_0^2 g_{33}(j^\zeta)^2 +\right] \\ +&= +|\xi_\psi|^2 +\left[ +\frac{\mu_0^2\sigma(\mathbf j\cdot\mathbf B)}{|\nabla\psi|^2} ++\frac{(\mu_0p')^2}{B^2} +\right]. +\end{aligned} +\end{equation} + +The second equality for $|\mathbf V|^2$ is \emph{not} a generic metric identity: +it uses force balance. With the Lagrange identity +$|\mathbf j|^2B^2=(\mathbf j\cdot\mathbf B)^2+|\mathbf j\times\mathbf B|^2$ and +$\sigma\equiv\mathbf j\cdot\mathbf B/B^2$ (so the bracket +$\mu_0^2[g_{22}(j^\theta)^2+2g_{23}j^\theta j^\zeta+g_{33}(j^\zeta)^2]=\mu_0^2|\mathbf j|^2$), +the difference between the two forms of $|\mathbf V|^2/|\xi_\psi|^2$ is an exact +equality, not a proportionality: +\begin{equation} +\frac{\mu_0^2|\mathbf j|^2}{|\nabla\psi|^2} +-\left[\frac{\mu_0\sigma(\mu_0\mathbf j\cdot\mathbf B)}{|\nabla\psi|^2} ++\frac{(\mu_0p')^2}{B^2}\right] +=\frac{\mu_0^2|\mathbf j\times\mathbf B|^2-(\mu_0p')^2|\nabla\psi|^2} + {B^2|\nabla\psi|^2} +=0 , +\end{equation} +the numerator vanishing identically by force balance +$\mu_0\mathbf j\times\mathbf B=\mu_0\nabla p=\mu_0 p'\nabla\psi$. Equivalently, in +the DCON currents~\eqref{eq:app_dconcurrents} the $2\pi f'q/\mathcal J$ pieces +cancel and $\chi'(q\,\mu_0 j^\theta-\mu_0 j^\zeta)=\mu_0p'$, whence +$\mu_0^2|\mathbf j\times\mathbf B|^2 +=\chi'^2(q\,\mu_0 j^\theta-\mu_0 j^\zeta)^2|\nabla\psi|^2=(\mu_0p')^2|\nabla\psi|^2$: +the parallel ($f'$) current supplies the +$\mu_0\sigma(\mu_0\mathbf j\cdot\mathbf B)$ term and the in-surface pressure +current supplies the $(\mu_0p')^2/B^2$ term. + +\medskip\noindent\emph{Cross products.} + +\begin{equation} +\mathbf X^*\!\cdot\mathbf Z += +-\frac{2\pi i\chi'}{\mathcal J^2} +\sum_{m,m'} u_m^* v_{m'}'\,e^{2\pi i(m'-m)\theta} +\left[ +n(g_{22}+q g_{23})+m(g_{23}+q g_{33}) +\right], +\end{equation} + +\begin{equation} +\mathbf X^*\!\cdot\mathbf Y += +-\frac{(2\pi)^2\chi'}{\mathcal J^2} +\sum_{m,m'} (m'-nq)\,u_m^* v_{m'}\,e^{2\pi i(m'-m)\theta} +\left[ +n\,g_{12}+m\,g_{31} +\right], +\end{equation} + +\begin{equation} +\begin{aligned} +\mathbf X^*\!\cdot\mathbf U +&= +-\frac{2\pi i}{\mathcal J^2} +\sum_{m,m'} u_m^* v_{m'}\,e^{2\pi i(m'-m)\theta} \\ +&\qquad\times +\left[ +\chi''(n g_{22}+m g_{23})+(q\chi')'(n g_{23}+m g_{33}) +\right], +\end{aligned} +\end{equation} + +\begin{equation} +\begin{aligned} +\mathbf X^*\!\cdot\mathbf V +&= +-2\pi i +\sum_{m,m'} u_m^* v_{m'}\,e^{2\pi i(m'-m)\theta}\, +\mu_0\!\left(m j^\theta-n j^\zeta\right) \\ +&= +-2\pi i +\sum_{m,m'} u_m^* v_{m'}\,e^{2\pi i(m'-m)\theta} +\left[ +-\frac{2\pi f'}{\mathcal J}(m-nq)+n\frac{\mu_0p'}{\chi'} +\right], +\end{aligned} +\end{equation} + +Here $\mathbf W=n\vec v_2+m\vec v_3$, so the triple-product +rule~\eqref{eq:app_Vrule} gives the coefficient +$n\,\mu_0 j^\zeta-m\,\mu_0 j^\theta=-(m\,\mu_0 j^\theta-n\,\mu_0 j^\zeta)$ directly. + +\begin{equation} +\mathbf Y^*\!\cdot\mathbf Z += +\frac{2\pi i\chi'^2}{\mathcal J^2} +\sum_{m,m'} (m-nq)\,v_m^* v_{m'}'\,e^{2\pi i(m'-m)\theta} +\left[ +g_{12}+q\,g_{31} +\right], +\end{equation} + +\begin{equation} +\mathbf Y^*\!\cdot\mathbf U += +\frac{2\pi i\chi'}{\mathcal J^2} +\sum_{m,m'} (m-nq)\,v_m^* v_{m'}\,e^{2\pi i(m'-m)\theta} +\left[ +\chi''\,g_{12}+(q\chi')'\,g_{31} +\right], +\end{equation} + +\begin{equation} +\mathbf Z^*\!\cdot\mathbf U += +\frac{\chi'}{\mathcal J^2} +\sum_{m,m'} v_m^{\prime *} v_{m'}\,e^{2\pi i(m'-m)\theta} +\left[ +\chi''(g_{22}+q g_{23})+(q\chi')'(g_{23}+q g_{33}) +\right], +\end{equation} + +\begin{equation} +\mathbf Z^*\!\cdot\mathbf V += +\mu_0p'\,\xi_\psi^{\prime *}\xi_\psi +\qquad(\text{pointwise}), +\end{equation} + +For $\mathbf Z^*\!\cdot\mathbf V$ the rule~\eqref{eq:app_Vrule} with +$\mathbf W=\vec v_2+q\vec v_3$ gives, before inserting currents, +$\mathbf Z^*\!\cdot\mathbf V=\chi'\,\xi_\psi^{\prime *}\xi_\psi\, +(q\,\mu_0 j^\theta-\mu_0 j^\zeta)$; inserting~\eqref{eq:app_dconcurrents}, +$q\,\mu_0 j^\theta-\mu_0 j^\zeta +=-\tfrac{2\pi f'q}{\mathcal J}+\tfrac{\mu_0p'}{\chi'}+\tfrac{2\pi f'q}{\mathcal J} +=\tfrac{\mu_0p'}{\chi'}$ (the $2\pi f'q/\mathcal J$ terms cancel), so +$\mathbf Z^*\!\cdot\mathbf V=\mu_0p'\,\xi_\psi^{\prime *}\xi_\psi$. + +\begin{equation} +\mathbf U^*\!\cdot\mathbf V += +|\xi_\psi|^2 +\left[ +\mu_0p'\frac{\chi''}{\chi'}-\frac{2\pi f'q'\chi'}{\mathcal J} +\right] +\qquad(\text{pointwise}), +\end{equation} + +For $\mathbf U^*\!\cdot\mathbf V$ the rule~\eqref{eq:app_Vrule} with +$\mathbf W=\chi''\vec v_2+(q\chi')'\vec v_3$ leaves the pointwise +coefficient $|\xi_\psi|^2[-\chi''\mu_0 j^\zeta+(q\chi')'\mu_0 j^\theta]$; +inserting~\eqref{eq:app_dconcurrents} and expanding $(q\chi')'=q'\chi'+q\chi''$, +the $\pm2\pi f'q\chi''/\mathcal J$ cross terms cancel, leaving +$|\xi_\psi|^2[\mu_0p'\tfrac{\chi''}{\chi'}-\tfrac{2\pi f'q'\chi'}{\mathcal J}]$. + +\begin{equation} +\begin{aligned} +\mathcal J\,(\mathbf Y^*\!\cdot\mathbf V+\mathbf V^*\!\cdot\mathbf Y) +&= +\chi'\,\mathcal F +\left\{ +\left[(\partial_\theta+q\partial_\zeta)\xi_\psi^*\right]\xi_\psi ++\xi_\psi^*\left[(\partial_\theta+q\partial_\zeta)\xi_\psi\right] +\right\}, \\ +\mathcal F +&\equiv +\mathcal J\, +\frac{(\mu_0 j^\theta)(\nabla\zeta\cdot\nabla\psi)-(\mu_0 j^\zeta)(\nabla\theta\cdot\nabla\psi)}{|\nabla\psi|^2}. +\end{aligned} +\end{equation} + +\medskip\noindent\emph{Scalar Bernstein term.} + +\begin{equation} +-\mu_0\,\mathcal J\,K\frac{|\xi_\psi|^2}{|\nabla\psi|^2} += +|\xi_\psi|^2 +\left[ +-\mathcal J\mu_0\sigma S_\psi +-\mathcal J\frac{\mu_0^2\sigma^2B^2}{|\nabla\psi|^2} +-2\mu_0p'\mathcal J\frac{\kappa_\psi}{|\nabla\psi|^2} +\right]. +\end{equation} + +\subsection{Assembly of the $\mathbf V$-sector and the $\theta$-integration by parts} +\label{app:assembly} + +The four $\mathbf V$-touching contributions combine, under a common +$\mathcal J$ weight and before any equilibrium input, as + +\begin{equation} +\begin{aligned} +&\int d\theta\,d\zeta\;\mathcal J +\left[ +(\mathbf Y^*\!\cdot\mathbf V+\mathbf V^*\!\cdot\mathbf Y) ++(\mathbf U^*\!\cdot\mathbf V+\mathbf V^*\!\cdot\mathbf U) ++|\mathbf V|^2 +-\mu_0K\frac{|\xi_\psi|^2}{|\nabla\psi|^2} +\right]\\ +&=\int d\theta\,d\zeta\, +\bigg[ +\underbrace{\chi'\,\mathcal F\,\partial_\theta|\xi_\psi|^2}_{\mathbf Y^*\mathbf V+\text{c.c.}} ++|\xi_\psi|^2\Big( +\underbrace{2\mu_0p'\mathcal J\tfrac{\chi''}{\chi'}-4\pi f'q'\chi'}_{\mathbf U^*\mathbf V+\text{c.c.}} ++\underbrace{\mathcal J\tfrac{\mu_0^2\sigma(\mathbf j\cdot\mathbf B)}{|\nabla\psi|^2} ++\mathcal J\tfrac{(\mu_0p')^2}{B^2}}_{|\mathbf V|^2}\\ +&\hspace{5em} +\underbrace{ +-\,\mathcal J\mu_0\sigma S_\psi +-\mathcal J\tfrac{\mu_0^2\sigma^2B^2}{|\nabla\psi|^2} +-2\mu_0p'\mathcal J\tfrac{\kappa_\psi}{|\nabla\psi|^2}}_{-\mu_0K|\xi_\psi|^2/|\nabla\psi|^2} +\Big) +\bigg], +\quad +\mathcal F\equiv +\mathcal J\frac{\mu_0 j^\theta(\nabla\zeta\cdot\nabla\psi)-\mu_0 j^\zeta(\nabla\theta\cdot\nabla\psi)}{|\nabla\psi|^2}. +\end{aligned} +\end{equation} + +Only $\partial_\theta|\xi_\psi|^2$ appears (not the full +$(\partial_\theta+q\partial_\zeta)|\xi_\psi|^2$ that +$\mathbf Y^*\!\cdot\mathbf V+\text{c.c.}$ produces) because $|\xi_\psi|^2$ is +$\zeta$-independent for a single-$n$ perturbation, so +$\partial_\zeta|\xi_\psi|^2=0$ pointwise and the $q\,\partial_\zeta$ piece drops +identically. + +Only $\mathbf Y^*\!\cdot\mathbf V$ carries a $\theta$ derivative. Integrating +it by parts (the boundary term vanishes because $\chi'$ is a flux function and +$\mathcal F,|\xi_\psi|^2$ are $\theta$-periodic), its $|\xi_\psi|^2$ +coefficient becomes $-\chi'\,\partial_\theta\mathcal F$: +\begin{equation} +\int d\theta\,\chi'\mathcal F\,\partial_\theta|\xi_\psi|^2 +=-\int d\theta\;|\xi_\psi|^2\,\chi'\,\partial_\theta\mathcal F, +\qquad +-\chi'\,\partial_\theta\mathcal F +=-\frac{2\pi f'}{\chi'}\mathcal J S_\psi ++2\pi f'q'\chi' +-\mu_0p'\,\partial_\theta\!\left(\mathcal J\frac{\nabla\psi\cdot\nabla\theta}{|\nabla\psi|^2}\right), +\end{equation} + +A $\theta$ derivative cannot generate $q'=dq/d\psi$; the explicit +$+2\pi f'q'\chi'$ above only compensates the $q'$ introduced by rewriting part +of $-\chi'\partial_\theta\mathcal F$ through +$S_\psi=\tfrac{\chi'^2}{\mathcal J}[q'+\partial_\theta(\cdots)]$, so the net +$q'$ content of $-\chi'\partial_\theta\mathcal F$ is zero. As written, the +$+2\pi f'q'\chi'$ pairs below with the $-4\pi f'q'\chi'$ of +$\mathbf U^*\!\cdot\mathbf V$ to give $-2\pi f'q'\chi'\,\mathbf I$. + +Three immediate cancellations follow: + +\begin{equation} +-\frac{2\pi f'}{\chi'}\mathcal J S_\psi-\mathcal J\mu_0\sigma S_\psi +=-\mathcal J S_\psi\!\left(\frac{2\pi f'}{\chi'}+\mu_0\sigma\right), +\quad +\frac{\mu_0^2\sigma(\mathbf j\cdot\mathbf B)-\mu_0^2\sigma^2B^2}{|\nabla\psi|^2}=0, +\quad +2\pi f'q'\chi'-4\pi f'q'\chi'=-2\pi f'q'\chi', +\end{equation} +and substituting the DCON shear and curvature +\begin{equation} +S_\psi=\frac{\chi'^2}{\mathcal J}\left[q'+\partial_\theta\!\left(\frac{q \nabla\psi\cdot\nabla\theta-\nabla\psi\cdot\nabla\zeta}{|\nabla\psi|^2}\right)\right], +\qquad +\kappa_\psi=\frac{|\nabla\psi|^2}{B^2}\left[\mu_0 p'+\tfrac12\partial_\psi B^2+\tfrac12\partial_\theta B^2\frac{\nabla\psi\cdot\nabla\theta}{|\nabla\psi|^2}\right] +\end{equation} +(so that $\mathcal J$ cancels in $-\mathcal J S_\psi$ and the two $(\mu_0p')^2/B^2$ pieces combine), the whole sector becomes the single fully expanded identity +\begin{equation} +\begin{aligned} +&\int d\theta\,d\zeta\;\mathcal J +\left[ +(\mathbf Y^*\!\cdot\mathbf V+\mathbf V^*\!\cdot\mathbf Y) ++(\mathbf U^*\!\cdot\mathbf V+\mathbf V^*\!\cdot\mathbf U) ++|\mathbf V|^2 +-\mu_0K\frac{|\xi_\psi|^2}{|\nabla\psi|^2} +\right]\\ +&=\int d\theta\,d\zeta\;|\xi_\psi|^2 +\bigg[ +-2\pi f'q'\chi' ++2\mu_0p'\mathcal J\frac{\chi''}{\chi'} +-\mu_0p'\,\partial_\theta\!\left(\mathcal J\frac{\nabla\psi\cdot\nabla\theta}{|\nabla\psi|^2}\right) +-\mathcal J\frac{(\mu_0p')^2}{B^2}\\ +&\hspace{4em} +-\frac{\mu_0p'\mathcal J}{B^2}\partial_\psi B^2 +-\frac{\mu_0p'\mathcal J}{B^2}\partial_\theta B^2\frac{\nabla\psi\cdot\nabla\theta}{|\nabla\psi|^2} +-\chi'^2\!\left(\frac{2\pi f'}{\chi'}+\mu_0\sigma\right)\! +\left[q'+\partial_\theta\!\left(\frac{q \nabla\psi\cdot\nabla\theta-\nabla\psi\cdot\nabla\zeta}{|\nabla\psi|^2}\right)\right] +\bigg]. +\end{aligned} +\label{eq:H4_expanded} +\end{equation} + +Up to \eqref{eq:H4_expanded} no equilibrium input beyond the DCON currents has +been used. The first two terms give $-2\pi f'q'\chi'\mathbf I$ and +$\mu_0p'(\chi''/\chi')\mathbf J$; it remains to show, for arbitrary Fourier +amplitudes $\{v_m\}$, that the rest closes, +\begin{equation} +\begin{aligned} +&\int d\theta\,d\zeta\;|\xi_\psi|^2 +\bigg[ +-\chi'^2\!\left(\frac{2\pi f'}{\chi'}+\mu_0\sigma\right)\! +\left[q'+\partial_\theta\!\left(\frac{q \nabla\psi\cdot\nabla\theta-\nabla\psi\cdot\nabla\zeta}{|\nabla\psi|^2}\right)\right] +-\mu_0p'\,\partial_\theta\!\left(\mathcal J\frac{\nabla\psi\cdot\nabla\theta}{|\nabla\psi|^2}\right)\\ +&\hspace{4em} +-\mathcal J\frac{(\mu_0p')^2}{B^2} +-\frac{\mu_0p'\mathcal J}{B^2}\partial_\psi B^2 +-\frac{\mu_0p'\mathcal J}{B^2}\partial_\theta B^2\frac{\nabla\psi\cdot\nabla\theta}{|\nabla\psi|^2} +\bigg]\\ +&=\int d\theta\,d\zeta\;|\xi_\psi|^2 +\bigg[ +-2\pi f'q'\chi' +-\mu_0\sigma\chi'^2 q' +-2\pi f'\chi'\,\partial_\theta\!\left(\frac{q \nabla\psi\cdot\nabla\theta-\nabla\psi\cdot\nabla\zeta}{|\nabla\psi|^2}\right)\\ +&\hspace{4em} +-\mu_0\sigma\chi'^2\,\partial_\theta\!\left(\frac{q \nabla\psi\cdot\nabla\theta-\nabla\psi\cdot\nabla\zeta}{|\nabla\psi|^2}\right) +-\mu_0p'\,\partial_\theta\!\left(\mathcal J\frac{\nabla\psi\cdot\nabla\theta}{|\nabla\psi|^2}\right) +-\mathcal J\frac{(\mu_0p')^2}{B^2}\\ +&\hspace{4em} +-\frac{\mu_0p'\mathcal J}{B^2}\partial_\psi B^2 +-\frac{\mu_0p'\mathcal J}{B^2}\partial_\theta B^2\frac{\nabla\psi\cdot\nabla\theta}{|\nabla\psi|^2} +\bigg]. +\end{aligned} +\end{equation} + +\subsection{The $\sigma$ identity: collapse of the parallel-current coefficient} +\label{app:sigma} + +The remaining bracket closes because of a single physical identity: the +shear/parallel-current coefficient $\tfrac{2\pi f'}{\chi'}+\mu_0\sigma$ is not +zero but a pure pressure--geometry quantity. Write +$\sigma=\mathbf j\cdot\mathbf B/B^2$ in current components, using +$\mathbf B=\chi'\,(q\,\nabla\psi\times\nabla\theta-\nabla\psi\times\nabla\zeta)$ and +$\mathbf j=\mathcal J\,(j^\theta\,\nabla\zeta\times\nabla\psi+j^\zeta\,\nabla\psi\times\nabla\theta)$: +\begin{equation} +\begin{aligned} +\mu_0\mathbf j\cdot\mathbf B +&=\mathcal J\chi'\big[\mu_0 j^\theta\,(\nabla\zeta\times\nabla\psi)+\mu_0 j^\zeta\,(\nabla\psi\times\nabla\theta)\big] +\cdot(q\,\nabla\psi\times\nabla\theta-\nabla\psi\times\nabla\zeta),\\ +B^2&=\chi'^2\,|q\,\nabla\psi\times\nabla\theta-\nabla\psi\times\nabla\zeta|^2. +\end{aligned} +\end{equation} +Inserting the DCON contravariant currents +$\mu_0 j^\theta=-\dfrac{2\pi f'}{\mathcal J}$, +$\mu_0 j^\zeta=-\dfrac{\mu_0p'}{\chi'}-\dfrac{2\pi f'q}{\mathcal J}$ +and using $\nabla\zeta\times\nabla\psi=-\nabla\psi\times\nabla\zeta$ to group the $f'$ terms, +\begin{equation} +\begin{aligned} +\mu_0\mathbf j\cdot\mathbf B +&=\mathcal J\chi'\left[-\frac{2\pi f'}{\mathcal J}(q\,\nabla\psi\times\nabla\theta-\nabla\psi\times\nabla\zeta)-\frac{\mu_0p'}{\chi'}(\nabla\psi\times\nabla\theta)\right]\\ +&\qquad\cdot(q\,\nabla\psi\times\nabla\theta-\nabla\psi\times\nabla\zeta)\\ +&=-2\pi f'\chi'\,|q\,\nabla\psi\times\nabla\theta-\nabla\psi\times\nabla\zeta|^2 +-\mu_0p'\mathcal J\,(\nabla\psi\times\nabla\theta)\cdot(q\,\nabla\psi\times\nabla\theta-\nabla\psi\times\nabla\zeta). +\end{aligned} +\end{equation} +Dividing by $B^2$, +\begin{equation} +\mu_0\sigma=-\frac{2\pi f'}{\chi'}-\frac{\mu_0p'\mathcal J\,(\nabla\psi\times\nabla\theta)\cdot(q\,\nabla\psi\times\nabla\theta-\nabla\psi\times\nabla\zeta)}{B^2}, +\end{equation} +so the parallel-current coefficient collapses to a pure-$p'$ form, +\begin{equation} +\frac{2\pi f'}{\chi'}+\mu_0\sigma +=-\frac{\mu_0p'\mathcal J\,(\nabla\psi\times\nabla\theta)\cdot(q\,\nabla\psi\times\nabla\theta-\nabla\psi\times\nabla\zeta)}{B^2}. +\end{equation} +Applying the cross-product identity +$(\mathbf A\times\mathbf B)\cdot(\mathbf C\times\mathbf D)=(\mathbf A\cdot\mathbf C)(\mathbf B\cdot\mathbf D)-(\mathbf A\cdot\mathbf D)(\mathbf B\cdot\mathbf C)$ +to the two cross-product factors, +\begin{equation} +\begin{aligned} +|q\,\nabla\psi\times\nabla\theta-\nabla\psi\times\nabla\zeta|^2 +&=(\nabla\psi\cdot\nabla\psi)(q^2\nabla\theta\cdot\nabla\theta-2q\,\nabla\theta\cdot\nabla\zeta+\nabla\zeta\cdot\nabla\zeta) +-(q\,\nabla\psi\cdot\nabla\theta-\nabla\psi\cdot\nabla\zeta)^2,\\ +(\nabla\psi\times\nabla\theta)\cdot(q\,\nabla\psi\times\nabla\theta-\nabla\psi\times\nabla\zeta) +&=(\nabla\psi\cdot\nabla\psi)(q\,\nabla\theta\cdot\nabla\theta-\nabla\theta\cdot\nabla\zeta) +-(\nabla\psi\cdot\nabla\theta)(q\,\nabla\psi\cdot\nabla\theta-\nabla\psi\cdot\nabla\zeta). +\end{aligned} +\end{equation} +The first is $B^2/\chi'^2$; the second turns the collapse into the pure-$p'$, dot-product form +\begin{equation} +\frac{2\pi f'}{\chi'}+\mu_0\sigma +=-\frac{\mu_0p'\mathcal J}{B^2} +\big[(\nabla\psi\cdot\nabla\psi)(q\,\nabla\theta\cdot\nabla\theta-\nabla\theta\cdot\nabla\zeta) +-(\nabla\psi\cdot\nabla\theta)(q\,\nabla\psi\cdot\nabla\theta-\nabla\psi\cdot\nabla\zeta)\big]. +\end{equation} +Write the local shorthands +$\mathcal D\equiv(\nabla\psi\cdot\nabla\psi)(q\,\nabla\theta\cdot\nabla\theta-\nabla\theta\cdot\nabla\zeta) +-(\nabla\psi\cdot\nabla\theta)(q\,\nabla\psi\cdot\nabla\theta-\nabla\psi\cdot\nabla\zeta)$ +and +$G_s\equiv\dfrac{q\,\nabla\psi\cdot\nabla\theta-\nabla\psi\cdot\nabla\zeta}{|\nabla\psi|^2}$. +Inserting $\mu_0\sigma=-\tfrac{2\pi f'}{\chi'}-\tfrac{\mu_0 p'\mathcal J\,\mathcal D}{B^2}$ into the two $-\mu_0\sigma\chi'^2(\cdots)$ pieces of the distributed form, each $f'$ part \emph{exactly} cancels the existing $-2\pi f'\chi'(\cdots)$ partner: +\begin{equation} +\begin{aligned} +\cancel{-\,2\pi f'q'\chi'}\;-\,\mu_0\sigma\chi'^2 q' +&=\cancel{-\,2\pi f'q'\chi'}\;\cancel{+\,2\pi f'q'\chi'}\;+\;\frac{\mu_0p'\chi'^2\mathcal J\,q'\,\mathcal D}{B^2},\\ +\cancel{-\,2\pi f'\chi'\,\partial_\theta G_s}\;-\,\mu_0\sigma\chi'^2\,\partial_\theta G_s +&=\cancel{-\,2\pi f'\chi'\,\partial_\theta G_s}\;\cancel{+\,2\pi f'\chi'\,\partial_\theta G_s}\;+\;\frac{\mu_0p'\chi'^2\mathcal J\,\mathcal D}{B^2}\,\partial_\theta G_s. +\end{aligned} +\end{equation} +The crossed-out $f'$ pairs vanish identically, so the entire $\mathbf V$-sector +residual reduces to pure $\mu_0p'$ pieces; the surviving geometric +$\theta$-derivatives are closed by the Grad--Shafranov identity of +Sec.~\ref{app:master}. + +\subsection{Master identity, cofactor form, and the Grad--Shafranov closure} +\label{app:master} + +The closure is most transparent pointwise in $\theta$. + +\paragraph{Four weights and target.} +Write the sector as $\int d\theta\,\mathcal W\,|\xi_\psi|^2$ with +$\mathcal W=W_{UV}+W_V+W_K+W_{YV}$ ($\mathbf Y^*\!\cdot\mathbf V$ already +integrated by parts), abbreviating $g^{\psi\psi}=|\nabla\psi|^2$: +\begin{align} +W_{UV}&=2\mu_0p'\tfrac{\chi''}{\chi'}\mathcal J-4\pi f'q'\chi', +\label{eq:WUV}\\ +W_{V}&=\mathcal J\tfrac{\mu_0^2\sigma(\mathbf j\cdot\mathbf B)}{g^{\psi\psi}} + +\mathcal J\tfrac{(\mu_0p')^2}{B^2}, +\label{eq:WV}\\ +W_{K}&=-\mathcal J\mu_0\sigma S_\psi + -\mathcal J\tfrac{\mu_0^2\sigma^2B^2}{g^{\psi\psi}} + -\tfrac{2\mu_0p'\mathcal J\kappa_\psi}{g^{\psi\psi}}, +\label{eq:WK}\\ +W_{YV}&=-\tfrac{2\pi f'}{\chi'}\mathcal J S_\psi+2\pi f'q'\chi' + -\mu_0p'\,\partial_\theta\!\big(\mathcal J\tfrac{g^{\psi\theta}}{g^{\psi\psi}}\big). +\label{eq:WYV} +\end{align} +Target +$\;W_\star=\mu_0p'\tfrac{\chi''}{\chi'}\mathcal J+\mu_0p'\mathcal J'-2\pi f'q'\chi'$, +i.e.\ $\mathbf H_4=\mu_0p'(\tfrac{\chi''}{\chi'}\mathbf J+\mathbf J')-2\pi f'q'\chi'\mathbf I$. + +\paragraph{Step 1 — parallel current ($\mu_0\mathbf j\cdot\mathbf B=\mu_0\sigma B^2$).} +The first term of \eqref{eq:WV} and the second of \eqref{eq:WK} cancel pointwise: +\begin{equation} +\mathcal J\tfrac{\mu_0^2\sigma(\mathbf j\cdot\mathbf B)}{g^{\psi\psi}} +-\mathcal J\tfrac{\mu_0^2\sigma^2B^2}{g^{\psi\psi}}=0 +\;\Longrightarrow\; +W_V+W_K=\mathcal J\tfrac{(\mu_0p')^2}{B^2} +-\mathcal J\mu_0\sigma S_\psi +-\tfrac{2\mu_0p'\mathcal J\kappa_\psi}{g^{\psi\psi}}. +\label{eq:step1} +\end{equation} + +\paragraph{Step 2 — $\sigma$ identity +$\;\mu_0\sigma=-\tfrac{2\pi f'}{\chi'}-\tfrac{\mu_0p'\mathcal J\,\mathcal D}{B^2}$.} +The shear term of \eqref{eq:step1} splits: +\begin{equation} +-\mathcal J\mu_0\sigma S_\psi +=\underbrace{\tfrac{2\pi f'}{\chi'}\mathcal J S_\psi}_{\text{cancels \eqref{eq:WYV}}} ++\tfrac{\mu_0p'\mathcal J^2\mathcal D}{B^2}\,S_\psi . +\label{eq:step2} +\end{equation} + +\paragraph{Step 3 — sum $\mathcal W=\eqref{eq:WUV}+\eqref{eq:step1}+\eqref{eq:WYV}$.} +The $\tfrac{2\pi f'}{\chi'}\mathcal J S_\psi$ pair cancels and +$-4\pi f'q'\chi'+2\pi f'q'\chi'=-2\pi f'q'\chi'$, leaving +\begin{equation} +\mathcal W=-2\pi f'q'\chi' ++2\mu_0p'\tfrac{\chi''}{\chi'}\mathcal J ++\mathcal J\tfrac{(\mu_0p')^2}{B^2} +-\tfrac{2\mu_0p'\mathcal J\kappa_\psi}{g^{\psi\psi}} +-\mu_0p'\,\partial_\theta\!\big(\mathcal J\tfrac{g^{\psi\theta}}{g^{\psi\psi}}\big) ++\tfrac{\mu_0p'\mathcal J^2\mathcal D}{B^2}\,S_\psi . +\label{eq:step3} +\end{equation} + +\paragraph{Step 4 — $\mathcal W=W_\star$, divide by $\mu_0p'$ (master identity).} +Subtracting $W_\star$ (the $-2\pi f'q'\chi'$ and one $\mu_0p'\tfrac{\chi''}{\chi'}\mathcal J$ match): +\begin{equation} +\boxed{\; +\mathcal J\frac{\chi''}{\chi'} ++\frac{\mathcal J\,\mu_0p'}{B^2} +-\frac{2\mathcal J\,\kappa_\psi}{g^{\psi\psi}} +-\partial_\theta\!\Big(\mathcal J\frac{g^{\psi\theta}}{g^{\psi\psi}}\Big) ++\frac{\mathcal J^2\mathcal D}{B^2}\,S_\psi +=\mathcal J' \;} +\label{eq:master} +\end{equation} +with the inputs +\begin{equation} +\mathcal D +\equiv g^{\psi\psi}(q\,g^{\theta\theta}-g^{\theta\zeta}) +-g^{\psi\theta}(q\,g^{\psi\theta}-g^{\psi\zeta}), +\qquad +S_\psi=\frac{\chi'^2}{\mathcal J}\Big[q'+\partial_\theta G_s\Big], +\qquad +G_s\equiv\frac{q\,g^{\psi\theta}-g^{\psi\zeta}}{g^{\psi\psi}} . +\label{eq:DSdef} +\end{equation} + +\paragraph{Cofactor form of $\mathcal D$ ($g^{ij}=(g_{ij})^{-1}$, $\det g_{ij}=\mathcal J^2$).} +The minors are +$g^{\psi\psi}g^{\theta\theta}-(g^{\psi\theta})^2=g_{\zeta\zeta}/\mathcal J^2$, +$\;g^{\psi\psi}g^{\theta\zeta}-g^{\psi\theta}g^{\psi\zeta}=-g_{\theta\zeta}/\mathcal J^2$, hence +\begin{equation} +\boxed{\; +\mathcal D=\frac{q\,g_{\zeta\zeta}+g_{\theta\zeta}}{\mathcal J^2} +=\frac{\partial_q G_\alpha}{2\mathcal J^2}, +\qquad +\frac{\chi'^2\mathcal D}{B^2} +=\frac{q\,g_{\zeta\zeta}+g_{\theta\zeta}}{G_\alpha}, +\;} +\qquad +G_\alpha\equiv g_{\theta\theta}+2q\,g_{\theta\zeta}+q^2g_{\zeta\zeta}=\frac{\mathcal J^2B^2}{\chi'^2}. +\label{eq:cofactor} +\end{equation} + +\paragraph{Step 5 — insert $\kappa_\psi$ and $S_\psi$ into \eqref{eq:master}.} +With +$\kappa_\psi=\tfrac{g^{\psi\psi}}{B^2}\big[\mu_0p' ++\tfrac12\partial_\psi B^2+\tfrac12\partial_\theta B^2\,\tfrac{g^{\psi\theta}}{g^{\psi\psi}}\big]$, +\begin{equation} +-\frac{2\mathcal J\kappa_\psi}{g^{\psi\psi}} +=-\frac{2\mathcal J\mu_0p'}{B^2} + -\frac{\mathcal J\,\partial_\psi B^2}{B^2} + -\frac{\mathcal J\,g^{\psi\theta}\,\partial_\theta B^2}{g^{\psi\psi}B^2}, +\qquad +\frac{\mathcal J^2\mathcal D}{B^2}\,S_\psi +=\mathcal J\,\frac{q\,g_{\zeta\zeta}+g_{\theta\zeta}}{G_\alpha}\,\big(q'+\partial_\theta G_s\big). +\label{eq:step5a} +\end{equation} +Using $\beta^2\equiv B^2/\chi'^2=G_\alpha/\mathcal J^2$, +\begin{equation} +\frac{\partial_\psi B^2}{B^2}=2\frac{\chi''}{\chi'}+\frac{\partial_\psi\beta^2}{\beta^2}, +\qquad +\frac{\partial_\theta B^2}{B^2}=\frac{\partial_\theta G_\alpha}{G_\alpha}-2\frac{\partial_\theta\mathcal J}{\mathcal J}, +\qquad +\frac{\mathcal J\mu_0p'}{B^2}-\frac{2\mathcal J\mu_0p'}{B^2}=-\frac{\mathcal J\mu_0p'}{B^2}, +\label{eq:step5b} +\end{equation} +so $\mathcal J\tfrac{\chi''}{\chi'}-2\mathcal J\tfrac{\chi''}{\chi'}=-\mathcal J\tfrac{\chi''}{\chi'}$ and \eqref{eq:master} becomes +\begin{equation} +\boxed{\; +\begin{aligned} +&-\mathcal J\frac{\chi''}{\chi'} +-\frac{\mathcal J\mu_0p'}{B^2} +-\mathcal J\frac{\partial_\psi\beta^2}{\beta^2} +-\frac{\mathcal J\,g^{\psi\theta}}{g^{\psi\psi}}\Big(\frac{\partial_\theta G_\alpha}{G_\alpha}-2\frac{\partial_\theta\mathcal J}{\mathcal J}\Big)\\ +&\qquad +-\partial_\theta\!\Big(\mathcal J\frac{g^{\psi\theta}}{g^{\psi\psi}}\Big) ++\frac{\mathcal J\,(q\,g_{\zeta\zeta}+g_{\theta\zeta})}{G_\alpha}\big(q'+\partial_\theta G_s\big) +=\mathcal J' . +\end{aligned}\;} +\label{eq:gsstep} +\end{equation} + +Equation~\eqref{eq:gsstep} is the remaining Grad--Shafranov input. It can be +derived directly from the covariant-component form of +\(\nabla\times\mathbf B=\mu_0\mathbf j\), as follows. Define +\begin{equation} +A\equiv\frac{g^{\psi\theta}}{g^{\psi\psi}}, +\qquad +H\equiv qg_{\zeta\zeta}+g_{\theta\zeta}, +\qquad +\mathcal C_B\equiv B_\theta+qB_\zeta . +\end{equation} +Since \(B^\theta=\chi'/\mathcal J\) and \(B^\zeta=q\chi'/\mathcal J\), +\begin{equation} +B_i=g_{i\theta}B^\theta+g_{i\zeta}B^\zeta +=\frac{\chi'}{\mathcal J}(g_{i\theta}+qg_{i\zeta}), +\qquad +B_\zeta=\frac{\chi'}{\mathcal J}H, +\qquad +\mathcal C_B=\frac{\chi'}{\mathcal J}G_\alpha +=\frac{\mathcal J B^2}{\chi'} . +\end{equation} +The inverse-metric cofactor identities give +\begin{equation} +g_{\psi\theta}+qg_{\psi\zeta} +=-A\,G_\alpha+G_s\,H, +\end{equation} +and therefore +\begin{equation} +B_\psi=-A\,\mathcal C_B+G_s B_\zeta . +\label{eq:Bpsi_GS_closure} +\end{equation} + +For an axisymmetric equilibrium the contravariant curl components are +\begin{equation} +\mu_0j^\theta=-\frac{1}{\mathcal J}\partial_\psi B_\zeta, +\qquad +\mu_0j^\zeta= +\frac{1}{\mathcal J}\left(\partial_\psi B_\theta-\partial_\theta B_\psi\right). +\label{eq:covcurl_GS_closure} +\end{equation} +Using the physical-\(f'\) current convention of +\eqref{eq:app_dconcurrents}, the first of \eqref{eq:covcurl_GS_closure} gives +\begin{equation} +\partial_\psi B_\zeta=2\pi f', +\qquad +B_\zeta=2\pi f(\psi), +\qquad +\partial_\theta B_\zeta=0 . +\end{equation} +Here the last two statements are the usual Grad--Shafranov convention that the +covariant toroidal-field function \(f\) is a flux function; this is the same +convention encoded in \eqref{eq:app_dconcurrents}. The second of +\eqref{eq:covcurl_GS_closure} gives +\begin{equation} +\partial_\psi B_\theta-\partial_\theta B_\psi +=-\frac{\mathcal J\mu_0p'}{\chi'}-2\pi qf' . +\end{equation} +Substituting \(\mathcal C_B=B_\theta+qB_\zeta\) and +\(\partial_\psi B_\zeta=2\pi f'\) cancels the explicit \(2\pi qf'\) terms and +leaves +\begin{equation} +\partial_\psi\mathcal C_B-\partial_\theta B_\psi +=q'B_\zeta-\frac{\mathcal J\mu_0p'}{\chi'} . +\end{equation} +Using \eqref{eq:Bpsi_GS_closure} and \(\partial_\theta B_\zeta=0\), this becomes +\begin{equation} +\partial_\psi\mathcal C_B+\partial_\theta(A\mathcal C_B) +-B_\zeta(q'+\partial_\theta G_s) ++\frac{\mathcal J\mu_0p'}{\chi'}=0 . +\label{eq:CB_GS_closure} +\end{equation} + +Multiply \eqref{eq:CB_GS_closure} by +\(\chi'/B^2=\mathcal J/\mathcal C_B\). The three geometric factors reduce as +\begin{align} +\frac{\chi'}{B^2}\partial_\psi\mathcal C_B +&= +\mathcal J' ++\mathcal J\frac{\partial_\psi B^2}{B^2} +-\mathcal J\frac{\chi''}{\chi'},\\ +\frac{\chi'}{B^2}\partial_\theta(A\mathcal C_B) +&= +\partial_\theta(\mathcal J A) ++\mathcal J A\frac{\partial_\theta B^2}{B^2},\\ +\frac{\chi'}{B^2}B_\zeta +&= +\mathcal J\frac{H}{G_\alpha}. +\end{align} +Thus Grad--Shafranov gives +\begin{equation} +\begin{aligned} +\mathcal J' +&= +\mathcal J\frac{\chi''}{\chi'} +-\mathcal J\frac{\partial_\psi B^2}{B^2} +-\partial_\theta(\mathcal J A) +-\mathcal J A\frac{\partial_\theta B^2}{B^2}\\ +&\quad ++\mathcal J\frac{H}{G_\alpha}(q'+\partial_\theta G_s) +-\frac{\mathcal J\mu_0p'}{B^2}. +\end{aligned} +\label{eq:GS_before_beta} +\end{equation} +Finally use +\[ +\frac{\partial_\psi B^2}{B^2} +=2\frac{\chi''}{\chi'}+\frac{\partial_\psi\beta^2}{\beta^2}, +\qquad +\frac{\partial_\theta B^2}{B^2} +=\frac{\partial_\theta G_\alpha}{G_\alpha} +-2\frac{\partial_\theta\mathcal J}{\mathcal J}, +\qquad +H=qg_{\zeta\zeta}+g_{\theta\zeta}. +\] +Equation~\eqref{eq:GS_before_beta} is then exactly \eqref{eq:gsstep}. Hence the +master identity \eqref{eq:master} is an analytic consequence of the +Grad--Shafranov equilibrium, not a numerical closure. + +This closes the residual of Sec.~\ref{app:assembly}: multiplying~\eqref{eq:gsstep} by $\mu_0p'$ +converts the geometric $\theta$-derivative +$-\mu_0p'\partial_\theta(\mathcal J\,g^{\psi\theta}/g^{\psi\psi})$, the +$(\mu_0p')^2/B^2$ term of $|\mathbf V|^2$, and the pressure--curvature part of +$K$ into $\mu_0p'\mathcal J'$, so the bracket of~\eqref{eq:H4_expanded} becomes +$\mu_0p'(\mathcal J'-\mathcal J\chi''/\chi')$. Together with the clean +\(\mu_0p'\mathcal J\chi''/\chi'\) piece already isolated in +\eqref{eq:step3}, this gives +\[ +\mathcal W +=-2\pi f'q'\chi' ++\mu_0p'\left(\mathcal J\frac{\chi''}{\chi'}+\mathcal J'\right), +\] +and the compact $\mathbf H_4$ follows by Fourier projection. + +\subsection{The two equivalent final forms of $\mathbf H_4$} + +\paragraph{Equilibrium-free form.} +Projecting \eqref{eq:WUV}--\eqref{eq:WYV} directly, +\begin{equation} +\boxed{\;\mathbf H_4=\mathbf H_{YV}+\mathbf H_{UV}+\mathbf H_V+\mathbf H_K\;,} +\end{equation} +\begin{equation} +\begin{aligned} +\mathbf H_{UV}&=2\mu_0p'\tfrac{\chi''}{\chi'}\mathbf J-4\pi f'q'\chi'\,\mathbf I, +&\; +(\mathbf H_V)_{mm'}&=\Big\langle\tfrac{(\mu_0p')^2}{B^2}+\tfrac{\mu_0\sigma(\mu_0\mathbf j\cdot\mathbf B)}{g^{\psi\psi}}\Big\rangle_{mm'},\\[4pt] +(\mathbf H_K)_{mm'}&=-\Big\langle\mu_0\sigma S_\psi+\tfrac{\mu_0^2\sigma^2B^2}{g^{\psi\psi}}+\tfrac{2\mu_0p'\kappa_\psi}{g^{\psi\psi}}\Big\rangle_{mm'}, +&\; +(\mathbf H_{YV})_{mm'}&=-\chi'\!\int_0^1\! e^{2\pi i(m'-m)\theta}\,\partial_\theta\mathcal F\,d\theta, +\end{aligned} +\end{equation} +exact term by term ($=$ Eq.~\eqref{eq:ep_bernsteinK}), with no equilibrium +identity invoked. The compact form +$\mathbf H_4=\mu_0p'(\tfrac{\chi''}{\chi'}\mathbf J+\mathbf J')-2\pi f'q'\chi'\mathbf I$ +is the same matrix, obtained from \eqref{eq:WUV}--\eqref{eq:WYV} through the +Grad--Shafranov closure \eqref{eq:gsstep} derived above. The global +term-to-matrix assignment and the resulting $\mathbf C,\mathbf E,\mathbf H$ +matrices are collected in the main text. + +\bibliographystyle{plain} +\bibliography{references} + +\end{document} diff --git a/docs/tex/recon/references.bib b/docs/tex/recon/references.bib new file mode 100644 index 00000000..deb3bc5f --- /dev/null +++ b/docs/tex/recon/references.bib @@ -0,0 +1,58 @@ +@techreport{chance1992mhd, + author = {Chance, M. S. and Jardin, S. C. and Kessel, C. E. and Okabayashi, M. and others}, + title = {Mhd stability of tokamak plasmas}, + institution = {Princeton Univ., NJ (United States). Plasma Physics Lab}, + year = {1992}, + type = {Technical Report} +} +@article{RevModPhys.76.1071, + title = {Physics of magnetically confined plasmas}, + author = {Boozer, Allen H.}, + journal = {Rev. Mod. Phys.}, + volume = {76}, + issue = {4}, + pages = {1071--1141}, + numpages = {0}, + year = {2005}, + month = {Jan}, + publisher = {American Physical Society}, + doi = {10.1103/RevModPhys.76.1071}, + url = {https://link.aps.org/doi/10.1103/RevModPhys.76.1071} +} +@phdthesis{Park2009Ideal, + author = {Park, Jong-Kyu}, + title = {Ideal Perturbed Equilibria in Tokamaks}, + school = {Princeton University}, + year = {2009}, + month = {June}, + address = {Princeton, NJ}, + note = {Advisers: Jonathan E. Menard and Allen H. Boozer}, + department = {Department of Astrophysical Sciences, Program in Plasma Physics} +} +@incollection{Bernstein1983, + author = {Bernstein, I. B.}, + title = {Basic Plasma Physics I}, + booktitle = {Handbook of Plasma Physics, Volume 1}, + editor = {A. A. Galeev and R. N. Sudan}, + publisher = {North-Holland}, + address = {Amsterdam}, + year = {1983}, + pages = {421} +} +@article{10.1063/1.4958328, + author = {Glasser, A. H.}, + title = {The direct criterion of Newcomb for the ideal MHD stability of an axisymmetric toroidal plasma}, + journal = {Physics of Plasmas}, + volume = {23}, + number = {7}, + pages = {072505}, + year = {2016}, + month = {07}, + abstract = {A method is presented for determining the ideal magnetohydrodynamic stability of an axisymmetric toroidal plasma, based on a toroidal generalization of the method developed by Newcomb for fixed-boundary modes in a cylindrical plasma. For toroidal mode number n≠0, the stability problem is reduced to the numerical integration of a high-order complex system of ordinary differential equations, the Euler-Lagrange equation for extremizing the potential energy, for the coupled amplitudes of poloidal harmonics m as a function of the radial coordinate ψ in a straight-fieldline flux coordinate system. Unlike the cylindrical case, different poloidal harmonics couple to each other, which introduces coupling between adjacent singular intervals. A boundary condition is used at each singular surface, where m = nq and q(ψ) is the safety factor, to cross the singular surface and continue the solutions beyond it. Fixed-boundary instability is indicated by the vanishing of a real determinant of a Hermitian complex matrix constructed from the fundamental matrix of solutions, the generalization of Newcomb's crossing criterion. In the absence of fixed-boundary instabilities, an M × M plasma response matrix WP, with M the number of poloidal harmonics used, is constructed from the Euler-Lagrange solutions at the plasma-vacuum boundary. This is added to a vacuum response matrix WV to form a total response matrix WT. The existence of negative eigenvalues of WT indicates the presence of free-boundary instabilities. The method is implemented in the fast and accurate DCON code.}, + issn = {1070-664X}, + doi = {10.1063/1.4958328}, + url = {https://doi.org/10.1063/1.4958328}, + eprint = {https://pubs.aip.org/aip/pop/article-pdf/doi/10.1063/1.4958328/14131506/072505_1_online.pdf}, +} + + diff --git a/gpec/gpec.f b/gpec/gpec.f index 5342d12e..84b467ae 100644 --- a/gpec/gpec.f +++ b/gpec/gpec.f @@ -39,7 +39,7 @@ PROGRAM gpec_main $ arbsurf_flag,angles_flag,surfmode_flag,rzpgrid_flag, $ singcurs_flag,m3d_flag,cas3d_flag,test_flag,nrzeq_flag, $ arzphifun_flag,xbrzphifun_flag,pmodbmn_flag,xclebsch_flag, - $ filter_flag,gal_flag,delpsi_flag,use_res_spot, + $ filter_flag,gal_flag,delpsi_flag,recon_flag,use_res_spot, $ singthresh_callen_flag,singthresh_slayer_flag,singthresh_flag LOGICAL, DIMENSION(100) :: ss_flag COMPLEX(r8), DIMENSION(:), POINTER :: finmn,foutmn,xspmn, @@ -73,7 +73,9 @@ PROGRAM gpec_main $ xclebsch_flag,pbrzphi_flag,verbose,max_linesout,filter_flag, $ netcdf_flag,ascii_flag,singthresh_flag, $ singthresh_callen_flag,singthresh_slayer_flag, - $ singthresh_slayer_inpr,singthresh_slayer_inpr_prof + $ singthresh_slayer_inpr,singthresh_slayer_inpr_prof, + $ recon_flag,recon_int,cveri_flag,recon_out, + $ recon_flag2,recon_flag3 NAMELIST/gpec_diagnose/singcurs_flag,xbcontra_flag, $ xbnobo_flag,d3_flag,div_flag,xbst_flag,jacfac_flag, $ pmodbmn_flag,rzphibx_flag,radvar_flag,eigen_flag,magpot_flag, @@ -199,6 +201,12 @@ PROGRAM gpec_main test_flag=.FALSE. eigm_flag=.FALSE. mutual_test_flag=.FALSE. + recon_flag=.FALSE. + recon_flag2=.FALSE. + recon_flag3=.FALSE. + recon_int="spline" + cveri_flag=.FALSE. + recon_out=.TRUE. majr=10.0 minr=1.0 @@ -229,6 +237,12 @@ PROGRAM gpec_main READ(in_unit,NML=gpec_output) READ(in_unit,NML=gpec_diagnose) CALL ascii_close(in_unit) + SELECT CASE(TRIM(recon_int)) + CASE("spline","trapezoid") + CASE DEFAULT + CALL gpec_stop("gpec_output recon_int must be spline or "// + $ "trapezoid") + END SELECT galsol%gal_flag=gal_flag IF(timeit) CALL gpec_timer(0) c----------------------------------------------------------------------- @@ -619,7 +633,15 @@ PROGRAM gpec_main IF (arzphifun_flag) THEN CALL gpout_arzphifun(mode,xspmn) ENDIF - + IF (recon_flag) THEN + CALL gpout_recon(mode,xspmn) + ENDIF + IF (recon_flag2) THEN + CALL gpout_recon2(mode,xspmn) + ENDIF + IF (recon_flag3) THEN + CALL gpout_recon3(mode,xspmn) + ENDIF c----------------------------------------------------------------------- c diagnose. c----------------------------------------------------------------------- @@ -775,4 +797,3 @@ PROGRAM gpec_main CALL gpec_dealloc CALL gpec_stop("Normal termination.") END PROGRAM gpec_main - diff --git a/gpec/gpeq.f b/gpec/gpeq.f index c0e503a9..d846b9a8 100644 --- a/gpec/gpeq.f +++ b/gpec/gpeq.f @@ -14,17 +14,23 @@ c 6. gpeq_parallel c 7. gpeq_rzphi c 8. gpeq_surface -c 9. gpeq_fcoords -c 10. gpeq_fcoordsout -c 11. gpeq_bcoords -c 12. gpeq_bcoordsout -c 13. gpeq_weight -c 14. gpeq_rzpgrid -c 15. gpeq_rzpdiv -c 16. gpeq_alloc -c 17. gpeq_dealloc -c 18. gpeq_interp_singsurf -c 19. gpeq_interp_sol +c 9. gpeq_epf (reconstruction: C vector for EPF) +c 10. gpeq_dst (reconstruction: C vector for DST) +c 11. gpeq_shear (reconstruction: magnetic shear) +c 12. gpeq_curvature (reconstruction: curvature) +c 13. gpeq_K (reconstruction: Bernstein K quantity) +c 13b. gpeq_recon3 (reconstruction: |C|^2 decomposition) +c 14. gpeq_fcoords +c 15. gpeq_fcoordsout +c 16. gpeq_bcoords +c 17. gpeq_bcoordsout +c 18. gpeq_weight +c 19. gpeq_rzpgrid +c 20. gpeq_rzpdiv +c 21. gpeq_alloc +c 22. gpeq_dealloc +c 23. gpeq_interp_singsurf +c 24. gpeq_interp_sol c----------------------------------------------------------------------- c subprogram 0. gpeq_mod. c module declarations. @@ -96,7 +102,7 @@ SUBROUTINE gpeq_sol(psi) xss_mn=-MATMUL(bmat,xsp1_mn)-MATMUL(cmat,xsp_mn) ENDIF c----------------------------------------------------------------------- -c compute contravariant b fields. +c compute Jacobian-weighted contravariant b fields: J b^i. c----------------------------------------------------------------------- bwp_mn=(chi1*singfac*twopi*ifac*xsp_mn) bwt_mn=-(chi1*xsp1_mn+twopi*ifac*nn*xss_mn) @@ -225,7 +231,8 @@ SUBROUTINE gpeq_contra(psi) END SUBROUTINE gpeq_contra c----------------------------------------------------------------------- c subprogram 3. gpeq_cova. -c compute covariant components. +c compute Jacobian-weighted covariant components from +c Jacobian-weighted contravariant components. c----------------------------------------------------------------------- SUBROUTINE gpeq_cova(psi) c----------------------------------------------------------------------- @@ -261,7 +268,7 @@ SUBROUTINE gpeq_cova(psi) g31(1:mband)=CONJG(g31(-1:-mband:-1)) g12(1:mband)=CONJG(g12(-1:-mband:-1)) c----------------------------------------------------------------------- -c compute covariant components with metric tensors. +c compute Jacobian-weighted covariant components with metric tensors. c----------------------------------------------------------------------- ipert=0 xvp_mn=0 @@ -645,7 +652,1113 @@ SUBROUTINE gpeq_surface(psi) RETURN END SUBROUTINE gpeq_surface c----------------------------------------------------------------------- -c subprogram 9. gpeq_fcoords. +c subprogram 9. gpeq_c. +c compute C vector (covariant + contravariant) for EPF calculation. +c----------------------------------------------------------------------- + SUBROUTINE gpeq_c(psi, ipsi) +c----------------------------------------------------------------------- +c declaration. +c----------------------------------------------------------------------- + REAL(r8), INTENT(IN) :: psi + INTEGER, INTENT(IN) :: ipsi + + INTEGER :: itheta, ipert, jpert, m1, dm + + REAL(r8) :: q, q1, f1raw, p1, chi1, jac + REAL(r8) :: eta, rfac, v21, v22, v23, v33 + REAL(r8), DIMENSION(0:mthsurf) :: jacs, dphi, r_vec, z_vec + + COMPLEX(r8), DIMENSION(0:mthsurf) :: xwp_fun, xno_fun, bno_fun + COMPLEX(r8), DIMENSION(0:mthsurf) :: bwp_fun, bmt_fun, bmz_fun + COMPLEX(r8), DIMENSION(0:mthsurf) :: bvp_fun, bvt_fun, bvz_fun + +c Local temporal arrays for computation + COMPLEX(r8), DIMENSION(0:mthsurf) :: cwp_fun, cwt_fun, cwz_fun + COMPLEX(r8), DIMENSION(0:mthsurf) :: cvp_fun, cvt_fun, cvz_fun + COMPLEX(r8), DIMENSION(0:mthsurf) :: cvp_funp, cvt_funp, cvz_funp + +c Supporting variables for Fourier reconstruction + REAL(r8) :: jwt, jwz + +c Supporting functions + REAL(r8), DIMENSION(0:mthsurf) :: delpsi, dpdt, dpdz + REAL(r8), DIMENSION(0:mthsurf) :: g_22, g_23, g_33 + COMPLEX(r8), DIMENSION(-mband:mband) :: g11,g22,g33,g23,g31,g12 + + IF(debug_flag) PRINT *, "Entering gpeq_epf at ipsi=", ipsi +c----------------------------------------------------------------------- +c 1) Setup: equilibrium and metric at this psi. +c----------------------------------------------------------------------- + CALL spline_eval(sq, psi, 1) + CALL cspline_eval(metric%cs, psi, 0) + q = sq%f(4) + q1 = sq%f1(4) + f1raw = sq%f1(1) + p1 = sq%f1(2) / mu0 + chi1 = psio * twopi +c----------------------------------------------------------------------- + CALL iscdftb(mfac,mpert,xwp_fun,mthsurf,xwp_mn) +c----------------------------------------------------------------------- +c 3) Reconstruct eigenfunctions in spatial representation. +c----------------------------------------------------------------------- + CALL iscdftb(mfac, mpert, bwp_fun, mthsurf, bwp_mn) + CALL iscdftb(mfac, mpert, bmt_fun, mthsurf, bmt_mn) + CALL iscdftb(mfac, mpert, bmz_fun, mthsurf, bmz_mn) + CALL iscdftb(mfac, mpert, bvp_fun, mthsurf, bvp_mn) + CALL iscdftb(mfac, mpert, bvt_fun, mthsurf, bvt_mn) + CALL iscdftb(mfac, mpert, bvz_fun, mthsurf, bvz_mn) + +c----------------------------------------------------------------------- +c 4) Extract contravariant components: divide by jacobian. +c +c bwp_mn, bwt_mn, bwz_mn are complex Fourier mode coefficients +c (derived from complex eigenfunctions xsp_mn, xss_mn). +c After IFFT via iscdftb(), they remain complex-valued functions +c of theta. Dividing by real jacobian preserves complex nature. +c This is essential because perturbations are inherently complex +c functions in MHD stability analysis. +c----------------------------------------------------------------------- + DO itheta = 0, mthsurf + CALL bicube_eval(rzphi, psi, theta(itheta), 1) + jac = rzphi%f(4) + rfac = SQRT(rzphi%f(1)) + eta = twopi*(theta(itheta) + rzphi%f(2)) + r_vec(itheta) = ro + rfac*COS(eta) + z_vec(itheta) = zo + rfac*SIN(eta) + jacs(itheta) = jac + dphi(itheta) = rzphi%f(3) + + w(1,1) = (1.0+ rzphi%fy(2))*twopi**2*rfac*r_vec(itheta)/jac + w(1,2) = -rzphi%fy(1)*pi*r_vec(itheta)/(rfac*jac) + + w(2,1) = -twopi**2*rfac*r_vec(itheta)*rzphi%fx(2)/jac + w(2,2) = pi*r_vec(itheta)*rzphi%fx(1)/(rfac*jac) + + w(3,1) = (twopi*r_vec(itheta)*rfac/jac)* + $ (rzphi%fx(2)*rzphi%fy(3)-rzphi%fx(3)*(1+rzphi%fy(2))) + w(3,2) = (r_vec(itheta)/(2*rfac*jac))* + $ (rzphi%fx(3)*rzphi%fy(1)-rzphi%fx(1)*rzphi%fy(3)) + + delpsi(itheta) = SQRT(w(1,1)**2 + w(1,2)**2) + dpdt(itheta) = w(1,1)*w(2,1) + w(1,2)*w(2,2) + dpdz(itheta) = w(1,1)*w(3,1) + w(1,2)*w(3,2) + +c contravariant basis vectors (idcon_metric style - NO jac) + v21 = rzphi%fy(1)/(2*rfac) + v22 = (1+rzphi%fy(2))*twopi*rfac + v23 = rzphi%fy(3)*r_vec(itheta) + v33 = twopi*r_vec(itheta) + +c metric tensor: g_ij = sum(v_i * v_j) + g_22(itheta) = (v21**2 + v22**2 + v23**2) + g_33(itheta) = (v33**2) + g_23(itheta) = (v23*v33) + ENDDO + + xno_fun=xwp_fun/(jacs*delpsi) + bno_fun=bwp_fun/(jacs*delpsi) +c----------------------------------------------------------------------- +c 6) Compute C components. +c bwp stores J Q^psi, while bmt/bmz store the modified +c upper-family J Q^theta/J Q^zeta used consistently by gpeq_cova. +c +c The metric stored in metric%cs is g_ij / J, so lowering via gpeq_cova +c maps J Q^i -> Q_i. Therefore bv* and cv2* are lower/covariant +c components without an extra Jacobian factor. +c +c xwp_fun stores J xi^psi. Therefore: +c - upper J C^i corrections use xwp_fun/(J |grad psi|^2), +c - lower C_i corrections use xwp_fun/|grad psi|^2 or xwp_fun*j^i. +c This matches Eqs. (148)-(151) together with the metric convention +c used in idcon_metric. +c----------------------------------------------------------------------- + DO itheta = 0, mthsurf + jac = jacs(itheta) + jwt = - f1raw / jac + jwz = - p1 * mu0 / chi1 - f1raw * q / jac + +c J C^psi = J Q^psi + cwp_fun(itheta)= bwp_fun(itheta) + +c J C^theta = J Q^theta + xi^psi/|grad psi|^2 +c * [mu0 j^theta g_theta_zeta + mu0 j^zeta g_zeta_zeta] +c = J Q^theta + xwp_fun/(J |grad psi|^2) * [...] + cwt_fun(itheta) = bmt_fun(itheta) + xwp_fun(itheta) + $ / ((delpsi(itheta)**2) * jac )* + $ (jwt * g_23(itheta) + jwz * g_33(itheta)) + +c J C^zeta = J Q^zeta - xi^psi/|grad psi|^2 +c * [mu0 j^theta g_theta_theta + mu0 j^zeta g_theta_zeta] +c = J Q^zeta - xwp_fun/(J |grad psi|^2) * [...] + cwz_fun(itheta) = bmz_fun(itheta) - xwp_fun(itheta) + $ / ((delpsi(itheta)**2) * jac )* + $ (jwt * g_22(itheta) + jwz * g_23(itheta)) + +c C_psi = Q_psi + (J xi^psi)/|grad psi|^2 +c * [mu0 j^theta (grad psi.grad zeta) - mu0 j^zeta (grad psi.grad theta)] + cvp_fun(itheta) = bvp_fun(itheta) + + $ xwp_fun(itheta) / (delpsi(itheta)**2) * + $ (jwt * dpdz(itheta) - jwz * dpdt(itheta)) + +c C_theta = Q_theta + (J xi^psi) mu0 j^zeta + cvt_fun(itheta) = bvt_fun(itheta) + + $ xwp_fun(itheta) * jwz + +c C_zeta = Q_zeta - (J xi^psi) mu0 j^theta + cvz_fun(itheta) = bvz_fun(itheta) - + $ xwp_fun(itheta) * jwt + + ENDDO + cwp_mn = 0 + cwt_mn = 0 + cwz_mn = 0 +c----------------------------------------------------------------------- +c compute lower half of matrices. +c----------------------------------------------------------------------- + g11(0:-mband:-1)=metric%cs%f(1:mband+1) + g22(0:-mband:-1)=metric%cs%f(mband+2:2*mband+2) + g33(0:-mband:-1)=metric%cs%f(2*mband+3:3*mband+3) + g23(0:-mband:-1)=metric%cs%f(3*mband+4:4*mband+4) + g31(0:-mband:-1)=metric%cs%f(4*mband+5:5*mband+5) + g12(0:-mband:-1)=metric%cs%f(5*mband+6:6*mband+6) +c----------------------------------------------------------------------- +c compute upper half of matrices. +c----------------------------------------------------------------------- + g11(1:mband)=CONJG(g11(-1:-mband:-1)) + g22(1:mband)=CONJG(g22(-1:-mband:-1)) + g33(1:mband)=CONJG(g33(-1:-mband:-1)) + g23(1:mband)=CONJG(g23(-1:-mband:-1)) + g31(1:mband)=CONJG(g31(-1:-mband:-1)) + g12(1:mband)=CONJG(g12(-1:-mband:-1)) + + CALL iscdftf(mfac, mpert, cwp_fun, mthsurf,cwp_mn) + CALL iscdftf(mfac, mpert, cwt_fun, mthsurf,cwt_mn) + CALL iscdftf(mfac, mpert, cwz_fun, mthsurf,cwz_mn) + CALL iscdftf(mfac, mpert, cvp_fun, mthsurf,cvp_mn) + CALL iscdftf(mfac, mpert, cvt_fun, mthsurf,cvt_mn) + CALL iscdftf(mfac, mpert, cvz_fun, mthsurf,cvz_mn) + + ipert = 0 + c2vp_mn = 0 + c2vt_mn = 0 + c2vz_mn = 0 + DO m1=mlow,mhigh + ipert=ipert+1 + DO dm=MAX(1-ipert,-mband),MIN(mpert-ipert,mband) + jpert=ipert+dm + c2vp_mn(ipert)=c2vp_mn(ipert)+g11(dm)*cwp_mn(jpert)+ + $ g12(dm)*cwt_mn(jpert)+g31(dm)*cwz_mn(jpert) + c2vt_mn(ipert)=c2vt_mn(ipert)+g12(dm)*cwp_mn(jpert)+ + $ g22(dm)*cwt_mn(jpert)+g23(dm)*cwz_mn(jpert) + c2vz_mn(ipert)=c2vz_mn(ipert)+g31(dm)*cwp_mn(jpert)+ + $ g23(dm)*cwt_mn(jpert)+g33(dm)*cwz_mn(jpert) + ENDDO + ENDDO + + CALL iscdftb(mfac, mpert, cvp_funp, mthsurf, c2vp_mn) + CALL iscdftb(mfac, mpert, cvt_funp, mthsurf, c2vt_mn) + CALL iscdftb(mfac, mpert, cvz_funp, mthsurf, c2vz_mn) + + IF(debug_flag) PRINT *, "->Leaving gpeq_c at ipsi=", ipsi +c----------------------------------------------------------------------- +c terminate. +c----------------------------------------------------------------------- + RETURN + END SUBROUTINE gpeq_c +c----------------------------------------------------------------------- +c subprogram 9c. gpeq_cveri. +c verify the C-vector identity +c +c (curl C) · grad(psi) +c = (d C_zeta / d theta - d C_theta / d zeta) / J +c +c using the single-n Fourier convention +c +c exp[2*pi*i*(m*theta - n*zeta)]. +c +c Therefore +c +c d/dtheta -> 2*pi*i*m +c d/dzeta -> -2*pi*i*n +c +c and +c +c (curl C) · grad(psi) +c = (d_theta C_zeta + 2*pi*i*n*C_theta)/J. +c----------------------------------------------------------------------- + SUBROUTINE gpeq_cveri(psi, cveri_fun) +c----------------------------------------------------------------------- +c declaration. +c----------------------------------------------------------------------- + REAL(r8), INTENT(IN) :: psi + COMPLEX(r8), DIMENSION(0:mthsurf), INTENT(OUT) :: cveri_fun + + INTEGER :: itheta, ipert + COMPLEX(r8), DIMENSION(mpert) :: dth_cvz_mn, dzt_cvt_mn + COMPLEX(r8), DIMENSION(mpert) :: curlpsi_mn + + IF(debug_flag) PRINT *, "Entering gpeq_cveri" +c----------------------------------------------------------------------- +c prepare psi-local equilibrium and reconstruct C. +c----------------------------------------------------------------------- + CALL gpeq_sol(psi) + CALL gpeq_contra(psi) + CALL gpeq_cova(psi) + CALL gpeq_normal(psi) + CALL gpeq_c(psi, 0) + +c----------------------------------------------------------------------- +c exact theta/zeta derivatives in mode space. +c----------------------------------------------------------------------- + DO ipert = 1, mpert + dth_cvz_mn(ipert) = twopi * ifac * mfac(ipert) * cvz_mn(ipert) + dzt_cvt_mn(ipert) = -twopi * ifac * nn * cvt_mn(ipert) + curlpsi_mn(ipert) = dth_cvz_mn(ipert) - dzt_cvt_mn(ipert) + ENDDO + + CALL iscdftb(mfac, mpert, cveri_fun, mthsurf, curlpsi_mn) + +c----------------------------------------------------------------------- +c divide by Jacobian pointwise: +c (curl C) · grad(psi) = (d_theta C_zeta - d_zeta C_theta)/J +c----------------------------------------------------------------------- + DO itheta = 0, mthsurf + CALL bicube_eval(rzphi, psi, theta(itheta), 0) + jac = rzphi%f(4) + cveri_fun(itheta) = cveri_fun(itheta) / jac + ENDDO + + IF(debug_flag) PRINT *, "->Leaving gpeq_cveri" +c----------------------------------------------------------------------- +c terminate. +c----------------------------------------------------------------------- + RETURN + END SUBROUTINE gpeq_cveri +c----------------------------------------------------------------------- +c subprogram 9d. gpeq_firstform. +c evaluate the first-form plasma energy density components on one +c flux surface with gamma term omitted: +c +c |Q|^2 / mu0 +c j . (Q x xi*) / mu0 +c (div xi)* (xi . grad p) +c +c Here xi.grad p is evaluated directly as p'(psi) xi^psi, and +c div xi is evaluated directly from the Jacobian-weighted +c contravariant displacement: +c +c div xi = 1/J [ d(J xi^psi)/dpsi +c + d(J xi^theta)/dtheta +c + d(J xi^zeta)/dzeta ]. +c----------------------------------------------------------------------- + SUBROUTINE gpeq_firstform(psi, q2_int, jqx_int, pdiv_int, + $ total_int, q2_fun, jqx_fun, pdiv_fun, total_fun) +c----------------------------------------------------------------------- +c declaration. +c----------------------------------------------------------------------- + REAL(r8), INTENT(IN) :: psi + REAL(r8), INTENT(OUT) :: q2_int + COMPLEX(r8), INTENT(OUT) :: jqx_int, pdiv_int, total_int + COMPLEX(r8), DIMENSION(0:mthsurf), OPTIONAL, INTENT(OUT) :: + $ q2_fun, jqx_fun, pdiv_fun, total_fun + + INTEGER :: itheta + REAL(r8) :: f1raw, p1 + REAL(r8) :: q2_density + REAL(r8) :: mu0jtheta, mu0jzeta + COMPLEX(r8) :: jqx_density, total_density, pdiv_density + COMPLEX(r8) :: det_term, divxi_density, xigradp_density + COMPLEX(r8), DIMENSION(0:mthsurf) :: xsp_fun, xmp1_fun + COMPLEX(r8), DIMENSION(0:mthsurf) :: xwp_fun, xmt_fun, xmz_fun + COMPLEX(r8), DIMENSION(0:mthsurf) :: bwp_fun, bmt_fun, bmz_fun + COMPLEX(r8), DIMENSION(0:mthsurf) :: bvp_fun, bvt_fun, bvz_fun + TYPE(cspline_type) :: divspl + + IF(debug_flag) PRINT *, "Entering gpeq_firstform" +c----------------------------------------------------------------------- +c prepare psi-local perturbed equilibrium quantities. +c----------------------------------------------------------------------- + CALL gpeq_sol(psi) + CALL gpeq_contra(psi) + CALL gpeq_cova(psi) + + CALL spline_eval(sq, psi, 1) + f1raw = sq%f1(1) + p1 = sq%f1(2) / mu0 + + CALL iscdftb(mfac, mpert, xsp_fun, mthsurf, xsp_mn) + CALL iscdftb(mfac, mpert, xmp1_fun, mthsurf, xmp1_mn) + CALL iscdftb(mfac, mpert, xwp_fun, mthsurf, xwp_mn) + CALL iscdftb(mfac, mpert, xmt_fun, mthsurf, xmt_mn) + CALL iscdftb(mfac, mpert, xmz_fun, mthsurf, xmz_mn) + CALL iscdftb(mfac, mpert, bwp_fun, mthsurf, bwp_mn) + CALL iscdftb(mfac, mpert, bmt_fun, mthsurf, bmt_mn) + CALL iscdftb(mfac, mpert, bmz_fun, mthsurf, bmz_mn) + CALL iscdftb(mfac, mpert, bvp_fun, mthsurf, bvp_mn) + CALL iscdftb(mfac, mpert, bvt_fun, mthsurf, bvt_mn) + CALL iscdftb(mfac, mpert, bvz_fun, mthsurf, bvz_mn) + + CALL cspline_alloc(divspl, mthsurf, 2) + divspl%xs = theta + DO itheta = 0, mthsurf + divspl%fs(itheta,1) = xmt_fun(itheta) + divspl%fs(itheta,2) = xmz_fun(itheta) + ENDDO + CALL cspline_fit(divspl, "periodic") + + q2_int = 0.0_r8 + jqx_int = CMPLX(0.0_r8, 0.0_r8, r8) + pdiv_int = CMPLX(0.0_r8, 0.0_r8, r8) + total_int = CMPLX(0.0_r8, 0.0_r8, r8) + + DO itheta = 0, mthsurf + CALL bicube_eval(rzphi, psi, theta(itheta), 1) + CALL cspline_eval(divspl, theta(itheta), 1) + jac = rzphi%f(4) + jac1 = rzphi%fx(4) + +c pointwise contravariant current components: mu0 j^theta, +c mu0 j^zeta. These match the definitions already used in gpeq_c. + mu0jtheta = -f1raw / jac + mu0jzeta = -mu0 * p1 / chi1 - sq%f(4) * f1raw / jac + + q2_density = REAL(CONJG(bwp_fun(itheta)) * bvp_fun(itheta) + + $ CONJG(bmt_fun(itheta)) * bvt_fun(itheta) + + $ CONJG(bmz_fun(itheta)) * bvz_fun(itheta), r8) + $ / (mu0 * jac) + + det_term = -mu0jtheta * + $ (bwp_fun(itheta) * CONJG(xmz_fun(itheta)) - + $ bmz_fun(itheta) * CONJG(xwp_fun(itheta))) + + $ mu0jzeta * + $ (bwp_fun(itheta) * CONJG(xmt_fun(itheta)) - + $ bmt_fun(itheta) * CONJG(xwp_fun(itheta))) + jqx_density = det_term / (mu0 * jac) + + divxi_density = xmp1_fun(itheta) + + $ (jac1 / jac) * xsp_fun(itheta) + + $ divspl%f1(1) / jac - + $ (twopi * ifac * nn) * divspl%f(2) / jac + xigradp_density = p1 * xsp_fun(itheta) + pdiv_density = CONJG(divxi_density) * xigradp_density + total_density = CMPLX(q2_density, 0.0_r8, r8) - + $ jqx_density + pdiv_density + + IF (PRESENT(q2_fun)) q2_fun(itheta) = + $ CMPLX(q2_density, 0.0_r8, r8) + IF (PRESENT(jqx_fun)) jqx_fun(itheta) = jqx_density + IF (PRESENT(pdiv_fun)) pdiv_fun(itheta) = pdiv_density + IF (PRESENT(total_fun)) total_fun(itheta) = total_density + + IF (itheta < mthsurf) THEN + q2_int = q2_int + q2_density * jac / REAL(mthsurf, r8) + jqx_int = jqx_int + jqx_density * jac / REAL(mthsurf, r8) + pdiv_int = pdiv_int + pdiv_density * jac / + $ REAL(mthsurf, r8) + total_int = total_int + total_density * jac / + $ REAL(mthsurf, r8) + ENDIF + ENDDO + + CALL cspline_dealloc(divspl) + + IF(debug_flag) PRINT *, "->Leaving gpeq_firstform" +c----------------------------------------------------------------------- +c terminate. +c----------------------------------------------------------------------- + RETURN + END SUBROUTINE gpeq_firstform +c----------------------------------------------------------------------- +c subprogram 9b. gpeq_epf. +c compute the flux-surface integral of the first EPF kernel, +c +c \int dtheta dzeta J |C|^2 / mu0 +c +c and return the theta/zeta integrated value for one psi. +c----------------------------------------------------------------------- + SUBROUTINE gpeq_epf(psi, epf_int, epf_p, epf_t, epf_z, + $ epf_den_fun, epf_p_fun, epf_t_fun, epf_z_fun) +c----------------------------------------------------------------------- +c declaration. +c----------------------------------------------------------------------- + REAL(r8), INTENT(IN) :: psi + REAL(r8), INTENT(OUT) :: epf_int + REAL(r8), OPTIONAL, INTENT(OUT) :: epf_p, epf_t, epf_z +c optional per-theta C^2/mu0 densities for R-Z heatmaps. Same +c convention as gpeq_recon3 epf_density: integral = sum den*jac/mth. + REAL(r8), DIMENSION(0:mthsurf), OPTIONAL, INTENT(OUT) :: + $ epf_den_fun, epf_p_fun, epf_t_fun, epf_z_fun + INTEGER :: itheta + COMPLEX(r8), DIMENSION(0:mthsurf) :: cwp_fun, cwt_fun, cwz_fun + COMPLEX(r8), DIMENSION(0:mthsurf) :: cvp_fun, cvt_fun, cvz_fun + REAL(r8) :: epf_psi_int, epf_theta_int, epf_zeta_int + REAL(r8) :: dpsi_d, dthe_d, dzet_d + + IF(debug_flag) PRINT *, "Entering gpeq_epf" +c----------------------------------------------------------------------- +c Prepare psi-local perturbed equilibrium and compute C components. +c----------------------------------------------------------------------- + CALL gpeq_sol(psi) + CALL gpeq_contra(psi) + CALL gpeq_cova(psi) + CALL gpeq_normal(psi) + CALL gpeq_c(psi, 0) + + CALL iscdftb(mfac, mpert, cwp_fun, mthsurf, cwp_mn) + CALL iscdftb(mfac, mpert, cwt_fun, mthsurf, cwt_mn) + CALL iscdftb(mfac, mpert, cwz_fun, mthsurf, cwz_mn) + CALL iscdftb(mfac, mpert, cvp_fun, mthsurf, cvp_mn) + CALL iscdftb(mfac, mpert, cvt_fun, mthsurf, cvt_mn) + CALL iscdftb(mfac, mpert, cvz_fun, mthsurf, cvz_mn) + + epf_int = 0.0_r8 + epf_psi_int = 0.0_r8 + epf_theta_int = 0.0_r8 + epf_zeta_int = 0.0_r8 + DO itheta = 0, mthsurf + CALL bicube_eval(rzphi, psi, theta(itheta), 0) + jac = rzphi%f(4) +c per-theta densities (Re(conjg(cw_i) cv_i)/(mu0 jac)). + dpsi_d = REAL(CONJG(cwp_fun(itheta)) * cvp_fun(itheta), r8) + $ / (mu0 * jac) + dthe_d = REAL(CONJG(cwt_fun(itheta)) * cvt_fun(itheta), r8) + $ / (mu0 * jac) + dzet_d = REAL(CONJG(cwz_fun(itheta)) * cvz_fun(itheta), r8) + $ / (mu0 * jac) + IF (PRESENT(epf_p_fun)) epf_p_fun(itheta) = dpsi_d + IF (PRESENT(epf_t_fun)) epf_t_fun(itheta) = dthe_d + IF (PRESENT(epf_z_fun)) epf_z_fun(itheta) = dzet_d + IF (PRESENT(epf_den_fun)) epf_den_fun(itheta) = + $ dpsi_d + dthe_d + dzet_d + IF (itheta < mthsurf) THEN + epf_psi_int = epf_psi_int + dpsi_d*jac/REAL(mthsurf, r8) + epf_theta_int = epf_theta_int + dthe_d*jac/REAL(mthsurf, r8) + epf_zeta_int = epf_zeta_int + dzet_d*jac/REAL(mthsurf, r8) + ENDIF + ENDDO + epf_int = epf_psi_int + epf_theta_int + epf_zeta_int + IF (PRESENT(epf_p)) epf_p = epf_psi_int + IF (PRESENT(epf_t)) epf_t = epf_theta_int + IF (PRESENT(epf_z)) epf_z = epf_zeta_int + + IF(debug_flag) PRINT *, "->Leaving gpeq_epf" +c----------------------------------------------------------------------- +c terminate. +c----------------------------------------------------------------------- + RETURN + END SUBROUTINE gpeq_epf +c----------------------------------------------------------------------- +c subprogram 10. gpeq_dst. +c compute the flux-surface integral of the destabilizing kernel, +c +c \int dtheta dzeta J * K * xi_n^2 +c +c and return the theta/zeta integrated value for one psi. +c----------------------------------------------------------------------- + SUBROUTINE gpeq_dst(psi, dst_int, dst_t1, dst_t2, dst_t3, + $ dst1_den_fun, dst2_den_fun, dst3_den_fun) + REAL(r8), INTENT(IN) :: psi + COMPLEX(r8), INTENT(OUT) :: dst_int + COMPLEX(r8), OPTIONAL, INTENT(OUT) :: dst_t1, dst_t2, dst_t3 +c optional per-theta K_i xi_n^2 densities for R-Z heatmaps. Same +c convention as gpeq_recon3: integral = sum den*jac/mthsurf. + REAL(r8), DIMENSION(0:mthsurf), OPTIONAL, INTENT(OUT) :: + $ dst1_den_fun, dst2_den_fun, dst3_den_fun + INTEGER :: itheta + COMPLEX(r8), DIMENSION(0:mthsurf) :: K_fun, xno_fun + REAL(r8), DIMENSION(0:mthsurf) :: K_term1, K_term2, K_term3 + REAL(r8) :: xin2_fac, xin2_loc, d1, d2, d3 + COMPLEX(r8) :: dst1_int, dst2_int, dst3_int + +c----------------------------------------------------------------------- +c DST(psi) = \int dtheta dzeta J * K * xi_n^2 +c----------------------------------------------------------------------- + CALL gpeq_sol(psi) + CALL gpeq_contra(psi) + CALL gpeq_cova(psi) + CALL gpeq_normal(psi) + CALL gpeq_K(psi, K_fun, K_term1, K_term2, K_term3) + CALL iscdftb(mfac, mpert, xno_fun, mthsurf, xno_mn) + dst_int = CMPLX(0.0_r8, 0.0_r8, r8) + dst1_int = CMPLX(0.0_r8, 0.0_r8, r8) + dst2_int = CMPLX(0.0_r8, 0.0_r8, r8) + dst3_int = CMPLX(0.0_r8, 0.0_r8, r8) + DO itheta = 0, mthsurf + CALL bicube_eval(rzphi, psi, theta(itheta), 0) + jac = rzphi%f(4) + xin2_loc = ABS(xno_fun(itheta))**2 + d1 = K_term1(itheta) * xin2_loc + d2 = K_term2(itheta) * xin2_loc + d3 = K_term3(itheta) * xin2_loc + IF (PRESENT(dst1_den_fun)) dst1_den_fun(itheta) = d1 + IF (PRESENT(dst2_den_fun)) dst2_den_fun(itheta) = d2 + IF (PRESENT(dst3_den_fun)) dst3_den_fun(itheta) = d3 + IF (itheta < mthsurf) THEN + xin2_fac = jac / REAL(mthsurf, r8) + dst1_int = dst1_int + CMPLX(d1 * xin2_fac, 0.0_r8, r8) + dst2_int = dst2_int + CMPLX(d2 * xin2_fac, 0.0_r8, r8) + dst3_int = dst3_int + CMPLX(d3 * xin2_fac, 0.0_r8, r8) + ENDIF + ENDDO + dst_int = dst1_int + dst2_int + dst3_int + IF (PRESENT(dst_t1)) dst_t1 = dst1_int + IF (PRESENT(dst_t2)) dst_t2 = dst2_int + IF (PRESENT(dst_t3)) dst_t3 = dst3_int +c----------------------------------------------------------------------- +c terminate. +c----------------------------------------------------------------------- + RETURN + END SUBROUTINE gpeq_dst +c----------------------------------------------------------------------- +c subprogram 11. gpeq_shear. +c compute magnetic shear at a single psi level. +c approach: compute spatial domain first (theta), then FFT to mode space. +c----------------------------------------------------------------------- + SUBROUTINE gpeq_shear(psi, shear_fun) + REAL(r8), INTENT(IN) :: psi + COMPLEX(r8), DIMENSION(0:mthsurf), INTENT(OUT) :: shear_fun + + INTEGER :: itheta + REAL(r8) :: r_val, theta_val + REAL(r8) :: dpdp, dpdt, dpdz, shear_deriv + REAL(r8) :: shear_contra + TYPE(spline_type) :: shear_temp + +c----------------------------------------------------------------------- +c Setup: get equilibrium at this psi. +c----------------------------------------------------------------------- + CALL spline_eval(sq, psi, 1) + q = sq%f(4) + q1 = sq%f1(4) + +c----------------------------------------------------------------------- +c Allocate spline for shear_temp with 5 components: +c 1: legacy covariant numerator term +c 2: (q*dpdt - dpdz) numerator +c 3: g_psi_g_psi denominator +c 4: legacy covariant denominator +c 5: DCON shear geometry term (component 2 / component 3) +c----------------------------------------------------------------------- + CALL spline_alloc(shear_temp, mthsurf, 5) + shear_temp%xs = theta(0:mthsurf) + shear_temp%name = "shear_" + +c----------------------------------------------------------------------- +c Step 1: Compute shear components at all theta points. +c----------------------------------------------------------------------- + DO itheta = 0, mthsurf + theta_val = theta(itheta) + CALL bicube_eval(rzphi, psi, theta(itheta), 1) + + jac = rzphi%f(4) + rfac = SQRT(MAX(rzphi%f(1), 0.0_r8)) + eta = twopi*(theta_val + rzphi%f(2)) + r_val = ro + rfac*COS(eta) + + +c Compute w_ij contravariant metric in Cartesian components + w(1,1) = (1.0_r8 + rzphi%fy(2))*twopi**2*rfac*r_val/jac + w(1,2) = -rzphi%fy(1)*pi*r_val/(rfac*jac) + + w(2,1) = -twopi**2*rfac*r_val*rzphi%fx(2)/jac + w(2,2) = pi*r_val*rzphi%fx(1)/(rfac*jac) + + w(3,1) = (twopi*r_val*rfac/jac)* + $ (rzphi%fx(2)*rzphi%fy(3) - rzphi%fx(3)*(1.0_r8 + + $ rzphi%fy(2))) + w(3,2) = (r_val/(2.0_r8*rfac*jac))* + $ (rzphi%fx(3)*rzphi%fy(1) - rzphi%fx(1)*rzphi%fy(3)) + +c Compute covariant basis v_ij = ∂r/∂ξ_j (Cartesian components) +c From recon_metric: + v(1,1) = rzphi%fx(1)/(2.0_r8*rfac*jac) + v(1,2) = rzphi%fx(2)*twopi*rfac/jac + v(1,3) = rzphi%fx(3)*r_val/jac + + v(2,1) = rzphi%fy(1)/(2.0_r8*rfac*jac) + v(2,2) = (1.0_r8 + rzphi%fy(2))*twopi*rfac/jac + v(2,3) = rzphi%fy(3)*r_val/jac + +c Component 33 of covariant basis + v(3,3) = twopi*r_val/jac + +c Contravariant dot products: dpdp = ∇ψ·∇ψ, etc. + dpdp = w(1,1)**2 + w(1,2)**2 + dpdt = w(1,1)*w(2,1) + w(1,2)*w(2,2) + dpdz = w(1,1)*w(3,1) + w(1,2)*w(3,2) + + shear_temp%fs(itheta, 1) = twopi*r_val*jac* + $ (-(v(1,1)*v(2,1)+v(1,2)*v(2,2))*(v(2,3)+q*v(3,3)) + $ +v(1,3)*(v(2,1)**2+v(2,2)**2)) + shear_temp%fs(itheta, 2) = (q*dpdt - dpdz) + shear_temp%fs(itheta, 3) = dpdp + shear_temp%fs(itheta, 4) = + $ twopi**2 * r_val**2 * (v(2,1)**2 + v(2,2)**2) + + + shear_temp%fs(itheta, 5) = + $ shear_temp%fs(itheta, 2) / shear_temp%fs(itheta, 3) + + ENDDO +c----------------------------------------------------------------------- +c Step 2: Fit spline to compute derivatives. +c----------------------------------------------------------------------- + CALL spline_fit(shear_temp, "periodic") +c----------------------------------------------------------------------- +c Step 3: Compute shear_fun = (chi1^2/J)*(q' + d shear_temp/d theta) +c with shear_temp = (q*g^psi_theta - g^psi_zeta)/g^psi_psi. +c----------------------------------------------------------------------- + DO itheta = 0, mthsurf + theta_val = theta(itheta) + + CALL spline_eval(shear_temp, theta_val, 1) + CALL bicube_eval(rzphi, psi, theta_val, 1) + jac = rzphi%f(4) + +c Contravariant approach: derivative of component 5 + shear_deriv = shear_temp%f1(5) + shear_contra = (chi1**2/jac)*(q1 + shear_deriv) + shear_fun(itheta) = CMPLX(shear_contra, 0.0_r8, r8) + + ENDDO + +c----------------------------------------------------------------------- +c Step 4: Transform to mode space via FFT. +c----------------------------------------------------------------------- + CALL spline_dealloc(shear_temp) +c----------------------------------------------------------------------- + RETURN + END SUBROUTINE gpeq_shear +c----------------------------------------------------------------------- +c subprogram 12. gpeq_curvature. +c compute curvature in spatial domain. +c κ·∇ψ = (|∇ψ|²/B²)[μ₀p' + (1/2)(∂B²/∂ψ) + (1/2)(∂B²/∂θ)(∇ψ·∇θ)/(∇ψ·∇ψ)] +c----------------------------------------------------------------------- + SUBROUTINE gpeq_curvature(psi, curv_fun) + REAL(r8), INTENT(IN) :: psi + COMPLEX(r8), DIMENSION(0:mthsurf), INTENT(OUT) :: curv_fun + + INTEGER :: itheta + REAL(r8) :: r_val, theta_val + REAL(r8) :: delpsi + REAL(r8) :: bsq_val, bsq_psi, bsq_theta + REAL(r8) :: dpdt , kappa_psi + +c----------------------------------------------------------------------- +c Setup: get equilibrium at this psi. +c----------------------------------------------------------------------- + CALL spline_eval(sq, psi, 1) + p1 = sq%f1(2) / mu0 + +c----------------------------------------------------------------------- +c Step 3: Compute curvature at all theta points. +c----------------------------------------------------------------------- + DO itheta = 0, mthsurf + theta_val = theta(itheta) + CALL bicube_eval(rzphi, psi, theta_val, 1) + + jac = rzphi%f(4) + rfac = SQRT(rzphi%f(1)) + eta = twopi*(theta_val + rzphi%f(2)) + r_val = ro + rfac*COS(eta) + +c Compute contravariant metrics + w(1,1) = (1.0+ rzphi%fy(2))*(twopi**2)*rfac*r_val/jac + w(1,2) = -rzphi%fy(1)*pi*r_val/(rfac*jac) + + w(2,1) = -(twopi**2)*rfac*r_val*rzphi%fx(2)/jac + w(2,2) = pi*r_val*rzphi%fx(1)/(rfac*jac) + + w(3,1) = (twopi*r_val*rfac/jac)* + $ (rzphi%fx(2)*rzphi%fy(3) - rzphi%fx(3)* + $ (1.0_r8 + rzphi%fy(2))) + w(3,2) = (r_val/(2.0_r8*rfac*jac))* + $ (rzphi%fx(3)*rzphi%fy(1) - rzphi%fx(1)*rzphi%fy(3)) + +c Compute |∇ψ|² and ∇ψ·∇θ + delpsi = SQRT(w(1,1)**2 + w(1,2)**2) + dpdt = w(1,1)*w(2,1) + w(1,2)*w(2,2) + + CALL bicube_eval(eqfun, psi, theta_val, 1) +c Retrieve B² and derivatives + bsq_val = eqfun%f(1) ** 2 + bsq_psi = eqfun%fx(1) * 2 * eqfun%f(1) + bsq_theta = eqfun%fy(1) * 2 * eqfun%f(1) + +c Compute κ·∇ψ safely + + kappa_psi = (delpsi**2 / bsq_val) * + $ (p1*mu0 + 0.5_r8*bsq_psi + + $ 0.5_r8*bsq_theta*dpdt/(delpsi**2)) + + curv_fun(itheta) = CMPLX(kappa_psi, 0.0_r8, r8) + + ENDDO +c----------------------------------------------------------------------- + RETURN + END SUBROUTINE gpeq_curvature +c----------------------------------------------------------------------- +c subprogram 13. gpeq_K. +c compute Bernstein K quantity (stability indicator). +c K = |∇ψ_dcon|^2 * σ * S_dcon +c + mu0 * B^2 * σ^2 + 2 p' * κ^psi +c Uses pre-computed metric from idcon_metric +c----------------------------------------------------------------------- + SUBROUTINE gpeq_K(psi, K_fun, K_term1, K_term2, K_term3, + $ sigma_fun, jdotb_fun, shear_out, curv_out) + REAL(r8), INTENT(IN) :: psi + COMPLEX(r8), DIMENSION(0:mthsurf), INTENT(OUT) :: K_fun + REAL(r8), DIMENSION(0:mthsurf), OPTIONAL, INTENT(OUT) :: + $ K_term1, K_term2, K_term3, sigma_fun, jdotb_fun + COMPLEX(r8), DIMENSION(0:mthsurf), OPTIONAL, INTENT(OUT) :: + $ shear_out, curv_out + + INTEGER :: itheta + REAL(r8) :: f1raw, delpsi + REAL(r8) :: jwt, jwz, bth, bze, bsq_val, sigma, jdotb_val + REAL(r8) :: g22, g23, g33, r_val + REAL(r8), DIMENSION(0:mthsurf) :: K_t1, K_t2, K_t3 + REAL(r8), DIMENSION(0:mthsurf) :: sigma_vals, jdotb_vals + COMPLEX(r8), DIMENSION(0:mthsurf) :: shear_fun, curv_fun + + chi1 = psio * twopi + + IF(debug_flag) PRINT *, "Entering gpeq_K" +c----------------------------------------------------------------------- +c load equilibrium parameters +c----------------------------------------------------------------------- + CALL spline_eval(sq, psi, 1) + f1raw = sq%f1(1) + p1 = sq%f1(2) / mu0 + q = sq%f(4) +c----------------------------------------------------------------------- +c compute shear and curvature +c----------------------------------------------------------------------- + CALL gpeq_shear(psi, shear_fun) + CALL gpeq_curvature(psi, curv_fun) +c----------------------------------------------------------------------- +c compute K in spatial domain (idcon_metric convention) +c----------------------------------------------------------------------- + DO itheta = 0, mthsurf + CALL bicube_eval(rzphi, psi, theta(itheta), 1) + jac = rzphi%f(4) + rfac = SQRT(rzphi%f(1)) + eta = twopi*(theta(itheta) + rzphi%f(2)) + r_val = ro + rfac*COS(eta) + +c |∇ψ| magnitude + w(1,1)=(1+rzphi%fy(2))*twopi**2*rfac*r_val/jac + w(1,2)=-rzphi%fy(1)*pi*r_val/(rfac*jac) + delpsi=SQRT(w(1,1)**2+w(1,2)**2) + +c current and magnetic field components +c note: -f1raw/jac is the mu0*j^theta component (DCON convention), +c so divide by mu0 to get the physical current j for K = mu0*sigma^2*B^2. + jwt = -f1raw/(jac*mu0) + jwz = q*jwt - p1/chi1 + bth = chi1 / jac + bze = q * chi1 / jac + +c contravariant basis vectors (idcon_metric style - NO jac) + v(2,1) = rzphi%fy(1)/(2*rfac) + v(2,2) = (1+rzphi%fy(2))*twopi*rfac + v(2,3) = rzphi%fy(3)*r_val + + v(3,3) = twopi*r_val + +c metric tensor: g_ij = sum(v_i * v_j) (idcon_metric convention) + g22 = (v(2,1)**2 + v(2,2)**2 + v(2,3)**2) + g33 = (v(3,3)**2) + g23 = (v(2,3)*v(3,3)) + +c j·B and σ = (j·B)/B² + CALL bicube_eval(eqfun, psi, theta(itheta), 0) + bsq_val = eqfun%f(1)**2 + + jdotb_val = g22*bth*jwt + g33*bze*jwz + + $ g23*(bth*jwz + bze*jwt) + sigma = jdotb_val / bsq_val + sigma_vals(itheta) = sigma + jdotb_vals(itheta) = jdotb_val + +c Term1: |∇ψ_dcon|^2 * σ * S_dcon + K_t1(itheta) = (delpsi**2) * sigma * REAL(shear_fun(itheta)) + +c Term2: mu0 * B^2 * σ^2 + K_t2(itheta) = bsq_val * sigma**2 * mu0 + +c Term3: 2 p' * κ^psi + K_t3(itheta) = 2.0_r8 * REAL(curv_fun(itheta)) * p1 + ENDDO + +c total K = T1 + T2 + T3 + DO itheta = 0, mthsurf + K_fun(itheta) = CMPLX(K_t1(itheta) + K_t2(itheta) + + $ K_t3(itheta), 0.0_r8, r8) + ENDDO + +c optionally return individual term values + IF (PRESENT(K_term1)) K_term1 = K_t1 + IF (PRESENT(K_term2)) K_term2 = K_t2 + IF (PRESENT(K_term3)) K_term3 = K_t3 + IF (PRESENT(sigma_fun)) sigma_fun = sigma_vals + IF (PRESENT(jdotb_fun)) jdotb_fun = jdotb_vals + IF (PRESENT(shear_out)) shear_out = shear_fun + IF (PRESENT(curv_out)) curv_out = curv_fun + + IF(debug_flag) PRINT *, "Exiting gpeq_K" +c----------------------------------------------------------------------- +c terminate. +c----------------------------------------------------------------------- + RETURN + END SUBROUTINE gpeq_K +c----------------------------------------------------------------------- +c subprogram 13b. gpeq_recon3. +c Bernstein-form |C|^2 decomposition for recon3 diagnostic. +c Vector identity: C = Q + V with V = xi_n * mu0 * j x n_hat. +c |C|^2 = |Q|^2 + 2 Re(V*.Q) + |V|^2 +c j is purely tangent to flux surface (j.n=0), so +c |V|^2 = mu0^2 xi_n^2 |j|^2 = mu0^2 xi_n^2 (sigma^2 B^2 +c + p'^2 |grad psi|^2 / B^2). +c Returns the surface integrals (theta only, jac measure): +c A_int = int |Q|^2 / mu0 * J dtheta / mthsurf +c B_int = int 2 Re(V*.Q) / mu0 * J dtheta / mthsurf +c directly via V = C - Q (covariant pairing) +c Cpar_int = int mu0 sigma^2 B^2 xi_n^2 * J dtheta / mthsurf +c (= K_2 xi_n^2 integral; matches gpeq_dst's dst2) +c Iperp_int= int mu0 p'^2 |grad psi|^2 xi_n^2 / B^2 * +c J dtheta / mthsurf +c epf_int = int |C|^2 / mu0 * J dtheta / mthsurf +c Identity check: epf_int = A_int + B_int + Cpar_int + Iperp_int +c should hold within numerical roundoff. +c----------------------------------------------------------------------- + SUBROUTINE gpeq_recon3(psi, A_int, B_int, Iperp_int, Cpar_int, + $ epf_int, A_fun, B_fun, Iperp_fun, Cpar_fun, surf_extra) + REAL(r8), INTENT(IN) :: psi + REAL(r8), INTENT(OUT) :: A_int, B_int, Iperp_int, Cpar_int, + $ epf_int + REAL(r8), DIMENSION(0:mthsurf), OPTIONAL, INTENT(OUT) :: + $ A_fun, B_fun, Iperp_fun, Cpar_fun +c surf_extra: packed per-theta fields for R-Z heatmaps (recon_out). +c columns: 1 VdotQ_re, 2 VdotQ_im, 3 Bcur_den, 4 Bpre_den, +c 5/6 Qp_re/im, 7/8 Qt_re/im, 9/10 Qz_re/im, +c 11/12 Vt_re/im, 13/14 Vz_re/im, 15 jpar, 16 jperp, +c 17/18 xin_re/im, 19 delpsi, 20 Bmod, +c 21 dst1_den (K1 xin2), 22 dst3_den (K3 xin2). + REAL(r8), DIMENSION(0:mthsurf,22), OPTIONAL, INTENT(OUT) :: + $ surf_extra + + INTEGER :: itheta + REAL(r8) :: f1raw, p1_local + REAL(r8) :: A_density, B_density, Iperp_density, Cpar_density, + $ epf_density + REAL(r8) :: delpsi_val, bsq_val, sigma_local, jdotb_val + REAL(r8) :: jwt, jwz, bth, bze, g22, g33, g23, r_val + REAL(r8) :: xin2, mu0jwt, mu0jwz + REAL(r8) :: mu0jpar_t, mu0jpar_z, mu0jper_t, mu0jper_z + REAL(r8) :: jsq_val, jpar_val, bmod_val + COMPLEX(r8) :: jvt_loc, jvz_loc, vdotq_loc + COMPLEX(r8) :: jvt_cur, jvz_cur, jvt_pre, jvz_pre + COMPLEX(r8), DIMENSION(0:mthsurf) :: kfun_loc + REAL(r8), DIMENSION(0:mthsurf) :: kt1_loc, kt2_loc, kt3_loc + COMPLEX(r8), DIMENSION(0:mthsurf) :: bwp_fun, bmt_fun, bmz_fun + COMPLEX(r8), DIMENSION(0:mthsurf) :: bvp_fun, bvt_fun, bvz_fun + COMPLEX(r8), DIMENSION(0:mthsurf) :: cwp_fun, cwt_fun, cwz_fun + COMPLEX(r8), DIMENSION(0:mthsurf) :: cvp_fun, cvt_fun, cvz_fun + COMPLEX(r8), DIMENSION(0:mthsurf) :: xno_fun, xwp_fun + + IF(debug_flag) PRINT *, "Entering gpeq_recon3" +c----------------------------------------------------------------------- +c prepare psi-local perturbed equilibrium and C, Q. +c----------------------------------------------------------------------- + CALL gpeq_sol(psi) + CALL gpeq_contra(psi) + CALL gpeq_cova(psi) + CALL gpeq_normal(psi) + CALL gpeq_c(psi, 0) + + CALL spline_eval(sq, psi, 1) + f1raw = sq%f1(1) + p1_local = sq%f1(2) / mu0 + chi1 = psio * twopi + + CALL iscdftb(mfac, mpert, xno_fun, mthsurf, xno_mn) + CALL iscdftb(mfac, mpert, bwp_fun, mthsurf, bwp_mn) + CALL iscdftb(mfac, mpert, bmt_fun, mthsurf, bmt_mn) + CALL iscdftb(mfac, mpert, bmz_fun, mthsurf, bmz_mn) + CALL iscdftb(mfac, mpert, bvp_fun, mthsurf, bvp_mn) + CALL iscdftb(mfac, mpert, bvt_fun, mthsurf, bvt_mn) + CALL iscdftb(mfac, mpert, bvz_fun, mthsurf, bvz_mn) + CALL iscdftb(mfac, mpert, cwp_fun, mthsurf, cwp_mn) + CALL iscdftb(mfac, mpert, cwt_fun, mthsurf, cwt_mn) + CALL iscdftb(mfac, mpert, cwz_fun, mthsurf, cwz_mn) + CALL iscdftb(mfac, mpert, cvp_fun, mthsurf, cvp_mn) + CALL iscdftb(mfac, mpert, cvt_fun, mthsurf, cvt_mn) + CALL iscdftb(mfac, mpert, cvz_fun, mthsurf, cvz_mn) + CALL iscdftb(mfac, mpert, xwp_fun, mthsurf, xwp_mn) + +c K term breakdown (K1 shear, K3 curvature) for dst1/dst3 densities; +c only needed for the R-Z heatmap output. + IF (PRESENT(surf_extra)) THEN + CALL gpeq_K(psi, kfun_loc, kt1_loc, kt2_loc, kt3_loc) + ENDIF + + A_int = 0.0_r8 + B_int = 0.0_r8 + Iperp_int = 0.0_r8 + Cpar_int = 0.0_r8 + epf_int = 0.0_r8 +c----------------------------------------------------------------------- +c accumulate densities in theta with jac measure. +c----------------------------------------------------------------------- + DO itheta = 0, mthsurf + CALL bicube_eval(rzphi, psi, theta(itheta), 1) + CALL bicube_eval(eqfun, psi, theta(itheta), 0) + jac = rzphi%f(4) + rfac = SQRT(rzphi%f(1)) + eta = twopi*(theta(itheta) + rzphi%f(2)) + r_val = ro + rfac*COS(eta) + bsq_val = eqfun%f(1)**2 + +c |grad psi| + w(1,1)=(1+rzphi%fy(2))*twopi**2*rfac*r_val/jac + w(1,2)=-rzphi%fy(1)*pi*r_val/(rfac*jac) + delpsi_val=SQRT(w(1,1)**2+w(1,2)**2) + +c currents and B (idcon_metric convention, same as gpeq_K). + jwt = -f1raw/(jac*mu0) + jwz = sq%f(4)*jwt - p1_local/chi1 + bth = chi1 / jac + bze = sq%f(4) * chi1 / jac + + v(2,1) = rzphi%fy(1)/(2*rfac) + v(2,2) = (1+rzphi%fy(2))*twopi*rfac + v(2,3) = rzphi%fy(3)*r_val + v(3,3) = twopi*r_val + g22 = (v(2,1)**2 + v(2,2)**2 + v(2,3)**2) + g33 = (v(3,3)**2) + g23 = (v(2,3)*v(3,3)) + + jdotb_val = g22*bth*jwt + g33*bze*jwz + + $ g23*(bth*jwz + bze*jwt) + sigma_local = jdotb_val / bsq_val + + xin2 = REAL(CONJG(xno_fun(itheta))*xno_fun(itheta), r8) + +c |Q|^2 / mu0 density (matches gpeq_firstform q2_density) + A_density = REAL(CONJG(bwp_fun(itheta)) * bvp_fun(itheta) + + $ CONJG(bmt_fun(itheta)) * bvt_fun(itheta) + + $ CONJG(bmz_fun(itheta)) * bvz_fun(itheta), r8) + $ / (mu0 * jac) + +c |C|^2 / mu0 density (matches gpeq_epf integrand) + epf_density = REAL( + $ CONJG(cwp_fun(itheta)) * cvp_fun(itheta) + + $ CONJG(cwt_fun(itheta)) * cvt_fun(itheta) + + $ CONJG(cwz_fun(itheta)) * cvz_fun(itheta), r8) + $ / (mu0 * jac) + +c B density = 2 Re(V*.Q) / mu0, with V = xi_n (mu0 j x n_hat) +c computed DIRECTLY from the equilibrium current, NOT from C - Q. +c These are the same C^i - Q^i terms derived in main.tex (Sec. 5, +c the C component box): J V^psi = 0, and with xwp_fun = J xi^psi, +c J V^theta = xwp/(|grad psi|^2 J) (mu0 j^theta g23 + mu0 j^zeta g33) +c J V^zeta = -xwp/(|grad psi|^2 J) (mu0 j^theta g22 + mu0 j^zeta g23). +c mu0 j^i = mu0 * (physical j^i); jwt,jwz already hold physical j^i. + mu0jwt = mu0 * jwt + mu0jwz = mu0 * jwz + jvt_loc = xwp_fun(itheta) / (delpsi_val**2 * jac) * + $ (mu0jwt * g23 + mu0jwz * g33) + jvz_loc = -xwp_fun(itheta) / (delpsi_val**2 * jac) * + $ (mu0jwt * g22 + mu0jwz * g23) +c V is contravariant (J V^i); pair with covariant Q_i (bvt,bvz). + B_density = 2.0_r8 * REAL( + $ CONJG(jvt_loc) * bvt_fun(itheta) + + $ CONJG(jvz_loc) * bvz_fun(itheta), r8) / (mu0 * jac) + +c K_2 xi_n^2 density (parallel current; should match dst2) + Cpar_density = mu0 * sigma_local**2 * bsq_val * xin2 + +c I_perp density (perpendicular Pfirsch-Schlueter current^2) + Iperp_density = mu0 * p1_local**2 * delpsi_val**2 * + $ xin2 / bsq_val + +c Extra per-theta fields for R-Z heatmaps (only if requested). + IF (PRESENT(surf_extra)) THEN + bmod_val = SQRT(bsq_val) +c V*.Q (complex); B_density = 2 Re(vdotq_loc)/mu0. + vdotq_loc = (CONJG(jvt_loc) * bvt_fun(itheta) + + $ CONJG(jvz_loc) * bvz_fun(itheta)) / jac +c Split V = V_par + V_perp via j = j_par + j_perp. +c Parallel current: mu0 j_par^i = mu0 sigma B^i (B^t=bth,B^z=bze). + mu0jpar_t = mu0 * sigma_local * bth + mu0jpar_z = mu0 * sigma_local * bze + mu0jper_t = mu0jwt - mu0jpar_t + mu0jper_z = mu0jwz - mu0jpar_z + jvt_cur = xwp_fun(itheta) / (delpsi_val**2 * jac) * + $ (mu0jpar_t * g23 + mu0jpar_z * g33) + jvz_cur = -xwp_fun(itheta) / (delpsi_val**2 * jac) * + $ (mu0jpar_t * g22 + mu0jpar_z * g23) + jvt_pre = jvt_loc - jvt_cur + jvz_pre = jvz_loc - jvz_cur +c j_par = sigma B; |j_perp|^2 = |j|^2 - j_par^2 (j physical). + jpar_val = sigma_local * bmod_val + jsq_val = g22 * jwt**2 + g33 * jwz**2 + + $ 2.0_r8 * g23 * jwt * jwz + surf_extra(itheta,1) = REAL(vdotq_loc, r8) + surf_extra(itheta,2) = AIMAG(vdotq_loc) + surf_extra(itheta,3) = 2.0_r8 * REAL( + $ CONJG(jvt_cur) * bvt_fun(itheta) + + $ CONJG(jvz_cur) * bvz_fun(itheta), r8) / (mu0 * jac) + surf_extra(itheta,4) = 2.0_r8 * REAL( + $ CONJG(jvt_pre) * bvt_fun(itheta) + + $ CONJG(jvz_pre) * bvz_fun(itheta), r8) / (mu0 * jac) + surf_extra(itheta,5) = REAL(bvp_fun(itheta), r8) + surf_extra(itheta,6) = AIMAG(bvp_fun(itheta)) + surf_extra(itheta,7) = REAL(bvt_fun(itheta), r8) + surf_extra(itheta,8) = AIMAG(bvt_fun(itheta)) + surf_extra(itheta,9) = REAL(bvz_fun(itheta), r8) + surf_extra(itheta,10) = AIMAG(bvz_fun(itheta)) + surf_extra(itheta,11) = REAL(jvt_loc, r8) + surf_extra(itheta,12) = AIMAG(jvt_loc) + surf_extra(itheta,13) = REAL(jvz_loc, r8) + surf_extra(itheta,14) = AIMAG(jvz_loc) + surf_extra(itheta,15) = jpar_val + surf_extra(itheta,16) = SQRT(MAX(0.0_r8, + $ jsq_val - jpar_val**2)) + surf_extra(itheta,17) = REAL(xno_fun(itheta), r8) + surf_extra(itheta,18) = AIMAG(xno_fun(itheta)) + surf_extra(itheta,19) = delpsi_val + surf_extra(itheta,20) = bmod_val +c dst1 = K1 xin2 (shear), dst3 = K3 xin2 (curvature). + surf_extra(itheta,21) = kt1_loc(itheta) * xin2 + surf_extra(itheta,22) = kt3_loc(itheta) * xin2 + ENDIF + + IF (PRESENT(A_fun)) A_fun(itheta) = A_density + IF (PRESENT(B_fun)) B_fun(itheta) = B_density + IF (PRESENT(Iperp_fun)) Iperp_fun(itheta) = Iperp_density + IF (PRESENT(Cpar_fun)) Cpar_fun(itheta) = Cpar_density + + IF (itheta < mthsurf) THEN + A_int = A_int + A_density * jac / REAL(mthsurf, r8) + B_int = B_int + B_density * jac / REAL(mthsurf, r8) + epf_int = epf_int + epf_density * jac / REAL(mthsurf, r8) + Cpar_int = Cpar_int + Cpar_density * jac / + $ REAL(mthsurf, r8) + Iperp_int = Iperp_int + Iperp_density * jac / + $ REAL(mthsurf, r8) + ENDIF + ENDDO + + IF(debug_flag) PRINT *, "->Leaving gpeq_recon3" +c----------------------------------------------------------------------- +c terminate. +c----------------------------------------------------------------------- + RETURN + END SUBROUTINE gpeq_recon3 +c----------------------------------------------------------------------- +c subprogram 14. gpeq_fcoords. c transform coordinates to dcon coordinates. c----------------------------------------------------------------------- SUBROUTINE gpeq_fcoords(psi,ftnmn,amf,amp,ri,bpi,bi,rci,ti,ji) @@ -779,7 +1892,7 @@ SUBROUTINE gpeq_fcoords(psi,ftnmn,amf,amp,ri,bpi,bi,rci,ti,ji) RETURN END SUBROUTINE gpeq_fcoords c----------------------------------------------------------------------- -c subprogram 10. gpeq_fcoordsout. +c subprogram 14. gpeq_fcoordsout. c transform to dcon coordinates. Assumes mpert,lmpert,jac_out c----------------------------------------------------------------------- SUBROUTINE gpeq_fcoordsout(fmo,fmi,psi,ti,ji) @@ -842,7 +1955,7 @@ SUBROUTINE gpeq_fcoordsout(fmo,fmi,psi,ti,ji) RETURN END SUBROUTINE gpeq_fcoordsout c----------------------------------------------------------------------- -c subprogram 11. gpeq_bcoords. +c subprogram 15. gpeq_bcoords. c transform dcon coordinates to other coordinates. c----------------------------------------------------------------------- SUBROUTINE gpeq_bcoords(psi,ftnmn,amf,amp,ri,bpi,bi,rci,ti,ji) @@ -981,7 +2094,7 @@ SUBROUTINE gpeq_bcoords(psi,ftnmn,amf,amp,ri,bpi,bi,rci,ti,ji) RETURN END SUBROUTINE gpeq_bcoords c----------------------------------------------------------------------- -c subprogram 12. gpeq_bcoordsout. +c subprogram 16. gpeq_bcoordsout. c transform dcon to other coordinates. Assumes mpert,lmpert,jac_out c----------------------------------------------------------------------- SUBROUTINE gpeq_bcoordsout(fmo,fmi,psi,ti,ji) @@ -1044,7 +2157,7 @@ SUBROUTINE gpeq_bcoordsout(fmo,fmi,psi,ti,ji) RETURN END SUBROUTINE gpeq_bcoordsout c----------------------------------------------------------------------- -c subprogram 13. gpeq_weight. +c subprogram 17. gpeq_weight. c switch between a function and a weighted function. c----------------------------------------------------------------------- SUBROUTINE gpeq_weight(psi,ftnmn,amf,amp,wegt) @@ -1115,7 +2228,7 @@ SUBROUTINE gpeq_weight(psi,ftnmn,amf,amp,wegt) RETURN END SUBROUTINE gpeq_weight c----------------------------------------------------------------------- -c subprogram 14. gpeq_rzpgrid. +c subprogram 18. gpeq_rzpgrid. c find magnetic coordinates for given rz coords. c----------------------------------------------------------------------- SUBROUTINE gpeq_rzpgrid(nr,nz,psixy) @@ -1218,7 +2331,7 @@ SUBROUTINE gpeq_rzpgrid(nr,nz,psixy) RETURN END SUBROUTINE gpeq_rzpgrid c----------------------------------------------------------------------- -c subprogram 15. gpeq_rzpdiv. +c subprogram 19. gpeq_rzpdiv. c make zero divergence of rzphi functions. c----------------------------------------------------------------------- SUBROUTINE gpeq_rzpdiv(nr,nz,rval,zval,fr,fz,fp) @@ -1284,7 +2397,7 @@ SUBROUTINE gpeq_rzpdiv(nr,nz,rval,zval,fr,fz,fp) RETURN END SUBROUTINE gpeq_rzpdiv c----------------------------------------------------------------------- -c subprogram 16. gpeq_alloc. +c subprogram 20. gpeq_alloc. c allocate essential vectors in fourier space c----------------------------------------------------------------------- SUBROUTINE gpeq_alloc @@ -1299,7 +2412,10 @@ SUBROUTINE gpeq_alloc $ xno_mn(mpert),xta_mn(mpert),xpa_mn(mpert), $ bno_mn(mpert),bta_mn(mpert),bpa_mn(mpert), $ xrr_mn(mpert),xrz_mn(mpert),xrp_mn(mpert), - $ brr_mn(mpert),brz_mn(mpert),brp_mn(mpert)) + $ brr_mn(mpert),brz_mn(mpert),brp_mn(mpert), + $ c2vp_mn(mpert),c2vt_mn(mpert),c2vz_mn(mpert), + $ cvp_mn(mpert),cvt_mn(mpert),cvz_mn(mpert), + $ cwp_mn(mpert),cwt_mn(mpert),cwz_mn(mpert)) IF(debug_flag) PRINT *, "->Leaving gpeq_alloc" c----------------------------------------------------------------------- c terminate. @@ -1307,7 +2423,7 @@ SUBROUTINE gpeq_alloc RETURN END SUBROUTINE gpeq_alloc c----------------------------------------------------------------------- -c subprogram 17. gpeq_dealloc. +c subprogram 21. gpeq_dealloc. c deallocate essential vectors in fourier space c----------------------------------------------------------------------- SUBROUTINE gpeq_dealloc @@ -1317,7 +2433,9 @@ SUBROUTINE gpeq_dealloc $ xwp_mn,xwt_mn,xwz_mn,bwp_mn,bwt_mn,bwz_mn,xmt_mn,bmt_mn, $ xvp_mn,xvt_mn,xvz_mn,bvp_mn,bvt_mn,bvz_mn,xmz_mn,bmz_mn, $ xno_mn,xta_mn,xpa_mn,bno_mn,bta_mn,bpa_mn, - $ xrr_mn,xrz_mn,xrp_mn,brr_mn,brz_mn,brp_mn) + $ xrr_mn,xrz_mn,xrp_mn,brr_mn,brz_mn,brp_mn, + $ c2vp_mn,c2vt_mn,c2vz_mn,cvp_mn,cvt_mn,cvz_mn, + $ cwp_mn,cwt_mn,cwz_mn) IF(debug_flag) PRINT *, "->Leaving gpeq_dealloc" c----------------------------------------------------------------------- c terminate. @@ -1325,7 +2443,7 @@ SUBROUTINE gpeq_dealloc RETURN END SUBROUTINE gpeq_dealloc c----------------------------------------------------------------------- -c subprogram 18. gpeq_interp_singsurf. +c subprogram 22. gpeq_interp_singsurf. c create spline for interpretation of solution near singular surface. c----------------------------------------------------------------------- SUBROUTINE gpeq_interp_singsurf(fsp_sol,spots,npsi) @@ -1409,7 +2527,7 @@ SUBROUTINE gpeq_interp_singsurf(fsp_sol,spots,npsi) RETURN END SUBROUTINE gpeq_interp_singsurf c----------------------------------------------------------------------- -c subprogram 19. gpeq_interp_sol. +c subprogram 23. gpeq_interp_sol. c get bwn at psi after calling gpeq_interp_singsurf. c----------------------------------------------------------------------- SUBROUTINE gpeq_interp_sol(fsp_sol,psi,interpbwn) diff --git a/gpec/gpglobal.F b/gpec/gpglobal.F index ce75712d..0c3c6cdf 100644 --- a/gpec/gpglobal.F +++ b/gpec/gpglobal.F @@ -18,7 +18,8 @@ MODULE gpglobal_mod $ fun_flag,flux_flag,vsbrzphi_flag,displacement_flag, $ chebyshev_flag,coil_flag,eigm_flag,bwp_pest_flag,verbose, $ debug_flag,timeit,kin_flag,con_flag,resp_induct_flag, - $ netcdf_flag,ascii_flag,wv_farwall_flag,mutual_test_flag + $ netcdf_flag,ascii_flag,wv_farwall_flag,mutual_test_flag, + $ cveri_flag,recon_out,recon_flag2,recon_flag3 INTEGER :: mr,mz,mpsi,mstep,mpert,mband,mtheta,mthvac,mthsurf, $ mfix,mhigh,mlow,msing,nfm2,nths,nths2,lmpert,lmlow,lmhigh, @@ -38,8 +39,9 @@ MODULE gpglobal_mod CHARACTER(2) :: sn,ss CHARACTER(10) :: date,time,zone - CHARACTER(16) :: jac_type,jac_in,jac_out,data_type - CHARACTER(128) :: ieqfile,idconfile,ivacuumfile,rdconfile,dcon_dir + CHARACTER(16) :: jac_type,jac_in,jac_out,data_type,recon_int + CHARACTER(128) :: ieqfile,idconfile,ivacuumfile,rdconfile + CHARACTER(128) :: dcon_dir INTEGER, PARAMETER :: hmnum=128 REAL(r8), PARAMETER :: gauss=0.0001 @@ -56,8 +58,8 @@ MODULE gpglobal_mod REAL(r8), DIMENSION(:), ALLOCATABLE :: psifac,rhofac,qfac,singfac, $ r,z,theta,ee,surfee,surfei,rpsifac, - $ surf_indev,vsurf_indev,fsurf_indev,surf_indinvev - $ ,eft,efp + $ surf_indev,vsurf_indev,fsurf_indev,surf_indinvev, + $ eft,efp REAL(r8), DIMENSION(:,:), ALLOCATABLE :: xzpts, $ chperr,chpsqr,grri,grre,griw,grrw,gdr,gdz,gdpsi,gdthe,gdphi REAL(r8), DIMENSION(3,3) :: w,v @@ -72,6 +74,8 @@ MODULE gpglobal_mod $ chi_mn,che_mn,kax_mn,sbno_mn,sbno_fun,edge_mn,edge_fun, $ chy_mn,chx_mn,chw_mn,kaw_mn,mutual_indev,mutual_indinvev, $ bwp_mn_rmatch + COMPLEX(r8), DIMENSION(:), ALLOCATABLE :: cwp_mn,cwt_mn, + $ cwz_mn, cvp_mn,cvt_mn,cvz_mn,c2vp_mn,c2vt_mn, c2vz_mn COMPLEX(r8), DIMENSION(:,:), ALLOCATABLE :: wt,wt0,chp_mn,kap_mn, $ permeabev,chimats,chemats,flxmats,kaxmats,singbno_mn, $ plas_indev,plas_indinvev,reluctev,indrelev,permeabsv, @@ -89,6 +93,9 @@ MODULE gpglobal_mod TYPE(spline_type) :: sq TYPE(bicube_type) :: psi_in,eqfun,rzphi + TYPE(bicube_type), ALLOCATABLE, TARGET :: gpout_shear + TYPE(bicube_type), ALLOCATABLE, TARGET :: gpout_curvature + TYPE(bicube_type), ALLOCATABLE, TARGET :: gpout_k TYPE(cspline_type) :: u1,u2,u3,u4,u5,bpsi TYPE(cspline_type) :: smats,tmats,xmats,ymats,zmats TYPE(fspline_type) :: metric @@ -153,7 +160,7 @@ SUBROUTINE gpec_dealloc $ plas_indevmats,permeabevmats,chxmats,chwmats, $ mutual_indmats,mutual_indevmats,mutual_indinvmats, $ mutual_indinvevmats) - + CALL spline_dealloc(sq) CALL bicube_dealloc(eqfun) CALL bicube_dealloc(rzphi) diff --git a/gpec/gpout.f b/gpec/gpout.f index 0e91c699..710f2674 100644 --- a/gpec/gpout.f +++ b/gpec/gpout.f @@ -26,6 +26,9 @@ c 18. gpout_qrv c 19. gpout_init_netcdf c 20. gpout_close_netcdf +c 21. gpout_recon +c 22. gpout_recon2 +c 23. gpout_recon3 c----------------------------------------------------------------------- c subprogram 0. gpout_mod. c module declarations. @@ -35,6 +38,7 @@ c----------------------------------------------------------------------- MODULE gpout_mod USE netcdf + USE gpglobal_mod, ONLY : recon_out USE gpresp_mod USE gpvacuum_mod USE gpdiag_mod @@ -6892,4 +6896,1172 @@ SUBROUTINE gpout_close_netcdf RETURN END SUBROUTINE gpout_close_netcdf +c----------------------------------------------------------------------- +c subprogram 20. gpout_recon +c main entry point for reconstruction diagnostics. +c calls gpeq functions to compute shear, curvature, K quantities +c and stores results as bicubes for visualization/analysis. +c----------------------------------------------------------------------- + SUBROUTINE gpout_recon(mode, xspmn) +c----------------------------------------------------------------------- +c declaration. +c----------------------------------------------------------------------- + INTEGER, INTENT(IN) :: mode + COMPLEX(r8), DIMENSION(:), INTENT(IN) :: xspmn + + INTEGER :: ipsi, itheta, ipert, ushear_fun, ucurv, uk, ucveri, + $ uk_sigma, ucw_fun, ucw_mn, ucv_fun, ucv_mn, ucv2_fun, + $ ucv2_mn, uqv_mn, u_int, u_log, u_terms + REAL(r8), DIMENSION(0:mthsurf) :: epf_den_fun, epf_p_fun, + $ epf_t_fun, epf_z_fun, dst1_den_fun, dst2_den_fun, + $ dst3_den_fun + REAL(r8) :: psi, eta, rfac + REAL(r8), DIMENSION(0:mthsurf) :: rvals, zvals + REAL(r8) :: epf_int, epf_p_int, epf_t_int, epf_z_int + COMPLEX(r8) :: dst_int + COMPLEX(r8), DIMENSION(mpert) :: curv_mn, + $ term_mn + COMPLEX(r8), DIMENSION(0:mthsurf) :: shear_fun, curv_fun, K_fun, + $ cveri_fun + COMPLEX(r8), DIMENSION(0:mthsurf,3) :: cw_fun, cv_fun, cv2_fun + COMPLEX(r8), DIMENSION(mpert) :: qvp_mn, qvt_mn, qvz_mn + REAL(r8), DIMENSION(0:mthsurf) :: K_term1, K_term2, K_term3 + REAL(r8), DIMENSION(0:mthsurf) :: sigma_vals, jdotb_vals + CHARACTER(8) :: smode + CHARACTER(128) :: shear_fun_file, curv_file, cveri_file + CHARACTER(128) :: k_file, k_sigma_file + CHARACTER(128) :: cw_fun_file, cw_mn_file, cv_fun_file, cv_mn_file + CHARACTER(128) :: cv2_fun_file, cv2_mn_file, int_file, terms_file + CHARACTER(128) :: qv_mn_file + REAL(r8) :: dpsi_local + INTEGER :: istep, nint_pts, iint, ep_index + REAL(r8) :: psi_prev, psi_curr, epf_prev, epf_curr, ep_selected + REAL(r8) :: epf_p_prev, epf_t_prev, epf_z_prev + REAL(r8) :: epf_p_curr, epf_t_curr, epf_z_curr + REAL(r8) :: epf_int_total_hr, epf_p_total_hr, + $ epf_t_total_hr, epf_z_total_hr, dcon_fac + REAL(r8) :: cveri_abs, cveri_max, cveri_rms, cveri_sumsq, + $ cveri_tol + COMPLEX(r8) :: dst_prev, dst_curr, dst1_prev, dst2_prev, + $ dst3_prev, dst1_curr, dst2_curr, dst3_curr, + $ dst_int_total_k_hr, dst1_total_hr, dst2_total_hr, + $ dst3_total_hr, dw_raw_k_hr, dw_dcon_k_hr, dw_cum + REAL(r8), DIMENSION(:), ALLOCATABLE :: psi_int_pts, epf_vals, + $ epf_p_vals, epf_t_vals, epf_z_vals + COMPLEX(r8), DIMENSION(:), ALLOCATABLE :: dst_vals, dst1_vals, + $ dst2_vals, dst3_vals + TYPE(cspline_type) :: recon_epf_spl, recon_dst_spl + INTEGER :: cveri_count + CHARACTER(4) :: cveri_stat + +c Diagnostic variables for C vector comparison + INTEGER :: ipsi_mid, itheta_mid, ipert_diag, m1 + REAL(r8) :: psi_mid, theta_mid, delpsi_sq, jac_mid, q_mid, + $ q1_mid, chi1_mid, f1_mid, p1_mid, j_theta_coef, j_zeta_coef + COMPLEX(r8) :: term_exp, j_cross_factor, xi_psi_val + COMPLEX(r8), DIMENSION(-mband:mband) :: g11_diag, g12_diag, + $ g22_diag, g23_diag, g31_diag, g33_diag + REAL(r8), DIMENSION(10,10) :: w + REAL(r8) :: g11_r, g22_r, g33_r, g23_r, g31_r, g12_r + + IF(verbose) WRITE(*,*)"" + IF(verbose) WRITE(*,*)"GPOUT_RECON: Starting reconstruction"// + $ " diagnostics" + IF(verbose) WRITE(*,*)"__________________________________________" + +c----------------------------------------------------------------------- +c allocate bicubes for shear, curvature, K only when the recon +c output tables are requested. +c----------------------------------------------------------------------- + IF (recon_out) THEN + ALLOCATE(gpout_shear) + ALLOCATE(gpout_curvature) + ALLOCATE(gpout_k) + ENDIF + + WRITE(smode,'(I8)') mode + smode = ADJUSTL(smode) + shear_fun_file = "gpec_recon_shear_fun_sol"//TRIM(smode)//".out" + curv_file = "gpec_recon_curvature_sol"//TRIM(smode)//".out" + cveri_file = "gpec_recon_cveri_sol"//TRIM(smode)//".out" + k_file = "gpec_recon_k_sol"//TRIM(smode)//".out" + k_sigma_file = "gpec_recon_k_sigma_check_sol"//TRIM(smode)//".out" + cw_fun_file = "gpec_recon_cwfun_sol"//TRIM(smode)//".out" + cw_mn_file = "gpec_recon_cwmn_sol"//TRIM(smode)//".out" + cv_fun_file = "gpec_recon_cvfun_sol"//TRIM(smode)//".out" + cv_mn_file = "gpec_recon_cvmn_sol"//TRIM(smode)//".out" + cv2_fun_file = "gpec_recon_cv2fun_sol"//TRIM(smode)//".out" + cv2_mn_file = "gpec_recon_cv2mn_sol"//TRIM(smode)//".out" + qv_mn_file = "gpec_recon_qvmn_sol"//TRIM(smode)//".out" + int_file = "gpec_recon_integration_sol"//TRIM(smode)//".out" + terms_file = "gpec_recon_terms_sol"//TRIM(smode)//".out" + + ushear_fun = 82 + ucurv = 72 + ucveri = 74 + uk = 73 + uk_sigma = 85 + ucw_fun = 86 + ucw_mn = 87 + ucv_fun = 88 + ucv_mn = 89 + ucv2_fun = 90 + ucv2_mn = 91 + u_int = 92 + u_log = 93 + u_terms = 94 + uqv_mn = 95 + + IF (recon_out) THEN + OPEN(UNIT=ushear_fun, FILE=shear_fun_file, STATUS="UNKNOWN") + OPEN(UNIT=ucurv, FILE=curv_file, STATUS="UNKNOWN") + IF (cveri_flag) THEN + OPEN(UNIT=ucveri, FILE=cveri_file, STATUS="UNKNOWN") + ENDIF + OPEN(UNIT=uk, FILE=k_file, STATUS="UNKNOWN") + OPEN(UNIT=uk_sigma, FILE=k_sigma_file, STATUS="UNKNOWN") + CALL ascii_open(ucw_fun, cw_fun_file, "UNKNOWN") + CALL ascii_open(ucw_mn, cw_mn_file, "UNKNOWN") + CALL ascii_open(ucv_fun, cv_fun_file, "UNKNOWN") + CALL ascii_open(ucv_mn, cv_mn_file, "UNKNOWN") + CALL ascii_open(ucv2_fun, cv2_fun_file, "UNKNOWN") + CALL ascii_open(ucv2_mn, cv2_mn_file, "UNKNOWN") + CALL ascii_open(uqv_mn, qv_mn_file, "UNKNOWN") + OPEN(UNIT=u_int, FILE=int_file, STATUS="UNKNOWN") + OPEN(UNIT=u_terms, FILE=terms_file, STATUS="UNKNOWN") + WRITE(ushear_fun,'(6(1x,a16))') + $ "psi","theta","r","z","shear_re","shear_im" + WRITE(ucurv,'(6(1x,a16))') + $ "psi","theta","r","z","curv_re","curv_im" + IF (cveri_flag) WRITE(ucveri,'(6(1x,a16))') + $ "psi","theta","r","z","cveri_re","cveri_im" + WRITE(uk,'(9(1x,a16))') + $ "psi","theta","r","z","K_re","K_im", + $ "T1_re","T2_re","T3_re" + WRITE(uk_sigma,'(8(1x,a16))') + $ "psi","theta","r","z","sigma_re","jdotb_re", + $ "mu0_bsig2_re","mu0_sigjdotb" + WRITE(ucw_fun,'(10(1x,a16))') + $ "psi","theta","r","z","cwp_re","cwp_im", + $ "cwt_re","cwt_im","cwz_re","cwz_im" + WRITE(ucw_mn,'(8(1x,a16))') + $ "psi","m","cwp_re","cwp_im","cwt_re","cwt_im", + $ "cwz_re","cwz_im" + WRITE(ucv_fun,'(10(1x,a16))') + $ "psi","theta","r","z","cvp_re","cvp_im", + $ "cvt_re","cvt_im","cvz_re","cvz_im" + WRITE(ucv_mn,'(8(1x,a16))') + $ "psi","m","cvp_re","cvp_im","cvt_re","cvt_im", + $ "cvz_re","cvz_im" + WRITE(ucv2_fun,'(10(1x,a16))') + $ "psi","theta","r","z","cv2p_re","cv2p_im", + $ "cv2t_re","cv2t_im","cv2z_re","cv2z_im" + WRITE(ucv2_mn,'(8(1x,a16))') + $ "psi","m","cv2p_re","cv2p_im","cv2t_re","cv2t_im", + $ "cv2z_re","cv2z_im" + WRITE(uqv_mn,'(8(1x,a16))') + $ "psi","m","qvp_re","qvp_im","qvt_re","qvt_im", + $ "qvz_re","qvz_im" + WRITE(u_int,'(15(1x,a16))') + $ "psi","c2_mu0","k_xin2_re","k_xin2_im", + $ "c2_mu0_sum","k_xin2_c_re","k_xin2_c_im", + $ "dw_c_re","dw_c_im","c2_psi","c2_theta","c2_zeta", + $ "k1_xin2_re","k2_xin2_re","k3_xin2_re" + WRITE(u_terms,'(11(1x,a16))') + $ "psi","theta","r","z","c2_den","c2_psi_den", + $ "c2_theta_den","c2_zeta_den","dst1_den","dst2_den", + $ "dst3_den" + ENDIF + ep_index = 1 + IF (mode_flag) ep_index = mode + IF (ep_index < 1 .OR. ep_index > mpert) THEN + CALL gpec_stop("gpout_recon selected mode is out of range") + ENDIF + ep_selected = REAL(ep(ep_index)) + cveri_max = 0.0_r8 + cveri_rms = 0.0_r8 + cveri_sumsq = 0.0_r8 + cveri_tol = 1.0e-10_r8 + cveri_count = 0 +c----------------------------------------------------------------------- +c main loop over all psi levels. +c compute gpeq reconstruction diagnostics at each psi. +c----------------------------------------------------------------------- + CALL idcon_build(mode, xspmn) + CALL gpeq_alloc + + DO ipsi = 0, mpsi + psi = rzphi%xs(ipsi) + IF (psi > psilim) EXIT + +c Compute the J|C|^2/mu0 state once so C fields below use +c the current psi. This keeps the psi-local reconstruction path +c identical to the original logic. + CALL gpeq_epf(psi, epf_int, epf_den_fun=epf_den_fun, + $ epf_p_fun=epf_p_fun, epf_t_fun=epf_t_fun, + $ epf_z_fun=epf_z_fun) + +c Capture covariant bare-Q components (Q_i) set by gpeq_cova +c inside gpeq_epf, before gpeq_K/gpeq_dst run. These are the +c genuine perturbed-field covariant components delta-B_i; the +c effective field is cvmn = C_i = Q_i + V_i. Writing qvmn lets +c (curl Q).grad(psi) be formed from covariant components, the +c same way cveri forms (curl C).grad(psi) from cvmn. + IF (recon_out) THEN + qvp_mn = bvp_mn + qvt_mn = bvt_mn + qvz_mn = bvz_mn + ENDIF + +c Reconstruct detailed K diagnostics for output. Keep this on +c the original path so recon terminal totals remain unchanged. + CALL gpeq_K(psi, K_fun, K_term1, K_term2, K_term3, + $ sigma_vals, jdotb_vals, shear_fun, curv_fun) +c Verify (curl C).grad(psi)=0 using the same psi-local C state +c already prepared by gpeq_epf. This avoids rebuilding the +c full equilibrium/C reconstruction a second time at each psi. + IF (cveri_flag) THEN + DO ipert = 1, mpert + curv_mn(ipert) = twopi * ifac * mfac(ipert) + $ * cvz_mn(ipert) + term_mn(ipert) = curv_mn(ipert) + + $ twopi * ifac * nn * cvt_mn(ipert) + ENDDO + CALL iscdftb(mfac, mpert, cveri_fun, mthsurf, term_mn) + DO itheta = 0, mthsurf + CALL bicube_eval(rzphi, psi, theta(itheta), 0) + jac = rzphi%f(4) + cveri_fun(itheta) = cveri_fun(itheta) / jac + cveri_abs = ABS(cveri_fun(itheta)) + cveri_max = MAX(cveri_max, cveri_abs) + cveri_sumsq = cveri_sumsq + cveri_abs**2 + cveri_count = cveri_count + 1 + ENDDO + ENDIF + + IF (recon_out) THEN +c Store spatial values with coordinates only when recon file +c output is requested. +c K_i xi_n^2 per-theta densities for the recon terms file. + CALL gpeq_dst(psi, dst_int, dst1_den_fun=dst1_den_fun, + $ dst2_den_fun=dst2_den_fun, dst3_den_fun=dst3_den_fun) + DO itheta = 0, mthsurf + CALL bicube_eval(rzphi, psi, theta(itheta), 1) + rfac = SQRT(rzphi%f(1)) + eta = twopi*(theta(itheta) + rzphi%f(2)) + rvals(itheta) = ro + rfac*COS(eta) + zvals(itheta) = zo + rfac*SIN(eta) + + WRITE(u_terms,'(11(es17.8e3))') psi, theta(itheta), + $ rvals(itheta), zvals(itheta), epf_den_fun(itheta), + $ epf_p_fun(itheta), epf_t_fun(itheta), + $ epf_z_fun(itheta), dst1_den_fun(itheta), + $ dst2_den_fun(itheta), dst3_den_fun(itheta) + WRITE(ushear_fun,'(6(es17.8e3))') psi, theta(itheta), + $ rvals(itheta), zvals(itheta), + $ REAL(shear_fun(itheta)), AIMAG(shear_fun(itheta)) + WRITE(ucurv,'(6(es17.8e3))') psi, theta(itheta), + $ rvals(itheta), zvals(itheta), + $ REAL(curv_fun(itheta)), + $ AIMAG(curv_fun(itheta)) + IF (cveri_flag) WRITE(ucveri,'(6(es17.8e3))') psi, + $ theta(itheta), rvals(itheta), zvals(itheta), + $ REAL(cveri_fun(itheta)), AIMAG(cveri_fun(itheta)) + WRITE(uk,'(9(es17.8e3))') psi, theta(itheta), + $ rvals(itheta), zvals(itheta), REAL(K_fun(itheta)), + $ AIMAG(K_fun(itheta)), K_term1(itheta), + $ K_term2(itheta), K_term3(itheta) + WRITE(uk_sigma,'(8(es17.8e3))') psi, theta(itheta), + $ rvals(itheta), zvals(itheta), sigma_vals(itheta), + $ jdotb_vals(itheta), K_term2(itheta), + $ mu0 * sigma_vals(itheta) * jdotb_vals(itheta) + ENDDO + WRITE(u_terms,*) + WRITE(ushear_fun,*) + WRITE(ucurv,*) + IF (cveri_flag) WRITE(ucveri,*) + WRITE(uk,*) + WRITE(uk_sigma,*) + +c Reconstruct C fields from the already computed C state only +c for recon output tables. + CALL iscdftb(mfac, mpert, cw_fun(:,1), mthsurf, cwp_mn) + CALL iscdftb(mfac, mpert, cw_fun(:,2), mthsurf, cwt_mn) + CALL iscdftb(mfac, mpert, cw_fun(:,3), mthsurf, cwz_mn) + CALL iscdftb(mfac, mpert, cv_fun(:,1), mthsurf, cvp_mn) + CALL iscdftb(mfac, mpert, cv_fun(:,2), mthsurf, cvt_mn) + CALL iscdftb(mfac, mpert, cv_fun(:,3), mthsurf, cvz_mn) + CALL iscdftb(mfac, mpert, cv2_fun(:,1), mthsurf, c2vp_mn) + CALL iscdftb(mfac, mpert, cv2_fun(:,2), mthsurf, c2vt_mn) + CALL iscdftb(mfac, mpert, cv2_fun(:,3), mthsurf, c2vz_mn) + DO itheta = 0, mthsurf + WRITE(ucw_fun,'(10(es17.8e3))') psi, theta(itheta), + $ rvals(itheta), zvals(itheta), + $ REAL(cw_fun(itheta,1)), AIMAG(cw_fun(itheta,1)), + $ REAL(cw_fun(itheta,2)), AIMAG(cw_fun(itheta,2)), + $ REAL(cw_fun(itheta,3)), AIMAG(cw_fun(itheta,3)) + WRITE(ucv_fun,'(10(es17.8e3))') psi, theta(itheta), + $ rvals(itheta), zvals(itheta), + $ REAL(cv_fun(itheta,1)), AIMAG(cv_fun(itheta,1)), + $ REAL(cv_fun(itheta,2)), AIMAG(cv_fun(itheta,2)), + $ REAL(cv_fun(itheta,3)), AIMAG(cv_fun(itheta,3)) + WRITE(ucv2_fun,'(10(es17.8e3))') psi, theta(itheta), + $ rvals(itheta), zvals(itheta), + $ REAL(cv2_fun(itheta,1)), AIMAG(cv2_fun(itheta,1)), + $ REAL(cv2_fun(itheta,2)), AIMAG(cv2_fun(itheta,2)), + $ REAL(cv2_fun(itheta,3)), AIMAG(cv2_fun(itheta,3)) + ENDDO + DO ipert = 1, mpert + WRITE(ucw_mn,'(1x,es17.8e3,1x,I8,6(es17.8e3))') psi, + $ mfac(ipert), cwp_mn(ipert), cwt_mn(ipert), + $ cwz_mn(ipert) + WRITE(ucv_mn,'(1x,es17.8e3,1x,I8,6(es17.8e3))') psi, + $ mfac(ipert), cvp_mn(ipert), cvt_mn(ipert), + $ cvz_mn(ipert) + WRITE(ucv2_mn,'(1x,es17.8e3,1x,I8,6(es17.8e3))') psi, + $ mfac(ipert), c2vp_mn(ipert), c2vt_mn(ipert), + $ c2vz_mn(ipert) + WRITE(uqv_mn,'(1x,es17.8e3,1x,I8,6(es17.8e3))') psi, + $ mfac(ipert), qvp_mn(ipert), qvt_mn(ipert), + $ qvz_mn(ipert) + ENDDO + WRITE(ucw_fun,*) + WRITE(ucw_mn,*) + WRITE(ucv_fun,*) + WRITE(ucv_mn,*) + WRITE(ucv2_fun,*) + WRITE(ucv2_mn,*) + WRITE(uqv_mn,*) + ENDIF + ENDDO + +c----------------------------------------------------------------------- +c Perform the energy check on the DCON solution grid. Do not use +c rzphi%xs for this comparison; it is a coarse geometry grid. +c----------------------------------------------------------------------- + dcon_fac = (mu0*2.0_r8) / psio**2 / (chi1*1.0e-3_r8)**2 + epf_int_total_hr = 0.0_r8 + epf_p_total_hr = 0.0_r8 + epf_t_total_hr = 0.0_r8 + epf_z_total_hr = 0.0_r8 + dst_int_total_k_hr = CMPLX(0.0_r8, 0.0_r8, r8) + dst1_total_hr = CMPLX(0.0_r8, 0.0_r8, r8) + dst2_total_hr = CMPLX(0.0_r8, 0.0_r8, r8) + dst3_total_hr = CMPLX(0.0_r8, 0.0_r8, r8) + psi_prev = MAX(psifac(0), rzphi%xs(0)) + CALL gpeq_epf(psi_prev, epf_prev, epf_p_prev, epf_t_prev, + $ epf_z_prev) + CALL gpeq_dst(psi_prev, dst_prev, dst1_prev, dst2_prev, + $ dst3_prev) + IF (recon_out) THEN + WRITE(u_int,'(a)') "c psifac-grid integration results" + WRITE(u_int,'(a)') "c C2/mu0 = int J |C|^2 / mu0" + WRITE(u_int,'(a)') "c Kxin2 = int J K |xi_n|^2" + WRITE(u_int,'(a)') "c dW_gpec(K) = 0.5 * (C2/mu0 - Kxin2)" + WRITE(u_int,'(a,a)') "c recon_int = ", TRIM(recon_int) + ENDIF + nint_pts = 1 + DO istep = 0, mstep-1 + psi_curr = psifac(istep+1) + IF (psi_curr < rzphi%xs(0)) CYCLE + IF (psi_curr > rzphi%xs(mpsi)) EXIT + nint_pts = nint_pts + 1 + ENDDO + ALLOCATE(psi_int_pts(nint_pts), epf_vals(nint_pts), + $ epf_p_vals(nint_pts), epf_t_vals(nint_pts), + $ epf_z_vals(nint_pts), dst_vals(nint_pts), + $ dst1_vals(nint_pts), dst2_vals(nint_pts), + $ dst3_vals(nint_pts)) + psi_int_pts(1) = psi_prev + epf_vals(1) = epf_prev + epf_p_vals(1) = epf_p_prev + epf_t_vals(1) = epf_t_prev + epf_z_vals(1) = epf_z_prev + dst_vals(1) = dst_prev + dst1_vals(1) = dst1_prev + dst2_vals(1) = dst2_prev + dst3_vals(1) = dst3_prev + iint = 1 + DO istep = 0, mstep-1 + psi_curr = psifac(istep+1) + IF (psi_curr < rzphi%xs(0)) CYCLE + IF (psi_curr > rzphi%xs(mpsi)) EXIT + CALL gpeq_epf(psi_curr, epf_curr, epf_p_curr, + $ epf_t_curr, epf_z_curr) + CALL gpeq_dst(psi_curr, dst_curr, dst1_curr, dst2_curr, + $ dst3_curr) + iint = iint + 1 + psi_int_pts(iint) = psi_curr + epf_vals(iint) = epf_curr + epf_p_vals(iint) = epf_p_curr + epf_t_vals(iint) = epf_t_curr + epf_z_vals(iint) = epf_z_curr + dst_vals(iint) = dst_curr + dst1_vals(iint) = dst1_curr + dst2_vals(iint) = dst2_curr + dst3_vals(iint) = dst3_curr + ENDDO + IF (TRIM(recon_int) == "spline" .AND. nint_pts > 1) THEN + CALL cspline_alloc(recon_epf_spl, nint_pts-1, 4) + CALL cspline_alloc(recon_dst_spl, nint_pts-1, 4) + recon_epf_spl%xs = psi_int_pts + recon_dst_spl%xs = psi_int_pts + recon_epf_spl%fs(:,1) = CMPLX(epf_vals, 0.0_r8, r8) + recon_epf_spl%fs(:,2) = CMPLX(epf_p_vals, 0.0_r8, r8) + recon_epf_spl%fs(:,3) = CMPLX(epf_t_vals, 0.0_r8, r8) + recon_epf_spl%fs(:,4) = CMPLX(epf_z_vals, 0.0_r8, r8) + recon_dst_spl%fs(:,1) = dst_vals + recon_dst_spl%fs(:,2) = dst1_vals + recon_dst_spl%fs(:,3) = dst2_vals + recon_dst_spl%fs(:,4) = dst3_vals + CALL cspline_fit(recon_epf_spl, "extrap") + CALL cspline_fit(recon_dst_spl, "extrap") + CALL cspline_int(recon_epf_spl) + CALL cspline_int(recon_dst_spl) + DO iint = 1, nint_pts + epf_int_total_hr = REAL(recon_epf_spl%fsi(iint-1,1)) + epf_p_total_hr = REAL(recon_epf_spl%fsi(iint-1,2)) + epf_t_total_hr = REAL(recon_epf_spl%fsi(iint-1,3)) + epf_z_total_hr = REAL(recon_epf_spl%fsi(iint-1,4)) + dst_int_total_k_hr = recon_dst_spl%fsi(iint-1,1) + dst1_total_hr = recon_dst_spl%fsi(iint-1,2) + dst2_total_hr = recon_dst_spl%fsi(iint-1,3) + dst3_total_hr = recon_dst_spl%fsi(iint-1,4) + dw_cum = 0.5_r8 * + $ (CMPLX(epf_int_total_hr, 0.0_r8, r8) - + $ dst_int_total_k_hr) + IF (recon_out) WRITE(u_int,'(15(es17.8e3))') + $ psi_int_pts(iint), epf_vals(iint), + $ REAL(dst_vals(iint)), AIMAG(dst_vals(iint)), + $ epf_int_total_hr, REAL(dst_int_total_k_hr), + $ AIMAG(dst_int_total_k_hr), REAL(dw_cum), AIMAG(dw_cum), + $ epf_p_vals(iint), epf_t_vals(iint), epf_z_vals(iint), + $ REAL(dst1_vals(iint)), REAL(dst2_vals(iint)), + $ REAL(dst3_vals(iint)) + ENDDO + CALL cspline_dealloc(recon_epf_spl) + CALL cspline_dealloc(recon_dst_spl) + ELSE + dw_cum = CMPLX(0.0_r8, 0.0_r8, r8) + IF (recon_out) WRITE(u_int,'(15(es17.8e3))') + $ psi_int_pts(1), epf_vals(1), REAL(dst_vals(1)), + $ AIMAG(dst_vals(1)), epf_int_total_hr, + $ REAL(dst_int_total_k_hr), AIMAG(dst_int_total_k_hr), + $ REAL(dw_cum), AIMAG(dw_cum), epf_p_vals(1), epf_t_vals(1), + $ epf_z_vals(1), REAL(dst1_vals(1)), REAL(dst2_vals(1)), + $ REAL(dst3_vals(1)) + DO iint = 2, nint_pts + dpsi_local = psi_int_pts(iint) - psi_int_pts(iint-1) + epf_int_total_hr = epf_int_total_hr + + $ (epf_vals(iint-1) + epf_vals(iint)) * dpsi_local / 2.0_r8 + epf_p_total_hr = epf_p_total_hr + + $ (epf_p_vals(iint-1) + epf_p_vals(iint)) * dpsi_local + $ / 2.0_r8 + epf_t_total_hr = epf_t_total_hr + + $ (epf_t_vals(iint-1) + epf_t_vals(iint)) * dpsi_local + $ / 2.0_r8 + epf_z_total_hr = epf_z_total_hr + + $ (epf_z_vals(iint-1) + epf_z_vals(iint)) * dpsi_local + $ / 2.0_r8 + dst_int_total_k_hr = dst_int_total_k_hr + + $ (dst_vals(iint-1) + dst_vals(iint)) * dpsi_local / 2.0_r8 + dst1_total_hr = dst1_total_hr + + $ (dst1_vals(iint-1) + dst1_vals(iint)) * + $ dpsi_local / 2.0_r8 + dst2_total_hr = dst2_total_hr + + $ (dst2_vals(iint-1) + dst2_vals(iint)) * + $ dpsi_local / 2.0_r8 + dst3_total_hr = dst3_total_hr + + $ (dst3_vals(iint-1) + dst3_vals(iint)) * + $ dpsi_local / 2.0_r8 + dw_cum = 0.5_r8 * + $ (CMPLX(epf_int_total_hr, 0.0_r8, r8) - + $ dst_int_total_k_hr) + IF (recon_out) WRITE(u_int,'(15(es17.8e3))') + $ psi_int_pts(iint), epf_vals(iint), + $ REAL(dst_vals(iint)), AIMAG(dst_vals(iint)), + $ epf_int_total_hr, REAL(dst_int_total_k_hr), + $ AIMAG(dst_int_total_k_hr), REAL(dw_cum), AIMAG(dw_cum), + $ epf_p_vals(iint), epf_t_vals(iint), epf_z_vals(iint), + $ REAL(dst1_vals(iint)), REAL(dst2_vals(iint)), + $ REAL(dst3_vals(iint)) + ENDDO + ENDIF + DEALLOCATE(psi_int_pts, epf_vals, epf_p_vals, epf_t_vals, + $ epf_z_vals, dst_vals, dst1_vals, dst2_vals, dst3_vals) + dw_raw_k_hr = 0.5_r8 * + $ (CMPLX(epf_int_total_hr, 0.0_r8, r8) - dst_int_total_k_hr) + dw_dcon_k_hr = dw_raw_k_hr * dcon_fac + + IF (recon_out) THEN + WRITE(u_int,*) + WRITE(u_int,'(a)') "c Final psifac-grid integration results:" + WRITE(u_int,'(3(es17.8e3))') epf_int_total_hr, + $ REAL(dst_int_total_k_hr), AIMAG(dst_int_total_k_hr) + WRITE(u_int,'(a)') "c Final C2 component totals:" + WRITE(u_int,'(3(es17.8e3))') epf_p_total_hr, + $ epf_t_total_hr, epf_z_total_hr + WRITE(u_int,'(a)') "c Final K component totals:" + WRITE(u_int,'(3(es17.8e3))') REAL(dst1_total_hr), + $ REAL(dst2_total_hr), REAL(dst3_total_hr) + WRITE(u_int,'(a)') "c dW_gpec(K)" + WRITE(u_int,'(2(es17.8e3))') REAL(dw_raw_k_hr), + $ AIMAG(dw_raw_k_hr) + WRITE(u_int,'(a)') "c dW_dcon(K), normalized from dW_gpec(K)" + WRITE(u_int,'(2(es17.8e3))') REAL(dw_dcon_k_hr), + $ AIMAG(dw_dcon_k_hr) + ENDIF + + WRITE(*,'(a)') "GPEC_RECON C2 component totals:" + WRITE(*,'(a,es17.8e3)') " C2_psi/mu0 = ", + $ epf_p_total_hr + WRITE(*,'(a,es17.8e3)') " C2_theta/mu0 = ", + $ epf_t_total_hr + WRITE(*,'(a,es17.8e3)') " C2_zeta/mu0 = ", + $ epf_z_total_hr + WRITE(*,'(a)') "GPEC_RECON K component totals:" + WRITE(*,'(a,es17.8e3)') " K1_xin2 = ", + $ REAL(dst1_total_hr) + WRITE(*,'(a,es17.8e3)') " K2_xin2 = ", + $ REAL(dst2_total_hr) + WRITE(*,'(a,es17.8e3)') " K3_xin2 = ", + $ REAL(dst3_total_hr) + WRITE(*,'(a)') "GPEC_RECON final psifac-grid dW terms:" + WRITE(*,'(a,es17.8e3)') " int_J_C2_over_mu0 = ", + $ epf_int_total_hr + WRITE(*,'(a,es17.8e3)') " int_J_K_xin2 = ", + $ REAL(dst_int_total_k_hr) + WRITE(*,'(a,es17.8e3)') " dW_p(recon) = ", + $ REAL(dw_raw_k_hr) + WRITE(*,'(a,es17.8e3)') " dW_p(recon-normalize)= ", + $ REAL(dw_dcon_k_hr) + WRITE(*,'(a,a,a,es17.8e3)') " GPEC ep(", TRIM(smode), ") = ", + $ ep_selected + WRITE(*,'(a,a,a,es17.8e3)') " DCON ep(", TRIM(smode), ") = ", + $ ep_selected*dcon_fac + IF (cveri_flag) THEN + cveri_stat = "PASS" + IF (cveri_count > 0) THEN + cveri_rms = SQRT(cveri_sumsq / REAL(cveri_count, r8)) + ELSE + cveri_stat = "SKIP" + ENDIF + IF (cveri_max > cveri_tol) cveri_stat = "WARN" + WRITE(*,'(a,a)') "GPEC_RECON C verify: ", TRIM(cveri_stat) + WRITE(*,'(a,es17.8e3)') " max = ", cveri_max + WRITE(*,'(a,es17.8e3)') " rms = ", cveri_rms + WRITE(*,'(a,es17.8e3)') " tol = ", cveri_tol + ENDIF + + IF (recon_out) THEN + OPEN(UNIT=u_log, FILE="gpec.log", STATUS="UNKNOWN", + $ POSITION="APPEND") + WRITE(u_log,'(a)') "GPEC_RECON final results:" + WRITE(u_log,'(a,I8)') " mode = ", mode + WRITE(u_log,'(a,L1)') " reg_flag = ", reg_flag + WRITE(u_log,'(a)') " C2 component totals:" + WRITE(u_log,'(a,es17.8e3)') " C2_psi/mu0 = ", + $ epf_p_total_hr + WRITE(u_log,'(a,es17.8e3)') " C2_theta/mu0 = ", + $ epf_t_total_hr + WRITE(u_log,'(a,es17.8e3)') " C2_zeta/mu0 = ", + $ epf_z_total_hr + WRITE(u_log,'(a)') " K component totals:" + WRITE(u_log,'(a,es17.8e3)') " K1_xin2 = ", + $ REAL(dst1_total_hr) + WRITE(u_log,'(a,es17.8e3)') " K2_xin2 = ", + $ REAL(dst2_total_hr) + WRITE(u_log,'(a,es17.8e3)') " K3_xin2 = ", + $ REAL(dst3_total_hr) + WRITE(u_log,'(a)') " psifac-grid dW terms:" + WRITE(u_log,'(a,es17.8e3)') " int_J_C2_over_mu0 = ", + $ epf_int_total_hr + WRITE(u_log,'(a,es17.8e3)') " int_J_K_xin2 = ", + $ REAL(dst_int_total_k_hr) + WRITE(u_log,'(a,es17.8e3)') " dW_p(recon) = ", + $ REAL(dw_raw_k_hr) + WRITE(u_log,'(a,es17.8e3)') " dW_p(recon-normalize)= ", + $ REAL(dw_dcon_k_hr) + WRITE(u_log,'(a,a,a,es17.8e3)') " GPEC ep(", TRIM(smode), + $ ") = ", ep_selected + WRITE(u_log,'(a,a,a,es17.8e3)') " DCON ep(", TRIM(smode), + $ ") = ", ep_selected*dcon_fac + IF (cveri_flag) THEN + WRITE(u_log,'(a,a)') " C verify: ", TRIM(cveri_stat) + WRITE(u_log,'(a,es17.8e3)') " max = ", cveri_max + WRITE(u_log,'(a,es17.8e3)') " rms = ", cveri_rms + WRITE(u_log,'(a,es17.8e3)') " tol = ", cveri_tol + ENDIF + WRITE(u_log,*) + CLOSE(u_log) + ENDIF + + CALL gpeq_dealloc + IF (recon_out) THEN + CLOSE(ushear_fun) + CLOSE(ucurv) + IF (cveri_flag) CLOSE(ucveri) + CLOSE(uk) + CLOSE(uk_sigma) + CLOSE(u_int) + CLOSE(u_terms) + CALL ascii_close(ucw_fun) + CALL ascii_close(ucw_mn) + CALL ascii_close(ucv_fun) + CALL ascii_close(ucv_mn) + CALL ascii_close(ucv2_fun) + CALL ascii_close(ucv2_mn) + CALL ascii_close(uqv_mn) + ENDIF +c----------------------------------------------------------------------- +c terminate. +c----------------------------------------------------------------------- + RETURN + END SUBROUTINE gpout_recon +c----------------------------------------------------------------------- +c subprogram 22. gpout_recon2 +c first-form plasma energy diagnostic based on +c +c |Q|^2 - j.(Q x xi*) + mu0 (div xi)* (xi.grad p) +c +c with the gamma p |div xi|^2 term intentionally omitted. +c The pressure-divergence contribution is evaluated directly from +c div xi and xi.grad p rather than closed as a Bernstein residual. +c----------------------------------------------------------------------- + SUBROUTINE gpout_recon2(mode, xspmn) +c----------------------------------------------------------------------- +c declaration. +c----------------------------------------------------------------------- + INTEGER, INTENT(IN) :: mode + COMPLEX(r8), DIMENSION(:), INTENT(IN) :: xspmn + + INTEGER :: ipsi, itheta, usurf, u_int, u_log + INTEGER :: istep, nint_pts, iint + REAL(r8) :: psi, eta, rfac, dpsi_local, q2_hr + REAL(r8), DIMENSION(0:mthsurf) :: rvals, zvals + COMPLEX(r8) :: jqx_hr, pdiv_hr, total_hr + COMPLEX(r8) :: jqx_cum, pdiv_cum, total_cum, dw_raw_hr, + $ dw_dcon_hr + COMPLEX(r8), DIMENSION(0:mthsurf) :: q2_fun, jqx_fun, pdiv_fun, + $ total_fun + CHARACTER(8) :: smode + CHARACTER(128) :: surf_file, int_file + REAL(r8) :: dcon_fac + INTEGER :: ep_index + REAL(r8) :: ep_selected, ep_dcon + REAL(r8), DIMENSION(:), ALLOCATABLE :: psi_int_pts, q2_vals_r + COMPLEX(r8), DIMENSION(:), ALLOCATABLE :: q2_vals, jqx_vals, + $ pdiv_vals, total_vals + TYPE(cspline_type) :: recon2_spl + + IF(verbose) WRITE(*,*)"" + IF(verbose) WRITE(*,*)"GPOUT_RECON2: Starting first-form"// + $ " diagnostics" + IF(verbose) WRITE(*,*)"__________________________________________" + + WRITE(smode,'(I8)') mode + smode = ADJUSTL(smode) + surf_file = "gpec_recon2_terms_sol"//TRIM(smode)//".out" + int_file = "gpec_recon2_integration_sol"//TRIM(smode)//".out" + usurf = 94 + u_int = 95 + u_log = 96 + + IF (recon_out) THEN + OPEN(UNIT=usurf, FILE=surf_file, STATUS="UNKNOWN") + OPEN(UNIT=u_int, FILE=int_file, STATUS="UNKNOWN") + WRITE(usurf,'(12(1x,a16))') + $ "psi","theta","r","z","q2_re","q2_im","jqx_re", + $ "jqx_im","pdiv_re","pdiv_im","total_re","total_im" + WRITE(u_int,'(10(1x,a16))') + $ "psi","q2_re","q2_im","jqx_re","jqx_im","pdiv_re", + $ "pdiv_im","total_re","total_im","dw_re" + ENDIF + + CALL idcon_build(mode, xspmn) + CALL gpeq_alloc + + DO ipsi = 0, mpsi + psi = rzphi%xs(ipsi) + IF (psi > psilim) EXIT + CALL gpeq_firstform(psi, q2_hr, jqx_hr, pdiv_hr, total_hr, + $ q2_fun, jqx_fun, pdiv_fun, total_fun) + + IF (recon_out) THEN + DO itheta = 0, mthsurf + CALL bicube_eval(rzphi, psi, theta(itheta), 1) + rfac = SQRT(rzphi%f(1)) + eta = twopi*(theta(itheta) + rzphi%f(2)) + rvals(itheta) = ro + rfac*COS(eta) + zvals(itheta) = zo + rfac*SIN(eta) + WRITE(usurf,'(12(es17.8e3))') psi, theta(itheta), + $ rvals(itheta), zvals(itheta), REAL(q2_fun(itheta)), + $ AIMAG(q2_fun(itheta)), REAL(jqx_fun(itheta)), + $ AIMAG(jqx_fun(itheta)), REAL(pdiv_fun(itheta)), + $ AIMAG(pdiv_fun(itheta)), REAL(total_fun(itheta)), + $ AIMAG(total_fun(itheta)) + ENDDO + WRITE(usurf,*) + ENDIF + ENDDO + + dcon_fac = (mu0*2.0_r8) / psio**2 / (chi1*1.0e-3_r8)**2 + ep_index = 1 + IF (mode_flag) ep_index = mode + IF (ep_index < 1 .OR. ep_index > mpert) THEN + CALL gpec_stop("gpout_recon2 selected mode is out of range") + ENDIF + ep_selected = REAL(ep(ep_index)) + ep_dcon = ep_selected * dcon_fac + q2_hr = 0.0_r8 + jqx_hr = CMPLX(0.0_r8, 0.0_r8, r8) + pdiv_hr = CMPLX(0.0_r8, 0.0_r8, r8) + total_hr = CMPLX(0.0_r8, 0.0_r8, r8) + + nint_pts = 0 + DO istep = 0, mstep + IF (psifac(istep) < rzphi%xs(0)) CYCLE + IF (psifac(istep) > rzphi%xs(mpsi)) EXIT + nint_pts = nint_pts + 1 + ENDDO + ALLOCATE(psi_int_pts(nint_pts), q2_vals_r(nint_pts), + $ q2_vals(nint_pts), jqx_vals(nint_pts), pdiv_vals(nint_pts), + $ total_vals(nint_pts)) + iint = 0 + DO istep = 0, mstep + psi = psifac(istep) + IF (psi < rzphi%xs(0)) CYCLE + IF (psi > rzphi%xs(mpsi)) EXIT + CALL gpeq_firstform(psi, q2_hr, jqx_hr, pdiv_hr, total_hr, + $ q2_fun, jqx_fun, pdiv_fun, total_fun) + iint = iint + 1 + psi_int_pts(iint) = psi + q2_vals_r(iint) = q2_hr + q2_vals(iint) = CMPLX(q2_hr, 0.0_r8, r8) + jqx_vals(iint) = jqx_hr + pdiv_vals(iint) = pdiv_hr + total_vals(iint) = total_hr + ENDDO + + IF (recon_out) THEN + WRITE(u_int,'(a)') "c first-form plasma-energy terms" + WRITE(u_int,'(a)') "c total = q2 - jqx + pdiv" + WRITE(u_int,'(a)') "c pdiv term is computed directly" + WRITE(u_int,'(a,a)') "c recon_int = ", TRIM(recon_int) + ENDIF + + IF (TRIM(recon_int) == "spline" .AND. nint_pts > 1) THEN + CALL cspline_alloc(recon2_spl, nint_pts-1, 4) + recon2_spl%xs = psi_int_pts + recon2_spl%fs(:,1) = q2_vals + recon2_spl%fs(:,2) = jqx_vals + recon2_spl%fs(:,3) = pdiv_vals + recon2_spl%fs(:,4) = total_vals + CALL cspline_fit(recon2_spl, "extrap") + CALL cspline_int(recon2_spl) + DO iint = 1, nint_pts + q2_hr = REAL(recon2_spl%fsi(iint-1,1), r8) + jqx_hr = recon2_spl%fsi(iint-1,2) + pdiv_hr = recon2_spl%fsi(iint-1,3) + total_hr = recon2_spl%fsi(iint-1,4) + dw_raw_hr = 0.5_r8 * total_hr + IF (recon_out) WRITE(u_int,'(10(es17.8e3))') + $ psi_int_pts(iint), q2_vals_r(iint), 0.0_r8, + $ REAL(jqx_vals(iint)), AIMAG(jqx_vals(iint)), + $ REAL(pdiv_vals(iint)), AIMAG(pdiv_vals(iint)), + $ REAL(total_vals(iint)), AIMAG(total_vals(iint)), + $ REAL(dw_raw_hr) + ENDDO + CALL cspline_dealloc(recon2_spl) + ELSE + q2_hr = 0.0_r8 + jqx_hr = CMPLX(0.0_r8, 0.0_r8, r8) + pdiv_hr = CMPLX(0.0_r8, 0.0_r8, r8) + total_hr = CMPLX(0.0_r8, 0.0_r8, r8) + DO iint = 1, nint_pts + IF (iint > 1) THEN + dpsi_local = psi_int_pts(iint) - psi_int_pts(iint-1) + q2_hr = q2_hr + (q2_vals_r(iint-1) + q2_vals_r(iint)) * + $ dpsi_local / 2.0_r8 + jqx_hr = jqx_hr + (jqx_vals(iint-1) + jqx_vals(iint)) * + $ dpsi_local / 2.0_r8 + pdiv_hr = pdiv_hr + (pdiv_vals(iint-1) + + $ pdiv_vals(iint)) * dpsi_local / 2.0_r8 + total_hr = total_hr + (total_vals(iint-1) + + $ total_vals(iint)) * dpsi_local / 2.0_r8 + ENDIF + dw_raw_hr = 0.5_r8 * total_hr + IF (recon_out) WRITE(u_int,'(10(es17.8e3))') + $ psi_int_pts(iint), q2_vals_r(iint), 0.0_r8, + $ REAL(jqx_vals(iint)), AIMAG(jqx_vals(iint)), + $ REAL(pdiv_vals(iint)), AIMAG(pdiv_vals(iint)), + $ REAL(total_vals(iint)), AIMAG(total_vals(iint)), + $ REAL(dw_raw_hr) + ENDDO + ENDIF + + dw_raw_hr = 0.5_r8 * total_hr + dw_dcon_hr = dw_raw_hr * dcon_fac + + IF (recon_out) THEN + WRITE(u_int,*) + WRITE(u_int,'(a)') "c Final first-form totals:" + WRITE(u_int,'(2(es17.8e3))') q2_hr, 0.0_r8 + WRITE(u_int,'(2(es17.8e3))') REAL(jqx_hr), AIMAG(jqx_hr) + WRITE(u_int,'(2(es17.8e3))') REAL(pdiv_hr), AIMAG(pdiv_hr) + WRITE(u_int,'(2(es17.8e3))') REAL(total_hr), AIMAG(total_hr) + WRITE(u_int,'(a)') "c dW_p(recon2)" + WRITE(u_int,'(2(es17.8e3))') REAL(dw_raw_hr), AIMAG(dw_raw_hr) + WRITE(u_int,'(a)') "c dW_p(recon2-normalize)" + WRITE(u_int,'(2(es17.8e3))') REAL(dw_dcon_hr), + $ AIMAG(dw_dcon_hr) + ENDIF + + WRITE(*,'(a)') "GPEC_RECON2 final first-form totals:" + WRITE(*,'(a,es17.8e3)') " int_J_Q2_over_mu0 = ", q2_hr + WRITE(*,'(a,2es17.8e3)') " int_J_jQxixi_over_mu0= ", + $ REAL(jqx_hr), AIMAG(jqx_hr) + WRITE(*,'(a,2es17.8e3)') " int_J_pdiv_term = ", + $ REAL(pdiv_hr), AIMAG(pdiv_hr) + WRITE(*,'(a,2es17.8e3)') " int_J_total = ", + $ REAL(total_hr), AIMAG(total_hr) + WRITE(*,'(a,es17.8e3)') " dW_p(recon2) = ", + $ REAL(dw_raw_hr) + WRITE(*,'(a,es17.8e3)') " dW_p(recon2-normalize)= ", + $ REAL(dw_dcon_hr) + WRITE(*,'(a,a,a,es17.8e3)') " GPEC ep(", TRIM(smode), ") = ", + $ ep_selected + WRITE(*,'(a,a,a,es17.8e3)') " DCON ep(", TRIM(smode), ") = ", + $ ep_dcon + + IF (recon_out) THEN + OPEN(UNIT=u_log, FILE="gpec.log", STATUS="UNKNOWN", + $ POSITION="APPEND") + WRITE(u_log,'(a)') "GPEC_RECON2 final results:" + WRITE(u_log,'(a,I8)') " mode = ", mode + WRITE(u_log,'(a,L1)') " reg_flag = ", reg_flag + WRITE(u_log,'(a,es17.8e3)') " int_J_Q2_over_mu0 = ", q2_hr + WRITE(u_log,'(a,2es17.8e3)') " int_J_jQxixi_over_mu0= ", + $ REAL(jqx_hr), AIMAG(jqx_hr) + WRITE(u_log,'(a,2es17.8e3)') " int_J_pdiv_term = ", + $ REAL(pdiv_hr), AIMAG(pdiv_hr) + WRITE(u_log,'(a,2es17.8e3)') " int_J_total = ", + $ REAL(total_hr), AIMAG(total_hr) + WRITE(u_log,'(a,es17.8e3)') " dW_p(recon2) = ", + $ REAL(dw_raw_hr) + WRITE(u_log,'(a,es17.8e3)') " dW_p(recon2-normalize)= ", + $ REAL(dw_dcon_hr) + WRITE(u_log,'(a,a,a,es17.8e3)') " GPEC ep(", TRIM(smode), + $ ") = ", ep_selected + WRITE(u_log,'(a,a,a,es17.8e3)') " DCON ep(", TRIM(smode), + $ ") = ", ep_dcon + WRITE(u_log,*) + CLOSE(u_log) + ENDIF + + DEALLOCATE(psi_int_pts, q2_vals_r, q2_vals, jqx_vals, pdiv_vals, + $ total_vals) + CALL gpeq_dealloc + IF (recon_out) THEN + CLOSE(usurf) + CLOSE(u_int) + ENDIF +c----------------------------------------------------------------------- +c terminate. +c----------------------------------------------------------------------- + RETURN + END SUBROUTINE gpout_recon2 +c----------------------------------------------------------------------- +c subprogram 23. gpout_recon3 +c Bernstein |C|^2 decomposition diagnostic. +c |C|^2 = |Q|^2 + 2 Re(V*.Q) + |V|^2 with V = xi_n mu0 j x n_hat. +c j is tangent to the flux surface (j.n=0) so +c |V|^2 = mu0^2 xi_n^2 |j|^2 = mu0^2 xi_n^2 (sigma^2 B^2 +c + p'^2 |grad psi|^2 / B^2). +c This routine writes ABS decomposition pieces: +c A = int J |Q|^2 / mu0 (perturbed B energy) +c Cpar = int J mu0 sigma^2 B^2 xi_n^2 (parallel current^2) +c Iperp = int J mu0 p'^2 |grad psi|^2 xi_n^2 / B^2 (perp current^2) +c epf = int J |C|^2 / mu0 (already in recon1) +c and derives B = epf - A - Cpar - Iperp. +c Diagnostic: Cpar should equal dst2 (K_2 xi_n^2) from recon1 +c pointwise and integrated, since both express mu0 sigma^2 B^2 xi_n^2. +c----------------------------------------------------------------------- + SUBROUTINE gpout_recon3(mode, xspmn) +c----------------------------------------------------------------------- +c declaration. +c----------------------------------------------------------------------- + INTEGER, INTENT(IN) :: mode + COMPLEX(r8), DIMENSION(:), INTENT(IN) :: xspmn + + INTEGER :: ipsi, itheta, usurf, u_int, u_log + INTEGER :: istep, nint_pts, iint + REAL(r8) :: psi, dpsi_local + REAL(r8) :: A_hr, B_hr, Iperp_hr, Cpar_hr, epf_hr + REAL(r8) :: A_total, B_total, Iperp_total, Cpar_total, epf_total + REAL(r8) :: dst1_total, dst3_total + REAL(r8) :: identity_residual + REAL(r8) :: dw_raw3, dw_dcon3, ep_selected, ep_dcon + INTEGER :: ep_index + COMPLEX(r8) :: dst_int_c, dst1_c, dst2_c, dst3_c + REAL(r8), DIMENSION(0:mthsurf) :: rvals, zvals + REAL(r8), DIMENSION(0:mthsurf) :: A_fun, B_fun, Iperp_fun + REAL(r8), DIMENSION(0:mthsurf) :: Cpar_fun + REAL(r8), DIMENSION(0:mthsurf,22) :: surf_extra + INTEGER :: kcol + CHARACTER(8) :: smode + CHARACTER(128) :: surf_file, int_file + REAL(r8) :: dcon_fac + REAL(r8), DIMENSION(:), ALLOCATABLE :: psi_int_pts + REAL(r8), DIMENSION(:), ALLOCATABLE :: A_vals, B_vals, + $ Iperp_vals, Cpar_vals, epf_vals, dst1_vals, dst3_vals + TYPE(cspline_type) :: recon3_spl + + IF(verbose) WRITE(*,*)"" + IF(verbose) WRITE(*,*)"GPOUT_RECON3: Bernstein |C|^2 decomp" + IF(verbose) WRITE(*,*)"__________________________________________" + + WRITE(smode,'(I8)') mode + smode = ADJUSTL(smode) + surf_file = "gpec_recon3_terms_sol"//TRIM(smode)//".out" + int_file = "gpec_recon3_integration_sol"//TRIM(smode)//".out" + usurf = 97 + u_int = 98 + u_log = 99 + + IF (recon_out) THEN + OPEN(UNIT=usurf, FILE=surf_file, STATUS="UNKNOWN") + OPEN(UNIT=u_int, FILE=int_file, STATUS="UNKNOWN") + WRITE(usurf,'(30(1x,a16))') + $ "psi","theta","r","z","A_den","B_den","Iperp_den", + $ "Cpar_den","VdotQ_re","VdotQ_im","Bcur_den","Bpre_den", + $ "Qp_re","Qp_im","Qt_re","Qt_im","Qz_re","Qz_im", + $ "Vt_re","Vt_im","Vz_re","Vz_im","jpar","jperp", + $ "xin_re","xin_im","delpsi","Bmod","dst1_den","dst3_den" + WRITE(u_int,'(12(1x,a16))') + $ "psi","A","B","Iperp","Cpar","epf","A_cum","B_cum", + $ "Iperp_cum","Cpar_cum","epf_cum","identity_res" + ENDIF + + CALL idcon_build(mode, xspmn) + CALL gpeq_alloc + + DO ipsi = 0, mpsi + psi = rzphi%xs(ipsi) + IF (psi > psilim) EXIT + CALL gpeq_recon3(psi, A_hr, B_hr, Iperp_hr, Cpar_hr, epf_hr, + $ A_fun, B_fun, Iperp_fun, Cpar_fun, surf_extra) + IF (recon_out) THEN + DO itheta = 0, mthsurf + CALL bicube_eval(rzphi, psi, theta(itheta), 1) + rfac = SQRT(rzphi%f(1)) + eta = twopi*(theta(itheta) + rzphi%f(2)) + rvals(itheta) = ro + rfac*COS(eta) + zvals(itheta) = zo + rfac*SIN(eta) + WRITE(usurf,'(30(es17.8e3))') psi, theta(itheta), + $ rvals(itheta), zvals(itheta), A_fun(itheta), + $ B_fun(itheta), Iperp_fun(itheta), Cpar_fun(itheta), + $ (surf_extra(itheta,kcol), kcol=1,22) + ENDDO + WRITE(usurf,*) + ENDIF + ENDDO + + dcon_fac = (mu0*2.0_r8) / psio**2 / (chi1*1.0e-3_r8)**2 + + nint_pts = 0 + DO istep = 0, mstep + IF (psifac(istep) < rzphi%xs(0)) CYCLE + IF (psifac(istep) > rzphi%xs(mpsi)) EXIT + nint_pts = nint_pts + 1 + ENDDO + ALLOCATE(psi_int_pts(nint_pts), A_vals(nint_pts), + $ B_vals(nint_pts), Iperp_vals(nint_pts), Cpar_vals(nint_pts), + $ epf_vals(nint_pts), dst1_vals(nint_pts), dst3_vals(nint_pts)) + iint = 0 + DO istep = 0, mstep + psi = psifac(istep) + IF (psi < rzphi%xs(0)) CYCLE + IF (psi > rzphi%xs(mpsi)) EXIT + CALL gpeq_recon3(psi, A_hr, B_hr, Iperp_hr, Cpar_hr, epf_hr) +c dst1 (=K1 xi_n^2) and dst3 (=K3 xi_n^2) for the C-free dW form +c 2 dW_p = A + B + Iperp - dst1 - dst3 (dst2 = Cpar cancels). + CALL gpeq_dst(psi, dst_int_c, dst1_c, dst2_c, dst3_c) + iint = iint + 1 + psi_int_pts(iint) = psi + A_vals(iint) = A_hr + B_vals(iint) = B_hr + Iperp_vals(iint) = Iperp_hr + Cpar_vals(iint) = Cpar_hr + epf_vals(iint) = epf_hr + dst1_vals(iint) = REAL(dst1_c, r8) + dst3_vals(iint) = REAL(dst3_c, r8) + ENDDO + + IF (recon_out) THEN + WRITE(u_int,'(a)') "c Bernstein |C|^2 decomposition (direct)" + WRITE(u_int,'(a)') "c identity: epf = A + B + Cpar + Iperp" + WRITE(u_int,'(a)') "c identity_res = epf - A - B - Cpar"// + $ " - Iperp (should be near zero)" + WRITE(u_int,'(a)') "c Cpar should equal dst2 from recon1" + WRITE(u_int,'(a,a)') "c recon_int = ", TRIM(recon_int) + ENDIF + + A_total = 0.0_r8 + B_total = 0.0_r8 + Iperp_total = 0.0_r8 + Cpar_total = 0.0_r8 + epf_total = 0.0_r8 + dst1_total = 0.0_r8 + dst3_total = 0.0_r8 + + IF (TRIM(recon_int) == "spline" .AND. nint_pts > 1) THEN + CALL cspline_alloc(recon3_spl, nint_pts-1, 7) + recon3_spl%xs = psi_int_pts + recon3_spl%fs(:,1) = CMPLX(A_vals, 0.0_r8, r8) + recon3_spl%fs(:,2) = CMPLX(B_vals, 0.0_r8, r8) + recon3_spl%fs(:,3) = CMPLX(Iperp_vals, 0.0_r8, r8) + recon3_spl%fs(:,4) = CMPLX(Cpar_vals, 0.0_r8, r8) + recon3_spl%fs(:,5) = CMPLX(epf_vals, 0.0_r8, r8) + recon3_spl%fs(:,6) = CMPLX(dst1_vals, 0.0_r8, r8) + recon3_spl%fs(:,7) = CMPLX(dst3_vals, 0.0_r8, r8) + CALL cspline_fit(recon3_spl, "extrap") + CALL cspline_int(recon3_spl) + DO iint = 1, nint_pts + A_total = REAL(recon3_spl%fsi(iint-1,1), r8) + B_total = REAL(recon3_spl%fsi(iint-1,2), r8) + Iperp_total = REAL(recon3_spl%fsi(iint-1,3), r8) + Cpar_total = REAL(recon3_spl%fsi(iint-1,4), r8) + epf_total = REAL(recon3_spl%fsi(iint-1,5), r8) + dst1_total = REAL(recon3_spl%fsi(iint-1,6), r8) + dst3_total = REAL(recon3_spl%fsi(iint-1,7), r8) + identity_residual = epf_total - A_total - B_total - + $ Cpar_total - Iperp_total + IF (recon_out) WRITE(u_int,'(12(es17.8e3))') + $ psi_int_pts(iint), A_vals(iint), B_vals(iint), + $ Iperp_vals(iint), Cpar_vals(iint), epf_vals(iint), + $ A_total, B_total, Iperp_total, Cpar_total, epf_total, + $ identity_residual + ENDDO + CALL cspline_dealloc(recon3_spl) + ELSE + DO iint = 1, nint_pts + IF (iint > 1) THEN + dpsi_local = psi_int_pts(iint) - psi_int_pts(iint-1) + A_total = A_total + (A_vals(iint-1)+A_vals(iint)) * + $ dpsi_local / 2.0_r8 + B_total = B_total + (B_vals(iint-1)+B_vals(iint)) * + $ dpsi_local / 2.0_r8 + Iperp_total = Iperp_total + (Iperp_vals(iint-1) + + $ Iperp_vals(iint)) * dpsi_local / 2.0_r8 + Cpar_total = Cpar_total + (Cpar_vals(iint-1) + + $ Cpar_vals(iint)) * dpsi_local / 2.0_r8 + epf_total = epf_total + (epf_vals(iint-1) + + $ epf_vals(iint)) * dpsi_local / 2.0_r8 + dst1_total = dst1_total + (dst1_vals(iint-1) + + $ dst1_vals(iint)) * dpsi_local / 2.0_r8 + dst3_total = dst3_total + (dst3_vals(iint-1) + + $ dst3_vals(iint)) * dpsi_local / 2.0_r8 + ENDIF + identity_residual = epf_total - A_total - B_total - + $ Cpar_total - Iperp_total + IF (recon_out) WRITE(u_int,'(12(es17.8e3))') + $ psi_int_pts(iint), A_vals(iint), B_vals(iint), + $ Iperp_vals(iint), Cpar_vals(iint), epf_vals(iint), + $ A_total, B_total, Iperp_total, Cpar_total, epf_total, + $ identity_residual + ENDDO + ENDIF + + identity_residual = epf_total - A_total - B_total - Cpar_total + $ - Iperp_total + +c C-free reconstructed dW_p: 2 dW = A + B + Iperp - dst1 - dst3 +c (the parallel-current piece dst2 = Cpar cancels identically). + dw_raw3 = 0.5_r8 * (A_total + B_total + Iperp_total + $ - dst1_total - dst3_total) + dw_dcon3 = dw_raw3 * dcon_fac + ep_index = 1 + IF (mode_flag) ep_index = mode + IF (ep_index < 1 .OR. ep_index > mpert) THEN + CALL gpec_stop("gpout_recon3 selected mode is out of range") + ENDIF + ep_selected = REAL(ep(ep_index)) + ep_dcon = ep_selected * dcon_fac + + IF (recon_out) THEN + WRITE(u_int,*) + WRITE(u_int,'(a)') "c Final |C|^2 decomposition totals:" + WRITE(u_int,'(a)') "c A_total = int J |Q|^2 / mu0" + WRITE(u_int,'(a)') "c B_total = int J 2 Re(V*.Q) / mu0" + WRITE(u_int,'(a)') "c Cpar_total = int J mu0 sigma^2 B^2"// + $ " xi_n^2" + WRITE(u_int,'(a)') "c Iperp_total= int J mu0 p'^2 |gp|^2"// + $ " xi_n^2 / B^2" + WRITE(u_int,'(a)') "c epf_total = int J |C|^2 / mu0" + WRITE(u_int,'(a)') "c identity_res = epf - (A+B+Cpar+Iperp)" + WRITE(u_int,'(es17.8e3)') A_total + WRITE(u_int,'(es17.8e3)') B_total + WRITE(u_int,'(es17.8e3)') Cpar_total + WRITE(u_int,'(es17.8e3)') Iperp_total + WRITE(u_int,'(es17.8e3)') epf_total + WRITE(u_int,'(es17.8e3)') identity_residual + ENDIF + + WRITE(*,'(a)') "GPEC_RECON3 |C|^2 decomposition totals:" + WRITE(*,'(a,es17.8e3)') " A_total = ", A_total + WRITE(*,'(a,es17.8e3)') " B_total = ", B_total + WRITE(*,'(a,es17.8e3)') " Cpar_total = ", Cpar_total + WRITE(*,'(a,es17.8e3)') " Iperp_total = ", Iperp_total + WRITE(*,'(a,es17.8e3)') " epf_total = ", epf_total + WRITE(*,'(a,es17.8e3)') " identity_res = ", identity_residual + WRITE(*,'(a)') " (identity_res should be ~ 0; Cpar = dst2.)" + WRITE(*,'(a,es17.8e3)') " dW_p(recon3) = ", dw_raw3 + WRITE(*,'(a,es17.8e3)') " dW_p(recon3-normalize)= ", dw_dcon3 + WRITE(*,'(a,a,a,es17.8e3)') " GPEC ep(", TRIM(smode), ") = ", + $ ep_selected + WRITE(*,'(a,a,a,es17.8e3)') " DCON ep(", TRIM(smode), ") = ", + $ ep_dcon + + IF (recon_out) THEN + OPEN(UNIT=u_log, FILE="gpec.log", STATUS="UNKNOWN", + $ POSITION="APPEND") + WRITE(u_log,'(a)') "GPEC_RECON3 final results:" + WRITE(u_log,'(a,I8)') " mode = ", mode + WRITE(u_log,'(a,L1)') " reg_flag = ", reg_flag + WRITE(u_log,'(a)') " Bernstein |C|^2 decomposition:" + WRITE(u_log,'(a,es17.8e3)') " A_total = ", A_total + WRITE(u_log,'(a,es17.8e3)') " B_total = ", B_total + WRITE(u_log,'(a,es17.8e3)') " Cpar_total = ", Cpar_total + WRITE(u_log,'(a,es17.8e3)') " Iperp_total = ", Iperp_total + WRITE(u_log,'(a,es17.8e3)') " epf_total = ", epf_total + WRITE(u_log,'(a,es17.8e3)') " identity_res = ", + $ identity_residual + WRITE(u_log,'(a)') " (identity_res should be ~0;"// + $ " Cpar should match dst2 from recon1.)" + WRITE(u_log,'(a,es17.8e3)') " dW_p(recon3) = ", + $ dw_raw3 + WRITE(u_log,'(a,es17.8e3)') " dW_p(recon3-normalize)= ", + $ dw_dcon3 + WRITE(u_log,'(a,a,a,es17.8e3)') " GPEC ep(", TRIM(smode), + $ ") = ", ep_selected + WRITE(u_log,'(a,a,a,es17.8e3)') " DCON ep(", TRIM(smode), + $ ") = ", ep_dcon + WRITE(u_log,*) + CLOSE(u_log) + ENDIF + + DEALLOCATE(psi_int_pts, A_vals, B_vals, Iperp_vals, Cpar_vals, + $ epf_vals, dst1_vals, dst3_vals) + CALL gpeq_dealloc + IF (recon_out) THEN + CLOSE(usurf) + CLOSE(u_int) + ENDIF +c----------------------------------------------------------------------- +c terminate. +c----------------------------------------------------------------------- + RETURN + END SUBROUTINE gpout_recon3 + END MODULE gpout_mod diff --git a/input/gpec.in b/input/gpec.in index 7d36414d..a5c3a534 100644 --- a/input/gpec.in +++ b/input/gpec.in @@ -89,6 +89,13 @@ bin_2d_flag=f ! Write 2D binary outputs for use with xdraw verbose=t ! Print run log to terminal + + recon_flag=f ! Calculate each contribution of destabilizing term and effective perturbed field [Bernstein, Handbook of Plasma Physics, 1983]. + recon_flag2=f ! Calculate the first-form plasma energy terms from Eq. (36) with the gamma|div xi|^2 term omitted. + cveri_flag=f ! Under recon_flag, verify (curl C).grad(psi-)=0 and write gpec_recon_cveri_sol*.out. + recon_out=t ! Under recon_flag, write recon diagnostic files and gpec.log entries. If false, keep terminal summary only. + recon_int="spline" ! recon_flag radial integration on psifac grid. Options: "spline" or "trapezoid". + ! For DCON eigenmode reconstruction set coil_flag=f, mode_flag=t, choose mode as needed, and keep reg_flag=f. / &GPEC_DIAGNOSE timeit=f ! Print timer splits for major subroutines diff --git a/input/match.in b/input/match.in index e4b9e15e..31acfa65 100644 --- a/input/match.in +++ b/input/match.in @@ -5,6 +5,8 @@ ideal_flag=t contour_flag=t + sol_num=1 !DCON eigen function index (1 is most unstable), which will be calculated by MATCH + res_flag=f ff0=1e6 ff1=1e6 diff --git a/install/DEPENDENCIES.inc b/install/DEPENDENCIES.inc index a7c0d3f4..d842459f 100644 --- a/install/DEPENDENCIES.inc +++ b/install/DEPENDENCIES.inc @@ -197,7 +197,7 @@ ../gpec/gpec.o: ../coil/coil.o ../gpec/gpdiag.o ../gpec/gpout.o ../gpec/rdcon.o ../gpec/gpeq.o: ../gpec/idcon.o ../gpec/gpglobal.o: ../equil/bicube.o ../equil/fspline.o ../equil/local.o -../gpec/gpout.o: ../coil/field.o ../gpec/gpdiag.o ../gpec/gpresp.o ../gpec/gpvacuum.o ../gpec/idcon.o ../gpec/version.inc ../pentrc/inputs.o ../pentrc/pentrc_interface.o ../pentrc/utilities.o ../slayer/gslayer.o +../gpec/gpout.o: ../coil/field.o ../gpec/gpdiag.o ../gpec/gpglobal.o ../gpec/gpresp.o ../gpec/gpvacuum.o ../gpec/idcon.o ../gpec/version.inc ../pentrc/inputs.o ../pentrc/pentrc_interface.o ../pentrc/utilities.o ../slayer/gslayer.o ../gpec/gpresp.o: ../gpec/gpeq.o ../gpec/gpvacuum.o: ../gpec/gpeq.o ../vacuum/vacuum_ma.o ../gpec/idcon.o: ../equil/equil.o ../gpec/gpglobal.o ../gpec/ismath.o ../vacuum/vacuum_ma.o diff --git a/match/ideal.f b/match/ideal.f index deaaa3db..8b28c820 100644 --- a/match/ideal.f +++ b/match/ideal.f @@ -305,7 +305,9 @@ END SUBROUTINE ideal_transform c----------------------------------------------------------------------- c declarations. c----------------------------------------------------------------------- - SUBROUTINE ideal_build + SUBROUTINE ideal_build(sol_num) + + INTEGER, INTENT(IN) :: sol_num INTEGER :: istep,ifix,jfix,kfix,ieq,info INTEGER, DIMENSION(mpert) :: ipiv @@ -319,7 +321,8 @@ SUBROUTINE ideal_build uedge=0 uedge(mripple-mlow+1)=1 ELSE - uedge=wt(:,1) + WRITE(*,*)"sol_num = ",sol_num + uedge=wt(:,sol_num) ENDIF temp2=soltype(mstep)%u(:,1:mpert,1) CALL zgetrf(mpert,mpert,temp2,mpert,ipiv,info) @@ -357,12 +360,15 @@ END SUBROUTINE ideal_build c----------------------------------------------------------------------- c declarations. c----------------------------------------------------------------------- - SUBROUTINE ideal_write + SUBROUTINE ideal_write(sol_num) + + INTEGER, INTENT(IN) :: sol_num LOGICAL, PARAMETER :: diagnose=.FALSE. INTEGER :: ipert,istep,m REAL(r8), DIMENSION(mpert) :: singfac COMPLEX(r8), DIMENSION(mpert,0:mstep) :: b + CHARACTER(128) :: outputname c----------------------------------------------------------------------- c compute normal perturbed magnetic field. c----------------------------------------------------------------------- @@ -375,7 +381,14 @@ SUBROUTINE ideal_write c write binary output for graphs. c----------------------------------------------------------------------- WRITE(*,*)"Write binary output for graphs." - CALL bin_open(bin_unit,"solutions.bin","UNKNOWN","REWIND","none") + + IF (sol_num == 1) THEN + outputname = "solutions.bin" + ELSE + WRITE(outputname, '(A,I0,A)') "solutions", sol_num, ".bin" + END IF + + CALL bin_open(bin_unit, outputname, "UNKNOWN", "REWIND", "none") DO ipert=1,mpert DO istep=0,mstep WRITE(bin_unit)REAL(psifac(istep),4),REAL(rho(istep),4), diff --git a/match/match.f b/match/match.f index e73e03f4..4b5b1b20 100644 --- a/match/match.f +++ b/match/match.f @@ -29,7 +29,7 @@ PROGRAM match_main $ big,small,ns,root,scan_flag,root_flag,contour_flag,full, $ z_level_type,phi_level,transform_flag,poly_form_type, $ poly_test_type,scan0,scan1,nscan,delta0,delta1,condense, - $ ripple_flag,mripple + $ ripple_flag,mripple,sol_num c----------------------------------------------------------------------- c format statements. c----------------------------------------------------------------------- @@ -55,8 +55,8 @@ PROGRAM match_main c----------------------------------------------------------------------- IF(ideal_flag)THEN CALL ideal_transform - CALL ideal_build - CALL ideal_write + CALL ideal_build(sol_num) + CALL ideal_write(sol_num) CALL ideal_chord IF(contour_flag)CALL ideal_contour ENDIF diff --git a/match/match_mod.f b/match/match_mod.f index 501e91bc..5ff8d20c 100644 --- a/match/match_mod.f +++ b/match/match_mod.f @@ -42,6 +42,7 @@ MODULE match_mod $ kin_flag=.FALSE.,con_flag=.FALSE. LOGICAL, DIMENSION(:), POINTER :: sing_flag INTEGER :: mfix,mhigh,mlow,mpert,mstep,nn,msing,mmatch,mbit,mterm + INTEGER :: sol_num = 1 INTEGER, DIMENSION(:), POINTER :: fixstep REAL(r8) :: sfac0=1 REAL(r8), DIMENSION(:), POINTER :: psifac,rho,q