Skip to content

Expand connections after model instantiation and add stream connectors #1348

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 31 commits into from
Nov 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d365af1
Expand connect during structural_simplify
YingboMa Oct 26, 2021
6d89888
Fix bugs
YingboMa Oct 26, 2021
9ba7fd2
Fix minor issue
YingboMa Oct 26, 2021
49b02f7
Add tests
YingboMa Oct 26, 2021
fe6ea72
Connector overhaul
YingboMa Nov 5, 2021
d6ea908
Update tests
YingboMa Nov 5, 2021
7c9c2e1
Merge branch 'master' into myb/connector
YingboMa Nov 8, 2021
1455768
Update tests
YingboMa Nov 8, 2021
d1cd98b
Refactor
YingboMa Nov 9, 2021
24c01e5
connection_type -> connector_type and move some code around
YingboMa Nov 9, 2021
adfbb8e
Basically rewrite connectors
YingboMa Nov 9, 2021
7b66667
Fix some typos
YingboMa Nov 9, 2021
704fed3
Better tests
YingboMa Nov 9, 2021
60a5430
Quick fix
YingboMa Nov 9, 2021
b8aac3c
WIP
YingboMa Nov 12, 2021
b3f504c
Merge branch 'master' into myb/connector
YingboMa Nov 13, 2021
8d7b2d1
Minor fix
YingboMa Nov 13, 2021
fb791eb
WIP
YingboMa Nov 15, 2021
ec9a51f
Implement draft inner/outer criterion for `instream`
YingboMa Nov 15, 2021
2f8aa2a
Draft of various connections
YingboMa Nov 15, 2021
fdb8535
Impose arity constraint in the `connect` signature
YingboMa Nov 15, 2021
6b6800b
WIP
YingboMa Nov 16, 2021
69957eb
Ignore stream vars
YingboMa Nov 16, 2021
c3a8aed
Fix additional equations generation
YingboMa Nov 16, 2021
1d34c07
Make sure `x in AliasGraphKeySet` is in bounds
YingboMa Nov 17, 2021
ed0f8c1
Fix some minor issues
YingboMa Nov 17, 2021
9353d6b
Remove dead code
YingboMa Nov 17, 2021
b2cbeb5
remove incorrect check
YingboMa Nov 17, 2021
b96121e
Fix typo
YingboMa Nov 19, 2021
968831c
Add stream connector tests
YingboMa Nov 20, 2021
71a8e9d
roundtrip print for connect
YingboMa Nov 20, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 1 addition & 13 deletions examples/electrical_components.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,10 @@ using ModelingToolkit, OrdinaryDiffEq

@parameters t
@connector function Pin(;name)
sts = @variables v(t)=1.0 i(t)=1.0
sts = @variables v(t)=1.0 i(t)=1.0 [connect = Flow]
ODESystem(Equation[], t, sts, []; name=name)
end

function ModelingToolkit.connect(::Type{Pin}, ps...)
eqs = [
0 ~ sum(p->p.i, ps) # KCL
]
# KVL
for i in 1:length(ps)-1
push!(eqs, ps[i].v ~ ps[i+1].v)
end

return eqs
end

function Ground(;name)
@named g = Pin()
eqs = [g.v ~ 0]
Expand Down
3 changes: 2 additions & 1 deletion examples/rc_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ V = 1.0
rc_eqs = [
connect(source.p, resistor.p)
connect(resistor.n, capacitor.p)
connect(capacitor.n, source.n, ground.g)
connect(capacitor.n, source.n)
connect(capacitor.n, ground.g)
]

@named rc_model = ODESystem(rc_eqs, t)
Expand Down
3 changes: 2 additions & 1 deletion examples/serial_inductor.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ eqs = [
connect(source.p, resistor.p)
connect(resistor.n, inductor1.p)
connect(inductor1.n, inductor2.p)
connect(source.n, inductor2.n, ground.g)
connect(source.n, inductor2.n)
connect(inductor2.n, ground.g)
]

@named ll_model = ODESystem(eqs, t)
Expand Down
10 changes: 5 additions & 5 deletions src/ModelingToolkit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ if !isdefined(Graphs, :IncrementalCycleTracker)
end

