Programming Finite Element Method - matlab

I am trying to teach myself how about Finite Element Methods.
All of my code is adapted from the following link pages 16-20
http://homepages.cae.wisc.edu/~suresh/ME964Website/M964Notes/Notes/introfem.pdf
I am programming along in Matlab to perform a finite element analysis on a single 8 node cube element. I have defined the xi,eta,zeta local axes (we can think about this as x, y, z for now), so I get the following shape functions:
%%shape functions
zeta = 0:.01:1;
eta = 0:.01:1;
xi = 0:.01:1;
N1 = 1/8*(1-xi).*(1-eta).*(1-zeta);
N2 = 1/8*(1+xi).*(1-eta).*(1-zeta);
N3 = 1/8*(1+xi).*(1+eta).*(1-zeta);
N4 = 1/8*(1-xi).*(1+eta).*(1-zeta);
N5 = 1/8*(1-xi).*(1-eta).*(1+zeta);
N6 = 1/8*(1+xi).*(1-eta).*(1+zeta);
N7 = 1/8*(1+xi).*(1+eta).*(1+zeta);
N8 = 1/8*(1-xi).*(1+eta).*(1+zeta);
The [N] Matrix is to be arranged like this according to the text I am reading:
%N Matrix
N= [N1 0 0 N2 0 0 N3 0 0 N4 0 0 N5 0 0 N6 0 0 N7 0 0 N8 0 0;
0 N1 0 0 N2 0 0 N3 0 0 N4 0 0 N5 0 0 N6 0 0 N7 0 0 N8 0;
0 0 N1 0 0 N2 0 0 N3 0 0 N4 0 0 N5 0 0 N6 0 0 N7 0 0 N8];
To find the [B] matrix i have to use the following [D] matrix:
%%Del Matrix for node i
%[ d/dx 0 0
% 0 d/dy 0
% 0 0 d/dz . . .
% d/dy d/dx 0
% 0 d/dz d/dy
% d/dz 0 d/dx ]
which is an operator to go on [N]. (B=DN)
Later on, as the text shows, I will be making calculations involving integrals of this [B] matrix over the volume of this element.
So, my question is, how can I store these polynomial shape functions in a matrix, operate on them with differentiation, and then integrate them numerically. I can tell with the way I have this set up right now, that it wont work because I have defined the functions as a vector over an interval [0,1] and then storing these vectors in the [N] matrix. Then using diff() function to differentiate appropriately to find the [B] matrix.
But since the matrix elements of [B] are now vectors over an interval [0,1] I think that is going to cause problems. How would you guys go about these calculations described in the textbook I posted above?

Solved my problem using anonymous functions and storing the polynomials in a symbolic matrix. example:
syms xi eta zeta
N1= ... %type out in terms of xi eta and zeta
.
.
.
dN1dXi = diff(N1,xi) %symbolic differentiation with respect to xi
can also perform symbolic integration when needed:
intN1 = int(N1,xi,lowerLimit,upperLimit) %symbolic integration with respect to xi
and when ready to substitute in actual values to evaluate the symbolic functions:
subs(N1,{xi,eta,zeta},{value1,value2,value3})

You should check page 24 about how to map from a parametric domain ([0,1]^) to the physical domain.

Although I think you can do as you said, using symbolic. I think symbolic calculation in Matlab is very time-consuming.
I would go for derivate N manually and store as dN, and use it when need it.
Regards,
German

after you have the shape functions you need to substitute it in the stiffness matrix, the stiffness matrix should be 24x24 as you have 24 degrees of freedom. to solve you need to build a linear system (Ax=b), the right hand side is based on the PDE you are solving and you have to include neuman boundary conditions in the right hand side plus the source term. In python for 2d element (4 DOF) will be like:
def shapefxncoef (Valxy):
#creating a temporary metrix to store zeros and get the size of the shape
#function matrix.
n_temp = np.zeros((4,4))
#filling the values of the matrix with a loop.
for i in range(4):
#the values used in the matrix are from the Valxy x and y components.
xi = Valxy [0, i];
yi = Valxy [1, i];
n_temp[i, 0] = 1;
n_temp[i, 1] = xi;
n_temp[i, 2] = yi;
n_temp[i, 3] = xi*yi;
#this gives an identity matrix and the stiffness matric can be derived
#if we take the inverse.
n = np.linalg.inv(n_temp);
return n;
def N (Valxy, x, y):
n = shapefxncoef (Valxy);
res = n[0, :] + n[1, :]*x + n[2, :]*y + n[3, :]*x*y;
return res;
def Be (Valxy, x, y):
res = np.zeros ((2,4));
res_temp = shapefxncoef (Valxy);
for i in range (4):
res_tempi = res_temp[:, i];
dNix = res_tempi[1] + res_tempi[3]*y;
dNiy = res_tempi[2] + res_tempi[3]*x;
res[0, i] = dNix;
res[1, i] = dNiy;
return res;
def Ke (Valxy, conduct):
a = lambda x, y: conduct * np.dot ((Be(Valxy, x, y)).T, Be(Valxy, x, y));
k = intr.integrateOnQuadrangle (Valxy.T, a, np.zeros((4,4)));
return k;

