Symbolic Small Quantity Approximation in MATLAB - matlab

I am trying to make a small angle approximation in MATLAB's symbolic toolbox. This is being used for the equations of motion in a spacecraft control simulation (and yes, I need to linearize, I can't leave them in their more exact form). For those unfamiliar, the small quantity approximation does a few main things that I need. For small quantities delta and gamma,
delta times gamma is approximately 0
delta^2 is approximately 0 (same with higher powers)
sin(delta) is approximately delta
cos(delta) is approximately 1
I have tried using MATLAB's taylor function (link here), but it doesn't seem to be doing what I want except in a very specific scenario (which I am sure is coincidental anyway). A test case is presented below:
syms psiX psiY psiZ rGMag mu Ixx Iyy Izz
QLB = [1,psiZ,-psiY;-psiZ,1,psiX;psiY,-psiX,1]; %linearized version of the rotation matrix from the L frame to the B frame
rG_LVLH = [0;0;rGMag]; %magnitude of the rG vector expressed in the L frame
rG = QLB*rG_LVLH
G = 3*mu/rGMag^5 .* [rG(2)*rG(3)*(Izz-Iyy);rG(1)*rG(3)*(Ixx-Izz);rG(1)*rG(2)*(Iyy-Ixx)]; %gravity-gradient torque
The desired output of the above should have the G vector with a 0 in the third component and symbolic variables left in the other two. This particular example doesn't include a trigonometric example, but I can provide if necessary. Thanks.

Related

Computing the DFT of an arbitrary signal

