/*!\file Penta.cpp
 * \brief: implementation of the Penta object
 */

/*Headers:*/
/*{{{*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#else
#error "Cannot compile with HAVE_CONFIG_H symbol! run configure first!"
#endif

#include <stdio.h>
#include <string.h>
#include "../../classes.h"
#include "../../../shared/shared.h"
#include "../../../include/include.h"
#include "../../../Container/Container.h"
/*}}}*/

/*Element macros*/
#define NUMVERTICES   6
#define NUMVERTICES2D 3

/*Constructors/destructor/copy*/
/*FUNCTION Penta::Penta(){{{*/
Penta::Penta(){

	int i;

	this->nodes=NULL;
	this->matice=NULL;
	this->matpar=NULL;
	this->verticalneighbors=NULL;
	this->inputs=NULL;
	this->parameters=NULL;
	this->results=NULL;
	for(i=0;i<3;i++)this->horizontalneighborsids[i]=UNDEF;
}
/*}}}*/
/*FUNCTION Penta::~Penta(){{{*/
Penta::~Penta(){
	delete inputs;
	delete results;
	this->parameters=NULL;
}
/*}}}*/
/*FUNCTION Penta::Penta(int id, int index, IoModel* iomodel,int nummodels) {{{*/
Penta::Penta(int penta_id, int penta_sid, int index, IoModel* iomodel,int nummodels)
	:PentaRef(nummodels)
	,PentaHook(nummodels,index+1,iomodel) //index+1: matice id, iomodel->numberofelements+1: matpar id
                                                                      { //i is the element index

	int i;
	int penta_elements_ids[2];

	/*Checks in debugging mode*/
	/*{{{*/
	_assert_(iomodel->Data(MeshUpperelementsEnum));
	_assert_(iomodel->Data(MeshLowerelementsEnum));
	/*}}}*/

	/*id: */
	this->id=penta_id;
	this->sid=penta_sid;

	/*Build neighbors list*/
	if (xIsNan<IssmDouble>(iomodel->Data(MeshUpperelementsEnum)[index])) penta_elements_ids[1]=this->id; //upper penta is the same penta
	else                                    penta_elements_ids[1]=(int)(iomodel->Data(MeshUpperelementsEnum)[index]);
	if (xIsNan<IssmDouble>(iomodel->Data(MeshLowerelementsEnum)[index])) penta_elements_ids[0]=this->id; //lower penta is the same penta
	else                                    penta_elements_ids[0]=(int)(iomodel->Data(MeshLowerelementsEnum)[index]);
	this->InitHookNeighbors(penta_elements_ids);

	/*Build horizontalneighborsids list: */
	_assert_(iomodel->Data(MeshElementconnectivityEnum));
	for(i=0;i<3;i++) this->horizontalneighborsids[i]=(int)iomodel->Data(MeshElementconnectivityEnum)[3*index+i]-1;

	//this->parameters: we still can't point to it, it may not even exist. Configure will handle this.
	this->parameters=NULL;

	/*intialize inputs and results: */
	this->inputs=new Inputs();
	this->results=new Results();
	
	/*initialize pointers:*/
	this->nodes=NULL;
	this->matice=NULL;
	this->matpar=NULL;
	this->verticalneighbors=NULL;
}
/*}}}*/
/*FUNCTION Penta::copy {{{*/
Object* Penta::copy() {

	int i;

	Penta* penta=NULL;

	penta=new Penta();

	//deal with PentaRef mother class
	penta->element_type_list=xNew<int>(this->numanalyses);
	for(i=0;i<this->numanalyses;i++) penta->element_type_list[i]=this->element_type_list[i];

	//deal with PentaHook mother class
	penta->numanalyses=this->numanalyses;
	penta->hnodes=new Hook*[penta->numanalyses];
	for(i=0;i<penta->numanalyses;i++)penta->hnodes[i]=(Hook*)this->hnodes[i]->copy();
	penta->hmatice=(Hook*)this->hmatice->copy();
	penta->hmatpar=(Hook*)this->hmatpar->copy();
	penta->hneighbors=(Hook*)this->hneighbors->copy();

	/*deal with Penta  copy fields: */
	penta->id=this->id;
	penta->sid=this->sid;
	if(this->inputs){
		penta->inputs=(Inputs*)this->inputs->Copy();
	}
	else{
		penta->inputs=new Inputs();
	}
	if(this->results){
		penta->results=(Results*)this->results->Copy();
	}
	else{
		penta->results=new Results();
	}
	/*point parameters: */
	penta->parameters=this->parameters;

	/*recover objects: */
	penta->nodes=xNew<Node*>(6); //we cannot rely on an analysis_counter to tell us which analysis_type we are running, so we just copy the nodes.
	for(i=0;i<6;i++)penta->nodes[i]=this->nodes[i];
	penta->matice=(Matice*)penta->hmatice->delivers();
	penta->matpar=(Matpar*)penta->hmatpar->delivers();
	penta->verticalneighbors=(Penta**)penta->hneighbors->deliverp();

	/*neighbors: */
	for(i=0;i<3;i++)penta->horizontalneighborsids[i]=this->horizontalneighborsids[i];

	return penta;
}
/*}}}*/

/*Other*/
/*FUNCTION Penta::AverageOntoPartition {{{*/
void  Penta::AverageOntoPartition(Vector* partition_contributions,Vector* partition_areas,IssmDouble* vertex_response,IssmDouble* qmu_part){
	_error2_("Not supported yet!");
}
/*}}}*/
/*FUNCTION Penta::BedNormal {{{*/
void Penta::BedNormal(IssmDouble* bed_normal, IssmDouble xyz_list[3][3]){

	int i;
	IssmDouble v13[3],v23[3];
	IssmDouble normal[3];
	IssmDouble normal_norm;

	for (i=0;i<3;i++){
		v13[i]=xyz_list[0][i]-xyz_list[2][i];
		v23[i]=xyz_list[1][i]-xyz_list[2][i];
	}

	normal[0]=v13[1]*v23[2]-v13[2]*v23[1];
	normal[1]=v13[2]*v23[0]-v13[0]*v23[2];
	normal[2]=v13[0]*v23[1]-v13[1]*v23[0];
	normal_norm=sqrt( pow(normal[0],2)+pow(normal[1],2)+pow(normal[2],2) );

	/*Bed normal is opposite to surface normal*/
	*(bed_normal)=-normal[0]/normal_norm;
	*(bed_normal+1)=-normal[1]/normal_norm;
	*(bed_normal+2)=-normal[2]/normal_norm;
}
/*}}}*/
/*FUNCTION Penta::BasalFrictionCreateInput {{{*/
void Penta::BasalFrictionCreateInput(void){

	/*Constants*/
	const int  numdof=NUMVERTICES*NDOF1;

	/*Intermediaries */
	int    count,ig;
	IssmDouble basalfriction[NUMVERTICES]={0,0,0,0,0,0};
	IssmDouble alpha2,vx,vy;
	Friction*  friction=NULL;
	GaussPenta* gauss=NULL;


	/* Basal friction can only be found at the base of an ice sheet: */
	if (!IsOnBed() || IsFloating()){
		//empty friction: 
		this->inputs->AddInput(new PentaP1Input(BasalFrictionEnum,&basalfriction[0]));
		return;
	}

	/*Retrieve all inputs and parameters*/
	Input* vx_input=inputs->GetInput(VxEnum);                         _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);                         _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);                         _assert_(vz_input);


	/*Build friction element, needed later: */
	friction=new Friction("3d",inputs,matpar,DiagnosticHorizAnalysisEnum);

	/* Start looping on the number of gauss 2d (nodes on the bedrock) */
	gauss=new GaussPenta(0,1,2,2);
	count=0;
	for(ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		friction->GetAlpha2(&alpha2,gauss,VxEnum,VyEnum,VzEnum);
		vx_input->GetInputValue(&vx,gauss);
		vy_input->GetInputValue(&vy,gauss);
		basalfriction[count]=alpha2*(pow(vx,2.0)+pow(vy,2.0));
		count++;
	}
	
	/*Create PentaVertex input, which will hold the basal friction:*/
	this->inputs->AddInput(new PentaP1Input(BasalFrictionEnum,&basalfriction[0]));

	/*Clean up and return*/
	delete gauss;
	delete friction;
}
/*}}}*/
/*FUNCTION Penta::ComputeBasalStress {{{*/
void  Penta::ComputeBasalStress(Vector* sigma_b){

	int         i,j,ig;
	int         dofv[3]={0,1,2};
	int         dofp[1]={3};
	int         analysis_type,approximation;
	int         doflist[NUMVERTICES];
	IssmDouble      xyz_list[NUMVERTICES][3];
	IssmDouble      xyz_list_tria[3][3];
	IssmDouble      rho_ice,gravity,stokesreconditioning;
	IssmDouble      pressure,viscosity,bed,Jdet2d;
	IssmDouble      bed_normal[3];
	IssmDouble      basalforce[3];
	IssmDouble      epsilon[6]; /* epsilon=[exx,eyy,ezz,exy,exz,eyz];*/
	IssmDouble      devstresstensor[6]; /* epsilon=[exx,eyy,ezz,exy,exz,eyz];*/
	IssmDouble      stresstensor[6]={0.0};
	IssmDouble      sigma_xx,sigma_yy,sigma_zz;
	IssmDouble      sigma_xy,sigma_xz,sigma_yz;
	IssmDouble      surface=0,value=0;
	GaussPenta* gauss;

	/*retrive parameters: */
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	inputs->GetInputValue(&approximation,ApproximationEnum);

	/*Check analysis_types*/
	if (analysis_type!=DiagnosticHorizAnalysisEnum) _error2_("Not supported yet!");
	if (approximation!=StokesApproximationEnum) _error2_("Not supported yet!");

	/*retrieve some parameters: */
	this->parameters->FindParam(&stokesreconditioning,DiagnosticStokesreconditioningEnum);
	
	if(!IsOnBed()){
		//put zero
		sigma_b->SetValue(id-1,0.0,INS_VAL);
		return;
	}

	/*recovre material parameters: */
	rho_ice=matpar->GetRhoIce();
	gravity=matpar->GetG();

	/* Get node coordinates and dof list: */
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	for(i=0;i<3;i++) for(j=0;j<3;j++) xyz_list_tria[i][j]=xyz_list[i][j];

	/*Retrieve all inputs we will be needing: */
	Input* pressure_input=inputs->GetInput(PressureEnum); _assert_(pressure_input);
	Input* vx_input=inputs->GetInput(VxEnum);             _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);             _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);             _assert_(vz_input);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(0,1,2,2);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		/*Compute strain rate viscosity and pressure: */
		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		matice->GetViscosity3dStokes(&viscosity,&epsilon[0]);
		pressure_input->GetInputValue(&pressure,gauss);

		/*Compute Stress*/
		sigma_xx=2*viscosity*epsilon[0]-pressure*stokesreconditioning; // sigma = nu eps - pressure
		sigma_yy=2*viscosity*epsilon[1]-pressure*stokesreconditioning;
		sigma_zz=2*viscosity*epsilon[2]-pressure*stokesreconditioning;
		sigma_xy=2*viscosity*epsilon[3];
		sigma_xz=2*viscosity*epsilon[4];
		sigma_yz=2*viscosity*epsilon[5];

		/*Get normal vector to the bed */
		BedNormal(&bed_normal[0],xyz_list_tria);

		/*basalforce*/
		basalforce[0] += sigma_xx*bed_normal[0] + sigma_xy*bed_normal[1] + sigma_xz*bed_normal[2];
		basalforce[1] += sigma_xy*bed_normal[0] + sigma_yy*bed_normal[1] + sigma_yz*bed_normal[2];
		basalforce[2] += sigma_xz*bed_normal[0] + sigma_yz*bed_normal[1] + sigma_zz*bed_normal[2];

		GetTriaJacobianDeterminant(&Jdet2d, &xyz_list_tria[0][0],gauss);
		value+=sigma_zz*Jdet2d*gauss->weight;
		surface+=Jdet2d*gauss->weight;
	}
	value=value/surface;

	/*Add value to output*/
	sigma_b->SetValue(id-1,value,INS_VAL);
}
/*}}}*/
/*FUNCTION Penta::ComputeStrainRate {{{*/
void  Penta::ComputeStrainRate(Vector* eps){

	_error2_("Not implemented yet");

}
/*}}}*/
/*FUNCTION Penta::ComputeStressTensor {{{*/
void  Penta::ComputeStressTensor(){

	int         iv;
	IssmDouble      xyz_list[NUMVERTICES][3];
	IssmDouble      pressure,viscosity;
	IssmDouble      epsilon[6]; /* epsilon=[exx,eyy,exy];*/
	IssmDouble      sigma_xx[NUMVERTICES];
	IssmDouble		sigma_yy[NUMVERTICES];
	IssmDouble		sigma_zz[NUMVERTICES];
	IssmDouble      sigma_xy[NUMVERTICES];
	IssmDouble		sigma_xz[NUMVERTICES];
	IssmDouble		sigma_yz[NUMVERTICES];
	GaussPenta* gauss=NULL;

	/* Get node coordinates and dof list: */
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);

	/*Retrieve all inputs we will be needing: */
	Input* pressure_input=inputs->GetInput(PressureEnum); _assert_(pressure_input);
	Input* vx_input=inputs->GetInput(VxEnum);             _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);             _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);             _assert_(vz_input);

	/* Start looping on the number of vertices: */
	gauss=new GaussPenta();
	for (int iv=0;iv<NUMVERTICES;iv++){
		gauss->GaussVertex(iv);

		/*Compute strain rate viscosity and pressure: */
		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		matice->GetViscosity3d(&viscosity,&epsilon[0]);
		pressure_input->GetInputValue(&pressure,gauss);

		/*Compute Stress*/
		sigma_xx[iv]=2*viscosity*epsilon[0]-pressure; // sigma = nu eps - pressure
		sigma_yy[iv]=2*viscosity*epsilon[1]-pressure;
		sigma_zz[iv]=2*viscosity*epsilon[2]-pressure;
		sigma_xy[iv]=2*viscosity*epsilon[3];
		sigma_xz[iv]=2*viscosity*epsilon[4];
		sigma_yz[iv]=2*viscosity*epsilon[5];
	}
	
	/*Add Stress tensor components into inputs*/
	this->inputs->AddInput(new PentaP1Input(StressTensorxxEnum,&sigma_xx[0]));
	this->inputs->AddInput(new PentaP1Input(StressTensorxyEnum,&sigma_xy[0]));
	this->inputs->AddInput(new PentaP1Input(StressTensorxzEnum,&sigma_xz[0]));
	this->inputs->AddInput(new PentaP1Input(StressTensoryyEnum,&sigma_yy[0]));
	this->inputs->AddInput(new PentaP1Input(StressTensoryzEnum,&sigma_yz[0]));
	this->inputs->AddInput(new PentaP1Input(StressTensorzzEnum,&sigma_zz[0]));

	/*Clean up and return*/
	delete gauss;
}
/*}}}*/
		/*FUNCTION Penta::Configure {{{*/
void  Penta::Configure(Elements* elementsin, Loads* loadsin, DataSet* nodesin, Materials* materialsin, Parameters* parametersin){

	int analysis_counter;
	
	/*go into parameters and get the analysis_counter: */
	parametersin->FindParam(&analysis_counter,AnalysisCounterEnum);

	/*Get Element type*/
	this->element_type=this->element_type_list[analysis_counter];

	/*Take care of hooking up all objects for this element, ie links the objects in the hooks to their respective 
	 * datasets, using internal ids and offsets hidden in hooks: */
	if (this->hnodes[analysis_counter]) this->hnodes[analysis_counter]->configure(nodesin);
	this->hmatice->configure(materialsin);
	this->hmatpar->configure(materialsin);
	this->hneighbors->configure(elementsin);

	/*Now, go pick up the objects inside the hooks: */
	if (this->hnodes[analysis_counter]) this->nodes=(Node**)this->hnodes[analysis_counter]->deliverp();
	else this->nodes=NULL;
	this->matice=(Matice*)this->hmatice->delivers();
	this->matpar=(Matpar*)this->hmatpar->delivers();
	this->verticalneighbors=(Penta**)this->hneighbors->deliverp();

	/*point parameters to real dataset: */
	this->parameters=parametersin;

	/*get inputs configured too: */
	this->inputs->Configure(parameters);
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrix {{{*/
void  Penta::CreateKMatrix(Matrix* Kff, Matrix* Kfs,Vector* df){

	/*retrieve parameters: */
	ElementMatrix* Ke=NULL;
	ElementVector* De=NULL;
	int analysis_type;
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);

	/*Checks in debugging {{{*/
	_assert_(this->nodes && this->matice && this->matpar && this->verticalneighbors && this->parameters && this->inputs);
	/*}}}*/
	
	/*Skip if water element*/
	if(IsOnWater()) return;

	/*Just branch to the correct element stiffness matrix generator, according to the type of analysis we are carrying out: */
	switch(analysis_type){
		#ifdef _HAVE_DIAGNOSTIC_
		case DiagnosticHorizAnalysisEnum:
			Ke=CreateKMatrixDiagnosticHoriz(); De=CreateDVectorDiagnosticHoriz();
			break;
		case AdjointHorizAnalysisEnum:
			Ke=CreateKMatrixAdjointHoriz();
			break;
		case DiagnosticHutterAnalysisEnum:
			Ke=CreateKMatrixDiagnosticHutter();
			break;
		case DiagnosticVertAnalysisEnum:
			Ke=CreateKMatrixDiagnosticVert();
			break;
		#endif
		case BedSlopeXAnalysisEnum: case SurfaceSlopeXAnalysisEnum: case BedSlopeYAnalysisEnum: case SurfaceSlopeYAnalysisEnum:
			Ke=CreateKMatrixSlope();
			break;
		case PrognosticAnalysisEnum:
			Ke=CreateKMatrixPrognostic();
			break;
		#ifdef _HAVE_BALANCED_
		case BalancethicknessAnalysisEnum:
			Ke=CreateKMatrixBalancethickness();
			break;
		#endif
		#ifdef _HAVE_THERMAL_
		case ThermalAnalysisEnum:
			Ke=CreateKMatrixThermal();
			break;
		case EnthalpyAnalysisEnum:
			Ke=CreateKMatrixEnthalpy();
			break;
		case MeltingAnalysisEnum:
			Ke=CreateKMatrixMelting();
			break;
		#endif
		default:
			_error2_("analysis " << analysis_type << " (" << EnumToStringx(analysis_type) << ") not supported yet");
	}

	/*Add to global matrix*/
	if(Ke){
		Ke->AddToGlobal(Kff,Kfs);
		delete Ke;
	}
	/*Add to global Vector*/
	if(De){
		De->InsertIntoGlobal(df);
		delete De;
	}
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixPrognostic {{{*/
ElementMatrix* Penta::CreateKMatrixPrognostic(void){

	if (!IsOnBed()) return NULL;

	/*Depth Averaging Vx and Vy*/
	this->InputDepthAverageAtBase(VxEnum,VxAverageEnum);
	this->InputDepthAverageAtBase(VyEnum,VyAverageEnum);

	Tria* tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.
	ElementMatrix* Ke=tria->CreateKMatrixPrognostic();
	delete tria->matice; delete tria;

	/*Delete Vx and Vy averaged*/
	this->inputs->DeleteInput(VxAverageEnum);
	this->inputs->DeleteInput(VyAverageEnum);

	/*clean up and return*/
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixSlope {{{*/
ElementMatrix* Penta::CreateKMatrixSlope(void){

	if (!IsOnBed()) return NULL;

	Tria* tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.
	ElementMatrix* Ke=tria->CreateKMatrixSlope();
	delete tria->matice; delete tria;

	/*clean up and return*/
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreatePVector {{{*/
void  Penta::CreatePVector(Vector* pf){

	/*retrive parameters: */
	ElementVector* pe=NULL;
	int analysis_type;
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);

	/*if debugging mode, check that all pointers exist {{{*/
	_assert_(this->nodes && this->matice && this->matpar && this->verticalneighbors && this->parameters && this->inputs);
	/*}}}*/

	/*Skip if water element*/
	if(IsOnWater()) return;

	/*Just branch to the correct element stiffness matrix generator, according to the type of analysis we are carrying out: */
	switch(analysis_type){
		#ifdef _HAVE_DIAGNOSTIC_
		case DiagnosticHorizAnalysisEnum:
			pe=CreatePVectorDiagnosticHoriz();
			break;
		case DiagnosticHutterAnalysisEnum:
			pe=CreatePVectorDiagnosticHutter();
			break;
		case DiagnosticVertAnalysisEnum:
			pe=CreatePVectorDiagnosticVert();
			break;
		#endif
	 	#ifdef _HAVE_CONTROL_
		case AdjointHorizAnalysisEnum:
			pe=CreatePVectorAdjointHoriz();
			break;
		#endif
		#ifdef _HAVE_THERMAL_
		case ThermalAnalysisEnum:
			pe=CreatePVectorThermal();
			break;
		case EnthalpyAnalysisEnum:
			pe=CreatePVectorEnthalpy();
			break;
		case MeltingAnalysisEnum:
			pe=CreatePVectorMelting();
			break;
		#endif
		case BedSlopeXAnalysisEnum: case SurfaceSlopeXAnalysisEnum: case BedSlopeYAnalysisEnum: case SurfaceSlopeYAnalysisEnum:
			pe=CreatePVectorSlope();
			break;
		case PrognosticAnalysisEnum:
			pe=CreatePVectorPrognostic();
			break;
		#ifdef _HAVE_BALANCED_
		case BalancethicknessAnalysisEnum:
			pe=CreatePVectorBalancethickness();
			break;
		#endif
		default:
			_error2_("analysis " << analysis_type << " (" << EnumToStringx(analysis_type) << ") not supported yet");
	}

	/*Add to global Vector*/
	if(pe){
		pe->AddToGlobal(pf);
		delete pe;
	}
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorPrognostic {{{*/
ElementVector* Penta::CreatePVectorPrognostic(void){

	if (!IsOnBed()) return NULL;

	/*Depth Averaging Vx and Vy*/
	this->InputDepthAverageAtBase(VxEnum,VxAverageEnum);
	this->InputDepthAverageAtBase(VyEnum,VyAverageEnum);

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.
	ElementVector* pe=tria->CreatePVectorPrognostic();
	delete tria->matice; delete tria;

	/*Delete Vx and Vy averaged*/
	this->inputs->DeleteInput(VxAverageEnum);
	this->inputs->DeleteInput(VyAverageEnum);

	/*Clean up and return*/
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorSlope {{{*/
ElementVector* Penta::CreatePVectorSlope(void){

	if (!IsOnBed()) return NULL;

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.
	ElementVector* pe=tria->CreatePVectorSlope();
	delete tria->matice; delete tria;

	/*clean up and return*/
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreateJacobianMatrix{{{*/
void  Penta::CreateJacobianMatrix(Matrix* Jff){

	/*retrieve parameters: */
	ElementMatrix* Ke=NULL;
	int analysis_type;
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);

	/*Checks in debugging {{{*/
	_assert_(this->nodes && this->matice && this->matpar && this->verticalneighbors && this->parameters && this->inputs);
	/*}}}*/

	/*Skip if water element*/
	if(IsOnWater()) return;

	/*Just branch to the correct element stiffness matrix generator, according to the type of analysis we are carrying out: */
	switch(analysis_type){
#ifdef _HAVE_DIAGNOSTIC_
		case DiagnosticHorizAnalysisEnum:
			Ke=CreateJacobianDiagnosticHoriz();
			break;
#endif
		default:
			_error2_("analysis " << analysis_type << " (" << EnumToStringx(analysis_type) << ") not supported yet");
	}

	/*Add to global matrix*/
	if(Ke){
		Ke->AddToGlobal(Jff);
		delete Ke;
	}
}
/*}}}*/
/*FUNCTION Penta::DeepEcho{{{*/
void Penta::DeepEcho(void){

	int i;
	
	_printLine_("Penta:");
	_printLine_("   id: " << id);
	nodes[0]->DeepEcho();
	nodes[1]->DeepEcho();
	nodes[2]->DeepEcho();
	nodes[3]->DeepEcho();
	nodes[4]->DeepEcho();
	nodes[5]->DeepEcho();
	matice->DeepEcho();
	matpar->DeepEcho();
	_printLine_("   neighbor ids: " << verticalneighbors[0]->Id() << "-" << verticalneighbors[1]->Id());
	_printLine_("   parameters");
	parameters->DeepEcho();
	_printLine_("   inputs");
	inputs->DeepEcho();
	_printLine_("   results");
	results->DeepEcho();
	_printLine_("neighboor sids: ");
	_printLine_(" " << horizontalneighborsids[0] << " " << horizontalneighborsids[1] << " " << horizontalneighborsids[2]);

	return;
}
/*}}}*/
/*FUNCTION Penta::DeleteResults {{{*/
void  Penta::DeleteResults(void){

	/*Delete and reinitialize results*/
	delete this->results;
	this->results=new Results();

}
/*}}}*/
/*FUNCTION Penta::Delta18oParameterization{{{*/
void  Penta::Delta18oParameterization(void){

	IssmDouble monthlytemperatures[NUMVERTICES][12],monthlyprec[NUMVERTICES][12];
	IssmDouble TemperaturesPresentday[NUMVERTICES][12],TemperaturesLgm[NUMVERTICES][12];
	IssmDouble PrecipitationsPresentday[NUMVERTICES][12];
	IssmDouble Delta18oPresent,Delta18oLgm,Delta18oTime;
	IssmDouble Delta18oSurfacePresent,Delta18oSurfaceLgm,Delta18oSurfaceTime;
	IssmDouble time,yts,finaltime;
	this->parameters->FindParam(&time,TimeEnum);
	this->parameters->FindParam(&yts,ConstantsYtsEnum);
	this->parameters->FindParam(&finaltime,TimesteppingFinalTimeEnum);

	/*Recover present day temperature and precipitation*/
	Input*     input=inputs->GetInput(SurfaceforcingsTemperaturesPresentdayEnum); _assert_(input);
	Input*     input2=inputs->GetInput(SurfaceforcingsTemperaturesLgmEnum); _assert_(input2);
	Input*     input3=inputs->GetInput(SurfaceforcingsPrecipitationsPresentdayEnum); _assert_(input3);
	GaussPenta* gauss=new GaussPenta();
	for(int month=0;month<12;month++) {
		for(int iv=0;iv<NUMVERTICES;iv++) {
			gauss->GaussVertex(iv);
			input->GetInputValue(&TemperaturesPresentday[iv][month],gauss,month/12.*yts);
			input2->GetInputValue(&TemperaturesLgm[iv][month],gauss,month/12.*yts);
			input3->GetInputValue(&PrecipitationsPresentday[iv][month],gauss,month/12.*yts);
			monthlyprec[iv][month]=monthlyprec[iv][month]*yts; // convertion to m/yr
		}
	}

	/*Recover delta18o and Delta18oSurface at present day, lgm and at time t*/
	this->parameters->FindParam(&Delta18oPresent,SurfaceforcingsDelta18oEnum,finaltime*yts);
	this->parameters->FindParam(&Delta18oLgm,SurfaceforcingsDelta18oEnum,(finaltime-21000)*yts);
	this->parameters->FindParam(&Delta18oTime,SurfaceforcingsDelta18oEnum,time*yts);
	this->parameters->FindParam(&Delta18oSurfacePresent,SurfaceforcingsDelta18oSurfaceEnum,finaltime*yts);
	this->parameters->FindParam(&Delta18oSurfaceLgm,SurfaceforcingsDelta18oSurfaceEnum,(finaltime-21000)*yts);
	this->parameters->FindParam(&Delta18oSurfaceTime,SurfaceforcingsDelta18oSurfaceEnum,time*yts);

	/*Compute the temperature and precipitation*/
	for(int iv=0;iv<NUMVERTICES;iv++){
		ComputeDelta18oTemperaturePrecipitation(Delta18oSurfacePresent, Delta18oSurfaceLgm, Delta18oSurfaceTime, 
					Delta18oPresent, Delta18oLgm, Delta18oTime,
					&PrecipitationsPresentday[iv][0], 
					&TemperaturesLgm[iv][0], &TemperaturesPresentday[iv][0], 
					&monthlytemperatures[iv][0], &monthlyprec[iv][0]);
	}

	/*Update inputs*/ 
	TransientInput* NewTemperatureInput = new TransientInput(SurfaceforcingsMonthlytemperaturesEnum);
	TransientInput* NewPrecipitationInput = new TransientInput(SurfaceforcingsPrecipitationEnum);
	for (int imonth=0;imonth<12;imonth++) {
		for(int iv=0;iv<NUMVERTICES;iv++) {
			PentaP1Input* newmonthinput1 = new PentaP1Input(SurfaceforcingsMonthlytemperaturesEnum,&monthlytemperatures[iv][imonth]);
			NewTemperatureInput->AddTimeInput(newmonthinput1,imonth/12.*yts);
			PentaP1Input* newmonthinput2 = new PentaP1Input(SurfaceforcingsPrecipitationEnum,&monthlyprec[iv][imonth]);
			NewPrecipitationInput->AddTimeInput(newmonthinput2,imonth/12.*yts);
		}
	}
	this->inputs->AddInput(NewTemperatureInput);
	this->inputs->AddInput(NewPrecipitationInput);

	/*clean-up*/
	delete gauss;
}
/*}}}*/
/*FUNCTION Penta::Echo{{{*/

void Penta::Echo(void){
	this->DeepEcho();
}
/*}}}*/
/*FUNCTION Penta::ObjectEnum{{{*/
int Penta::ObjectEnum(void){

	return PentaEnum;

}
/*}}}*/
/*FUNCTION Penta::GetBasalElement{{{*/
Penta* Penta::GetBasalElement(void){

	/*Output*/
	Penta* penta=NULL;

	/*Go through all elements till the bed is reached*/
	penta=this;
	for(;;){
		/*Stop if we have reached the surface, else, take lower penta*/
		if (penta->IsOnBed()) break;

		/* get lower Penta*/
		penta=penta->GetLowerElement();
		_assert_(penta->Id()!=this->id);
	}

	/*return output*/
	return penta;
}
/*}}}*/
/*FUNCTION Penta::GetDofList {{{*/
void  Penta::GetDofList(int** pdoflist,int approximation_enum,int setenum){

	int  i,j,count=0;
	int  numberofdofs=0;
	int* doflist=NULL;

	/*First, figure out size of doflist: */
	for(i=0;i<6;i++) numberofdofs+=nodes[i]->GetNumberOfDofs(approximation_enum,setenum);

	/*Allocate: */
	doflist=xNew<int>(numberofdofs);

	/*Populate: */
	count=0;
	for(i=0;i<6;i++){
		nodes[i]->GetDofList(doflist+count,approximation_enum,setenum);
		count+=nodes[i]->GetNumberOfDofs(approximation_enum,setenum);
	}

	/*Assign output pointers:*/
	*pdoflist=doflist;
}
/*}}}*/
/*FUNCTION Penta::GetDofList1 {{{*/
void  Penta::GetDofList1(int* doflist){

	int i;
	for(i=0;i<6;i++) doflist[i]=nodes[i]->GetDofList1();

}
/*}}}*/
/*FUNCTION Penta::GetConnectivityList {{{*/
void  Penta::GetConnectivityList(int* connectivity){
	for(int i=0;i<NUMVERTICES;i++) connectivity[i]=nodes[i]->GetConnectivity();
}
/*}}}*/
/*FUNCTION Penta::GetElementType {{{*/
int Penta::GetElementType(){

	/*return PentaRef field*/
	return this->element_type;
}
/*}}}*/
/*FUNCTION Penta::GetElementSizes{{{*/
void Penta::GetElementSizes(IssmDouble* hx,IssmDouble* hy,IssmDouble* hz){

	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble xmin,ymin,zmin;
	IssmDouble xmax,ymax,zmax;

	/*Get xyz list: */
	GetVerticesCoordinates(&xyz_list[0][0],nodes,NUMVERTICES);
	xmin=xyz_list[0][0]; xmax=xyz_list[0][0];
	ymin=xyz_list[0][1]; ymax=xyz_list[0][1];
	zmin=xyz_list[0][2]; zmax=xyz_list[0][2];

	for(int i=1;i<NUMVERTICES;i++){
		if(xyz_list[i][0]<xmin) xmin=xyz_list[i][0];
		if(xyz_list[i][0]>xmax) xmax=xyz_list[i][0];
		if(xyz_list[i][1]<ymin) ymin=xyz_list[i][1];
		if(xyz_list[i][1]>ymax) ymax=xyz_list[i][1];
		if(xyz_list[i][2]<zmin) zmin=xyz_list[i][2];
		if(xyz_list[i][2]>zmax) zmax=xyz_list[i][2];
	}

	*hx=xmax-xmin;
	*hy=ymax-ymin;
	*hz=zmax-zmin;
}
/*}}}*/
/*FUNCTION Penta::GetHorizontalNeighboorSids {{{*/
int* Penta::GetHorizontalNeighboorSids(){

	/*return PentaRef field*/
	return &this->horizontalneighborsids[0];

}
/*}}}*/
/*FUNCTION Penta::GetLowerElement{{{*/
Penta* Penta::GetLowerElement(void){

	Penta* upper_penta=NULL;

	upper_penta=(Penta*)verticalneighbors[0]; //first one (0) under, second one (1) above

	return upper_penta;
}
/*}}}*/
/*FUNCTION Penta::GetNodeIndex {{{*/
int Penta::GetNodeIndex(Node* node){

	_assert_(nodes);
	for(int i=0;i<NUMVERTICES;i++){
		if(node==nodes[i])
		 return i;
	}
	_error2_("Node provided not found among element nodes");

}
/*}}}*/
/*FUNCTION Penta::GetInputListOnVertices(IssmDouble* pvalue,int enumtype) {{{*/
void Penta::GetInputListOnVertices(IssmDouble* pvalue,int enumtype){

	/*Intermediaries*/
	IssmDouble     value[NUMVERTICES];
	GaussPenta *gauss              = NULL;

	/*Recover input*/
	Input* input=inputs->GetInput(enumtype);
	if (!input) _error2_("Input " << EnumToStringx(enumtype) << " not found in element");

	/*Checks in debugging mode*/
	_assert_(pvalue);

	/* Start looping on the number of vertices: */
	gauss=new GaussPenta();
	for (int iv=0;iv<NUMVERTICES;iv++){
		gauss->GaussVertex(iv);
		input->GetInputValue(&pvalue[iv],gauss);
	}

	/*clean-up*/
	delete gauss;
}
/*}}}*/
/*FUNCTION Penta::GetInputListOnVertices(IssmDouble* pvalue,int enumtype,IssmDouble defaultvalue) {{{*/
void Penta::GetInputListOnVertices(IssmDouble* pvalue,int enumtype,IssmDouble defaultvalue){

	/*Intermediaries*/
	IssmDouble     value[NUMVERTICES];
	GaussPenta *gauss              = NULL;

	/*Recover input*/
	Input* input=inputs->GetInput(enumtype);

	/*Checks in debugging mode*/
	_assert_(pvalue);

	/* Start looping on the number of vertices: */
	if (input){
		gauss=new GaussPenta();
		for (int iv=0;iv<NUMVERTICES;iv++){
			gauss->GaussVertex(iv);
			input->GetInputValue(&pvalue[iv],gauss);
		}
	}
	else{
		for (int iv=0;iv<NUMVERTICES;iv++) pvalue[iv]=defaultvalue;
	}

	/*clean-up*/
	delete gauss;
}
/*}}}*/
/*FUNCTION Penta::GetInputValue(IssmDouble* pvalue,Node* node,int enumtype) {{{*/
void Penta::GetInputValue(IssmDouble* pvalue,Node* node,int enumtype){

	Input* input=inputs->GetInput(enumtype);
	if(!input) _error2_("No input of type " << EnumToStringx(enumtype) << " found in tria");

	GaussPenta* gauss=new GaussPenta();
	gauss->GaussVertex(this->GetNodeIndex(node));

	input->GetInputValue(pvalue,gauss);
	delete gauss;
}
/*}}}*/
/*FUNCTION Penta::GetPhi {{{*/
void Penta::GetPhi(IssmDouble* phi, IssmDouble*  epsilon, IssmDouble viscosity){
	/*Compute deformational heating from epsilon and viscosity */

	IssmDouble epsilon_matrix[3][3];
	IssmDouble epsilon_eff;
	IssmDouble epsilon_sqr[3][3];

	/* Build epsilon matrix */
	epsilon_matrix[0][0]=*(epsilon+0);
	epsilon_matrix[1][0]=*(epsilon+3);
	epsilon_matrix[2][0]=*(epsilon+4);
	epsilon_matrix[0][1]=*(epsilon+3);
	epsilon_matrix[1][1]=*(epsilon+1);
	epsilon_matrix[2][1]=*(epsilon+5);
	epsilon_matrix[0][2]=*(epsilon+4);
	epsilon_matrix[1][2]=*(epsilon+5);
	epsilon_matrix[2][2]=*(epsilon+2);

	/* Effective value of epsilon_matrix */
	epsilon_sqr[0][0]=pow(epsilon_matrix[0][0],2);
	epsilon_sqr[1][0]=pow(epsilon_matrix[1][0],2);
	epsilon_sqr[2][0]=pow(epsilon_matrix[2][0],2);
	epsilon_sqr[0][1]=pow(epsilon_matrix[0][1],2);
	epsilon_sqr[1][1]=pow(epsilon_matrix[1][1],2);
	epsilon_sqr[2][1]=pow(epsilon_matrix[2][1],2);
	epsilon_sqr[0][2]=pow(epsilon_matrix[0][2],2);
	epsilon_sqr[1][2]=pow(epsilon_matrix[1][2],2);
	epsilon_sqr[2][2]=pow(epsilon_matrix[2][2],2);
	epsilon_eff=1/pow(2,0.5)*pow((epsilon_sqr[0][0]+epsilon_sqr[0][1]+ epsilon_sqr[0][2]+ epsilon_sqr[1][0]+ epsilon_sqr[1][1]+ epsilon_sqr[1][2]+ epsilon_sqr[2][0]+ epsilon_sqr[2][1]+ epsilon_sqr[2][2]),0.5);

	/*Phi = Tr(sigma * eps) 
	 *    = Tr(sigma'* eps)
	 *    = 2 * eps_eff * sigma'_eff
	 *    = 4 * mu * eps_eff ^2*/
	*phi=4*pow(epsilon_eff,2.0)*viscosity;
}
/*}}}*/
/*FUNCTION Penta::GetSidList{{{*/
void  Penta::GetSidList(int* sidlist){

	int i;
	for(i=0;i<NUMVERTICES;i++) sidlist[i]=nodes[i]->GetSidList();

}
/*}}}*/
/*FUNCTION Penta::GetSolutionFromInputs{{{*/
void  Penta::GetSolutionFromInputs(Vector* solution){

	int analysis_type;

	/*retrive parameters: */
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);

	/*Just branch to the correct InputUpdateFromSolution generator, according to the type of analysis we are carrying out: */
	switch(analysis_type){
	#ifdef _HAVE_DIAGNOSTIC_
	case DiagnosticHorizAnalysisEnum:
		int approximation;
		inputs->GetInputValue(&approximation,ApproximationEnum);
		if(approximation==StokesApproximationEnum || approximation==NoneApproximationEnum){
			GetSolutionFromInputsDiagnosticStokes(solution);
		}
		else if (approximation==MacAyealApproximationEnum || approximation==PattynApproximationEnum || approximation==HutterApproximationEnum){
			GetSolutionFromInputsDiagnosticHoriz(solution);
		}
		else if (approximation==MacAyealPattynApproximationEnum || approximation==PattynStokesApproximationEnum || approximation==MacAyealStokesApproximationEnum){
			return; //the elements around will create the solution
		}
		break;
	case DiagnosticHutterAnalysisEnum:
		GetSolutionFromInputsDiagnosticHutter(solution);
		break;
	case DiagnosticVertAnalysisEnum:
		GetSolutionFromInputsDiagnosticVert(solution);
		break;
	#endif
	#ifdef _HAVE_THERMAL_
	case ThermalAnalysisEnum:
		GetSolutionFromInputsThermal(solution);
		break;
	case EnthalpyAnalysisEnum:
		GetSolutionFromInputsEnthalpy(solution);
		break;
	#endif
	default:
		_error2_("analysis: " << analysis_type << " (" << EnumToStringx(analysis_type) << ") not supported yet");
	}
}
/*}}}*/
/*FUNCTION Penta::GetStabilizationParameter {{{*/
IssmDouble Penta::GetStabilizationParameter(IssmDouble u, IssmDouble v, IssmDouble w, IssmDouble diameter, IssmDouble kappa){
	/*Compute stabilization parameter*/
	/*kappa=thermalconductivity/(rho_ice*hearcapacity) for thermal model*/
	/*kappa=enthalpydiffusionparameter for enthalpy model*/

	IssmDouble normu;
	IssmDouble tau_parameter;

	normu=pow(pow(u,2)+pow(v,2)+pow(w,2),0.5);
	if(normu*diameter/(3*2*kappa)<1){ 
		tau_parameter=pow(diameter,2)/(3*2*2*kappa);
	}
	else tau_parameter=diameter/(2*normu);

	return tau_parameter;
}
/*}}}*/
/*FUNCTION Penta::GetStrainRate3dPattyn{{{*/
void Penta::GetStrainRate3dPattyn(IssmDouble* epsilon,IssmDouble* xyz_list, GaussPenta* gauss, Input* vx_input, Input* vy_input){
	/*Compute the 3d Blatter/PattynStrain Rate (5 components):
	 *
	 * epsilon=[exx eyy exy exz eyz]
	 *
	 * with exz=1/2 du/dz
	 *      eyz=1/2 dv/dz
	 *
	 * the contribution of vz is neglected
	 */

	int i;
	IssmDouble epsilonvx[5];
	IssmDouble epsilonvy[5];

	/*Check that both inputs have been found*/
	if (!vx_input || !vy_input){
		_error2_("Input missing. Here are the input pointers we have for vx: " << vx_input << ", vy: " << vy_input << "\n");
	}

	/*Get strain rate assuming that epsilon has been allocated*/
	vx_input->GetVxStrainRate3dPattyn(epsilonvx,xyz_list,gauss);
	vy_input->GetVyStrainRate3dPattyn(epsilonvy,xyz_list,gauss);

	/*Sum all contributions*/
	for(i=0;i<5;i++) epsilon[i]=epsilonvx[i]+epsilonvy[i];
}
/*}}}*/
/*FUNCTION Penta::GetStrainRate3d{{{*/
void Penta::GetStrainRate3d(IssmDouble* epsilon,IssmDouble* xyz_list, GaussPenta* gauss, Input* vx_input, Input* vy_input, Input* vz_input){
	/*Compute the 3d Strain Rate (6 components):
	 *
	 * epsilon=[exx eyy ezz exy exz eyz]
	 */

	int i;
	IssmDouble epsilonvx[6];
	IssmDouble epsilonvy[6];
	IssmDouble epsilonvz[6];

	/*Check that both inputs have been found*/
	if (!vx_input || !vy_input || !vz_input){
		_error2_("Input missing. Here are the input pointers we have for vx: " << vx_input << ", vy: " << vy_input << ", vz: " << vz_input << "\n");
	}

	/*Get strain rate assuming that epsilon has been allocated*/
	vx_input->GetVxStrainRate3d(epsilonvx,xyz_list,gauss);
	vy_input->GetVyStrainRate3d(epsilonvy,xyz_list,gauss);
	vz_input->GetVzStrainRate3d(epsilonvz,xyz_list,gauss);

	/*Sum all contributions*/
	for(i=0;i<6;i++) epsilon[i]=epsilonvx[i]+epsilonvy[i]+epsilonvz[i];
}
/*}}}*/
/*FUNCTION Penta::GetUpperElement{{{*/
Penta* Penta::GetUpperElement(void){

	Penta* upper_penta=NULL;

	upper_penta=(Penta*)verticalneighbors[1]; //first one under, second one above

	return upper_penta;
}
/*}}}*/
/*FUNCTION Penta::GetVectorFromInputs{{{*/
void  Penta::GetVectorFromInputs(Vector* vector,int input_enum){

	int doflist1[NUMVERTICES];

	/*Get out if this is not an element input*/
	if (!IsInput(input_enum)) return;

	/*Prepare index list*/
	this->GetDofList1(&doflist1[0]);

	/*Get input (either in element or material)*/
	Input* input=inputs->GetInput(input_enum);
	if(!input) _error2_("Input " << EnumToStringx(input_enum) << " not found in element");

	/*We found the enum.  Use its values to fill into the vector, using the vertices ids: */
	input->GetVectorFromInputs(vector,&doflist1[0]);
}
/*}}}*/
/*FUNCTION Penta::GetVectorFromResults{{{*/
void  Penta::GetVectorFromResults(Vector* vector,int offset,int enum_in,int interp){

	/*Get result*/
	ElementResult* elementresult=(ElementResult*)this->results->GetObjectByOffset(offset);
	if(elementresult->InstanceEnum()!=enum_in){
		_error_("Results of offset %i is %s, when %s was expected",offset,EnumToStringx(elementresult->InstanceEnum()),EnumToStringx(enum_in));
	}  
	if(interp==P1Enum){
		int doflist1[NUMVERTICES];
		int connectivity[NUMVERTICES];
		this->GetSidList(&doflist1[0]);
		this->GetConnectivityList(&connectivity[0]);
		elementresult->GetVectorFromResults(vector,&doflist1[0],&connectivity[0],NUMVERTICES);
	}
	else if(interp==P0Enum){
		elementresult->GetElementVectorFromResults(vector,sid);
	}
	else{
		_printLine_("Interpolation " << EnumToStringx(interp) << " not supported");
	}
}
/*}}}*/
/*FUNCTION Penta::GetZcoord {{{*/
IssmDouble Penta::GetZcoord(GaussPenta* gauss){

	int    i;
	IssmDouble z;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble z_list[NUMVERTICES];

	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	for(i=0;i<NUMVERTICES;i++) z_list[i]=xyz_list[i][2];
	PentaRef::GetInputValue(&z,z_list,gauss);

	return z;
}
/*}}}*/
/*FUNCTION Penta::Sid {{{*/
int    Penta::Sid(){
	
	return sid;

}
/*}}}*/
/*FUNCTION Penta::Id {{{*/
int    Penta::Id(void){
	return id; 
}
/*}}}*/
/*FUNCTION Penta::InputArtificialNoise{{{*/
void  Penta::InputArtificialNoise(int enum_type,IssmDouble min,IssmDouble max){

	Input* input=NULL;

	/*Make a copy of the original input: */
	input=(Input*)this->inputs->GetInput(enum_type);
	if(!input)_error2_("could not find old input with enum: " << EnumToStringx(enum_type));

	/*ArtificialNoise: */
	input->ArtificialNoise(min,max);
}
/*}}}*/
/*FUNCTION Penta::InputConvergence{{{*/
bool Penta::InputConvergence(IssmDouble* eps, int* enums,int num_enums,int* criterionenums,IssmDouble* criterionvalues,int num_criterionenums){

	int i;
	bool    converged=true;
	Input** new_inputs=NULL;
	Input** old_inputs=NULL;

	new_inputs=xNew<Input*>(num_enums/2); //half the enums are for the new inputs
	old_inputs=xNew<Input*>(num_enums/2); //half the enums are for the old inputs

	for(i=0;i<num_enums/2;i++){
		new_inputs[i]=(Input*)this->inputs->GetInput(enums[2*i+0]);
		old_inputs[i]=(Input*)this->inputs->GetInput(enums[2*i+1]);
		if(!new_inputs[i])_error2_("could not find input with enum " << EnumToStringx(enums[2*i+0]));
		if(!old_inputs[i])_error2_("could not find input with enum " << EnumToStringx(enums[2*i+0]));
	}

	/*ok, we've got the inputs (new and old), now loop throught the number of criterions and fill the eps array:*/
	for(i=0;i<num_criterionenums;i++){
		IsInputConverged(eps+i,new_inputs,old_inputs,num_enums/2,criterionenums[i]);
		if(eps[i]>criterionvalues[i]) converged=false; 
	}

	/*clean up*/
	xDelete<Input*>(new_inputs);
	xDelete<Input*>(old_inputs);

	/*Return output*/
	return converged;
}
/*}}}*/
/*FUNCTION Penta::InputCreate(IssmDouble scalar,int enum,int code);{{{*/
void Penta::InputCreate(IssmDouble scalar,int name,int code){

	/*Check that name is an element input*/
	if (!IsInput(name)) return;
	
	if ((code==5) || (code==1)){ //boolean
		this->inputs->AddInput(new BoolInput(name,(bool)scalar));
	}
	else if ((code==6) || (code==2)){ //integer
		this->inputs->AddInput(new IntInput(name,(int)scalar));
	}
	else if ((code==7) || (code==3)){ //IssmDouble
		this->inputs->AddInput(new DoubleInput(name,(IssmDouble)scalar));
	}
	else _error2_("could not recognize nature of vector from code " << code);

}
/*}}}*/
/*FUNCTION Penta::InputCreate(IssmDouble* vector,int index,IoModel* iomodel,int M,int N,int vector_type,int vector_enum,int code){{{*/
void Penta::InputCreate(IssmDouble* vector, int index,IoModel* iomodel,int M,int N,int vector_type,int vector_enum,int code){//index into elements

	/*Intermediaries*/
	int    i,j,t;
	int    penta_vertex_ids[6];
	int    row;
	IssmDouble nodeinputs[6];
	IssmDouble time;
	TransientInput* transientinput=NULL;

	int    numberofvertices;
	int    numberofelements;
	IssmDouble yts;

	/*Fetch parameters: */
	iomodel->Constant(&numberofvertices,MeshNumberofverticesEnum);
	iomodel->Constant(&numberofelements,MeshNumberofelementsEnum);
	iomodel->Constant(&yts,ConstantsYtsEnum);

	/*Branch on type of vector: nodal or elementary: */
	if(vector_type==1){ //nodal vector

		/*Recover vertices ids needed to initialize inputs*/
		for(i=0;i<6;i++){ 
			_assert_(iomodel->Data(MeshElementsEnum));
			penta_vertex_ids[i]=(int)iomodel->Data(MeshElementsEnum)[6*index+i]; //ids for vertices are in the elements array from Matlab
		}

		/*Are we in transient or static? */
		if(M==numberofvertices){

			/*create input values: */
			for(i=0;i<6;i++)nodeinputs[i]=(IssmDouble)vector[penta_vertex_ids[i]-1];

			/*process units: */
			UnitConversion(&nodeinputs[0], 6 ,ExtToIuEnum, vector_enum);

			/*create static input: */
			this->inputs->AddInput(new PentaP1Input(vector_enum,nodeinputs));
		}
		else if(M==numberofvertices+1){
			/*create transient input: */
			for(t=0;t<N;t++){ //N is the number of times

				/*create input values: */
				for(i=0;i<6;i++){
					row=penta_vertex_ids[i]-1;
					nodeinputs[i]=(IssmDouble)vector[N*row+t];
				}

				/*process units: */
				UnitConversion(&nodeinputs[0], 6 ,ExtToIuEnum, vector_enum);

				/*time? :*/
				time=(IssmDouble)vector[(M-1)*N+t]*yts;

				if(t==0)transientinput=new TransientInput(vector_enum);
				transientinput->AddTimeInput(new PentaP1Input(vector_enum,nodeinputs),time);
			}
			this->inputs->AddInput(transientinput);
		}
		else _error2_("nodal vector is either numberofnodes (" << numberofvertices << "), or numberofnodes+1 long. Field provided is " << M << " long. Enum " << EnumToStringx(vector_enum));
	}
	else if(vector_type==2){ //element vector
		/*Are we in transient or static? */
		if(M==numberofelements){

			/*static mode: create an input out of the element value: */

			if (code==5){ //boolean
				this->inputs->AddInput(new BoolInput(vector_enum,(bool)vector[index]));
			}
			else if (code==6){ //integer
				this->inputs->AddInput(new IntInput(vector_enum,(int)vector[index]));
			}
			else if (code==7){ //IssmDouble
				this->inputs->AddInput(new DoubleInput(vector_enum,(IssmDouble)vector[index]));
			}
			else _error2_("could not recognize nature of vector from code " << code);
		}
		else {
			_error2_("transient elementary inputs not supported yet!");
		}
	}
	else{
		_error2_("Cannot add input for vector type " << vector_type << " (not supported)");
	}

}
/*}}}*/
/*FUNCTION Penta::InputDepthAverageAtBase{{{*/
void  Penta::InputDepthAverageAtBase(int enum_type,int average_enum_type,int object_enum){

	int  step,i;
	IssmDouble  xyz_list[NUMVERTICES][3];
	IssmDouble  Helem_list[NUMVERTICES];
	IssmDouble  zeros_list[NUMVERTICES]={0.0};
	Penta* penta=NULL;
	Input* original_input=NULL;
	Input* element_integrated_input=NULL;
	Input* total_integrated_input=NULL;
	Input* element_thickness_input=NULL;
	Input* total_thickness_input=NULL;
	Input* depth_averaged_input=NULL;

	/*recover parameters: */

	/*Are we on the base? If not, return*/
	if(!IsOnBed()) return;

	/*OK, we are on bed. Initialize global inputs as 0*/
	total_thickness_input =new PentaP1Input(ThicknessEnum,zeros_list);

	/*Now follow all the upper element from the base to the surface to integrate the input*/
	penta=this;
	step =0;
	for(;;){

		/*Step1: Get original input (to be depth avegaged): */
		if (object_enum==MeshElementsEnum)
		 original_input=(Input*)penta->inputs->GetInput(enum_type);
		else if (object_enum==MaterialsEnum)
		 original_input=(Input*)penta->matice->inputs->GetInput(enum_type);
		else
		 _error2_("object " << EnumToStringx(object_enum) << " not supported yet");
		if(!original_input) _error2_("could not find input with enum " << EnumToStringx(enum_type));

		/*If first time, initialize total_integrated_input*/
		if (step==0){
			if (original_input->ObjectEnum()==PentaP1InputEnum)
			 total_integrated_input=new PentaP1Input(average_enum_type,zeros_list);
			else if (original_input->ObjectEnum()==ControlInputEnum)
			 total_integrated_input=new PentaP1Input(average_enum_type,zeros_list);
			else if (original_input->ObjectEnum()==DoubleInputEnum)
			 total_integrated_input=new DoubleInput(average_enum_type,0.0);
			else
			 _error2_("object " << EnumToStringx(original_input->ObjectEnum()) << " not supported yet");
		}

		/*Step2: Create element thickness input*/
		GetVerticesCoordinates(&xyz_list[0][0],penta->nodes,NUMVERTICES);
		for(i=0;i<3;i++){
			Helem_list[i]=xyz_list[i+3][2]-xyz_list[i][2];
			Helem_list[i+3]=Helem_list[i];
		}
		element_thickness_input=new PentaP1Input(ThicknessEnum,Helem_list);

		/*Step3: Vertically integrate A COPY of the original*/
		element_integrated_input=(Input*)original_input->copy();
		element_integrated_input->VerticallyIntegrate(element_thickness_input);

		/*Add contributions to global inputs*/
		total_integrated_input->AXPY(element_integrated_input,1.0);
		total_thickness_input ->AXPY(element_thickness_input,1.0);

		/*Clean up*/
		delete element_thickness_input;
		delete element_integrated_input;

		/*Stop if we have reached the surface, else, take upper penta*/
		if (penta->IsOnSurface()) break;

		/* get upper Penta*/
		penta=penta->GetUpperElement();
		_assert_(penta->Id()!=this->id);

		/*increase couter*/
		step++;
	}

	/*OK, now we only need to divide the depth integrated input by the total thickness!*/
	depth_averaged_input=total_integrated_input->PointwiseDivide(total_thickness_input);
	depth_averaged_input->ChangeEnum(average_enum_type);

	/*Clean up*/
	delete total_thickness_input;
	delete total_integrated_input;

	/*Finally, add to inputs*/
	if (object_enum==MeshElementsEnum)
	 this->inputs->AddInput((Input*)depth_averaged_input);
	else if (object_enum==MaterialsEnum)
	 this->matice->inputs->AddInput((Input*)depth_averaged_input);
	else
	 _error2_("object " << EnumToStringx(object_enum) << " not supported yet");
}
/*}}}*/
/*FUNCTION Penta::InputDuplicate{{{*/
void  Penta::InputDuplicate(int original_enum,int new_enum){

	/*Call inputs method*/
	if (IsInput(original_enum)) inputs->DuplicateInput(original_enum,new_enum);

}
/*}}}*/
/*FUNCTION Penta::InputExtrude {{{*/
void  Penta::InputExtrude(int enum_type,int object_type){

	int     i,num_inputs;
	Penta  *penta       = NULL;
	Input  *copy        = NULL;
	Input **base_inputs = NULL;

	/*Are we on the base, not on the surface?:*/
	if(!IsOnBed()) return;

	/*Step1: Get and Extrude original input: */
	if (object_type==ElementEnum){
		num_inputs=1;
		base_inputs=xNew<Input*>(num_inputs);
		base_inputs[0]=(Input*)this->inputs->GetInput(enum_type);
	}
	else if (object_type==MaterialsEnum){
		num_inputs=1;
		base_inputs=xNew<Input*>(num_inputs);
		base_inputs[0]=(Input*)matice->inputs->GetInput(enum_type);
	}
	else if (object_type==NodeEnum){
		num_inputs=3; //only the three upper nodes
		base_inputs=xNew<Input*>(num_inputs);
		for(i=0;i<num_inputs;i++){
			base_inputs[i]=(Input*)this->nodes[i]->inputs->GetInput(enum_type);
		}
	}
	else{
		_error2_("object of type " << EnumToStringx(object_type) << " not supported yet");
	}
	for(i=0;i<num_inputs;i++){
		if(!base_inputs[i]) _error2_("could not find input with enum " << EnumToStringx(enum_type) << " in object " << EnumToStringx(object_type));
		base_inputs[i]->Extrude();
	}

	/*Stop if there is only one layer of element*/
	if (this->IsOnSurface()) return;

	/*Step 2: this input has been extruded for this element, now follow the upper element*/
	penta=this;
	for(;;){
		/* get upper Penta*/
		penta=penta->GetUpperElement();
		_assert_(penta->Id()!=this->id);

		/*Add input of the basal element to penta->inputs*/
		for(i=0;i<num_inputs;i++){
			copy=(Input*)base_inputs[i]->copy();
			if (object_type==ElementEnum){
				penta->inputs->AddInput((Input*)copy);
			}
			else if(object_type==MaterialsEnum){
				penta->matice->inputs->AddInput((Input*)copy);
			}
			else if(object_type==NodeEnum){
				penta->nodes[i+3]->inputs->AddInput((Input*)copy); //change only the three upper nodes
			}
			else{
				_error2_("object of type " << EnumToStringx(object_type) << " not supported yet");
			}
		}

		/*Stop if we have reached the surface*/
		if (penta->IsOnSurface()) break;
	}

	/*clean-up and return*/
	xDelete<Input*>(base_inputs);
}
/*}}}*/
/*FUNCTION Penta::InputScale{{{*/
void  Penta::InputScale(int enum_type,IssmDouble scale_factor){

	Input* input=NULL;

	/*Make a copy of the original input: */
	input=(Input*)this->inputs->GetInput(enum_type);
	if(!input)_error2_("could not find old input with enum: " << EnumToStringx(enum_type));

	/*Scale: */
	input->Scale(scale_factor);
}
/*}}}*/
/*FUNCTION Penta::InputToResult{{{*/
void  Penta::InputToResult(int enum_type,int step,IssmDouble time){

	int    i;
	bool   found = false;
	Input *input = NULL;

	/*Go through all the input objects, and find the one corresponding to enum_type, if it exists: */
	if (enum_type==MaterialsRheologyBbarEnum) input=this->matice->inputs->GetInput(MaterialsRheologyBEnum);
	else input=this->inputs->GetInput(enum_type);
	//if (!input) _error2_("Input " << EnumToStringx(enum_type) << " not found in penta->inputs"); why error out? if the requested input does not exist, we should still 
	//try and output whatever we can instead of just failing.
	if(!input)return;

	/*If we don't find it, no big deal, just don't do the transfer. Otherwise, build a new Result 
	 * object out of the input, with the additional step and time information: */
	this->results->AddObject((Object*)input->SpawnResult(step,time));
	#ifdef _HAVE_CONTROL_
	if(input->ObjectEnum()==ControlInputEnum){
		if(((ControlInput*)input)->gradient!=NULL) this->results->AddObject((Object*)((ControlInput*)input)->SpawnGradient(step,time));
	}
	#endif

}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromConstant(bool value, int name);{{{*/
void  Penta::InputUpdateFromConstant(bool constant, int name){

	/*Check that name is an element input*/
	if (!IsInput(name)) return;

	/*update input*/
	this->inputs->AddInput(new BoolInput(name,constant));
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromConstant(IssmDouble value, int name);{{{*/
void  Penta::InputUpdateFromConstant(IssmDouble constant, int name){
	/*Check that name is an element input*/
	if (!IsInput(name)) return;

	/*update input*/
	this->inputs->AddInput(new DoubleInput(name,constant));
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromConstant(int value, int name);{{{*/
void  Penta::InputUpdateFromConstant(int constant, int name){
	/*Check that name is an element input*/
	if (!IsInput(name)) return;

	/*update input*/
	this->inputs->AddInput(new IntInput(name,constant));
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromIoModel {{{*/
void Penta::InputUpdateFromIoModel(int index,IoModel* iomodel){ 

	/*Intermediaries*/
	IssmInt i,j;
	int     penta_vertex_ids[6];
	IssmDouble  nodeinputs[6];
	IssmDouble  cmmininputs[6];
	IssmDouble  cmmaxinputs[6];

	IssmDouble  yts;
	bool    control_analysis;
	int     num_control_type;
	int     num_cm_responses;

	/*Fetch parameters: */
	iomodel->Constant(&yts,ConstantsYtsEnum);
	iomodel->Constant(&control_analysis,InversionIscontrolEnum);
	if(control_analysis) iomodel->Constant(&num_control_type,InversionNumControlParametersEnum);
	if(control_analysis) iomodel->Constant(&num_cm_responses,InversionNumCostFunctionsEnum);

	/*Checks if debuging*/
	/*{{{*/
	_assert_(iomodel->Data(MeshElementsEnum));
	/*}}}*/

	/*Recover vertices ids needed to initialize inputs*/
	for(i=0;i<6;i++){ 
		penta_vertex_ids[i]=(int)iomodel->Data(MeshElementsEnum)[6*index+i]; //ids for vertices are in the elements array from Matlab
	}

	/*Control Inputs*/
	#ifdef _HAVE_CONTROL_
	if (control_analysis && iomodel->Data(InversionControlParametersEnum)){
		for(i=0;i<num_control_type;i++){
			switch((int)iomodel->Data(InversionControlParametersEnum)[i]){
				case BalancethicknessThickeningRateEnum:
					if (iomodel->Data(BalancethicknessThickeningRateEnum)){
						for(j=0;j<6;j++)nodeinputs[j]=iomodel->Data(BalancethicknessThickeningRateEnum)[penta_vertex_ids[j]-1]/yts;
						for(j=0;j<6;j++)cmmininputs[j]=iomodel->Data(InversionMinParametersEnum)[(penta_vertex_ids[j]-1)*num_control_type+i]/yts;
						for(j=0;j<6;j++)cmmaxinputs[j]=iomodel->Data(InversionMaxParametersEnum)[(penta_vertex_ids[j]-1)*num_control_type+i]/yts;
						this->inputs->AddInput(new ControlInput(BalancethicknessThickeningRateEnum,PentaP1InputEnum,nodeinputs,cmmininputs,cmmaxinputs,i+1));
					}
					break;
				case VxEnum:
					if (iomodel->Data(VxEnum)){
						for(j=0;j<6;j++)nodeinputs[j]=iomodel->Data(VxEnum)[penta_vertex_ids[j]-1]/yts;
						for(j=0;j<6;j++)cmmininputs[j]=iomodel->Data(InversionMinParametersEnum)[(penta_vertex_ids[j]-1)*num_control_type+i]/yts;
						for(j=0;j<6;j++)cmmaxinputs[j]=iomodel->Data(InversionMaxParametersEnum)[(penta_vertex_ids[j]-1)*num_control_type+i]/yts;
						this->inputs->AddInput(new ControlInput(VxEnum,PentaP1InputEnum,nodeinputs,cmmininputs,cmmaxinputs,i+1));
					}
					break;
				case VyEnum:
					if (iomodel->Data(VyEnum)){
						for(j=0;j<6;j++)nodeinputs[j]=iomodel->Data(VyEnum)[penta_vertex_ids[j]-1]/yts;
						for(j=0;j<6;j++)cmmininputs[j]=iomodel->Data(InversionMinParametersEnum)[(penta_vertex_ids[j]-1)*num_control_type+i]/yts;
						for(j=0;j<6;j++)cmmaxinputs[j]=iomodel->Data(InversionMaxParametersEnum)[(penta_vertex_ids[j]-1)*num_control_type+i]/yts;
						this->inputs->AddInput(new ControlInput(VyEnum,PentaP1InputEnum,nodeinputs,cmmininputs,cmmaxinputs,i+1));
					}
					break;
				case FrictionCoefficientEnum:
					if (iomodel->Data(FrictionCoefficientEnum)){
						for(j=0;j<6;j++)nodeinputs[j]=iomodel->Data(FrictionCoefficientEnum)[penta_vertex_ids[j]-1];
						for(j=0;j<6;j++)cmmininputs[j]=iomodel->Data(InversionMinParametersEnum)[(penta_vertex_ids[j]-1)*num_control_type+i];
						for(j=0;j<6;j++)cmmaxinputs[j]=iomodel->Data(InversionMaxParametersEnum)[(penta_vertex_ids[j]-1)*num_control_type+i];
						this->inputs->AddInput(new ControlInput(FrictionCoefficientEnum,PentaP1InputEnum,nodeinputs,cmmininputs,cmmaxinputs,i+1));
					}
					break;
				case MaterialsRheologyBbarEnum:
					/*Matice will take care of it*/ break;
				default:
					_error2_("Control " << EnumToStringx((int)iomodel->Data(InversionControlParametersEnum)[i]) << " not implemented yet");
			}
		}
	}
	#endif

	//Need to know the type of approximation for this element
	if(iomodel->Data(FlowequationElementEquationEnum)){
		if (*(iomodel->Data(FlowequationElementEquationEnum)+index)==MacAyealApproximationEnum){
			this->inputs->AddInput(new IntInput(ApproximationEnum,MacAyealApproximationEnum));
		}
		else if (*(iomodel->Data(FlowequationElementEquationEnum)+index)==PattynApproximationEnum){
			this->inputs->AddInput(new IntInput(ApproximationEnum,PattynApproximationEnum));
		}
		else if (*(iomodel->Data(FlowequationElementEquationEnum)+index)==MacAyealPattynApproximationEnum){
			this->inputs->AddInput(new IntInput(ApproximationEnum,MacAyealPattynApproximationEnum));
		}
		else if (*(iomodel->Data(FlowequationElementEquationEnum)+index)==HutterApproximationEnum){
			this->inputs->AddInput(new IntInput(ApproximationEnum,HutterApproximationEnum));
		}
		else if (*(iomodel->Data(FlowequationElementEquationEnum)+index)==StokesApproximationEnum){
			this->inputs->AddInput(new IntInput(ApproximationEnum,StokesApproximationEnum));
		}
		else if (*(iomodel->Data(FlowequationElementEquationEnum)+index)==MacAyealStokesApproximationEnum){
			this->inputs->AddInput(new IntInput(ApproximationEnum,MacAyealStokesApproximationEnum));
		}
		else if (*(iomodel->Data(FlowequationElementEquationEnum)+index)==PattynStokesApproximationEnum){
			this->inputs->AddInput(new IntInput(ApproximationEnum,PattynStokesApproximationEnum));
		}
		else if (*(iomodel->Data(FlowequationElementEquationEnum)+index)==NoneApproximationEnum){
			this->inputs->AddInput(new IntInput(ApproximationEnum,NoneApproximationEnum));
		}
		else{
			_error2_("Approximation type " << EnumToStringx((int)*(iomodel->Data(FlowequationElementEquationEnum)+index)) << " not supported yet");
		}
	}

	/*DatasetInputs*/
	if (control_analysis && iomodel->Data(InversionCostFunctionsCoefficientsEnum)) {

		/*Create inputs and add to DataSetInput*/
		DatasetInput* datasetinput=new DatasetInput(InversionCostFunctionsCoefficientsEnum);
		for(i=0;i<num_cm_responses;i++){
			for(j=0;j<6;j++)nodeinputs[j]=iomodel->Data(InversionCostFunctionsCoefficientsEnum)[(penta_vertex_ids[j]-1)*num_cm_responses+i];
			datasetinput->inputs->AddObject(new PentaP1Input(InversionCostFunctionsCoefficientsEnum,nodeinputs));
		}

		/*Add datasetinput to element inputs*/
		this->inputs->AddInput(datasetinput);
	}
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolution {{{*/
void  Penta::InputUpdateFromSolution(IssmDouble* solution){

	int analysis_type;

	/*retreive parameters: */
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);

	/*Just branch to the correct InputUpdateFromSolution generator, according to the type of analysis we are carrying out: */
	switch(analysis_type){
	#ifdef _HAVE_DIAGNOSTIC_
	case DiagnosticHorizAnalysisEnum:
		InputUpdateFromSolutionDiagnosticHoriz( solution);
		break;
	case DiagnosticHutterAnalysisEnum:
		InputUpdateFromSolutionDiagnosticHutter( solution);
		break;
	case DiagnosticVertAnalysisEnum:
		InputUpdateFromSolutionDiagnosticVert( solution);
		break;
	#endif
	#ifdef _HAVE_CONTROL_
	case AdjointHorizAnalysisEnum:
		int approximation;
		inputs->GetInputValue(&approximation,ApproximationEnum);
		if(approximation==StokesApproximationEnum || approximation==NoneApproximationEnum){
			InputUpdateFromSolutionAdjointStokes( solution);
		}
		else{
			InputUpdateFromSolutionAdjointHoriz( solution);
		}
		break;
	#endif
	#ifdef _HAVE_THERMAL_
	case ThermalAnalysisEnum:
		InputUpdateFromSolutionThermal( solution);
		break;
	case EnthalpyAnalysisEnum:
		InputUpdateFromSolutionEnthalpy( solution);
		break;
	case MeltingAnalysisEnum:
		InputUpdateFromSolutionOneDof(solution,BasalforcingsMeltingRateEnum);
		break;
	#endif
	case BedSlopeXAnalysisEnum:
		InputUpdateFromSolutionOneDofCollapsed(solution,BedSlopeXEnum);
		break;
	case BedSlopeYAnalysisEnum:
		InputUpdateFromSolutionOneDofCollapsed(solution,BedSlopeYEnum);
		break;
	case SurfaceSlopeXAnalysisEnum:
		InputUpdateFromSolutionOneDofCollapsed(solution,SurfaceSlopeXEnum);
		break;
	case SurfaceSlopeYAnalysisEnum:
		InputUpdateFromSolutionOneDofCollapsed(solution,SurfaceSlopeYEnum);
		break;
	case PrognosticAnalysisEnum:
		InputUpdateFromSolutionPrognostic(solution);
		break;
	#ifdef _HAVE_BALANCED_
	case BalancethicknessAnalysisEnum:
		InputUpdateFromSolutionOneDofCollapsed(solution,ThicknessEnum);
		break;
	#endif
	default: 
		_error2_("analysis " << analysis_type << " (" << EnumToStringx(analysis_type) << ") not supported yet");
	}
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionPrognostic{{{*/
void  Penta::InputUpdateFromSolutionPrognostic(IssmDouble* solution){

	const int  numdof   = NDOF1*NUMVERTICES;
	const int  numdof2d = NDOF1*NUMVERTICES2D;

	int    i,hydroadjustment;
	int*   doflist = NULL;
	IssmDouble rho_ice,rho_water,minthickness;
	IssmDouble newthickness[numdof];
	IssmDouble newbed[numdof];
	IssmDouble newsurface[numdof];
	IssmDouble oldbed[NUMVERTICES];
	IssmDouble oldsurface[NUMVERTICES];
	IssmDouble oldthickness[NUMVERTICES];
	Penta  *penta   = NULL;

	/*If not on bed, return*/
	if (!IsOnBed()) return;

	/*Get dof list: */
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);

	/*Use the dof list to index into the solution vector and extrude it */
	this->parameters->FindParam(&minthickness,PrognosticMinThicknessEnum);
	for(i=0;i<numdof2d;i++){
		newthickness[i]=solution[doflist[i]];
		if(xIsNan<IssmDouble>(newthickness[i])) _error2_("NaN found in solution vector");
		/*Constrain thickness to be at least 1m*/
		if(newthickness[i]<minthickness) newthickness[i]=minthickness;
		newthickness[i+numdof2d]=newthickness[i];
	}

	/*Get previous bed, thickness and surface*/
	GetInputListOnVertices(&oldbed[0],BedEnum);
	GetInputListOnVertices(&oldsurface[0],SurfaceEnum);
	GetInputListOnVertices(&oldthickness[0],ThicknessEnum);

	/*Fing PrognosticHydrostaticAdjustment to figure out how to update the geometry:*/
	this->parameters->FindParam(&hydroadjustment,PrognosticHydrostaticAdjustmentEnum);

	/*recover material parameters: */
	rho_ice=matpar->GetRhoIce();
	rho_water=matpar->GetRhoWater();

	for(i=0;i<numdof;i++) {
		/*If shelf: hydrostatic equilibrium*/
		if (this->nodes[i]->IsGrounded()){
			newsurface[i]=oldbed[i]+newthickness[i]; //surface = oldbed + newthickness
			newbed[i]=oldbed[i];               //same bed: do nothing
		}
		else{ //so it is an ice shelf
			if(hydroadjustment==AbsoluteEnum){
				newsurface[i]=newthickness[i]*(1-rho_ice/rho_water);
				newbed[i]=newthickness[i]*(-rho_ice/rho_water);
			}
			else if(hydroadjustment==IncrementalEnum){
				newsurface[i]=oldsurface[i]+(1.0-rho_ice/rho_water)*(newthickness[i]-oldthickness[i]); //surface = oldsurface + (1-di) * dH 
				newbed[i]=oldbed[i]-rho_ice/rho_water*(newthickness[i]-oldthickness[i]); //bed = oldbed + di * dH
			}
			else _error2_("Hydrostatic adjustment " << hydroadjustment << " (" << EnumToStringx(hydroadjustment) << ") not supported yet");
		}
	}

	/*Start looping over all elements above current element and update all inputs*/
	penta=this;
	for(;;){
		/*Add input to the element: */
		penta->inputs->AddInput(new PentaP1Input(ThicknessEnum,newthickness));
		penta->inputs->AddInput(new PentaP1Input(SurfaceEnum,newsurface));
		penta->inputs->AddInput(new PentaP1Input(BedEnum,newbed));

		/*Stop if we have reached the surface*/
		if (penta->IsOnSurface()) break;

		/* get upper Penta*/
		penta=penta->GetUpperElement(); _assert_(penta->Id()!=this->id);
	}
	
	/*Free ressources:*/
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionOneDof{{{*/
void  Penta::InputUpdateFromSolutionOneDof(IssmDouble* solution,int enum_type){

	const int numdof = NDOF1*NUMVERTICES;

	IssmDouble values[numdof];
	int*   doflist=NULL;

	/*Get dof list: */
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);

	/*Use the dof list to index into the solution vector: */
	for(int i=0;i<numdof;i++){
		values[i]=solution[doflist[i]];
		if(xIsNan<IssmDouble>(values[i])) _error2_("NaN found in solution vector");
	}

	/*Add input to the element: */
	this->inputs->AddInput(new PentaP1Input(enum_type,values));
	
	/*Free ressources:*/
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionOneDofCollpased{{{*/
void  Penta::InputUpdateFromSolutionOneDofCollapsed(IssmDouble* solution,int enum_type){

	const int  numdof   = NDOF1*NUMVERTICES;
	const int  numdof2d = NDOF1*NUMVERTICES2D;

	IssmDouble  values[numdof];
	int*    doflist = NULL;
	Penta  *penta   = NULL;

	/*If not on bed, return*/
	if (!IsOnBed()) return;

	/*Get dof list: */
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);

	/*Use the dof list to index into the solution vector and extrude it */
	for(int i=0;i<numdof2d;i++){
		values[i]         =solution[doflist[i]];
		values[i+numdof2d]=values[i];
		if(xIsNan<IssmDouble>(values[i])) _error2_("NaN found in solution vector");
	}

	/*Start looping over all elements above current element and update all inputs*/
	penta=this;
	for(;;){
		/*Add input to the element: */
		penta->inputs->AddInput(new PentaP1Input(enum_type,values));

		/*Stop if we have reached the surface*/
		if (penta->IsOnSurface()) break;

		/* get upper Penta*/
		penta=penta->GetUpperElement(); _assert_(penta->Id()!=this->id);
	}
	
	/*Free ressources:*/
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromVector(IssmDouble* vector, int name, int type);{{{*/
void  Penta::InputUpdateFromVector(IssmDouble* vector, int name, int type){

	/*Check that name is an element input*/
	if (!IsInput(name)) return;

	/*Penta update B in InputUpdateFromSolutionThermal, so don't look for B update here.*/

	switch(type){

		case VertexEnum:

			/*New PentaVertexInpu*/
			IssmDouble values[6];

			/*Get values on the 6 vertices*/
			for (int i=0;i<6;i++){
				values[i]=vector[this->nodes[i]->GetVertexDof()];
			}

			/*update input*/
			this->inputs->AddInput(new PentaP1Input(name,values));
			return;

		default:

			_error2_("type " << type << " (" << EnumToStringx(type) << ") not implemented yet");
	}
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromVector(int* vector, int name, int type);{{{*/
void  Penta::InputUpdateFromVector(int* vector, int name, int type){
	_error2_("not supported yet!");
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromVector(bool* vector, int name, int type);{{{*/
void  Penta::InputUpdateFromVector(bool* vector, int name, int type){
	_error2_("not supported yet!");
}
/*}}}*/
/*FUNCTION Penta::IsOnBed{{{*/
bool Penta::IsOnBed(void){

	bool onbed;
	inputs->GetInputValue(&onbed,MeshElementonbedEnum);
	return onbed;
}
/*}}}*/
/*FUNCTION Penta::IsInput{{{*/
bool Penta::IsInput(int name){
	if (
				name==ThicknessEnum ||
				name==SurfaceEnum ||
				name==BedEnum ||
				name==SurfaceSlopeXEnum ||
				name==SurfaceSlopeYEnum ||
				name==SurfaceforcingsMassBalanceEnum ||
				name==BasalforcingsMeltingRateEnum ||
				name==BasalforcingsGeothermalfluxEnum ||
				name==SurfaceAreaEnum||
				name==PressureEnum ||
				name==VxEnum ||
				name==VyEnum ||
				name==VzEnum ||
				name==VxMeshEnum ||
				name==VyMeshEnum ||
				name==VzMeshEnum ||
				name==InversionVxObsEnum ||
				name==InversionVyObsEnum ||
				name==InversionVzObsEnum ||
				name==TemperatureEnum ||
				name==EnthalpyEnum ||
				name==EnthalpyPicardEnum ||
				name==WaterfractionEnum||
				name==FrictionCoefficientEnum ||
				name==GradientEnum ||
				name==OldGradientEnum  ||
				name==ConvergedEnum || 
				name==QmuVxEnum ||
				name==QmuVyEnum ||
				name==QmuPressureEnum ||
				name==QmuBedEnum ||
				name==QmuThicknessEnum ||
				name==QmuSurfaceEnum ||
				name==QmuTemperatureEnum ||
				name==QmuMeltingEnum
				) {
		return true;
	}
	else return false;
}
/*}}}*/
/*FUNCTION Penta::IsFloating{{{*/
bool   Penta::IsFloating(){

	bool onshelf;
	inputs->GetInputValue(&onshelf,MaskElementonfloatingiceEnum);
	return onshelf;
}
/*}}}*/
/*FUNCTION Penta::IsNodeOnShelf {{{*/
bool   Penta::IsNodeOnShelf(){

	int  i;
	bool shelf=false;

	for(i=0;i<6;i++){
		if (nodes[i]->IsFloating()){
			shelf=true;
			break;
		}
	}
	return shelf;
}
/*}}}*/
/*FUNCTION Penta::IsNodeOnShelfFromFlags {{{*/
bool   Penta::IsNodeOnShelfFromFlags(IssmDouble* flags){

	int  i;
	bool shelf=false;

	for(i=0;i<NUMVERTICES;i++){
		if (flags[nodes[i]->Sid()]){
			shelf=true;
			break;
		}
	}
	return shelf;
}
/*}}}*/
/*FUNCTION Penta::IsOnSurface{{{*/
bool Penta::IsOnSurface(void){

	bool onsurface;
	inputs->GetInputValue(&onsurface,MeshElementonsurfaceEnum);
	return onsurface;
}
/*}}}*/
/*FUNCTION Penta::IsOnWater {{{*/
bool   Penta::IsOnWater(){

	bool onwater;
	inputs->GetInputValue(&onwater,MaskElementonwaterEnum);
	return onwater;
}
/*}}}*/
/*FUNCTION Penta::ListResultsInfo{{{*/
void Penta::ListResultsInfo(int** in_resultsenums,int** in_resultssizes,IssmDouble** in_resultstimes,int** in_resultssteps,int* in_num_results){

	/*Intermediaries*/
	int     i;
	int     numberofresults = 0;
	int     *resultsenums   = NULL;
	int     *resultssizes   = NULL;
	IssmDouble  *resultstimes   = NULL;
	int     *resultssteps   = NULL;

	/*Checks*/
	_assert_(in_num_results);

	/*Count number of results*/
	for(i=0;i<this->results->Size();i++){
		ElementResult* elementresult=(ElementResult*)this->results->GetObjectByOffset(i);
		numberofresults++;
	}

	if(numberofresults){

		/*Allocate output*/
		resultsenums=xNew<int>(numberofresults);
		resultssizes=xNew<int>(numberofresults);
		resultstimes=xNew<IssmDouble>(numberofresults);
		resultssteps=xNew<int>(numberofresults);

		/*populate enums*/
		for(i=0;i<this->results->Size();i++){
			ElementResult* elementresult=(ElementResult*)this->results->GetObjectByOffset(i);
			resultsenums[i]=elementresult->InstanceEnum();
			resultstimes[i]=elementresult->GetTime();
			resultssteps[i]=elementresult->GetStep();
			if(elementresult->ObjectEnum()==PentaP1ElementResultEnum){
				resultssizes[i]=P1Enum;
			}
			else{
				resultssizes[i]=P0Enum;
			}
		}
	}

	/*Assign output pointers:*/
	*in_num_results=numberofresults;
	*in_resultsenums=resultsenums;
	*in_resultssizes=resultssizes;
	*in_resultstimes=resultstimes;
	*in_resultssteps=resultssteps;

}/*}}}*/
/*FUNCTION Penta::MigrateGroundingLine{{{*/
void  Penta::MigrateGroundingLine(IssmDouble* old_floating_ice,IssmDouble* sheet_ungrounding){

	int     i,migration_style,unground;
	bool    elementonshelf = false;
	IssmDouble  bed_hydro,yts,gl_melting_rate;
	IssmDouble  rho_water,rho_ice,density;
	IssmDouble  melting[NUMVERTICES];
	IssmDouble  h[NUMVERTICES],s[NUMVERTICES],b[NUMVERTICES],ba[NUMVERTICES];

	if(!IsOnBed()) return;

	/*Recover info at the vertices: */
	parameters->FindParam(&migration_style,GroundinglineMigrationEnum);
	parameters->FindParam(&yts,ConstantsYtsEnum);
	GetInputListOnVertices(&h[0],ThicknessEnum);
	GetInputListOnVertices(&s[0],SurfaceEnum);
	GetInputListOnVertices(&b[0],BedEnum);
	GetInputListOnVertices(&ba[0],BathymetryEnum);
	rho_water=matpar->GetRhoWater();
	rho_ice=matpar->GetRhoIce();
	density=rho_ice/rho_water;
	
	/*go through vertices, and update inputs, considering them to be PentaVertex type: */
	for(i=0;i<NUMVERTICES;i++){
		/*Ice shelf: if bed below bathymetry, impose it at the bathymetry and update surface, elso do nothing */
		if(old_floating_ice[nodes[i]->Sid()]){
			if(b[i]<=ba[i]){ 
				b[i]=ba[i];
				s[i]=b[i]+h[i];
				nodes[i]->inputs->AddInput(new BoolInput(MaskVertexonfloatingiceEnum,false));
				nodes[i]->inputs->AddInput(new BoolInput(MaskVertexongroundediceEnum,true));
			}
		}
		/*Ice sheet: if hydrostatic bed above bathymetry, ice sheet starts to unground, elso do nothing */
		/*Change only if AgressiveMigration or if the ice sheet is in contact with the ocean*/
		else{
			bed_hydro=-density*h[i];
			if (bed_hydro>ba[i]){
				/*Unground only if the element is connected to the ice shelf*/
				if(migration_style==AgressiveMigrationEnum){
					s[i]=(1-density)*h[i];
					b[i]=-density*h[i];
					nodes[i]->inputs->AddInput(new BoolInput(MaskVertexonfloatingiceEnum,true));
					nodes[i]->inputs->AddInput(new BoolInput(MaskVertexongroundediceEnum,false));
				}
				else if(migration_style==SoftMigrationEnum && sheet_ungrounding[nodes[i]->Sid()]){
					s[i]=(1-density)*h[i];
					b[i]=-density*h[i];
					nodes[i]->inputs->AddInput(new BoolInput(MaskVertexonfloatingiceEnum,true));
					nodes[i]->inputs->AddInput(new BoolInput(MaskVertexongroundediceEnum,false));
				}
			}
		}
	}

	/*If at least one vertex is now floating, the element is now floating*/
	for(i=0;i<NUMVERTICES;i++){
		if(nodes[i]->IsFloating()){
			elementonshelf=true;
			break;
		}
	}
	
   /*Add basal melting rate if element just ungrounded*/
	if(!this->IsFloating() && elementonshelf==true){
		for(i=0;i<NUMVERTICES;i++)melting[i]=gl_melting_rate/yts;
		this->inputs->AddInput(new PentaP1Input(BasalforcingsMeltingRateEnum,&melting[0]));
	} 

	/*Update inputs*/
	this->inputs->AddInput(new PentaP1Input(SurfaceEnum,&s[0]));
	this->inputs->AddInput(new PentaP1Input(BedEnum,&b[0]));
   this->inputs->AddInput(new BoolInput(MaskElementonfloatingiceEnum,elementonshelf));

	/*Extrude inputs*/
	this->InputExtrude(SurfaceEnum,ElementEnum);
	this->InputExtrude(BedEnum,ElementEnum);
	this->InputExtrude(MaskElementonfloatingiceEnum,ElementEnum);
	this->InputExtrude(MaskVertexonfloatingiceEnum,NodeEnum);
	this->InputExtrude(MaskVertexongroundediceEnum,NodeEnum);
}
/*}}}*/
/*FUNCTION Penta::MinEdgeLength{{{*/
IssmDouble Penta::MinEdgeLength(IssmDouble xyz_list[6][3]){
	/*Return the minimum lenght of the nine egdes of the penta*/

	int    i,node0,node1;
	int    edges[9][2]={{0,1},{0,2},{1,2},{3,4},{3,5},{4,5},{0,3},{1,4},{2,5}}; //list of the nine edges
	IssmDouble length;
	IssmDouble minlength=-1;

	for(i=0;i<9;i++){
		/*Find the two nodes for this edge*/
		node0=edges[i][0];
		node1=edges[i][1];

		/*Compute the length of this edge and compare it to the minimal length*/
		length=pow(pow(xyz_list[node0][0]-xyz_list[node1][0],2.0)+pow(xyz_list[node0][1]-xyz_list[node1][1],2.0)+pow(xyz_list[node0][2]-xyz_list[node1][2],2.0),0.5);
		if(length<minlength || minlength<0) minlength=length;
	}

	return minlength;
}
/*}}}*/
/*FUNCTION Penta::MyRank {{{*/
int    Penta::MyRank(void){ 
	extern int my_rank;
	return my_rank; 
}
/*}}}*/
/*FUNCTION Penta::NodalValue {{{*/
int    Penta::NodalValue(IssmDouble* pvalue, int index, int natureofdataenum,bool process_units){

	int i;
	int found=0;
	IssmDouble value;
	Input* data=NULL;
	GaussPenta* gauss=NULL;

	/*First, serarch the input: */
	data=inputs->GetInput(natureofdataenum); 

	/*figure out if we have the vertex id: */
	found=0;
	for(i=0;i<NUMVERTICES;i++){
		if(index==nodes[i]->GetVertexId()){
			/*Do we have natureofdataenum in our inputs? :*/
			if(data){
				/*ok, we are good. retrieve value of input at vertex :*/
				gauss=new GaussPenta(); gauss->GaussVertex(i);
				data->GetInputValue(&value,gauss);
				found=1;
				break;
			}
		}
	}

	delete gauss;
	if(found)*pvalue=value;
	return found;
}
/*}}}*/
/*FUNCTION Penta::PatchFill{{{*/
void  Penta::PatchFill(int* pcount, Patch* patch){

	int i,count;
	int vertices_ids[6];

	/*recover pointer: */
	count=*pcount;
		
	/*will be needed later: */
	for(i=0;i<6;i++) vertices_ids[i]=nodes[i]->GetVertexId(); //vertices id start at column 3 of the patch.

	for(i=0;i<this->results->Size();i++){
		ElementResult* elementresult=(ElementResult*)this->results->GetObjectByOffset(i);

		/*For this result,fill the information in the Patch object (element id + vertices ids), and then hand 
		 *it to the result object, to fill the rest: */
		patch->fillelementinfo(count,this->sid+1,vertices_ids,6);
		elementresult->PatchFill(count,patch);

		/*increment counter: */
		count++;
	}

	/*Assign output pointers:*/
	*pcount=count;
}/*}}}*/
/*FUNCTION Penta::PatchSize{{{*/
void  Penta::PatchSize(int* pnumrows, int* pnumvertices,int* pnumnodes){

	int     i;
	int     numrows       = 0;
	int     numnodes      = 0;
	int     temp_numnodes = 0;

	/*Go through all the results objects, and update the counters: */
	for (i=0;i<this->results->Size();i++){
		ElementResult* elementresult=(ElementResult*)this->results->GetObjectByOffset(i);
		/*first, we have one more result: */
		numrows++;
		/*now, how many vertices and how many nodal values for this result? :*/
		temp_numnodes=elementresult->NumberOfNodalValues(); //ask result object.
		if(temp_numnodes>numnodes)numnodes=temp_numnodes;
	}

	/*Assign output pointers:*/
	*pnumrows=numrows;
	*pnumvertices=NUMVERTICES;
	*pnumnodes=numnodes;
}
/*}}}*/
/*FUNCTION Penta::PositiveDegreeDay{{{*/
void  Penta::PositiveDegreeDay(IssmDouble* pdds,IssmDouble* pds,IssmDouble signorm){

   IssmDouble agd[NUMVERTICES];             // surface mass balance
   IssmDouble monthlytemperatures[NUMVERTICES][12],monthlyprec[NUMVERTICES][12];
   IssmDouble h[NUMVERTICES],s[NUMVERTICES]; // ,b
   IssmDouble rho_water,rho_ice;

   /*Recover monthly temperatures and precipitation*/
   Input*     input=inputs->GetInput(SurfaceforcingsMonthlytemperaturesEnum); _assert_(input);
   Input*     input2=inputs->GetInput(SurfaceforcingsPrecipitationEnum); _assert_(input2);
   GaussPenta* gauss=new GaussPenta();
   IssmDouble time,yts;
   this->parameters->FindParam(&time,TimeEnum);
   this->parameters->FindParam(&yts,ConstantsYtsEnum);
   for(int month=0;month<12;month++) {
     for(int iv=0;iv<NUMVERTICES;iv++) {
       gauss->GaussVertex(iv);
       input->GetInputValue(&monthlytemperatures[iv][month],gauss,time+month/12.*yts);
       monthlytemperatures[iv][month]=monthlytemperatures[iv][month]-273.15; // conversion from Kelvin to celcius
       input2->GetInputValue(&monthlyprec[iv][month],gauss,time+month/12.*yts);
       monthlyprec[iv][month]=monthlyprec[iv][month]*yts; // convertion to m/yr
     }
   } 

  /*Recover info at the vertices: */
  GetInputListOnVertices(&h[0],ThicknessEnum);
  GetInputListOnVertices(&s[0],SurfaceEnum);

  /*Get material parameters :*/
  rho_ice=matpar->GetRhoIce();
  rho_water=matpar->GetRhoFreshwater();

   /*measure the surface mass balance*/
   for (int iv = 0; iv < NUMVERTICES; iv++){
     agd[iv]=PddSurfaceMassBlance(&monthlytemperatures[iv][0], &monthlyprec[iv][0], pdds, pds, signorm, yts, h[iv], s[iv], rho_ice, rho_water);
   }

   /*Update inputs*/    
   this->inputs->AddInput(new PentaP1Input(SurfaceforcingsMassBalanceEnum,&agd[0]));
   //this->inputs->AddInput(new PentaVertexInput(ThermalSpcTemperatureEnum,&Tsurf[0]));
   this->InputExtrude(SurfaceforcingsMassBalanceEnum,ElementEnum);

	/*clean-up*/
	delete gauss;
}
/*}}}*/
/*FUNCTION Penta::PotentialSheetUngrounding{{{*/
void  Penta::PotentialSheetUngrounding(Vector* potential_sheet_ungrounding){

	int     i;
	IssmDouble  h[NUMVERTICES],ba[NUMVERTICES];
	IssmDouble  bed_hydro;
	IssmDouble  rho_water,rho_ice,density;
	bool    elementonshelf = false;

	/*material parameters: */
	rho_water=matpar->GetRhoWater();
	rho_ice=matpar->GetRhoIce();
	density=rho_ice/rho_water;
	GetInputListOnVertices(&h[0],ThicknessEnum);
	GetInputListOnVertices(&ba[0],BathymetryEnum);

	/*go through vertices, and figure out which ones are on the ice sheet, and want to unground: */
	for(i=0;i<NUMVERTICES;i++){
		/*Find if grounded vertices want to start floating*/
		if (!nodes[i]->IsFloating()){
			bed_hydro=-density*h[i];
			if (bed_hydro>ba[i]){
				/*Vertex that could potentially unground, flag it*/
				potential_sheet_ungrounding->SetValue(nodes[i]->Sid(),1,INS_VAL);
			}
		}
	}
}
/*}}}*/
/*FUNCTION Penta::ProcessResultsUnits{{{*/
void  Penta::ProcessResultsUnits(void){

	int i;

	for(i=0;i<this->results->Size();i++){
		ElementResult* elementresult=(ElementResult*)this->results->GetObjectByOffset(i);
		elementresult->ProcessUnits(this->parameters);
	}
}
/*}}}*/
/*FUNCTION Penta::ReduceMatrixStokes {{{*/
void Penta::ReduceMatrixStokes(IssmDouble* Ke_reduced, IssmDouble* Ke_temp){

	int    i,j;
	IssmDouble Kii[24][24];
	IssmDouble Kib[24][3];
	IssmDouble Kbb[3][3];
	IssmDouble Kbi[3][24];
	IssmDouble Kbbinv[3][3];
	IssmDouble Kright[24][24];

	/*Create the four matrices used for reduction */
	for(i=0;i<24;i++){
		for(j=0;j<24;j++){
			Kii[i][j]=*(Ke_temp+27*i+j);
		}
		for(j=0;j<3;j++){
			Kib[i][j]=*(Ke_temp+27*i+j+24);
		}
	}
	for(i=0;i<3;i++){
		for(j=0;j<24;j++){
			Kbi[i][j]=*(Ke_temp+27*(i+24)+j);
		}
		for(j=0;j<3;j++){
			Kbb[i][j]=*(Ke_temp+27*(i+24)+j+24);
		}
	}

	/*Inverse the matrix corresponding to bubble part Kbb */
	Matrix3x3Invert(&Kbbinv[0][0], &Kbb[0][0]);

	/*Multiply matrices to create the reduce matrix Ke_reduced */
	TripleMultiply(&Kib[0][0],24,3,0,
				&Kbbinv[0][0],3,3,0,
				&Kbi[0][0],3,24,0,
				&Kright[0][0],0);

	/*Affect value to the reduced matrix */
	for(i=0;i<24;i++) for(j=0;j<24;j++) *(Ke_reduced+24*i+j)=Kii[i][j]-Kright[i][j];
}
/*}}}*/
/*FUNCTION Penta::ReduceVectorStokes {{{*/
void Penta::ReduceVectorStokes(IssmDouble* Pe_reduced, IssmDouble* Ke_temp, IssmDouble* Pe_temp){

	int    i,j;
	IssmDouble Pi[24];
	IssmDouble Pb[3];
	IssmDouble Kbb[3][3];
	IssmDouble Kib[24][3];
	IssmDouble Kbbinv[3][3];
	IssmDouble Pright[24];

	/*Create the four matrices used for reduction */
	for(i=0;i<24;i++) Pi[i]=*(Pe_temp+i);
	for(i=0;i<3;i++) Pb[i]=*(Pe_temp+i+24);
	for(j=0;j<3;j++){
		for(i=0;i<24;i++){
			Kib[i][j]=*(Ke_temp+3*i+j);
		}
		for(i=0;i<3;i++){
			Kbb[i][j]=*(Ke_temp+3*(i+24)+j);
		}
	}

	/*Inverse the matrix corresponding to bubble part Kbb */
	Matrix3x3Invert(&Kbbinv[0][0], &Kbb[0][0]);

	/*Multiply matrices to create the reduce matrix Ke_reduced */
	TripleMultiply(&Kib[0][0],24,3,0,
				&Kbbinv[0][0],3,3,0,
				&Pb[0],3,1,0,&Pright[0],0);

	/*Affect value to the reduced matrix */
	for(i=0;i<24;i++) *(Pe_reduced+i)=Pi[i]-Pright[i];
}
/*}}}*/
/*FUNCTION Penta::RequestedOutput{{{*/
void Penta::RequestedOutput(int output_enum,int step,IssmDouble time){
			
	if(IsInput(output_enum)){
		/*just transfer this input to results, and we are done: */
		InputToResult(output_enum,step,time);
	}
	else{
		/*this input does not exist, compute it, and then transfer to results: */
		switch(output_enum){
			case BasalFrictionEnum:

				/*create input: */
				BasalFrictionCreateInput();

				/*transfer to results :*/
				InputToResult(output_enum,step,time);

				/*erase input: */
				inputs->DeleteInput(output_enum);
				break;
			case ViscousHeatingEnum:

				/*create input: */
				ViscousHeatingCreateInput();

				/*transfer to results :*/
				InputToResult(output_enum,step,time);

				/*erase input: */
				inputs->DeleteInput(output_enum);
				break;

			case StressTensorEnum: 
				this->ComputeStressTensor();
				InputToResult(StressTensorxxEnum,step,time);
				InputToResult(StressTensorxyEnum,step,time);
				InputToResult(StressTensorxzEnum,step,time);
				InputToResult(StressTensoryyEnum,step,time);
				InputToResult(StressTensoryzEnum,step,time);
				InputToResult(StressTensorzzEnum,step,time);
				break;

			default:
				/*do nothing, no need to derail the computation because one of the outputs requested cannot be found: */
				break;
		}
	}

}
/*}}}*/
/*FUNCTION Penta::ResetCoordinateSystem{{{*/
void  Penta::ResetCoordinateSystem(void){

	int    approximation;
	IssmDouble slopex[NUMVERTICES];
	IssmDouble slopey[NUMVERTICES];
	IssmDouble xz_plane[6];

	/*For Stokes only: we want the CS to be tangential to the bedrock*/
	inputs->GetInputValue(&approximation,ApproximationEnum);
	if(IsFloating() || !IsOnBed() || (approximation!=StokesApproximationEnum && approximation!=MacAyealStokesApproximationEnum &&  approximation!=PattynStokesApproximationEnum)) return;

	/*Get slope on each node*/
	GetInputListOnVertices(&slopex[0],BedSlopeXEnum);
	GetInputListOnVertices(&slopey[0],BedSlopeYEnum);

	/*Loop over basal nodes (first 3) and update their CS*/
	for(int i=0;i<NUMVERTICES2D;i++){

		/*New X axis             New Z axis*/
		xz_plane[0]=1.;          xz_plane[3]=-slopex[i];  
		xz_plane[1]=0.;          xz_plane[4]=-slopey[i];  
		xz_plane[2]=slopex[i];   xz_plane[5]=1.;          

		XZvectorsToCoordinateSystem(&this->nodes[i]->coord_system[0][0],&xz_plane[0]);
	}
}
/*}}}*/
/*FUNCTION Penta::SetClone {{{*/
void  Penta::SetClone(int* minranks){

	_error2_("not implemented yet");
}
/*}}}*/
/*FUNCTION Penta::SetCurrentConfiguration {{{*/
void  Penta::SetCurrentConfiguration(Elements* elementsin, Loads* loadsin, DataSet* nodesin, Materials* materialsin, Parameters* parametersin){

	int analysis_counter;

	/*go into parameters and get the analysis_counter: */
	parametersin->FindParam(&analysis_counter,AnalysisCounterEnum);

	/*Get Element type*/
	this->element_type=this->element_type_list[analysis_counter];

	/*Pick up nodes */
	if (this->hnodes[analysis_counter]) this->nodes=(Node**)this->hnodes[analysis_counter]->deliverp();
	else this->nodes=NULL;
}
/*}}}*/
/*FUNCTION Penta::SpawnTria {{{*/
Tria*  Penta::SpawnTria(int g0, int g1, int g2){

	int   i,analysis_counter;
	int   indices[3];
	int   zero=0;
	Tria*       tria            = NULL;
	Inputs*     tria_inputs     = NULL;
	Results*    tria_results    = NULL;
	Parameters* tria_parameters = NULL;

	/*go into parameters and get the analysis_counter: */
	this->parameters->FindParam(&analysis_counter,AnalysisCounterEnum);

	indices[0]=g0;
	indices[1]=g1;
	indices[2]=g2;

	tria_parameters=this->parameters;
	tria_inputs=(Inputs*)this->inputs->SpawnTriaInputs(indices);
	tria_results=(Results*)this->results->SpawnTriaResults(indices);

	tria=new Tria();
	tria->id=this->id;
	tria->inputs=tria_inputs;
	tria->results=tria_results;
	tria->parameters=tria_parameters;
	tria->element_type=P1Enum; //Only P1 CG for now (TO BE CHANGED)
	this->SpawnTriaHook(dynamic_cast<TriaHook*>(tria),&indices[0]);

	/*Spawn matice*/
	tria->matice=NULL;
	tria->matice=(Matice*)this->matice->copy();
	delete tria->matice->inputs;
	tria->matice->inputs=(Inputs*)this->matice->inputs->SpawnTriaInputs(indices);

	/*recover nodes, matice and matpar: */
	tria->nodes=(Node**)tria->hnodes[analysis_counter]->deliverp();
	tria->matpar=(Matpar*)tria->hmatpar->delivers();

	return tria;
}
/*}}}*/
/*FUNCTION Penta::SmbGradients{{{*/
void Penta::SmbGradients(void){

	int i;

	// input
   IssmDouble h[NUMVERTICES];					// ice thickness (m)		
	IssmDouble s[NUMVERTICES];					// surface elevation (m)
	IssmDouble a_pos[NUMVERTICES];				// Hs-SMB relation parameter 
	IssmDouble b_pos[NUMVERTICES];				// Hs-SMB relation parameter
	IssmDouble a_neg[NUMVERTICES];				// Hs-SMB relation parameter
	IssmDouble b_neg[NUMVERTICES];				// Hs-SMB relation paremeter
	IssmDouble Hc[NUMVERTICES];					// elevation of transition between accumulation regime and ablation regime
	IssmDouble smb_pos_max[NUMVERTICES];		// maximum SMB value in the accumulation regime
	IssmDouble smb_pos_min[NUMVERTICES];		// minimum SMB value in the accumulation regime
   IssmDouble rho_water;                   // density of fresh water
	IssmDouble rho_ice;                     // density of ice

	// output
	IssmDouble smb[NUMVERTICES];					// surface mass balance (m/yr ice)

	/*Recover SmbGradients*/
	GetInputListOnVertices(&Hc[0],SurfaceforcingsHcEnum);
	GetInputListOnVertices(&smb_pos_max[0],SurfaceforcingsSmbPosMaxEnum);
	GetInputListOnVertices(&smb_pos_min[0],SurfaceforcingsSmbPosMinEnum);
	GetInputListOnVertices(&a_pos[0],SurfaceforcingsAPosEnum);
	GetInputListOnVertices(&b_pos[0],SurfaceforcingsBPosEnum);
	GetInputListOnVertices(&a_neg[0],SurfaceforcingsANegEnum);
	GetInputListOnVertices(&b_neg[0],SurfaceforcingsBNegEnum);
	
   /*Recover surface elevatio at vertices: */
	GetInputListOnVertices(&h[0],ThicknessEnum);
	GetInputListOnVertices(&s[0],SurfaceEnum);

   /*Get material parameters :*/
   rho_ice=matpar->GetRhoIce();
   rho_water=matpar->GetRhoFreshwater();
			
   // loop over all vertices
   for(i=0;i<NUMVERTICES;i++){
     if(s[i]>Hc[i]){
	    smb[i]=a_pos[i]+b_pos[i]*s[i];
		 if(smb[i]>smb_pos_max[i]){smb[i]=smb_pos_max[i];}
		 if(smb[i]<smb_pos_min[i]){smb[i]=smb_pos_min[i];}
	  }
	  else{
	    smb[i]=a_neg[i]+b_neg[i]*s[i];
	  }
	  smb[i]=smb[i]/rho_ice;      // SMB in m/y ice		
	  
	}  //end of the loop over the vertices
	  /*Update inputs*/
	  this->inputs->AddInput(new PentaP1Input(SurfaceforcingsMassBalanceEnum,&smb[0]));
}
/*}}}*/
/*FUNCTION Penta::SurfaceArea {{{*/
IssmDouble Penta::SurfaceArea(void){

	int    approximation;
	IssmDouble S;
	Tria*  tria=NULL;

	/*retrieve inputs :*/
	inputs->GetInputValue(&approximation,ApproximationEnum);

	/*If on water, return 0: */
	if(IsOnWater())return 0;

	/*Bail out if this element if:
	 * -> Non MacAyeal not on the surface
	 * -> MacAyeal (2d model) and not on bed) */
	if ((approximation!=MacAyealApproximationEnum && !IsOnSurface()) || (approximation==MacAyealApproximationEnum && !IsOnBed())){
		return 0;
	}
	else if (approximation==MacAyealApproximationEnum){

		/*This element should be collapsed into a tria element at its base. Create this tria element, 
		 * and compute SurfaceArea*/
		tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria (lower face).
		S=tria->SurfaceArea();
		delete tria->matice; delete tria;
		return S;
	}
	else{

		tria=(Tria*)SpawnTria(3,4,5); //nodes 3, 4 and 5 make the new tria (upper face).
		S=tria->SurfaceArea();
		delete tria->matice; delete tria;
		return S;
	}
}
/*}}}*/
/*FUNCTION Penta::SurfaceNormal {{{*/
void Penta::SurfaceNormal(IssmDouble* surface_normal, IssmDouble xyz_list[3][3]){

	int    i;
	IssmDouble v13[3],v23[3];
	IssmDouble normal[3];
	IssmDouble normal_norm;

	for (i=0;i<3;i++){
		v13[i]=xyz_list[0][i]-xyz_list[2][i];
		v23[i]=xyz_list[1][i]-xyz_list[2][i];
	}

	normal[0]=v13[1]*v23[2]-v13[2]*v23[1];
	normal[1]=v13[2]*v23[0]-v13[0]*v23[2];
	normal[2]=v13[0]*v23[1]-v13[1]*v23[0];

	normal_norm=sqrt( pow(normal[0],2)+pow(normal[1],2)+pow(normal[2],2) );

	*(surface_normal)=normal[0]/normal_norm;
	*(surface_normal+1)=normal[1]/normal_norm;
	*(surface_normal+2)=normal[2]/normal_norm;
}
/*}}}*/
/*FUNCTION Penta::TimeAdapt{{{*/
IssmDouble  Penta::TimeAdapt(void){

	int    i;
	IssmDouble C,dx,dy,dz,dt;
	IssmDouble maxabsvx,maxabsvy,maxabsvz;
	IssmDouble maxx,minx,maxy,miny,maxz,minz;
	IssmDouble xyz_list[NUMVERTICES][3];

	/*get CFL coefficient:*/
	this->parameters->FindParam(&C,TimesteppingCflCoefficientEnum);

	/*Get for Vx and Vy, the max of abs value: */
	this->MaxAbsVx(&maxabsvx,false);
	this->MaxAbsVy(&maxabsvy,false);
	this->MaxAbsVz(&maxabsvz,false);

	/* Get node coordinates and dof list: */
	GetVerticesCoordinates(&xyz_list[0][0], this->nodes, NUMVERTICES);

	minx=xyz_list[0][0];
	maxx=xyz_list[0][0];
	miny=xyz_list[0][1];
	maxy=xyz_list[0][1];
	minz=xyz_list[0][2];
	maxz=xyz_list[0][2];
	
	for(i=1;i<NUMVERTICES;i++){
		if (xyz_list[i][0]<minx)minx=xyz_list[i][0];
		if (xyz_list[i][0]>maxx)maxx=xyz_list[i][0];
		if (xyz_list[i][1]<miny)miny=xyz_list[i][1];
		if (xyz_list[i][1]>maxy)maxy=xyz_list[i][1];
		if (xyz_list[i][2]<minz)minz=xyz_list[i][2];
		if (xyz_list[i][2]>maxz)maxz=xyz_list[i][2];
	}
	dx=maxx-minx;
	dy=maxy-miny;
	dz=maxz-minz;

	/*CFL criterion: */
	dt=C/(maxabsvy/dx+maxabsvy/dy+maxabsvz/dz);

	return dt;
}/*}}}*/
/*FUNCTION Penta::Update(int index,IoModel* iomodel,int analysis_counter,int analysis_type) {{{*/
void Penta::Update(int index,IoModel* iomodel,int analysis_counter,int analysis_type){ 

	/*Intermediaries*/
	IssmInt i,j;
	int     penta_type;
	int     penta_node_ids[6];
	int     penta_vertex_ids[6];
	IssmDouble  nodeinputs[6];
	IssmDouble  yts;
	int     stabilization;
	bool    dakota_analysis;
	bool    isstokes;
	IssmDouble  beta,heatcapacity,referencetemperature,meltingpoint,latentheat;

	/*Fetch parameters: */
	iomodel->Constant(&yts,ConstantsYtsEnum);
	iomodel->Constant(&stabilization,PrognosticStabilizationEnum);
	iomodel->Constant(&dakota_analysis,QmuIsdakotaEnum);
	iomodel->Constant(&isstokes,FlowequationIsstokesEnum);
	iomodel->Constant(&beta,MaterialsBetaEnum);
	iomodel->Constant(&heatcapacity,MaterialsHeatcapacityEnum);
	iomodel->Constant(&referencetemperature,ConstantsReferencetemperatureEnum);
	iomodel->Constant(&meltingpoint,MaterialsMeltingpointEnum);
	iomodel->Constant(&latentheat,MaterialsLatentheatEnum);

	/*Checks if debuging*/
	/*{{{*/
	_assert_(iomodel->Data(MeshElementsEnum));
	/*}}}*/

	/*Recover element type*/
	if ((analysis_type==PrognosticAnalysisEnum || analysis_type==BalancethicknessAnalysisEnum) && stabilization==3){
		/*P1 Discontinuous Galerkin*/
		penta_type=P1DGEnum;
	}
	else{
		/*P1 Continuous Galerkin*/
		penta_type=P1Enum;
	}
	this->SetElementType(penta_type,analysis_counter);

	/*Recover vertices ids needed to initialize inputs*/
	for(i=0;i<6;i++) penta_vertex_ids[i]=(int)iomodel->Data(MeshElementsEnum)[6*index+i]; //ids for vertices are in the elements array from Matlab

	/*Recover nodes ids needed to initialize the node hook.*/
	for(i=0;i<6;i++){ 
		//go recover node ids, needed to initialize the node hook.
		//WARNING: We assume P1 elements here!!!!!
		penta_node_ids[i]=iomodel->nodecounter+(int)iomodel->Data(MeshElementsEnum)[6*index+i]; //ids for vertices are in the elements array from Matlab
	}

	/*hooks: */
	this->SetHookNodes(penta_node_ids,analysis_counter); this->nodes=NULL; //set hook to nodes, for this analysis type

	/*Fill with IoModel*/
	this->InputUpdateFromIoModel(index,iomodel);

	/*Defaults if not provided in iomodel*/
	switch(analysis_type){

		case DiagnosticHorizAnalysisEnum:

			/*default vx,vy and vz: either observation or 0 */
			if(!iomodel->Data(VxEnum)){
				for(i=0;i<6;i++)nodeinputs[i]=0;
				this->inputs->AddInput(new PentaP1Input(VxEnum,nodeinputs));
				if(dakota_analysis) this->inputs->AddInput(new PentaP1Input(QmuVxEnum,nodeinputs));
			}
			if(!iomodel->Data(VyEnum)){
				for(i=0;i<6;i++)nodeinputs[i]=0;
				this->inputs->AddInput(new PentaP1Input(VyEnum,nodeinputs));
				if(dakota_analysis) this->inputs->AddInput(new PentaP1Input(QmuVyEnum,nodeinputs));
			}
			if(!iomodel->Data(VzEnum)){
				for(i=0;i<6;i++)nodeinputs[i]=0;
				this->inputs->AddInput(new PentaP1Input(VzEnum,nodeinputs));
				if(dakota_analysis) this->inputs->AddInput(new PentaP1Input(QmuVzEnum,nodeinputs));
			}
			if(!iomodel->Data(PressureEnum)){
				for(i=0;i<6;i++)nodeinputs[i]=0;
				if(dakota_analysis){
					this->inputs->AddInput(new PentaP1Input(PressureEnum,nodeinputs));
					this->inputs->AddInput(new PentaP1Input(QmuPressureEnum,nodeinputs));
				}
				if(isstokes){
					this->inputs->AddInput(new PentaP1Input(PressureEnum,nodeinputs));
					this->inputs->AddInput(new PentaP1Input(PressurePicardEnum,nodeinputs));
				}
			}
			if(*(iomodel->Data(FlowequationElementEquationEnum)+index)==PattynStokesApproximationEnum){
				/*Create VzPattyn and VzStokes Enums*/
				if(iomodel->Data(VzEnum) && iomodel->Data(FlowequationBorderstokesEnum)){
					for(i=0;i<6;i++) nodeinputs[i]=iomodel->Data(VzEnum)[penta_vertex_ids[i]-1]/yts*iomodel->Data(FlowequationBorderstokesEnum)[penta_vertex_ids[i]-1];
					this->inputs->AddInput(new PentaP1Input(VzStokesEnum,nodeinputs));
					for(i=0;i<6;i++) nodeinputs[i]=iomodel->Data(VzEnum)[penta_vertex_ids[i]-1]/yts*(1-iomodel->Data(FlowequationBorderstokesEnum)[penta_vertex_ids[i]-1]);
					this->inputs->AddInput(new PentaP1Input(VzPattynEnum,nodeinputs));
				}
				else{
					for(i=0;i<6;i++)nodeinputs[i]=0;
					this->inputs->AddInput(new PentaP1Input(VzStokesEnum,nodeinputs));
					this->inputs->AddInput(new PentaP1Input(VzPattynEnum,nodeinputs));
				}
			}
			if(*(iomodel->Data(FlowequationElementEquationEnum)+index)==MacAyealStokesApproximationEnum){
				/*Create VzMacAyeal and VzStokes Enums*/
				if(iomodel->Data(VzEnum) && iomodel->Data(FlowequationBorderstokesEnum)){
					for(i=0;i<6;i++) nodeinputs[i]=iomodel->Data(VzEnum)[penta_vertex_ids[i]-1]/yts*iomodel->Data(FlowequationBorderstokesEnum)[penta_vertex_ids[i]-1];
					this->inputs->AddInput(new PentaP1Input(VzStokesEnum,nodeinputs));
					for(i=0;i<6;i++) nodeinputs[i]=iomodel->Data(VzEnum)[penta_vertex_ids[i]-1]/yts*(1-iomodel->Data(FlowequationBorderstokesEnum)[penta_vertex_ids[i]-1]);
					this->inputs->AddInput(new PentaP1Input(VzMacAyealEnum,nodeinputs));
				}
				else{
					for(i=0;i<6;i++)nodeinputs[i]=0;
					this->inputs->AddInput(new PentaP1Input(VzStokesEnum,nodeinputs));
					this->inputs->AddInput(new PentaP1Input(VzMacAyealEnum,nodeinputs));
				}
			}
			break;

		case ThermalAnalysisEnum:
			/*Initialize mesh velocity*/
			for(i=0;i<6;i++)nodeinputs[i]=0;
			this->inputs->AddInput(new PentaP1Input(VxMeshEnum,nodeinputs));
			this->inputs->AddInput(new PentaP1Input(VyMeshEnum,nodeinputs));
			this->inputs->AddInput(new PentaP1Input(VzMeshEnum,nodeinputs));
			break;

		case EnthalpyAnalysisEnum:
			/*Initialize mesh velocity*/
			for(i=0;i<6;i++)nodeinputs[i]=0;
			this->inputs->AddInput(new PentaP1Input(VxMeshEnum,nodeinputs));
			this->inputs->AddInput(new PentaP1Input(VyMeshEnum,nodeinputs));
			this->inputs->AddInput(new PentaP1Input(VzMeshEnum,nodeinputs));
			if (iomodel->Data(TemperatureEnum) && iomodel->Data(WaterfractionEnum) && iomodel->Data(PressureEnum)) {
				for(i=0;i<6;i++){
					if(iomodel->Data(TemperatureEnum)[penta_vertex_ids[i]-1] < meltingpoint-beta*iomodel->Data(PressureEnum)[penta_vertex_ids[i]-1]){
						nodeinputs[i]=heatcapacity*(iomodel->Data(TemperatureEnum)[penta_vertex_ids[i]-1]-referencetemperature);
					}
					else nodeinputs[i]=heatcapacity*
					 (meltingpoint-beta*iomodel->Data(PressureEnum)[penta_vertex_ids[i]-1]-referencetemperature)
						+latentheat*iomodel->Data(WaterfractionEnum)[penta_vertex_ids[i]-1];
				}
				this->inputs->AddInput(new PentaP1Input(EnthalpyEnum,nodeinputs));
			}
			else _error2_("temperature and waterfraction required for the enthalpy solution");
			break;

		default:
			/*No update for other solution types*/
			break;
	}
}
/*}}}*/
/*FUNCTION Penta::UpdatePotentialSheetUngrounding{{{*/
int Penta::UpdatePotentialSheetUngrounding(IssmDouble* vertices_potentially_ungrounding,Vector* vec_nodes_on_iceshelf,IssmDouble* nodes_on_iceshelf){

	int i;
	int nflipped=0;

	/*Go through nodes, and whoever is on the potential_sheet_ungrounding, ends up in nodes_on_iceshelf: */
	for(i=0;i<NUMVERTICES;i++){
		if (vertices_potentially_ungrounding[nodes[i]->Sid()]){
			vec_nodes_on_iceshelf->SetValue(nodes[i]->Sid(),1,INS_VAL);
		
			/*If node was not on ice shelf, we flipped*/
			if(nodes_on_iceshelf[nodes[i]->Sid()]==0){
				nflipped++;
			}
		}
	}
	return nflipped;
}
/*}}}*/
/*FUNCTION Penta::ViscousHeatingCreateInput {{{*/
void Penta::ViscousHeatingCreateInput(void){

	/*Constants*/
	const int  numdof=NUMVERTICES*NDOF1;

	/*Intermediaries*/
	int    iv;
	IssmDouble phi;
	IssmDouble viscosity;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble epsilon[6];
	IssmDouble     viscousheating[NUMVERTICES]={0,0,0,0,0,0};
	IssmDouble     thickness;
	GaussPenta *gauss=NULL;

	/*Initialize Element vector*/
	ElementVector* pe=new ElementVector(nodes,NUMVERTICES,this->parameters);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	Input* vx_input=inputs->GetInput(VxEnum); _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum); _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum); _assert_(vz_input);
	Input* thickness_input=inputs->GetInput(ThicknessEnum); _assert_(thickness_input);

	/*loop over vertices: */
	gauss=new GaussPenta();
	for (int iv=0;iv<NUMVERTICES;iv++){
		gauss->GaussVertex(iv);
		
		thickness_input->GetInputValue(&thickness,gauss);

		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		matice->GetViscosity3dStokes(&viscosity,&epsilon[0]);
		GetPhi(&phi, &epsilon[0], viscosity);
		

		viscousheating[iv]=phi*thickness;
	}

	/*Create PentaVertex input, which will hold the basal friction:*/
	this->inputs->AddInput(new PentaP1Input(ViscousHeatingEnum,&viscousheating[0]));

	/*Clean up and return*/
	delete gauss;
}
/*}}}*/
/*FUNCTION Penta::SmearFunction {{{*/
void  Penta::SmearFunction(Vector* smearedvector,IssmDouble (*WeightFunction)(IssmDouble distance,IssmDouble radius),IssmDouble radius){
	_error2_("not implemented yet");
}
/*}}}*/

#ifdef _HAVE_RESPONSES_
/*FUNCTION Penta::IceVolume {{{*/
IssmDouble Penta::IceVolume(void){

	/*The volume of a troncated prism is base * 1/3 sum(length of edges)*/
	IssmDouble base,height;
	IssmDouble xyz_list[NUMVERTICES][3];

	if(IsOnWater())return 0;

	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);

	/*First calculate the area of the base (cross section triangle)
	 * http://en.wikipedia.org/wiki/Pentangle
	 * base = 1/2 abs((xA-xC)(yB-yA)-(xA-xB)(yC-yA))*/
	base = 1./2.*fabs((xyz_list[0][0]-xyz_list[2][0])*(xyz_list[1][1]-xyz_list[0][1]) - (xyz_list[0][0]-xyz_list[1][0])*(xyz_list[2][1]-xyz_list[0][1]));

	/*Now get the average height*/
	height = 1./3.*((xyz_list[3][2]-xyz_list[0][2])+(xyz_list[4][2]-xyz_list[1][2])+(xyz_list[5][2]-xyz_list[2][2]));

	/*Return: */
	return base*height;
}
/*}}}*/
/*FUNCTION Penta::MinVel{{{*/
void  Penta::MinVel(IssmDouble* pminvel, bool process_units){

	/*Get minimum:*/
	IssmDouble minvel=this->inputs->Min(VelEnum);

	/*process units if requested: */
	if(process_units) minvel=UnitConversion(minvel,IuToExtEnum,VelEnum);

	/*Assign output pointers:*/
	*pminvel=minvel;
}
/*}}}*/
/*FUNCTION Penta::MinVx{{{*/
void  Penta::MinVx(IssmDouble* pminvx, bool process_units){

	/*Get minimum:*/
	IssmDouble minvx=this->inputs->Min(VxEnum);

	/*process units if requested: */
	if(process_units) minvx=UnitConversion(minvx,IuToExtEnum,VxEnum);

	/*Assign output pointers:*/
	*pminvx=minvx;
}
/*}}}*/
/*FUNCTION Penta::MinVy{{{*/
void  Penta::MinVy(IssmDouble* pminvy, bool process_units){

	/*Get minimum:*/
	IssmDouble minvy=this->inputs->Min(VyEnum);

	/*process units if requested: */
	if(process_units) minvy=UnitConversion(minvy,IuToExtEnum,VyEnum);

	/*Assign output pointers:*/
	*pminvy=minvy;
}
/*}}}*/
/*FUNCTION Penta::MinVz{{{*/
void  Penta::MinVz(IssmDouble* pminvz, bool process_units){

	/*Get minimum:*/
	IssmDouble minvz=this->inputs->Min(VzEnum);

	/*process units if requested: */
	if(process_units) minvz=UnitConversion(minvz,IuToExtEnum,VzEnum);

	/*Assign output pointers:*/
	*pminvz=minvz;
}
/*}}}*/
/*FUNCTION Penta::MassFlux {{{*/
IssmDouble Penta::MassFlux( IssmDouble* segment,bool process_units){

	IssmDouble mass_flux=0;

	if(!IsOnBed()) return mass_flux;

	/*Depth Averaging Vx and Vy*/
	this->InputDepthAverageAtBase(VxEnum,VxAverageEnum);
	this->InputDepthAverageAtBase(VyEnum,VyAverageEnum);

	/*Spawn Tria element from the base of the Penta: */
	Tria* tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.
	mass_flux=tria->MassFlux(segment,process_units);
	delete tria->matice; delete tria;

	/*Delete Vx and Vy averaged*/
	this->inputs->DeleteInput(VxAverageEnum);
	this->inputs->DeleteInput(VyAverageEnum);

	/*clean up and return*/
	return mass_flux;
}
/*}}}*/
/*FUNCTION Penta::MaxAbsVx{{{*/
void  Penta::MaxAbsVx(IssmDouble* pmaxabsvx, bool process_units){

	/*Get maximum:*/
	IssmDouble maxabsvx=this->inputs->MaxAbs(VxEnum);

	/*process units if requested: */
	if(process_units) maxabsvx=UnitConversion(maxabsvx,IuToExtEnum,VxEnum);

	/*Assign output pointers:*/
	*pmaxabsvx=maxabsvx;
}
/*}}}*/
/*FUNCTION Penta::MaxAbsVy{{{*/
void  Penta::MaxAbsVy(IssmDouble* pmaxabsvy, bool process_units){

	/*Get maximum:*/
	IssmDouble maxabsvy=this->inputs->MaxAbs(VyEnum);

	/*process units if requested: */
	if(process_units) maxabsvy=UnitConversion(maxabsvy,IuToExtEnum,VyEnum);

	/*Assign output pointers:*/
	*pmaxabsvy=maxabsvy;
}
/*}}}*/
/*FUNCTION Penta::MaxAbsVz{{{*/
void  Penta::MaxAbsVz(IssmDouble* pmaxabsvz, bool process_units){

	/*Get maximum:*/
	IssmDouble maxabsvz=this->inputs->MaxAbs(VzEnum);

	/*process units if requested: */
	if(process_units) maxabsvz=UnitConversion(maxabsvz,IuToExtEnum,VyEnum);

	/*Assign output pointers:*/
	*pmaxabsvz=maxabsvz;
}
/*}}}*/
/*FUNCTION Penta::MaxVel{{{*/
void  Penta::MaxVel(IssmDouble* pmaxvel, bool process_units){

	/*Get maximum:*/
	IssmDouble maxvel=this->inputs->Max(VelEnum);

	/*process units if requested: */
	if(process_units) maxvel=UnitConversion(maxvel,IuToExtEnum,VelEnum);

	/*Assign output pointers:*/
	*pmaxvel=maxvel;

}
/*}}}*/
/*FUNCTION Penta::MaxVx{{{*/
void  Penta::MaxVx(IssmDouble* pmaxvx, bool process_units){

	/*Get maximum:*/
	IssmDouble maxvx=this->inputs->Max(VxEnum);

	/*process units if requested: */
	if(process_units) maxvx=UnitConversion(maxvx,IuToExtEnum,VxEnum);

	/*Assign output pointers:*/
	*pmaxvx=maxvx;
}
/*}}}*/
/*FUNCTION Penta::MaxVy{{{*/
void  Penta::MaxVy(IssmDouble* pmaxvy, bool process_units){

	/*Get maximum:*/
	IssmDouble maxvy=this->inputs->Max(VyEnum);

	/*process units if requested: */
	if(process_units) maxvy=UnitConversion(maxvy,IuToExtEnum,VyEnum);

	/*Assign output pointers:*/
	*pmaxvy=maxvy;
}
/*}}}*/
/*FUNCTION Penta::MaxVz{{{*/
void  Penta::MaxVz(IssmDouble* pmaxvz, bool process_units){

	/*Get maximum:*/
	IssmDouble maxvz=this->inputs->Max(VzEnum);

	/*process units if requested: */
	if(process_units) maxvz=UnitConversion(maxvz,IuToExtEnum,VzEnum);

	/*Assign output pointers:*/
	*pmaxvz=maxvz;
}
/*}}}*/
/*FUNCTION Penta::ElementResponse{{{*/
void Penta::ElementResponse(IssmDouble* presponse,int response_enum,bool process_units){

	switch(response_enum){
		case MaterialsRheologyBbarEnum:
			*presponse=this->matice->GetBbar();
			break;
		case VelEnum:

			/*Get input:*/
			IssmDouble vel;
			Input* vel_input;

			vel_input=this->inputs->GetInput(VelEnum); _assert_(vel_input);
			vel_input->GetInputAverage(&vel);

			/*process units if requested: */
			if(process_units) vel=UnitConversion(vel,IuToExtEnum,VelEnum);

			/*Assign output pointers:*/
			*presponse=vel;
		default:  
			_error2_("Response type " << EnumToStringx(response_enum) << " not supported yet!");
	}

}
/*}}}*/
/*FUNCTION Penta::TotalSmb {{{*/
IssmDouble Penta::TotalSmb(void){

	/*The smb[Gt yr-1] of one element is area[m2] * smb [ m ice yr^-1] * rho_ice [kg m-3] / 1e+10^12 */
	IssmDouble base,smb,rho_ice;
	IssmDouble Total_Smb=0;
	IssmDouble xyz_list[NUMVERTICES][3];

	/*Get material parameters :*/
	rho_ice=matpar->GetRhoIce();

	if(IsOnWater() || !IsOnSurface()) return 0.;
	
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);

	/*First calculate the area of the base (cross section triangle)
	 * http://en.wikipedia.org/wiki/Triangle
	 * base = 1/2 abs((xA-xC)(yB-yA)-(xA-xB)(yC-yA))*/
	base = 1./2. * fabs((xyz_list[0][0]-xyz_list[2][0])*(xyz_list[1][1]-xyz_list[0][1]) - (xyz_list[0][0]-xyz_list[1][0])*(xyz_list[2][1]-xyz_list[0][1]));

	/*Now get the average SMB over the element*/
	Input* smb_input = inputs->GetInput(SurfaceforcingsMassBalanceEnum); _assert_(smb_input);
	smb_input->GetInputAverage(&smb);
	Total_Smb=rho_ice*base*smb;// smb on element in kg s-1
	
	/*Process units: */
	Total_Smb=UnitConversion(Total_Smb,IuToExtEnum,TotalSmbEnum);// smb on element in GigaTon yr-1
	
	/*Return: */
	return Total_Smb;
}
/*}}}*/
#endif

#ifdef _HAVE_THERMAL_
/*FUNCTION Penta::CreateKMatrixEnthalpy {{{*/
ElementMatrix* Penta::CreateKMatrixEnthalpy(void){
	
	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixEnthalpyVolume();
	ElementMatrix* Ke2=CreateKMatrixEnthalpyShelf();
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2);
	
	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixEnthalpyVolume {{{*/
ElementMatrix* Penta::CreateKMatrixEnthalpyVolume(void){

	/*Constants*/
	const int    numdof=NDOF1*NUMVERTICES;

	/*Intermediaries */
	int        stabilization;
	int        i,j,ig,found=0;
	IssmDouble     Jdet,u,v,w,um,vm,wm;
	IssmDouble     h,hx,hy,hz,vx,vy,vz,vel;
	IssmDouble     gravity,rho_ice,rho_water;
	IssmDouble     epsvel=2.220446049250313e-16;
	IssmDouble     heatcapacity,thermalconductivity,dt;
	IssmDouble     pressure,enthalpy;
	IssmDouble     latentheat,kappa;
	IssmDouble     tau_parameter,diameter;
	IssmDouble     xyz_list[NUMVERTICES][3];
	IssmDouble     B_conduct[3][numdof];
	IssmDouble     B_advec[3][numdof];
	IssmDouble     Bprime_advec[3][numdof];
	IssmDouble     L[numdof];
	IssmDouble     dbasis[3][6];
	IssmDouble     D_scalar_conduct,D_scalar_advec;
	IssmDouble     D_scalar_trans,D_scalar_stab;
	IssmDouble     D[3][3];
	IssmDouble     K[3][3]={0.0};
	Tria*      tria=NULL;
	GaussPenta *gauss=NULL;

	/*Initialize Element matrix*/
	ElementMatrix* Ke=new ElementMatrix(nodes,NUMVERTICES,this->parameters,NoneApproximationEnum);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	rho_water=matpar->GetRhoWater();
	rho_ice=matpar->GetRhoIce();
	gravity=matpar->GetG();
	heatcapacity=matpar->GetHeatCapacity();
	latentheat=matpar->GetLatentHeat();
	thermalconductivity=matpar->GetThermalConductivity();
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	this->parameters->FindParam(&stabilization,ThermalStabilizationEnum);
	Input* pressure_input=inputs->GetInput(PressureEnum);      _assert_(pressure_input);
	Input* enthalpy_input=inputs->GetInput(EnthalpyPicardEnum);_assert_(enthalpy_input); //for this iteration of the step
	Input* vx_input=inputs->GetInput(VxEnum);                  _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);                  _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);                  _assert_(vz_input);
	Input* vxm_input=inputs->GetInput(VxMeshEnum);             _assert_(vxm_input);
	Input* vym_input=inputs->GetInput(VyMeshEnum);             _assert_(vym_input);
	Input* vzm_input=inputs->GetInput(VzMeshEnum);             _assert_(vzm_input);
	if (stabilization==2) diameter=MinEdgeLength(xyz_list);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(2,2);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);

		/*Conduction: */  
		/*Need to change that depending on enthalpy value -> cold or temperate ice: */  
		GetBConduct(&B_conduct[0][0],&xyz_list[0][0],gauss); 

		enthalpy_input->GetInputValue(&enthalpy, gauss);
		pressure_input->GetInputValue(&pressure, gauss);
		kappa=matpar->GetEnthalpyDiffusionParameter(enthalpy,pressure);
		D_scalar_conduct=gauss->weight*Jdet*kappa;
		if(dt) D_scalar_conduct=D_scalar_conduct*dt;

		D[0][0]=D_scalar_conduct; D[0][1]=0; D[0][2]=0;
		D[1][0]=0; D[1][1]=D_scalar_conduct; D[1][2]=0;
		D[2][0]=0; D[2][1]=0; D[2][2]=D_scalar_conduct;

		TripleMultiply(&B_conduct[0][0],3,numdof,1,
					&D[0][0],3,3,0,
					&B_conduct[0][0],3,numdof,0,
					&Ke->values[0],1);

		/*Advection: */
		GetBAdvec(&B_advec[0][0],&xyz_list[0][0],gauss); 
		GetBprimeAdvec(&Bprime_advec[0][0],&xyz_list[0][0],gauss); 

		vx_input->GetInputValue(&u, gauss); vxm_input->GetInputValue(&um,gauss); vx=u-um; 
		vy_input->GetInputValue(&v, gauss); vym_input->GetInputValue(&vm,gauss); vy=v-vm; 
		vz_input->GetInputValue(&w, gauss); vzm_input->GetInputValue(&wm,gauss); vz=w-wm;

		D_scalar_advec=gauss->weight*Jdet;
		if(dt) D_scalar_advec=D_scalar_advec*dt;

		D[0][0]=D_scalar_advec*vx;D[0][1]=0;                D[0][2]=0;
		D[1][0]=0;                D[1][1]=D_scalar_advec*vy;D[1][2]=0;
		D[2][0]=0;                D[2][1]=0;                D[2][2]=D_scalar_advec*vz;

		TripleMultiply(&B_advec[0][0],3,numdof,1,
					&D[0][0],3,3,0,
					&Bprime_advec[0][0],3,numdof,0,
					&Ke->values[0],1);

		/*Transient: */
		if(dt){
			GetNodalFunctionsP1(&L[0], gauss);
			D_scalar_trans=gauss->weight*Jdet;
			D_scalar_trans=D_scalar_trans;

			TripleMultiply(&L[0],numdof,1,0,
						&D_scalar_trans,1,1,0,
						&L[0],1,numdof,0,
						&Ke->values[0],1);
		}

		/*Artifficial diffusivity*/
		if(stabilization==1){
			/*Build K: */
			GetElementSizes(&hx,&hy,&hz);
			vel=sqrt(pow(vx,2.)+pow(vy,2.)+pow(vz,2.))+1.e-14;
			h=sqrt( pow(hx*vx/vel,2.) + pow(hy*vy/vel,2.) + pow(hz*vz/vel,2.));
			K[0][0]=h/(2*vel)*vx*vx;  K[0][1]=h/(2*vel)*vx*vy; K[0][2]=h/(2*vel)*vx*vz;
			K[1][0]=h/(2*vel)*vy*vx;  K[1][1]=h/(2*vel)*vy*vy; K[1][2]=h/(2*vel)*vy*vz;
			K[2][0]=h/(2*vel)*vz*vx;  K[2][1]=h/(2*vel)*vz*vy; K[2][2]=h/(2*vel)*vz*vz;
			D_scalar_stab=gauss->weight*Jdet;
			if(dt) D_scalar_stab=D_scalar_stab*dt;
			for(i=0;i<3;i++) for(j=0;j<3;j++) K[i][j] = D_scalar_stab*K[i][j];

			GetBprimeAdvec(&Bprime_advec[0][0],&xyz_list[0][0],gauss); 

			TripleMultiply(&Bprime_advec[0][0],3,numdof,1,
						&K[0][0],3,3,0,
						&Bprime_advec[0][0],3,numdof,0,
						&Ke->values[0],1);
		}
		else if(stabilization==2){
			GetNodalFunctionsP1Derivatives(&dbasis[0][0],&xyz_list[0][0], gauss);
			tau_parameter=GetStabilizationParameter(u-um,v-vm,w-wm,diameter,kappa);

			for(i=0;i<numdof;i++){
				for(j=0;j<numdof;j++){
					Ke->values[i*numdof+j]+=tau_parameter*D_scalar_advec*
					  ((u-um)*dbasis[0][i]+(v-vm)*dbasis[1][i]+(w-wm)*dbasis[2][i])*((u-um)*dbasis[0][j]+(v-vm)*dbasis[1][j]+(w-wm)*dbasis[2][j]);
				}
			}
			if(dt){
				for(i=0;i<numdof;i++){
					for(j=0;j<numdof;j++){
						Ke->values[i*numdof+j]+=tau_parameter*D_scalar_trans*L[j]*((u-um)*dbasis[0][i]+(v-vm)*dbasis[1][i]+(w-wm)*dbasis[2][i]);
					}
				}
			}
		}
	}

	/*Clean up and return*/
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixEnthalpyShelf {{{*/
ElementMatrix* Penta::CreateKMatrixEnthalpyShelf(void){

	/*Constants*/
	const int    numdof=NDOF1*NUMVERTICES;

	/*Intermediaries */
	int       i,j,ig;
	IssmDouble    mixed_layer_capacity,thermal_exchange_velocity;
	IssmDouble    rho_ice,rho_water,heatcapacity;
	IssmDouble    Jdet2d,dt;
	IssmDouble    xyz_list[NUMVERTICES][3];
	IssmDouble	 xyz_list_tria[NUMVERTICES2D][3];
	IssmDouble    basis[NUMVERTICES];
	IssmDouble    D_scalar;
	GaussPenta *gauss=NULL;

	/*Initialize Element matrix and return if necessary*/
	if (!IsOnBed() || !IsFloating()) return NULL;
	ElementMatrix* Ke=new ElementMatrix(nodes,NUMVERTICES,this->parameters,NoneApproximationEnum);

	/*Retrieve all inputs and parameters*/
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	mixed_layer_capacity=matpar->GetMixedLayerCapacity();
	thermal_exchange_velocity=matpar->GetThermalExchangeVelocity();
	rho_water=matpar->GetRhoWater();
	rho_ice=matpar->GetRhoIce();
	heatcapacity=matpar->GetHeatCapacity();
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;j++) xyz_list_tria[i][j]=xyz_list[i][j];

	/* Start looping on the number of gauss (nodes on the bedrock) */
	gauss=new GaussPenta(0,1,2,2);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);
		
		GetTriaJacobianDeterminant(&Jdet2d, &xyz_list_tria[0][0], gauss);
		GetNodalFunctionsP1(&basis[0], gauss);
				
		D_scalar=gauss->weight*Jdet2d*rho_water*mixed_layer_capacity*thermal_exchange_velocity/(rho_ice*heatcapacity);
		if(dt) D_scalar=dt*D_scalar;

		TripleMultiply(&basis[0],numdof,1,0,
					&D_scalar,1,1,0,
					&basis[0],1,numdof,0,
					&Ke->values[0],1);
	}
	
	/*Clean up and return*/
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixMelting {{{*/
ElementMatrix* Penta::CreateKMatrixMelting(void){

	if (!IsOnBed()) return NULL;

	Tria* tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.
	ElementMatrix* Ke=tria->CreateKMatrixMelting();

	delete tria->matice; delete tria;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixThermal {{{*/
ElementMatrix* Penta::CreateKMatrixThermal(void){
	
	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixThermalVolume();
	ElementMatrix* Ke2=CreateKMatrixThermalShelf();
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2);
	
	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixThermalVolume {{{*/
ElementMatrix* Penta::CreateKMatrixThermalVolume(void){

	/*Constants*/
	const int    numdof=NDOF1*NUMVERTICES;

	/*Intermediaries */
	int        stabilization;
	int        i,j,ig,found=0;
	IssmDouble     Jdet,u,v,w,um,vm,wm,vel;
	IssmDouble     h,hx,hy,hz,vx,vy,vz;
	IssmDouble     gravity,rho_ice,rho_water,kappa;
	IssmDouble     heatcapacity,thermalconductivity,dt;
	IssmDouble     tau_parameter,diameter;
	IssmDouble     xyz_list[NUMVERTICES][3];
	IssmDouble     B_conduct[3][numdof];
	IssmDouble     B_advec[3][numdof];
	IssmDouble     Bprime_advec[3][numdof];
	IssmDouble     L[numdof];
	IssmDouble     dbasis[3][6];
	IssmDouble     D_scalar_conduct,D_scalar_advec;
	IssmDouble     D_scalar_trans,D_scalar_stab;
	IssmDouble     D[3][3];
	IssmDouble     K[3][3]={0.0};
	Tria*      tria=NULL;
	GaussPenta *gauss=NULL;

	/*Initialize Element matrix*/
	ElementMatrix* Ke=new ElementMatrix(nodes,NUMVERTICES,this->parameters,NoneApproximationEnum);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	rho_water=matpar->GetRhoWater();
	rho_ice=matpar->GetRhoIce();
	gravity=matpar->GetG();
	heatcapacity=matpar->GetHeatCapacity();
	thermalconductivity=matpar->GetThermalConductivity();
	kappa=thermalconductivity/(rho_ice*heatcapacity);
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	this->parameters->FindParam(&stabilization,ThermalStabilizationEnum);
	Input* vx_input=inputs->GetInput(VxEnum);      _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);      _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);      _assert_(vz_input);
	Input* vxm_input=inputs->GetInput(VxMeshEnum); _assert_(vxm_input);
	Input* vym_input=inputs->GetInput(VyMeshEnum); _assert_(vym_input);
	Input* vzm_input=inputs->GetInput(VzMeshEnum); _assert_(vzm_input);
	if (stabilization==2) diameter=MinEdgeLength(xyz_list);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(2,2);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);

		/*Conduction: */

		GetBConduct(&B_conduct[0][0],&xyz_list[0][0],gauss); 

		D_scalar_conduct=gauss->weight*Jdet*kappa;
		if(dt) D_scalar_conduct=D_scalar_conduct*dt;

		D[0][0]=D_scalar_conduct; D[0][1]=0; D[0][2]=0;
		D[1][0]=0; D[1][1]=D_scalar_conduct; D[1][2]=0;
		D[2][0]=0; D[2][1]=0; D[2][2]=D_scalar_conduct;

		TripleMultiply(&B_conduct[0][0],3,numdof,1,
					&D[0][0],3,3,0,
					&B_conduct[0][0],3,numdof,0,
					&Ke->values[0],1);

		/*Advection: */

		GetBAdvec(&B_advec[0][0],&xyz_list[0][0],gauss); 
		GetBprimeAdvec(&Bprime_advec[0][0],&xyz_list[0][0],gauss); 

		vx_input->GetInputValue(&u, gauss); vxm_input->GetInputValue(&um,gauss); vx=u-um;
		vy_input->GetInputValue(&v, gauss); vym_input->GetInputValue(&vm,gauss); vy=v-vm;
		vz_input->GetInputValue(&w, gauss); vzm_input->GetInputValue(&wm,gauss); vz=w-wm;

		D_scalar_advec=gauss->weight*Jdet;
		if(dt) D_scalar_advec=D_scalar_advec*dt;

		D[0][0]=D_scalar_advec*vx;    D[0][1]=0;                    D[0][2]=0;
		D[1][0]=0;                    D[1][1]=D_scalar_advec*vy;    D[1][2]=0;
		D[2][0]=0;                    D[2][1]=0;                    D[2][2]=D_scalar_advec*vz;

		TripleMultiply(&B_advec[0][0],3,numdof,1,
					&D[0][0],3,3,0,
					&Bprime_advec[0][0],3,numdof,0,
					&Ke->values[0],1);

		/*Transient: */
		if(dt){
			GetNodalFunctionsP1(&L[0], gauss);
			D_scalar_trans=gauss->weight*Jdet;
			D_scalar_trans=D_scalar_trans;

			TripleMultiply(&L[0],numdof,1,0,
						&D_scalar_trans,1,1,0,
						&L[0],1,numdof,0,
						&Ke->values[0],1);
		}

		/*Artifficial diffusivity*/
		if(stabilization==1){
			/*Build K: */
			GetElementSizes(&hx,&hy,&hz);
			vel=sqrt(pow(vx,2.)+pow(vy,2.)+pow(vz,2.))+1.e-14;
			h=sqrt( pow(hx*vx/vel,2.) + pow(hy*vy/vel,2.) + pow(hz*vz/vel,2.));

			K[0][0]=h/(2*vel)*fabs(vx*vx);  K[0][1]=h/(2*vel)*fabs(vx*vy); K[0][2]=h/(2*vel)*fabs(vx*vz);
			K[1][0]=h/(2*vel)*fabs(vy*vx);  K[1][1]=h/(2*vel)*fabs(vy*vy); K[1][2]=h/(2*vel)*fabs(vy*vz);
			K[2][0]=h/(2*vel)*fabs(vz*vx);  K[2][1]=h/(2*vel)*fabs(vz*vy); K[2][2]=h/(2*vel)*fabs(vz*vz);

			D_scalar_stab=gauss->weight*Jdet;
			if(dt) D_scalar_stab=D_scalar_stab*dt;
			for(i=0;i<3;i++) for(j=0;j<3;j++) K[i][j] = D_scalar_stab*K[i][j];

			GetBprimeAdvec(&Bprime_advec[0][0],&xyz_list[0][0],gauss); 

			TripleMultiply(&Bprime_advec[0][0],3,numdof,1,
						&K[0][0],3,3,0,
						&Bprime_advec[0][0],3,numdof,0,
						&Ke->values[0],1);
		}
		else if(stabilization==2){
			GetNodalFunctionsP1Derivatives(&dbasis[0][0],&xyz_list[0][0], gauss);
			tau_parameter=GetStabilizationParameter(u-um,v-vm,w-wm,diameter,kappa);

			for(i=0;i<numdof;i++){
				for(j=0;j<numdof;j++){
					Ke->values[i*numdof+j]+=tau_parameter*D_scalar_advec*
					  ((u-um)*dbasis[0][i]+(v-vm)*dbasis[1][i]+(w-wm)*dbasis[2][i])*((u-um)*dbasis[0][j]+(v-vm)*dbasis[1][j]+(w-wm)*dbasis[2][j]);
				}
			}
			if(dt){
				for(i=0;i<numdof;i++){
					for(j=0;j<numdof;j++){
						Ke->values[i*numdof+j]+=tau_parameter*D_scalar_trans*L[j]*((u-um)*dbasis[0][i]+(v-vm)*dbasis[1][i]+(w-wm)*dbasis[2][i]);
					}
				}
			}
		}
	}

	/*Clean up and return*/
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixThermalShelf {{{*/
ElementMatrix* Penta::CreateKMatrixThermalShelf(void){


	/*Constants*/
	const int    numdof=NDOF1*NUMVERTICES;

	/*Intermediaries */
	int       i,j,ig;
	IssmDouble    mixed_layer_capacity,thermal_exchange_velocity;
	IssmDouble    rho_ice,rho_water,heatcapacity;
	IssmDouble    Jdet2d,dt;
	IssmDouble    xyz_list[NUMVERTICES][3];
	IssmDouble	 xyz_list_tria[NUMVERTICES2D][3];
	IssmDouble    basis[NUMVERTICES];
	IssmDouble    D_scalar;
	GaussPenta *gauss=NULL;

	/*Initialize Element matrix and return if necessary*/
	if (!IsOnBed() || !IsFloating()) return NULL;
	ElementMatrix* Ke=new ElementMatrix(nodes,NUMVERTICES,this->parameters,NoneApproximationEnum);

	/*Retrieve all inputs and parameters*/
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	mixed_layer_capacity=matpar->GetMixedLayerCapacity();
	thermal_exchange_velocity=matpar->GetThermalExchangeVelocity();
	rho_water=matpar->GetRhoWater();
	rho_ice=matpar->GetRhoIce();
	heatcapacity=matpar->GetHeatCapacity();
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;j++) xyz_list_tria[i][j]=xyz_list[i][j];

	/* Start looping on the number of gauss (nodes on the bedrock) */
	gauss=new GaussPenta(0,1,2,2);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);
		
		GetTriaJacobianDeterminant(&Jdet2d, &xyz_list_tria[0][0], gauss);
		GetNodalFunctionsP1(&basis[0], gauss);
				
		D_scalar=gauss->weight*Jdet2d*rho_water*mixed_layer_capacity*thermal_exchange_velocity/(heatcapacity*rho_ice);
		if(dt) D_scalar=dt*D_scalar;

		TripleMultiply(&basis[0],numdof,1,0,
					&D_scalar,1,1,0,
					&basis[0],1,numdof,0,
					&Ke->values[0],1);
	}
	
	/*Clean up and return*/
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorEnthalpy {{{*/
ElementVector* Penta::CreatePVectorEnthalpy(void){

	/*compute all load vectors for this element*/
	ElementVector* pe1=CreatePVectorEnthalpyVolume();
	ElementVector* pe2=CreatePVectorEnthalpySheet();
	ElementVector* pe3=CreatePVectorEnthalpyShelf();
	ElementVector* pe =new ElementVector(pe1,pe2,pe3);

	/*clean-up and return*/
	delete pe1;
	delete pe2;
	delete pe3;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorEnthalpyVolume {{{*/
ElementVector* Penta::CreatePVectorEnthalpyVolume(void){

	/*Constants*/
	const int  numdof=NUMVERTICES*NDOF1;

	/*Intermediaries*/
	int    i,j,ig,found=0;
	int    friction_type,stabilization;
	IssmDouble Jdet,phi,dt;
	IssmDouble rho_ice,heatcapacity;
	IssmDouble thermalconductivity,kappa;
	IssmDouble viscosity,pressure;
	IssmDouble enthalpy,enthalpypicard;
	IssmDouble tau_parameter,diameter;
	IssmDouble u,v,w;
	IssmDouble scalar_def,scalar_transient;
	IssmDouble temperature_list[NUMVERTICES];
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble L[numdof];
	IssmDouble dbasis[3][6];
	IssmDouble epsilon[6];
	GaussPenta *gauss=NULL;

	/*Initialize Element vector*/
	ElementVector* pe=new ElementVector(nodes,NUMVERTICES,this->parameters);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	rho_ice=matpar->GetRhoIce();
	heatcapacity=matpar->GetHeatCapacity();
	thermalconductivity=matpar->GetThermalConductivity();
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	this->parameters->FindParam(&stabilization,ThermalStabilizationEnum);
	Input* vx_input=inputs->GetInput(VxEnum);                                  _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);                                  _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);                                  _assert_(vz_input);
	Input* pressure_input=inputs->GetInput(PressureEnum);                      _assert_(pressure_input);
	Input* enthalpy_input=NULL; 
	Input* enthalpypicard_input=NULL; 
	if(dt){
		enthalpy_input=inputs->GetInput(EnthalpyEnum); _assert_(enthalpy_input);
	}
	if (stabilization==2){
		diameter=MinEdgeLength(xyz_list);
		enthalpypicard_input=inputs->GetInput(EnthalpyPicardEnum); _assert_(enthalpypicard_input);
	}

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(2,3);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctionsP1(&L[0], gauss);

		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		matice->GetViscosity3dStokes(&viscosity,&epsilon[0]);
		GetPhi(&phi, &epsilon[0], viscosity);

		scalar_def=phi/rho_ice*Jdet*gauss->weight;
		if(dt) scalar_def=scalar_def*dt;

		for(i=0;i<NUMVERTICES;i++)  pe->values[i]+=scalar_def*L[i];

		/* Build transient now */
		if(dt){
			enthalpy_input->GetInputValue(&enthalpy, gauss);
			scalar_transient=enthalpy*Jdet*gauss->weight;
			for(i=0;i<NUMVERTICES;i++)  pe->values[i]+=scalar_transient*L[i];
		}

		if(stabilization==2){
			GetNodalFunctionsP1Derivatives(&dbasis[0][0],&xyz_list[0][0], gauss);

			vx_input->GetInputValue(&u, gauss);
			vy_input->GetInputValue(&v, gauss);
			vz_input->GetInputValue(&w, gauss);
			pressure_input->GetInputValue(&pressure, gauss);
			enthalpypicard_input->GetInputValue(&enthalpypicard, gauss);
			kappa=matpar->GetEnthalpyDiffusionParameter(enthalpy,pressure);
			tau_parameter=GetStabilizationParameter(u,v,w,diameter,kappa);

			for(i=0;i<NUMVERTICES;i++)  pe->values[i]+=tau_parameter*scalar_def*(u*dbasis[0][i]+v*dbasis[1][i]+w*dbasis[2][i]);
			if(dt){
				for(i=0;i<NUMVERTICES;i++)  pe->values[i]+=tau_parameter*scalar_transient*(u*dbasis[0][i]+v*dbasis[1][i]+w*dbasis[2][i]);
			}
		}
	}

	/*Clean up and return*/
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorEnthalpyShelf {{{*/
ElementVector* Penta::CreatePVectorEnthalpyShelf(void){

	/*Constants*/
	const int  numdof=NUMVERTICES*NDOF1;

	/*Intermediaries */
	int        i,j,ig;
	IssmDouble     Jdet2d;
	IssmDouble     heatcapacity,h_pmp;
	IssmDouble     mixed_layer_capacity,thermal_exchange_velocity;
	IssmDouble     rho_ice,rho_water,pressure,dt,scalar_ocean;
	IssmDouble     xyz_list[NUMVERTICES][3];
	IssmDouble     xyz_list_tria[NUMVERTICES2D][3];
	IssmDouble     basis[NUMVERTICES];
	GaussPenta* gauss=NULL;

	/* Ice/ocean heat exchange flux on ice shelf base */
	if (!IsOnBed() || !IsFloating()) return NULL;

	/*Initialize Element vector*/
	ElementVector* pe=new ElementVector(nodes,NUMVERTICES,this->parameters);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;j++) xyz_list_tria[i][j]=xyz_list[i][j];
	mixed_layer_capacity=matpar->GetMixedLayerCapacity();
	thermal_exchange_velocity=matpar->GetThermalExchangeVelocity();
	rho_water=matpar->GetRhoWater();
	rho_ice=matpar->GetRhoIce();
	heatcapacity=matpar->GetHeatCapacity();
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	Input* pressure_input=inputs->GetInput(PressureEnum); _assert_(pressure_input);

	/* Start looping on the number of gauss 2d (nodes on the bedrock) */
	gauss=new GaussPenta(0,1,2,2);
	for(ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetTriaJacobianDeterminant(&Jdet2d, &xyz_list_tria[0][0], gauss);
		GetNodalFunctionsP1(&basis[0], gauss);

		pressure_input->GetInputValue(&pressure,gauss);
		h_pmp=matpar->PureIceEnthalpy(pressure);

		scalar_ocean=gauss->weight*Jdet2d*rho_water*mixed_layer_capacity*thermal_exchange_velocity*(h_pmp)/(rho_ice*heatcapacity);
		if(dt) scalar_ocean=dt*scalar_ocean;

		for(i=0;i<numdof;i++) pe->values[i]+=scalar_ocean*basis[i];
	}

	/*Clean up and return*/
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorEnthalpySheet {{{*/
ElementVector* Penta::CreatePVectorEnthalpySheet(void){

	/*Constants*/
	const int  numdof=NUMVERTICES*NDOF1;

	/*Intermediaries */
	int        i,j,ig;
	int        analysis_type;
	IssmDouble     xyz_list[NUMVERTICES][3];
	IssmDouble     xyz_list_tria[NUMVERTICES2D][3]={0.0};
	IssmDouble     Jdet2d,dt;
	IssmDouble     rho_ice,heatcapacity,geothermalflux_value;
	IssmDouble     basalfriction,alpha2,vx,vy;
	IssmDouble     scalar,enthalpy,enthalpyup;
	IssmDouble     pressure,pressureup;
	IssmDouble     basis[NUMVERTICES];
	Friction*  friction=NULL;
	GaussPenta* gauss=NULL;
	GaussPenta* gaussup=NULL;

	/* Geothermal flux on ice sheet base and basal friction */
	if (!IsOnBed() || IsFloating()) return NULL;

	/*Initialize Element vector*/
	ElementVector* pe=new ElementVector(nodes,NUMVERTICES,this->parameters);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<2;j++) xyz_list_tria[i][j]=xyz_list[i][j];
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	rho_ice=matpar->GetRhoIce();
	heatcapacity=matpar->GetHeatCapacity();
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	Input* vx_input=inputs->GetInput(VxEnum);                         _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);                         _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);                         _assert_(vz_input);
	Input* enthalpy_input=inputs->GetInput(EnthalpyEnum);             _assert_(enthalpy_input);
	Input* pressure_input=inputs->GetInput(PressureEnum);             _assert_(pressure_input);
	Input* geothermalflux_input=inputs->GetInput(BasalforcingsGeothermalfluxEnum); _assert_(geothermalflux_input);

	/*Build frictoin element, needed later: */
	friction=new Friction("3d",inputs,matpar,analysis_type);

	/* Start looping on the number of gauss 2d (nodes on the bedrock) */
	gauss=new GaussPenta(0,1,2,2);
	gaussup=new GaussPenta(3,4,5,2);
	for(ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);
		gaussup->GaussPoint(ig);

		GetTriaJacobianDeterminant(&Jdet2d, &xyz_list_tria[0][0], gauss);
		GetNodalFunctionsP1(&basis[0], gauss);

		enthalpy_input->GetInputValue(&enthalpy,gauss);
		pressure_input->GetInputValue(&pressure,gauss);
//		if(enthalpy>matpar->PureIceEnthalpy(pressure)){
//			enthalpy_input->GetInputValue(&enthalpyup,gaussup);
//			pressure_input->GetInputValue(&pressureup,gaussup);
//			if(enthalpyup>matpar->PureIceEnthalpy(pressureup)){
//				//do nothing, don't add heatflux
//			}
//			else{
//				//need to change spcenthalpy according to Aschwanden 
//				//NEED TO UPDATE
//			}
//		}
//		else{
			geothermalflux_input->GetInputValue(&geothermalflux_value,gauss);
			friction->GetAlpha2(&alpha2,gauss,VxEnum,VyEnum,VzEnum);
			vx_input->GetInputValue(&vx,gauss);
			vy_input->GetInputValue(&vy,gauss);
			basalfriction=alpha2*(pow(vx,2.0)+pow(vy,2.0));

			scalar=gauss->weight*Jdet2d*(basalfriction+geothermalflux_value)/(rho_ice);
			if(dt) scalar=dt*scalar;

			for(i=0;i<numdof;i++) pe->values[i]+=scalar*basis[i];
//		}
	}

	/*Clean up and return*/
	delete gauss;
	delete gaussup;
	delete friction;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorMelting {{{*/
ElementVector* Penta::CreatePVectorMelting(void){
	return NULL;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorThermal {{{*/
ElementVector* Penta::CreatePVectorThermal(void){

	/*compute all load vectors for this element*/
	ElementVector* pe1=CreatePVectorThermalVolume();
	ElementVector* pe2=CreatePVectorThermalSheet();
	ElementVector* pe3=CreatePVectorThermalShelf();
	ElementVector* pe =new ElementVector(pe1,pe2,pe3);

	/*clean-up and return*/
	delete pe1;
	delete pe2;
	delete pe3;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorThermalVolume {{{*/
ElementVector* Penta::CreatePVectorThermalVolume(void){

	/*Constants*/
	const int  numdof=NUMVERTICES*NDOF1;

	/*Intermediaries*/
	int    i,j,ig,found=0;
	int    friction_type,stabilization;
	IssmDouble Jdet,phi,dt;
	IssmDouble rho_ice,heatcapacity;
	IssmDouble thermalconductivity,kappa;
	IssmDouble viscosity,temperature;
	IssmDouble tau_parameter,diameter;
	IssmDouble u,v,w;
	IssmDouble scalar_def,scalar_transient;
	IssmDouble temperature_list[NUMVERTICES];
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble L[numdof];
	IssmDouble dbasis[3][6];
	IssmDouble epsilon[6];
	GaussPenta *gauss=NULL;

	/*Initialize Element vector*/
	ElementVector* pe=new ElementVector(nodes,NUMVERTICES,this->parameters);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	rho_ice=matpar->GetRhoIce();
	heatcapacity=matpar->GetHeatCapacity();
	thermalconductivity=matpar->GetThermalConductivity();
	kappa=thermalconductivity/(rho_ice*heatcapacity);
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	this->parameters->FindParam(&stabilization,ThermalStabilizationEnum);
	Input* vx_input=inputs->GetInput(VxEnum); _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum); _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum); _assert_(vz_input);
	Input* temperature_input=NULL;
	if (dt) temperature_input=inputs->GetInput(TemperatureEnum); _assert_(inputs);
	if (stabilization==2) diameter=MinEdgeLength(xyz_list);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(2,3);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctionsP1(&L[0], gauss);

		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		matice->GetViscosity3dStokes(&viscosity,&epsilon[0]);
		GetPhi(&phi, &epsilon[0], viscosity);

		scalar_def=phi/(rho_ice*heatcapacity)*Jdet*gauss->weight;
		if(dt) scalar_def=scalar_def*dt;

		for(i=0;i<NUMVERTICES;i++)  pe->values[i]+=scalar_def*L[i];

		/* Build transient now */
		if(dt){
			temperature_input->GetInputValue(&temperature, gauss);
			scalar_transient=temperature*Jdet*gauss->weight;
			for(i=0;i<NUMVERTICES;i++)  pe->values[i]+=scalar_transient*L[i];
		}

		if(stabilization==2){
			GetNodalFunctionsP1Derivatives(&dbasis[0][0],&xyz_list[0][0], gauss);

			vx_input->GetInputValue(&u, gauss);
			vy_input->GetInputValue(&v, gauss);
			vz_input->GetInputValue(&w, gauss);

			tau_parameter=GetStabilizationParameter(u,v,w,diameter,kappa);

			for(i=0;i<NUMVERTICES;i++)  pe->values[i]+=tau_parameter*scalar_def*(u*dbasis[0][i]+v*dbasis[1][i]+w*dbasis[2][i]);
			if(dt){
				for(i=0;i<NUMVERTICES;i++)  pe->values[i]+=tau_parameter*scalar_transient*(u*dbasis[0][i]+v*dbasis[1][i]+w*dbasis[2][i]);
			}
		}
	}

	/*Clean up and return*/
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorThermalShelf {{{*/
ElementVector* Penta::CreatePVectorThermalShelf(void){

	/*Constants*/
	const int  numdof=NUMVERTICES*NDOF1;

	/*Intermediaries */
	int        i,j,ig;
	IssmDouble     Jdet2d;
	IssmDouble     mixed_layer_capacity,thermal_exchange_velocity;
	IssmDouble     rho_ice,rho_water,pressure,dt,scalar_ocean;
	IssmDouble     heatcapacity,t_pmp;
	IssmDouble     xyz_list[NUMVERTICES][3];
	IssmDouble     xyz_list_tria[NUMVERTICES2D][3];
	IssmDouble     basis[NUMVERTICES];
	GaussPenta* gauss=NULL;

	/* Ice/ocean heat exchange flux on ice shelf base */
	if (!IsOnBed() || !IsFloating()) return NULL;

	/*Initialize Element vector*/
	ElementVector* pe=new ElementVector(nodes,NUMVERTICES,this->parameters);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;j++) xyz_list_tria[i][j]=xyz_list[i][j];
	mixed_layer_capacity=matpar->GetMixedLayerCapacity();
	thermal_exchange_velocity=matpar->GetThermalExchangeVelocity();
	rho_water=matpar->GetRhoWater();
	rho_ice=matpar->GetRhoIce();
	heatcapacity=matpar->GetHeatCapacity();
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	Input* pressure_input=inputs->GetInput(PressureEnum); _assert_(pressure_input);

	/* Start looping on the number of gauss 2d (nodes on the bedrock) */
	gauss=new GaussPenta(0,1,2,2);
	for(ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetTriaJacobianDeterminant(&Jdet2d, &xyz_list_tria[0][0], gauss);
		GetNodalFunctionsP1(&basis[0], gauss);

		pressure_input->GetInputValue(&pressure,gauss);
		t_pmp=matpar->TMeltingPoint(pressure);

		scalar_ocean=gauss->weight*Jdet2d*rho_water*mixed_layer_capacity*thermal_exchange_velocity*(t_pmp)/(heatcapacity*rho_ice);
		if(dt) scalar_ocean=dt*scalar_ocean;

		for(i=0;i<numdof;i++) pe->values[i]+=scalar_ocean*basis[i];
	}

	/*Clean up and return*/
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorThermalSheet {{{*/
ElementVector* Penta::CreatePVectorThermalSheet(void){

	/*Constants*/
	const int  numdof=NUMVERTICES*NDOF1;

	/*Intermediaries */
	int        i,j,ig;
	int        analysis_type;
	IssmDouble     xyz_list[NUMVERTICES][3];
	IssmDouble     xyz_list_tria[NUMVERTICES2D][3]={0.0};
	IssmDouble     Jdet2d,dt;
	IssmDouble     rho_ice,heatcapacity,geothermalflux_value;
	IssmDouble     basalfriction,alpha2,vx,vy;
	IssmDouble     basis[NUMVERTICES];
	IssmDouble     scalar;
	Friction*  friction=NULL;
	GaussPenta* gauss=NULL;

	/* Geothermal flux on ice sheet base and basal friction */
	if (!IsOnBed() || IsFloating()) return NULL;

	/*Initialize Element vector*/
	ElementVector* pe=new ElementVector(nodes,NUMVERTICES,this->parameters);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<2;j++) xyz_list_tria[i][j]=xyz_list[i][j];
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	rho_ice=matpar->GetRhoIce();
	heatcapacity=matpar->GetHeatCapacity();
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	Input* vx_input=inputs->GetInput(VxEnum);                         _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);                         _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);                         _assert_(vz_input);
	Input* geothermalflux_input=inputs->GetInput(BasalforcingsGeothermalfluxEnum); _assert_(geothermalflux_input);

	/*Build frictoin element, needed later: */
	friction=new Friction("3d",inputs,matpar,analysis_type);

	/* Start looping on the number of gauss 2d (nodes on the bedrock) */
	gauss=new GaussPenta(0,1,2,2);
	for(ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetTriaJacobianDeterminant(&Jdet2d, &xyz_list_tria[0][0], gauss);
		GetNodalFunctionsP1(&basis[0], gauss);

			geothermalflux_input->GetInputValue(&geothermalflux_value,gauss);
			friction->GetAlpha2(&alpha2,gauss,VxEnum,VyEnum,VzEnum);
			vx_input->GetInputValue(&vx,gauss);
			vy_input->GetInputValue(&vy,gauss);
			basalfriction=alpha2*(pow(vx,2.0)+pow(vy,2.0));

			scalar=gauss->weight*Jdet2d*(basalfriction+geothermalflux_value)/(heatcapacity*rho_ice);
			if(dt) scalar=dt*scalar;

			for(i=0;i<numdof;i++) pe->values[i]+=scalar*basis[i];
	}

	/*Clean up and return*/
	delete gauss;
	delete friction;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::GetSolutionFromInputsThermal{{{*/
void  Penta::GetSolutionFromInputsThermal(Vector* solution){

	const int    numdof=NDOF1*NUMVERTICES;

	int          i;
	int*         doflist=NULL;
	IssmDouble       values[numdof];
	IssmDouble       temp;
	GaussPenta   *gauss=NULL;

	/*Get dof list: */
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);
	Input* t_input=inputs->GetInput(TemperatureEnum); _assert_(t_input);

	gauss=new GaussPenta();
	for(i=0;i<NUMVERTICES;i++){
		/*Recover temperature*/
		gauss->GaussVertex(i);
		t_input->GetInputValue(&temp,gauss);
		values[i]=temp;
	}

	/*Add value to global vector*/
	solution->SetValues(numdof,doflist,values,INS_VAL);

	/*Free ressources:*/
	delete gauss;
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Penta::GetSolutionFromInputsEnthalpy{{{*/
void  Penta::GetSolutionFromInputsEnthalpy(Vector* solution){

	const int    numdof=NDOF1*NUMVERTICES;

	int          i;
	int*         doflist=NULL;
	IssmDouble       values[numdof];
	IssmDouble       enthalpy;
	GaussPenta   *gauss=NULL;

	/*Get dof list: */
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);
	Input* h_input=inputs->GetInput(EnthalpyEnum); _assert_(h_input);

	gauss=new GaussPenta();
	for(i=0;i<NUMVERTICES;i++){
		/*Recover temperature*/
		gauss->GaussVertex(i);
		h_input->GetInputValue(&enthalpy,gauss);
		values[i]=enthalpy;
	}

	/*Add value to global vector*/
	solution->SetValues(numdof,doflist,values,INS_VAL);

	/*Free ressources:*/
	delete gauss;
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionThermal {{{*/
void  Penta::InputUpdateFromSolutionThermal(IssmDouble* solution){

	const int    numdof=NDOF1*NUMVERTICES;

	bool   converged;
	int    i,rheology_law;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble values[numdof];
	IssmDouble B[numdof];
	IssmDouble B_average,s_average;
	int*   doflist=NULL;
	//IssmDouble pressure[numdof];

	/*Get dof list: */
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);

	/*Use the dof list to index into the solution vector: */
	for(i=0;i<numdof;i++){
		values[i]=solution[doflist[i]];
		//GetInputListOnVertices(&pressure[0],PressureEnum);
		//if(values[i]>matpar->TMeltingPoint(pressure[i])) values[i]=matpar->TMeltingPoint(pressure[i]);
		//if(values[i]<matpar->TMeltingPoint(pressure[i])-50) values[i]=matpar->TMeltingPoint(pressure[i])-50;

		/*Check solution*/
		if(xIsNan<IssmDouble>(values[i])) _error2_("NaN found in solution vector");
		//if(values[i]<0)      _printLine_("temperature < 0°K found in solution vector");
		//if(values[i]>275)    _printLine_("temperature > 275°K found in solution vector (Paterson's rheology associated is negative)");
	}

	/*Get all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	Input* surface_input=inputs->GetInput(SurfaceEnum); _assert_(surface_input);

	this->inputs->GetInputValue(&converged,ConvergedEnum);
	if(converged){
		this->inputs->AddInput(new PentaP1Input(TemperatureEnum,values));

		/*Update Rheology only if converged (we must make sure that the temperature is below melting point
		 * otherwise the rheology could be negative*/
		this->parameters->FindParam(&rheology_law,MaterialsRheologyLawEnum);
		switch(rheology_law){
			case NoneEnum:
				/*Do nothing: B is not temperature dependent*/
				break;
			case PatersonEnum:
				B_average=Paterson((values[0]+values[1]+values[2]+values[3]+values[4]+values[5])/6.0);
				for(i=0;i<numdof;i++) B[i]=B_average;
				this->matice->inputs->AddInput(new PentaP1Input(MaterialsRheologyBEnum,B));
				break;
			case ArrheniusEnum:
				surface_input->GetInputAverage(&s_average);
				B_average=Arrhenius((values[0]+values[1]+values[2]+values[3]+values[4]+values[5])/6.0,
							s_average-((xyz_list[0][2]+xyz_list[1][2]+xyz_list[2][2]+xyz_list[3][2]+xyz_list[4][2]+xyz_list[5][2])/6.0),
							matice->GetN());
				for(i=0;i<numdof;i++) B[i]=B_average;
				this->matice->inputs->AddInput(new PentaP1Input(MaterialsRheologyBEnum,B));
				break;
			default:
				_error2_("Rheology law " << EnumToStringx(rheology_law) << " not supported yet");

		}
	}
	else{
		this->inputs->AddInput(new PentaP1Input(TemperaturePicardEnum,values));
	}

	/*Free ressources:*/
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionEnthalpy {{{*/
void  Penta::InputUpdateFromSolutionEnthalpy(IssmDouble* solution){

	const int    numdof=NDOF1*NUMVERTICES;

	bool   converged=false;
	int    i,rheology_law;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble values[numdof];
	IssmDouble pressure[NUMVERTICES];
	IssmDouble temperatures[numdof];
	IssmDouble waterfraction[numdof];
	IssmDouble B[numdof];
	IssmDouble B_average,s_average;
	int*   doflist=NULL;

	/*Get dof list: */
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);

	/*Use the dof list to index into the solution vector: */
	for(i=0;i<numdof;i++){
		values[i]=solution[doflist[i]];

		/*Check solution*/
		if(xIsNan<IssmDouble>(values[i])) _error2_("NaN found in solution vector");
	}

	/*Get all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	GetInputListOnVertices(&pressure[0],PressureEnum);
	Input* surface_input=inputs->GetInput(SurfaceEnum); _assert_(surface_input);
	
	this->inputs->GetInputValue(&converged,ConvergedEnum);
	if(converged){
		/*Convert enthalpy into temperature and water fraction*/
		for(i=0;i<numdof;i++){
			matpar->EnthalpyToThermal(&temperatures[i],&waterfraction[i],values[i],pressure[i]);
			if(waterfraction[i]<0) _error2_("Negative water fraction found in solution vector");
			//if(waterfraction[i]>1) _error2_("Water fraction >1 found in solution vector");
		}
			
		this->inputs->AddInput(new PentaP1Input(EnthalpyEnum,values));
		this->inputs->AddInput(new PentaP1Input(WaterfractionEnum,waterfraction));
		this->inputs->AddInput(new PentaP1Input(TemperatureEnum,temperatures));

		/*Update Rheology only if converged (we must make sure that the temperature is below melting point
		 * otherwise the rheology could be negative*/
		this->parameters->FindParam(&rheology_law,MaterialsRheologyLawEnum);
		switch(rheology_law){
			case NoneEnum:
				/*Do nothing: B is not temperature dependent*/
				break;
			case PatersonEnum:
				B_average=Paterson((temperatures[0]+temperatures[1]+temperatures[2]+temperatures[3]+temperatures[4]+temperatures[5])/6.0);
				for(i=0;i<numdof;i++) B[i]=B_average;
				this->matice->inputs->AddInput(new PentaP1Input(MaterialsRheologyBEnum,B));
				break;
			case ArrheniusEnum:
				surface_input->GetInputAverage(&s_average);
				B_average=Arrhenius((temperatures[0]+temperatures[1]+temperatures[2]+temperatures[3]+temperatures[4]+temperatures[5])/6.0,
							s_average-((xyz_list[0][2]+xyz_list[1][2]+xyz_list[2][2]+xyz_list[3][2]+xyz_list[4][2]+xyz_list[5][2])/6.0),
							matice->GetN());
				for(i=0;i<numdof;i++) B[i]=B_average;
				this->matice->inputs->AddInput(new PentaP1Input(MaterialsRheologyBEnum,B));
				break;
			default:
				_error2_("Rheology law " << EnumToStringx(rheology_law) << " not supported yet");

		}
	}
	else{
		this->inputs->AddInput(new PentaP1Input(EnthalpyPicardEnum,values));
	}

	/*Free ressources:*/
	xDelete<int>(doflist);
}
/*}}}*/
#endif

#ifdef _HAVE_CONTROL_
/*FUNCTION Penta::ControlInputGetGradient{{{*/
void Penta::ControlInputGetGradient(Vector* gradient,int enum_type,int control_index){

	int doflist1[NUMVERTICES];
	Input* input=NULL;

	if(enum_type==MaterialsRheologyBbarEnum){
		if(!IsOnBed()) return;
		input=(Input*)matice->inputs->GetInput(MaterialsRheologyBEnum);
	}
	else{
		input=inputs->GetInput(enum_type);
	}
	if (!input) _error2_("Input " << EnumToStringx(enum_type) << " not found");
	if (input->ObjectEnum()!=ControlInputEnum) _error2_("Input " << EnumToStringx(enum_type) << " is not a ControlInput");

	GradientIndexing(&doflist1[0],control_index);
	((ControlInput*)input)->GetGradient(gradient,&doflist1[0]);

}/*}}}*/
/*FUNCTION Penta::ControlInputScaleGradient{{{*/
void Penta::ControlInputScaleGradient(int enum_type,IssmDouble scale){

	Input* input=NULL;

	if(enum_type==MaterialsRheologyBbarEnum){
		input=(Input*)matice->inputs->GetInput(MaterialsRheologyBEnum);
	}
	else{
		input=inputs->GetInput(enum_type);
	}
	if (!input) _error2_("Input " << EnumToStringx(enum_type) << " not found");
	if (input->ObjectEnum()!=ControlInputEnum) _error2_("Input " << EnumToStringx(enum_type) << " is not a ControlInput");

	((ControlInput*)input)->ScaleGradient(scale);
}/*}}}*/
/*FUNCTION Penta::ControlInputSetGradient{{{*/
void Penta::ControlInputSetGradient(IssmDouble* gradient,int enum_type,int control_index){

	int    doflist1[NUMVERTICES];
	IssmDouble grad_list[NUMVERTICES];
	Input* grad_input=NULL;
	Input* input=NULL;

	if(enum_type==MaterialsRheologyBbarEnum){
		input=(Input*)matice->inputs->GetInput(MaterialsRheologyBEnum);
	}
	else{
		input=inputs->GetInput(enum_type);
	}
	if (!input) _error2_("Input " << EnumToStringx(enum_type) << " not found");
	if (input->ObjectEnum()!=ControlInputEnum) _error2_("Input " << EnumToStringx(enum_type) << " is not a ControlInput");

	GradientIndexing(&doflist1[0],control_index);
	for(int i=0;i<NUMVERTICES;i++) grad_list[i]=gradient[doflist1[i]];
	grad_input=new PentaP1Input(GradientEnum,grad_list);
	((ControlInput*)input)->SetGradient(grad_input);

}/*}}}*/
/*FUNCTION Penta::CreateKMatrixAdjointHoriz{{{*/
ElementMatrix* Penta::CreateKMatrixAdjointHoriz(void){

	int approximation;
	inputs->GetInputValue(&approximation,ApproximationEnum);

	switch(approximation){
		case MacAyealApproximationEnum:
			return CreateKMatrixAdjointMacAyeal2d();
		case PattynApproximationEnum:
			return CreateKMatrixAdjointPattyn();
		case StokesApproximationEnum:
			return CreateKMatrixAdjointStokes();
		case NoneApproximationEnum:
			return NULL;
		default:
			_error2_("Approximation " << EnumToStringx(approximation) << " not supported yet");
	}
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixAdjointMacAyeal2d{{{*/
ElementMatrix* Penta::CreateKMatrixAdjointMacAyeal2d(void){

	/*Figure out if this penta is collapsed. If so, then bailout, except if it is at the 
	  bedrock, in which case we spawn a tria element using the 3 first nodes, and use it to build 
	  the stiffness matrix. */
	if (!IsOnBed()) return NULL;

	/*Depth Averaging B*/
	this->InputDepthAverageAtBase(MaterialsRheologyBEnum,MaterialsRheologyBbarEnum,MaterialsEnum);

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.
	ElementMatrix* Ke=tria->CreateKMatrixAdjointMacAyeal();
	delete tria->matice; delete tria;

	/*Delete B averaged*/
	this->matice->inputs->DeleteInput(MaterialsRheologyBbarEnum);

	/*clean up and return*/
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixAdjointPattyn{{{*/
ElementMatrix* Penta::CreateKMatrixAdjointPattyn(void){

	/*Constants*/
	const int    numdof=NDOF2*NUMVERTICES;

	/*Intermediaries */
	int        i,j,ig;
	bool       incomplete_adjoint;
	IssmDouble     xyz_list[NUMVERTICES][3];
	IssmDouble     Jdet;
	IssmDouble     eps1dotdphii,eps1dotdphij;
	IssmDouble     eps2dotdphii,eps2dotdphij;
	IssmDouble     mu_prime;
	IssmDouble     epsilon[5]; /* epsilon=[exx,eyy,exy,exz,eyz];*/
	IssmDouble     eps1[3],eps2[3];
	IssmDouble     phi[NUMVERTICES];
	IssmDouble     dphi[3][NUMVERTICES];
	GaussPenta *gauss=NULL;

	/*Initialize Jacobian with regular Pattyn (first part of the Gateau derivative)*/
	parameters->FindParam(&incomplete_adjoint,InversionIncompleteAdjointEnum);
	ElementMatrix* Ke=CreateKMatrixDiagnosticPattyn();
	if(incomplete_adjoint) return Ke;

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	Input* vx_input=inputs->GetInput(VxEnum);       _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);       _assert_(vy_input);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(5,5);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctionsP1Derivatives(&dphi[0][0],&xyz_list[0][0],gauss);

		this->GetStrainRate3dPattyn(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
		matice->GetViscosityDerivativeEpsSquare(&mu_prime,&epsilon[0]);
		eps1[0]=2*epsilon[0]+epsilon[1];   eps2[0]=epsilon[2];
		eps1[1]=epsilon[2];                eps2[1]=epsilon[0]+2*epsilon[1];
		eps1[2]=epsilon[3];                eps2[2]=epsilon[4];

		for(i=0;i<6;i++){
			for(j=0;j<6;j++){
				eps1dotdphii=eps1[0]*dphi[0][i]+eps1[1]*dphi[1][i]+eps1[2]*dphi[2][i];
				eps1dotdphij=eps1[0]*dphi[0][j]+eps1[1]*dphi[1][j]+eps1[2]*dphi[2][j];
				eps2dotdphii=eps2[0]*dphi[0][i]+eps2[1]*dphi[1][i]+eps2[2]*dphi[2][i];
				eps2dotdphij=eps2[0]*dphi[0][j]+eps2[1]*dphi[1][j]+eps2[2]*dphi[2][j];

				Ke->values[12*(2*i+0)+2*j+0]+=gauss->weight*Jdet*2*mu_prime*eps1dotdphij*eps1dotdphii;
				Ke->values[12*(2*i+0)+2*j+1]+=gauss->weight*Jdet*2*mu_prime*eps2dotdphij*eps1dotdphii;
				Ke->values[12*(2*i+1)+2*j+0]+=gauss->weight*Jdet*2*mu_prime*eps1dotdphij*eps2dotdphii;
				Ke->values[12*(2*i+1)+2*j+1]+=gauss->weight*Jdet*2*mu_prime*eps2dotdphij*eps2dotdphii;
			}
		}
	}

	/*Transform Coordinate System*/
	TransformStiffnessMatrixCoord(Ke,nodes,NUMVERTICES,XYEnum);

	/*Clean up and return*/
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixAdjointStokes{{{*/
ElementMatrix* Penta::CreateKMatrixAdjointStokes(void){

	/*Constants*/
	const int    numdof=NDOF4*NUMVERTICES;

	/*Intermediaries */
	int        i,j,ig;
	bool       incomplete_adjoint;
	IssmDouble     xyz_list[NUMVERTICES][3];
	IssmDouble     Jdet;
	IssmDouble     eps1dotdphii,eps1dotdphij;
	IssmDouble     eps2dotdphii,eps2dotdphij;
	IssmDouble     eps3dotdphii,eps3dotdphij;
	IssmDouble     mu_prime;
	IssmDouble     epsilon[5]; /* epsilon=[exx,eyy,exy,exz,eyz];*/
	IssmDouble     eps1[3],eps2[3],eps3[3];
	IssmDouble     phi[NUMVERTICES];
	IssmDouble     dphi[3][NUMVERTICES];
	GaussPenta *gauss=NULL;

	/*Initialize Jacobian with regular Stokes (first part of the Gateau derivative)*/
	parameters->FindParam(&incomplete_adjoint,InversionIncompleteAdjointEnum);
	ElementMatrix* Ke=CreateKMatrixDiagnosticStokes();
	if(incomplete_adjoint) return Ke;

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	Input* vx_input=inputs->GetInput(VxEnum);       _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);       _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);       _assert_(vz_input);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(5,5);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctionsP1Derivatives(&dphi[0][0],&xyz_list[0][0],gauss);

		this->GetStrainRate3dPattyn(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
		matice->GetViscosityDerivativeEpsSquare(&mu_prime,&epsilon[0]);
		eps1[0]=epsilon[0];   eps2[0]=epsilon[2];   eps3[0]=epsilon[3];
		eps1[1]=epsilon[2];   eps2[1]=epsilon[1];   eps3[1]=epsilon[4];
		eps1[2]=epsilon[3];   eps2[2]=epsilon[4];   eps3[2]= -epsilon[0] -epsilon[1];

		for(i=0;i<6;i++){
			for(j=0;j<6;j++){
				eps1dotdphii=eps1[0]*dphi[0][i]+eps1[1]*dphi[1][i]+eps1[2]*dphi[2][i];
				eps1dotdphij=eps1[0]*dphi[0][j]+eps1[1]*dphi[1][j]+eps1[2]*dphi[2][j];
				eps2dotdphii=eps2[0]*dphi[0][i]+eps2[1]*dphi[1][i]+eps2[2]*dphi[2][i];
				eps2dotdphij=eps2[0]*dphi[0][j]+eps2[1]*dphi[1][j]+eps2[2]*dphi[2][j];
				eps3dotdphii=eps3[0]*dphi[0][i]+eps3[1]*dphi[1][i]+eps3[2]*dphi[2][i];
				eps3dotdphij=eps3[0]*dphi[0][j]+eps3[1]*dphi[1][j]+eps3[2]*dphi[2][j];

				Ke->values[numdof*(4*i+0)+4*j+0]+=gauss->weight*Jdet*2*mu_prime*eps1dotdphij*eps1dotdphii;
				Ke->values[numdof*(4*i+0)+4*j+1]+=gauss->weight*Jdet*2*mu_prime*eps2dotdphij*eps1dotdphii;
				Ke->values[numdof*(4*i+0)+4*j+2]+=gauss->weight*Jdet*2*mu_prime*eps3dotdphij*eps1dotdphii;

				Ke->values[numdof*(4*i+1)+4*j+0]+=gauss->weight*Jdet*2*mu_prime*eps1dotdphij*eps2dotdphii;
				Ke->values[numdof*(4*i+1)+4*j+1]+=gauss->weight*Jdet*2*mu_prime*eps2dotdphij*eps2dotdphii;
				Ke->values[numdof*(4*i+1)+4*j+2]+=gauss->weight*Jdet*2*mu_prime*eps3dotdphij*eps2dotdphii;

				Ke->values[numdof*(4*i+2)+4*j+0]+=gauss->weight*Jdet*2*mu_prime*eps1dotdphij*eps3dotdphii;
				Ke->values[numdof*(4*i+2)+4*j+1]+=gauss->weight*Jdet*2*mu_prime*eps2dotdphij*eps3dotdphii;
				Ke->values[numdof*(4*i+2)+4*j+2]+=gauss->weight*Jdet*2*mu_prime*eps3dotdphij*eps3dotdphii;
			}
		}
	}

	/*Transform Coordinate System*/
	TransformStiffnessMatrixCoord(Ke,nodes,NUMVERTICES,XYZPEnum);

	/*Clean up and return*/
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorAdjointHoriz{{{*/
ElementVector* Penta::CreatePVectorAdjointHoriz(void){

	int approximation;
	inputs->GetInputValue(&approximation,ApproximationEnum);

	switch(approximation){
		case MacAyealApproximationEnum:
			return CreatePVectorAdjointMacAyeal();
		case PattynApproximationEnum:
			return CreatePVectorAdjointPattyn();
		case NoneApproximationEnum:
			return NULL;
		case StokesApproximationEnum:
			return CreatePVectorAdjointStokes();
		default:
			_error2_("Approximation " << EnumToStringx(approximation) << " not supported yet");
	}
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorAdjointMacAyeal{{{*/
ElementVector* Penta::CreatePVectorAdjointMacAyeal(){

	if (!IsOnBed()) return NULL;

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.
	ElementVector* pe=tria->CreatePVectorAdjointHoriz();
	delete tria->matice; delete tria;

	/*clean up and return*/
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorAdjointPattyn{{{*/
ElementVector* Penta::CreatePVectorAdjointPattyn(void){

	if (!IsOnSurface()) return NULL;

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(3,4,5); //nodes 3, 4 and 5 make the new tria (upper face).
	ElementVector* pe=tria->CreatePVectorAdjointHoriz();
	delete tria->matice; delete tria;

	/*clean up and return*/
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorAdjointStokes{{{*/
ElementVector* Penta::CreatePVectorAdjointStokes(void){

	if (!IsOnSurface()) return NULL;

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(3,4,5); //nodes 3, 4 and 5 make the new tria (upper face).
	ElementVector* pe=tria->CreatePVectorAdjointStokes();
	delete tria->matice; delete tria;

	/*clean up and return*/
	return pe;
}
/*}}}*/
/*FUNCTION Penta::GradientIndexing{{{*/
void Penta::GradientIndexing(int* indexing,int control_index){

	/*Get some parameters*/
	int num_controls;
	parameters->FindParam(&num_controls,InversionNumControlParametersEnum);

	/*get gradient indices*/
	for(int i=0;i<NUMVERTICES;i++){
		indexing[i]=num_controls*this->nodes[i]->GetVertexDof() + control_index;
	}

}
/*}}}*/
/*FUNCTION Penta::Gradj {{{*/
void  Penta::Gradj(Vector* gradient,int control_type,int control_index){
	/*dJ/dalpha = ∂L/∂alpha = ∂J/∂alpha + ∂/∂alpha(KU-F)*/

	int              i,approximation;
	Tria*            tria=NULL;

	/*If on water, skip grad (=0): */
	if(IsOnWater())return;

	/*First deal with ∂/∂alpha(KU-F)*/
	switch(control_type){

		case FrictionCoefficientEnum:
			inputs->GetInputValue(&approximation,ApproximationEnum);
			switch(approximation){
				case MacAyealApproximationEnum:
					GradjDragMacAyeal(gradient,control_index);
					break;
				case PattynApproximationEnum:
					GradjDragPattyn(gradient,control_index);
					break;
				case StokesApproximationEnum:
					GradjDragStokes(gradient,control_index);
					break;
				case NoneApproximationEnum:
					/*Gradient is 0*/
					break;
				default:
					_error2_("approximation " << EnumToStringx(approximation) << " not supported yet");
			}
			break;

		case MaterialsRheologyBbarEnum:
			inputs->GetInputValue(&approximation,ApproximationEnum);
			switch(approximation){
				case MacAyealApproximationEnum:
					GradjBbarMacAyeal(gradient,control_index);
					break;
				case PattynApproximationEnum:
					GradjBbarPattyn(gradient,control_index);
					break;
				case StokesApproximationEnum:
					GradjBbarStokes(gradient,control_index);
					break;
				case NoneApproximationEnum:
					/*Gradient is 0*/
					break;
				default:
					_error2_("approximation " << EnumToStringx(approximation) << " not supported yet");
			}
			break;

		default:
			_error2_("control type " << EnumToStringx(control_type) << " not supported yet: ");
	}

	/*Now deal with ∂J/∂alpha*/
	int        *responses = NULL;
	int         num_responses,resp;
	this->parameters->FindParam(&num_responses,InversionNumCostFunctionsEnum);
	this->parameters->FindParam(&responses,NULL,NULL,StepResponsesEnum);

	for(resp=0;resp<num_responses;resp++) switch(responses[resp]){

		case ThicknessAbsMisfitEnum:
		case SurfaceAbsVelMisfitEnum:
		case SurfaceRelVelMisfitEnum:
		case SurfaceLogVelMisfitEnum:
		case SurfaceLogVxVyMisfitEnum:
		case SurfaceAverageVelMisfitEnum:
			/*Nothing, J does not depends on the parameter being inverted for*/
			break;
		case DragCoefficientAbsGradientEnum:
			if (!IsOnBed()) return;
			tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.
			tria->GradjDragGradient(gradient,resp,control_index);
			delete tria->matice; delete tria;
			break;
		case RheologyBbarAbsGradientEnum:
			if (!IsOnBed()) return;
			tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.
			tria->GradjBGradient(gradient,resp,control_index);
			delete tria->matice; delete tria;
			break;
		default:
			_error2_("response " << EnumToStringx(responses[resp]) << " not supported yet");
	}
	xDelete<int>(responses);
}
/*}}}*/
/*FUNCTION Penta::GradjDragMacAyeal {{{*/
void  Penta::GradjDragMacAyeal(Vector* gradient,int control_index){

	/*Gradient is 0 if on shelf or not on bed*/
	if(IsFloating() || !IsOnBed()) return;

	/*Spawn tria*/
	Tria* tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.
	tria->GradjDragMacAyeal(gradient,control_index);
	delete tria->matice; delete tria;

} /*}}}*/
/*FUNCTION Penta::GradjDragPattyn {{{*/
void  Penta::GradjDragPattyn(Vector* gradient,int control_index){

	int        i,j,ig;
	int        analysis_type;
	int        doflist1[NUMVERTICES];
	IssmDouble     vx,vy,lambda,mu,alpha_complement,Jdet;
	IssmDouble     bed,thickness,Neff,drag;
	IssmDouble     xyz_list[NUMVERTICES][3];
	IssmDouble     xyz_list_tria[NUMVERTICES2D][3]={0.0};
	IssmDouble     dk[NDOF3]; 
	IssmDouble     grade_g[NUMVERTICES]={0.0};
	IssmDouble     grade_g_gaussian[NUMVERTICES];
	IssmDouble     basis[6];
	Friction*  friction=NULL;
	GaussPenta  *gauss=NULL;

	/*Gradient is 0 if on shelf or not on bed*/
	if(IsFloating() || !IsOnBed()) return;

	/*Retrieve all inputs and parameters*/
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	GradientIndexing(&doflist1[0],control_index);
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<2;j++) xyz_list_tria[i][j]=xyz_list[i][j];
	Input* adjointx_input=inputs->GetInput(AdjointxEnum);               _assert_(adjointx_input);
	Input* adjointy_input=inputs->GetInput(AdjointyEnum);               _assert_(adjointy_input);
	Input* vx_input=inputs->GetInput(VxEnum);                           _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);                           _assert_(vy_input);
	Input* dragcoefficient_input=inputs->GetInput(FrictionCoefficientEnum); _assert_(dragcoefficient_input);

	/*Build frictoin element, needed later: */
	friction=new Friction("2d",inputs,matpar,analysis_type);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(0,1,2,4);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetTriaJacobianDeterminant(&Jdet, &xyz_list_tria[0][0],gauss);
		GetNodalFunctionsP1(basis, gauss);

		/*Build alpha_complement_list: */
		friction->GetAlphaComplement(&alpha_complement, gauss,VxEnum,VyEnum,VzEnum);

		dragcoefficient_input->GetInputValue(&drag, gauss);
		adjointx_input->GetInputValue(&lambda, gauss);
		adjointy_input->GetInputValue(&mu, gauss);
		vx_input->GetInputValue(&vx,gauss);
		vy_input->GetInputValue(&vy,gauss);
		dragcoefficient_input->GetInputDerivativeValue(&dk[0],&xyz_list[0][0],gauss);

		/*Build gradje_g_gaussian vector (actually -dJ/ddrag): */
		for (i=0;i<NUMVERTICES;i++){
			grade_g_gaussian[i]=-2*drag*alpha_complement*((lambda*vx+mu*vy))*Jdet*gauss->weight*basis[i]; /*basis are 0 for the 3 upper nodes*/
		}

		/*Add gradje_g_gaussian vector to gradje_g: */
		for(i=0;i<NUMVERTICES;i++){
			_assert_(!xIsNan<IssmDouble>(grade_g[i]));
			grade_g[i]+=grade_g_gaussian[i];
		}
	}
	gradient->SetValues(NUMVERTICES,doflist1,grade_g,ADD_VAL);

	/*Clean up and return*/
	delete gauss;
	delete friction;
}
/*}}}*/
/*FUNCTION Penta::GradjDragStokes {{{*/
void  Penta::GradjDragStokes(Vector* gradient,int control_index){

	int        i,j,ig;
	int        analysis_type;
	int        doflist1[NUMVERTICES];
	IssmDouble     bed,thickness,Neff;
	IssmDouble     lambda,mu,xi,Jdet,vx,vy,vz;
	IssmDouble     alpha_complement,drag;
	IssmDouble     surface_normal[3],bed_normal[3];
	IssmDouble     xyz_list[NUMVERTICES][3];
	IssmDouble     xyz_list_tria[NUMVERTICES2D][3]={0.0};
	IssmDouble     dk[NDOF3]; 
	IssmDouble     basis[6];
	IssmDouble     grade_g[NUMVERTICES]={0.0};
	IssmDouble     grade_g_gaussian[NUMVERTICES];
	Friction*  friction=NULL;
	GaussPenta* gauss=NULL;

	/*Gradient is 0 if on shelf or not on bed*/
	if(IsFloating() || !IsOnBed()) return;

	/*Retrieve all inputs and parameters*/
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<2;j++) xyz_list_tria[i][j]=xyz_list[i][j];
	GradientIndexing(&doflist1[0],control_index);
	Input* drag_input    =inputs->GetInput(FrictionCoefficientEnum); _assert_(drag_input);
	Input* vx_input      =inputs->GetInput(VxEnum);                  _assert_(vx_input);
	Input* vy_input      =inputs->GetInput(VyEnum);                  _assert_(vy_input);
	Input* vz_input      =inputs->GetInput(VzEnum);                  _assert_(vz_input);
	Input* adjointx_input=inputs->GetInput(AdjointxEnum);            _assert_(adjointx_input);
	Input* adjointy_input=inputs->GetInput(AdjointyEnum);            _assert_(adjointy_input);
	Input* adjointz_input=inputs->GetInput(AdjointzEnum);            _assert_(adjointz_input);

	/*Build frictoin element, needed later: */
	friction=new Friction("3d",inputs,matpar,analysis_type);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(0,1,2,4);
	for(ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		/*Recover alpha_complement and drag: */
		friction->GetAlphaComplement(&alpha_complement, gauss,VxEnum,VyEnum,VzEnum);
		drag_input->GetInputValue(&drag,gauss);

		/*recover lambda mu and xi: */
		adjointx_input->GetInputValue(&lambda,gauss);
		adjointy_input->GetInputValue(&mu    ,gauss);
		adjointz_input->GetInputValue(&xi    ,gauss);

		/*recover vx vy and vz: */
		vx_input->GetInputValue(&vx, gauss);
		vy_input->GetInputValue(&vy, gauss);
		vz_input->GetInputValue(&vz, gauss);

		/*Get normal vector to the bed */
		SurfaceNormal(&surface_normal[0],xyz_list_tria);

		bed_normal[0]=-surface_normal[0]; //Function is for upper surface, so the normal to the bed is the opposite of the result
		bed_normal[1]=-surface_normal[1];
		bed_normal[2]=-surface_normal[2];

		/* Get Jacobian determinant: */
		GetTriaJacobianDeterminant(&Jdet,&xyz_list_tria[0][0],gauss);
		GetNodalFunctionsP1(basis, gauss);

		/*Get k derivative: dk/dx */
		drag_input->GetInputDerivativeValue(&dk[0],&xyz_list[0][0],gauss);

		/*Build gradje_g_gaussian vector (actually -dJ/ddrag): */
		for (i=0;i<NUMVERTICES;i++){
			//standard gradient dJ/dki
			grade_g_gaussian[i]=(
						-lambda*(2*drag*alpha_complement*(vx - vz*bed_normal[0]*bed_normal[2]))
						-mu    *(2*drag*alpha_complement*(vy - vz*bed_normal[1]*bed_normal[2]))
						-xi    *(2*drag*alpha_complement*(-vx*bed_normal[0]*bed_normal[2]-vy*bed_normal[1]*bed_normal[2]))
						)*Jdet*gauss->weight*basis[i]; 
		}

		/*Add gradje_g_gaussian vector to gradje_g: */
		for( i=0; i<NUMVERTICES; i++)grade_g[i]+=grade_g_gaussian[i];
	}

	gradient->SetValues(NUMVERTICES,doflist1,grade_g,ADD_VAL);

	delete friction;
	delete gauss;
}
/*}}}*/
/*FUNCTION Penta::GradjBbarMacAyeal {{{*/
void  Penta::GradjBbarMacAyeal(Vector* gradient,int control_index){

	/*This element should be collapsed into a tria element at its base*/
	if (!IsOnBed()) return; 

	/*Depth Average B*/
	this->InputDepthAverageAtBase(MaterialsRheologyBEnum,MaterialsRheologyBbarEnum,MaterialsEnum);

	/*Collapse element to the base*/
	Tria* tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria (lower face).
	tria->GradjBMacAyeal(gradient,control_index);
	delete tria->matice; delete tria;

	/*delete Average B*/
	this->matice->inputs->DeleteInput(MaterialsRheologyBbarEnum);

} /*}}}*/
/*FUNCTION Penta::GradjBbarPattyn {{{*/
void  Penta::GradjBbarPattyn(Vector* gradient,int control_index){

	/*Gradient is computed on bed only (Bbar)*/
	if (!IsOnBed()) return;

	/*Depth Average B*/
	this->InputDepthAverageAtBase(MaterialsRheologyBEnum,MaterialsRheologyBbarEnum,MaterialsEnum);

	/*Collapse element to the base*/
	Tria* tria=(Tria*)SpawnTria(0,1,2);
	tria->GradjBMacAyeal(gradient,control_index);    //We use MacAyeal as an estimate for now
	delete tria->matice; delete tria;

	/*delete Average B*/
	this->matice->inputs->DeleteInput(MaterialsRheologyBbarEnum);
} /*}}}*/
/*FUNCTION Penta::GradjBbarStokes {{{*/
void  Penta::GradjBbarStokes(Vector* gradient,int control_index){

	/*Gradient is computed on bed only (Bbar)*/
	if (!IsOnBed()) return;

	/*Depth Average B*/
	this->InputDepthAverageAtBase(MaterialsRheologyBEnum,MaterialsRheologyBbarEnum,MaterialsEnum);

	/*Collapse element to the base*/
	Tria* tria=(Tria*)SpawnTria(0,1,2);
	tria->GradjBMacAyeal(gradient,control_index);    //We use MacAyeal as an estimate for now
	delete tria->matice; delete tria;

	/*delete Average B*/
	this->matice->inputs->DeleteInput(MaterialsRheologyBbarEnum);
} /*}}}*/
/*FUNCTION Penta::InputControlUpdate{{{*/
void  Penta::InputControlUpdate(IssmDouble scalar,bool save_parameter){

	/*Intermediary*/
	int    num_controls;
	int*   control_type=NULL;
	Input* input=NULL;

	/*retrieve some parameters: */
	this->parameters->FindParam(&num_controls,InversionNumControlParametersEnum);
	this->parameters->FindParam(&control_type,NULL,InversionControlParametersEnum);

	for(int i=0;i<num_controls;i++){

		if(control_type[i]==MaterialsRheologyBbarEnum){
			if (!IsOnBed()) goto cleanup_and_return;
			input=(Input*)matice->inputs->GetInput(MaterialsRheologyBEnum); _assert_(input);
		}
		else{
			input=(Input*)this->inputs->GetInput(control_type[i]); _assert_(input);
		}

		if (input->ObjectEnum()!=ControlInputEnum) _error2_("input " << EnumToStringx(control_type[i]) << " is not a ControlInput");

		((ControlInput*)input)->UpdateValue(scalar);
		((ControlInput*)input)->Constrain();
		if (save_parameter) ((ControlInput*)input)->SaveValue();

		if(control_type[i]==MaterialsRheologyBbarEnum){
			this->InputExtrude(MaterialsRheologyBEnum,MaterialsEnum);
		}
	}

	/*Clean up and return*/
cleanup_and_return:
	xDelete<int>(control_type);
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionAdjointStokes {{{*/
void  Penta::InputUpdateFromSolutionAdjointStokes(IssmDouble* solution){

	const int    numdof=NDOF4*NUMVERTICES;

	int    i;
	IssmDouble values[numdof];
	IssmDouble lambdax[NUMVERTICES];
	IssmDouble lambday[NUMVERTICES];
	IssmDouble lambdaz[NUMVERTICES];
	IssmDouble lambdap[NUMVERTICES];
	int*   doflist=NULL;

	/*Get dof list: */
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);

	/*Use the dof list to index into the solution vector: */
	for(i=0;i<numdof;i++) values[i]=solution[doflist[i]];

	/*Ok, we have vx and vy in values, fill in vx and vy arrays: */
	for(i=0;i<NUMVERTICES;i++){
		lambdax[i]=values[i*NDOF4+0];
		lambday[i]=values[i*NDOF4+1];
		lambdaz[i]=values[i*NDOF4+2];
		lambdap[i]=values[i*NDOF4+3];

		/*Check solution*/
		if(xIsNan<IssmDouble>(lambdax[i])) _error2_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(lambday[i])) _error2_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(lambdaz[i])) _error2_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(lambdap[i])) _error2_("NaN found in solution vector");
	}

	/*Add vx and vy as inputs to the tria element: */
	this->inputs->AddInput(new PentaP1Input(AdjointxEnum,lambdax));
	this->inputs->AddInput(new PentaP1Input(AdjointyEnum,lambday));
	this->inputs->AddInput(new PentaP1Input(AdjointzEnum,lambdaz));
	this->inputs->AddInput(new PentaP1Input(AdjointpEnum,lambdap));

	/*Free ressources:*/
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionAdjointHoriz {{{*/
void  Penta::InputUpdateFromSolutionAdjointHoriz(IssmDouble* solution){

	const int numdof=NDOF2*NUMVERTICES;

	int    i;
	IssmDouble values[numdof];
	IssmDouble lambdax[NUMVERTICES];
	IssmDouble lambday[NUMVERTICES];
	int*   doflist=NULL;

	/*Get dof list: */
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);

	/*Use the dof list to index into the solution vector: */
	for(i=0;i<numdof;i++) values[i]=solution[doflist[i]];

	/*Ok, we have vx and vy in values, fill in vx and vy arrays: */
	for(i=0;i<NUMVERTICES;i++){
		lambdax[i]=values[i*NDOF2+0];
		lambday[i]=values[i*NDOF2+1];

		/*Check solution*/
		if(xIsNan<IssmDouble>(lambdax[i]))       _error2_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(lambday[i]))       _error2_("NaN found in solution vector");
	}

	/*Add vx and vy as inputs to the tria element: */
	this->inputs->AddInput(new PentaP1Input(AdjointxEnum,lambdax));
	this->inputs->AddInput(new PentaP1Input(AdjointyEnum,lambday));

	/*Free ressources:*/
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Penta::SurfaceAverageVelMisfit {{{*/
IssmDouble Penta::SurfaceAverageVelMisfit(bool process_units,int weight_index){

	int    approximation;
	IssmDouble J;
	Tria*  tria=NULL;

	/*retrieve inputs :*/
	inputs->GetInputValue(&approximation,ApproximationEnum);

	/*If on water, return 0: */
	if(IsOnWater())return 0;

	/*Bail out if this element if:
	 * -> Non MacAyeal and not on the surface
	 * -> MacAyeal (2d model) and not on bed) */
	if ((approximation!=MacAyealApproximationEnum && !IsOnSurface()) || (approximation==MacAyealApproximationEnum && !IsOnBed())){
		return 0;
	}
	else if (approximation==MacAyealApproximationEnum){

		/*This element should be collapsed into a tria element at its base. Create this tria element, 
		 * and compute SurfaceAverageVelMisfit*/
		tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria (lower face).
		J=tria->SurfaceAverageVelMisfit(process_units,weight_index);
		delete tria->matice; delete tria;
		return J;
	}
	else{

		tria=(Tria*)SpawnTria(3,4,5); //nodes 3, 4 and 5 make the new tria (upper face).
		J=tria->SurfaceAverageVelMisfit(process_units,weight_index);
		delete tria->matice; delete tria;
		return J;
	}
}
/*}}}*/
/*FUNCTION Penta::SurfaceAbsVelMisfit {{{*/
IssmDouble Penta::SurfaceAbsVelMisfit(bool process_units,int weight_index){

	int    approximation;
	IssmDouble J;
	Tria*  tria=NULL;

	/*retrieve inputs :*/
	inputs->GetInputValue(&approximation,ApproximationEnum);

	/*If on water, return 0: */
	if(IsOnWater())return 0;

	/*Bail out if this element if:
	 * -> Non MacAyeal and not on the surface
	 * -> MacAyeal (2d model) and not on bed) */
	if ((approximation!=MacAyealApproximationEnum && !IsOnSurface()) || (approximation==MacAyealApproximationEnum && !IsOnBed())){
		return 0;
	}
	else if (approximation==MacAyealApproximationEnum){

		/*This element should be collapsed into a tria element at its base. Create this tria element, 
		 * and compute SurfaceAbsVelMisfit*/
		tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria (lower face).
		J=tria->SurfaceAbsVelMisfit(process_units,weight_index);
		delete tria->matice; delete tria;
		return J;
	}
	else{

		tria=(Tria*)SpawnTria(3,4,5); //nodes 3, 4 and 5 make the new tria (upper face).
		J=tria->SurfaceAbsVelMisfit(process_units,weight_index);
		delete tria->matice; delete tria;
		return J;
	}
}
/*}}}*/
/*FUNCTION Penta::SurfaceLogVelMisfit {{{*/
IssmDouble Penta::SurfaceLogVelMisfit(bool process_units,int weight_index){

	int    approximation;
	IssmDouble J;
	Tria*  tria=NULL;

	/*retrieve inputs :*/
	inputs->GetInputValue(&approximation,ApproximationEnum);

	/*If on water, return 0: */
	if(IsOnWater())return 0;

	/*Bail out if this element if:
	 * -> Non MacAyeal and not on the surface
	 * -> MacAyeal (2d model) and not on bed) */
	if ((approximation!=MacAyealApproximationEnum && !IsOnSurface()) || (approximation==MacAyealApproximationEnum && !IsOnBed())){
		return 0;
	}
	else if (approximation==MacAyealApproximationEnum){

		/*This element should be collapsed into a tria element at its base. Create this tria element, 
		 * and compute SurfaceLogVelMisfit*/
		tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria (lower face).
		J=tria->SurfaceLogVelMisfit(process_units,weight_index);
		delete tria->matice; delete tria;
		return J;
	}
	else{

		tria=(Tria*)SpawnTria(3,4,5); //nodes 3, 4 and 5 make the new tria (upper face).
		J=tria->SurfaceLogVelMisfit(process_units,weight_index);
		delete tria->matice; delete tria;
		return J;
	}
}
/*}}}*/
/*FUNCTION Penta::SurfaceLogVxVyMisfit {{{*/
IssmDouble Penta::SurfaceLogVxVyMisfit(bool process_units,int weight_index){

	IssmDouble J;
	Tria* tria=NULL;

	/*inputs: */
	int  approximation;

	/*retrieve inputs :*/
	inputs->GetInputValue(&approximation,ApproximationEnum);

	/*If on water, return 0: */
	if(IsOnWater())return 0;

	/*Bail out if this element if:
	 * -> Non MacAyeal and not on the surface
	 * -> MacAyeal (2d model) and not on bed) */
	if ((approximation!=MacAyealApproximationEnum && !IsOnSurface()) || (approximation==MacAyealApproximationEnum && !IsOnBed())){
		return 0;
	}
	else if (approximation==MacAyealApproximationEnum){

		/*This element should be collapsed into a tria element at its base. Create this tria element, 
		 * and compute SurfaceLogVxVyMisfit*/
		tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria (lower face).
		J=tria->SurfaceLogVxVyMisfit(process_units,weight_index);
		delete tria->matice; delete tria;
		return J;
	}
	else{

		tria=(Tria*)SpawnTria(3,4,5); //nodes 3, 4 and 5 make the new tria (upper face).
		J=tria->SurfaceLogVxVyMisfit(process_units,weight_index);
		delete tria->matice; delete tria;
		return J;
	}
}
/*}}}*/
/*FUNCTION Penta::SurfaceRelVelMisfit {{{*/
IssmDouble Penta::SurfaceRelVelMisfit(bool process_units,int weight_index){

	int    approximation;
	IssmDouble J;
	Tria*  tria=NULL;

	/*retrieve inputs :*/
	inputs->GetInputValue(&approximation,ApproximationEnum);

	/*If on water, return 0: */
	if(IsOnWater())return 0;

	/*Bail out if this element if:
	 * -> Non MacAyeal and not on the surface
	 * -> MacAyeal (2d model) and not on bed) */
	if ((approximation!=MacAyealApproximationEnum && !IsOnSurface()) || (approximation==MacAyealApproximationEnum && !IsOnBed())){
		return 0;
	}
	else if (approximation==MacAyealApproximationEnum){

		/*This element should be collapsed into a tria element at its base. Create this tria element, 
		 * and compute SurfaceRelVelMisfit*/
		tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria (lower face).
		J=tria->SurfaceRelVelMisfit(process_units,weight_index);
		delete tria->matice; delete tria;
		return J;
	}
	else{

		tria=(Tria*)SpawnTria(3,4,5); //nodes 3, 4 and 5 make the new tria (upper face).
		J=tria->SurfaceRelVelMisfit(process_units,weight_index);
		delete tria->matice; delete tria;
		return J;
	}
}
/*}}}*/
/*FUNCTION Penta::ThicknessAbsGradient{{{*/
IssmDouble Penta::ThicknessAbsGradient(bool process_units,int weight_index){

	_error2_("Not implemented yet");
}
/*}}}*/
/*FUNCTION Penta::ThicknessAbsMisfit {{{*/
IssmDouble Penta::ThicknessAbsMisfit(bool process_units,int weight_index){

	int    approximation;
	IssmDouble J;
	Tria*  tria=NULL;

	/*retrieve inputs :*/
	inputs->GetInputValue(&approximation,ApproximationEnum);

	/*If on water, return 0: */
	if(IsOnWater())return 0;
	_error2_("Not implemented yet");

	tria=(Tria*)SpawnTria(0,1,2);
	J=tria->ThicknessAbsMisfit(process_units,weight_index);
	delete tria->matice; delete tria;
	return J;
}
/*}}}*/
/*FUNCTION Penta::DragCoefficientAbsGradient{{{*/
IssmDouble Penta::DragCoefficientAbsGradient(bool process_units,int weight_index){

	IssmDouble J;
	Tria*  tria=NULL;

	/*If on water, on shelf or not on bed, skip: */
	if(IsOnWater()|| IsFloating() || !IsOnBed()) return 0;

	tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria
	J=tria->DragCoefficientAbsGradient(process_units,weight_index);
	delete tria->matice; delete tria;
	return J;
}
/*}}}*/
/*FUNCTION Penta::RheologyBbarAbsGradient{{{*/
IssmDouble Penta::RheologyBbarAbsGradient(bool process_units,int weight_index){

	IssmDouble J;
	Tria*  tria=NULL;

	/*If on water, on shelf or not on bed, skip: */
	if(IsOnWater() || !IsOnBed()) return 0;

	tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria
	J=tria->RheologyBbarAbsGradient(process_units,weight_index);
	delete tria->matice; delete tria;
	return J;
}
/*}}}*/
/*FUNCTION Penta::GetVectorFromControlInputs{{{*/
void  Penta::GetVectorFromControlInputs(Vector* vector,int control_enum,int control_index,const char* data){

	int doflist1[NUMVERTICES];

	/*Get out if this is not an element input*/
	if(!IsInput(control_enum)) return;

	/*Prepare index list*/
	GradientIndexing(&doflist1[0],control_index);

	/*Get input (either in element or material)*/
	Input* input=inputs->GetInput(control_enum);
	if(!input) _error2_("Input " << EnumToStringx(control_enum) << " not found in element");

	/*Check that it is a ControlInput*/
	if (input->ObjectEnum()!=ControlInputEnum){
		_error2_("input " << EnumToStringx(control_enum) << " is not a ControlInput");
	}

	((ControlInput*)input)->GetVectorFromInputs(vector,&doflist1[0],data);
}
/*}}}*/
/*FUNCTION Penta::SetControlInputsFromVector{{{*/
void  Penta::SetControlInputsFromVector(IssmDouble* vector,int control_enum,int control_index){

	IssmDouble  values[NUMVERTICES];
	int     doflist1[NUMVERTICES];
	Input  *input     = NULL;
	Input  *new_input = NULL;

	/*Get out if this is not an element input*/
	if(!IsInput(control_enum)) return;

	/*Prepare index list*/
	GradientIndexing(&doflist1[0],control_index);

	/*Get values on vertices*/
	for (int i=0;i<NUMVERTICES;i++){
		values[i]=vector[doflist1[i]];
	}
	new_input = new PentaP1Input(control_enum,values);


	if(control_enum==MaterialsRheologyBbarEnum){
		input=(Input*)matice->inputs->GetInput(control_enum); _assert_(input);
	}
	else{
		input=(Input*)this->inputs->GetInput(control_enum);   _assert_(input);
	}

	if (input->ObjectEnum()!=ControlInputEnum){
		_error2_("input " << EnumToStringx(control_enum) << " is not a ControlInput");
	}

	((ControlInput*)input)->SetInput(new_input);
}
/*}}}*/
#endif

#ifdef _HAVE_DAKOTA_
/*FUNCTION Penta::InputUpdateFromVectorDakota(IssmDouble* vector, int name, int type);{{{*/
void  Penta::InputUpdateFromVectorDakota(IssmDouble* vector, int name, int type){
	
	int i,j;

	/*Check that name is an element input*/
	if (!IsInput(name)) return;

	switch(type){

		case VertexEnum:

			/*New PentaP1Input*/
			IssmDouble values[6];

			/*Get values on the 6 vertices*/
			for (i=0;i<6;i++){
				values[i]=vector[this->nodes[i]->GetSidList()]; //careful, vector of values here is not parallel distributed, but serial distributed (from a serial Dakota core!)
			}

			/*Branch on the specified type of update: */
			switch(name){
				case ThicknessEnum:
					/*Update thickness + surface: assume bed is constant. On ice shelves, takes hydrostatic equilibrium {{{*/
					IssmDouble  thickness[6];
					IssmDouble  thickness_init[6];
					IssmDouble  hydrostatic_ratio[6];
					IssmDouble  surface[6];
					IssmDouble  bed[6];
					
					/*retrieve inputs: */
					GetInputListOnVertices(&thickness_init[0],ThicknessEnum);
					GetInputListOnVertices(&hydrostatic_ratio[0],GeometryHydrostaticRatioEnum);
					GetInputListOnVertices(&bed[0],BedEnum);
					GetInputListOnVertices(&surface[0],SurfaceEnum);

					/*build new thickness: */
//					for(j=0;j<6;j++)thickness[j]=values[j];

					/*build new bed and surface: */
					if (this->IsFloating()){
						/*hydrostatic equilibrium: */
						IssmDouble rho_ice,rho_water,di;
						rho_ice=this->matpar->GetRhoIce();
						rho_water=this->matpar->GetRhoWater();

						di=rho_ice/rho_water;

						/*build new thickness: */
						for (j=0; j<6; j++) {
						/*  for observed/interpolated/hydrostatic thickness, remove scaling from any hydrostatic thickness  */
							if     (hydrostatic_ratio[j] >= 0.)
								thickness[j]=values[j]-(values[j]/thickness_init[j]-1.)*hydrostatic_ratio[j]*surface[j]/(1.-di);
						/*  for minimum thickness, don't scale  */
							else
								thickness[j]=thickness_init[j];

						/*  check the computed thickness and update bed  */
							if (thickness[j] < 0.)
								thickness[j]=1./(1.-di);
							bed[j]=surface[j]-thickness[j];
						}

//						for(j=0;j<6;j++){
//							surface[j]=(1-di)*thickness[j];
//							bed[j]=-di*thickness[j];
//						}
					}
					else{
						/*build new thickness: */
						for (j=0; j<6; j++) {
						/*  for observed thickness, use scaled value  */
							if(hydrostatic_ratio[j] >= 0.)
								thickness[j]=values[j];
						/*  for minimum thickness, don't scale  */
							else
								thickness[j]=thickness_init[j];
						}

						/*update bed on grounded ice: */
//						for(j=0;j<6;j++)surface[j]=bed[j]+thickness[j];
						for(j=0;j<6;j++)bed[j]=surface[j]-thickness[j];
					}

					/*Add new inputs: */
					this->inputs->AddInput(new PentaP1Input(ThicknessEnum,thickness));
					this->inputs->AddInput(new PentaP1Input(BedEnum,bed));
					this->inputs->AddInput(new PentaP1Input(SurfaceEnum,surface));

					/*}}}*/
					break;
				default:
					this->inputs->AddInput(new PentaP1Input(name,values));
			}
			break;

		default:
			_error2_("type " << type << " (" << EnumToStringx(type) << ") not implemented yet");
	}

}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromVectorDakota(int* vector, int name, int type);{{{*/
void  Penta::InputUpdateFromVectorDakota(int* vector, int name, int type){
	_error2_("not supported yet!");
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromVectorDakota(bool* vector, int name, int type);{{{*/
void  Penta::InputUpdateFromVectorDakota(bool* vector, int name, int type){
	_error2_("not supported yet!");
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromMatrixDakota(IssmDouble* matrix, int nrows, int ncols, int name, int type);{{{*/
void  Penta::InputUpdateFromMatrixDakota(IssmDouble* matrix, int nrows, int ncols, int name, int type){
	
	int i,j,t;
	TransientInput* transientinput=NULL;
	IssmDouble values[6];
	IssmDouble time;
	int row;
	IssmDouble yts;

	/*Check that name is an element input*/
	if (!IsInput(name)) return;

	switch(type){

		case VertexEnum:
			
			/*Create transient input: */
						
			parameters->FindParam(&yts,ConstantsYtsEnum);

			for(t=0;t<ncols;t++){ //ncols is the number of times

				/*create input values: */
				for(i=0;i<6;i++){
					row=this->nodes[i]->GetSidList();
					values[i]=(IssmDouble)matrix[ncols*row+t];
				}

				/*time? :*/
				time=(IssmDouble)matrix[(nrows-1)*ncols+t]*yts;

				if(t==0) transientinput=new TransientInput(name);
				transientinput->AddTimeInput(new PentaP1Input(name,values),time);
				transientinput->Configure(parameters);
			}
			this->inputs->AddInput(transientinput);
			break;

		default:
			_error2_("type " << type << " (" << EnumToStringx(type) << ") not implemented yet");
	}

}
/*}}}*/
#endif

#ifdef _HAVE_DIAGNOSTIC_
/*FUNCTION Penta::CreateDVectorDiagnosticHoriz {{{*/
ElementVector* Penta::CreateDVectorDiagnosticHoriz(void){

	int approximation;
	inputs->GetInputValue(&approximation,ApproximationEnum);

	switch(approximation){
		case StokesApproximationEnum:
			return CreateDVectorDiagnosticStokes();
		default:
			return NULL; //no need for doftypes outside of stokes approximation
	}
}
/*}}}*/
/*FUNCTION Penta::CreateDVectorDiagnosticStokes{{{*/
ElementVector* Penta::CreateDVectorDiagnosticStokes(void){

	/*output: */
	ElementVector* De=NULL;
	/*intermediary: */
	int approximation;
	int i;

	/*Initialize Element vector and return if necessary*/
	inputs->GetInputValue(&approximation,ApproximationEnum);
	if(approximation!=StokesApproximationEnum) return NULL;

	De=new ElementVector(nodes,NUMVERTICES,this->parameters,StokesApproximationEnum);

	for (i=0;i<NUMVERTICES;i++){
		De->values[i*4+0]=VelocityEnum;
		De->values[i*4+1]=VelocityEnum;
		De->values[i*4+2]=VelocityEnum;
		De->values[i*4+3]=PressureEnum;
	}

	return De;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixCouplingMacAyealPattyn{{{*/
ElementMatrix* Penta::CreateKMatrixCouplingMacAyealPattyn(void){
	
	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixCouplingMacAyealPattynViscous();
	ElementMatrix* Ke2=CreateKMatrixCouplingMacAyealPattynFriction();
	ElementMatrix* Ke=new ElementMatrix(Ke1,Ke2);
	
	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixCouplingMacAyealPattynViscous{{{*/
ElementMatrix* Penta::CreateKMatrixCouplingMacAyealPattynViscous(void){

	/*Constants*/
	const int numnodes    = 2 *NUMVERTICES;
	const int numdofm     = NDOF2 *NUMVERTICES2D;
	const int numdofp     = NDOF2 *NUMVERTICES;
	const int numdoftotal = 2 *NDOF2*NUMVERTICES;

	/*Intermediaries */
	int         i,j,ig;
	IssmDouble      Jdet;
	IssmDouble      viscosity,oldviscosity,newviscosity,viscosity_overshoot; //viscosity
	IssmDouble      epsilon[5],oldepsilon[5]; /* epsilon=[exx,eyy,exy,exz,eyz];*/
	IssmDouble      xyz_list[NUMVERTICES][3];
	IssmDouble      B[3][numdofp];
	IssmDouble      Bprime[3][numdofm];
	IssmDouble      D[3][3]={0.0};            // material matrix, simple scalar matrix.
	IssmDouble      D_scalar;
	IssmDouble      Ke_gg[numdofp][numdofm]={0.0}; //local element stiffness matrix 
	IssmDouble      Ke_gg_gaussian[numdofp][numdofm]; //stiffness matrix evaluated at the gaussian point.
	GaussPenta *gauss=NULL;
	GaussTria  *gauss_tria=NULL;
	Node       *node_list[numnodes];
	int         cs_list[numnodes];

	/*Find penta on bed as pattyn must be coupled to the dofs on the bed: */
	Penta* pentabase=GetBasalElement();
	Tria*  tria=pentabase->SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.

	/*Prepare node list*/
	for(i=0;i<NUMVERTICES;i++){
		node_list[i+0*NUMVERTICES] = pentabase->nodes[i];
		node_list[i+1*NUMVERTICES] = this->nodes[i];
		cs_list[i+0*NUMVERTICES] = XYEnum;
		cs_list[i+1*NUMVERTICES] = XYEnum;
	}

	/*Initialize Element matrix*/
	ElementMatrix* Ke1=new ElementMatrix(pentabase->nodes,NUMVERTICES,this->parameters,MacAyealApproximationEnum);
	ElementMatrix* Ke2=new ElementMatrix(this->nodes     ,NUMVERTICES,this->parameters,PattynApproximationEnum);
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2);
	delete Ke1; delete Ke2;

	/* Get node coordinates and dof list: */
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	this->parameters->FindParam(&viscosity_overshoot,DiagnosticViscosityOvershootEnum);
	Input* vx_input=inputs->GetInput(VxEnum);       _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);       _assert_(vy_input);
	Input* vxold_input=inputs->GetInput(VxPicardEnum); _assert_(vxold_input);
	Input* vyold_input=inputs->GetInput(VyPicardEnum); _assert_(vyold_input);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(5,5);
	gauss_tria=new GaussTria();
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);
		gauss->SynchronizeGaussTria(gauss_tria);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetBMacAyealPattyn(&B[0][0], &xyz_list[0][0], gauss);
		tria->GetBprimeMacAyeal(&Bprime[0][0], &xyz_list[0][0], gauss_tria);

		this->GetStrainRate3dPattyn(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
		this->GetStrainRate3dPattyn(&oldepsilon[0],&xyz_list[0][0],gauss,vxold_input,vyold_input);
		matice->GetViscosity3d(&viscosity, &epsilon[0]);
		matice->GetViscosity3d(&oldviscosity, &oldepsilon[0]);

		newviscosity=viscosity+viscosity_overshoot*(viscosity-oldviscosity);
		D_scalar=2*newviscosity*gauss->weight*Jdet;
		for (i=0;i<3;i++) D[i][i]=D_scalar;

		TripleMultiply( &B[0][0],3,numdofp,1,
					&D[0][0],3,3,0,
					&Bprime[0][0],3,numdofm,0,
					&Ke_gg_gaussian[0][0],0);

		for( i=0; i<numdofp; i++) for(j=0;j<numdofm; j++) Ke_gg[i][j]+=Ke_gg_gaussian[i][j];
	} 
	for(i=0;i<numdofp;i++) for(j=0;j<numdofm;j++) Ke->values[(i+2*numdofm)*numdoftotal+j]+=Ke_gg[i][j];
	for(i=0;i<numdofm;i++) for(j=0;j<numdofp;j++) Ke->values[i*numdoftotal+(j+2*numdofm)]+=Ke_gg[j][i];

	/*Transform Coordinate System*/
	TransformStiffnessMatrixCoord(Ke,node_list,numnodes,cs_list);

	/*Clean-up and return*/
	delete tria->matice; delete tria;
	delete gauss;
	delete gauss_tria;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixCouplingMacAyealPattynFriction{{{*/
ElementMatrix* Penta::CreateKMatrixCouplingMacAyealPattynFriction(void){

	/*Constants*/
	const int numnodes    = 2 *NUMVERTICES;
	const int numdof      = NDOF2 *NUMVERTICES;
	const int numdoftotal = NDOF4 *NUMVERTICES;
	
	/*Intermediaries */
	int       i,j,ig,analysis_type;
	IssmDouble    Jdet2d,slope_magnitude,alpha2;
	IssmDouble    xyz_list[NUMVERTICES][3];
	IssmDouble    xyz_list_tria[NUMVERTICES2D][3]={0.0};
	IssmDouble    slope[3]={0.0,0.0,0.0};
	IssmDouble    MAXSLOPE=.06; // 6 %
	IssmDouble    MOUNTAINKEXPONENT=10;
	IssmDouble    L[2][numdof];
	IssmDouble    DL[2][2]                  ={{ 0,0 },{0,0}}; //for basal drag
	IssmDouble    DL_scalar;
	IssmDouble    Ke_gg[numdof][numdof]     ={0.0};
	IssmDouble    Ke_gg_gaussian[numdof][numdof]; //stiffness matrix contribution from drag
	Friction  *friction = NULL;
	GaussPenta *gauss=NULL;
	Node       *node_list[numnodes];
	int         cs_list[numnodes];

	/*Initialize Element matrix and return if necessary*/
	if(IsFloating() || !IsOnBed()) return NULL;
	ElementMatrix* Ke1=new ElementMatrix(nodes,NUMVERTICES,this->parameters,MacAyealApproximationEnum);
	ElementMatrix* Ke2=new ElementMatrix(nodes,NUMVERTICES,this->parameters,PattynApproximationEnum);
	ElementMatrix* Ke=new ElementMatrix(Ke1,Ke2);
	delete Ke1; delete Ke2;

	/*Prepare node list*/
	for(i=0;i<NUMVERTICES;i++){
		node_list[i+0*NUMVERTICES] = this->nodes[i];
		node_list[i+1*NUMVERTICES] = this->nodes[i];
		cs_list[i+0*NUMVERTICES] = XYEnum;
		cs_list[i+1*NUMVERTICES] = XYEnum;
	}

	/*retrieve inputs :*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<2;j++) xyz_list_tria[i][j]=xyz_list[i][j];
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	Input* surface_input=inputs->GetInput(SurfaceEnum); _assert_(surface_input);
	Input* vx_input=inputs->GetInput(VxEnum);           _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);           _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);           _assert_(vz_input);

	/*build friction object, used later on: */
	friction=new Friction("2d",inputs,matpar,analysis_type);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(0,1,2,2);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		/*Friction: */
		friction->GetAlpha2(&alpha2, gauss,VxEnum,VyEnum,VzEnum);

		// If we have a slope > 6% for this element,  it means  we are on a mountain. In this particular case, 
		//velocity should be = 0. To achieve this result, we set alpha2_list to a very high value: */
		surface_input->GetInputDerivativeValue(&slope[0],&xyz_list[0][0],gauss);
		slope_magnitude=sqrt(pow(slope[0],2)+pow(slope[1],2));

		if (slope_magnitude>MAXSLOPE){
			alpha2=pow((IssmDouble)10,MOUNTAINKEXPONENT);
		}

		GetTriaJacobianDeterminant(&Jdet2d, &xyz_list_tria[0][0],gauss);
		GetL(&L[0][0], gauss,NDOF2);

		DL_scalar=alpha2*gauss->weight*Jdet2d;
		for (i=0;i<2;i++) DL[i][i]=DL_scalar; 
		
		/*  Do the triple producte tL*D*L: */
		TripleMultiply( &L[0][0],2,numdof,1,
					&DL[0][0],2,2,0,
					&L[0][0],2,numdof,0,
					&Ke_gg_gaussian[0][0],0);

		for(i=0;i<numdof;i++) for(j=0;j<numdof;j++) Ke_gg[i][j]+=Ke_gg_gaussian[i][j];
	}

	for(i=0;i<numdof;i++) for(j=0;j<numdof;j++) Ke->values[i*numdoftotal+(numdof+j)]+=Ke_gg[i][j];
	for(i=0;i<numdof;i++) for(j=0;j<numdof;j++) Ke->values[(i+numdof)*numdoftotal+j]+=Ke_gg[i][j];

	/*Transform Coordinate System*/
	TransformStiffnessMatrixCoord(Ke,node_list,numnodes,cs_list);

	/*Clean up and return*/
	delete gauss;
	delete friction;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixCouplingMacAyealStokes{{{*/
ElementMatrix* Penta::CreateKMatrixCouplingMacAyealStokes(void){

	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixCouplingMacAyealStokesViscous();
	ElementMatrix* Ke2=CreateKMatrixCouplingMacAyealStokesFriction();
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixCouplingMacAyealStokesViscous{{{*/
ElementMatrix* Penta::CreateKMatrixCouplingMacAyealStokesViscous(void){

	/*Constants*/
	const int numnodes    = 2 *NUMVERTICES;
	const int numdofm     = NDOF2 *NUMVERTICES2D;
	const int numdofs     = NDOF4 *NUMVERTICES;
	const int numdoftotal = 2 *numdofm+numdofs;

	/*Intermediaries */
	int         i,j,ig;
	IssmDouble      Jdet;
	IssmDouble      viscosity,stokesreconditioning; //viscosity
	IssmDouble      epsilon[6]; /* epsilon=[exx,eyy,exy,exz,eyz];*/
	IssmDouble      xyz_list[NUMVERTICES][3];
	IssmDouble      B[4][numdofs+3];
	IssmDouble      Bprime[4][numdofm];
	IssmDouble      B2[3][numdofm];
	IssmDouble      Bprime2[3][numdofs+3];
	IssmDouble      D[4][4]={0.0};            // material matrix, simple scalar matrix.
	IssmDouble      D2[3][3]={0.0};            // material matrix, simple scalar matrix.
	IssmDouble      D_scalar;
	IssmDouble      Ke_gg[numdofs][numdofm]={0.0}; //local element stiffness matrix 
	IssmDouble      Ke_gg2[numdofm][numdofs]={0.0}; //local element stiffness matrix 
	IssmDouble      Ke_gg_gaussian[numdofs+3][numdofm]; //stiffness matrix evaluated at the gaussian point.
	IssmDouble      Ke_gg_gaussian2[numdofm][numdofs+3]; //stiffness matrix evaluated at the gaussian point.
	GaussPenta *gauss=NULL;
	GaussTria  *gauss_tria=NULL;
	Node       *node_list[numnodes];
	int         cs_list[numnodes];

	/*Find penta on bed as stokes must be coupled to the dofs on the bed: */
	Penta* pentabase=GetBasalElement();
	Tria* tria=pentabase->SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.

	/*Prepare node list*/
	for(i=0;i<NUMVERTICES;i++){
		node_list[i+0*NUMVERTICES] = pentabase->nodes[i];
		node_list[i+1*NUMVERTICES] = this->nodes[i];
		cs_list[i+0*NUMVERTICES] = XYEnum;
		cs_list[i+1*NUMVERTICES] = XYZPEnum;
	}

	/*Initialize Element matrix and return if necessary*/
	ElementMatrix* Ke1=new ElementMatrix(pentabase->nodes,NUMVERTICES,this->parameters,MacAyealApproximationEnum);
	ElementMatrix* Ke2=new ElementMatrix(this->nodes     ,NUMVERTICES,this->parameters,StokesApproximationEnum);
	ElementMatrix* Ke=new ElementMatrix(Ke1,Ke2);
	delete Ke1; delete Ke2;

	/* Get node coordinates and dof list: */
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	parameters->FindParam(&stokesreconditioning,DiagnosticStokesreconditioningEnum);
	Input* vx_input=inputs->GetInput(VxEnum);       _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);       _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);       _assert_(vz_input);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(5,5);
	gauss_tria=new GaussTria();
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);
		gauss->SynchronizeGaussTria(gauss_tria);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetBMacAyealStokes(&B[0][0], &xyz_list[0][0], gauss);
		tria->GetBprimeMacAyealStokes(&Bprime[0][0], &xyz_list[0][0], gauss_tria);
		tria->GetBMacAyealStokes(&B2[0][0], &xyz_list[0][0], gauss_tria);
		GetBprimeMacAyealStokes(&Bprime2[0][0], &xyz_list[0][0], gauss);

		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		matice->GetViscosity3dStokes(&viscosity, &epsilon[0]);

		D_scalar=2*viscosity*gauss->weight*Jdet;
		for (i=0;i<3;i++) D[i][i]=D_scalar;
		D[3][3]=-gauss->weight*Jdet*stokesreconditioning;
		for (i=0;i<3;i++) D2[i][i]=D_scalar;

		TripleMultiply( &B[0][0],4,numdofs+3,1,
					&D[0][0],4,4,0,
					&Bprime[0][0],4,numdofm,0,
					&Ke_gg_gaussian[0][0],0);

		TripleMultiply( &B2[0][0],3,numdofm,1,
					&D2[0][0],3,3,0,
					&Bprime2[0][0],3,numdofs+3,0,
					&Ke_gg_gaussian2[0][0],0);

		for( i=0; i<numdofs; i++) for(j=0;j<numdofm; j++) Ke_gg[i][j]+=Ke_gg_gaussian[i][j];
		for( i=0; i<numdofm; i++) for(j=0;j<numdofs; j++) Ke_gg2[i][j]+=Ke_gg_gaussian2[i][j];
	} 
	for(i=0;i<numdofs;i++) for(j=0;j<numdofm;j++) Ke->values[(i+2*numdofm)*numdoftotal+j]+=Ke_gg[i][j];
	for(i=0;i<numdofm;i++) for(j=0;j<numdofs;j++) Ke->values[i*numdoftotal+(j+2*numdofm)]+=Ke_gg2[i][j];

	/*Transform Coordinate System*/
	TransformStiffnessMatrixCoord(Ke,node_list,numnodes,cs_list);

	/*Clean-up and return*/
	delete tria->matice; delete tria;
	delete gauss;
	delete gauss_tria;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixCouplingMacAyealStokesFriction {{{*/
ElementMatrix* Penta::CreateKMatrixCouplingMacAyealStokesFriction(void){

	/*Constants*/
	const int numnodes  = 2 *NUMVERTICES;
	const int numdof    = NUMVERTICES *NDOF4;
	const int numdofm   = NUMVERTICES *NDOF2;
	const int numdof2d  = NUMVERTICES2D *NDOF4;
	const int numdof2dm = NUMVERTICES2D *NDOF2;
	const int numdoftot = numdof+numdofm;

	/*Intermediaries */
	int        i,j,ig;
	int        analysis_type,approximation;
	IssmDouble     stokesreconditioning;
	IssmDouble     viscosity,alpha2_gauss,Jdet2d;
	IssmDouble	  bed_normal[3];
	IssmDouble     epsilon[6]; /* epsilon=[exx,eyy,ezz,exy,exz,eyz];*/
	IssmDouble     xyz_list[NUMVERTICES][3];
	IssmDouble	  xyz_list_tria[NUMVERTICES2D][3];
	IssmDouble     LMacAyealStokes[8][numdof2dm];
	IssmDouble     LprimeMacAyealStokes[8][numdof2d];
	IssmDouble     DLMacAyealStokes[8][8]={0.0};
	IssmDouble     LStokesMacAyeal[4][numdof2d];
	IssmDouble     LprimeStokesMacAyeal[4][numdof2dm];
	IssmDouble     DLStokesMacAyeal[4][4]={0.0};
	IssmDouble     Ke_drag_gaussian[numdof2dm][numdof2d];
	IssmDouble     Ke_drag_gaussian2[numdof2d][numdof2dm];
	Friction*  friction=NULL;
	GaussPenta *gauss=NULL;
	Node       *node_list[numnodes];
	int         cs_list[numnodes];

	/*If on water or not Stokes, skip stiffness: */
	inputs->GetInputValue(&approximation,ApproximationEnum);
	if(IsFloating() || !IsOnBed()) return NULL;
	ElementMatrix* Ke1=new ElementMatrix(this->nodes,NUMVERTICES,this->parameters,MacAyealApproximationEnum);
	ElementMatrix* Ke2=new ElementMatrix(this->nodes,NUMVERTICES,this->parameters,StokesApproximationEnum);
	ElementMatrix* Ke=new ElementMatrix(Ke1,Ke2);
	delete Ke1; delete Ke2;

	/*Prepare node list*/
	for(i=0;i<NUMVERTICES;i++){
		node_list[i+0*NUMVERTICES] = this->nodes[i];
		node_list[i+1*NUMVERTICES] = this->nodes[i];
		cs_list[i+0*NUMVERTICES] = XYEnum;
		cs_list[i+1*NUMVERTICES] = XYZPEnum;
	}

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	parameters->FindParam(&stokesreconditioning,DiagnosticStokesreconditioningEnum);
	Input* vx_input=inputs->GetInput(VxEnum); _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum); _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum); _assert_(vz_input);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;j++) xyz_list_tria[i][j]=xyz_list[i][j];

	/*build friction object, used later on: */
	friction=new Friction("3d",inputs,matpar,analysis_type);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(0,1,2,2);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetTriaJacobianDeterminant(&Jdet2d, &xyz_list_tria[0][0],gauss);
		GetLMacAyealStokes(&LMacAyealStokes[0][0], gauss);
		GetLprimeMacAyealStokes(&LprimeMacAyealStokes[0][0], &xyz_list[0][0], gauss);
		GetLStokesMacAyeal(&LStokesMacAyeal[0][0], gauss);
		GetLprimeStokesMacAyeal(&LprimeStokesMacAyeal[0][0], &xyz_list[0][0], gauss);

		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		matice->GetViscosity3dStokes(&viscosity,&epsilon[0]);

		BedNormal(&bed_normal[0],xyz_list_tria);
		friction->GetAlpha2(&alpha2_gauss, gauss,VxEnum,VyEnum,VzEnum);

		DLMacAyealStokes[0][0]=alpha2_gauss*gauss->weight*Jdet2d;
		DLMacAyealStokes[1][1]=alpha2_gauss*gauss->weight*Jdet2d;
		DLMacAyealStokes[2][2]=-alpha2_gauss*gauss->weight*Jdet2d*bed_normal[0]*bed_normal[2];
		DLMacAyealStokes[3][3]=-alpha2_gauss*gauss->weight*Jdet2d*bed_normal[1]*bed_normal[2];
		DLMacAyealStokes[4][4]=-2*viscosity*gauss->weight*Jdet2d*bed_normal[0];
		DLMacAyealStokes[5][5]=-2*viscosity*gauss->weight*Jdet2d*bed_normal[1];
		DLMacAyealStokes[6][6]=stokesreconditioning*gauss->weight*Jdet2d*bed_normal[0];
		DLMacAyealStokes[7][7]=stokesreconditioning*gauss->weight*Jdet2d*bed_normal[1];

		DLStokesMacAyeal[0][0]=alpha2_gauss*gauss->weight*Jdet2d;
		DLStokesMacAyeal[1][1]=alpha2_gauss*gauss->weight*Jdet2d;
		DLStokesMacAyeal[2][2]=-alpha2_gauss*gauss->weight*Jdet2d*bed_normal[0]*bed_normal[2];
		DLStokesMacAyeal[3][3]=-alpha2_gauss*gauss->weight*Jdet2d*bed_normal[1]*bed_normal[2];
		
		TripleMultiply( &LMacAyealStokes[0][0],8,numdof2dm,1,
					&DLMacAyealStokes[0][0],8,8,0,
					&LprimeMacAyealStokes[0][0],8,numdof2d,0,
					&Ke_drag_gaussian[0][0],0);

		TripleMultiply( &LStokesMacAyeal[0][0],4,numdof2d,1,
					&DLStokesMacAyeal[0][0],4,4,0,
					&LprimeStokesMacAyeal[0][0],4,numdof2dm,0,
					&Ke_drag_gaussian2[0][0],0);

		for(i=0;i<numdof2dm;i++) for(j=0;j<numdof2d;j++) Ke->values[i*numdoftot+j+numdofm]+=Ke_drag_gaussian[i][j];
		for(i=0;i<numdof2d;i++) for(j=0;j<numdof2dm;j++) Ke->values[(i+numdofm)*numdoftot+j]+=Ke_drag_gaussian2[i][j];
	}

	/*Transform Coordinate System*/
	TransformStiffnessMatrixCoord(Ke,node_list,numnodes,cs_list);

	/*Clean up and return*/
	delete gauss;
	delete friction;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixCouplingPattynStokes{{{*/
ElementMatrix* Penta::CreateKMatrixCouplingPattynStokes(void){

	/*Constants*/
	const int numnodes  = 2 *NUMVERTICES;
	const int numdofp     = NDOF2 *NUMVERTICES;
	const int numdofs     = NDOF4 *NUMVERTICES;
	const int numdoftotal = (NDOF2+NDOF4) *NUMVERTICES;

	/*Intermediaries*/
	Node     *node_list[numnodes];
	int       cs_list[numnodes];
	int       i,j;

	/*Prepare node list*/
	for(i=0;i<NUMVERTICES;i++){
		node_list[i+0*NUMVERTICES] = this->nodes[i];
		node_list[i+1*NUMVERTICES] = this->nodes[i];
		cs_list[i+0*NUMVERTICES] = XYEnum;
		cs_list[i+1*NUMVERTICES] = XYZPEnum;
	}

	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=new ElementMatrix(this->nodes,NUMVERTICES,this->parameters,PattynApproximationEnum);
	ElementMatrix* Ke2=new ElementMatrix(this->nodes,NUMVERTICES,this->parameters,StokesApproximationEnum);
	ElementMatrix* Ke=new ElementMatrix(Ke1,Ke2);
	delete Ke1;
	delete Ke2;
	Ke1=CreateKMatrixDiagnosticPattyn(); TransformInvStiffnessMatrixCoord(Ke1,this->nodes,NUMVERTICES,XYEnum);
	Ke2=CreateKMatrixDiagnosticStokes(); TransformInvStiffnessMatrixCoord(Ke2,this->nodes,NUMVERTICES,XYZPEnum);

	for(i=0;i<numdofs;i++) for(j=0;j<NUMVERTICES;j++){
		Ke->values[(i+numdofp)*numdoftotal+NDOF2*j+0]+=Ke2->values[i*numdofs+NDOF4*j+0];
		Ke->values[(i+numdofp)*numdoftotal+NDOF2*j+1]+=Ke2->values[i*numdofs+NDOF4*j+1];
	}
	for(i=0;i<numdofp;i++) for(j=0;j<NUMVERTICES;j++){
		Ke->values[i*numdoftotal+numdofp+NDOF4*j+0]+=Ke1->values[i*numdofp+NDOF2*j+0];
		Ke->values[i*numdoftotal+numdofp+NDOF4*j+1]+=Ke1->values[i*numdofp+NDOF2*j+1];
	}

	/*Transform Coordinate System*/
	TransformStiffnessMatrixCoord(Ke,node_list,numnodes,cs_list);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixDiagnosticHoriz {{{*/
ElementMatrix* Penta::CreateKMatrixDiagnosticHoriz(void){

	int approximation;
	inputs->GetInputValue(&approximation,ApproximationEnum);

	switch(approximation){
		case MacAyealApproximationEnum:
			return CreateKMatrixDiagnosticMacAyeal2d();
		case PattynApproximationEnum:
			return CreateKMatrixDiagnosticPattyn();
		case StokesApproximationEnum:
			return CreateKMatrixDiagnosticStokes();
		case HutterApproximationEnum:
			return NULL;
		case NoneApproximationEnum:
			return NULL;
		case MacAyealPattynApproximationEnum:
			return CreateKMatrixDiagnosticMacAyealPattyn();
		case MacAyealStokesApproximationEnum:
			return CreateKMatrixDiagnosticMacAyealStokes();
		case PattynStokesApproximationEnum:
			return CreateKMatrixDiagnosticPattynStokes();
		default:
			_error2_("Approximation " << EnumToStringx(approximation) << " not supported yet");
	}
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixDiagnosticHutter{{{*/
ElementMatrix* Penta::CreateKMatrixDiagnosticHutter(void){

	/*Constants*/
	const int numdof=NDOF2*NUMVERTICES;

	/*Intermediaries*/
	int       connectivity[2];
	int       i,i0,i1,j0,j1;
	IssmDouble    one0,one1;

	/*Initialize Element matrix*/
	ElementMatrix* Ke=new ElementMatrix(nodes,NUMVERTICES,this->parameters,NoneApproximationEnum);

	/*Spawn 3 beam elements: */
	for(i=0;i<3;i++){
		/*2 dofs of first node*/
		i0=2*i;
		i1=2*i+1;
		/*2 dofs of second node*/
		j0=2*(i+3);
		j1=2*(i+3)+1;

		/*Find connectivity for the two nodes*/
		connectivity[0]=nodes[i]->GetConnectivity();
		connectivity[1]=nodes[i+3]->GetConnectivity();
		one0=1/(IssmDouble)connectivity[0];
		one1=1/(IssmDouble)connectivity[1];

		/*Create matrix for these two nodes*/
		if (IsOnBed() && IsOnSurface()){
			Ke->values[i0*numdof+i0]=one0;
			Ke->values[i1*numdof+i1]=one0;
			Ke->values[j0*numdof+i0]=-one1;
			Ke->values[j0*numdof+j0]=one1;
			Ke->values[j1*numdof+i1]=-one1;
			Ke->values[j1*numdof+j1]=one1;
		}
		else if (IsOnBed()){
			Ke->values[i0*numdof+i0]=one0;
			Ke->values[i1*numdof+i1]=one0;
			Ke->values[j0*numdof+i0]=-2*one1;
			Ke->values[j0*numdof+j0]=2*one1;
			Ke->values[j1*numdof+i1]=-2*one1;
			Ke->values[j1*numdof+j1]=2*one1;
		}
		else if (IsOnSurface()){
			Ke->values[j0*numdof+i0]=-one1;
			Ke->values[j0*numdof+j0]=one1;
			Ke->values[j1*numdof+i1]=-one1;
			Ke->values[j1*numdof+j1]=one1;
		}
		else{ //node is on two horizontal layers and beams include the values only once, so the have to use half of the connectivity
			Ke->values[j0*numdof+i0]=-2*one1;
			Ke->values[j0*numdof+j0]=2*one1;
			Ke->values[j1*numdof+i1]=-2*one1;
			Ke->values[j1*numdof+j1]=2*one1;
		}
	}

	/*Clean up and return*/
	return Ke;
}/*}}}*/
/*FUNCTION Penta::CreateKMatrixDiagnosticMacAyeal2d{{{*/
ElementMatrix* Penta::CreateKMatrixDiagnosticMacAyeal2d(void){

	/*Figure out if this penta is collapsed. If so, then bailout, except if it is at the 
	  bedrock, in which case we spawn a tria element using the 3 first nodes, and use it to build 
	  the stiffness matrix. */
	if (!IsOnBed()) return NULL;

	/*Depth Averaging B*/
	this->InputDepthAverageAtBase(MaterialsRheologyBEnum,MaterialsRheologyBbarEnum,MaterialsEnum);

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.
	ElementMatrix* Ke=tria->CreateKMatrixDiagnosticMacAyeal();
	delete tria->matice; delete tria;

	/*Delete B averaged*/
	this->matice->inputs->DeleteInput(MaterialsRheologyBbarEnum);

	/*clean up and return*/
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixDiagnosticMacAyeal3d{{{*/
ElementMatrix* Penta::CreateKMatrixDiagnosticMacAyeal3d(void){

	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixDiagnosticMacAyeal3dViscous();
	ElementMatrix* Ke2=CreateKMatrixDiagnosticMacAyeal3dFriction();
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixDiagnosticMacAyeal3dViscous{{{*/
ElementMatrix* Penta::CreateKMatrixDiagnosticMacAyeal3dViscous(void){

	/*Constants*/
	const int    numdof2d=2*NUMVERTICES2D;

	/*Intermediaries */
	int         i,j,ig,approximation;
	IssmDouble      Jdet;
	IssmDouble      viscosity, oldviscosity, newviscosity, viscosity_overshoot;
	IssmDouble      epsilon[5],oldepsilon[5]; /* epsilon=[exx,eyy,exy,exz,eyz];*/
	IssmDouble      epsilons[6]; //6 for stokes
	IssmDouble      xyz_list[NUMVERTICES][3];
	IssmDouble      B[3][numdof2d];
	IssmDouble      Bprime[3][numdof2d];
	IssmDouble      D[3][3]={0.0};            // material matrix, simple scalar matrix.
	IssmDouble      D_scalar;
	IssmDouble      Ke_gg_gaussian[numdof2d][numdof2d]; //stiffness matrix evaluated at the gaussian point.
	Tria*       tria=NULL;
	Penta*      pentabase=NULL;
	GaussPenta *gauss=NULL;
	GaussTria  *gauss_tria=NULL;

	/*Find penta on bed as this is a macayeal elements: */
	pentabase=GetBasalElement();
	tria=pentabase->SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.

	/*Initialize Element matrix*/
	ElementMatrix* Ke=new ElementMatrix(tria->nodes,NUMVERTICES2D,this->parameters,MacAyealApproximationEnum);
	inputs->GetInputValue(&approximation,ApproximationEnum);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes,NUMVERTICES);
	this->parameters->FindParam(&viscosity_overshoot,DiagnosticViscosityOvershootEnum);
	Input* vx_input=inputs->GetInput(VxEnum);       _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);       _assert_(vy_input);
	Input* vxold_input=inputs->GetInput(VxPicardEnum); _assert_(vxold_input);
	Input* vyold_input=inputs->GetInput(VyPicardEnum); _assert_(vyold_input);
	Input* vz_input=inputs->GetInput(VzEnum);       _assert_(vz_input);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(5,5);
	gauss_tria=new GaussTria();
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);
		gauss->SynchronizeGaussTria(gauss_tria);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		tria->GetBMacAyeal(&B[0][0], &xyz_list[0][0], gauss_tria);
		tria->GetBprimeMacAyeal(&Bprime[0][0], &xyz_list[0][0], gauss_tria);

		if(approximation==MacAyealPattynApproximationEnum){
			this->GetStrainRate3dPattyn(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
			this->GetStrainRate3dPattyn(&oldepsilon[0],&xyz_list[0][0],gauss,vxold_input,vyold_input);
			matice->GetViscosity3d(&viscosity, &epsilon[0]);
			matice->GetViscosity3d(&oldviscosity, &oldepsilon[0]);

			newviscosity=viscosity+viscosity_overshoot*(viscosity-oldviscosity);
		}
		else if (approximation==MacAyealStokesApproximationEnum){
			this->GetStrainRate3d(&epsilons[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
			matice->GetViscosity3dStokes(&newviscosity,&epsilons[0]);
		}
		else _error2_("approximation " << approximation << " (" << EnumToStringx(approximation) << ") not supported yet");

		D_scalar=2*newviscosity*gauss->weight*Jdet;
		for (i=0;i<3;i++) D[i][i]=D_scalar;

		TripleMultiply( &B[0][0],3,numdof2d,1,
					&D[0][0],3,3,0,
					&Bprime[0][0],3,numdof2d,0,
					&Ke_gg_gaussian[0][0],0);

		for(i=0;i<numdof2d;i++) for(j=0;j<numdof2d;j++) Ke->values[i*numdof2d+j]+=Ke_gg_gaussian[i][j];
	}

	/*Transform Coordinate System*/
	TransformStiffnessMatrixCoord(Ke,tria->nodes,NUMVERTICES2D,XYEnum);

	/*Clean up and return*/
	delete tria->matice;
	delete tria;
	delete gauss_tria;
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixDiagnosticMacAyeal3dFriction{{{*/
ElementMatrix* Penta::CreateKMatrixDiagnosticMacAyeal3dFriction(void){

	/*Initialize Element matrix and return if necessary*/
	if(IsFloating() || !IsOnBed()) return NULL;

	/*Build a tria element using the 3 nodes of the base of the penta. Then use 
	 * the tria functionality to build a friction stiffness matrix on these 3
	 * nodes: */
	Tria* tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.
	ElementMatrix* Ke=tria->CreateKMatrixDiagnosticMacAyealFriction();
	delete tria->matice; delete tria;

	/*clean-up and return*/
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixDiagnosticMacAyealPattyn{{{*/
ElementMatrix* Penta::CreateKMatrixDiagnosticMacAyealPattyn(void){

	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixDiagnosticMacAyeal3d();
	ElementMatrix* Ke2=CreateKMatrixDiagnosticPattyn();
	ElementMatrix* Ke3=CreateKMatrixCouplingMacAyealPattyn();
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2,Ke3);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	delete Ke3;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixDiagnosticMacAyealStokes{{{*/
ElementMatrix* Penta::CreateKMatrixDiagnosticMacAyealStokes(void){

	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixDiagnosticMacAyeal3d();
	ElementMatrix* Ke2=CreateKMatrixDiagnosticStokes();
	ElementMatrix* Ke3=CreateKMatrixCouplingMacAyealStokes();
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2,Ke3);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	delete Ke3;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixDiagnosticPattyn{{{*/
ElementMatrix* Penta::CreateKMatrixDiagnosticPattyn(void){

	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixDiagnosticPattynViscous();
	ElementMatrix* Ke2=CreateKMatrixDiagnosticPattynFriction();
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	return Ke;

}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixDiagnosticPattynViscous{{{*/
ElementMatrix* Penta::CreateKMatrixDiagnosticPattynViscous(void){

	/*Constants*/
	const int    numdof=NDOF2*NUMVERTICES;

	/*Intermediaries */
	int        i,j,ig;
	int        approximation;
	IssmDouble     xyz_list[NUMVERTICES][3];
	IssmDouble     Jdet;
	IssmDouble     viscosity,oldviscosity,newviscosity,viscosity_overshoot; //viscosity
	IssmDouble     epsilon[5],oldepsilon[5]; /* epsilon=[exx,eyy,exy,exz,eyz];*/
	IssmDouble     D_scalar;
	IssmDouble     D[5][5]={0.0};            // material matrix, simple scalar matrix.
	IssmDouble     B[5][numdof];
	IssmDouble     Bprime[5][numdof];
	Tria*      tria=NULL;
	GaussPenta *gauss=NULL;

	/*Initialize Element matrix*/
	ElementMatrix* Ke=new ElementMatrix(nodes,NUMVERTICES,this->parameters,PattynApproximationEnum);

	/*Retrieve all inputs and parameters*/
	inputs->GetInputValue(&approximation,ApproximationEnum);
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	this->parameters->FindParam(&viscosity_overshoot,DiagnosticViscosityOvershootEnum);
	Input* vx_input=inputs->GetInput(VxEnum);       _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);       _assert_(vy_input);
	Input* vxold_input=inputs->GetInput(VxPicardEnum); _assert_(vxold_input);
	Input* vyold_input=inputs->GetInput(VyPicardEnum); _assert_(vyold_input);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(5,5);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetBPattyn(&B[0][0], &xyz_list[0][0], gauss);
		GetBprimePattyn(&Bprime[0][0], &xyz_list[0][0], gauss);

		this->GetStrainRate3dPattyn(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
		this->GetStrainRate3dPattyn(&oldepsilon[0],&xyz_list[0][0],gauss,vxold_input,vyold_input);
		matice->GetViscosity3d(&viscosity, &epsilon[0]);
		matice->GetViscosity3d(&oldviscosity, &oldepsilon[0]);
		newviscosity=viscosity+viscosity_overshoot*(viscosity-oldviscosity);

		D_scalar=2*newviscosity*gauss->weight*Jdet;
		for (i=0;i<5;i++) D[i][i]=D_scalar;

		TripleMultiply( &B[0][0],5,numdof,1,
					&D[0][0],5,5,0,
					&Bprime[0][0],5,numdof,0,
					&Ke->values[0],1);
	}

	/*Transform Coordinate System*/
	TransformStiffnessMatrixCoord(Ke,nodes,NUMVERTICES,XYEnum);

	/*Clean up and return*/
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixDiagnosticPattynFriction{{{*/
ElementMatrix* Penta::CreateKMatrixDiagnosticPattynFriction(void){

	/*Constants*/
	const int numdof   = NDOF2*NUMVERTICES;
	
	/*Intermediaries */
	int       i,j,ig;
	int       analysis_type;
	IssmDouble    xyz_list[NUMVERTICES][3];
	IssmDouble    xyz_list_tria[NUMVERTICES2D][3]={0.0};
	IssmDouble    slope_magnitude,alpha2,Jdet;
	IssmDouble    slope[3]={0.0,0.0,0.0};
	IssmDouble    MAXSLOPE=.06; // 6 %
	IssmDouble    MOUNTAINKEXPONENT=10;
	IssmDouble    L[2][numdof];
	IssmDouble    DL[2][2]={{ 0,0 },{0,0}}; //for basal drag
	IssmDouble    DL_scalar;
	Friction  *friction = NULL;
	GaussPenta *gauss=NULL;

	/*Initialize Element matrix and return if necessary*/
	if(IsFloating() || !IsOnBed()) return NULL;

	ElementMatrix* Ke=new ElementMatrix(nodes,NUMVERTICES,this->parameters,PattynApproximationEnum);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<2;j++) xyz_list_tria[i][j]=xyz_list[i][j];
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	Input* surface_input=inputs->GetInput(SurfaceEnum); _assert_(surface_input);
	Input* vx_input=inputs->GetInput(VxEnum);           _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);           _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);           _assert_(vz_input);

	/*build friction object, used later on: */
	friction=new Friction("2d",inputs,matpar,analysis_type);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(0,1,2,2);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetTriaJacobianDeterminant(&Jdet, &xyz_list_tria[0][0],gauss);
		GetL(&L[0][0], gauss,NDOF2);

		surface_input->GetInputDerivativeValue(&slope[0],&xyz_list[0][0],gauss);
		friction->GetAlpha2(&alpha2, gauss,VxEnum,VyEnum,VzEnum); 
		slope_magnitude=sqrt(pow(slope[0],2)+pow(slope[1],2));

		// If we have a slope > 6% for this element,  it means  we are on a mountain. In this particular case, 
		//velocity should be = 0. To achieve this result, we set alpha2_list to a very high value: */
		if (slope_magnitude>MAXSLOPE){
			alpha2=pow((IssmDouble)10,MOUNTAINKEXPONENT);
		}
		
		DL_scalar=alpha2*gauss->weight*Jdet;
		for (i=0;i<2;i++) DL[i][i]=DL_scalar;
		
		TripleMultiply( &L[0][0],2,numdof,1,
					&DL[0][0],2,2,0,
					&L[0][0],2,numdof,0,
					&Ke->values[0],1);
	}

	/*Transform Coordinate System*/
	TransformStiffnessMatrixCoord(Ke,nodes,NUMVERTICES,XYEnum);

	/*Clean up and return*/
	delete gauss;
	delete friction;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixDiagnosticPattynStokes{{{*/
ElementMatrix* Penta::CreateKMatrixDiagnosticPattynStokes(void){

	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixDiagnosticPattyn();
	ElementMatrix* Ke2=CreateKMatrixDiagnosticStokes();
	ElementMatrix* Ke3=CreateKMatrixCouplingPattynStokes();
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2,Ke3);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	delete Ke3;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixDiagnosticStokes{{{*/
ElementMatrix* Penta::CreateKMatrixDiagnosticStokes(void){

	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixDiagnosticStokesViscous();
	ElementMatrix* Ke2=CreateKMatrixDiagnosticStokesFriction();
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixDiagnosticStokesViscous {{{*/
ElementMatrix* Penta::CreateKMatrixDiagnosticStokesViscous(void){

	/*Intermediaries */
	int        i,j,ig,approximation;
	IssmDouble     Jdet,viscosity,stokesreconditioning;
	IssmDouble     xyz_list[NUMVERTICES][3];
	IssmDouble     epsilon[6]; /* epsilon=[exx,eyy,ezz,exy,exz,eyz];*/
	IssmDouble     B[8][27];
	IssmDouble     B_prime[8][27];
	IssmDouble     D_scalar;
	IssmDouble     D[8][8]={0.0};
	IssmDouble     Ke_temp[27][27]={0.0}; //for the six nodes and the bubble 
	GaussPenta *gauss=NULL;

	/*If on water or not Stokes, skip stiffness: */
	inputs->GetInputValue(&approximation,ApproximationEnum);
	if(approximation!=StokesApproximationEnum && approximation!=MacAyealStokesApproximationEnum && approximation!=PattynStokesApproximationEnum) return NULL;
	ElementMatrix* Ke=new ElementMatrix(nodes,NUMVERTICES,this->parameters,StokesApproximationEnum);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	parameters->FindParam(&stokesreconditioning,DiagnosticStokesreconditioningEnum);
	Input* vx_input=inputs->GetInput(VxEnum); _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum); _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum); _assert_(vz_input);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(5,5);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetBStokes(&B[0][0],&xyz_list[0][0],gauss); 
		GetBprimeStokes(&B_prime[0][0],&xyz_list[0][0],gauss); 

		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		matice->GetViscosity3dStokes(&viscosity,&epsilon[0]);

		D_scalar=gauss->weight*Jdet;
		for (i=0;i<6;i++) D[i][i]=D_scalar*2*viscosity;
		for (i=6;i<8;i++) D[i][i]=-D_scalar*stokesreconditioning;

		TripleMultiply( &B[0][0],8,27,1,
					&D[0][0],8,8,0,
					&B_prime[0][0],8,27,0,
					&Ke_temp[0][0],1);
	}

	/*Condensation*/
	ReduceMatrixStokes(Ke->values, &Ke_temp[0][0]);

	/*Transform Coordinate System*/
	TransformStiffnessMatrixCoord(Ke,nodes,NUMVERTICES,XYZPEnum);

	/*Clean up and return*/
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixDiagnosticStokesFriction{{{*/
ElementMatrix* Penta::CreateKMatrixDiagnosticStokesFriction(void){

	/*Constants*/
	const int numdof=NUMVERTICES*NDOF4;
	const int numdof2d=NUMVERTICES2D*NDOF4;

	/*Intermediaries */
	int        i,j,ig;
	int        analysis_type,approximation;
	IssmDouble     alpha2,Jdet2d;
	IssmDouble     stokesreconditioning,viscosity;
	IssmDouble     epsilon[6]; /* epsilon=[exx,eyy,ezz,exy,exz,eyz];*/
	IssmDouble     xyz_list[NUMVERTICES][3];
	IssmDouble	  xyz_list_tria[NUMVERTICES2D][3];
	IssmDouble     LStokes[2][numdof2d];
	IssmDouble     DLStokes[2][2]={0.0};
	IssmDouble     Ke_drag_gaussian[numdof2d][numdof2d];
	Friction*  friction=NULL;
	GaussPenta *gauss=NULL;

	/*If on water or not Stokes, skip stiffness: */
	inputs->GetInputValue(&approximation,ApproximationEnum);
	if(IsFloating() || !IsOnBed() || (approximation!=StokesApproximationEnum && approximation!=MacAyealStokesApproximationEnum &&  approximation!=PattynStokesApproximationEnum)) return NULL;
	ElementMatrix* Ke=new ElementMatrix(nodes,NUMVERTICES,this->parameters,StokesApproximationEnum);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	parameters->FindParam(&stokesreconditioning,DiagnosticStokesreconditioningEnum);
	Input* vx_input=inputs->GetInput(VxEnum); _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum); _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum); _assert_(vz_input);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;j++) xyz_list_tria[i][j]=xyz_list[i][j];

	/*build friction object, used later on: */
	friction=new Friction("3d",inputs,matpar,analysis_type);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(0,1,2,2);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetTriaJacobianDeterminant(&Jdet2d, &xyz_list_tria[0][0],gauss);
		GetLStokes(&LStokes[0][0], gauss);

		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		matice->GetViscosity3dStokes(&viscosity,&epsilon[0]);

		friction->GetAlpha2(&alpha2, gauss,VxEnum,VyEnum,VzEnum);

		DLStokes[0][0] = +alpha2*gauss->weight*Jdet2d; //taub_x = -alpha2 vx
		DLStokes[1][1] = +alpha2*gauss->weight*Jdet2d; //taub_y = -alpha2 vy

		TripleMultiply( &LStokes[0][0],2,numdof2d,1,
					&DLStokes[0][0],2,2,0,
					&LStokes[0][0],2,numdof2d,0,
					&Ke_drag_gaussian[0][0],0);

		for(i=0;i<numdof2d;i++) for(j=0;j<numdof2d;j++) Ke->values[i*numdof+j]+=Ke_drag_gaussian[i][j];
	}

	/*DO NOT Transform Coordinate System: this stiffness matrix is already expressed in tangential coordinates*/
	//TransformStiffnessMatrixCoord(Ke,nodes,NUMVERTICES,XYZPEnum);
	
	/*Clean up and return*/
	delete gauss;
	delete friction;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixDiagnosticVert {{{*/
ElementMatrix* Penta::CreateKMatrixDiagnosticVert(void){
	
	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixDiagnosticVertVolume();
	ElementMatrix* Ke2=CreateKMatrixDiagnosticVertSurface();
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	return Ke;

}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixDiagnosticVertVolume {{{*/
ElementMatrix* Penta::CreateKMatrixDiagnosticVertVolume(void){

	/*Constants*/
	const int    numdof=NDOF1*NUMVERTICES;

	/*Intermediaries */
	int         i,j,ig;
	IssmDouble      Jdet;
	IssmDouble      xyz_list[NUMVERTICES][3];
	IssmDouble      B[NDOF1][NUMVERTICES];
	IssmDouble      Bprime[NDOF1][NUMVERTICES];
	IssmDouble      DL_scalar;
	GaussPenta  *gauss=NULL;

	/*Initialize Element matrix*/
	ElementMatrix* Ke=new ElementMatrix(nodes,NUMVERTICES,this->parameters,NoneApproximationEnum);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(2,2);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetBVert(&B[0][0], &xyz_list[0][0], gauss);
		GetBprimeVert(&Bprime[0][0], &xyz_list[0][0], gauss);

		DL_scalar=gauss->weight*Jdet;

		TripleMultiply( &B[0][0],1,NUMVERTICES,1,
					&DL_scalar,1,1,0,
					&Bprime[0][0],1,NUMVERTICES,0,
					&Ke->values[0],1);
	} 

	/*Clean up and return*/
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixDiagnosticVertSurface {{{*/
ElementMatrix* Penta::CreateKMatrixDiagnosticVertSurface(void){

	if (!IsOnSurface()) return NULL;

	/*Constants*/
	const int numdof=NDOF1*NUMVERTICES;

	/*Intermediaries */
	int       i,j,ig;
	IssmDouble    xyz_list[NUMVERTICES][3];
	IssmDouble    xyz_list_tria[NUMVERTICES2D][3];
	IssmDouble    surface_normal[3];
	IssmDouble    Jdet2d,DL_scalar;
	IssmDouble    basis[NUMVERTICES];
	GaussPenta *gauss=NULL;

	/*Initialize Element matrix*/
	ElementMatrix* Ke=new ElementMatrix(nodes,NUMVERTICES,this->parameters,NoneApproximationEnum);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;j++) xyz_list_tria[i][j]=xyz_list[i+3][j];
	SurfaceNormal(&surface_normal[0],xyz_list_tria);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(3,4,5,2);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetTriaJacobianDeterminant(&Jdet2d, &xyz_list_tria[0][0],gauss);
		GetNodalFunctionsP1(&basis[0], gauss);

		DL_scalar= - gauss->weight*Jdet2d*surface_normal[2]; 

		TripleMultiply( basis,1,numdof,1,
					&DL_scalar,1,1,0,
					basis,1,numdof,0,
					&Ke->values[0],1);
	}

	/*Clean up and return*/
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorCouplingMacAyealStokes {{{*/
ElementVector* Penta::CreatePVectorCouplingMacAyealStokes(void){

	/*compute all load vectors for this element*/
	ElementVector* pe1=CreatePVectorCouplingMacAyealStokesViscous();
	ElementVector* pe2=CreatePVectorCouplingMacAyealStokesFriction();
	ElementVector* pe =new ElementVector(pe1,pe2);

	/*clean-up and return*/
	delete pe1;
	delete pe2;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorCouplingMacAyealStokesViscous {{{*/
ElementVector* Penta::CreatePVectorCouplingMacAyealStokesViscous(void){

	/*Constants*/
	const int   numdof=NUMVERTICES*NDOF4;

	/*Intermediaries */
	int         i,j,ig;
	int         approximation;
	IssmDouble      viscosity,Jdet;
	IssmDouble      stokesreconditioning;
	IssmDouble      epsilon[6]; /* epsilon=[exx,eyy,ezz,exy,exz,eyz];*/
	IssmDouble      dw[3];
	IssmDouble      xyz_list[NUMVERTICES][3];
	IssmDouble      basis[6]; //for the six nodes of the penta
	IssmDouble      dbasis[3][6]; //for the six nodes of the penta
	GaussPenta *gauss=NULL;

	/*Initialize Element vector and return if necessary*/
	inputs->GetInputValue(&approximation,ApproximationEnum);
	if(approximation!=MacAyealStokesApproximationEnum) return NULL;
	ElementVector* pe=new ElementVector(nodes,NUMVERTICES,this->parameters,StokesApproximationEnum);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	this->parameters->FindParam(&stokesreconditioning,DiagnosticStokesreconditioningEnum);
	Input* vx_input=inputs->GetInput(VxEnum);               _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);               _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);               _assert_(vz_input);
	Input* vzmacayeal_input=inputs->GetInput(VzMacAyealEnum);   _assert_(vzmacayeal_input);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(5,5);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctionsP1(&basis[0], gauss);
		GetNodalFunctionsP1Derivatives(&dbasis[0][0],&xyz_list[0][0], gauss);

		vzmacayeal_input->GetInputDerivativeValue(&dw[0],&xyz_list[0][0],gauss);

		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		matice->GetViscosity3dStokes(&viscosity,&epsilon[0]);

		for(i=0;i<NUMVERTICES;i++){
			pe->values[i*NDOF4+0]+=-Jdet*gauss->weight*viscosity*dw[0]*dbasis[2][i];
			pe->values[i*NDOF4+1]+=-Jdet*gauss->weight*viscosity*dw[1]*dbasis[2][i];
			pe->values[i*NDOF4+2]+=-Jdet*gauss->weight*viscosity*(dw[0]*dbasis[0][i]+dw[1]*dbasis[1][i]+2*dw[2]*dbasis[2][i]);
			pe->values[i*NDOF4+3]+=Jdet*gauss->weight*stokesreconditioning*dw[2]*basis[i];
		}
	}

	/*Transform coordinate system*/
	TransformLoadVectorCoord(pe,nodes,NUMVERTICES,XYZPEnum);

	/*Clean up and return*/
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorCouplingMacAyealStokesFriction{{{*/
ElementVector* Penta::CreatePVectorCouplingMacAyealStokesFriction(void){

	/*Constants*/
	const int numdof=NUMVERTICES*NDOF4;

	/*Intermediaries*/
	int         i,j,ig;
	int         approximation,analysis_type;
	IssmDouble      Jdet,Jdet2d;
	IssmDouble      stokesreconditioning;
	IssmDouble	   bed_normal[3];
	IssmDouble      epsilon[6]; /* epsilon=[exx,eyy,ezz,exy,exz,eyz];*/
	IssmDouble      viscosity, w, alpha2_gauss;
	IssmDouble      dw[3];
	IssmDouble	   xyz_list_tria[NUMVERTICES2D][3];
	IssmDouble      xyz_list[NUMVERTICES][3];
	IssmDouble      basis[6]; //for the six nodes of the penta
	Tria*       tria=NULL;
	Friction*   friction=NULL;
	GaussPenta  *gauss=NULL;

	/*Initialize Element vector and return if necessary*/
	if(!IsOnBed() || IsFloating()) return NULL;
	inputs->GetInputValue(&approximation,ApproximationEnum);
	if(approximation!=MacAyealStokesApproximationEnum) return NULL;
	ElementVector* pe=new ElementVector(nodes,NUMVERTICES,this->parameters,StokesApproximationEnum);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	this->parameters->FindParam(&stokesreconditioning,DiagnosticStokesreconditioningEnum);
	Input* vx_input=inputs->GetInput(VxEnum);               _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);               _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);               _assert_(vz_input);
	Input* vzmacayeal_input=inputs->GetInput(VzMacAyealEnum);   _assert_(vzmacayeal_input);

	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;j++) xyz_list_tria[i][j]=xyz_list[i][j];

	/*build friction object, used later on: */
	friction=new Friction("3d",inputs,matpar,analysis_type);

	/* Start looping on the number of gauss 2d (nodes on the bedrock) */
	gauss=new GaussPenta(0,1,2,2);
	for(ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetTriaJacobianDeterminant(&Jdet2d, &xyz_list_tria[0][0], gauss);
		GetNodalFunctionsP1(basis, gauss);

		vzmacayeal_input->GetInputValue(&w, gauss);
		vzmacayeal_input->GetInputDerivativeValue(&dw[0],&xyz_list[0][0],gauss);

		BedNormal(&bed_normal[0],xyz_list_tria);
		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		matice->GetViscosity3dStokes(&viscosity,&epsilon[0]);
		friction->GetAlpha2(&alpha2_gauss, gauss,VxEnum,VyEnum,VzEnum);

		for(i=0;i<NUMVERTICES2D;i++){
			pe->values[i*NDOF4+0]+=Jdet2d*gauss->weight*(alpha2_gauss*w*bed_normal[0]*bed_normal[2]+2*viscosity*dw[2]*bed_normal[0])*basis[i];
			pe->values[i*NDOF4+1]+=Jdet2d*gauss->weight*(alpha2_gauss*w*bed_normal[1]*bed_normal[2]+2*viscosity*dw[2]*bed_normal[1])*basis[i];
			pe->values[i*NDOF4+2]+=Jdet2d*gauss->weight*2*viscosity*(dw[0]*bed_normal[0]+dw[1]*bed_normal[1]+dw[2]*bed_normal[2])*basis[i];
		}
	}

	/*Transform coordinate system*/
	TransformLoadVectorCoord(pe,nodes,NUMVERTICES,XYZPEnum);

	/*Clean up and return*/
	delete gauss;
	delete friction;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorCouplingPattynStokes {{{*/
ElementVector* Penta::CreatePVectorCouplingPattynStokes(void){

	/*compute all load vectors for this element*/
	ElementVector* pe1=CreatePVectorCouplingPattynStokesViscous();
	ElementVector* pe2=CreatePVectorCouplingPattynStokesFriction();
	ElementVector* pe =new ElementVector(pe1,pe2);

	/*clean-up and return*/
	delete pe1;
	delete pe2;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorCouplingPattynStokesViscous {{{*/
ElementVector* Penta::CreatePVectorCouplingPattynStokesViscous(void){

	/*Constants*/
	const int   numdof=NUMVERTICES*NDOF4;

	/*Intermediaries */
	int         i,j,ig;
	int         approximation;
	IssmDouble      viscosity,Jdet;
	IssmDouble      stokesreconditioning;
	IssmDouble      epsilon[6]; /* epsilon=[exx,eyy,ezz,exy,exz,eyz];*/
	IssmDouble      dw[3];
	IssmDouble      xyz_list[NUMVERTICES][3];
	IssmDouble      basis[6]; //for the six nodes of the penta
	IssmDouble      dbasis[3][6]; //for the six nodes of the penta
	GaussPenta *gauss=NULL;

	/*Initialize Element vector and return if necessary*/
	inputs->GetInputValue(&approximation,ApproximationEnum);
	if(approximation!=PattynStokesApproximationEnum) return NULL;
	ElementVector* pe=new ElementVector(nodes,NUMVERTICES,this->parameters,StokesApproximationEnum);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	this->parameters->FindParam(&stokesreconditioning,DiagnosticStokesreconditioningEnum);
	Input* vx_input=inputs->GetInput(VxEnum);               _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);               _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);               _assert_(vz_input);
	Input* vzpattyn_input=inputs->GetInput(VzPattynEnum);   _assert_(vzpattyn_input);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(5,5);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctionsP1(&basis[0], gauss);
		GetNodalFunctionsP1Derivatives(&dbasis[0][0],&xyz_list[0][0], gauss);

		vzpattyn_input->GetInputDerivativeValue(&dw[0],&xyz_list[0][0],gauss);

		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		matice->GetViscosity3dStokes(&viscosity,&epsilon[0]);

		for(i=0;i<NUMVERTICES;i++){
			pe->values[i*NDOF4+0]+=-Jdet*gauss->weight*viscosity*dw[0]*dbasis[2][i];
			pe->values[i*NDOF4+1]+=-Jdet*gauss->weight*viscosity*dw[1]*dbasis[2][i];
			pe->values[i*NDOF4+2]+=-Jdet*gauss->weight*viscosity*(dw[0]*dbasis[0][i]+dw[1]*dbasis[1][i]+2*dw[2]*dbasis[2][i]);
			pe->values[i*NDOF4+3]+=Jdet*gauss->weight*stokesreconditioning*dw[2]*basis[i];
		}
	}

	/*Transform coordinate system*/
	TransformLoadVectorCoord(pe,nodes,NUMVERTICES,XYZPEnum);

	/*Clean up and return*/
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorCouplingPattynStokesFriction{{{*/
ElementVector* Penta::CreatePVectorCouplingPattynStokesFriction(void){

	/*Constants*/
	const int numdof=NUMVERTICES*NDOF4;

	/*Intermediaries*/
	int         i,j,ig;
	int         approximation,analysis_type;
	IssmDouble      Jdet,Jdet2d;
	IssmDouble      stokesreconditioning;
	IssmDouble	   bed_normal[3];
	IssmDouble      epsilon[6]; /* epsilon=[exx,eyy,ezz,exy,exz,eyz];*/
	IssmDouble      viscosity, w, alpha2_gauss;
	IssmDouble      dw[3];
	IssmDouble	   xyz_list_tria[NUMVERTICES2D][3];
	IssmDouble      xyz_list[NUMVERTICES][3];
	IssmDouble      basis[6]; //for the six nodes of the penta
	Tria*       tria=NULL;
	Friction*   friction=NULL;
	GaussPenta  *gauss=NULL;

	/*Initialize Element vector and return if necessary*/
	if(!IsOnBed() || IsFloating()) return NULL;
	inputs->GetInputValue(&approximation,ApproximationEnum);
	if(approximation!=PattynStokesApproximationEnum) return NULL;
	ElementVector* pe=new ElementVector(nodes,NUMVERTICES,this->parameters,StokesApproximationEnum);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	this->parameters->FindParam(&stokesreconditioning,DiagnosticStokesreconditioningEnum);
	Input* vx_input=inputs->GetInput(VxEnum);               _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);               _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);               _assert_(vz_input);
	Input* vzpattyn_input=inputs->GetInput(VzPattynEnum);   _assert_(vzpattyn_input);

	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;j++) xyz_list_tria[i][j]=xyz_list[i][j];

	/*build friction object, used later on: */
	friction=new Friction("3d",inputs,matpar,analysis_type);

	/* Start looping on the number of gauss 2d (nodes on the bedrock) */
	gauss=new GaussPenta(0,1,2,2);
	for(ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetTriaJacobianDeterminant(&Jdet2d, &xyz_list_tria[0][0], gauss);
		GetNodalFunctionsP1(basis, gauss);

		vzpattyn_input->GetInputValue(&w, gauss);
		vzpattyn_input->GetInputDerivativeValue(&dw[0],&xyz_list[0][0],gauss);

		BedNormal(&bed_normal[0],xyz_list_tria);
		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		matice->GetViscosity3dStokes(&viscosity,&epsilon[0]);
		friction->GetAlpha2(&alpha2_gauss, gauss,VxEnum,VyEnum,VzEnum);

		for(i=0;i<NUMVERTICES2D;i++){
			pe->values[i*NDOF4+0]+=Jdet2d*gauss->weight*(alpha2_gauss*w*bed_normal[0]*bed_normal[2]+2*viscosity*dw[2]*bed_normal[0])*basis[i];
			pe->values[i*NDOF4+1]+=Jdet2d*gauss->weight*(alpha2_gauss*w*bed_normal[1]*bed_normal[2]+2*viscosity*dw[2]*bed_normal[1])*basis[i];
			pe->values[i*NDOF4+2]+=Jdet2d*gauss->weight*2*viscosity*(dw[0]*bed_normal[0]+dw[1]*bed_normal[1]+dw[2]*bed_normal[2])*basis[i];
		}
	}

	/*Transform coordinate system*/
	TransformLoadVectorCoord(pe,nodes,NUMVERTICES,XYZPEnum);

	/*Clean up and return*/
	delete gauss;
	delete friction;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorDiagnosticHoriz{{{*/
ElementVector* Penta::CreatePVectorDiagnosticHoriz(void){

	int approximation;
	inputs->GetInputValue(&approximation,ApproximationEnum);

	switch(approximation){
		case MacAyealApproximationEnum:
			return CreatePVectorDiagnosticMacAyeal();
		case PattynApproximationEnum:
			return CreatePVectorDiagnosticPattyn();
		case HutterApproximationEnum:
			return NULL;
		case NoneApproximationEnum:
			return NULL;
		case StokesApproximationEnum:
			return CreatePVectorDiagnosticStokes();
		case MacAyealPattynApproximationEnum:
			return CreatePVectorDiagnosticMacAyealPattyn();
		case MacAyealStokesApproximationEnum:
			return CreatePVectorDiagnosticMacAyealStokes();
		case PattynStokesApproximationEnum:
			return CreatePVectorDiagnosticPattynStokes();
		default:
			_error2_("Approximation " << EnumToStringx(approximation) << " not supported yet");
	}
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorDiagnosticMacAyealPattyn{{{*/
ElementVector* Penta::CreatePVectorDiagnosticMacAyealPattyn(void){

	/*compute all load vectors for this element*/
	ElementVector* pe1=CreatePVectorDiagnosticMacAyeal();
	ElementVector* pe2=CreatePVectorDiagnosticPattyn();
	ElementVector* pe =new ElementVector(pe1,pe2);

	/*clean-up and return*/
	delete pe1;
	delete pe2;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorDiagnosticMacAyealStokes{{{*/
ElementVector* Penta::CreatePVectorDiagnosticMacAyealStokes(void){

	/*compute all load vectors for this element*/
	ElementVector* pe1=CreatePVectorDiagnosticMacAyeal();
	ElementVector* pe2=CreatePVectorDiagnosticStokes();
	ElementVector* pe3=CreatePVectorCouplingMacAyealStokes();
	ElementVector* pe =new ElementVector(pe1,pe2,pe3);

	/*clean-up and return*/
	delete pe1;
	delete pe2;
	delete pe3;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorDiagnosticPattynStokes{{{*/
ElementVector* Penta::CreatePVectorDiagnosticPattynStokes(void){

	/*compute all load vectors for this element*/
	ElementVector* pe1=CreatePVectorDiagnosticPattyn();
	ElementVector* pe2=CreatePVectorDiagnosticStokes();
	ElementVector* pe3=CreatePVectorCouplingPattynStokes();
	ElementVector* pe =new ElementVector(pe1,pe2,pe3);

	/*clean-up and return*/
	delete pe1;
	delete pe2;
	delete pe3;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorDiagnosticHutter{{{*/
ElementVector* Penta::CreatePVectorDiagnosticHutter(void){

	/*Constants*/
	const int numdofs=NDOF2*NUMVERTICES;

	/*Intermediaries*/
	int          i,j,k,ig;
	int          node0,node1;
	int          connectivity[2];
	IssmDouble       Jdet;
	IssmDouble       xyz_list[NUMVERTICES][3];
	IssmDouble       xyz_list_segment[2][3];
	IssmDouble       z_list[NUMVERTICES];
	IssmDouble       z_segment[2],slope[2];
	IssmDouble       slope2,constant_part;
	IssmDouble       rho_ice,gravity,n,B;
	IssmDouble       ub,vb,z_g,surface,thickness;
	GaussPenta*  gauss=NULL;

	/*Initialize Element vector*/
	ElementVector* pe=new ElementVector(nodes,NUMVERTICES,this->parameters);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	rho_ice=matpar->GetRhoIce();
	gravity=matpar->GetG();
	n=matice->GetN();
	B=matice->GetB();
	Input* thickness_input=inputs->GetInput(ThicknessEnum);  _assert_(thickness_input);
	Input* surface_input=inputs->GetInput(SurfaceEnum);      _assert_(surface_input);
	Input* slopex_input=inputs->GetInput(SurfaceSlopeXEnum); _assert_(slopex_input);
	Input* slopey_input=inputs->GetInput(SurfaceSlopeYEnum); _assert_(slopey_input);
	for(i=0;i<NUMVERTICES;i++)z_list[i]=xyz_list[i][2];

	/*Loop on the three segments*/
	for(i=0;i<3;i++){
		node0=i;
		node1=i+3;

		for(j=0;j<3;j++){
			xyz_list_segment[0][j]=xyz_list[node0][j];
			xyz_list_segment[1][j]=xyz_list[node1][j];
		}

		connectivity[0]=nodes[node0]->GetConnectivity();
		connectivity[1]=nodes[node1]->GetConnectivity();

		/*Loop on the Gauss points: */
		gauss=new GaussPenta(node0,node1,3);
		for(ig=gauss->begin();ig<gauss->end();ig++){
			gauss->GaussPoint(ig);

			slopex_input->GetInputValue(&slope[0],gauss);
			slopey_input->GetInputValue(&slope[1],gauss);
			surface_input->GetInputValue(&surface,gauss);
			thickness_input->GetInputValue(&thickness,gauss);

			slope2=pow(slope[0],2)+pow(slope[1],2);
			constant_part=-2*pow(rho_ice*gravity,n)*pow(slope2,((n-1)/2));

			PentaRef::GetInputValue(&z_g,&z_list[0],gauss);
			GetSegmentJacobianDeterminant(&Jdet,&xyz_list_segment[0][0],gauss);

			if (IsOnSurface()){
				for(j=0;j<NDOF2;j++) pe->values[2*node1+j]+=constant_part*pow((surface-z_g)/B,n)*slope[j]*Jdet*gauss->weight/(IssmDouble)connectivity[1];
			}
			else{//connectivity is too large, should take only half on it
				for(j=0;j<NDOF2;j++) pe->values[2*node1+j]+=constant_part*pow((surface-z_g)/B,n)*slope[j]*Jdet*gauss->weight*2/(IssmDouble)connectivity[1];
			}
		}
		delete gauss;

		//Deal with lower surface
		if (IsOnBed()){
			constant_part=-1.58*pow((IssmDouble)10.0,-(IssmDouble)10.0)*rho_ice*gravity*thickness;
			ub=constant_part*slope[0];
			vb=constant_part*slope[1];

			pe->values[2*node0]+=ub/(IssmDouble)connectivity[0];
			pe->values[2*node0+1]+=vb/(IssmDouble)connectivity[0];
		}
	}

	/*Clean up and return*/
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorDiagnosticMacAyeal{{{*/
ElementVector* Penta::CreatePVectorDiagnosticMacAyeal(void){

	if (!IsOnBed()) return NULL;

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.
	ElementVector* pe=tria->CreatePVectorDiagnosticMacAyeal();
	delete tria->matice; delete tria;

	/*Clean up and return*/
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorDiagnosticPattyn{{{*/
ElementVector* Penta::CreatePVectorDiagnosticPattyn(void){

	/*Constants*/
	const int    numdof=NDOF2*NUMVERTICES;

	/*Intermediaries*/
	int         i,j,ig;
	IssmDouble      Jdet;
	IssmDouble      slope[3]; //do not put 2! this goes into GetInputDerivativeValue, which addresses slope[3] also!
	IssmDouble      driving_stress_baseline,thickness;
	IssmDouble      xyz_list[NUMVERTICES][3];
	IssmDouble      basis[6];
	GaussPenta  *gauss=NULL;

	/*Initialize Element vector*/
	ElementVector* pe=new ElementVector(nodes,NUMVERTICES,this->parameters,PattynApproximationEnum);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	Input* thickness_input=inputs->GetInput(ThicknessEnum); _assert_(thickness_input);
	Input* surface_input=inputs->GetInput(SurfaceEnum);     _assert_(surface_input);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(2,3);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctionsP1(basis, gauss);

		thickness_input->GetInputValue(&thickness, gauss);
		surface_input->GetInputDerivativeValue(&slope[0],&xyz_list[0][0],gauss);

		driving_stress_baseline=matpar->GetRhoIce()*matpar->GetG();

		for(i=0;i<NUMVERTICES;i++) for(j=0;j<NDOF2;j++) pe->values[i*NDOF2+j]+= -driving_stress_baseline*slope[j]*Jdet*gauss->weight*basis[i];
	}

	/*Transform coordinate system*/
	TransformLoadVectorCoord(pe,nodes,NUMVERTICES,XYEnum);

	/*Clean up and return*/
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorDiagnosticStokes {{{*/
ElementVector* Penta::CreatePVectorDiagnosticStokes(void){

	/*compute all load vectors for this element*/
	ElementVector* pe1=CreatePVectorDiagnosticStokesViscous();
	ElementVector* pe2=CreatePVectorDiagnosticStokesShelf();
	ElementVector* pe =new ElementVector(pe1,pe2);

	/*clean-up and return*/
	delete pe1;
	delete pe2;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorDiagnosticStokesViscous {{{*/
ElementVector* Penta::CreatePVectorDiagnosticStokesViscous(void){

	/*Constants*/
	const int numdofbubble=NDOF4*NUMVERTICES+NDOF3*1;

	/*Intermediaries*/
	int        i,j,ig;
	int        approximation;
	IssmDouble     Jdet,viscosity;
	IssmDouble     gravity,rho_ice,stokesreconditioning;
	IssmDouble     xyz_list[NUMVERTICES][3];
	IssmDouble     epsilon[6]; /* epsilon=[exx,eyy,ezz,exy,exz,eyz];*/
	IssmDouble     l1l7[7]; //for the six nodes and the bubble 
	IssmDouble     B[8][numdofbubble];
	IssmDouble     B_prime[8][numdofbubble];
	IssmDouble     B_prime_bubble[8][3];
	IssmDouble     D[8][8]={0.0};
	IssmDouble     D_scalar;
	IssmDouble     Pe_gaussian[numdofbubble]={0.0}; //for the six nodes and the bubble 
	IssmDouble     Ke_temp[numdofbubble][3]={0.0}; //for the six nodes and the bubble 
	IssmDouble     Ke_gaussian[numdofbubble][3];
	GaussPenta *gauss=NULL;

	/*Initialize Element vector and return if necessary*/
	inputs->GetInputValue(&approximation,ApproximationEnum);
	if(approximation!=StokesApproximationEnum && approximation!=MacAyealStokesApproximationEnum && approximation!=PattynStokesApproximationEnum) return NULL;
	ElementVector* pe=new ElementVector(nodes,NUMVERTICES,this->parameters,StokesApproximationEnum);

	/*Retrieve all inputs and parameters*/
	this->parameters->FindParam(&stokesreconditioning,DiagnosticStokesreconditioningEnum);
	rho_ice=matpar->GetRhoIce();
	gravity=matpar->GetG();
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	Input* vx_input=inputs->GetInput(VxEnum);   _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);   _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);   _assert_(vz_input);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(5,5);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetBStokes(&B[0][0],&xyz_list[0][0],gauss); 
		GetBprimeStokes(&B_prime[0][0],&xyz_list[0][0], gauss); 
		GetNodalFunctionsMINI(&l1l7[0], gauss);

		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		matice->GetViscosity3dStokes(&viscosity,&epsilon[0]);

		for(i=0;i<NUMVERTICES+1;i++){
			Pe_gaussian[i*NDOF4+2]+=-rho_ice*gravity*Jdet*gauss->weight*l1l7[i];
		}

		/*Get bubble part of Bprime */
		for(i=0;i<8;i++) for(j=0;j<3;j++) B_prime_bubble[i][j]=B_prime[i][j+24];

		D_scalar=gauss->weight*Jdet;
		for (i=0;i<6;i++) D[i][i]=D_scalar*2*viscosity;
		for (i=6;i<8;i++) D[i][i]=-D_scalar*stokesreconditioning;

		TripleMultiply(&B[0][0],8,numdofbubble,1,
					&D[0][0],8,8,0,
					&B_prime_bubble[0][0],8,3,0,
					&Ke_temp[0][0],1);
	}

	/*Condensation*/
	ReduceVectorStokes(pe->values, &Ke_temp[0][0], &Pe_gaussian[0]);

	/*Transform coordinate system*/
	TransformLoadVectorCoord(pe,nodes,NUMVERTICES,XYZPEnum);

	/*Clean up and return*/
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorDiagnosticStokesShelf{{{*/
ElementVector* Penta::CreatePVectorDiagnosticStokesShelf(void){

	/*Intermediaries*/
	int         i,j,ig;
	int         approximation,shelf_dampening;
	IssmDouble      gravity,rho_water,bed,water_pressure;
	IssmDouble      damper,normal_vel,vx,vy,vz,dt;
	IssmDouble		xyz_list_tria[NUMVERTICES2D][3];
	IssmDouble      xyz_list[NUMVERTICES][3];
	IssmDouble		bed_normal[3];
	IssmDouble      dz[3];
	IssmDouble      basis[6]; //for the six nodes of the penta
	IssmDouble      Jdet2d;
	GaussPenta  *gauss=NULL;

	/*Initialize Element vector and return if necessary*/
	if(!IsOnBed() || !IsFloating()) return NULL;
	inputs->GetInputValue(&approximation,ApproximationEnum);
	this->parameters->FindParam(&shelf_dampening,DiagnosticShelfDampeningEnum);
	if(approximation!=StokesApproximationEnum && approximation!=MacAyealStokesApproximationEnum && approximation!=PattynStokesApproximationEnum) return NULL;
	ElementVector* pe=new ElementVector(nodes,NUMVERTICES,this->parameters,StokesApproximationEnum);

	/*Retrieve all inputs and parameters*/
	rho_water=matpar->GetRhoWater();
	gravity=matpar->GetG();
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	Input* bed_input=inputs->GetInput(BedEnum); _assert_(bed_input);
	Input* vx_input=inputs->GetInput(VxEnum);   _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);   _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);   _assert_(vz_input);

	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;j++) xyz_list_tria[i][j]=xyz_list[i][j];

	/* Start looping on the number of gauss 2d (nodes on the bedrock) */
	gauss=new GaussPenta(0,1,2,2);
	for(ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetTriaJacobianDeterminant(&Jdet2d, &xyz_list_tria[0][0], gauss);
		GetNodalFunctionsP1(basis, gauss);

		BedNormal(&bed_normal[0],xyz_list_tria);
		bed_input->GetInputValue(&bed, gauss);
		if(shelf_dampening){ //add dampening to avoid too high vertical velocities when not in hydrostatic equilibrium
			bed_input->GetInputDerivativeValue(&dz[0],&xyz_list[0][0],gauss);
			vx_input->GetInputValue(&vx, gauss);
			vy_input->GetInputValue(&vy, gauss);
			vz_input->GetInputValue(&vz, gauss);
			dt=0;
			normal_vel=bed_normal[0]*vx+bed_normal[1]*vy+bed_normal[2]*vz;
			damper=gravity*rho_water*pow(1+pow(dz[0],2)+pow(dz[1],2),0.5)*normal_vel*dt;
		}
		else damper=0;
		water_pressure=gravity*rho_water*bed;

		for(i=0;i<NUMVERTICES;i++) for(j=0;j<3;j++) pe->values[i*NDOF4+j]+=(water_pressure+damper)*gauss->weight*Jdet2d*basis[i]*bed_normal[j];
	}

	/*Transform coordinate system*/
	TransformLoadVectorCoord(pe,nodes,NUMVERTICES,XYZPEnum);

	/*Clean up and return*/
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorDiagnosticVert {{{*/
ElementVector* Penta::CreatePVectorDiagnosticVert(void){

	/*compute all load vectors for this element*/
	ElementVector* pe1=CreatePVectorDiagnosticVertVolume();
	ElementVector* pe2=CreatePVectorDiagnosticVertBase();
	ElementVector* pe =new ElementVector(pe1,pe2);

	/*clean-up and return*/
	delete pe1;
	delete pe2;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorDiagnosticVertVolume {{{*/
ElementVector* Penta::CreatePVectorDiagnosticVertVolume(void){

	/*Constants*/
	const int  numdof=NDOF1*NUMVERTICES;

	/*Intermediaries*/
	int        i,ig;
	int        approximation;
	IssmDouble     Jdet;
	IssmDouble     xyz_list[NUMVERTICES][3];
	IssmDouble     dudx,dvdy,dwdz;
	IssmDouble     du[3],dv[3],dw[3];
	IssmDouble     basis[6];
	GaussPenta *gauss=NULL;

	/*Initialize Element vector*/
	ElementVector* pe=new ElementVector(nodes,NUMVERTICES,this->parameters);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	inputs->GetInputValue(&approximation,ApproximationEnum);
	Input* vx_input=inputs->GetInput(VxEnum); _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum); _assert_(vy_input);
	Input* vzstokes_input=NULL;
	if(approximation==PattynStokesApproximationEnum || approximation==MacAyealStokesApproximationEnum){
		vzstokes_input=inputs->GetInput(VzStokesEnum); _assert_(vzstokes_input);
	}

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(2,2);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctionsP1(basis, gauss);

		vx_input->GetInputDerivativeValue(&du[0],&xyz_list[0][0],gauss);
		vy_input->GetInputDerivativeValue(&dv[0],&xyz_list[0][0],gauss);
		if(approximation==PattynStokesApproximationEnum || approximation==MacAyealStokesApproximationEnum){
			vzstokes_input->GetInputDerivativeValue(&dw[0],&xyz_list[0][0],gauss);
			dwdz=dw[2];
		}
		else dwdz=0;
		dudx=du[0];
		dvdy=dv[1];

		for (i=0;i<numdof;i++) pe->values[i] += (dudx+dvdy+dwdz)*Jdet*gauss->weight*basis[i];
	}

	/*Clean up and return*/
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorDiagnosticVertBase {{{*/
ElementVector* Penta::CreatePVectorDiagnosticVertBase(void){


	/*Constants*/
	const int    numdof=NDOF1*NUMVERTICES;

	/*Intermediaries */
	int        i,j,ig;
	int        approximation;
	IssmDouble     xyz_list[NUMVERTICES][3];
	IssmDouble     xyz_list_tria[NUMVERTICES2D][3];
	IssmDouble     Jdet2d;
	IssmDouble     vx,vy,vz,dbdx,dbdy,basalmeltingvalue;
	IssmDouble     slope[3];
	IssmDouble     basis[NUMVERTICES];
	GaussPenta* gauss=NULL;

	if (!IsOnBed()) return NULL;

	/*Initialize Element vector*/
	ElementVector* pe=new ElementVector(nodes,NUMVERTICES,this->parameters);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;j++) xyz_list_tria[i][j]=xyz_list[i][j];
	inputs->GetInputValue(&approximation,ApproximationEnum);
	Input* bed_input=inputs->GetInput(BedEnum);                                _assert_(bed_input);
	Input* basal_melting_input=inputs->GetInput(BasalforcingsMeltingRateEnum); _assert_(basal_melting_input);
	Input* vx_input=inputs->GetInput(VxEnum);                                  _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);                                  _assert_(vy_input);
	Input* vzstokes_input=NULL;
	if(approximation==PattynStokesApproximationEnum || approximation==MacAyealStokesApproximationEnum){
		vzstokes_input=inputs->GetInput(VzStokesEnum);       _assert_(vzstokes_input);
	}

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(0,1,2,2);
	for(ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		basal_melting_input->GetInputValue(&basalmeltingvalue, gauss);
		bed_input->GetInputDerivativeValue(&slope[0],&xyz_list[0][0],gauss);
		vx_input->GetInputValue(&vx, gauss);
		vy_input->GetInputValue(&vy, gauss);
		if(approximation==PattynStokesApproximationEnum || approximation==MacAyealStokesApproximationEnum){
			vzstokes_input->GetInputValue(&vz, gauss);
		}
		else vz=0;

		dbdx=slope[0];
		dbdy=slope[1];

		GetTriaJacobianDeterminant(&Jdet2d, &xyz_list_tria[0][0],gauss);
		GetNodalFunctionsP1(&basis[0], gauss);

		for(i=0;i<numdof;i++) pe->values[i]+=-Jdet2d*gauss->weight*(vx*dbdx+vy*dbdy-vz-basalmeltingvalue)*basis[i];
	}

	/*Clean up and return*/
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreateJacobianDiagnosticHoriz{{{*/
ElementMatrix* Penta::CreateJacobianDiagnosticHoriz(void){

	int approximation;
	inputs->GetInputValue(&approximation,ApproximationEnum);

	switch(approximation){
		case MacAyealApproximationEnum:
			return CreateJacobianDiagnosticMacayeal2d();
		case PattynApproximationEnum:
			return CreateJacobianDiagnosticPattyn();
		case StokesApproximationEnum:
			return CreateJacobianDiagnosticStokes();
		case NoneApproximationEnum:
			return NULL;
		default:
			_error2_("Approximation " << EnumToStringx(approximation) << " not supported yet");
	}
}
/*}}}*/
/*FUNCTION Penta::CreateJacobianDiagnosticMacayeal2d{{{*/
ElementMatrix* Penta::CreateJacobianDiagnosticMacayeal2d(void){

	/*Figure out if this penta is collapsed. If so, then bailout, except if it is at the 
	  bedrock, in which case we spawn a tria element using the 3 first nodes, and use it to build 
	  the stiffness matrix. */
	if (!IsOnBed()) return NULL;

	/*Depth Averaging B*/
	this->InputDepthAverageAtBase(MaterialsRheologyBEnum,MaterialsRheologyBbarEnum,MaterialsEnum);

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.
	ElementMatrix* Ke=tria->CreateJacobianDiagnosticMacayeal();
	delete tria->matice; delete tria;

	/*Delete B averaged*/
	this->matice->inputs->DeleteInput(MaterialsRheologyBbarEnum);

	/*clean up and return*/
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateJacobianDiagnosticPattyn{{{*/
ElementMatrix* Penta::CreateJacobianDiagnosticPattyn(void){

	/*Constants*/
	const int    numdof=NDOF2*NUMVERTICES;

	/*Intermediaries */
	int        i,j,ig;
	IssmDouble     xyz_list[NUMVERTICES][3];
	IssmDouble     Jdet;
	IssmDouble     eps1dotdphii,eps1dotdphij;
	IssmDouble     eps2dotdphii,eps2dotdphij;
	IssmDouble     mu_prime;
	IssmDouble     epsilon[5]; /* epsilon=[exx,eyy,exy,exz,eyz];*/
	IssmDouble     eps1[3],eps2[3];
	IssmDouble     phi[NUMVERTICES];
	IssmDouble     dphi[3][NUMVERTICES];
	GaussPenta *gauss=NULL;

	/*Initialize Jacobian with regular Pattyn (first part of the Gateau derivative)*/
	ElementMatrix* Ke=CreateKMatrixDiagnosticPattyn();

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	Input* vx_input=inputs->GetInput(VxEnum);       _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);       _assert_(vy_input);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(5,5);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctionsP1Derivatives(&dphi[0][0],&xyz_list[0][0],gauss);

		this->GetStrainRate3dPattyn(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
		matice->GetViscosityDerivativeEpsSquare(&mu_prime,&epsilon[0]);
		eps1[0]=2*epsilon[0]+epsilon[1];   eps2[0]=epsilon[2];
		eps1[1]=epsilon[2];                eps2[1]=epsilon[0]+2*epsilon[1];
		eps1[2]=epsilon[3];                eps2[2]=epsilon[4];

		for(i=0;i<6;i++){
			for(j=0;j<6;j++){
				eps1dotdphii=eps1[0]*dphi[0][i]+eps1[1]*dphi[1][i]+eps1[2]*dphi[2][i];
				eps1dotdphij=eps1[0]*dphi[0][j]+eps1[1]*dphi[1][j]+eps1[2]*dphi[2][j];
				eps2dotdphii=eps2[0]*dphi[0][i]+eps2[1]*dphi[1][i]+eps2[2]*dphi[2][i];
				eps2dotdphij=eps2[0]*dphi[0][j]+eps2[1]*dphi[1][j]+eps2[2]*dphi[2][j];

				Ke->values[12*(2*i+0)+2*j+0]+=gauss->weight*Jdet*2*mu_prime*eps1dotdphij*eps1dotdphii;
				Ke->values[12*(2*i+0)+2*j+1]+=gauss->weight*Jdet*2*mu_prime*eps2dotdphij*eps1dotdphii;
				Ke->values[12*(2*i+1)+2*j+0]+=gauss->weight*Jdet*2*mu_prime*eps1dotdphij*eps2dotdphii;
				Ke->values[12*(2*i+1)+2*j+1]+=gauss->weight*Jdet*2*mu_prime*eps2dotdphij*eps2dotdphii;
			}
		}
	}

	/*Transform Coordinate System*/
	TransformStiffnessMatrixCoord(Ke,nodes,NUMVERTICES,XYEnum);

	/*Clean up and return*/
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateJacobianDiagnosticStokes{{{*/
ElementMatrix* Penta::CreateJacobianDiagnosticStokes(void){

	/*Constants*/
	const int    numdof=NDOF4*NUMVERTICES;

	/*Intermediaries */
	int        i,j,ig;
	IssmDouble     xyz_list[NUMVERTICES][3];
	IssmDouble     Jdet;
	IssmDouble     eps1dotdphii,eps1dotdphij;
	IssmDouble     eps2dotdphii,eps2dotdphij;
	IssmDouble     eps3dotdphii,eps3dotdphij;
	IssmDouble     mu_prime;
	IssmDouble     epsilon[5]; /* epsilon=[exx,eyy,exy,exz,eyz];*/
	IssmDouble     eps1[3],eps2[3],eps3[3];
	IssmDouble     phi[NUMVERTICES];
	IssmDouble     dphi[3][NUMVERTICES];
	GaussPenta *gauss=NULL;

	/*Initialize Jacobian with regular Stokes (first part of the Gateau derivative)*/
	ElementMatrix* Ke=CreateKMatrixDiagnosticStokes();

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);
	Input* vx_input=inputs->GetInput(VxEnum);       _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);       _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);       _assert_(vz_input);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(5,5);
	for (ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctionsP1Derivatives(&dphi[0][0],&xyz_list[0][0],gauss);

		this->GetStrainRate3dPattyn(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
		matice->GetViscosityDerivativeEpsSquare(&mu_prime,&epsilon[0]);
		eps1[0]=epsilon[0];   eps2[0]=epsilon[2];   eps3[0]=epsilon[3];
		eps1[1]=epsilon[2];   eps2[1]=epsilon[1];   eps3[1]=epsilon[4];
		eps1[2]=epsilon[3];   eps2[2]=epsilon[4];   eps3[2]= -epsilon[0] -epsilon[1];

		for(i=0;i<6;i++){
			for(j=0;j<6;j++){
				eps1dotdphii=eps1[0]*dphi[0][i]+eps1[1]*dphi[1][i]+eps1[2]*dphi[2][i];
				eps1dotdphij=eps1[0]*dphi[0][j]+eps1[1]*dphi[1][j]+eps1[2]*dphi[2][j];
				eps2dotdphii=eps2[0]*dphi[0][i]+eps2[1]*dphi[1][i]+eps2[2]*dphi[2][i];
				eps2dotdphij=eps2[0]*dphi[0][j]+eps2[1]*dphi[1][j]+eps2[2]*dphi[2][j];
				eps3dotdphii=eps3[0]*dphi[0][i]+eps3[1]*dphi[1][i]+eps3[2]*dphi[2][i];
				eps3dotdphij=eps3[0]*dphi[0][j]+eps3[1]*dphi[1][j]+eps3[2]*dphi[2][j];

				Ke->values[numdof*(4*i+0)+4*j+0]+=gauss->weight*Jdet*2*mu_prime*eps1dotdphij*eps1dotdphii;
				Ke->values[numdof*(4*i+0)+4*j+1]+=gauss->weight*Jdet*2*mu_prime*eps2dotdphij*eps1dotdphii;
				Ke->values[numdof*(4*i+0)+4*j+2]+=gauss->weight*Jdet*2*mu_prime*eps3dotdphij*eps1dotdphii;

				Ke->values[numdof*(4*i+1)+4*j+0]+=gauss->weight*Jdet*2*mu_prime*eps1dotdphij*eps2dotdphii;
				Ke->values[numdof*(4*i+1)+4*j+1]+=gauss->weight*Jdet*2*mu_prime*eps2dotdphij*eps2dotdphii;
				Ke->values[numdof*(4*i+1)+4*j+2]+=gauss->weight*Jdet*2*mu_prime*eps3dotdphij*eps2dotdphii;

				Ke->values[numdof*(4*i+2)+4*j+0]+=gauss->weight*Jdet*2*mu_prime*eps1dotdphij*eps3dotdphii;
				Ke->values[numdof*(4*i+2)+4*j+1]+=gauss->weight*Jdet*2*mu_prime*eps2dotdphij*eps3dotdphii;
				Ke->values[numdof*(4*i+2)+4*j+2]+=gauss->weight*Jdet*2*mu_prime*eps3dotdphij*eps3dotdphii;
			}
		}
	}

	/*Transform Coordinate System*/
	TransformStiffnessMatrixCoord(Ke,nodes,NUMVERTICES,XYZPEnum);

	/*Clean up and return*/
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::GetSolutionFromInputsDiagnosticHoriz{{{*/
void  Penta::GetSolutionFromInputsDiagnosticHoriz(Vector* solution){

	const int    numdof=NDOF2*NUMVERTICES;

	int          i;
	int          approximation;
	int*         doflist=NULL;
	IssmDouble       vx,vy;
	IssmDouble       values[numdof];
	GaussPenta*  gauss;

	/*Get approximation enum and dof list: */
	inputs->GetInputValue(&approximation,ApproximationEnum);
	Input* vx_input=inputs->GetInput(VxEnum); _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum); _assert_(vy_input);

	/*If the element is a coupling, do nothing: every node is also on an other elements 
	 * (as coupling is between MacAyeal and Pattyn) so the other element will take care of it*/
	GetDofList(&doflist,approximation,GsetEnum);

	/*Ok, we have vx and vy in values, fill in vx and vy arrays: */
	/*P1 element only for now*/
	gauss=new GaussPenta();
	for(i=0;i<NUMVERTICES;i++){

		/*Recover vx and vy*/
		gauss->GaussVertex(i);
		vx_input->GetInputValue(&vx,gauss);
		vy_input->GetInputValue(&vy,gauss);
		values[i*NDOF2+0]=vx;
		values[i*NDOF2+1]=vy;
	}

	/*Add value to global vector*/
	solution->SetValues(numdof,doflist,values,INS_VAL);

	/*Free ressources:*/
	delete gauss;
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Penta::GetSolutionFromInputsDiagnosticHutter{{{*/
void  Penta::GetSolutionFromInputsDiagnosticHutter(Vector* solution){

	const int    numdof=NDOF2*NUMVERTICES;

	int          i;
	int*         doflist=NULL;
	IssmDouble       vx,vy;
	IssmDouble       values[numdof];
	GaussPenta*  gauss=NULL;

	/*Get dof list: */
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);
	Input* vx_input=inputs->GetInput(VxEnum); _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum); _assert_(vy_input);

	/*Ok, we have vx and vy in values, fill in vx and vy arrays: */
	/*P1 element only for now*/
	gauss=new GaussPenta();
	for(i=0;i<NUMVERTICES;i++){
		/*Recover vx and vy*/
		gauss->GaussVertex(i);
		vx_input->GetInputValue(&vx,gauss);
		vy_input->GetInputValue(&vy,gauss);
		values[i*NDOF2+0]=vx;
		values[i*NDOF2+1]=vy;
	}

	/*Add value to global vector*/
	solution->SetValues(numdof,doflist,values,INS_VAL);

	/*Free ressources:*/
	delete gauss;
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Penta::GetSolutionFromInputsDiagnosticVert{{{*/
void  Penta::GetSolutionFromInputsDiagnosticVert(Vector* solution){

	const int    numdof=NDOF1*NUMVERTICES;

	int          i;
	int*         doflist=NULL;
	IssmDouble       vz;
	IssmDouble       values[numdof];
	GaussPenta*  gauss=NULL;

	/*Get dof list: */
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);
	Input* vz_input=inputs->GetInput(VzEnum); _assert_(vz_input);

	/*Ok, we have vx and vy in values, fill in vx and vy arrays: */
	/*P1 element only for now*/
	gauss=new GaussPenta();
	for(i=0;i<NUMVERTICES;i++){
		/*Recover vz */
		gauss->GaussVertex(i);
		vz_input->GetInputValue(&vz,gauss);
		values[i]=vz;
	}

	/*Add value to global vector*/
	solution->SetValues(numdof,doflist,values,INS_VAL);

	/*Free ressources:*/
	delete gauss;
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Penta::GetSolutionFromInputsDiagnosticStokes{{{*/
void  Penta::GetSolutionFromInputsDiagnosticStokes(Vector* solution){

	const int    numdof=NDOF4*NUMVERTICES;

	int          i;
	int*         doflist=NULL;
	IssmDouble       vx,vy,vz,p;
	IssmDouble       stokesreconditioning;
	IssmDouble       values[numdof];
	GaussPenta   *gauss;

	/*Get dof list: */
	GetDofList(&doflist,StokesApproximationEnum,GsetEnum);
	Input* vx_input=inputs->GetInput(VxEnum);       _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);       _assert_(vy_input);
	Input* vz_input=inputs->GetInput(VzEnum);       _assert_(vz_input);
	Input* p_input =inputs->GetInput(PressureEnum); _assert_(p_input);

	/*Recondition pressure: */
	this->parameters->FindParam(&stokesreconditioning,DiagnosticStokesreconditioningEnum);

	/*Ok, we have vx vy vz and P in values, fill in vx vy vz P arrays: */
	/*P1 element only for now*/
	gauss=new GaussPenta();
	for(i=0;i<NUMVERTICES;i++){
		gauss->GaussVertex(i);
		vx_input->GetInputValue(&vx,gauss);
		vy_input->GetInputValue(&vy,gauss);
		vz_input->GetInputValue(&vz,gauss);
		p_input ->GetInputValue(&p ,gauss);
		values[i*NDOF4+0]=vx;
		values[i*NDOF4+1]=vy;
		values[i*NDOF4+2]=vz;
		values[i*NDOF4+3]=p/stokesreconditioning;
	}

	/*Add value to global vector*/
	solution->SetValues(numdof,doflist,values,INS_VAL);

	/*Free ressources:*/
	delete gauss;
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionDiagnosticHoriz {{{*/
void  Penta::InputUpdateFromSolutionDiagnosticHoriz(IssmDouble* solution){

	int  approximation;

	/*Recover inputs*/
	inputs->GetInputValue(&approximation,ApproximationEnum);

	/*MacAyeal, everything is done by the element on bed*/
	if (approximation==MacAyealApproximationEnum){
		if (!IsOnBed()){
			/*Do nothing. Element on bed will take care of it*/
			return;
		}
		else{
			InputUpdateFromSolutionDiagnosticMacAyeal(solution);
			return;
		}
	}
	else if (approximation==PattynApproximationEnum){
		InputUpdateFromSolutionDiagnosticPattyn(solution);
	}
	else if (approximation==PattynStokesApproximationEnum){
		InputUpdateFromSolutionDiagnosticPattynStokes(solution);
	}
	else if (approximation==MacAyealStokesApproximationEnum){
		InputUpdateFromSolutionDiagnosticMacAyealStokes(solution);
	}
	else if (approximation==StokesApproximationEnum || approximation==NoneApproximationEnum){
		InputUpdateFromSolutionDiagnosticStokes(solution);
	}
	else if (approximation==MacAyealPattynApproximationEnum){
		InputUpdateFromSolutionDiagnosticMacAyealPattyn(solution);
	}
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionDiagnosticMacAyeal {{{*/
void  Penta::InputUpdateFromSolutionDiagnosticMacAyeal(IssmDouble* solution){

	const int    numdof=NDOF2*NUMVERTICES;

	int     i;
	IssmDouble  rho_ice,g;
	IssmDouble  values[numdof];
	IssmDouble  vx[NUMVERTICES];
	IssmDouble  vy[NUMVERTICES];
	IssmDouble  vz[NUMVERTICES];
	IssmDouble  vel[NUMVERTICES];
	IssmDouble  pressure[NUMVERTICES];
	IssmDouble  surface[NUMVERTICES];
	IssmDouble  xyz_list[NUMVERTICES][3];
	int    *doflist = NULL;
	Penta  *penta   = NULL;

	/*Get dof list: */
	GetDofList(&doflist,MacAyealApproximationEnum,GsetEnum);

	/*Use the dof list to index into the solution vector: */
	for(i=0;i<numdof;i++) values[i]=solution[doflist[i]];

	/*Transform solution in Cartesian Space*/
	TransformSolutionCoord(&values[0],nodes,NUMVERTICES2D,XYEnum); /*2D: only the first 3 nodes are taken*/

	/*Ok, we have vx and vy in values, fill in vx and vy arrays and extrude */
	for(i=0;i<3;i++){
		vx[i]  =values[i*NDOF2+0];
		vy[i]  =values[i*NDOF2+1];
		vx[i+3]=vx[i];
		vy[i+3]=vy[i];

		/*Check solution*/
		if(xIsNan<IssmDouble>(vx[i])) _error2_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vy[i])) _error2_("NaN found in solution vector");
	}

	/*Get parameters fro pressure computation*/
	rho_ice=matpar->GetRhoIce();
	g=matpar->GetG();

	/*Start looping over all elements above current element and update all inputs*/
	penta=this;
	for(;;){

		/*Get node data: */
		GetVerticesCoordinates(&xyz_list[0][0],penta->nodes,NUMVERTICES);

		/*Now Compute vel*/
		GetInputListOnVertices(&vz[0],VzEnum,0.0); //default is 0
		for(i=0;i<NUMVERTICES;i++) vel[i]=pow( pow(vx[i],2.0) + pow(vy[i],2.0) + pow(vz[i],2.0) , 0.5);

		/*Now compute pressure*/
		GetInputListOnVertices(&surface[0],SurfaceEnum);
		for(i=0;i<NUMVERTICES;i++) pressure[i]=rho_ice*g*(surface[i]-xyz_list[i][2]);

		/*Now, we have to move the previous Vx and Vy inputs  to old 
		 * status, otherwise, we'll wipe them off: */
		penta->inputs->ChangeEnum(VxEnum,VxPicardEnum);
		penta->inputs->ChangeEnum(VyEnum,VyPicardEnum);
		penta->inputs->ChangeEnum(PressureEnum,PressurePicardEnum);

		/*Add vx and vy as inputs to the tria element: */
		penta->inputs->AddInput(new PentaP1Input(VxEnum,vx));
		penta->inputs->AddInput(new PentaP1Input(VyEnum,vy));
		penta->inputs->AddInput(new PentaP1Input(VelEnum,vel));
		penta->inputs->AddInput(new PentaP1Input(PressureEnum,pressure));

		/*Stop if we have reached the surface*/
		if (penta->IsOnSurface()) break;

		/* get upper Penta*/
		penta=penta->GetUpperElement(); _assert_(penta->Id()!=this->id);
	}
	
	/*Free ressources:*/
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionDiagnosticMacAyealPattyn {{{*/
void  Penta::InputUpdateFromSolutionDiagnosticMacAyealPattyn(IssmDouble* solution){

	const int    numdof=NDOF2*NUMVERTICES;
	const int    numdof2d=NDOF2*NUMVERTICES2D;

	int     i;
	IssmDouble  rho_ice,g;
	IssmDouble  macayeal_values[numdof];
	IssmDouble  pattyn_values[numdof];
	IssmDouble  vx[NUMVERTICES];
	IssmDouble  vy[NUMVERTICES];
	IssmDouble  vz[NUMVERTICES];
	IssmDouble  vel[NUMVERTICES];
	IssmDouble  pressure[NUMVERTICES];
	IssmDouble  surface[NUMVERTICES];
	IssmDouble  xyz_list[NUMVERTICES][3];
	int*    doflistp = NULL;
	int*    doflistm = NULL;
	Penta   *penta   = NULL;

	/*OK, we have to add results of this element for pattyn 
	 * and results from the penta at base for macayeal. Now recover results*/
	penta=GetBasalElement();

	/*Get dof listof this element (pattyn dofs) and of the penta at base (macayeal dofs): */
	GetDofList(&doflistp,PattynApproximationEnum,GsetEnum);
	penta->GetDofList(&doflistm,MacAyealApproximationEnum,GsetEnum);

	/*Get node data: */
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);

	/*Use the dof list to index into the solution vector: */
	for(i=0;i<numdof2d;i++){
		pattyn_values[i]=solution[doflistp[i]];
		macayeal_values[i]=solution[doflistm[i]];
	}
	for(i=numdof2d;i<numdof;i++){
		pattyn_values[i]=solution[doflistp[i]];
		macayeal_values[i]=macayeal_values[i-numdof2d];
	}

	/*Transform solution in Cartesian Space*/
	TransformSolutionCoord(&macayeal_values[0],penta->nodes,NUMVERTICES,XYEnum);
	TransformSolutionCoord(&pattyn_values[0],   this->nodes,NUMVERTICES,XYEnum);

	/*Ok, we have vx and vy in values, fill in vx and vy arrays: */
	for(i=0;i<NUMVERTICES;i++){
		vx[i]=macayeal_values[i*NDOF2+0]+pattyn_values[i*NDOF2+0];
		vy[i]=macayeal_values[i*NDOF2+1]+pattyn_values[i*NDOF2+1];

		/*Check solution*/
		if(xIsNan<IssmDouble>(vx[i])) _error2_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vy[i])) _error2_("NaN found in solution vector");
	}

	/*Now Compute vel*/
	GetInputListOnVertices(&vz[0],VzEnum,0.0); //default is 0
	for(i=0;i<NUMVERTICES;i++) vel[i]=pow( pow(vx[i],2.0) + pow(vy[i],2.0) + pow(vz[i],2.0) , 0.5);

	/*For pressure: we have not computed pressure in this analysis, for this element. We are in 3D, 
	 *so the pressure is just the pressure at the z elevation: */
	rho_ice=matpar->GetRhoIce();
	g=matpar->GetG();
	GetInputListOnVertices(&surface[0],SurfaceEnum);
	for(i=0;i<NUMVERTICES;i++) pressure[i]=rho_ice*g*(surface[i]-xyz_list[i][2]);

	/*Now, we have to move the previous Vx and Vy inputs  to old 
	 * status, otherwise, we'll wipe them off: */
	this->inputs->ChangeEnum(VxEnum,VxPicardEnum);
	this->inputs->ChangeEnum(VyEnum,VyPicardEnum);
	this->inputs->ChangeEnum(PressureEnum,PressurePicardEnum);

	/*Add vx and vy as inputs to the tria element: */
	this->inputs->AddInput(new PentaP1Input(VxEnum,vx));
	this->inputs->AddInput(new PentaP1Input(VyEnum,vy));
	this->inputs->AddInput(new PentaP1Input(VelEnum,vel));
	this->inputs->AddInput(new PentaP1Input(PressureEnum,pressure));

	/*Free ressources:*/
	xDelete<int>(doflistp);
	xDelete<int>(doflistm);
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionDiagnosticMacAyealStokes {{{*/
void  Penta::InputUpdateFromSolutionDiagnosticMacAyealStokes(IssmDouble* solution){

	const int    numdofm=NDOF2*NUMVERTICES;
	const int    numdofs=NDOF4*NUMVERTICES;
	const int    numdof2d=NDOF2*NUMVERTICES2D;

	int     i;
	IssmDouble  stokesreconditioning;
	IssmDouble  macayeal_values[numdofm];
	IssmDouble  stokes_values[numdofs];
	IssmDouble  vx[NUMVERTICES];
	IssmDouble  vy[NUMVERTICES];
	IssmDouble  vz[NUMVERTICES];
	IssmDouble  vzmacayeal[NUMVERTICES];
	IssmDouble  vzstokes[NUMVERTICES];
	IssmDouble  vel[NUMVERTICES];
	IssmDouble  pressure[NUMVERTICES];
	IssmDouble  xyz_list[NUMVERTICES][3];
	int*    doflistm        = NULL;
	int*    doflists        = NULL;
	Penta   *penta          = NULL;

	/*OK, we have to add results of this element for macayeal 
	 * and results from the penta at base for macayeal. Now recover results*/
	penta=GetBasalElement();

	/*Get dof listof this element (macayeal dofs) and of the penta at base (macayeal dofs): */
	penta->GetDofList(&doflistm,MacAyealApproximationEnum,GsetEnum);
	GetDofList(&doflists,StokesApproximationEnum,GsetEnum);
	this->parameters->FindParam(&stokesreconditioning,DiagnosticStokesreconditioningEnum);

	/*Get node data: */
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);

	/*Use the dof list to index into the solution vector: */
	for(i=0;i<numdof2d;i++){
		macayeal_values[i]=solution[doflistm[i]];
		macayeal_values[i+numdof2d]=solution[doflistm[i]];
	}
	for(i=0;i<numdofs;i++){
		stokes_values[i]=solution[doflists[i]];
	}

	/*Transform solution in Cartesian Space*/
	TransformSolutionCoord(&macayeal_values[0],this->nodes,NUMVERTICES,XYEnum);
	TransformSolutionCoord(&stokes_values[0],this->nodes,NUMVERTICES,XYZPEnum);

	/*Ok, we have vx and vy in values, fill in vx and vy arrays: */
	for(i=0;i<NUMVERTICES;i++){
		vx[i]=stokes_values[i*NDOF4+0]+macayeal_values[i*NDOF2+0];
		vy[i]=stokes_values[i*NDOF4+1]+macayeal_values[i*NDOF2+1];
		vzstokes[i]=stokes_values[i*NDOF4+2];
		pressure[i]=stokes_values[i*NDOF4+3]*stokesreconditioning;

		/*Check solution*/
		if(xIsNan<IssmDouble>(vx[i]))       _error2_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vy[i]))       _error2_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vzstokes[i])) _error2_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(pressure[i])) _error2_("NaN found in solution vector");
	}

	/*Get Vz*/
	Input* vzmacayeal_input=inputs->GetInput(VzMacAyealEnum);
	if (vzmacayeal_input){
		if (vzmacayeal_input->ObjectEnum()!=PentaP1InputEnum){
			_error2_("Cannot compute Vel as VzMacAyeal is of type " << EnumToStringx(vzmacayeal_input->ObjectEnum()));
		}
		GetInputListOnVertices(&vzmacayeal[0],VzMacAyealEnum);
	}
	else{
		_error2_("Cannot update solution as VzMacAyeal is not present");
	}

	/*Now Compute vel*/
	for(i=0;i<NUMVERTICES;i++) {
		vz[i]=vzmacayeal[i]+vzstokes[i];
		vel[i]=pow( pow(vx[i],2.0) + pow(vy[i],2.0) + pow(vz[i],2.0) , 0.5);
	}

	/*Now, we have to move the previous Vx and Vy inputs  to old 
	 * status, otherwise, we'll wipe them off: */
	this->inputs->ChangeEnum(VxEnum,VxPicardEnum);
	this->inputs->ChangeEnum(VyEnum,VyPicardEnum);
	this->inputs->ChangeEnum(VzEnum,VzPicardEnum);
	this->inputs->ChangeEnum(PressureEnum,PressurePicardEnum);

	/*Add vx and vy as inputs to the tria element: */
	this->inputs->AddInput(new PentaP1Input(VxEnum,vx));
	this->inputs->AddInput(new PentaP1Input(VyEnum,vy));
	this->inputs->AddInput(new PentaP1Input(VzEnum,vz));
	this->inputs->AddInput(new PentaP1Input(VzStokesEnum,vzstokes));
	this->inputs->AddInput(new PentaP1Input(VelEnum,vel));
	this->inputs->AddInput(new PentaP1Input(PressureEnum,pressure));

	/*Free ressources:*/
	xDelete<int>(doflistm);
	xDelete<int>(doflists);
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionDiagnosticPattyn {{{*/
void  Penta::InputUpdateFromSolutionDiagnosticPattyn(IssmDouble* solution){
	
	const int    numdof=NDOF2*NUMVERTICES;

	int    i;
	IssmDouble rho_ice,g;
	IssmDouble values[numdof];
	IssmDouble vx[NUMVERTICES];
	IssmDouble vy[NUMVERTICES];
	IssmDouble vz[NUMVERTICES];
	IssmDouble vel[NUMVERTICES];
	IssmDouble pressure[NUMVERTICES];
	IssmDouble surface[NUMVERTICES];
	IssmDouble xyz_list[NUMVERTICES][3];
	int*   doflist = NULL;

	/*Get dof list: */
	GetDofList(&doflist,PattynApproximationEnum,GsetEnum);

	/*Get node data: */
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);

	/*Use the dof list to index into the solution vector: */
	for(i=0;i<numdof;i++) values[i]=solution[doflist[i]];

	/*Transform solution in Cartesian Space*/
	TransformSolutionCoord(&values[0],nodes,NUMVERTICES,XYEnum);

	/*Ok, we have vx and vy in values, fill in vx and vy arrays: */
	for(i=0;i<NUMVERTICES;i++){
		vx[i]=values[i*NDOF2+0];
		vy[i]=values[i*NDOF2+1];

		/*Check solution*/
		if(xIsNan<IssmDouble>(vx[i])) _error2_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vy[i])) _error2_("NaN found in solution vector");
	}

	/*Get Vz*/
	Input* vz_input=inputs->GetInput(VzEnum);
	if (vz_input){
		GetInputListOnVertices(&vz[0],VzEnum);
	}
	else{
		for(i=0;i<NUMVERTICES;i++) vz[i]=0.0;
	}

	/*Now Compute vel*/
	for(i=0;i<NUMVERTICES;i++) vel[i]=pow( pow(vx[i],2.0) + pow(vy[i],2.0) + pow(vz[i],2.0) , 0.5);

	/*For pressure: we have not computed pressure in this analysis, for this element. We are in 3D, 
	 *so the pressure is just the pressure at the z elevation: */
	rho_ice=matpar->GetRhoIce();
	g=matpar->GetG();
	GetInputListOnVertices(&surface[0],SurfaceEnum);
	for(i=0;i<NUMVERTICES;i++) pressure[i]=rho_ice*g*(surface[i]-xyz_list[i][2]);

	/*Now, we have to move the previous Vx and Vy inputs  to old 
	 * status, otherwise, we'll wipe them off: */
	this->inputs->ChangeEnum(VxEnum,VxPicardEnum);
	this->inputs->ChangeEnum(VyEnum,VyPicardEnum);
	this->inputs->ChangeEnum(PressureEnum,PressurePicardEnum);

	/*Add vx and vy as inputs to the tria element: */
	this->inputs->AddInput(new PentaP1Input(VxEnum,vx));
	this->inputs->AddInput(new PentaP1Input(VyEnum,vy));
	this->inputs->AddInput(new PentaP1Input(VelEnum,vel));
	this->inputs->AddInput(new PentaP1Input(PressureEnum,pressure));

	/*Free ressources:*/
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionDiagnosticPattynStokes {{{*/
void  Penta::InputUpdateFromSolutionDiagnosticPattynStokes(IssmDouble* solution){

	const int    numdofp=NDOF2*NUMVERTICES;
	const int    numdofs=NDOF4*NUMVERTICES;

	int    i;
	IssmDouble pattyn_values[numdofp];
	IssmDouble stokes_values[numdofs];
	IssmDouble vx[NUMVERTICES];
	IssmDouble vy[NUMVERTICES];
	IssmDouble vz[NUMVERTICES];
	IssmDouble vzpattyn[NUMVERTICES];
	IssmDouble vzstokes[NUMVERTICES];
	IssmDouble vel[NUMVERTICES];
	IssmDouble pressure[NUMVERTICES];
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble stokesreconditioning;
	int*   doflistp      = NULL;
	int*   doflists      = NULL;
	Penta  *penta        = NULL;

	/*OK, we have to add results of this element for pattyn 
	 * and results from the penta at base for macayeal. Now recover results*/
	penta=GetBasalElement();

	/*Get dof listof this element (pattyn dofs) and of the penta at base (macayeal dofs): */
	GetDofList(&doflistp,PattynApproximationEnum,GsetEnum);
	GetDofList(&doflists,StokesApproximationEnum,GsetEnum);
	this->parameters->FindParam(&stokesreconditioning,DiagnosticStokesreconditioningEnum);

	/*Get node data: */
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);

	/*Use the dof list to index into the solution vector: */
	for(i=0;i<numdofp;i++) pattyn_values[i]=solution[doflistp[i]];
	for(i=0;i<numdofs;i++) stokes_values[i]=solution[doflists[i]];

	/*Transform solution in Cartesian Space*/
	TransformSolutionCoord(&pattyn_values[0],this->nodes,NUMVERTICES,XYEnum);
	TransformSolutionCoord(&stokes_values[0],this->nodes,NUMVERTICES,XYZPEnum);

	/*Ok, we have vx and vy in values, fill in vx and vy arrays: */
	for(i=0;i<NUMVERTICES;i++){
		vx[i]=stokes_values[i*NDOF4+0]+pattyn_values[i*NDOF2+0];
		vy[i]=stokes_values[i*NDOF4+1]+pattyn_values[i*NDOF2+1];
		vzstokes[i]=stokes_values[i*NDOF4+2];
		pressure[i]=stokes_values[i*NDOF4+3]*stokesreconditioning;

		/*Check solution*/
		if(xIsNan<IssmDouble>(vx[i]))       _error2_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vy[i]))       _error2_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vzstokes[i])) _error2_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(pressure[i])) _error2_("NaN found in solution vector");
	}

	/*Get Vz*/
	Input* vzpattyn_input=inputs->GetInput(VzPattynEnum);
	if (vzpattyn_input){
		if (vzpattyn_input->ObjectEnum()!=PentaP1InputEnum){
			_error2_("Cannot compute Vel as VzPattyn is of type " << EnumToStringx(vzpattyn_input->ObjectEnum()));
		}
		GetInputListOnVertices(&vzpattyn[0],VzPattynEnum);
	}
	else{
		_error2_("Cannot update solution as VzPattyn is not present");
	}

	/*Now Compute vel*/
	for(i=0;i<NUMVERTICES;i++) {
		vz[i]=vzpattyn[i]+vzstokes[i];
		vel[i]=pow( pow(vx[i],2.0) + pow(vy[i],2.0) + pow(vz[i],2.0) , 0.5);
	}

	/*Now, we have to move the previous Vx and Vy inputs  to old 
	 * status, otherwise, we'll wipe them off: */
	this->inputs->ChangeEnum(VxEnum,VxPicardEnum);
	this->inputs->ChangeEnum(VyEnum,VyPicardEnum);
	this->inputs->ChangeEnum(VzEnum,VzPicardEnum);
	this->inputs->ChangeEnum(PressureEnum,PressurePicardEnum);

	/*Add vx and vy as inputs to the tria element: */
	this->inputs->AddInput(new PentaP1Input(VxEnum,vx));
	this->inputs->AddInput(new PentaP1Input(VyEnum,vy));
	this->inputs->AddInput(new PentaP1Input(VzEnum,vz));
	this->inputs->AddInput(new PentaP1Input(VzStokesEnum,vzstokes));
	this->inputs->AddInput(new PentaP1Input(VelEnum,vel));
	this->inputs->AddInput(new PentaP1Input(PressureEnum,pressure));

	/*Free ressources:*/
	xDelete<int>(doflistp);
	xDelete<int>(doflists);
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionDiagnosticHutter {{{*/
void  Penta::InputUpdateFromSolutionDiagnosticHutter(IssmDouble* solution){
	
	const int    numdof=NDOF2*NUMVERTICES;

	int     i;
	IssmDouble  rho_ice,g;
	IssmDouble  values[numdof];
	IssmDouble  vx[NUMVERTICES];
	IssmDouble  vy[NUMVERTICES];
	IssmDouble  vz[NUMVERTICES];
	IssmDouble  vel[NUMVERTICES];
	IssmDouble  pressure[NUMVERTICES];
	IssmDouble  surface[NUMVERTICES];
	IssmDouble  xyz_list[NUMVERTICES][3];
	int*    doflist = NULL;

	/*Get dof list: */
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);

	/*Get node data: */
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);

	/*Use the dof list to index into the solution vector: */
	for(i=0;i<numdof;i++) values[i]=solution[doflist[i]];

	/*Ok, we have vx and vy in values, fill in vx and vy arrays: */
	for(i=0;i<NUMVERTICES;i++){
		vx[i]=values[i*NDOF2+0];
		vy[i]=values[i*NDOF2+1];

		/*Check solution*/
		if(xIsNan<IssmDouble>(vx[i])) _error2_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vy[i])) _error2_("NaN found in solution vector");
	}

	/*Now Compute vel*/
	GetInputListOnVertices(&vz[0],VzEnum,0.0); //default is 0
	for(i=0;i<NUMVERTICES;i++) vel[i]=pow( pow(vx[i],2.0) + pow(vy[i],2.0) + pow(vz[i],2.0) , 0.5);

	/*For pressure: we have not computed pressure in this analysis, for this element. We are in 3D, 
	 *so the pressure is just the pressure at the z elevation: */
	rho_ice=matpar->GetRhoIce();
	g=matpar->GetG();
	GetInputListOnVertices(&surface[0],SurfaceEnum);
	for(i=0;i<NUMVERTICES;i++) pressure[i]=rho_ice*g*(surface[i]-xyz_list[i][2]);

	/*Now, we have to move the previous Vx and Vy inputs  to old 
	 * status, otherwise, we'll wipe them off: */
	this->inputs->ChangeEnum(VxEnum,VxPicardEnum);
	this->inputs->ChangeEnum(VyEnum,VyPicardEnum);
	this->inputs->ChangeEnum(PressureEnum,PressurePicardEnum);

	/*Add vx and vy as inputs to the tria element: */
	this->inputs->AddInput(new PentaP1Input(VxEnum,vx));
	this->inputs->AddInput(new PentaP1Input(VyEnum,vy));
	this->inputs->AddInput(new PentaP1Input(VelEnum,vel));
	this->inputs->AddInput(new PentaP1Input(PressureEnum,pressure));

	/*Free ressources:*/
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionDiagnosticVert {{{*/
void  Penta::InputUpdateFromSolutionDiagnosticVert(IssmDouble* solution){

	const int numdof=NDOF1*NUMVERTICES;
	
	int      i;
	int      approximation;
	IssmDouble   rho_ice,g;
	IssmDouble   values[numdof];
	IssmDouble   vx[NUMVERTICES];
	IssmDouble   vy[NUMVERTICES];
	IssmDouble   vz[NUMVERTICES];
	IssmDouble   vzmacayeal[NUMVERTICES];
	IssmDouble   vzpattyn[NUMVERTICES];
	IssmDouble   vzstokes[NUMVERTICES];
	IssmDouble   vel[NUMVERTICES];
	IssmDouble   pressure[NUMVERTICES];
	IssmDouble   surface[NUMVERTICES];
	IssmDouble   xyz_list[NUMVERTICES][3];
	int*     doflist      = NULL;


	/*Get the approximation and do nothing if the element in Stokes or None*/
	inputs->GetInputValue(&approximation,ApproximationEnum);
	if(approximation==StokesApproximationEnum || approximation==NoneApproximationEnum){
		return;
	}

	/*Get dof list and vertices coordinates: */
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);
	GetVerticesCoordinates(&xyz_list[0][0], nodes, NUMVERTICES);

	/*Use the dof list to index into the solution vector vz: */
	for(i=0;i<numdof;i++) values[i]=solution[doflist[i]];
	for(i=0;i<NUMVERTICES;i++){
		vz[i]=values[i*NDOF1+0];

		/*Check solution*/
		if(xIsNan<IssmDouble>(vz[i])) _error2_("NaN found in solution vector");
	}

	/*Get Vx and Vy*/
	GetInputListOnVertices(&vx[0],VxEnum,0.0); //default is 0
	GetInputListOnVertices(&vy[0],VyEnum,0.0); //default is 0

	/*Do some modifications if we actually have a PattynStokes or MacAyealStokes element*/
	if(approximation==PattynStokesApproximationEnum){
		Input* vzstokes_input=inputs->GetInput(VzStokesEnum);
		if (vzstokes_input){
			if (vzstokes_input->ObjectEnum()!=PentaP1InputEnum) _error2_("Cannot compute Vel as VzStokes is of type " << EnumToStringx(vzstokes_input->ObjectEnum()));
			GetInputListOnVertices(&vzstokes[0],VzStokesEnum);
		}
		else _error2_("Cannot compute Vz as VzStokes in not present in PattynStokes element");
		for(i=0;i<NUMVERTICES;i++){
			vzpattyn[i]=vz[i];
			vz[i]=vzpattyn[i]+vzstokes[i];
		}
	}
	else if(approximation==MacAyealStokesApproximationEnum){
		Input* vzstokes_input=inputs->GetInput(VzStokesEnum);
		if (vzstokes_input){
			if (vzstokes_input->ObjectEnum()!=PentaP1InputEnum) _error2_("Cannot compute Vel as VzStokes is of type " << EnumToStringx(vzstokes_input->ObjectEnum()));
			GetInputListOnVertices(&vzstokes[0],VzStokesEnum);
		}
		else _error2_("Cannot compute Vz as VzStokes in not present in MacAyealStokes element");
		for(i=0;i<NUMVERTICES;i++){
			vzmacayeal[i]=vz[i];
			vz[i]=vzmacayeal[i]+vzstokes[i];
		}
	}

	/*Now Compute vel*/
	for(i=0;i<NUMVERTICES;i++) vel[i]=pow( pow(vx[i],2.0) + pow(vy[i],2.0) + pow(vz[i],2.0) , 0.5);

	/*For pressure: we have not computed pressure in this analysis, for this element. We are in 3D, 
	 *so the pressure is just the pressure at the z elevation: except it this is a PattynStokes element */
	if(approximation!=PattynStokesApproximationEnum &&  approximation!=MacAyealStokesApproximationEnum){
		rho_ice=matpar->GetRhoIce();
		g=matpar->GetG();
		GetInputListOnVertices(&surface[0],SurfaceEnum);
		for(i=0;i<NUMVERTICES;i++) pressure[i]=rho_ice*g*(surface[i]-xyz_list[i][2]);
	}

	/*Now, we have to move the previous Vz inputs to old 
	 * status, otherwise, we'll wipe them off and add the new inputs: */
	this->inputs->ChangeEnum(VzEnum,VzPicardEnum);

	if(approximation!=PattynStokesApproximationEnum && approximation!=MacAyealStokesApproximationEnum){
		this->inputs->ChangeEnum(PressureEnum,PressurePicardEnum);
		this->inputs->AddInput(new PentaP1Input(PressureEnum,pressure));
	}
	else if(approximation==PattynStokesApproximationEnum){
		this->inputs->AddInput(new PentaP1Input(VzPattynEnum,vzpattyn));
	}
	else if(approximation==MacAyealStokesApproximationEnum){
		this->inputs->AddInput(new PentaP1Input(VzMacAyealEnum,vzmacayeal));
	}
	this->inputs->AddInput(new PentaP1Input(VzEnum,vz));
	this->inputs->AddInput(new PentaP1Input(VelEnum,vel));

	/*Free ressources:*/
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionDiagnosticStokes {{{*/
void  Penta::InputUpdateFromSolutionDiagnosticStokes(IssmDouble* solution){
	
	const int numdof=NDOF4*NUMVERTICES;

	int     i;
	IssmDouble  values[numdof];
	IssmDouble  vx[NUMVERTICES];
	IssmDouble  vy[NUMVERTICES];
	IssmDouble  vz[NUMVERTICES];
	IssmDouble  vel[NUMVERTICES];
	IssmDouble  pressure[NUMVERTICES];
	IssmDouble  stokesreconditioning;
	int*    doflist=NULL;

	/*Get dof list: */
	GetDofList(&doflist,StokesApproximationEnum,GsetEnum);

	/*Use the dof list to index into the solution vector: */
	for(i=0;i<numdof;i++) values[i]=solution[doflist[i]];

	/*Transform solution in Cartesian Space*/
	TransformSolutionCoord(&values[0],nodes,NUMVERTICES,XYZPEnum);

	/*Ok, we have vx and vy in values, fill in all arrays: */
	for(i=0;i<NUMVERTICES;i++){
		vx[i]=values[i*NDOF4+0];
		vy[i]=values[i*NDOF4+1];
		vz[i]=values[i*NDOF4+2];
		pressure[i]=values[i*NDOF4+3];

		/*Check solution*/
		if(xIsNan<IssmDouble>(vx[i]))       _error2_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vy[i]))       _error2_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vz[i]))       _error2_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(pressure[i])) _error2_("NaN found in solution vector");
	}

	/*Recondition pressure and compute vel: */
	this->parameters->FindParam(&stokesreconditioning,DiagnosticStokesreconditioningEnum);
	for(i=0;i<NUMVERTICES;i++) pressure[i]=pressure[i]*stokesreconditioning;
	for(i=0;i<NUMVERTICES;i++) vel[i]=pow( pow(vx[i],2.0) + pow(vy[i],2.0) + pow(vz[i],2.0) , 0.5);
	
	/*Now, we have to move the previous inputs  to old 
	 * status, otherwise, we'll wipe them off: */
	this->inputs->ChangeEnum(VxEnum,VxPicardEnum);
	this->inputs->ChangeEnum(VyEnum,VyPicardEnum);
	this->inputs->ChangeEnum(VzEnum,VzPicardEnum);
	this->inputs->ChangeEnum(PressureEnum,PressurePicardEnum);

	/*Add vx and vy as inputs to the tria element: */
	this->inputs->AddInput(new PentaP1Input(VxEnum,vx));
	this->inputs->AddInput(new PentaP1Input(VyEnum,vy));
	this->inputs->AddInput(new PentaP1Input(VzEnum,vz));
	this->inputs->AddInput(new PentaP1Input(VelEnum,vel));
	this->inputs->AddInput(new PentaP1Input(PressureEnum,pressure));

	/*Free ressources:*/
	xDelete<int>(doflist);
}
/*}}}*/
#endif

#ifdef _HAVE_BALANCED_
/*FUNCTION Penta::CreateKMatrixBalancethickness {{{*/
ElementMatrix* Penta::CreateKMatrixBalancethickness(void){

	/*Figure out if this penta is collapsed. If so, then bailout, except if it is at the 
	  bedrock, in which case we spawn a tria element using the 3 first nodes, and use it to build 
	  the stiffness matrix. */
	if (!IsOnBed()) return NULL;

	/*Depth Averaging Vx and Vy*/
	this->InputDepthAverageAtBase(VxEnum,VxAverageEnum);
	this->InputDepthAverageAtBase(VyEnum,VyAverageEnum);

	/*Spawn Tria element from the base of the Penta: */
	Tria* tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.
	ElementMatrix* Ke=tria->CreateKMatrixBalancethickness();
	delete tria->matice; delete tria;

	/*Delete Vx and Vy averaged*/
	this->inputs->DeleteInput(VxAverageEnum);
	this->inputs->DeleteInput(VyAverageEnum);

	/*clean up and return*/
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorBalancethickness {{{*/
ElementVector* Penta::CreatePVectorBalancethickness(void){

	if (!IsOnBed()) return NULL;

	/*Depth Averaging Vx and Vy*/
	this->InputDepthAverageAtBase(VxEnum,VxAverageEnum);
	this->InputDepthAverageAtBase(VyEnum,VyAverageEnum);

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(0,1,2); //nodes 0, 1 and 2 make the new tria.
	ElementVector* pe=tria->CreatePVectorBalancethickness();
	delete tria->matice; delete tria;

	/*Delete Vx and Vy averaged*/
	this->inputs->DeleteInput(VxAverageEnum);
	this->inputs->DeleteInput(VyAverageEnum);

	/*Clean up and return*/
	return pe;
}
/*}}}*/
#endif

