DiffusionCrankNic.m

14. DiffusionCrankNic.m#

function [x, err] = TriDiagonal(a, b, c, f)
    % Solution of a linear system of equations with a tridiagonal
    % matrix.
    %
    %   a(k) x(k-1) + b(k) x(k) + c(k) x(k+1) = f(k)
    %
    % with a(1) = c(n) = 0.
    %
    % Input
    %   a(1:n), b(1:n), c(1:n) : the coefficients of the system
    %                            matrix
    %   f(1:n) : the right hand side vector
    %
    % Output
    %   x(1:n) : the solution to the linear system of equations, if
    %            no division by zero occurs
    %   err : = 0, if no division by zero occurs
    %         = 1, if division by zero is encountered
    n = length(f);
    alpha = zeros(n, 1);
    beta = zeros(n, 1);
    err = 0;

    if abs(b(1)) > eps(b(1))
        alpha(1) = -c(1) / b(1);
        beta(1) = f(1) / b(1);
    else
        err = 1;
        return;
    end

    for k = 2:n
        denominator = a(k) * alpha(k - 1) + b(k);

        if abs(denominator) > eps(denominator)
            alpha(k) =- c(k) / denominator;
            beta(k) = (f(k) - a(k) * beta(k - 1)) / denominator;
        else
            err = 1;
            return;
        end

    end

    if abs(a(n) * alpha(n - 1) + b(n)) > eps(b(n))
        x(n) = (f(n) - a(n) * beta(n - 1)) / (a(n) * alpha(n - 1) + b(n));
    else
        err = 1;
        return;
    end

    for k = n - 1:-1:1
        x(k) = alpha(k) * x(k + 1) + beta(k);
    end

end
warning: using the gnuplot graphics toolkit is discouraged

The gnuplot graphics toolkit is not actively maintained and has a number
of limitations that are unlikely to be fixed.  Communication with gnuplot
uses a one-directional pipe and limited information is passed back to the
Octave interpreter so most changes made interactively in the plot window
will not be reflected in the graphics properties managed by Octave.  For
example, if the plot window is closed with a mouse click, Octave will not
be notified and will not update its internal list of open figure windows.
The qt toolkit is recommended instead.
function frc = fForcing(N, t)
    %
    % Constructs the forcing term:
    %
    % Input
    %   N : to compute the mesh size
    %   t : the current time at the time interval midpoint
    %
    % Output
    %   f(1:N-1) : the array of values of the forcing function
    %
    frc = zeros(1, N - 1);
    h = 1.0 / N;

    for i = 1:N - 1
        x = i * h;
        tpx = 2.0 * pi * x;
        tpt = 2.0 * pi * t;
        frc(i) = 2.0 * pi * sin(tpt) * (1.0 - exp(sin(tpx))) ...
            + 4.0 * pi * pi * cos(tpt) * exp(sin(tpx)) ...
            * (sin(tpx) - cos(tpx) * cos(tpx));
    end

end
function ue = uExact(N, t)
    %
    % Constructs the exact solution:
    %
    % Input
    %   N : to compute the mesh size
    %   t : the current time
    %
    % Output
    %   ue(1:N+1) : the array of values of the exact solution
    %
    ue = zeros(1, N + 1);
    h = 1.0 / N;

    for i = 0:N
        x = i * h;
        tpx = 2.0 * pi * x;
        tpt = 2.0 * pi * t;
        ue(i + 1) = cos(tpt) * (exp(sin(tpx)) - 1.0);
    end

