use oxictl::passivity::{IdaPbcConfig, IdaPbcController};
const J_PLANT: [[f64; 2]; 2] = [[0.0, 1.0], [-1.0, 0.0]];
const R_PLANT: [[f64; 2]; 2] = [[0.0, 0.0], [0.0, 0.1]];
const G_PLANT: [[f64; 1]; 2] = [[0.0], [1.0]];
fn grad_h_plant(x: &[f64; 2]) -> [f64; 2] {
[x[0], x[1]]
}
fn grad_hd(x: &[f64; 2]) -> [f64; 2] {
[x[0] - 1.0, x[1]]
}
fn hamiltonian(x: [f64; 2]) -> f64 {
0.5 * (x[0] * x[0] + x[1] * x[1])
}
fn hamiltonian_d(x: [f64; 2]) -> f64 {
let dq = x[0] - 1.0;
0.5 * (dq * dq + x[1] * x[1])
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== IDA-PBC: Mass-Spring-Damper Closed-Loop Simulation ===\n");
println!(" Plant: m=1, k=1, b=0.1");
println!(" Design: desired equilibrium q* = 1.0, added damping r_a = 1.0");
println!(" Start: q=0.0, p=0.0");
println!();
let j_desired: [[f64; 2]; 2] = [[0.0, 1.0], [-1.0, 0.0]];
let r_desired: [[f64; 2]; 2] = [[0.0, 0.0], [0.0, 1.1]];
let ida_config = IdaPbcConfig::<f64, 2, 1>::new(j_desired, r_desired, grad_hd)
.expect("IDA-PBC config should be valid: J_d skew-symmetric, R_d PSD");
let controller = IdaPbcController::new(ida_config);
let dt = 0.05_f64;
let mut x: [f64; 2] = [0.0, 0.0];
println!(
"{:>6} {:>12} {:>12} {:>14} {:>14}",
"step", "position_q", "velocity_p", "H (plant)", "H_d (desired)"
);
println!("{}", "-".repeat(66));
for step in 0..=20_usize {
let h_val = hamiltonian(x);
let hd_val = hamiltonian_d(x);
println!(
"{:>6} {:>12.6} {:>12.6} {:>14.6} {:>14.6}",
step, x[0], x[1], h_val, hd_val
);
if step == 20 {
break;
}
let u = controller
.compute_with_closures(&J_PLANT, &R_PLANT, &G_PLANT, grad_h_plant, &x)
.map_err(|e| format!("IDA-PBC compute failed: {}", e))?;
let grad_h = grad_h_plant(&x);
let jr00 = J_PLANT[0][0] - R_PLANT[0][0];
let jr01 = J_PLANT[0][1] - R_PLANT[0][1];
let jr10 = J_PLANT[1][0] - R_PLANT[1][0];
let jr11 = J_PLANT[1][1] - R_PLANT[1][1];
let jrg_q = jr00 * grad_h[0] + jr01 * grad_h[1];
let jrg_p = jr10 * grad_h[0] + jr11 * grad_h[1];
let gu_q = G_PLANT[0][0] * u[0];
let gu_p = G_PLANT[1][0] * u[0];
let xdot = [jrg_q + gu_q, jrg_p + gu_p];
x = [x[0] + dt * xdot[0], x[1] + dt * xdot[1]];
}
let h_final = hamiltonian(x);
let hd_final = hamiltonian_d(x);
let err_q = (x[0] - 1.0).abs();
let err_p = x[1].abs();
println!("\n=== Summary ===");
println!(
"Final position q: {:.6} (target q* = 1.0, error = {:.6})",
x[0], err_q
);
println!(
"Final momentum p: {:.6} (target p* = 0.0, error = {:.6})",
x[1], err_p
);
println!("Final H (plant): {:.6}", h_final);
println!(
"Final H_d (desired): {:.6} (minimum = 0.0 at equilibrium)",
hd_final
);
if err_q < 0.1 && err_p < 0.1 {
println!("Result: [CONVERGED] state within 0.1 of desired equilibrium");
} else {
println!("Result: [CONVERGING] extend simulation for full convergence");
}
Ok(())
}