Source code for scpn_fusion.control.sliding_mode_vertical
# SPDX-License-Identifier: AGPL-3.0-or-later
# Commercial license available
# © Concepts 1996–2026 Miroslav Šotek. All rights reserved.
# © Code 2020–2026 Miroslav Šotek. All rights reserved.
# ORCID: 0009-0009-3560-0851
# Contact: www.anulum.li | protoscience@anulum.li
# SCPN Fusion Core — Super-Twisting Sliding Mode Control (Vertical)
"""Super-twisting sliding-mode control utilities for vertical stabilisation."""
from __future__ import annotations
import math
import numpy as np
[docs]
class SuperTwistingSMC:
"""Second-order sliding mode controller (super-twisting algorithm)."""
def __init__(self, alpha: float, beta: float, c: float, u_max: float):
self.alpha = alpha
self.beta = beta
self.c = c
self.u_max = u_max
self.v = 0.0
[docs]
def sliding_surface(self, e: float, de_dt: float) -> float:
"""S = e + c * de/dt."""
return e + self.c * de_dt
[docs]
def step(self, e: float, de_dt: float, dt: float) -> float:
"""U = -alpha * abs(s)^0.5 * sign(s) + v, v_dot = -beta * sign(s)."""
s = self.sliding_surface(e, de_dt)
if dt > 0:
self.v -= self.beta * np.sign(s) * dt
self.v = np.clip(self.v, -self.u_max, self.u_max)
u = -self.alpha * math.sqrt(abs(s)) * np.sign(s) + self.v
return float(np.clip(u, -self.u_max, self.u_max))
[docs]
class VerticalStabilizer:
"""Vertical stability controller wrapper for a tokamak."""
def __init__(
self,
n_index: float,
Ip_MA: float,
R0: float,
m_eff: float,
tau_wall: float,
smc: SuperTwistingSMC,
):
self.n_index = n_index
self.Ip = Ip_MA * 1e6
self.R0 = R0
self.m_eff = m_eff
self.tau_wall = tau_wall
self.smc = smc
[docs]
def step(self, Z_meas: float, Z_ref: float, dZ_dt_meas: float, dt: float) -> float:
"""Compute vertical stabilization command from position error and velocity."""
e = Z_meas - Z_ref
u = self.smc.step(e, dZ_dt_meas, dt)
return u
[docs]
def lyapunov_certificate(alpha: float, beta: float, L_max: float) -> bool:
"""Verify gain conditions: alpha > sqrt(2 L_max), beta > L_max."""
cond1 = alpha > math.sqrt(2.0 * max(L_max, 1e-12))
cond2 = beta > max(L_max, 1e-12)
return cond1 and cond2
[docs]
def estimate_convergence_time(alpha: float, beta: float, L_max: float, s0: float) -> float:
"""Upper bound on time to reach s=0."""
if L_max < 0 or alpha <= math.sqrt(2.0 * L_max):
return float("inf")
denom = alpha - math.sqrt(2.0 * L_max)
if denom <= 0:
return float("inf")
return 2.0 * math.sqrt(abs(s0)) / denom