@@ -3142,3 +3142,180 @@ has_diff_eqs(osys21) # returns `false`.
3142
3142
```
3143
3143
"""
3144
3144
has_diff_eqs (sys:: AbstractSystem ) = any (is_diff_equation, get_eqs (sys))
3145
+
3146
+ """
3147
+ $(TYPEDSIGNATURES)
3148
+
3149
+ Validate the rules for replacement of subcomponents as defined in `substitute_component`.
3150
+ """
3151
+ function validate_replacement_rule (
3152
+ rule:: Pair{T, T} ; namespace = []) where {T <: AbstractSystem }
3153
+ lhs, rhs = rule
3154
+
3155
+ iscomplete (lhs) && throw (ArgumentError (" LHS of replacement rule cannot be completed." ))
3156
+ iscomplete (rhs) && throw (ArgumentError (" RHS of replacement rule cannot be completed." ))
3157
+
3158
+ rhs_h = namespace_hierarchy (nameof (rhs))
3159
+ if length (rhs_h) != 1
3160
+ throw (ArgumentError (" RHS of replacement rule must not be namespaced." ))
3161
+ end
3162
+ rhs_h[1 ] == namespace_hierarchy (nameof (lhs))[end ] ||
3163
+ throw (ArgumentError (" LHS and RHS must have the same name." ))
3164
+
3165
+ if ! isequal (get_iv (lhs), get_iv (rhs))
3166
+ throw (ArgumentError (" LHS and RHS of replacement rule must have the same independent variable." ))
3167
+ end
3168
+
3169
+ lhs_u = get_unknowns (lhs)
3170
+ rhs_u = Dict (get_unknowns (rhs) .=> nothing )
3171
+ for u in lhs_u
3172
+ if ! haskey (rhs_u, u)
3173
+ if isempty (namespace)
3174
+ throw (ArgumentError (" RHS of replacement rule does not contain unknown $u ." ))
3175
+ else
3176
+ throw (ArgumentError (" Subsystem $(join ([namespace; nameof (lhs)], NAMESPACE_SEPARATOR)) of RHS does not contain unknown $u ." ))
3177
+ end
3178
+ end
3179
+ ru = getkey (rhs_u, u, nothing )
3180
+ name = join ([namespace; nameof (lhs); (hasname (u) ? getname (u) : Symbol (u))],
3181
+ NAMESPACE_SEPARATOR)
3182
+ l_connect = something (getconnect (u), Equality)
3183
+ r_connect = something (getconnect (ru), Equality)
3184
+ if l_connect != r_connect
3185
+ throw (ArgumentError (" Variable $(name) should have connection metadata $(l_connect) ," ))
3186
+ end
3187
+
3188
+ l_input = isinput (u)
3189
+ r_input = isinput (ru)
3190
+ if l_input != r_input
3191
+ throw (ArgumentError (" Variable $name has differing causality. Marked as `input = $l_input ` in LHS and `input = $r_input ` in RHS." ))
3192
+ end
3193
+ l_output = isoutput (u)
3194
+ r_output = isoutput (ru)
3195
+ if l_output != r_output
3196
+ throw (ArgumentError (" Variable $name has differing causality. Marked as `output = $l_output ` in LHS and `output = $r_output ` in RHS." ))
3197
+ end
3198
+ end
3199
+
3200
+ lhs_p = get_ps (lhs)
3201
+ rhs_p = Set (get_ps (rhs))
3202
+ for p in lhs_p
3203
+ if ! (p in rhs_p)
3204
+ if isempty (namespace)
3205
+ throw (ArgumentError (" RHS of replacement rule does not contain parameter $p " ))
3206
+ else
3207
+ throw (ArgumentError (" Subsystem $(join ([namespace; nameof (lhs)], NAMESPACE_SEPARATOR)) of RHS does not contain parameter $p ." ))
3208
+ end
3209
+ end
3210
+ end
3211
+
3212
+ lhs_s = get_systems (lhs)
3213
+ rhs_s = Dict (nameof (s) => s for s in get_systems (rhs))
3214
+
3215
+ for s in lhs_s
3216
+ if haskey (rhs_s, nameof (s))
3217
+ rs = rhs_s[nameof (s)]
3218
+ if isconnector (s)
3219
+ name = join ([namespace; nameof (lhs); nameof (s)], NAMESPACE_SEPARATOR)
3220
+ if ! isconnector (rs)
3221
+ throw (ArgumentError (" Subsystem $name of RHS is not a connector." ))
3222
+ end
3223
+ if (lct = get_connector_type (s)) != = (rct = get_connector_type (rs))
3224
+ throw (ArgumentError (" Subsystem $name of RHS has connection type $rct but LHS has $lct ." ))
3225
+ end
3226
+ end
3227
+ validate_replacement_rule (s => rs; namespace = [namespace; nameof (rhs)])
3228
+ continue
3229
+ end
3230
+ name1 = join ([namespace; nameof (lhs)], NAMESPACE_SEPARATOR)
3231
+ throw (ArgumentError (" $name1 of replacement rule does not contain subsystem $(nameof (s)) ." ))
3232
+ end
3233
+ end
3234
+
3235
+ """
3236
+ $(TYPEDSIGNATURES)
3237
+
3238
+ Chain `getproperty` calls on `root` in the order given in `hierarchy`.
3239
+
3240
+ # Keyword Arguments
3241
+
3242
+ - `skip_namespace_first`: Whether to avoid namespacing in the first `getproperty` call.
3243
+ """
3244
+ function recursive_getproperty (
3245
+ root:: AbstractSystem , hierarchy:: Vector{Symbol} ; skip_namespace_first = true )
3246
+ cur = root
3247
+ for (i, name) in enumerate (hierarchy)
3248
+ cur = getproperty (cur, name; namespace = i > 1 || ! skip_namespace_first)
3249
+ end
3250
+ return cur
3251
+ end
3252
+
3253
+ """
3254
+ $(TYPEDSIGNATURES)
3255
+
3256
+ Recursively descend through `sys`, finding all connection equations and re-creating them
3257
+ using the names of the involved variables/systems and finding the required variables/
3258
+ systems in the hierarchy.
3259
+ """
3260
+ function recreate_connections (sys:: AbstractSystem )
3261
+ eqs = map (get_eqs (sys)) do eq
3262
+ eq. lhs isa Union{Connection, AnalysisPoint} || return eq
3263
+ if eq. lhs isa Connection
3264
+ oldargs = get_systems (eq. rhs)
3265
+ else
3266
+ ap:: AnalysisPoint = eq. rhs
3267
+ oldargs = [ap. input; ap. outputs]
3268
+ end
3269
+ newargs = map (get_systems (eq. rhs)) do arg
3270
+ name = arg isa AbstractSystem ? nameof (arg) : getname (arg)
3271
+ hierarchy = namespace_hierarchy (name)
3272
+ return recursive_getproperty (sys, hierarchy)
3273
+ end
3274
+ if eq. lhs isa Connection
3275
+ return eq. lhs ~ Connection (newargs)
3276
+ else
3277
+ return eq. lhs ~ AnalysisPoint (newargs[1 ], eq. rhs. name, newargs[2 : end ])
3278
+ end
3279
+ end
3280
+ @set! sys. eqs = eqs
3281
+ @set! sys. systems = map (recreate_connections, get_systems (sys))
3282
+ return sys
3283
+ end
3284
+
3285
+ """
3286
+ $(TYPEDSIGNATURES)
3287
+
3288
+ Given a hierarchical system `sys` and a rule `lhs => rhs`, replace the subsystem `lhs` in
3289
+ `sys` by `rhs`. The `lhs` must be the namespaced version of a subsystem of `sys` (e.g.
3290
+ obtained via `sys.inner.component`). The `rhs` must be valid as per the following
3291
+ conditions:
3292
+
3293
+ 1. `rhs` must not be namespaced.
3294
+ 2. The name of `rhs` must be the same as the unnamespaced name of `lhs`.
3295
+ 3. Neither one of `lhs` or `rhs` can be marked as complete.
3296
+ 4. Both `lhs` and `rhs` must share the same independent variable.
3297
+ 5. `rhs` must contain at least all of the unknowns and parameters present in
3298
+ `lhs`.
3299
+ 6. Corresponding unknowns in `rhs` must share the same connection and causality
3300
+ (input/output) metadata as their counterparts in `lhs`.
3301
+ 7. For each subsystem of `lhs`, there must be an identically named subsystem of `rhs`.
3302
+ These two corresponding subsystems must satisfy conditions 3, 4, 5, 6, 7. If the
3303
+ subsystem of `lhs` is a connector, the corresponding subsystem of `rhs` must also
3304
+ be a connector of the same type.
3305
+
3306
+ `sys` also cannot be marked as complete.
3307
+ """
3308
+ function substitute_component (sys:: T , rule:: Pair{T, T} ) where {T <: AbstractSystem }
3309
+ iscomplete (sys) &&
3310
+ throw (ArgumentError (" Cannot replace subsystems of completed systems" ))
3311
+
3312
+ validate_replacement_rule (rule)
3313
+
3314
+ lhs, rhs = rule
3315
+ hierarchy = namespace_hierarchy (nameof (lhs))
3316
+
3317
+ newsys, _ = modify_nested_subsystem (sys, hierarchy) do inner
3318
+ return rhs, ()
3319
+ end
3320
+ return recreate_connections (newsys)
3321
+ end
0 commit comments