Related

Find rotation matrix with two vectors

I want to find the rotation matrix between two vectors.
[0;0;1] = R * [0.0023;0.0019;0.9899]
How do I find the 3*3 rotation matrix?
This is a simple rearrangement
% [0;0;1] = R * [0.0023;0.0019;0.9899];
% So ...
% [0;0;1] / [0.0023;0.0019;0.9899] = R
% This is a valid MATLAB command
R = [0;0;1] / [0.0023;0.0019;0.9899];
>> R =
[ 0 0 0
0 0 0
0 0 1.0102 ]
We can validate this result
R * [0.0023;0.0019;0.9899]
>> ans =
[0; 0; 1]
Your problem can be defined as a linear equation, say,
y = mx
where, y and x are matrices. Find m.
Solution:
m = x\y or m = mldivide(x,y)
Notice the backslash. It is not a forward slash / as Wolfie mentioned in his answer. For details see https://www.mathworks.com/help/matlab/ref/mldivide.html
Additional Details:
If x is a singular matrix, use pinv. See https://www.mathworks.com/help/matlab/ref/pinv.html for reference.

How to convert a vector into a matrix where values on columns are 1 where the column number is the vector element, else 0?

I'm unsure how to phrase the question, but I think an example will help. Suppose I have a vector y = [3;1;4;1;6]. I want to create the matrix Y =
[0 0 1 0 0 0;
1 0 0 0 0 0;
0 0 0 1 0 0;
1 0 0 0 0 0;
0 0 0 0 0 1]
↑ ↑ ↑ ↑ ↑ ↑
1 2 3 4 5 6
where the element on each column is one or zero corresponding to the value in the vector.
I found that I could do it using
Y = []; for k = 1:max(y); Y = [Y (y==k)]; end
Can I do it without a for loop (and is this method more efficient if y has thousands of elements)?
Thanks!
Your method is not efficient because you're growing the size of Y in the loop which is not a good programming practice. Here is how your code can be fixed:
Ele = numel(y);
Y= zeros(Ele, max(y));
for k = 1:Ele
Y (k,y(k))= 1;
end
And here is an alternative approach without a loop:
Ele = numel(y); %Finding no. of elements in y
Y= zeros(Ele, max(y)); % Initiailizing the matrix of the required size with all zeros
lin_idx = sub2ind(size(Y), 1:Ele, y.'); % Finding linear indexes
Y(lin_idx)=1 % Storing 1 in those indexes
You can use bsxfun:
result = double(bsxfun(#eq, y(:), 1:max(y)));
If you are running the code on Matlab version R2016b or later, you can simplify the syntax to
result = double(y(:)==(1:max(y)));
Another approach, possibly more efficient, is to fill in the values directly using accumarray:
result = accumarray([(1:numel(y)).' y(:)], 1);
I found another solution:
E = eye(max(y));
Y = E(y,:);
Another solution:
Y = repmat(1:max(y), size(y)) == repmat(y, 1, max(y))

How to zero out the centre k by k matrix in an input matrix with odd number of columns and rows

I am trying to solve this problem:
Write a function called cancel_middle that takes A, an n-by-m
matrix, as an input where both n and m are odd numbers and k, a positive
odd integer that is smaller than both m and n (the function does not have to
check the input). The function returns the input matrix with its center k-by-k
matrix zeroed out.
Check out the following run:
>> cancel_middle(ones(5),3)
ans =
1 1 1 1 1
1 0 0 0 1
1 0 0 0 1
1 0 0 0 1
1 1 1 1 1
My code works only when k=3. How can I generalize it for all odd values of k? Here's what I have so far:
function test(n,m,k)
A = ones(n,m);
B = zeros(k);
A((end+1)/2,(end+1)/2)=B((end+1)/2,(end+1)/2);
A(((end+1)/2)-1,((end+1)/2)-1)= B(1,1);
A(((end+1)/2)-1,((end+1)/2))= B(1,2);
A(((end+1)/2)-1,((end+1)/2)+1)= B(1,3);
A(((end+1)/2),((end+1)/2)-1)= B(2,1);
A(((end+1)/2),((end+1)/2)+1)= B(2,3);
A(((end+1)/2)+1,((end+1)/2)-1)= B(3,1);
A(((end+1)/2)+1,((end+1)/2))= B(3,2);
A((end+1)/2+1,(end+1)/2+1)=B(3,3)
end
You can simplify your code. Please have a look at
Matrix Indexing in MATLAB. "one or both of the row and column subscripts can be vectors", i.e. you can define a submatrix. Then you simply need to do the indexing correct: as you have odd numbers just subtract m-k and n-k and you have the number of elements left from your old matrix A. If you divide it by 2 you get the padding on the left/right, top/bottom. And another +1/-1 because of Matlab indexing.
% Generate test data
n = 13;
m = 11;
A = reshape( 1:m*n, n, m )
k = 3;
% Do the calculations
start_row = (n-k)/2 + 1
start_col = (m-k)/2 + 1
A( start_row:start_row+k-1, start_col:start_col+k-1 ) = zeros( k )
function b = cancel_middle(a,k)
[n,m] = size(a);
start_row = (n-k)/2 + 1;
start_column = (m-k)/2 + 1;
end_row = (n-k)/2 + k;
end_column = (m-k)/2 + k;
a(start_row:end_row,start_column:end_column) = 0;
b = a;
end
I have made a function in an m file called cancel_middle and it basically converts the central k by k matrix as a zero matrix with the same dimensions i.e. k by k.
the rest of the matrix remains the same. It is a general function and you'll need to give 2 inputs i.e the matrix you want to convert and the order of submatrix, which is k.

Matlab obtain Transformation matrix

I have two Matrices: Matrix 1: A, that is the Matrix I have in the beginning and Matrix 2: B that has some values from A permuted. Both are filled with only ones and zeros (mainly zeros)
for example
0 0 0 0 0 1
A = 0 1 0 and B = 0 0 0
0 0 0 0 0 0
where I move the value 1 from the middle of matrix A to the top right corner in matrix B. I can do this manually by multiplicating with the Transformation-matrices T1 and T2.
for the example above:
0 1 0 0 0 0
T1 = 0 0 0 and T2 = 0 0 1 such that P1 * A * P2 = B
0 0 0 0 0 0
How can I calculate these two Transformation-matrices P1 and P2 with Matlab really fast (for matrices > 5000x5000) by only knowing matrix A and B?
The goal is to move some of the ones with every step on matrix A. As I have some other Matrices that act like layers, I would like to move them the same way, so I'd like to get T1 and T2 so I can change them the same way.
So the problem is that I have an island, that I get from GoogleMaps, that I convert in 0 (water) and 1 (land) and I randomly put People on that Map M. Then I randomly set the position of some people where M has a one. These people are set on matrix A with a one, rest is zero. The people move, the new position is given by Matrix B. While not all people are infected by zombies yet, I have a vector, that defines a subMatrix, where the zombies are. This little Matrix is the important one, so I move my zombie and some other attributes, that the zombie has (they are defined by other Matrices, I call them lazer Matrices). So I only want the changes of the Zombiematrix tracked (for that I need the Transformation Matrix), so that all layer matrices that have the attributes of the zombie Submatrix are moved the same way. The ZombieMatrix then grows bigger and bigger, the more people are infected. To save time, I look for a subMatrix that contains all the Zombies by each step and then perform the Transformationmatrix on this subMatrix.
I know that sounds all fuzzy, that's why I just asked the way above the line.
Since your problem is nonlinear it can have multiple solutions, from which the comment of #knedlsepp. For this reason I think that resorting to a genetic algorithm might be a good option, naturally without looking at the performances...
Let us reformulate your problem as an optimization one
Here we will adopt the Frobenius norm.
Then the code would look like
%// Build the test matrices
A = [0 0 0 ; 0 1 0 ; 0 0 0];
B = [0 0 1 ; 0 0 0 ; 0 0 0];
n = size(A, 1);
%// Define the optimization problem.
nvars = 2*n^2;
lb = zeros(nvars, 1);
ub = ones(nvars, 1);
intCon = 1:nvars;
options = gaoptimset();
%// Solve the problem.
[t,err,exitflag] = ga(#(t) fitnessfcn(t, A, B, n), ...
nvars, [], [], [], [], lb, ub, [], intCon, options);
%// Retrieve the solution
T1 = reshape(t(1:n^2), n, n);
T2 = reshape(t(n^2+1:end), n, n);
and fitnessfcn is
function err = fitnessfcn(t, A, B, n)
T1 = reshape(t(1:n^2), n, n);
T2 = reshape(t(n^2+1:end), n, n);
E = T1*A*T2-B;
err = norm(E, 'fro');
I hope that by playing with the options you can apply this code to more realistic cases.

Finding sub-matrix with minimum elementwise sum

I have a symmetric m-by-m matrix A. Each element has a value between 0 and 1. I now want to choose n rows / columns of A which form an n-by-n sub-matrix B.
The criteria for choosing these elements, is that the sum of all elements of B must be the minimum out of all possible n-by-n sub-matrices of A.
For example, suppose that A is a 4-by-4 matrix:
A = [0 0.5 1 0; 0.5 0 0.5 0; 1 0.5 1 1; 0 0 1 0.5]
And n is set to 3. Then, the best B is the one taking the first, second and fourth rows / columns of A:
B = [0 0.5 0; 0.5 0 0; 0 0 0.5]
Where the sum of these elements is 0 + 0.5 + 0 + 0.5 + 0 + 0 + 0 + 0 + 0.5 = 1.5, which is smaller than another other possible 3-by-3 sub-matrices (e.g. using the first, third and fourth rows / columns).
How can I do this?
This is partly a mathematics question, and partly a Matlab one. Any help with either would be great!
Do the following:
m = size(A,1);
n=3;
sub = nchoosek(1:m,n); % (numCombinations x n)
subR = permute(sub,[2,3,1]); % (n x 1 x numCombinations), row indices
subC = permute(sub,[3,2,1]); % (1 x n x numCombinations), column indices
lin = bsxfun(#plus,subR,m*(subC-1)); % (n x n x numCombinations), linear indices
allB = A(lin); % (n x n x numCombinations), all possible Bs
sumB = sum(sum(allB,1),2); % (1 x 1 x numCombinations), sum of Bs
sumB = squeeze(sumB); % (numCombinations x 1), sum of Bs
[minB,minBInd] = min(sumB);
fprintf('Indices for minimum B: %s\n',mat2str(sub(minBInd,:)))
fprintf('Minimum B: %s (Sum: %g)\n',mat2str(allB(:,:,minBInd)),minB)
This looks only for submatrices where the row indices are the same as the column indices, and not necessarily consecutive. That is how I understood the question.
This is a bit brute force, but should work
A = [0 0.5 1 0; 0.5 0 0.5 0; 1 0.5 1 1; 0 0 1 0.5];
sizeA = size(A,1);
size_sub=3;
idx_combs = nchoosek(1:sizeA, size_sub);
for ii=1:size(idx_combs,1)
sub_temp = A(idx_combs(ii,:),:);
sub = sub_temp(:,idx_combs(ii,:));
sum_temp = sum(sub);
sums(ii) = sum(sum_temp);
end
[min_set, idx] = min(sums);
sub_temp = A(idx_combs(idx,:),:);
sub = sub_temp(:,idx_combs(idx,:))
Try to convolve the matrix A with a smaller matrix M. Eg if you is interested in finding the 3x3 submatrix then let M be ones(3). This code shows how it works.
A = toeplitz(10:-1:1) % Create a to eplitz matrix (example matrix)
m = 3; % Submatrix size
mC = ceil(m/2); % Distance to center of submatrix
M = ones(m);
Aconv = conv2(A,M); % Do the convolution.
[~,minColIdx] = min(min(Aconv(1+mC:end-mC,1+mC:end-mC))); % Find column center with smallest sum
[~,minRowIdx] = min(min(Aconv(1+mC:end-mC,minColIdx+mC),[],2)); % Find row center with smlest sum
minRowIdx = minRowIdx+mC-1 % Convoluted matrix is larger than A
minColIdx = minColIdx+mC-1 % Convoluted matrix is larger than A
range = -mC+1:mC-1
B = A(minRowIdx+range, minColIdx+range)
The idea is to imitate a fir filter y(n) = 1*x(n-1)+1*x(n)+1*x(n+1). For now it only finds the first smallest matrix though. Notice the +1 adjustment because first matrix element is 1. Then notice the the restoration right below.