function [K,f] = FEM_system(X,T,referenceElement,example)
% [K,f] = FEM_system(X,T,referenceElement,example)
% Matrix K and r.h.s vector f obtained after discretizong a 2D convection-diffusion equation
%
% X:            nodal coordinates
% T:            connectivities (elements)
% referenceElement: reference element properties (quadrature, shape functions...)
% example: example properties



velo = example.velo;
a = example.a;
nu = example.nu;
sigma = example.sigma;
s=example.s;
method = example.method; 
ax = velo(1);
ay = velo(2);
Te = T(1,:); Xe = X(Te,:); 
hx = max(Xe(:,1)) - min(Xe(:,1)); 
hy = max(Xe(:,2)) - min(Xe(:,2)); 
h = (hx+hy)/2; 


nen = referenceElement.nen;
ngaus = referenceElement.ngaus;
wgp = referenceElement.GaussWeights;
N = referenceElement.N;
Nxi = referenceElement.Nxi;
Neta = referenceElement.Neta;

% Number of elements and number of nodes in the mesh
nElem = size(T,1);
nPt = size(X,1);

K = zeros(nPt,nPt);
f = zeros(nPt,1);

if method == 0
    % Galerkin
    tau = 0;
elseif method == 1
    % Artificial Diffusion
    Pe_x = ax*h/(2*nu);
    Pe_y = ay*h/(2*nu);
    alpha_x = coth(Pe_x)-1/Pe_x;
    alpha_y = coth(Pe_y)-1/Pe_y;
    tau = h*(ax*alpha_x + ay*alpha_y)/2;
    nubar_p = tau*a*a;
    disp(strcat('Recommended value for the artificial diffusion = ',num2str(nubar_p)));
    nubar = cinput('Artificial diffusion to be used', nubar_p);
    if isempty(nubar)
        nubar = nubar_p;
    end
    nu = nu + nubar;
    tau=0;
elseif method == 2 || method ==3
    % SUPG
    Pe = a*h/(2*nu);
    tau_p = h*(1 + 9/Pe^2)^(-1/2)/(2*a);
    disp(strcat('Recommended stabilization parameter = ',num2str(tau_p)));
    tau = cinput('Stabilization parameter',tau_p);
    if isempty(tau)
        tau = tau_p;
    end
else
    error ('Unavailable method')
end

% Loop on elements
for ielem = 1:nElem
    % Te: global number of the nodes in the current element
    Te = T(ielem,:);
    % Xe: coordenates of the nodes in the current element
    Xe = X(Te,:);
    % Element matrices
    [Ke,fe] = EleMat(Xe,nen,ngaus,wgp,N,Nxi,Neta,method,tau,velo,nu,sigma,s);
    % Assemble the element matrices
    K(Te,Te) = K(Te,Te) + Ke;
    f(Te) = f(Te) + fe;
end






function [Ke,fe] = EleMat(Xe,nen,ngaus,wgp,N,Nxi,Neta,method,tau,velo,nu,sigma,s)
%

ax = velo(1);
ay = velo(2);

Ke = zeros(nen,nen);
fe = zeros(nen,1);


% Loop on Gauss points (computation of integrals on the current element)
for ig = 1:ngaus
    N_ig = N(ig,:);
    Nxi_ig = Nxi(ig,:);
    Neta_ig = Neta(ig,:);
    Jacob = [Nxi_ig*(Xe(:,1))	Nxi_ig*(Xe(:,2))
        Neta_ig*(Xe(:,1))	Neta_ig*(Xe(:,2))];
    dvolu = wgp(ig)*det(Jacob);
    res = Jacob\[Nxi_ig;Neta_ig];
    Nx = res(1,:);
    Ny = res(2,:);
    
    if method == 0
        % Galerkin
        Ke = Ke + (nu*(Nx'*Nx+Ny'*Ny) + N_ig'*(ax*Nx+ay*Ny) + sigma*N_ig'*N_ig)*dvolu;
        aux = N_ig*Xe; 
        f_ig = SourceTerm(aux,s);
        fe = fe + N_ig'*(f_ig*dvolu);
    elseif method == 1
        % Artifficial diffusion
        Ke = Ke + (nu*(Nx'*Nx+Ny'*Ny) + N_ig'*(ax*Nx+ay*Ny) + sigma*N_ig'*N_ig)*dvolu;
        aux = N_ig*Xe; 
        f_ig = SourceTerm(aux,s);
        fe = fe + N_ig'*(f_ig*dvolu);
    elseif method ==2
        % SUPG
        Ke = Ke + (nu*(Nx'*Nx+Ny'*Ny) + N_ig'*(ax*Nx+ay*Ny) + sigma*N_ig'*N_ig + ...
            tau*((ax*Nx+ay*Ny)'*(ax*Nx+ay*Ny) + (ax*Nx+ay*Ny)'*sigma*N_ig))*dvolu;
        aux = N_ig*Xe; 
        f_ig = SourceTerm(aux,s);
        fe = fe + (N_ig+tau*(ax*Nx+ay*Ny))'*(f_ig*dvolu);
    elseif method==3
         % GLS
        Ke = Ke + (nu*(Nx'*Nx+Ny'*Ny) + N_ig'*(ax*Nx+ay*Ny) + sigma*N_ig'*N_ig + ...
            tau*((ax*Nx+ay*Ny+sigma*N_ig)'*(ax*Nx+ay*Ny) + (ax*Nx+ay*Ny+sigma*N_ig)'*sigma*N_ig))*dvolu;
        aux = N_ig*Xe; 
        f_ig = SourceTerm(aux,s);
        fe = fe + (N_ig+tau*(ax*Nx+ay*Ny))'*(f_ig*dvolu);
    end
end

