"""AMIGO tuning rules (Approximate M-constrained Integral Gain Optimization). References ---------- * K. J. Åström and T. Hägglund, "Revisiting the Ziegler-Nichols step response method for PID control", *Journal of Process Control*, 14, 635–650, 2004. * K. J. Åström and T. Hägglund, *Advanced PID Control*, ISA, 2006, ch. 7. Survey context: ``docs/design/03_tuning_methods.md`` §4. """ from __future__ import annotations from typing import Any, ClassVar from pidtune.models.base import ProcessModel from pidtune.models.fopdt import FOPDT from pidtune.tuners.base import ControllerType, Tuner, TuningResult, register_tuner __all__ = ["AMIGOTuner"] @register_tuner class AMIGOTuner(Tuner): """AMIGO tuning from a FOPDT model. AMIGO rules were derived by constrained optimization (maximize integral gain subject to a maximum-sensitivity constraint ``Ms ≈ 1.4``) over a large process test batch, then fit to simple formulas in the FOPDT parameters. They are robust defaults across a wide range of dead-time ratios — generally the recommended starting point in this library. With ``K``, ``τ``, ``θ`` the FOPDT parameters: **PI** :: Kp = (0.15/K) + (0.35 − θ·τ / (θ + τ)²) · (τ / (K·θ)) Ti = 0.35·θ + 13·θ·τ² / (τ² + 12·θ·τ + 7·θ²) **PID** :: Kp = (1/K) · (0.2 + 0.45·τ/θ) Ti = θ · (0.4·θ + 0.8·τ) / (θ + 0.1·τ) Td = 0.5·θ·τ / (0.3·θ + τ) AMIGO additionally recommends setpoint weighting: ``b = 0`` for ``θ/τ < 0.5``, ``b = 1`` otherwise (with ``c = 0``). The recommended weights are returned in ``TuningResult.metadata`` so callers can apply them to :class:`~pidtune.controllers.pid.PIDController`. Integrating processes --------------------- For an integrating process with delay ``Kv·exp(-θs)/s`` (modeled as FOPDT with very large ``τ``, i.e. ``θ/τ → 0``), the limiting forms are used automatically when ``θ/τ < integrating_threshold`` (default 1e-3):: PI: Kp = 0.35 / (Kv·θ), Ti = 13.4·θ PID: Kp = 0.45 / (Kv·θ), Ti = 8·θ, Td = 0.5·θ where ``Kv = K/τ``. Applicability ------------- Valid over the full test-batch range ``0.02 ≤ θ/τ ≤ 100`` (with the integrating limit below). ``θ == 0`` raises :class:`~pidtune.exceptions.TuningError`. ``TuningResult.metadata`` keys: ``{"theta_over_tau": float, "b": float, "c": float, "ms_target": 1.4, "integrating_form": bool}``. """ name: ClassVar[str] = "amigo" supported_controller_types: ClassVar[frozenset] = frozenset( {ControllerType.PI, ControllerType.PID} ) def __init__(self, *, integrating_threshold: float = 1e-3) -> None: """Configure the tuner. Parameters ---------- integrating_threshold: ``θ/τ`` ratio below which the integrating-process limiting formulas are used instead of the general FOPDT formulas. Must be positive. """ self.integrating_threshold = float(integrating_threshold) def supports(self, model: ProcessModel) -> bool: """Accept only :class:`~pidtune.models.fopdt.FOPDT` models.""" return isinstance(model, FOPDT) def tune( self, model: ProcessModel, *, controller_type: ControllerType = ControllerType.PID, **options: Any, ) -> TuningResult: """Apply the AMIGO formulas to a FOPDT model. Parameters ---------- model: A :class:`~pidtune.models.fopdt.FOPDT` instance with ``θ > 0``. controller_type: PI or PID. **options: No options are accepted; any keyword raises :class:`TypeError`. Returns ------- TuningResult Gains plus recommended setpoint weights in ``metadata``. """ raise NotImplementedError("Implemented in Milestone 3.")