simWell()

function simWell
    wellName = "Mallet";

    % Current Values
    currentSurfacePosition = 0.0;
    currentSurfaceLoad = 0.0;
    currentDownholePosition = 0.0;
    currentDownholeLoad = 0.0;

    currentCard=Card(0);

    % Constants
    YM_STEEL=30.5;
    YM_FIBERGLASS=7.2;

    % User Inputs
    dt = 0.03333333;
    tubingHeadPressure = 40.0;
    fluidGradient = 0.45;
    stuffingBoxFriction = 100.0;
    numTapers = 3;
    tubingAnchorDepth = 0.0;
    pumpDiameter = 2.5;
    tubingInnerDiameter = 1.995;
    tubingOuterDiameter = 2.375;
    structuralRating = 320000;

    % Rod String Inputs
    c = [0.08 0.08 0.08];
    rodLength = [1475.0 1525.0 2000.0];
    rodDiameter = [1.0 0.875 0.750];
    rodYM = [YM_STEEL YM_STEEL YM_STEEL];
    rodWeightPerFoot = [Well.lookupRodWeightPerFoot(YM_STEEL, rodDiameter(1)) ...
        Well.lookupRodWeightPerFoot(YM_STEEL, rodDiameter(2)) ...
        Well.lookupRodWeightPerFoot(YM_STEEL, rodDiameter(3))];

    % Calculated Taper Parameters
    % Initialize
    a = zeros(1, numTapers, 'double');
    area = zeros(1, numTapers+1, 'double');
    pressure = zeros(1, numTapers, 'double');
    stretch = zeros(1, numTapers, 'double');
    force = zeros(1, numTapers, 'double');
    alpha = zeros(1, numTapers, 'double');
    xOverA = zeros(1, numTapers, 'double');
    factorArray = zeros(1, numTapers, 'double');
    lagIndex = zeros(1, numTapers, 'uint8');
    lengthRequired = zeros(1, numTapers, 'uint8');
    centerPoint = zeros(1, numTapers, 'uint8');
    count = zeros(1, numTapers, 'uint8');

    buoyantForceTotal = 0.0;
    rodDepthTotal = 0.0;
    rodWeightAirTotal = 0.0;
    rodWeightFluidTotal = 0.0;

    topPosArray = zeros(10, 250,'double');
    topLoadArray = zeros(10, 250, 'double');

    % cross-function variables for position and load
    % POSITION MUST ALWAYS BE CALLED FIRST.
    loadBefore = 0.0;
    loadAfter = 0.0;
    loadBefore3 = 0.0;
    loadAfter3 = 0.0;
    tempLoadValue = 0.0;

    for i = 1:numTapers
        area(i) = pi / 4 * rodDiameter(i) ^ 2;
    end

    for i = 1:numTapers
        a(i) = 1000.0 * sqrt(32.3 * rodYM(i) / rodWeightPerFoot(i));

    %     if i > 1
    %         rodDepth(i) = rodDepth(i-1) + rodLength(i);
    %     else
    %         rodDepth(i) = 0.0;
    %     end
        rodDepthTotal = rodDepthTotal + rodLength(i);

        if i > 1
            pressure(i) = pressure(i - 1) + fluidGradient * rodLength(i);
        else
            pressure(i) = tubingHeadPressure + fluidGradient * rodLength(i);
        end

    %     buoyantForce(i) = pressure(i) * (area(i+1) - area(i));
        buoyantForceTotal = buoyantForceTotal + pressure(i) * (area(i+1) - area(i));

    %     rodWeightAir(i) = rodWeightPerFoot(i) * rodLength(i);
        rodWeightAirTotal = rodWeightAirTotal + rodWeightPerFoot(i) * rodLength(i);

    %     rodWeightFluid(i) = rodWeightAir(i) + buoyantForce(i);
        rodWeightFluidTotal = rodWeightFluidTotal + buoyantForceTotal;
    end

    for j = 1:numTapers
        weightData = 0.0;
        annularForceData = 0.0;

        for i = j+1:numTapers
            weightData = weightData + rodWeightPerFoot(i) * rodLength(i);
        end

        for i = j:numTapers-1
            annularForceData = annularForceData + pressure(i) * (area(i) - area(i+1));
        end

        force(j) = (-area(numTapers) * pressure(numTapers)) + weightData - annularForceData;
        alpha(j) = (force(j) + rodWeightPerFoot(j) * rodLength(j)) / (rodYM(j) * power(10, 6) * area(j));

        if j > 1
            stretch(j) = stretch(j - 1) + alpha(j) * rodLength(j) - ...
                (rodWeightPerFoot(j) * power(rodLength(j), 2)) / ...
                (2 * rodYM(j) * power(10, 6) * area(j));
        else
            stretch(j) = 0.0 + alpha(j) * rodLength(j) - ...
                (rodWeightPerFoot(j) * power(rodLength(j), 2)) / ...
                (2 * rodYM(j) * power(10, 6) * area(j));
        end
    end

    for i = 1:numTapers
        xOverA(i) = rodLength(i) / a(i);
        lagIndex(i) = floor(rodLength(i) / (a(i) * dt));
        factorArray(i) = (xOverA(i) - double(lagIndex(i)) * dt) / dt;
        centerPoint(i) = lagIndex(i) + 2;
        lengthRequired(i) = 2 * (lagIndex(i) + 1) + 1;
    end

    m = csvread('Mallet No Tag.csv');
    for i = 2:size(m,1)
        [dhPos, dhLoad, status] = calc(m(i,1), m(i-1,1), m(i,2));
        if status == 1
            currentCard.push(m(i,1), m(i,2), dhPos, dhLoad);
        end
    end
