function md=macayealdiagnostic(md)
%MACAYEALDIAGNOSTIC - compute the velocity field of a 2d model using MacAyeal solution
%
%   this routine solves the problem using MacAyeal's model. It calculates the velocity
%   field corresponding to the parameters and the geometry given by the model md
%
%   Usage:
%      md=macayealdiagnostic(md)

%First check we do have the correct argument number
if ((nargin~=1) || (nargout~=1)),
	macayealdiagnosticusage();
	error('macayealdiagnostic error message: incorrect number of input and output arguments');
end

if ~isa(md,'model'),
	macayealdiagnosticusage();
	error('macayealdiagnostic error message: input argument is not a @model object');
end

%start timing
t1=clock;

%Transfer model fields into matlab variables
x=md.x;
y=md.y;

index=md.elements;index=sort(index,2); %necessary 
nods=md.numberofgrids;
nel=md.numberofelements;
z_thick=md.thickness;
z_surf=md.surface;
z_bed=md.bed;
z_thick_bar=(z_thick(index(:,1))+z_thick(index(:,2))+z_thick(index(:,3)))/3;
rho_ice=md.rho_ice;
rho_water=md.rho_water;
g=md.g;
viscosity_overshoot=md.viscosity_overshoot;
index_icefront=md.segmentonneumann_diag; index_icefront=index_icefront(:,1:2); %we strip the last column, which holds the element number for the boundary segment
nodes_on_boundary=md.gridonboundary;
nodes_on_icefront=zeros(nods,1); nodes_on_icefront(index_icefront)=1;
node_on_dirichlet=md.gridondirichlet_diag;
nodes_on_icesheet=md.gridonicesheet;
element_on_icesheet=md.elementonicesheet;
qcoeff=md.q;
pcoeff=md.p;
drag_type=md.drag_type;
drag=md.drag;

criterion_rel=md.eps_rel;
criterion_abs=md.eps_abs;
yts=md.yts;
B=md.B; B_bar=(B(index(:,1))+B(index(:,2))+B(index(:,3)))/3;
glen_coeff=md.n;