end
function [error] = DiffusionCrankNic(finalT, N, K, numPlots)
    %
    % This function computes numerical approximations to solutions
    % of the linear diffusion equation
    %
    % u_t - u_xx = f(x,t)
    %
    % using the Crank-Nicolson (CN) method on the domain [0,1]. The
    % forcing function, f, and the intial conditions are constructed
    % so that the true solution is
    %
    % u(x,t) = cos(2*pi*t)*(exp(sin(2*pi*x))-1.0).
    %
    % This can be easily modified.
    %
    % Input
    %   finalT : the final time
    %   N : the number of spatial grid points in [0,1]
    %   K : the number of time steps in the interval [0,finalT]
    %   numPlots : the number of output frames in the time interval
    %              [0,finalT]. numPlots must be a divisor of K.
    %
    % Output
    %   error: the max norm error of the CN scheme at t = finalT
    %
    error = 0.0;

    if N > 0 && N - floor(N) == 0
        h = 1.0 / N;
    else
        display('Error: N must be a positive integer.')
        return
    end

    if (K > 0 && K - floor(K) == 0) && finalT > 0
        tau = finalT / K;
    else
        display('Error: K must be a positive integer, and')
        display('       finalT must be positive.')
        return
    end

    mu = tau / (2.0 * h * h);

    x = 0:h:1.0;
    uoCN(1:N + 1) = uExact(N, 0.0);

    if mod(K, numPlots) == 0
        stepsPerPlot = K / numPlots;
    else
        display('Error: numPlots is not a divisor of K.')
        return
    end

    %
    % Define the tridiagonal system matrix to be inverted:
    %
    a(1) = 0.0;
    a(2:N - 1) = -mu;
    b(1:N - 1) = 1.0 + 2.0 * mu;
    c(1:N - 2) = -mu;
    c(N - 1) = 0.0;
    %
    % Main time loop:
    %
    for k = 1:numPlots

        for j = 1:stepsPerPlot
            kk = (k - 1) * stepsPerPlot + j;
            currTime = tau * (kk);
            lastTime = tau * (kk - 1.0);
            f(1:N - 1) = tau * (fForcing(N, currTime) ...
                +fForcing(N, lastTime)) / 2.0;

            for ell = 1:N - 1
                rhs(ell) = f(ell) + uoCN(ell + 1) + mu * (uoCN(ell) ...
                    -2.0 * uoCN(ell + 1) + uoCN(ell + 2));
            end

            [uCN(2:N), err] = TriDiagonal(a, b, c, rhs);
            uCN(1) = 0.0;
            uCN(N + 1) = 0.0;
            uoCN = uCN;
        end

        hf = figure(k);
        clf
        plot(x, uExact(N, currTime), 'k-', x, uCN, 'b-o')
        grid on,
        xlabel("x");
        ylabel('exact and approximate solutions');
        title(['Crank--Nicolson Approximation at T = ', ...
                num2str(currTime), ', h = ', num2str(h), ...
                ', and tau =', num2str(tau)]);
        legend("Exact", "Crank--Nicolson")
        set(gca, "xTick", 0:0.1:1)

        s1 = ['000', num2str(k)];
        s2 = s1((length(s1) - 3):length(s1));
        s3 = ['OUT/diff', s2, '.pdf'];
        %exportgraphics(gca, s3)
    end

    error = max(abs(uExact(N, currTime) - uCN));
end
finalT = 10;
N = 100;
K = 50;
numPlots = 25;
[error] = DiffusionCrankNic(finalT, N, K, numPlots)
error = 0.019031
_images/147df562aff40d5917bc23bfcb767893855a8c81d26432116b49b949ed17ea3d.png _images/5316521c9539c92b9afd9d1451e97a255d0828d8a6808ece9b9f4429eed49340.png _images/77c81e3c15060b79956e93d319bcd0628ec39ea4d43c5e4a49c617d478c43c1c.png _images/92b3eb0b880e19a7ad8baf7dc2c1ee9e0a7e6ee619bd55598498214e73dd68b2.png _images/b4d26720164614d9de2157f440bc60696c32383353d7e59dbcd3e240378b92e1.png _images/f29aba3df7ea3f44d5698a28bc0eeb5858e616eaa16493170a83e8d4062bdce7.png _images/5de32706b833efbcfa470e70b4315f810441c1e806bc177428c3fe878eda0050.png _images/44742bbb456a3050869918a21237c90de91d17dee20b73ef8ceeae3f07946d77.png _images/1ffbea3c319fa47519df65c5bc31b600411f3574fd1c1a782837c25ad801c573.png _images/e323bf7169a27fc9a7cf10764d07b4221a558e161a2542ab3e0cc08c79dae6bd.png _images/731ba5efc83e558c80d5040e91638686eec96ed257c21a2109a9204023db302f.png _images/0363f9c73fec4293c66377943492d07a300a6f322dfb32716949c257bbdc7c7b.png _images/736199a2de0cd26a1632d794305a5525fd7bf801ad3ca079e86aa4466276cb56.png _images/82b44fd5a86d42b10a29c1065fa3a18ec76e1f9cbac5d845b1f59a7bf269f04e.png _images/151b8f01615770f510edcabf7c13f8234e3d3c714b527a24bdeb2a552c6ba264.png _images/5d1591d170c6bba062ffd2560bd9ee5f86d7d0ada03084d334c717caa2f1d280.png _images/b6a945160e67cef6323e5dfb03e59e9be25e26abc6db837c66a6a2a051ac7769.png _images/024f2622f3b4ce848be955ead62f07a7f16f435f64a7ccadfe6bfe96d082dab8.png _images/b51f7fde16c8fe6b9ce58cc026efbacf511ba699295533538139451cf1c55df7.png _images/d5415e4c553a6d5b543e36a1bfb233d1e13412894f31919b82cad926710eb3ad.png _images/6e630eab9bf5a8e769067d439fb8b569e95e4e2f92a1d183f4593836aff8f9e1.png _images/203b1763d2afd5b53ba09f3df09d36e742d0d98355c6829abc969ba0ef2cbca9.png _images/e0bdd2d5c32c67f72428163e54e05027b74bc5f56f5770bbbd4cd2a2020ae5ce.png _images/5d32f06728bdf70e927c903c1eff6f6176c1442a913226b602124d7a3822290b.png _images/35cccfd0ceabced2c91e909d8e2b2426ac86ef4b3fb8c7bf9facc49b6f89a6d6.png