classdef WallMechanics < handle
    % The 'WallMechanics' code contains the following sections of the model:
    %   The Contraction and Mechanical Models.
    %   Please refer to the relevient sections in the documentation for 
    %   full information on the equations and variable names.
    properties
        params
        u0
        index
        n_out
        idx_out
        enabled
    end
    methods
        function self = WallMechanics(varargin)
            self.params = parse_inputs(varargin{:});
            self.index = indices();
            self.u0 = initial_conditions(self.index);
            self.enabled = true(size(self.u0));
            [self.idx_out, self.n_out] = output_indices();
        end
        function [du, varargout] = rhs(self, t, u, Ca_i, R_cGMP2)
            % Initalise inputs and parameters
            idx = self.index;
            p = self.params;
            % Make the output function compute these so as to avoid
            % duplicating equations
            [R, h] = self.shared(t, u);

            Mp = u(idx.Mp, :);
            AMp = u(idx.AMp, :);
            AM = u(idx.AM, :);
            
            %% Contraction Equations
            K_1 = p.gamma_cross * Ca_i.^p.n_cross;
            K_6 = K_1;
            K_2 = 58.1395 * p.k_mlcp_b + 58.1395 * p.k_mlcp_c * R_cGMP2;
            K_5 = K_2;
            
            M = 1 - AM - AMp - Mp;
            du(idx.Mp, :) = p.K_4 * AMp + K_1 .* M - (K_2 + p.K_3) .* Mp;
            du(idx.AMp, :) = p.K_3 * Mp + K_6 .* AM - (p.K_4 + K_5) .* AMp;
            du(idx.AM, :) = K_5 .* AMp - (p.K_7 + K_6) .* AM;
            
            % Mechanical Equations
            F_r = AMp + AM;
            E = p.E_passive + F_r * (p.E_active - p.E_passive);
            R_0 = p.R_0_passive + F_r * (p.alpha - 1) * p.R_0_passive;

            du(idx.R, :) =  p.R_0_passive / p.eta * ( R * p.P_T ./ h - ...
                E .* (R - R_0) ./ R_0);

            du = bsxfun(@times, self.enabled, du);
            
            if nargout == 2
               Uout = zeros(self.n_out, size(u, 2));
               Uout(self.idx_out.M, :) = M;
               Uout(self.idx_out.F_r, :) = F_r;
               Uout(self.idx_out.R, :) = R;
               Uout(self.idx_out.K_2, :) = K_2;
               varargout{1} = Uout;
            end
        end 

        function [R, h] = shared(self, ~,u)
           
            R = u(self.index.R, :);
            h = 0.1 * R; 
        end  

        function names = varnames(self)
            names = [fieldnames(self.index); fieldnames(self.idx_out)];
        end
    end 
end

function idx = indices()
    % Index of parameters needing inital conditions 
    idx.Mp = 1;
    idx.AMp = 2;
    idx.AM = 3;
    idx.R = 4;
end

function [idx, n] = output_indices()
    % Index of all other output parameters
    idx.M = 1;
    idx.F_r = 2;
    idx.R = 3;
    idx.K_2 = 4;
    n = numel(fieldnames(idx));
end

function params = parse_inputs(varargin)
    parser = inputParser();
    % Contraction Equation Constants
    parser.addParameter('K_3', 0.4); % s^-1
    parser.addParameter('K_4', 0.1); % s^-1
    parser.addParameter('K_7', 0.1); % s^-1
    parser.addParameter('gamma_cross', 17); %uM^-3 s^-1
    parser.addParameter('n_cross', 3); % fraction constant of the phosphorylation crossbridge
    
    % Mechanical Equation Constants
    parser.addParameter('eta', 1e4); %Pa s
    parser.addParameter('R_0_passive', 20e-6); % m
    parser.addParameter('P_T', 4000); % Pa
    parser.addParameter('E_passive', 66e3); % Pa
    parser.addParameter('E_active', 233e3); % Pa
    parser.addParameter('alpha', 0.6); % [-]
    parser.addParameter('k_mlcp_b', 0.0086); % [s^-1]
    parser.addParameter('k_mlcp_c', 0.0327); % [s^-1]

    parser.parse(varargin{:});
    params = parser.Results;

end
function u0 = initial_conditions(idx)
    u0 = zeros(length(fieldnames(idx)), 1);
    % Inital estimations of parameters from experimental data
    u0(idx.Mp) = .25;
    u0(idx.AMp) = .25;
    u0(idx.AM) = .25;
    u0(idx.R) = 24.8e-6;
end