UNPKG

@casadi/casadi-wasm

Version:

CasADi — symbolic framework for algorithmic differentiation and numerical optimization, compiled to WebAssembly. Runs in Node.js with on-demand solver plugins (ipopt, fatrop, sundials, ...).

97 lines (82 loc) 4.01 kB
// // MIT No Attribution // // Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl, KU Leuven. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // // JS port of docs/examples/python/multipoint_simulation.py. // // Simulate a Van der Pol ODE over a grid, accumulating the integral of // the sum-of-squares output via the integrator's quadrature. // // JS notes (see README.md): // * cvodes is not available in the wasm build; we substitute the "rk" // (fixed-step Runge-Kutta) integrator, which handles this ODE fine. // * numpy/matplotlib output is dropped; we log the trajectories. async function example(M, log) { // Nonlinear system with two states and one control input const x1 = M.MX.sym("x1"); const x2 = M.MX.sym("x2"); const x = M.vertcat(x1, x2); const u = M.MX.sym("u"); const xdot = M.vertcat(M.plus(M.minus(M.times(M.minus(1, M.times(x2, x2)), x1), x2), u), x1); // Sum-of-squares distance from the origin const y = M.plus(M.times(x1, x1), M.times(x2, x2)); // Time horizon, discretization const N = 10; const T = 10.0; const tgrid = []; for (let k = 0; k <= N; k++) tgrid.push((T * k) / N); // Integrate, also calculating the integral of y (substitute rk for cvodes) const dae = { x: x, u: u, ode: xdot, quad: y }; const F = M.integrator("F", "rk", dae, tgrid[0], tgrid.slice(1), { simplify: true }); // Pointwise values of y const yfun = new M.Function("yfun", [x], [y], ["x"], ["y"]); // Initial conditions and piecewise-constant controls const uvals = []; for (let k = 0; k < N; k++) uvals.push(-1 + (2 * k) / (N - 1)); const x0 = M.DM([0, 0]); // Simulate const Fk = F.call({ x0: x0, u: M.DM([uvals]) }); let xf = Fk["xf"]; let qf = Fk["qf"]; // Prepend the state at the initial time xf = M.horzcat(x0, xf); qf = M.horzcat(M.DM(0), qf); // y at each grid point const yf = yfun.call([xf])[0]; const x_sim = xf.nonzeros(); // column-major: [x1_0,x2_0,x1_1,x2_1,...] const y_sim = yf.nonzeros(); const q_sim = qf.nonzeros(); log("-----"); log("x1 = " + x_sim.filter((_, i) => i % 2 === 0).map((v) => v.toFixed(4)).join(" ")); log("x2 = " + x_sim.filter((_, i) => i % 2 === 1).map((v) => v.toFixed(4)).join(" ")); log("y = " + y_sim.map((v) => v.toFixed(4)).join(" ")); log("integral of y (quadrature) = " + q_sim.map((v) => v.toFixed(4)).join(" ")); // Compare integral with a Riemann sum of the pointwise y values let acc = 0; const cum = y_sim.map((v) => (acc += (T / N) * v)); log("integral of y (cumsum) = " + cum.map((v) => v.toFixed(4)).join(" ")); } if (typeof require !== "undefined" && typeof module !== "undefined" && require.main === module) { const path = require("path"); const casadiPath = process.env.CASADI_JS || path.resolve(__dirname, "../../../build-wasm/swig/wasm-js/casadi.js"); require(casadiPath)() .then((M) => example(M, (...a) => console.log(...a))) .catch((e) => { console.error("FATAL:", e.message || e); process.exit(1); }); } if (typeof module !== "undefined" && module.exports) module.exports = example;