How to interface symbolic solutions with numeric ode solvers

6 views (last 30 days)
Hello Community,
My question is of an optimisation nature, and as the title suggests, I have been having difficulty getting results in acceptable time when finding the solution to a dynamical system using the symbolic toolbox and then applying it to an ode solver.
I have copied and pasted the equations of motion, in symbolic form, then substituted parameters, and then solved for the second derivatives and stored this as 'solution', i.e. solution is a symbolic structure which contains the solutions to the second derivatives for the physical system.
When I use the function 'subs' on my solution variable within my derivative function that gets passed to ode45 the issue is that the software spends all it's time within the environment of the symbolic toolbox and getting a time series for 50 seconds takes >20 minutes, which is ridiculous (confirmed with the profiler).
As a work around I have defined a structure consisting of functions that have been converted from the solutions into function handles, and I pass this structure to the ode solver so that the second derivatives can be found outside of the symbolic toolbox and this has made the simulation at least feasible (in the order of 3mins). However when I look at the profiler I can see that the functions themselves are using the symbolic toolbox, even though they contain no symbols - specifically they are using symengine. They have no reason to use the symengine since they have no symbolic variables (all their symbolic variables are converted to function variables with matlabFunction)
eom = subs(euler_equations+generalised_dissipation-generalised_drag,parameters);
sol = solve(eom, [x_dd phi_dd theta_dd psi_dd]);
solution.x_dd = matlabFunction(sol.x_dd); % @(Qdrag_x,Qdrag_psi,Qdrag_theta,phi_d,psi_,psi_d,theta,theta_d)
solution.phi_dd = matlabFunction(sol.phi_dd); % @(Qdrag_phi,phi_d,theta_d)Qdrag_phi-phi_d+theta_d
solution.theta_dd = matlabFunction(sol.theta_dd); % @(Qdrag_x,Qdrag_psi,Qdrag_theta,phi_d,psi_,psi_d,theta,theta_d)
solution.psi_dd = matlabFunction(sol.psi_dd); % @(Qdrag_x,Qdrag_psi,Qdrag_theta,phi_d,psi_,psi_d,theta,theta_d)
Where euler_equations, generalised_dissipation and generalised_drag are symbolic variables that describe the system and parameters is a structure containing the values of the parameters.
The symbolic toolbox is still eating up ~1/3 of the simulation time, and I would like to know how to define my functions so that they wont under any circumstance use the symbolic toolbox. I understand that I am have most likely used bad MatLab practise when interfacing between symbolic toolbox and solutions that are applicable to ode45 and I am open to suggestions on how to more optimally achieve my aims.
The code that leads to the simulation is as follows:
X0 = [0;30;0;0;0;0;pi;0];
opts = odeset('RelTol',1e-3,'AbsTol',1e-6,'MaxStep',1e-3);
[t, X] = ode45(@(t,X) unicycleODEs(t, X, solution, inf_drag, parameters), [0,50], X0, opts);
function Xdot = unicycleODEs(t, X, X_dd, inf_drag, parameters)
% Extract state variables from X
x = X(1);
x_d = X(2);
phi = X(3);
phi_d = X(4);
theta = X(5);
theta_d = X(6);
psi_ = X(7);
psi_d = X(8);
% find generalised force of drag
inf_drag_funct = @(chi) inf_drag(chi,psi_,psi_d,theta,theta_d,x_d);
generalised_drag = integral(inf_drag_funct,0,parameters.l,'ArrayValued',true);
% grab components
Qdrag_x = generalised_drag(1);
Qdrag_phi = generalised_drag(2);
Qdrag_theta = generalised_drag(3);
Qdrag_psi = generalised_drag(4);
% get Xdd
% @(Qdrag_x,Qdrag_psi,Qdrag_theta,phi_d,psi_,psi_d,theta,theta_d)
x_dd = X_dd.x_dd(Qdrag_x,Qdrag_psi,Qdrag_theta,phi_d,psi_,psi_d,theta,theta_d);
% @(Qdrag_phi,phi_d,theta_d)
phi_dd = X_dd.phi_dd(Qdrag_phi,phi_d,theta_d);
% @(Qdrag_x,Qdrag_psi,Qdrag_theta,phi_d,psi_,psi_d,theta,theta_d)
theta_dd = X_dd.theta_dd(Qdrag_x,Qdrag_psi,Qdrag_theta,phi_d,psi_,psi_d,theta,theta_d);
% @(Qdrag_x,Qdrag_psi,Qdrag_theta,phi_d,psi_,psi_d,theta,theta_d)
psi_dd = X_dd.psi_dd(Qdrag_x,Qdrag_psi,Qdrag_theta,phi_d,psi_,psi_d,theta,theta_d);
% Assign the derivatives of each state variable to Xdot
Xdot = [x_d; x_dd; phi_d; phi_dd; theta_d; theta_dd; psi_d; psi_dd];
end
For completness, the most time is spent doing a numeric integration of inf_drag at every time step of the derivative function.
In summary, I have gotten the simulation to work but I know that the way I've done it would go against wider wisdom and convention, and this shows in the simulation times. How can I optimise the process, why does symbolic toolbox completely kill simulation times whenever it is found within the derivative function loop?
The system itself is not that complicated and should not take this long to simulate 50 seconds - once this project is done I will likely want to simulate this system over a time span of ~1000, which is not feasible as it stands.
Thank you in advance,
Should you require the full code that includes the equations of motion I would be happy to provide it.

