Source code for torx.analysis.fast_tracer_m
"""Implementation of a class for fast fieldline tracing."""
import numpy as np
from torx.decorators import AutoDocClass
# Available numbalsoda integrators. Both share the lsoda_sig cfunc signature
# and the same call/return interface, so make_trace_eq() works with either.
# lsoda is the default (adaptive stiff/non-stiff); dop853 is an explicit
# Runge-Kutta 8(5,3) that can be more accurate for non-stiff field lines.
_INTEGRATORS = ("lsoda", "dop853")
[docs]
class FastTracer(AutoDocClass):
"""Fast tracing class using numbalsoda package."""
[docs]
def __init__(self, equi):
"""Initialize fast trace class."""
self.equi = equi
# Build the trace function now, so that errors show here
self.equi.make_trace_eq()
@property
def funcptr(self) -> int:
"""Return the address of the trace function of the equilibrium."""
# Asked on each use, so changes of the equilibrium are traced
return self.equi.make_trace_eq()
def trace(self, r_start, z_start,
phi_initial=0.0, phi_to_max=2*np.pi, rtol=1e-4, atol=1e-4,
npoints=100, method="lsoda"):
"""
Evaluate fast trace with numbalsoda.
Parameters
----------
r_start, z_start : float
Starting normalized R and Z coordinates of the field line.
phi_initial : float, optional
Toroidal angle at the start of the trace (default 0.0).
phi_to_max : float, optional
Toroidal angle at the end of the trace (default 2*pi).
rtol, atol : float, optional
Relative and absolute integration tolerances (default 1e-4).
npoints : int, optional
Number of evenly spaced output points (default 100).
method : {"lsoda", "dop853"}, optional
numbalsoda integrator to use. "lsoda" (default) is adaptive
stiff/non-stiff; "dop853" is an explicit Runge-Kutta 8(5,3)
that can be more accurate for non-stiff field lines.
Returns
-------
numpy.ndarray
Array of shape (npoints, 3) with the (R, Z, phi) solution. If
the integration fails, for example because the field line
leaves the domain, the array is returned filled with NaN
rather than the solver's partially written buffer.
"""
if method not in _INTEGRATORS:
raise ValueError(
f"Unknown method {method!r}; "
f"choose from {sorted(_INTEGRATORS)}."
)
import numbalsoda
integrator = getattr(numbalsoda, method)
u0 = np.array([r_start, z_start, phi_initial])
t_eval = np.linspace(phi_initial, phi_to_max, npoints, endpoint=True)
usol, success = integrator(self.funcptr, u0, t_eval,
rtol=rtol, atol=atol)
if not success:
usol[:] = np.nan
return usol