Skip to content

Commit c3a8aed

Browse files
committed
Fix additional equations generation
1 parent 69957eb commit c3a8aed

File tree

1 file changed

+180
-17
lines changed

1 file changed

+180
-17
lines changed

src/systems/connectors.jl

Lines changed: 180 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ function connector_type(sys::AbstractSystem)
3838
vtype === Stream && (n_stream += 1)
3939
vtype === Flow && (n_flow += 1)
4040
end
41-
(n_stream > 1 && n_flow > 1) && error("There are multiple flow variables in $(nameof(sys))!")
42-
n_stream > 1 ? StreamConnector() : RegularConnector()
41+
(n_stream > 0 && n_flow > 1) && error("There are multiple flow variables in $(nameof(sys))!")
42+
n_stream > 0 ? StreamConnector() : RegularConnector()
4343
end
4444

4545
Base.@kwdef struct Connection
@@ -120,7 +120,8 @@ end
120120
instream(a) = term(instream, unwrap(a), type=symtype(a))
121121

122122
isconnector(s::AbstractSystem) = has_connector_type(s) && get_connector_type(s) !== nothing
123-
isstreamconnector(s::AbstractSystem) = isconnector(s) && get_connector_type(s) === Stream
123+
isstreamconnector(s::AbstractSystem) = isconnector(s) && get_connector_type(s) isa StreamConnector
124+
isstreamconnection(c::Connection) = any(isstreamconnector, c.inners) || any(isstreamconnector, c.outers)
124125

125126
print_with_indent(n, x) = println(" " ^ n, x)
126127

@@ -176,6 +177,18 @@ function split_sys_var(var)
176177
connector_name, streamvar_name
177178
end
178179

