Source code for torus_solver.current_potential
from __future__ import annotations
import jax.numpy as jnp
from .spectral import spectral_derivative
from .torus import TorusSurface
[docs]
def surface_current_from_current_potential(surface: TorusSurface, Phi: jnp.ndarray) -> jnp.ndarray:
"""Surface current K (A/m) from a *single-valued* current potential Phi (A).
This is the REGCOIL / "current potential" model:
K = n_hat × ∇_s Phi
On a circular torus with coordinates (θ, φ) and F=0, this can be written as:
K = (Phi_θ * r_φ - Phi_φ * r_θ) / sqrt(g)
where r_θ and r_φ are the surface tangent vectors and sqrt(g)=norm(r_θ×r_φ).
"""
Phi_theta = spectral_derivative(Phi, surface.k_theta, axis=0)
Phi_phi = spectral_derivative(Phi, surface.k_phi, axis=1)
denom = surface.sqrt_g[..., None] + 1e-30
return (Phi_theta[..., None] * surface.r_phi - Phi_phi[..., None] * surface.r_theta) / denom
[docs]
def surface_current_from_current_potential_with_net_currents(
surface: TorusSurface,
Phi_single_valued: jnp.ndarray,
*,
net_poloidal_current_A: float = 0.0,
net_toroidal_current_A: float = 0.0,
) -> jnp.ndarray:
"""Surface current from current potential with net poloidal/toroidal currents.
The net-current contributions correspond to *multi-valued* secular terms in Phi,
which must NOT be differentiated using FFTs. Instead we add their derivatives
analytically: Phi_theta adds Itor/(2π) and Phi_phi adds Ipol/(2π).
"""
twopi = 2.0 * jnp.pi
Phi_theta = spectral_derivative(Phi_single_valued, surface.k_theta, axis=0) + (
net_toroidal_current_A / twopi
)
Phi_phi = spectral_derivative(Phi_single_valued, surface.k_phi, axis=1) + (
net_poloidal_current_A / twopi
)
denom = surface.sqrt_g[..., None] + 1e-30
return (Phi_theta[..., None] * surface.r_phi - Phi_phi[..., None] * surface.r_theta) / denom
[docs]
def add_secular_current_terms_for_visualization(
surface: TorusSurface, *, net_poloidal_current_A: float = 0.0, net_toroidal_current_A: float = 0.0
) -> jnp.ndarray:
"""Return a *multi-valued* Phi_sec(θ,φ) for visualization.
The current potential can be multi-valued; its derivatives define K.
Convention (matches REGCOIL naming):
- net_poloidal_current_A multiplies φ / (2π) and yields poloidal surface current K_θ.
- net_toroidal_current_A multiplies θ / (2π) and yields toroidal surface current K_φ.
Warning: Do not pass this function's output to `surface_current_from_current_potential`,
since the secular terms are not periodic/smooth and FFT derivatives will be wrong.
"""
twopi = 2.0 * jnp.pi
return (net_toroidal_current_A / twopi) * surface.theta[:, None] + (
net_poloidal_current_A / twopi
) * surface.phi[None, :]
# Backwards-compatible alias (visualization-only).
add_secular_current_terms = add_secular_current_terms_for_visualization