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:
| Name | Description | Units | Default value |
|---|---|---|---|
a | Denominator coefficients in descending powers of s | – | [1.0, 1.0] |
b | Numerator coefficients in descending powers of s | – | [1.0] |
na | Number of denominator coefficients (= system order + 1) | – | size(a, 1) |
nb | Number of numerator coefficients (must be <= na) | – | size(b, 1) |
nx | Number of states (= na - 1, the system order) | – | na - 1 |
init_mode | Initialization mode selector | – | BlockCompon...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
| Name | Description | Units |
|---|---|---|
x_scaled | Scaled internal states (actual integration variables) | – |
x | Unscaled states (user-observable) | – |
ax_sum | Partial sums for dot product a[2:na]·x_scaled | – |
cy_sum | Partial sums for dot product c_coeff·x_scaled | – |
Behavior
using BlockComponents #hide
using ModelingToolkit #hide
@named sys = BlockComponents.Continuous.TransferFunction() #hide
let eqs = full_equations(sys); Base.length(eqs) > 25 ? nothing : eqs end #hideSource
"""
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"}
}
}
endFlattened Source
"""
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"}
}
}
endTest Cases
No test cases defined.
Related
Examples
Experiments
Analyses
Tests