include("systems/abstractsystem.jl")
include("systems/connectors.jl")

include("systems/diffeqs/odesystem.jl")
include("systems/diffeqs/sdesystem.jl")
Expand Down Expand Up @@ -157,8 +158,6 @@ for S in subtypes(ModelingToolkit.AbstractSystem)
@eval convert_system(::Type{<:$S}, sys::$S) = sys
end

struct Flow end

export AbstractTimeDependentSystem, AbstractTimeIndependentSystem, AbstractMultivariateSystem
export ODESystem, ODEFunction, ODEFunctionExpr, ODEProblemExpr, convert_system
export DAEFunctionExpr, DAEProblemExpr
Expand All @@ -173,7 +172,8 @@ export SteadyStateProblem, SteadyStateProblemExpr
export JumpProblem, DiscreteProblem
export NonlinearSystem, OptimizationSystem
export ControlSystem
export alias_elimination, flatten, connect, @connector
export alias_elimination, flatten
export connect, @connector, Connection, Flow, Stream, instream
export ode_order_lowering, liouville_transform
export runge_kutta_discretize
export PDESystem
Expand All @@ -182,7 +182,7 @@ export Equation, ConstrainedEquation
export Term, Sym
export SymScope, LocalScope, ParentScope, GlobalScope
export independent_variables, independent_variable, states, parameters, equations, controls, observed, structure
export structural_simplify
export structural_simplify, expand_connections
export DiscreteSystem, DiscreteProblem

export calculate_jacobian, generate_jacobian, generate_function
Expand All @@ -204,7 +204,7 @@ export toexpr, get_variables
export simplify, substitute
export build_function
export modelingtoolkitize
export @variables, @parameters, Flow
export @variables, @parameters
export @named, @nonamespace, @namespace, extend, compose

end # module
97 changes: 27 additions & 70 deletions src/systems/abstractsystem.jl
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,8 @@ for prop in [
:domain
:ivs
:dvs
:connection_type
:connector_type
:connections
:preface
]
fname1 = Symbol(:get_, prop)
Expand Down Expand Up @@ -282,7 +283,7 @@ function getvar(sys::AbstractSystem, name::Symbol; namespace=false)
elseif !isempty(systems)
i = findfirst(x->nameof(x)==name, systems)
if i !== nothing
return namespace ? rename(systems[i], renamespace(sys, name)) : systems[i]
return namespace ? renamespace(sys, systems[i]) : systems[i]
end
end

