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, ...).

98 lines (85 loc) 3.97 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/dae_single_shooting.py. // // Direct single shooting on a small DAE OCP. // // minimize integral_{0}^{10} x0^2 + x1^2 + u^2 dt // s.t. dot(x0) == z*x0 - x1 + u // dot(x1) == x0 // 0 == x1^2 + z - 1 // x0(0)=0, x1(0)=1, x0(10)=x1(10)=0, -0.75 <= u <= 1 // // JS notes (see README.md): // * The Python version uses the "idas" DAE integrator, which aborts the // wasm runtime. We substitute "collocation", which handles the implicit // algebraic equation and produces an equivalent solution. // * The Python `ma27` linear solver option is dropped (default mumps used). async function example(M, log) { // Declare variables const x = M.SX.sym("x", 2); // Differential states const z = M.SX.sym("z"); // Algebraic variable const u = M.SX.sym("u"); // Control const [x0e, x1e] = M.vertsplit(x); // Differential equation const f_x = M.vertcat(M.plus(M.minus(M.times(z, x0e), x1e), u), x0e); // Algebraic equation const f_z = M.minus(M.plus(M.times(x1e, x1e), z), 1); // Lagrange cost term (quadrature) const f_q = M.plus(M.plus(M.times(x0e, x0e), M.times(x1e, x1e)), M.times(u, u)); // Create an integrator (interval length 0.5s) -- "collocation" stands in for "idas" const dae = { x: x, z: z, p: u, ode: f_x, alg: f_z, quad: f_q }; const I = M.integrator("I", "collocation", dae, 0, 0.5); // All controls const U = M.MX.sym("U", 20); const Us = M.vertsplit(U); // Construct graph of integrator calls let X = M.MX([0, 1]); let J = M.MX(0); for (let k = 0; k < 20; k++) { const Ik = I.call({ x0: X, p: Us[k] }); X = Ik["xf"]; J = M.plus(J, Ik["qf"]); // Sum up quadratures } // Allocate an NLP solver const nlp = { x: U, f: J, g: X }; const solver = M.nlpsol("solver", "ipopt", nlp); // Pass bounds, initial guess and solve NLP const sol = solver.call({ lbx: -0.75, // Lower variable bound ubx: 1.0, // Upper variable bound lbg: 0.0, // Lower constraint bound ubg: 0.0, // Upper constraint bound x0: 0.0, // Initial guess }); log("-----"); log("objective at solution = " + sol["f"].nonzeros().join(" ")); log("u_opt = " + sol["x"].nonzeros().map((v) => v.toFixed(4)).join(" ")); log("terminal state x(10) = " + sol["g"].nonzeros().map((v) => v.toExponential(2)).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;