@@ -3184,3 +3184,188 @@ has_diff_eqs(osys21) # returns `false`.
31843184``` 
31853185""" 
31863186has_diff_eqs (sys:: AbstractSystem ) =  any (is_diff_equation, get_eqs (sys))
3187+ 
3188+ """ 
3189+     $(TYPEDSIGNATURES)  
3190+ 
3191+ Validate the rules for replacement of subcomponents as defined in `substitute_component`. 
3192+ """ 
3193+ function  validate_replacement_rule (
3194+         rule:: Pair{T, T} ; namespace =  []) where  {T <:  AbstractSystem }
3195+     lhs, rhs =  rule
3196+ 
3197+     iscomplete (lhs) &&  throw (ArgumentError (" LHS of replacement rule cannot be completed." 
3198+     iscomplete (rhs) &&  throw (ArgumentError (" RHS of replacement rule cannot be completed." 
3199+ 
3200+     rhs_h =  namespace_hierarchy (nameof (rhs))
3201+     if  length (rhs_h) !=  1 
3202+         throw (ArgumentError (" RHS of replacement rule must not be namespaced." 
3203+     end 
3204+     rhs_h[1 ] ==  namespace_hierarchy (nameof (lhs))[end ] || 
3205+         throw (ArgumentError (" LHS and RHS must have the same name." 
3206+ 
3207+     if  ! isequal (get_iv (lhs), get_iv (rhs))
3208+         throw (ArgumentError (" LHS and RHS of replacement rule must have the same independent variable." 
3209+     end 
3210+ 
3211+     lhs_u =  get_unknowns (lhs)
3212+     rhs_u =  Dict (get_unknowns (rhs) .=>  nothing )
3213+     for  u in  lhs_u
3214+         if  ! haskey (rhs_u, u)
3215+             if  isempty (namespace)
3216+                 throw (ArgumentError (" RHS of replacement rule does not contain unknown $u ." 
3217+             else 
3218+                 throw (ArgumentError (" Subsystem $(join ([namespace; nameof (lhs)], NAMESPACE_SEPARATOR))  of RHS does not contain unknown $u ." 
3219+             end 
3220+         end 
3221+         ru =  getkey (rhs_u, u, nothing )
3222+         name =  join ([namespace; nameof (lhs); (hasname (u) ?  getname (u) :  Symbol (u))],
3223+             NAMESPACE_SEPARATOR)
3224+         l_connect =  something (getconnect (u), Equality)
3225+         r_connect =  something (getconnect (ru), Equality)
3226+         if  l_connect !=  r_connect
3227+             throw (ArgumentError (" Variable $(name)  should have connection metadata $(l_connect) ," 
3228+         end 
3229+ 
3230+         l_input =  isinput (u)
3231+         r_input =  isinput (ru)
3232+         if  l_input !=  r_input
3233+             throw (ArgumentError (" Variable $name  has differing causality. Marked as `input = $l_input ` in LHS and `input = $r_input ` in RHS." 
3234+         end 
3235+         l_output =  isoutput (u)
3236+         r_output =  isoutput (ru)
3237+         if  l_output !=  r_output
3238+             throw (ArgumentError (" Variable $name  has differing causality. Marked as `output = $l_output ` in LHS and `output = $r_output ` in RHS." 
3239+         end 
3240+     end 
3241+ 
3242+     lhs_p =  get_ps (lhs)
3243+     rhs_p =  Set (get_ps (rhs))
3244+     for  p in  lhs_p
3245+         if  ! (p in  rhs_p)
3246+             if  isempty (namespace)
3247+                 throw (ArgumentError (" RHS of replacement rule does not contain parameter $p " 
3248+             else 
3249+                 throw (ArgumentError (" Subsystem $(join ([namespace; nameof (lhs)], NAMESPACE_SEPARATOR))  of RHS does not contain parameter $p ." 
3250+             end 
3251+         end 
3252+     end 
3253+ 
3254+     lhs_s =  get_systems (lhs)
3255+     rhs_s =  Dict (nameof (s) =>  s for  s in  get_systems (rhs))
3256+ 
3257+     for  s in  lhs_s
3258+         if  haskey (rhs_s, nameof (s))
3259+             rs =  rhs_s[nameof (s)]
3260+             if  isconnector (s)
3261+                 name =  join ([namespace; nameof (lhs); nameof (s)], NAMESPACE_SEPARATOR)
3262+                 if  ! isconnector (rs)
3263+                     throw (ArgumentError (" Subsystem $name  of RHS is not a connector." 
3264+                 end 
3265+                 if  (lct =  get_connector_type (s)) != =  (rct =  get_connector_type (rs))
3266+                     throw (ArgumentError (" Subsystem $name  of RHS has connection type $rct  but LHS has $lct ." 
3267+                 end 
3268+             end 
3269+             validate_replacement_rule (s =>  rs; namespace =  [namespace; nameof (rhs)])
3270+             continue 
3271+         end 
3272+         name1 =  join ([namespace; nameof (lhs)], NAMESPACE_SEPARATOR)
3273+         throw (ArgumentError (" $name1  of replacement rule does not contain subsystem $(nameof (s)) ." 
3274+     end 
3275+ end 
3276+ 
3277+ """ 
3278+     $(TYPEDSIGNATURES)  
3279+ 
3280+ Chain `getproperty` calls on `root` in the order given in `hierarchy`. 
3281+ 
3282+ # Keyword Arguments 
3283+ 
3284+ - `skip_namespace_first`: Whether to avoid namespacing in the first `getproperty` call. 
3285+ """ 
3286+ function  recursive_getproperty (
3287+         root:: AbstractSystem , hierarchy:: Vector{Symbol} ; skip_namespace_first =  true )
3288+     cur =  root
3289+     for  (i, name) in  enumerate (hierarchy)
3290+         cur =  getproperty (cur, name; namespace =  i >  1  ||  ! skip_namespace_first)
3291+     end 
3292+     return  unwrap (cur)
3293+ end 
3294+ 
3295+ """ 
3296+     $(TYPEDSIGNATURES)  
3297+ 
3298+ Recursively descend through `sys`, finding all connection equations and re-creating them 
3299+ using the names of the involved variables/systems and finding the required variables/ 
3300+ systems in the hierarchy. 
3301+ """ 
3302+ function  recreate_connections (sys:: AbstractSystem )
3303+     eqs =  map (get_eqs (sys)) do  eq
3304+         eq. lhs isa  Union{Connection, AnalysisPoint} ||  return  eq
3305+         if  eq. lhs isa  Connection
3306+             oldargs =  get_systems (eq. rhs)
3307+         else 
3308+             ap:: AnalysisPoint  =  eq. rhs
3309+             oldargs =  [ap. input; ap. outputs]
3310+         end 
3311+         newargs =  map (get_systems (eq. rhs)) do  arg
3312+             rewrap_nameof =  arg isa  SymbolicWithNameof
3313+             if  rewrap_nameof
3314+                 arg =  arg. var
3315+             end 
3316+             name =  arg isa  AbstractSystem ?  nameof (arg) :  getname (arg)
3317+             hierarchy =  namespace_hierarchy (name)
3318+             newarg =  recursive_getproperty (sys, hierarchy)
3319+             if  rewrap_nameof
3320+                 newarg =  SymbolicWithNameof (newarg)
3321+             end 
3322+             return  newarg
3323+         end 
3324+         if  eq. lhs isa  Connection
3325+             return  eq. lhs ~  Connection (newargs)
3326+         else 
3327+             return  eq. lhs ~  AnalysisPoint (newargs[1 ], eq. rhs. name, newargs[2 : end ])
3328+         end 
3329+     end 
3330+     @set!  sys. eqs =  eqs
3331+     @set!  sys. systems =  map (recreate_connections, get_systems (sys))
3332+     return  sys
3333+ end 
3334+ 
3335+ """ 
3336+     $(TYPEDSIGNATURES)  
3337+ 
3338+ Given a hierarchical system `sys` and a rule `lhs => rhs`, replace the subsystem `lhs` in 
3339+ `sys` by `rhs`. The `lhs` must be the namespaced version of a subsystem of `sys` (e.g. 
3340+ obtained via `sys.inner.component`). The `rhs` must be valid as per the following 
3341+ conditions: 
3342+ 
3343+ 1. `rhs` must not be namespaced. 
3344+ 2. The name of `rhs` must be the same as the unnamespaced name of `lhs`. 
3345+ 3. Neither one of `lhs` or `rhs` can be marked as complete. 
3346+ 4. Both `lhs` and `rhs` must share the same independent variable. 
3347+ 5. `rhs` must contain at least all of the unknowns and parameters present in 
3348+    `lhs`. 
3349+ 6. Corresponding unknowns in `rhs` must share the same connection and causality 
3350+    (input/output) metadata as their counterparts in `lhs`. 
3351+ 7. For each subsystem of `lhs`, there must be an identically named subsystem of `rhs`. 
3352+    These two corresponding subsystems must satisfy conditions 3, 4, 5, 6, 7. If the 
3353+    subsystem of `lhs` is a connector, the corresponding subsystem of `rhs` must also 
3354+    be a connector of the same type. 
3355+ 
3356+ `sys` also cannot be marked as complete. 
3357+ """ 
3358+ function  substitute_component (sys:: T , rule:: Pair{T, T} ) where  {T <:  AbstractSystem }
3359+     iscomplete (sys) && 
3360+         throw (ArgumentError (" Cannot replace subsystems of completed systems" 
3361+ 
3362+     validate_replacement_rule (rule)
3363+ 
3364+     lhs, rhs =  rule
3365+     hierarchy =  namespace_hierarchy (nameof (lhs))
3366+ 
3367+     newsys, _ =  modify_nested_subsystem (sys, hierarchy) do  inner
3368+         return  rhs, ()
3369+     end 
3370+     return  recreate_connections (newsys)
3371+ end 
0 commit comments