Skip to content
LIBRARY
Nonlinear.PadeDelay.md

Nonlinear.PadeDelay

Padé approximation of a fixed time delay.

Approximates y(t) = u(t - delayTime) by a rational transfer function whose Taylor expansion matches exp(-delayTime*s) up to order n + m, realized in controller canonical form as an n-state linear ODE:

Being a linear ODE, the block is continuous, differentiable, and linearizable — unlike a true history-based delay. Higher n gives a sharper approximation over a wider input-frequency range; m = n yields direct feedthrough (the characteristic initial undershoot).

The balance flag selects the state realization:

  • balance = true (default): a balancing state transformation (balance_abc) rescales the companion form to keep the system matrix well-conditioned. Highly recommended — the textbook form has coefficients spanning 1 … (1/delayTime)^n and becomes numerically unreliable for large n or small delayTime.

  • balance = false: the textbook controller canonical form (s = ones).

Input→output behavior is identical for both; balance only changes the internal state coordinates and their conditioning.

The coefficients b[:], a[:] are chosen so the Taylor expansion of the delay exp(-delayTime*s) around s = 0 matches b(s)/a(s) up to order n + m. States are initialized in steady state (der(x) = 0), so the block starts in equilibrium with the input at t = 0.

Reference: Otto Föllinger, Regelungstechnik, 8th ed., ch. 11.9, pp. 412–414, Hüthig Verlag Heidelberg, 1994.

Mirrors Modelica.Blocks.Nonlinear.PadeDelay.

Deviations from MSL:

  • delayTime is a structural parameter here (fixed at compile time), because the Padé coefficients are computed from it at construction.

  • Default is balance = true (MSL defaults false only for backward compatibility, but documents true as strongly recommended).

This component extends from BlockComponents.Interfaces.SISO

Usage

BlockComponents.Nonlinear.PadeDelay(a1=BlockComponents.pade_a1(delayTime, n, m, balance), b11=BlockComponents.pade_b11(delayTime, n, m, balance), c_coeff=BlockComponents.pade_c(delayTime, n, m, balance), d_coeff=BlockComponents.pade_d(delayTime, n, m, balance), s=BlockComponents.pade_s(delayTime, n, m, balance))

Parameters:

NameDescriptionUnitsDefault value
nOrder of the Padé denominator (number of states)1
mOrder of the Padé numerator (usually m = n, or m = n-1)n
delayTimeDelay time of output with respect to input signals1.0
balanceUse a balanced state realization for better numerical conditioningtrue

Connectors

  • u - This connector represents a real signal as an input to a component (RealInput)

  • y - This connector represents a real signal as an output from a component (RealOutput)

Variables

NameDescriptionUnits
xController-canonical-form states
ax_sumPartial sums for the dot product a1·x
cy_sumPartial sums for the dot product c·x

Behavior

Source

