function [w1, w2] = vvrk42D(varargin)
% crankNicolson: uses Crank-Nicolson algorithm to approximate the solution
% to the parabolic PDE:
% u_{t}(x,t) - alpha^2 u_{xx}(x,t) = 0, 0<x<L, 0<t<T
% subject to the boundary conditions
% u(0,t) = u(L,t) = 0, 0<t<T
% and the initial conditions
% u(x,0) = f(x), 0<=x<=L
%
% arguments:
% L (vector 1by2) - upper bound of spatial (x) variable
% (Default L = [1,1])
% T (scalar) - upper bound of time (t) variable
% (Default T = 10)
% alpha (scalar) - square root of coefficient of u_{xx} term
% (Default alpha = 5)
% m (vector 1by2) - number of discrete spatial intervals
% (Default m = [100,100])
% n (scalar) - number of discrete time intervals
% (Default n = 100)
%
% w (m+1 x m+1 x n) - approximation to u(x,t) at discrete space/time positions
%
% author: Troy J. Winkstern
% email: tjw8191@rit.edu
% date: 30 Jan 2011
% parse input arguments
[L,T,alpha,m,n] = parseInputs(varargin{:});
L_x = L(1);
L_y = L(2);
m_x = m(1);
m_y = m(2);
% initialize h, k, lambda, and w
h_x = L_x/m_x;
h_y = L_y/m_y;
kappa = T./(200*n);
lambda_x = (alpha.^2).*kappa./(h_x.^2);
lambda_y = (alpha.^2).*kappa./(h_y.^2);
w1 = zeros(m_y+1,m_x+1,n);
w2 = zeros(m_y+1,m_x+1,n);
% initialize w1 (to sin(pix1/l)*sin(pix2/l)
for i=2:m_x
for j=2:m_y
w1(j,i,1) = sin(pi.*h_x.*(i-1)./L_x).*sin(pi.*h_y.*(j-1)./L_y);
end
end
% initialize w2 (to 4*sin(pix1/l)*sin(pix2/l)
for i=2:m_x
for j=2:m_y
w2(j,i,1) = sin(pi.*h_x.*(i-1)./L_x).*sin(pi.*h_y.*(j-1)./L_y);
end
end
for t=1:(n-1)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
f1 = (alpha^2)*laplacian(w1(:,:,t),h_y,h_x);
K_1a = kappa*f1;
g1a = w1(:,:,t) + ((K_1a)/2);
K_2a = kappa*(alpha^2)*laplacian(g1a,h_y,h_x);
g2a = w1(:,:,t) + K_2a/2;
K_3a = kappa*(alpha^2)*laplacian(g2a,h_y,h_x);
g3a = w1(:,:,t) + K_3a;
K_4a = kappa*(alpha^2)*laplacian(g3a,h_y,h_x);
rhs1 = w1(:,:,t) + (1/6)*(K_1a + 2*K_2a + 2*K_3a + K_4a);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
f2 = (alpha^2)*laplacian(w2(:,:,t),h_y,h_x);
K_1b = kappa*f2;
g1b = w2(:,:,t) + ((K_1b)/2);
K_2b = kappa*(alpha^2)*laplacian(g1b,h_y,h_x);
g2b = w2(:,:,t) + K_2b/2;
K_3b = kappa*(alpha^2)*laplacian(g2b,h_y,h_x);
g3b = w2(:,:,t) + K_3b;
K_4b = kappa*(alpha^2)*laplacian(g3b,h_y,h_x);
rhs2 = w2(:,:,t) + (1/6)*(K_1b + 2*K_2b + 2*K_3b + K_4b);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% went out to length 2m
X1 = sqrt(2./m_y).*imag(fft(rhs1, 2*m_y,1));
X1 = X1(1:m_y+1,:);
X2 = sqrt(2./m_y).*imag(fft(rhs2, 2*m_y,1));
X2 = X2(1:m_y+1,:);
Y1 = sqrt(2./m_x).*imag(fft(X1, 2*m_x,2));
Y1 = Y1(:,1:m_x+1);
Y2 = sqrt(2./m_x).*imag(fft(X2, 2*m_x,2));
Y2 = Y2(:,1:m_x+1);
% angles divided by m
a_x = repmat((pi./m_x).*(0:m_x),[m_y+1,1]);
a_y = repmat((pi./m_y).*(0:m_y).',[1,m_x+1]);
% changed lambda to negative
c1 = 1 + (((lambda_x)*(h_x)^2)*((lambda_y)*(h_y)^2)) + (1/6)*...
(((lambda_x)*cos(a_x)*(h_x)^2)*((lambda_y)*cos(a_y)*(h_y)^2));
c2 = 1 + (((lambda_x)*(h_x)^2)*((lambda_y)*(h_y)^2)) + (1/6)*...
(((lambda_x)*cos(a_x)*(h_x)^2)*((lambda_y)*cos(a_y)*(h_y)^2));
% went out to length 2m
Z1 = sqrt(2./m_y).*imag(fft(Y1./c1, 2*m_y,1));
Z1 = Z1(1:m_y+1,:);
Z2 = sqrt(2./m_y).*imag(fft(Y2./c2, 2*m_y,1));
Z2 = Z2(1:m_y+1,:);
R1 = sqrt(2./m_x).*imag(fft(Z1, 2*m_x,2));
R2 = sqrt(2./m_x).*imag(fft(Z2, 2*m_x,2));
w1(2:m_y,2:m_x,t+1) = R1(2:m_y,2:m_x);
w2(2:m_y,2:m_x,t+1) = R2(2:m_y,2:m_x);
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% subfunction parseInputs
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [L,T,alpha,m,n] = parseInputs(varargin)
% check number of input arguments
nargs = length(varargin);
error(nargchk(0,5,nargs));
% get/set n
if nargs<5
n = [];
else
n = varargin{5};
end
if isempty(n)
n = 100;
end
% check n has valid value
if (numel(n)>1) || (n<1) || ~isequal(round(n),n)
error('n must be a positive integer.');
end
% get/set m
if nargs<4
m = [];
else
m = varargin{4};
end
if isempty(m)
m = [100,100];
end
% check m has valid value
if (numel(m)~=2) || any(m<1) || any(~isequal(round(m),m))
error('m must be a vector of positive integers.');
end
% get/set alpha
if nargs<3
alpha = [];
else
alpha = varargin{3};
end
if isempty(alpha)
alpha = 5;
end
% check alpha has valid value
if numel(alpha)>1
error('alpha must be a real scalar.');
end
% get/set T
if nargs<2
T = [];
else
T = varargin{2};
end
if isempty(T)
T = 10;
end
% check T has valid value
if (numel(T)>1) || (T<=0)
error('T must be a positive real scalar.');
end
% get/set L
if nargs<1
L = [];
else
L = varargin{1};
end
if isempty(L)
L = [1,1];
end
% check L has valid value
if (numel(L)~=2) || any(L<=0)
error('L must be a positive vector with 2 elements.');
end
% warning if L not integer
if any(~isequal(round(L),L))
warning('L should be an vector of integers so that the inital condition takes on the value of 0 for t = 0 and t = T.');
endarning('L should be an integer so that the inital condition takes on the value of 0 for t = 0 and t = T.');
end
%another subfunction
function y = laplacian(x,h_y,h_x)
[m,n] = size(x);
y = zeros(m,n);
for i = 2:n-1
for j = 2:m-1
y(j,i) = (x(j-1,i)+x(j+1,i)-2*x(j,i))/(h_y.^2)+(x(j,i-1)+x(j,i+1)-2*x(j,i))/(h_x.^2);
end
end