function varargout = pdeSolve(pdeFun, tIn, u0, bc, varargin)
%PDESOLVE   Solve PDEs using Chebfun.
%
%   PDESOLVE() solves solves an initial-boundary value problem via a method of
%   lines approach. See @CHEBFUN/PDE15S() for documentation.
%
% See also PDE15S, PDE23T, PDESET.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

global DIFFORDER SYSSIZE
DIFFORDER = 0;
SYSSIZE = 0;

% Default options:
tol = 1e-6;             % 'eps' in Chebfun terminology
% The default behaviour with no outputs is to plot. If the method is called with
% outputs, by default, we don't plot.
doPlot = ( nargout == 0 );
doHold = false;         % Hold plot?
plotOpts = {'-'};       % Plotting style
adjustBCs = true;       % Adjust inconsistent BCs
throwBCwarning = true;  % Throw a warning for inconsistent BCs
timeChunks = 51;        % Default number of time slices if not specified

% Parse the variable inputs:
if ( numel(varargin) == 2 )
    opt = varargin{1};
    opt.N = varargin{2};
elseif ( numel(varargin) == 1 )
    if ( isstruct(varargin{1}) )
        opt = varargin{1};
    else
        opt = pdeset;
        opt.N = varargin{1};
    end
else
    opt = pdeset;
end
optN = opt.N;
if ( isempty(optN) )
    optN = NaN;
end

% Get the domain:
DOMAIN = domain(u0, 'ends');

if ( isfield(opt, 'AdjustBCs') && ~isempty(opt.AdjustBCs) && ~opt.AdjustBCs )
    throwBCwarning = false;
    adjustBCs = false;
end

% PDE solver options:
if ( ~isempty(opt.Eps) )
    tol = opt.Eps;
end
if ( ~isempty(opt.Plot) )
    doPlot = strcmpi(opt.Plot, 'on');
end
if ( ~isempty(opt.HoldPlot) )
    doHold = strcmpi(opt.HoldPlot, 'on') || strcmp(opt.HoldPlot, '1');
end
if ( ~isempty(opt.PlotStyle) )
    plotOpts = opt.PlotStyle;
end
if ( ~isempty(opt.ODESolver) )
    ODESOLVER = opt.ODESolver;
else
    ODESOLVER = @ode15s;
end

% Set time chunks:
if ( length(tIn) == 2 )
    tt = linspace(tIn(1), tIn(2), timeChunks);
else
    tt = tIn;
end

userMassSet = false;
ISPERIODIC = false;
DONE = false;
COUNTER = 1;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%%%%%%%%%%%%%%%%%%%%%%%%%  PARSE INPUTS TO PDEFUN  %%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Determine the size of the system, i.e., number of dependent variables.
SYSSIZE = min(size(u0));
[pdeFun, varNamesParsed] = parseFun(pdeFun);
if ( isfield(opt, 'difforder') )
    DIFFORDER = opt.difforder;
else
    getDIFFORDER(pdeFun, DOMAIN);
end

if ( (max(DIFFORDER) < 2) && isequal(ODESOLVER, @ode15s) )
    warning('CHEBFUN:PdeSolver', ...
        'PDE23T() is recommended for non-diffusive problems.');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  EVENT SETUP  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if ( ~isnan(optN) )
    opt.OutputFcn = @nonAdaptiveEvent;
else
    opt.OutputFcn = @adaptiveEvent;
end

    function status = nonAdaptiveEvent(t, U, flag)
        % This event is called at the end of each chunk in non-adaptive mode.
        status = false;
        if ( ~isempty(flag) )
            return
        end

        % Sometimes we get given more than one time slice.
        for kk = 1:numel(t)

            if ( ~any(abs(tSpan - t(kk)) < 1e-6) )
                % This is not a designated time slice!
                continue
            end

            % Reshape solution:
            Uk = reshape(U(:,kk), n, SYSSIZE);
            uCurrent = chebfun(Uk, DOMAIN, 'tech', techHandle);
            tCurrent = t(kk);
            % Store for output:
            COUNTER = COUNTER + 1;
            uOut{COUNTER} = uCurrent;

            % Plot current solution:
            if ( doPlot )
                plotFun(uCurrent, t(kk));
            end
        end

        if ( guiFlag )
            status = guiEvent(status);
        end

    end

    function status = adaptiveEvent(t, U, flag)
        % This event is called at the end of each chunk in adaptive mode.

        status = false;
        if ( ~isempty(flag) )
            return
        end

        % Sometimes we get given more than one time slice.
        for kk = 1:numel(t)
            % Reshape solution:
            Uk = reshape(U(:,kk), currentLength, SYSSIZE);

            % Happiness check:
            c = (1+sin(1:SYSSIZE)).'; % Arbitrarily linear combination.
            Uk2 = (Uk*c/sum(c));
            uk2 = tech.make(Uk2, pref);
            [ishappy, cutoff] = happinessCheck(uk2, Uk2, [], [], pref); %#ok<ASGLU>

            if ( ishappy )

                if ( ~any(abs(tSpan - t(kk)) < 1e-6) )
                    % This is not a designated time slice!
                    continue
                end

                % Store these values:
                tCurrent = t(kk);
                uCurrent = chebfun(Uk, DOMAIN, 'tech', techHandle);
                uCurrent = simplify(uCurrent);

                COUNTER = COUNTER + 1;
                uOut{COUNTER} = uCurrent;
                tOut(COUNTER) = tCurrent;

                % Plot current solution (if plotting is on)
                if ( doPlot )
                    plotFun(uCurrent, tCurrent);
                end
                % TODO: Re-insert this?
%                 % If we have 2.5 times as many coefficients as we need then
%                 % shorten the representation and cause the integrator to stop.
%                 if ( cutoff < 0.4*n && n > 17)
%                     currentLength = round(1.25*cutoff)';
%                     %currentLength = floor( currentLength / 1.5  );
%                     currentLength = currentLength + 1 - rem(currentLength,2);
%                     currentLength = max(currentLength, 17);
%                     status = true;
%                     return
%                 end

            else

                % Increase length and bail out:
                currentLength = 2*currentLength-1;
                status = true;
                break

            end

            if ( guiFlag )
                status = guiEvent(status);
            end

        end

    end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%  PLOTTING SETUP  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Determine which figure to plot to (for CHEBGUI) and set default display values
% for variables.
YLim = opt.YLim;
gridOn = 0;
guiFlag = false;
if ( isfield(opt, 'handles') )
    if ( opt.handles.gui )
        guiFlag = true;
        axesSol = opt.handles.fig_sol;
        axesNorm = opt.handles.fig_norm;
        gridOn = opt.handles.guifile.options.grid;
        solveButton = opt.handles.button_solve;
        clearButton = opt.handles.button_clear;
        panelSol = opt.handles.panel_figSol;
    end
    varNames = opt.handles.varnames;
    xLabel = opt.handles.indVarName{1};
    tlabel = opt.handles.indVarName{2};
    fontsize = opt.handles.fontsizePanels;
    set(axesSol, 'fontsize', fontsize);
else
    % Obtain the correct variable names from the parsing of the operator
    if ( length(varNamesParsed) == numColumns(u0) )
        % Space and time variables did not get passed explicitly to operator
        tlabel = 't';
        xLabel = 'x';
    else
        tlabel = varNamesParsed{1};
        xLabel = varNamesParsed{2};
    end
    varNames = varNamesParsed(end-numColumns(u0)+1:end);
    if ( doPlot )    % Set up ploting in non-gui mode
        axesSol = gca;
        cla(axesSol)
        xlabel(axesSol, xLabel);
        % Bring attention to the figure
        shg
    end
end

% Set plot options
if ( doPlot )

    % Use linear scale. Always.
    if ( ~ishold )
        set(gca, 'XScale', 'Linear', 'YScale', 'Linear');
    end

    set(axesSol, 'NextPlot', 'replacechildren');

    % Fix x limits
    set(axesSol, 'xLim', u0.domain);

    % Fix y limits if they were specified
    if ( ~isempty(YLim) )
        set(axesSol, 'ylim', YLim);
    end

    % Initialize lines for plotting
    hLines = plot(axesSol, DOMAIN, NaN(length(DOMAIN), numColumns(u0)), ...
        plotOpts{:});

    % Grid on?
    if ( gridOn )
        grid(axesSol, 'on')
    else
        grid(axesSol, 'off')
    end

    % Hold on?
    if ( doHold )
        hold(axesSol, 'on')
    else
        hold(axesSol, 'off')
    end

    % For plotting, it's useful to know whether we're running in old or new
    % Matlab graphics mode
    if ( ~verLessThan('matlab', '8.4') )
        newMatlabVersion = true;
    else
        newMatlabVersion = false;
    end