dyad
"""
Padé approximation of a fixed time delay.

Approximates `y(t) = u(t - delayTime)` by a rational transfer function whose
Taylor expansion matches `exp(-delayTime*s)` up to order `n + m`, realized in
controller canonical form as an `n`-state linear ODE:

```math
\\dot{x}_1 = a_1 \\cdot x + b_{11}\\,u, \\quad
\\dot{x}_j = s_{j-1}\\,x_{j-1}\\ (j \\ge 2), \\quad
y = c \\cdot x + d\\,u
```

Being a linear ODE, the block is continuous, differentiable, and linearizable —
unlike a true history-based delay. Higher `n` gives a sharper approximation over
a wider input-frequency range; `m = n` yields direct feedthrough (the
characteristic initial undershoot).

The `balance` flag selects the state realization:
- `balance = true` (default): a balancing state transformation (`balance_abc`)
  rescales the companion form to keep the system matrix well-conditioned. Highly
  recommended — the textbook form has coefficients spanning `1 … (1/delayTime)^n`
  and becomes numerically unreliable for large `n` or small `delayTime`.
- `balance = false`: the textbook controller canonical form (`s = ones`).

Input→output behavior is identical for both; `balance` only changes the internal
state coordinates and their conditioning.

The coefficients `b[:]`, `a[:]` are chosen so the Taylor expansion of the delay
`exp(-delayTime*s)` around `s = 0` matches `b(s)/a(s)` up to order `n + m`. States
are initialized in steady state (`der(x) = 0`), so the block starts in equilibrium
with the input at `t = 0`.

Reference: Otto Föllinger, *Regelungstechnik*, 8th ed., ch. 11.9, pp. 412–414,
Hüthig Verlag Heidelberg, 1994.

Mirrors `Modelica.Blocks.Nonlinear.PadeDelay`.

Deviations from MSL:
- `delayTime` is a structural parameter here (fixed at compile time), because the
  Padé coefficients are computed from it at construction.
- Default is `balance = true` (MSL defaults `false` only for backward
  compatibility, but documents `true` as strongly recommended).
"""
component PadeDelay
  extends BlockComponents.Interfaces.SISO
  "Order of the Padé denominator (number of states)"
  structural parameter n::Integer = 1
  "Order of the Padé numerator (usually m = n, or m = n-1)"
  structural parameter m::Integer = n
  "Delay time of output with respect to input signal"
  structural parameter delayTime::Time = 1.0
  "Use a balanced state realization for better numerical conditioning"
  structural parameter balance::Boolean = true
  "First row of the system matrix A"
  final parameter a1::Real[n] = BlockComponents.pade_a1(delayTime, n, m, balance)
  "Input coupling B[1]"
  final parameter b11::Real = BlockComponents.pade_b11(delayTime, n, m, balance)
  "Output coupling row C"
  final parameter c_coeff::Real[n] = BlockComponents.pade_c(delayTime, n, m, balance)
  "Direct feedthrough coefficient d (nonzero only when m = n)"
  final parameter d_coeff::Real = BlockComponents.pade_d(delayTime, n, m, balance)
  "Sub-diagonal state scalings (entry n is unused padding)"
  final parameter s::Real[n] = BlockComponents.pade_s(delayTime, n, m, balance)
  "Controller-canonical-form states"
  variable x::Real[n]
  "Partial sums for the dot product a1·x"
  variable ax_sum::Real[n]
  "Partial sums for the dot product c·x"
  variable cy_sum::Real[n]
relations
  # Steady-state initialization (der(x) = 0), matching MSL's balance=true init:
  # the block starts in equilibrium with the input value at t = 0.
  for j in 1:n
    initial der(x[j]) = 0.0
  end
  # Dot product a1·x via a running partial sum
  ax_sum[1] = a1[1] * x[1]
  for j in 2:n
    ax_sum[j] = ax_sum[j - 1] + a1[j] * x[j]
  end
  # First state (dense feedback row):  der(x[1]) = a1·x + b11·u
  der(x[1]) = ax_sum[n] + b11 * u
  # Scaled integrator chain:  der(x[j]) = s[j-1]·x[j-1]
  for j in 2:n
    der(x[j]) = s[j - 1] * x[j - 1]
  end
  # Output dot product c·x via a running partial sum
  cy_sum[1] = c_coeff[1] * x[1]
  for j in 2:n
    cy_sum[j] = cy_sum[j - 1] + c_coeff[j] * x[j]
  end
  # Output:  y = c·x + d·u
  y = cy_sum[n] + d_coeff * u
