/*!\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 "../classes.h"
#include "../../shared/shared.h"
/*}}}*/

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

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

	this->nodes             = NULL;
	this->vertices          = NULL;
	this->material          = NULL;
	this->matpar            = NULL;
	this->verticalneighbors = NULL;
	this->inputs            = NULL;
	this->parameters        = NULL;
	this->results           = NULL;
}
/*}}}*/
/*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)
	,ElementHook(nummodels,index+1,6,iomodel) //index+1: material 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]) || iomodel->Data(MeshUpperelementsEnum)[index]==-1.) penta_elements_ids[1]=this->id; //upper penta is the same penta
	else                                    penta_elements_ids[1]=reCast<int,IssmDouble>((iomodel->Data(MeshUpperelementsEnum)[index]));
	if (xIsNan<IssmDouble>(iomodel->Data(MeshLowerelementsEnum)[index]) || iomodel->Data(MeshLowerelementsEnum)[index]==-1.) penta_elements_ids[0]=this->id; //lower penta is the same penta
	else                                    penta_elements_ids[0]=reCast<int,IssmDouble>((iomodel->Data(MeshLowerelementsEnum)[index]));
	this->InitHookNeighbors(penta_elements_ids);

	//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->vertices          = NULL;
	this->material          = 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 ElementHook
	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->hvertices=(Hook*)this->hvertices->copy();
	penta->hmaterial=(Hook*)this->hmaterial->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->vertices=(Vertex**)penta->hvertices->deliverp();
	penta->material=(Material*)penta->hmaterial->delivers();
	penta->matpar=(Matpar*)penta->hmatpar->delivers();
	penta->verticalneighbors=(Penta**)penta->hneighbors->deliverp();

	return penta;
}
/*}}}*/

