@@ -198,19 +198,23 @@ function modify_nested_subsystem(
198198
199199 # recursive helper function which does the searching and modification
200200 function _helper (sys:: AbstractSystem , i:: Int )
201- # we reached past the end, so everything matched and
202- # `sys` is the system to modify.
203201 if i > length (hierarchy)
202+ # we reached past the end, so everything matched and
203+ # `sys` is the system to modify.
204204 sys, vars = fn (sys)
205205 else
206+ # find the subsystem with the given name and error otherwise
206207 cur = hierarchy[i]
207208 idx = findfirst (subsys -> nameof (subsys) == cur, get_systems (sys))
208209 idx === nothing &&
209210 error (" System $(join ([nameof (root); hierarchy[1 : i- 1 ]], ' .' )) does not have a subsystem named $cur ." )
210211
212+ # recurse into new subsystem
211213 newsys, vars = _helper (get_systems (sys)[idx], i + 1 )
214+ # update this system with modified subsystem
212215 @set! sys. systems[idx] = newsys
213216 end
217+ # only namespace variables from inner systems
214218 if i != 1
215219 vars = ntuple (Val (length (vars))) do i
216220 renamespace (sys, vars[i])
270274
271275# ### PRIMITIVE TRANSFORMATIONS
272276
277+ const DOC_WILL_REMOVE_AP = """
278+ Note that this transformation will remove `ap`, causing any subsequent transformations \
279+ referring to it to fail.\
280+ """
281+
282+ const DOC_ADDED_VARIABLE = """
283+ The added variable(s) will have a default of zero, of the appropriate type and size.\
284+ """
285+
273286"""
274287 $(TYPEDEF)
275288
@@ -278,11 +291,9 @@ it will add a new input variable which connects to the outputs of the analysis p
278291`apply_transformation` returns the new input variable (if added) as the auxiliary
279292information. The new input variable will have the name `Symbol(:d_, nameof(ap))`.
280293
281- Note that this transformation will remove `ap`, causing any subsequent transformations
282- referring to it to fail.
294+ $DOC_WILL_REMOVE_AP
283295
284- The added variable, if present, will have a default of zero, of the appropriate type and
285- size.
296+ $DOC_ADDED_VARIABLE
286297
287298## Fields
288299
@@ -340,17 +351,15 @@ end
340351 $(TYPEDEF)
341352
342353A transformation which returns the variable corresponding to the input of the analysis
343- point.
344-
345- `apply_transformation` returns the variable as auxiliary information.
354+ point. Does not modify the system.
346355
347356## Fields
348357
349358$(TYPEDFIELDS)
350359"""
351360struct GetInput <: AnalysisPointTransformation
352361 """
353- The analysis point to add the output to .
362+ The analysis point to get the input of .
354363 """
355364 ap:: AnalysisPoint
356365end
@@ -380,15 +389,12 @@ the analysis point before connecting to the outputs. The new variable will have
380389
381390If `with_output == true`, also creates an additional new variable which has the value
382391provided to the outputs after the above modification. This new variable has the same name
383- as the analysis point.
384-
385- `apply_transformation` returns a 1-tuple of the perturbation variable if
386- `with_output == false` and a 2-tuple of the perturbation variable and output variable if
387- `with_output == true`.
392+ as the analysis point and will be the second variable in the tuple of new variables returned
393+ from `apply_transformation`.
388394
389- Removes the analysis point `ap`, so any subsequent transformations requiring it will fail.
395+ $DOC_WILL_REMOVE_AP
390396
391- The added variable(s) will have a default of zero, of the appropriate type and size.
397+ $DOC_ADDED_VARIABLE
392398
393399## Fields
394400
@@ -454,17 +460,33 @@ function apply_transformation(tf::PerturbOutput, sys::AbstractSystem)
454460end
455461
456462"""
457- $(TYPEDSIGNATURES )
463+ $(TYPEDEF )
458464
459465A transformation which adds a variable named `name` to the system containing the analysis
460- point `ap`. The added variable has the same type and size as the input of the analysis
461- point.
466+ point `ap`. $DOC_ADDED_VARIABLE
467+
468+ # Fields
469+
470+ $(TYPEDFIELDS)
462471"""
463472struct AddVariable <: AnalysisPointTransformation
473+ """
474+ The analysis point in the system to modify, and whose input should be used as the
475+ template for the new variable.
476+ """
464477 ap:: AnalysisPoint
478+ """
479+ The name of the added variable.
480+ """
465481 name:: Symbol
466482end
467483
484+ """
485+ $(TYPEDSIGNATURES)
486+
487+ Add a new variable to the system containing analysis point `ap` with the same name as the
488+ analysis point.
489+ """
468490AddVariable (ap:: AnalysisPoint ) = AddVariable (ap, nameof (ap))
469491
470492function apply_transformation (tf:: AddVariable , sys:: AbstractSystem )
@@ -494,26 +516,34 @@ end
494516 $(TYPEDSIGNATURES)
495517
496518A transformation enable calculating the sensitivity function about the analysis point `ap`.
497- `apply_transformation` returns a 2-tuple `du, u` as auxiliary information.
519+ The returned added variables are `(du, u)` where `du` is the perturbation added to the
520+ input, and `u` is the output after perturbation.
498521
499- Removes the analysis point `ap`, so any subsequent transformations requiring it will fail.
522+ $DOC_WILL_REMOVE_AP
500523
501- The added variables will have a default of zero, of the appropriate type and size.
524+ $DOC_ADDED_VARIABLE
502525"""
503526SensitivityTransform (ap:: AnalysisPoint ) = PerturbOutput (ap, true )
504527
505528"""
506529 $(TYPEDEF)
507530
508531A transformation to enable calculating the complementary sensitivity function about the
509- analysis point `ap`. `apply_transformation` returns a 2-tuple ` du, u` as auxiliary
510- information .
532+ analysis point `ap`. The returned added variables are `( du, u)` where `du` is the
533+ perturbation added to the outputs and `u` is the input to the analysis point .
511534
512- Removes the analysis point `ap`, so any subsequent transformations requiring it will fail.
535+ $DOC_WILL_REMOVE_AP
513536
514- The added variables will have a default of zero, of the appropriate type and size.
537+ $DOC_ADDED_VARIABLE
538+
539+ # Fields
540+
541+ $(TYPEDFIELDS)
515542"""
516543struct ComplementarySensitivityTransform <: AnalysisPointTransformation
544+ """
545+ The analysis point to modify.
546+ """
517547 ap:: AnalysisPoint
518548end
519549
@@ -537,7 +567,25 @@ function apply_transformation(cst::ComplementarySensitivityTransform, sys::Abstr
537567 return sys, (du, u)
538568end
539569
570+ """
571+ $(TYPEDEF)
572+
573+ A transformation to enable calculating the loop transfer function about the analysis point
574+ `ap`. The returned added variables are `(du, u)` where `du` feeds into the outputs of `ap`
575+ and `u` is the input of `ap`.
576+
577+ $DOC_WILL_REMOVE_AP
578+
579+ $DOC_ADDED_VARIABLE
580+
581+ # Fields
582+
583+ $(TYPEDFIELDS)
584+ """
540585struct LoopTransferTransform <: AnalysisPointTransformation
586+ """
587+ The analysis point to modify.
588+ """
541589 ap:: AnalysisPoint
542590end
543591
@@ -547,15 +595,25 @@ function apply_transformation(tf::LoopTransferTransform, sys::AbstractSystem)
547595 return sys, (du, u)
548596end
549597
550- # ## TODO : Move these
598+ """
599+ $(TYPEDSIGNATURES)
551600
601+ A utility function to get the "canonical" form of a list of analysis points. Always returns
602+ a list of values. Any value that cannot be turned into an `AnalysisPoint` (i.e. isn't
603+ already an `AnalysisPoint` or `Symbol`) is simply wrapped in an array.
604+ """
552605canonicalize_ap (ap:: Symbol ) = [AnalysisPoint (ap)]
553606canonicalize_ap (ap:: AnalysisPoint ) = [ap]
554607canonicalize_ap (ap) = [ap]
555608function canonicalize_ap (aps:: Vector )
556609 mapreduce (canonicalize_ap, vcat, aps; init = [])
557610end
558611
612+ """
613+ $(TYPEDSIGNATURES)
614+
615+ Given a list of analysis points, break the connection for each and set the output to zero.
616+ """
559617function handle_loop_openings (sys:: AbstractSystem , aps)
560618 for ap in canonicalize_ap (aps)
561619 sys, (outvar,) = apply_transformation (Break (ap, true ), sys)
@@ -568,63 +626,130 @@ function handle_loop_openings(sys::AbstractSystem, aps)
568626 return sys
569627end
570628
571- function get_sensitivity_function (
572- sys:: AbstractSystem , aps; system_modifier = identity, loop_openings = [], kwargs... )
629+ const DOC_LOOP_OPENINGS = """
630+ - `loop_openings`: A list of analysis points whose connections should be removed and
631+ the outputs set to zero as a part of the linear analysis.
632+ """
633+
634+ const DOC_SYS_MODIFIER = """
635+ - `system_modifier`: A function taking the transformed system and applying any
636+ additional transformations, returning the modified system. The modified system
637+ is passed to `linearization_function`.
638+ """
639+ """
640+ $(TYPEDSIGNATURES)
641+
642+ Utility function for linear analyses that apply a transformation `transform`, which
643+ returns the added variables `(du, u)`, to each of the analysis points in `aps` and then
644+ calls `linearization_function` with all the `du`s as inputs and `u`s as outputs. Returns
645+ the linearization function and modified, simplified system.
646+
647+ # Keyword arguments
648+
649+ $DOC_LOOP_OPENINGS
650+ $DOC_SYS_MODIFIER
651+
652+ All other keyword arguments are forwarded to `linearization_function`.
653+ """
654+ function get_linear_analysis_function (
655+ sys:: AbstractSystem , transform, aps; system_modifier = identity, loop_openings = [], kwargs... )
573656 sys = handle_loop_openings (sys, loop_openings)
574657 aps = canonicalize_ap (aps)
575658 dus = []
576659 us = []
577660 for ap in aps
578- sys, (du, u) = apply_transformation (SensitivityTransform (ap), sys)
661+ sys, (du, u) = apply_transformation (transform (ap), sys)
579662 push! (dus, du)
580663 push! (us, u)
581664 end
582665 linearization_function (system_modifier (sys), dus, us; kwargs... )
583666end
584667
585- function get_comp_sensitivity_function (
586- sys:: AbstractSystem , aps; system_modifier = identity, loop_openings = [], kwargs... )
587- sys = handle_loop_openings (sys, loop_openings)
588- aps = canonicalize_ap (aps)
589- dus = []
590- us = []
591- for ap in aps
592- sys, (du, u) = apply_transformation (ComplementarySensitivityTransform (ap), sys)
593- push! (dus, du)
594- push! (us, u)
595- end
596- linearization_function (system_modifier (sys), dus, us; kwargs... )
668+ """
669+ $(TYPEDSIGNATURES)
670+
671+ Return the sensitivity function for the analysis point(s) `aps`, and the modified system
672+ simplified with the appropriate inputs and outputs.
673+
674+ # Keyword Arguments
675+
676+ $DOC_LOOP_OPENINGS
677+ $DOC_SYS_MODIFIER
678+
679+ All other keyword arguments are forwarded to `linearization_function`.
680+ """
681+ function get_sensitivity_function (sys:: AbstractSystem , aps; kwargs... )
682+ get_linear_analysis_function (sys, SensitivityTransform, aps; kwargs... )
597683end
598684
599- function get_looptransfer_function (
600- sys, aps; system_modifier = identity, loop_openings = [], kwargs... )
601- sys = handle_loop_openings (sys, loop_openings)
602- aps = canonicalize_ap (aps)
603- dus = []
604- us = []
605- for ap in aps
606- sys, (du, u) = apply_transformation (LoopTransferTransform (ap), sys)
607- push! (dus, du)
608- push! (us, u)
609- end
610- linearization_function (system_modifier (sys), dus, us; kwargs... )
685+ """
686+ $(TYPEDSIGNATURES)
687+
688+ Return the complementary sensitivity function for the analysis point(s) `aps`, and the
689+ modified system simplified with the appropriate inputs and outputs.
690+
691+ # Keyword Arguments
692+
693+ $DOC_LOOP_OPENINGS
694+ $DOC_SYS_MODIFIER
695+
696+ All other keyword arguments are forwarded to `linearization_function`.
697+ """
698+ function get_comp_sensitivity_function (sys:: AbstractSystem , aps; kwargs... )
699+ get_linear_analysis_function (sys, ComplementarySensitivityTransform, aps; kwargs... )
700+ end
701+
702+ """
703+ $(TYPEDSIGNATURES)
704+
705+ Return the loop-transfer function for the analysis point(s) `aps`, and the modified
706+ system simplified with the appropriate inputs and outputs.
707+
708+ # Keyword Arguments
709+
710+ $DOC_LOOP_OPENINGS
711+ $DOC_SYS_MODIFIER
712+
713+ All other keyword arguments are forwarded to `linearization_function`.
714+ """
715+ function get_looptransfer_function (sys:: AbstractSystem , aps; kwargs... )
716+ get_linear_analysis_function (sys, LoopTransferTransform, aps; kwargs... )
611717end
612718
613719for f in [:get_sensitivity , :get_comp_sensitivity , :get_looptransfer ]
720+ utility_fun = Symbol (f, :_function )
614721 @eval function $f (
615722 sys, ap, args... ; loop_openings = [], system_modifier = identity, kwargs... )
616- lin_fun, ssys = $ (Symbol (f, :_function ) )(
723+ lin_fun, ssys = $ (utility_fun )(
617724 sys, ap, args... ; loop_openings, system_modifier, kwargs... )
618725 ModelingToolkit. linearize (ssys, lin_fun; kwargs... ), ssys
619726 end
727+ @eval @doc """
728+ $(TYPEDSIGNATURES)
729+
730+ Call `$($ utility_fun) ` and perform the linearization. All keyword arguments are
731+ forwarded to `$($ utility_fun) ` and subsequently `linearize`.
732+ """ $ f
620733end
621734
622- function open_loop (sys, ap:: Union{Symbol, AnalysisPoint} ; kwargs... )
735+ """
736+ $(TYPEDSIGNATURES)
737+
738+ Apply `LoopTransferTransform` to the analysis point `ap` and return the
739+ result of `apply_transformation`.
740+
741+ # Keyword Arguments
742+
743+ - `system_modifier`: a function which takes the modified system and returns a new system
744+ with any required further modifications peformed.
745+ """
746+ function open_loop (sys, ap:: Union{Symbol, AnalysisPoint} ; system_modifier = identity)
623747 if ap isa Symbol
624748 ap = AnalysisPoint (ap)
625749 end
626750 tf = LoopTransferTransform (ap)
627- return apply_transformation (tf, sys)
751+ sys, vars = apply_transformation (tf, sys)
752+ return system_modifier (sys), vars
628753end
629754
630755function linearization_function (sys:: AbstractSystem ,
0 commit comments