Accepted Answer

Star Strider
Star Strider on 12 Mar 2024
Moved: Star Strider on 12 Mar 2024
With an initially symbolic set of differential equations, the symbolic versions should not be part of a numerical integration. The usual approach to creating linear or nonlinear differential equations using the Symbolic Math Toolbox to set them up and then using that result to numerically integrate them is something like this —
syms x(t) t Y
Dx = diff(x);
D2x = diff(Dx);
DE = D2x + Dx + x == exp(0.01*x) * sin(2*pi*x)
DE(t) = 
[VF,Sbs] = odeToVectorField(DE)
VF = 
Sbs = 
odefcn = matlabFunction(VF, 'Vars',{t,Y})
odefcn = function_handle with value:
@(t,Y)[Y(2);exp(Y(1)./1.0e+2).*sin(pi.*Y(1).*2.0)-Y(1)-Y(2)]
[t,y] = ode45(odefcn, [0 15], [0 1]);
figure
plot(t, y)
grid
xlabel('t')
ylabel('Value')
legend(string(Sbs), 'Location','best')
.
  10 Comments
Matthew Ediz Beadman
Matthew Ediz Beadman on 14 Mar 2024
You solved this, I only replied as I thought you had something to add. Thank you :)

Sign in to comment.

More Answers (1)

Walter Roberson
Walter Roberson on 12 Mar 2024
Edited: Walter Roberson on 12 Mar 2024
When you symfun(), an anonymous function is generated that does not use the symbolic toolbox.
The traceback for the anonymous function shows it to have been generated inside the symbolic toolbox, but that does not mean that it uses the symbolic toolbox in any way.
syms x
f(x) = x^2
f(x) = 
h = matlabFunction(f)
h = function_handle with value:
@(x)x.^2
functions(h)
ans = struct with fields:
function: '@(x)x.^2' type: 'anonymous' file: '/MATLAB/toolbox/symbolic/symbolic/symengine.p' workspace: {[1×1 struct]} within_file_path: ''
Generated inside symengine.p does not mean that it relies on the symbolic toolbox to execute.
  1 Comment
Matthew Ediz Beadman
Matthew Ediz Beadman on 14 Mar 2024
Hi, thanks for your answer. I'd just like to clarify to other readers that I have tested this by defining all the functions that are defined within the program as their own functions (without defining symbolic variables) in a seperate script and timing the execution of both scripts.
Both scripts take the same time so you are indeed correct, thank you.
Both replies to this post have answered different questions I had, however Star Strider's response was more in line the question title and so I will accept his answer. I am grateful for your answer regardless :)

Sign in to comment.

Categories

Find more on Symbolic Math Toolbox in Help Center and File Exchange

Products


Release

R2023b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!