end

    function status = guiEvent(status)
        %GUIEVENT   Deal with GUI events ('stop', 'pause', etc).
        % OUTPUTS:
        %   status = true exits the current time chunk.
        %   DONE = true exits PDE solver. (Note DONE is a GLOBAL variable).

        % Interrupt computation if stop or pause button is pressed in the GUI.
        if ( strcmp(get(solveButton, 'String'), 'Solve') )
            % Stop.
            tt = tt( tt <= tCurrent );
            uOut(COUNTER+1:end) = [];
            status = true;
            DONE = true;
        elseif ( strcmp(get(clearButton, 'String'), 'Continue') )
            % Wait, pause.

            % Plot a waterfall plot to the bottom figure window:
            axes(axesNorm)
            uuTmp = prepare4output(uOut(1:COUNTER));
            if ( SYSSIZE == 1 )
                waterfall(uuTmp, tt(tt<=tCurrent), 'linewidth', 2);
            else
                cols = get(0, 'DefaultAxesColorOrder');
                waterfall(uuTmp, tt(tt<=tCurrent), 'linewidth', 2, ...
                    'EdgeColors', cols);
            end
            xlabel(xLabel), ylabel(tlabel), zlabel(varNames)
            if ( gridOn )
                grid on
            end
            view([322.5 30])
            box off
            axes(axesSol)
            % Hang around until 'CONTINUE' or 'STOP' is presed.
            waitfor(clearButton, 'String');
            % Call again to see if 'STOP' was pressed.
            status = guiEvent(status);

            set(axesNorm, 'fontsize', fontsize);
        end
    end

    function plotFun(U, t)
        %PLOTFUN    Plot current solution U at a time t.

        % Obtain plotting data for the solution
        Udata = plotData(U);
        UdataX = Udata.xLine;
        UdataY = Udata.yLine;

        % Update the YData of the lines plotted
        if ( ~doHold )
            for hCounter = 1:length(hLines)
                set(hLines(hCounter), 'XData', UdataX);
                set(hLines(hCounter), 'YData', UdataY(:, hCounter)');
            end
        else
            % Reset color cycle prior to point plot if running R2014b.
            if ( newMatlabVersion )
                set(axesSol, 'ColorOrderIndex', 1);
            end
            hLines = plot(axesSol, UdataX, UdataY, plotOpts{:});
        end

        % Update the title, either of the GUI panel or the figure
        if ( guiFlag )
            set(panelSol, 'Title', sprintf('%s = %.3f', tlabel, t))
        elseif ( nargin > 1 )
            title(sprintf('%s = %.3f', tlabel, t))
        end

        % Update the plot
        drawnow

    end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%%%%%%%%%%%%%%%%%%%%%%%% PARSE BOUNDARY CONDITIONS %%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if ( ischar(bc) && (strcmpi(bc, 'neumann') || strcmpi(bc, 'dirichlet')) )
    bc = struct( 'left', bc, 'right', bc);
elseif ( iscell(bc) && numel(bc) == 2 )
    bc = struct( 'left', bc{1}, 'right', bc{2});
end
if ( isstruct(bc) && ~isfield(bc, 'middle') )
    bc.middle.op = [];
end

% Initialise some global variables.
numLinBCs = 0; % linear BCS (total)
numLBCs = 0;   % nonlinear LBCs
numMBCs = 0;   % nonlinear other constraints
numRBCs = 0;   % nonlinear RBCs
BCRHS = {};    % RHS values for BCs

    % Differentiation matrices at left and right endpoints:
    function D = neumannLeft(n)
        x = -cos(pi*(1:n-2)/(n-1));
        D = [0, 2./(1+x), .5];
        D(1:2:end) = -D(1:2:end);
        D(1) = -sum(D);
    end
    function D = neumannRight(n)
        x = -cos(pi*(1:n-2)/(n-1));
        D = [-.5, 2./(x-1), 0];
        D(end:-2:1) = -D(end:-2:1);
        D(end) = -sum(D);
    end

if ( ischar(bc) && any(strcmpi(bc, {'periodic', 'trig'})) )
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%% PERIODIC BCS  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    ISPERIODIC = true;

    % One can still use a Chebyshev basis and enforce periodic conditions by
    % enforcing suitable constraints on the derivative. However, using a
    % periodic basis (for now, trigtech) is much more efficient.
%     r = cell(sum(DIFFORDER), 1);
%     count = 1;
%     for j = 1:SYSSIZE
%         for k = 0:DIFFORDER(j)-1
%             c = (diff(DOMAIN)/2)^k;
%             A = @(n) [1 zeros(1, n-2) -1]*chebcolloc2.diffmat(n, k)*c;
%             r{count} = @(n) [zeros(1, (j-1)*n) A(n) zeros(1,(SYSSIZE-j)*n)];
%             count = count + 1;
%         end
%     end
%     bc = struct( 'left', [], 'right', []);
%     bc.left.op = r(1:2:end);
%     bc.right.op = r(2:2:end);
%     BCRHS = num2cell(zeros(1, numel(r)));

else
    %%%%%%%%%%%%%%%%%%%%%%%%%%% NONPERIODIC BCS  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    if ( isfield(bc, 'left') && ~isfield(bc, 'right') )
        bc.right = [];
    elseif ( isfield(bc, 'right') && ~isfield(bc, 'left') )
        bc.left = [];
    elseif ( ~isfield(bc, 'left') && ~isfield(bc, 'right') )
        bc = struct('left', [], 'right', [], 'middle', bc);
    end

    % Deal with struct and numeric input:
    bc.left = dealWithStructInput(bc.left);
    bc.right = dealWithStructInput(bc.right);

    % Temporary chebdouble for evaluating BC function handles:
    uTmp = chebdouble(ones(1, SYSSIZE), DOMAIN);

    %% LEFT BCS
    if ( isempty(bc.left) )
        bc.left = struct('op', []);

    elseif ( ischar(bc.left) || (iscell(bc.left) && ischar(bc.left{1})) )
        %%%%%%%%%%%%%%%%%%% DIRICHLET AND NEUMANN BCS (LEFT) %%%%%%%%%%%%%%%%%
        if ( iscell(bc.left) )
            v = bc.left{2};
            bc.left = bc.left{1};
        else
            v = 0;
        end
        if ( ~isnumeric(v) )
            error('CHEBFUN:CHEBFUN:pde15s:nonNumericVal1', ...
                'For BCs of the form {char, val} val must be numeric.')
        end
        BCRHS = num2cell(repmat(v, SYSSIZE, 1));
        numLinBCs = numel(v)*SYSSIZE;

        if ( strcmpi(bc.left, 'dirichlet') )
            A = @(n) [1, zeros(1, n-1)];
        elseif ( strcmpi(bc.left, 'neumann') )
            A = @(n) neumannLeft(n)*(diff(DOMAIN)/2);
        else
            error('CHEBFUN:CHEBFUN:pde15s:bcSyntax1', 'Unknown BC syntax');
        end
        bc.left = struct('op', []);
        bc.left.op = cell(SYSSIZE, 1);
        for k = 1:SYSSIZE
            bc.left.op{k} = ...
                @(n) [zeros(1,(k-1)*n),  A(n),  zeros(1, (SYSSIZE-k)*n)];
        end

    elseif ( numel(bc.left) == 1 && isa(bc.left, 'function_handle') )
        %%%%%%%%%%%%%%%%%%%%%%%  GENERAL BCS (LEFT)  %%%%%%%%%%%%%%%%%%%%%%%%%%
        opLBC = parseFun(bc.left, 'lbc');
        numLBCs = max(size(opLBC(0, DOMAIN(1), uTmp)));
        bc.left = struct('op', []);

    else
        error('CHEBFUN:CHEBFUN:pde15s:bcSyntax2', 'Unknown BC syntax');

    end

    %% MIDDLE BCS / OTHER CONSTRAINTS
    if ( isfield(bc, 'middle') && isa(bc.middle, 'function_handle') )
        %%%%%%%%%%%%%%%%%%%%%  GENERAL BCS (MIDDLE)  %%%%%%%%%%%%%%%%%%%%%%%%%%
        opMBC = parseFun(bc.middle, 'bc');
        numMBCs = max(size(opMBC(0, mean(DOMAIN), uTmp)));
        bc.middle = [];

    end

    %% RIGHT BCS
    if ( isempty(bc.right) )
        bc.right = struct('op', []);

    elseif ( ischar(bc.right) || (iscell(bc.right) && ischar(bc.right{1})) )
        %%%%%%%%%%%%%%%%%%% DIRICHLET AND NEUMANN BCS (RIGHT) %%%%%%%%%%%%%%%%%
        if ( iscell(bc.right) )
            v = bc.right{2};
            bc.right = bc.right{1};
        else
            v = 0;
        end
        if ( ~isnumeric(v) )
            error('CHEBFUN:CHEBFUN:pde15s:nonNumericVal1', ...
                'For BCs of the form {char, val} val must be numeric.')
        end
        BCRHS = [BCRHS, num2cell(repmat(v, SYSSIZE, 1))];
        numLinBCs = numLinBCs + numel(v)*SYSSIZE;

        if ( strcmpi(bc.right, 'dirichlet') )
            A = @(n) [zeros(1, n-1), 1];
        elseif ( strcmpi(bc.right, 'neumann') )
            A = @(n) neumannRight(n)*(diff(DOMAIN)/2);
        else
            error('CHEBFUN:CHEBFUN:pde15s:bcSyntax3', 'Unknown BC syntax');
        end
        bc.right = struct('op', []);
        bc.right.op = cell(SYSSIZE, 1);
        for k = 1:SYSSIZE
            bc.right.op{k} = ...
                @(n) [zeros(1,(k-1)*n),  A(n),  zeros(1,(SYSSIZE-k)*n)];
        end

    elseif ( numel(bc.right) == 1 && isa(bc.right, 'function_handle') )
        %%%%%%%%%%%%%%%%%%%%%%  GENERAL BCS (RIGHT)  %%%%%%%%%%%%%%%%%%%%%%%%%%
        opRBC = parseFun(bc.right, 'rbc');
        numRBCs = max(size(opRBC(0, DOMAIN(end), uTmp)));
        bc.right = struct('op', []);

    else
        error('CHEBFUN:CHEBFUN:pde15s:bcSyntax4', 'Unknown BC syntax');

    end

end

% Total number of BCs:
numBCs = numLinBCs + numLBCs + numMBCs + numRBCs;

if ( isstruct(bc) && ~isfield(bc, 'middle') )
    bc.middle.op = [];
end

%% %%%%%%%%%%%%%%%%%%%%%%% SUPPORT FOR COUPLED BVP-PDES! %%%%%%%%%%%%%%%%%%%%%%%
% Experimental feature for coupled ode/pde systems: (An entry equal to 1 denotes
% that the corresponding variable appears with a time derivative. 0 otherwise.)
if ( isfield(opt, 'PDEflag') && ~isempty(opt.PDEflag) )
    pdeFlag = opt.PDEflag;
else
    pdeFlag = true;
end
if ( numel(pdeFlag) == 1 )
    pdeFlag = repmat(pdeFlag, 1, SYSSIZE);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% PERIODIC %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if ( ~ISPERIODIC )
    techHandle = @chebtech2;
    points = @chebpts;
    mydouble = @chebdouble;
else
    techHandle = @trigtech;
    points = @trigpts;
    mydouble = @trigdouble;
    u0 = chebfun(u0, 'trig', 'eps', tol);
    u0 = simplify(u0, tol);
end
tech = techHandle();

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%%%%%%%%%%%%%%%%%% SETUP TOLERANCES AND INITIAL CONDITION %%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% ODE tolerances:
aTol = odeget(opt, 'AbsTol', tol);
rTol = odeget(opt, 'RelTol', tol);
if ( isnan(optN) )
    aTol = min(aTol, tol);
    rTol = min(rTol, tol);
end
opt.AbsTol = aTol;
opt.RelTol = rTol;

% Check for (and try to remove) piecewise initial conditions:
u0Trans = u0(1).isTransposed;
if ( u0Trans )
    u0 = transpose(u0);
end
for k = 1:numel(u0)
    if ( numel(u0(k).funs) > 1 )
        u0(k) = merge(u0(k), 'all', 1025, tol);
        if ( u0(k).nfuns > 1 )
            error('CHEBFUN:CHEBFUN:pde15s:piecewise', ...
                'Piecewise initial conditions are not supported.');
        end
    end
end

% Simplify initial condition to tolerance or fixed size in optN:
if ( isnan(optN) )
    u0 = simplify(u0);
else
    for k = 1:numel(u0)
        u0(k).funs{1}.onefun = prolong(u0(k).funs{1}.onefun, optN);
    end
end

% Initial condition:
uCurrent = u0;
% Storage:
uOut = cell(1, numel(tt));
uOut{1} = uCurrent;
tOut(1) = tt(1);

% Initialise variables for ONESTEP():
M = []; P = []; n = []; B = 0;

% Set the preferences:
pref = tech.techPref();
pref.chebfuneps = tol;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%% TIME CHUNKS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Plot initial condition:
currentLength = length(u0);
if ( doPlot )
    plotFun(u0, tt(1));
    leg = legend(axesSol, varNames);
    set(leg, 'HandleVisibility', 'off')
    drawnow
end
DONE = false;

if ( ~isnan(optN) )
    % Non-adaptive in space:
    currentLength = optN;
    tSpan = tt;
    solvePDE(tSpan); % Do all chunks at once!

else
    % Adaptive in space

    % Min-length is 9
    currentLength = max(currentLength, 9);
    tCurrent = tt(1);
    tSpan = tt;
    while ( tCurrent < tt(end) && ~DONE )
        tSpan(tSpan < tCurrent) = [];
        solvePDE(tSpan);
    end

end

if ( doPlot && ~doHold )
    hold off
end

% Ensure tOut is a column vector
tOut = tOut(:);

uOut = prepare4output(uOut);

    function uOut = prepare4output(uIn)
        % If we only had one dependent variable, return an array valued CHEBFUN
        % instead of a QUASIMATRIX.
        if ( SYSSIZE == 1 )
            uOut = horzcat(uIn{:});
        else
            blocks = cell(SYSSIZE, numel(uIn));
            for kk = 1:SYSSIZE
                blocks(kk,:) = cellfun(@(u) extractColumns(u, kk), uIn, ...
                    'UniformOutput', false);
            end
            uOut = chebmatrix(blocks); % CHEBMATRIX
        end
    end

switch nargout
    case 0
    case 1
        if ( length(tIn) == 2 )     % Only T_start and T_end were given.
            uOut = uOut(:,[1,end]); % Strip intermediate steps.
        end
        varargout{1} = uOut;
    case 2
        varargout{1} = tOut;
        varargout{2} = uOut;
    case 1 + size(uOut, 1)
        varargout{1} = tOut;
        for varCounter = 1:size(uOut, 1)
            varargout{varCounter + 1} = chebfun(uOut(varCounter,:)); %#ok<AGROW>
        end
    otherwise
        error('CHEBFUN:CHEBFUN:pdeSolve:nargout', ...
            'Incorrect number of output arguments.');
end

clear global

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  ONESTEP  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    function solvePDE(tSpan)
        % Constructs the result of one time chunk at fixed discretization.

        % Evaluate the chebfun at discrete points:
        x = points(currentLength, DOMAIN);
        U0 = feval(uCurrent, x);

        if ( ISPERIODIC )

            % The discretisation length
            n = currentLength;

        elseif ( isempty(n) || (n ~= currentLength) )
            % This depends only on the size of n. If this is the same, reuse!

            % The new discretisation length
            n = currentLength;

            % Linear constraints:
            if ( numLinBCs > 0 )
                bcop = [bc.left.op ; bc.middle.op ; bc.right.op];
                B = cell2mat(cellfun(@(f) feval(f, n), bcop, 'UniformOutput', false));
            end

            % Zero matrix for algebraic (boundary) terms:
            Z = zeros(numBCs, SYSSIZE*n);
            % Initialise evaluated BC rhs vector:
            q = zeros(numBCs, 1);

            % Project / mass matrix.
            P = cell(1, SYSSIZE);
            M = cell(1, SYSSIZE);

            for kk = 1:SYSSIZE
%                 xk = chebpts(n-DIFFORDER(kk), DOMAIN, 1);
                xk = chebpts(n-DIFFORDER(kk)+2, DOMAIN, 2); xk([1,end]) = [];
                P{kk} = barymat(xk, x);
                M{kk} = pdeFlag(kk)*P{kk};
            end
            P = [ Z ; blkdiag(P{:}) ];
            M = [ Z ; blkdiag(M{:}) ];

            % Multiply by user-defined mass matrix
            if ( userMassSet )
                M = feval(userMass, n)*M;
            end

            % ODE options: (mass matrix)
            opt = odeset(opt, 'Mass', M, 'MassSingular', 'yes', ...
                'MStateDependence', 'none');

        end

        if ( ~ISPERIODIC )
            % We have to ensure the starting condition satisfies the boundary
            % conditions, or else we will get a singularity in time. We do this
            % by tweaking the BC values to match the reality. Changing the IC
            % itself is much trickier. Find out what the BC deviance from
            % nominal really is:
            BCVALOFFSET = 0;             % recover nominal value in next call
            F = odeFun(tSpan(1), U0(:)); % also assigns values to "q"

            % If this is for the initial chunk, check whether the initial
            % condition nearly satisfies the BCs. We're quite lax about this,
            % because discretization at low N can cause derivatives to look
            % fairly bad.
            BCrows = 1:numBCs;
            if ( throwBCwarning && (length(uOut) > 1) && ...
                    (norm(F(BCrows)) > 0.05*norm(F)) )
                warning('CHEBFUN:CHEBFUN:pde15s:BadIC',...
                    'Initial state may not satisfy the boundary conditions.')
                throwBCwarning = false;
            end
            if ( adjustBCs )
                BCVALOFFSET = F(BCrows) - q;
            end
        end

        % Solve ODE over time chunk with the selected solver:
        try
            [~, ~] = ODESOLVER(@odeFun, tSpan, U0, opt);
        catch ME
            if ( strcmp(ME.identifier, 'MATLAB:odearguments:SizeIC') )
                error('CHEBFUN:CHEBFUN:pde15s:dims', ...
                    'Dimension mismatch. Check boundary conditions.');
            else
                rethrow(ME)
            end
        end

        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        %% %%%%%%%%%%%%%%%%%%%%%%%%%%%  ODEFUN  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function F = odeFun(t, U)
            % This is what the ODESOLVER() calls.

            % Reshape to n by SYSSIZE:
            U = reshape(U, n, SYSSIZE);

            % Evaluate the PDEFUN:
            myU = mydouble(U, DOMAIN);
            F = double(pdeFun(t, x, myU));
            F = F(:);

            if ( ISPERIODIC )
                return
            end

            % Enforce boundary constraints:

            % Project:
            F = P*F;

            % Replacements for the BC algebraic conditions:
            if ( numLinBCs > 0 )
                % Get the right-hand sides: (may be time-dependent)
                for l = 1:numLinBCs
                    if ( isa(BCRHS{l}, 'function_handle') )
                        q(l,1) = feval(BCRHS{l}, t);
                    else
                        q(l,1) = BCRHS{l};
                    end
                end
                indx = 1:numLinBCs;
                F(indx) = B*U(:) - q(indx);
            else
                indx = 0;
            end

            % Replacements for the nonlinear BC conditions:
            if ( numLBCs > 0 )
                indx = indx(end) + (1:numLBCs);
                fTmp = double(opLBC(t, x, myU));
                F(indx) = fTmp(1, :);
            end
            if ( numMBCs > 0 )
                % TODO: This won't work if there are also left nonlin BCs
                indx = indx(end) + (1:numMBCs);
                F(indx) = double(opMBC(t, x, myU));
            end
            if ( numRBCs > 0 )
                indx = indx(end) + (1:numRBCs);
                fTmp = double(opRBC(t, x, myU));
                F(indx) = fTmp(end,:);
            end

            % Adjust BC rows by the needed offset from original time:
            indx = 1:numBCs;
            F(indx) = F(indx) - BCVALOFFSET;

        end
    end

end

%% %%%%%%%%%%%%%%%%%%%%%%%%%%  MISC  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function [outFun, varNamesParsed] = parseFun(inFun, bcFlag)
% Rewrites the input function handle to call the right DIFF, SUM, methods, etc,
% and convert the input @(t, x, u, v, w, ...) to @(t, x, U).
%
% For PARSEFUN(INFUN), if the INFUN has only one independent variable as an
% input, we assume this is x. PARSEFUN(INFUN, 'lbc') or PARSEFUN(INFUN, 'rbc')
% will assume the variable is t. PARSEFUN(INFUN, 'bc') will assume space, but
% throw a warning.

global SYSSIZE

% Ensure backwards compatibility by processing the input function.
[inFun, varNamesParsed] = backCompat(inFun);

% V4: We don't accept only time or space as input args (i.e., both or nothing).
% V5: Actually, we now accept @(u, x) diff(u, 2) + sin(x).*u (for CHEBOPs).
Nind = nargin(inFun) - SYSSIZE;
if ( Nind == 0 )
    outFun = @(t, x, u) conv2cell(inFun, u);
elseif ( Nind == 1 )
    if ( nargin > 1 )
        if ( strcmp(bcFlag, 'bc') )
            warning('CHEBFUN:CHEBFUN:pde15s:bcInput', ...
                ['Please input both time and space independent variable to ' ...
                 '.BC function handle inputs.\nAssuming given variable is ' ...
                 'time.']);
             outFun = @(t, x, u) conv2cell(inFun, u, x);
        else
            outFun = @(t, x, u) conv2cell(inFun, u, t);
        end
    else
        outFun = @(t, x, u) conv2cell(inFun, u, x);
    end
elseif ( Nind == 2 )
    outFun = @(t, x, u) conv2cell(inFun, u, t, x);
else
    error('CHEBFUN:CHEBFUN:pde15s:inputs_ind', ...
        ['Incorrect number of independent variables in input function. ' ...
         '(Must be 0, 1, or 2).']);
end

end

function newFun = conv2cell(oldFun, u, varargin)
% This function allows the use of different variables in the anonymous function,
% rather than using the clunky quasi-matrix notation.
%
% Inputs:
%   oldFun in the original function handle
%   u will be the solution we evaluate at
%   varargin possibly contains the time and space variables
% Output
%   newFun the modified function handle

global SYSSIZE
% Note. This is faster than mat2cell!
tmpCell = cell(1, SYSSIZE);
for qk = 1:SYSSIZE
    tmpCell{qk} = extractColumns(u, qk);
end
newFun = oldFun(varargin{:}, tmpCell{:});
end

function getDIFFORDER(pdeFun, DOMAIN)
% Extract the DIFFORDER by evaluating the operator at some NaN values.
global DIFFORDER SYSSIZE
% Find the order of each of the variables:
tmp = chebdouble(zeros(SYSSIZE), DOMAIN);
v = pdeFun(0, 0, tmp);
DIFFORDER = get(v, 'diffOrder');
DIFFORDER = max(DIFFORDER, 0);
end

function out = dealWithStructInput(in)
% Parse the inputs for BCs.
% Throw a warning or an error for a struct. Convert numeric value to 'dirichet'.
if ( isstruct(in) )
    if ( numel(in) == 1)
        warning('CHEBFUN:CHEBFUN:pde15s:bcstruct', ...
            'PDE15S no longer supports struct inputs for bc.left and bc.right.')
        if ( isfield(in, 'val') )
            out = {in.op, in.val};
        else
            out = in.op;
        end
    else
        error('CHEBFUN:CHEBFUN:pde15s:bcstruct', ...
            'PDE15S no longer supports struct inputs for bc.left and bc.right.')
    end
elseif ( isempty(in) )
    out = [];
elseif ( isnumeric(in) )
    out = {'dirichlet', in};
else
    out = in;
end
end

function [outFun, varNamesOut] = backCompat(inFun)
% In V4 PDE15S required the user to pass in dummy function handles for the
% differential operators. For example, u'' + u' + sum(u) would have needed
%  pdefun = @(u, t, x, diff, sum) diff(u, 2) + diff(u) + sum(u).
% V5 deals with this in a different way (by using the CHEBDOUBLE class), but we
% would still like to support the old syntax (at least for now).
%
% To do this, we parse the input function for the strings 'diff', 'Diff', and
% 'D'. If we find any of these, we assume the old syntax is being used, and
% redefine the function handle appropriately. Of course, this is not fool-proof,
% but it should work most of the time.

global SYSSIZE

% Determine the variable names:
str = func2str(inFun);
parLoc = strfind(str, ')');
varList = str(3:parLoc(1)-1);
commaLoc = strfind(varList, ',');
varNames = cell(1, numel(commaLoc)+1);
for k = 1:numel(commaLoc)
    varNames{k} = varList(1:commaLoc(k)-1);
    varList(1:commaLoc(k)) = [];
    commaLoc = commaLoc - commaLoc(k);
end
varNames{numel(commaLoc)+1} = varList;

% Look for 'diff', 'Diff', or 'D':
diffLoc = cellfun(@(v) any(strcmp(v, {'diff', 'Diff', 'D'})), varNames);
idx = find(diffLoc);
if ( isempty(idx) )
    % None present - new syntax!
    outFun = inFun;
    varNamesOut = varNames;
    return
end

warning('CHEBFUN:CHEBFUN:pde15s:oldSyntax', ...
    ['The syntax\n ' str '\nis deprecated and may not be supported in ', ...
     'future releases.\n Please see the PDE15S documentation for details.'] )

% Switch the order for V5 syntax:
varNamesNewOrder = varNames([(1+SYSSIZE):(idx-1), 1:SYSSIZE]);

% Get the new list of variables:
varList1 = varNamesNewOrder{1};
varList2 = varNames{1};
for k = 2:idx-1
    varList1 = [varList1, ',', varNamesNewOrder{k}]; %#ok<AGROW>
    varList2 = [varList2, ',', varNames{k}]; %#ok<AGROW>
end

% Compile the new function string:
funList = {'@diff', '@sum', '@cumsum', '@fred'};
newStr = ['@(', varList1, ')' 'inFun(' varList2];
for k = 0:(nargin(inFun) - idx)
    newStr = [newStr, ',', funList{k+1}]; %#ok<AGROW>
end
newStr = [newStr, ')'];

% Make the new function handle:
outFun = eval(newStr);

% Return the obtained variable names
varNamesOut = varNamesNewOrder;
end


function [iF, IF_f] = invertFrame(F)

% INVERTFRAME  Invert frame.
%   INVERTFRAME(F)  gives the frame iF, inverse of F, so that its
%   composition is the origin frame:
%       O = COMPOSEFRAMES(F,iF) ==> O.x = [0 0 0 1 0 0 0]'
%
%   [iF, IF_f] = INVERTFRAME(F) returns tha Jacobian of the inversion.

if nargout == 1

    q = q2qc(F.q);
    t = -Rtp(F.q,F.t);
    iF.x = [t;q];
    iF = updateFrame(iF);

else

    [q, Q_q] = q2qc(F.q);
    [nt, nT_q, nT_t] = Rtp(F.q,F.t); % this should be -Rtp(). n** means negative.

    iF.x = [-nt;q];
    iF = updateFrame(iF);

%     IF_f = zeros(7);
    IF_f(1:3,1:3) = -nT_t;
    IF_f(1:3,4:7) = -nT_q;
    IF_f(4:7,4:7) = Q_q;

end

% ========== End of function - Start GPL license ==========

%   # START GPL LICENSE

%---------------------------------------------------------------------
%
%   This file is part of SLAMTB, a SLAM toolbox for Matlab.
%
%   SLAMTB is free software: you can redistribute it and/or modify
%   it under the terms of the GNU General Public License as published by
%   the Free Software Foundation, either version 3 of the License, or
%   (at your option) any later version.
%
%   SLAMTB is distributed in the hope that it will be useful,
%   but WITHOUT ANY WARRANTY; without even the implied warranty of
%   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%   GNU General Public License for more details.
%
%   You should have received a copy of the GNU General Public License
%   along with SLAMTB.  If not, see <http://www.gnu.org/licenses/>.
%
%---------------------------------------------------------------------

%   SLAMTB is Copyright:
%   Copyright (c) 2008-2010, Joan Sola @ LAAS-CNRS,
%   Copyright (c) 2010-2013, Joan Sola,
%   Copyright (c) 2014-2015, Joan Sola @ IRI-UPC-CSIC,
%   SLAMTB is Copyright 2009
%   by Joan Sola, Teresa Vidal-Calleja, David Marquez and Jean Marie Codol
%   @ LAAS-CNRS.
%   See on top of this file for its particular copyright.

%   # END GPL LICENSE


function [Sbus, dSbus_dVm] = makeSbus(baseMVA, bus, gen, mpopt, Vm, Sg)
%MAKESBUS   Builds the vector of complex bus power injections.
%   SBUS = MAKESBUS(BASEMVA, BUS, GEN)
%   SBUS = MAKESBUS(BASEMVA, BUS, GEN, MPOPT, VM)
%   SBUS = MAKESBUS(BASEMVA, BUS, GEN, MPOPT, VM, SG)
%   returns the vector of complex bus power injections, that is, generation
%   minus load. Power is expressed in per unit. If the MPOPT and VM arguments
%   are present it evaluates any ZIP loads based on the provided voltage
%   magnitude vector. If VM is empty, it assumes nominal voltage. If SG is
%   provided, it is a complex ng x 1 vector of generator power injections in
%   p.u., and overrides the PG and QG columns in GEN, using GEN only for
%   connectivity information.
%
%   [SBUS, DSBUS_DVM] = MAKESBUS(BASEMVA, BUS, GEN, MPOPT, VM)
%   With two output arguments, it computes the partial derivative of the
%   bus injections with respect to voltage magnitude, leaving the first
%   return value SBUS empty. If VM is empty, it assumes no voltage dependence
%   and returns a sparse zero matrix.
%
%   See also MAKEYBUS.

%   MATPOWER
%   Copyright (c) 1996-2016, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%
%   This file is part of MATPOWER.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://matpower.org for more info.

%% define named indices into bus, gen matrices
[PQ, PV, REF, NONE, BUS_I, BUS_TYPE, PD, QD, GS, BS, BUS_AREA, VM, ...
    VA, BASE_KV, ZONE, VMAX, VMIN, LAM_P, LAM_Q, MU_VMAX, MU_VMIN] = idx_bus;
[GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
    MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
    QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;

%% default inputs
if nargin < 5
    Vm = [];
    if nargin < 4
        mpopt = [];
    end
end
nb = size(bus, 1);

%% get load parameters
Sd = makeSdzip(baseMVA, bus, mpopt);

if nargout == 2
    Sbus = [];
    if isempty(Vm)
        dSbus_dVm = sparse(nb, nb);
    else
        dSbus_dVm = -(spdiags(Sd.i + 2 * Vm .* Sd.z, 0, nb, nb));
    end
else
    %% compute per-bus generation in p.u.
    on = find(gen(:, GEN_STATUS) > 0);      %% which generators are on?
    gbus = gen(on, GEN_BUS);                %% what buses are they at?
    ngon = size(on, 1);
    Cg = sparse(gbus, (1:ngon)', 1, nb, ngon);  %% connection matrix
                                                %% element i, j is 1 if
                                                %% gen on(j) at bus i is ON
    if nargin > 5 && ~isempty(Sg)
        Sbusg = Cg * Sg(on);
    else
        Sbusg = Cg * (gen(on, PG) + 1j * gen(on, QG)) / baseMVA;
    end

    %% compute per-bus loads in p.u.
    if isempty(Vm)
        Vm = ones(nb, 1);
    end
    Sbusd = Sd.p + Sd.i .* Vm + Sd.z .* Vm.^2;

    %% form net complex bus power injection vector
    %% (power injected by generators + power injected by loads)
    Sbus = Sbusg - Sbusd;
end


classdef VehicleArticulatedLinear < VehicleDynamicsLateral.VehicleArticulated
    % VehicleArticulatedLinear Linear articulated vehicle model.
    %
    % It inherits properties from VehicleArticulated.

    methods
        % Constructor
        function self = VehicleArticulatedLinear()
            self.mF0 = 5200;
            self.mR0 = 2400;
            self.mF = 6000;
            self.mR = 10000;
            self.mM = 17000;
            self.IT = 46000;
            self.IS = 450000;
            self.lT = 3.5;
            self.lS = 7.7;
            self.c = -0.3;
            self.nF = 2;
            self.nR = 4;
            self.nM = 8;
            self.wT = 2.6;
            self.wS = 2.4;
            self.muy = 0.3;
            self.deltaf = 0;
            self.Fxf = 0;
            self.Fxr = 0;
            self.Fxm = 0;
        end

        function dx = Model(self,t,states,tspan)
            % Vehicle parameters
            mT = self.mT;
            mS = self.mS;
            % IT = self.IT;
            % IS = self.IS;
            a = self.a;
            b = self.b;
            c = self.c;
            d = self.d;
            e = self.e;
            nF = self.nF;
            nR = self.nR;
            nM = self.nM;
            g = 9.81;

            % Vertical forces
            FzF = self.mF * g;
            FzR = self.mR * g;
            FzM = self.mM * g;
            muy = self.muy;

            v0 = 20;

            % State
            X = states(1,1);
            Y = states(2,1);
            PSI     = states(3,1);
            PHI     = states(4,1);
            V       = states(5,1);
            ALPHAT  = states(6,1);
            dPSI    = states(7,1);
            dPHI    = states(8,1);

            if isa(self.deltaf,'function_handle')
                deltaf = self.deltaf([X;Y;PSI;PHI;V;ALPHAT;dPSI;dPHI],t);
            elseif length(self.deltaf)>1
                deltaf = interp1(tspan,self.deltaf,t);
            else
                deltaf = self.deltaf;
            end

            % Slip angles - linear
            ALPHAF = ALPHAT + a/v0*dPSI - deltaf;
            ALPHAR = ALPHAT - b/v0*dPSI;
            ALPHAM = ALPHAT + PHI - (dPSI*(b + c + d + e))/v0 + (dPHI*(d + e))/v0;

            % Longitudinal forces
            if isa(self.Fxf,'function_handle')
                FxF = self.Fxf([X;Y;PSI;PHI;V;ALPHAT;dPSI;dPHI],t);
            elseif length(self.Fxf)>1
                FxF = interp1(tspan,self.Fxf,t);
            else
                FxF = self.Fxf;
            end

            if isa(self.Fxr,'function_handle')
                FxR = self.Fxr([X;Y;PSI;PHI;V;ALPHAT;dPSI;dPHI],t);
            elseif length(self.Fxr)>1
                FxR = interp1(tspan,self.Fxr,t);
            else
                FxR = self.Fxr;
            end

            if isa(self.Fxm,'function_handle')
                FxM = self.Fxm([X;Y;PSI;PHI;V;ALPHAT;dPSI;dPHI],t);
            elseif length(self.Fxm)>1
                FxM = interp1(tspan,self.Fxm,t);
            else
                FxM = self.Fxm;
            end

            % Lateral forces - Characteristic curve
            FyF = nF*self.tire.Characteristic(ALPHAF,FzF/nF,muy);
            FyR = nR*self.tire.Characteristic(ALPHAR,FzR/nR,muy);
            FyM = nM*self.tire.Characteristic(ALPHAM,FzM/nM,muy);

            A = [ 0  0   0  0  1   0                       0  0;...
                  0  0  v0  0  0  v0                       0  0;...
                  0  0   0  0  0   0                       1  0;...
                  0  0   0  0  0   0                       0  1;...
                  0  0   0  0  0   0                       0  0;...
                  0  0   0  0  0   0           (-v0*(mS + mT))  0;...
                  0  0   0  0  0   0  (mS*(d*v0 + v0*(b + c)))  0;...
                  0  0   0  0  0   0                (-d*mS*v0)  0];

            B = [ 0  0  0  0  0   0                0;...
                  0  0  0  0  0   0                0;...
                  0  0  0  0  0   0                0;...
                  0  0  0  0  0   0                0;...
                  0  1  1  1  0   0                0;...
                  0  0  0  0  1   1                1;...
                  0  0  0  0  a  -b  (- b - c - d - e);...
                  0  0  0  0  0   0            (d + e)];

            vetEst = [X ; Y ; PSI ; PHI ; V ; ALPHAT ; dPSI ; dPHI];
            vetEnt = [deltaf ; FxF ; FxR ; FxM ; FyF ; FyR ; FyM];

            % Integrator output
            dx = A*vetEst + B*vetEnt;
        end

        function [value,isterminal,direction] = velocity(~,~,states)
            % If the velocity is less than 0.1m/s the integrator stops.
            % The MassMatrix is singular when the velocity is 0 m/s.
            value = states(5,1) - 0.1;
            isterminal = 1;
            direction = -1;
        end

        function E = MassMatrix(self,~,~)
            % Vehicle parameters
            mT = self.mT;
            mS = self.mS;
            IT = self.IT;
            IS = self.IS;
            % a = self.a;
            b = self.b;
            c = self.c;
            d = self.d;
            % e = self.e;
            % deltaf = self.deltaf;
            % nF = self.nF;
            % nR = self.nR;
            % nM = self.nM;

            % g = 9.81;

            v0 = 20;

            % Mass matrix
            E = [ 1  0  0  0        0                        0                                               0                            0;...
                  0  1  0  0        0                        0                                               0                            0;...
                  0  0  1  0        0                        0                                               0                            0;...
                  0  0  0  1        0                        0                                               0                            0;...
                  0  0  0  0  (mS + mT)                      0                                               0                            0;...
                  0  0  0  0        0             (v0*(mS + mT))                                 (-mS*(b + c + d))                         (d*mS);...
                  0  0  0  0        0  ( -mS*v0*(b + c + d))            (IS + IT + mS*(b + c + d)^2)  (- IS - mS*(d^2 + (b + c)*d));...
                  0  0  0  0        0                  (d*mS*v0)                     (- IS - mS*(d^2 + (b + c)*d))                  (mS*d^2 + IS)];

        end
    end
end

%% See Also
%
% <../../index.html Home>
%


classdef math_model_opf_dc < mp.math_model_opf
%MP.MATH_MODEL_OPF_DC  MATPOWER mathematical model for DC optimal power flow (OPF) problem.
%   ?
%
%   MP.MATH_MODEL_OPF_DC ... power flow ...
%
%   Properties
%       ? - ?
%
%   Methods
%       ?

%   MATPOWER
%   Copyright (c) 2021-2022, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%
%   This file is part of MATPOWER.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://matpower.org for more info.

%     properties
%     end

    methods
        %% constructor
        function obj = math_model_opf_dc()
            obj@mp.math_model_opf();
            obj.element_classes = { @mp.mme_bus_opf_dc, @mp.mme_gen_opf_dc, ...
                @mp.mme_load_pf_dc, @mp.mme_branch_opf_dc, @mp.mme_shunt_pf_dc };
        end

        function tag = form_tag(obj)
            tag = 'dc';
        end

        function name = form_name(obj)
            name = 'DC';
        end

        function [vx, z, x] = convert_x_m2n(obj, mmx, nm)
            nm_vars = obj.update_nm_vars(mmx, nm);

            %% convert (real) math model x to network model x
            vx = nm_vars.va;
            z  = nm_vars.z;
            if nargout < 2
                vx = [vx; z];
            elseif nargout > 2
                x = [vx; z];
            end
        end

        function obj = add_node_balance_constraints(obj, nm, dm, mpopt)
            [B, K, p] = nm.get_params();

            %% power balance constraints
            C = nm.C;
            Amis = C * [B*C' K*nm.D'];
            bmis = -C * p;
            obj.add_lin_constraint('Pmis', Amis, bmis, bmis, ...
                                [nm.va.order; nm.z.order]);
        end

        function opt = solve_opts(obj, nm, dm, mpopt)
            opt = mpopt2qpopt(mpopt, obj.problem_type());

            switch opt.alg
                case {'MIPS', 'IPOPT'}
                    if mpopt.opf.start < 2      %% initialize interior point
                        opt.x0 = obj.interior_x0(obj, nm, dm);
                    end
                case 'OSQP'
                    opt.x0 = [];        %% disable provided starting point
            end
        end
    end     %% methods
end         %% classdef


function varargout = McCabeThieleGUI(varargin)
% MCCABETHIELEGUI M-file for McCabeThieleGUI.fig
%      MCCABETHIELEGUI, by itself, creates a new MCCABETHIELEGUI or raises the existing
%      singleton*.
%
%      H = MCCABETHIELEGUI returns the handle to a new MCCABETHIELEGUI or the handle to
%      the existing singleton*.
%
%      MCCABETHIELEGUI('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in MCCABETHIELEGUI.M with the given input arguments.
%
%      MCCABETHIELEGUI('Property','Value',...) creates a new MCCABETHIELEGUI or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before McCabeThieleGUI_OpeningFunction gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to McCabeThieleGUI_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Last Modified by GUIDE v2.5 06-Jul-2010 12:55:42

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @McCabeThieleGUI_OpeningFcn, ...
                   'gui_OutputFcn',  @McCabeThieleGUI_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT

% --- Executes just before McCabeThieleGUI is made visible.
function McCabeThieleGUI_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to McCabeThieleGUI (see VARARGIN)

set(handles.mixturepanel_buttongroup,'SelectionChangeFcn',@mixturepanel_buttongroup_SelectionChangeFcn);

% Choose default command line output for McCabeThieleGUI
handles.output = hObject;

% Update handles structure
guidata(hObject, handles);

% UIWAIT makes McCabeThieleGUI wait for user response (see UIRESUME)
% uiwait(handles.figure1);

% --- Outputs from this function are returned to the command line.
function varargout = McCabeThieleGUI_OutputFcn(hObject, eventdata, handles)
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure
varargout{1} = handles.output;
%% Mixture Panel

function mixturepanel_buttongroup_SelectionChangeFcn(hObject, eventdata)

handles = guidata(hObject);

switch get(eventdata.NewValue,'Tag')   % Get Tag of selected object
    case 'methanolwater_radiobutton'
        %equilibrium data
        xMW = '[0    0.0200    0.0400    0.0600    0.0800    0.1000    0.1500    0.2000    0.3000    0.4000    0.5000    0.6000    0.7000    0.8000    0.9000    0.9500    1.0000]';
        yMW = '[0    0.1340    0.2300    0.3040    0.3650    0.4180    0.5170    0.5790    0.6650    0.7290    0.7790    0.8250    0.8700    0.9150    0.9580    0.9790    1.0000]';
        set(handles.userdefined_input,'Enable','Off')
        set(handles.userdefined_input2,'Enable','Off')
        set(handles.userdefined_input,'String',xMW)
        set(handles.userdefined_input2,'String',yMW)
        %hold off
        %methanol-water mixture
        %polynomial regression and plot of equilibrium data
        %p = polyfit(xMW,yMW,6);
        %x = 0:.1:100;
        %y = polyval(p,x);
        %plot(xMW,yMW)
        %hold on
        %axis([0 1 0 1])
        %plot(x,y)
        %plot(x,x)
        %xlabel('Liquid Mole Fraction of Methanol')
        %ylabel('Vapor Mole Fraction of Methanol')
        %title('McCabe-Thiele Diagram for Methanol-Water')

    case 'ethanolwater_radiobutton'
        %equilibrium data
        xEW = '[0    0.0190    0.0721    0.0966    0.1238    0.1661    0.2337    0.2608    0.3273    0.3965    0.5198    0.5732    0.6763    0.7472    0.8943    1.0000]';
        yEW = '[0    0.1700    0.3891    0.4375    0.4704    0.5089    0.5445    0.5580    0.5826    0.6122    0.6599    0.6841    0.7385    0.7815    0.8943    1.0000]';
        xeq = num2str(xEW);
        yeq = num2str(yEW);
        set(handles.userdefined_input,'Enable','Off')
        set(handles.userdefined_input2,'Enable','Off')
        set(handles.userdefined_input,'String',xeq)
        set(handles.userdefined_input2,'String',yeq)
        %hold off
        %ethanol-water mixture
        %polynomial regression and plot of equilibrium data
        %p = polyfit(xEW,yEW,8);
        %x = 0:.1:100;
        %y = polyval(p,x);
        %plot(x,y)
        %plot(xEW,yEW)
        %hold on
        %axis([0 1 0 1])
        %plot(x,x)
        %xlabel('Liquid Mole Fraction of Ethanol')
        %ylabel('Vapor Mole Fraction of Ethanol')
        %title('McCabe-Thiele Diagram for Ethanol-Water')

    case 'acetonethanol_radiobutton'
        %equilibrium data
        xAE = '[0    0.1000    0.1500    0.2000    0.2500    0.3000    0.3500    0.4000    0.5000    0.6000    0.7000    0.8000    0.9000    1.0000]';
        yAE = '[0    0.2620    0.3480    0.4170    0.4780    0.5240    0.5660    0.6050    0.6740    0.7390    0.8020    0.8650    0.9290    1.0000]';
        set(handles.userdefined_input,'Enable','Off')
        set(handles.userdefined_input2,'Enable','Off')
        set(handles.userdefined_input,'String',xAE)
        set(handles.userdefined_input2,'String',yAE)
        %hold off
        %acetone-ethanol mixture
        %polynomial regression and plot of equilibrium data
        %p = polyfit(xAE,yAE,6);
        %x = 0:.1:100;
        %y = polyval(p,x);
        %plot(xAE,yAE)
        %hold on
        %plot(x,y)
        %plot(x,x)
        %axis([0 1 0 1])
        %xlabel('Liquid Mole Fraction of Acetone')
        %ylabel('Vapor Mole Fraction of Acetone')
        %title('McCabe-Thiele Diagram for Acetone-Ethanol')

    case 'userdefined_radiobutton'
        hold off
       %User Defined Input
        choice = menu('Input Equilibrium Data as a Row Vector or as a Relative Volatility (alpha)','Vector','Alpha');
        switch choice
            case 1
                set(handles.userdefined_input,'Enable','On')
                set(handles.userdefined_input2,'Enable','On')
                set(handles.userdefined_input,'String','[Liquid Mole Fractions]')
                set(handles.userdefined_input2,'String','[Vapor Mole Fractions]')
                warndlg('Enlose your vectors with brackets and single quotes', 'Warning');
                %disp('You must enter your own equilibrium data in vector form ')
                %MVC = input('Enter the name of the more volatile component ','s');
                %LVC = input('Enter the name of the less volatile component ','s');
                %xU = input('Enter liquid mole fraction data as a row vector (use brackets) \n');
                %a = length(xU);
                %fprintf('You have entered %1.0f "x" data points, you must enter an equal number of y data points \n',a)
                %yU = input('Enter vapor phase mole fraction data as a row vector (use brackets) \n');
                %b = length(yU);
                %if a == b
                    %plot(xU,yU,'o')
                    %hold on
                    %p = polyfit(xU,yU,6);
                    %x = 0:.1:100;
                    %y = polyval(p,x);
                    %plot(x,y)
                    %plot(x,x)
                    %x_label = sprintf('Liquid Phase Mole Fraction of %s',MVC);
                    %y_label = sprintf('Vapor Phase Mole Fraction of %s',MVC);
                    %t_itle = sprintf('McCabe-Thiele Diagram for %s-%s',MVC,LVC);
                    %xlabel(x_label)
                    %ylabel(y_label)
                    %title(t_itle)
                %else
                    %disp('Equilibrium data does not match, vectors must be equal lengths')
                %end
            case 2
                set(handles.userdefined_input,'Enable','On')
                set(handles.userdefined_input2,'String','[]')
                set(handles.userdefined_input,'String','<Enter Relative Volatility>')
        end
end
%updates the handles structure
guidata(hObject, handles);
function userdefined_input_Callback(hObject, eventdata, handles)
% --- Executes during object creation, after setting all properties.
function userdefined_input_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end
function userdefined_input2_Callback(hObject, eventdata, handles)
% --- Executes during object creation, after setting all properties.
function userdefined_input2_CreateFcn(hObject, eventdata, handles)

if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%% Column Pressure Checkbox

%Column Pressure Input
function pressureinput_Callback(hObject, eventdata, handles)
% Hints: get(hObject,'String') returns contents of pressureinput as text
%        str2double(get(hObject,'String')) returns contents of pressureinput as a double

% --- Executes during object creation, after setting all properties.
function pressureinput_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function columnpressure_checkbox_Callback(hObject, eventdata, handles)

checkbox1_Status = get(handles.columnpressure_checkbox,'Value');
if checkbox1_Status == 0
    set(handles.pressureinput,'String',' ')
else
    set(handles.pressureinput,'Enable','On')
    set(handles.pressureinput,'String','<Enter Column Pressure>')
end

%% Feed
%% Feed Flow Rate
%Feed Flow Rate Input
function feedflowinput_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function feedflowinput_CreateFcn(hObject, eventdata, handles)

    if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
    end

%Feed Flow Rate Checkbox
function feedflowrate_checkbox_Callback(hObject, eventdata, handles)

checkbox2_Status = get(handles.feedflowrate_checkbox,'Value');

if checkbox2_Status == 0
    set(handles.feedflowinput,'Enable','Off')
    set(handles.feedflowinput,'String',' ');
else
    set(handles.feedflowinput,'Enable','On')
    set(handles.feedflowinput,'String','<Enter Feed Flow Rate>')
end

%% Feed Composition of the Volatile Component Input
function feedcompvolatile_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function feedcompvolatile_CreateFcn(hObject, eventdata, handles)

if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%Feed Composition of the Less Volatile Component Input
function feedcomplessvolatile_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function feedcomplessvolatile_CreateFcn(hObject, eventdata, handles)

if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%Feed Composition Checkbox
function feedcomposition_checkbox_Callback(hObject, eventdata, handles)

checkbox3_Status = get(handles.feedcomposition_checkbox,'Value');

if checkbox3_Status == 0
    set(handles.feedcompvolatile,'Enable','Off')
    set(handles.feedcomplessvolatile,'Enable','Off')
    set(handles.feedcompvolatile,'String',' ')
    set(handles.feedcomplessvolatile,'String',' ')
else
    set(handles.feedcompvolatile,'Enable','On')
    set(handles.feedcomplessvolatile,'Enable','On')
    set(handles.feedcompvolatile,'String','<Volatile>')
    set(handles.feedcomplessvolatile,'String','<Non-Volatile>')
end

%% Feed Enthalpy
%Feed Enthalpy Input
function feedenthalpyinput_Callback(hObject, eventdata, handles)
function feedenthalpyinput_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%Feed Enthalpy Checkbox
function feedenthalpy_Callback(hObject, eventdata, handles)

checkbox4_Status = get(handles.feedenthalpy,'Value');

if checkbox4_Status == 0
    set(handles.feedenthalpyinput,'Enable','Off')
    set(handles.feedenthalpyinput,'String',' ')
else
    set(handles.feedenthalpyinput,'Enable','On')
    set(handles.feedenthalpyinput,'String','<Feed Enthalpy>')
end

%Reboiler Duty Input
function reboilerdutyinput_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function reboilerdutyinput_CreateFcn(hObject, eventdata, handles)

if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

% Reboiler Duty Checkbox
function reboilerdutycheckbox_Callback(hObject, eventdata, handles)

checkbox7_Status = get(handles.reboilerdutycheckbox,'Value');
if checkbox7_Status == 0
    set(handles.reboilerdutyinput,'Enable','Off')
    set(handles.reboilerdutyinput,'String',' ')
else
    set(handles.reboilerdutyinput,'Enable','On')
    set(handles.reboilerdutyinput,'String','<Reboiler Duty>')
end

%% Distillate composition

%Distillate Composition, Volatile Component Input
function distcompvolatile_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function distcompvolatile_CreateFcn(hObject, eventdata, handles)

if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%Distillate Composition of the less Volatile Component Input
function distcomplessvolatile_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function distcomplessvolatile_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%Distillate composition checkbox
function distillatecomposition_checkbox_Callback(hObject, eventdata, handles)

checkbox9_Status = get(handles.distillatecomposition_checkbox,'Value');

if checkbox9_Status == 0
    set(handles.distcompvolatile,'Enable','Off')
    set(handles.distcomplessvolatile,'Enable','Off')
    set(handles.distcompvolatile,'String',' ')
    set(handles.distcomplessvolatile,'String',' ')
else
    set(handles.distcompvolatile,'Enable','On')
    set(handles.distcomplessvolatile,'Enable','On')
    set(handles.distcompvolatile,'String','<Volatile>')
    set(handles.distcomplessvolatile,'String','<Non-Volatile>')
end

%% Reflux Ratio
%Reflux Ratio Input
function refluxratio_input_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function refluxratio_input_CreateFcn(hObject, eventdata, handles)

if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function minimumrefluxinput_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function minimumrefluxinput_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%Reflux Ratio Checkbox
function refluxratio_checkbox_Callback(hObject, eventdata, handles)

checkbox11_Status = get(handles.refluxratio_checkbox,'Value');

if checkbox11_Status == 0
    set(handles.refluxratio_input,'Enable','Off')
    set(handles.refluxratio_input,'String',' ')
    set(handles.minimumrefluxinput,'Enable','Off')
    set(handles.minimumrefluxinput,'String',' ')
elseif checkbox11_Status == 1
    choice = menu('Are you specifying minimum reflux or actual reflux','Actual','Minumum');
    switch choice
        case 1
            set(handles.refluxratio_input,'Enable','On')
            set(handles.refluxratio_input,'String','<Reflux Ratio>')
            set(handles.minimumrefluxinput,'Enable','Off')
            set(handles.minimumrefluxinput,'String',' ')
        case 2
            Rmin = str2double(get(handles.minimumrefluxinput,'String'));
            if isnan(Rmin) == 1
                set(handles.refluxratio_input,'Enable','On')
                set(handles.refluxratio_input,'String','<Operating Factor>')
                set(handles.minimumrefluxinput,'Enable','On')
                set(handles.minimumrefluxinput,'String','<Minumum Reflux>')
            elseif isnan(Rmin) == 0
                set(handles.refluxratio_input,'Enable','On')
                set(handles.refluxratio_input,'String','<Operating Factor>')
                set(handles.minimumrefluxinput,'Enable','Off')
            end
    end

end

%% Bottoms Composition
%Bottoms Composition of the Less Volatile Component Input
function botcomplessvolatile_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function botcomplessvolatile_CreateFcn(hObject, eventdata, handles)

if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%Bottoms Composition of the Volatile Component Input
function botcompvolatile_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function botcompvolatile_CreateFcn(hObject, eventdata, handles)

if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%Bottoms Composition Checkbox
function bottomscomposition_checkbox_Callback(hObject, eventdata, handles)

checkbox10_Status = get(handles.bottomscomposition_checkbox,'Value');

if checkbox10_Status == 0
    set(handles.botcomplessvolatile,'Enable','Off')
    set(handles.botcompvolatile,'Enable','Off')
    set(handles.botcomplessvolatile,'String',' ')
    set(handles.botcompvolatile,'String',' ')
elseif checkbox10_Status == 1
    set(handles.botcomplessvolatile,'Enable','On')
    set(handles.botcompvolatile,'Enable','On')
    set(handles.botcompvolatile,'String','<Volatile>')
    set(handles.botcomplessvolatile,'String','<Non-Volatile>')
end

%% Optimum Feed Plate
%Optimum Feed Plate Checkbox
function optimumfeedplate_Callback(hObject, eventdata, handles)

%% Distillate Flow Rate
%Distillate Flow Rate Input
function distillateflowrateinput_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function distillateflowrateinput_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%Distillate Flow Rate Checkbox
function distillateflowrate_checkbox_Callback(hObject, eventdata, handles)
checkbox14_Status = get(handles.distillateflowrate_checkbox,'Value');

if checkbox14_Status == 0
    set(handles.distillateflowrateinput,'Enable','Off')
    set(handles.distillateflowrateinput,'String',' ')
else
    set(handles.distillateflowrateinput,'Enable','On')
    set(handles.distillateflowrateinput,'String','<Enter Distillate Flow Rate>')
end

%% Fractional Recoveries

%Fractional Recovery of the Volatile Component in the Distillate Input
function fracrecoveryvolatile_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function fracrecoveryvolatile_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%Fractional Recovery of the Less Volatile Component in the Bottoms Input
function fracrecoverylessvolatile_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function fracrecoverylessvolatile_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%Fractional Recoveries Checkbox
function fractionalrecoveries_checkbox_Callback(hObject, eventdata, handles)

checkbox12_Status = get(handles.fractionalrecoveries_checkbox,'Value');

if checkbox12_Status == 0
    set(handles.fracrecoveryvolatile,'Enable','Off')
    set(handles.fracrecoverylessvolatile,'Enable','Off')
    set(handles.fracrecoveryvolatile,'String','0')
    set(handles.fracrecoverylessvolatile,'String','0')
else
    set(handles.fracrecoveryvolatile,'Enable','On')
    set(handles.fracrecoverylessvolatile,'Enable','On')
    set(handles.fracrecoveryvolatile,'String','<Volatile>')
    set(handles.fracrecoverylessvolatile,'String','<Non-Volatile>')
end

%% Bottoms Flow Rate
function bottomsflowrateinput_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function bottomsflowrateinput_CreateFcn(hObject, eventdata, handles)

if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%Bottoms Flow Rate Checkbox
function bottomsflowrate_checkbox_Callback(hObject, eventdata, handles)
checkbox15_Status = get(handles.bottomsflowrate_checkbox,'Value');
if checkbox15_Status == 0
    set(handles.bottomsflowrateinput,'Enable','Off')
    set(handles.bottomsflowrateinput,'String',' ')
else
    set(handles.bottomsflowrateinput,'Enable','On')
    set(handles.bottomsflowrateinput,'String','<Enter Bottoms Flow Rate>')
end

%% Boilup Ratio Checkbox

%Boilup Ratio Input
function boilupratioinput_Callback(hObject, eventdata, handles)

function boilupratioinput_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function boilupratio_checkbox_Callback(hObject, eventdata, handles)

checkbox15_Status = get(handles.boilupratio_checkbox,'Value');

if checkbox15_Status == 0
    set(handles.boilupratioinput,'Enable','Off')
    set(handles.boilupratioinput,'String',' ')
elseif checkbox15_Status == 1
    set(handles.boilupratioinput,'Enable','On')
    set(handles.boilupratioinput,'String','<Enter Boilup Ratio>')
elseif checkbox15_Status == 0 && checkbox11_Status == 1 && checkbox6_Status == 1 && checkbox9_Status == 1
    set(handles.boilupratioinput,'Enable','Off')
    set(handles.boilupratioinput,'String',' ')
    xi = (-(Q-1)*(1-R/(R+1))*xd-zf)/((Q-1)*R/(R+1)-Q);
    yi = (zf+xd*Q/R)/(1+Q/R);
    xs = linspace(xb,xi);
    ys = linspace(xb,yi);
    plot(xs,ys)
    hold on
end

%% Feed Quality

%Feed Quality Input
function feedqualityinput_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function feedqualityinput_CreateFcn(hObject, eventdata, handles)

if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%Feed Quality Checkbox
function feedquality_checkbox_Callback(hObject, eventdata, handles)

checkbox6_Status = get(handles.feedquality_checkbox,'Value');

if checkbox6_Status == 0
    set(handles.feedqualityinput,'Enable','Off')
    set(handles.feedqualityinput,'String',' ')
else
    set(handles.feedqualityinput,'Enable','On')
    set(handles.feedqualityinput,'String','<Enter Feed Quality>')
end

%Condenser Duty Input
function condenserdutyinput_Callback(hObject, eventdata, handles)
function condenserdutyinput_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%Condenser Duty Checkbox
function condenserdutycheckbox_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function condenserdutycheckbox_CreateFcn(hObject, eventdata, handles)

if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

checkbox5_Status = get(handles.condenserdutycheckbox,'Value');
if checkbox5_Status == 0
    set(handles.condenserdutyinput,'Enable','Off')
    set(handles.condenserdutyinput,'String',' ')
else
    set(handles.condenserdutyinput,'Enable','On')
    set(handles.condenserdutyinput,'String','<Enter Condenser Duty>')
end

%Reflux Enthalpy Input
function refluxenthalpyinput_Callback(hObject, eventdata, handles)
% --- Executes during object creation, after setting all properties.
function refluxenthalpyinput_CreateFcn(hObject, eventdata, handles)

if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end
%Reflux Enthalpy Checkbox
function refluxenthalpy_Callback(hObject, eventdata, handles)

checkbox8_Status = get(handles.refluxenthalpy,'Value');
if checkbox8_Status == 0
    set(handles.refluxenthalpyinput,'Enable','Off')
    set(handles.refluxenthalpyinput,'String',' ')
else
    set(handles.refluxenthalpyinput,'Enable','On')
    set(handles.refluxenthalpyinput,'String','<Reflux Enthalpy>')
end

%Boilup Enthalpy Input
function boilupenthalpyinput_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function boilupenthalpyinput_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%Boilup Enthalpy Checkbox
function boilupenthalpycheckbox_Callback(hObject, eventdata, handles)
checkbox20_Status = get(handles.boilupenthalpycheckbox,'Value');
if checkbox20_Status == 0
    set(handles.boilupenthalpyinput,'Enable','Off')
    set(handles.boilupenthalpyinput,'String',' ')
else
    set(handles.boilupenthalpyinput,'Enable','On')
    set(handles.boilupenthalpyinput,'String','<Boilup Enthalpy>')
end

%Distillate Enthalpy
function distillateenthalpyinput_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function distillateenthalpyinput_CreateFcn(hObject, eventdata, handles)

if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

% --- Executes on button press in distillateenthalpycheckbox.
function distillateenthalpycheckbox_Callback(hObject, eventdata, handles)
checkbox21_Status = get(handles.distillateenthalpycheckbox,'Value');
if checkbox21_Status == 0
    set(handles.distillateenthalpyinput,'Enable','Off')
    set(handles.distillateenthalpyinput,'String',' ')
else
    set(handles.distillateenthalpyinput,'Enable','On')
    set(handles.distillateenthalpyinput,'String','<Distillate Enthalpy>')
end

%Bottoms enthalpy
function bottomsenthalpyinput_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function bottomsenthalpyinput_CreateFcn(hObject, eventdata, handles)

if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

% --- Executes on button press in bottomsenthalpycheckbox.
function bottomsenthalpycheckbox_Callback(hObject, eventdata, handles)
checkbox22_Status = get(handles.bottomsenthalpycheckbox,'Value');
if checkbox22_Status == 0
    set(handles.bottomsenthalpyinput,'Enable','Off')
    set(handles.bottomsenthalpyinput,'String',' ')
else
    set(handles.bottomsenthalpyinput,'Enable','On')
    set(handles.bottomsenthalpyinput,'String','<Bottoms Enthalpy>')
end

% --- Executes on button press in startbutton.
function startbutton_Callback(hObject, eventdata, handles)

% Get Values

Alpha = str2double(get(handles.userdefined_input,'String'));

P = str2double(get(handles.pressureinput,'String'));
F = str2double(get(handles.feedflowinput,'String'));
zf = str2double(get(handles.feedcompvolatile,'String'));
zfnv = str2double(get(handles.feedcomplessvolatile,'String'));
xd = str2double(get(handles.distcompvolatile,'String'));
xdnv = str2double(get(handles.distcomplessvolatile,'String'));
xb = str2double(get(handles.botcompvolatile,'String'));
xbnv = str2double(get(handles.botcomplessvolatile,'String'));
D = str2double(get(handles.distillateflowrateinput,'String'));
fv = str2double(get(handles.fracrecoveryvolatile,'String'));
fnv = str2double(get(handles.fracrecoverylessvolatile,'String'));
B = str2double(get(handles.bottomsflowrateinput,'String'));
S = str2double(get(handles.boilupratioinput,'String'));
Q = str2double(get(handles.feedqualityinput,'String'));
R = str2double(get(handles.refluxratio_input,'String'));
Rmin = str2double(get(handles.minimumrefluxinput,'String'));
hf = str2double(get(handles.feedenthalpyinput,'String'));
hD = str2double(get(handles.refluxenthalpyinput,'String'));
hB = str2double(get(handles.boilupenthalpyinput,'String'));
Qc = str2double(get(handles.condenserdutyinput,'String'));
Qr = str2double(get(handles.reboilerdutyinput,'String'));
HD = str2double(get(handles.distillateenthalpyinput,'String'));
HB = str2double(get(handles.bottomsenthalpyinput,'String'));

%xie = 1/2*(-Q+zf*Alpha+Q*Alpha-zf-Alpha+(2*Q*Alpha^2*zf+Q^2-2*Q*zf+2*zf*Alpha-2*zf^2*Alpha+zf^2*Alpha^2-2*zf*Alpha^2+zf^2+2*Q*Alpha-2*Q^2*Alpha+Alpha^2+Q^2*Alpha^2-2*Q*Alpha^2)^(1/2))/Q/(-1+Alpha);
%yie = xie*Alpha/(1-xie+xie*Alpha);
xi = (-(Q-1)*(1-R/(R+1))*xd-zf)/((Q-1)*R/(R+1)-Q);
yi = (zf+xd*Q/R)/(1+Q/R);

%Error Tests

%Feed Quality & Feed Line
checkbox6_Status = get(handles.feedquality_checkbox,'Value');
if checkbox6_Status == 0
    errordlg('Feed quality must be specified')
    return
elseif checkbox6_Status == 1 && isnan(Q) == 1
    errordlg('Feed quality must be specified')
    return
elseif checkbox6_Status == 1 && isnan(Q) == 0
    ye = 0:.001:1;
    if Q > -inf && Q <1
            xf = 0:.001:1;
    elseif Q > 1
            xf = zf:.001:1;
    end
    if Q ~= 1
        yf = yfeed(xf,Q,zf);
    elseif Q == 1
        yf = 0:.001:1;
        xf = zf*ones(length(yf));
    end
end

%Equilibrium Data
if any(isnan(Alpha)) == 1
    xeq = str2num(get(handles.userdefined_input,'String'));
    yeq = str2num(get(handles.userdefined_input2,'String'));
    %Error Test on Equilibrium Vector Length
    if length(yeq) ~= length(xeq) && isempty(yeq) == 1 && length(xeq) > 1
        errordlg('Equilibrium data does not match')
        return
    elseif length(yeq) == length(xeq) && length(xeq) < 10
        errordlg('Too few data points will give a poor equilibrium curve')
    else
        k = 1; %k is a constant, metric for stepping off equilibrium stages
    end
elseif isnan(Alpha) == 0 && length(Alpha) == 1
    yeq = [];
    if Alpha <= 1
        errordlg('Relative volatility must be greater than 1')
        return
    elseif Alpha > 1
        k = 2;
    end
end
if k == 1
    xx = 0:.001:1;
    yy = spline(xeq,yeq,xx);
    if Q == 0
        xx = .001:.001:1.001;
        yy = spline(xeq,yeq,xx);
        diffe = abs(yy-zf);
        [a b] = min(diffe) ;
        xie = xx(b);
        yie = zf;
    elseif Q == 1
        yie = spline(xeq,yeq,zf);
        xie = zf;
    elseif Q~=0 && Q~=1
        diffe = abs(yy-yf);
        [a b] = min(diffe);
        xie = xx(b);
        yie = yy(b);
    end
elseif k == 2
    xie = 1/2*(-Q+zf*Alpha+Q*Alpha-zf-Alpha+(2*Q*Alpha^2*zf+Q^2-2*Q*zf+2*zf*Alpha-2*zf^2*Alpha+zf^2*Alpha^2-2*zf*Alpha^2+zf^2+2*Q*Alpha-2*Q^2*Alpha+Alpha^2+Q^2*Alpha^2-2*Q*Alpha^2)^(1/2))/Q/(-1+Alpha);
    yie = xie*Alpha/(1-xie+xie*Alpha);
end

%Overall Mass Balance
checkbox2_Status = get(handles.feedflowrate_checkbox,'Value');
checkbox17_Status = get(handles.distillateflowrate_checkbox,'Value');
checkbox18_Status = get(handles.bottomsflowrate_checkbox,'Value');
l = [checkbox2_Status checkbox17_Status checkbox18_Status]; %l is a status vector for mass flow streams
if any([F D B]< 0)
    errordlg('Mass flow rates must be greater than zero')
    return
elseif sum(l) == 3 && D + B ~= F
    errordlg('The mass balance is incorrect')
    return
elseif sum(l) == 2
    if isnan(F) == 0
        if isnan(B) == 0
            D = F - B;
            Ds = num2str(D);
            set(handles.distillateflowrateinput,'String',Ds)
        elseif isnan(D) == 0
            B = F - D;
            Bs = num2str(B);
            set(handles.bottomsflowrateinput,'String',Bs)
        end
    elseif isnan(F) == 1
        F = B + D;
        Fs = num2str(F);
        set(handles.feedflowinput,'String',Fs)
    end
end

%Feed Composition
checkbox3_Status = get(handles.feedcomposition_checkbox,'Value');
if checkbox3_Status == 1
    if isnan(zf) == 1 && isnan(zfnv) == 1
        errordlg('Feed composition must be a real number')
        return
    elseif isnan(zf) == 0 && isnan(zfnv) == 1
        zfnv = 1 - zf;
        zfnv = num2str(zfnv);
        set(handles.feedcomplessvolatile,'String',zfnv)
    elseif isnan(zf) == 1 && isnan(zfnv) == 0
        zf = 1 - zfnv;
        zf = num2str(zf);
        set(handles.feedcompvolatile,'String',zf)
    elseif isnan(zf) == 0 && isnan(zfnv) == 0
        if zf + zfnv ~= 1
            errordlg('Mole fractions must add up to 1')
            return
        elseif zf < 0 || zf > 1 || zfnv < 0 || zfnv > 1
            errordlg('Mole fractions must be between 0 and 1')
            return
        end
    end
end

%Distillate Composition
checkbox9_Status = get(handles.distillatecomposition_checkbox,'Value');
if checkbox9_Status == 1
    if isnan(xd) && isnan(xdnv)
        errordlg('Distillate composition must be a real number')
        return
    elseif isnan(xd) == 0 && isnan(xdnv) == 1
        xdnv = 1 - xd;
        xdnvs = num2str(xdnv);
        set(handles.distcomplessvolatile,'String',xdnvs)
    elseif isnan(xd) == 1 && isnan(xdnv) == 0
        xd = 1 - xdnv;
        xds = num2str(xd);
        set(handles.distcompvolatile,'String',xds)
    elseif isnan(xd) == 0 && isnan(xdnv) == 0
        if xd + xdnv ~= 1
            errordlg('Mole fractions must add up to 1')
            return
        elseif xd < 0 || xd > 1 || xdnv < 0 || xdnv > 1
            errordlg('Mole fractions must be between 0 and 1')
            return
        end
    end
end

%Bottoms Composition
checkbox10_Status = get(handles.bottomscomposition_checkbox,'Value');
if checkbox10_Status == 1
    if isnan(xb) == 1 && isnan(xbnv) == 1
        errordlg('Bottoms composition must be a real number')
        return
    elseif isnan(xb) == 0 && isnan(xbnv) == 1
        xbnv = 1 - xb;
        xbnvs = num2str(xbnv);
        set(handles.botcomplessvolatile,'String',xbnvs)
    elseif isnan(xb) == 1 && isnan(xbnv) == 0
        xb = 1 - xbnv;
        xbs = num2str(xb);
        set(handles.botcompvolatile,'String',xbs)
    elseif isnan(xb) == 0 && isnan(xbnv) == 0
        if xb + xbnv ~= 1
            errordlg('Mole fractions must add up to 1')
            return
        elseif xb < 0 || xb > 1 || xbnv < 0 || xbnv > 1
            errordlg('Mole fractions must be between 0 and 1')
            return
        end
    end
end

%Test to see if the mass balance has converged
%lk is a status vector for feed stream compositions
lk = [checkbox3_Status checkbox9_Status checkbox10_Status];
if sum(lk) == 3
    if sum(l) == 1
        if isnan(F) == 0
            if isnan(D) == 1 && isnan(B) == 1
                D = F*(zf-xb)/(xd-xb);
                Ds = num2str(D);
                set(handles.distillateflowrateinput,'String',Ds)
                B = F - D;
                Bs = num2str(B);
                set(handles.bottomsflowrateinput,'String',Bs)
                fv = D*xd/F/zf;
                fvs = num2str(fv);
                set(handles.fracrecoveryvolatile,'String',fvs)
                fnv = B*(1-xb)/F/(1-zf);
                fnvs = num2str(fnv);
                set(handles.fracrecoverylessvolatile,'String',fnvs)
            end
        end
    elseif sum(l) == 2
        if isnan(F) == 0
            if isnan(D) == 0
                Dk = F*(zf-xb)/(xd-xb);
                Bk = F - Dk;
                if D ~= Dk
                    Ds = num2str(Dk);
                    set(handles.distillateflowrateinput,'String',Ds)
                    Bk = F-Dk;
                    Bs = num2str(Bk);
                    set(handles.bottomsflowrateinput,'String',Bs)
                    fv = D*xd/F/zf;
                    fvs = num2str(fv);
                    set(handles.fracrecoveryvolatile,'String',fvs)
                    fnv = B*(1-xb)/F/(1-zf);
                    fnvs = num2str(fnv);
                    set(handles.fracrecoverylessvolatile,'String',fnvs)
                end
                if B ~= Bk
                    Bs = num2str(Bk);
                    set(handles.bottomsflowrateinput,'String',Bs)
                    Dk = F-Bk;
                    Ds = num2str(Dk);
                    set(handles.distillateflowrateinput,'String',Ds)
                    fv = D*xd/F/zf;
                    fvs = num2str(fv);
                    set(handles.fracrecoveryvolatile,'String',fvs)
                    fnv = B*(1-xb)/F/(1-zf);
                    fnvs = num2str(fnv);
                    set(handles.fracrecoverylessvolatile,'String',fnvs)
                end
            end
        end
    end
elseif sum(lk) == 1 && isnan(zf) == 0
checkbox12_Status = get(handles.fractionalrecoveries_checkbox,'Value');
    if sum(l) == 2 || sum(l) == 3
        %Fractional Recoveries
        if  checkbox12_Status == 1
            if any(isnan([fv fnv])) == 1
                errordlg('Fractional recovery must be a real number')
                return
            elseif any([fv fnv]>=1) || any([fv fnv]<0)
                errordlg('Fractional recovery must be between 0 and 1')
                return
            elseif any([fv fnv]==1)
                errordlg('100 percent recovery is physically impossible, try a different value')
                return
            else
                xd = fv/D*F*zf;
                xds = num2str(xd);
                set(handles.distcompvolatile,'String',xds)
                xb = 1-fnv/(F-D)*F*(1-zf);
                xbs = num2str(xb);
                set(handles.botcompvolatile,'String',xbs)
            end
        end
    elseif checkbox12_Status == 0
        errordlg('The system is underspecified')
        return
    end
end

%Fractional Recoveries
checkbox12_Status = get(handles.fractionalrecoveries_checkbox,'Value');
if checkbox12_Status == 0
    if all(isnan([F D B zf xd xb])) == 0
        fv = D*xd/F/zf;
        fvs = num2str(fv);
        set(handles.fracrecoveryvolatile,'String',fvs)
        fnv = B*(1-xb)/F/(1-zf);
        fnvs = num2str(fnv);
        set(handles.fracrecoverylessvolatile,'String',fnvs)
    end
end

%Feed Enthalpy
checkbox4_Status = get(handles.feedenthalpy,'Value');
if checkbox4_Status == 1 && isnan(hf) == 1
    errordlg('Feed enthalpy must be a real number')
    return
end

%Condenser Duty
checkbox5_Status = get(handles.condenserdutycheckbox,'Value');
if checkbox5_Status == 1 && isnan(Qc) == 1
    errordlg('Condenser duty must be a real number')
    return
end

%Reboiler Duty
checkbox7_Status = get(handles.reboilerdutycheckbox,'Value');
if checkbox7_Status == 1 && isnan(Qr) == 1
    errordlg('Reboiler duty must be a real number')
    return
end

%Reflux Enthalpy
checkbox8_Status = get(handles.refluxenthalpy,'Value');
if checkbox8_Status == 1 && isnan(hD) == 1
    errordlg('Reflux enthalpy must be a real number')
    return
end

%Reflux Ratio
checkbox11_Status = get(handles.refluxratio_checkbox,'Value');
if checkbox11_Status == 1
    if isnan(R) == 1 && isnan(Rmin) == 1
        errordlg('Reflux ratio must be a real number')
        return
    elseif R < 0
        errordlg('Reflux ratio must be greater than 0')
        return
    elseif Rmin < 0
        errordlg('Minumum reflux must be greater than 0')
        return
    elseif isnan(Rmin) == 0 && isnan(R) == 1
        errordlg('You must specify an operating factor')
        return
    end
elseif checkbox11_Status == 0
    if isnan(xd) == 1 && isnan(Q) == 1
        errordlg('You must specify feed quality and distillate composition')
        return
    elseif isnan(xd) == 0 && isnan(Q) == 1
        errordlg('You must specify feed quality')
        return
    elseif isnan(xd) == 1 && isnan(Q) == 0
        errordlg('You must specify distillate composition')
        return
    elseif isnan(Rmin) == 1 && isnan(Q) == 0 && isnan(xd) == 0
        if isnan(Alpha) == 0
            if isnan(R) == 1
                Emin = (xd-yie)/(xd-xie);
                Rmin = Emin/(1-Emin);
                Rmins = num2str(Rmin);
                set(handles.minimumrefluxinput,'String',Rmins)
                errordlg('Minimum reflux has been calculated and returned but you must specify an operating factor')
                return
            elseif isnan(R) == 0
                Emin = (xd-yie)/(xd-xie);
                Rmin = Emin/(1-Emin);
                Rmins = num2str(Rmin);
                R = R*Rmin;
                Rs = num2str(R);
                set(handles.minimumrefluxinput,'String',Rmins)
                set(handles.refluxratio_input,'String',Rs)
            elseif isnan(Rmin) == 0 && isnan(R) == 1
                errordlg('You must specify an operating factor')
                return
            end
        elseif isnan(Alpha) == 1 && k ==1
            if isnan(R) == 1
                Emin = (xd-yie)/(xd-xie);
                Rmin = Emin/(1-Emin);
                Rmins = num2str(Rmin);
                set(handles.minimumrefluxinput,'String',Rmins)
                errordlg('Minimum reflux has been calculated and returned but you must specify an operating factor')
                return
            elseif isnan(R) == 0
                Emin = (xd-yie)/(xd-xie);
                Rmin = Emin/(1-Emin);
                Rmins = num2str(Rmin);
                R = R*Rmin;
                Rs = num2str(R);
                set(handles.minimumrefluxinput,'String',Rmins)
                set(handles.refluxratio_input,'String',Rs)
            elseif isnan(Rmin) == 0 && isnan(R) == 1
                errordlg('You must specify an operating factor')
                return
            end
        end
    end
end

%Optimum Feed Plate
checkbox12_Status = get(handles.optimumfeedplate,'Value');
if checkbox12_Status == 0
    errordlg('This version does not support non-optimum feed plate')
    return
end

%Boilup Ratio
if S < 0
    errordlg('Boilup ratio must be greater than 0')
    return
end

%Boilup Enthalpy
checkbox20_Status = get(handles.boilupenthalpycheckbox,'Value');
if checkbox20_Status == 1 && isnan(hB) == 1
    errordlg('Boilup enthalpy must be a real number')
    return
end

%Pressure
if P < 0
    errordlg('Pressure must be greater than 0')
end

%Energy Balance
%checkbox4_Status = get(handles.feedenthalpy,'Value');
%checkbox5_Status = get(handles.condenserdutycheckbox,'Value');
%checkbox7_Status = get(handles.reboilerdutycheckbox,'Value');
%checkbox8_Status = get(handles.refluxenthalpy,'Value');
%checkbox20_Status = get(handles.boilupenthalpycheckbox,'Value');
%checkbox21_Status = get(handles.distillateenthalpycheckbox,'Value');
%checkbox22_Status = get(handles.bottomsenthalpycheckbox,'Value');
%jkl = [checkbox4_Status checkbox5_Status checkbox7_Status checkbox8_Status checkbox20_Status checkbox21_Status checkbox22_Status]; %Status vector
%Condenser Energy Balance
if isnan(Qc) == 0
    if isnan(hD) == 1
        hD = Qc/D/(R+1)*3600;
        hDs = num2str(hD);
        set(handles.refluxenthalpyinput,'String',hDs)
    end
elseif isnan(hD) == 0
    if isnan(Qc) == 1
        Qc = hB*D*(R+1)/3600;
        Qcs = num2str(Qc);
        set(handles.condenserdutyinput,'String',Qcs)
    end
end
%Reboiler Energy Balance
if isnan(Qr) == 0
    if isnan(hB) == 1
        hB = Qr/B/S*3600;
        hBs = num2str(hB);
        set(handles.boilupenthalpyinput,'String',hBs)
    end
elseif isnan(hB) == 0
    if isnan(Qr) == 1
        Qr = B*S*hB/3600;
        Qrs = num2str(Qr);
        set(handles.reboilerdutyinput,'String',Qrs)
    end
end
%jklm = [isnan(F) isnan(hf) isnan(Qc) isnan(Qr) isnan(B) isnan(HB) isnan(D) isnan(HD)];
%if sum(jklm) == 0
%    if F*hf + Qc + Qr - B*HB - D*HD >= 100
%        errordlg('The energy balance is not correct')
%        return
%    end
%end

%Equilibrium Curves
if k ==1 %Vector
    xx = 0:.001:1;
    yy = spline(xeq,yeq,xx);
    hold off
    plot(xx,yy)
    axis([0 1 0 1])
    hold on
    plot(xx,xx)
elseif k == 2 %Alpha
    ye = 0:.001:1;
    xe = equilib(ye,Alpha);
    hold off
    plot(xe,ye)
    axis([0 1 0 1])
    hold on
    plot(ye,ye)
end

%Plotting the Operating Lines
ytopop = ytop(ye,R,xd);
ybotop = ybot(ye,S,xb);
plot(xf,yf);
plot(ye,ytopop)
plot(ye,ybotop)
plot(ye,ye)
plot(xb,xb,'o')
text(xb+.025,xb-.025,'x_B')
plot(xd,xd,'o')
text(xd+.025,xd-.025,'x_D')
plot(zf,zf,'o')

% Computing the intersection of feed line and operating lines
plot(xi,yi,'o')
text(xi-.1,yi+.025,'Feed Line')
text(xb-.025,xb+.05,'Stripping Line')
text(xd-.25,xd-.05,'Enriching Line')
xlabel('Liquid Phase Mole Fraction, x')
ylabel('Vapor Phase Mole Fraction, y')
title('McCabe-Thiele Diagram for Binary Column Distillation')
if k == 2
    yi2 = interp1 (xe,ye,xi);
    if yi > yi2
        errordlg ('The distillation is not possible. Try a different operating condition.')
        return
    end
elseif k == 1
    yi2 = interp1(xx,yy,xi);
    if yi > yi2
        errordlg('The distillation is not possible. Try a different operating condition.')
        return
    end
end

% Rectifying section
if k == 2 %user defined systems
    i = 1;
    xp(1) = xd;
    yp(1) = xd;
    y = xd;
    while xp (i) > xi
        xp(i+1)= equilib(y,Alpha);
        yp(i+1)= R/(R+1)*xp (i+1)+xd/(R+1);
        y = yp (i+1);
        a = linspace(xp(i),xp(i+1));
        b = yp(i)*ones(length(a));
        plot(a,b)
        text ((xp(i+1)-.025),(yp(i)+.025),num2str (i))
        if xp (i+1) > xi
            c = linspace(yp(i),yp(i+1));
            d = xp(i+1)*ones(length(c));
            plot(d,c)
        end
        i = i+1;
    end
    % Stripping section
    xs = linspace(xb,xi);
    ys = linspace(xb,yi);
    plot(xs,ys)
    ss = (yi-xb)/(xi-xb);
    S = 1/(ss-1);
    S = num2str(S);
    set(handles.boilupratioinput,'String',S)
    yp(i) = ss*(xp(i)-xb)+xb;
    y = yp(i);
    a = linspace(yp(i-1),yp(i));
    b = xp(i)*ones(length(a));
    plot(b,a)
    while xp (i) > xb
        xp(i+1) = equilib(y,Alpha);
        yp(i+1) = ss*(xp(i+1)-xb)+xb;
        y = yp(i+1);
        a = linspace(xp(i),xp(i+1));
        b = yp(i)*ones(length(a));
        plot(a,b)
        text ((xp(i+1)-.025),(yp(i)+.025),num2str (i))
        if xp (i+1) > xb
            a = linspace(yp(i),yp(i+1));
            b = xp(i+1)*ones(length(a));
            plot(b,a)
        end
        i = i+1;
    end

elseif k ==1 %predefined equilibrium systems
    i = 1;
    xp(1) = xd;
    yp(1) = xd;
    y = xd;
    while xp(i) > xi
        xp(i+1)= predefeq(xeq,yeq,y);
        yp(i+1)= R/(R+1)*xp(i+1)+xd/(R+1);
        y = yp(i+1);
        a = linspace(xp(i),xp(i+1));
        b = yp(i)*ones(length(a));
        plot(a,b)
        text ((xp(i+1)-.025),(yp(i)+.025),num2str (i))
        if xp (i+1) > xi
            c = linspace(yp(i),yp(i+1));
            d = xp(i+1)*ones(length(c));
            plot(d,c)
        end
        i = i+1;
    end
    % Stripping section
    xs = linspace(xb,xi);
    ys = linspace(xb,yi);
    plot(xs,ys)
    ss = (yi-xb)/(xi-xb);
    S = 1/(ss-1);
    S = num2str(S);
    set(handles.boilupratioinput,'String',S)
    yp(i) = ss*(xp(i)-xb)+xb;
    y = yp(i);
    a = linspace(yp(i-1),yp(i));
    b = xp(i)*ones(length(a));
    plot(b,a)
    while xp (i) > xb
        xp(i+1) = predefeq(xeq,yeq,y);
        yp(i+1) = ss*(xp(i+1)-xb)+xb;
        y = yp(i+1);
        a = linspace(xp(i),xp(i+1));
        b = yp(i)*ones(length(a));
        plot(a,b)
        text ((xp(i+1)-.025),(yp(i)+.025),num2str (i))
        if xp (i+1) > xb
            a = linspace(yp(i),yp(i+1));
            b = xp(i+1)*ones(length(a));
            plot(b,a)
        end
        i = i+1;
    end
end

% Functions called with start button: operating & equilibrium lines
function yf = yfeed(xf,Q,zf)
yf = Q/(Q-1)*xf+zf/(1-Q);

function xa = equilib(ya,Alpha)
xa = ya./(Alpha-ya*(Alpha-1));

function ybotop = ybot(x,S,xb)
ybotop = (S+1)/S*x-xb/S;

function ytopop = ytop(x,R,xd)
ytopop = R/(R+1)*x+xd/(R+1);

function backl = predefeq(xeq,yeq,val)
xx = 0:.001:1;
yy = spline(xeq,yeq,xx);
ym = val*ones(1,length(xx));
diffe = abs(yy-ym);
[a b] = min(diffe);
xk = xx(b);
backl = xk;

%System of Nonlinear Equations -- Will be functional in a later version

%User Input Vector
%L = R*D;
%V = D*(R+1);
%Vbar = S*B;
%Lbar = B*(S+1);
%Input_Vector = [F,zf,zfnv,xd,xdnv,xb,xbnv,D,fv,fnv,B,S,Q,R,Rmin,hf,hD,hB,Qc,Qr,L,V,Lbar,Vbar,xie,yie,xi,yi];
%for k = 1:20
%    if isnan(Input_Vector(k)) == 0
%        v(k) = Input_Vector(k);
%    end
%end

%v(1) = F;
%v(2) = zf;
%v(3) = zfnv;
%v(4) = xd;
%v(5) = xdnv;
%v(6) = xb;
%v(7) = xbnv;
%v(8) = D;
%v(9) = fv;
%v(10) = fnv;
%v(11) = B;
%v(12) = S;
%v(13) = Q;
%v(14) = R;
%v(15) = Rmin;
%v(16) = hf;
%v(17) = hD;
%v(18) = hB;
%v(19) = Qc;
%v(20) = Qr;
%v(21) = L;
%v(22) = V;
%v(23) = Lbar;
%v(24) = Vbar;
%v(25) = xie;
%v(26) = yie;
%v(27) = xi;
%v(28) = yi;

%F = @(v) [v(10)-v(11).*v(7)./v(1)./v(3); v(9)-v(8).*v(4)./v(1)./v(2); v(11)+v(24)-v(23);
%    v(21)+v(8)-v(22); v(23)./v(24)-1./v(12)+1; v(22)./v(21)-1./v(14)-1; v(11)+v(8)-v(1);
%    v(11).*v(6)+v(8).*v(4)-v(1).*v(2); v(2)+v(3)-1; v(6)+v(7)-1; v(4)+v(5)-1;
%    v(1).*v(16)+v(19)+v(20)-v(8).*v(17)-v(11).*v(18); v(15)./(1+v(15))-(v(4)-v(26))./(v(4)-v(25));
%    v(12)-1./(((v(28)-v(6))./(v(27)-v(6)))-1); v(14)./(1+v(14))-(v(4)-v(28))./(v(4)-v(27))];

%InitialGuess = [50;.5;.5;.5;.5;.5;.5;25;.75;.75;25;2;.5;3;1.5;1;1;1;1;1;1;1;1;1;.5;.5;.5;.5];
%Options = optimset('Display','iter');
%XY = fsolve(F, InitialGuess, Options);

%fnv - B*xbnv/F/zfnv
%fv - D*xd/F/zf;
%B + Vbar - Lbar;
%L + D - V;
%Lbar/Vbar - 1/S + 1;
%V/L - 1/R - 1;
%B + D - F;
%B*xb + D*xd - F*zf;
%zf + zfnv - 1;
%xb + xbnv - 1;
%xd + xdnv - 1;
%F*hf + Qc + Qr - D*hD - B*hB;
%Rmin/(1+Rmin) - (xd-yie)/(xd-xie);
%S - 1/((yi-xb)/(xi-xb)-1);
%R/(1+R)-(xd-yi)/(xd-xi);