The equations can be found here. As you can see it is set of 8 scalar equations closed to 3 matrix ones. In order to let Matlab know that equations are matrix - wise, I declare variable time dependent vector functions as:
syms t p1(t) p2(t) p3(t)
p(t) = symfun([p1(t);p2(t);p3(t)], t);
p = formula(p(t)); % allows indexing for vector p
% same goes for w(t) and m(t)...
Known matrices are declared as follows:
A = sym('A%d%d',[3 3]);
Fq = sym('Fq%d%d',[2 3]);
Im = diag(sym('Im%d%d',[1 3]));
The system is now ready to be modeled according to guide:
eqs = [diff(p) == A*w + Fq'*m,...
diff(w) == -Im*p,...
Fq*w == 0];
vars = [p; w; m];
At this point, when I try to reduce index (since it equals 2), I receive following error:
[DAEs,DAEvars] = reduceDAEIndex(eqs,vars);
Error using sym/reduceDAEIndex (line 95)
Expecting as many equations as variables.
The error would not arise if we had declared all variables as scalars:
syms A Im Fq real p(t) w(t) m(t)
Quoting symfun documentation (tips section):
Symbolic functions are always scalars, therefore, you cannot index into a function.
However it is hard for me to believe that it's not possible to solve these equations matrix - wise. Obviously, one can expand it to 8 scalar equations, but the multi body system concerned here is very simple and the aim is to be able to solve complex ones - hence the question: is it possible to solve matrix DAE in Matlab, and if so - what has to be fixed in order for this to work?
Ps. I have another issue with Matlab DAE solver: input variables (known coefficient functions) for my model are time variant. As far as example is concerned, they are constant in all domain, however for my problem they change in time. This problem has been brought out here. I would be grateful if you referred to it, should you have any solution.
Finally, I managed to find correct syntax for this problem. I made a mistake of treating matrix variables (such as A, Fq) as a single entity. Below I present code that utilizes matrix approach and solves this particular DAE:
% Define symbolic variables.
q = sym('q%d',[3 1]); % state variables
a = sym('a'); k = sym('k'); % constant parameters
t = sym('t','real'); % independent variable
% Define system variables and group them in vectors:
p1(t) = sym('p1(t)'); p2(t) = sym('p2(t)'); p3(t) = sym('p3(t)');
w1(t) = sym('w1(t)'); w2(t) = sym('w2(t)'); w3(t) = sym('w3(t)');
m1(t) = sym('m1(t)'); m2(t) = sym('m2(t)');
pvect = [p1(t); p2(t); p3(t)];
wvect = [w1(t); w2(t); w3(t)];
mvect = [m1(t); m2(t)];
% Define matrices:
mass = diag(sym('ms%d',[1 3]));
Fq = [0 -1 a;
0 0 1];
A = [1 0 0;
0 1 a;
0 a -q(1)*a] * k;
% Define sets of equations and aggregate them into one set:
set1 = diff(pvect,t) == A*wvect + Fq'*mvect;
set2 = mass*diff(wvect,t) == -pvect;
set3 = Fq*wvect == 0;
eqs = [set1; set2; set3];
% Close all system variables in one vector:
vars = [pvect; wvect; mvect];
% Reduce index of the system and remove redundnat equations:
[DAEs,DAEvars] = reduceDAEIndex(eqs,vars);
[DAEs,DAEvars] = reduceRedundancies(DAEs,DAEvars);
[M,F] = massMatrixForm(DAEs,DAEvars);
We receive very simple 2x2 ODE for two variables p1(t) and w1(t). Keep in mind that after reducing redundancies we got rid of all elements from state vector q. This means that all left variables (k and mass(1,1)) are not time dependent. If there had been time dependency of some variables within the system, the case would have been much harder to solve.
% Replace symbolic variables with numeric ones:
M = odeFunction(M, DAEvars,mass(1,1));
F = odeFunction(F, DAEvars, k);
k = 2000; numericMass = 4;
F = #(t, Y) F(t, Y, k);
M = #(t, Y) M(t, Y, numericMass);
% set the solver:
opt = odeset('Mass', M); % Mass matrix of the system
TIME = [1; 0]; % Time boundaries of the simulation (backwards in time)
y0 = [1 0]'; % Initial conditions for left variables p1(t) and w1(t)
% Call the solver
[T, solution] = ode15s(F, TIME, y0, opt);
% Plot results
plot(T,solution(:,1),T,solution(:,2))
Related
I am struggling with solving a problem as efficient as possible.
I have two equations and want to solve them together. Furthermore, I want to solve them for four different cases. I can solve them independently altering the code for each case. I can also solve them by accessing a vector that contains the desired value for one of them (e.g., the first value of A) by using arrayfun. However I can't manage to do all of them together.
My code:
clc
clear all
close all
% scalar parameters
g = 9.807; % Gravity constant, m/s2
d = 1.225; % Density of air, kg/m3
x = 1000; % Height, m
% vector parameters
%t = 0:2:10; % time, seconds
A = [1.2; 1.7; 1.8; 0.3]; % Area, m^2
m = [82; 84; 90; 25]; % Mass, kg
Cd = [0.3; 1.14; 0.29; 0.045]; % Drag coefficient, -
syms v t
eqn1 = 2*m./(A.*Cd*d).* log(abs(cosh(t.*sqrt(A.*Cd*d*g./(2*m))))) == x;
eqn2 = (2*g*m./(d*A.*Cd)).*tanh(t.* sqrt((g*d*Cd.*A)./(2*m))) - v == 0;
eqns=[eqn1 eqn2];
variables = [v t];
result = arrayfun(#vpasolve, eqns, 'uniform', 0)
%disp('v='),disp(eval(v));
%disp('t='),disp(eval(t));
I get a result for t (which is weirdly negative and I don't know why), but I only get a {1×1 struct} for v, which I don't want. I know I can solve this also by using a for loop, but I wanted to make it more efficient.
I tried the code written above and solved it in various forms, however not as desired.
The way you have it implemented you're effectively trying to solve four equations with two unknowns, which isn't supported by vpasolve except in the case of polynomial systems.
It looks like you're trying to solve the same system for four different combinations of values for the parameters A, m, and Cd. The easiest way to do that is to just break this out as a for loop (there is simply no benefit to using arrayfun here – speed or otherwise). You'll need to create some temporary symbolic variable for the parameter (there are a few ways this could be done). Here is example code to do just that:
% scalar parameters
g = 9.807; % Gravity constant, m/s2
d = 1.225; % Density of air, kg/m3
x = 1000; % Height, m
% vector parameters
%t = 0:2:10; % time, seconds
A = [1.2; 1.7; 1.8; 0.3]; % Area, m^2
m = [82; 84; 90; 25]; % Mass, kg
Cd = [0.3; 1.14; 0.29; 0.045]; % Drag coefficient, -
syms v t real
syms A_sym m_sym Cd_sym real % Temporary symbolic variables
eqn1 = 2*m_sym./(A_sym.*Cd_sym*d).* log(abs(cosh(t.*sqrt(A_sym.*Cd_sym*d*g./(2*m_sym))))) == x;
eqn2 = (2*g*m_sym./(d*A_sym.*Cd_sym)).*tanh(t.* sqrt((g*d*Cd_sym.*A_sym)./(2*m_sym))) - v == 0;
eqns=[eqn1 eqn2];
variables = [v t];
n = length(A); % Number of prameter sets
result = cell(n,1); % Preallocate resultant cell array
for i = 1:n
% Substitute in numeric values for i-th parameter set
eqns_sub = subs(eqns,{A_sym,m_sym,Cd_sym},{A(i),m(i),Cd(i)});
% Solve and store in cell array
result{i} = vpasolve(eqns_sub,variables);
end
The output, result, is a 4-by-1 cell array of structs with your two variables, v and t, as VPA-valued fields for each.
You could replace the last few lines with the following if what you want is a numeric array as the output (this assumes that only one solution is found for each parameter set):
n = length(A); % Number of prameter sets
result = zeros(n,length(variables)); % Preallocate resultant array
for i = 1:n
% Substitute in numeric values for i-th parameter set
eqns_sub = subs(eqns,{A_sym,m_sym,Cd_sym},{A(i),m(i),Cd(i)});
% Solve, convert to double precision, and store in array
out = vpasolve(eqns_sub,variables);
result(i,:) = double([out.v out.t]);
end
I have written a script to compute and solve a simple inverted pendalum system.Now suppose that I want to solve the nonlinear dynamic equation of the system with ODE45 function with different values of initial conditions.How could I use a for loop to solve for state vector of X for different values of initial conditions?I wrote a for loop to do that but I could not get the answer I wanted.Help me please.Here are my function and mfile as follows:
function xDot = of(x,g,L,u)
xDot = zeros(2,1);
xDot(1) = x(2);
xDot(2) = ((g./L)*sin(x(1)))+u;
end
And this is my main code:
clc;
clear;close all;
%% Solve The Nonlinear Equation
L = 1;
g = 9.81;
h = 0.25;
t = [0:h:5];
A = [0 1;(g/L) 0];
B =[0 1]';
Ics = [pi,0;pi/2 0;pi/5 0;0.001 0;pi 0.5;pi/2 0.5;pi/5 0.5;0.001 0.5];
[Poles,~] = eig(A); %Poles Of Closed LOop System
R = 0.01;
Q = eye(2);
K = lqr(A,B,Q,R);
u = #(x)-K*(x);
for i=1:size(Ics,1)
[~,X] = ode45(#(t,x)of(x,g,L,u(x)),t,Ics(i,:));
end
Also note that I want the first column of X vector which is the angular displacements of the pendulum in each iteration because the second column of X vector in ODE45 is always the Derivative of the main state vector.
You can store all the integration outputs for the different initial conditions in a 3D array.
The number of rows of Xout will equal the number of time steps at which you want to evaluate your solution, so numel(t). The number of columns is the number of states, and then the third dimension will be the number of initial conditions you want to test.
Xout = zeros(numel(t), size(Ics, 2), size(Ics, 1)); % initialize the 3D array
for k = 1:size(Ics, 1)
[~, Xout(:, :, k)] = ode45(#(t, x)of(x, g, L, u(x)), t, Ics(k, :));
end
The following is my code. I try to model PFR in Matlab using ode23s. It works well with one component irreversible reaction. But when extending more dependent variables, 'Matrix dimensions must agree' problem shows. Have no idea how to fix it. Is possible to use other software to solve similar problems?
Thank you.
function PFR_MA_length
clear all; clc; close all;
function dCdt = df(t,C)
dCdt = zeros(N,2);
dCddt = [0; -vo*diff(C(:,1))./diff(V)-(-kM*C(2:end,1).*C(2:end,2)-kS*C(2:end,1))];
dCmdt = [0; -vo*diff(C(:,2))./diff(V)-(-kM*C(2:end,1).*C(2:end,2))];
dCdt(:,1) = dCddt;
dCdt(:,2) = dCmdt;
end
kM = 1;
kS = 0.5; % assumptions of the rate constants
C0 = [2, 2]; % assumptions of the entering concentration
vo = 2; % volumetric flow rate
volume = 20; % total volume of reactor, spacetime = 10
N = 100; % number of points to discretize the reactor volume on
init = zeros(N,2); % Concentration in reactor at t = 0
init(1,:) = C0; % concentration at entrance
V = linspace(0,volume,N)'; % discretized volume elements, in column form
tspan = [0 20];
[t,C] = ode23s(#(t,C) df(t,C),tspan,init);
end
'''
You can put a break point on the line that computes dCddt and observe that the size of the matrices C and V are different.
>> size(C)
ans =
200 1
>> size(V)
ans =
100 1
The element-wise divide operation, ./, between these two variables would then result in the error that you mentioned.
Per ode23s's help, the output of the call to dCdt = df(t,C) needs to be a vector. However, you are returning a matrix of size 100x2. In the next call to the same function, ode32s converts it to a vector when computing the value of C, hence the size 200x1.
In the GNU octave interpretation of Matlab behavior, one has to explicitly make sure that the solver only sees flat one-dimensional state vectors. These have to be translated forward and back in the application of the model.
Explicitly reading the object A as flat array A(:) forgets the matrix dimension information, these can be added back with the reshape(A,m,n) command.
function dCdt = df(t,C)
C = reshape(C,N,2);
...
dCdt = dCdt(:);
end
...
[t,C] = ode45(#(t,C) df(t,C), tspan, init(:));
I have some obscurely long symbolic expression of 3 variables (g_eq), which i would like to minimize with some constraints. I am trying to do so by converting it to a function handle and using fmicon:
cfun = matlabFunction(g_eq,'vars',[kappa;theta;sigma]);
A = [-1 0 0; 0 -1 0; 0 0 -1];
b = [0; 0; 0];
[x,fval] = fmincon(#(kappa, theta, sigma) cfun, x0, A, b)
Which Matlab doesn't like:
FMINCON requires all values returned by user functions to be of
data type double.
I would suspect, that the problem is with cfun, as it is full of numbers with symbolic precision, can I somehow convert it, so that they're double? Or better (computation time wise) while creating my objective function (cfun) (complicated process of some transformation of data and a parametric model), can I use symbols or some other "proxy for the variables" with double for the numeric part of the expressions?
Thanks,
J.
Edit - MCVE:
My aim is to find parameters of a model by minimizing a difference between the model implied and data implied laplace transforms over some weighted regions. Here I provide the problem over one small region without use of weights over the regions and some further simplifications. In part 0, I provide the code for functions of the transformation, in part II I make the parametric transformation, while in III the data transformation and attempt to minimize it in IV.
%% 0. Functions used
%% 0.1 L_V1 - transform of parametric
function lv = L_V1(u,sigma,kappa,theta)
lv = (1/(1+u.*sigma^2/(2*kappa))).^(2*kappa*theta/sigma^2);
end
%% 0.2 LV_2 - transform of data
function lv = L_hat1(u,D,n,T)
A_u = cos(sqrt(2 .*u) *sqrt(n) .*D);
Z_u = 1/n * sum(A_u);
lv = 1/T * sum(Z_u);
end
%% I. Pre-estimation
ulng1=100; %select number of points on the evaluated interval
u1 = linspace(.8, 1.6,ulng1); % create region of interest
%% II. Parametric part
par_mat1 = sym(zeros(ulng1,1)); % create interval for parametric
syms sigma kappa theta LV_par1;
for i = 1:ulng1
par_mat1(i) = L_V1(u1(i),sigma,kappa,theta); %transformations of parametric
end
LV_par1 = sum(par_mat1); %sum of parametric over the region
%% III. Data part
n = 100; %choose number of days
T = 20; %choose number of obs over a day
D = rand([n-1, T]); %vector of observations, here just random numbers
for i = 1:ulng1
hat_mat1(i) = L_hat1(u1(i),D,n,T); %transformations of data
end
hat_1 = sum(hat_mat1); %sum of transforms over the region
%% IV. Minimize
W = 1; %weighting matrix, here just one region, hence 1
MC = hat_1 - LV_par1 ; %moment condition
g_eq = (MC) * (W) *(MC.'); %objective function (symbolic)
cfun = matlabFunction(g_eq,'vars',[kappa;theta;sigma]); %create a function handle from the symbolic
x0 = [.5; 1; .5];
A = [-1 0 0; 0 -1 0; 0 0 -1]; %constrains
b = [0; 0; 0]; %constrains
[x,fval] = fmincon(#(kappa, theta, sigma) cfun, x0, A, b) %minimize
The optimization parameters are always passed as vector.
[x,fval] = fmincon(#(x) cfun(x(1),x(2),x(3)), x0, A, b)
I am trying to solve equation systems, which contain algebraic as well as differential equations. To do this symbolically I need to combine dsolve and solve (do I?).
Consider the following example:
We have three base equations
a == b + c; % algebraic equation
diff(b,1) == 1/C1*y(t); % differential equation 1
diff(c,1) == 1/C2*y(t); % differential equation 2
Solving both differential equations, eliminating int(y,0..t) and then solving for c=f(C1,C2,a) yields
C1*b == C2*c or C1*(a-c) == C2*c
c = C1/(C1+C2) * a
How can I convince Matlab to give me that result? Here is what I tried:
syms a b c y C1 C2;
Eq1 = a == b + c; % algebraic equation
dEq1 = 'Db == 1/C1*y(t)'; % differential equation 1
dEq2 = 'Dc == 1/C2*y(t)'; % differential equation 2
[sol_dEq1, sol_dEq2]=dsolve(dEq1,dEq2,'b(0)==0','c(0)==0'); % this works, but no inclusion of algebraic equation
%[sol_dEq1, sol_dEq2]=dsolve(dEq1,dEq2,Eq1,'c'); % does not work
%solve(Eq1,dEq1,dEq2,'c') % does not work
%solve(Eq1,sol_dEq_C1,sol_dEq_C2,'c') % does not work
No combination of solve and/or dsolve with the equations or their solutions I tried gives me a useful result. Any ideas?
Now I assumed that you wanted the code to be rather general, so I made it to be able to work with any given number of equations and any given number of variables, and I did no calculation by hand.
Note that the way that the symbolic Toolbox works changes drastically from year to year, but hopefully this will work for you. Now one can add the equation Eq1 to the list of inputs of dSolve but there are two problems with that: One is that dSolve seems to prefer character inputs and the second is that dSolve doesn't seem to realize that there are 3 independent variables a, b, and c (it only sees 2 variables, b and c).
To solve the second problem, I differentiated the original equation to get a new differential equation, there were three problems with that: the first is Matlab evaluated the derivative of a with respect to t as 0, so I had to replace a with a(t) and such like for b and c (I called a(t) the long version of a). The second problem was Matlab used inconsistent notation, instead of representing the derivative of a as Da, it represented it as diff(a(t), t) thus I had to replace the latter with the former and such like for b and c; this gave me Da = Db + Dc. The final problem with that is that the system is now under determined, so I had to get the initial values, here I could have solved for a(0) but Matlab seemed happy with using a(0) = b(0) + c(0).
Now back to the original first problem, to solve that I had to convert every sym back into a char.
Here is the code
function SolveExample
syms a b c y C1 C2 t;
Eq1 = sym('a = b + c');
dEq1 = 'Db = 1/C1*y(t)';
dEq2 = 'Dc = 1/C2*y(t)';
[dEq3, initEq3] = ...
TurnEqIntoDEq(Eq1, [a b c], t, 0);
% In the most general case Eq1 will be an array
% and thus DEq3 will be one too
dEq3_char = SymArray2CharCell(dEq3);
initEq3_char = SymArray2CharCell(initEq3);
% Below is the same as
% dsolve(dEq1, dEq2, 'Da = Db + Dc', ...
% 'b(0)=0','c(0)=0', 'a(0) = b(0) + c(0)', 't');
[sol_dEq1, sol_dEq2, sol_dEq3] = dsolve(...
dEq1, dEq2, dEq3_char{:}, ...
'b(0)=0','c(0)=0', initEq3_char{:}, 't')
end
function [D_Eq, initEq] = ...
TurnEqIntoDEq(eq, depVars, indepVar, initialVal)
% Note that eq and depVars
% may all be vectors or scalars
% and they need not be the same size.
% eq = equations
% depVars = dependent variables
% indepVar = independent variable
% initialVal = initial value of indepVar
depVarsLong = sym(zeros(size(depVars)));
for k = 1:numel(depVars)
% Make the variables functions
% eg. a becomes a(t)
% This is so that diff(a, t) does not become 0
depVarsLong(k) = sym([char(depVars(k)) '(' ...
char(indepVar) ')']);
end
% Next make the equation in terms of these functions
eqLong = subs(eq, depVars, depVarsLong);
% Now find the ODE corresponding to the equation
D_EqLong = diff(eqLong, indepVar);
% Now replace all the long terms like 'diff(a(t), t)'
% with short terms like 'Da'
% otherwise dSolve will not work.
% First make the short variables 'Da'
D_depVarsShort = sym(zeros(size(depVars)));
for k = 1:numel(depVars)
D_depVarsShort(k) = sym(['D' char(depVars(k))]);
end
% Next make the long names like 'diff(a(t), t)'
D_depVarsLong = diff(depVarsLong, indepVar);
% Finally replace
D_Eq = subs(D_EqLong, D_depVarsLong, D_depVarsShort);
% Finally determine the equation
% governing the initial values
initEq = subs(eqLong, indepVar, initialVal);
end
function cc = SymArray2CharCell(sa)
cc = cell(size(sa));
for k = 1:numel(sa)
cc{k} = char(sa(k));
end
end
Some minor notes, I changed the == to = as that seems to be a difference between our versions of Matlab. Also I added t as the independent variable in dsolve. I also assumed that you know about cells, numel, linear indexes, ect.