As part of a course in signal processing at university, we have been asked to write an algorithm in Matlab to calculate the single sided spectrum of our signal using DFT, without using the fft() function built in to matlab. this isn't an assessed part of the course, I'm just interested in getting this "right" for myself. I am currently using the 2018b version of Matlab, should anyone find this useful.
I have built a signal of a 1 KHz and 2KHz sinusoid, phase shifted by 135 degrees (2*pi/3 rad).
then using the equations in 9.1 of Discrete time signal processing (Allan V. Oppenheim) and Euler's formula to simplify the exponent, I produce this code:
%%DFT(currently buggy)
n=0;m=0;
for m=1:DFT_N-1 %DFT_Fmin;DFT_Fmax; %scrolls through DFT m values (K in text.)
for n=1:DFT_N-1;%;(DFT_N-1);%<<redundant code? from Oppenheim eqn. 9.1 % eulers identity, K=m and n=n
X(m)=x(n)*(cos((2*pi*n*m)/DFT_N)-j*sin((2*pi*n*m)/DFT_N));
n=n+1;
end
%m=m+1; %redundant code?
end
This takes x as the input, in this case the signal mentioned earlier, as well as the resolution of the transform, as represented by the DFT_N, which has been initialized to 100. The output of this function, X, should be something in the frequency domain, but plotting X yields a circular plot slightly larger than the unit circle, and with a gap on the left hand edge.
I am struggling to see how I am supposed to convert this to the stem() plots as given by the in-built DFT algorithm.
Many thanks, J.
This is your bug:
replace X(m)=x(n)*(cos.. with X(m)=X(m)+x(n)*(cos..
For a given m, it does not integrate over the variable n, but overwrites X(m) only the last calculation for n = DFT_N-1.
Notice that integrating over n=1:DFT_N-1 omits one harmonic, i.e., the first one, exp(-j*2*pi). Replace
n=1:DFT_N-1 with n=1:DFT_N to include that. I would also replace m=1:DFT_N-1 with m=1:DFT_N for plotting concerns.
Also replace any 2*pi*n*m with 2*pi*(n-1)*(m-1) to get the phase right, since the first entry of X should correspond to zero-frequency, yielding sum_n x(n) * (cos(0) + j sin(0)) = sum_n x(n). If your signal x is real-valued then the zero-frequency component X(1) should be real-valued, angle(X(1))=0.
Last remark, don't forget to shift zero-frequency component to the center of the spectrum for better visibility, X = circshift(X,floor(size(X)/2));
If you are interested in the single-sided spectrum only, than you can just calculate X(m) for m=1:DFT_N/2 since X it is conjugate symmetric around m=DFT_N/2, i.e., X(DFT_N/2+m) = X(DFT_N/2-m)', due to exp(-j*(pi*n+2*pi/DFT_N*m)) = exp(-j*(pi*n-2*pi/DFT_N*m))'.
As a side note, for a given m this program calculates an inner product between the array x and another array of complex exponentials, i.e., exp(-j*2*pi/DFT_N*m*n), for n = 0,1,...,N-1. MATLAB syntax is very convenient for such calculations, and you can avoid this inner loop by the following command
exp(-j*2*pi/DFT_N*m*(0:DFT_N-1)) * x
where x is a column vector. Similarly, you can avoid the first loop too by expanding your complex exponential vector row-wise for every m, i.e., build the matrix exp(-j*2*pi/DFT_N*(0:DFT_N-1)'*(0:DFT_N-1)). Then your DFT is simply
X = exp(-j*2*pi/DFT_N*(0:DFT_N-1)'*(0:DFT_N-1)) * x
For single-sided spectrum, instead use
X = exp(-j*2*pi/DFT_N*(0:floor((DFT_N-1)/2))'*(0:DFT_N-1)) * x

Curve fitting with an integral function involved

I have a 2 parameter model that includes an integral function and I´m quite lost how to solve this.
My data consists of a set of given molecule sizes, r_m, and a calculated response, K.
The theoretical model is a function of this molecule size, r_m, but also involves the integral of the Gaussian distribution (to make it simpler) of the pore size distribution of an adsorbent material. So it has the following form:
r_p and s_p are the 2 parameters.
So far I've tried to solve this in MATLAB based on the following post:
https://de.mathworks.com/help/optim/examples/nonlinear-data-fitting.html
and this is my code so far:
Data = ...
[0.5 1
1.1 0.83
1.6 0.74
2.2 0.55
2.5 0.28
3.5 0];
r = Data(:,1);
K_exp = Data(:,2);
F = #(x,xdata) quad( (exp(-1/2.*((xdata - x(1))/x(2)).^2).*(1 - (xdata(1)/xdata).^2)),xdata(1),120)./quad(exp(-1/2.*((xdata - x(1))/x(2)).^2,0,120) ; ;
x0 = [6 0.5] ;
[x,resnorm,~,exitflag,output] = lsqcurvefit(F,x0,r,K_exp)
Does this adaption make sense? I'm not sure on the one hand how to properly include an integral into the fitting syntax, and on the other hand I don´t know how to properly tell MATLAB that the lower bound of integration for the numerator should be the size of the molecule, r_m. What I'm imaging is that xdata is some sort of vector for the size, r, as in the example in the link for time, t. However, the lower limit of integration is not fixed but varies for each of the points I want to fit.
Any help is greatly appreciated!

Solving integral in Matlab containing unknown variables?

The task is to create a cone hat in Matlab by creating a developable surface with numerical methods. There are 3 parts of which I have done 2. My question is regarding part 3 where I need to calculate the least rectangular paper surface that can contain the hat. And I need to calculate the material waste of the paper.
YOU CAN MAYBE SKIP THE LONG BACKGROUND AND GO TO LAST PARAGRAPH
BACKGROUND:
The cone hat can be created with a skewed cone with its tip located at (a; 0; b) and with a circle-formed base.
x = Rcos u,
y = Rsin u
z = 0
0<_ u >_2pi
with
known values for R, a and b
epsilon and eta ('n') is the curves x- and y- values when the parameter u goes from 0 to 2pi and alpha is the angle of inclination for the curve at point (epsilon, eta). Starting values at A:
u=0, alhpa=0, epsilon=0, eta=0.
Curve stops at B where the parameter u has reached 2pi.
1.
I plotted the curve by using Runge-Kutta-4 and showed that the tip is located at P = (0, sqrt(b^2 + (R-alpha)^2))
2.
I showed that by using smaller intervals in RK4 I still get quite good accuracy but the problem then is that the curve isn't smooth. Therefore I used Hermite-Interpolation of epsilon and eta as functions of u in every interval to get a better curve.
3.
Ok so now I need to calculate the least rectangular paper surface that can contain the hat and the size of the material waste of the paper. If the end angle alpha(2pi) in the template is pi or pi/2 the material waste will be less. I now get values for R & alpha (R=7.8 and alpha=5.5) and my task is to calculate which height, b the cone hat is going to get with the construction-criteria alpha(2pi)=pi (and then for alpha(2pi)=pi/2 for another sized hat).
So I took the first equation above (the expression containing b) and rewrote it like an integral:
TO THE QUESTION
What I understand is that I need to solve this integral in matlab and then choose b so that alpha(2pi)-pi=0 (using the given criteria above).
The values for R and alpha is given and t is defined as an interval earlier (in part 1 where I did the RK4). So when the integral is solved I get f(b) = 0 which I should be able to solve with for example the secant method? But I'm not able to solve the integral with the matlab function 'integral'.. cause I don't have the value of b of course, that is what I am looking for. So how am I going to go about this? Is there a function in matlab which can be used?
You can use the differential equation for alpha and test different values for b until the condition alpha(2pi)=pi is met. For example:
b0=1 %initial seed
b=fsolve(#find_b,b0) %use the function fsolve or any of your choice
The function to be solved is:
function[obj]=find_b(b)
alpha0=0 %initual valur for alpha at u=0
uspan=[0 2*pi] %range for u
%Use the internal ode solver or any of your choice
[u,alpha] = ode45(#(u,alpha) integrate_alpha(u,alpha,b), uspan, alpha0);
alpha_final=alpha(end) %Get the last value for alpha (at u=2*pi)
obj=alpha_final-pi %Function to be solved
end
And the integration can be done like this:
function[dalpha]=integrate_alpha(u,alpha,b)
a=1; %you can use the right value here
R=1; %you can use the right value here
dalpha=(R-a*cos(u))/sqrt(b^2+(R-a*cos(u))^2);
end

Speed up calculation in Physics simulation in Matlab

I am working on a MR-physic simulation written in Matlab which simulates bloch's equations on an defined object. The magnetisation in the object is updated every time-step with the following functions.
function Mt = evolveMtrans(gamma, delta_B, G, T2, Mt0, delta_t)
% this function calculates precession and relaxation of the
% transversal component, Mt, of M
delta_phi = gamma*(delta_B + G)*delta_t;
Mt = Mt0 .* exp(-delta_t*1./T2 - 1i*delta_phi);
end
This function is a very small part of the entire code but is called upon up to 250.000 times and thus slows down the code and the performance of the entire simulation. I have thought about how I can speed up the calculation but haven't come up with a good solution. There is one line that is VERY time consuming and stands for approximately 50% - 60% of the overall simulation time. This is the line,
Mt = Mt0 .* exp(-delta_t*1./T2 - 1i*delta_phi);
where
Mt0 = 512x512 matrix
delta_t = a scalar
T2 = 512x512 matrix
delta_phi = 512x512 matrix
I would be very grateful for any suggestion to speed up this calculation.
More info below,
The function evovleMtrans is called every timestep during the simulation.
The parameters that are used for calling the function are,
gamma = a constant. (gyramagnetic constant)
delta_B = the magnetic field value
G = gradientstrength
T2 = a 512x512 matrix with T2-values for the object
Mstart.r = a 512x512 matrix with the values M.r had the last timestep
delta_t = a scalar with the difference in time since the last calculated M.r
The only parameters of these that changed during the simulation are,
G, Mstart.r and delta_t. The rest do not change their values during the simulation.
The part below is the part in the main code that calls the function.
% update phase and relaxation to calcTime
delta_t = calcTime - Mstart_t;
delta_B = (d-d0)*B0;
G = Sq.Gx*Sq.xGxref + Sq.Gz*Sq.zGzref;
% Precession around B0 (z-axis) and B1 (+-x-axis or +-y-axis)
% is defined clock-wise in a right hand system x, y, z and
% x', y', z (see the Bloch equation, Bloch 1946 and Levitt
% 1997). The x-axis has angle zero and the y-axis has angle 90.
% For flipping/precession around B1 in the xy-plane, z-axis has
% angle zero.
% For testing of precession direction:
% delta_phi = gamma*((ones(size(d)))*1e-6*B0)*delta_t;
M.r = evolveMtrans(gamma, delta_B, G, T2, Mstart.r, delta_t);
M.l = evolveMlong(T1, M0.l, Mstart.l, delta_t);
This is not a surprise.
That "single line" is a matrix equation. It's really 1,024 simultaneous equations.
Per Jannick, that first term means element-wise division, so "delta_t/T[i,j]". Multiplying a matrix by a scalar is O(N^2). Matrix addition is O(N^2). Evaluating exponential of a matrix will be O(N^2).
I'm not sure if I saw a complex argument in there as well. Does that mean complex matricies with real and imaginary entries? Does your equation simplify to real and imaginary parts? That means twice the number of computations.
Your best hope is to exploit symmetry as much as possible. If all your matricies are symmetric, you cut your calculations roughly in half.
Use parallelization if you can.
Algorithm choice can make a big difference, too. If you're using explicit Euler integration, you may have time step limitations due to stability concerns. Is that why you have 250,000 steps? Maybe a larger time step is possible with a more stable integration schema. Think about a higher order adaptive scheme with error correction, like 5th order Runge Kutta.
There are several possibilities to improve the speed of the code but all that I see come with a caveat.
Numerical ode integration
The first possibility would be to change your analytical solution by numerical differential equation solver. This has several advantages
The analytical solution includes the complex exponential function, which is costly to calculate, while the differential equation contains only multiplication and addition. (d/dt u = -a u => u=exp(-at))
There are plenty of built-in solvers for matlab available and they are typically pretty fast (e.g. ode45). The built-ins however all use a variable step size. This improves speed and accuracy but would be a problem if you really need a fixed equally spaced grid of time points. Here are unofficial fixed step solvers.
As a start you could also try to use just an euler step by replacing
M.r = evolveMtrans(gamma, delta_B, G, T2, Mstart.r, delta_t);
by
delta_phi = gamma*(delta_B + G)*t_step;
M.r += M.r .* (1-t_step*1./T2 - 1i*delta_phi);
You can then further improve that by precalculating all constant values, e.g. one_over_T1=1/T1, moving delta_phi out of the loop.
Caveat:
You are bound to a minimum step size or the accuracy suffers. Therefore this is only a good idea if you time-spacing is quite fine.
Less points in time
You should carfully analyze whether you really need so many points in time. It seems somewhat puzzling to me that you need so many points. As you know the full analytical solution you can freely choose how to sample the time and maybe use this to your advantage.
Going fortran
This might seem like a grand step but in my experience basic (simple loops, matrix operations etc.) matlab code can be relatively easily translated to fortran line-by-line. This would be especially helpful in addition to my first point. If you still want to use the full analytical solution probably there is not much to gain here because exp is already pretty fast in matlab.

How to do I calculate the hazard rate accurately in Matlab?

I need to calculate the hazard-rate, PDF/(1-CDF), of a Rayleigh function over x.
x = 0:0.001:2.5;
HR = pdf('rayl',x,sqrt(1/18))./(1-cdf('rayl',x,sqrt(1/18)));
plot(x,HR)
Here the plot becomes funny at approximately x = 2. How can I improve accuracy of the HR?
You're running into numerical issues, specifically catastrophic cancellation. Try plotting just the denominator of your hazard rate function, the complementary CDF, on a semilogy plot:
x = 0:0.001:2.5;
semilogy(x,1-cdf('rayl',x,sqrt(1/18)))
As you can see, there are issues a bit before x = 2 when the denominator is approximately machine epsilon, eps. This is when cdf('rayl',x,sqrt(1/18)) is about 1.
Luckily, Matlab provides a way around this with an option that calculates the complementary CDF, or tail probability of the CDF, directly via the 'upper' option for cdf:
x = 0:0.001:2.5;
HR = pdf('rayl',x,sqrt(1/18))./cdf('rayl',x,sqrt(1/18),'upper');
plot(x,HR)
which now returns a straight line with a slope of 18, as expected. The raylcdf function also supports this option.