180+
function flowvar(sys::AbstractSystem)
181+
sts = get_states(sys)
182+
for s in sts
183+
vtype = getmetadata(s, ModelingToolkit.VariableConnectType, nothing)
184+
vtype === Flow && return s
185+
end
186+
error("There in no flow variable in $(nameof(sys))")
187+
end
188+
189+
positivemax(m, ::Any; tol=nothing)= max(m, something(tol, 1e-8))
190+
191+
#=
179192
function find_connection(connector_name, ogsys, names)
180193
cs = get_connections(ogsys)
181194
cs === nothing || for c in cs
@@ -193,17 +206,6 @@ function find_connection(connector_name, ogsys, names)
193206
error("$connector_name cannot be found in $(nameof(ogsys)) with levels $(names)")
194207
end
195208
196-
function flowvar(sys::AbstractSystem)
197-
sts = get_states(sys)
198-
for s in sts
199-
vtype = getmetadata(s, ModelingToolkit.VariableConnectType, nothing)
200-
vtype === Flow && return s
201-
end
202-
error("There in no flow variable in $(nameof(sys))")
203-
end
204-
205-
positivemax(m, ::Any; tol=nothing)= max(m, something(tol, 1e-8))
206-
207209
function expand_instream(ogsys, sys::AbstractSystem=ogsys, names=[]; debug=false, tol=nothing)
208210
subsys = get_systems(sys)
209211
isempty(subsys) && return sys
@@ -373,6 +375,7 @@ function expand_instream(ogsys, sys::AbstractSystem=ogsys, names=[]; debug=false
373375
@set! sys.eqs = [eqs; instream_eqs; additional_eqs]
374376
return sys
375377
end
378+
=#
376379

377380
function expand_connections(sys::AbstractSystem; debug=false)
378381
sys = collect_connections(sys; debug=debug)
@@ -402,15 +405,27 @@ function collect_connections(sys::AbstractSystem; debug=false)
402405
end
403406
end
404407

408+
sys = flatten(sys)
405409
eqs′ = get_eqs(sys)
406410
eqs = Equation[]
411+
instream_eqs = Equation[]
412+
instream_exprs = []
407413
cts = [] # connections
408414
for eq in eqs′
409-
eq.lhs isa Connection ? push!(cts, get_systems(eq.rhs)) : push!(eqs, eq) # split connections and equations
415+
if eq.lhs isa Connection
416+
push!(cts, get_systems(eq.rhs))
417+
elseif collect_instream!(instream_exprs, eq)
418+
push!(instream_eqs, eq)
419+
else
420+
push!(eqs, eq) # split connections and equations
421+
end
410422
end
411423

412424
# if there are no connections, we are done
413-
isempty(cts) && return sys
425+
if isempty(cts)
426+
isempty(instream_eqs) || error("Illegal instream function in $(nameof(sys))")
427+
return sys
428+
end
414429

415430
sys2idx = Dict{Symbol,Int}() # system (name) to n-th connect statement
416431
narg_connects = Connection[]
@@ -476,6 +491,154 @@ function collect_connections(sys::AbstractSystem; debug=false)
476491
println("=============END=================")
477492
end
478493

479-
@set! sys.eqs = eqs
494+
# stream variables
495+
stream_connects = filter(isstreamconnection, narg_connects)
496+
@show length(stream_connects)
497+
instream_eqs, additional_eqs = expand_instream(instream_eqs, instream_exprs, stream_connects; debug=debug)
498+
499+
@set! sys.eqs = [eqs; instream_eqs; additional_eqs]
480500
return sys
481501
end
502+
503+
function expand_instream(instream_eqs, instream_exprs, connects; debug=false)
504+
sub = Dict()
505+
seen = Set()
506+
for ex in instream_exprs
507+
var = only(arguments(ex))
508+
connector_name, streamvar_name = split_sys_var(var)
509+
510+
# find the connect
511+
cidx = findfirst(c->connector_name in c, connects)
512+
cidx === nothing && error("$var is not a variable inside stream connectors")
513+
connect = connects[cidx]
514+
connectors = Iterators.flatten((connect.inners, connect.outers))
515+
# stream variable
516+
sv = getproperty(first(connectors), streamvar_name; namespace=false)
517+
inner_sc = connect.inners
518+
outer_sc = connect.outers
519+
520+
n_outers = length(outer_sc)
521+
n_inners = length(inner_sc)
522+
outer_names = (nameof(s) for s in outer_sc)
523+
inner_names = (nameof(s) for s in inner_sc)
524+
if debug
525+
println("Expanding: $ex")
526+
isempty(inner_names) || println("Inner connectors: $(collect(inner_names))")
527+
isempty(outer_names) || println("Outer connectors: $(collect(outer_names))")
528+
end
529+
530+
# expand `instream`s
531+
# https://specification.modelica.org/v3.4/Ch15.html
532+
# Based on the above requirements, the following implementation is
533+
# recommended:
534+
if n_inners == 1 && n_outers == 0
535+
connector_name === only(inner_names) || error("$var is not in any stream connector of $(nameof(ogsys))")
536+
sub[ex] = var
537+
elseif n_inners == 2 && n_outers == 0
538+
@info connector_name collect(inner_names) length(inner_sc)
539+
connector_name in inner_names || error("$var is not in any stream connector of $(nameof(ogsys))")
540+
idx = findfirst(c->nameof(c) === connector_name, inner_sc)
541+
other = idx == 1 ? 2 : 1
542+
sub[ex] = states(inner_sc[other], sv)
543+
elseif n_inners == 1 && n_outers == 1
544+
isinner = connector_name === only(inner_names)
545+
isouter = connector_name === only(outer_names)
546+
(isinner || isouter) || error("$var is not in any stream connector of $(nameof(ogsys))")
547+
if isinner
548+
outerstream = states(only(outer_sc), sv) # c_1.h_outflow
549+
sub[ex] = outerstream
550+
end
551+
else
552+
fv = flowvar(first(connectors))
553+
idx = findfirst(c->nameof(c) === connector_name, inner_sc)
554+
if idx !== nothing
555+
si = sum(s->max(states(s, fv), 0), outer_sc)
556+
for j in 1:n_inners; j == i && continue
557+
f = states(inner_sc[j], fv)
558+
si += max(-f, 0)
559+
end
560+
561+
num = 0
562+
den = 0
563+
for j in 1:n_inners; j == i && continue
564+
f = states(inner_sc[j], fv)
565+
tmp = positivemax(-f, si; tol=tol)
566+
den += tmp
567+
num += tmp * states(inner_sc[j], sv)
568+
end
569+
for k in 1:n_outers
570+
f = states(outer_sc[k], fv)
571+
tmp = positivemax(f, si; tol=tol)
572+
den += tmp
573+
num += tmp * instream(states(outer_sc[k], sv))
574+
end
575+
sub[ex] = num / den
576+
end
577+
578+
end
579+
end
580+
581+
# additional equations
582+
additional_eqs = Equation[]
583+
for c in connects
584+
outer_sc = c.outers
585+
isempty(outer_sc) && continue
586+
inner_sc = c.inners
587+
n_outers = length(outer_sc)
588+
n_inners = length(inner_sc)
589+
for sv in get_states(first(outer_sc))
590+
vtype = getmetadata(sv, ModelingToolkit.VariableConnectType, nothing)
591+
vtype === Stream || continue
592+
if n_inners == 1 && n_outers == 1
593+
innerstream = states(only(inner_sc), sv)
594+
outerstream = states(only(outer_sc), sv)
595+
push!(additional_eqs, outerstream ~ innerstream)
596+
elseif n_inners == 0 && n_outers == 2
597+
# we don't expand `instream` in this case.
598+
v1 = states(outer_sc[1], sv)
599+
v2 = states(outer_sc[2], sv)
600+
push!(additional_eqs, v1 ~ instream(v2))
601+
push!(additional_eqs, v2 ~ instream(v1))
602+
else
603+
for q in 1:n_outers
604+
sq += sum(s->max(-states(s, fv), 0), inner_sc)
605+
for k in 1:n_outers; k == q && continue
606+
f = states(outer_sc[j], fv)
607+
si += max(f, 0)
608+
end
609+
610+
num = 0
611+
den = 0
612+
for j in 1:n_inners
613+
f = states(inner_sc[j], fv)
614+
tmp = positivemax(-f, sq; tol=tol)
615+
den += tmp
616+
num += tmp * states(inner_sc[j], sv)
617+
end
618+
for k in 1:n_outers; k == q && continue
619+
f = states(outer_sc[k], fv)
620+
tmp = positivemax(f, sq; tol=tol)
621+
den += tmp
622+
num += tmp * instream(states(outer_sc[k], sv))
623+
end
624+
push!(additional_eqs, states(outer_sc[q], sv) ~ num / den)
625+
end
626+
end
627+
end
628+
end
629+
630+
instream_eqs = map(Base.Fix2(substitute, sub), instream_eqs)
631+
if debug
632+
println("Expanded equations:")
633+
for eq in instream_eqs
634+
print_with_indent(4, eq)
635+
end
636+
if !isempty(additional_eqs)
637+
println("Additional equations:")
638+
for eq in additional_eqs
639+
print_with_indent(4, eq)
640+
end
641+
end
642+
end
643+
return instream_eqs, additional_eqs
644+
end

0 commit comments

Comments
 (0)