Topology (pyna.topo)¶
The pyna.topo package provides algorithms for analysing the topological
structure of Poincaré maps: magnetic islands, X/O cycles, stable/unstable
manifolds, and heteroclinic tangles.
Poincaré Maps¶
Multi-section Poincaré map infrastructure.
Provides an extensible framework for collecting field-line crossings on arbitrary 2-D surfaces in (R, Z, φ) space.
Rename history¶
PoincareMap→PoincareAccumulator(conflicts withpyna.topo.dynamics.PoincareMap)
Backward-compatible aliases at module level keep old import paths working:
from pyna.topo.poincare import PoincareMap # still works
from pyna.topo.poincare import ToroidalSection # still works
Classes¶
PoincareSection — abstract base for crossing surfaces (poincare-accumulator use) PoincareToroidalSection — φ = φ₀ plane, implements detect_crossing PoincareAccumulator — accumulate crossings on multiple sections
Functions¶
poincare_from_fieldlines — trace field lines and collect crossings
- class pyna.topo.poincare.PoincareAccumulator(sections)[source]¶
Bases:
objectCollect and store trajectory crossings on multiple sections.
- pyna.topo.poincare.PoincareMap¶
Alias for
PoincareAccumulatorkept for backward compatibility.
- class pyna.topo.poincare.PoincareSection[source]¶
Bases:
ABCAbstract crossing surface for use with PoincareAccumulator.
Concrete subclasses define any 2-D surface in 3-D (R, Z, φ) space. The
detect_crossing()method uses linear interpolation between consecutive trajectory points to find where the trajectory crosses this surface.
- class pyna.topo.poincare.PoincareToroidalSection(phi0=0.0)[source]¶
Bases:
PoincareSectionToroidal section at φ = φ₀ for use with PoincareAccumulator.
Detects when the toroidal angle φ crosses φ₀ (modulo 2π), using linear interpolation to find the precise crossing point.
- Parameters:
phi0 (float) – The toroidal angle of the section (radians). Default 0.0.
- pyna.topo.poincare.Section¶
Backward-compatible alias:
Sectionfrom poincare module → PoincareSection ABC For the canonical Section ABC, usepyna.topo.section.Section.
- pyna.topo.poincare.ToroidalSection¶
Backward-compatible alias:
ToroidalSectionfrom poincare module → PoincareToroidalSection (has detect_crossing for use with PoincareAccumulator) For section-based topology queries, usepyna.topo.section.ToroidalSection.
- pyna.topo.poincare.poincare_from_fieldlines(field_func, start_pts, sections, t_max, dt=0.04, backend=None)[source]¶
Trace field lines and collect Poincaré crossings.
- Parameters:
field_func (callable) –
field_func(rzphi) → (dR, dZ, dphi)unit-tangent ODE rhs.start_pts (ndarray of shape (N, 3)) – Starting points (R, Z, φ).
sections (list of Section) – Sections on which to collect crossings.
t_max (float) – Maximum arc-length parameter for each field line.
dt (float) – Step size for the RK4 integrator.
backend (FieldLineTracer or None) – If None, a
pyna.flt.FieldLineTraceris created.
- Return type:
- Returns:
PoincareAccumulator
- pyna.topo.poincare.rotational_transform_from_trajectory(traj, axis_RZ=None, n_turns=None)[source]¶
Estimate the rotational transform ι (or safety factor q = 1/ι) from a traced field-line trajectory.
The rotational transform is the average poloidal angle advance per toroidal turn. For a closed (periodic) orbit on a flux surface with safety factor q = m/n, we have ι = 1/q = n/m.
Algorithm¶
Compute the cumulative poloidal angle Θ(s) of the trajectory around the magnetic axis
axis_RZ.Compute the cumulative toroidal angle Φ(s) from the φ column of the trajectory.
Fit a linear relationship Θ ≈ ι · Φ by least squares. The slope ι is the rotational transform.
- type traj:
- param traj:
Trajectory (R, Z, φ) as returned by
pyna.flt.FieldLineTracer.trace().- type traj:
ndarray, shape (N, 3)
- type axis_RZ:
- param axis_RZ:
Approximate position [R, Z] of the magnetic axis (or any reference point inside the flux surface). If
None, the centroid of the trajectory projection onto the (R, Z) plane is used.- type axis_RZ:
array_like of shape (2,) or None
- type n_turns:
- param n_turns:
If provided, restrict the computation to the first
n_turnstoroidal traversals. IfNone, use the full trajectory.- type n_turns:
int or None
- rtype:
- returns:
iota (float) – Estimated rotational transform ι = dΘ/dΦ. The safety factor is q = 1 / ι.
Notes
For a regular (KAM) flux surface, the returned value converges as the trajectory length increases. For a chaotic trajectory, the “effective” rotational transform still gives a useful indicator of the local winding rate, though it will not converge to a rational number.
Examples
>>> iota = rotational_transform_from_trajectory(traj, axis_RZ=[1.0, 0.0]) >>> q = 1.0 / iota # safety factor
Magnetic Islands¶
pyna.topo.toroidal_island — backward-compat shim.
Re-exports utilities from pyna.topo.toroidal._utils.
- pyna.topo.toroidal_island.all_rational_q(m_max, n_max, q_min=None, q_max=None)[source]¶
Enumerate all unique q = m/n rational values (m, n > 0).
- pyna.topo.toroidal_island.island_halfwidth(m, n, S_res, S, q_profile, tilde_b_mn, tilde_b_mn_index=None)[source]¶
Estimate the half-width of a magnetic island at a rational surface.
- Return type:
Manifold Computation¶
- pyna.topo.manifold.create_W1d_interpolator_s_to_RZdRZds(afield, Wivp_bundle, phi, phi_epsilon, mturn=<class 'int'>)[source]¶
- pyna.topo.manifold.grow_manifold_from_Xcycle_eig_interp(afield, Xcycle_RZdiff, Jac_evosol_along_Xcycle, eigind, S_span, S_num, Phi_span, Phi_num, rev_eigvec=False, first_step=5e-05, max_step=0.0001)[source]¶
- pyna.topo.manifold.grow_manifold_from_Xcycle_naive_carousel(afield, Xcycle_RZdiff, Jac_evosol_along_Xcycle, eigind, Phi_span, W_nPhi, Ind_num, first_step=5e-05, max_step=0.0001)[source]¶
- pyna.topo.manifold.grow_manifold_from_Xcycle_naive_init_segment(afield, Xcycle_RZdiff, Jac_evosol_along_Xcycle, eigind, Phi_span, total_deltaPhi, ptsnum_initseg=300, initseg_len=8e-05, *arg_sovle_ivp, **kwarg_solve_ivp)[source]¶
- pyna.topo.manifold.smooth1D(x, window_len=11, window='hanning')[source]¶
smooth the data using a window with requested size.
This method is based on the convolution of a scaled window with the signal. The signal is prepared by introducing reflected copies of the signal (with the window size) in both ends so that transient parts are minimized in the begining and end part of the output signal. This function is borrowed from https://scipy-cookbook.readthedocs.io/items/SignalSmooth.html
- input:
x: the input signal window_len: the dimension of the smoothing window; should be an odd integer window: the type of window from ‘flat’, ‘hanning’, ‘hamming’, ‘bartlett’, ‘blackman’
flat window will produce a moving average smoothing.
- output:
the smoothed signal
example:
t=linspace(-2,2,0.1) x=sin(t)+randn(len(t))*0.1 y=smooth(x)
see also:
numpy.hanning, numpy.hamming, numpy.bartlett, numpy.blackman, numpy.convolve scipy.signal.lfilter
TODO: the window parameter could be the window itself if an array instead of a string NOTE: length(output) != length(input), to correct this: return y[(window_len/2-1):-(window_len/2)] instead of just y.
Periodic Orbits / Cycles¶
Toroidal periodic-orbit traces and Newton solvers.
This module is explicitly for toroidal field-line dynamics parameterised by phi. It stores sampled toroidal periodic-orbit traces and provides Newton search utilities in (R, Z, phi).
- class pyna.topo.toroidal_cycle.ToroidalPeriodicOrbitTrace(rzphi0, period_m, trajectory, DPm)[source]¶
Bases:
objectA periodic field-line orbit.
- rzphi0¶
Starting point (R, Z, phi0).
- Type:
ndarray, shape (3,)
- period_m¶
Number of toroidal turns (period of Poincaré map). Corresponds to m in q=m/n notation: P^m(x) = x.
- Type:
- trajectory¶
Full orbit trajectory (R, Z, phi).
- Type:
ndarray, shape (N, 3)
- DPm¶
Monodromy matrix DPm = DX_pol(phi_end), phi_end = 2π·m. Eigenvalues characterize stability.
- Type:
ndarray, shape (2, 2)
- property is_stable: bool¶
True if |eigenvalues| ≤ 1 (elliptic, O-point type).
- section_cut(section)[source]¶
Return the point(s) at which this orbit crosses a toroidal section.
- Parameters:
section (float | ToroidalSection) – Toroidal section angle (radians) or a concrete
ToroidalSection.- Return type:
- Returns:
list of dict, each with keys
'R','Z','phi','kind'.Returns an empty list if the trajectory is not available or no
crossing is found within tolerance.
- property stability_index: float¶
Tr(DPm)/2 for a 2x2 symplectic map. |k|<1 → elliptic, |k|>1 → hyperbolic.
- pyna.topo.toroidal_cycle.find_all_cycles_near_resonance(field_func, equilibrium, m, n, n_seeds=8, dt=0.05, RZlimit=None)[source]¶
Find all O- and X-point cycles near the q=m/n resonant surface.
For a q = m/n resonance, there are m O-points and m X-points at equally spaced angular positions around the resonant surface. This function seeds the Newton-Raphson solver at 2·m·n_seeds angular positions around the resonant flux surface and deduplicates the resulting orbits.
- Parameters:
field_func (callable)
equilibrium (StellaratorSimple or similar) – Must have
resonant_psi(m, n)andR0,r0attributes.m (int) – Mode numbers defining the resonance q = m/n.
n (int) – Mode numbers defining the resonance q = m/n.
n_seeds (int) – Number of angular seeds per expected fixed point.
dt (float) – Integration step.
RZlimit (tuple or None)
- Return type:
- Returns:
list of ToroidalPeriodicOrbitTrace
- pyna.topo.toroidal_cycle.find_cycle(field_func, init_rzphi, n_turns=1, dt=0.05, RZlimit=None, max_iter=50, tol=1e-08, n_fallback_seeds=12, fallback_radius=0.05)[source]¶
Find a periodic orbit starting from init_rzphi using Newton-Raphson.
G(x0) = P^n(x0) - x0 = 0
If Newton diverges or leaves domain, automatically tries n_fallback_seeds alternative starting points distributed on a circle of radius fallback_radius around init_rzphi.
- Parameters:
field_func (callable) – Field function
f(rzphi) → (dR/dl, dZ/dl, dphi/dl).init_rzphi (array (3,)) – Initial guess (R0, Z0, phi0).
n_turns (int) – Period (number of toroidal turns).
dt (float) – Integration step size in φ.
RZlimit (tuple or None) – Domain limits (R_min, R_max, Z_min, Z_max).
max_iter (int) – Maximum Newton iterations.
n_fallback_seeds (int) – Number of fallback seeds to try if primary Newton fails.
fallback_radius (float) – Radius around init_rzphi for fallback seeds.
- Return type:
- Returns:
ToroidalPeriodicOrbitTrace or None if not found.
- pyna.topo.toroidal_cycle.jacobian_of_poincare_map(field_func, rzphi0, n_turns, dt=0.05, eps=1e-05)[source]¶
Finite-difference Jacobian ∂(R_f, Z_f)/∂(R_0, Z_0) of n-turn Poincaré map.
- Parameters:
- Return type:
- Returns:
DPm (ndarray, shape (2, 2)) – Monodromy matrix (Jacobian of the n-turn Poincaré map). det(DPm) ≈ 1 for area-preserving.
- pyna.topo.toroidal_cycle.poincare_map_n(field_func, rzphi0, n_turns, dt=0.05, RZlimit=None)[source]¶
Integrate field line for n toroidal turns, return final (R, Z).
The integration uses the toroidal angle φ as the independent variable.
- Parameters:
field_func (callable) –
field_func(rzphi) → (dR/dl, dZ/dl, dphi/dl).rzphi0 (array-like (3,)) – Starting point (R, Z, phi).
n_turns (int) – Number of toroidal turns to integrate (φ increases by 2π·n_turns).
dt (float) – Step size in φ (radians) for the integrator.
RZlimit (tuple or None) – Optional
(R_min, R_max, Z_min, Z_max)domain boundary.
- Return type:
- Returns:
(R_final, Z_final) or (nan, nan) if field left domain.