%average of p and q over the grids (size nel->nods)
pcoeff_grid=zeros(nods,1);
qcoeff_grid=zeros(nods,1);
for i=1:nods
    %1: find the elements that contain the grid i
    neighbors_gridi=[];
    for j=1:3
        neighbors_gridi=[neighbors_gridi find(index(:,j)==i)'];
    end
    numberofneighbors_gridi=length(neighbors_gridi);
    %2 retrieve the value of p and q over each of these elements. The average is
    %plugged into dbx_grid
    qcoeff_grid(i)=sum(qcoeff(neighbors_gridi))/numberofneighbors_gridi;
    pcoeff_grid(i)=sum(pcoeff(neighbors_gridi))/numberofneighbors_gridi;
end

%Build length_icefront and normal_icefront:
[length_icefront,normal_icefront]=buildicefrontnormal(x,y,index_icefront);

%Start building areas
aire=zeros(md.numberofelements,1);

for n=1:nel
	aire(n)=1/2 * det([1 1 1;x(index(n,:))';y(index(n,:))']);
end

aire=abs(aire); % if index is sorted from its original value, then aire could be negative

alpha=zeros(nel,3);
beta=zeros(nel,3);
gamma=zeros(nel,3);

for n=1:nel
	X=inv([x(index(n,:)) y(index(n,:)) ones(3,1)]);
	alpha(n,:)=X(1,:);
	beta(n,:)=X(2,:);
	gamma(n,:)=X(3,:);
end

clear X

%Initialize viscosity
nu_bar=viscosity(index,nel,alpha,beta,{},{},B_bar,glen_coeff);


 %  Do once and for all the initial computation of matrix-locations:

row_location=zeros(nel*3*3,1);
col_location=zeros(nel*3*3,1);
row_location_AD=zeros(nel*6,1);
col_location_AD=zeros(nel*6,1);

count=-nel+1;

for i=1:3
	for j=1:3
			count=count+nel;
			row_location(count:count+nel-1)=index(:,i);
			col_location(count:count+nel-1)=index(:,j);
	end
end


count=-nel+1;

for i=1:3
	for j=i:3
			count=count+nel;
			row_location_AD(count:count+nel-1)=index(:,i);
			col_location_AD(count:count+nel-1)=index(:,j);
	end
end



permanent_pieces_of_A=zeros(nel*6,1);
permanent_pieces_of_B=zeros(nel*3*3,1);
permanent_pieces_of_C=zeros(nel*3*3,1);
permanent_pieces_of_D=zeros(nel*6,1);

count=-nel+1;

for i=1:3
	for j=i:3
			count=count+nel;
			permanent_pieces_of_A(count:count+nel-1)= z_thick_bar .* aire ...
				.*(2*alpha(:,i).*alpha(:,j) + 1/2*beta(:,i).*beta(:,j));				
				% This loop structure works only when index is sorted
			permanent_pieces_of_D(count:count+nel-1)= z_thick_bar .* aire ...
				.*(2*beta(:,i).*beta(:,j) + 1/2*alpha(:,i).*alpha(:,j));

	end
end

count=-nel+1;

for i=1:3
	for j=1:3
			count=count+nel;

			permanent_pieces_of_B(count:count+nel-1)= z_thick_bar .* aire ...
			.*(alpha(:,j).*beta(:,i) + 1/2*beta(:,j).*alpha(:,i));

			permanent_pieces_of_C(count:count+nel-1)= z_thick_bar .* aire ...
			.*(beta(:,j).*alpha(:,i) + 1/2*alpha(:,j).*beta(:,i));


	end
end

% Step 3 -- Set up right-hand side of the problem. 

% (Note to myself: to avoid vector dependency problem, yet still vectorize,
% I treat the Rhs vector as a sparse2 matrix

Rhs_x=zeros(nel*27,1);
Rhs_y=zeros(nel*27,1);
Rhs_y=ones(nel*27,1);
row_rhs=zeros(nel*27,1);


count=-nel+1;
for i=1:3
	for n=1:3
		for m=1:3
			count=count+nel;

			Rhs_x(count:count+nel-1)= -rho_ice * g  * ...
			 z_thick(index(:,n)).*z_surf(index(:,m)) ...
			.* aire(:) .* alpha(:,m) * ( (n==i)/6 + (n~=i)/12 );

			Rhs_y(count:count+nel-1)= -rho_ice * g  * ...
			 z_thick(index(:,n)).*z_surf(index(:,m)) ...
			.* aire(:) .* beta(:,m) .* ( (n==i)/6 + (n~=i)/12 );

			row_rhs(count:count+nel-1)=index(:,i);

		end
	end
end

Rhs=full([sparse2(row_rhs,ones(nel*27,1),Rhs_x,nods,1)
   sparse2(row_rhs,ones(nel*27,1),Rhs_y,nods,1)]);

for k=1:2
	for l=1:2
		for j=1:2
				Rhs(index_icefront(:,k))=Rhs(index_icefront(:,k)) + ...
(rho_ice*g/2*z_thick(index_icefront(:,l)).*z_thick(index_icefront(:,j)) ...
+ rho_water*g/2*(md.gridoniceshelf(index_icefront(:,k))) ...
.*(-min(0,z_bed(index_icefront(:,l))).*min(0,z_bed(index_icefront(:,j))) ...
+min(0,z_thick(index_icefront(:,l))+z_bed(index_icefront(:,l)))...
.*min(0,z_thick(index_icefront(:,j))+z_bed(index_icefront(:,j)))))...
.*normal_icefront(:,1).*length_icefront(:) ...
/(4*(l==k & j==k) + 12*(l~=k | j~=k) );

				Rhs(index_icefront(:,k)+nods)=Rhs(index_icefront(:,k)+nods) + ...
(rho_ice*g/2*z_thick(index_icefront(:,l)).*z_thick(index_icefront(:,j)) ...
+ rho_water*g/2*(md.gridoniceshelf(index_icefront(:,k))) ...
.*(-min(0,z_bed(index_icefront(:,l))).*min(0,z_bed(index_icefront(:,j))) ...
+min(0,z_thick(index_icefront(:,l))+z_bed(index_icefront(:,l)))...
.*min(0,z_thick(index_icefront(:,j))+z_bed(index_icefront(:,j)))))...
.*normal_icefront(:,2).*length_icefront(:) ...
/(4*(l==k & j==k) + 12*(l~=k | j~=k) );

		end
	end
end

clear Rhs_x Rhs_y row_rhs

% Step 4 -- Create the parsing matrix to "wring out"
%           the known boundary conditions

% kinematic condition (u,v=0) specified at non-ice-front boundaries;
num_specified= 2*sum(node_on_dirichlet);
row=zeros(2*nods-num_specified,1);
col=zeros(2*nods-num_specified,1);
value=zeros(2*nods-num_specified,1);
count=0;
for n=1:nods
   if(~node_on_dirichlet(n))
   		count=count+1;
		row(count)=count;
		col(count)=n;
		value(count)=1;
	end
end
for n=1:nods
   if (~node_on_dirichlet(n))
         count=count+1;
		row(count)=count;
		col(count)=nods+n;
		value(count)=1;
	end
end

P=sparse2(row,col,value,2*nods-num_specified,2*nods);


specified_velocity=zeros(2*nods,1);
pos=find(node_on_dirichlet);
specified_velocity(pos)=md.dirichletvalues_diag(pos,1)/md.yts;
specified_velocity(nods+pos)=md.dirichletvalues_diag(pos,2)/md.yts;


% ######## an attention grabbing break ########
%      Iterate on flow law till converged

converged_yet=0;
loop=0;

u_old=zeros(nods,1);
v_old=zeros(nods,1);
convergence_count=1;

while (~converged_yet)

	if (loop>(100))
		warning('Maximum Viscosity Iterations  Reached.')
		break;
	end;
	loop=loop+1;

	nu_bar_oldvalue=nu_bar;

	% Step 4 -- Set up stress-balance matrix  (whos solution is the velocity
	% field):

	matrix_value_A=zeros(nel*6,1);
	matrix_value_B=zeros(nel*3*3,1);
	matrix_value_C=zeros(nel*3*3,1);
	matrix_value_D=zeros(nel*6,1);

	count=-nel+1;

	for i=1:3
		for j=i:3
				count=count+nel;

				matrix_value_A(count:count+nel-1)=nu_bar .* ...
				  permanent_pieces_of_A(count:count+nel-1);

				matrix_value_D(count:count+nel-1)=nu_bar .* ...
				  permanent_pieces_of_D(count:count+nel-1);


		end
	end

	count=-nel+1;

	for i=1:3
		for j=1:3
				count=count+nel;

				matrix_value_B(count:count+nel-1)=nu_bar .* ...
				  permanent_pieces_of_B(count:count+nel-1);

				matrix_value_C(count:count+nel-1)=nu_bar .* ...
				  permanent_pieces_of_C(count:count+nel-1);


		end
	end



	A=sparse2(row_location_AD,col_location_AD,matrix_value_A,nods,nods);
	A=A+triu(A,1)';
	B=sparse2(row_location,col_location,matrix_value_B,nods,nods);
	C=sparse2(row_location,col_location,matrix_value_C,nods,nods);
	D=sparse2(row_location_AD,col_location_AD,matrix_value_D,nods,nods);
	D=D+triu(D,1)';


	F=[A C
	B D];

	%Now, take care of the basal friction if there is any: to make things easier, we translate u = k *Neff^(-q)* sigma^p into 
	% sigma= drag^2 * Neff ^(r) * u^s with r=q/p and s=1/p : */
	 
	if  (drag_type==2), 
		%compute coeffs: 
		rcoeff=qcoeff_grid./pcoeff_grid;
		scoeff=1./pcoeff_grid;
		
		%initialization of basal drag stiffness
		Dragoperator=spalloc(2*nods,2*nods,0);

		if loop~=1,
			
			%retrieve the velocity magnitude
			velocity_mag=sqrt(solution(1:nods).^2+solution(nods+1:2*nods).^2);
		
			%Computation of the effective pressure
			Neff=g*(rho_ice*z_thick+rho_water*z_bed);

			%If effective pressure becomes negative, sliding becomes unstable (Paterson 4th edition p 148). This is because 
			%the water pressure is so high, the ice sheet elevates over its ice bumps and slides. But the limit behaviour 
			%for this should be an ice shelf sliding (no basal drag). Therefore, for any effective pressure Neff < 0, we should 
			%replace it by Neff=0 (ie, equival it to an ice shelf)*/
			pos=find(Neff<0);
			Neff(pos)=0;
			
			%Basal drag coefficient: Tau_x=-alpha^2 u, Tau_y=-alpha^2 v (See
			%MacAyeal)
			alpha2=(drag.^2).*(Neff.^rcoeff).*(velocity_mag.^(scoeff-1));

			%stiffness due to basal drag 
			%initialization
			count=0;
			value=zeros(nel,27);
			row=zeros(nel,27);
			col=zeros(nel,27);

			for m=1:3
				for k=1:3
				for l=1:3
					if ( (m==k) + (m==l) + (l==k) )==3
					fac=1/10;
					elseif ( (m==k) + (m==l) + (l==k) )==1
					fac=1/30;
					else
					fac=1/60;
					end

					count=count+1;
					row(:,count)=index(:,m);
					col(:,count)=index(:,k);
					value(:,count)=fac*aire(:).* ...
					(alpha2(index(:,l)));%.*element_on_icesheet(index(:,l));

				end
				end
			end

			Dragoperator=[sparse2(row,col,value,nods,nods) spalloc(nods,nods,0)
				spalloc(nods,nods,0) sparse2(row,col,value,nods,nods)];
			end

		F=F+Dragoperator;  %plug into the global stiffness matrix
		
	end


	Rhs_parsed=P*(Rhs - F*specified_velocity);
	F=P*F*P';


	% Step 5 -- Solve the problem:

	% Digression: if need be clear up some memory:

	clear   matrix_value_A ...
	matrix_value_B matrix_value_C matrix_value_D A B C D 

	% We can use either the LU or the Cholesky decomposition, but the
	% Cholesky decomposition is twice as efficient as LU for symmetric
	% definite positive matrix

		if strcmpi(md.solver_type,'lu'),
			% Solve by LU decomposition. 
			[L,U] = lu(F);
			solution= U\(L\Rhs_parsed);
		elseif strcmpi(md.solver_type,'cholesky'),
			% Solve by Choleski decomposition.
			L = chol(F);
			solution= L\(L'\Rhs_parsed);
		else
			% use matlab's generic solver
			solution = F\Rhs_parsed;
		end
	   


	%Add spcs to the calculated solution
	solution=P'*solution + specified_velocity;

	%Recover solution vector
	u=solution(1:nods);
	v=solution(nods+1:2*nods);

	%Compute viscosity 
	nu_bar=viscosity(index,nel,alpha,beta,u,v,B_bar,glen_coeff);
	change=1 - nu_bar_oldvalue./nu_bar; 

	%Update viscosity
	nu_bar=nu_bar.*(1+viscosity_overshoot*change);
	location=find(nu_bar<=0);
	nu_bar(location)=-nu_bar(location);

	%Test for direct shooting convergence
	if convergence_count>1,

		ug=[u_old;v_old];
		nug=norm(ug,2);
		dug=[u; v]-[u_old; v_old];
		ndug=norm(dug,2);
		relative_change=ndug/nug;


		%Figure out if viscosity converged
		if relative_change<criterion_rel,
			disp(sprintf('%s %g %s %g %s','   Convergence criterion: norm(du)/norm(u)=',relative_change,' < ',criterion_rel,' m/yr'));
			converged_yet=1;
		else
			disp(sprintf('%s %g %s %g %s','   Convergence criterion: norm(du)/norm(u)=',relative_change,' > ',criterion_rel,' m/yr'));
			converged_yet=0;
		end

		if ~isnan(criterion_abs),
			change=max(abs(dug))*yts;
			if change<criterion_abs
				disp(sprintf('%s %g %s %g %s','   Convergence criterion: max(du)=',change,' < ',criterion_abs,' m/yr'));
				converged_yet=1;
			else
				disp(sprintf('%s %g %s %g %s','   Convergence criterion: max(du)=',change,' > ',criterion_abs,' m/yr'));
				converged_yet=0;
			end
		end
	else 
		converged_yet=0;
	end

	u_old=u;
	v_old=v;

	convergence_count=convergence_count+1;
		
end % This end statement terminates the "while" command way above

%Load results onto md:
md.vx=u*yts;
md.vy=v*yts;
md.vel=sqrt(md.vx.^2+md.vy.^2);

%stop timing
t2=clock;

disp(sprintf('\n%s\n',['solution converged in ' num2str(etime(t2,t1)) ' seconds']));

function macayealdiagnosticusage();
disp('md=macayealdiagnostic(md)');
disp('   where md is a structure of class @model');