Expand Down Expand Up @@ -355,6 +356,7 @@ GlobalScope(sym::Union{Num, Symbolic}) = setmetadata(sym, SymScope, GlobalScope(

renamespace(sys, eq::Equation) = namespace_equation(eq, sys)

renamespace(names::AbstractVector, x) = foldr(renamespace, names, init=x)
function renamespace(sys, x)
x = unwrap(x)
if x isa Symbolic
Expand All @@ -367,6 +369,8 @@ function renamespace(sys, x)
x
end
end
elseif x isa AbstractSystem
rename(x, renamespace(sys, nameof(x)))
else
Symbol(getname(sys), :₊, x)
end
Expand Down Expand Up @@ -562,7 +566,24 @@ function round_trip_expr(t, var2name)
args = map(Base.Fix2(round_trip_expr, var2name), arguments(t))
return :($f($(args...)))
end
round_trip_eq(eq, var2name) = Expr(:call, :~, round_trip_expr(eq.lhs, var2name), round_trip_expr(eq.rhs, var2name))

function round_trip_eq(eq::Equation, var2name)
if eq.lhs isa Connection
syss = get_systems(eq.rhs)
call = Expr(:call, connect)
for sys in syss
strs = split(string(nameof(sys)), "₊")
s = Symbol(strs[1])
for st in strs[2:end]
s = Expr(:., s, Meta.quot(Symbol(st)))
end
push!(call.args, s)
end
call
else
Expr(:call, (~), round_trip_expr(eq.lhs, var2name), round_trip_expr(eq.rhs, var2name))
end
end

function push_eqs!(stmt, eqs, var2name)
eqs_name = gensym(:eqs)
Expand Down Expand Up @@ -854,6 +875,7 @@ topological sort of the observed equations. When `simplify=true`, the `simplify`
function will be applied during the tearing process.
"""
function structural_simplify(sys::AbstractSystem; simplify=false)
sys = expand_connections(sys)
sys = initialize_system_structure(alias_elimination(sys))
check_consistency(sys)
if sys isa ODESystem
Expand Down Expand Up @@ -905,71 +927,6 @@ function check_eqs_u0(eqs, dvs, u0; check_length=true, kwargs...)
return nothing
end

###
### Connectors
###

function with_connection_type(expr)
@assert expr isa Expr && (expr.head == :function || (expr.head == :(=) &&
expr.args[1] isa Expr &&
expr.args[1].head == :call))

sig = expr.args[1]
body = expr.args[2]

fname = sig.args[1]
args = sig.args[2:end]

quote
struct $fname
$(gensym()) -> 1 # this removes the default constructor
end
function $fname($(args...))
function f()
$body
end
res = f()
$isdefined(res, :connection_type) ? $Setfield.@set!(res.connection_type = $fname) : res
end
end
end

macro connector(expr)
esc(with_connection_type(expr))
end

promote_connect_rule(::Type{T}, ::Type{S}) where {T, S} = Union{}
promote_connect_rule(::Type{T}, ::Type{T}) where {T} = T
promote_connect_type(t1::Type, t2::Type, ts::Type...) = promote_connect_type(promote_connect_rule(t1, t2), ts...)
@inline function promote_connect_type(::Type{T}, ::Type{S}) where {T,S}
promote_connect_result(
T,
S,
promote_connect_rule(T,S),
promote_connect_rule(S,T)
)
end

promote_connect_result(::Type, ::Type, ::Type{T}, ::Type{Union{}}) where {T} = T
promote_connect_result(::Type, ::Type, ::Type{Union{}}, ::Type{S}) where {S} = S
promote_connect_result(::Type, ::Type, ::Type{T}, ::Type{T}) where {T} = T
function promote_connect_result(::Type{T}, ::Type{S}, ::Type{P1}, ::Type{P2}) where {T,S,P1,P2}
throw(ArgumentError("connection promotion for $T and $S resulted in $P1 and $P2. " *
"Define promotion only in one direction."))
end

throw_connector_promotion(T, S) = throw(ArgumentError("Don't know how to connect systems of type $S and $T"))
promote_connect_result(::Type{T},::Type{S},::Type{Union{}},::Type{Union{}}) where {T,S} = throw_connector_promotion(T,S)

promote_connect_type(::Type{T}, ::Type{T}) where {T} = T
function promote_connect_type(T, S)
error("Don't know how to connect systems of type $S and $T")
end

function connect(syss...)
connect(promote_connect_type(map(get_connection_type, syss)...), syss...)
end

###
### Inheritance & composition
###
Expand All @@ -990,7 +947,7 @@ function Base.hash(sys::AbstractSystem, s::UInt)
end

"""
$(TYPEDSIGNATURES)
$(TYPEDSIGNATURES)

entend the `basesys` with `sys`, the resulting system would inherit `sys`'s name
by default.
Expand Down Expand Up @@ -1026,7 +983,7 @@ end
Base.:(&)(sys::AbstractSystem, basesys::AbstractSystem; name::Symbol=nameof(sys)) = extend(sys, basesys; name=name)

"""
$(SIGNATURES)
$(SIGNATURES)

compose multiple systems together. The resulting system would inherit the first
system's name.
Expand Down
5 changes: 4 additions & 1 deletion src/systems/alias_elimination.jl
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,10 @@ struct AliasGraphKeySet <: AbstractSet{Int}
end
Base.keys(ag::AliasGraph) = AliasGraphKeySet(ag)
Base.iterate(agk::AliasGraphKeySet, state...) = Base.iterate(agk.ag.eliminated, state...)
Base.in(i::Int, agk::AliasGraphKeySet) = agk.ag.aliasto[i] !== nothing
function Base.in(i::Int, agk::AliasGraphKeySet)
aliasto = agk.ag.aliasto
1 <= i <= length(aliasto) && aliasto[i] !== nothing
end

count_nonzeros(a::AbstractArray) = count(!iszero, a)

Expand Down
Loading