You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Determine whether or not input/output variable `u` is "bound" within the system, i.e., if it's to be considered internal to `sys`.
63
+
A variable/signal is considered bound if it appears in an equation together with variables from other subsystems.
64
+
The typical usecase for this function is to determine whether the input to an IO component is connected to another component,
65
+
or if it remains an external input that the user has to supply before simulating the system.
66
+
67
+
See also [`bound_inputs`](@ref), [`unbound_inputs`](@ref), [`bound_outputs`](@ref), [`unbound_outputs`](@ref)
68
+
"""
69
+
functionis_bound(sys, u, stack=[])
70
+
#=
71
+
For observed quantities, we check if a variable is connected to something that is bound to something further out.
72
+
In the following scenario
73
+
julia> observed(syss)
74
+
2-element Vector{Equation}:
75
+
sys₊y(tv) ~ sys₊x(tv)
76
+
y(tv) ~ sys₊x(tv)
77
+
sys₊y(t) is bound to the outer y(t) through the variable sys₊x(t) and should thus return is_bound(sys₊y(t)) = true.
78
+
When asking is_bound(sys₊y(t)), we know that we are looking through observed equations and can thus ask
79
+
if var is bound, if it is, then sys₊y(t) is also bound. This can lead to an infinite recursion, so we maintain a stack of variables we have previously asked about to be able to break cycles
80
+
=#
81
+
u ∈Set(stack) &&returnfalse# Cycle detected
82
+
eqs =equations(sys)
83
+
eqs =filter(eq->has_var(eq, u), eqs) # Only look at equations that contain u
84
+
# isout = isoutput(u)
85
+
for eq in eqs
86
+
vars = [get_variables(eq.rhs); get_variables(eq.lhs)]
87
+
for var in vars
88
+
var === u &&continue
89
+
if!same_or_inner_namespace(u, var)
90
+
returntrue
91
+
end
92
+
end
93
+
end
94
+
# Look through observed equations as well
95
+
oeqs =observed(sys)
96
+
oeqs =filter(eq->has_var(eq, u), oeqs) # Only look at equations that contain u
97
+
for eq in oeqs
98
+
vars = [get_variables(eq.rhs); get_variables(eq.lhs)]
99
+
for var in vars
100
+
var === u &&continue
101
+
if!same_or_inner_namespace(u, var)
102
+
returntrue
103
+
end
104
+
ifis_bound(sys, var, [stack; u]) &&!inner_namespace(u, var) # The variable we are comparing to can not come from an inner namespace, binding only counts outwards
105
+
returntrue
106
+
end
107
+
end
108
+
end
109
+
false
110
+
end
111
+
112
+
113
+
114
+
"""
115
+
same_or_inner_namespace(u, var)
116
+
117
+
Determine whether or not `var` is in the same namespace as `u`, or a namespace internal to the namespace of `u`.
118
+
Example: `sys.u ~ sys.inner.u` will bind `sys.inner.u`, but `sys.u` remains an unbound, external signal. The namepsaced signal `sys.inner.u` lives in a namspace internal to `sys`.
119
+
"""
120
+
functionsame_or_inner_namespace(u, var)
121
+
nu =get_namespace(u)
122
+
nv =get_namespace(var)
123
+
nu == nv ||# namespaces are the same
124
+
startswith(nv, nu) ||# or nv starts with nu, i.e., nv is an inner namepsace to nu
125
+
occursin('₊', var) &&!occursin('₊', u) # or u is top level but var is internal
126
+
end
127
+
128
+
functioninner_namespace(u, var)
129
+
nu =get_namespace(u)
130
+
nv =get_namespace(var)
131
+
nu == nv &&returnfalse
132
+
startswith(nv, nu) ||# or nv starts with nu, i.e., nv is an inner namepsace to nu
133
+
occursin('₊', var) &&!occursin('₊', u) # or u is top level but var is internal
134
+
end
135
+
136
+
"""
137
+
get_namespace(x)
138
+
139
+
Return the namespace of a variable as a string. If the variable is not namespaced, the string is empty.
140
+
"""
141
+
functionget_namespace(x)
142
+
sname =string(x)
143
+
parts =split(sname, '₊')
144
+
iflength(parts) ==1
145
+
return""
146
+
end
147
+
join(parts[1:end-1], '₊')
148
+
end
149
+
150
+
"""
151
+
has_var(eq, x)
152
+
153
+
Determine whether or not an equation or expression contains variable `x`.
154
+
"""
155
+
functionhas_var(eq::Equation, x)
156
+
has_var(eq.rhs, x) ||has_var(eq.lhs, x)
157
+
end
158
+
159
+
has_var(ex, x) = x ∈Set(get_variables(ex))
160
+
161
+
# Build control function
162
+
163
+
"""
164
+
(f_oop, f_ip), dvs, p = generate_control_function(sys::AbstractODESystem, dvs = states(sys), ps = parameters(sys); implicit_dae = false, ddvs = if implicit_dae
165
+
166
+
For a system `sys` that has unbound inputs (as determined by [`unbound_inputs`](@ref)), generate a function with additional input argument `in`
167
+
```
168
+
f_oop : (u,in,p,t) -> rhs
169
+
f_ip : (uout,u,in,p,t) -> nothing
170
+
```
171
+
The return values also include the remaining states and parameters, in the order they appear as arguments to `f`.
172
+
173
+
# Example
174
+
```
175
+
using ModelingToolkit: generate_control_function, varmap_to_vars, defaults
f =build_function(rhss, args...; postprocess_fbody=pre, states=sol_states, kwargs...)
233
+
f, dvs, ps
234
+
end
235
+
236
+
"""
237
+
toparam(sys, ctrls::AbstractVector)
238
+
239
+
Transform all instances of `@varibales` in `ctrls` appearing as states and in equations of `sys` with similarly named `@parameters`. This allows [`structural_simplify`](@ref)(sys) in the presence unbound inputs.
0 commit comments