Skip to content
LIBRARY
Continuous.TransferFunction.md

Continuous.TransferFunction

General linear SISO transfer function block.

Implements: b[1]_s^(nb-1) + b[2]_s^(nb-2) + ... + b[nb] y = –––––––––––––––––––––––– * u a[1]_s^(na-1) + a[2]_s^(na-2) + ... + a[na]

Uses controller canonical form with scaled internal states.

Requires length(a) >= 2 (at least first-order). For static gain use a Gain block.

Usage: tf = BlockComponents.Continuous.TransferFunction(a = [1.0, 1.0], b = [1.0]) # H(s) = 1/(s+1) tf = BlockComponents.Continuous.TransferFunction(a = [1.0, 1.0, 1.0], b = [2.0, 4.0]) # H(s) = (2s+4)/(s²+s+1)

Usage

BlockComponents.Continuous.TransferFunction(c_coeff=BlockComponents.tf_c_coeff(a, b, na, nb), bb=vcat(fill(0.0, na - nb), b), d_coeff=bb[1] / a[1], a_end=ifelse(a[na] > 100 * Base.eps() * sqrt(transpose(a) * a), a[na], 1.0))

Parameters:

NameDescriptionUnitsDefault value
aDenominator coefficients in descending powers of s[1.0, 1.0]
bNumerator coefficients in descending powers of s[1.0]
naNumber of denominator coefficients (= system order + 1)size(a, 1)
nbNumber of numerator coefficients (must be <= na)size(b, 1)
nxNumber of states (= na - 1, the system order)na - 1
init_modeInitialization mode selectorBlockCompon...tialState()

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
x_scaledScaled internal states (actual integration variables)
xUnscaled states (user-observable)
ax_sumPartial sums for dot product a[2:na]·x_scaled
cy_sumPartial sums for dot product c_coeff·x_scaled

Behavior

@example
using BlockComponents #hide
using ModelingToolkit #hide
@named sys = BlockComponents.Continuous.TransferFunction() #hide
let eqs = full_equations(sys); Base.length(eqs) > 25 ? nothing : eqs end #hide

Source

dyad
"""
General linear SISO transfer function block.

Implements:
    b[1]*s^(nb-1) + b[2]*s^(nb-2) + ... + b[nb]
y = ------------------------------------------------ * u
    a[1]*s^(na-1) + a[2]*s^(na-2) + ... + a[na]

Uses controller canonical form with scaled internal states.

Requires length(a) >= 2 (at least first-order). For static gain use a Gain block.

Usage:
  tf = BlockComponents.Continuous.TransferFunction(a = [1.0, 1.0], b = [1.0])           # H(s) = 1/(s+1)
  tf = BlockComponents.Continuous.TransferFunction(a = [1.0, 1.0, 1.0], b = [2.0, 4.0]) # H(s) = (2s+4)/(s²+s+1)
"""
component TransferFunction
  "Input signal"
  u = RealInput() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -100, "y1": 450, "x2": 0, "y2": 550, "rot": 0}
      },
      "tags": []
    }
  }
  "Output signal"
  y = RealOutput() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 1000, "y1": 450, "x2": 1100, "y2": 550, "rot": 0}
      },
      "tags": []
    }
  }
  "Denominator coefficients in descending powers of s"
  structural parameter a::Real[:] = [1.0, 1.0]
  "Numerator coefficients in descending powers of s"
  structural parameter b::Real[:] = [1.0]
  "Number of denominator coefficients (= system order + 1)"
  final structural parameter na::Integer = size(a, 1)
  "Number of numerator coefficients (must be <= na)"
  final structural parameter nb::Integer = size(b, 1)
  "Number of states (= na - 1, the system order)"
  final structural parameter nx::Integer = na - 1
  "Initialization mode selector"
  structural parameter init_mode::BlockComponents.Continuous.BlockInitMode = BlockComponents.Continuous.BlockInitMode.InitialState()
  "Output coupling coefficients (remainder from long division)"
  final parameter c_coeff::Real[nx] = BlockComponents.tf_c_coeff(a, b, na, nb)
  "Padded numerator coefficients (length na)"
  final parameter bb::Real[na] = vcat(fill(0.0, na - nb), b)
  "Direct feedthrough coefficient d = bb[1]/a[1]"
  final parameter d_coeff::Real = bb[1] / a[1]
  "Scaling factor for numerical conditioning"
  final parameter a_end::Real = ifelse(a[na] > 100 * Base.eps() * sqrt(transpose(a) * a), a[na], 1.0)
  "Scaled internal states (actual integration variables)"
  variable x_scaled::Real[nx]
  "Unscaled states (user-observable)"
  variable x::Real[nx]
  "Partial sums for dot product a[2:na]·x_scaled"
  variable ax_sum::Real[nx]
  "Partial sums for dot product c_coeff·x_scaled"
  variable cy_sum::Real[nx]
relations
  switch init_mode
    case NoInit
    case SteadyState
      for j in 1:nx
        initial der(x_scaled[j]) = 0.0
      end
    case InitialState
      for j in 1:nx
        initial x_scaled[j] = getindex(getproperty(init_mode, Symbol("x0")), j) * a_end
      end
    case InitialOutput
      initial y = getproperty(init_mode, Symbol("y_start"))
      for j in 2:nx
        initial der(x_scaled[j]) = 0.0
      end
  end
  # Dot product: a[2:na] · x_scaled
  ax_sum[1] = a[2] * x_scaled[1]
  for j in 2:nx
    ax_sum[j] = ax_sum[j - 1] + a[j + 1] * x_scaled[j]
  end
  # First state equation
  der(x_scaled[1]) = (-ax_sum[nx] + a_end * u) / a[1]
  # Chain of integrators
  for j in 2:nx
    der(x_scaled[j]) = x_scaled[j - 1]
  end
  # Dot product: c_coeff · x_scaled
  cy_sum[1] = c_coeff[1] * x_scaled[1]
  for j in 2:nx
    cy_sum[j] = cy_sum[j - 1] + c_coeff[j] * x_scaled[j]
  end
  # Output equation
  y = cy_sum[nx] / a_end + d_coeff * u
  # State unscaling
  for j in 1:nx
    x[j] = x_scaled[j] / a_end
  end