metadata {
  "Dyad": {
    "labels": [{"label": "$(instance)", "x": 500, "y": 1100, "rot": 0}],
    "icons": {"default": "dyad://BlockComponents/PadeDelay.svg"}
  }
}
end
Flattened Source
dyad
"""
Padé approximation of a fixed time delay.

Approximates `y(t) = u(t - delayTime)` by a rational transfer function whose
Taylor expansion matches `exp(-delayTime*s)` up to order `n + m`, realized in
controller canonical form as an `n`-state linear ODE:

```math
\\dot{x}_1 = a_1 \\cdot x + b_{11}\\,u, \\quad
\\dot{x}_j = s_{j-1}\\,x_{j-1}\\ (j \\ge 2), \\quad
y = c \\cdot x + d\\,u
```

Being a linear ODE, the block is continuous, differentiable, and linearizable —
unlike a true history-based delay. Higher `n` gives a sharper approximation over
a wider input-frequency range; `m = n` yields direct feedthrough (the
characteristic initial undershoot).

The `balance` flag selects the state realization:
- `balance = true` (default): a balancing state transformation (`balance_abc`)
  rescales the companion form to keep the system matrix well-conditioned. Highly
  recommended — the textbook form has coefficients spanning `1 … (1/delayTime)^n`
  and becomes numerically unreliable for large `n` or small `delayTime`.
- `balance = false`: the textbook controller canonical form (`s = ones`).

Input→output behavior is identical for both; `balance` only changes the internal
state coordinates and their conditioning.

The coefficients `b[:]`, `a[:]` are chosen so the Taylor expansion of the delay
`exp(-delayTime*s)` around `s = 0` matches `b(s)/a(s)` up to order `n + m`. States
are initialized in steady state (`der(x) = 0`), so the block starts in equilibrium
with the input at `t = 0`.

Reference: Otto Föllinger, *Regelungstechnik*, 8th ed., ch. 11.9, pp. 412–414,
Hüthig Verlag Heidelberg, 1994.

Mirrors `Modelica.Blocks.Nonlinear.PadeDelay`.

Deviations from MSL:
- `delayTime` is a structural parameter here (fixed at compile time), because the
  Padé coefficients are computed from it at construction.
- Default is `balance = true` (MSL defaults `false` only for backward
  compatibility, but documents `true` as strongly recommended).
"""
component PadeDelay
  "Input signal port"
  u = RealInput() {
    "Dyad": {
      "placement": {
        "icon": {"iconName": "input", "x1": -100, "y1": 450, "x2": 0, "y2": 550, "rot": 0},
        "diagram": {"iconName": "input", "x1": -100, "y1": 450, "x2": 0, "y2": 550, "rot": 0}
      }
    }
  }
  "Output signal port"
  y = RealOutput() {
    "Dyad": {
      "placement": {
        "icon": {"iconName": "output", "x1": 1000, "y1": 450, "x2": 1100, "y2": 550, "rot": 0},
        "diagram": {"iconName": "output", "x1": 1000, "y1": 450, "x2": 1100, "y2": 550, "rot": 0}
      }
    }
  }
  "Order of the Padé denominator (number of states)"
  structural parameter n::Integer = 1
  "Order of the Padé numerator (usually m = n, or m = n-1)"
  structural parameter m::Integer = n
  "Delay time of output with respect to input signal"
  structural parameter delayTime::Time = 1.0
  "Use a balanced state realization for better numerical conditioning"
  structural parameter balance::Boolean = true
  "First row of the system matrix A"
  final parameter a1::Real[n] = BlockComponents.pade_a1(delayTime, n, m, balance)
  "Input coupling B[1]"
  final parameter b11::Real = BlockComponents.pade_b11(delayTime, n, m, balance)
  "Output coupling row C"
  final parameter c_coeff::Real[n] = BlockComponents.pade_c(delayTime, n, m, balance)
  "Direct feedthrough coefficient d (nonzero only when m = n)"
  final parameter d_coeff::Real = BlockComponents.pade_d(delayTime, n, m, balance)
  "Sub-diagonal state scalings (entry n is unused padding)"
  final parameter s::Real[n] = BlockComponents.pade_s(delayTime, n, m, balance)
  "Controller-canonical-form states"
  variable x::Real[n]
  "Partial sums for the dot product a1·x"
  variable ax_sum::Real[n]
  "Partial sums for the dot product c·x"
  variable cy_sum::Real[n]
relations
  # Steady-state initialization (der(x) = 0), matching MSL's balance=true init:
  # the block starts in equilibrium with the input value at t = 0.
  for j in 1:n
    initial der(x[j]) = 0.0
  end
  # Dot product a1·x via a running partial sum
  ax_sum[1] = a1[1] * x[1]
  for j in 2:n
    ax_sum[j] = ax_sum[j - 1] + a1[j] * x[j]
  end
  # First state (dense feedback row):  der(x[1]) = a1·x + b11·u
  der(x[1]) = ax_sum[n] + b11 * u
  # Scaled integrator chain:  der(x[j]) = s[j-1]·x[j-1]
  for j in 2:n
    der(x[j]) = s[j - 1] * x[j - 1]
  end
  # Output dot product c·x via a running partial sum
  cy_sum[1] = c_coeff[1] * x[1]
  for j in 2:n
    cy_sum[j] = cy_sum[j - 1] + c_coeff[j] * x[j]
  end
  # Output:  y = c·x + d·u
  y = cy_sum[n] + d_coeff * u
metadata {
  "Dyad": {
    "labels": [{"label": "$(instance)", "x": 500, "y": 1100, "rot": 0}],
    "icons": {"default": "dyad://BlockComponents/PadeDelay.svg"}
  }
}
end


Test Cases

No test cases defined.