/*Other*/
/*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;
	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 PentaInput(BasalFrictionEnum,&basalfriction[0],P1Enum));
		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,StressbalanceAnalysisEnum);

	/* Start looping on the number of gauss 2d (nodes on the bedrock) */
	gauss=new GaussPenta(0,1,2,2);
	count=0;
	for(int 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 PentaInput(BasalFrictionEnum,&basalfriction[0],P1Enum));

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

	int         i,j;
	int         dofv[3]={0,1,2};
	int         dofp[1]={3};
	int         analysis_type,approximation;
	IssmDouble  xyz_list[NUMVERTICES][3];
	IssmDouble  xyz_list_tria[3][3];
	IssmDouble  rho_ice,gravity,FSreconditioning;
	IssmDouble  pressure,viscosity,Jdet2d;
	IssmDouble  bed_normal[3];
	IssmDouble  basalforce[3];
	IssmDouble  epsilon[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!=StressbalanceAnalysisEnum) _error_("Not supported yet!");
	if (approximation!=FSApproximationEnum) _error_("Not supported yet!");

	/*retrieve some parameters: */
	this->parameters->FindParam(&FSreconditioning,StressbalanceFSreconditioningEnum);

	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],vertices,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(int 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);
		material->GetViscosity3dFS(&viscosity,&epsilon[0]);
		pressure_input->GetInputValue(&pressure,gauss);

		/*Compute Stress*/
		sigma_xx=2*viscosity*epsilon[0]-pressure*FSreconditioning; // sigma = nu eps - pressure
		sigma_yy=2*viscosity*epsilon[1]-pressure*FSreconditioning;
		sigma_zz=2*viscosity*epsilon[2]-pressure*FSreconditioning;
		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<IssmDouble>* eps){

	_error_("Not implemented yet");

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

	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],vertices,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);
		material->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 PentaInput(StressTensorxxEnum,&sigma_xx[0],P1Enum));
	this->inputs->AddInput(new PentaInput(StressTensorxyEnum,&sigma_xy[0],P1Enum));
	this->inputs->AddInput(new PentaInput(StressTensorxzEnum,&sigma_xz[0],P1Enum));
	this->inputs->AddInput(new PentaInput(StressTensoryyEnum,&sigma_yy[0],P1Enum));
	this->inputs->AddInput(new PentaInput(StressTensoryzEnum,&sigma_yz[0],P1Enum));
	this->inputs->AddInput(new PentaInput(StressTensorzzEnum,&sigma_zz[0],P1Enum));

	/*Clean up and return*/
	delete gauss;
}
/*}}}*/
/*FUNCTION Penta::Configure {{{*/
void  Penta::Configure(Elements* elementsin, Loads* loadsin, Nodes* nodesin,Vertices* verticesin, 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->hvertices->configure(verticesin);
	this->hmaterial->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->vertices          = (Vertex**)this->hvertices->deliverp();
	this->material          = (Material*)this->hmaterial->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(Matrix<IssmDouble>* Kff, Matrix<IssmDouble>* Kfs) {{{*/
void  Penta::CreateKMatrix(Matrix<IssmDouble>* Kff, Matrix<IssmDouble>* Kfs){

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

	/*Create element stiffness matrix*/
	ElementMatrix* Ke=CreateKMatrix();

	if(Ke){
		int analysis_type;
		parameters->FindParam(&analysis_type,AnalysisTypeEnum);
		if(analysis_type==StressbalanceAnalysisEnum){
			int approximation;
			inputs->GetInputValue(&approximation,ApproximationEnum);
			if(approximation==HOFSApproximationEnum || approximation==SSAFSApproximationEnum){
				//Do nothing condensatino already done for Stokes part
			}
			else{
				/*Condense if requested*/
				if(this->element_type==MINIcondensedEnum){
					int indices[3]={18,19,20};
					Ke->StaticCondensation(3,&indices[0]);
				}
				else if(this->element_type==P1bubblecondensedEnum){
					int size   = nodes[6]->GetNumberOfDofs(NoneApproximationEnum,GsetEnum);
					int offset = 0;
					for(int i=0;i<6;i++) offset+=nodes[i]->GetNumberOfDofs(NoneApproximationEnum,GsetEnum);
					int* indices=xNew<int>(size);
					for(int i=0;i<size;i++) indices[i] = offset+i;
					Ke->StaticCondensation(size,indices);
					xDelete<int>(indices);
				}
			}
		}

		/*Add to global matrix*/
		Ke->AddToGlobal(Kff,Kfs);
		delete Ke;
	}
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrix(void){{{*/
ElementMatrix* Penta::CreateKMatrix(void){

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

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

	/*Skip if water element*/
	if(NoIceInElement()) return NULL;

	/*Just branch to the correct element stiffness matrix generator, according to the type of analysis we are carrying out: */
	switch(analysis_type){
		#ifdef _HAVE_STRESSBALANCE_
		case StressbalanceAnalysisEnum:
			return CreateKMatrixStressbalanceHoriz();
			break;
		case AdjointHorizAnalysisEnum:
			return CreateKMatrixAdjointHoriz();
			break;
		case StressbalanceSIAAnalysisEnum:
			return CreateKMatrixStressbalanceSIA();
			break;
		case StressbalanceVerticalAnalysisEnum:
			return CreateKMatrixStressbalanceVert();
			break;
		#endif
		case BedSlopeXAnalysisEnum: case SurfaceSlopeXAnalysisEnum: case BedSlopeYAnalysisEnum: case SurfaceSlopeYAnalysisEnum:
			return CreateBasalMassMatrix();
			break;
		case MasstransportAnalysisEnum:
			return CreateKMatrixMasstransport();
			break;
		case FreeSurfaceTopAnalysisEnum:
			return CreateKMatrixFreeSurfaceTop();
			break;
		case FreeSurfaceBaseAnalysisEnum:
			return CreateKMatrixFreeSurfaceBase();
			break;
		#ifdef _HAVE_BALANCED_
		case BalancethicknessAnalysisEnum:
			return CreateKMatrixBalancethickness();
			break;
		#endif
		#ifdef _HAVE_THERMAL_
		case ThermalAnalysisEnum:
			return CreateKMatrixThermal();
			break;
		case EnthalpyAnalysisEnum:
			return CreateKMatrixEnthalpy();
			break;
		case MeltingAnalysisEnum:
			return CreateKMatrixMelting();
			break;
		#endif
		#ifdef _HAVE_HYDROLOGY_
	  case HydrologyDCInefficientAnalysisEnum:
			return CreateKMatrixHydrologyDCInefficient();
			break;
	  case HydrologyDCEfficientAnalysisEnum:
			return CreateKMatrixHydrologyDCEfficient();
			break;
		#endif
		default:
			_error_("analysis " << analysis_type << " (" << EnumToStringx(analysis_type) << ") not supported yet");
	}

	/*Make compiler happy*/
	return NULL;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixMasstransport {{{*/
ElementMatrix* Penta::CreateKMatrixMasstransport(void){

	if (!IsOnBed()) return NULL;

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

	Tria* tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
	ElementMatrix* Ke=tria->CreateKMatrixMasstransport();
	delete tria->material; delete tria;

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

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

	if(!IsOnSurface()) return NULL;

	Tria* tria=(Tria*)SpawnTria(1); //lower face is 0, upper face is 1.
	ElementMatrix* Ke=tria->CreateKMatrixFreeSurfaceTop();
	delete tria->material; delete tria;

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

	if(!IsOnBed()) return NULL;

	Tria* tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
	ElementMatrix* Ke=tria->CreateKMatrixFreeSurfaceBase();
	delete tria->material; delete tria;

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

	if (!IsOnBed()) return NULL;

	Tria* tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
	ElementMatrix* Ke=tria->CreateMassMatrix();
	delete tria->material; delete tria;

	/*clean up and return*/
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateDVector {{{*/
void  Penta::CreateDVector(Vector<IssmDouble>* df){

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

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

	switch(analysis_type){
		#ifdef _HAVE_STRESSBALANCE_
		case StressbalanceAnalysisEnum:
			De=CreateDVectorStressbalanceHoriz();
			break;
		#endif
	}

	/*Add to global Vector*/
	if(De){
		De->InsertIntoGlobal(df);
		delete De;
	}
}
/*}}}*/
/*FUNCTION Penta::CreatePVector(Vector<IssmDouble>* pf) {{{*/
void  Penta::CreatePVector(Vector<IssmDouble>* pf){

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

	/*Create element load vector*/
	ElementVector* pe = CreatePVector();

	if(pe){
		int analysis_type;
		parameters->FindParam(&analysis_type,AnalysisTypeEnum);
		if(analysis_type==StressbalanceAnalysisEnum){
			/*StaticCondensation if requested*/
			if(this->element_type==MINIcondensedEnum){
				int approximation;
				inputs->GetInputValue(&approximation,ApproximationEnum);
				if(approximation==HOFSApproximationEnum || approximation==SSAFSApproximationEnum){
					//Do nothing, condensation already done in PVectorCoupling
				}
				else{
					int indices[3]={18,19,20};

					this->element_type=MINIEnum;
					ElementMatrix* Ke = CreateKMatrixStressbalanceFS();
					this->element_type=MINIcondensedEnum;

					pe->StaticCondensation(Ke,3,&indices[0]);
					delete Ke;
				}
			}
			else if(this->element_type==P1bubblecondensedEnum){
				int size   = nodes[6]->GetNumberOfDofs(NoneApproximationEnum,GsetEnum);
				int offset = 0;
				for(int i=0;i<6;i++) offset+=nodes[i]->GetNumberOfDofs(NoneApproximationEnum,GsetEnum);
				int* indices=xNew<int>(size);
				for(int i=0;i<size;i++) indices[i] = offset+i;

				this->element_type=P1bubbleEnum;
				ElementMatrix* Ke = CreateKMatrix();
				this->element_type=P1bubblecondensedEnum;
				pe->StaticCondensation(Ke,size,indices);
				xDelete<int>(indices);
				delete Ke;
			}
		}

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

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

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

	/*Skip if water element*/
	if(NoIceInElement()) return NULL;

	/*Just branch to the correct element stiffness matrix generator, according to the type of analysis we are carrying out: */
	switch(analysis_type){
		#ifdef _HAVE_STRESSBALANCE_
		case StressbalanceAnalysisEnum:
			return CreatePVectorStressbalanceHoriz();
			break;
		case StressbalanceSIAAnalysisEnum:
			return CreatePVectorStressbalanceSIA();
			break;
		case StressbalanceVerticalAnalysisEnum:
			return CreatePVectorStressbalanceVert();
			break;
		#endif
	 	#ifdef _HAVE_CONTROL_
		case AdjointHorizAnalysisEnum:
			return CreatePVectorAdjointHoriz();
			break;
		#endif
		#ifdef _HAVE_THERMAL_
		case ThermalAnalysisEnum:
			return CreatePVectorThermal();
			break;
		case EnthalpyAnalysisEnum:
			return CreatePVectorEnthalpy();
			break;
		case MeltingAnalysisEnum:
			return CreatePVectorMelting();
			break;
		#endif
		case BedSlopeXAnalysisEnum: case SurfaceSlopeXAnalysisEnum: case BedSlopeYAnalysisEnum: case SurfaceSlopeYAnalysisEnum:
			return CreatePVectorSlope();
			break;
		case MasstransportAnalysisEnum:
			return CreatePVectorMasstransport();
			break;
		case FreeSurfaceTopAnalysisEnum:
			return CreatePVectorFreeSurfaceTop();
			break;
		case FreeSurfaceBaseAnalysisEnum:
			return CreatePVectorFreeSurfaceBase();
			break;
		#ifdef _HAVE_BALANCED_
		case BalancethicknessAnalysisEnum:
			return CreatePVectorBalancethickness();
			break;
		#endif
		#ifdef _HAVE_HYDROLOGY_
	  case HydrologyDCInefficientAnalysisEnum:
			return CreatePVectorHydrologyDCInefficient();
			break;
	  case HydrologyDCEfficientAnalysisEnum:
			return CreatePVectorHydrologyDCEfficient();
			break;
		#endif
		default:
			_error_("analysis " << analysis_type << " (" << EnumToStringx(analysis_type) << ") not supported yet");
	}

	/*make compiler happy*/
	return NULL;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorMasstransport {{{*/
ElementVector* Penta::CreatePVectorMasstransport(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); //lower face is 0, upper face is 1.
	ElementVector* pe=tria->CreatePVectorMasstransport();
	delete tria->material; delete tria;

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

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

	if(!IsOnSurface()) return NULL;

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(1); //lower face is 0, upper face is 1.
	ElementVector* pe=tria->CreatePVectorFreeSurfaceTop();
	delete tria->material; delete tria;

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

	if(!IsOnBed()) return NULL;

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
	ElementVector* pe=tria->CreatePVectorFreeSurfaceBase();
	delete tria->material; delete tria;

	/*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); //lower face is 0, upper face is 1.
	ElementVector* pe=tria->CreatePVectorSlope();
	delete tria->material; delete tria;

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

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

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

	/*Skip if water element*/
	if(NoIceInElement()) 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_STRESSBALANCE_
		case StressbalanceAnalysisEnum:
			Ke=CreateJacobianStressbalanceHoriz();
			break;
#endif
		default:
			_error_("analysis " << analysis_type << " (" << EnumToStringx(analysis_type) << ") not supported yet");
	}

	/*Add to global matrix*/
	if(Ke){
		/*Condense if requested*/
		if(this->element_type==MINIcondensedEnum){
			int indices[3]={18,19,20};
			Ke->StaticCondensation(3,&indices[0]);
		}
		else if(this->element_type==P1bubblecondensedEnum){
			int size   = nodes[6]->GetNumberOfDofs(NoneApproximationEnum,GsetEnum);
			int offset = 0;
			for(int i=0;i<6;i++) offset+=nodes[i]->GetNumberOfDofs(NoneApproximationEnum,GsetEnum);
			int* indices=xNew<int>(size);
			for(int i=0;i<size;i++) indices[i] = offset+i;
			Ke->StaticCondensation(size,indices);
			xDelete<int>(indices);
		}

		Ke->AddToGlobal(Jff);
		delete Ke;
	}
}
/*}}}*/
/*FUNCTION Penta::DeepEcho{{{*/
void Penta::DeepEcho(void){

	_printf_("Penta:\n");
	_printf_("   id: " << id << "\n");
	nodes[0]->DeepEcho();
	nodes[1]->DeepEcho();
	nodes[2]->DeepEcho();
	nodes[3]->DeepEcho();
	nodes[4]->DeepEcho();
	nodes[5]->DeepEcho();
	material->DeepEcho();
	matpar->DeepEcho();
	_printf_("   neighbor ids: " << verticalneighbors[0]->Id() << "-" << verticalneighbors[1]->Id() << "\n");
	_printf_("   parameters\n");
	parameters->DeepEcho();
	_printf_("   inputs\n");
	inputs->DeepEcho();
	_printf_("   results\n");
	results->DeepEcho();
}
/*}}}*/
/*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){
        /*Are we on the base? If not, return*/
        if(!IsOnBed()) return;

	int        i;
	IssmDouble monthlytemperatures[NUMVERTICES][12],monthlyprec[NUMVERTICES][12];
	IssmDouble TemperaturesPresentday[NUMVERTICES][12],TemperaturesLgm[NUMVERTICES][12];
	IssmDouble PrecipitationsPresentday[NUMVERTICES][12];
	IssmDouble tmp[NUMVERTICES];
	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);
			PrecipitationsPresentday[iv][month]=PrecipitationsPresentday[iv][month]/yts; // converion in m/sec
		}
	}

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

	/*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(i=0;i<NUMVERTICES;i++) tmp[i]=monthlytemperatures[i][imonth];
		PentaInput* newmonthinput1 = new PentaInput(SurfaceforcingsMonthlytemperaturesEnum,&tmp[0],P1Enum);
		NewTemperatureInput->AddTimeInput(newmonthinput1,time+imonth/12.*yts);

		for(i=0;i<NUMVERTICES;i++) tmp[i]=monthlyprec[i][imonth];
		PentaInput* newmonthinput2 = new PentaInput(SurfaceforcingsPrecipitationEnum,&tmp[0],P1Enum);
		NewPrecipitationInput->AddTimeInput(newmonthinput2,time+imonth/12.*yts);
	}

	this->inputs->AddInput(NewTemperatureInput);
	this->inputs->AddInput(NewPrecipitationInput);

	this->InputExtrude(SurfaceforcingsMonthlytemperaturesEnum,ElementEnum);
	this->InputExtrude(SurfaceforcingsPrecipitationEnum,ElementEnum);

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

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

	return PentaEnum;

}
/*}}}*/
/*FUNCTION Penta::GetAreaCoordinates{{{*/
void Penta::GetAreaCoordinates(IssmDouble* area_coordinates,IssmDouble xyz_zero[4][3],IssmDouble xyz_list[6][3],int numpoints){
	/*Computeportion of the element that is grounded*/ 

	int         i,j,k;
	IssmDouble  area_init,area_portion;
	IssmDouble  xyz_bis[3][3];

	area_init=fabs(xyz_list[1][0]*xyz_list[2][1] - xyz_list[1][1]*xyz_list[2][0] + xyz_list[0][0]*xyz_list[1][1] - xyz_list[0][1]*xyz_list[1][0] + xyz_list[2][0]*xyz_list[0][1] - xyz_list[2][1]*xyz_list[0][0])/2.;

	/*Initialize xyz_list with original xyz_list of triangle coordinates*/
	for(j=0;j<3;j++){ 
		for(k=0;k<3;k++){
			xyz_bis[j][k]=xyz_list[j][k];
		}
	}
	for(i=0;i<numpoints;i++){
		for(j=0;j<3;j++){ 
			for(k=0;k<3;k++){
				/*Change appropriate line*/
				xyz_bis[j][k]=xyz_zero[i][k];
			}

			/*Compute area fraction*/
			area_portion=fabs(xyz_bis[1][0]*xyz_bis[2][1] - xyz_bis[1][1]*xyz_bis[2][0] + xyz_bis[0][0]*xyz_bis[1][1] - xyz_bis[0][1]*xyz_bis[1][0] + xyz_bis[2][0]*xyz_bis[0][1] - xyz_bis[2][1]*xyz_bis[0][0])/2.;
			*(area_coordinates+3*i+j)=area_portion/area_init;

			/*Reinitialize xyz_list*/
			for(k=0;k<3;k++){
				/*Reinitialize xyz_list with original coordinates*/
				xyz_bis[j][k]=xyz_list[j][k];
			}
		}
	}
}
/*}}}*/
/*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){

	/*Fetch number of nodes and dof for this finite element*/
	int numnodes = this->NumberofNodes();

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

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

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

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

	/*Fetch number of nodes and dof for this finite element*/
	int numnodes = this->NumberofNodesVelocity();

	/*First, figure out size of doflist and create it: */
	int numberofdofs=0;
	for(int i=0;i<numnodes;i++) numberofdofs+=nodes[i]->GetNumberOfDofs(FSvelocityEnum,setenum);

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

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

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

	/*Fetch number of nodes and dof for this finite element*/
	int vnumnodes = this->NumberofNodesVelocity();
	int pnumnodes = this->NumberofNodesPressure();

	/*First, figure out size of doflist and create it: */
	int numberofdofs=0;
	for(int i=vnumnodes;i<vnumnodes+pnumnodes;i++) numberofdofs+=nodes[i]->GetNumberOfDofs(FSApproximationEnum,setenum);

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

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

	/*Assign output pointers:*/
	*pdoflist=doflist;
}
/*}}}*/
/*FUNCTION Penta::GetGroundedPart{{{*/
void Penta::GetGroundedPart(int* point1,IssmDouble* fraction1,IssmDouble* fraction2, bool* mainlyfloating){
	/*Computeportion of the element that is grounded*/ 

	bool               floating=true;
	int                point;
	const IssmPDouble  epsilon= 1.e-15;
	IssmDouble         gl[NUMVERTICES];
	IssmDouble         f1,f2;

	/*Recover parameters and values*/
	GetInputListOnVertices(&gl[0],MaskGroundediceLevelsetEnum);

	/*Be sure that values are not zero*/
	if(gl[0]==0.) gl[0]=gl[0]+epsilon;
	if(gl[1]==0.) gl[1]=gl[1]+epsilon;
	if(gl[2]==0.) gl[2]=gl[2]+epsilon;

	/*Check that not all nodes are grounded or floating*/
	if(gl[0]>0 && gl[1]>0 && gl[2]>0){ // All grounded
		point=0;
		f1=1.;
		f2=1.;
	}
	else if(gl[0]<0 && gl[1]<0 && gl[2]<0){ //All floating
		point=0;
		f1=0.;
		f2=0.;
	}
	else{
		if(gl[0]*gl[1]*gl[2]<0) floating=false;

		if(gl[0]*gl[1]>0){ //Nodes 0 and 1 are similar, so points must be found on segment 0-2 and 1-2
			point=2;
			f1=gl[2]/(gl[2]-gl[0]);
			f2=gl[2]/(gl[2]-gl[1]);
		}
		else if(gl[1]*gl[2]>0){ //Nodes 1 and 2 are similar, so points must be found on segment 0-1 and 0-2
			point=0;
			f1=gl[0]/(gl[0]-gl[1]);
			f2=gl[0]/(gl[0]-gl[2]);
		}
		else if(gl[0]*gl[2]>0){ //Nodes 0 and 2 are similar, so points must be found on segment 1-0 and 1-2
			point=1;
			f1=gl[1]/(gl[1]-gl[2]);
			f2=gl[1]/(gl[1]-gl[0]);
		}
	}
	*point1=point;
	*fraction1=f1;
	*fraction2=f2;
	*mainlyfloating=floating;
}
/*}}}*/
/*FUNCTION Penta::GetGroundedPortion{{{*/
IssmDouble Penta::GetGroundedPortion(IssmDouble* xyz_list){
	/*Computeportion of the element that is grounded*/ 

	bool               mainlyfloating = true;
	const IssmPDouble  epsilon= 1.e-15;
	IssmDouble         phi,s1,s2,area_init,area_grounded;
	IssmDouble         gl[NUMVERTICES];
	IssmDouble         xyz_bis[NUMVERTICES2D][3];

	/*Recover parameters and values*/
	GetInputListOnVertices(&gl[0],MaskGroundediceLevelsetEnum);

	/*Be sure that values are not zero*/
	if(gl[0]==0.) gl[0]=gl[0]+epsilon;
	if(gl[1]==0.) gl[1]=gl[1]+epsilon;
	if(gl[2]==0.) gl[2]=gl[2]+epsilon;

	/*Check that not all nodes are grounded or floating*/
	if(gl[0]>0 && gl[1]>0 && gl[2]>0){ // All grounded
		phi=1;
	}
	else if(gl[0]<0 && gl[1]<0 && gl[2]<0){ //All floating
		phi=0;
	}
	else{
		/*Figure out if two nodes are floating or grounded*/
		if(gl[0]*gl[1]*gl[2]>0) mainlyfloating=false;

		if(gl[0]*gl[1]>0){ //Nodes 0 and 1 are similar, so points must be found on segment 0-2 and 1-2
			/*Coordinates of point 2: same as initial point 2*/
			xyz_bis[2][0]=xyz_list[3*2+0];
			xyz_bis[2][1]=xyz_list[3*2+1];
			xyz_bis[2][2]=xyz_list[3*2+2];

			/*Portion of the segments*/
			s1=gl[2]/(gl[2]-gl[1]);
			s2=gl[2]/(gl[2]-gl[0]);

			/*New point 1*/
			xyz_bis[1][0]=xyz_list[3*2+0]+s1*(xyz_list[3*1+0]-xyz_list[3*2+0]);
			xyz_bis[1][1]=xyz_list[3*2+1]+s1*(xyz_list[3*1+1]-xyz_list[3*2+1]);
			xyz_bis[1][2]=xyz_list[3*2+2]+s1*(xyz_list[3*1+2]-xyz_list[3*2+2]);

			/*New point 0*/
			xyz_bis[0][0]=xyz_list[3*2+0]+s2*(xyz_list[3*0+0]-xyz_list[3*2+0]);
			xyz_bis[0][1]=xyz_list[3*2+1]+s2*(xyz_list[3*0+1]-xyz_list[3*2+1]);
			xyz_bis[0][2]=xyz_list[3*2+2]+s2*(xyz_list[3*0+2]-xyz_list[3*2+2]);
		}
		else if(gl[1]*gl[2]>0){ //Nodes 1 and 2 are similar, so points must be found on segment 0-1 and 0-2
			/*Coordinates of point 0: same as initial point 2*/
			xyz_bis[0][0]=*(xyz_list+3*0+0);
			xyz_bis[0][1]=*(xyz_list+3*0+1);
			xyz_bis[0][2]=*(xyz_list+3*0+2);

			/*Portion of the segments*/
			s1=gl[0]/(gl[0]-gl[1]);
			s2=gl[0]/(gl[0]-gl[2]);

			/*New point 1*/
			xyz_bis[1][0]=*(xyz_list+3*0+0)+s1*(*(xyz_list+3*1+0)-*(xyz_list+3*0+0));
			xyz_bis[1][1]=*(xyz_list+3*0+1)+s1*(*(xyz_list+3*1+1)-*(xyz_list+3*0+1));
			xyz_bis[1][2]=*(xyz_list+3*0+2)+s1*(*(xyz_list+3*1+2)-*(xyz_list+3*0+2));

			/*New point 2*/
			xyz_bis[2][0]=*(xyz_list+3*0+0)+s2*(*(xyz_list+3*2+0)-*(xyz_list+3*0+0));
			xyz_bis[2][1]=*(xyz_list+3*0+1)+s2*(*(xyz_list+3*2+1)-*(xyz_list+3*0+1));
			xyz_bis[2][2]=*(xyz_list+3*0+2)+s2*(*(xyz_list+3*2+2)-*(xyz_list+3*0+2));
		}
		else if(gl[0]*gl[2]>0){ //Nodes 0 and 2 are similar, so points must be found on segment 1-0 and 1-2
			/*Coordinates of point 1: same as initial point 2*/
			xyz_bis[1][0]=*(xyz_list+3*1+0);
			xyz_bis[1][1]=*(xyz_list+3*1+1);
			xyz_bis[1][2]=*(xyz_list+3*1+2);

			/*Portion of the segments*/
			s1=gl[1]/(gl[1]-gl[0]);
			s2=gl[1]/(gl[1]-gl[2]);

			/*New point 0*/
			xyz_bis[0][0]=*(xyz_list+3*1+0)+s1*(*(xyz_list+3*0+0)-*(xyz_list+3*1+0));
			xyz_bis[0][1]=*(xyz_list+3*1+1)+s1*(*(xyz_list+3*0+1)-*(xyz_list+3*1+1));
			xyz_bis[0][2]=*(xyz_list+3*1+2)+s1*(*(xyz_list+3*0+2)-*(xyz_list+3*1+2));

			/*New point 2*/
			xyz_bis[2][0]=*(xyz_list+3*1+0)+s2*(*(xyz_list+3*2+0)-*(xyz_list+3*1+0));
			xyz_bis[2][1]=*(xyz_list+3*1+1)+s2*(*(xyz_list+3*2+1)-*(xyz_list+3*1+1));
			xyz_bis[2][2]=*(xyz_list+3*1+2)+s2*(*(xyz_list+3*2+2)-*(xyz_list+3*1+2));
		}

		/*Compute fraction of grounded element*/
		GetTriaJacobianDeterminant(&area_init, xyz_list,NULL);
		GetTriaJacobianDeterminant(&area_grounded, &xyz_bis[0][0],NULL);
		if(mainlyfloating==true) area_grounded=area_init-area_grounded;
		phi=area_grounded/area_init;
	}

	if(phi>1. || phi<0.) _error_("Error. Problem with portion of grounded element: value should be between 0 and 1");

	return phi;
}
/*}}}*/
/*FUNCTION Penta::GetVertexPidList {{{*/
void  Penta::GetVertexPidList(int* doflist){

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

}
/*}}}*/
/*FUNCTION Penta::GetVertexSidList{{{*/
void  Penta::GetVertexSidList(int* sidlist){

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

}
/*}}}*/
/*FUNCTION Penta::GetConnectivityList {{{*/
void  Penta::GetConnectivityList(int* connectivity){
	for(int i=0;i<NUMVERTICES;i++) connectivity[i]=vertices[i]->Connectivity();
}
/*}}}*/
/*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],vertices,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::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);
	int numnodes = this->NumberofNodes();

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

}
/*}}}*/
/*FUNCTION Penta::GetNodesSidList{{{*/
void Penta::GetNodesSidList(int* sidlist){

	_assert_(sidlist);
	_assert_(nodes);
	int numnodes = this->NumberofNodes();

	for(int i=0;i<numnodes;i++){
		sidlist[i]=nodes[i]->Sid();
	}
}
/*}}}*/
/*FUNCTION Penta::GetNodesLidList{{{*/
void Penta::GetNodesLidList(int* lidlist){

	_assert_(lidlist);
	_assert_(nodes);
	int numnodes = this->NumberofNodes();

	for(int i=0;i<numnodes;i++){
		lidlist[i]=nodes[i]->Lid();
	}
}
/*}}}*/
/*FUNCTION Penta::GetNumberOfNodes;{{{*/
int Penta::GetNumberOfNodes(void){
	return this->NumberofNodes();
}
/*}}}*/
/*FUNCTION Penta::GetInputListOnVertices(IssmDouble* pvalue,int enumtype) {{{*/
void Penta::GetInputListOnVertices(IssmDouble* pvalue,int enumtype){

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

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

	/* Start looping on the number of vertices: */
	GaussPenta *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){

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

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

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

	_assert_(pvalue);

	Input *input    = inputs->GetInput(enumtype);
	int    numnodes = this->NumberofNodes();

	/* Start looping on the number of vertices: */
	if(input){
		GaussPenta* gauss=new GaussPenta();
		for(int iv=0;iv<this->NumberofNodes();iv++){
			gauss->GaussNode(this->element_type,iv);
			input->GetInputValue(&pvalue[iv],gauss);
		}
		delete gauss;
	}
	else{
		for(int iv=0;iv<numnodes;iv++) pvalue[iv]=defaultvalue;
	}
}
/*}}}*/
/*FUNCTION Penta::GetInputListOnNodes(IssmDouble* pvalue,int enumtype) {{{*/
void Penta::GetInputListOnNodes(IssmDouble* pvalue,int enumtype){

	_assert_(pvalue);

	/*Recover input*/
	Input* input=inputs->GetInput(enumtype);
	if (!input) _error_("Input " << EnumToStringx(enumtype) << " not found in element");
	int    numnodes = this->NumberofNodes();

	/* Start looping on the number of vertices: */
	GaussPenta* gauss=new GaussPenta();
	for (int iv=0;iv<this->NumberofNodes();iv++){
		gauss->GaussNode(this->element_type,iv);
		input->GetInputValue(&pvalue[iv],gauss);
	}
	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) _error_("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::GetQuadNormal {{{*/
void Penta:: GetQuadNormal(IssmDouble* normal,IssmDouble xyz_list[4][3]){

	/*Build unit outward pointing vector*/
	IssmDouble AB[3];
	IssmDouble AC[3];
	IssmDouble norm;

	AB[0]=xyz_list[1][0] - xyz_list[0][0];
	AB[1]=xyz_list[1][1] - xyz_list[0][1];
	AB[2]=xyz_list[1][2] - xyz_list[0][2];
	AC[0]=xyz_list[2][0] - xyz_list[0][0];
	AC[1]=xyz_list[2][1] - xyz_list[0][1];
	AC[2]=xyz_list[2][2] - xyz_list[0][2];

	cross(normal,AB,AC);
	norm=sqrt(pow(normal[0],2.0)+pow(normal[1],2.0)+pow(normal[2],2.0));

	for(int i=0;i<3;i++) normal[i]=normal[i]/norm;
}
/*}}}*/
/*FUNCTION Penta::GetSolutionFromInputs{{{*/
void  Penta::GetSolutionFromInputs(Vector<IssmDouble>* 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_STRESSBALANCE_
	case StressbalanceAnalysisEnum:
		int approximation;
		inputs->GetInputValue(&approximation,ApproximationEnum);
		if(approximation==FSApproximationEnum || approximation==NoneApproximationEnum){
			GetSolutionFromInputsStressbalanceFS(solution);
		}
		else if (approximation==SSAApproximationEnum || approximation==HOApproximationEnum || approximation==SIAApproximationEnum){
			GetSolutionFromInputsStressbalanceHoriz(solution);
		}
		else if (approximation==SSAHOApproximationEnum || approximation==HOFSApproximationEnum || approximation==SSAFSApproximationEnum){
			return; //the elements around will create the solution
		}
		break;
	case StressbalanceSIAAnalysisEnum:
		GetSolutionFromInputsStressbalanceSIA(solution);
		break;
	case StressbalanceVerticalAnalysisEnum:
		//GetSolutionFromInputsStressbalanceVert(solution);
		GetSolutionFromInputsOneDof(solution, VzEnum);
		break;
	#endif
	#ifdef _HAVE_THERMAL_
	case ThermalAnalysisEnum:
		//GetSolutionFromInputsThermal(solution);
		GetSolutionFromInputsOneDof(solution, TemperatureEnum);
		break;
	case EnthalpyAnalysisEnum:
		//GetSolutionFromInputsEnthalpy(solution);
		GetSolutionFromInputsOneDof(solution, EnthalpyEnum);
		break;
	#endif
	#ifdef _HAVE_HYDROLOGY_
	case HydrologyDCInefficientAnalysisEnum:
		GetSolutionFromInputsOneDof(solution, SedimentHeadEnum);
		break;
	case HydrologyDCEfficientAnalysisEnum:
		GetSolutionFromInputsOneDof(solution, EplHeadEnum);
		break;
  #endif
	default:
		_error_("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::GetStrainRate3dHO{{{*/
void Penta::GetStrainRate3dHO(IssmDouble* epsilon,IssmDouble* xyz_list, GaussPenta* gauss, Input* vx_input, Input* vy_input){
	/*Compute the 3d Blatter/HOStrain 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){
		_error_("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->GetVxStrainRate3dHO(epsilonvx,xyz_list,gauss);
	vy_input->GetVyStrainRate3dHO(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]
	 */

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

	/*Check that both inputs have been found*/
	if (!vx_input || !vy_input || !vz_input){
		_error_("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(int 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<IssmDouble>* vector,int input_enum){

	int vertexpidlist[NUMVERTICES];

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

	/*Prepare index list*/
	this->GetVertexPidList(&vertexpidlist[0]);

	/*Get input (either in element or material)*/
	Input* input=inputs->GetInput(input_enum);
	if(!input) _error_("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,&vertexpidlist[0]);
}
/*}}}*/
/*FUNCTION Penta::GetVectorFromResults{{{*/
void  Penta::GetVectorFromResults(Vector<IssmDouble>* 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 "<<offset<<" is "<<EnumToStringx(elementresult->InstanceEnum())<<" when "<<EnumToStringx(enum_in)<<" was expected");
	}  
	if(interp==P1Enum){
		int vertexpidlist[NUMVERTICES];
		int connectivity[NUMVERTICES];
		this->GetVertexSidList(&vertexpidlist[0]);
		this->GetConnectivityList(&connectivity[0]);
		elementresult->GetVectorFromResults(vector,&vertexpidlist[0],&connectivity[0],NUMVERTICES);
	}
	else if(interp==P0Enum){
		elementresult->GetElementVectorFromResults(vector,sid);
	}
	else{
		_printf_("Interpolation " << EnumToStringx(interp) << " not supported\n");
	}
}
/*}}}*/
/*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],vertices,NUMVERTICES);
	for(i=0;i<NUMVERTICES;i++) z_list[i]=xyz_list[i][2];
	PentaRef::GetInputValue(&z,z_list,gauss);

	return z;
}
/*}}}*/
/*FUNCTION Penta::GetZeroLevelsetCoordinates{{{*/
void Penta::GetZeroLevelsetCoordinates(IssmDouble* xyz_zero,IssmDouble xyz_list[6][3],int levelsetenum){
	/*Computeportion of the element that is grounded*/ 

	int         normal_orientation;
	IssmDouble  s1,s2;
	IssmDouble  levelset[NUMVERTICES];

	/*Recover parameters and values*/
	GetInputListOnVertices(&levelset[0],levelsetenum);

	if(levelset[0]*levelset[1]>0){ //Nodes 0 and 1 are similar, so points must be found on segment 0-2 and 1-2
		/*Portion of the segments*/
		s1=levelset[2]/(levelset[2]-levelset[1]);
		s2=levelset[2]/(levelset[2]-levelset[0]);

		if(levelset[2]>0) normal_orientation=1;
		/*New point 1*/
		xyz_zero[3*normal_orientation+0]=xyz_list[2][0]+s1*(xyz_list[1][0]-xyz_list[2][0]);
		xyz_zero[3*normal_orientation+1]=xyz_list[2][1]+s1*(xyz_list[1][1]-xyz_list[2][1]);
		xyz_zero[3*normal_orientation+2]=xyz_list[2][2]+s1*(xyz_list[1][2]-xyz_list[2][2]);

		/*New point 0*/
		xyz_zero[3*(1-normal_orientation)+0]=xyz_list[2][0]+s2*(xyz_list[0][0]-xyz_list[2][0]);
		xyz_zero[3*(1-normal_orientation)+1]=xyz_list[2][1]+s2*(xyz_list[0][1]-xyz_list[2][1]);
		xyz_zero[3*(1-normal_orientation)+2]=xyz_list[2][2]+s2*(xyz_list[0][2]-xyz_list[2][2]);

		/*New point 3*/
		xyz_zero[3*(2+1-normal_orientation)+0]=xyz_list[5][0]+s1*(xyz_list[4][0]-xyz_list[5][0]);
		xyz_zero[3*(2+1-normal_orientation)+1]=xyz_list[5][1]+s1*(xyz_list[4][1]-xyz_list[5][1]);
		xyz_zero[3*(2+1-normal_orientation)+2]=xyz_list[5][2]+s1*(xyz_list[4][2]-xyz_list[5][2]);

		/*New point 4*/
		xyz_zero[3*(2+normal_orientation)+0]=xyz_list[5][0]+s2*(xyz_list[3][0]-xyz_list[5][0]);
		xyz_zero[3*(2+normal_orientation)+1]=xyz_list[5][1]+s2*(xyz_list[3][1]-xyz_list[5][1]);
		xyz_zero[3*(2+normal_orientation)+2]=xyz_list[5][2]+s2*(xyz_list[3][2]-xyz_list[5][2]);
	}
	else if(levelset[1]*levelset[2]>0){ //Nodes 1 and 2 are similar, so points must be found on segment 0-1 and 0-2
		/*Portion of the segments*/
		s1=levelset[0]/(levelset[0]-levelset[2]);
		s2=levelset[0]/(levelset[0]-levelset[1]);

		if(levelset[0]>0) normal_orientation=1;
		/*New point 1*/
		xyz_zero[3*normal_orientation+0]=xyz_list[0][0]+s1*(xyz_list[2][0]-xyz_list[0][0]);
		xyz_zero[3*normal_orientation+1]=xyz_list[0][1]+s1*(xyz_list[2][1]-xyz_list[0][1]);
		xyz_zero[3*normal_orientation+2]=xyz_list[0][2]+s1*(xyz_list[2][2]-xyz_list[0][2]);

		/*New point 2*/
		xyz_zero[3*(1-normal_orientation)+0]=xyz_list[0][0]+s2*(xyz_list[1][0]-xyz_list[0][0]);
		xyz_zero[3*(1-normal_orientation)+1]=xyz_list[0][1]+s2*(xyz_list[1][1]-xyz_list[0][1]);
		xyz_zero[3*(1-normal_orientation)+2]=xyz_list[0][2]+s2*(xyz_list[1][2]-xyz_list[0][2]);

		/*New point 3*/
		xyz_zero[3*(2+1-normal_orientation)+0]=xyz_list[3][0]+s1*(xyz_list[5][0]-xyz_list[3][0]);
		xyz_zero[3*(2+1-normal_orientation)+1]=xyz_list[3][1]+s1*(xyz_list[5][1]-xyz_list[3][1]);
		xyz_zero[3*(2+1-normal_orientation)+2]=xyz_list[3][2]+s1*(xyz_list[5][2]-xyz_list[3][2]);

		/*New point 4*/
		xyz_zero[3*(2+normal_orientation)+0]=xyz_list[3][0]+s2*(xyz_list[4][0]-xyz_list[3][0]);
		xyz_zero[3*(2+normal_orientation)+1]=xyz_list[3][1]+s2*(xyz_list[4][1]-xyz_list[3][1]);
		xyz_zero[3*(2+normal_orientation)+2]=xyz_list[3][2]+s2*(xyz_list[4][2]-xyz_list[3][2]);
	}
	else if(levelset[0]*levelset[2]>0){ //Nodes 0 and 2 are similar, so points must be found on segment 1-0 and 1-2
		/*Portion of the segments*/
		s1=levelset[1]/(levelset[1]-levelset[0]);
		s2=levelset[1]/(levelset[1]-levelset[2]);

		if(levelset[1]>0) normal_orientation=1;
		/*New point 0*/
		xyz_zero[3*normal_orientation+0]=xyz_list[1][0]+s1*(xyz_list[0][0]-xyz_list[1][0]);
		xyz_zero[3*normal_orientation+1]=xyz_list[1][1]+s1*(xyz_list[0][1]-xyz_list[1][1]);
		xyz_zero[3*normal_orientation+2]=xyz_list[1][2]+s1*(xyz_list[0][2]-xyz_list[1][2]);

		/*New point 2*/
		xyz_zero[3*(1-normal_orientation)+0]=xyz_list[1][0]+s2*(xyz_list[2][0]-xyz_list[1][0]);
		xyz_zero[3*(1-normal_orientation)+1]=xyz_list[1][1]+s2*(xyz_list[2][1]-xyz_list[1][1]);
		xyz_zero[3*(1-normal_orientation)+2]=xyz_list[1][2]+s2*(xyz_list[2][2]-xyz_list[1][2]);

		/*New point 3*/
		xyz_zero[3*(2+1-normal_orientation)+0]=xyz_list[4][0]+s1*(xyz_list[3][0]-xyz_list[4][0]);
		xyz_zero[3*(2+1-normal_orientation)+1]=xyz_list[4][1]+s1*(xyz_list[3][1]-xyz_list[4][1]);
		xyz_zero[3*(2+1-normal_orientation)+2]=xyz_list[4][2]+s1*(xyz_list[3][2]-xyz_list[4][2]);

		/*New point 4*/
		xyz_zero[3*(2+normal_orientation)+0]=xyz_list[4][0]+s2*(xyz_list[5][0]-xyz_list[4][0]);
		xyz_zero[3*(2+normal_orientation)+1]=xyz_list[4][1]+s2*(xyz_list[5][1]-xyz_list[4][1]);
		xyz_zero[3*(2+normal_orientation)+2]=xyz_list[4][2]+s2*(xyz_list[5][2]-xyz_list[4][2]);
	}
	else if(levelset[0]==0 && levelset[1]==0){ //front is on point 0 and 1
		xyz_zero[3*0+0]=xyz_list[0][0];
		xyz_zero[3*0+1]=xyz_list[0][1];
		xyz_zero[3*0+2]=xyz_list[0][2];

		/*New point 2*/
		xyz_zero[3*1+0]=xyz_list[1][0];
		xyz_zero[3*1+1]=xyz_list[1][1];
		xyz_zero[3*1+2]=xyz_list[1][2];

		/*New point 3*/
		xyz_zero[3*2+0]=xyz_list[4][0];
		xyz_zero[3*2+1]=xyz_list[4][1];
		xyz_zero[3*2+2]=xyz_list[4][2];

		/*New point 4*/
		xyz_zero[3*3+0]=xyz_list[3][0];
		xyz_zero[3*3+1]=xyz_list[3][1];
		xyz_zero[3*3+2]=xyz_list[3][2];
	}
	else if(levelset[0]==0 && levelset[2]==0){ //front is on point 0 and 1
		xyz_zero[3*0+0]=xyz_list[2][0];
		xyz_zero[3*0+1]=xyz_list[2][1];
		xyz_zero[3*0+2]=xyz_list[2][2];

		/*New point 2*/
		xyz_zero[3*1+0]=xyz_list[0][0];
		xyz_zero[3*1+1]=xyz_list[0][1];
		xyz_zero[3*1+2]=xyz_list[0][2];

		/*New point 3*/
		xyz_zero[3*2+0]=xyz_list[3][0];
		xyz_zero[3*2+1]=xyz_list[3][1];
		xyz_zero[3*2+2]=xyz_list[3][2];

		/*New point 4*/
		xyz_zero[3*3+0]=xyz_list[5][0];
		xyz_zero[3*3+1]=xyz_list[5][1];
		xyz_zero[3*3+2]=xyz_list[5][2];
	}
	else if(levelset[1]==0 && levelset[2]==0){ //front is on point 0 and 1
		xyz_zero[3*0+0]=xyz_list[1][0];
		xyz_zero[3*0+1]=xyz_list[1][1];
		xyz_zero[3*0+2]=xyz_list[1][2];

		/*New point 2*/
		xyz_zero[3*1+0]=xyz_list[2][0];
		xyz_zero[3*1+1]=xyz_list[2][1];
		xyz_zero[3*1+2]=xyz_list[2][2];

		/*New point 3*/
		xyz_zero[3*2+0]=xyz_list[5][0];
		xyz_zero[3*2+1]=xyz_list[5][1];
		xyz_zero[3*2+2]=xyz_list[5][2];

		/*New point 4*/
		xyz_zero[3*3+0]=xyz_list[4][0];
		xyz_zero[3*3+1]=xyz_list[4][1];
		xyz_zero[3*3+2]=xyz_list[4][2];
	}
	else _error_("Case not covered");
}
/*}}}*/
/*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)_error_("could not find old input with enum: " << EnumToStringx(enum_type));

	/*ArtificialNoise: */
	input->ArtificialNoise(min,max);
}
/*}}}*/
/*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,reCast<bool,IssmDouble>(scalar)));
	}
	else if ((code==6) || (code==2)){ //integer
		this->inputs->AddInput(new IntInput(name,reCast<int,IssmDouble>(scalar)));
	}
	else if ((code==7) || (code==3)){ //IssmDouble
		this->inputs->AddInput(new DoubleInput(name,scalar));
	}
	else _error_("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,t;
	int             penta_vertex_ids[6];
	int             row;
	IssmDouble      nodeinputs[6];
	IssmDouble      time;
	TransientInput *transientinput      = NULL;

	/*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->elements);
			penta_vertex_ids[i]=iomodel->elements[6*index+i]; //ids for vertices are in the elements array from Matlab
		}

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

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

			/*create static input: */
			this->inputs->AddInput(new PentaInput(vector_enum,nodeinputs,P1Enum));
		}
		else if(M==iomodel->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];
				}

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

				if(t==0)transientinput=new TransientInput(vector_enum);
				transientinput->AddTimeInput(new PentaInput(vector_enum,nodeinputs,P1Enum),time);
			}
			this->inputs->AddInput(transientinput);
		}
		else _error_("nodal vector is either numberofvertices (" << iomodel->numberofvertices << "), or numberofvertices+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==iomodel->numberofelements){

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

			if (code==5){ //boolean
				this->inputs->AddInput(new BoolInput(vector_enum,reCast<bool,IssmDouble>(vector[index])));
			}
			else if (code==6){ //integer
				this->inputs->AddInput(new IntInput(vector_enum,reCast<int,IssmDouble>(vector[index])));
			}
			else if (code==7){ //IssmDouble
				this->inputs->AddInput(new DoubleInput(vector_enum,vector[index]));
			}
			else _error_("could not recognize nature of vector from code " << code);
		}
		else {
			_error_("transient elementary inputs not supported yet!");
		}
	}
	else{
		_error_("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 PentaInput(ThicknessEnum,zeros_list,P1Enum);

	/*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->material->inputs->GetInput(enum_type);
		else
		 _error_("object " << EnumToStringx(object_enum) << " not supported yet");
		if(!original_input) _error_("could not find input with enum " << EnumToStringx(enum_type));

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

		/*Step2: Create element thickness input*/
		GetVerticesCoordinates(&xyz_list[0][0],penta->vertices,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 PentaInput(ThicknessEnum,Helem_list,P1Enum);

		/*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->material->inputs->AddInput((Input*)depth_averaged_input);
	else
	 _error_("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*)material->inputs->GetInput(enum_type);
	}
	else{
		_error_("object of type " << EnumToStringx(object_type) << " not supported yet");
	}
	for(i=0;i<num_inputs;i++){
		if(!base_inputs[i]) _error_("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->material->inputs->AddInput((Input*)copy);
			}
			else{
				_error_("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)_error_("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){

	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->material->inputs->GetInput(MaterialsRheologyBEnum);
	else if (enum_type==MaterialsRheologyZbarEnum) input=this->material->inputs->GetInput(MaterialsRheologyZEnum);
	else input=this->inputs->GetInput(enum_type);
	//if (!input) _error_("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->AddResult(input->SpawnResult(step,time));
	#ifdef _HAVE_CONTROL_
	if(input->ObjectEnum()==ControlInputEnum){
		if(((ControlInput*)input)->gradient!=NULL) this->results->AddResult(((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->elements);
	/*}}}*/

	/*Recover vertices ids needed to initialize inputs*/
	for(i=0;i<6;i++){ 
		penta_vertex_ids[i]=iomodel->elements[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(reCast<int,IssmDouble>(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,PentaInputEnum,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,PentaInputEnum,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,PentaInputEnum,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,PentaInputEnum,nodeinputs,cmmininputs,cmmaxinputs,i+1));
					}
					break;
				case MaterialsRheologyBbarEnum:
				case MaterialsRheologyZbarEnum:
					/*Material will take care of it*/ break;
				default:
					_error_("Control " << EnumToStringx(reCast<int,IssmDouble>(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]==SSAApproximationEnum){
			this->inputs->AddInput(new IntInput(ApproximationEnum,SSAApproximationEnum));
		}
		else if (iomodel->Data(FlowequationElementEquationEnum)[index]==HOApproximationEnum){
			this->inputs->AddInput(new IntInput(ApproximationEnum,HOApproximationEnum));
		}
		else if (iomodel->Data(FlowequationElementEquationEnum)[index]==SSAHOApproximationEnum){
			this->inputs->AddInput(new IntInput(ApproximationEnum,SSAHOApproximationEnum));
		}
		else if (iomodel->Data(FlowequationElementEquationEnum)[index]==SIAApproximationEnum){
			this->inputs->AddInput(new IntInput(ApproximationEnum,SIAApproximationEnum));
		}
		else if (iomodel->Data(FlowequationElementEquationEnum)[index]==L1L2ApproximationEnum){
			this->inputs->AddInput(new IntInput(ApproximationEnum,L1L2ApproximationEnum));
		}
		else if (iomodel->Data(FlowequationElementEquationEnum)[index]==FSApproximationEnum){
			this->inputs->AddInput(new IntInput(ApproximationEnum,FSApproximationEnum));
		}
		else if (iomodel->Data(FlowequationElementEquationEnum)[index]==SSAFSApproximationEnum){
			this->inputs->AddInput(new IntInput(ApproximationEnum,SSAFSApproximationEnum));
		}
		else if (iomodel->Data(FlowequationElementEquationEnum)[index]==HOFSApproximationEnum){
			this->inputs->AddInput(new IntInput(ApproximationEnum,HOFSApproximationEnum));
		}
		else if (iomodel->Data(FlowequationElementEquationEnum)[index]==NoneApproximationEnum){
			this->inputs->AddInput(new IntInput(ApproximationEnum,NoneApproximationEnum));
		}
		else{
			_error_("Approximation type " << EnumToStringx(reCast<int,IssmDouble>(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 PentaInput(InversionCostFunctionsCoefficientsEnum,nodeinputs,P1Enum));
		}

		/*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_STRESSBALANCE_
	case StressbalanceAnalysisEnum:
		InputUpdateFromSolutionStressbalanceHoriz( solution);
		break;
	case StressbalanceSIAAnalysisEnum:
		InputUpdateFromSolutionStressbalanceSIA( solution);
		break;
	case StressbalanceVerticalAnalysisEnum:
		InputUpdateFromSolutionStressbalanceVert( solution);
		break;
	#endif
	#ifdef _HAVE_CONTROL_
	case AdjointHorizAnalysisEnum:
		int approximation;
		inputs->GetInputValue(&approximation,ApproximationEnum);
		if(approximation==FSApproximationEnum || approximation==NoneApproximationEnum){
			InputUpdateFromSolutionAdjointFS( 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 MasstransportAnalysisEnum:
		InputUpdateFromSolutionMasstransport(solution);
		break;
	case FreeSurfaceTopAnalysisEnum:
		InputUpdateFromSolutionFreeSurfaceTop(solution);
		break;
	case FreeSurfaceBaseAnalysisEnum:
		InputUpdateFromSolutionFreeSurfaceBase(solution);
		break;
	#ifdef _HAVE_BALANCED_
	case BalancethicknessAnalysisEnum:
		InputUpdateFromSolutionOneDofCollapsed(solution,ThicknessEnum);
		break;
	#endif
	#ifdef _HAVE_HYDROLOGY_
	case HydrologyDCInefficientAnalysisEnum:
		InputUpdateFromSolutionHydrologyDCInefficient(solution);
		break;
	case HydrologyDCEfficientAnalysisEnum:
		InputUpdateFromSolutionOneDofCollapsed(solution,EplHeadEnum);
		break;
	#endif
	default: 
		_error_("analysis " << analysis_type << " (" << EnumToStringx(analysis_type) << ") not supported yet");
	}
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionMasstransport{{{*/
void  Penta::InputUpdateFromSolutionMasstransport(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];
	IssmDouble phi[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,MasstransportMinThicknessEnum);
	for(i=0;i<numdof2d;i++){
		newthickness[i]=solution[doflist[i]];
		if(xIsNan<IssmDouble>(newthickness[i])) _error_("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);
	GetInputListOnVertices(&phi[0],MaskGroundediceLevelsetEnum);

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

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

	for(i=0;i<numdof;i++) {
		/*If shelf: hydrostatic equilibrium*/
		if (phi[i]>0.){
			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 _error_("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 PentaInput(ThicknessEnum,newthickness,P1Enum));
		penta->inputs->AddInput(new PentaInput(SurfaceEnum,newsurface,P1Enum));
		penta->inputs->AddInput(new PentaInput(BedEnum,newbed,P1Enum));

		/*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::InputUpdateFromSolutionFreeSurfaceTop{{{*/
void  Penta::InputUpdateFromSolutionFreeSurfaceTop(IssmDouble* solution){

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

	int    i;
	int*   doflist = NULL;
	IssmDouble newthickness[numdof];
	IssmDouble newbed[numdof];
	IssmDouble newsurface[numdof];

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

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

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

	/*Get previous bed and thickness*/
	GetInputListOnVertices(&newbed[0],BedEnum);

	for(i=0;i<numdof;i++) {
		newthickness[i]=newsurface[i]-newbed[i];
		_assert_(newthickness[i]>0.);
	}

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

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

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

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

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

	int    i;
	int*   doflist = NULL;
	IssmDouble newthickness[numdof];
	IssmDouble newbed[numdof];
	IssmDouble newsurface[numdof];

	/*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(i=0;i<numdof2d;i++){
		newbed[i]=solution[doflist[i]];
		if(xIsNan<IssmDouble>(newbed[i])) _error_("NaN found in solution vector");
		newbed[i+numdof2d]=newbed[i];
	}

	/*Get previous bed and thickness*/
	GetInputListOnVertices(&newsurface[0],SurfaceEnum);

	for(i=0;i<numdof;i++) {
		newthickness[i]=newsurface[i]-newbed[i];
		_assert_(newthickness[i]>0.);
	}

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

		/*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])) _error_("NaN found in solution vector");
	}

	/*Add input to the element: */
	this->inputs->AddInput(new PentaInput(enum_type,values,P1Enum));

	/*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])) _error_("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 PentaInput(enum_type,values,P1Enum));

		/*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){

	const int   numdof         = NDOF1 *NUMVERTICES;
	int        *doflist        = NULL;
	IssmDouble  values[numdof];

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

	switch(type){
		case VertexPIdEnum: 
			for (int i=0;i<NUMVERTICES;i++){
				values[i]=vector[this->vertices[i]->Pid()];
			}
			/*update input*/
			if (name==MaterialsRheologyZEnum || name==MaterialsRheologyZbarEnum){
				material->inputs->AddInput(new PentaInput(name,values,P1Enum));
			}
			else{
				this->inputs->AddInput(new PentaInput(name,values,P1Enum));
			}
			return;

		case VertexSIdEnum: 
			for (int i=0;i<NUMVERTICES;i++){
				values[i]=vector[this->vertices[i]->Sid()];
			}
			/*update input*/
			if (name==MaterialsRheologyZEnum || name==MaterialsRheologyZbarEnum){
				material->inputs->AddInput(new PentaInput(name,values,P1Enum));
			}
			else{
				this->inputs->AddInput(new PentaInput(name,values,P1Enum));
			}
			return;

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

			/*Use the dof list to index into the vector: */
			for(int i=0;i<numdof;i++){
				values[i]=vector[doflist[i]];
				if(xIsNan<IssmDouble>(values[i])) _error_("NaN found in solution vector");
			}
			/*Add input to the element: */
			this->inputs->AddInput(new PentaInput(name,values,P1Enum));

			/*Free ressources:*/
			xDelete<int>(doflist);
			return;

	  case NodeSIdEnum:
			for(int i=0;i<NUMVERTICES;i++){
				values[i]=vector[nodes[i]->Sid()];
				if(xIsNan<IssmDouble>(values[i])) _error_("NaN found in solution vector");
			}
			/*Add input to the element: */
			this->inputs->AddInput(new PentaInput(name,values,P1Enum));

			/*Free ressources:*/
			xDelete<int>(doflist);
			return;

	  default:
			_error_("type " << type << " (" << EnumToStringx(type) << ") not implemented 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==MaskGroundediceLevelsetEnum ||
				name==MaskIceLevelsetEnum ||
				name==GradientEnum ||
				name==OldGradientEnum  ||
				name==ConvergedEnum || 
				name==QmuVxEnum ||
				name==QmuVyEnum ||
				name==QmuVzEnum ||
				name==QmuVxMeshEnum ||
				name==QmuVyMeshEnum ||
				name==QmuVzMeshEnum ||
				name==QmuPressureEnum ||
				name==QmuBedEnum ||
				name==QmuThicknessEnum ||
				name==QmuSurfaceEnum ||
				name==QmuTemperatureEnum ||
				name==QmuMeltingEnum ||
				name==GiaWEnum || 
				name==GiadWdtEnum ||
				name==SedimentHeadEnum ||
				name==EplHeadEnum ||
				name==SedimentHeadOldEnum ||
				name==EplHeadOldEnum ||
				name==HydrologydcMaskEplactiveEnum ||
				name==WaterTransferEnum

				) {
		return true;
	}
	else return false;
}
/*}}}*/
/*FUNCTION Penta::IsFloating{{{*/
bool   Penta::IsFloating(){

	bool shelf;
	int  migration_style;
	parameters->FindParam(&migration_style,GroundinglineMigrationEnum);

	if(migration_style==SubelementMigrationEnum || migration_style==SubelementMigration2Enum){ //Floating if all nodes are floating
		if(this->inputs->Max(MaskGroundediceLevelsetEnum) <= 0.) shelf=true;
		else shelf=false;
	}
	else if(migration_style==NoneEnum || migration_style==AgressiveMigrationEnum || migration_style==SoftMigrationEnum){ //Floating if all nodes are floating
		if(this->inputs->Min(MaskGroundediceLevelsetEnum) > 0.) shelf=false;
		else shelf=true;
	}
	else _error_("migration_style not implemented yet");

	return shelf;
}
/*}}}*/
/*FUNCTION Penta::IsNodeOnShelfFromFlags {{{*/
bool   Penta::IsNodeOnShelfFromFlags(IssmDouble* flags){

	int  i;
	bool shelf=false;

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

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

	/*Get levelset*/
	IssmDouble ls[NUMVERTICES];
	GetInputListOnVertices(&ls[0],MaskIceLevelsetEnum);

	/*If the level set is >0, ice is present in this element*/
	if(ls[0]>0. || ls[1]>0. || ls[2]>0.) return false;

	/*If the level set is awlays <=0, there is no ice here*/
	return true;
}
/*}}}*/
/*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::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=sqrt(pow(xyz_list[node0][0]-xyz_list[node1][0],2)+pow(xyz_list[node0][1]-xyz_list[node1][1],2)+pow(xyz_list[node0][2]-xyz_list[node1][2],2));
		if(length<minlength || minlength<0) minlength=length;
	}

	return minlength;
}
/*}}}*/
/*FUNCTION Penta::NodalValue {{{*/
int    Penta::NodalValue(IssmDouble* pvalue, int index, int natureofdataenum){

	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==vertices[i]->Id()){
			/*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]=vertices[i]->Id(); //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,desfac,s0p;

   /*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();

  /*Get desertification effect parameters*/
  desfac=matpar->GetDesFac();
  s0p=matpar->GetS0p();

   /*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, desfac, s0p);
   }

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

	/*clean-up*/
	delete gauss;
}
/*}}}*/
/*FUNCTION Penta::ReduceMatrixFS {{{*/
void Penta::ReduceMatrixFS(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::ReduceVectorFS {{{*/
void Penta::ReduceVectorFS(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;
	int        numindices;
	int       *indices = NULL;
	IssmDouble slopex,slopey;
	IssmDouble xz_plane[6];

	/*For FS only: we want the CS to be tangential to the bedrock*/
	inputs->GetInputValue(&approximation,ApproximationEnum);
	if(IsFloating() || !IsOnBed() || (approximation!=FSApproximationEnum && approximation!=SSAFSApproximationEnum &&  approximation!=HOFSApproximationEnum)) return;

	/*Get number of nodes for velocity only and base*/
	BasalNodeIndices(&numindices,&indices,this->VelocityInterpolation());

	/*Get inputs*/
	Input* slopex_input=inputs->GetInput(BedSlopeXEnum); _assert_(slopex_input);
	Input* slopey_input=inputs->GetInput(BedSlopeYEnum); _assert_(slopey_input);

	/*Loop over basal nodes and update their CS*/
	GaussPenta* gauss = new GaussPenta();
	for(int i=0;i<numindices;i++){//FIXME
		gauss->GaussNode(this->VelocityInterpolation(),indices[i]);

		slopex_input->GetInputValue(&slopex,gauss);
		slopey_input->GetInputValue(&slopey,gauss);

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

		XZvectorsToCoordinateSystem(&this->nodes[indices[i]]->coord_system[0][0],&xz_plane[0]);
	}

	/*cleanup*/
	xDelete<int>(indices);
	delete gauss;
}
/*}}}*/
/*FUNCTION Penta::SetClone {{{*/
void  Penta::SetClone(int* minranks){

	_error_("not implemented yet");
}
/*}}}*/
/*FUNCTION Penta::SetCurrentConfiguration {{{*/
void  Penta::SetCurrentConfiguration(Elements* elementsin, Loads* loadsin, Nodes* 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::SetwiseNodeConnectivity{{{*/
void Penta::SetwiseNodeConnectivity(int* pd_nz,int* po_nz,Node* node,bool* flags,int* flagsindices,int set1_enum,int set2_enum){

	/*Intermediaries*/
	const int numnodes = this->NumberofNodes();

	/*Output */
	int d_nz = 0;
	int o_nz = 0;

	/*Loop over all nodes*/
	for(int i=0;i<numnodes;i++){

		if(!flags[this->nodes[i]->Lid()]){

			/*flag current node so that no other element processes it*/
			flags[this->nodes[i]->Lid()]=true;

			int counter=0;
			while(flagsindices[counter]>=0) counter++;
			flagsindices[counter]=this->nodes[i]->Lid();

			/*if node is clone, we have an off-diagonal non-zero, else it is a diagonal non-zero*/
			switch(set2_enum){
				case FsetEnum:
					if(nodes[i]->indexing.fsize){
						if(this->nodes[i]->IsClone())
						 o_nz += 1;
						else
						 d_nz += 1;
					}
					break;
				case GsetEnum:
					if(nodes[i]->indexing.gsize){
						if(this->nodes[i]->IsClone())
						 o_nz += 1;
						else
						 d_nz += 1;
					}
					break;
				case SsetEnum:
					if(nodes[i]->indexing.ssize){
						if(this->nodes[i]->IsClone())
						 o_nz += 1;
						else
						 d_nz += 1;
					}
					break;
				default: _error_("not supported");
			}
		}
	}

	/*Special case: 2d/3d coupling, the node of this element might be connected
	 *to the basal element*/
	int analysis_type,approximation,numlayers;
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	if(analysis_type==StressbalanceAnalysisEnum){
		inputs->GetInputValue(&approximation,ApproximationEnum);
		if(approximation==SSAHOApproximationEnum || approximation==SSAFSApproximationEnum){
			parameters->FindParam(&numlayers,MeshNumberoflayersEnum);
			o_nz += numlayers*3;
			d_nz += numlayers*3;
		}
	}

	/*Assign output pointers: */
	*pd_nz=d_nz;
	*po_nz=o_nz;
}
/*}}}*/
/*FUNCTION Penta::SpawnTria {{{*/
Tria*  Penta::SpawnTria(int location){

	int analysis_counter;

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

	/*Create Tria*/
	Tria* tria=new Tria();
	tria->id=this->id;
	tria->inputs=(Inputs*)this->inputs->SpawnTriaInputs(location);
	tria->parameters=this->parameters;
	tria->element_type=P1Enum; //Only P1 CG for now (TO BE CHANGED)
	this->SpawnTriaHook(dynamic_cast<ElementHook*>(tria),location);

	/*Spawn material*/
	tria->material=NULL;
	tria->material=(Material*)this->material->copy();
	delete tria->material->inputs;
	tria->material->inputs=(Inputs*)this->material->inputs->SpawnTriaInputs(location);

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

	/*Return new Tria*/
	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 b_pos[NUMVERTICES];				// Hs-SMB relation parameter
	IssmDouble b_neg[NUMVERTICES];				// Hs-SMB relation paremeter
	IssmDouble Href[NUMVERTICES];					// reference elevation from which deviations are used to calculate the SMB adjustment
	IssmDouble Smbref[NUMVERTICES];				// reference SMB to which deviations are added
   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(&Href[0],SurfaceforcingsHrefEnum);
	GetInputListOnVertices(&Smbref[0],SurfaceforcingsSmbrefEnum);
	GetInputListOnVertices(&b_pos[0],SurfaceforcingsBPosEnum);
	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(Smbref[i]>0){
		  smb[i]=Smbref[i]+b_pos[i]*(s[i]-Href[i]);
	  }
	  else{
		  smb[i]=Smbref[i]+b_neg[i]*(s[i]-Href[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 PentaInput(SurfaceforcingsMassBalanceEnum,&smb[0],P1Enum));
}
/*}}}*/
/*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(NoIceInElement())return 0;

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

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

		tria=(Tria*)SpawnTria(1); //lower face is 0, upper face is 1.
		S=tria->SurfaceArea();
		delete tria->material; 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);
	this->MaxAbsVy(&maxabsvy);
	this->MaxAbsVz(&maxabsvz);

	/* Get node coordinates and dof list: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,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 {{{*/
void Penta::Update(int index,IoModel* iomodel,int analysis_counter,int analysis_type,int finiteelement_type){ 

	/*Intermediaries*/
	int        i;
	int        penta_vertex_ids[6];
	IssmDouble nodeinputs[6];
	IssmDouble yts;
	bool       dakota_analysis;
	bool       isFS;
	IssmDouble beta,heatcapacity,referencetemperature,meltingpoint,latentheat;
	int        numnodes;
	int*       penta_node_ids = NULL;

	/*Fetch parameters: */
	iomodel->Constant(&yts,ConstantsYtsEnum);
	iomodel->Constant(&dakota_analysis,QmuIsdakotaEnum);
	iomodel->Constant(&isFS,FlowequationIsFSEnum);
	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->elements);
	/*}}}*/

	/*Recover element type*/
	this->SetElementType(finiteelement_type,analysis_counter);

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

	/*Recover nodes ids needed to initialize the node hook.*/
	switch(finiteelement_type){
		case P1Enum:
			numnodes         = 6;
			penta_node_ids   = xNew<int>(numnodes);
			penta_node_ids[0]=iomodel->nodecounter+iomodel->elements[6*index+0];
			penta_node_ids[1]=iomodel->nodecounter+iomodel->elements[6*index+1];
			penta_node_ids[2]=iomodel->nodecounter+iomodel->elements[6*index+2];
			penta_node_ids[3]=iomodel->nodecounter+iomodel->elements[6*index+3];
			penta_node_ids[4]=iomodel->nodecounter+iomodel->elements[6*index+4];
			penta_node_ids[5]=iomodel->nodecounter+iomodel->elements[6*index+5];
			break;
		case P1bubbleEnum: case P1bubblecondensedEnum:
			numnodes         = 7;
			penta_node_ids   = xNew<int>(numnodes);
			penta_node_ids[0]=iomodel->nodecounter+iomodel->elements[6*index+0];
			penta_node_ids[1]=iomodel->nodecounter+iomodel->elements[6*index+1];
			penta_node_ids[2]=iomodel->nodecounter+iomodel->elements[6*index+2];
			penta_node_ids[3]=iomodel->nodecounter+iomodel->elements[6*index+3];
			penta_node_ids[4]=iomodel->nodecounter+iomodel->elements[6*index+4];
			penta_node_ids[5]=iomodel->nodecounter+iomodel->elements[6*index+5];
			penta_node_ids[6]=iomodel->nodecounter+iomodel->numberofvertices+index+1;
			break;
		case P1xP2Enum:
			numnodes         = 9;
			penta_node_ids   = xNew<int>(numnodes);
			penta_node_ids[ 0]=iomodel->nodecounter+iomodel->elements[6*index+0];
			penta_node_ids[ 1]=iomodel->nodecounter+iomodel->elements[6*index+1];
			penta_node_ids[ 2]=iomodel->nodecounter+iomodel->elements[6*index+2];
			penta_node_ids[ 3]=iomodel->nodecounter+iomodel->elements[6*index+3];
			penta_node_ids[ 4]=iomodel->nodecounter+iomodel->elements[6*index+4];
			penta_node_ids[ 5]=iomodel->nodecounter+iomodel->elements[6*index+5];
			penta_node_ids[ 6]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+0]+1;
			penta_node_ids[ 7]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+1]+1;
			penta_node_ids[ 8]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+2]+1;
			break;
		case P2xP1Enum:
			numnodes         = 12;
			penta_node_ids   = xNew<int>(numnodes);
			penta_node_ids[ 0]=iomodel->nodecounter+iomodel->elements[6*index+0];
			penta_node_ids[ 1]=iomodel->nodecounter+iomodel->elements[6*index+1];
			penta_node_ids[ 2]=iomodel->nodecounter+iomodel->elements[6*index+2];
			penta_node_ids[ 3]=iomodel->nodecounter+iomodel->elements[6*index+3];
			penta_node_ids[ 4]=iomodel->nodecounter+iomodel->elements[6*index+4];
			penta_node_ids[ 5]=iomodel->nodecounter+iomodel->elements[6*index+5];
			penta_node_ids[ 6]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+3]+1;
			penta_node_ids[ 7]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+4]+1;
			penta_node_ids[ 8]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+5]+1;
			penta_node_ids[ 9]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+6]+1;
			penta_node_ids[10]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+7]+1;
			penta_node_ids[11]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+8]+1;
			break;
		case P2Enum:
			numnodes         = 15;
			penta_node_ids   = xNew<int>(numnodes);
			penta_node_ids[ 0]=iomodel->nodecounter+iomodel->elements[6*index+0];
			penta_node_ids[ 1]=iomodel->nodecounter+iomodel->elements[6*index+1];
			penta_node_ids[ 2]=iomodel->nodecounter+iomodel->elements[6*index+2];
			penta_node_ids[ 3]=iomodel->nodecounter+iomodel->elements[6*index+3];
			penta_node_ids[ 4]=iomodel->nodecounter+iomodel->elements[6*index+4];
			penta_node_ids[ 5]=iomodel->nodecounter+iomodel->elements[6*index+5];
			penta_node_ids[ 6]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+0]+1;
			penta_node_ids[ 7]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+1]+1;
			penta_node_ids[ 8]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+2]+1;
			penta_node_ids[ 9]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+3]+1;
			penta_node_ids[10]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+4]+1;
			penta_node_ids[11]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+5]+1;
			penta_node_ids[12]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+6]+1;
			penta_node_ids[13]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+7]+1;
			penta_node_ids[14]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+8]+1;
			break;
		case P1P1Enum: case P1P1GLSEnum:
			numnodes         = 12;
			penta_node_ids   = xNew<int>(numnodes);
			penta_node_ids[ 0]=iomodel->nodecounter+iomodel->elements[6*index+0];
			penta_node_ids[ 1]=iomodel->nodecounter+iomodel->elements[6*index+1];
			penta_node_ids[ 2]=iomodel->nodecounter+iomodel->elements[6*index+2];
			penta_node_ids[ 3]=iomodel->nodecounter+iomodel->elements[6*index+3];
			penta_node_ids[ 4]=iomodel->nodecounter+iomodel->elements[6*index+4];
			penta_node_ids[ 5]=iomodel->nodecounter+iomodel->elements[6*index+5];

			penta_node_ids[ 6]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elements[6*index+0];
			penta_node_ids[ 7]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elements[6*index+1];
			penta_node_ids[ 8]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elements[6*index+2];
			penta_node_ids[ 9]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elements[6*index+3];
			penta_node_ids[10]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elements[6*index+4];
			penta_node_ids[11]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elements[6*index+5];
			break;
		case MINIEnum: case MINIcondensedEnum:
			numnodes         = 13;
			penta_node_ids   = xNew<int>(numnodes);
			penta_node_ids[ 0]=iomodel->nodecounter+iomodel->elements[6*index+0];
			penta_node_ids[ 1]=iomodel->nodecounter+iomodel->elements[6*index+1];
			penta_node_ids[ 2]=iomodel->nodecounter+iomodel->elements[6*index+2];
			penta_node_ids[ 3]=iomodel->nodecounter+iomodel->elements[6*index+3];
			penta_node_ids[ 4]=iomodel->nodecounter+iomodel->elements[6*index+4];
			penta_node_ids[ 5]=iomodel->nodecounter+iomodel->elements[6*index+5];
			penta_node_ids[ 6]=iomodel->nodecounter+iomodel->numberofvertices+index+1;

			penta_node_ids[ 7]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->numberofelements+iomodel->elements[6*index+0];
			penta_node_ids[ 8]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->numberofelements+iomodel->elements[6*index+1];
			penta_node_ids[ 9]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->numberofelements+iomodel->elements[6*index+2];
			penta_node_ids[10]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->numberofelements+iomodel->elements[6*index+3];
			penta_node_ids[11]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->numberofelements+iomodel->elements[6*index+4];
			penta_node_ids[12]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->numberofelements+iomodel->elements[6*index+5];
			break;
		case TaylorHoodEnum:
			numnodes         = 21;
			penta_node_ids   = xNew<int>(numnodes);
			penta_node_ids[ 0]=iomodel->nodecounter+iomodel->elements[6*index+0];
			penta_node_ids[ 1]=iomodel->nodecounter+iomodel->elements[6*index+1];
			penta_node_ids[ 2]=iomodel->nodecounter+iomodel->elements[6*index+2];
			penta_node_ids[ 3]=iomodel->nodecounter+iomodel->elements[6*index+3];
			penta_node_ids[ 4]=iomodel->nodecounter+iomodel->elements[6*index+4];
			penta_node_ids[ 5]=iomodel->nodecounter+iomodel->elements[6*index+5];
			penta_node_ids[ 6]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+0]+1;
			penta_node_ids[ 7]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+1]+1;
			penta_node_ids[ 8]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+2]+1;
			penta_node_ids[ 9]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+3]+1;
			penta_node_ids[10]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+4]+1;
			penta_node_ids[11]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+5]+1;
			penta_node_ids[12]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+6]+1;
			penta_node_ids[13]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+7]+1;
			penta_node_ids[14]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[9*index+8]+1;

			penta_node_ids[15]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->numberofedges+iomodel->elements[6*index+0];
			penta_node_ids[16]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->numberofedges+iomodel->elements[6*index+1];
			penta_node_ids[17]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->numberofedges+iomodel->elements[6*index+2];
			penta_node_ids[18]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->numberofedges+iomodel->elements[6*index+3];
			penta_node_ids[19]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->numberofedges+iomodel->elements[6*index+4];
			penta_node_ids[20]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->numberofedges+iomodel->elements[6*index+5];
			break;
		default:
			_error_("Finite element "<<EnumToStringx(finiteelement_type)<<" not supported yet");
	}

	/*hooks: */
	this->SetHookNodes(penta_node_ids,numnodes,analysis_counter); this->nodes=NULL;
	xDelete<int>(penta_node_ids);

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

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

		case StressbalanceAnalysisEnum:

			/*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 PentaInput(VxEnum,nodeinputs,P1Enum));
				if(dakota_analysis) this->inputs->AddInput(new PentaInput(QmuVxEnum,nodeinputs,P1Enum));
			}
			if(!iomodel->Data(VyEnum)){
				for(i=0;i<6;i++)nodeinputs[i]=0;
				this->inputs->AddInput(new PentaInput(VyEnum,nodeinputs,P1Enum));
				if(dakota_analysis) this->inputs->AddInput(new PentaInput(QmuVyEnum,nodeinputs,P1Enum));
			}
			if(!iomodel->Data(VzEnum)){
				for(i=0;i<6;i++)nodeinputs[i]=0;
				this->inputs->AddInput(new PentaInput(VzEnum,nodeinputs,P1Enum));
				if(dakota_analysis) this->inputs->AddInput(new PentaInput(QmuVzEnum,nodeinputs,P1Enum));
			}
			if(!iomodel->Data(PressureEnum)){
				for(i=0;i<6;i++)nodeinputs[i]=0;
				if(dakota_analysis){
					this->inputs->AddInput(new PentaInput(PressureEnum,nodeinputs,P1Enum));
					this->inputs->AddInput(new PentaInput(QmuPressureEnum,nodeinputs,P1Enum));
				}
				if(isFS){
					this->inputs->AddInput(new PentaInput(PressureEnum,nodeinputs,P1Enum));
					this->inputs->AddInput(new PentaInput(PressurePicardEnum,nodeinputs,P1Enum));
				}
			}
			if(*(iomodel->Data(FlowequationElementEquationEnum)+index)==HOFSApproximationEnum){
				/*Create VzHO and VzFS Enums*/
				if(iomodel->Data(VzEnum) && iomodel->Data(FlowequationBorderFSEnum)){
					for(i=0;i<6;i++) nodeinputs[i]=iomodel->Data(VzEnum)[penta_vertex_ids[i]-1]*iomodel->Data(FlowequationBorderFSEnum)[penta_vertex_ids[i]-1];
					this->inputs->AddInput(new PentaInput(VzFSEnum,nodeinputs,P1Enum));
					for(i=0;i<6;i++) nodeinputs[i]=iomodel->Data(VzEnum)[penta_vertex_ids[i]-1]*(1-iomodel->Data(FlowequationBorderFSEnum)[penta_vertex_ids[i]-1]);
					this->inputs->AddInput(new PentaInput(VzHOEnum,nodeinputs,P1Enum));
				}
				else{
					for(i=0;i<6;i++)nodeinputs[i]=0;
					this->inputs->AddInput(new PentaInput(VzFSEnum,nodeinputs,P1Enum));
					this->inputs->AddInput(new PentaInput(VzHOEnum,nodeinputs,P1Enum));
				}
			}
			if(*(iomodel->Data(FlowequationElementEquationEnum)+index)==SSAFSApproximationEnum){
				/*Create VzSSA and VzFS Enums*/
				if(iomodel->Data(VzEnum) && iomodel->Data(FlowequationBorderFSEnum)){
					for(i=0;i<6;i++) nodeinputs[i]=iomodel->Data(VzEnum)[penta_vertex_ids[i]-1]*iomodel->Data(FlowequationBorderFSEnum)[penta_vertex_ids[i]-1];
					this->inputs->AddInput(new PentaInput(VzFSEnum,nodeinputs,P1Enum));
					for(i=0;i<6;i++) nodeinputs[i]=iomodel->Data(VzEnum)[penta_vertex_ids[i]-1]*(1-iomodel->Data(FlowequationBorderFSEnum)[penta_vertex_ids[i]-1]);
					this->inputs->AddInput(new PentaInput(VzSSAEnum,nodeinputs,P1Enum));
				}
				else{
					for(i=0;i<6;i++)nodeinputs[i]=0;
					this->inputs->AddInput(new PentaInput(VzFSEnum,nodeinputs,P1Enum));
					this->inputs->AddInput(new PentaInput(VzSSAEnum,nodeinputs,P1Enum));
				}
			}
			break;

		case ThermalAnalysisEnum:
			/*Initialize mesh velocity*/
			for(i=0;i<6;i++)nodeinputs[i]=0;
			this->inputs->AddInput(new PentaInput(VxMeshEnum,nodeinputs,P1Enum));
			this->inputs->AddInput(new PentaInput(VyMeshEnum,nodeinputs,P1Enum));
			this->inputs->AddInput(new PentaInput(VzMeshEnum,nodeinputs,P1Enum));
			if(dakota_analysis){
				this->inputs->AddInput(new PentaInput(QmuVxMeshEnum,nodeinputs,P1Enum));
				this->inputs->AddInput(new PentaInput(QmuVyMeshEnum,nodeinputs,P1Enum));
				this->inputs->AddInput(new PentaInput(QmuVzMeshEnum,nodeinputs,P1Enum));
			}
			break;

		case EnthalpyAnalysisEnum:
			/*Initialize mesh velocity*/
			for(i=0;i<6;i++)nodeinputs[i]=0;
			this->inputs->AddInput(new PentaInput(VxMeshEnum,nodeinputs,P1Enum));
			this->inputs->AddInput(new PentaInput(VyMeshEnum,nodeinputs,P1Enum));
			this->inputs->AddInput(new PentaInput(VzMeshEnum,nodeinputs,P1Enum));
			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 PentaInput(EnthalpyEnum,nodeinputs,P1Enum));
			}
			else _error_("temperature and waterfraction required for the enthalpy solution");
			break;

		default:
			/*No update for other solution types*/
			break;
	}
}
/*}}}*/
/*FUNCTION Penta::ViscousHeatingCreateInput {{{*/
void Penta::ViscousHeatingCreateInput(void){

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

	/*Intermediaries*/
	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],vertices,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);
		material->GetViscosity3dFS(&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 PentaInput(ViscousHeatingEnum,&viscousheating[0],P1Enum));

	/*Clean up and return*/
	delete gauss;
}
/*}}}*/

#ifdef _HAVE_RESPONSES_
/*FUNCTION Penta::AverageOntoPartition {{{*/
void  Penta::AverageOntoPartition(Vector<IssmDouble>* partition_contributions,Vector<IssmDouble>* partition_areas,IssmDouble* vertex_response,IssmDouble* qmu_part){
	_error_("Not supported yet!");
}
/*}}}*/
/*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(NoIceInElement())return 0;

	GetVerticesCoordinates(&xyz_list[0][0],vertices,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){

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

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

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

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

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

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

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

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

	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); //lower face is 0, upper face is 1.
	mass_flux=tria->MassFlux(segment);
	delete tria->material; 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){

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

				/*Assign output pointers:*/
				*presponse=vel;
			}
			break;
		default:  
			_error_("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(NoIceInElement() || !IsOnSurface()) return 0.;

	GetVerticesCoordinates(&xyz_list[0][0],vertices,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

	/*Return: */
	return Total_Smb;
}
/*}}}*/
#endif

#ifdef _HAVE_GIA_
/*FUNCTION Penta::GiaDeflection {{{*/
void Penta::GiaDeflection(Vector<IssmDouble>* wg,Vector<IssmDouble>* dwgdt,IssmDouble* x,IssmDouble* y){
	_error_("GIA deflection not implemented yet!");
}
/*}}}*/
#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,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],vertices,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(int 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(reCast<bool,IssmDouble>(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(reCast<bool,IssmDouble>(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(reCast<bool,IssmDouble>(dt)){
			GetNodalFunctionsP1(&L[0], gauss);
			D_scalar_trans=gauss->weight*Jdet;

			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(vx*vx + vy*vy + vz*vz)+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(reCast<bool,IssmDouble>(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(reCast<bool,IssmDouble>(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;
	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],vertices,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(int 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(reCast<bool,IssmDouble>(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); //lower face is 0, upper face is 1.
	ElementMatrix* Ke=tria->CreateKMatrixMelting();

	delete tria->material; 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,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],vertices,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(int 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(reCast<bool,IssmDouble>(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(reCast<bool,IssmDouble>(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(reCast<bool,IssmDouble>(dt)){
			GetNodalFunctionsP1(&L[0], gauss);
			D_scalar_trans=gauss->weight*Jdet;

			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(vx*vx + vy*vy + vz*vz)+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(reCast<bool,IssmDouble>(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(reCast<bool,IssmDouble>(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;
	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],vertices,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(int 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(reCast<bool,IssmDouble>(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,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],vertices,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(reCast<bool,IssmDouble>(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(int 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);
		material->GetViscosity3dFS(&viscosity,&epsilon[0]);
		GetPhi(&phi, &epsilon[0], viscosity);

		scalar_def=phi/rho_ice*Jdet*gauss->weight;
		if(reCast<bool,IssmDouble>(dt)) scalar_def=scalar_def*dt;

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

		/* Build transient now */
		if(reCast<bool,IssmDouble>(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(reCast<bool,IssmDouble>(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;
	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],vertices,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(int 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(reCast<bool,IssmDouble>(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;
	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 watercolumn;
	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],vertices,NUMVERTICES);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;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);
	Input* watercolumn_input=inputs->GetInput(WatercolumnEnum); _assert_(watercolumn_input);

	/*Build friction 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(int 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)){
				// temperate ice has positive thickness: grad enthalpy*n=0.
			}
			else{
				// only base temperate, set Dirichlet BCs in Penta::UpdateThermalBasalConstraints()
			}
		}
		else{
			watercolumn_input->GetInputValue(&watercolumn,gauss);
			if(watercolumn==0.){
				/*add geothermal heat flux and basal friction*/
				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(reCast<bool,IssmDouble>(dt)) scalar=dt*scalar;

				for(i=0;i<numdof;i++) pe->values[i]+=scalar*basis[i];
			}
			else{  /*do nothing (water layer acts as insulation)*/  }
		}
	}

	/*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,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],vertices,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 (reCast<bool,IssmDouble>(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(int 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);
		material->GetViscosity3dFS(&viscosity,&epsilon[0]);
		GetPhi(&phi, &epsilon[0], viscosity);

		scalar_def=phi/(rho_ice*heatcapacity)*Jdet*gauss->weight;
		if(reCast<bool,IssmDouble>(dt)) scalar_def=scalar_def*dt;

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

		/* Build transient now */
		if(reCast<bool,IssmDouble>(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(reCast<bool,IssmDouble>(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;
	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],vertices,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(int 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(reCast<bool,IssmDouble>(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;
	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],vertices,NUMVERTICES);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;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(int 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(reCast<bool,IssmDouble>(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<IssmDouble>* 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<IssmDouble>* solution){

	const int    numdof=NDOF1*NUMVERTICES;

	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(int 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])) _error_("NaN found in solution vector");
		//if(values[i]<0)      _printf_("temperature < 0°K found in solution vector\n");
		//if(values[i]>275)    _printf_("temperature > 275°K found in solution vector (Paterson's rheology associated is negative)\n");
	}

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

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

		/*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->material->inputs->AddInput(new PentaInput(MaterialsRheologyBEnum,B,P1Enum));
				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),
							material->GetN());
				for(i=0;i<numdof;i++) B[i]=B_average;
				this->material->inputs->AddInput(new PentaInput(MaterialsRheologyBEnum,B,P1Enum));
				break;
			default:
				_error_("Rheology law " << EnumToStringx(rheology_law) << " not supported yet");

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

	/*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])) _error_("NaN found in solution vector");
	}

	/*Get all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,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) _error_("Negative water fraction found in solution vector");
			//if(waterfraction[i]>1) _error_("Water fraction >1 found in solution vector");
		}

		this->inputs->AddInput(new PentaInput(EnthalpyEnum,values,P1Enum));
		this->inputs->AddInput(new PentaInput(WaterfractionEnum,waterfraction,P1Enum));
		this->inputs->AddInput(new PentaInput(TemperatureEnum,temperatures,P1Enum));

		/*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->material->inputs->AddInput(new PentaInput(MaterialsRheologyBEnum,B,P1Enum));
				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),
							material->GetN());
				for(i=0;i<numdof;i++) B[i]=B_average;
				this->material->inputs->AddInput(new PentaInput(MaterialsRheologyBEnum,B,P1Enum));
				break;
			case LliboutryDuvalEnum:
				B_average=LliboutryDuval((values[0]+values[1]+values[2]+values[3]+values[4]+values[5])/6.0,
							(pressure[0]+pressure[1]+pressure[2]+pressure[3]+pressure[4]+pressure[5])/6.0,
							material->GetN());
				for(i=0;i<numdof;i++) B[i]=B_average;
				this->material->inputs->AddInput(new PentaInput(MaterialsRheologyBEnum,B,P1Enum));
				break;
			default:
				_error_("Rheology law " << EnumToStringx(rheology_law) << " not supported yet");
		}
	}
	else{
		this->inputs->AddInput(new PentaInput(EnthalpyPicardEnum,values,P1Enum));
	}

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

	/*Intermediary*/
	bool        isenthalpy,isdynamicbasalspc,istemperatelayer;
	int         numindices, numindicesup;
	IssmDouble  h_pmp,pressure, pressureup;
    IssmDouble  enthalpy, enthalpyup;
	int        *indices = NULL, *indicesup = NULL;

	/* Only update Constraints at the base of grounded ice*/
	if(!IsOnBed() || IsFloating()) return;

	/*Check wether dynamic basal boundary conditions are activated */
	parameters->FindParam(&isenthalpy,ThermalIsenthalpyEnum);
	if(!isenthalpy) return;
	//parameters->FindParam(&isdynamicbasalspc,ThermalIsdynamicbasalspcEnum);
	isdynamicbasalspc = true;
	if(!isdynamicbasalspc) return;

	/*Fetch indices of basal & surface nodes for this finite element*/
	BasalNodeIndices(&numindices,&indices,this->element_type);
    SurfaceNodeIndices(&numindicesup,&indicesup,this->element_type);
    _assert_(numindices==numindicesup);

	/*Get parameters and inputs: */
	Input* pressure_input=inputs->GetInput(PressureEnum); _assert_(pressure_input);
    Input* enthalpy_input=inputs->GetInput(EnthalpyEnum); _assert_(enthalpy_input);

	/*if there is a temperate layer of positive thickness, set enthalpy=h_pmp at that node*/
	GaussPenta* gauss=new GaussPenta();
    GaussPenta* gaussup=new GaussPenta();
	for(int i=0;i<numindices;i++){
		gauss->GaussNode(this->element_type,indices[i]);
        gaussup->GaussNode(this->element_type,indicesup[i]); // TODO: check: are the nodes corresponding?

		/*Check wether there is a temperate layer at the base or not -> TODO: Johannes:) */
        /*check if node is temperate, if not, return*/
        enthalpy_input->GetInputValue(&enthalpy, gauss);
		pressure_input->GetInputValue(&pressure, gauss);
        if (enthalpy<matpar->PureIceEnthalpy(pressure)){
          // TODO: reset, if necessary, all spcs to non-valid 
          continue;
        }
        /*check if upper node is temperate. if yes, then we have a temperate layer of positive thickness. if not, continue.*/
        enthalpy_input->GetInputValue(&enthalpyup, gaussup);
		pressure_input->GetInputValue(&pressureup, gaussup);    
        istemperatelayer = false;
        if (enthalpyup>=matpar->PureIceEnthalpy(pressureup))
          istemperatelayer=true;

		/*Add Dirichlet constraint to this node if there is no layer of temperate ice with positive thickness*/
		if(!istemperatelayer){
			/*Calculate enthalpy at pressure melting point */
			h_pmp=matpar->PureIceEnthalpy(pressure);

			/*Apply Dirichlet condition (dof = 0 here, since there is only one degree of freedom per node)*/
			nodes[indices[i]]->ApplyConstraint(1,h_pmp);
		}
        else {
          /*remove spc*/
          nodes[indices[i]]->DofInFSet(0);
        }
	}

	/*Free ressources:*/
	xDelete<int>(indices);
    xDelete<int>(indicesup);
}
/*}}}*/
/*FUNCTION Penta::ComputeBasalMeltingrate{{{*/
void Penta::ComputeBasalMeltingrate(void){
	/*Calculate the basal melt rates of the enthalpy model after Aschwanden 2012*/

	/* Intermediaries */
	bool        isenthalpy, checkpositivethickness, istemperatelayer;
	int         i,j,analysis_type;
	IssmDouble  xyz_list[NUMVERTICES][3];
	IssmDouble  xyz_list_tria[NUMVERTICES2D][3];
	IssmDouble  heatflux,kappa;
	IssmDouble  vec_heatflux[3];
	IssmDouble  normal_base[3], d1enthalpy[3];
	IssmDouble  basalmeltingrate[NUMVERTICES], watercolumn[NUMVERTICES];
	IssmDouble  enthalpy[NUMVERTICES],pressure[NUMVERTICES];
	IssmDouble  temperature, waterfraction;
	IssmDouble  latentheat, rho_ice;
	IssmDouble  basalfriction, alpha2;
	IssmDouble  vx[NUMVERTICES],vy[NUMVERTICES],vz[NUMVERTICES];
	IssmDouble  geothermalflux[NUMVERTICES];
	IssmDouble  dt,meltingrate_enthalpy;
	Friction   *friction  = NULL;

	/* Only compute melt rates at the base of grounded ice*/
	if(!IsOnBed() || IsFloating()) return;

	/*Check wether enthalpy is activated*/
	parameters->FindParam(&isenthalpy,ThermalIsenthalpyEnum);
	if(!isenthalpy) return;

	/*Fetch parameters and inputs */
	latentheat=matpar->GetLatentHeat();
	rho_ice=this->matpar->GetRhoIce();
	GetInputListOnVertices(&vx[0],VxEnum);
	GetInputListOnVertices(&vy[0],VyEnum);
	GetInputListOnVertices(&vz[0],VzEnum);
	GetInputListOnVertices(&enthalpy[0],EnthalpyEnum);
	GetInputListOnVertices(&pressure[0],PressureEnum);
	GetInputListOnVertices(&watercolumn[0],WatercolumnEnum);
	GetInputListOnVertices(&basalmeltingrate[0],BasalforcingsMeltingRateEnum);
	GetInputListOnVertices(&geothermalflux[0],BasalforcingsGeothermalfluxEnum);
	Input* enthalpy_input=inputs->GetInput(EnthalpyEnum); _assert_(enthalpy_input);

	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;j++) xyz_list_tria[i][j]=xyz_list[i][j];

	/*Build friction element, needed later: */
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	friction=new Friction("3d",inputs,matpar,analysis_type);

	/*Ok, get meltingrates now from basal conditions*/
	GaussPenta* gauss=new GaussPenta();
	for(int iv=0;iv<3;iv++){

		gauss->GaussVertex(iv);
		checkpositivethickness=true;

		/*Calculate basal meltingrate after Fig.5 of A.Aschwanden 2012*/
		meltingrate_enthalpy=0.;
		if((watercolumn[iv]>0.) && (enthalpy[iv]<matpar->PureIceEnthalpy(pressure[iv]))){
			/*ensure that no ice is at T<Tm(p), if water layer present*/
			enthalpy[iv]=matpar->PureIceEnthalpy(pressure[iv]); 
			//meltingrate_enthalpy=0.;
		}
		else if(enthalpy[iv]<matpar->PureIceEnthalpy(pressure[iv])){
			/*cold base: set q*n=q_geo*n+frictionheating as Neumann BC in Penta::CreatePVectorEnthalpySheet*/
			meltingrate_enthalpy=0.;
			checkpositivethickness=false;
		}
		else {/*do nothing, go to next check*/}

		if(checkpositivethickness){
			/*ok, from here on all basal ice is temperate. Check for positive thickness of layer of temperate ice. */
			istemperatelayer=false;
			if(enthalpy[iv+3]>=matpar->PureIceEnthalpy(pressure[iv+3])) istemperatelayer=true;
			if(istemperatelayer) for(i=0;i<3;i++) vec_heatflux[i]=0.;
			else{
				enthalpy_input->GetInputDerivativeValue(&d1enthalpy[0],&xyz_list[0][0],gauss);
				kappa=matpar->GetEnthalpyDiffusionParameter(enthalpy[iv],pressure[iv]);
				for(i=0;i<3;i++) vec_heatflux[i]=-kappa*d1enthalpy[i];
			}

			/*geothermal heatflux*/
			BedNormal(&normal_base[0],xyz_list_tria);
			heatflux=0.;
			for(i=0;i<3;i++) heatflux+=(vec_heatflux[i])*normal_base[i];

			/*basal friction*/
			friction->GetAlpha2(&alpha2,gauss,VxEnum,VyEnum,VzEnum);
			basalfriction=alpha2*(pow(vx[iv],2.0)+pow(vy[iv],2.0)+pow(vz[iv],2.0));

			matpar->EnthalpyToThermal(&temperature, &waterfraction, enthalpy[iv],pressure[iv]);
			meltingrate_enthalpy=(basalfriction-(heatflux-geothermalflux[iv]))/((1-waterfraction)*latentheat*rho_ice); // m/yr water equivalent 
		}

		/*Update water column, basal meltingrate*/
		basalmeltingrate[iv]+=meltingrate_enthalpy;
		this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
		if(reCast<bool,IssmDouble>(dt))
		 watercolumn[iv]+=dt*meltingrate_enthalpy; 
		else
		 watercolumn[iv]+=meltingrate_enthalpy;
	}  
	/*feed updated variables back into model*/
	this->inputs->AddInput(new PentaInput(EnthalpyEnum,enthalpy,P1Enum));
	this->inputs->AddInput(new PentaInput(WatercolumnEnum,watercolumn,P1Enum));
	this->inputs->AddInput(new PentaInput(BasalforcingsMeltingRateEnum,basalmeltingrate,P1Enum));

	/*Clean up and return*/
	delete gauss;
	delete friction;
}
/*}}}*/
/*FUNCTION Penta::DrainWaterfraction{{{*/
void Penta::DrainWaterfraction(void){
    
    /*Intermediaries*/
	bool isenthalpy;
	IssmDouble waterfraction[NUMVERTICES], temperature[NUMVERTICES];
	IssmDouble enthalpy[NUMVERTICES], pressure[NUMVERTICES]; 
	IssmDouble latentheat, dt;

	/*Check wether enthalpy is activated*/
	parameters->FindParam(&isenthalpy,ThermalIsenthalpyEnum);
	if(!isenthalpy) return;       

	GetInputListOnVertices(&enthalpy[0],EnthalpyEnum);
	GetInputListOnVertices(&pressure[0],PressureEnum);
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	latentheat=matpar->GetLatentHeat();

	for(int iv=0;iv<NUMVERTICES;iv++){ 
		matpar->EnthalpyToThermal(&temperature[iv],&waterfraction[iv], enthalpy[iv],pressure[iv]); 

		/*drain water fraction & update enthalpy*/
		waterfraction[iv]-=DrainageFunctionWaterfraction(waterfraction[iv], dt);
		matpar->ThermalToEnthalpy(&enthalpy[iv], temperature[iv], waterfraction[iv], pressure[iv]);        
	}
	/*feed updated results back into model*/
	this->inputs->AddInput(new PentaInput(EnthalpyEnum,enthalpy,P1Enum));
	this->inputs->AddInput(new PentaInput(WaterfractionEnum,waterfraction,P1Enum));
	// this->inputs->AddInput(new PentaInput(TemperatureEnum,temperature,P1Enum));    // temperature should not change during drainage...
}
/*}}}*/
#endif

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

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

	if(enum_type==MaterialsRheologyBbarEnum){
		if(!IsOnBed()) return;
		input=(Input*)material->inputs->GetInput(MaterialsRheologyBEnum);
	}
	else if(enum_type==MaterialsRheologyZbarEnum){
		if(!IsOnBed()) return;
		input=(Input*)material->inputs->GetInput(MaterialsRheologyZEnum);
	}

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

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

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

	Input* input=NULL;

	if(enum_type==MaterialsRheologyBbarEnum){
		input=(Input*)material->inputs->GetInput(MaterialsRheologyBEnum);
	}
	else if(enum_type==MaterialsRheologyZbarEnum){
		input=(Input*)material->inputs->GetInput(MaterialsRheologyZEnum);
	}
	else{
		input=inputs->GetInput(enum_type);
	}
	if (!input) _error_("Input " << EnumToStringx(enum_type) << " not found");
	if (input->ObjectEnum()!=ControlInputEnum) _error_("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    vertexpidlist[NUMVERTICES];
	IssmDouble grad_list[NUMVERTICES];
	Input* grad_input=NULL;
	Input* input=NULL;

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

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

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

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

	switch(approximation){
		case SSAApproximationEnum:
			return CreateKMatrixAdjointSSA2d();
		case HOApproximationEnum:
			return CreateKMatrixAdjointHO();
		case FSApproximationEnum:
			return CreateKMatrixAdjointFS();
		case NoneApproximationEnum:
			return NULL;
		default:
			_error_("Approximation " << EnumToStringx(approximation) << " not supported yet");
	}
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixAdjointSSA2d{{{*/
ElementMatrix* Penta::CreateKMatrixAdjointSSA2d(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 average some fields*/
	switch(this->material->ObjectEnum()){
		case MaticeEnum:
			this->InputDepthAverageAtBase(MaterialsRheologyBEnum,MaterialsRheologyBbarEnum,MaterialsEnum);
			break;
		case MatdamageiceEnum:
			this->InputDepthAverageAtBase(MaterialsRheologyBEnum,MaterialsRheologyBbarEnum,MaterialsEnum);
			this->InputDepthAverageAtBase(MaterialsRheologyZEnum,MaterialsRheologyZbarEnum,MaterialsEnum);
			break;
		default:
			_error_("material "<<EnumToStringx(this->material->ObjectEnum())<<" not supported");
	}

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
	ElementMatrix* Ke=tria->CreateKMatrixAdjointSSA();
	delete tria->material; delete tria;

	/*Delete averaged fields*/
	this->material->inputs->DeleteInput(MaterialsRheologyBbarEnum);
	this->material->inputs->DeleteInput(MaterialsRheologyZbarEnum);

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

	/*Intermediaries */
	int        i,j;
	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 HO (first part of the Gateau derivative)*/
	parameters->FindParam(&incomplete_adjoint,InversionIncompleteAdjointEnum);
	ElementMatrix* Ke=CreateKMatrixStressbalanceHO();
	if(incomplete_adjoint) return Ke;

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,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(int 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->GetStrainRate3dHO(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
		material->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::CreateKMatrixAdjointFS{{{*/
ElementMatrix* Penta::CreateKMatrixAdjointFS(void){

	/*Intermediaries */
	int        i,j;
	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 dphi[3][NUMVERTICES];
	GaussPenta *gauss=NULL;

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

	/*Fetch number of nodes and dof for this finite element*/
	int vnumnodes = this->NumberofNodesVelocity();
	int pnumnodes = this->NumberofNodesPressure();
	int numdof    = vnumnodes*NDOF3 + pnumnodes*NDOF1;

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,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(int 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->GetStrainRate3dHO(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
		material->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,XYZEnum);

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

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

	switch(approximation){
		case SSAApproximationEnum:
			return CreatePVectorAdjointSSA();
		case HOApproximationEnum:
			return CreatePVectorAdjointHO();
		case NoneApproximationEnum:
			return NULL;
		case FSApproximationEnum:
			return CreatePVectorAdjointFS();
		default:
			_error_("Approximation " << EnumToStringx(approximation) << " not supported yet");
	}
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorAdjointSSA{{{*/
ElementVector* Penta::CreatePVectorAdjointSSA(){

	if (!IsOnBed()) return NULL;

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
	ElementVector* pe=tria->CreatePVectorAdjointHoriz();
	delete tria->material; delete tria;

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


	/*Nothing to be done if not on surface*/
	if (!IsOnSurface()) return NULL;

	/*Intermediaries */
	int        i,j,resp;
	int       *responses=NULL;
	int        num_responses;
	IssmDouble Jdet2d;
	IssmDouble obs_velocity_mag,velocity_mag;
	IssmDouble dux,duy;
	IssmDouble epsvel=2.220446049250313e-16;
	IssmDouble meanvel=3.170979198376458e-05; /*1000 m/yr*/
	IssmDouble scalex=0.,scaley=0.,scale=0.,S=0.;
	IssmDouble vx,vy,vxobs,vyobs,weight;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble xyz_list_tria[NUMVERTICES2D][3];

	/*Fetch number of nodes and dof for this finite element*/
	int numnodes = this->NumberofNodes();

	/*Initialize Element vector*/
	ElementVector* pe    = new ElementVector(nodes,numnodes,this->parameters);
	IssmDouble*    basis = xNew<IssmDouble>(numnodes);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	this->parameters->FindParam(&num_responses,InversionNumCostFunctionsEnum);
	this->parameters->FindParam(&responses,NULL,NULL,StepResponsesEnum);
	Input* weights_input=inputs->GetInput(InversionCostFunctionsCoefficientsEnum);   _assert_(weights_input);
	Input* vx_input     =inputs->GetInput(VxEnum);        _assert_(vx_input);
	Input* vy_input     =inputs->GetInput(VyEnum);        _assert_(vy_input);
	Input* vxobs_input  =inputs->GetInput(InversionVxObsEnum);     _assert_(vxobs_input);
	Input* vyobs_input  =inputs->GetInput(InversionVyObsEnum);     _assert_(vyobs_input);

	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;j++) xyz_list_tria[i][j]=xyz_list[i][j];

	/*Get Surface if required by one response*/
	for(resp=0;resp<num_responses;resp++){
		if(responses[resp]==SurfaceAverageVelMisfitEnum){
			inputs->GetInputValue(&S,SurfaceAreaEnum); break;
		}
	}

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

		gauss->GaussPoint(ig);

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

		/*Get all parameters at gaussian point*/
		vx_input->GetInputValue(&vx,gauss);
		vy_input->GetInputValue(&vy,gauss);
		vxobs_input->GetInputValue(&vxobs,gauss);
		vyobs_input->GetInputValue(&vyobs,gauss);
		GetNodalFunctions(basis, gauss);

		/*Loop over all requested responses*/
		for(resp=0;resp<num_responses;resp++){

			weights_input->GetInputValue(&weight,gauss,resp);

			switch(responses[resp]){
				case SurfaceAbsVelMisfitEnum:
					/*
					 *      1  [           2              2 ]
					 * J = --- | (u - u   )  +  (v - v   )  |
					 *      2  [       obs            obs   ]
					 *
					 *        dJ
					 * DU = - -- = (u   - u )
					 *        du     obs
					 */
					for(i=0;i<numnodes;i++){
						dux=vxobs-vx;
						duy=vyobs-vy;
						pe->values[i*NDOF2+0]+=dux*weight*Jdet2d*gauss->weight*basis[i]; 
						pe->values[i*NDOF2+1]+=duy*weight*Jdet2d*gauss->weight*basis[i]; 
					}
					break;
				case SurfaceRelVelMisfitEnum:
					/*
					 *      1  [     \bar{v}^2             2   \bar{v}^2              2 ]
					 * J = --- | -------------  (u - u   ) + -------------  (v - v   )  |
					 *      2  [  (u   + eps)^2       obs    (v   + eps)^2       obs    ]
					 *              obs                        obs                      
					 *
					 *        dJ     \bar{v}^2
					 * DU = - -- = ------------- (u   - u )
					 *        du   (u   + eps)^2    obs
					 *               obs
					 */
					for(i=0;i<numnodes;i++){
						scalex=pow(meanvel/(vxobs+epsvel),2); if(vxobs==0)scalex=0;
						scaley=pow(meanvel/(vyobs+epsvel),2); if(vyobs==0)scaley=0;
						dux=scalex*(vxobs-vx);
						duy=scaley*(vyobs-vy);
						pe->values[i*NDOF2+0]+=dux*weight*Jdet2d*gauss->weight*basis[i]; 
						pe->values[i*NDOF2+1]+=duy*weight*Jdet2d*gauss->weight*basis[i]; 
					}
					break;
				case SurfaceLogVelMisfitEnum:
					/*
					 *                 [        vel + eps     ] 2
					 * J = 4 \bar{v}^2 | log ( -----------  ) |  
					 *                 [       vel   + eps    ]
					 *                            obs
					 *
					 *        dJ                 2 * log(...)
					 * DU = - -- = - 4 \bar{v}^2 -------------  u
					 *        du                 vel^2 + eps
					 *            
					 */
					for(i=0;i<numnodes;i++){
						velocity_mag    =sqrt(pow(vx,   2)+pow(vy,   2))+epsvel;
						obs_velocity_mag=sqrt(pow(vxobs,2)+pow(vyobs,2))+epsvel;
						scale=-8*pow(meanvel,2)/pow(velocity_mag,2)*log(velocity_mag/obs_velocity_mag);
						dux=scale*vx;
						duy=scale*vy;
						pe->values[i*NDOF2+0]+=dux*weight*Jdet2d*gauss->weight*basis[i]; 
						pe->values[i*NDOF2+1]+=duy*weight*Jdet2d*gauss->weight*basis[i]; 
					}
					break;
				case SurfaceAverageVelMisfitEnum:
					/*
					 *      1                    2              2
					 * J = ---  sqrt(  (u - u   )  +  (v - v   )  )
					 *      S                obs            obs
					 *
					 *        dJ      1       1 
					 * DU = - -- = - --- ----------- * 2 (u - u   )
					 *        du      S  2 sqrt(...)           obs
					 */
					for(i=0;i<numnodes;i++){
						scale=1./(S*2*sqrt(pow(vx-vxobs,2)+pow(vy-vyobs,2))+epsvel);
						dux=scale*(vxobs-vx);
						duy=scale*(vyobs-vy);
						pe->values[i*NDOF2+0]+=dux*weight*Jdet2d*gauss->weight*basis[i]; 
						pe->values[i*NDOF2+1]+=duy*weight*Jdet2d*gauss->weight*basis[i]; 
					}
					break;
				case SurfaceLogVxVyMisfitEnum:
					/*
					 *      1            [        |u| + eps     2          |v| + eps     2  ]
					 * J = --- \bar{v}^2 | log ( -----------  )   +  log ( -----------  )   |  
					 *      2            [       |u    |+ eps              |v    |+ eps     ]
					 *                              obs                       obs
					 *        dJ                              1      u                             1
					 * DU = - -- = - \bar{v}^2 log(u...) --------- ----  ~ - \bar{v}^2 log(u...) ------
					 *        du                         |u| + eps  |u|                           u + eps
					 */
					for(i=0;i<numnodes;i++){
						dux = - meanvel*meanvel * log((fabs(vx)+epsvel)/(fabs(vxobs)+epsvel)) / (vx+epsvel);
						duy = - meanvel*meanvel * log((fabs(vy)+epsvel)/(fabs(vyobs)+epsvel)) / (vy+epsvel);
						pe->values[i*NDOF2+0]+=dux*weight*Jdet2d*gauss->weight*basis[i]; 
						pe->values[i*NDOF2+1]+=duy*weight*Jdet2d*gauss->weight*basis[i]; 
					}
					break;
				case DragCoefficientAbsGradientEnum:
					/*Nothing in P vector*/
					break;
				case ThicknessAbsGradientEnum:
					/*Nothing in P vector*/
					break;
				case ThicknessAlongGradientEnum:
					/*Nothing in P vector*/
					break;
				case ThicknessAcrossGradientEnum:
					/*Nothing in P vector*/
					break;
				case RheologyBbarAbsGradientEnum:
					/*Nothing in P vector*/
					break;
				default:
					_error_("response " << EnumToStringx(responses[resp]) << " not supported yet");
			}
		}
	}

	/*Clean up and return*/
	xDelete<IssmDouble>(basis);
	xDelete<int>(responses);
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorAdjointFS{{{*/
ElementVector* Penta::CreatePVectorAdjointFS(void){

	if(!IsOnSurface()) return NULL;

	/*Intermediaries */
	int         i,j,resp;
	int        *responses= NULL;
	int         num_responses;
	IssmDouble  Jdet2d;
	IssmDouble  obs_velocity_mag,velocity_mag;
	IssmDouble  dux,duy;
	IssmDouble  epsvel  = 2.220446049250313e-16;
	IssmDouble  meanvel = 3.170979198376458e-05;  /*1000 m/yr */
	IssmDouble  scalex  = 0,scaley=0,scale=0,S=0;
	IssmDouble  vx,vy,vxobs,vyobs,weight;
	IssmDouble  xyz_list[NUMVERTICES][3];
	IssmDouble	xyz_list_tria[NUMVERTICES2D][3];

	/*Fetch number of nodes and dof for this finite element*/
	int vnumnodes = this->NumberofNodesVelocity();
	int pnumnodes = this->NumberofNodesPressure();

	/*Prepare coordinate system list*/
	int* cs_list = xNew<int>(vnumnodes+pnumnodes);
	for(i=0;i<vnumnodes;i++) cs_list[i]           = XYZEnum;
	for(i=0;i<pnumnodes;i++) cs_list[vnumnodes+i] = PressureEnum;

	/*Initialize Element matrix and vectors*/
	ElementVector* pe     = new ElementVector(nodes,vnumnodes+pnumnodes,this->parameters,FSApproximationEnum);
	IssmDouble*    vbasis = xNew<IssmDouble>(vnumnodes);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	this->parameters->FindParam(&num_responses,InversionNumCostFunctionsEnum);
	this->parameters->FindParam(&responses,NULL,NULL,StepResponsesEnum);
	Input* weights_input = inputs->GetInput(InversionCostFunctionsCoefficientsEnum); _assert_(weights_input);
	Input* vx_input      = inputs->GetInput(VxEnum);                                 _assert_(vx_input);
	Input* vy_input      = inputs->GetInput(VyEnum);                                 _assert_(vy_input);
	Input* vxobs_input   = inputs->GetInput(InversionVxObsEnum);                     _assert_(vxobs_input);
	Input* vyobs_input   = inputs->GetInput(InversionVyObsEnum);                     _assert_(vyobs_input);

	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;j++) xyz_list_tria[i][j]=xyz_list[i][j];

	/*Get Surface if required by one response*/
	for(resp=0;resp<num_responses;resp++){
		if(responses[resp]==SurfaceAverageVelMisfitEnum){
			inputs->GetInputValue(&S,SurfaceAreaEnum); break;
		}
	}

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

		gauss->GaussPoint(ig);

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

		/*Get all parameters at gaussian point*/
		vx_input->GetInputValue(&vx,gauss);
		vy_input->GetInputValue(&vy,gauss);
		vxobs_input->GetInputValue(&vxobs,gauss);
		vyobs_input->GetInputValue(&vyobs,gauss);
		GetNodalFunctionsVelocity(vbasis,gauss);

		/*Loop over all requested responses*/
		for(resp=0;resp<num_responses;resp++){

			weights_input->GetInputValue(&weight,gauss,resp);

			switch(responses[resp]){

				case SurfaceAbsVelMisfitEnum:
					/*
					 *      1  [           2              2 ]
					 * J = --- | (u - u   )  +  (v - v   )  |
					 *      2  [       obs            obs   ]
					 *
					 *        dJ
					 * DU = - -- = (u   - u )
					 *        du     obs
					 */
					for(i=0;i<vnumnodes;i++){
						dux=vxobs-vx;
						duy=vyobs-vy;
						pe->values[i*NDOF3+0]+=dux*weight*Jdet2d*gauss->weight*vbasis[i]; 
						pe->values[i*NDOF3+1]+=duy*weight*Jdet2d*gauss->weight*vbasis[i]; 
					}
					break;
				case SurfaceRelVelMisfitEnum:
					/*
					 *      1  [     \bar{v}^2             2   \bar{v}^2              2 ]
					 * J = --- | -------------  (u - u   ) + -------------  (v - v   )  |
					 *      2  [  (u   + eps)^2       obs    (v   + eps)^2       obs    ]
					 *              obs                        obs                      
					 *
					 *        dJ     \bar{v}^2
					 * DU = - -- = ------------- (u   - u )
					 *        du   (u   + eps)^2    obs
					 *               obs
					 */
					for(i=0;i<vnumnodes;i++){
						scalex=pow(meanvel/(vxobs+epsvel),2); if(vxobs==0)scalex=0;
						scaley=pow(meanvel/(vyobs+epsvel),2); if(vyobs==0)scaley=0;
						dux=scalex*(vxobs-vx);
						duy=scaley*(vyobs-vy);
						pe->values[i*NDOF3+0]+=dux*weight*Jdet2d*gauss->weight*vbasis[i]; 
						pe->values[i*NDOF3+1]+=duy*weight*Jdet2d*gauss->weight*vbasis[i]; 
					}
					break;
				case SurfaceLogVelMisfitEnum:
					/*
					 *                 [        vel + eps     ] 2
					 * J = 4 \bar{v}^2 | log ( -----------  ) |  
					 *                 [       vel   + eps    ]
					 *                            obs
					 *
					 *        dJ                 2 * log(...)
					 * DU = - -- = - 4 \bar{v}^2 -------------  u
					 *        du                 vel^2 + eps
					 *            
					 */
					for(i=0;i<vnumnodes;i++){
						velocity_mag    =sqrt(pow(vx,   2)+pow(vy,   2))+epsvel;
						obs_velocity_mag=sqrt(pow(vxobs,2)+pow(vyobs,2))+epsvel;
						scale=-8*pow(meanvel,2)/pow(velocity_mag,2)*log(velocity_mag/obs_velocity_mag);
						dux=scale*vx;
						duy=scale*vy;
						pe->values[i*NDOF3+0]+=dux*weight*Jdet2d*gauss->weight*vbasis[i]; 
						pe->values[i*NDOF3+1]+=duy*weight*Jdet2d*gauss->weight*vbasis[i]; 
					}
					break;
				case SurfaceAverageVelMisfitEnum:
					/*
					 *      1                    2              2
					 * J = ---  sqrt(  (u - u   )  +  (v - v   )  )
					 *      S                obs            obs
					 *
					 *        dJ      1       1 
					 * DU = - -- = - --- ----------- * 2 (u - u   )
					 *        du      S  2 sqrt(...)           obs
					 */
					for(i=0;i<vnumnodes;i++){
						scale=1./(S*2*sqrt(pow(vx-vxobs,2)+pow(vy-vyobs,2))+epsvel);
						dux=scale*(vxobs-vx);
						duy=scale*(vyobs-vy);
						pe->values[i*NDOF3+0]+=dux*weight*Jdet2d*gauss->weight*vbasis[i]; 
						pe->values[i*NDOF3+1]+=duy*weight*Jdet2d*gauss->weight*vbasis[i]; 
					}
					break;
				case SurfaceLogVxVyMisfitEnum:
					/*
					 *      1            [        |u| + eps     2          |v| + eps     2  ]
					 * J = --- \bar{v}^2 | log ( -----------  )   +  log ( -----------  )   |  
					 *      2            [       |u    |+ eps              |v    |+ eps     ]
					 *                              obs                       obs
					 *        dJ                              1      u                             1
					 * DU = - -- = - \bar{v}^2 log(u...) --------- ----  ~ - \bar{v}^2 log(u...) ------
					 *        du                         |u| + eps  |u|                           u + eps
					 */
					for(i=0;i<vnumnodes;i++){
						dux = - meanvel*meanvel * log((fabs(vx)+epsvel)/(fabs(vxobs)+epsvel)) / (vx+epsvel);
						duy = - meanvel*meanvel * log((fabs(vy)+epsvel)/(fabs(vyobs)+epsvel)) / (vy+epsvel);
						pe->values[i*NDOF3+0]+=dux*weight*Jdet2d*gauss->weight*vbasis[i]; 
						pe->values[i*NDOF3+1]+=duy*weight*Jdet2d*gauss->weight*vbasis[i]; 
					}
					break;
				case DragCoefficientAbsGradientEnum:
					/*Nothing in P vector*/
					break;
				case ThicknessAbsGradientEnum:
					/*Nothing in P vector*/
					break;
				case ThicknessAcrossGradientEnum:
					/*Nothing in P vector*/
					break;
				case ThicknessAlongGradientEnum:
					/*Nothing in P vector*/
					break;
				case RheologyBbarAbsGradientEnum:
					/*Nothing in P vector*/
					break;
				default:
					_error_("response " << EnumToStringx(responses[resp]) << " not supported yet");
			}
		}
	}

	/*Clean up and return*/
	xDelete<int>(responses);
	xDelete<int>(cs_list);
	xDelete<IssmDouble>(vbasis);
	delete gauss;
	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->vertices[i]->Pid() + control_index;
	}

}
/*}}}*/
/*FUNCTION Penta::Gradj {{{*/
void  Penta::Gradj(Vector<IssmDouble>* 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(NoIceInElement())return;

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

		case FrictionCoefficientEnum:
			inputs->GetInputValue(&approximation,ApproximationEnum);
			switch(approximation){
				case SSAApproximationEnum:
					GradjDragSSA(gradient,control_index);
					break;
				case HOApproximationEnum:
					GradjDragHO(gradient,control_index);
					break;
				case FSApproximationEnum:
					GradjDragFS(gradient,control_index);
					break;
				case NoneApproximationEnum:
					/*Gradient is 0*/
					break;
				default:
					_error_("approximation " << EnumToStringx(approximation) << " not supported yet");
			}
			break;

		case MaterialsRheologyBbarEnum:
			inputs->GetInputValue(&approximation,ApproximationEnum);
			switch(approximation){
				case SSAApproximationEnum:
					GradjBbarSSA(gradient,control_index);
					break;
				case HOApproximationEnum:
					GradjBbarHO(gradient,control_index);
					break;
				case FSApproximationEnum:
					GradjBbarFS(gradient,control_index);
					break;
				case NoneApproximationEnum:
					/*Gradient is 0*/
					break;
				default:
					_error_("approximation " << EnumToStringx(approximation) << " not supported yet");
			}
			break;

		default:
			_error_("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()){
				tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
				tria->GradjDragGradient(gradient,resp,control_index);
				delete tria->material; delete tria;
			}
			break;
		case RheologyBbarAbsGradientEnum:
			if(IsOnBed()){
				tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
				tria->GradjBGradient(gradient,resp,control_index);
				delete tria->material; delete tria;
			}
			break;
		default:
			_error_("response " << EnumToStringx(responses[resp]) << " not supported yet");
	}
	xDelete<int>(responses);
}
/*}}}*/
/*FUNCTION Penta::GradjDragSSA {{{*/
void  Penta::GradjDragSSA(Vector<IssmDouble>* 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); //lower face is 0, upper face is 1.
	tria->GradjDragSSA(gradient,control_index);
	delete tria->material; delete tria;

} /*}}}*/
/*FUNCTION Penta::GradjDragHO {{{*/
void  Penta::GradjDragHO(Vector<IssmDouble>* gradient,int control_index){

	int        i,j;
	int        analysis_type;
	int        vertexpidlist[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(&vertexpidlist[0],control_index);
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;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(int 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];
		}

		/*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,vertexpidlist,grade_g,ADD_VAL);

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

	int        i,j;
	int        analysis_type;
	int        vertexpidlist[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],vertices,NUMVERTICES);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;j++) xyz_list_tria[i][j]=xyz_list[i][j];
	GradientIndexing(&vertexpidlist[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(int 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,vertexpidlist,grade_g,ADD_VAL);

	delete friction;
	delete gauss;
}
/*}}}*/
/*FUNCTION Penta::GradjBbarSSA {{{*/
void  Penta::GradjBbarSSA(Vector<IssmDouble>* 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); //lower face is 0, upper face is 1.
	tria->GradjBSSA(gradient,control_index);
	delete tria->material; delete tria;

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

} /*}}}*/
/*FUNCTION Penta::GradjBbarHO {{{*/
void  Penta::GradjBbarHO(Vector<IssmDouble>* 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);
	tria->GradjBSSA(gradient,control_index);    //We use SSA as an estimate for now
	delete tria->material; delete tria;

	/*delete Average B*/
	this->material->inputs->DeleteInput(MaterialsRheologyBbarEnum);
} /*}}}*/
/*FUNCTION Penta::GradjBbarFS {{{*/
void  Penta::GradjBbarFS(Vector<IssmDouble>* 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);
	tria->GradjBSSA(gradient,control_index);    //We use SSA as an estimate for now
	delete tria->material; delete tria;

	/*delete Average B*/
	this->material->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*)material->inputs->GetInput(MaterialsRheologyBEnum); _assert_(input);
		}
		else if(control_type[i]==MaterialsRheologyZbarEnum){
			if (!IsOnBed()) goto cleanup_and_return;
			input=(Input*)material->inputs->GetInput(MaterialsRheologyZEnum); _assert_(input);
		}
		else{
			input=(Input*)this->inputs->GetInput(control_type[i]); _assert_(input);
		}

		if (input->ObjectEnum()!=ControlInputEnum) _error_("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);
		}
		else if(control_type[i]==MaterialsRheologyZbarEnum){
			this->InputExtrude(MaterialsRheologyZEnum,MaterialsEnum);
		}
	}

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

	int          i;
	int*         vdoflist=NULL;
	int*         pdoflist=NULL;
	IssmDouble   FSreconditioning;
	GaussPenta  *gauss;

	/*Fetch number of nodes and dof for this finite element*/
	int vnumnodes = this->NumberofNodesVelocity();
	int pnumnodes = this->NumberofNodesPressure();
	int vnumdof   = vnumnodes*NDOF3;
	int pnumdof   = pnumnodes*NDOF1;

	/*Initialize values*/
	IssmDouble* vvalues = xNew<IssmDouble>(vnumdof);
	IssmDouble* pvalues = xNew<IssmDouble>(pnumdof);
	IssmDouble* lambdax = xNew<IssmDouble>(vnumnodes);
	IssmDouble* lambday = xNew<IssmDouble>(vnumnodes);
	IssmDouble* lambdaz = xNew<IssmDouble>(vnumnodes);
	IssmDouble* lambdap = xNew<IssmDouble>(pnumnodes);

	/*Get dof list: */
	GetDofListVelocity(&vdoflist,GsetEnum);
	GetDofListPressure(&pdoflist,GsetEnum);

	/*Use the dof list to index into the solution vector: */
	for(i=0;i<vnumdof;i++) vvalues[i]=solution[vdoflist[i]];
	for(i=0;i<pnumdof;i++) pvalues[i]=solution[pdoflist[i]];

	/*Transform solution in Cartesian Space*/
	TransformSolutionCoord(&vvalues[0],nodes,vnumnodes,XYZEnum);

	/*fill in all arrays: */
	for(i=0;i<vnumnodes;i++){
		lambdax[i] = vvalues[i*NDOF3+0]; if(xIsNan<IssmDouble>(lambdax[i])) _error_("NaN found in solution vector");
		lambday[i] = vvalues[i*NDOF3+1]; if(xIsNan<IssmDouble>(lambday[i])) _error_("NaN found in solution vector");
		lambdaz[i] = vvalues[i*NDOF3+2]; if(xIsNan<IssmDouble>(lambdaz[i])) _error_("NaN found in solution vector");
	}
	for(i=0;i<pnumnodes;i++){
		lambdap[i] = pvalues[i]; if(xIsNan<IssmDouble>(lambdap[i])) _error_("NaN found in solution vector");
	}

	/*Recondition pressure and compute vel: */
	this->parameters->FindParam(&FSreconditioning,StressbalanceFSreconditioningEnum);
	for(i=0;i<pnumnodes;i++) lambdap[i]=lambdap[i]*FSreconditioning;

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

	/*Free ressources:*/
	xDelete<int>(vdoflist);
	xDelete<int>(pdoflist);
	xDelete<IssmDouble>(lambdap);
	xDelete<IssmDouble>(lambdaz);
	xDelete<IssmDouble>(lambday);
	xDelete<IssmDouble>(lambdax);
	xDelete<IssmDouble>(vvalues);
	xDelete<IssmDouble>(pvalues);
}
/*}}}*/
/*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]))       _error_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(lambday[i]))       _error_("NaN found in solution vector");
	}

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

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

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

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

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

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

		/*This element should be collapsed into a tria element at its base. Create this tria element, 
		 * and compute SurfaceAverageVelMisfit*/
		tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
		J=tria->SurfaceAverageVelMisfit(weight_index);
		delete tria->material; delete tria;
		return J;
	}
	else{

		tria=(Tria*)SpawnTria(1); //lower face is 0, upper face is 1.
		J=tria->SurfaceAverageVelMisfit(weight_index);
		delete tria->material; delete tria;
		return J;
	}
}
/*}}}*/
/*FUNCTION Penta::SurfaceAbsVelMisfit {{{*/
IssmDouble Penta::SurfaceAbsVelMisfit(int weight_index){

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

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

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

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

		/*This element should be collapsed into a tria element at its base. Create this tria element, 
		 * and compute SurfaceAbsVelMisfit*/
		tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
		J=tria->SurfaceAbsVelMisfit(weight_index);
		delete tria->material; delete tria;
		return J;
	}
	else{

		tria=(Tria*)SpawnTria(1); //lower face is 0, upper face is 1.
		J=tria->SurfaceAbsVelMisfit(weight_index);
		delete tria->material; delete tria;
		return J;
	}
}
/*}}}*/
/*FUNCTION Penta::SurfaceLogVelMisfit {{{*/
IssmDouble Penta::SurfaceLogVelMisfit(int weight_index){

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

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

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

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

		/*This element should be collapsed into a tria element at its base. Create this tria element, 
		 * and compute SurfaceLogVelMisfit*/
		tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
		J=tria->SurfaceLogVelMisfit(weight_index);
		delete tria->material; delete tria;
		return J;
	}
	else{

		tria=(Tria*)SpawnTria(1); //lower face is 0, upper face is 1.
		J=tria->SurfaceLogVelMisfit(weight_index);
		delete tria->material; delete tria;
		return J;
	}
}
/*}}}*/
/*FUNCTION Penta::SurfaceLogVxVyMisfit {{{*/
IssmDouble Penta::SurfaceLogVxVyMisfit(int weight_index){

	IssmDouble J;
	Tria* tria=NULL;

	/*inputs: */
	int  approximation;

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

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

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

		/*This element should be collapsed into a tria element at its base. Create this tria element, 
		 * and compute SurfaceLogVxVyMisfit*/
		tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
		J=tria->SurfaceLogVxVyMisfit(weight_index);
		delete tria->material; delete tria;
		return J;
	}
	else{

		tria=(Tria*)SpawnTria(1); //lower face is 0, upper face is 1.
		J=tria->SurfaceLogVxVyMisfit(weight_index);
		delete tria->material; delete tria;
		return J;
	}
}
/*}}}*/
/*FUNCTION Penta::SurfaceRelVelMisfit {{{*/
IssmDouble Penta::SurfaceRelVelMisfit(int weight_index){

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

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

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

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

		/*This element should be collapsed into a tria element at its base. Create this tria element, 
		 * and compute SurfaceRelVelMisfit*/
		tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
		J=tria->SurfaceRelVelMisfit(weight_index);
		delete tria->material; delete tria;
		return J;
	}
	else{

		tria=(Tria*)SpawnTria(1); //lower face is 0, upper face is 1.
		J=tria->SurfaceRelVelMisfit(weight_index);
		delete tria->material; delete tria;
		return J;
	}
}
/*}}}*/
/*FUNCTION Penta::ThicknessAbsGradient{{{*/
IssmDouble Penta::ThicknessAbsGradient(int weight_index){

	_error_("Not implemented yet");
}
/*}}}*/
/*FUNCTION Penta::ThicknessAbsMisfit {{{*/
IssmDouble Penta::ThicknessAbsMisfit(int weight_index){

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

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

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

	tria=(Tria*)SpawnTria(0);
	J=tria->ThicknessAbsMisfit(weight_index);
	delete tria->material; delete tria;
	return J;
}
/*}}}*/
/*FUNCTION Penta::DragCoefficientAbsGradient{{{*/
IssmDouble Penta::DragCoefficientAbsGradient(int weight_index){

	IssmDouble J;
	Tria*  tria=NULL;

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

	tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1
	J=tria->DragCoefficientAbsGradient(weight_index);
	delete tria->material; delete tria;
	return J;
}
/*}}}*/
/*FUNCTION Penta::RheologyBbarAbsGradient{{{*/
IssmDouble Penta::RheologyBbarAbsGradient(int weight_index){

	IssmDouble J;
	Tria*  tria=NULL;

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

	tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1
	J=tria->RheologyBbarAbsGradient(weight_index);
	delete tria->material; delete tria;
	return J;
}
/*}}}*/
/*FUNCTION Penta::GetVectorFromControlInputs{{{*/
void  Penta::GetVectorFromControlInputs(Vector<IssmDouble>* vector,int control_enum,int control_index,const char* data){

	int vertexpidlist[NUMVERTICES];

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

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

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

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

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

	IssmDouble  values[NUMVERTICES];
	int     vertexpidlist[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(&vertexpidlist[0],control_index);

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

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

	if (input->ObjectEnum()!=ControlInputEnum){
		_error_("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 PentaInput*/
			IssmDouble values[6];

			/*Get values on the 6 vertices*/
			for (i=0;i<6;i++){
				values[i]=vector[this->vertices[i]->Sid()]; //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 PentaInput(ThicknessEnum,thickness,P1Enum));
					this->inputs->AddInput(new PentaInput(BedEnum,bed,P1Enum));
					this->inputs->AddInput(new PentaInput(SurfaceEnum,surface,P1Enum));

					break;
				default:
					this->inputs->AddInput(new PentaInput(name,values,P1Enum));
			}
			break;

		default:
			_error_("type " << type << " (" << EnumToStringx(type) << ") not implemented 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,t,row;
	IssmDouble      time;
	TransientInput *transientinput = NULL;
	IssmDouble      values[6];

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

	switch(type){

		case VertexEnum:
			/*Create transient input: */
			for(t=0;t<ncols;t++){ //ncols is the number of times

				/*create input values: */
				for(i=0;i<6;i++){
					row=this->vertices[i]->Sid();
					values[i]=matrix[ncols*row+t];
				}

				/*time:*/
				time=matrix[(nrows-1)*ncols+t];

				if(t==0) transientinput=new TransientInput(name);
				transientinput->AddTimeInput(new PentaInput(name,values,P1Enum),time);
				transientinput->Configure(parameters);
			}
			this->inputs->AddInput(transientinput);
			break;

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

}
/*}}}*/
#endif

#ifdef _HAVE_STRESSBALANCE_
/*FUNCTION Penta::CreateDVectorStressbalanceHoriz {{{*/
ElementVector* Penta::CreateDVectorStressbalanceHoriz(void){

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

	switch(approximation){
		case FSApproximationEnum:
			return CreateDVectorStressbalanceFS();
		default:
			return NULL; //no need for doftypes outside of FS approximation
	}
}
/*}}}*/
/*FUNCTION Penta::CreateDVectorStressbalanceFS{{{*/
ElementVector* Penta::CreateDVectorStressbalanceFS(void){

	/*output: */
	ElementVector* De=NULL;

	/*Initialize Element vector and return if necessary*/
	int approximation;
	inputs->GetInputValue(&approximation,ApproximationEnum);
	if(approximation!=FSApproximationEnum) return NULL;

	/*Fetch number of nodes and dof for this finite element*/
	int vnumnodes = this->NumberofNodesVelocity();
	int pnumnodes = this->NumberofNodesPressure();

	De=new ElementVector(nodes,vnumnodes+pnumnodes,this->parameters,FSvelocityEnum);

	for(int i=0;i<vnumnodes;i++){
		De->values[i*3+0]=VelocityEnum;
		De->values[i*3+1]=VelocityEnum;
		De->values[i*3+2]=VelocityEnum;
	}
	for(int i=0;i<pnumnodes;i++){
		De->values[vnumnodes*3+i]=PressureEnum;
	}

	return De;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixCouplingSSAHO{{{*/
ElementMatrix* Penta::CreateKMatrixCouplingSSAHO(void){

	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixCouplingSSAHOViscous();
	ElementMatrix* Ke2=CreateKMatrixCouplingSSAHOFriction();
	ElementMatrix* Ke=new ElementMatrix(Ke1,Ke2);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixCouplingSSAHOViscous{{{*/
ElementMatrix* Penta::CreateKMatrixCouplingSSAHOViscous(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;
	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 HO must be coupled to the dofs on the bed: */
	Penta* pentabase=GetBasalElement();
	Tria*  tria=pentabase->SpawnTria(0); //lower face is 0, upper face is 1.

	/*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,SSAApproximationEnum);
	ElementMatrix* Ke2=new ElementMatrix(this->nodes     ,NUMVERTICES,this->parameters,HOApproximationEnum);
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2);
	delete Ke1; delete Ke2;

	/* Get node coordinates and dof list: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	this->parameters->FindParam(&viscosity_overshoot,StressbalanceViscosityOvershootEnum);
	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(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);
		gauss->SynchronizeGaussTria(gauss_tria);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetBSSAHO(&B[0][0], &xyz_list[0][0], gauss);
		tria->GetBprimeSSA(&Bprime[0][0], &xyz_list[0][0], gauss_tria);

		this->GetStrainRate3dHO(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
		this->GetStrainRate3dHO(&oldepsilon[0],&xyz_list[0][0],gauss,vxold_input,vyold_input);
		material->GetViscosity3d(&viscosity, &epsilon[0]);
		material->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->material; delete tria;
	delete gauss;
	delete gauss_tria;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixCouplingSSAHOFriction{{{*/
ElementMatrix* Penta::CreateKMatrixCouplingSSAHOFriction(void){

	/*Constants*/
	const int numnodes    = 2 *NUMVERTICES;
	const int numdof      = NDOF2 *NUMVERTICES;
	const int numdoftotal = NDOF4 *NUMVERTICES;

	/*Intermediaries */
	int       i,j,analysis_type;
	IssmDouble Jdet2d,alpha2;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble xyz_list_tria[NUMVERTICES2D][3]={0.0};
	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,SSAApproximationEnum);
	ElementMatrix* Ke2=new ElementMatrix(nodes,NUMVERTICES,this->parameters,HOApproximationEnum);
	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],vertices,NUMVERTICES);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;j++) xyz_list_tria[i][j]=xyz_list[i][j];
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	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(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		/*Friction: */
		friction->GetAlpha2(&alpha2, gauss,VxEnum,VyEnum,VzEnum);

		GetTriaJacobianDeterminant(&Jdet2d, &xyz_list_tria[0][0],gauss);
		GetBHOFriction(&L[0][0],gauss);

		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::CreateKMatrixCouplingSSAFS{{{*/
ElementMatrix* Penta::CreateKMatrixCouplingSSAFS(void){

	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixCouplingSSAFSViscous();
	ElementMatrix* Ke2=CreateKMatrixCouplingSSAFSFriction();
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixCouplingSSAFSViscous{{{*/
ElementMatrix* Penta::CreateKMatrixCouplingSSAFSViscous(void){

	/*Constants*/
	const int numdofm     = NDOF2 *NUMVERTICES2D;
	const int numdofs     = NDOF4 *NUMVERTICES + NDOF3;
	const int numdoftotal = 2 *numdofm+numdofs;

	/*Intermediaries */
	int        i,j;
	IssmDouble Jdet;
	IssmDouble viscosity,FSreconditioning; //viscosity
	IssmDouble epsilon[6]; /* epsilon=[exx,eyy,exy,exz,eyz];*/
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble B[4][numdofs];
	IssmDouble Bprime[4][numdofm];
	IssmDouble B2[3][numdofm];
	IssmDouble Bprime2[3][numdofs];
	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][numdofm]; //stiffness matrix evaluated at the gaussian point.
	IssmDouble Ke_gg_gaussian2[numdofm][numdofs]; //stiffness matrix evaluated at the gaussian point.
	GaussPenta *gauss=NULL;
	GaussTria  *gauss_tria=NULL;
	Node       *node_list[20];

	/*Find penta on bed as FS must be coupled to the dofs on the bed: */
	Penta* pentabase=GetBasalElement();
	Tria* tria=pentabase->SpawnTria(0); //lower face is 0, upper face is 1.

	int vnumnodes = this->NumberofNodesVelocity();
	int pnumnodes = this->NumberofNodesPressure();
	int numnodes  = 2*vnumnodes-1+pnumnodes;

	/*Prepare node list*/
	int* cs_list = xNew<int>(2*vnumnodes+pnumnodes);
	for(i=0;i<vnumnodes-1;i++){
		node_list[i] = pentabase->nodes[i];
		cs_list[i] = XYEnum;
	}
	for(i=0;i<vnumnodes;i++){
		node_list[i+vnumnodes-1] = this->nodes[i];
		cs_list[i+vnumnodes-1] = XYZEnum;
	}
	for(i=0;i<pnumnodes;i++){
		node_list[2*vnumnodes-1+i] = this->nodes[vnumnodes+i];
		cs_list[2*vnumnodes-1+i] = PressureEnum;
	}

	/*Initialize Element matrix and return if necessary*/
	ElementMatrix* Ke1=new ElementMatrix(pentabase->nodes,NUMVERTICES,    this->parameters,SSAApproximationEnum);
	ElementMatrix* Ke2=new ElementMatrix(this->nodes     ,2*NUMVERTICES+1,this->parameters,FSvelocityEnum);
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2);
	delete Ke1; delete Ke2;

	/* Get node coordinates and dof list: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	parameters->FindParam(&FSreconditioning,StressbalanceFSreconditioningEnum);
	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(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);
		gauss->SynchronizeGaussTria(gauss_tria);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetBSSAFS(&B[0][0], &xyz_list[0][0], gauss);
		tria->GetBprimeSSAFS(&Bprime[0][0], &xyz_list[0][0], gauss_tria);
		tria->GetBSSAFS(&B2[0][0], &xyz_list[0][0], gauss_tria);
		GetBprimeSSAFS(&Bprime2[0][0], &xyz_list[0][0], gauss);

		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		material->GetViscosity3dFS(&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*FSreconditioning;
		for (i=0;i<3;i++) D2[i][i]=D_scalar;

		TripleMultiply( &B[0][0],4,numdofs,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,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*/
	xDelete<int>(cs_list);
	delete tria->material; delete tria;
	delete gauss;
	delete gauss_tria;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixCouplingSSAFSFriction {{{*/
ElementMatrix* Penta::CreateKMatrixCouplingSSAFSFriction(void){
	/*Constants*/
	const int numdofs   = (NUMVERTICES+1)*NDOF3 + NUMVERTICES*NDOF1;
	const int numdofm   = NUMVERTICES *NDOF2;
	const int numdof2d  = NUMVERTICES2D *NDOF3;
	const int numdof2dm = NUMVERTICES2D *NDOF2;
	const int numdoftot = NUMVERTICES*2 + (NUMVERTICES+1)*3 +NUMVERTICES; // HO + FS vel + FS Pressure

	/*Intermediaries */
	int        i,j;
	int        analysis_type,approximation;
	IssmDouble FSreconditioning;
	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 LSSAFS[8][numdof2dm];
	IssmDouble LprimeSSAFS[8][numdofs];
	IssmDouble DLSSAFS[8][8]={0.0};
	IssmDouble LFSSSA[4][numdof2d];
	IssmDouble LprimeFSSSA[4][numdof2dm];
	IssmDouble DLFSSSA[4][4]={0.0};
	IssmDouble Ke_drag_gaussian[numdof2dm][numdofs];
	IssmDouble Ke_drag_gaussian2[numdof2d][numdof2dm];
	Friction*  friction=NULL;
	GaussPenta *gauss=NULL;
	Node       *node_list[20];

	/*If on water or not FS, skip stiffness: */
	inputs->GetInputValue(&approximation,ApproximationEnum);
	if(IsFloating() || !IsOnBed()) return NULL;

	int vnumnodes = this->NumberofNodesVelocity();
	int pnumnodes = this->NumberofNodesPressure();
	int numnodes  = 2*vnumnodes-1+pnumnodes;

	/*Prepare node list*/
	int* cs_list = xNew<int>(2*vnumnodes+pnumnodes);
	for(i=0;i<vnumnodes-1;i++){
		node_list[i] = this->nodes[i];
		cs_list[i] = XYEnum;
	}
	for(i=0;i<vnumnodes;i++){
		node_list[i+vnumnodes-1] = this->nodes[i];
		cs_list[i+vnumnodes-1] = XYZEnum;
	}
	for(i=0;i<pnumnodes;i++){
		node_list[2*vnumnodes-1+i] = this->nodes[vnumnodes+i];
		cs_list[2*vnumnodes-1+i] = PressureEnum;
	}

	ElementMatrix* Ke1=new ElementMatrix(this->nodes,NUMVERTICES,        this->parameters,SSAApproximationEnum);
	ElementMatrix* Ke2=new ElementMatrix(this->nodes,vnumnodes+pnumnodes,this->parameters,FSvelocityEnum);
	ElementMatrix* Ke=new ElementMatrix(Ke1,Ke2);
	delete Ke1; delete Ke2;

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	parameters->FindParam(&FSreconditioning,StressbalanceFSreconditioningEnum);
	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(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetTriaJacobianDeterminant(&Jdet2d, &xyz_list_tria[0][0],gauss);
		GetLSSAFS(&LSSAFS[0][0], gauss);
		GetLprimeSSAFS(&LprimeSSAFS[0][0], &xyz_list[0][0], gauss);
		GetLFSSSA(&LFSSSA[0][0], gauss);
		GetLprimeFSSSA(&LprimeFSSSA[0][0], &xyz_list[0][0], gauss);

		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		material->GetViscosity3dFS(&viscosity,&epsilon[0]);

		BedNormal(&bed_normal[0],xyz_list_tria);
		friction->GetAlpha2(&alpha2_gauss, gauss,VxEnum,VyEnum,VzEnum);

		DLSSAFS[0][0]=alpha2_gauss*gauss->weight*Jdet2d;
		DLSSAFS[1][1]=alpha2_gauss*gauss->weight*Jdet2d;
		DLSSAFS[2][2]=-alpha2_gauss*gauss->weight*Jdet2d*bed_normal[0]*bed_normal[2];
		DLSSAFS[3][3]=-alpha2_gauss*gauss->weight*Jdet2d*bed_normal[1]*bed_normal[2];
		DLSSAFS[4][4]=-2*viscosity*gauss->weight*Jdet2d*bed_normal[0];
		DLSSAFS[5][5]=-2*viscosity*gauss->weight*Jdet2d*bed_normal[1];
		DLSSAFS[6][6]=FSreconditioning*gauss->weight*Jdet2d*bed_normal[0];
		DLSSAFS[7][7]=FSreconditioning*gauss->weight*Jdet2d*bed_normal[1];

		DLFSSSA[0][0]=alpha2_gauss*gauss->weight*Jdet2d;
		DLFSSSA[1][1]=alpha2_gauss*gauss->weight*Jdet2d;
		DLFSSSA[2][2]=-alpha2_gauss*gauss->weight*Jdet2d*bed_normal[0]*bed_normal[2];
		DLFSSSA[3][3]=-alpha2_gauss*gauss->weight*Jdet2d*bed_normal[1]*bed_normal[2];

		TripleMultiply( &LSSAFS[0][0],8,numdof2dm,1,
					&DLSSAFS[0][0],8,8,0,
					&LprimeSSAFS[0][0],8,numdofs,0,
					&Ke_drag_gaussian[0][0],0);

		TripleMultiply( &LFSSSA[0][0],4,numdof2d,1,
					&DLFSSSA[0][0],4,4,0,
					&LprimeFSSSA[0][0],4,numdof2dm,0,
					&Ke_drag_gaussian2[0][0],0);
		for(i=0;i<numdof2dm;i++) for(j=0;j<numdofs;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*/
	xDelete<int>(cs_list);
	delete gauss;
	delete friction;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixCouplingHOFS{{{*/
ElementMatrix* Penta::CreateKMatrixCouplingHOFS(void){

	/*Constants*/
	const int numnodes       = 3 *NUMVERTICES+1;
	const int numdofp        = NDOF2 *NUMVERTICES;
	const int numdofs        = NDOF4 * 6 + NDOF3;
	const int numdoftotal    = (NDOF2+NDOF4) *NUMVERTICES + NDOF3;

	/*Intermediaries*/
	int   i,j,init;
	Node  *node_list[NUMVERTICES*3+1];
	int   cs_list[NUMVERTICES*3+1];
	int   cs_list2[NUMVERTICES*2+1];

	/*Some parameters needed*/
	init=this->element_type;

	/*prepare node list*/
	for(i=0;i<NUMVERTICES+1;i++){
		node_list[i+NUMVERTICES] = this->nodes[i];
		cs_list[i+NUMVERTICES]   = XYZEnum;
		cs_list2[i]              = XYZEnum;
	}
	for(i=0;i<NUMVERTICES;i++){
		node_list[i]                 = this->nodes[i];
		node_list[i+2*NUMVERTICES+1] = this->nodes[i+NUMVERTICES+1];
		cs_list[i]                   = XYEnum;
		cs_list[i+2*NUMVERTICES+1]   = PressureEnum;
		cs_list2[i+NUMVERTICES+1]    = PressureEnum;
	}

	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=new ElementMatrix(this->nodes,NUMVERTICES,this->parameters,HOApproximationEnum);
	ElementMatrix* Ke2=new ElementMatrix(this->nodes,2*NUMVERTICES+1,this->parameters,FSvelocityEnum);
	ElementMatrix* Ke=new ElementMatrix(Ke1,Ke2);
	delete Ke1; delete Ke2;
	
	/*Compute HO Matrix with P1 element type\n");*/
	this->element_type=P1Enum;
	Ke1=CreateKMatrixStressbalanceHO(); TransformInvStiffnessMatrixCoord(Ke1,this->nodes,NUMVERTICES,XYEnum);
	this->element_type=init;
	/*Compute FS Matrix and condense it \n");*/
	Ke2=CreateKMatrixStressbalanceFS(); TransformInvStiffnessMatrixCoord(Ke2,this->nodes,2*NUMVERTICES+1,cs_list2);
	int indices[3]={18,19,20};
	Ke2->StaticCondensation(3,&indices[0]);

	for(i=0;i<numdofs;i++) for(j=0;j<NUMVERTICES;j++){
		Ke->values[(i+numdofp)*numdoftotal+NDOF2*j+0]+=Ke2->values[i*numdofs+NDOF3*j+0];
		Ke->values[(i+numdofp)*numdoftotal+NDOF2*j+1]+=Ke2->values[i*numdofs+NDOF3*j+1];
	}
	for(i=0;i<numdofp;i++) for(j=0;j<NUMVERTICES;j++){
		Ke->values[i*numdoftotal+numdofp+NDOF3*j+0]+=Ke1->values[i*numdofp+NDOF2*j+0];
		Ke->values[i*numdoftotal+numdofp+NDOF3*j+1]+=Ke1->values[i*numdofp+NDOF2*j+1];
	}

	/*Transform Coordinate System*/ //Do not transform, already done in the matrices
	TransformStiffnessMatrixCoord(Ke,node_list,numnodes,cs_list);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	return Ke;
}
//*}}}*/
/*FUNCTION Penta::CreateKMatrixStressbalanceHoriz {{{*/
ElementMatrix* Penta::CreateKMatrixStressbalanceHoriz(void){

	int approximation;
	inputs->GetInputValue(&approximation,ApproximationEnum);
	switch(approximation){
		case SSAApproximationEnum:
			return CreateKMatrixStressbalanceSSA2d();
		case L1L2ApproximationEnum:
			return CreateKMatrixStressbalanceL1L2();
		case HOApproximationEnum:
			return CreateKMatrixStressbalanceHO();
		case FSApproximationEnum:
			return CreateKMatrixStressbalanceFS();
		case SIAApproximationEnum:
			return NULL;
		case NoneApproximationEnum:
			return NULL;
		case SSAHOApproximationEnum:
			return CreateKMatrixStressbalanceSSAHO();
		case SSAFSApproximationEnum:
			return CreateKMatrixStressbalanceSSAFS();
		case HOFSApproximationEnum:
			return CreateKMatrixStressbalanceHOFS();
		default:
			_error_("Approximation " << EnumToStringx(approximation) << " not supported yet");
	}
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixStressbalanceSIA{{{*/
ElementMatrix* Penta::CreateKMatrixStressbalanceSIA(void){

	/*Intermediaries*/
	IssmDouble connectivity[2];
	IssmDouble one0,one1;
	int        i,i0,i1,j0,j1;

	/*Fetch number of nodes and dof for this finite element*/
	int numnodes = this->NumberofNodes(); _assert_(numnodes==6); 
	int numdof   = numnodes*NDOF2;

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

	/*3 vertical edges*/
	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]=(IssmDouble)vertices[i]->Connectivity();
		connectivity[1]=(IssmDouble)vertices[i+3]->Connectivity();
		one0=1./connectivity[0];
		one1=1./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::CreateKMatrixStressbalanceSSA2d{{{*/
ElementMatrix* Penta::CreateKMatrixStressbalanceSSA2d(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 average some fields*/
	switch(this->material->ObjectEnum()){
		case MaticeEnum:
			this->InputDepthAverageAtBase(MaterialsRheologyBEnum,MaterialsRheologyBbarEnum,MaterialsEnum);
			break;
		case MatdamageiceEnum:
			this->InputDepthAverageAtBase(MaterialsRheologyBEnum,MaterialsRheologyBbarEnum,MaterialsEnum);
			this->InputDepthAverageAtBase(MaterialsRheologyZEnum,MaterialsRheologyZbarEnum,MaterialsEnum);
			break;
		default:
			_error_("material "<<EnumToStringx(this->material->ObjectEnum())<<" not supported");
	}

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
	ElementMatrix* Ke=tria->CreateKMatrixStressbalanceSSA();
	delete tria->material; delete tria;

	/*Delete averaged fields*/
	this->material->inputs->DeleteInput(MaterialsRheologyBbarEnum);
	this->material->inputs->DeleteInput(MaterialsRheologyZbarEnum);

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

	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixStressbalanceSSA3dViscous();
	ElementMatrix* Ke2=CreateKMatrixStressbalanceSSA3dFriction();
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixStressbalanceSSA3dViscous{{{*/
ElementMatrix* Penta::CreateKMatrixStressbalanceSSA3dViscous(void){

	/*Constants*/
	const int    numdof2d=2*NUMVERTICES2D;

	/*Intermediaries */
	int         i,j,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 FS
	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];
	Tria       *tria       = NULL;
	Penta      *pentabase  = NULL;
	GaussPenta *gauss      = NULL;
	GaussTria  *gauss_tria = NULL;

	/*Find penta on bed as this is a SSA elements: */
	pentabase=GetBasalElement();
	tria=pentabase->SpawnTria(0); //lower face is 0, upper face is 1.

	/*Initialize Element matrix*/
	ElementMatrix* Ke=new ElementMatrix(tria->nodes,NUMVERTICES2D,this->parameters,SSAApproximationEnum);
	inputs->GetInputValue(&approximation,ApproximationEnum);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	this->parameters->FindParam(&viscosity_overshoot,StressbalanceViscosityOvershootEnum);
	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(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);
		gauss->SynchronizeGaussTria(gauss_tria);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		tria->GetBSSA(&B[0][0], &xyz_list[0][0], gauss_tria);
		tria->GetBprimeSSA(&Bprime[0][0], &xyz_list[0][0], gauss_tria);

		if(approximation==SSAHOApproximationEnum){
			this->GetStrainRate3dHO(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
			this->GetStrainRate3dHO(&oldepsilon[0],&xyz_list[0][0],gauss,vxold_input,vyold_input);
			material->GetViscosity3d(&viscosity, &epsilon[0]);
			material->GetViscosity3d(&oldviscosity, &oldepsilon[0]);

			newviscosity=viscosity+viscosity_overshoot*(viscosity-oldviscosity);
		}
		else if (approximation==SSAFSApproximationEnum){
			this->GetStrainRate3d(&epsilons[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
			material->GetViscosity3dFS(&newviscosity,&epsilons[0]);
		}
		else _error_("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->material;
	delete tria;
	delete gauss_tria;
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixStressbalanceSSA3dFriction{{{*/
ElementMatrix* Penta::CreateKMatrixStressbalanceSSA3dFriction(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); //lower face is 0, upper face is 1.
	ElementMatrix* Ke=tria->CreateKMatrixStressbalanceSSAFriction();
	delete tria->material; delete tria;

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

	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixStressbalanceSSA3d();
	ElementMatrix* Ke2=CreateKMatrixStressbalanceHO();
	ElementMatrix* Ke3=CreateKMatrixCouplingSSAHO();
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2,Ke3);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	delete Ke3;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixStressbalanceSSAFS{{{*/
ElementMatrix* Penta::CreateKMatrixStressbalanceSSAFS(void){

	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixStressbalanceFS();
	int indices[3]={18,19,20};
	Ke1->StaticCondensation(3,&indices[0]);
	int init = this->element_type;
	this->element_type=P1Enum; //P1 needed for HO 
	ElementMatrix* Ke2=CreateKMatrixStressbalanceSSA3d();
	this->element_type=init;
	ElementMatrix* Ke3=CreateKMatrixCouplingSSAFS();
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2,Ke3);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	delete Ke3;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixStressbalanceL1L2{{{*/
ElementMatrix* Penta::CreateKMatrixStressbalanceL1L2(void){

	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixStressbalanceL1L2Viscous();
	ElementMatrix* Ke2=CreateKMatrixStressbalanceL1L2Friction();
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixStressbalanceL1L2Viscous{{{*/
ElementMatrix* Penta::CreateKMatrixStressbalanceL1L2Viscous(void){

	/*Constants*/
	const int    numdof2d=2*NUMVERTICES2D;

	/*Intermediaries */
	int         i,j;
	IssmDouble  Jdet,viscosity;
	IssmDouble  epsilon[5];       /* epsilon=[exx,eyy,exy,exz,eyz];*/
	IssmDouble  xyz_list[NUMVERTICES][3];
	IssmDouble  B[3][numdof2d];
	IssmDouble  Bprime[3][numdof2d];
	IssmDouble  Ke_gg_gaussian[numdof2d][numdof2d];
	IssmDouble  D[3][3]= {0.0};                 // material matrix, simple scalar matrix.
	Tria       *tria       = NULL;
	Penta      *pentabase  = NULL;
	GaussPenta *gauss      = NULL;
	GaussTria  *gauss_tria = NULL;

	/*Find penta on bed as this is a SSA elements: */
	pentabase=GetBasalElement();
	tria=pentabase->SpawnTria(0); //lower face is 0, upper face is 1.

	/*Initialize Element matrix*/
	ElementMatrix* Ke=new ElementMatrix(tria->nodes,NUMVERTICES2D,this->parameters,L1L2ApproximationEnum);

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

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

		gauss->GaussPoint(ig);
		gauss->SynchronizeGaussTria(gauss_tria);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		tria->GetBSSA(&B[0][0], &xyz_list[0][0], gauss_tria);
		tria->GetBprimeSSA(&Bprime[0][0], &xyz_list[0][0], gauss_tria);

		/*Get viscosity for L1L2 model*/
		GetL1L2Viscosity(&viscosity,&xyz_list[0][0],gauss,vx_input,vy_input,surf_input);

		for(i=0;i<3;i++) D[i][i]=2*viscosity*gauss->weight*Jdet;

		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->material;
	delete tria;
	delete gauss_tria;
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixStressbalanceL1L2Friction{{{*/
ElementMatrix* Penta::CreateKMatrixStressbalanceL1L2Friction(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); //lower face is 0, upper face is 1.
	ElementMatrix* Ke=tria->CreateKMatrixStressbalanceSSAFriction();
	delete tria->material; delete tria;

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

	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixStressbalanceHOViscous();
	ElementMatrix* Ke2=CreateKMatrixStressbalanceHOFriction();
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	return Ke;

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

	/*Intermediaries */
	int         i,j;
	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;
	GaussPenta *gauss=NULL;

	/*Fetch number of nodes and dof for this finite element*/
	int numnodes = this->NumberofNodes();
	int numdof   = numnodes*NDOF2;

	/*Initialize Element matrix and vectors*/
	ElementMatrix* Ke     = new ElementMatrix(nodes,numnodes,this->parameters,HOApproximationEnum);
	IssmDouble*    B      = xNew<IssmDouble>(5*numdof);
	IssmDouble*    Bprime = xNew<IssmDouble>(5*numdof);
	IssmDouble*    D      = xNewZeroInit<IssmDouble>(5*5);

	/*Retrieve all inputs and parameters*/
	inputs->GetInputValue(&approximation,ApproximationEnum);
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	this->parameters->FindParam(&viscosity_overshoot,StressbalanceViscosityOvershootEnum);
	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(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetBHO(&B[0], &xyz_list[0][0], gauss);
		GetBprimeHO(&Bprime[0], &xyz_list[0][0], gauss);

		this->GetStrainRate3dHO(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
		this->GetStrainRate3dHO(&oldepsilon[0],&xyz_list[0][0],gauss,vxold_input,vyold_input);
		material->GetViscosity3d(&viscosity, &epsilon[0]);
		material->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*5+i]=D_scalar;

		TripleMultiply(B,5,numdof,1,
					D,5,5,0,
					Bprime,5,numdof,0,
					&Ke->values[0],1);
	}

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

	/*Clean up and return*/
	delete gauss;
	xDelete<IssmDouble>(D);
	xDelete<IssmDouble>(Bprime);
	xDelete<IssmDouble>(B);
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixStressbalanceHOFriction{{{*/
ElementMatrix* Penta::CreateKMatrixStressbalanceHOFriction(void){

	/*Intermediaries */
	bool       mainlyfloating;
	int         i,j;
	int         analysis_type,migration_style;
	int         point1;
	IssmDouble  xyz_list[NUMVERTICES][3];
	IssmDouble  xyz_list_tria[NUMVERTICES2D][3];
	IssmDouble  alpha2,Jdet;
	IssmDouble fraction1,fraction2;
	IssmDouble gllevelset;
	IssmDouble  phi=1.0;
	IssmDouble  DL_scalar;
	Friction   *friction = NULL;
	GaussPenta *gauss    = NULL;

	/*Initialize Element matrix and return if necessary*/
	if(IsFloating() || !IsOnBed()) return NULL;

	/*Fetch number of nodes and dof for this finite element*/
	int numnodes = this->NumberofNodes();
	int numdof   = numnodes*NDOF2;

	/*Initialize Element matrix and vectors*/
	ElementMatrix* Ke     = new ElementMatrix(nodes,numnodes,this->parameters,HOApproximationEnum);
	IssmDouble*    B      = xNew<IssmDouble>(2*numdof);
	IssmDouble*    D      = xNewZeroInit<IssmDouble>(2*2);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	for(i=0;i<NUMVERTICES2D;i++) for(j=0;j<3;j++) xyz_list_tria[i][j]=xyz_list[i][j];
	parameters->FindParam(&migration_style,GroundinglineMigrationEnum);
	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);
	Input* gllevelset_input=NULL;

	/*build friction object, used later on: */
	friction=new Friction("2d",inputs,matpar,analysis_type);

	/*Recover portion of element that is grounded*/
	if(migration_style==SubelementMigrationEnum) phi=this->GetGroundedPortion(&xyz_list_tria[0][0]);
	if(migration_style==SubelementMigration2Enum){
		gllevelset_input=inputs->GetInput(MaskGroundediceLevelsetEnum); _assert_(gllevelset_input);
		this->GetGroundedPart(&point1,&fraction1,&fraction2,&mainlyfloating);
		//gauss=new GaussPenta(point1,fraction1,fraction2,mainlyfloating,2);
		gauss=new GaussPenta(0,1,2,2);
	}
	else{
		gauss=new GaussPenta(0,1,2,2);
	}

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

		gauss->GaussPoint(ig);

		GetTriaJacobianDeterminant(&Jdet,&xyz_list_tria[0][0],gauss);
		GetBHOFriction(&B[0],gauss);

		friction->GetAlpha2(&alpha2, gauss,VxEnum,VyEnum,VzEnum); 
		if(migration_style==SubelementMigrationEnum) alpha2=phi*alpha2;
		if(migration_style==SubelementMigration2Enum){
			gllevelset_input->GetInputValue(&gllevelset, gauss);
			if(gllevelset<0) alpha2=0;
		}

		DL_scalar=alpha2*gauss->weight*Jdet;
		for (i=0;i<2;i++) D[i*2+i]=DL_scalar;

		TripleMultiply(B,2,numdof,1,
					D,2,2,0,
					B,2,numdof,0,
					&Ke->values[0],1);
	}

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

	/*Clean up and return*/
	delete gauss;
	xDelete<IssmDouble>(D);
	xDelete<IssmDouble>(B);
	delete friction;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixStressbalanceHOFS{{{*/
ElementMatrix* Penta::CreateKMatrixStressbalanceHOFS(void){

	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixStressbalanceFS();
	int indices[3]={18,19,20};
	Ke1->StaticCondensation(3,&indices[0]);
	int init = this->element_type;
	this->element_type=P1Enum; //P1 needed for HO 
	ElementMatrix* Ke2=CreateKMatrixStressbalanceHO();
	this->element_type=init;
	ElementMatrix* Ke3=CreateKMatrixCouplingHOFS();
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2,Ke3);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	delete Ke3;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixStressbalanceFS{{{*/
ElementMatrix* Penta::CreateKMatrixStressbalanceFS(void){

	ElementMatrix* Ke1 = NULL;
	ElementMatrix* Ke2 = NULL;
	ElementMatrix* Ke  = NULL;

	/*compute all stiffness matrices for this element*/
	Ke1=CreateKMatrixStressbalanceFSViscous();
	Ke2=CreateKMatrixStressbalanceFSFriction();
	Ke =new ElementMatrix(Ke1,Ke2);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	return Ke;

}
/*}}}*/
/*FUNCTION Penta::KMatrixGLSstabilization{{{*/
void Penta::KMatrixGLSstabilization(ElementMatrix* Ke){

	int        numdof  = NUMVERTICES*NDOF4;

	/*Intermediaries */
	int        i,j,approximation;
	IssmDouble Jdet,viscosity,FSreconditioning,diameter,rigidity;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble epsilon[6]; /* epsilon=[exx,eyy,ezz,exy,exz,eyz];*/
	IssmDouble B[8][24];
	IssmDouble B_prime[8][24];
	IssmDouble B_stab[3][NUMVERTICES];
	IssmDouble D_scalar,D_scalar_stab;
	IssmDouble D[8][8]={0.0};
	IssmDouble D_stab[3][3]={0.0};
	IssmDouble Ke_temp[24][24]={0.0}; //for the six nodes
	IssmDouble Ke_temp_stab[6][6]={0.0}; //for the six nodes
	GaussPenta *gauss=NULL;

	/*Stabilization*/
	bool       stabilization = true;
	IssmDouble dbasis[3][6];
	IssmDouble dmu[3];
	IssmDouble mu;
	IssmDouble dnodalbasis[6][6][3];
	IssmDouble SU[6][4][4];
	IssmDouble SW[6][4][4];
	int p,q,ii;
	int c=3; //index of pressure

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	parameters->FindParam(&FSreconditioning,StressbalanceFSreconditioningEnum);
	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);

	/*Find minimal length and B*/
	rigidity=material->GetB();
	diameter=MinEdgeLength(xyz_list);

		gauss=new GaussPenta();
		for(int iv=0;iv<6;iv++){
			gauss->GaussVertex(iv);
			GetNodalFunctionsP1Derivatives(&dbasis[0][0],&xyz_list[0][0],gauss);
			for(i=0;i<6;i++){
				for(j=0;j<3;j++){
					dnodalbasis[i][iv][j] = dbasis[j][i];
				}
			}
		}
		delete gauss;

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

		gauss->GaussPoint(ig);

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

		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		material->GetViscosity3dFS(&viscosity,&epsilon[0]);

		D_scalar=gauss->weight*Jdet;


		/*Add stabilization*/
		GetNodalFunctionsP1Derivatives(&dbasis[0][0],&xyz_list[0][0],gauss);
		dmu[0]=0.; dmu[1]=0.; dmu[2]=0.;
		mu = 2.*viscosity;
		for(p=0;p<6;p++) for(i=0;i<4;i++) for(j=0;j<4;j++){
			SU[p][i][j]=0.;
			SW[p][i][j]=0.;
		}
		for(p=0;p<6;p++){
			for(i=0;i<3;i++){
				SU[p][i][c] += dbasis[i][p];
				SW[p][c][i] += dbasis[i][p];
				for(j=0;j<3;j++){
					SU[p][i][i] += -dmu[j]*dbasis[j][p];
					SU[p][i][j] += -dmu[i]*dbasis[i][p];
					for(ii=0;ii<6;ii++){
						SU[p][i][i] += -mu*dnodalbasis[p][ii][j]*dbasis[j][ii];
						SU[p][i][j] += -mu*dnodalbasis[p][ii][i]*dbasis[j][ii];
					}
					SW[p][i][i] += -dmu[j]*dbasis[j][p];
					SW[p][j][i] += -dmu[j]*dbasis[i][p];
					for(ii=0;ii<6;ii++){
						SW[p][i][i] += -mu*dnodalbasis[p][ii][j]*dbasis[j][ii];
						SW[p][j][i] += -mu*dnodalbasis[p][ii][i]*dbasis[j][ii];
					}
				}
			}
		}
		IssmDouble tau = 1./3.*pow(diameter,2)/(8.*2.*viscosity);
		for(p=0;p<6;p++){
			for(q=0;q<6;q++){
				for(i=0;i<4;i++){
					for(j=0;j<4;j++){
						Ke->values[(p*4+i)*numdof+q*4+j] += gauss->weight*Jdet*tau*SW[p][i][0]*SU[q][0][j];
						Ke->values[(p*4+i)*numdof+q*4+j] += gauss->weight*Jdet*tau*SW[p][i][1]*SU[q][1][j];
						Ke->values[(p*4+i)*numdof+q*4+j] += gauss->weight*Jdet*tau*SW[p][i][2]*SU[q][2][j];
					}
				}
			}
		}
	}

	/*Clean up*/
	delete gauss;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixStressbalanceFSViscous {{{*/
ElementMatrix* Penta::CreateKMatrixStressbalanceFSViscous(void){

	/*Intermediaries */
	int        i,j,approximation;
	IssmDouble Jdet,viscosity,FSreconditioning,D_scalar;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble epsilon[6]; /* epsilon=[exx,eyy,ezz,exy,exz,eyz];*/
	GaussPenta *gauss=NULL;

	/*If on water or not FS, skip stiffness: */
	inputs->GetInputValue(&approximation,ApproximationEnum);
	if(approximation!=FSApproximationEnum && approximation!=SSAFSApproximationEnum && approximation!=HOFSApproximationEnum) return NULL;

	/*Fetch number of nodes and dof for this finite element*/
	int vnumnodes = this->NumberofNodesVelocity();
	int pnumnodes = this->NumberofNodesPressure();
	int numdof    = vnumnodes*NDOF3 + pnumnodes*NDOF1;

	/*Prepare coordinate system list*/
	int* cs_list = xNew<int>(vnumnodes+pnumnodes);
	for(i=0;i<vnumnodes;i++) cs_list[i]           = XYZEnum;
	for(i=0;i<pnumnodes;i++) cs_list[vnumnodes+i] = PressureEnum;

	/*Initialize Element matrix and vectors*/
	ElementMatrix* Ke     = new ElementMatrix(nodes,vnumnodes+pnumnodes,this->parameters,FSvelocityEnum);
	IssmDouble*    B      = xNew<IssmDouble>(8*numdof);
	IssmDouble*    Bprime = xNew<IssmDouble>(8*numdof);
	IssmDouble*    D      = xNewZeroInit<IssmDouble>(8*8);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	parameters->FindParam(&FSreconditioning,StressbalanceFSreconditioningEnum);
	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(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetBFS(B,&xyz_list[0][0],gauss); 
		GetBprimeFS(Bprime,&xyz_list[0][0],gauss); 

		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		material->GetViscosity3dFS(&viscosity,&epsilon[0]);

		D_scalar=gauss->weight*Jdet;
		for(i=0;i<6;i++) D[i*8+i] = +D_scalar*2.*viscosity;
		for(i=6;i<8;i++) D[i*8+i] = -D_scalar*FSreconditioning;

		TripleMultiply(B,8,numdof,1,
					D,8,8,0,
					Bprime,8,numdof,0,
					Ke->values,1);
	}

	/*Transform Coordinate System*/
	TransformStiffnessMatrixCoord(Ke,nodes,(vnumnodes+pnumnodes),cs_list);

	/*Clean up and return*/
	delete gauss;
	xDelete<int>(cs_list);
	xDelete<IssmDouble>(B);
	xDelete<IssmDouble>(Bprime);
	xDelete<IssmDouble>(D);
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixStressbalanceFSFriction{{{*/
ElementMatrix* Penta::CreateKMatrixStressbalanceFSFriction(void){

	/*Intermediaries */
	int         i,j;
	int         analysis_type,approximation;
	IssmDouble  alpha2,Jdet2d;
	IssmDouble  FSreconditioning,viscosity;
	IssmDouble  epsilon[6]; /* epsilon=[exx,eyy,ezz,exy,exz,eyz];*/
	IssmDouble  xyz_list[NUMVERTICES][3];
	IssmDouble  xyz_list_tria[NUMVERTICES2D][3];
	Friction   *friction = NULL;
	GaussPenta *gauss    = NULL;

	/*Initialize Element matrix and return if necessary*/
	if(IsFloating() || !IsOnBed()) return NULL;

	/*If on water or not FS, skip stiffness: */
	inputs->GetInputValue(&approximation,ApproximationEnum);
	if(approximation!=FSApproximationEnum && approximation!=SSAFSApproximationEnum && approximation!=HOFSApproximationEnum) return NULL;

	/*Fetch number of nodes and dof for this finite element*/
	int vnumnodes = this->NumberofNodesVelocity();
	int pnumnodes = this->NumberofNodesPressure();
	int  numdof   = vnumnodes*NDOF3 + pnumnodes*NDOF1;

	/*Initialize Element matrix and vectors*/
	ElementMatrix* Ke        = new ElementMatrix(nodes,vnumnodes+pnumnodes,this->parameters,FSvelocityEnum);
	IssmDouble*    BFriction = xNew<IssmDouble>(2*numdof);
	IssmDouble*    D         = xNewZeroInit<IssmDouble>(2*2);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	parameters->FindParam(&FSreconditioning,StressbalanceFSreconditioningEnum);
	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(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetTriaJacobianDeterminant(&Jdet2d, &xyz_list_tria[0][0],gauss);
		GetLFS(BFriction,gauss);

		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		material->GetViscosity3dFS(&viscosity,&epsilon[0]);

		friction->GetAlpha2(&alpha2,gauss,VxEnum,VyEnum,VzEnum);

		D[0*2+0] = +alpha2*gauss->weight*Jdet2d; //taub_x = -alpha2 vx
		D[1*2+1] = +alpha2*gauss->weight*Jdet2d; //taub_y = -alpha2 vy

		TripleMultiply(BFriction,2,numdof,1,
					D,2,2,0,
					BFriction,2,numdof,0,
					Ke->values,1);
	}

	/*DO NOT Transform Coordinate System: this stiffness matrix is already expressed in tangential coordinates*/
	//TransformStiffnessMatrixCoord(Ke,nodes,NUMVERTICES,XYZEnum);

	/*Clean up and return*/
	delete gauss;
	delete friction;
	xDelete<IssmDouble>(BFriction);
	xDelete<IssmDouble>(D);
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::CreateKMatrixStressbalanceVert {{{*/
ElementMatrix* Penta::CreateKMatrixStressbalanceVert(void){

	/*compute all stiffness matrices for this element*/
	ElementMatrix* Ke1=CreateKMatrixStressbalanceVertVolume();
	ElementMatrix* Ke2=CreateKMatrixStressbalanceVertSurface();
	ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2);

	/*clean-up and return*/
	delete Ke1;
	delete Ke2;
	return Ke;

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

	/*Intermediaries */
	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],vertices,NUMVERTICES);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(2,2);
	for(int 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::CreateKMatrixStressbalanceVertSurface {{{*/
ElementMatrix* Penta::CreateKMatrixStressbalanceVertSurface(void){

	if (!IsOnSurface()) return NULL;

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

	/*Intermediaries */
	int       i,j;
	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],vertices,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(int 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::CreatePVectorCouplingSSAFS {{{*/
ElementVector* Penta::CreatePVectorCouplingSSAFS(void){

	/*compute all load vectors for this element*/
	ElementVector* pe1=CreatePVectorCouplingSSAFSViscous();
	ElementVector* pe2=CreatePVectorCouplingSSAFSFriction();
	ElementVector* pe =new ElementVector(pe1,pe2);

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

	/*Intermediaries */
	int         i,j;
	int         approximation;
	IssmDouble  viscosity,Jdet;
	IssmDouble  FSreconditioning;
	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!=SSAFSApproximationEnum) return NULL;
	int vnumnodes = this->NumberofNodesVelocity();
	int pnumnodes = this->NumberofNodesPressure();

	/*Prepare coordinate system list*/
	int* cs_list = xNew<int>(vnumnodes+pnumnodes);
	for(i=0;i<vnumnodes;i++) cs_list[i]           = XYZEnum;
	for(i=0;i<pnumnodes;i++) cs_list[vnumnodes+i] = PressureEnum;
	ElementVector* pe=new ElementVector(nodes,vnumnodes+pnumnodes,this->parameters,FSvelocityEnum);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	this->parameters->FindParam(&FSreconditioning,StressbalanceFSreconditioningEnum);
	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* vzSSA_input=inputs->GetInput(VzSSAEnum);   _assert_(vzSSA_input);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(5,5);
	for(int 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);

		vzSSA_input->GetInputDerivativeValue(&dw[0],&xyz_list[0][0],gauss);

		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		material->GetViscosity3dFS(&viscosity,&epsilon[0]);

		for(i=0;i<NUMVERTICES;i++){
			pe->values[i*NDOF3+0]+=-Jdet*gauss->weight*viscosity*dw[0]*dbasis[2][i];
			pe->values[i*NDOF3+1]+=-Jdet*gauss->weight*viscosity*dw[1]*dbasis[2][i];
			pe->values[i*NDOF3+2]+=-Jdet*gauss->weight*viscosity*(dw[0]*dbasis[0][i]+dw[1]*dbasis[1][i]+2*dw[2]*dbasis[2][i]);
			pe->values[NDOF3*vnumnodes+i]+=Jdet*gauss->weight*FSreconditioning*dw[2]*basis[i];
		}
	}

	/*Transform coordinate system*/
	TransformLoadVectorCoord(pe,nodes,vnumnodes+pnumnodes,cs_list);

	/*Clean up and return*/
	xDelete<int>(cs_list);
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorCouplingSSAFSFriction{{{*/
ElementVector* Penta::CreatePVectorCouplingSSAFSFriction(void){

	/*Intermediaries*/
	int         i,j;
	int         approximation,analysis_type;
	IssmDouble  Jdet,Jdet2d;
	IssmDouble  FSreconditioning;
	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!=SSAFSApproximationEnum) return NULL;
	int vnumnodes = this->NumberofNodesVelocity();
	int pnumnodes = this->NumberofNodesPressure();

	/*Prepare coordinate system list*/
	int* cs_list = xNew<int>(vnumnodes+pnumnodes);
	for(i=0;i<vnumnodes;i++) cs_list[i]           = XYZEnum;
	for(i=0;i<pnumnodes;i++) cs_list[vnumnodes+i] = PressureEnum;
	ElementVector* pe=new ElementVector(nodes,vnumnodes+pnumnodes,this->parameters,FSvelocityEnum);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	this->parameters->FindParam(&FSreconditioning,StressbalanceFSreconditioningEnum);
	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* vzSSA_input=inputs->GetInput(VzSSAEnum);  _assert_(vzSSA_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(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

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

		vzSSA_input->GetInputValue(&w, gauss);
		vzSSA_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);
		material->GetViscosity3dFS(&viscosity,&epsilon[0]);
		friction->GetAlpha2(&alpha2_gauss, gauss,VxEnum,VyEnum,VzEnum);

		for(i=0;i<NUMVERTICES2D;i++){
			pe->values[i*NDOF3+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*NDOF3+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*NDOF3+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,(vnumnodes+pnumnodes),cs_list);

	/*Clean up and return*/
	xDelete<int>(cs_list);
	delete gauss;
	delete friction;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorCouplingHOFS {{{*/
ElementVector* Penta::CreatePVectorCouplingHOFS(void){

	/*compute all load vectors for this element*/
	ElementVector* pe1=CreatePVectorCouplingHOFSViscous();
	ElementVector* pe2=CreatePVectorCouplingHOFSFriction();
	ElementVector* pe =new ElementVector(pe1,pe2);

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

	/*Intermediaries */
	int         i;
	int         approximation;
	IssmDouble  viscosity,Jdet;
	IssmDouble  FSreconditioning;
	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!=HOFSApproximationEnum) return NULL;
	int vnumnodes = this->NumberofNodesVelocity();
	int pnumnodes = this->NumberofNodesPressure();

	/*Prepare coordinate system list*/
	int* cs_list = xNew<int>(vnumnodes+pnumnodes);
	for(i=0;i<vnumnodes;i++) cs_list[i]           = XYZEnum;
	for(i=0;i<pnumnodes;i++) cs_list[vnumnodes+i] = PressureEnum;

	/*Initialize Element matrix and vectors*/
	ElementVector* pe=new ElementVector(nodes,vnumnodes+pnumnodes,this->parameters,FSvelocityEnum);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	this->parameters->FindParam(&FSreconditioning,StressbalanceFSreconditioningEnum);
	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* vzHO_input=inputs->GetInput(VzHOEnum);   _assert_(vzHO_input);

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(5,5);
	for(int 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);

		vzHO_input->GetInputDerivativeValue(&dw[0],&xyz_list[0][0],gauss);

		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		material->GetViscosity3dFS(&viscosity,&epsilon[0]);

		for(i=0;i<NUMVERTICES;i++){
			pe->values[i*NDOF3+0]+=-Jdet*gauss->weight*viscosity*dw[0]*dbasis[2][i];
			pe->values[i*NDOF3+1]+=-Jdet*gauss->weight*viscosity*dw[1]*dbasis[2][i];
			pe->values[i*NDOF3+2]+=-Jdet*gauss->weight*viscosity*(dw[0]*dbasis[0][i]+dw[1]*dbasis[1][i]+2*dw[2]*dbasis[2][i]);
			pe->values[NDOF3*vnumnodes+i]+=Jdet*gauss->weight*FSreconditioning*dw[2]*basis[i];
		}
	}

	/*Transform coordinate system*/
	TransformLoadVectorCoord(pe,nodes,vnumnodes+pnumnodes,cs_list);

	/*Clean up and return*/
	xDelete<int>(cs_list);
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorCouplingHOFSFriction{{{*/
ElementVector* Penta::CreatePVectorCouplingHOFSFriction(void){

	/*Intermediaries*/
	int         i,j;
	int         approximation,analysis_type;
	IssmDouble  Jdet,Jdet2d;
	IssmDouble  FSreconditioning;
	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!=HOFSApproximationEnum) return NULL;

	int vnumnodes = this->NumberofNodesVelocity();
	int pnumnodes = this->NumberofNodesPressure();
	int numnodes  = vnumnodes+pnumnodes;

	/*Prepare coordinate system list*/
	int* cs_list = xNew<int>(vnumnodes+pnumnodes);
	for(i=0;i<vnumnodes;i++) cs_list[i]           = XYZEnum;
	for(i=0;i<pnumnodes;i++) cs_list[vnumnodes+i] = PressureEnum;

	ElementVector* pe=new ElementVector(nodes,numnodes,this->parameters,FSvelocityEnum);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	this->parameters->FindParam(&FSreconditioning,StressbalanceFSreconditioningEnum);
	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* vzHO_input=inputs->GetInput(VzHOEnum);   _assert_(vzHO_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(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

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

		vzHO_input->GetInputValue(&w, gauss);
		vzHO_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);
		material->GetViscosity3dFS(&viscosity,&epsilon[0]);
		friction->GetAlpha2(&alpha2_gauss, gauss,VxEnum,VyEnum,VzEnum);

		for(i=0;i<NUMVERTICES2D;i++){
			pe->values[i*NDOF3+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*NDOF3+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*NDOF3+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,vnumnodes+pnumnodes,cs_list);

	/*Clean up and return*/
	xDelete<int>(cs_list);
	delete gauss;
	delete friction;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorStressbalanceHoriz{{{*/
ElementVector* Penta::CreatePVectorStressbalanceHoriz(void){

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

	switch(approximation){
		case SSAApproximationEnum:
			return CreatePVectorStressbalanceSSA();
		case HOApproximationEnum:
			return CreatePVectorStressbalanceHO();
		case L1L2ApproximationEnum:
			return CreatePVectorStressbalanceL1L2();
		case SIAApproximationEnum:
			return NULL;
		case NoneApproximationEnum:
			return NULL;
		case FSApproximationEnum:
			return CreatePVectorStressbalanceFS();
		case SSAHOApproximationEnum:
			return CreatePVectorStressbalanceSSAHO();
		case SSAFSApproximationEnum:
			return CreatePVectorStressbalanceSSAFS();
		case HOFSApproximationEnum:
			return CreatePVectorStressbalanceHOFS();
		default:
			_error_("Approximation " << EnumToStringx(approximation) << " not supported yet");
	}
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorStressbalanceSSAHO{{{*/
ElementVector* Penta::CreatePVectorStressbalanceSSAHO(void){

	/*compute all load vectors for this element*/
	ElementVector* pe1=CreatePVectorStressbalanceSSA();
	ElementVector* pe2=CreatePVectorStressbalanceHO();
	ElementVector* pe =new ElementVector(pe1,pe2);

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

	/*compute all load vectors for this element*/
	int init = this->element_type;
	this->element_type=P1Enum;
	ElementVector* pe1=CreatePVectorStressbalanceSSA();
	this->element_type=init;
	ElementVector* pe2=CreatePVectorStressbalanceFS();
	int indices[3]={18,19,20};
	this->element_type=MINIcondensedEnum;
	ElementMatrix* Ke = CreateKMatrixStressbalanceFS();
	this->element_type=init;
	pe2->StaticCondensation(Ke,3,&indices[0]);
	delete Ke;
	ElementVector* pe3=CreatePVectorCouplingSSAFS();
	ElementVector* pe =new ElementVector(pe1,pe2,pe3);

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

	/*compute all load vectors for this element*/
	int init = this->element_type;
	this->element_type=P1Enum;
	ElementVector* pe1=CreatePVectorStressbalanceHO();
	this->element_type=init;
	ElementVector* pe2=CreatePVectorStressbalanceFS();
	int indices[3]={18,19,20};
	this->element_type=MINIcondensedEnum;
	ElementMatrix* Ke = CreateKMatrixStressbalanceFS();
	this->element_type=init;
	pe2->StaticCondensation(Ke,3,&indices[0]);
	delete Ke;
	ElementVector* pe3=CreatePVectorCouplingHOFS();
	ElementVector* pe =new ElementVector(pe1,pe2,pe3);

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

	/*Intermediaries*/
	int         i,j;
	int         node0,node1;
	IssmDouble  connectivity[2];
	IssmDouble  Jdet;
	IssmDouble  xyz_list[NUMVERTICES][3];
	IssmDouble  xyz_list_segment[2][3];
	IssmDouble  z_list[NUMVERTICES];
	IssmDouble  slope[2];
	IssmDouble  slope2,constant_part;
	IssmDouble  rho_ice,gravity,n,B;
	IssmDouble  ub,vb,z_g,surface,thickness;
	GaussPenta* gauss=NULL;

	/*Fetch number of nodes and dof for this finite element*/
	int numnodes = this->NumberofNodes(); _assert_(numnodes==6); 

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

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	rho_ice=matpar->GetRhoIce();
	gravity=matpar->GetG();
	n=material->GetN();
	B=material->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]=(IssmDouble)vertices[node0]->Connectivity();
		connectivity[1]=(IssmDouble)vertices[node1]->Connectivity();

		/*Loop on the Gauss points: */
		gauss=new GaussPenta(node0,node1,3);
		for(int 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()){
				pe->values[2*node1+0]+=constant_part*pow((surface-z_g)/B,n)*slope[0]*Jdet*gauss->weight/connectivity[1];
				pe->values[2*node1+1]+=constant_part*pow((surface-z_g)/B,n)*slope[1]*Jdet*gauss->weight/connectivity[1];
			}
			else{/*connectivity is too large, should take only half on it*/
				pe->values[2*node1+0]+=constant_part*pow((surface-z_g)/B,n)*slope[0]*Jdet*gauss->weight*2./connectivity[1];
				pe->values[2*node1+1]+=constant_part*pow((surface-z_g)/B,n)*slope[1]*Jdet*gauss->weight*2./connectivity[1];
			}
		}
		delete gauss;

		/*Deal with lower surface*/
		if(IsOnBed()){
			constant_part=-1.58*1.e-10*rho_ice*gravity*thickness;
			ub=constant_part*slope[0];
			vb=constant_part*slope[1];

			pe->values[2*node0+0]+=ub/connectivity[0];
			pe->values[2*node0+1]+=vb/connectivity[0];
		}
	}

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

	if (!IsOnBed()) return NULL;

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
	ElementVector* pe=tria->CreatePVectorStressbalanceSSA();
	delete tria->material; delete tria;

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

	if (!IsOnBed()) return NULL;

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
	ElementVector* pe=tria->CreatePVectorStressbalanceSSA();
	delete tria->material; delete tria;

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

	/*compute all load vectors for this element*/
	ElementVector* pe1=CreatePVectorStressbalanceHODrivingStress();
	ElementVector* pe2=CreatePVectorStressbalanceHOFront();
	ElementVector* pe =new ElementVector(pe1,pe2);

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

	/*Intermediaries*/
	int         i,j;
	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];
	GaussPenta  *gauss=NULL;

	/*Fetch number of nodes and dof for this finite element*/
	int numnodes = this->NumberofNodes();

	/*Initialize vector*/
	ElementVector* pe    = new ElementVector(nodes,numnodes,this->parameters,HOApproximationEnum);
	IssmDouble*    basis = xNewZeroInit<IssmDouble>(numnodes);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,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(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctions(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<numnodes;i++){
			pe->values[i*NDOF2+0]+= -driving_stress_baseline*slope[0]*Jdet*gauss->weight*basis[i];
			pe->values[i*NDOF2+1]+= -driving_stress_baseline*slope[1]*Jdet*gauss->weight*basis[i];
		}
	}

	/*Transform coordinate system*/
	TransformLoadVectorCoord(pe,nodes,numnodes,XYEnum);

	/*Clean up and return*/
	xDelete<IssmDouble>(basis);
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorStressbalanceHOFront{{{*/
ElementVector* Penta::CreatePVectorStressbalanceHOFront(void){

	/*Intermediaries */
	IssmDouble  ls[NUMVERTICES];
	IssmDouble  xyz_list[NUMVERTICES][3];
	bool        isfront;

	/*Retrieve all inputs and parameters*/
	GetInputListOnVertices(&ls[0],MaskIceLevelsetEnum);

	/*If the level set is awlays <=0, there is no ice front here*/
	isfront = false;
	if(ls[0]>0. || ls[1]>0. || ls[2]>0.){
		if(ls[0]*ls[1]<0. || ls[0]*ls[2]<0. || (ls[0]*ls[1]+ls[0]*ls[2]+ls[1]*ls[2]==0.)){
			isfront = true;
		}
	}

	/*If no front, return NULL*/
	if(!isfront) return NULL;

	/*Intermediaries*/
	IssmDouble  rho_ice,rho_water,gravity;
	IssmDouble  Jdet,surface,z_g,water_pressure,ice_pressure,air_pressure;
	IssmDouble  surface_under_water,base_under_water,pressure;
	IssmDouble  xyz_list_front[4][3];
	IssmDouble  area_coordinates[4][3];
	IssmDouble  normal[3];
	GaussPenta* gauss;

	/*Fetch number of nodes and dof for this finite element*/
	int numnodes = this->NumberofNodes();

	/*Initialize Element vector and other vectors*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	ElementVector* pe    = new ElementVector(nodes,numnodes,this->parameters,HOApproximationEnum);
	IssmDouble*    basis = xNew<IssmDouble>(numnodes);

	/*Retrieve all inputs and parameters*/
	Input* surface_input=inputs->GetInput(SurfaceEnum); _assert_(surface_input);
	rho_water=matpar->GetRhoWater();
	rho_ice  =matpar->GetRhoIce();
	gravity  =matpar->GetG();
	GetZeroLevelsetCoordinates(&xyz_list_front[0][0],xyz_list,MaskIceLevelsetEnum);
	GetAreaCoordinates(&area_coordinates[0][0],xyz_list_front,xyz_list,4);
	GetQuadNormal(&normal[0],xyz_list_front);

	/*Initialize gauss points*/
	IssmDouble zmax=xyz_list[0][2]; for(int i=1;i<6;i++) if(xyz_list[i][2]>zmax) zmax=xyz_list[i][2];
	IssmDouble zmin=xyz_list[0][2]; for(int i=1;i<6;i++) if(xyz_list[i][2]<zmin) zmin=xyz_list[i][2];
	if(zmax>0 && zmin<0) gauss=new GaussPenta(area_coordinates,3,10); //refined in vertical because of the sea level discontinuity
	else                 gauss=new GaussPenta(area_coordinates,3,3);

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

		gauss->GaussPoint(ig);
		surface_input->GetInputValue(&surface,gauss);
		z_g=GetZcoord(gauss);
		GetNodalFunctions(basis,gauss);
		GetQuadJacobianDeterminant(&Jdet,xyz_list_front,gauss);

		water_pressure = rho_water*gravity*min(0.,z_g);//0 if the gaussian point is above water level
		ice_pressure   = rho_ice*gravity*(surface-z_g);
		air_pressure   = 0;
		pressure       = ice_pressure + water_pressure + air_pressure;

		for (int i=0;i<numnodes;i++){
			pe->values[2*i+0]+= pressure*Jdet*gauss->weight*normal[0]*basis[i];
			pe->values[2*i+1]+= pressure*Jdet*gauss->weight*normal[1]*basis[i];
		}
	}

	/*Transform coordinate system*/
	TransformLoadVectorCoord(pe,nodes,numnodes,XYEnum);

	/*Clean up and return*/
	xDelete<IssmDouble>(basis);
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorStressbalanceFS {{{*/
ElementVector* Penta::CreatePVectorStressbalanceFS(void){

	ElementVector* pe1;
	ElementVector* pe2;
	ElementVector* pe3;
	ElementVector* pe;

	/*compute all stiffness matrices for this element*/
	pe1=CreatePVectorStressbalanceFSViscous();
	pe2=CreatePVectorStressbalanceFSShelf();
	pe3=CreatePVectorStressbalanceFSFront();
	pe =new ElementVector(pe1,pe2,pe3);

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

	/*Intermediaries */
	int         i;
	IssmDouble  ls[NUMVERTICES];
	IssmDouble  xyz_list[NUMVERTICES][3];
	bool        isfront;

	/*Retrieve all inputs and parameters*/
	GetInputListOnVertices(&ls[0],MaskIceLevelsetEnum);

	/*If the level set is awlays <=0, there is no ice front here*/
	isfront = false;
	if(ls[0]>0. || ls[1]>0. || ls[2]>0.){
		if(ls[0]*ls[1]<0. || ls[0]*ls[2]<0. || (ls[0]*ls[1]+ls[0]*ls[2]+ls[1]*ls[2]==0.)){
			isfront = true;
		}
	}

	/*If no front, return NULL*/
	if(!isfront) return NULL;

	/*Fetch number of nodes and dof for this finite element*/
	IssmDouble  rho_ice,rho_water,gravity;
	IssmDouble  Jdet,z_g,water_pressure,air_pressure;
	IssmDouble  surface_under_water,base_under_water,pressure;
	IssmDouble  xyz_list_front[4][3];
	IssmDouble  area_coordinates[4][3];
	IssmDouble  normal[3];
	GaussPenta* gauss;

	/*Fetch number of nodes and dof for this finite element*/
	int vnumnodes = this->NumberofNodesVelocity();
	int pnumnodes = this->NumberofNodesPressure();

	/*Prepare coordinate system list*/
	int* cs_list = xNew<int>(vnumnodes+pnumnodes);
	for(i=0;i<vnumnodes;i++) cs_list[i]           = XYZEnum;
	for(i=0;i<pnumnodes;i++) cs_list[vnumnodes+i] = PressureEnum;

	/*Initialize Element matrix and vectors*/
	ElementVector* pe     = new ElementVector(nodes,vnumnodes+pnumnodes,this->parameters,FSvelocityEnum);
	IssmDouble*    vbasis = xNew<IssmDouble>(vnumnodes);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	rho_water=matpar->GetRhoWater();
	rho_ice  =matpar->GetRhoIce();
	gravity  =matpar->GetG();
	GetZeroLevelsetCoordinates(&xyz_list_front[0][0],xyz_list,MaskIceLevelsetEnum);
	GetAreaCoordinates(&area_coordinates[0][0],xyz_list_front,xyz_list,4);
	GetQuadNormal(&normal[0],xyz_list_front);

	/*Start looping on Gaussian points*/
	IssmDouble zmax=xyz_list[0][2]; for(int i=1;i<6;i++) if(xyz_list[i][2]>zmax) zmax=xyz_list[i][2];
	IssmDouble zmin=xyz_list[0][2]; for(int i=1;i<6;i++) if(xyz_list[i][2]<zmin) zmin=xyz_list[i][2];
	if(zmax>0 && zmin<0) gauss=new GaussPenta(area_coordinates,3,30); //refined in vertical because of the sea level discontinuity
	else                 gauss=new GaussPenta(area_coordinates,3,3);

	for(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);
		z_g=GetZcoord(gauss);
		GetNodalFunctionsVelocity(vbasis,gauss);
		GetQuadJacobianDeterminant(&Jdet,xyz_list_front,gauss);

		water_pressure=rho_water*gravity*min(0.,z_g);//0 if the gaussian point is above water level
		air_pressure=0;
		pressure = water_pressure + air_pressure;

		for(i=0;i<vnumnodes;i++){
			pe->values[3*i+0]+= pressure*Jdet*gauss->weight*normal[0]*vbasis[i];
			pe->values[3*i+1]+= pressure*Jdet*gauss->weight*normal[1]*vbasis[i];
			pe->values[3*i+2]+= pressure*Jdet*gauss->weight*normal[2]*vbasis[i];
		}
	}

	/*Transform coordinate system*/
	TransformLoadVectorCoord(pe,nodes,(vnumnodes+pnumnodes),cs_list);

	/*Clean up and return*/
	xDelete<int>(cs_list);
	xDelete<IssmDouble>(vbasis);
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Penta::PVectorGLSstabilization{{{*/
void Penta::PVectorGLSstabilization(ElementVector* pe){

	/*Intermediaries*/
	int        i,j;
	int        approximation;
	IssmDouble Jdet,gravity,rho_ice,B,D_scalar_stab,viscosity;
	IssmDouble forcex,forcey,forcez,diameter,FSreconditioning;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble epsilon[6]; /* epsilon=[exx,eyy,ezz,exy,exz,eyz];*/
	IssmDouble l1l6[6]; //for the six nodes and the bubble 
	IssmDouble dh1dh6[3][NUMVERTICES];
	GaussPenta *gauss=NULL;

	/*Stabilization*/
	bool       stabilization = true;
	IssmDouble dbasis[3][6];
	IssmDouble dmu[3];
	IssmDouble mu;
	IssmDouble dnodalbasis[6][6][3];
	IssmDouble SW[6][4][4];
	int p,q,ii;
	int c=3; //index of pressure

	/*Retrieve all inputs and parameters*/
	rho_ice=matpar->GetRhoIce();
	gravity=matpar->GetG();
	B=material->GetB();
	GetVerticesCoordinates(&xyz_list[0][0],vertices,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);

	/*Find minimal length*/
	diameter=MinEdgeLength(xyz_list);

		gauss=new GaussPenta();
		for(int iv=0;iv<6;iv++){
			gauss->GaussVertex(iv);
			GetNodalFunctionsP1Derivatives(&dbasis[0][0],&xyz_list[0][0],gauss);
			for(i=0;i<6;i++){
				for(j=0;j<3;j++){
					dnodalbasis[i][iv][j] = dbasis[j][i];
				}
			}
		}
		delete gauss;

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

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctionsP1(&l1l6[0], gauss);
		this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		material->GetViscosity3dFS(&viscosity,&epsilon[0]);

		GetNodalFunctionsP1Derivatives(&dbasis[0][0],&xyz_list[0][0],gauss);
		dmu[0]=0.; dmu[1]=0.; dmu[2]=0.;
		mu = 2.*viscosity;
		for(p=0;p<6;p++) for(i=0;i<4;i++) for(j=0;j<4;j++){
			SW[p][i][j]=0.;
		}
		for(p=0;p<6;p++){
			for(i=0;i<3;i++){
				SW[p][c][i] += dbasis[i][p];
				for(j=0;j<3;j++){
					SW[p][i][i] += -dmu[j]*dbasis[j][p];
					SW[p][j][i] += -dmu[j]*dbasis[i][p];
					for(ii=0;ii<6;ii++){
						SW[p][i][i] += -mu*dnodalbasis[p][ii][j]*dbasis[j][ii];
						SW[p][j][i] += -mu*dnodalbasis[p][ii][i]*dbasis[j][ii];
					}
				}
			}
		}
		IssmDouble tau = 1./3.*pow(diameter,2)/(8.*2.*viscosity);
		for(p=0;p<6;p++){
			for(j=0;j<4;j++){
				pe->values[p*4+j] += gauss->weight*Jdet*tau*rho_ice*forcex*SW[p][j][0];
				pe->values[p*4+j] += gauss->weight*Jdet*tau*rho_ice*forcey*SW[p][j][1];
				pe->values[p*4+j] += gauss->weight*Jdet*tau*rho_ice*(forcez-gravity)*SW[p][j][2];
			}
		}
	}

	/*Clean up*/
	delete gauss;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorStressbalanceFSViscous {{{*/
ElementVector* Penta::CreatePVectorStressbalanceFSViscous(void){

	/*Intermediaries*/
	int        i,j;
	int        approximation;
	IssmDouble Jdet,gravity,rho_ice;
	IssmDouble forcex,forcey,forcez;
	IssmDouble xyz_list[NUMVERTICES][3];
	GaussPenta *gauss=NULL;

	/*Initialize Element vector and return if necessary*/
	inputs->GetInputValue(&approximation,ApproximationEnum);
	if(approximation!=FSApproximationEnum && approximation!=SSAFSApproximationEnum && approximation!=HOFSApproximationEnum) return NULL;

	/*Fetch number of nodes and dof for this finite element*/
	int vnumnodes = this->NumberofNodesVelocity();
	int pnumnodes = this->NumberofNodesPressure();

	/*Prepare coordinate system list*/
	int* cs_list = xNew<int>(vnumnodes+pnumnodes);
	for(i=0;i<vnumnodes;i++) cs_list[i]           = XYZEnum;
	for(i=0;i<pnumnodes;i++) cs_list[vnumnodes+i] = PressureEnum;

	/*Initialize Element matrix and vectors*/
	ElementVector* pe     = new ElementVector(nodes,vnumnodes+pnumnodes,this->parameters,FSvelocityEnum);
	IssmDouble*    vbasis = xNew<IssmDouble>(vnumnodes);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	Input* loadingforcex_input=inputs->GetInput(LoadingforceXEnum);  _assert_(loadingforcex_input);
	Input* loadingforcey_input=inputs->GetInput(LoadingforceYEnum);  _assert_(loadingforcey_input);
	Input* loadingforcez_input=inputs->GetInput(LoadingforceZEnum);  _assert_(loadingforcez_input);
	rho_ice = matpar->GetRhoIce();
	gravity = matpar->GetG();

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

		gauss->GaussPoint(ig);

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

		loadingforcex_input->GetInputValue(&forcex,gauss);
		loadingforcey_input->GetInputValue(&forcey,gauss);
		loadingforcez_input->GetInputValue(&forcez,gauss);

		for(i=0;i<vnumnodes;i++){
			pe->values[i*NDOF3+2] += -rho_ice*gravity*Jdet*gauss->weight*vbasis[i];
			pe->values[i*NDOF3+0] += +rho_ice*forcex *Jdet*gauss->weight*vbasis[i];
			pe->values[i*NDOF3+1] += +rho_ice*forcey *Jdet*gauss->weight*vbasis[i];
			pe->values[i*NDOF3+2] += +rho_ice*forcez *Jdet*gauss->weight*vbasis[i];
		}
	}

	/*Transform coordinate system*/
	TransformLoadVectorCoord(pe,nodes,vnumnodes+pnumnodes,cs_list);

	/*Clean up and return*/
	delete gauss;
	xDelete<int>(cs_list);
	xDelete<IssmDouble>(vbasis);
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorStressbalanceFSShelf{{{*/
ElementVector* Penta::CreatePVectorStressbalanceFSShelf(void){

	/*Intermediaries*/
	int         i,j;
	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  Jdet2d;
	GaussPenta  *gauss=NULL;

	/*Initialize Element vector and return if necessary*/
	if(!IsOnBed() || !IsFloating()) return NULL;
	inputs->GetInputValue(&approximation,ApproximationEnum);
	if(approximation!=FSApproximationEnum && approximation!=SSAFSApproximationEnum && approximation!=HOFSApproximationEnum) return NULL;

	/*Fetch number of nodes and dof for this finite element*/
	int vnumnodes = this->NumberofNodesVelocity();
	int pnumnodes = this->NumberofNodesPressure();

	/*Prepare coordinate system list*/
	int* cs_list = xNew<int>(vnumnodes+pnumnodes);
	for(i=0;i<vnumnodes;i++) cs_list[i]           = XYZEnum;
	for(i=0;i<pnumnodes;i++) cs_list[vnumnodes+i] = PressureEnum;

	/*Initialize Element matrix and vectors*/
	ElementVector* pe     = new ElementVector(nodes,vnumnodes+pnumnodes,this->parameters,FSvelocityEnum);
	IssmDouble*    vbasis = xNew<IssmDouble>(vnumnodes);

	/*Retrieve all inputs and parameters*/
	this->parameters->FindParam(&shelf_dampening,StressbalanceShelfDampeningEnum);
	rho_water=matpar->GetRhoWater();
	gravity=matpar->GetG();
	GetVerticesCoordinates(&xyz_list[0][0],vertices,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(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		GetTriaJacobianDeterminant(&Jdet2d, &xyz_list_tria[0][0], gauss);
		GetNodalFunctionsVelocity(vbasis, 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<vnumnodes;i++){
			pe->values[3*i+0]+=(water_pressure+damper)*gauss->weight*Jdet2d*vbasis[i]*bed_normal[0];
			pe->values[3*i+1]+=(water_pressure+damper)*gauss->weight*Jdet2d*vbasis[i]*bed_normal[1];
			pe->values[3*i+2]+=(water_pressure+damper)*gauss->weight*Jdet2d*vbasis[i]*bed_normal[2];
		}
	}

	/*Transform coordinate system*/
	TransformLoadVectorCoord(pe,nodes,(vnumnodes+pnumnodes),cs_list);

	/*Clean up and return*/
	delete gauss;
	xDelete<int>(cs_list);
	xDelete<IssmDouble>(vbasis);
	return pe;
}
/*}}}*/
/*FUNCTION Penta::CreatePVectorStressbalanceVert {{{*/
ElementVector* Penta::CreatePVectorStressbalanceVert(void){

	/*compute all load vectors for this element*/
	ElementVector* pe1=CreatePVectorStressbalanceVertVolume();
	ElementVector* pe2=CreatePVectorStressbalanceVertBase();
	ElementVector* pe =new ElementVector(pe1,pe2);

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

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

	/*Intermediaries*/
	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],vertices,NUMVERTICES);
	inputs->GetInputValue(&approximation,ApproximationEnum);
	Input* vx_input=inputs->GetInput(VxEnum); _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum); _assert_(vy_input);
	Input* vzFS_input=NULL;
	if(approximation==HOFSApproximationEnum || approximation==SSAFSApproximationEnum){
		vzFS_input=inputs->GetInput(VzFSEnum); _assert_(vzFS_input);
	}

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(2,2);
	for(int 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==HOFSApproximationEnum || approximation==SSAFSApproximationEnum){
			vzFS_input->GetInputDerivativeValue(&dw[0],&xyz_list[0][0],gauss);
			dwdz=dw[2];
		}
		else dwdz=0;
		dudx=du[0];
		dvdy=dv[1];

		for(int 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::CreatePVectorStressbalanceVertBase {{{*/
ElementVector* Penta::CreatePVectorStressbalanceVertBase(void){

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

	/*Intermediaries */
	int        i,j;
	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],vertices,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* vzFS_input=NULL;
	if(approximation==HOFSApproximationEnum || approximation==SSAFSApproximationEnum){
		vzFS_input=inputs->GetInput(VzFSEnum);       _assert_(vzFS_input);
	}

	/* Start  looping on the number of gaussian points: */
	gauss=new GaussPenta(0,1,2,2);
	for(int 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==HOFSApproximationEnum || approximation==SSAFSApproximationEnum){
			vzFS_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::CreateJacobianStressbalanceHoriz{{{*/
ElementMatrix* Penta::CreateJacobianStressbalanceHoriz(void){

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

	switch(approximation){
		case SSAApproximationEnum:
			return CreateJacobianStressbalanceSSA2d();
		case HOApproximationEnum:
			return CreateJacobianStressbalanceHO();
		case FSApproximationEnum:
			return CreateJacobianStressbalanceFS();
		case NoneApproximationEnum:
			return NULL;
		default:
			_error_("Approximation " << EnumToStringx(approximation) << " not supported yet");
	}
}
/*}}}*/
/*FUNCTION Penta::CreateJacobianStressbalanceSSA2d{{{*/
ElementMatrix* Penta::CreateJacobianStressbalanceSSA2d(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 average some fields*/
	switch(this->material->ObjectEnum()){
		case MaticeEnum:
			this->InputDepthAverageAtBase(MaterialsRheologyBEnum,MaterialsRheologyBbarEnum,MaterialsEnum);
			break;
		case MatdamageiceEnum:
			this->InputDepthAverageAtBase(MaterialsRheologyBEnum,MaterialsRheologyBbarEnum,MaterialsEnum);
			this->InputDepthAverageAtBase(MaterialsRheologyZEnum,MaterialsRheologyZbarEnum,MaterialsEnum);
			break;
		default:
			_error_("material "<<EnumToStringx(this->material->ObjectEnum())<<" not supported");
	}

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
	ElementMatrix* Ke=tria->CreateJacobianStressbalanceSSA();
	delete tria->material; delete tria;

	/*Delete averaged inputs*/
	this->material->inputs->DeleteInput(MaterialsRheologyBbarEnum);
	this->material->inputs->DeleteInput(MaterialsRheologyZbarEnum);

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

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

	/*Intermediaries */
	int        i,j;
	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 dphi[3][NUMVERTICES];
	GaussPenta *gauss=NULL;

	/*Initialize Jacobian with regular HO (first part of the Gateau derivative)*/
	ElementMatrix* Ke=CreateKMatrixStressbalanceHO();

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,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(int 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->GetStrainRate3dHO(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
		material->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::CreateJacobianStressbalanceFS{{{*/
ElementMatrix* Penta::CreateJacobianStressbalanceFS(void){

	/*Intermediaries */
	int        i,j,approximation;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble Jdet;
	IssmDouble eps1dotdphii,eps1dotdphij;
	IssmDouble eps2dotdphii,eps2dotdphij;
	IssmDouble eps3dotdphii,eps3dotdphij;
	IssmDouble mu_prime;
	IssmDouble epsilon[6]; /* epsilon=[exx,eyy,ezz,exy,exz,eyz];*/
	IssmDouble eps1[3],eps2[3],eps3[3];
	GaussPenta *gauss=NULL;

	/*If on water or not FS, skip stiffness: */
	//inputs->GetInputValue(&approximation,ApproximationEnum);
	//if(approximation!=FSApproximationEnum && approximation!=SSAFSApproximationEnum && approximation!=HOFSApproximationEnum) return NULL;

	/*Fetch number of nodes and dof for this finite element*/
	int vnumnodes = this->NumberofNodesVelocity();
	int pnumnodes = this->NumberofNodesPressure();
	int numdof    = vnumnodes*NDOF3 + pnumnodes*NDOF1;

	/*Prepare coordinate system list*/
	int* cs_list = xNew<int>(vnumnodes+pnumnodes);
	for(i=0;i<vnumnodes;i++) cs_list[i]           = XYZEnum;
	for(i=0;i<pnumnodes;i++) cs_list[vnumnodes+i] = PressureEnum;

	/*Initialize Jacobian with regular FS (first part of the Gateau derivative)*/
	ElementMatrix* Ke=CreateKMatrixStressbalanceFS();
	IssmDouble*    dbasis = xNew<IssmDouble>(3*vnumnodes);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,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(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

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

		//this->GetStrainRate3d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input,vz_input);
		//material->GetViscosityDerivativeEpsSquareFS(&mu_prime,&epsilon[0]);
		//eps1[0]=epsilon[0];   eps2[0]=epsilon[3];   eps3[0]=epsilon[4];
		//eps1[1]=epsilon[3];   eps2[1]=epsilon[1];   eps3[1]=epsilon[5];
		//eps1[2]=epsilon[4];   eps2[2]=epsilon[5];   eps3[2]=epsilon[2];
		this->GetStrainRate3dHO(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
		material->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<vnumnodes-1;i++){
			for(j=0;j<vnumnodes-1;j++){
				eps1dotdphii=eps1[0]*dbasis[0*vnumnodes+i]+eps1[1]*dbasis[1*vnumnodes+i]+eps1[2]*dbasis[2*vnumnodes+i];
				eps1dotdphij=eps1[0]*dbasis[0*vnumnodes+j]+eps1[1]*dbasis[1*vnumnodes+j]+eps1[2]*dbasis[2*vnumnodes+j];
				eps2dotdphii=eps2[0]*dbasis[0*vnumnodes+i]+eps2[1]*dbasis[1*vnumnodes+i]+eps2[2]*dbasis[2*vnumnodes+i];
				eps2dotdphij=eps2[0]*dbasis[0*vnumnodes+j]+eps2[1]*dbasis[1*vnumnodes+j]+eps2[2]*dbasis[2*vnumnodes+j];
				eps3dotdphii=eps3[0]*dbasis[0*vnumnodes+i]+eps3[1]*dbasis[1*vnumnodes+i]+eps3[2]*dbasis[2*vnumnodes+i];
				eps3dotdphij=eps3[0]*dbasis[0*vnumnodes+j]+eps3[1]*dbasis[1*vnumnodes+j]+eps3[2]*dbasis[2*vnumnodes+j];

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

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

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

	/*Transform Coordinate System*/
	TransformStiffnessMatrixCoord(Ke,nodes,(vnumnodes+pnumnodes),cs_list);

	/*Clean up and return*/
	xDelete<int>(cs_list);
	xDelete<IssmDouble>(dbasis);
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Penta::GetSolutionFromInputsStressbalanceHoriz{{{*/
void  Penta::GetSolutionFromInputsStressbalanceHoriz(Vector<IssmDouble>* solution){

	int         approximation;
	int        *doflist = NULL;
	IssmDouble  vx,vy;

	/*Fetch number of nodes and dof for this finite element*/
	int numnodes = this->NumberofNodes();
	int numdof   = numnodes*NDOF2;

	/*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);

	/*Fetch dof list and allocate solution vectors*/
	GetDofList(&doflist,approximation,GsetEnum);
	IssmDouble* values = xNew<IssmDouble>(numdof);

	/*Ok, we have vx and vy in values, fill in vx and vy arrays: */
	GaussPenta* gauss=new GaussPenta();
	for(int i=0;i<numnodes;i++){
		gauss->GaussNode(this->element_type,i);

		/*Recover vx and vy*/
		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<IssmDouble>(values);
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Penta::GetSolutionFromInputsStressbalanceSIA{{{*/
void  Penta::GetSolutionFromInputsStressbalanceSIA(Vector<IssmDouble>* 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::GetSolutionFromInputsStressbalanceVert{{{*/
void  Penta::GetSolutionFromInputsStressbalanceVert(Vector<IssmDouble>* 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::GetSolutionFromInputsStressbalanceFS{{{*/
void  Penta::GetSolutionFromInputsStressbalanceFS(Vector<IssmDouble>* solution){

	int*         vdoflist=NULL;
	int*         pdoflist=NULL;
	IssmDouble   vx,vy,vz,p;
	IssmDouble   FSreconditioning;
	GaussPenta  *gauss;

	/*Fetch number of nodes and dof for this finite element*/
	int vnumnodes = this->NumberofNodesVelocity();
	int pnumnodes = this->NumberofNodesPressure();
	int vnumdof   = vnumnodes*NDOF3;
	int pnumdof   = pnumnodes*NDOF1;

	/*Initialize values*/
	IssmDouble* vvalues = xNew<IssmDouble>(vnumdof);
	IssmDouble* pvalues = xNew<IssmDouble>(pnumdof);

	/*Get dof list: */
	GetDofListVelocity(&vdoflist,GsetEnum);
	GetDofListPressure(&pdoflist,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);

	this->parameters->FindParam(&FSreconditioning,StressbalanceFSreconditioningEnum);

	/*Ok, we have vx vy vz in values, fill in vx vy vz arrays: */
	gauss = new GaussPenta();
	for(int i=0;i<vnumnodes;i++){
		gauss->GaussNode(this->VelocityInterpolation(),i);
		vx_input->GetInputValue(&vx,gauss);
		vy_input->GetInputValue(&vy,gauss);
		vz_input->GetInputValue(&vz,gauss);
		vvalues[i*NDOF3+0]=vx;
		vvalues[i*NDOF3+1]=vy;
		vvalues[i*NDOF3+2]=vz;
	}
	for(int i=0;i<pnumnodes;i++){
		gauss->GaussNode(this->PressureInterpolation(),i);
		p_input ->GetInputValue(&p ,gauss);
		pvalues[i]=p/FSreconditioning;
	}

	/*Add value to global vector*/
	solution->SetValues(vnumdof,vdoflist,vvalues,INS_VAL);
	solution->SetValues(pnumdof,pdoflist,pvalues,INS_VAL);

	/*Free ressources:*/
	delete gauss;
	xDelete<int>(pdoflist);
	xDelete<int>(vdoflist);
	xDelete<IssmDouble>(pvalues);
	xDelete<IssmDouble>(vvalues);
}
/*}}}*/
/*FUNCTION Penta::GetL1L2Viscosity{{{*/
void Penta::GetL1L2Viscosity(IssmDouble* pviscosity,IssmDouble* xyz_list, GaussPenta* gauss, Input* vx_input, Input* vy_input,Input* surface_input){
	/*Compute the L1L2 viscosity
	 *
	 *      1
	 * mu = - A^-1 (sigma'_e)^(1-n)
	 *      2
	 *
	 * sigma'_e^2 = |sigma'_//|^2 + |sigma'_perp|^2 (see Perego 2012 eq. 17,18)
	 *
	 * L1L2 assumptions:
	 *
	 * (1) |eps_b|_// = A (|sigma'_//|^2 + |sigma'_perp|^2)^((n-1)/2) |sigma'_//|
	 * (2) |sigma'_perp|^2 = |rho g (s-z) grad(s)|^2
	 *
	 * Assuming that n = 3, we have a polynom of degree 3 to solve (the only unkown is X=|sigma'_//|)
	 *
	 * A X^3 + A |rho g (s-z) grad(s)|^2 X - |eps_b|_// = 0     */

	int        i;
	IssmDouble z,s,viscosity,p,q,delta;
	IssmDouble tau_perp,tau_par,eps_b,A;
	IssmDouble epsilonvx[5]; /*exx eyy exy exz eyz*/
	IssmDouble epsilonvy[5]; /*exx eyy exy exz eyz*/
	IssmDouble epsilon[5];   /*exx eyy exy exz eyz*/
	IssmDouble z_list[NUMVERTICES];
	IssmDouble slope[3];

	/*Check that both inputs have been found*/
	if (!vx_input || !vy_input || !surface_input) _error_("Input missing");

	/*Get tau_perp*/
	for(i=0;i<NUMVERTICES;i++) z_list[i]=xyz_list[3*i+2];
	surface_input->GetInputValue(&s,gauss);
	surface_input->GetInputDerivativeValue(&slope[0],xyz_list,gauss);
	PentaRef::GetInputValue(&z,&z_list[0],gauss);
	tau_perp = matpar->GetRhoIce() * matpar->GetG() * fabs(s-z)*sqrt(slope[0]*slope[0]+slope[1]*slope[1]);

	/* Get eps_b*/
	vx_input->GetVxStrainRate3dHO(epsilonvx,xyz_list,gauss);
	vy_input->GetVyStrainRate3dHO(epsilonvy,xyz_list,gauss);
	for(i=0;i<5;i++) epsilon[i]=epsilonvx[i]+epsilonvy[i];
	eps_b = sqrt(epsilon[0]*epsilon[0] + epsilon[1]*epsilon[1] + epsilon[0]*epsilon[1] + epsilon[2]*epsilon[2]);
	if(eps_b==0.){
		*pviscosity = 2.5e+17;
		return;
	}

	/*Get A*/
	_assert_(material->GetN()==3.0);
	A=material->GetA();

	/*Solve for tau_perp (http://fr.wikipedia.org/wiki/Méthode_de_Cardan)*/
	p     = tau_perp *tau_perp;
	q     = - eps_b/A;
	delta = q *q + p*p*p*4./27.;
	_assert_(delta>0);
	tau_par = pow(0.5*(-q+sqrt(delta)),1./3.) - pow(0.5*(q+sqrt(delta)),1./3.);

	/*Viscosity*/
	viscosity = 1./(2.*A*(tau_par*tau_par + tau_perp*tau_perp));
	_assert_(!xIsNan(viscosity));
	_assert_(viscosity > 0.);

	/*Assign output pointer*/
	*pviscosity = viscosity;
	return; 
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionStressbalanceHoriz {{{*/
void  Penta::InputUpdateFromSolutionStressbalanceHoriz(IssmDouble* solution){

	int  approximation;

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

	/*SSA, everything is done by the element on bed*/
	if (approximation==SSAApproximationEnum){
		if (!IsOnBed()){
			/*Do nothing. Element on bed will take care of it*/
			return;
		}
		else{
			InputUpdateFromSolutionStressbalanceSSA(solution);
			return;
		}
	}
	if (approximation==L1L2ApproximationEnum){
		if (!IsOnBed()) return;
		InputUpdateFromSolutionStressbalanceL1L2(solution);
		return;
	}
	else if (approximation==HOApproximationEnum){
		InputUpdateFromSolutionStressbalanceHO(solution);
	}
	else if (approximation==HOFSApproximationEnum){
		InputUpdateFromSolutionStressbalanceHOFS(solution);
	}
	else if (approximation==SSAFSApproximationEnum){
		InputUpdateFromSolutionStressbalanceSSAFS(solution);
	}
	else if (approximation==FSApproximationEnum || approximation==NoneApproximationEnum){
		InputUpdateFromSolutionStressbalanceFS(solution);
	}
	else if (approximation==SSAHOApproximationEnum){
		InputUpdateFromSolutionStressbalanceSSAHO(solution);
	}
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionStressbalanceSSA {{{*/
void  Penta::InputUpdateFromSolutionStressbalanceSSA(IssmDouble* solution){

	int         numnodes = this->NumberofNodes();
	int         numdof   = NDOF2*numnodes;

	int         i;
	IssmDouble  rho_ice,g;
	IssmDouble  values[2*NUMVERTICES];
	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,SSAApproximationEnum,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])) _error_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vy[i])) _error_("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->vertices,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 PentaInput(VxEnum,vx,P1Enum));
		penta->inputs->AddInput(new PentaInput(VyEnum,vy,P1Enum));
		penta->inputs->AddInput(new PentaInput(VelEnum,vel,P1Enum));
		penta->inputs->AddInput(new PentaInput(PressureEnum,pressure,P1Enum));

		/*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::InputUpdateFromSolutionStressbalanceSSAHO {{{*/
void  Penta::InputUpdateFromSolutionStressbalanceSSAHO(IssmDouble* solution){

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

	int     i;
	IssmDouble  rho_ice,g;
	IssmDouble  SSA_values[numdof];
	IssmDouble  HO_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 HO 
	 * and results from the penta at base for SSA. Now recover results*/
	penta=GetBasalElement();

	/*Get dof listof this element (HO dofs) and of the penta at base (SSA dofs): */
	GetDofList(&doflistp,HOApproximationEnum,GsetEnum);
	penta->GetDofList(&doflistm,SSAApproximationEnum,GsetEnum);

	/*Get node data: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);

	/*Use the dof list to index into the solution vector: */
	for(i=0;i<numdof2d;i++){
		HO_values[i]=solution[doflistp[i]];
		SSA_values[i]=solution[doflistm[i]];
	}
	for(i=numdof2d;i<numdof;i++){
		HO_values[i]=solution[doflistp[i]];
		SSA_values[i]=SSA_values[i-numdof2d];
	}

	/*Transform solution in Cartesian Space*/
	TransformSolutionCoord(&SSA_values[0],penta->nodes,NUMVERTICES,XYEnum);
	TransformSolutionCoord(&HO_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]=SSA_values[i*NDOF2+0]+HO_values[i*NDOF2+0];
		vy[i]=SSA_values[i*NDOF2+1]+HO_values[i*NDOF2+1];

		/*Check solution*/
		if(xIsNan<IssmDouble>(vx[i])) _error_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vy[i])) _error_("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 PentaInput(VxEnum,vx,P1Enum));
	this->inputs->AddInput(new PentaInput(VyEnum,vy,P1Enum));
	this->inputs->AddInput(new PentaInput(VelEnum,vel,P1Enum));
	this->inputs->AddInput(new PentaInput(PressureEnum,pressure,P1Enum));

	/*Free ressources:*/
	xDelete<int>(doflistp);
	xDelete<int>(doflistm);
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionStressbalanceSSAFS {{{*/
void  Penta::InputUpdateFromSolutionStressbalanceSSAFS(IssmDouble* solution){

	const int    numdofm=NDOF2*NUMVERTICES;
	const int    numdofs=NDOF3*NUMVERTICES;
	const int    numdof2d=NDOF2*NUMVERTICES2D;
	const int    numdofpressure=NDOF1*NUMVERTICES;

	int     i;
	IssmDouble  FSreconditioning;
	IssmDouble  SSA_values[numdofm];
	IssmDouble  FS_values[numdofs];
	IssmDouble  Pressure_values[numdofs];
	IssmDouble  vx[NUMVERTICES];
	IssmDouble  vy[NUMVERTICES];
	IssmDouble  vz[NUMVERTICES];
	IssmDouble  vzSSA[NUMVERTICES];
	IssmDouble  vzFS[NUMVERTICES];
	IssmDouble  vel[NUMVERTICES];
	IssmDouble  pressure[NUMVERTICES];
	IssmDouble  xyz_list[NUMVERTICES][3];
	int*    doflistm        = NULL;
	int*    doflists        = NULL;
	int*    doflistpressure = NULL;
	Penta   *penta          = NULL;

	/*OK, we have to add results of this element for SSA 
	 * and results from the penta at base for SSA. Now recover results*/
	penta=GetBasalElement();

	/*Get dof listof this element (SSA dofs) and of the penta at base (SSA dofs): */
	penta->GetDofList(&doflistm,SSAApproximationEnum,GsetEnum);
	GetDofList(&doflists,FSvelocityEnum,GsetEnum);
	GetDofListPressure(&doflistpressure,GsetEnum);
	this->parameters->FindParam(&FSreconditioning,StressbalanceFSreconditioningEnum);

	/*Get node data: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);

	/*Use the dof list to index into the solution vector: */
	for(i=0;i<numdof2d;i++){
		SSA_values[i]=solution[doflistm[i]];
		SSA_values[i+numdof2d]=solution[doflistm[i]];
	}
	for(i=0;i<numdofs;i++)FS_values[i]=solution[doflists[i]];
	for(i=0;i<numdofpressure;i++) Pressure_values[i]=solution[doflistpressure[i]];

	/*Transform solution in Cartesian Space*/
	TransformSolutionCoord(&SSA_values[0],this->nodes,NUMVERTICES,XYEnum);
	TransformSolutionCoord(&FS_values[0],this->nodes,NUMVERTICES,XYZEnum);

	/*Ok, we have vx and vy in values, fill in vx and vy arrays: */
	for(i=0;i<NUMVERTICES;i++){
		vx[i]=FS_values[i*NDOF3+0]+SSA_values[i*NDOF2+0];
		vy[i]=FS_values[i*NDOF3+1]+SSA_values[i*NDOF2+1];
		vzFS[i]=FS_values[i*NDOF3+2];
		pressure[i]=Pressure_values[i*NDOF1]*FSreconditioning;

		/*Check solution*/
		if(xIsNan<IssmDouble>(vx[i]))       _error_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vy[i]))       _error_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vzFS[i])) _error_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(pressure[i])) _error_("NaN found in solution vector");
	}

	/*Get Vz*/
	Input* vzSSA_input=inputs->GetInput(VzSSAEnum);
	if (vzSSA_input){
		if (vzSSA_input->ObjectEnum()!=PentaInputEnum){
			_error_("Cannot compute Vel as VzSSA is of type " << EnumToStringx(vzSSA_input->ObjectEnum()));
		}
		GetInputListOnVertices(&vzSSA[0],VzSSAEnum);
	}
	else{
		_error_("Cannot update solution as VzSSA is not present");
	}

	/*Now Compute vel*/
	for(i=0;i<NUMVERTICES;i++) {
		vz[i]=vzSSA[i]+vzFS[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 PentaInput(VxEnum,vx,P1Enum));
	this->inputs->AddInput(new PentaInput(VyEnum,vy,P1Enum));
	this->inputs->AddInput(new PentaInput(VzEnum,vz,P1Enum));
	this->inputs->AddInput(new PentaInput(VzFSEnum,vzFS,P1Enum));
	this->inputs->AddInput(new PentaInput(VelEnum,vel,P1Enum));
	this->inputs->AddInput(new PentaInput(PressureEnum,pressure,P1Enum));

	/*Free ressources:*/
	xDelete<int>(doflistm);
	xDelete<int>(doflists);
	xDelete<int>(doflistpressure);
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionStressbalanceL1L2 {{{*/
void  Penta::InputUpdateFromSolutionStressbalanceL1L2(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,L1L2ApproximationEnum,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])) _error_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vy[i])) _error_("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->vertices,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 PentaInput(VxEnum,vx,P1Enum));
		penta->inputs->AddInput(new PentaInput(VyEnum,vy,P1Enum));
		penta->inputs->AddInput(new PentaInput(VelEnum,vel,P1Enum));
		penta->inputs->AddInput(new PentaInput(PressureEnum,pressure,P1Enum));

		/*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::InputUpdateFromSolutionStressbalanceHO {{{*/
void  Penta::InputUpdateFromSolutionStressbalanceHO(IssmDouble* solution){

	int         i;
	IssmDouble  rho_ice,g;
	IssmDouble  xyz_list[NUMVERTICES][3];
	int        *doflist = NULL;

	/*Fetch number of nodes and dof for this finite element*/
	int numnodes = this->NumberofNodes();
	int numdof   = numnodes*NDOF2;

	/*Fetch dof list and allocate solution vectors*/
	GetDofList(&doflist,HOApproximationEnum,GsetEnum);
	IssmDouble* values    = xNew<IssmDouble>(numdof);
	IssmDouble* vx        = xNew<IssmDouble>(numnodes);
	IssmDouble* vy        = xNew<IssmDouble>(numnodes);
	IssmDouble* vz        = xNew<IssmDouble>(numnodes);
	IssmDouble* vel       = xNew<IssmDouble>(numnodes);
	IssmDouble* pressure  = xNew<IssmDouble>(numnodes);
	IssmDouble* surface   = xNew<IssmDouble>(numnodes);

	/*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);
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);

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

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

	/*Get Vz and compute vel*/
	GetInputListOnNodes(&vz[0],VzEnum,0.);
	for(i=0;i<numnodes;i++) vel[i]=sqrt(vx[i]*vx[i] + vy[i]*vy[i] + vz[i]*vz[i]);

	/*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 PentaInput(VxEnum,vx,P1Enum));
	this->inputs->AddInput(new PentaInput(VyEnum,vy,P1Enum));
	this->inputs->AddInput(new PentaInput(VelEnum,vel,P1Enum));
	this->inputs->AddInput(new PentaInput(PressureEnum,pressure,P1Enum));

	/*Free ressources:*/
	xDelete<IssmDouble>(surface);
	xDelete<IssmDouble>(pressure);
	xDelete<IssmDouble>(vel);
	xDelete<IssmDouble>(vz);
	xDelete<IssmDouble>(vy);
	xDelete<IssmDouble>(vx);
	xDelete<IssmDouble>(values);
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionStressbalanceHOFS {{{*/
void  Penta::InputUpdateFromSolutionStressbalanceHOFS(IssmDouble* solution){

	const int    numdofp=NDOF2*NUMVERTICES;
	const int    numdofs=NDOF3*NUMVERTICES;
	const int    numdofpressure=NDOF1*NUMVERTICES;

	int        i;
	IssmDouble HO_values[numdofp];
	IssmDouble FS_values[numdofs];
	IssmDouble Pressure_values[numdofpressure];
	IssmDouble vx[NUMVERTICES];
	IssmDouble vy[NUMVERTICES];
	IssmDouble vz[NUMVERTICES];
	IssmDouble vzHO[NUMVERTICES];
	IssmDouble vzFS[NUMVERTICES];
	IssmDouble vel[NUMVERTICES];
	IssmDouble pressure[NUMVERTICES];
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble FSreconditioning;
	int*       doflistp        = NULL;
	int*       doflists        = NULL;
	int*       doflistpressure = NULL;
	Penta      *penta          = NULL;

	/*OK, we have to add results of this element for HO 
	 * and results from the penta at base for SSA. Now recover results*/
	penta=GetBasalElement();

	/*Get dof listof this element (HO dofs) and of the penta at base (SSA dofs): */
	GetDofList(&doflistp,HOApproximationEnum,GsetEnum);
	GetDofList(&doflists,FSvelocityEnum,GsetEnum);
	GetDofListPressure(&doflistpressure,GsetEnum);
	this->parameters->FindParam(&FSreconditioning,StressbalanceFSreconditioningEnum);

	/*Get node data: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);

	/*Use the dof list to index into the solution vector: */
	for(i=0;i<numdofp;i++) HO_values[i]=solution[doflistp[i]];
	for(i=0;i<numdofs;i++) FS_values[i]=solution[doflists[i]];
	for(i=0;i<numdofpressure;i++) Pressure_values[i]=solution[doflistpressure[i]];

	/*Transform solution in Cartesian Space*/
	TransformSolutionCoord(&HO_values[0],this->nodes,NUMVERTICES,XYEnum);
	TransformSolutionCoord(&FS_values[0],this->nodes,NUMVERTICES,XYZEnum);

	/*Ok, we have vx and vy in values, fill in vx and vy arrays: */
	for(i=0;i<NUMVERTICES;i++){
		vx[i]=FS_values[i*NDOF3+0]+HO_values[i*NDOF2+0];
		vy[i]=FS_values[i*NDOF3+1]+HO_values[i*NDOF2+1];
		vzFS[i]=FS_values[i*NDOF3+2];
		pressure[i]=Pressure_values[i*NDOF1]*FSreconditioning;

		/*Check solution*/
		if(xIsNan<IssmDouble>(vx[i]))       _error_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vy[i]))       _error_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vzFS[i])) _error_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(pressure[i])) _error_("NaN found in solution vector");
	}

	/*Get Vz*/
	Input* vzHO_input=inputs->GetInput(VzHOEnum);
	if (vzHO_input){
		if (vzHO_input->ObjectEnum()!=PentaInputEnum){
			_error_("Cannot compute Vel as VzHO is of type " << EnumToStringx(vzHO_input->ObjectEnum()));
		}
		GetInputListOnVertices(&vzHO[0],VzHOEnum);
	}
	else{
		_error_("Cannot update solution as VzHO is not present");
	}

	/*Now Compute vel*/
	for(i=0;i<NUMVERTICES;i++) {
		vz[i]=vzHO[i]+vzFS[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 PentaInput(VxEnum,vx,P1Enum));
	this->inputs->AddInput(new PentaInput(VyEnum,vy,P1Enum));
	this->inputs->AddInput(new PentaInput(VzEnum,vz,P1Enum));
	this->inputs->AddInput(new PentaInput(VzFSEnum,vzFS,P1Enum));
	this->inputs->AddInput(new PentaInput(VelEnum,vel,P1Enum));
	this->inputs->AddInput(new PentaInput(PressureEnum,pressure,P1Enum));

	/*Free ressources:*/
	xDelete<int>(doflistp);
	xDelete<int>(doflists);
	xDelete<int>(doflistpressure);
}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionStressbalanceSIA {{{*/
void  Penta::InputUpdateFromSolutionStressbalanceSIA(IssmDouble* solution){

	int         numnodes = this->NumberofNodes();
	int         numdof=NDOF2*numnodes;

	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],vertices,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])) _error_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vy[i])) _error_("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 PentaInput(VxEnum,vx,P1Enum));
	this->inputs->AddInput(new PentaInput(VyEnum,vy,P1Enum));
	this->inputs->AddInput(new PentaInput(VelEnum,vel,P1Enum));
	this->inputs->AddInput(new PentaInput(PressureEnum,pressure,P1Enum));

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

	int          numnodes = this->NumberofNodes();
	int          numdof=NDOF1*numnodes;

	int          i;
	int          approximation;
	IssmDouble   rho_ice,g;
	IssmDouble   values[numdof];
	IssmDouble   vx[NUMVERTICES];
	IssmDouble   vy[NUMVERTICES];
	IssmDouble   vz[NUMVERTICES];
	IssmDouble   vzSSA[NUMVERTICES];
	IssmDouble   vzHO[NUMVERTICES];
	IssmDouble   vzFS[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 FS or None*/
	inputs->GetInputValue(&approximation,ApproximationEnum);
	if(approximation==FSApproximationEnum || approximation==NoneApproximationEnum){
		return;
	}

	/*Get dof list and vertices coordinates: */
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);
	GetVerticesCoordinates(&xyz_list[0][0],vertices,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])) _error_("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 HOFS or SSAFS element*/
	if(approximation==HOFSApproximationEnum){
		Input* vzFS_input=inputs->GetInput(VzFSEnum);
		if (vzFS_input){
			if (vzFS_input->ObjectEnum()!=PentaInputEnum) _error_("Cannot compute Vel as VzFS is of type " << EnumToStringx(vzFS_input->ObjectEnum()));
			GetInputListOnVertices(&vzFS[0],VzFSEnum);
		}
		else _error_("Cannot compute Vz as VzFS in not present in HOFS element");
		for(i=0;i<NUMVERTICES;i++){
			vzHO[i]=vz[i];
			vz[i]=vzHO[i]+vzFS[i];
		}
	}
	else if(approximation==SSAFSApproximationEnum){
		Input* vzFS_input=inputs->GetInput(VzFSEnum);
		if (vzFS_input){
			if (vzFS_input->ObjectEnum()!=PentaInputEnum) _error_("Cannot compute Vel as VzFS is of type " << EnumToStringx(vzFS_input->ObjectEnum()));
			GetInputListOnVertices(&vzFS[0],VzFSEnum);
		}
		else _error_("Cannot compute Vz as VzFS in not present in SSAFS element");
		for(i=0;i<NUMVERTICES;i++){
			vzSSA[i]=vz[i];
			vz[i]=vzSSA[i]+vzFS[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 HOFS element */
	if(approximation!=HOFSApproximationEnum &&  approximation!=SSAFSApproximationEnum){
		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!=HOFSApproximationEnum && approximation!=SSAFSApproximationEnum){
		this->inputs->ChangeEnum(PressureEnum,PressurePicardEnum);
		this->inputs->AddInput(new PentaInput(PressureEnum,pressure,P1Enum));
	}
	else if(approximation==HOFSApproximationEnum){
		this->inputs->AddInput(new PentaInput(VzHOEnum,vzHO,P1Enum));
	}
	else if(approximation==SSAFSApproximationEnum){
		this->inputs->AddInput(new PentaInput(VzSSAEnum,vzSSA,P1Enum));
	}
	this->inputs->AddInput(new PentaInput(VzEnum,vz,P1Enum));
	this->inputs->AddInput(new PentaInput(VelEnum,vel,P1Enum));

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

	int          i;
	int*         vdoflist=NULL;
	int*         pdoflist=NULL;
	IssmDouble   FSreconditioning;
	GaussPenta  *gauss;

	/*Fetch number of nodes and dof for this finite element*/
	int vnumnodes = this->NumberofNodesVelocity();
	int pnumnodes = this->NumberofNodesPressure();
	int vnumdof   = vnumnodes*NDOF3;
	int pnumdof   = pnumnodes*NDOF1;

	/*Initialize values*/
	IssmDouble* vvalues  = xNew<IssmDouble>(vnumdof);
	IssmDouble* pvalues  = xNew<IssmDouble>(pnumdof);
	IssmDouble* vx       = xNew<IssmDouble>(vnumnodes);
	IssmDouble* vy       = xNew<IssmDouble>(vnumnodes);
	IssmDouble* vz       = xNew<IssmDouble>(vnumnodes);
	IssmDouble* vel      = xNew<IssmDouble>(vnumnodes);
	IssmDouble* pressure = xNew<IssmDouble>(vnumnodes);

	/*Get dof list: */
	GetDofListVelocity(&vdoflist,GsetEnum);
	GetDofListPressure(&pdoflist,GsetEnum);

	/*Use the dof list to index into the solution vector: */
	for(i=0;i<vnumdof;i++) vvalues[i]=solution[vdoflist[i]];
	for(i=0;i<pnumdof;i++) pvalues[i]=solution[pdoflist[i]];

	/*Transform solution in Cartesian Space*/
	TransformSolutionCoord(&vvalues[0],nodes,vnumnodes,XYZEnum);

	/*Ok, we have vx and vy in values, fill in all arrays: */
	for(i=0;i<vnumnodes;i++){
		vx[i] = vvalues[i*NDOF3+0];
		vy[i] = vvalues[i*NDOF3+1];
		vz[i] = vvalues[i*NDOF3+2];
		if(xIsNan<IssmDouble>(vx[i])) _error_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vy[i])) _error_("NaN found in solution vector");
		if(xIsNan<IssmDouble>(vz[i])) _error_("NaN found in solution vector");
	}
	for(i=0;i<pnumnodes;i++){
		pressure[i] = pvalues[i];
		if(xIsNan<IssmDouble>(pressure[i])) _error_("NaN found in solution vector");
	}

	/*Recondition pressure and compute vel: */
	this->parameters->FindParam(&FSreconditioning,StressbalanceFSreconditioningEnum);
	for(i=0;i<pnumnodes;i++) pressure[i]=pressure[i]*FSreconditioning;
	for(i=0;i<vnumnodes;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 PentaInput(VxEnum,vx,P1Enum));
	this->inputs->AddInput(new PentaInput(VyEnum,vy,P1Enum));
	this->inputs->AddInput(new PentaInput(VzEnum,vz,P1Enum));
	this->inputs->AddInput(new PentaInput(VelEnum,vel,P1Enum));
	this->inputs->AddInput(new PentaInput(PressureEnum,pressure,P1Enum));

	/*Free ressources:*/
	xDelete<IssmDouble>(pressure);
	xDelete<IssmDouble>(vel);
	xDelete<IssmDouble>(vz);
	xDelete<IssmDouble>(vy);
	xDelete<IssmDouble>(vx);
	xDelete<IssmDouble>(vvalues);
	xDelete<IssmDouble>(pvalues);
	xDelete<int>(vdoflist);
	xDelete<int>(pdoflist);
}
/*}}}*/
#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); //lower face is 0, upper face is 1.
	ElementMatrix* Ke=tria->CreateKMatrixBalancethickness();
	delete tria->material; 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); //lower face is 0, upper face is 1.
	ElementVector* pe=tria->CreatePVectorBalancethickness();
	delete tria->material; delete tria;

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

	/*Clean up and return*/
	return pe;
}
/*}}}*/
#endif

#ifdef _HAVE_HYDROLOGY_
/*FUNCTION Penta::CreateKMatrixHydrologyDCInefficient {{{*/
ElementMatrix* Penta::CreateKMatrixHydrologyDCInefficient(void){

	if (!IsOnBed()) return NULL;

	Tria* tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
	ElementMatrix* Ke=tria->CreateKMatrixHydrologyDCInefficient();
	delete tria->material; delete tria;

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

	if (!IsOnBed()) return NULL;

	Tria* tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
	ElementMatrix* Ke=tria->CreateKMatrixHydrologyDCEfficient();
	delete tria->material; delete tria;

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

	if (!IsOnBed()) return NULL;

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
	ElementVector* pe=tria->CreatePVectorHydrologyDCInefficient();
	delete tria->material; delete tria;

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

	if (!IsOnBed()) return NULL;

	/*Call Tria function*/
	Tria* tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
	ElementVector* pe=tria->CreatePVectorHydrologyDCEfficient();
	delete tria->material; delete tria;

	/*Clean up and return*/
	return pe;
}
/*}}}*/
/*FUNCTION Penta::GetHydrologyDCInefficientHmax{{{*/
void  Penta::GetHydrologyDCInefficientHmax(IssmDouble* ph_max, Node* innode){

	if (!IsOnBed()) return;

	Tria* tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
	tria->GetHydrologyDCInefficientHmax(ph_max,innode);
	delete tria->material; delete tria;
}
/*}}}*/
/*FUNCTION Penta::GetHydrologyTransfer{{{*/
void  Penta::GetHydrologyTransfer(Vector<IssmDouble>* transfer){

	if (!IsOnBed()) return;

	Tria* tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
	tria->GetHydrologyTransfer(transfer);
	delete tria->material; delete tria;
}
/*}}}*/
/*FUNCTION Penta::GetSolutionFromInputsOneDof {{{*/
void Penta::GetSolutionFromInputsOneDof(Vector<IssmDouble>* solution, int enum_type){

	const int    numdof=NDOF1*NUMVERTICES;

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

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

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

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

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

	if (!IsOnBed())return;

	Tria* tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
	tria->HydrologyEPLGetActive(active_vec);
	delete tria->material; delete tria;

}
/*}}}*/
/*FUNCTION Penta::HydrologyEPLGetMask{{{*/
void  Penta::HydrologyEPLGetMask(Vector<IssmDouble>* vec_mask){

	if (!IsOnBed())return;

	Tria* tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.
	tria->HydrologyEPLGetMask(vec_mask);
	delete tria->material; delete tria;

}
/*}}}*/
/*FUNCTION Penta::InputUpdateFromSolutionHydrologyDCInefficient{{{*/
void  Penta::InputUpdateFromSolutionHydrologyDCInefficient(IssmDouble* solution){

	const int   numdof   = NDOF1*NUMVERTICES;
	const int   numdof2d = NDOF1*NUMVERTICES2D;
	int*        doflist  = NULL;
	bool        converged;
	IssmDouble  values[numdof];
	IssmDouble  residual[numdof];
	IssmDouble  penalty_factor;
	IssmDouble  kmax, kappa, h_max;
	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])) _error_("NaN found in solution vector");
	}

	/*If converged keep the residual in mind*/
	this->inputs->GetInputValue(&converged,ConvergedEnum);

	/*Get inputs*/
	if(converged){
		this->parameters->FindParam(&kmax,HydrologySedimentKmaxEnum);
		this->parameters->FindParam(&penalty_factor,HydrologydcPenaltyFactorEnum);

		kappa=kmax*pow(10.,penalty_factor);

		Tria* tria=(Tria*)SpawnTria(0); //lower face is 0, upper face is 1.	
		for(int i=0;i<NUMVERTICES2D;i++){
			tria->GetHydrologyDCInefficientHmax(&h_max,nodes[i]);
			if(values[i]>h_max){
				residual[i]=kappa*(values[i]-h_max);
				residual[i+numdof2d]=residual[i];
			}
			else{
				residual[i]=0.0;
				residual[i+numdof2d]=residual[i];
			}
		}
		delete tria->material; delete tria;
	}

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

		/*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);
}
/*}}}*/
#endif

#ifdef _HAVE_GROUNDINGLINE_
/*FUNCTION Penta::MigrateGroundingLine{{{*/
void  Penta::MigrateGroundingLine(IssmDouble* phi_ungrounding){

	int        i,migration_style;
	bool       oldfloating;
	bool       floatingelement = false;
	bool       groundedelement = false;
	IssmDouble bed_hydro,yts,gl_melting_rate;
	IssmDouble rho_water,rho_ice,density;
	IssmDouble h[NUMVERTICES],s[NUMVERTICES],b[NUMVERTICES],r[NUMVERTICES];
	IssmDouble melting[NUMVERTICES],phi[NUMVERTICES];
	bool       grounded[NUMVERTICES],floating[NUMVERTICES];

	if(!IsOnBed()) return;

	/*Recover info at the vertices: */
	parameters->FindParam(&migration_style,GroundinglineMigrationEnum);
	parameters->FindParam(&gl_melting_rate,GroundinglineMeltingRateEnum);
	parameters->FindParam(&yts,ConstantsYtsEnum);
	GetInputListOnVertices(&h[0],ThicknessEnum);
	GetInputListOnVertices(&s[0],SurfaceEnum);
	GetInputListOnVertices(&b[0],BedEnum);
	GetInputListOnVertices(&r[0],BathymetryEnum);
	GetInputListOnVertices(&phi[0],MaskGroundediceLevelsetEnum);
	rho_water   = matpar->GetRhoWater();
	rho_ice     = matpar->GetRhoIce();
	density     = rho_ice/rho_water;
	oldfloating = this->IsFloating(); //figure out if the element is floating before starting to change everything

	/*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(phi[i]<=0){
			if(b[i]<=r[i]){ 
				b[i]        = r[i];
				s[i]        = b[i]+h[i];
				floating[i] = false;
				grounded[i] = 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>r[i]){
				/*Unground only if the element is connected to the ice shelf*/
				if(migration_style==AgressiveMigrationEnum || migration_style==SubelementMigrationEnum || migration_style==SubelementMigration2Enum){
					s[i]        = (1-density)*h[i];
					b[i]        = -density*h[i];
					floating[i] = true;
					grounded[i] = false;
				}
				else if(migration_style==SoftMigrationEnum && phi_ungrounding[vertices[i]->Pid()]<0.){
					s[i]        = (1-density)*h[i];
					b[i]        = -density*h[i];
					floating[i] = true;
					grounded[i] = false;
				}
				else{
					if(migration_style!=SoftMigrationEnum) _error_("Error: migration should be Aggressive, Soft or Subelement");
				}
			}
		}
	}

	/*Recalculate phi*/
	for(i=0;i<NUMVERTICES;i++){
		if(migration_style==SoftMigrationEnum){
			bed_hydro=-density*h[i];
			if(phi[i]<0. || bed_hydro<=r[i] || phi_ungrounding[vertices[i]->Pid()]<0.){
				phi[i]=h[i]+r[i]/density;
			}
		}
		else phi[i]=h[i]+r[i]/density;
	}
	this->inputs->AddInput(new PentaInput(MaskGroundediceLevelsetEnum,&phi[0],P1Enum));
	this->InputExtrude(MaskGroundediceLevelsetEnum,ElementEnum);

	/*SubelementMigrationEnum: if one grounded, all grounded*/
	if(migration_style==SubelementMigrationEnum || migration_style==SubelementMigration2Enum){
		for(i=0;i<NUMVERTICES;i++){
			if(phi[i]>0.){
				groundedelement=true;
				break;
			}
		}
		floatingelement=!groundedelement;
	}
	else{
		for(i=0;i<NUMVERTICES;i++){
			if(phi[i]<=0.){
				floatingelement=true;
				break;
			}
		}
	}

   /*Add basal melting rate if element just ungrounded*/
	if(!oldfloating && floatingelement==true){
		for(i=0;i<NUMVERTICES;i++)melting[i]=gl_melting_rate/yts;
		this->inputs->AddInput(new PentaInput(BasalforcingsMeltingRateEnum,&melting[0],P1Enum));
	} 

	/*Update inputs*/
	this->inputs->AddInput(new PentaInput(SurfaceEnum,&s[0],P1Enum));
	this->inputs->AddInput(new PentaInput(BedEnum,&b[0],P1Enum));

	/*Extrude inputs*/
	this->InputExtrude(SurfaceEnum,ElementEnum);
	this->InputExtrude(BedEnum,ElementEnum);
}
/*}}}*/
/*FUNCTION Penta::PotentialUngrounding{{{*/
void  Penta::PotentialUngrounding(Vector<IssmDouble>* potential_ungrounding){

	IssmDouble  h[NUMVERTICES],r[NUMVERTICES],gl[NUMVERTICES];
	IssmDouble  bed_hydro;
	IssmDouble  rho_water,rho_ice,density;

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

	/*go through vertices, and figure out which ones are on the ice sheet, and want to unground: */
	for(int i=0;i<NUMVERTICES;i++){
		/*Find if grounded vertices want to start floating*/
		if (gl[i]>0.){
			bed_hydro=-density*h[i];
			if(bed_hydro>r[i]){
				/*Vertex that could potentially unground, flag it*/
				potential_ungrounding->SetValue(vertices[i]->Pid(),1,INS_VAL);
			}
		}
	}
}
/*}}}*/
/*FUNCTION Penta::UpdatePotentialUngrounding{{{*/
int Penta::UpdatePotentialUngrounding(IssmDouble* vertices_potentially_ungrounding,Vector<IssmDouble>* vec_nodes_on_iceshelf,IssmDouble* nodes_on_iceshelf){

	int i;
	int nflipped=0;

	/*Go through nodes, and whoever is on the potential_ungrounding, ends up in nodes_on_iceshelf: */
	for(i=0;i<NUMVERTICES;i++){
		if (reCast<bool,IssmDouble>(vertices_potentially_ungrounding[vertices[i]->Pid()])){
			vec_nodes_on_iceshelf->SetValue(vertices[i]->Pid(),-1.,INS_VAL);

			/*If node was not on ice shelf, we flipped*/
			if(nodes_on_iceshelf[vertices[i]->Pid()]>=0.){
				nflipped++;
			}
		}
	}
	return nflipped;
}
/*}}}*/
#endif
