### Simulate Nominal Model with Deterministic Noise Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Simulates the nominal plant model with a stationary input and deterministic noise. This setup is used to observe parameter drift due to insufficient excitation, matching Rohr's example. ```julia simulate(nominal_sim!, nominal_ic; input_signal = 2.0, deterministic_noise = (x, p, t) -> 0.5sin(16.1t), noise_param = nothing, tspan = (0.0, 100.0), adapt_gain = 3.35) ``` -------------------------------- ### Training with LBFGS Optimizer Source: https://docs.sciml.ai/ComponentArrays/stable/examples/DiffEqFlux Continues training the model using the LBFGS optimizer, starting from the results obtained with ADAM. ```julia res2 = sciml_train(loss_n_ode, res1.minimizer, LBFGS(); cb) cb(res2.minimizer, loss_n_ode(res2.minimizer)...; doplot = true) ``` -------------------------------- ### Get Val-Wrapped Keys for ComponentVector Source: https://docs.sciml.ai/ComponentArrays/stable/api Returns Val-wrapped keys of a ComponentVector for fast iteration over component keys. This method also works directly on an AbstractAxis. ```julia julia> using ComponentArrays julia> ca = ComponentArray(a = 1, b = [1, 2, 3], c = (a = 4,)) ComponentVector{Int64}(a = 1, b = [1, 2, 3], c = (a = 4)) julia> [ca[k] for k in valkeys(ca)] 3-element Vector{Any}: 1 [1, 2, 3] ComponentVector{Int64}(a = 4) julia> sum(prod(ca[k]) for k in valkeys(ca)) 11 ``` -------------------------------- ### ComponentArrays.labels Source: https://docs.sciml.ai/ComponentArrays/stable/api Get string labels for each index of a ComponentVector. This is useful for automatic plot legend labeling. ```APIDOC ## labels(x::ComponentVector) ### Description Get string labels for for each index of a `ComponentVector`. Useful for automatic plot legend labelling. ### Parameters #### Path Parameters - **x** (ComponentVector) - The ComponentVector to get labels from. ### Response #### Success Response - Returns a `Vector{String}` containing the labels for each index. ``` -------------------------------- ### Get Axes of a ComponentArray Source: https://docs.sciml.ai/ComponentArrays/stable/api Demonstrates using the `getaxes` function to retrieve the axes associated with a ComponentArray, which differs from the standard `axes` function. ```julia using ComponentArrays ax = Axis(a = 1:3, b = (4:6, (a = 1, b = 2:3))) A = zeros(6, 6) ca = ComponentArray(A, (ax, ax)) getaxes(ca) ``` -------------------------------- ### Get String Labels for ComponentVector Indices Source: https://docs.sciml.ai/ComponentArrays/stable/api Retrieves string labels for each index of a ComponentVector. This is useful for automatic plot legend labeling. ```julia julia> x = ComponentArray(a = 5, b = [(a = (a = 20, b = 1), b = 0), (a = (a = 33, b = 1), b = 0)], c = (a = (a = 2, b = [1, 2]), b = [1.0 2.0; 5 6])) ComponentVector{Float64}(a = 5.0, b = ComponentVector{Float64, SubArray{Float64, 1, Vector{Float64}, Tuple{UnitRange{Int64}}, true}, Tuple{Axis{(a = ViewAxis(1:2, Axis(a = 1, b = 2)), b = 3)}}}[(a = (a = 20.0, b = 1.0), b = 0.0), (a = (a = 33.0, b = 1.0), b = 0.0)], c = (a = (a = 2.0, b = [1.0, 2.0]), b = [1.0 2.0; 5.0 6.0])) julia> ComponentArrays.labels(x) 14-element Vector{String}: "a" "b[1].a.a" "b[1].a.b" "b[1].b" "b[2].a.a" "b[2].a.b" "b[2].b" "c.a.a" "c.a.b[1]" "c.a.b[2]" "c.b[1,1]" "c.b[2,1]" "c.b[1,2]" "c.b[2,2]" ``` -------------------------------- ### Define Simulation Inputs Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Sets up simulation time span, input signal function, and initial conditions for various components. ```julia # Simulation time span tspan = (0.0, 30.0) # Input signal input_signal = (x, p, t) -> sin(3t) # Initial conditions ref_ic = zeros(1) nominal_ic = zeros(1) truth_ic = zeros(3) sensor_ic = zeros(1) θ_est_ic = ComponentArray(θr = 0.0, θy = 0.0) ``` -------------------------------- ### Setting Up the ODE Problem and Data Source: https://docs.sciml.ai/ComponentArrays/stable/examples/DiffEqFlux Defines the initial conditions, time span, the true ODE function, and generates synthetic data for training. ```julia u0 = Float32[2.0; 0.0] datasize = 30 tspan = (0.0f0, 1.5f0) function trueODEfunc(du, u, p, t) true_A = [-0.1 2.0; -2.0 -0.1] du .= ((u .^ 3)'true_A)' end t = range(tspan[1], tspan[2], length = datasize) prob = ODEProblem(trueODEfunc, u0, tspan) ode_data = Array(solve(prob, Tsit5(), saveat = t)) ``` -------------------------------- ### Set Up and Simulate Adaptive Control System Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Defines the simulation function, including noise handling, parameter estimation, and solving ODE/SDE problems. Use this function to run adaptive control simulations. ```julia function simulate(plant_fun, plant_ic; tspan = tspan, input_signal = input_signal, adapt_gain = 1.5, noise_param = nothing, deterministic_noise = 0.0) noise(D, vars, p, t) = (D.feedback_loop.sensor[1] = noise_param) # Truth control parameters θ_truth = (r = bm/bp, y = (ap-am)/bp) # Initial conditions ic = ComponentArray( reference_model = ref_ic, feedback_loop = ( parameter_estimates = θ_est_ic, sensor = sensor_ic, plant_model = plant_ic ) ) # Model parameters p = (; gamma = adapt_gain, plant_fun) sim_fun = apply_inputs(system!; r = input_signal, n = deterministic_noise) # We can also choose whether we want to include random noise in our model by switching between an ODE # and an SDE problem. if noise_param === nothing prob = ODEProblem(sim_fun, ic, tspan, p, max_iters = 2000) else prob = SDEProblem(sim_fun, noise, ic, tspan, p, max_iters = 2000) end ## Solve! sol = solve(prob) ## Plot # Reference model tracking top = plot( sol, vars = ["reference_model[1]", "feedback_loop.sensor"], legend = :right, title = "Reference Model Tracking" ) # Parameter estimate tracking bottom = plot(sol, vars = "feedback_loop.parameter_estimates") plot!( bottom, [tspan...], [θ_truth.r θ_truth.y; θ_truth.r θ_truth.y], labels = ["θr truth" "θy truth"], legend = :right, title = "Parameter Estimate Tracking" ) # Combine both plots plot(top, bottom, layout = (2, 1), size = (800, 800)) end ``` -------------------------------- ### Full System Simulation Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Simulates the complete system, including the reference model and the feedback loop. Takes input signal `r` and noise `n`. ```julia function system!(D, vars, p, t; r = 0.0, n = 0.0) @unpack reference_model, feedback_loop = vars ym = ref_sim!(D.reference_model, reference_model, (), t; u = r) yp = feedback_sys!(D.feedback_loop, feedback_loop, p, t; ym, r, n) return yp end ``` -------------------------------- ### Interactive GUI for system simulation Source: https://docs.sciml.ai/ComponentArrays/stable/examples/coulomb_control An interactive GUI allowing users to adjust PID gains (kp, ki, kd), switch between Coulomb and Viscous friction models, and select input types (Sine or Step) with varying magnitude and period. It visualizes the system's response and phase plot. ```julia ## Interactive GUI for switching out plant models and varying PID gains @manipulate for kp in 0:0.01:15, ki in 0:0.01:15, kd in 0:0.01:15, damping in Dict( "Coulomb" => coulomb_block!, "Viscous" => viscous_block! ), reference in Dict( "Sine" => sine_input, "Step" => step_input ), magnitude in 0:0.01:10, # pop-pop! period in 1:0.01:30, plot_v in false # Inputs tspan = (0.0, 30.0) ctrl_fun = PID_controller! # plant_fun = coulomb_block! ref = if reference==sine_input reference(; period, mag = magnitude) else reference(mag = magnitude) end m = 50.0 μ = 0.1 ω = 2π/period c = 4*μ*m*g/(π*ω*magnitude) # Viscous equivalent damping k = 50.0 plant_p = (; m, μ, c, k) # We'll just put everything for both models in here ctrl_p = (; kp, ki, kd) plant_ic = (v = 0, x = 0) ctrl_ic = (; x = 0) # Set up and solve sys_p = ( ctrl = (params = ctrl_p, fun = ctrl_fun), plant = (params = plant_p, fun = damping) ) sys_ic = ComponentArray(ctrl = ctrl_ic, plant = plant_ic) sys_fun = ODEFunction(simulator(feedback_sys!; ref), syms = [:u, :v, :x]) sys_prob = ODEProblem(sys_fun, sys_ic, tspan, sys_p) sol = solve(sys_prob, Tsit5()) # Plot t = tspan[1]:0.1:tspan[2] lims = magnitude*[-1, 1] plotvars = plot_v ? [3, 2] : [3] strip = plot(t, ref.(0, 0, t), ylim = 1.2lims, label = "r(t)") plot!(strip, sol, vars = plotvars) phase = plot(ref.(0, 0, t), map(x->x.plant.x, sol(t).u), xlim = lims, ylim = 1.2lims, legend = false, xlabel = "r(t)", ylabel = "x(t)" ) plot(strip, phase, layout = (2, 1), size = (700, 800)) end ``` -------------------------------- ### Importing Necessary Libraries Source: https://docs.sciml.ai/ComponentArrays/stable/examples/DiffEqFlux Imports essential libraries for numerical computation, differential equations, plotting, and optimization. ```julia using ComponentArrays using OrdinaryDiffEq using Plots using UnPack using DiffEqFlux: sciml_train using Flux: glorot_uniform, ADAM using Optim: LBFGS ``` -------------------------------- ### Importing Necessary Libraries Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Imports essential Julia packages for component-based arrays, control systems, differential equations, unpacking, and plotting. ```julia using ComponentArrays using ControlSystems using DifferentialEquations using UnPack using Plots ``` -------------------------------- ### Simulate with Unmodeled Dynamics Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Simulates a system with unmodeled dynamics, demonstrating the robustness of adaptive control. This is useful for testing how well the adaptation law performs when the plant model is not perfectly known. ```julia simulate(truth_sim!, truth_ic; noise_param = 0.2) ``` -------------------------------- ### Indexing with Multiple Symbols to Select and Reorder Components Source: https://docs.sciml.ai/ComponentArrays/stable/indexing_behavior Demonstrates indexing a ComponentArray with a tuple of symbols to select specific components and change their order. ```julia ca[(:c, :a)] ``` -------------------------------- ### Simulate Truth Model with Deterministic Noise Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Simulates the truth plant model with a stationary input and deterministic noise. This demonstrates how parameter drift, caused by insufficient excitation, can lead to system instability. ```julia simulate(truth_sim!, truth_ic; input_signal = 2.0, deterministic_noise = (x, p, t) -> 0.5sin(16.1t), noise_param = nothing, tspan = (0.0, 72.9), adapt_gain = 3.35) ``` -------------------------------- ### Open-loop response of Coulomb friction system Source: https://docs.sciml.ai/ComponentArrays/stable/examples/coulomb_control Simulates and plots the open-loop response of the sliding block with Coulomb friction when a constant input of 5 is applied. Requires defining constants for time span, mass, friction coefficient, and stiffness. ```julia const tspan = (0.0, 30.0) const m = 50.0 const μ = 0.1 const k = 50.0 p = (; m, μ, k) ic = ComponentArray(v = 0, x = 0) ODEProblem(simulator(coulomb_block!, u = 5), ic, tspan, p) |> solve |> plot ``` -------------------------------- ### Applying Inputs with Keyword Arguments Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Defines a utility function `apply_inputs` that allows system inputs to be specified as values or functions of (x, p, t) via keyword arguments. This is useful for integrating control systems into differential equation solvers. ```julia maybe_apply(f::Function, x, p, t) = f(x, p, t) maybe_apply(f, x, p, t) = f function apply_inputs(func; kwargs...) simfun(dx, x, p, t) = func( dx, x, p, t; map(f->maybe_apply(f, x, p, t), (; kwargs...))...) simfun(x, p, t) = func(x, p, t; map(f->maybe_apply(f, x, p, t), (; kwargs...))...) return simfun end ``` -------------------------------- ### Training with ADAM Optimizer Source: https://docs.sciml.ai/ComponentArrays/stable/examples/DiffEqFlux Sets up a callback for visualization and trains the neural ODE model using the ADAM optimizer for a specified number of iterations. ```julia cb = function (θ, loss, pred; doplot = false) display(loss) # plot current prediction against data pl = scatter(t, ode_data[1, :], label = "data") scatter!(pl, t, pred[1, :], label = "prediction") display(plot(pl)) return false end cb(θ, loss_n_ode(θ)...) data = Iterators.repeated((), 1000) res1 = sciml_train(loss_n_ode, θ, ADAM(0.05); cb, maxiters = 100) cb(res1.minimizer, loss_n_ode(res1.minimizer)...; doplot = true) ``` -------------------------------- ### Simulate and Plot Step Response Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Generate and plot the step response for both the truth plant and the nominal plant to compare their dynamics. Requires `truth_ic` and `nominal_ic` to be defined. ```julia step_p = plot(solve(ODEProblem(apply_inputs(truth_sim!; u = 1), truth_ic, (0.0, 10.0))); vars = 1, label = "truth model") plot!(step_p, solve(ODEProblem(apply_inputs(nominal_sim!; u = 1), nominal_ic, (0.0, 10.0))); vars = 1, label = "nominal model") ``` -------------------------------- ### Run Simulation with Noise Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Executes the simulation function with specified plant model and initial conditions, including deterministic noise. This is used to generate simulation results for analysis. ```julia simulate(nominal_sim!, nominal_ic; noise_param = 0.2) ``` -------------------------------- ### Using @view Macro for Slicing Behavior Source: https://docs.sciml.ai/ComponentArrays/stable/indexing_behavior Shows how to use the `@view` macro to ensure that indexing operations create views rather than slices, allowing modifications to affect the original array. ```julia @view(ca[:b])[1] = 0 ca ``` -------------------------------- ### Define Reference Model Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Create a reference model transfer function and its simulator. This defines the desired system behavior. ```julia am = 3 bm = 3 ref_model = bm / (s + am) ref_sim! = SISO_simulator(ref_model) ``` -------------------------------- ### ComponentArrays.PartitionedAxis Source: https://docs.sciml.ai/ComponentArrays/stable/api Defines an Axis for creating arrays of ComponentArrays, specifying a partition size and an index map. ```APIDOC ## `ComponentArrays.PartitionedAxis` — Type ### Description Axis for creating arrays of `ComponentArray`s. ### Usage ```julia pa = PartitionedAxis(partition_size, index_map) ``` ``` -------------------------------- ### Constructing and Modifying ComponentArrays Source: https://docs.sciml.ai/ComponentArrays/stable/quickstart Demonstrates the creation of 1D ComponentArrays using NamedTuple-like syntax and shows how to modify individual elements and nested fields. Also shows how to collect the array into a flat vector. ```julia julia> c = (a = 2, b = [1, 2]); julia> x = ComponentArray(a = 1.0, b = [2, 1, 4], c = c) ComponentVector{Float64}(a = 1.0, b = [2.0, 1.0, 4.0], c = (a = 2.0, b = [1.0, 2.0])) julia> x.c.a = 400; x ComponentVector{Float64}(a = 1.0, b = [2.0, 1.0, 4.0], c = (a = 400.0, b = [1.0, 2.0])) julia> x[5] 400.0 julia> collect(x) 7-element Vector{Float64}: 1.0 2.0 1.0 4.0 400.0 1.0 2.0 julia> typeof(similar(x, Int32)) === typeof(ComponentVector{Int32}(a = 1, b = [2, 1, 4], c = c)) true ``` -------------------------------- ### Unpacking ComponentArray to StaticArrays Source: https://docs.sciml.ai/ComponentArrays/stable/static_unpack Demonstrates the usage of the @static_unpack macro. Use this when you need to perform operations on small array components of a ComponentArray and want to leverage the performance benefits of StaticArrays by avoiding heap allocations. ```julia julia> x = ComponentVector(a = 5, b = [4, 1], c = [1 2; 3 4], d = (e = 2, f = [6, 30.0])); julia> @static_unpack a, b, c, d = x; julia> a 5.0 julia> b 2-element SVector{2, Float64} with indices SOneTo(2): 4.0 1.0 julia> c 2×2 SMatrix{2, 2, Float64, 4} with indices SOneTo(2)×SOneTo(2): 1.0 2.0 3.0 4.0 julia> d ComponentVector{Float64,SubArray...}(e = 2.0, f = [6.0, 30.0]) ``` -------------------------------- ### Creating SISO Simulator from Transfer Function Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Provides a function `SISO_simulator` to create a derivative function for a Single-Input Single-Output (SISO) state-space system, suitable for use with differential equation solvers. It converts a `TransferFunction` or `StateSpace` object into a form where the first state variable represents the system's output. ```julia SISO_simulator(P::TransferFunction) = SISO_simulator(ss(P)) function SISO_simulator(P::AbstractStateSpace) @unpack A, B, C, D = P if size(D)!=(1, 1) error("This is not a SISO system") end # Put into transposed observer canonical form so the first element is also the y value BB = reverse(vec(C)) CC = reverse(vec(B))' DD = D[1, 1] return function sim!(dx, x, p, t; u = 0.0) dx .= A*x + BB*u return CC*x + DD*u end end ``` -------------------------------- ### Closed-loop response with PID control Source: https://docs.sciml.ai/ComponentArrays/stable/examples/coulomb_control Simulates and plots the closed-loop response of the system using a PID controller and the Coulomb friction block. Sets up the parameters for both the controller and the plant, and defines the initial conditions. ```julia p = ( ctrl = ( params = (kp = 13, ki = 12, kd = 5), fun = PID_controller! ), plant = ( params = plant_p, fun = coulomb_block! ) ) ic = ComponentArray(ctrl = (; x = 0), plant = plant_ic) sol = ODEProblem(simulator(feedback_sys!, ref = 10), ic, tspan, p) |> solve plot(sol, vars = 3) ``` -------------------------------- ### Feedback System Simulation Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Simulates a feedback control loop, calculating control signals, plant output, tracking error, and updating parameters. Assumes `vars` contains `parameter_estimates`, `plant_model`, and `sensor`, and `p` contains `gamma` and `plant_fun`. ```julia function feedback_sys!(D, vars, p, t; ym, r, n) @unpack parameter_estimates, plant_model, sensor = vars γ = p.gamma regressor = [r, plant_model[1]] u = control(parameter_estimates, regressor) yp = p.plant_fun(D.plant_model, plant_model, (), t; u = yp[1]) ŷ = sensor_sim!(D.sensor, sensor, (), t; u = yp[1]) + n e = ŷ .- ym regressor[2] = ŷ adapt!(D.parameter_estimates, parameter_estimates, γ, t; e, w = regressor) return yp end ``` -------------------------------- ### Multi-Symbol Indexing with Tuple vs. Array Source: https://docs.sciml.ai/ComponentArrays/stable/indexing_behavior Compares multi-symbol indexing using a tuple of symbols versus an array of symbols, showing they yield equivalent results. ```julia ca[[:c, :a]] == ca[(:c, :a)] ``` -------------------------------- ### Import necessary libraries Source: https://docs.sciml.ai/ComponentArrays/stable/examples/coulomb_control Imports required Julia packages for component arrays, differential equations, interactive manipulation, parameter handling, and plotting. ```julia using ComponentArrays using DifferentialEquations using Interact: @manipulate using Parameters: @unpack using Plots ``` -------------------------------- ### Using @view with Multiple Symbols Source: https://docs.sciml.ai/ComponentArrays/stable/indexing_behavior Applies the `@view` macro to multi-symbol indexing to create a view of selected and reordered components. ```julia @view ca[(:c, :a)] ``` -------------------------------- ### Simulate and Plot Sine Response Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Generate and plot the sine response for both the truth plant and the nominal plant. Requires `truth_ic` and `nominal_ic` to be defined. ```julia u = (x, p, t) -> sin(3t) sin_p = plot(solve(ODEProblem(apply_inputs(truth_sim!; u), truth_ic, (0.0, 10.0))); vars = 1, label = "truth model") plot!(sin_p, solve(ODEProblem(apply_inputs(nominal_sim!; u), nominal_ic, (0.0, 10.0))); vars = 1, label = "nominal model") ``` -------------------------------- ### Solve Composed ODE Problem and Unit Test Component Source: https://docs.sciml.ai/ComponentArrays/stable/examples/ODE_jac Solves the composed ODE problem and demonstrates how to solve and obtain results for a single component system (Lotka-Volterra) for unit testing purposes. ```julia ## Solve problem # We can solve the composed system... comp_sol = solve(comp_prob, Rodas5()) # ...or we can unit test one of the component systems lotka_sol = solve(lotka_prob, Rodas5()) ``` -------------------------------- ### Create and Access ComponentArray with Axis Source: https://docs.sciml.ai/ComponentArrays/stable/api Demonstrates creating a ComponentArray with a complex Axis structure and accessing its nested components. ```julia using ComponentArrays ax = Axis((a = 1, b = ViewAxis(2:7, PartitionedAxis(2, (a = 1, b = 2))), c = ViewAxis(8:10, (a = 1, b = 2:3)))) A = [100, 4, 1.3, 1, 1, 4.4, 0.4, 2, 1, 45] ca = ComponentArray(A, ax) ca.a ca.b ca.c ca.c.b ``` -------------------------------- ### Input signal generators Source: https://docs.sciml.ai/ComponentArrays/stable/examples/coulomb_control Provides functions to generate step and sine wave inputs for the system. These are used to define the reference trajectory. ```julia step_input(; time = 1.0, mag = 1.0) = (x, p, t) -> t>time ? mag : 0 sine_input(; mag = 1.0, period = 10.0) = (x, p, t) -> mag*sin(t*2π/period) ``` -------------------------------- ### Create a ComponentArray Source: https://docs.sciml.ai/ComponentArrays/stable/indexing_behavior Initializes a ComponentArray with named scalar and vector components. ```julia using ComponentArrays ca = ComponentArray(a = 5, b = [4, 1]) ``` -------------------------------- ### Construct ComponentArray from NamedTuple or Keyword Arguments Source: https://docs.sciml.ai/ComponentArrays/stable/api Shows how to create a ComponentArray using a NamedTuple or keyword arguments, providing struct-like access to elements. ```julia using ComponentArrays x = ComponentArray(a = 1, b = [2, 1, 4], c = (a = 2, b = [1, 2])) x.c.a = 400 x x[5] collect(x) ``` -------------------------------- ### Constructing ComponentArrays from Existing Arrays Source: https://docs.sciml.ai/ComponentArrays/stable/quickstart Shows how to create a new ComponentArray by copying an existing one and overriding or adding fields. Note that nested fields cannot be changed directly during this construction. ```julia julia> v = ComponentVector(a = 1, b = 2, c = 3); julia> ComponentVector(v; a = 11, new = 42) ComponentVector{Int64}(a = 11, b = 2, c = 3, new = 42) ``` -------------------------------- ### PID controller implementation Source: https://docs.sciml.ai/ComponentArrays/stable/examples/coulomb_control Implements a Proportional-Integral-Derivative (PID) controller. This function calculates the control output based on the error, its integral, and its derivative. ```julia function PID_controller!(D, vars, p, t; err = 0.0, v = 0.0) @unpack kp, ki, kd = p @unpack x = vars D.x = ki*err return x + kp*err + kd*v end ``` -------------------------------- ### Define Nominal Plant Model Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Specify the nominal plant model and its simulator. This represents the known structure for the adaptation law. ```julia ap = 1 bp = 2 nominal_plant = bp / (s + ap) nominal_sim! = SISO_simulator(nominal_plant) ``` -------------------------------- ### Sliding block with Coulomb friction model Source: https://docs.sciml.ai/ComponentArrays/stable/examples/coulomb_control Defines the differential equations for a sliding block system with Coulomb friction. Includes a deadzone to aid simulation stability when velocity and acceleration are near zero. ```julia # Sliding block with coulomb friction function coulomb_block!(D, vars, p, t; u = 0.0) @unpack m, μ, k = p @unpack v, x = vars D.x = v a = -μ*g*softsign(v) + k*(u-x)/m D.v = abs(a)<1e-3 && abs(v)<1e-3 ? -10v : a #deadzone to help the simulation return x end ``` -------------------------------- ### Creating a Dense Neural Layer Component Source: https://docs.sciml.ai/ComponentArrays/stable/examples/DiffEqFlux Defines a function to create a dense layer component using ComponentArrays, initializing weights and biases. ```julia dense_layer(in, out) = ComponentArray{Float32}(W = glorot_uniform(out, in), b = zeros(out)) ``` -------------------------------- ### Define Sensor Dynamics Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Create a first-order sensor dynamics transfer function and its simulator. This is used for adding noise to measurements. ```julia τ = 0.005 sensor_plant = 1 / (τ*s + 1) sensor_sim! = SISO_simulator(sensor_plant) ``` -------------------------------- ### Define Truth Plant with Unmodeled Dynamics Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Construct the truth plant model by incorporating unmodeled dynamics. This represents the actual system behavior. ```julia unmodeled_dynamics = 229/(s^2 + 30s + 229) truth_plant = nominal_plant * unmodeled_dynamics truth_sim! = SISO_simulator(truth_plant) ``` -------------------------------- ### Creating Higher Dimensional ComponentArrays Source: https://docs.sciml.ai/ComponentArrays/stable/quickstart Illustrates the creation of a 2D ComponentArray (ComponentMatrix) through broadcasted operations on a 1D ComponentArray. This is useful for automatic dimension expansion in modeling contexts. ```julia julia> x2 = x .* x' 7×7 ComponentMatrix{Float64} with axes Axis(a = 1, b = ViewAxis(2:4, Shaped1DAxis((3,))), c = ViewAxis(5:7, Axis(a = 1, b = ViewAxis(2:3, Shaped1DAxis((2,)))))) × Axis(a = 1, b = ViewAxis(2:4, Shaped1DAxis((3,))), c = ViewAxis(5:7, Axis(a = 1, b = ViewAxis(2:3, Shaped1DAxis((2,)))))) 1.0 2.0 1.0 4.0 400.0 1.0 2.0 2.0 4.0 2.0 8.0 800.0 2.0 4.0 1.0 2.0 1.0 4.0 400.0 1.0 2.0 4.0 8.0 4.0 16.0 1600.0 4.0 8.0 400.0 800.0 400.0 1600.0 160000.0 400.0 800.0 1.0 2.0 1.0 4.0 400.0 1.0 2.0 2.0 4.0 2.0 8.0 800.0 2.0 4.0 julia> x2[:c, :c] 3×3 ComponentMatrix{Float64} with axes Axis(a = 1, b = ViewAxis(2:3, Shaped1DAxis((2,)))) × Axis(a = 1, b = ViewAxis(2:3, Shaped1DAxis((2,)))) 160000.0 400.0 800.0 400.0 1.0 2.0 800.0 2.0 4.0 julia> x2[:a, :a] 1.0 julia> x2[:a, :c] ComponentVector{Float64}(a = 400.0, b = [1.0, 2.0]) julia> x2[:b, :c] 3×3 ComponentMatrix{Float64} with axes Shaped1DAxis((3,)) × Axis(a = 1, b = ViewAxis(2:3, Shaped1DAxis((2,)))) 800.0 2.0 4.0 400.0 1.0 2.0 1600.0 4.0 8.0 ``` -------------------------------- ### KeepIndex with Integer Index Source: https://docs.sciml.ai/ComponentArrays/stable/indexing_behavior Shows how `KeepIndex` can be used with a single integer index to capture a full component and retain its name. ```julia ca[KeepIndex(1)] ``` -------------------------------- ### Defining the Parameter ComponentArray Source: https://docs.sciml.ai/ComponentArrays/stable/examples/DiffEqFlux Constructs a ComponentArray to hold the initial conditions and the parameters of the dense neural layers. ```julia layers = (L1 = dense_layer(2, 50), L2 = dense_layer(50, 2)) θ = ComponentArray(u = u0, p = layers) ``` -------------------------------- ### Sliding block with viscous friction model Source: https://docs.sciml.ai/ComponentArrays/stable/examples/coulomb_control Defines the differential equations for a sliding block system incorporating viscous friction. Use this for systems where damping is proportional to velocity. ```julia # Sliding block with viscous friction function viscous_block!(D, vars, p, t; u = 0.0) @unpack m, c, k = p @unpack v, x = vars D.x = v D.v = (-c*v + k*(u-x))/m return x end ``` -------------------------------- ### Feedback system definition Source: https://docs.sciml.ai/ComponentArrays/stable/examples/coulomb_control Defines the overall feedback system, combining a controller and a plant model. It calculates the control input `u` and applies it to the plant. ```julia function feedback_sys!(D, components, p, t; ref = 0.0) @unpack ctrl, plant = components u = p.ctrl.fun(D.ctrl, ctrl, p.ctrl.params, t; err = ref-plant.x, v = -plant.v) return p.plant.fun(D.plant, plant, p.plant.params, t; u) end ``` -------------------------------- ### KeepIndex with Full Range Index Source: https://docs.sciml.ai/ComponentArrays/stable/indexing_behavior Demonstrates using `KeepIndex` with a range that spans multiple components, retaining the names of the components that are fully captured. ```julia ca[KeepIndex(1:3)] ``` -------------------------------- ### Indexing Behavior: Slicing vs. In-place Update Source: https://docs.sciml.ai/ComponentArrays/stable/indexing_behavior Illustrates that indexing with symbols slices the array, creating a copy, while direct assignment to a component updates in-place. This highlights the default slicing behavior of ComponentArrays. ```julia ca[:b][1] = 0 ca[:a] = 0 ca ``` -------------------------------- ### Gradient Descent Adaptation Law Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Implements the gradient descent adaptation law for updating parameters. Requires `e` (error) and `w` (regressor vector) as keyword arguments. ```julia function adapt!(Dθ, θ, γ, t; e, w) Dθ .= -γ*e*w return nothing end ``` -------------------------------- ### Create a ComponentArray with Nested Structure Source: https://docs.sciml.ai/ComponentArrays/stable/indexing_behavior Initializes a ComponentArray with nested named components, including a nested tuple. ```julia ca = ComponentArray(a = 5, b = [4, 1], c = (a = 2, b = [6, 30.0])) ``` -------------------------------- ### Define simulator utility function Source: https://docs.sciml.ai/ComponentArrays/stable/examples/coulomb_control A utility function to wrap simulation functions, allowing for optional application of functions based on their type. ```julia const g = 9.80665 maybe_apply(f::Function, x, p, t) = f(x, p, t) maybe_apply(f, x, p, t) = f # Applies functions of form f(x,p,t) to be applied and passed in as inputs function simulator(func; kwargs...) simfun(dx, x, p, t) = func( dx, x, p, t; map(f->maybe_apply(f, x, p, t), (; kwargs...))...) simfun(x, p, t) = func(x, p, t; map(f->maybe_apply(f, x, p, t), (; kwargs...))...) return simfun end softsign(x) = tanh(1e3x) ``` -------------------------------- ### Combine and Plot Responses Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Combine the step and sine response plots into a single figure for comparison. Assumes `step_p` and `sin_p` are defined. ```julia plot(step_p, sin_p; layout = (2, 1), size = (800, 800)) ``` -------------------------------- ### Calculate equivalent viscous damping Source: https://docs.sciml.ai/ComponentArrays/stable/examples/coulomb_control Calculates the equivalent viscous damping coefficient for a system, based on Coulomb friction parameters. This is useful for comparing different friction models. ```julia # Equivalent viscous damping coefficient taken from: # https://engineering.purdue.edu/~deadams/ME563/lecture2010.pdf visc_equiv(μ, N, ω, mag) = 4*μ*N/(π*ω*mag) ``` -------------------------------- ### KeepIndex with Range Index Source: https://docs.sciml.ai/ComponentArrays/stable/indexing_behavior Illustrates using `KeepIndex` with a range to capture multiple elements and retain the names of any full components captured within the range. ```julia ca[KeepIndex(2:3)] ``` -------------------------------- ### Define Lotka-Volterra System ODE and Jacobian Source: https://docs.sciml.ai/ComponentArrays/stable/examples/ODE_jac Defines the ODE function `lotka!` and its Jacobian function `lotka_jac!` for the Lotka-Volterra system using ComponentArrays. The Jacobian function uses symbolic indexing. ```julia ## Lotka-Volterra system function lotka!(D, u, p, t; f = 0.0) @unpack α, β, γ, δ = p @unpack x, y = u D.x = α*x - β*x*y + f D.y = -γ*y + δ*x*y return nothing end function lotka_jac!(D, u, p, t) @unpack α, β, γ, δ = p @unpack x, y = u D[:x, :x] = α - β*y D[:x, :y] = -β*x D[:y, :x] = δ*y D[:y, :y] = -γ + δ*x return nothing end lotka_p = (α = 2/3, β = 4/3, γ = 1.0, δ = 1.0) lotka_ic = ComponentArray(x = 1.0, y = 1.0) lotka_fun = ODEFunction(lotka!, jac = lotka_jac!) lotka_prob = ODEProblem(lotka_fun, lotka_ic, tspan, lotka_p) ``` -------------------------------- ### Define Laplace Variable Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Initialize the Laplace variable 's' for use in transfer function definitions. ```julia s = tf("s") ``` -------------------------------- ### Access and Modify ComponentArray Properties using Dot Notation Source: https://docs.sciml.ai/ComponentArrays/stable/indexing_behavior Demonstrates accessing and modifying components of a ComponentArray using dot notation, similar to structs or NamedTuples. ```julia ca.b[1] = 99 ca.a = 22 ca ``` -------------------------------- ### Define Lorenz System ODE and Jacobian Source: https://docs.sciml.ai/ComponentArrays/stable/examples/ODE_jac Defines the ODE function `lorenz!` and its Jacobian function `lorenz_jac!` for the Lorenz system using ComponentArrays. The Jacobian function uses symbolic indexing for clarity. ```julia using ComponentArrays using DifferentialEquations using Parameters: @unpack tspan = (0.0, 20.0) ## Lorenz system function lorenz!(D, u, p, t; f = 0.0) @unpack σ, ρ, β = p @unpack x, y, z = u D.x = σ*(y - x) D.y = x*(ρ - z) - y - f D.z = x*y - β*z return nothing end function lorenz_jac!(D, u, p, t) @unpack σ, ρ, β = p @unpack x, y, z = u D[:x, :x] = -σ D[:x, :y] = σ D[:y, :x] = ρ D[:y, :y] = -1 D[:y, :z] = -x D[:z, :x] = y D[:z, :y] = x D[:z, :z] = -β return nothing end lorenz_p = (σ = 10.0, ρ = 28.0, β = 8/3) lorenz_ic = ComponentArray(x = 0.0, y = 0.0, z = 0.0) lorenz_fun = ODEFunction(lorenz!, jac = lorenz_jac!) lorenz_prob = ODEProblem(lorenz_fun, lorenz_ic, tspan, lorenz_p) ``` -------------------------------- ### Retain Component Labels using KeepIndex Wrapper Source: https://docs.sciml.ai/ComponentArrays/stable/indexing_behavior Demonstrates using the `KeepIndex` wrapper to index into a ComponentArray and retain the component name, returning a ComponentVector instead of a plain array. ```julia ca[KeepIndex(:b)] ``` -------------------------------- ### Create a Nested ComponentArray Source: https://docs.sciml.ai/ComponentArrays/stable/indexing_behavior Initializes a ComponentArray with a deeply nested structure, including named scalar and vector components within a nested tuple. ```julia ca = ComponentArray(a = 5, b = [4, 1], c = (a = 2, b = [6, 30])) ``` -------------------------------- ### Control Law Definition Source: https://docs.sciml.ai/ComponentArrays/stable/examples/adaptive_control Defines the basic control law equation. ```julia control(θ, w) = θ'w ``` -------------------------------- ### ComponentArrays.valkeys Source: https://docs.sciml.ai/ComponentArrays/stable/api Returns `Val`-wrapped keys of `ComponentVector` for fast iteration over component keys. Also works directly on an `AbstractAxis`. ```APIDOC ## valkeys(x) ### Description Returns `Val`-wrapped keys of `ComponentVector` for fast iteration over component keys. Also works directly on an `AbstractAxis`. ### Parameters #### Path Parameters - **x** (ComponentVector or AbstractAxis) - The ComponentVector or AbstractAxis to get keys from. ### Response #### Success Response - Returns an iterable collection of `Val`-wrapped keys. ``` -------------------------------- ### Prediction and Loss Function Source: https://docs.sciml.ai/ComponentArrays/stable/examples/DiffEqFlux Defines functions to predict ODE solutions using the neural network and to calculate the loss based on the difference from true data. ```julia predict_n_ode(θ) = Array(solve(prob, Tsit5(), u0 = θ.u, p = θ.p, saveat = t)) function loss_n_ode(θ) pred = predict_n_ode(θ) loss = sum(abs2, ode_data .- pred) return loss, pred end loss_n_ode(θ) ``` -------------------------------- ### Define Composed System ODE and Jacobian Source: https://docs.sciml.ai/ComponentArrays/stable/examples/ODE_jac Defines the ODE function `composed!` and its Jacobian function `composed_jac!` for a system composed of Lorenz and Lotka-Volterra models using ComponentArrays. It demonstrates how to handle nested ComponentArrays and partial derivatives. ```julia ## Composed Lorenz and Lotka-Volterra system function composed!(D, u, p, t) c = p.c #coupling parameter @unpack lorenz, lotka = u lorenz!(D.lorenz, lorenz, p.lorenz, t, f = c*lotka.x) lotka!(D.lotka, lotka, p.lotka, t, f = c*lorenz.x) return nothing end function composed_jac!(D, u, p, t) c = p.c @unpack lorenz, lotka = u lorenz_jac!(@view(D[:lorenz, :lorenz]), lorenz, p.lorenz, t) lotka_jac!(@view(D[:lotka, :lotka]), lotka, p.lotka, t) @view(D[:lorenz, :lotka])[:y, :x] = -c @view(D[:lotka, :lorenz])[:x, :x] = c return nothing end comp_p = (lorenz = lorenz_p, lotka = lotka_p, c = 0.01) comp_ic = ComponentArray(lorenz = lorenz_ic, lotka = lotka_ic) comp_fun = ODEFunction(composed!, jac = composed_jac!) comp_prob = ODEProblem(comp_fun, comp_ic, tspan, comp_p) ``` -------------------------------- ### KeepIndex with Range to End Source: https://docs.sciml.ai/ComponentArrays/stable/indexing_behavior Shows how `KeepIndex` with a range to the end captures elements and retains the names of the components fully included in the range. ```julia ca[KeepIndex(2:end)] ``` -------------------------------- ### ComponentArrays.ViewAxis Source: https://docs.sciml.ai/ComponentArrays/stable/api An Axis used for creating arrays of ComponentArrays, specifying a parent index and an index map. ```APIDOC ## `ComponentArrays.ViewAxis` — Type ### Description Axis for creating arrays of `ComponentArray`s. ### Usage ```julia va = ViewAxis(parent_index, index_map) ``` ```