Skip to content

Commit 8629c6a

Browse files
fix: ignore connections removed by AP transforms in expand_connections
1 parent 0a405f1 commit 8629c6a

File tree

2 files changed

+151
-10
lines changed

2 files changed

+151
-10
lines changed

src/systems/abstractsystem.jl

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,6 +1395,75 @@ function assertions(sys::AbstractSystem)
13951395
return merge(asserts, namespaced_asserts)
13961396
end
13971397

1398+
const HierarchyVariableT = Vector{Union{BasicSymbolic, Symbol}}
1399+
const HierarchySystemT = Vector{Union{AbstractSystem, Symbol}}
1400+
"""
1401+
The type returned from `as_hierarchy`.
1402+
"""
1403+
const HierarchyT = Union{HierarchyVariableT, HierarchySystemT}
1404+
1405+
"""
1406+
$(TYPEDSIGNATURES)
1407+
1408+
The inverse operation of `as_hierarchy`.
1409+
"""
1410+
function from_hierarchy(hierarchy::HierarchyT)
1411+
namefn = hierarchy[1] isa AbstractSystem ? nameof : getname
1412+
foldl(@view hierarchy[2:end]; init = hierarchy[1]) do sys, name
1413+
rename(sys, Symbol(name, NAMESPACE_SEPARATOR, namefn(sys)))
1414+
end
1415+
end
1416+
1417+
"""
1418+
$(TYPEDSIGNATURES)
1419+
1420+
Represent a namespaced system (or variable) `sys` as a hierarchy. Return a vector, where
1421+
the first element is the unnamespaced system (variable) and subsequent elements are
1422+
`Symbol`s representing the parents of the unnamespaced system (variable) in order from
1423+
inner to outer.
1424+
"""
1425+
function as_hierarchy(sys::Union{AbstractSystem, BasicSymbolic})::HierarchyT
1426+
namefn = sys isa AbstractSystem ? nameof : getname
1427+
# get the hierarchy
1428+
hierarchy = namespace_hierarchy(namefn(sys))
1429+
# rename the system with unnamespaced name
1430+
newsys = rename(sys, hierarchy[end])
1431+
# and remove it from the list
1432+
pop!(hierarchy)
1433+
# reverse it to go from inner to outer
1434+
reverse!(hierarchy)
1435+
# concatenate
1436+
T = sys isa AbstractSystem ? AbstractSystem : BasicSymbolic
1437+
return Union{Symbol, T}[newsys; hierarchy]
1438+
end
1439+
1440+
"""
1441+
$(TYPEDSIGNATURES)
1442+
1443+
Get the connections to ignore for `sys` and its subsystems. The returned value is a
1444+
`Tuple` similar in structure to the `ignored_connections` field. Each system (variable)
1445+
in the first (second) element of the tuple is also passed through `as_hierarchy`.
1446+
"""
1447+
function ignored_connections(sys::AbstractSystem)
1448+
has_ignored_connections(sys) || return (HierarchySystemT[], HierarchyVariableT[])
1449+
1450+
ics = get_ignored_connections(sys)
1451+
if ics === nothing
1452+
ics = (HierarchySystemT[], HierarchyVariableT[])
1453+
end
1454+
# turn into hierarchies
1455+
ics = (map(as_hierarchy, ics[1]), map(as_hierarchy, ics[2]))
1456+
systems = get_systems(sys)
1457+
# for each subsystem, get its ignored connections, add the name of the subsystem
1458+
# to the hierarchy and concatenate corresponding buffers of the result
1459+
result = mapreduce(Broadcast.BroadcastFunction(vcat), systems; init = ics) do subsys
1460+
sub_ics = ignored_connections(subsys)
1461+
(map(Base.Fix2(push!, nameof(subsys)), sub_ics[1]),
1462+
map(Base.Fix2(push!, nameof(subsys)), sub_ics[2]))
1463+
end
1464+
return (Vector{HierarchySystemT}(result[1]), Vector{HierarchyVariableT}(result[2]))
1465+
end
1466+
13981467
"""
13991468
$(TYPEDSIGNATURES)
14001469

src/systems/connectors.jl

Lines changed: 82 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -395,16 +395,43 @@ function generate_connection_set(
395395
connectionsets = ConnectionSet[]
396396
domain_csets = ConnectionSet[]
397397
sys = generate_connection_set!(
398-
connectionsets, domain_csets, sys, find, replace, scalarize)
398+
connectionsets, domain_csets, sys, find, replace, scalarize, nothing,
399+
# include systems to be ignored
400+
ignored_connections(sys)[1])
399401
csets = merge(connectionsets)
400402
domain_csets = merge([csets; domain_csets], true)
401403

402404
sys, (csets, domain_csets)
403405
end
404406

407+
"""
408+
$(TYPEDSIGNATURES)
409+
410+
Generate connection sets from `connect` equations.
411+
412+
# Arguments
413+
414+
- `connectionsets` is the list of connection sets to be populated by recursively
415+
descending `sys`.
416+
- `domain_csets` is the list of connection sets for domain connections.
417+
- `sys` is the system whose equations are to be searched.
418+
- `namespace` is a system representing the namespace in which `sys` exists, or `nothing`
419+
for no namespace (if `sys` is top-level).
420+
- `ignored_systems` is a list of systems (in the format returned by `as_hierarchy`) to
421+
be ignored when generating connections. This is typically because the connections
422+
they are used in were removed by analysis point transformations.
423+
"""
405424
function generate_connection_set!(connectionsets, domain_csets,
406-
sys::AbstractSystem, find, replace, scalarize, namespace = nothing)
425+
sys::AbstractSystem, find, replace, scalarize, namespace = nothing, ignored_systems = [])
407426
subsys = get_systems(sys)
427+
# turn hierarchies into namespaced systems
428+
namespaced_ignored = from_hierarchy.(ignored_systems)
429+
# filter the subsystems of `sys` to exclude ignored ones
430+
filtered_subsys = filter(subsys) do ss
431+
all(namespaced_ignored) do igsys
432+
nameof(igsys) != nameof(ss)
433+
end
434+
end
408435

409436
isouter = generate_isouter(sys)
410437
eqs′ = get_eqs(sys)
@@ -430,9 +457,21 @@ function generate_connection_set!(connectionsets, domain_csets,
430457
neweq isa AbstractArray ? append!(eqs, neweq) : push!(eqs, neweq)
431458
else
432459
if lhs isa Connection && get_systems(lhs) === :domain
433-
connection2set!(domain_csets, namespace, get_systems(rhs), isouter)
460+
# don't consider systems that should be ignored
461+
systems_to_connect = filter(get_systems(rhs)) do ss
462+
all(namespaced_ignored) do igsys
463+
nameof(igsys) != nameof(ss)
464+
end
465+
end
466+
connection2set!(domain_csets, namespace, systems_to_connect, isouter)
434467
elseif isconnection(rhs)
435-
push!(cts, get_systems(rhs))
468+
# ignore required systems
469+
systems_to_connect = filter(get_systems(rhs)) do ss
470+
all(namespaced_ignored) do igsys
471+
nameof(igsys) != nameof(ss)
472+
end
473+
end
474+
push!(cts, systems_to_connect)
436475
else
437476
# split connections and equations
438477
if eq.lhs isa AbstractArray || eq.rhs isa AbstractArray
@@ -446,7 +485,8 @@ function generate_connection_set!(connectionsets, domain_csets,
446485

447486
# all connectors are eventually inside connectors.
448487
T = ConnectionElement
449-
for s in subsys
488+
# only generate connection sets for systems that are not ignored
489+
for s in filtered_subsys
450490
isconnector(s) || continue
451491
is_domain_connector(s) && continue
452492
for v in unknowns(s)
@@ -465,12 +505,32 @@ function generate_connection_set!(connectionsets, domain_csets,
465505
end
466506
@set! sys.systems = map(
467507
s -> generate_connection_set!(connectionsets, domain_csets, s,
468-
find, replace, scalarize,
469-
renamespace(namespace, s)),
508+
find, replace, scalarize, renamespace(namespace, s),
509+
ignored_systems_for_subsystem(s, ignored_systems)),
470510
subsys)
471511
@set! sys.eqs = eqs
472512
end
473513

514+
"""
515+
$(TYPEDSIGNATURES)
516+
517+
Given a subsystem `subsys` of a parent system and a list of systems (variables) to be
518+
ignored by `generate_connection_set!` (`expand_variable_connections`), filter
519+
`ignored_systems` to only include those present in the subtree of `subsys` and update
520+
their hierarchy to not include `subsys`.
521+
"""
522+
function ignored_systems_for_subsystem(
523+
subsys::AbstractSystem, ignored_systems::Vector{<:HierarchyT})
524+
result = eltype(ignored_systems)[]
525+
for igsys in ignored_systems
526+
if igsys[end] == nameof(subsys)
527+
push!(result, copy(igsys))
528+
pop!(result[end])
529+
end
530+
end
531+
return result
532+
end
533+
474534
function Base.merge(csets::AbstractVector{<:ConnectionSet}, allouter = false)
475535
ele2idx = Dict{ConnectionElement, Int}()
476536
idx2ele = ConnectionElement[]
@@ -576,16 +636,25 @@ end
576636
Recursively descend through the hierarchy of `sys` and expand all connection equations
577637
of causal variables. Return the modified system.
578638
"""
579-
function expand_variable_connections(sys::AbstractSystem)
639+
function expand_variable_connections(sys::AbstractSystem; ignored_variables = nothing)
640+
if ignored_variables === nothing
641+
ignored_variables = ignored_connections(sys)[2]
642+
end
643+
namespaced_ignored = from_hierarchy.(ignored_variables)
580644
eqs = copy(get_eqs(sys))
581645
valid_idxs = trues(length(eqs))
582646
additional_eqs = Equation[]
583647

584648
for (i, eq) in enumerate(eqs)
585649
eq.lhs isa Connection || continue
586650
connection = eq.rhs
587-
elements = connection.systems
651+
elements = get_systems(connection)
588652
is_causal_variable_connection(connection) || continue
653+
elements = filter(elements) do el
654+
all(namespaced_ignored) do var
655+
getname(var) != getname(el.var)
656+
end
657+
end
589658

590659
valid_idxs[i] = false
591660
elements = map(x -> x.var, elements)
@@ -595,7 +664,10 @@ function expand_variable_connections(sys::AbstractSystem)
595664
end
596665
end
597666
eqs = [eqs[valid_idxs]; additional_eqs]
598-
subsystems = map(expand_variable_connections, get_systems(sys))
667+
subsystems = map(get_systems(sys)) do subsys
668+
expand_variable_connections(subsys;
669+
ignored_variables = ignored_systems_for_subsystem(subsys, ignored_variables))
670+
end
599671
@set! sys.eqs = eqs
600672
@set! sys.systems = subsystems
601673
return sys

0 commit comments

Comments
 (0)