@@ -717,6 +717,22 @@ function add_initialization_parameters(sys::AbstractSystem)
717717 return sys
718718end
719719
720+ """
721+ Returns true if the parameter `p` is of the form `Initial(x)`.
722+ """
723+ function isinitial (p)
724+ p = unwrap (p)
725+ if iscall (p)
726+ operation (p) isa Initial && return true
727+ if operation (p) === getindex
728+ operation (arguments (p)[1 ]) isa Initial && return true
729+ end
730+ else
731+ return false
732+ end
733+ return false
734+ end
735+
720736"""
721737$(TYPEDSIGNATURES)
722738
@@ -757,38 +773,21 @@ function complete(sys::AbstractSystem; split = true, flatten = true)
757773 if ! isempty (all_ps)
758774 # reorder parameters by portions
759775 ps_split = reorder_parameters (sys, all_ps)
776+ # if there are tunables, they will all be in `ps_split[1]`
777+ # and the arrays will have been scalarized
778+ ordered_ps = eltype (all_ps)[]
760779 # if there are no tunables, vcat them
761- if isempty (get_index_cache (sys). tunable_idx)
762- ordered_ps = reduce (vcat, ps_split)
763- else
764- # if there are tunables, they will all be in `ps_split[1]`
765- # and the arrays will have been scalarized
766- ordered_ps = eltype (all_ps)[]
767- i = 1
768- # go through all the tunables
769- while i <= length (ps_split[1 ])
770- sym = ps_split[1 ][i]
771- # if the sym is not a scalarized array symbolic OR it was already scalarized,
772- # just push it as-is
773- if ! iscall (sym) || operation (sym) != getindex ||
774- any (isequal (sym), all_ps)
775- push! (ordered_ps, sym)
776- i += 1
777- continue
778- end
779- # the next `length(sym)` symbols should be scalarized versions of the same
780- # array symbolic
781- if ! allequal (first (arguments (x))
782- for x in view (ps_split[1 ], i: (i + length (sym) - 1 )))
783- error (" This should not be possible. Please open an issue in ModelingToolkit.jl with an MWE and stacktrace." )
784- end
785- arrsym = first (arguments (sym))
786- push! (ordered_ps, arrsym)
787- i += length (arrsym)
788- end
789- ordered_ps = vcat (
790- ordered_ps, reduce (vcat, ps_split[2 : end ]; init = eltype (ordered_ps)[]))
780+ if ! isempty (get_index_cache (sys). tunable_idx)
781+ unflatten_parameters! (ordered_ps, ps_split[1 ], all_ps)
782+ ps_split = Base. tail (ps_split)
783+ end
784+ # unflatten initial parameters
785+ if ! isempty (get_index_cache (sys). initials_idx)
786+ unflatten_parameters! (ordered_ps, ps_split[1 ], all_ps)
787+ ps_split = Base. tail (ps_split)
791788 end
789+ ordered_ps = vcat (
790+ ordered_ps, reduce (vcat, ps_split; init = eltype (ordered_ps)[]))
792791 @set! sys. ps = ordered_ps
793792 end
794793 elseif has_index_cache (sys)
@@ -800,6 +799,39 @@ function complete(sys::AbstractSystem; split = true, flatten = true)
800799 isdefined (sys, :complete ) ? (@set! sys. complete = true ) : sys
801800end
802801
802+ """
803+ $(TYPEDSIGNATURES)
804+
805+ Given a flattened array of parameters `params` and a collection of all (unscalarized)
806+ parameters in the system `all_ps`, unscalarize the elements in `params` and append
807+ to `buffer` in the same order as they are present in `params`. Effectively, if
808+ `params = [p[1], p[2], p[3], q]` then this is equivalent to `push!(buffer, p, q)`.
809+ """
810+ function unflatten_parameters! (buffer, params, all_ps)
811+ i = 1
812+ # go through all the tunables
813+ while i <= length (params)
814+ sym = params[i]
815+ # if the sym is not a scalarized array symbolic OR it was already scalarized,
816+ # just push it as-is
817+ if ! iscall (sym) || operation (sym) != getindex ||
818+ any (isequal (sym), all_ps)
819+ push! (buffer, sym)
820+ i += 1
821+ continue
822+ end
823+ # the next `length(sym)` symbols should be scalarized versions of the same
824+ # array symbolic
825+ if ! allequal (first (arguments (x))
826+ for x in view (params, i: (i + length (sym) - 1 )))
827+ error (" This should not be possible. Please open an issue in ModelingToolkit.jl with an MWE and stacktrace." )
828+ end
829+ arrsym = first (arguments (sym))
830+ push! (buffer, arrsym)
831+ i += length (arrsym)
832+ end
833+ end
834+
803835for prop in [:eqs
804836 :tag
805837 :noiseeqs
@@ -846,6 +878,7 @@ for prop in [:eqs
846878 :assertions
847879 :solved_unknowns
848880 :split_idxs
881+ :ignored_connections
849882 :parent
850883 :is_dde
851884 :tstops
@@ -1362,6 +1395,75 @@ function assertions(sys::AbstractSystem)
13621395 return merge (asserts, namespaced_asserts)
13631396end
13641397
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+
13651467"""
13661468$(TYPEDSIGNATURES)
13671469
0 commit comments