metadata {
  "Dyad": {
    "labels": [
      {"label": "$(instance)", "x": 500, "y": 1100, "rot": 0},
      {
        "label": "b(s)/a(s)",
        "x": 500,
        "y": 500,
        "rot": 0,
        "attrs": {"font-size": "175", "font-weight": "bold", "fill": "#00007f"}
      }
    ],
    "icons": {"default": "dyad://BlockComponents/TransferFunction.svg"}
  }
}
end
Flattened Source
dyad
"""
General linear SISO transfer function block.

Implements:
    b[1]*s^(nb-1) + b[2]*s^(nb-2) + ... + b[nb]
y = ------------------------------------------------ * u
    a[1]*s^(na-1) + a[2]*s^(na-2) + ... + a[na]

Uses controller canonical form with scaled internal states.

Requires length(a) >= 2 (at least first-order). For static gain use a Gain block.

Usage:
  tf = BlockComponents.Continuous.TransferFunction(a = [1.0, 1.0], b = [1.0])           # H(s) = 1/(s+1)
  tf = BlockComponents.Continuous.TransferFunction(a = [1.0, 1.0, 1.0], b = [2.0, 4.0]) # H(s) = (2s+4)/(s²+s+1)
"""
component TransferFunction
  "Input signal"
  u = RealInput() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -100, "y1": 450, "x2": 0, "y2": 550, "rot": 0}
      },
      "tags": []
    }
  }
  "Output signal"
  y = RealOutput() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 1000, "y1": 450, "x2": 1100, "y2": 550, "rot": 0}
      },
      "tags": []
    }
  }
  "Denominator coefficients in descending powers of s"
  structural parameter a::Real[:] = [1.0, 1.0]
  "Numerator coefficients in descending powers of s"
  structural parameter b::Real[:] = [1.0]
  "Number of denominator coefficients (= system order + 1)"
  final structural parameter na::Integer = size(a, 1)
  "Number of numerator coefficients (must be <= na)"
  final structural parameter nb::Integer = size(b, 1)
  "Number of states (= na - 1, the system order)"
  final structural parameter nx::Integer = na - 1
  "Initialization mode selector"
  structural parameter init_mode::BlockComponents.Continuous.BlockInitMode = BlockComponents.Continuous.BlockInitMode.InitialState()
  "Output coupling coefficients (remainder from long division)"
  final parameter c_coeff::Real[nx] = BlockComponents.tf_c_coeff(a, b, na, nb)
  "Padded numerator coefficients (length na)"
  final parameter bb::Real[na] = vcat(fill(0.0, na - nb), b)
  "Direct feedthrough coefficient d = bb[1]/a[1]"
  final parameter d_coeff::Real = bb[1] / a[1]
  "Scaling factor for numerical conditioning"
  final parameter a_end::Real = ifelse(a[na] > 100 * Base.eps() * sqrt(transpose(a) * a), a[na], 1.0)
  "Scaled internal states (actual integration variables)"
  variable x_scaled::Real[nx]
  "Unscaled states (user-observable)"
  variable x::Real[nx]
  "Partial sums for dot product a[2:na]·x_scaled"
  variable ax_sum::Real[nx]
  "Partial sums for dot product c_coeff·x_scaled"
  variable cy_sum::Real[nx]
relations
  switch init_mode
    case NoInit
    case SteadyState
      for j in 1:nx
        initial der(x_scaled[j]) = 0.0
      end
    case InitialState
      for j in 1:nx
        initial x_scaled[j] = getindex(getproperty(init_mode, Symbol("x0")), j) * a_end
      end
    case InitialOutput
      initial y = getproperty(init_mode, Symbol("y_start"))
      for j in 2:nx
        initial der(x_scaled[j]) = 0.0
      end
  end
  # Dot product: a[2:na] · x_scaled
  ax_sum[1] = a[2] * x_scaled[1]
  for j in 2:nx
    ax_sum[j] = ax_sum[j - 1] + a[j + 1] * x_scaled[j]
  end
  # First state equation
  der(x_scaled[1]) = (-ax_sum[nx] + a_end * u) / a[1]
  # Chain of integrators
  for j in 2:nx
    der(x_scaled[j]) = x_scaled[j - 1]
  end
  # Dot product: c_coeff · x_scaled
  cy_sum[1] = c_coeff[1] * x_scaled[1]
  for j in 2:nx
    cy_sum[j] = cy_sum[j - 1] + c_coeff[j] * x_scaled[j]
  end
  # Output equation
  y = cy_sum[nx] / a_end + d_coeff * u
  # State unscaling
  for j in 1:nx
    x[j] = x_scaled[j] / a_end
  end
metadata {
  "Dyad": {
    "labels": [
      {"label": "$(instance)", "x": 500, "y": 1100, "rot": 0},
      {
        "label": "b(s)/a(s)",
        "x": 500,
        "y": 500,
        "rot": 0,
        "attrs": {"font-size": "175", "font-weight": "bold", "fill": "#00007f"}
      }
    ],
    "icons": {"default": "dyad://BlockComponents/TransferFunction.svg"}
  }
}
end


Test Cases

No test cases defined.