%     currentCard.surfacePosition
%     scatter(currentCard.surfacePosition, currentCard.surfaceLoad)
    scatter(currentCard.downholePosition, currentCard.downholeLoad)





    % FUNCTION DECLARATIONS

    function pumpPosition = position(i)
        % Position Function
        ai = a(i);
        ci = c(i);
        factori = factorArray(i);
        lengthi = rodLength(i);
        lagi = double(lagIndex(i));
        yi = rodYM(i) * power(10, 6);
        areai = area(i);
        centeri = centerPoint(i);
        iBefore = centeri - lagIndex(i);
        iAfter = centeri + lagIndex(i);

        pumpPosition = exp(ci * lengthi / (2 * ai)) * (topPosArray(i, iAfter) + ...
            factori * topPosArray(i, iAfter+1));

        pumpPosition = pumpPosition + exp(ci * lengthi / (2 * ai)) * ...
            (topPosArray(i,iBefore-1) -topPosArray(i, iBefore));

        pumpPosition = 0.5 * pumpPosition;
        insideIntegral = 0.0;

        for jj = 1:2*lagi-1
            insideIntegral = insideIntegral + dt / (yi * areai) * ...
                (exp((-ci * (lagi - jj) *dt) / 2) * ...
                topLoadArray(i, iBefore + jj));
        end

        insideIntegral = insideIntegral + 0.5 * dt /(yi * areai) * ...
            (exp((-ci * lagi * dt) / 2) * topLoadArray(i, iBefore) + ...
            exp((-ci * -lagi * dt) / 2) * topLoadArray(i, iAfter));

        loadBefore = exp((-ci * lagi * dt) / 2) * topLoadArray(i, iBefore) + ...
            factori * (exp((-ci * (lagi + 1) * dt) / 2) * topLoadArray(i, iBefore-1) - ...
            exp((-ci * lagi * dt) / 2) * topLoadArray(i, iBefore));

        loadAfter = exp((-ci * -lagi * dt) / 2) * topLoadArray(i, iAfter) + ...
            factori * (exp((-ci * (-lagi - 1) * dt) / 2) * topLoadArray(i, iAfter+1) - ...
            exp((-ci * -lagi * dt) / 2) * topLoadArray(i,iAfter));

        insideIntegral = insideIntegral + 0.5 * factori * dt / (yi * areai) * ...
            (loadBefore + exp((-ci * lagi * dt) / 2) * topLoadArray(i, iBefore));

        insideIntegral = insideIntegral + 0.5 * factori * dt / (yi * areai) * ...
            (loadAfter + exp((-ci * -lagi * dt) / 2) * topLoadArray(i, iAfter));

        insideIntegral = 0.5 * ai * insideIntegral;
        pumpPosition = pumpPosition + insideIntegral;

        insideIntegral = 0.0;

        for jj = 1:2*lagi-1
            insideIntegral = insideIntegral + dt * (exp((-ci * (lagi - jj) * dt) / 2) * ...
                topPosArray(i, iBefore+jj));
        end

        insideIntegral = insideIntegral + 0.5 * dt * (exp((-ci * lagi * dt) / 2) * ...
            topPosArray(i, iBefore) + exp((-ci * -lagi * dt) / 2) * ... 
            topPosArray(i, iAfter));

        loadBefore3 = exp((-ci * lagi * dt) / 2) * topPosArray(i, iBefore) + ...
            factori * (exp((-ci * (lagi+1) * dt) / 2) * topPosArray(i, iBefore-1) - ...
            exp((-ci * lagi * dt) / 2) * topPosArray(i, iBefore));

        loadAfter3 = exp((-ci * -lagi * dt) / 2) * topPosArray(i, iAfter) + ...
            factori * (exp((-ci * (-lagi-1) * dt) / 2) * topPosArray(i, iAfter+1) - ...
            exp((-ci * -lagi * dt) / 2) * topPosArray(i, iAfter));

        insideIntegral = insideIntegral + 0.5 * factori * dt * (loadBefore3 + ...
            exp((-ci * lagi * dt) / 2) * topPosArray(i, iBefore));

        insideIntegral = insideIntegral + 0.5 * factori * dt * (loadAfter3 + ...
            exp((-ci * -lagi * dt) / 2) * topPosArray(i, iAfter));

        insideIntegral = -(ci * lengthi / 4) * 0.5 * (ci / (2 * ai)) * insideIntegral;
        tempLoadValue = insideIntegral / lengthi;
        pumpPosition = pumpPosition + insideIntegral;
    end

    function pumpLoad = load(i)
        % Load Function
        ai = a(i);
        ci = c(i);
        factori = factorArray(i);
        lengthi = rodLength(i);
        lagi = lagIndex(i);
        yi = rodYM(i) * power(10, 6);
        areai = area(i);
        centeri = centerPoint(i);
        iBefore = centeri - lagi;
        iAfter = centeri + lagi;

        pumpLoad = 0.5 * (ai / (yi * areai)) * (1 / ai) * (loadBefore + loadAfter);
        tempResult = yi * areai * pumpLoad;
        pumpLoad = pumpLoad - (ci * lengthi / 4) * (0.5 * (ci / (2 * ai))) * (1 / ai) * ...
            (loadBefore3 + loadAfter3);

        ptAfter = (topPosArray(i, iAfter+1) - topPosArray(i, iAfter-1)) / (2 * dt);
        ptBefore = (topPosArray(i, iBefore+1) - topPosArray(i, iBefore-1)) / (2 * dt);
        firstPart = (exp((ci * lengthi) / (2 * ai)) * ptAfter - exp((-ci * lengthi) / ...
            (2 * ai)) * ptBefore) / (2 * ai);

        firstPart = firstPart + (ci * exp((ci * lengthi) / (2 * ai)) * ... 
            topPosArray(i, iAfter) - ci * exp((-ci * lengthi) / (2 * ai)) * ...
            topPosArray(i, iBefore)) / (4 * ai);

        pumpLoad = yi * areai * (firstPart + pumpLoad); % + tempLoadValue ?
    end

    function [pumpPosition, pumpLoad, status] = calc(polishedRodPosition, lastPolishedRodPosition,...
        polishedRodLoad)
        pumpPosition = -1.0;
        pumpLoad = -1.0;
        status = -1;
        loadMult = 1.0;
        tapersAllowed = 1;

        for ii = 2:lengthRequired(1)
            topPosArray(1, ii-1) = topPosArray(1, ii);
            topLoadArray(1, ii-1) = topLoadArray(1, ii);
            topPosArray(1, lengthRequired(1)) = - polishedRodPosition / 12;

            if polishedRodPosition > lastPolishedRodPosition
                topLoadArray(1, lengthRequired(1)) = loadMult * ...
                    (polishedRodLoad - rodWeightFluidTotal) - stuffingBoxFriction;
            elseif polishedRodPosition > lastPolishedRodPosition
                topLoadArray(1, lengthRequired(1)) = loadMult * ...
                    (polishedRodLoad - rodWeightFLuidTotal) + stuffingBoxFriction;
            else
                topLoadArray(1, lengthRequired(1)) = loadMult * (polishedRodLoad - rodWeightFluidTotal);
            end
        end
        
        tap=1;
        while tap <= tapersAllowed
            count(tap) = count(tap) + 1;
            if count(tap) >= lengthRequired(tap)
                if tap+1 <= numTapers
                    % working our way down to the bottom of the well
                    for pti = 2:lengthRequired(tap+1)
                        topPosArray(tap+1, pti-1) = topPosArray(tap+1, pti);
                        topLoadArray(tap+1, pti-1) = topLoadArray(tap+1, pti);
                    end
                    pumpPosition = position(tap);
                    pumpLoad = load(tap);
                    status = 0;
                    topPosArray(tap+1, lengthRequired(tap+1)) = pumpPosition;
                    topLoadArray(tap+1, lengthRequired(tap+1)) = pumpLoad;
                else
                    pumpPosition = -12 * (position(tap) + stretch(numTapers));
                    pumpLoad = load(tap) + force(numTapers);
                    status = 1;
                end
                count(tap) = count(tap) - 1;
                tapersAllowed = tapersAllowed + 1;
                if tapersAllowed > numTapers
                    tapersAllowed = numTapers;
                end
            end
            tap = tap + 1;
        end
    end
end






