I am training my own self organizing map to cluster colorvalues. Now I want to make some sort of a U-matrix to show euclidean distances between the nodes and their direct neighbors. My problem now is, that my algorithm is quite unefficient!! There is certainly a way to compute this more efficiently?
function displayUmatrix(dims,weights) %#dims is [30 30], size(weights) = [900 3],
%#consisting of values between 1 and 0
hold on;
axis off;
A = zeros(dims(1), dims(2), 3);
B = reshape(weights',[dims(1) dims(2) size(weights,1)]);
if size(weights,1)==3
for i=1:dims(1)
for j=1:dims(2)
if i~=1
if j~=1
A(i,j,:)=A(i,j,:)+(B(i,j,:)-B(i-1,j-1,:)).^2;
end
A(i,j,:)=A(i,j,:)+(B(i,j,:)-B(i-1,j,:)).^2;
if j~=dims(2)
A(i,j,:)=A(i,j,:)+(B(i,j,:)-B(i-1,j+1,:)).^2;
end
end
if i~=dims(1)
if j~=1
A(i,j,:)=A(i,j,:)+(B(i,j,:)-B(i+1,j-1,:)).^2;
end
A(i,j,:)=A(i,j,:)+(B(i,j,:)-B(i+1,j,:)).^2;
if j~=dims(2)
A(i,j,:)=A(i,j,:)+(B(i,j,:)-B(i+1,j+1,:)).^2;
end
end
if j~=1
A(i,j,:)=A(i,j,:)+(B(i,j,:)-B(i,j-1,:)).^2;
end
if j~=dims(2)
A(i,j,:)=A(i,j,:)+(B(i,j,:)-B(i,j+1,:)).^2;
end
C(i,j)=sum(A(i,j,:));
end
end
D = flipud(C);
maximum = max(max(D));
D = D./maximum;
imagesc(D)
else
error('display function does only work on 3D input');
end
hold off;
drawnow;
end
Thanks, Max
You can calculate the (squared) distance of each point to its neigbor to the right by:
sum((B(:,1:end-1,:) - B(:,2:end,:)).^2, 3)
Similarly, you calculate the distance of each point to the point below, and on both diagonals. You don't have all those values for points on borders so you pad them with zeros. Then you add the distances and divide them by the number of neighbors a point has to get the average distance to all the neighbors.
Here is my code:
%calculate distances to neighbors
right = sum((B(:,1:end-1,:)- B(:,2:end,:)).^2, 3);
bottom = sum((B(1:end-1,:,:)- B(2:end,:,:)).^2, 3); zeros();
diag1 = sum((B(1:end-1,1:end-1,:)- B(2:end,2:end,:)).^2, 3);
diag2 = sum((B(2:end,2:end,:)- B(1:end-1,1:end-1,:)).^2, 3);
%pad them with zeros to the correct size
rightPadded = [right zeros(dim(1) , 1)];
leftPadded = [zeros(dim(1) , 1) right];
botomPadded = [bottom; zeros(1,dim(2))];
upPadded = [zeros(1,dim(2));bottom];
bottomRight = zeros(dim(1), dim(2));
bottomRight(1:end-1,1:end-1) = diag1;
upLeft = zeros(dim(1), dim(2));
upLeft(2:end,2:end) = diag1;
bottomLeft = zeros(dim(1), dim(2));
bottomLeft(1:end-1,2:end) = diag2;
upRight = zeros(dim(1), dim(2));
upRight(2:end,1:end-1) = diag2;
%add distances to all neighbors
sumDist = rightPadded + leftPadded + bottomRight + upLeft + bottomLeft + upRight;
%number of neighbors a point has
neighborNum = zeros(dim(1), dim(2)) + 8;
neighborNum([1 end],:) = 5;
neighborNum(:,[1 end]) = 5;
neighborNum([1 end],[1 end]) = 3;
%divide summed distance by number of neighbors
avgDist = sumDist./neighborNum;
It is all vectorized, so it should be faster than your version.
If you want the exact U-matrix, you can interleave the average distances with the neighboring distances.
Related
N = 500;
pattern = zeros(N,N);
grid on
plot(pattern)
% gets coordinates of modified cells
[x,y] = ginput;
% convert coordinates to integers
X = uint8(x);
Y = uint8(y);
% convert (X,Y) into linear indices
indx = sub2ind([N,N],x,y);
% switch desired cells on (value of 1)
pattern(indx) = 1;
I'm trying to assign several elements of a zeros array the value of 1. Basically I want to create an interactive plot where the user decides what cells he wants to turn on and then save his drawing as a matrix. In Python it's very simple to use the on_click with Matplotlib, but Matlab is weird and I can't find a clear answer. What's annoying is you can't see where you clicked until you save your changes and check the final matrix. You also can't erase a point if you made a mistake.
Moreover I get the following error : Error using sub2ind Out of range subscript. Error in createPattern (line 12) indx = sub2ind([N,N],X,Y);
Any idea how to fix it?
function CreatePattern
hFigure = figure;
hAxes = axes;
axis equal;
axis off;
hold on;
N = 3; % for line width
M = 20; % board size
squareEdgeSize = 5;
% create the board of patch objects
hPatchObjects = zeros(M,M);
for j = M:-1:1
for k = 1:M
hPatchObjects(M - j+ 1, k) = rectangle('Position', [k*squareEdgeSize,j*squareEdgeSize,squareEdgeSize,squareEdgeSize], 'FaceColor', [0 0 0],...
'EdgeColor', 'w', 'LineWidth', N, 'HitTest', 'on', 'ButtonDownFcn', {#OnPatchPressedCallback, M - j+ 1, k});
end
end
Board = zeros(M,M);
playerColours = [1 1 1; 0 0 0];
xlim([squareEdgeSize M*squareEdgeSize]);
ylim([squareEdgeSize M*squareEdgeSize]);
function OnPatchPressedCallback(hObject, eventdata, rowIndex, colIndex)
% change FaceColor to player colour
value = Board(rowIndex,colIndex);
if value == 1
set(hObject, 'FaceColor', playerColours(2, :));
Board(rowIndex,colIndex) = 0; % update board
else
set(hObject, 'FaceColor', playerColours(1, :));
Board(rowIndex,colIndex) = 1; % update board
end
end
end
I found this link and modified the code to be able to expand the board and also select cells that have been turned on already to switch them off.
Now I need a way to extract that board value to save the array.
Suppose I have an array of length 15
x = randi([0 5], 1,15);
I want to sum every 3 elements of x together and put each sum in a new array called y, as in the following:
y = [y1 y2 y3 y4 y5];
Please help me in doing that in Matlab using for loops.
Here's a vectorized approach that automatically deals with a possible smaller last chunk:
x = randi([0 5], 1, 15); % example data
N = 3; % chunk size
y = accumarray(ceil((1:numel(x))/N).', x(:));
you can use reshape as follows:
y = sum(reshape(x,3,[]))
This reshapes your vector x to an array 3 by whatever is left, then sum along right dimension...
For the case the # of elements you want to sum doesnt add up to the total length of the vector, you can pad with zeros or NaN at the end to make it work. Here's I chose adding zeros:
x = randi([0 5], 1,15);
n = 4 ; % sum every n elements (which is the number of rows in the reshape)
try
y = sum(reshape(x, n, []));
catch
disp('added trailing zeros!')
x(numel(x) + (n - mod(numel(x), n))) = 0;
y = sum(reshape(x, n, []));
end
(you can do this with an if condition instead, I just like try catch more here)
Using for loops:
y = zeros(1,5);
for i = 1:5
idx = (i-1)*3 + 1:(i-1)*3 + 3;
y(i) = sum(x(idx));
end
Using a reference variable Target that is used to indicate the start position of each partition the loop below can be achieved. If you would only like to use only loops an alternative inner loop can be done. This method works almost on the same premise as windowing.
Method 1: Single For-Loop with Indexing
x = randi([0 5], 1,15);
y = zeros(1,length(x)/3);
Index = 1;
for Target = 1: +3: 15
Partition = x(Target:Target+2);
y(1,Index) = sum(Partition);
Index = Index + 1;
end
Method 2: Outer and Inner For-Loops
x = randi([0 5], 1,15);
y = zeros(1,length(x)/3);
Partition = zeros(1,3);
Index = 1;
for Target = 1: +3: 15
for Column = 1: +1: 3
Partition(1,Column) = x(1,Target+Column-1);
end
y(1,Index) = sum(Partition);
Index = Index + 1;
end
I have the antenna array factor expression here:
I have coded the array factor expression as given below:
lambda = 1;
M = 100;N = 200; %an M x N array
dx = 0.3*lambda; %inter-element spacing in x direction
m = 1:M;
xm = (m - 0.5*(M+1))*dx; %element positions in x direction
dy = 0.4*lambda;
n = 1:N;
yn = (n - 0.5*(N+1))*dy;
thetaCount = 360; % no of theta values
thetaRes = 2*pi/thetaCount; % theta resolution
thetas = 0:thetaRes:2*pi-thetaRes; % theta values
phiCount = 180;
phiRes = pi/phiCount;
phis = -pi/2:phiRes:pi/2-phiRes;
cmpWeights = rand(N,M); %complex Weights
AF = zeros(phiCount,thetaCount); %Array factor
tic
for i = 1:phiCount
for j = 1:thetaCount
for p = 1:M
for q = 1:N
AF(i,j) = AF(i,j) + cmpWeights(q,p)*exp((2*pi*1j/lambda)*(xm(p)*sin(thetas(j))*cos(phis(i)) + yn(q)*sin(thetas(j))*sin(phis(i))));
end
end
end
end
How can I vectorize the code for calculating the Array Factor (AF).
I want the line:
AF(i,j) = AF(i,j) + cmpWeights(q,p)*exp((2*pi*1j/lambda)*(xm(p)*sin(thetas(j))*cos(phis(i)) + yn(q)*sin(thetas(j))*sin(phis(i))));
to be written in vectorized form (by modifying the for loop).
Approach #1: Full-throttle
The innermost nested loop generates this every iteration - cmpWeights(q,p)*exp((2*pi*1j/lambda)*(xm(p)*sin(thetas(j))*cos(phis(i)) + yn(q)*sin(thetas(j))*sin(phis(i)))), which are to summed up iteratively to give us the final output in AF.
Let's call the exp(.... part as B. Now, B basically has two parts, one is the scalar (2*pi*1j/lambda) and the other part
(xm(p)*sin(thetas(j))*cos(phis(i)) + yn(q)*sin(thetas(j))*sin(phis(i))) that is formed from the variables that are dependent on
the four iterators used in the original loopy versions - i,j,p,q. Let's call this other part as C for easy reference later on.
Let's put all that into perspective:
Loopy version had AF(i,j) = AF(i,j) + cmpWeights(q,p)*exp((2*pi*1j/lambda)*(xm(p)*sin(thetas(j))*cos(phis(i)) + yn(q)*sin(thetas(j))*sin(phis(i)))), which is now equivalent to AF(i,j) = AF(i,j) + cmpWeights(q,p)*B, where B = exp((2*pi*1j/lambda)*(xm(p)*sin(thetas(j))*cos(phis(i)) + yn(q)*sin(thetas(j))*sin(phis(i)))).
B could be simplified to B = exp((2*pi*1j/lambda)* C), where C = (xm(p)*sin(thetas(j))*cos(phis(i)) + yn(q)*sin(thetas(j))*sin(phis(i))).
C would depend on the iterators - i,j,p,q.
So, after porting onto a vectorized way, it would end up as this -
%// 1) Define vectors corresponding to iterators used in the loopy version
I = 1:phiCount;
J = 1:thetaCount;
P = 1:M;
Q = 1:N;
%// 2) Create vectorized version of C using all four vector iterators
mult1 = bsxfun(#times,sin(thetas(J)),cos(phis(I)).'); %//'
mult2 = bsxfun(#times,sin(thetas(J)),sin(phis(I)).'); %//'
mult1_xm = bsxfun(#times,mult1(:),permute(xm,[1 3 2]));
mult2_yn = bsxfun(#times,mult2(:),yn);
C_vect = bsxfun(#plus,mult1_xm,mult2_yn);
%// 3) Create vectorized version of B using vectorized C
B_vect = reshape(exp((2*pi*1j/lambda)*C_vect),phiCount*thetaCount,[]);
%// 4) Final output as matrix multiplication between vectorized versions of B and C
AF_vect = reshape(B_vect*cmpWeights(:),phiCount,thetaCount);
Approach #2: Less-memory intensive
This second approach would reduce the memory traffic and it uses the distributive property of exponential - exp(A+B) = exp(A)*exp(B).
Now, the original loopy version was this -
AF(i,j) = AF(i,j) + cmpWeights(q,p)*exp((2*pi*1j/lambda)*...
(xm(p)*sin(thetas(j))*cos(phis(i)) + yn(q)*sin(thetas(j))*sin(phis(i))))
So, after using the distributive property, we would endup with something like this -
K = (2*pi*1j/lambda)
part1 = K*xm(p)*sin(thetas(j))*cos(phis(i));
part2 = K*yn(q)*sin(thetas(j))*sin(phis(i));
AF(i,j) = AF(i,j) + cmpWeights(q,p)*exp(part1)*exp(part2);
Thus, the relevant vectorized approach would become something like this -
%// 1) Define vectors corresponding to iterators used in the loopy version
I = 1:phiCount;
J = 1:thetaCount;
P = 1:M;
Q = 1:N;
%// 2) Define the constant used at the start of EXP() call
K = (2*pi*1j/lambda);
%// 3) Perform the sine-cosine operations part1 & part2 in vectorized manners
mult1 = K*bsxfun(#times,sin(thetas(J)),cos(phis(I)).'); %//'
mult2 = K*bsxfun(#times,sin(thetas(J)),sin(phis(I)).'); %//'
%// Perform exp(part1) & exp(part2) in vectorized manners
part1_vect = exp(bsxfun(#times,mult1(:),xm));
part2_vect = exp(bsxfun(#times,mult2(:),yn));
%// Perform multiplications with cmpWeights for final output
AF = reshape(sum((part1_vect*cmpWeights.').*part2_vect,2),phiCount,[])
Quick Benchmarking
Here are the runtimes with the input data listed in the question for the original loopy approach and proposed approach #2 -
---------------------------- With Original Approach
Elapsed time is 358.081507 seconds.
---------------------------- With Proposed Approach #2
Elapsed time is 0.405038 seconds.
The runtimes suggests a crazy performance improvement with Approach #2!
The basic trick is to figure out what things are constant, and what things depend on the subscript term - and therefore are matrix terms.
Within the sum:
C(n,m) is a matrix
2π/λ is a constant
sin(θ)cos(φ) is a constant
x(m) and y(n) are vectors
So the two things I would do are:
Expand the xm and ym into matrices using meshgrid()
Take all the constant term stuff outside the loop.
Like this:
...
piFactor = 2 * pi * 1j / lambda;
[xgrid, ygrid] = meshgrid(xm, ym); % xgrid and ygrid will be size (N, M)
for i = 1:phiCount
for j = 1:thetaCount
xFactor = sin(thetas(j)) * cos(phis(i));
yFactor = sin(thetas(j)) * sin(phis(i));
expFactor = exp(piFactor * (xgrid * xFactor + ygrid * yFactor)); % expFactor is size (N, M)
elements = cmpWeights .* expFactor; % elements of sum, size (N, M)
AF(i, j) = AF(i, j) + sum(elements(:)); % sum and then integrate.
end
end
You could probably figure out how to vectorise the outer loop too, but hopefully that gives you a starting point.
I have a column vector (V1) of real numbers like:
123.2100
125.1290
...
954.2190
If I add, let's say, a number 1 to each row in this vector, I will get (V2):
124.2100
126.1290
...
955.2190
I need to find out how many elements from V2 are inside some error-window created from V1. For example the error-window = 0.1 (but in my case every element in V1 has it's own error window):
123.1100 123.3100
125.0290 125.2290
...
954.1190 954.3190
I can create some code like this:
% x - my vector
% ppm - a variable responsible for error-window
window = [(1-(ppm/1000000))*x, (1+(ppm/1000000))*x]; % - error-window
mdiff = 1:0.001:20; % the numbers I will iteratively add to x
% (like the number 1 in the example)
cdiff = zeros(length(mdiff),1); % a vector that will contain counts of elements
% corresponding to different mdiff temp = 0;
for i = 1:length(mdiff)
for j = 1:size(window,1)
xx = x + mdiff(i);
indxx = find( xx => window(j,1) & xx <= window(j,2) );
if any(indxx)
temp = temp + length(indxx); %edited
end
end
cdiff(i) = temp;
temp = 0;
end
So, at the end cdiff will contain all the counts corresponding to mdiff. The only thing, I would like to make the code faster. Or is there a way to avoid using the second loop (with j)? I mean to directly use a multidimensional condition.
EDIT
I decided to simpify the code like this (thanking to the feedback I got here):
% x - my vector
% ppm - a variable responsible for error-window
window = [(1-(ppm/1000000))*x, (1+(ppm/1000000))*x]; % - error-window
mdiff = 1:0.001:20; % the numbers I will iteratively add to x
% (like the number 1 in the example)
cdiff = zeros(length(mdiff),1); % a vector that will contain counts of elements
% corresponding to different mdiff temp = 0;
for i = 1:length(mdiff)
xx = x + mdiff(i);
cdiff(i) = sum(sum(bsxfun(#and,bsxfun(#ge,xx,window(:,1)'),bsxfun(#le,xx,window(:,2)'))));
end
In this case the code works faster and seems properly
add = 1; %// how much to add
error = .1; %// maximum allowed error
V2 = V1 + add; %// build V2
ind = sum(abs(bsxfun(#minus, V1(:).', V2(:)))<error)>1; %'// index of elements
%// of V1 satisfying the maximum error condition. ">1" is used to because each
%// element is at least equal to itself
count = nnz(ind);
Think this might work for you -
%%// Input data
V1 = 52+rand(4,1)
V2 = V1+1;
t= 0.1;
low_bd = any(abs(bsxfun(#minus,V2,[V1-t]'))<t,2); %%//'
up_bd = any(abs(bsxfun(#minus,V2,[V1+t]'))<t,2); %%//'
count = nnz( low_bd | up_bd )
One could also write it as -
diff_map = abs(bsxfun(#minus,[V1-t V1+t],permute(V2,[3 2 1])));
count = nnz(any(any(diff_map<t,2),1))
Edit 1:
low_bd = any(abs(bsxfun(#minus,V2,window(:,1)'))<t,2); %%//'
up_bd = any(abs(bsxfun(#minus,V2,window(:,2)'))<t,2); %%//'
count = nnz( low_bd | up_bd )
Edit 2: Vectorized form for the edited code
t1 = bsxfun(#plus,x,mdiff);
d1 = bsxfun(#ge,t1,permute(window(:,1),[3 2 1]));
d2 = bsxfun(#le,t1,permute(window(:,2),[3 2 1]));
t2 = d1.*d2;
cdiff_vect = max(sum(t2,3),[],1)';
I am trying to model heat conduction within a wood cylinder using implicit finite difference methods. The general heat equation that I'm using for cylindrical and spherical shapes is:
Where p is the shape factor, p = 1 for cylinder and p = 2 for sphere. Boundary conditions include convection at the surface. For more details about the model, please see the comments in the Matlab code below.
The main m-file is:
%--- main parameters
rhow = 650; % density of wood, kg/m^3
d = 0.02; % wood particle diameter, m
Ti = 300; % initial particle temp, K
Tinf = 673; % ambient temp, K
h = 60; % heat transfer coefficient, W/m^2*K
% A = pre-exponential factor, 1/s and E = activation energy, kJ/mol
A1 = 1.3e8; E1 = 140; % wood -> gas
A2 = 2e8; E2 = 133; % wood -> tar
A3 = 1.08e7; E3 = 121; % wood -> char
R = 0.008314; % universal gas constant, kJ/mol*K
%--- initial calculations
b = 1; % shape factor, b = 1 cylinder, b = 2 sphere
r = d/2; % particle radius, m
nt = 1000; % number of time steps
tmax = 840; % max time, s
dt = tmax/nt; % time step spacing, delta t
t = 0:dt:tmax; % time vector, s
m = 20; % number of radius nodes
steps = m-1; % number of radius steps
dr = r/steps; % radius step spacing, delta r
%--- build initial vectors for temperature and thermal properties
i = 1:m;
T(i,1) = Ti; % column vector of temperatures
TT(1,i) = Ti; % row vector to store temperatures
pw(1,i) = rhow; % initial density at each node is wood density, rhow
pg(1,i) = 0; % initial density of gas
pt(1,i) = 0; % inital density of tar
pc(1,i) = 0; % initial density of char
%--- solve system of equations [A][T]=[C] where T = A\C
for i = 2:nt+1
% kinetics at n
[rww, rwg, rwt, rwc] = funcY(A1,E1,A2,E2,A3,E3,R,T',pw(i-1,:));
pw(i,:) = pw(i-1,:) + rww.*dt; % update wood density
pg(i,:) = pg(i-1,:) + rwg.*dt; % update gas density
pt(i,:) = pt(i-1,:) + rwt.*dt; % update tar density
pc(i,:) = pc(i-1,:) + rwc.*dt; % update char density
Yw = pw(i,:)./(pw(i,:) + pc(i,:)); % wood fraction
Yc = pc(i,:)./(pw(i,:) + pc(i,:)); % char fraction
% thermal properties at n
cpw = 1112.0 + 4.85.*(T'-273.15); % wood heat capacity, J/(kg*K)
kw = 0.13 + (3e-4).*(T'-273.15); % wood thermal conductivity, W/(m*K)
cpc = 1003.2 + 2.09.*(T'-273.15); % char heat capacity, J/(kg*K)
kc = 0.08 - (1e-4).*(T'-273.15); % char thermal conductivity, W/(m*K)
cpbar = Yw.*cpw + Yc.*cpc; % effective heat capacity
kbar = Yw.*kw + Yc.*kc; % effective thermal conductivity
pbar = pw(i,:) + pc(i,:); % effective density
% temperature at n+1
Tn = funcACbar(pbar,cpbar,kbar,h,Tinf,b,m,dr,dt,T);
% kinetics at n+1
[rww, rwg, rwt, rwc] = funcY(A1,E1,A2,E2,A3,E3,R,Tn',pw(i-1,:));
pw(i,:) = pw(i-1,:) + rww.*dt;
pg(i,:) = pg(i-1,:) + rwg.*dt;
pt(i,:) = pt(i-1,:) + rwt.*dt;
pc(i,:) = pc(i-1,:) + rwc.*dt;
Yw = pw(i,:)./(pw(i,:) + pc(i,:));
Yc = pc(i,:)./(pw(i,:) + pc(i,:));
% thermal properties at n+1
cpw = 1112.0 + 4.85.*(Tn'-273.15);
kw = 0.13 + (3e-4).*(Tn'-273.15);
cpc = 1003.2 + 2.09.*(Tn'-273.15);
kc = 0.08 - (1e-4).*(Tn'-273.15);
cpbar = Yw.*cpw + Yc.*cpc;
kbar = Yw.*kw + Yc.*cpc;
pbar = pw(i,:) + pc(i,:);
% revise temperature at n+1
Tn = funcACbar(pbar,cpbar,kbar,h,Tinf,b,m,dr,dt,T);
% store temperature at n+1
T = Tn;
TT(i,:) = T';
end
%--- plot data
figure(1)
plot(t./60,TT(:,1),'-b',t./60,TT(:,m),'-r')
hold on
plot([0 tmax/60],[Tinf Tinf],':k')
hold off
xlabel('Time (min)'); ylabel('Temperature (K)');
sh = num2str(h); snt = num2str(nt); sm = num2str(m);
title(['Cylinder Model, d = 20mm, h = ',sh,', nt = ',snt,', m = ',sm])
legend('Tcenter','Tsurface',['T\infty = ',num2str(Tinf),'K'],'location','southeast')
figure(2)
plot(t./60,pw(:,1),'--',t./60,pw(:,m),'-','color',[0 0.7 0])
hold on
plot(t./60,pg(:,1),'--b',t./60,pg(:,m),'b')
hold on
plot(t./60,pt(:,1),'--k',t./60,pt(:,m),'k')
hold on
plot(t./60,pc(:,1),'--r',t./60,pc(:,m),'r')
hold off
xlabel('Time (min)'); ylabel('Density (kg/m^3)');
The function m-file, funcACbar, that creates the system of equations to solve is:
% Finite difference equations for cylinder and sphere
% for 1D transient heat conduction with convection at surface
% general equation is:
% 1/alpha*dT/dt = d^2T/dr^2 + p/r*dT/dr for r ~= 0
% 1/alpha*dT/dt = (1 + p)*d^2T/dr^2 for r = 0
% where p is shape factor, p = 1 for cylinder, p = 2 for sphere
function T = funcACbar(pbar,cpbar,kbar,h,Tinf,b,m,dr,dt,T)
alpha = kbar./(pbar.*cpbar); % effective thermal diffusivity
Fo = alpha.*dt./(dr^2); % effective Fourier number
Bi = h.*dr./kbar; % effective Biot number
% [A] is coefficient matrix at time level n+1
% {C} is column vector at time level n
A(1,1) = 1 + 2*(1+b)*Fo(1);
A(1,2) = -2*(1+b)*Fo(2);
C(1,1) = T(1);
for k = 2:m-1
A(k,k-1) = -Fo(k-1)*(1 - b/(2*(k-1))); % Tm-1
A(k,k) = 1 + 2*Fo(k); % Tm
A(k,k+1) = -Fo(k+1)*(1 + b/(2*(k-1))); % Tm+1
C(k,1) = T(k);
end
A(m,m-1) = -2*Fo(m-1);
A(m,m) = 1 + 2*Fo(m)*(1 + Bi(m) + (b/(2*m))*Bi(m));
C(m,1) = T(m) + 2*Fo(m)*Bi(m)*(1 + b/(2*m))*Tinf;
% solve system of equations [A]{T} = {C} where temperature T = [A]\{C}
T = A\C;
end
And finally the function that deals with the kinetic reactions, funcY, is:
% Kinetic equations for reactions of wood, first-order, Arrhenious type equations
% K = A*exp(-E/RT) where A = pre-exponential factor, 1/s
% and E = activation energy, kJ/mol
function [rww, rwg, rwt, rwc] = funcY(A1,E1,A2,E2,A3,E3,R,T,pww)
K1 = A1.*exp(-E1./(R.*T)); % wood -> gas (1/s)
K2 = A2.*exp(-E2./(R.*T)); % wood -> tar (1/s)
K3 = A3.*exp(-E3./(R.*T)); % wood -> char (1/s)
rww = -(K1+K2+K3).*pww; % rate of wood consumption (rho/s)
rwg = K1.*pww; % rate of gas production from wood (rho/s)
rwt = K2.*pww; % rate of tar production from wood (rho/s)
rwc = K3.*pww; % rate of char production from wood (rho/s)
end
Running the above code gives a temperature profile at the center and surface of the wood cylinder:
As you can see from this plot, for some reason the center and surface temperatures rapidly converge at the 2 min mark which isn't correct.
Any suggestions on how to fix this or create a more efficient way to solve the problem?
It looks like you are using a backward Euler implicit method of discretization of a diffusion PDE. A more accurate approach is the Crank-Nicolson method. Both methods are unconditionally stable.
The introduction of a T-dependent diffusion coefficient requires special treatment, best probably in the form of linearization, as explained briefly here. It would be useful to identify stability criteria to ensure that the time and distance step lengths are appropriate following introduction of T-dependent coefficients.
Note that matlab offers a PDE toolbox which might be useful to you, although I have not checked how you might use it in detail.