/*!\file Tria.cpp
 * \brief: implementation of the Tria object
 */
/*Headers:*/
/*{{{*/
#ifdef HAVE_CONFIG_H
	#include <config.h>
#else
#error "Cannot compile with HAVE_CONFIG_H symbol! run configure first!"
#endif

#include <stdio.h>
#include <string.h>
#include "../classes.h"
#include "../../shared/shared.h"
#ifdef _HAVE_GIA_
#include "../../modules/GiaDeflectionCorex/GiaDeflectionCorex.h"
#endif
/*}}}*/

/*Element macros*/
#define NUMVERTICES 3
/*Constructors/destructor/copy*/
/*FUNCTION Tria::Tria(){{{*/
Tria::Tria(){

	int i;

	this->nodes    = NULL;
	this->vertices = NULL;
	this->material = NULL;
	this->matpar   = NULL;
	this->inputs     = NULL;
	this->parameters = NULL;
	this->results    = NULL;

}
/*}}}*/
/*FUNCTION Tria::Tria(int id, int sid,int index, IoModel* iomodel,int nummodels){{{*/
Tria::Tria(int tria_id, int tria_sid, int index, IoModel* iomodel,int nummodels)
	:TriaRef(nummodels)
	,ElementHook(nummodels,index+1,3,iomodel){

		/*id: */
		this->id=tria_id;
		this->sid=tria_sid;

		//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;

}
/*}}}*/
/*FUNCTION Tria::~Tria(){{{*/
Tria::~Tria(){
	delete inputs;
	delete results;
	this->parameters=NULL;
}
/*}}}*/
/*FUNCTION Tria::copy {{{*/
Object* Tria::copy() {

	int i;
	Tria* tria=NULL;

	tria=new Tria();

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

	//deal with ElementHook mother class
	tria->numanalyses=this->numanalyses;
	tria->hnodes=new Hook*[tria->numanalyses];
	for(i=0;i<tria->numanalyses;i++)tria->hnodes[i]=(Hook*)this->hnodes[i]->copy();
	tria->hvertices=(Hook*)this->hvertices->copy();
	tria->hmaterial=(Hook*)this->hmaterial->copy();
	tria->hmatpar=(Hook*)this->hmatpar->copy();

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

	/*recover objects: */
	tria->nodes=xNew<Node*>(3); //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<3;i++)tria->nodes[i]=this->nodes[i];
	tria->vertices=(Vertex**)tria->hvertices->deliverp();
	tria->material=(Material*)tria->hmaterial->delivers();
	tria->matpar=(Matpar*)tria->hmatpar->delivers();

	return tria;
}
/*}}}*/

/*Other*/
/*FUNCTION Tria::SetwiseNodeConnectivity{{{*/
void Tria::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");
			}
		}
	}

	/*Assign output pointers: */
	*pd_nz=d_nz;
	*po_nz=o_nz;
}
/*}}}*/
/*FUNCTION Tria::CreateKMatrix(Matrix<IssmDouble>* Kff, Matrix<IssmDouble>* Kfs) {{{*/
void  Tria::CreateKMatrix(Matrix<IssmDouble>* Kff, Matrix<IssmDouble>* Kfs){

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

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

	if(Ke){
		/*Static condensation if requested*/
		if(this->element_type==P1bubblecondensedEnum){
			int size   = nodes[3]->GetNumberOfDofs(NoneApproximationEnum,GsetEnum);
			int offset = 0;
			for(int i=0;i<3;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 Tria::CreateKMatrix(void) {{{*/
ElementMatrix* Tria::CreateKMatrix(void){

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

	/*Checks in debugging mode{{{*/
	_assert_(this->nodes && this->material && this->matpar && 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 CreateKMatrixStressbalanceSSA();
			break;
		case StressbalanceSIAAnalysisEnum:
			return CreateKMatrixStressbalanceSIA();
			break;
		 #endif
		case BedSlopeXAnalysisEnum: case SurfaceSlopeXAnalysisEnum: case BedSlopeYAnalysisEnum: case SurfaceSlopeYAnalysisEnum:
			return CreateMassMatrix();
			break;
		case MasstransportAnalysisEnum:
			return CreateKMatrixMasstransport();
			break;
		#ifdef _HAVE_HYDROLOGY_
		case HydrologyShreveAnalysisEnum:
			return CreateKMatrixHydrologyShreve();
			break;
		case HydrologyDCInefficientAnalysisEnum:
			return CreateKMatrixHydrologyDCInefficient();
			break;
		case HydrologyDCEfficientAnalysisEnum:
			return CreateKMatrixHydrologyDCEfficient();
			break;
		#endif
		#ifdef _HAVE_BALANCED_
		case BalancethicknessAnalysisEnum:
			return CreateKMatrixBalancethickness();
			break;
		case BalancevelocityAnalysisEnum:
			return CreateKMatrixBalancevelocity();
			break;
		case SmoothedSurfaceSlopeXAnalysisEnum: case SmoothedSurfaceSlopeYAnalysisEnum:
			return CreateKMatrixSmoothedSlope();
			break;
		#endif
		#ifdef _HAVE_CONTROL_
		case AdjointBalancethicknessAnalysisEnum:
			return CreateKMatrixAdjointBalancethickness();
			break;
		case AdjointHorizAnalysisEnum:
			return CreateKMatrixAdjointSSA();
			break;
		#endif
		default:
			_error_("analysis " << analysis_type << " (" << EnumToStringx(analysis_type) << ") not supported yet");
	}

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

	/* Intermediaries */
	IssmDouble  D,Jdet;
	IssmDouble  xyz_list[NUMVERTICES][3];

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

	/*Initialize Element matrix and vectors*/
	ElementMatrix* Ke    = new ElementMatrix(nodes,numnodes,this->parameters,NoneApproximationEnum);
	IssmDouble*    basis = xNew<IssmDouble>(numnodes);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);

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

		gauss->GaussPoint(ig);

		GetNodalFunctions(basis,gauss);
		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		D=gauss->weight*Jdet;

		TripleMultiply(basis,1,numnodes,1,
					&D,1,1,0,
					basis,1,numnodes,0,
					&Ke->values[0],1);
	}

	/*Clean up and return*/
	delete gauss;
	xDelete<IssmDouble>(basis);
	return Ke;
}
/*}}}*/
/*FUNCTION Tria::CreateDVector {{{*/
void  Tria::CreateDVector(Vector<IssmDouble>* df){

	/*Nothing done yet*/
}
/*}}}*/
/*FUNCTION Tria::CreatePVector(Vector<IssmDouble>* pf) {{{*/
void  Tria::CreatePVector(Vector<IssmDouble>* pf){

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

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

	/*Add to global Vector*/
	if(pe){
		/*Static condensation if requested*/
		if(this->element_type==P1bubblecondensedEnum){
			int size   = nodes[3]->GetNumberOfDofs(NoneApproximationEnum,GsetEnum);
			int offset = 0;
			for(int i=0;i<3;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;
		}

		pe->AddToGlobal(pf);
		delete pe;
	}
}
/*}}}*/
/*FUNCTION Tria::CreatePVector(void){{{*/
ElementVector* Tria::CreatePVector(void){

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

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

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

	/*Just branch to the correct load generator, according to the type of analysis we are carrying out: */
	switch(analysis_type){
#ifdef _HAVE_STRESSBALANCE_
		case StressbalanceAnalysisEnum:
			return CreatePVectorStressbalanceSSA();
			break;
		case StressbalanceSIAAnalysisEnum:
			return CreatePVectorStressbalanceSIA();
			break;
#endif
		case BedSlopeXAnalysisEnum: case SurfaceSlopeXAnalysisEnum: case BedSlopeYAnalysisEnum: case SurfaceSlopeYAnalysisEnum:
			return CreatePVectorSlope();
			break;
		case MasstransportAnalysisEnum:
			return CreatePVectorMasstransport();
			break;
#ifdef _HAVE_HYDROLOGY_
		case HydrologyShreveAnalysisEnum:
			return CreatePVectorHydrologyShreve();
			break;
		case HydrologyDCInefficientAnalysisEnum:
			return CreatePVectorHydrologyDCInefficient();
			break;
		case HydrologyDCEfficientAnalysisEnum:
			return CreatePVectorHydrologyDCEfficient();
			break;
#endif
#ifdef _HAVE_BALANCED_
		case BalancethicknessAnalysisEnum:
			return CreatePVectorBalancethickness();
			break;
		case BalancevelocityAnalysisEnum:
			return CreatePVectorBalancevelocity();
			break;
		case SmoothedSurfaceSlopeXAnalysisEnum:
			return CreatePVectorSmoothedSlopeX();
			break;
		case SmoothedSurfaceSlopeYAnalysisEnum:
			return CreatePVectorSmoothedSlopeY();
			break;
#endif
#ifdef _HAVE_CONTROL_
		case AdjointBalancethicknessAnalysisEnum:
			return CreatePVectorAdjointBalancethickness();
			break;
		case AdjointHorizAnalysisEnum:
			return CreatePVectorAdjointHoriz();
			break;
#endif
		default:
			_error_("analysis " << analysis_type << " (" << EnumToStringx(analysis_type) << ") not supported yet");
	}

	/*make compiler happy*/
	return NULL;
}
/*}}}*/
/*FUNCTION Tria::CreatePVectorSlope {{{*/
ElementVector* Tria::CreatePVectorSlope(void){

	/*Intermediaries */
	int        i,analysis_type;
	IssmDouble Jdet;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble slope[2];

	/*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*/
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	Input* slope_input=NULL;
	if ( (analysis_type==SurfaceSlopeXAnalysisEnum) || (analysis_type==SurfaceSlopeYAnalysisEnum)){
		slope_input=inputs->GetInput(SurfaceEnum); _assert_(slope_input);
	}
	if ( (analysis_type==BedSlopeXAnalysisEnum) || (analysis_type==BedSlopeYAnalysisEnum)){
		slope_input=inputs->GetInput(BedEnum);     _assert_(slope_input);
	}

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

		gauss->GaussPoint(ig);

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

		slope_input->GetInputDerivativeValue(&slope[0],&xyz_list[0][0],gauss);

		if( (analysis_type==SurfaceSlopeXAnalysisEnum) || (analysis_type==BedSlopeXAnalysisEnum)){
			for(i=0;i<numnodes;i++) pe->values[i]+=Jdet*gauss->weight*slope[0]*basis[i];
		}
		if( (analysis_type==SurfaceSlopeYAnalysisEnum) || (analysis_type==BedSlopeYAnalysisEnum)){
			for(i=0;i<numnodes;i++) pe->values[i]+=Jdet*gauss->weight*slope[1]*basis[i];
		}
	}

	/*Clean up and return*/
	xDelete<IssmDouble>(basis);
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Tria::CreateJacobianMatrix{{{*/
void  Tria::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->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=CreateJacobianStressbalanceSSA();
			break;
#endif
		default:
			_error_("analysis " << analysis_type << " (" << EnumToStringx(analysis_type) << ") not supported yet");
	}

	/*Add to global matrix*/
	if(Ke){
		Ke->AddToGlobal(Jff);
		delete Ke;
	}
}
/*}}}*/
/*FUNCTION Tria::ComputeBasalStress {{{*/
void  Tria::ComputeBasalStress(Vector<IssmDouble>* eps){
	_error_("Not Implemented yet");
}
/*}}}*/
/*FUNCTION Tria::ComputeStrainRate {{{*/
void  Tria::ComputeStrainRate(Vector<IssmDouble>* eps){
	_error_("Not Implemented yet");
}
/*}}}*/
/*FUNCTION Tria::ComputeStressTensor {{{*/
void  Tria::ComputeStressTensor(){

	IssmDouble      xyz_list[NUMVERTICES][3];
	IssmDouble      pressure,viscosity;
	IssmDouble      epsilon[3]; /* epsilon=[exx,eyy,exy];*/
	IssmDouble      sigma_xx[NUMVERTICES];
	IssmDouble		sigma_yy[NUMVERTICES];
	IssmDouble		sigma_zz[NUMVERTICES]={0,0,0};
	IssmDouble      sigma_xy[NUMVERTICES];
	IssmDouble		sigma_xz[NUMVERTICES]={0,0,0};
	IssmDouble		sigma_yz[NUMVERTICES]={0,0,0};
	GaussTria* 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);

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

		/*Compute strain rate viscosity and pressure: */
		this->GetStrainRate2d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
		material->GetViscosity2d(&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_xy[iv]=2*viscosity*epsilon[2];
	}

	/*Add Stress tensor components into inputs*/
	this->inputs->AddInput(new TriaInput(StressTensorxxEnum,&sigma_xx[0],P1Enum));
	this->inputs->AddInput(new TriaInput(StressTensorxyEnum,&sigma_xy[0],P1Enum));
	this->inputs->AddInput(new TriaInput(StressTensorxzEnum,&sigma_xz[0],P1Enum));
	this->inputs->AddInput(new TriaInput(StressTensoryyEnum,&sigma_yy[0],P1Enum));
	this->inputs->AddInput(new TriaInput(StressTensoryzEnum,&sigma_yz[0],P1Enum));
	this->inputs->AddInput(new TriaInput(StressTensorzzEnum,&sigma_zz[0],P1Enum));

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

	/*go into parameters and get the analysis_counter: */
	int 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);

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

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

	/*get inputs configured too: */
	this->inputs->Configure(parameters);

}
/*}}}*/
/*FUNCTION Tria::DeepEcho{{{*/
void Tria::DeepEcho(void){

	_printf_("Tria:\n");
	_printf_("   id: " << id << "\n");
	if(nodes){
		nodes[0]->DeepEcho();
		nodes[1]->DeepEcho();
		nodes[2]->DeepEcho();
	}
	else _printf_("nodes = NULL\n");

	if (material) material->DeepEcho();
	else _printf_("material = NULL\n");

	if (matpar) matpar->DeepEcho();
	else _printf_("matpar = NULL\n");

	_printf_("   parameters\n");
	if (parameters) parameters->DeepEcho();
	else _printf_("parameters = NULL\n");

	_printf_("   inputs\n");
	if (inputs) inputs->DeepEcho();
	else _printf_("inputs=NULL\n");

	if (results) results->DeepEcho();
	else _printf_("results=NULL\n");

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

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

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

	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;

	/*Recover parameters*/
	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);
	GaussTria* gauss=new GaussTria();
	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];
		TriaInput* newmonthinput1 = new TriaInput(SurfaceforcingsMonthlytemperaturesEnum,&tmp[0],P1Enum);
		NewTemperatureInput->AddTimeInput(newmonthinput1,time+imonth/12.*yts);

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

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

	/*clean-up*/
	delete gauss;
}
/*}}}*/
/*FUNCTION Tria::Echo{{{*/
void Tria::Echo(void){
	_printf_("Tria:\n");
	_printf_("   id: " << id << "\n");
	if(nodes){
		nodes[0]->Echo();
		nodes[1]->Echo();
		nodes[2]->Echo();
	}
	else _printf_("nodes = NULL\n");

	if (material) material->Echo();
	else _printf_("material = NULL\n");

	if (matpar) matpar->Echo();
	else _printf_("matpar = NULL\n");

	_printf_("   parameters\n");
	if (parameters) parameters->Echo();
	else _printf_("parameters = NULL\n");

	_printf_("   inputs\n");
	if (inputs) inputs->Echo();
	else _printf_("inputs=NULL\n");

	if (results) results->Echo();
	else _printf_("results=NULL\n");
}
/*}}}*/
/*FUNCTION Tria::ObjectEnum{{{*/
int Tria::ObjectEnum(void){

	return TriaEnum;

}
/*}}}*/
/*FUNCTION Tria::GetArea {{{*/
IssmDouble Tria::GetArea(void){

	IssmDouble area=0;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble x1,y1,x2,y2,x3,y3;

	/*Get xyz list: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	x1=xyz_list[0][0]; y1=xyz_list[0][1];
	x2=xyz_list[1][0]; y2=xyz_list[1][1];
	x3=xyz_list[2][0]; y3=xyz_list[2][1];

	_assert_(x2*y3 - y2*x3 + x1*y2 - y1*x2 + x3*y1 - y3*x1>0);
	return (x2*y3 - y2*x3 + x1*y2 - y1*x2 + x3*y1 - y3*x1)/2;
}
/*}}}*/
/*FUNCTION Tria::GetAreaCoordinates{{{*/
void Tria::GetAreaCoordinates(IssmDouble* area_coordinates,IssmDouble xyz_zero[3][3],IssmDouble xyz_list[3][3],int numpoints){
	/*Computeportion of the element that is grounded*/ 

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

	area_init=GetArea();

	/*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 Tria::GetDofList {{{*/
void  Tria::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 Tria::GetElementType {{{*/
int Tria::GetElementType(){

	/*return TriaRef field*/
	return this->element_type;

}
/*}}}*/
/*FUNCTION Tria::GetGroundedPart{{{*/
void Tria::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 Tria::GetGroundedPortion{{{*/
IssmDouble Tria::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[3][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*/
		GetJacobianDeterminant(&area_init, xyz_list,NULL);
		GetJacobianDeterminant(&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 Tria::GetSegmentNormal {{{*/
void Tria:: GetSegmentNormal(IssmDouble* normal,IssmDouble xyz_list[2][3]){

	/*Build unit outward pointing vector*/
	IssmDouble vector[2];
	IssmDouble norm;

	vector[0]=xyz_list[1][0] - xyz_list[0][0];
	vector[1]=xyz_list[1][1] - xyz_list[0][1];

	norm=sqrt(vector[0]*vector[0] + vector[1]*vector[1]);

	normal[0]= + vector[1]/norm;
	normal[1]= - vector[0]/norm;
}
/*}}}*/
/*FUNCTION Tria::GetZeroLevelsetCoordinates{{{*/
void Tria::GetZeroLevelsetCoordinates(IssmDouble* xyz_zero,IssmDouble xyz_list[3][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]);
	}
	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]);
	}
	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]);
	}
	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];
	}
	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];
	}
	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];
	}
	else _error_("Case not covered");
}
/*}}}*/
/*FUNCTION Tria::GetNodeIndex {{{*/
int Tria::GetNodeIndex(Node* node){

	_assert_(nodes);
	for(int i=0;i<NUMVERTICES;i++){
		if(node==nodes[i])
		 return i;
	}
	_error_("Node provided not found among element nodes");
}
/*}}}*/
/*FUNCTION Tria::GetNodesSidList{{{*/
void Tria::GetNodesSidList(int* sidlist){

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

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

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

	for(int i=0;i<numnodes;i++){
		lidlist[i]=nodes[i]->Lid();
	}
}
/*}}}*/
/*FUNCTION Tria::GetNumberOfNodes;{{{*/
int Tria::GetNumberOfNodes(void){
	return this->NumberofNodes();
}
/*}}}*/
/*FUNCTION Tria::GetInputListOnVertices(IssmDouble* pvalue,int enumtype) {{{*/
void Tria::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: */
	GaussTria* gauss=new GaussTria();
	for (int iv=0;iv<NUMVERTICES;iv++){
		gauss->GaussVertex(iv);
		input->GetInputValue(&pvalue[iv],gauss);
	}

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

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

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

	/* Start looping on the number of vertices: */
	if (input){
		GaussTria* gauss=new GaussTria();
		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 Tria::GetInputListOnVertices(IssmDouble* pvalue,int enumtype,IssmDouble defaultvalue,int index) TO BE REMOVED{{{*/
void Tria::GetInputListOnVertices(IssmDouble* pvalue,int enumtype,IssmDouble defaultvalue,int index){

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

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

	/* Start looping on the number of vertices: */
	if (input){
		GaussTria* gauss=new GaussTria();
		for (int iv=0;iv<NUMVERTICES;iv++){
			gauss->GaussVertex(iv);
			input->GetInputValue(&pvalue[iv],gauss,index);
		}
		delete gauss;
	}
	else{
		for (int iv=0;iv<NUMVERTICES;iv++) pvalue[iv]=defaultvalue;
	}
}
/*}}}*/
/*FUNCTION Tria::GetInputListOnNodes(IssmDouble* pvalue,int enumtype,IssmDouble defaultvalue) {{{*/
void Tria::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){
		GaussTria* gauss=new GaussTria();
		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 Tria::GetInputListOnNodes(IssmDouble* pvalue,int enumtype) {{{*/
void Tria::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: */
	GaussTria* gauss=new GaussTria();
	for (int iv=0;iv<this->NumberofNodes();iv++){
		gauss->GaussNode(this->element_type,iv);
		input->GetInputValue(&pvalue[iv],gauss);
	}
	delete gauss;
}
/*}}}*/
/*FUNCTION Tria::GetInputValue(IssmDouble* pvalue,Node* node,int enumtype) {{{*/
void Tria::GetInputValue(IssmDouble* pvalue,Node* node,int enumtype){

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

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

	input->GetInputValue(pvalue,gauss);
	delete gauss;
}
/*}}}*/
/*FUNCTION Tria::GetVertexPidList {{{*/
void  Tria::GetVertexPidList(int* doflist){

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

}
/*}}}*/
/*FUNCTION Tria::GetVertexSidList {{{*/
void  Tria::GetVertexSidList(int* sidlist){
	for(int i=0;i<NUMVERTICES;i++) sidlist[i]=vertices[i]->Sid();
}
/*}}}*/
/*FUNCTION Tria::GetConnectivityList {{{*/
void  Tria::GetConnectivityList(int* connectivity){
	for(int i=0;i<NUMVERTICES;i++) connectivity[i]=vertices[i]->Connectivity();
}
/*}}}*/
/*FUNCTION Tria::GetSolutionFromInputs{{{*/
void  Tria::GetSolutionFromInputs(Vector<IssmDouble>* solution){

	/*retrive parameters: */
	int analysis_type;
	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:
		GetSolutionFromInputsStressbalanceHoriz(solution);
		break;
	case StressbalanceSIAAnalysisEnum:
		GetSolutionFromInputsStressbalanceSIA(solution);
		break;
	#endif
	#ifdef _HAVE_HYDROLOGY_
	case HydrologyShreveAnalysisEnum:
		GetSolutionFromInputsOneDof(solution,WatercolumnEnum);
		break;
	case HydrologyDCInefficientAnalysisEnum:
		GetSolutionFromInputsOneDof(solution,SedimentHeadEnum);
		break;
	case HydrologyDCEfficientAnalysisEnum:
		GetSolutionFromInputsOneDof(solution,EplHeadEnum);
		break;
	#endif
	default:
		_error_("analysis: " << EnumToStringx(analysis_type) << " not supported yet");
	}

}
/*}}}*/
/*FUNCTION Tria::GetStrainRate2d(IssmDouble* epsilon,IssmDouble* xyz_list, GaussTria* gauss, Input* vx_input, Input* vy_input){{{*/
void Tria::GetStrainRate2d(IssmDouble* epsilon,IssmDouble* xyz_list, GaussTria* gauss, Input* vx_input, Input* vy_input){
	/*Compute the 2d Strain Rate (3 components):
	 * epsilon=[exx eyy exy] */

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

	/*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->GetVxStrainRate2d(epsilonvx,xyz_list,gauss);
	vy_input->GetVyStrainRate2d(epsilonvy,xyz_list,gauss);

	/*Sum all contributions*/
	for(i=0;i<3;i++) epsilon[i]=epsilonvx[i]+epsilonvy[i];
}
/*}}}*/
/*FUNCTION Tria::GetVectorFromInputs{{{*/
void  Tria::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 Tria::GetVectorFromResults{{{*/
void  Tria::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 Tria::Id {{{*/
int    Tria::Id(){

	return id;

}
/*}}}*/
/*FUNCTION Tria::Sid {{{*/
int    Tria::Sid(){

	return sid;

}
/*}}}*/
/*FUNCTION Tria::InputArtificialNoise{{{*/
void  Tria::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 Tria::InputDepthAverageAtBase {{{*/
void  Tria::InputDepthAverageAtBase(int enum_type,int average_enum_type,int object_enum){

	/*New input*/
	Input* oldinput=NULL;
	Input* newinput=NULL;

	/*copy input of enum_type*/
	if (object_enum==MeshElementsEnum)
	 oldinput=(Input*)this->inputs->GetInput(enum_type);
	else if (object_enum==MaterialsEnum)
	 oldinput=(Input*)this->material->inputs->GetInput(enum_type);
	else
	 _error_("object " << EnumToStringx(object_enum) << " not supported yet");
	if(!oldinput)_error_("could not find old input with enum: " << EnumToStringx(enum_type));
	newinput=(Input*)oldinput->copy();

	/*Assign new name (average)*/
	newinput->ChangeEnum(average_enum_type);

	/*Add new input to current element*/
	if (object_enum==MeshElementsEnum)
	 this->inputs->AddInput((Input*)newinput);
	else if (object_enum==MaterialsEnum)
	 this->material->inputs->AddInput((Input*)newinput);
	else
	 _error_("object " << EnumToStringx(object_enum) << " not supported yet");
}
/*}}}*/
/*FUNCTION Tria::InputDuplicate{{{*/
void  Tria::InputDuplicate(int original_enum,int new_enum){

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

}
/*}}}*/
/*FUNCTION Tria::InputScale{{{*/
void  Tria::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 Tria::InputToResult{{{*/
void  Tria::InputToResult(int enum_type,int step,IssmDouble time){

	Input *input = NULL;	

	/*Go through all the input objects, and find the one corresponding to enum_type, if it exists: */
	if (enum_type==MaterialsRheologyBbarEnum || enum_type==MaterialsRheologyZbarEnum)
	 input=this->material->inputs->GetInput(enum_type);
	else
	 input=this->inputs->GetInput(enum_type);
	//if (!input) _error_("Input " << EnumToStringx(enum_type) << " not found in tria->inputs");
	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 Tria::InputUpdateFromConstant(int value, int name);{{{*/
void  Tria::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 Tria::InputUpdateFromConstant(IssmDouble value, int name);{{{*/
void  Tria::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 Tria::InputUpdateFromConstant(bool value, int name);{{{*/
void  Tria::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 Tria::InputUpdateFromIoModel{{{*/
void Tria::InputUpdateFromIoModel(int index, IoModel* iomodel){ //i is the element index

	/*Intermediaries*/
	int        i,j;
	int        tria_vertex_ids[3];
	IssmDouble nodeinputs[3];
	IssmDouble cmmininputs[3];
	IssmDouble cmmaxinputs[3];
	bool       control_analysis   = false;
	int        num_control_type;
	IssmDouble yts;
	int        num_cm_responses;

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

	/*Recover vertices ids needed to initialize inputs*/
	for(i=0;i<3;i++){ 
		tria_vertex_ids[i]=reCast<int>(iomodel->elements[3*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>(iomodel->Data(InversionControlParametersEnum)[i])){
				case BalancethicknessThickeningRateEnum:
					if (iomodel->Data(BalancethicknessThickeningRateEnum)){
						for(j=0;j<3;j++)nodeinputs[j]=iomodel->Data(BalancethicknessThickeningRateEnum)[tria_vertex_ids[j]-1];
						for(j=0;j<3;j++)cmmininputs[j]=iomodel->Data(InversionMinParametersEnum)[(tria_vertex_ids[j]-1)*num_control_type+i]/yts;
						for(j=0;j<3;j++)cmmaxinputs[j]=iomodel->Data(InversionMaxParametersEnum)[(tria_vertex_ids[j]-1)*num_control_type+i]/yts;
						this->inputs->AddInput(new ControlInput(BalancethicknessThickeningRateEnum,TriaInputEnum,nodeinputs,cmmininputs,cmmaxinputs,i+1));
					}
					break;
				case VxEnum:
					if (iomodel->Data(VxEnum)){
						for(j=0;j<3;j++)nodeinputs[j]=iomodel->Data(VxEnum)[tria_vertex_ids[j]-1];
						for(j=0;j<3;j++)cmmininputs[j]=iomodel->Data(InversionMinParametersEnum)[(tria_vertex_ids[j]-1)*num_control_type+i]/yts;
						for(j=0;j<3;j++)cmmaxinputs[j]=iomodel->Data(InversionMaxParametersEnum)[(tria_vertex_ids[j]-1)*num_control_type+i]/yts;
						this->inputs->AddInput(new ControlInput(VxEnum,TriaInputEnum,nodeinputs,cmmininputs,cmmaxinputs,i+1));
					}
					break;
				case VyEnum:
					if (iomodel->Data(VyEnum)){
						for(j=0;j<3;j++)nodeinputs[j]=iomodel->Data(VyEnum)[tria_vertex_ids[j]-1];
						for(j=0;j<3;j++)cmmininputs[j]=iomodel->Data(InversionMinParametersEnum)[(tria_vertex_ids[j]-1)*num_control_type+i]/yts;
						for(j=0;j<3;j++)cmmaxinputs[j]=iomodel->Data(InversionMaxParametersEnum)[(tria_vertex_ids[j]-1)*num_control_type+i]/yts;
						this->inputs->AddInput(new ControlInput(VyEnum,TriaInputEnum,nodeinputs,cmmininputs,cmmaxinputs,i+1));
					}
					break;
				case ThicknessEnum:
					if (iomodel->Data(ThicknessEnum)){
						for(j=0;j<3;j++)nodeinputs[j]=iomodel->Data(ThicknessEnum)[tria_vertex_ids[j]-1];
						for(j=0;j<3;j++)cmmininputs[j]=iomodel->Data(InversionMinParametersEnum)[(tria_vertex_ids[j]-1)*num_control_type+i];
						for(j=0;j<3;j++)cmmaxinputs[j]=iomodel->Data(InversionMaxParametersEnum)[(tria_vertex_ids[j]-1)*num_control_type+i];
						this->inputs->AddInput(new ControlInput(ThicknessEnum,TriaInputEnum,nodeinputs,cmmininputs,cmmaxinputs,i+1));
					}
					break;
				case FrictionCoefficientEnum:
					if (iomodel->Data(FrictionCoefficientEnum)){
						for(j=0;j<3;j++)nodeinputs[j]=iomodel->Data(FrictionCoefficientEnum)[tria_vertex_ids[j]-1];
						for(j=0;j<3;j++)cmmininputs[j]=iomodel->Data(InversionMinParametersEnum)[(tria_vertex_ids[j]-1)*num_control_type+i];
						for(j=0;j<3;j++)cmmaxinputs[j]=iomodel->Data(InversionMaxParametersEnum)[(tria_vertex_ids[j]-1)*num_control_type+i];
						this->inputs->AddInput(new ControlInput(FrictionCoefficientEnum,TriaInputEnum,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

	/*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<3;j++)nodeinputs[j]=iomodel->Data(InversionCostFunctionsCoefficientsEnum)[(tria_vertex_ids[j]-1)*num_cm_responses+i];
			datasetinput->inputs->AddObject(new TriaInput(InversionCostFunctionsCoefficientsEnum,nodeinputs,P1Enum));
		}

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

	/*retrive parameters: */
	int analysis_type;
	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:
			InputUpdateFromSolutionStressbalanceHoriz(solution);
			break;
		#endif
		#ifdef _HAVE_CONTROL_
		case AdjointHorizAnalysisEnum:
			InputUpdateFromSolutionAdjointHoriz(solution);
			break;
		case AdjointBalancethicknessAnalysisEnum:
			InputUpdateFromSolutionOneDof(solution,AdjointEnum);
			break;
		#endif
		#ifdef _HAVE_HYDROLOGY_ 
		case HydrologyShreveAnalysisEnum:
			InputUpdateFromSolutionHydrologyShreve(solution);
			break;
		case HydrologyDCInefficientAnalysisEnum:
			InputUpdateFromSolutionHydrologyDCInefficient(solution);
			break;
		case HydrologyDCEfficientAnalysisEnum:
			InputUpdateFromSolutionOneDof(solution,EplHeadEnum);
			break;
		#endif
	 	#ifdef _HAVE_BALANCED_
		case BalancethicknessAnalysisEnum:
			InputUpdateFromSolutionOneDof(solution,ThicknessEnum);
			break;
		case BalancevelocityAnalysisEnum:
			InputUpdateFromSolutionOneDof(solution,VelEnum);
			break;
		case SmoothedSurfaceSlopeXAnalysisEnum:
			InputUpdateFromSolutionOneDof(solution,SurfaceSlopeXEnum);
			break;
		case SmoothedSurfaceSlopeYAnalysisEnum:
			InputUpdateFromSolutionOneDof(solution,SurfaceSlopeYEnum);
			break;
		#endif
		case BedSlopeXAnalysisEnum:
			InputUpdateFromSolutionOneDof(solution,BedSlopeXEnum);
			break;
		case BedSlopeYAnalysisEnum:
			InputUpdateFromSolutionOneDof(solution,BedSlopeYEnum);
			break;
		case SurfaceSlopeXAnalysisEnum:
			InputUpdateFromSolutionOneDof(solution,SurfaceSlopeXEnum);
			break;
		case SurfaceSlopeYAnalysisEnum:
			InputUpdateFromSolutionOneDof(solution,SurfaceSlopeYEnum);
			break;
		case MasstransportAnalysisEnum:
			InputUpdateFromSolutionMasstransport(solution);
			break;
		default:
			_error_("analysis " << analysis_type << " (" << EnumToStringx(analysis_type) << ") not supported yet");
	}
}
/*}}}*/
/*FUNCTION Tria::InputUpdateFromSolutionOneDof{{{*/
void  Tria::InputUpdateFromSolutionOneDof(IssmDouble* solution,int enum_type){

	/*Intermediary*/
	int* doflist = NULL;

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

	/*Fetch dof list and allocate solution vector*/
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);
	IssmDouble* values    = xNew<IssmDouble>(numnodes);

	/*Use the dof list to index into the solution vector: */
	for(int i=0;i<numnodes;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 TriaInput(enum_type,values,P1Enum));

	/*Free ressources:*/
	xDelete<IssmDouble>(values);
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Tria::InputUpdateFromSolutionMasstransport{{{*/
void  Tria::InputUpdateFromSolutionMasstransport(IssmDouble* solution){

	/*Intermediaries*/
	int        i,hydroadjustment;
	int*       doflist=NULL;
	IssmDouble rho_ice,rho_water,minthickness;

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

	/*Fetch dof list and allocate solution vector*/
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);
	IssmDouble* newthickness = xNew<IssmDouble>(numnodes);
	IssmDouble* newbed       = xNew<IssmDouble>(numnodes);
	IssmDouble* newsurface   = xNew<IssmDouble>(numnodes);
	IssmDouble* oldthickness = xNew<IssmDouble>(numnodes);
	IssmDouble* oldbed       = xNew<IssmDouble>(numnodes);
	IssmDouble* oldsurface   = xNew<IssmDouble>(numnodes);
	IssmDouble* phi          = xNew<IssmDouble>(numnodes);

	/*Use the dof list to index into the solution vector: */
	this->parameters->FindParam(&minthickness,MasstransportMinThicknessEnum);
	for(i=0;i<numnodes;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;
	}

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

	/*Find MasstransportHydrostaticAdjustment to figure out how to update the geometry:*/
	this->parameters->FindParam(&hydroadjustment,MasstransportHydrostaticAdjustmentEnum);
	rho_ice=matpar->GetRhoIce();
	rho_water=matpar->GetRhoWater();

	for(i=0;i<numnodes;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{ //this 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");
		}
	}

	/*Add input to the element: */
	this->inputs->AddInput(new TriaInput(ThicknessEnum,newthickness,P1Enum));
	this->inputs->AddInput(new TriaInput(SurfaceEnum,newsurface,P1Enum));
	this->inputs->AddInput(new TriaInput(BedEnum,newbed,P1Enum));

	/*Free ressources:*/
	xDelete<IssmDouble>(newthickness);
	xDelete<IssmDouble>(newbed);
	xDelete<IssmDouble>(newsurface);
	xDelete<IssmDouble>(oldthickness);
	xDelete<IssmDouble>(oldbed);
	xDelete<IssmDouble>(oldsurface);
	xDelete<IssmDouble>(phi);
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Tria::InputUpdateFromVector(IssmDouble* vector, int name, int type);{{{*/
void  Tria::InputUpdateFromVector(IssmDouble* vector, int name, int type){


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

	int         numnodes;
	int        *doflist = NULL;
	IssmDouble *values  = NULL;

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

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

	case NodesEnum:

		/*Get number of nodes and dof list: */
		numnodes = this->NumberofNodes();
		values   = xNew<IssmDouble>(numnodes);
		GetDofList(&doflist,NoneApproximationEnum,GsetEnum);

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

		/*Add input to the element: */
		this->inputs->AddInput(new TriaInput(name,values,this->element_type));

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

	case NodeSIdEnum:

		/*Get number of nodes and dof list: */
		numnodes = this->NumberofNodes();
		values   = xNew<IssmDouble>(numnodes);

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

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

	/*Clean-up*/
	xDelete<int>(doflist);
	xDelete<IssmDouble>(values);

}
/*}}}*/
/*FUNCTION Tria::InputCreate(IssmDouble scalar,int enum,int code);{{{*/
void Tria::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>(scalar)));
	}
	else if ((code==6) || (code==2)){ //integer
		this->inputs->AddInput(new IntInput(name,reCast<int>(scalar)));
	}
	else if ((code==7) || (code==3)){ //IssmDouble
		this->inputs->AddInput(new DoubleInput(name,reCast<int>(scalar)));
	}
	else _error_("could not recognize nature of vector from code " << code);

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

	/*Intermediaries*/
	int    i,j,t;
	int    tria_vertex_ids[3];
	int    row;
	IssmDouble nodeinputs[3];
	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<3;i++){ 
			_assert_(iomodel->elements);
			tria_vertex_ids[i]=reCast<int>(iomodel->elements[3*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<3;i++)nodeinputs[i]=(IssmDouble)vector[tria_vertex_ids[i]-1];

			/*create static input: */
			this->inputs->AddInput(new TriaInput(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<3;i++){
					row=tria_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 TriaInput(vector_enum,nodeinputs,P1Enum),time);
			}
			this->inputs->AddInput(transientinput);
		}
		else _error_("nodal vector is either numberofvertices or numberofvertices+1 long. Field provided (" << EnumToStringx(vector_enum) << ") is " << M << " long");
	}
	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>(vector[index])));
			}
			else if (code==6){ //integer
				this->inputs->AddInput(new IntInput(vector_enum,reCast<int>(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 element inputs not supported yet!");
		}
	}
	else{
		_error_("Cannot add input for vector type " << vector_type << " (not supported)");
	}

}
/*}}}*/
/*FUNCTION Tria::IsInput{{{*/
bool Tria::IsInput(int name){
	if (
				name==ThicknessEnum ||
				name==SurfaceEnum ||
				name==BathymetryEnum ||
				name==BedEnum ||
				name==MaskGroundediceLevelsetEnum ||
				name==MaskIceLevelsetEnum ||
				name==SurfaceSlopeXEnum ||
				name==SurfaceSlopeYEnum ||
				name==BasalforcingsMeltingRateEnum ||
				name==WatercolumnEnum || 
				name==SurfaceforcingsMassBalanceEnum ||
				name==SurfaceAreaEnum||
				name==VxEnum ||
				name==VyEnum ||
				name==InversionVxObsEnum ||
				name==InversionVyObsEnum ||
				name==FrictionCoefficientEnum ||
				name==MaterialsRheologyBbarEnum ||
				name==MaterialsRheologyZbarEnum ||
				name==GradientEnum ||
				name==OldGradientEnum ||
				name==ConvergedEnum ||
				name==SedimentHeadOldEnum ||
				name==SedimentHeadEnum ||
				name==EplHeadOldEnum ||
				name==EplHeadEnum ||
				name==HydrologydcMaskEplactiveEnum ||
				name==WaterTransferEnum ||
				name==QmuVxEnum ||
				name==QmuVyEnum ||
				name==QmuPressureEnum ||
				name==QmuBedEnum ||
				name==QmuThicknessEnum ||
				name==QmuSurfaceEnum ||
				name==QmuTemperatureEnum ||
				name==QmuMeltingEnum || 
				name==GiaWEnum || 
				name==GiadWdtEnum
		){
		return true;
	}
	else return false;
}
/*}}}*/
/*FUNCTION Tria::IsOnBed {{{*/
bool Tria::IsOnBed(){

	bool onbed;
	inputs->GetInputValue(&onbed,MeshElementonbedEnum);
	return onbed;
}
/*}}}*/
/*FUNCTION Tria::IsFloating {{{*/
bool   Tria::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 Tria::IsNodeOnShelfFromFlags {{{*/
bool   Tria::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 Tria::NoIceInElement {{{*/
bool   Tria::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 Tria::ListResultsInfo{{{*/
void Tria::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()==TriaP1ElementResultEnum){
				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 Tria::NodalValue {{{*/
int    Tria::NodalValue(IssmDouble* pvalue, int index, int natureofdataenum){

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

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

	/*figure out if we have the vertex id: */
	found=0;
	for(int 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 GaussTria(); gauss->GaussVertex(i);
				data->GetInputValue(&value,gauss);
				found=1;
				break;
			}
		}
	}

	/*clean-up*/
	delete gauss;

	if(found)*pvalue=value;
	return found;
}
/*}}}*/
/*FUNCTION Tria::PatchFill{{{*/
void  Tria::PatchFill(int* prow, Patch* patch){

	int i,row;
	int vertices_ids[3];

	/*recover pointer: */
	row=*prow;

	for(i=0;i<3;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(row,this->sid+1,vertices_ids,3);
		elementresult->PatchFill(row,patch);

		/*increment rower: */
		row++;
	}

	/*Assign output pointers:*/
	*prow=row;
}
/*}}}*/
/*FUNCTION Tria::PatchSize{{{*/
void  Tria::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 Tria::PositiveDegreeDay{{{*/
void  Tria::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];
   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);
   GaussTria* gauss=new GaussTria();
   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 in m/y
     }
   }

  /*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 TriaInput(SurfaceforcingsMassBalanceEnum,&agd[0],P1Enum));
   // this->inputs->AddInput(new TriaVertexInput(ThermalSpcTemperatureEnum,&Tsurf[0]));

	/*clean-up*/
	delete gauss;
}
/*}}}*/
/*FUNCTION Tria::RequestedOutput{{{*/
void Tria::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 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 Tria::SetClone {{{*/
void  Tria::SetClone(int* minranks){

	_error_("not implemented yet");
}
/*}}}*/
/*FUNCTION Tria::SmbGradients{{{*/
void Tria::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 TriaInput(SurfaceforcingsMassBalanceEnum,&smb[0],P1Enum));
}
/*}}}*/
/*FUNCTION Tria::SetCurrentConfiguration {{{*/
void  Tria::SetCurrentConfiguration(Elements* elementsin, Loads* loadsin, Nodes* nodesin, Materials* materialsin, Parameters* parametersin){

	/*go into parameters and get the analysis_counter: */
	int 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 Tria::SurfaceArea {{{*/
IssmDouble Tria::SurfaceArea(void){

	IssmDouble S;
	IssmDouble normal[3];
	IssmDouble v13[3],v23[3];
	IssmDouble xyz_list[NUMVERTICES][3];

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

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

	for(int 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];

	S = 0.5 * sqrt(normal[0]*normal[0] + normal[1]*normal[1] + normal[2]*normal[2]);

	/*Return: */
	return S;
}
/*}}}*/
/*FUNCTION Tria::SurfaceNormal{{{*/
void Tria::SurfaceNormal(IssmDouble* surface_normal, IssmDouble xyz_list[3][3]){

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

	for(int 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( normal[0]*normal[0] + normal[1]*normal[1] + normal[2]*normal[2]);

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

	/*intermediary: */
	int    i;
	IssmDouble C,dt;
	IssmDouble dx,dy;
	IssmDouble maxx,minx;
	IssmDouble maxy,miny;
	IssmDouble maxabsvx,maxabsvy;
	IssmDouble xyz_list[NUMVERTICES][3];

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

	/*Get for Vx and Vy, the max of abs value: */
	#ifdef _HAVE_RESPONSES_
	this->MaxAbsVx(&maxabsvx);
	this->MaxAbsVy(&maxabsvy);
	#else
		_error_("ISSM was not compiled with responses compiled in, exiting!");
	#endif

	/* 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];

	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];
	}
	dx=maxx-minx;
	dy=maxy-miny;

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

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

	/*Intermediaries*/
	int        i,j;
	int        tria_vertex_ids[3];
	IssmDouble nodeinputs[3];
	IssmDouble yts;
	int        progstabilization,balancestabilization;
	int        fe_ssa;
	bool       dakota_analysis;
	int        numnodes;
	int*       tria_node_ids = NULL;

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

	/*Fetch parameters: */
	iomodel->Constant(&yts,ConstantsYtsEnum);
	iomodel->Constant(&progstabilization,MasstransportStabilizationEnum);
	iomodel->Constant(&balancestabilization,BalancethicknessStabilizationEnum);
	iomodel->Constant(&fe_ssa,FlowequationFeSSAEnum);
	iomodel->Constant(&dakota_analysis,QmuIsdakotaEnum);

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

	/*Recover vertices ids needed to initialize inputs*/
	for(i=0;i<3;i++){ 
		tria_vertex_ids[i]=reCast<int>(iomodel->elements[3*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        = 3;
			tria_node_ids   = xNew<int>(numnodes);
			tria_node_ids[0]=iomodel->nodecounter+iomodel->elements[3*index+0];
			tria_node_ids[1]=iomodel->nodecounter+iomodel->elements[3*index+1];
			tria_node_ids[2]=iomodel->nodecounter+iomodel->elements[3*index+2];
			break;
		case P1DGEnum:
			numnodes        = 3;
			tria_node_ids   = xNew<int>(numnodes);
			tria_node_ids[0]=iomodel->nodecounter+3*index+1;
			tria_node_ids[1]=iomodel->nodecounter+3*index+2;
			tria_node_ids[2]=iomodel->nodecounter+3*index+3;
			break;
		case P1bubbleEnum: case P1bubblecondensedEnum:
			numnodes        = 4;
			tria_node_ids   = xNew<int>(numnodes);
			tria_node_ids[0]=iomodel->nodecounter+iomodel->elements[3*index+0];
			tria_node_ids[1]=iomodel->nodecounter+iomodel->elements[3*index+1];
			tria_node_ids[2]=iomodel->nodecounter+iomodel->elements[3*index+2];
			tria_node_ids[3]=iomodel->nodecounter+iomodel->numberofvertices+index+1;
			break;
		case P2Enum:
			numnodes        = 6;
			tria_node_ids   = xNew<int>(numnodes);
			tria_node_ids[0]=iomodel->nodecounter+iomodel->elements[3*index+0];
			tria_node_ids[1]=iomodel->nodecounter+iomodel->elements[3*index+1];
			tria_node_ids[2]=iomodel->nodecounter+iomodel->elements[3*index+2];
			tria_node_ids[3]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[3*index+0]+1;
			tria_node_ids[4]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[3*index+1]+1;
			tria_node_ids[5]=iomodel->nodecounter+iomodel->numberofvertices+iomodel->elementtoedgeconnectivity[3*index+2]+1;
			break;
		default:
			_error_("Finite element "<<EnumToStringx(finiteelement_type)<<" not supported yet");
	}

	/*hooks: */
	this->SetHookNodes(tria_node_ids,numnodes,analysis_counter); this->nodes=NULL;
	xDelete<int>(tria_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<3;i++)nodeinputs[i]=0;
				this->inputs->AddInput(new TriaInput(VxEnum,nodeinputs,P1Enum));
				if(dakota_analysis) this->inputs->AddInput(new TriaInput(QmuVxEnum,nodeinputs,P1Enum));
			}
			if(!iomodel->Data(VyEnum)){
				for(i=0;i<3;i++)nodeinputs[i]=0;
				this->inputs->AddInput(new TriaInput(VyEnum,nodeinputs,P1Enum));
				if(dakota_analysis) this->inputs->AddInput(new TriaInput(QmuVyEnum,nodeinputs,P1Enum));
			}
			if(!iomodel->Data(VzEnum)){
				for(i=0;i<3;i++)nodeinputs[i]=0;
				this->inputs->AddInput(new TriaInput(VzEnum,nodeinputs,P1Enum));
				if(dakota_analysis) this->inputs->AddInput(new TriaInput(QmuVzEnum,nodeinputs,P1Enum));
			}
			if(!iomodel->Data(PressureEnum)){
				for(i=0;i<3;i++)nodeinputs[i]=0;
				if(dakota_analysis){
					this->inputs->AddInput(new TriaInput(PressureEnum,nodeinputs,P1Enum));
					this->inputs->AddInput(new TriaInput(QmuPressureEnum,nodeinputs,P1Enum));
				}
			}
			break;

		default:
			/*No update for other solution types*/
			break;

	}

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

#ifdef _HAVE_RESPONSES_
/*FUNCTION Tria::AverageOntoPartition {{{*/
void  Tria::AverageOntoPartition(Vector<IssmDouble>* partition_contributions,Vector<IssmDouble>* partition_areas,IssmDouble* vertex_response,IssmDouble* qmu_part){

	bool       already = false;
	int        i,j;
	int        partition[NUMVERTICES];
	int        offsetsid[NUMVERTICES];
	int        offsetdof[NUMVERTICES];
	IssmDouble area;
	IssmDouble mean;

	/*First, get the area: */
	area=this->GetArea();

	/*Figure out the average for this element: */
	this->GetVertexSidList(&offsetsid[0]);
	this->GetVertexPidList(&offsetdof[0]);
	mean=0;
	for(i=0;i<NUMVERTICES;i++){
		partition[i]=reCast<int>(qmu_part[offsetsid[i]]);
		mean=mean+1.0/NUMVERTICES*vertex_response[offsetdof[i]];
	}

	/*Add contribution: */
	for(i=0;i<NUMVERTICES;i++){
		already=false;
		for(j=0;j<i;j++){
			if (partition[i]==partition[j]){
				already=true;
				break;
			}
		}
		if(!already){
			partition_contributions->SetValue(partition[i],mean*area,ADD_VAL);
			partition_areas->SetValue(partition[i],area,ADD_VAL);
		};
	}
}
/*}}}*/
/*FUNCTION Tria::IceVolume {{{*/
IssmDouble Tria::IceVolume(void){

	/*The volume of a troncated prism is base * 1/3 sum(length of edges)*/
	IssmDouble base,surface,bed;
	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/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 height*/
	Input* surface_input = inputs->GetInput(SurfaceEnum); _assert_(surface_input);
	Input* bed_input     = inputs->GetInput(BedEnum);     _assert_(bed_input);
	surface_input->GetInputAverage(&surface);
	bed_input->GetInputAverage(&bed);

	/*Return: */
	return base*(surface-bed);
}
/*}}}*/
/*FUNCTION Tria::MassFlux {{{*/
IssmDouble Tria::MassFlux( IssmDouble* segment){

	int        dim;
	IssmDouble mass_flux=0.;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble normal[2];
	IssmDouble length,rho_ice;
	IssmDouble x1,y1,x2,y2,h1,h2;
	IssmDouble vx1,vx2,vy1,vy2;
	GaussTria* gauss_1=NULL;
	GaussTria* gauss_2=NULL;

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

	/*First off, check that this segment belongs to this element: */
	if (reCast<int>(*(segment+4))!=this->id)_error_("error message: segment with id " << reCast<int>(*(segment+4)) << " does not belong to element with id:" << this->id);

	/*Recover segment node locations: */
	x1=*(segment+0); y1=*(segment+1); x2=*(segment+2); y2=*(segment+3);

	/*Get xyz list: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);

	/*get area coordinates of 0 and 1 locations: */
	gauss_1=new GaussTria();
	gauss_1->GaussFromCoords(x1,y1,&xyz_list[0][0]);
	gauss_2=new GaussTria();
	gauss_2->GaussFromCoords(x2,y2,&xyz_list[0][0]);

	normal[0]=cos(atan2(x1-x2,y2-y1));
	normal[1]=sin(atan2(x1-x2,y2-y1));

	length=sqrt(pow(x2-x1,2)+pow(y2-y1,2));

	Input* thickness_input=inputs->GetInput(ThicknessEnum); _assert_(thickness_input);
	this->parameters->FindParam(&dim,MeshDimensionEnum);
	Input* vx_input=NULL;
	Input* vy_input=NULL;
	if(dim==2){
		vx_input=inputs->GetInput(VxEnum); _assert_(vx_input);
		vy_input=inputs->GetInput(VyEnum); _assert_(vy_input);
	}
	else{
		vx_input=inputs->GetInput(VxAverageEnum); _assert_(vx_input);
		vy_input=inputs->GetInput(VyAverageEnum); _assert_(vy_input);
	}

	thickness_input->GetInputValue(&h1, gauss_1);
	thickness_input->GetInputValue(&h2, gauss_2);
	vx_input->GetInputValue(&vx1,gauss_1);
	vx_input->GetInputValue(&vx2,gauss_2);
	vy_input->GetInputValue(&vy1,gauss_1);
	vy_input->GetInputValue(&vy2,gauss_2);

	mass_flux= rho_ice*length*(  
				(ONETHIRD*(h1-h2)*(vx1-vx2)+0.5*h2*(vx1-vx2)+0.5*(h1-h2)*vx2+h2*vx2)*normal[0]+
				(ONETHIRD*(h1-h2)*(vy1-vy2)+0.5*h2*(vy1-vy2)+0.5*(h1-h2)*vy2+h2*vy2)*normal[1]
				);

	/*clean up and return:*/
	delete gauss_1;
	delete gauss_2;
	return mass_flux;
}
/*}}}*/
/*FUNCTION Tria::MaxAbsVx{{{*/
void  Tria::MaxAbsVx(IssmDouble* pmaxabsvx){

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

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

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

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

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

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

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

	/*Assign output pointers:*/
	*pmaxvel=maxvel;
}
/*}}}*/
/*FUNCTION Tria::MaxVx{{{*/
void  Tria::MaxVx(IssmDouble* pmaxvx){

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

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

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

	/*Assign output pointers:*/
	*pmaxvy=maxvy;

}
/*}}}*/
/*FUNCTION Tria::MaxVz{{{*/
void  Tria::MaxVz(IssmDouble* pmaxvz){

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

	/*Assign output pointers:*/
	*pmaxvz=maxvz;
}
/*}}}*/
/*FUNCTION Tria::MinVel{{{*/
void  Tria::MinVel(IssmDouble* pminvel){

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

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

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

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

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

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

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

	/*Assign output pointers:*/
	*pminvz=minvz;
}
/*}}}*/
/*FUNCTION Tria::ElementResponse{{{*/
void Tria::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 Tria::TotalSmb {{{*/
IssmDouble Tria::TotalSmb(void){

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

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

   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/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]));	// area of element in m2

	/*Now get the average SMB over the element*/
	Input* smb_input = inputs->GetInput(SurfaceforcingsMassBalanceEnum); _assert_(smb_input);
	smb_input->GetInputAverage(&smb);																								// average smb on element in m ice s-1
   Total_Smb=rho_ice*base*smb;																											// smb on element in kg s-1

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

#ifdef _HAVE_GIA_
/*FUNCTION Tria::GiaDeflection {{{*/
void Tria::GiaDeflection(Vector<IssmDouble>* wg,Vector<IssmDouble>* dwgdt,IssmDouble* x, IssmDouble* y){

	int i;
	int gsize;
	IssmDouble xi,yi,ri,re,area;
	IssmDouble x0,y0;
	IssmDouble xyz_list[NUMVERTICES][3];

	/*thickness averages: */
	IssmDouble* hes=NULL;
	IssmDouble* times=NULL;
	IssmDouble  currenttime;
	int         numtimes;
	Input* thickness_input=NULL;

	/*gia solution parameters:*/
	int cross_section_shape=0;

	/*gia material parameters: */
	IssmDouble lithosphere_shear_modulus;
	IssmDouble lithosphere_density;
	IssmDouble mantle_shear_modulus;
	IssmDouble mantle_density;
	Input* mantle_viscosity_input=NULL;
	IssmDouble mantle_viscosity;
	Input* lithosphere_thickness_input=NULL;
	IssmDouble lithosphere_thickness;

	/*ice properties: */
	IssmDouble rho_ice;

	/*constants: */
	IssmDouble yts;

	/*output: */
	IssmDouble  wi;
	IssmDouble  dwidt;

	/*arguments to GiaDeflectionCorex: */
	GiaDeflectionCoreArgs arguments;

	/*how many dofs are we working with here? */
	this->parameters->FindParam(&gsize,MeshNumberofverticesEnum);
	this->parameters->FindParam(&yts,ConstantsYtsEnum);

	/*recover gia solution parameters: */
	this->parameters->FindParam(&cross_section_shape,GiaCrossSectionShapeEnum);

	/*what time is it? :*/
	this->parameters->FindParam(&currenttime,TimeEnum);

	/*recover material parameters: */
	lithosphere_shear_modulus=matpar->GetLithosphereShearModulus();
	lithosphere_density=matpar->GetLithosphereDensity();
	mantle_shear_modulus=matpar->GetMantleShearModulus();
	mantle_density=matpar->GetMantleDensity();
	rho_ice=matpar->GetRhoIce();

	/*pull thickness averages: */
	thickness_input=inputs->GetInput(ThicknessEnum); 
	if (!thickness_input)_error_("thickness input needed to compute gia deflection!");
	thickness_input->GetInputUpToCurrentTimeAverages(&hes,&times,&numtimes,currenttime);

	/*recover mantle viscosity: */
	mantle_viscosity_input=inputs->GetInput(GiaMantleViscosityEnum);
	if (!mantle_viscosity_input)_error_("mantle viscosity input needed to compute gia deflection!");
	mantle_viscosity_input->GetInputAverage(&mantle_viscosity);

	/*recover lithosphere thickness: */
	lithosphere_thickness_input=inputs->GetInput(GiaLithosphereThicknessEnum);
	if (!lithosphere_thickness_input)_error_("lithosphere thickness input needed to compute gia deflection!");
	lithosphere_thickness_input->GetInputAverage(&lithosphere_thickness);

	/*pull area of this Tria: */
	area=this->GetArea();

	/*element radius: */
	re=sqrt(area/PI);

	/*figure out gravity center of our element: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	x0=(xyz_list[0][0]+xyz_list[1][0]+xyz_list[2][0])/3.0;
	y0=(xyz_list[0][1]+xyz_list[1][1]+xyz_list[2][1])/3.0;

	/*start loading GiaDeflectionCore arguments: */
	arguments.re=re;
	arguments.hes=hes;
	arguments.times=times;
	arguments.numtimes=numtimes;
	arguments.currenttime=currenttime;
	arguments.lithosphere_shear_modulus=lithosphere_shear_modulus;
	arguments.lithosphere_density=lithosphere_density;
	arguments.mantle_shear_modulus=mantle_shear_modulus;
	arguments.mantle_viscosity=mantle_viscosity;
	arguments.mantle_density=mantle_density;
	arguments.lithosphere_thickness=lithosphere_thickness;
	arguments.rho_ice=rho_ice;
	arguments.idisk=this->id;
	arguments.iedge=cross_section_shape;
	arguments.yts=yts;

	for(i=0;i<gsize;i++){
		/*compute distance from the center of the tria to the vertex i: */
		xi=x[i]; yi=y[i];
		ri=sqrt(pow(xi-x0,2)+pow(yi-y0,2));

		/*load ri onto arguments for this vertex i: */
		arguments.ri=ri;

		/*for this Tria, compute contribution to rebound at vertex i: */
		GiaDeflectionCorex(&wi,&dwidt,&arguments);

		/*plug value into solution vector: */
		wg->SetValue(i,wi,ADD_VAL);
		dwgdt->SetValue(i,dwidt,ADD_VAL);

	}

	/*Free ressources: */
	xDelete<IssmDouble>(hes);
	xDelete<IssmDouble>(times);

	return;
}
/*}}}*/
#endif

#ifdef _HAVE_STRESSBALANCE_
/*FUNCTION Tria::CreateKMatrixStressbalanceSSA {{{*/
ElementMatrix* Tria::CreateKMatrixStressbalanceSSA(void){

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

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

	/*Intermediaries*/
	int        i,j;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble viscosity,newviscosity,oldviscosity;
	IssmDouble viscosity_overshoot,thickness,Jdet;
	IssmDouble epsilon[3],oldepsilon[3];    /* epsilon=[exx,eyy,exy];    */
	IssmDouble D_scalar;

	/*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,SSAApproximationEnum);
	IssmDouble*    B      = xNew<IssmDouble>(3*numdof);
	IssmDouble*    Bprime = xNew<IssmDouble>(3*numdof);
	IssmDouble*    D      = xNewZeroInit<IssmDouble>(3*3);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	Input* thickness_input=inputs->GetInput(ThicknessEnum); _assert_(thickness_input);
	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);
	this->parameters->FindParam(&viscosity_overshoot,StressbalanceViscosityOvershootEnum);

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

		gauss->GaussPoint(ig);

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

		this->GetStrainRate2d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
		this->GetStrainRate2d(&oldepsilon[0],&xyz_list[0][0],gauss,vxold_input,vyold_input);
		material->GetViscosity2d(&viscosity, &epsilon[0]);
		material->GetViscosity2d(&oldviscosity, &oldepsilon[0]);
		thickness_input->GetInputValue(&thickness, gauss);

		newviscosity=viscosity+viscosity_overshoot*(viscosity-oldviscosity);
		D_scalar=2.*newviscosity*thickness*gauss->weight*Jdet;
		for(i=0;i<3;i++) D[i*3+i]=D_scalar;

		TripleMultiply(B,3,numdof,1,
					D,3,3,0,
					Bprime,3,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 Tria::CreateKMatrixStressbalanceSSAFriction {{{*/
ElementMatrix* Tria::CreateKMatrixStressbalanceSSAFriction(void){

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

	/*Return if element is inactive*/
	if(IsFloating()) 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,SSAApproximationEnum);
	IssmDouble*    B      = xNew<IssmDouble>(2*numdof);
	IssmDouble*    D      = xNewZeroInit<IssmDouble>(2*2);

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

	/*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[0][0]);
	if(migration_style==SubelementMigration2Enum){
		gllevelset_input=inputs->GetInput(MaskGroundediceLevelsetEnum); _assert_(gllevelset_input);
		this->GetGroundedPart(&point1,&fraction1,&fraction2,&mainlyfloating);
		gauss=new GaussTria(point1,fraction1,fraction2,mainlyfloating,2);
	}
	else{
		gauss=new GaussTria(2);
	}

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

		gauss->GaussPoint(ig);

		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;
		}

		GetBSSAFriction(&B[0], &xyz_list[0][0], gauss);
		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		D_scalar=alpha2*gauss->weight*Jdet;
		for(i=0;i<2;i++) D[i*2+i]=D_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;
	delete friction;
	xDelete<IssmDouble>(D);
	xDelete<IssmDouble>(B);
	return Ke;
}
/*}}}*/
/*FUNCTION Tria::CreateKMatrixStressbalanceSIA{{{*/
ElementMatrix* Tria::CreateKMatrixStressbalanceSIA(void){

	/*Intermediaries*/
	IssmDouble connectivity;

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

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

	/*Create Element matrix*/
	for(int i=0;i<numnodes;i++){
		connectivity=(IssmDouble)vertices[i]->Connectivity();
		Ke->values[(2*i+0)*numdof+(2*i+0)]=1./connectivity;
		Ke->values[(2*i+1)*numdof+(2*i+1)]=1./connectivity;
	}

	/*Clean up and return*/
	return Ke;
}
/*}}}*/
/*FUNCTION Tria::CreatePVectorStressbalanceSSA {{{*/
ElementVector* Tria::CreatePVectorStressbalanceSSA(){

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

	/*clean-up and return*/
	delete pe1;
	delete pe2;
	return pe;
}
/*}}}*/
/*FUNCTION Tria::CreatePVectorStressbalanceSSADrivingStress {{{*/
ElementVector* Tria::CreatePVectorStressbalanceSSADrivingStress(){

	/*Intermediaries */
	int        i;
	IssmDouble driving_stress_baseline,thickness;
	IssmDouble Jdet;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble slope[2];
	IssmDouble icefrontlevel[3];

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

	/*Initialize Element vector and vectors*/
	ElementVector* pe=new ElementVector(nodes,numnodes,this->parameters,SSAApproximationEnum);
	IssmDouble*    basis = xNew<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);
	Input* drag_input=inputs->GetInput(FrictionCoefficientEnum);_assert_(drag_input);
	GetInputListOnVertices(&icefrontlevel[0],BedEnum);

	/* Start  looping on the number of gaussian points: */
	GaussTria*     gauss  = new GaussTria(2);
	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()*thickness;

		/*Build load vector: */
		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 Tria::CreatePVectorStressbalanceSSAFront {{{*/
ElementVector* Tria::CreatePVectorStressbalanceSSAFront(){

	/*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,thickness,bed,water_pressure,ice_pressure,air_pressure;
	IssmDouble  surface_under_water,base_under_water,pressure;
	IssmDouble  xyz_list_front[2][3];
	IssmDouble  area_coordinates[2][3];
	IssmDouble  normal[2];

	/*Fetch number of nodes 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,SSAApproximationEnum);
	IssmDouble*    basis = xNew<IssmDouble>(numnodes);

	/*Retrieve all inputs and parameters*/
	Input* thickness_input=inputs->GetInput(ThicknessEnum); _assert_(thickness_input);
	Input* bed_input      =inputs->GetInput(BedEnum);       _assert_(bed_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,2);
	GetSegmentNormal(&normal[0],xyz_list_front);

	/*Start looping on Gaussian points*/
	GaussTria* gauss=new GaussTria(area_coordinates,3);
	for(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);
		thickness_input->GetInputValue(&thickness,gauss);
		bed_input->GetInputValue(&bed,gauss);
		GetSegmentJacobianDeterminant(&Jdet,&xyz_list_front[0][0],gauss);
		GetNodalFunctions(basis,gauss);

		surface_under_water = min(0.,thickness+bed); // 0 if the top of the glacier is above water level
		base_under_water    = min(0.,bed);           // 0 if the bottom of the glacier is above water level
		water_pressure = 1.0/2.0*gravity*rho_water*(pow(surface_under_water,2) - pow(base_under_water,2));
		ice_pressure   = 1.0/2.0*gravity*rho_ice*pow(thickness,2);
		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 Tria::CreatePVectorStressbalanceSIA{{{*/
ElementVector* Tria::CreatePVectorStressbalanceSIA(void){

	/*Intermediaries */
	IssmDouble constant_part,ub,vb;
	IssmDouble rho_ice,gravity,n,B;
	IssmDouble slope2,thickness,connectivity;
	IssmDouble slope[2];

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

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

	/*Retrieve all inputs and parameters*/
	rho_ice=matpar->GetRhoIce();
	gravity=matpar->GetG();
	n=material->GetN();
	B=material->GetBbar();
	Input* slopex_input=inputs->GetInput(SurfaceSlopeXEnum); _assert_(slopex_input);
	Input* slopey_input=inputs->GetInput(SurfaceSlopeYEnum); _assert_(slopey_input);
	Input* thickness_input=inputs->GetInput(ThicknessEnum);  _assert_(thickness_input);

	/*Spawn 3 sing elements: */
	GaussTria* gauss=new GaussTria();
	for(int i=0;i<numnodes;i++){

		gauss->GaussVertex(i);

		connectivity=(IssmDouble)vertices[i]->Connectivity();

		thickness_input->GetInputValue(&thickness,gauss);
		slopex_input->GetInputValue(&slope[0],gauss);
		slopey_input->GetInputValue(&slope[1],gauss);
		slope2=pow(slope[0],2)+pow(slope[1],2);

		constant_part=-2.*pow(rho_ice*gravity,n)*pow(slope2,((n-1.)/2.));

		ub=-1.58*1.e-10*rho_ice*gravity*thickness*slope[0];
		vb=-1.58*1.e-10*rho_ice*gravity*thickness*slope[1];

		pe->values[2*i+0]=(ub-2.*pow(rho_ice*gravity,n)*pow(slope2,((n-1.)/2.))*pow(thickness,n)/(pow(B,n)*(n+1))*slope[0])/connectivity;
		pe->values[2*i+1]=(vb-2.*pow(rho_ice*gravity,n)*pow(slope2,((n-1.)/2.))*pow(thickness,n)/(pow(B,n)*(n+1))*slope[1])/connectivity;
	}

	/*Clean up and return*/
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Tria::CreateJacobianStressbalanceSSA{{{*/
ElementMatrix* Tria::CreateJacobianStressbalanceSSA(void){

	/*Intermediaries */
	int        i,j;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble Jdet,thickness;
	IssmDouble eps1dotdphii,eps1dotdphij;
	IssmDouble eps2dotdphii,eps2dotdphij;
	IssmDouble mu_prime;
	IssmDouble epsilon[3];/* epsilon=[exx,eyy,exy];*/
	IssmDouble eps1[2],eps2[2];
	GaussTria* gauss = NULL;

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

	/*Initialize Element matrix, vectors and Gaussian points*/
	ElementMatrix* Ke=CreateKMatrixStressbalanceSSA(); //Initialize Jacobian with regular SSA (first part of the Gateau derivative)
	IssmDouble*    dbasis = xNew<IssmDouble>(2*numnodes);

	/*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* thickness_input=inputs->GetInput(ThicknessEnum); _assert_(thickness_input);

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

		gauss->GaussPoint(ig);

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

		thickness_input->GetInputValue(&thickness, gauss);
		this->GetStrainRate2d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
		material->GetViscosity2dDerivativeEpsSquare(&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];

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

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

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

	/*Clean up and return*/
	xDelete<IssmDouble>(dbasis);
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Tria::GetSolutionFromInputsStressbalanceHoriz{{{*/
void  Tria::GetSolutionFromInputsStressbalanceHoriz(Vector<IssmDouble>* solution){

	IssmDouble   vx,vy;
	int*         doflist = NULL;
	GaussTria*   gauss   = 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 vector*/
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);
	IssmDouble* values = xNew<IssmDouble>(numdof);

	/*Get inputs*/
	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: */
	gauss=new GaussTria();
	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;
	}

	solution->SetValues(numdof,doflist,values,INS_VAL);

	/*Free ressources:*/
	delete gauss;
	xDelete<IssmDouble>(values);
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Tria::GetSolutionFromInputsStressbalanceSIA{{{*/
void  Tria::GetSolutionFromInputsStressbalanceSIA(Vector<IssmDouble>* solution){

	const int    numdof=NDOF2*NUMVERTICES;

	int        i;
	IssmDouble     vx,vy;
	IssmDouble     values[numdof];
	int       *doflist = NULL;
	GaussTria *gauss   = NULL;

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

	/*Get inputs*/
	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 GaussTria();
	for(i=0;i<NUMVERTICES;i++){

		gauss->GaussVertex(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;
	}

	solution->SetValues(numdof,doflist,values,INS_VAL);

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

	int        i;
	IssmDouble rho_ice,g;
	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,NoneApproximationEnum,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* thickness = 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,numnodes,XYEnum);

	/*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 2D, 
	 *so the pressure is just the pressure at the bedrock: */
	rho_ice=matpar->GetRhoIce();
	g=matpar->GetG();
	GetInputListOnNodes(&thickness[0],ThicknessEnum);
	for(i=0;i<numnodes;i++) pressure[i]=rho_ice*g*thickness[i];

	/*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 TriaInput(VxEnum,vx,P1Enum));
	this->inputs->AddInput(new TriaInput(VyEnum,vy,P1Enum));
	this->inputs->AddInput(new TriaInput(VelEnum,vel,P1Enum));
	this->inputs->AddInput(new TriaInput(PressureEnum,pressure,P1Enum));

	/*Free ressources:*/
	xDelete<IssmDouble>(thickness);
	xDelete<IssmDouble>(pressure);
	xDelete<IssmDouble>(vel);
	xDelete<IssmDouble>(vz);
	xDelete<IssmDouble>(vy);
	xDelete<IssmDouble>(vx);
	xDelete<IssmDouble>(values);
	xDelete<int>(doflist);

}
/*}}}*/
/*FUNCTION Tria::InputUpdateFromSolutionStressbalanceSIA {{{*/
void  Tria::InputUpdateFromSolutionStressbalanceSIA(IssmDouble* solution){

	int        i;
	IssmDouble rho_ice,g;
	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,NoneApproximationEnum,GsetEnum);
	IssmDouble* values    = xNew<IssmDouble>(numdof);
	IssmDouble* vx        = xNew<IssmDouble>(numdof);
	IssmDouble* vy        = xNew<IssmDouble>(numdof);
	IssmDouble* vz        = xNew<IssmDouble>(numdof);
	IssmDouble* vel       = xNew<IssmDouble>(numdof);
	IssmDouble* pressure  = xNew<IssmDouble>(numdof);
	IssmDouble* thickness = xNew<IssmDouble>(numdof);

	/*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,numnodes,XYEnum);

	/*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 2D, 
	 *so the pressure is just the pressure at the bedrock: */
	rho_ice=matpar->GetRhoIce();
	g=matpar->GetG();
	GetInputListOnNodes(&thickness[0],ThicknessEnum);
	for(i=0;i<numnodes;i++) pressure[i]=rho_ice*g*thickness[i];

	/*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 TriaInput(VxEnum,vx,P1Enum));
	this->inputs->AddInput(new TriaInput(VyEnum,vy,P1Enum));
	this->inputs->AddInput(new TriaInput(VelEnum,vel,P1Enum));
	this->inputs->AddInput(new TriaInput(PressureEnum,pressure,P1Enum));

	/*Free ressources:*/
	xDelete<IssmDouble>(thickness);
	xDelete<IssmDouble>(pressure);
	xDelete<IssmDouble>(vel);
	xDelete<IssmDouble>(vz);
	xDelete<IssmDouble>(vy);
	xDelete<IssmDouble>(vx);
	xDelete<IssmDouble>(values);
	xDelete<int>(doflist);
}
/*}}}*/
#endif

#ifdef _HAVE_CONTROL_
/*FUNCTION Tria::BalancethicknessMisfit{{{*/
IssmDouble Tria::BalancethicknessMisfit(int weight_index){

	/* Intermediaries */
	IssmDouble Jelem = 0;
	IssmDouble weight;
	IssmDouble Jdet,temp;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble dH[2];
	IssmDouble vx,vy,H;
	IssmDouble dvx[2],dvy[2];
	IssmDouble dhdt,basal_melting,surface_mass_balance;
	GaussTria *gauss = NULL;

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

	/*Retrieve all inputs we will be needing: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	Input* weights_input              = inputs->GetInput(InversionCostFunctionsCoefficientsEnum);   _assert_(weights_input);
	Input* thickness_input            = inputs->GetInput(ThicknessEnum); _assert_(thickness_input);
	Input* vx_input                   = inputs->GetInput(VxEnum);                                 _assert_(vx_input);
	Input* vy_input                   = inputs->GetInput(VyEnum);                                 _assert_(vy_input);
	Input* surface_mass_balance_input = inputs->GetInput(SurfaceforcingsMassBalanceEnum);         _assert_(surface_mass_balance_input);
	Input* basal_melting_input        = inputs->GetInput(BasalforcingsMeltingRateEnum);           _assert_(basal_melting_input);
	Input* dhdt_input                 = inputs->GetInput(BalancethicknessThickeningRateEnum);     _assert_(dhdt_input);

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

		gauss->GaussPoint(ig);

		/* Get Jacobian determinant: */
		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);

		/*Get all parameters at gaussian point*/
		weights_input->GetInputValue(&weight,gauss,weight_index);
		thickness_input->GetInputValue(&H, gauss);
		thickness_input->GetInputDerivativeValue(&dH[0],&xyz_list[0][0],gauss);
		surface_mass_balance_input->GetInputValue(&surface_mass_balance,gauss);
		basal_melting_input->GetInputValue(&basal_melting,gauss);
		dhdt_input->GetInputValue(&dhdt,gauss);
		vx_input->GetInputValue(&vx,gauss);
		vx_input->GetInputDerivativeValue(&dvx[0],&xyz_list[0][0],gauss);
		vy_input->GetInputValue(&vy,gauss);
		vy_input->GetInputDerivativeValue(&dvy[0],&xyz_list[0][0],gauss);

		/*Balance thickness soft constraint J = 1/2 (div(Hv)-a)^2*/
		temp  = vx*dH[0]+vy*dH[1]+H*(dvx[0]+dvy[1]) - (surface_mass_balance-basal_melting-dhdt);
		Jelem+=weight*1/2*temp*temp*Jdet*gauss->weight;
	}

	/*Clean up and return*/
	delete gauss;
	return Jelem;
}
/*}}}*/
/*FUNCTION Tria::InputControlUpdate{{{*/
void  Tria::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 || control_type[i]==MaterialsRheologyZbarEnum){
			input=(Input*)material->inputs->GetInput(control_type[i]); _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();

	}

	/*Clean up and return*/
	xDelete<int>(control_type);
}
/*}}}*/
/*FUNCTION Tria::ControlInputGetGradient{{{*/
void Tria::ControlInputGetGradient(Vector<IssmDouble>* gradient,int enum_type,int control_index){

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

	if(enum_type==MaterialsRheologyBbarEnum || enum_type==MaterialsRheologyZbarEnum){
		input=(Input*)material->inputs->GetInput(enum_type);
	}
	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 Tria::ControlInputScaleGradient{{{*/
void Tria::ControlInputScaleGradient(int enum_type,IssmDouble scale){

	Input* input=NULL;

	if(enum_type==MaterialsRheologyBbarEnum || enum_type==MaterialsRheologyZbarEnum){
		input=(Input*)material->inputs->GetInput(enum_type);
	}
	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 Tria::ControlInputSetGradient{{{*/
void Tria::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 || enum_type==MaterialsRheologyZbarEnum){
		input=(Input*)material->inputs->GetInput(enum_type);
	}
	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 TriaInput(GradientEnum,grad_list,P1Enum);

	((ControlInput*)input)->SetGradient(grad_input);

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

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

	/*First deal with ∂/∂alpha(KU-F)*/
	switch(control_type){
		case FrictionCoefficientEnum:
			GradjDragSSA(gradient,control_index);
			break;
		case MaterialsRheologyBbarEnum:
			GradjBSSA(gradient,control_index);
			break;
		case MaterialsRheologyZbarEnum:
			GradjZSSA(gradient,control_index);
			break;
		case BalancethicknessThickeningRateEnum:
			GradjDhDtBalancedthickness(gradient,control_index);
			break;
		case VxEnum:
			GradjVxBalancedthickness(gradient,control_index);
			break;
		case VyEnum:
			GradjVyBalancedthickness(gradient,control_index);
			break;
		case ThicknessEnum:
			GradjThicknessBalancethicknessSoft(gradient,control_index);
			break;
		default:
			_error_("control type not supported yet: " << control_type);
	}

	/*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]){
		//FIXME: the control type should be checked somewhere (with respect to what variable are we taking the gradient!)

		case ThicknessAbsMisfitEnum:
		case ThicknessAbsGradientEnum:
		case ThicknessAlongGradientEnum:
		case ThicknessAcrossGradientEnum:
		case BalancethicknessMisfitEnum:
		case SurfaceAbsVelMisfitEnum:
		case SurfaceRelVelMisfitEnum:
		case SurfaceLogVelMisfitEnum:
		case SurfaceLogVxVyMisfitEnum:
		case SurfaceAverageVelMisfitEnum:
			/*Nothing, J does not depends on the parameter being inverted for*/
			break;
		case DragCoefficientAbsGradientEnum:
			GradjDragGradient(gradient,resp,control_index);
			break;
		case RheologyBbarAbsGradientEnum:
			GradjBGradient(gradient,resp,control_index);
			break;
		default:
			_error_("response " << EnumToStringx(responses[resp]) << " not supported yet");
	}

	xDelete<int>(responses);
}
/*}}}*/
/*FUNCTION Tria::GradjBGradient{{{*/
void  Tria::GradjBGradient(Vector<IssmDouble>* gradient,int weight_index,int control_index){

	int        i;
	int        vertexpidlist[NUMVERTICES];
	IssmDouble Jdet,weight;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble dbasis[NDOF2][NUMVERTICES];
	IssmDouble dk[NDOF2]; 
	IssmDouble grade_g[NUMVERTICES]={0.0};
	GaussTria  *gauss=NULL;

	/*Retrieve all inputs we will be needing: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	GradientIndexing(&vertexpidlist[0],control_index);
	Input* rheologyb_input=material->inputs->GetInput(MaterialsRheologyBbarEnum); _assert_(rheologyb_input);
	Input* weights_input=inputs->GetInput(InversionCostFunctionsCoefficientsEnum);                _assert_(weights_input);

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

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctionsDerivatives(&dbasis[0][0],&xyz_list[0][0],gauss);
		weights_input->GetInputValue(&weight,gauss,weight_index);

		/*Build alpha_complement_list: */
		rheologyb_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[i]+=-weight*Jdet*gauss->weight*(dbasis[0][i]*dk[0]+dbasis[1][i]*dk[1]);
	}
	gradient->SetValues(NUMVERTICES,vertexpidlist,grade_g,ADD_VAL);

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

	int        i;
	int        vertexpidlist[NUMVERTICES];
	IssmDouble Jdet,weight;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble dbasis[NDOF2][NUMVERTICES];
	IssmDouble dk[NDOF2]; 
	IssmDouble grade_g[NUMVERTICES]={0.0};
	GaussTria  *gauss=NULL;

	/*Retrieve all inputs we will be needing: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	GradientIndexing(&vertexpidlist[0],control_index);
	Input* rheologyz_input=material->inputs->GetInput(MaterialsRheologyZbarEnum); _assert_(rheologyz_input);
	Input* weights_input=inputs->GetInput(InversionCostFunctionsCoefficientsEnum);                _assert_(weights_input);

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

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctionsDerivatives(&dbasis[0][0],&xyz_list[0][0],gauss);
		weights_input->GetInputValue(&weight,gauss,weight_index);

		/*Build alpha_complement_list: */
		rheologyz_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[i]+=-weight*Jdet*gauss->weight*(dbasis[0][i]*dk[0]+dbasis[1][i]*dk[1]);
	}
	gradient->SetValues(NUMVERTICES,vertexpidlist,grade_g,ADD_VAL);

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

	/*Intermediaries*/
	int        i;
	int        doflist[NUMVERTICES];
	IssmDouble vx,vy,lambda,mu,thickness,Jdet;
	IssmDouble viscosity_complement;
	IssmDouble dvx[NDOF2],dvy[NDOF2],dadjx[NDOF2],dadjy[NDOF2],dB[NDOF2]; 
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble basis[3],epsilon[3];
	IssmDouble grad[NUMVERTICES]={0.0};
	GaussTria *gauss = NULL;

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

	/*Retrieve all inputs*/
	Input* thickness_input=inputs->GetInput(ThicknessEnum);                     _assert_(thickness_input);
	Input* vx_input=inputs->GetInput(VxEnum);                                   _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);                                   _assert_(vy_input);
	Input* adjointx_input=inputs->GetInput(AdjointxEnum);                       _assert_(adjointx_input);
	Input* adjointy_input=inputs->GetInput(AdjointyEnum);                       _assert_(adjointy_input);
	Input* rheologyb_input=material->inputs->GetInput(MaterialsRheologyBbarEnum); _assert_(rheologyb_input);

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

		gauss->GaussPoint(ig);

		thickness_input->GetInputValue(&thickness,gauss);
		rheologyb_input->GetInputDerivativeValue(&dB[0],&xyz_list[0][0],gauss);
		vx_input->GetInputDerivativeValue(&dvx[0],&xyz_list[0][0],gauss);
		vy_input->GetInputDerivativeValue(&dvy[0],&xyz_list[0][0],gauss);
		adjointx_input->GetInputDerivativeValue(&dadjx[0],&xyz_list[0][0],gauss);
		adjointy_input->GetInputDerivativeValue(&dadjy[0],&xyz_list[0][0],gauss);

		this->GetStrainRate2d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
		material->GetViscosityComplement(&viscosity_complement,&epsilon[0]);

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

		/*standard gradient dJ/dki*/
		for (i=0;i<NUMVERTICES;i++) grad[i]+=-viscosity_complement*thickness*(
					(2*dvx[0]+dvy[1])*2*dadjx[0]+(dvx[1]+dvy[0])*(dadjx[1]+dadjy[0])+(2*dvy[1]+dvx[0])*2*dadjy[1]
					)*Jdet*gauss->weight*basis[i];
	}

	gradient->SetValues(NUMVERTICES,doflist,grad,ADD_VAL);

	/*clean-up*/
	delete gauss;
}
/*}}}*/
/*FUNCTION Tria::GradjZSSA{{{*/
void  Tria::GradjZSSA(Vector<IssmDouble>* gradient,int control_index){

	/*Intermediaries*/
	int        i;
	int        doflist[NUMVERTICES];
	IssmDouble vx,vy,lambda,mu,thickness,Jdet;
	IssmDouble viscosity_complement;
	IssmDouble dvx[NDOF2],dvy[NDOF2],dadjx[NDOF2],dadjy[NDOF2],dZ[NDOF2]; 
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble basis[3],epsilon[3];
	IssmDouble grad[NUMVERTICES]={0.0};
	GaussTria *gauss = NULL;

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

	/*Retrieve all inputs*/
	Input* thickness_input=inputs->GetInput(ThicknessEnum);                     _assert_(thickness_input);
	Input* vx_input=inputs->GetInput(VxEnum);                                   _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum);                                   _assert_(vy_input);
	Input* adjointx_input=inputs->GetInput(AdjointxEnum);                       _assert_(adjointx_input);
	Input* adjointy_input=inputs->GetInput(AdjointyEnum);                       _assert_(adjointy_input);
	Input* rheologyz_input=material->inputs->GetInput(MaterialsRheologyZbarEnum); _assert_(rheologyz_input);

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

		gauss->GaussPoint(ig);

		thickness_input->GetInputValue(&thickness,gauss);
		rheologyz_input->GetInputDerivativeValue(&dZ[0],&xyz_list[0][0],gauss);
		vx_input->GetInputDerivativeValue(&dvx[0],&xyz_list[0][0],gauss);
		vy_input->GetInputDerivativeValue(&dvy[0],&xyz_list[0][0],gauss);
		adjointx_input->GetInputDerivativeValue(&dadjx[0],&xyz_list[0][0],gauss);
		adjointy_input->GetInputDerivativeValue(&dadjy[0],&xyz_list[0][0],gauss);

		this->GetStrainRate2d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
		material->GetViscosityZComplement(&viscosity_complement,&epsilon[0]);

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

		/*standard gradient dJ/dki*/
		for (i=0;i<NUMVERTICES;i++) grad[i]+=-viscosity_complement*thickness*(
					(2*dvx[0]+dvy[1])*2*dadjx[0]+(dvx[1]+dvy[0])*(dadjx[1]+dadjy[0])+(2*dvy[1]+dvx[0])*2*dadjy[1]
					)*Jdet*gauss->weight*basis[i];
	}

	gradient->SetValues(NUMVERTICES,doflist,grad,ADD_VAL);

	/*clean-up*/
	delete gauss;
}
/*}}}*/
/*FUNCTION Tria::GradjDragSSA {{{*/
void  Tria::GradjDragSSA(Vector<IssmDouble>* gradient,int control_index){

	int        i;
	int        analysis_type;
	int        vertexpidlist[NUMVERTICES];
	int        connectivity[NUMVERTICES];
	IssmDouble vx,vy,lambda,mu,alpha_complement,Jdet;
	IssmDouble bed,thickness,Neff,drag;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble dk[NDOF2]; 
	IssmDouble grade_g[NUMVERTICES]={0.0};
	IssmDouble grade_g_gaussian[NUMVERTICES];
	IssmDouble basis[3];
	IssmDouble epsilon[3]; /* epsilon=[exx,eyy,exy];*/
	Friction*  friction=NULL;
	GaussTria  *gauss=NULL;

	if(IsFloating())return;

	/*retrive parameters: */
	parameters->FindParam(&analysis_type,AnalysisTypeEnum);
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	GradientIndexing(&vertexpidlist[0],control_index);
	this->GetConnectivityList(&connectivity[0]);

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

	/*Retrieve all inputs we will be needing: */
	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);

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

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctions(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];
		}
	}
	/*Analytical gradient*/
	//delete gauss;
	//gauss=new GaussTria();
	//for (int iv=0;iv<NUMVERTICES;iv++){
	//	gauss->GaussVertex(iv);
	//	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);
	//	grade_g[iv] = -2*1.e+7*drag*alpha_complement*(lambda*vx+mu*vy)/((IssmDouble)connectivity[iv]);
	//}
	/*End Analytical gradient*/

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

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

	int        i;
	int        vertexpidlist[NUMVERTICES];
	IssmDouble Jdet,weight;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble dbasis[NDOF2][NUMVERTICES];
	IssmDouble dk[NDOF2]; 
	IssmDouble grade_g[NUMVERTICES]={0.0};
	GaussTria  *gauss=NULL;

	/*Retrieve all inputs we will be needing: */
	if(IsFloating())return;
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	GradientIndexing(&vertexpidlist[0],control_index);
	Input* dragcoefficient_input=inputs->GetInput(FrictionCoefficientEnum); _assert_(dragcoefficient_input);
	Input* weights_input=inputs->GetInput(InversionCostFunctionsCoefficientsEnum);                 _assert_(weights_input);

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

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctionsDerivatives(&dbasis[0][0],&xyz_list[0][0],gauss);
		weights_input->GetInputValue(&weight,gauss,weight_index);

		/*Build alpha_complement_list: */
		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[i]+=-weight*Jdet*gauss->weight*(dbasis[0][i]*dk[0]+dbasis[1][i]*dk[1]);
			_assert_(!xIsNan<IssmDouble>(grade_g[i]));
		}
	}
	gradient->SetValues(NUMVERTICES,vertexpidlist,grade_g,ADD_VAL);

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

	/*Intermediaries*/
	int    vertexpidlist[NUMVERTICES];
	IssmDouble lambda[NUMVERTICES];
	IssmDouble gradient_g[NUMVERTICES];

	/*Compute Gradient*/
	GradientIndexing(&vertexpidlist[0],control_index);
	GetInputListOnVertices(&lambda[0],AdjointEnum);
	for(int i=0;i<NUMVERTICES;i++) gradient_g[i]=-lambda[i];

	gradient->SetValues(NUMVERTICES,vertexpidlist,gradient_g,INS_VAL);
}
/*}}}*/
/*FUNCTION Tria::GradjVxBalancedthickness{{{*/
void  Tria::GradjVxBalancedthickness(Vector<IssmDouble>* gradient,int control_index){

	/*Intermediaries*/
	int        i;
	int        vertexpidlist[NUMVERTICES];
	IssmDouble thickness,Jdet;
	IssmDouble basis[3];
	IssmDouble Dlambda[2],dp[2];
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble grade_g[NUMVERTICES] = {0.0};
	GaussTria *gauss                = NULL;

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

	/*Retrieve all inputs we will be needing: */
	Input* adjoint_input=inputs->GetInput(AdjointEnum);     _assert_(adjoint_input);
	Input* thickness_input=inputs->GetInput(ThicknessEnum); _assert_(thickness_input);

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

		gauss->GaussPoint(ig);

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

		adjoint_input->GetInputDerivativeValue(&Dlambda[0],&xyz_list[0][0],gauss);
		thickness_input->GetInputValue(&thickness, gauss);
		thickness_input->GetInputDerivativeValue(&dp[0],&xyz_list[0][0],gauss);

		for(i=0;i<NUMVERTICES;i++) grade_g[i]+=thickness*Dlambda[0]*Jdet*gauss->weight*basis[i];
	}

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

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

	/*Intermediaries*/
	int        i;
	int        vertexpidlist[NUMVERTICES];
	IssmDouble thickness,Jdet;
	IssmDouble basis[3];
	IssmDouble Dlambda[2],dp[2];
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble grade_g[NUMVERTICES] = {0.0};
	GaussTria *gauss                = NULL;

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

	/*Retrieve all inputs we will be needing: */
	Input* adjoint_input=inputs->GetInput(AdjointEnum);     _assert_(adjoint_input);
	Input* thickness_input=inputs->GetInput(ThicknessEnum); _assert_(thickness_input);

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

		gauss->GaussPoint(ig);

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

		adjoint_input->GetInputDerivativeValue(&Dlambda[0],&xyz_list[0][0],gauss);
		thickness_input->GetInputValue(&thickness, gauss);
		thickness_input->GetInputDerivativeValue(&dp[0],&xyz_list[0][0],gauss);

		for(i=0;i<NUMVERTICES;i++) grade_g[i]+=thickness*Dlambda[1]*Jdet*gauss->weight*basis[i];
	}
	gradient->SetValues(NUMVERTICES,vertexpidlist,grade_g,ADD_VAL);

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

	/*Intermediaries */
	int         i,resp;
	int         vertexpidlist[NUMVERTICES];
	IssmDouble  Jdet;
	IssmDouble  thickness,thicknessobs,weight;
	int         num_responses;
	IssmDouble  xyz_list[NUMVERTICES][3];
	IssmDouble  basis[3];
	IssmDouble  dbasis[NDOF2][NUMVERTICES];
	IssmDouble  dH[2];
	IssmDouble  vx,vy,vel;
	IssmDouble  dvx[2],dvy[2];
	IssmDouble dhdt,basal_melting,surface_mass_balance;
	GaussTria *gauss     = NULL;
	int       *responses = NULL;
	IssmDouble grade_g[NUMVERTICES] = {0.0};

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

	/*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* thickness_input            = inputs->GetInput(ThicknessEnum);                          _assert_(thickness_input);
	Input* thicknessobs_input         = inputs->GetInput(InversionThicknessObsEnum);              _assert_(thicknessobs_input);
	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* surface_mass_balance_input = inputs->GetInput(SurfaceforcingsMassBalanceEnum);         _assert_(surface_mass_balance_input);
	Input* basal_melting_input        = inputs->GetInput(BasalforcingsMeltingRateEnum);           _assert_(basal_melting_input);
	Input* dhdt_input                 = inputs->GetInput(BalancethicknessThickeningRateEnum);     _assert_(dhdt_input);

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

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctions(basis, gauss);
		GetNodalFunctionsDerivatives(&dbasis[0][0],&xyz_list[0][0],gauss);

		thickness_input->GetInputValue(&thickness, gauss);
		thickness_input->GetInputDerivativeValue(&dH[0],&xyz_list[0][0],gauss);
		thicknessobs_input->GetInputValue(&thicknessobs, gauss);

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

			case ThicknessAbsMisfitEnum:
				weights_input->GetInputValue(&weight, gauss,resp);
				for(i=0;i<NUMVERTICES;i++) grade_g[i]+= (thicknessobs-thickness)*weight*Jdet*gauss->weight*basis[i];
				break;
			case ThicknessAbsGradientEnum:
				weights_input->GetInputValue(&weight, gauss,resp);
				for(i=0;i<NUMVERTICES;i++) grade_g[i]+= - weight*dH[0]*dbasis[0][i]*Jdet*gauss->weight;
				for(i=0;i<NUMVERTICES;i++) grade_g[i]+= - weight*dH[1]*dbasis[1][i]*Jdet*gauss->weight;
				break;
			case ThicknessAlongGradientEnum:
				weights_input->GetInputValue(&weight, gauss,resp);
				vx_input->GetInputValue(&vx,gauss);
				vy_input->GetInputValue(&vy,gauss);
				vel = sqrt(vx*vx+vy*vy);
				vx  = vx/(vel+1.e-9);
				vy  = vy/(vel+1.e-9);
				for(i=0;i<NUMVERTICES;i++) grade_g[i]+= - weight*(dH[0]*vx+dH[1]*vy)*(dbasis[0][i]*vx+dbasis[1][i]*vy)*Jdet*gauss->weight;
				break;
			case ThicknessAcrossGradientEnum:
				weights_input->GetInputValue(&weight, gauss,resp);
				vx_input->GetInputValue(&vx,gauss);
				vy_input->GetInputValue(&vy,gauss);
				vel = sqrt(vx*vx+vy*vy);
				vx  = vx/(vel+1.e-9);
				vy  = vy/(vel+1.e-9);
				for(i=0;i<NUMVERTICES;i++) grade_g[i]+= - weight*(dH[0]*(-vy)+dH[1]*vx)*(dbasis[0][i]*(-vy)+dbasis[1][i]*vx)*Jdet*gauss->weight;
				break;
			case BalancethicknessMisfitEnum:
				weights_input->GetInputValue(&weight, gauss,resp);
				surface_mass_balance_input->GetInputValue(&surface_mass_balance,gauss);
				basal_melting_input->GetInputValue(&basal_melting,gauss);
				dhdt_input->GetInputValue(&dhdt,gauss);
				vx_input->GetInputValue(&vx,gauss);
				vx_input->GetInputDerivativeValue(&dvx[0],&xyz_list[0][0],gauss);
				vy_input->GetInputValue(&vy,gauss);
				vy_input->GetInputDerivativeValue(&dvy[0],&xyz_list[0][0],gauss);
				for(i=0;i<NUMVERTICES;i++){
					grade_g[i]+= - weight*Jdet*gauss->weight*(
								(vx*dH[0]+vy*dH[1] + thickness*(dvx[0]+dvy[1]))*(vx*dbasis[0][i]+ vy*dbasis[1][i] + basis[i]*(dvx[0]+dvy[1]))
								-(surface_mass_balance-basal_melting-dhdt)*(vx*dbasis[0][i]+ vy*dbasis[1][i] + basis[i]*(dvx[0]+dvy[1]))
								);
				}
				break;
			default:
				_error_("response " << EnumToStringx(responses[resp]) << " not supported yet");
		}
	}

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

	/*Clean up and return*/
	delete gauss;
	xDelete<int>(responses);
}
/*}}}*/
/*FUNCTION Tria::GradientIndexing{{{*/
void  Tria::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 Tria::RheologyBbarAbsGradient{{{*/
IssmDouble Tria::RheologyBbarAbsGradient(int weight_index){

	/* Intermediaries */
	IssmDouble Jelem = 0;
	IssmDouble weight;
	IssmDouble Jdet;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble dp[NDOF2];
	GaussTria *gauss = NULL;

	/*retrieve parameters and inputs*/

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

	/*Retrieve all inputs we will be needing: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	Input* weights_input  =inputs->GetInput(InversionCostFunctionsCoefficientsEnum);              _assert_(weights_input);
	Input* rheologyb_input=material->inputs->GetInput(MaterialsRheologyBbarEnum); _assert_(rheologyb_input);

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

		gauss->GaussPoint(ig);

		/* Get Jacobian determinant: */
		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);

		/*Get all parameters at gaussian point*/
		weights_input->GetInputValue(&weight,gauss,weight_index);
		rheologyb_input->GetInputDerivativeValue(&dp[0],&xyz_list[0][0],gauss);

		/*Tikhonov regularization: J = 1/2 ((dp/dx)^2 + (dp/dy)^2) */ 
		Jelem+=weight*1/2*(dp[0]*dp[0] + dp[1]*dp[1])*Jdet*gauss->weight;
	}

	/*Clean up and return*/
	delete gauss;
	return Jelem;
}
/*}}}*/
/*FUNCTION Tria::SurfaceAverageVelMisfit {{{*/
IssmDouble Tria::SurfaceAverageVelMisfit(int weight_index){

	IssmDouble Jelem=0,S,Jdet;
	IssmDouble misfit;
	IssmDouble vx,vy,vxobs,vyobs,weight;
	IssmDouble xyz_list[NUMVERTICES][3];
	GaussTria *gauss=NULL;

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

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

	/*Retrieve all inputs we will be needing: */
	inputs->GetInputValue(&S,SurfaceAreaEnum);
	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);

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

		gauss->GaussPoint(ig);

		/* Get Jacobian determinant: */
		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);

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

		/*Compute SurfaceAverageVelMisfitEnum:
		 *
		 *      1                    2              2
		 * J = ---  sqrt(  (u - u   )  +  (v - v   )  )
		 *      S                obs            obs
		 */
		misfit=1/S*sqrt( pow(vx-vxobs,2) + pow(vy-vyobs,2));

		/*Add to cost function*/
		Jelem+=misfit*weight*Jdet*gauss->weight;
	}

	/*clean-up and Return: */
	delete gauss;
	return Jelem;
}
/*}}}*/
/*FUNCTION Tria::SurfaceLogVelMisfit {{{*/
IssmDouble Tria::SurfaceLogVelMisfit(int weight_index){

	IssmDouble Jelem=0.;
	IssmDouble misfit,Jdet;
	IssmDouble epsvel=2.220446049250313e-16;
	IssmDouble meanvel=3.170979198376458e-05; /*1000 m/yr*/
	IssmDouble velocity_mag,obs_velocity_mag;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble vx,vy,vxobs,vyobs,weight;
	GaussTria *gauss=NULL;

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

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

	/*Retrieve all inputs we will be needing: */
	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);

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

		gauss->GaussPoint(ig);

		/* Get Jacobian determinant: */
		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);

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

		/*Compute SurfaceLogVelMisfit:
		 *        4         [        vel + eps     ] 2
		 * J = 4 \bar{v}^2 | log ( -----------  ) |  
		 *                 [       vel   + eps    ]
		 *                            obs
		 */
		velocity_mag    =sqrt(pow(vx,   2)+pow(vy,   2))+epsvel;
		obs_velocity_mag=sqrt(pow(vxobs,2)+pow(vyobs,2))+epsvel;
		misfit=4*pow(meanvel,2)*pow(log(velocity_mag/obs_velocity_mag),2);

		/*Add to cost function*/
		Jelem+=misfit*weight*Jdet*gauss->weight;
	}

	/*clean-up and Return: */
	delete gauss;
	return Jelem;
}
/*}}}*/
/*FUNCTION Tria::SurfaceLogVxVyMisfit {{{*/
IssmDouble Tria::SurfaceLogVxVyMisfit(int weight_index){

	int        fit=-1;
	IssmDouble Jelem=0, S=0;
	IssmDouble epsvel=2.220446049250313e-16;
	IssmDouble meanvel=3.170979198376458e-05; /*1000 m/yr*/
	IssmDouble misfit, Jdet;
	IssmDouble vx,vy,vxobs,vyobs,weight;
	IssmDouble xyz_list[NUMVERTICES][3];
	GaussTria *gauss=NULL;

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

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

	/*Retrieve all inputs we will be needing: */
	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);

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

		gauss->GaussPoint(ig);

		/* Get Jacobian determinant: */
		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);

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

		/*Compute SurfaceRelVelMisfit:
		 *
		 *      1            [        |u| + eps     2          |v| + eps     2  ]
		 * J = --- \bar{v}^2 | log ( -----------  )   +  log ( -----------  )   |  
		 *      2            [       |u    |+ eps              |v    |+ eps     ]
		 *                              obs                       obs
		 */
		misfit=0.5*pow(meanvel,2)*(
					pow(log((fabs(vx)+epsvel)/(fabs(vxobs)+epsvel)),2) +
					pow(log((fabs(vy)+epsvel)/(fabs(vyobs)+epsvel)),2) );

		/*Add to cost function*/
		Jelem+=misfit*weight*Jdet*gauss->weight;
	}

	/*clean-up and Return: */
	delete gauss;
	return Jelem;
}
/*}}}*/
/*FUNCTION Tria::SurfaceAbsVelMisfit {{{*/
IssmDouble Tria::SurfaceAbsVelMisfit(int weight_index){

	IssmDouble Jelem=0;
	IssmDouble misfit,Jdet;
	IssmDouble vx,vy,vxobs,vyobs,weight;
	IssmDouble xyz_list[NUMVERTICES][3];
	GaussTria *gauss=NULL;

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

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

	/*Retrieve all inputs we will be needing: */
	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);

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

		gauss->GaussPoint(ig);

		/* Get Jacobian determinant: */
		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);

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

		/*Compute SurfaceAbsVelMisfitEnum:
		 *
		 *      1  [           2              2 ]
		 * J = --- | (u - u   )  +  (v - v   )  |
		 *      2  [       obs            obs   ]
		 *
		 */
		misfit=0.5*( pow(vx-vxobs,2) + pow(vy-vyobs,2) );

		/*Add to cost function*/
		Jelem+=misfit*weight*Jdet*gauss->weight;
	}

	/*clean up and Return: */
	delete gauss;
	return Jelem;
}
/*}}}*/
/*FUNCTION Tria::SurfaceRelVelMisfit {{{*/
IssmDouble Tria::SurfaceRelVelMisfit(int weight_index){

	IssmDouble  Jelem=0;
	IssmDouble  scalex=1,scaley=1;
	IssmDouble  misfit,Jdet;
	IssmDouble  epsvel=2.220446049250313e-16;
	IssmDouble  meanvel=3.170979198376458e-05; /*1000 m/yr*/
	IssmDouble  vx,vy,vxobs,vyobs,weight;
	IssmDouble  xyz_list[NUMVERTICES][3];
	GaussTria *gauss=NULL;

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

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

	/*Retrieve all inputs we will be needing: */
	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);

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

		gauss->GaussPoint(ig);

		/* Get Jacobian determinant: */
		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);

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

		/*Compute SurfaceRelVelMisfit:
		 *                        
		 *      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                      
		 */
		scalex=pow(meanvel/(vxobs+epsvel),2); if(vxobs==0)scalex=0;
		scaley=pow(meanvel/(vyobs+epsvel),2); if(vyobs==0)scaley=0;
		misfit=0.5*(scalex*pow((vx-vxobs),2)+scaley*pow((vy-vyobs),2));

		/*Add to cost function*/
		Jelem+=misfit*weight*Jdet*gauss->weight;
	}

	/*clean up and Return: */
	delete gauss;
	return Jelem;
}
/*}}}*/
/*FUNCTION Tria::ThicknessAbsGradient{{{*/
IssmDouble Tria::ThicknessAbsGradient(int weight_index){

	/* Intermediaries */
	IssmDouble Jelem = 0;
	IssmDouble weight;
	IssmDouble Jdet;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble dp[NDOF2];
	GaussTria *gauss = NULL;

	/*retrieve parameters and inputs*/

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

	/*Retrieve all inputs we will be needing: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	Input* weights_input  =inputs->GetInput(InversionCostFunctionsCoefficientsEnum);   _assert_(weights_input);
	Input* thickness_input=inputs->GetInput(ThicknessEnum); _assert_(thickness_input);

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

		gauss->GaussPoint(ig);

		/* Get Jacobian determinant: */
		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);

		/*Get all parameters at gaussian point*/
		weights_input->GetInputValue(&weight,gauss,weight_index);
		thickness_input->GetInputDerivativeValue(&dp[0],&xyz_list[0][0],gauss);

		/*Tikhonov regularization: J = 1/2 ((dp/dx)^2 + (dp/dy)^2) */ 
		Jelem+=weight*1/2*(dp[0]*dp[0]+dp[1]*dp[1])*Jdet*gauss->weight;
	}

	/*Clean up and return*/
	delete gauss;
	return Jelem;
}
/*}}}*/
/*FUNCTION Tria::ThicknessAlongGradient{{{*/
IssmDouble Tria::ThicknessAlongGradient(int weight_index){

	/* Intermediaries */
	IssmDouble  Jelem = 0;
	IssmDouble  weight;
	IssmDouble  Jdet;
	IssmDouble  xyz_list[NUMVERTICES][3];
	IssmDouble  dp[NDOF2];
	IssmDouble  vx,vy,vel;
	GaussTria  *gauss                    = NULL;

	/*retrieve parameters and inputs*/

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

	/*Retrieve all inputs we will be needing: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	Input* weights_input  = inputs->GetInput(InversionCostFunctionsCoefficientsEnum); _assert_(weights_input);
	Input* thickness_input= inputs->GetInput(ThicknessEnum);                          _assert_(thickness_input);
	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 GaussTria(2);
	for(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		/* Get Jacobian determinant: */
		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);

		/*Get all parameters at gaussian point*/
		weights_input->GetInputValue(&weight,gauss,weight_index);
		thickness_input->GetInputDerivativeValue(&dp[0],&xyz_list[0][0],gauss);
		vx_input->GetInputValue(&vx,gauss);
		vy_input->GetInputValue(&vy,gauss);
		vel = sqrt(vx*vx+vy*vy);
		vx  = vx/(vel+1.e-9);
		vy  = vy/(vel+1.e-9);

		/*J = 1/2 ( vx*dH/dx + vy*dH/dy )^2 */
		Jelem+=weight*1/2*(vx*dp[0] + vy*dp[1])*(vx*dp[0] + vy*dp[1])*Jdet*gauss->weight;
	}

	/*Clean up and return*/
	delete gauss;
	return Jelem;
}
/*}}}*/
/*FUNCTION Tria::ThicknessAcrossGradient{{{*/
IssmDouble Tria::ThicknessAcrossGradient(int weight_index){

	/* Intermediaries */
	IssmDouble  Jelem = 0;
	IssmDouble  weight;
	IssmDouble  Jdet;
	IssmDouble  xyz_list[NUMVERTICES][3];
	IssmDouble  dp[NDOF2];
	IssmDouble  vx,vy,vel;
	GaussTria  *gauss                    = NULL;

	/*retrieve parameters and inputs*/

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

	/*Retrieve all inputs we will be needing: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	Input* weights_input  = inputs->GetInput(InversionCostFunctionsCoefficientsEnum); _assert_(weights_input);
	Input* thickness_input= inputs->GetInput(ThicknessEnum);                          _assert_(thickness_input);
	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 GaussTria(2);
	for(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		/* Get Jacobian determinant: */
		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);

		/*Get all parameters at gaussian point*/
		weights_input->GetInputValue(&weight,gauss,weight_index);
		thickness_input->GetInputDerivativeValue(&dp[0],&xyz_list[0][0],gauss);
		vx_input->GetInputValue(&vx,gauss);
		vy_input->GetInputValue(&vy,gauss);
		vel = sqrt(vx*vx+vy*vy);
		vx  = vx/(vel+1.e-9);
		vy  = vy/(vel+1.e-9);

		/*J = 1/2 ( -vy*dH/dx + vx*dH/dy )^2 */
		Jelem+=weight*1/2*(-vy*dp[0] + vx*dp[1])*(-vy*dp[0] + vx*dp[1])*Jdet*gauss->weight;
	}

	/*Clean up and return*/
	delete gauss;
	return Jelem;
}
/*}}}*/
/*FUNCTION Tria::ThicknessAbsMisfit {{{*/
IssmDouble Tria::ThicknessAbsMisfit(int weight_index){

	/*Intermediaries*/
	IssmDouble thickness,thicknessobs,weight;
	IssmDouble Jdet;
	IssmDouble Jelem = 0;
	IssmDouble xyz_list[NUMVERTICES][3];
	GaussTria *gauss = NULL;
	IssmDouble dH[2];

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

	/*Retrieve all inputs we will be needing: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	Input* thickness_input   =inputs->GetInput(ThicknessEnum);   _assert_(thickness_input);
	Input* thicknessobs_input=inputs->GetInput(InversionThicknessObsEnum);_assert_(thicknessobs_input);
	Input* weights_input     =inputs->GetInput(InversionCostFunctionsCoefficientsEnum);     _assert_(weights_input);

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

		gauss->GaussPoint(ig);

		/* Get Jacobian determinant: */
		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);

		/*Get parameters at gauss point*/
		thickness_input->GetInputValue(&thickness,gauss);
		thickness_input->GetInputDerivativeValue(&dH[0],&xyz_list[0][0],gauss);
		thicknessobs_input->GetInputValue(&thicknessobs,gauss);
		weights_input->GetInputValue(&weight,gauss,weight_index);

		/*compute ThicknessAbsMisfit*/
		Jelem+=0.5*(thickness-thicknessobs)*(thickness-thicknessobs)*weight*Jdet*gauss->weight;
	}

	/* clean up and Return: */
	delete gauss;
	return Jelem;
}
/*}}}*/
/*FUNCTION Tria::CreatePVectorAdjointBalancethickness{{{*/
ElementVector* Tria::CreatePVectorAdjointBalancethickness(void){

	/*Intermediaries */
	int        i,resp;
	IssmDouble Jdet;
	IssmDouble thickness,thicknessobs,weight;
	int        num_responses;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble dH[2];
	IssmDouble vx,vy,vel;
	int       *responses = NULL;

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

	/*Initialize Element vector and vectors*/
	ElementVector* pe     = new ElementVector(nodes,numnodes,this->parameters);
	IssmDouble*    basis  = xNew<IssmDouble>(numnodes);
	IssmDouble*    dbasis = xNew<IssmDouble>(2*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* thickness_input    = inputs->GetInput(ThicknessEnum);                          _assert_(thickness_input);
	Input* thicknessobs_input = inputs->GetInput(InversionThicknessObsEnum);              _assert_(thicknessobs_input);
	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);

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

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctions(basis, gauss);
		GetNodalFunctionsDerivatives(dbasis,&xyz_list[0][0],gauss);

		thickness_input->GetInputValue(&thickness, gauss);
		thickness_input->GetInputDerivativeValue(&dH[0],&xyz_list[0][0],gauss);
		thicknessobs_input->GetInputValue(&thicknessobs, gauss);

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

			case ThicknessAbsMisfitEnum:
				weights_input->GetInputValue(&weight, gauss,resp);
				for(i=0;i<numnodes;i++) pe->values[i]+=(thicknessobs-thickness)*weight*Jdet*gauss->weight*basis[i];
				break;
			case ThicknessAbsGradientEnum:
				weights_input->GetInputValue(&weight, gauss,resp);
				for(i=0;i<numnodes;i++) pe->values[i]+= - weight*dH[0]*dbasis[0*numnodes+i]*Jdet*gauss->weight;
				for(i=0;i<numnodes;i++) pe->values[i]+= - weight*dH[1]*dbasis[1*numnodes+i]*Jdet*gauss->weight;
				break;
			case ThicknessAlongGradientEnum:
				weights_input->GetInputValue(&weight, gauss,resp);
				vx_input->GetInputValue(&vx,gauss);
				vy_input->GetInputValue(&vy,gauss);
				vel = sqrt(vx*vx+vy*vy);
				vx  = vx/(vel+1.e-9);
				vy  = vy/(vel+1.e-9);
				for(i=0;i<numnodes;i++) pe->values[i]+= - weight*(dH[0]*vx+dH[1]*vy)*(dbasis[0*numnodes+i]*vx+dbasis[1*numnodes+i]*vy)*Jdet*gauss->weight;
				break;
			case ThicknessAcrossGradientEnum:
				weights_input->GetInputValue(&weight, gauss,resp);
				vx_input->GetInputValue(&vx,gauss);
				vy_input->GetInputValue(&vy,gauss);
				vel = sqrt(vx*vx+vy*vy);
				vx  = vx/(vel+1.e-9);
				vy  = vy/(vel+1.e-9);
				for(i=0;i<numnodes;i++) pe->values[i]+= - weight*(dH[0]*(-vy)+dH[1]*vx)*(dbasis[0*numnodes+i]*(-vy)+dbasis[1*numnodes+i]*vx)*Jdet*gauss->weight;
				break;
			default:
				_error_("response " << EnumToStringx(responses[resp]) << " not supported yet");
		}
	}

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

	/*Intermediaries */
	int        i,resp;
	int       *responses=NULL;
	int        num_responses;
	IssmDouble Jdet;
	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];

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

	/*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: */
	GaussTria* gauss=new GaussTria(4);
	for(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

		/* Get Jacobian determinant: */
		GetJacobianDeterminant(&Jdet, &xyz_list[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*Jdet*gauss->weight*basis[i]; 
						pe->values[i*NDOF2+1]+=duy*weight*Jdet*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*Jdet*gauss->weight*basis[i]; 
						pe->values[i*NDOF2+1]+=duy*weight*Jdet*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*Jdet*gauss->weight*basis[i]; 
						pe->values[i*NDOF2+1]+=duy*weight*Jdet*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*Jdet*gauss->weight*basis[i]; 
						pe->values[i*NDOF2+1]+=duy*weight*Jdet*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*Jdet*gauss->weight*basis[i]; 
						pe->values[i*NDOF2+1]+=duy*weight*Jdet*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 Tria::DragCoefficientAbsGradient{{{*/
IssmDouble Tria::DragCoefficientAbsGradient(int weight_index){

	/* Intermediaries */
	IssmDouble Jelem = 0;
	IssmDouble weight;
	IssmDouble Jdet;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble dp[NDOF2];
	GaussTria *gauss = NULL;

	/*retrieve parameters and inputs*/

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

	/*Retrieve all inputs we will be needing: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	Input* weights_input=inputs->GetInput(InversionCostFunctionsCoefficientsEnum);         _assert_(weights_input);
	Input* drag_input   =inputs->GetInput(FrictionCoefficientEnum); _assert_(drag_input);

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

		gauss->GaussPoint(ig);

		/* Get Jacobian determinant: */
		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);

		/*Get all parameters at gaussian point*/
		weights_input->GetInputValue(&weight,gauss,weight_index);
		drag_input->GetInputDerivativeValue(&dp[0],&xyz_list[0][0],gauss);

		/*Tikhonov regularization: J = 1/2 ((dp/dx)^2 + (dp/dy)^2) */ 
		Jelem+=weight*1/2*(dp[0]*dp[0]+dp[1]*dp[1])*Jdet*gauss->weight;
	}

	/*Clean up and return*/
	delete gauss;
	return Jelem;
}
/*}}}*/
/*FUNCTION Tria::CreateKMatrixAdjointBalancethickness {{{*/
ElementMatrix* Tria::CreateKMatrixAdjointBalancethickness(void){

	ElementMatrix* Ke=NULL;

	/*Get Element Matrix of the forward model*/
	switch(GetElementType()){
		case P1Enum:
			Ke=CreateKMatrixBalancethickness_CG();
			break;
		case P1DGEnum:
			Ke=CreateKMatrixBalancethickness_DG();
			break;
		default:
			_error_("Element type " << EnumToStringx(GetElementType()) << " not supported yet");
	}

	/*Transpose and return Ke*/
	Ke->Transpose();
	return Ke;
}
/*}}}*/
/*FUNCTION Tria::CreateKMatrixAdjointSSA{{{*/
ElementMatrix* Tria::CreateKMatrixAdjointSSA(void){

	/*Intermediaries */
	int         i,j;
	bool        incomplete_adjoint;
	IssmDouble  xyz_list[NUMVERTICES][3];
	IssmDouble  Jdet,thickness;
	IssmDouble  eps1dotdphii,eps1dotdphij;
	IssmDouble  eps2dotdphii,eps2dotdphij;
	IssmDouble  mu_prime;
	IssmDouble  epsilon[3];/* epsilon=[exx,eyy,exy];*/
	IssmDouble  eps1[2],eps2[2];
	GaussTria  *gauss=NULL;

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

	/*Initialize Jacobian with regular SSA (first part of the Gateau derivative)*/
	parameters->FindParam(&incomplete_adjoint,InversionIncompleteAdjointEnum);
	ElementMatrix* Ke=CreateKMatrixStressbalanceSSA();
	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);
	Input* thickness_input=inputs->GetInput(ThicknessEnum); _assert_(thickness_input);

	/*Allocate dbasis*/
	IssmDouble* dbasis = xNew<IssmDouble>(2*numnodes);

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

		gauss->GaussPoint(ig);

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

		thickness_input->GetInputValue(&thickness, gauss);
		this->GetStrainRate2d(&epsilon[0],&xyz_list[0][0],gauss,vx_input,vy_input);
		material->GetViscosity2dDerivativeEpsSquare(&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];

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

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

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

	/*Clean up and return*/
	delete gauss;
	xDelete<IssmDouble>(dbasis);
	//Ke->Transpose();
	return Ke;
}
/*}}}*/
/*FUNCTION Tria::InputUpdateFromSolutionAdjointHoriz {{{*/
void  Tria::InputUpdateFromSolutionAdjointHoriz(IssmDouble* solution){

	int  i;
	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,NoneApproximationEnum,GsetEnum);
	IssmDouble* values  = xNew<IssmDouble>(numdof);
	IssmDouble* lambdax = xNew<IssmDouble>(numnodes);
	IssmDouble* lambday = 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,numnodes,XYEnum);

	/*Ok, we have vx and vy in values, fill in vx and vy arrays: */
	for(i=0;i<numnodes;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 TriaInput(AdjointxEnum,lambdax,P1Enum));
	this->inputs->AddInput(new TriaInput(AdjointyEnum,lambday,P1Enum));

	/*Free ressources:*/
	xDelete<IssmDouble>(values);
	xDelete<IssmDouble>(lambdax);
	xDelete<IssmDouble>(lambday);
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Tria::GetVectorFromControlInputs{{{*/
void  Tria::GetVectorFromControlInputs(Vector<IssmDouble>* vector,int control_enum,int control_index,const char* data){

	int vertexpidlist[NUMVERTICES];
	Input *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 input (either in element or material)*/
	if(control_enum==MaterialsRheologyBbarEnum || control_enum==MaterialsRheologyZbarEnum){
		input=(Input*)material->inputs->GetInput(control_enum); _assert_(input);
	}
	else{
		input=(Input*)this->inputs->GetInput(control_enum);   _assert_(input);
	}

	/*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 Tria::SetControlInputsFromVector{{{*/
void  Tria::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 TriaInput(control_enum,values,P1Enum);

	if(control_enum==MaterialsRheologyBbarEnum || control_enum==MaterialsRheologyZbarEnum){
		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_THERMAL_
/*FUNCTION Tria::CreateKMatrixMelting {{{*/
ElementMatrix* Tria::CreateKMatrixMelting(void){

	/*Intermediaries */
	IssmDouble heatcapacity,latentheat;
	IssmDouble Jdet,D_scalar;
	IssmDouble xyz_list[NUMVERTICES][3];

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

	/*Initialize Element vector and vectors*/
	ElementMatrix* Ke=new ElementMatrix(nodes,numnodes,this->parameters,NoneApproximationEnum);
	IssmDouble*    basis = xNew<IssmDouble>(numnodes);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	latentheat   = matpar->GetLatentHeat();
	heatcapacity = matpar->GetHeatCapacity();

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

		gauss->GaussPoint(ig);

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

		D_scalar=latentheat/heatcapacity*gauss->weight*Jdet;

		TripleMultiply(basis,numnodes,1,0,
					&D_scalar,1,1,0,
					basis,1,numnodes,0,
					&Ke->values[0],1);
	}

	/*Clean up and return*/
	xDelete<IssmDouble>(basis);
	delete gauss;
	return Ke;
}
/*}}}*/
#endif

#ifdef _HAVE_HYDROLOGY_
/*FUNCTION Tria::AllActive{{{*/
bool Tria::AllActive(void){

	/*Intermediaries*/
	const int  numnodes = NUMVERTICES;

	for(int i=0;i<numnodes;i++){
		if(!this->nodes[i]->IsActive()) return false;
	}

	return true;
}
/*}}}*/
/*FUNCTION Tria::AnyActive{{{*/
bool Tria::AnyActive(void){

	/*Intermediaries*/
	const int  numnodes = NUMVERTICES;

	for(int i=0;i<numnodes;i++){
		if(this->nodes[i]->IsActive()) return true;
	}

	return false;
}/*}}}*/
/*FUNCTION Tria::CreateHydrologyWaterVelocityInput {{{*/
void Tria::CreateHydrologyWaterVelocityInput(void){

	/*material parameters: */
	IssmDouble mu_water;
	IssmDouble VelocityFactor;  // This factor represents the number 12 in laminar flow velocity which can vary by differnt hydrology.CR
	IssmDouble n_man,CR;
	IssmDouble w;
	IssmDouble rho_ice, rho_water, g;
	IssmDouble dsdx,dsdy,dbdx,dbdy;
	IssmDouble vx[NUMVERTICES];
	IssmDouble vy[NUMVERTICES];
	GaussTria *gauss = NULL;

	/*Retrieve all inputs and parameters*/
	rho_ice=matpar->GetRhoIce();
	rho_water=matpar->GetRhoWater();
	g=matpar->GetG();
	CR=matpar->GetHydrologyCR(); // To have Lebrocq equavalent equation: CR=0.01,n_man=0.02
	n_man=matpar->GetHydrologyN(); 
	mu_water=matpar->GetMuWater();
	Input* surfaceslopex_input=inputs->GetInput(SurfaceSlopeXEnum); _assert_(surfaceslopex_input);
	Input* surfaceslopey_input=inputs->GetInput(SurfaceSlopeYEnum); _assert_(surfaceslopey_input);
	Input* bedslopex_input=inputs->GetInput(BedSlopeXEnum);         _assert_(bedslopex_input);
	Input* bedslopey_input=inputs->GetInput(BedSlopeYEnum);         _assert_(bedslopey_input);
	Input* watercolumn_input=inputs->GetInput(WatercolumnEnum);     _assert_(watercolumn_input);

	/* compute VelocityFactor */
	VelocityFactor= n_man*CR*CR*rho_water*g/mu_water;

	gauss=new GaussTria();
	for(int iv=0;iv<NUMVERTICES;iv++){
		gauss->GaussVertex(iv);
		surfaceslopex_input->GetInputValue(&dsdx,gauss);
		surfaceslopey_input->GetInputValue(&dsdy,gauss);
		bedslopex_input->GetInputValue(&dbdx,gauss);
		bedslopey_input->GetInputValue(&dbdy,gauss);
		watercolumn_input->GetInputValue(&w,gauss);

		/* Water velocity x and y components */
	//	vx[iv]= - w*w/(12 * mu_water)*(rho_ice*g*dsdx+(rho_water-rho_ice)*g*dbdx);
	//	vy[iv]= - w*w/(12 * mu_water)*(rho_ice*g*dsdy+(rho_water-rho_ice)*g*dbdy);
		vx[iv]= - w*w/(VelocityFactor* mu_water)*(rho_ice*g*dsdx+(rho_water-rho_ice)*g*dbdx);
		vy[iv]= - w*w/(VelocityFactor* mu_water)*(rho_ice*g*dsdy+(rho_water-rho_ice)*g*dbdy);
	}

	/*clean-up*/
	delete gauss;

	/*Add to inputs*/
	this->inputs->AddInput(new TriaInput(HydrologyWaterVxEnum,vx,P1Enum));
	this->inputs->AddInput(new TriaInput(HydrologyWaterVyEnum,vy,P1Enum));
}
/*}}}*/
/*FUNCTION Tria::CreateKMatrixHydrologyShreve{{{*/
ElementMatrix* Tria::CreateKMatrixHydrologyShreve(void){

	/*Intermediaries */
	IssmDouble diffusivity;
	IssmDouble Jdet,D_scalar,dt,h;
	IssmDouble vx,vy,vel,dvxdx,dvydy;
	IssmDouble dvx[2],dvy[2];
	IssmDouble xyz_list[NUMVERTICES][3];

	/*Skip if water or ice shelf element*/
	if(NoIceInElement() | IsFloating()) return NULL;

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

	/*Initialize Element matrix and vectors*/
	ElementMatrix* Ke     = new ElementMatrix(nodes,numnodes,this->parameters,NoneApproximationEnum);
	IssmDouble*    basis  = xNew<IssmDouble>(numnodes);
	IssmDouble*    B      = xNew<IssmDouble>(2*numnodes);
	IssmDouble*    Bprime = xNew<IssmDouble>(2*numnodes);
	IssmDouble     D[2][2];

	/*Create water velocity vx and vy from current inputs*/
	CreateHydrologyWaterVelocityInput();

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	this->parameters->FindParam(&diffusivity,HydrologyshreveStabilizationEnum);
	Input* vx_input=inputs->GetInput(HydrologyWaterVxEnum); _assert_(vx_input);
	Input* vy_input=inputs->GetInput(HydrologyWaterVyEnum); _assert_(vy_input);
	h=sqrt(2*this->GetArea());

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

		gauss->GaussPoint(ig);

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

		vx_input->GetInputValue(&vx,gauss);
		vy_input->GetInputValue(&vy,gauss);
		vx_input->GetInputDerivativeValue(&dvx[0],&xyz_list[0][0],gauss);
		vy_input->GetInputDerivativeValue(&dvy[0],&xyz_list[0][0],gauss);

		D_scalar=gauss->weight*Jdet;

		TripleMultiply(basis,1,numnodes,1,
					&D_scalar,1,1,0,
					basis,1,numnodes,0,
					Ke->values,1);

		GetBMasstransport(B,&xyz_list[0][0], gauss);
		GetBprimeMasstransport(Bprime,&xyz_list[0][0], gauss);

		dvxdx=dvx[0];
		dvydy=dvy[1];
		D_scalar=dt*gauss->weight*Jdet;

		D[0][0]=D_scalar*dvxdx;
		D[0][1]=0.;
		D[1][0]=0.;
		D[1][1]=D_scalar*dvydy;
		TripleMultiply(B,2,numnodes,1,
					&D[0][0],2,2,0,
					B,2,numnodes,0,
					&Ke->values[0],1);

		D[0][0]=D_scalar*vx;
		D[1][1]=D_scalar*vy;
		TripleMultiply(B,2,numnodes,1,
					&D[0][0],2,2,0,
					Bprime,2,numnodes,0,
					&Ke->values[0],1);

		/*Artificial diffusivity*/
		vel=sqrt(vx*vx+vy*vy);
		D[0][0]=D_scalar*diffusivity*h/(2*vel)*vx*vx;
		D[1][0]=D_scalar*diffusivity*h/(2*vel)*vy*vx;
		D[0][1]=D_scalar*diffusivity*h/(2*vel)*vx*vy;
		D[1][1]=D_scalar*diffusivity*h/(2*vel)*vy*vy;
		TripleMultiply(Bprime,2,numnodes,1,
					&D[0][0],2,2,0,
					Bprime,2,numnodes,0,
					&Ke->values[0],1);
	}

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

	/* Intermediaries */
	IssmDouble  D_scalar,Jdet;
	IssmDouble 	sediment_transmitivity,dt;
	IssmDouble  sediment_storing;
	IssmDouble  xyz_list[NUMVERTICES][3];

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

	/*Initialize Element matrix and vectors*/
	ElementMatrix* Ke     = new ElementMatrix(nodes,numnodes,this->parameters);
	IssmDouble*    B      = xNew<IssmDouble>(5*numnodes);
	IssmDouble*    basis  = xNew<IssmDouble>(numnodes);
	IssmDouble     D[2][2];

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	sediment_storing       = matpar->GetSedimentStoring();
	sediment_transmitivity = matpar->GetSedimentTransmitivity();

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

		gauss->GaussPoint(ig);

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

		/*Diffusivity*/
		D_scalar=sediment_transmitivity*gauss->weight*Jdet;
		if(reCast<bool,IssmDouble>(dt)) D_scalar=D_scalar*dt;
		D[0][0]=D_scalar; D[0][1]=0.;
		D[1][0]=0.;       D[1][1]=D_scalar;
		GetBHydro(B,&xyz_list[0][0],gauss); 
		TripleMultiply(B,2,numnodes,1,
					&D[0][0],2,2,0,
					B,2,numnodes,0,
					&Ke->values[0],1);

		/*Transient*/
		if(reCast<bool,IssmDouble>(dt)){
			GetNodalFunctions(&basis[0],gauss);
			D_scalar=sediment_storing*gauss->weight*Jdet;

			TripleMultiply(basis,numnodes,1,0,
						&D_scalar,1,1,0,
						basis,1,numnodes,0,
						&Ke->values[0],1);
		}
	}
	/*Clean up and return*/
	xDelete<IssmDouble>(basis);
	xDelete<IssmDouble>(B);
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Tria::CreateKMatrixHydrologyDCEfficient{{{*/
ElementMatrix* Tria::CreateKMatrixHydrologyDCEfficient(void){

	/* Intermediaries */
	IssmDouble  D_scalar,Jdet;
	IssmDouble 	epl_transmitivity,dt;
	IssmDouble  epl_storing;
	IssmDouble  xyz_list[NUMVERTICES][3];

	/*Check that all nodes are active, else return empty matrix*/
	if(!this->AllActive()){
		return NULL;
	}

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

	/*Initialize Element matrix and vectors*/
	ElementMatrix* Ke     = new ElementMatrix(nodes,numnodes,this->parameters);
	IssmDouble*    B      = xNew<IssmDouble>(5*numnodes);
	IssmDouble*    basis  = xNew<IssmDouble>(numnodes);
	IssmDouble     D[2][2];

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	epl_storing       = matpar->GetEplStoring();
	epl_transmitivity = matpar->GetEplTransmitivity();

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

		gauss->GaussPoint(ig);
		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);

		/*Diffusivity*/
		D_scalar=epl_transmitivity*gauss->weight*Jdet;
		if(reCast<bool,IssmDouble>(dt)) D_scalar=D_scalar*dt;
		D[0][0]=D_scalar; D[0][1]=0.;
		D[1][0]=0.;       D[1][1]=D_scalar;
		GetBHydro(B,&xyz_list[0][0],gauss); 
		TripleMultiply(B,2,numnodes,1,
					&D[0][0],2,2,0,
					B,2,numnodes,0,
					&Ke->values[0],1);

		/*Transient*/
		if(reCast<bool,IssmDouble>(dt)){
			GetNodalFunctions(basis,gauss);
			D_scalar=epl_storing*gauss->weight*Jdet;

			TripleMultiply(basis,numnodes,1,0,
						&D_scalar,1,1,0,
						basis,1,numnodes,0,
						&Ke->values[0],1);
		}
	}

	/*Clean up and return*/
	xDelete<IssmDouble>(basis);
	xDelete<IssmDouble>(B);
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Tria::CreatePVectorHydrologyShreve {{{*/
ElementVector* Tria::CreatePVectorHydrologyShreve(void){

	/*Intermediaries */
	int        i;
	IssmDouble Jdet,dt;
	IssmDouble basal_melting_g;
	IssmDouble old_watercolumn_g;
	IssmDouble xyz_list[NUMVERTICES][3];

	/*Skip if water or ice shelf element*/
	if(NoIceInElement() | IsFloating()) return NULL;

	/*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 = xNewZeroInit<IssmDouble>(numnodes);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	Input* basal_melting_input=inputs->GetInput(BasalforcingsMeltingRateEnum); _assert_(basal_melting_input);
	Input* old_watercolumn_input=inputs->GetInput(WaterColumnOldEnum);         _assert_(old_watercolumn_input);

	/*Initialize basal_melting_correction_g to 0, do not forget!:*/
	/* Start  looping on the number of gaussian points: */
	GaussTria* gauss=new GaussTria(2);
	for(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

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

		basal_melting_input->GetInputValue(&basal_melting_g,gauss);
		old_watercolumn_input->GetInputValue(&old_watercolumn_g,gauss);

		if(reCast<int,IssmDouble>(dt)){
			for(i=0;i<numnodes;i++) pe->values[i]+=Jdet*gauss->weight*(old_watercolumn_g+dt*basal_melting_g)*basis[i];
		}
		else{
			for(i=0;i<numnodes;i++) pe->values[i]+=Jdet*gauss->weight*basal_melting_g*basis[i];
		}
	}

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

	/*Intermediaries */
	IssmDouble Jdet;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble dt,scalar,water_head;
	IssmDouble water_load,transfer;
	IssmDouble sediment_storing;

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

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

	/*Retrieve all inputs and parameters*/
	sediment_storing = matpar->GetSedimentStoring();
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	Input* water_input=inputs->GetInput(BasalforcingsMeltingRateEnum);  _assert_(water_input);
	Input* transfer_input=inputs->GetInput(WaterTransferEnum);  _assert_(transfer_input);
	Input* old_wh_input=NULL; 

	if(reCast<bool,IssmDouble>(dt)){
		old_wh_input=inputs->GetInput(SedimentHeadOldEnum); _assert_(old_wh_input);
	}

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

		gauss->GaussPoint(ig);
		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctions(basis, gauss);

		/*Loading term*/
		water_input->GetInputValue(&water_load,gauss);
		transfer_input->GetInputValue(&transfer,gauss);
		scalar = Jdet*gauss->weight*(water_load+transfer);
		if(reCast<bool,IssmDouble>(dt)) scalar = scalar*dt;
		for(int i=0;i<numnodes;i++) pe->values[i]+=scalar*basis[i];

		/*Transient term*/
		if(reCast<bool,IssmDouble>(dt)){
			old_wh_input->GetInputValue(&water_head,gauss);
			scalar = Jdet*gauss->weight*water_head*sediment_storing;
			for(int i=0;i<numnodes;i++) pe->values[i]+=scalar*basis[i];
		}
	}

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

	/*Intermediaries */
	IssmDouble connectivity;
	IssmDouble Jdet;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble dt,scalar,water_head;
	IssmDouble transfer,residual;
	IssmDouble epl_storing;
	GaussTria* gauss = NULL;

	/*Check that all nodes are active, else return empty matrix*/
	if(!this->AllActive()){
		return NULL;
	}

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

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

	/*Retrieve all inputs and parameters*/
	epl_storing = matpar->GetEplStoring();
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	Input* residual_input=inputs->GetInput(SedimentHeadResidualEnum);  _assert_(residual_input);
	Input* transfer_input=inputs->GetInput(WaterTransferEnum);  _assert_(transfer_input);
	Input* old_wh_input=NULL; 

	if(reCast<bool,IssmDouble>(dt)){
		old_wh_input=inputs->GetInput(EplHeadOldEnum); _assert_(old_wh_input);
	}

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

		gauss->GaussPoint(ig);
		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctions(basis,gauss);

		/*Loading term*/
		transfer_input->GetInputValue(&transfer,gauss);
		scalar = Jdet*gauss->weight*(-transfer);
		if(reCast<bool,IssmDouble>(dt)) scalar = scalar*dt;
		for(int i=0;i<numnodes;i++) pe->values[i]+=scalar*basis[i];

		/*Transient term*/
		if(reCast<bool,IssmDouble>(dt)){
			old_wh_input->GetInputValue(&water_head,gauss);
			scalar = Jdet*gauss->weight*water_head*epl_storing;
			for(int i=0;i<numnodes;i++) pe->values[i]+=scalar*basis[i];
		}
	}
	delete gauss;

	/*	Add residual if necessary*/
	gauss=new GaussTria();
	for(int iv=0;iv<NUMVERTICES;iv++){
		gauss->GaussVertex(iv);

		connectivity = IssmDouble(vertices[iv]->Connectivity());
		residual_input->GetInputValue(&residual,gauss);
		pe->values[iv]+=residual/connectivity;
	}

	/*Clean up and return*/
	xDelete<IssmDouble>(basis);
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Tria::GetSolutionFromInputsOneDof{{{*/
void  Tria::GetSolutionFromInputsOneDof(Vector<IssmDouble>* solution, int enum_type){

	int        *doflist = NULL;
	IssmDouble  value;

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

	/*Fetch dof list and allocate solution vector*/
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);
	IssmDouble* values = xNew<IssmDouble>(numnodes);

	/*Get inputs*/
	Input* enum_input=inputs->GetInput(enum_type); _assert_(enum_input);

	/*Ok, we have the values, fill in the array: */
	GaussTria* gauss=new GaussTria();
	for(int i=0;i<numnodes;i++){
		gauss->GaussNode(this->element_type,i);

		enum_input->GetInputValue(&value,gauss);
		values[i]=value;
	}

	solution->SetValues(numnodes,doflist,values,INS_VAL);

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

	/*Intermediary*/
	int* doflist = NULL;

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

	/*Fetch dof list and allocate solution vector*/
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);
	IssmDouble* values    = xNew<IssmDouble>(numnodes);

	/*Use the dof list to index into the solution vector: */
	for(int i=0;i<numnodes;i++){
		values[i]=solution[doflist[i]];
		if(xIsNan<IssmDouble>(values[i])) _error_("NaN found in solution vector");
		if (values[i]<10e-10) values[i]=10e-10; //correcting the water column to positive values
	}

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

	/*Free ressources:*/
	xDelete<IssmDouble>(values);
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Tria::InputUpdateFromSolutionHydrologyDCInefficient{{{*/
void  Tria::InputUpdateFromSolutionHydrologyDCInefficient(IssmDouble* solution){

	/*Intermediaries*/
	int        *doflist  = NULL;
	bool        converged;
	IssmDouble  penalty_factor, dt;
	IssmDouble  kmax, kappa, h_max;

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

	/*Fetch dof list and allocate solution vector*/
	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);
	IssmDouble* values   = xNew<IssmDouble>(numnodes);
	IssmDouble* residual = xNew<IssmDouble>(numnodes);

	/*Use the dof list to index into the solution vector: */
	for(int i=0;i<numnodes;i++){
		values[i]=solution[doflist[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);

		for(int i=0;i<numnodes;i++){
			this->GetHydrologyDCInefficientHmax(&h_max,nodes[i]);
			if(values[i]>h_max){
				residual[i]=kappa*(values[i]-h_max);
			}
			else{
				residual[i]=0.0;
			}
		}
		this->inputs->AddInput(new TriaInput(SedimentHeadResidualEnum,residual,P1Enum));
	}

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

	/*Free ressources:*/
	xDelete<IssmDouble>(values);
	xDelete<IssmDouble>(residual);
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Tria::GetHydrologyDCInefficientHmax{{{*/
void  Tria::GetHydrologyDCInefficientHmax(IssmDouble* ph_max, Node* innode){

		int        hmax_flag;
		IssmDouble h_max;
		IssmDouble rho_ice,rho_water;
		IssmDouble thickness,bed;
		/*Get the flag to the limitation method*/
		this->parameters->FindParam(&hmax_flag,HydrologydcSedimentlimitFlagEnum);

		/*Switch between the different cases*/
		switch(hmax_flag){
			case 0:
				h_max=1.0e+10;
				break;
			case 1:
				parameters->FindParam(&h_max,HydrologydcSedimentlimitEnum);
				break;
			case 2:
				rho_ice=matpar->GetRhoIce();
				rho_water=matpar->GetRhoFreshwater();
				this->GetInputValue(&thickness,innode,ThicknessEnum);
				this->GetInputValue(&bed,innode,BedEnum);
				h_max=((rho_ice*thickness)/rho_water)+bed;
				break;
			case 3:
				_error_("Using normal stress  not supported yet");
				break;
			default:
				_error_("no case higher than 3 for SedimentlimitFlag");
		}
		/*Assign output pointer*/
		*ph_max=h_max;
}
/*}}}*/
/*FUNCTION Tria::GetHydrologyTransfer{{{*/
void  Tria::GetHydrologyTransfer(Vector<IssmDouble>* transfer){

	const int  numdof   = NDOF1 *NUMVERTICES;
	int        *doflist = NULL;
	bool       isefficientlayer;
	int        transfermethod;
	IssmDouble sed_trans,sed_thick;
	IssmDouble leakage,h_max;
	IssmDouble wh_trans;
	IssmDouble activeEpl[numdof];
	IssmDouble eplstoring[numdof],sedstoring[numdof];
	IssmDouble epl_head[numdof],sed_head[numdof];

	GetDofList(&doflist,NoneApproximationEnum,GsetEnum);

	/*Get the flag to know if the efficient layer is present*/
	this->parameters->FindParam(&isefficientlayer,HydrologydcIsefficientlayerEnum);

	if(isefficientlayer){
		/*Also get the flag to the transfer method*/
		this->parameters->FindParam(&transfermethod,HydrologydcTransferFlagEnum);

		/*Switch between the different transfer methods cases*/
		switch(transfermethod){
		case 0:
			/*Just keepping the transfer to zero, should be OK with the initial value of transfer*/
			break;
		case 1:

			GetInputListOnVertices(&activeEpl[0],HydrologydcMaskEplactiveEnum);
			GetInputListOnVertices(&sed_head[0],SedimentHeadEnum); 
			GetInputListOnVertices(&epl_head[0],EplHeadEnum);

			this->parameters->FindParam(&leakage,HydrologydcLeakageFactorEnum);

			sed_trans = matpar->GetSedimentTransmitivity();
			sed_thick = matpar->GetSedimentThickness();

			for(int i=0;i<numdof;i++){
				/*No transfer if the EPL is not active*/
				if(activeEpl[i]==0.0){
					wh_trans=0.0;
				}
				else{
					eplstoring[i]=matpar->GetEplStoring();		
					sedstoring[i]=matpar->GetSedimentStoring();

					/*EPL head higher than sediment head, transfer from the epl to the sediment*/
					if(epl_head[i]>sed_head[i]){
						wh_trans=eplstoring[i]*sed_trans*(epl_head[i]-sed_head[i])/(leakage*sed_thick);				

						/*No transfer if the sediment head is allready at the maximum*/
						this->GetHydrologyDCInefficientHmax(&h_max,nodes[i]);
						if(sed_head[i]>=h_max)wh_trans=0.0;
					}
					/*EPL head lower than sediment head, transfer from the sediment to the epl*/
					else if(epl_head[i]<=sed_head[i]){
						wh_trans=sedstoring[i]*sed_trans*(epl_head[i]-sed_head[i])/(leakage*sed_thick);				
					}
				}
				/*Assign output pointer*/
				transfer->SetValue(doflist[i],wh_trans,INS_VAL);
			}
			break;
		default:
			_error_("no case higher than 1 for the Transfer method");
		}
	}
	/*Free ressources:*/
	xDelete<int>(doflist);
}
/*}}}*/
/*FUNCTION Tria::HydrologyEPLGetActive {{{*/
void Tria::HydrologyEPLGetActive(Vector<IssmDouble>* active_vec){

	/*Constants*/
	const int  numnodes = NUMVERTICES;
	IssmDouble flag     = 0.;
	IssmDouble active[numnodes];

	GetInputListOnVertices(&active[0],HydrologydcMaskEplactiveEnum);

	for(int i=0;i<numnodes;i++) flag+=active[i];

	if(flag>0.){
		for(int i=0;i<numnodes;i++){
			active_vec->SetValue(nodes[i]->Sid(),1.,INS_VAL);
		}
	}
	else{
		/*Do not do anything: at least one node is active for this element but this element is not solved for*/
	}

}
/*}}}*/
/*FUNCTION Tria::HydrologyEPLGetMask{{{*/
void  Tria::HydrologyEPLGetMask(Vector<IssmDouble>* vec_mask){

	/*Intermediaries*/
	int         i,j;
	const int   numdof         = NDOF1 *NUMVERTICES;
	IssmDouble  h_max;
	IssmDouble  sedheadmin;
	IssmDouble  old_active[numdof];
	IssmDouble  sedhead[numdof];
	IssmDouble  eplhead[numdof];
	IssmDouble  residual[numdof];

	GetInputListOnVertices(&old_active[0],HydrologydcMaskEplactiveEnum);	
	GetInputListOnVertices(&sedhead[0],SedimentHeadEnum);
	GetInputListOnVertices(&eplhead[0],EplHeadEnum);
	GetInputListOnVertices(&residual[0],SedimentHeadResidualEnum);

	/*Get minimum sediment head*/
	sedheadmin=sedhead[0];
	for(i=1;i<numdof;i++) if(sedhead[i]<=sedheadmin)sedheadmin=sedhead[i];

	for(i=0;i<numdof;i++){
		/*Activate EPL if residual is >0 */
		if(residual[i]>0.){
			vec_mask->SetValue(nodes[i]->Sid(),1.,INS_VAL);
		}

		/*If mask was alread one, keep one*/
		else if(old_active[i]>0.){
			vec_mask->SetValue(nodes[i]->Sid(),1.,INS_VAL);
		}

		/*Increase of the efficient system is needed if the epl head reach the maximum value (sediment max value for now)*/
		this->GetHydrologyDCInefficientHmax(&h_max,nodes[i]);
		if(eplhead[i]>=h_max && this->AnyActive()){
			for(j=0;j<numdof;j++){
				if(old_active[j]>0.){
					vec_mask->SetValue(nodes[i]->Sid(),1.,INS_VAL);
				}
				/*Increase of the domain is on the downstream node in term of sediment head*/
				if(sedhead[j] == sedheadmin){
					vec_mask->SetValue(nodes[j]->Sid(),1.,INS_VAL);
					//	break;
				}
			}
		}
	}
}
/*}}}*/
#endif

#ifdef _HAVE_MASSTRANSPORT_
/*FUNCTION Tria::CreateKMatrixMasstransport {{{*/
ElementMatrix* Tria::CreateKMatrixMasstransport(void){

	switch(GetElementType()){
		case P1Enum: case P2Enum:
			return CreateKMatrixMasstransport_CG();
		case P1DGEnum:
			return CreateKMatrixMasstransport_DG();
		default:
			_error_("Element type " << EnumToStringx(GetElementType()) << " not supported yet");
	}
}
/*}}}*/
/*FUNCTION Tria::CreateKMatrixMasstransport_CG {{{*/
ElementMatrix* Tria::CreateKMatrixMasstransport_CG(void){

	/*Intermediaries */
	int        stabilization;
	int        dim;
	IssmDouble Jdet,D_scalar,dt,h;
	IssmDouble vel,vx,vy,dvxdx,dvydy;
	IssmDouble dvx[2],dvy[2];
	IssmDouble xyz_list[NUMVERTICES][3];

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

	/*Initialize Element matrix and vectors*/
	ElementMatrix* Ke     = new ElementMatrix(nodes,numnodes,this->parameters,NoneApproximationEnum);
	IssmDouble*    basis  = xNew<IssmDouble>(numnodes);
	IssmDouble*    B      = xNew<IssmDouble>(2*numnodes);
	IssmDouble*    Bprime = xNew<IssmDouble>(2*numnodes);
	IssmDouble     D[2][2];

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	this->parameters->FindParam(&dim,MeshDimensionEnum);
	this->parameters->FindParam(&stabilization,MasstransportStabilizationEnum);
	Input* vxaverage_input=NULL;
	Input* vyaverage_input=NULL;
	if(dim==2){
		vxaverage_input=inputs->GetInput(VxEnum); _assert_(vxaverage_input);
		vyaverage_input=inputs->GetInput(VyEnum); _assert_(vyaverage_input);
	}
	else{
		vxaverage_input=inputs->GetInput(VxAverageEnum); _assert_(vxaverage_input);
		vyaverage_input=inputs->GetInput(VyAverageEnum); _assert_(vyaverage_input);
	}
	h=sqrt(2*this->GetArea());

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

		gauss->GaussPoint(ig);

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

		vxaverage_input->GetInputValue(&vx,gauss);
		vyaverage_input->GetInputValue(&vy,gauss);
		vxaverage_input->GetInputDerivativeValue(&dvx[0],&xyz_list[0][0],gauss);
		vyaverage_input->GetInputDerivativeValue(&dvy[0],&xyz_list[0][0],gauss);

		D_scalar=gauss->weight*Jdet;

		TripleMultiply(basis,1,numnodes,1,
					&D_scalar,1,1,0,
					basis,1,numnodes,0,
					&Ke->values[0],1);

		GetBMasstransport(B,&xyz_list[0][0],gauss);
		GetBprimeMasstransport(Bprime,&xyz_list[0][0],gauss);

		dvxdx=dvx[0];
		dvydy=dvy[1];
		D_scalar=dt*gauss->weight*Jdet;

		D[0][0]=D_scalar*dvxdx;
		D[0][1]=0.;
		D[1][0]=0.;
		D[1][1]=D_scalar*dvydy;
		TripleMultiply(B,2,numnodes,1,
					&D[0][0],2,2,0,
					B,2,numnodes,0,
					&Ke->values[0],1);

		D[0][0]=D_scalar*vx;
		D[1][1]=D_scalar*vy;
		TripleMultiply(B,2,numnodes,1,
					&D[0][0],2,2,0,
					Bprime,2,numnodes,0,
					&Ke->values[0],1);

		if(stabilization==2){
			/*Streamline upwinding*/
			vel=sqrt(vx*vx+vy*vy)+1.e-8;
			D[0][0]=h/(2*vel)*vx*vx;
			D[1][0]=h/(2*vel)*vy*vx;
			D[0][1]=h/(2*vel)*vx*vy;
			D[1][1]=h/(2*vel)*vy*vy;
		}
		else if(stabilization==1){
			/*SSA*/
			vxaverage_input->GetInputAverage(&vx);
			vyaverage_input->GetInputAverage(&vy);
			D[0][0]=h/2.0*fabs(vx);
			D[0][1]=0.;
			D[1][0]=0.;
			D[1][1]=h/2.0*fabs(vy);
		}
		if(stabilization==1 || stabilization==2){
			D[0][0]=D_scalar*D[0][0];
			D[1][0]=D_scalar*D[1][0];
			D[0][1]=D_scalar*D[0][1];
			D[1][1]=D_scalar*D[1][1];
			TripleMultiply(Bprime,2,numnodes,1,
						&D[0][0],2,2,0,
						Bprime,2,numnodes,0,
						&Ke->values[0],1);
		}
	}

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

	/*Intermediaries */
	int        dim;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble Jdet,D_scalar,dt,vx,vy;

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

	/*Initialize Element matrix and vectors*/
	ElementMatrix* Ke     = new ElementMatrix(nodes,numnodes,this->parameters,NoneApproximationEnum);
	IssmDouble*    basis  = xNew<IssmDouble>(numnodes);
	IssmDouble*    B      = xNew<IssmDouble>(2*numnodes);
	IssmDouble*    Bprime = xNew<IssmDouble>(2*numnodes);
	IssmDouble     D[2][2];

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	this->parameters->FindParam(&dim,MeshDimensionEnum);
	Input* vxaverage_input=NULL;
	Input* vyaverage_input=NULL;
	if(dim==2){
		vxaverage_input=inputs->GetInput(VxEnum); _assert_(vxaverage_input);
		vyaverage_input=inputs->GetInput(VyEnum); _assert_(vyaverage_input);
	}
	else{
		vxaverage_input=inputs->GetInput(VxAverageEnum); _assert_(vxaverage_input);
		vyaverage_input=inputs->GetInput(VyAverageEnum); _assert_(vyaverage_input);
	}

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

		gauss->GaussPoint(ig);

		vxaverage_input->GetInputValue(&vx,gauss);
		vyaverage_input->GetInputValue(&vy,gauss);

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

		D_scalar=gauss->weight*Jdet;

		TripleMultiply(basis,1,numnodes,1,
					&D_scalar,1,1,0,
					basis,1,numnodes,0,
					&Ke->values[0],1);

		/*WARNING: B and Bprime are inverted compared to usual masstransport!!!!*/
		GetBMasstransport(Bprime, &xyz_list[0][0], gauss);
		GetBprimeMasstransport(B, &xyz_list[0][0], gauss);

		D_scalar=-dt*gauss->weight*Jdet;
		D[0][0]=D_scalar*vx;
		D[0][1]=0.;
		D[1][0]=0.;
		D[1][1]=D_scalar*vy;

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

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

	/*Intermediaries */
	int        stabilization;
	int        dim;
	IssmDouble Jdet,D_scalar,dt,h;
	IssmDouble vel,vx,vy;
	IssmDouble xyz_list[NUMVERTICES][3];

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

	/*Initialize Element matrix and vectors*/
	ElementMatrix* Ke     = new ElementMatrix(nodes,numnodes,this->parameters,NoneApproximationEnum);
	IssmDouble*    basis  = xNew<IssmDouble>(numnodes);
	IssmDouble*    B      = xNew<IssmDouble>(2*numnodes);
	IssmDouble*    Bprime = xNew<IssmDouble>(2*numnodes);
	IssmDouble     D[2][2];

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	this->parameters->FindParam(&stabilization,MasstransportStabilizationEnum);
	Input* vx_input=inputs->GetInput(VxEnum); _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum); _assert_(vy_input);
	h=sqrt(2*this->GetArea());

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

		gauss->GaussPoint(ig);

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

		vx_input->GetInputValue(&vx,gauss);
		vy_input->GetInputValue(&vy,gauss);

		D_scalar=gauss->weight*Jdet;

		TripleMultiply(basis,1,numnodes,1,
					&D_scalar,1,1,0,
					basis,1,numnodes,0,
					&Ke->values[0],1);

		GetBMasstransport(B,&xyz_list[0][0],gauss);
		GetBprimeMasstransport(Bprime,&xyz_list[0][0],gauss);

		D_scalar=dt*gauss->weight*Jdet;

		D[0][0]=D_scalar*vx;
		D[0][1]=0.;
		D[1][0]=0.;
		D[1][1]=D_scalar*vy;
		TripleMultiply(B,2,numnodes,1,
					&D[0][0],2,2,0,
					Bprime,2,numnodes,0,
					&Ke->values[0],1);

		if(stabilization==2){
			/*Streamline upwinding*/
			vel=sqrt(vx*vx+vy*vy)+1.e-8;
			D[0][0]=h/(2*vel)*vx*vx;
			D[1][0]=h/(2*vel)*vy*vx;
			D[0][1]=h/(2*vel)*vx*vy;
			D[1][1]=h/(2*vel)*vy*vy;
		}
		else if(stabilization==1){
			/*SSA*/
			vx_input->GetInputAverage(&vx);
			vy_input->GetInputAverage(&vy);
			D[0][0]=h/2.0*fabs(vx);
			D[0][1]=0.;
			D[1][0]=0.;
			D[1][1]=h/2.0*fabs(vy);
		}
		if(stabilization==1 || stabilization==2){
			D[0][0]=D_scalar*D[0][0];
			D[1][0]=D_scalar*D[1][0];
			D[0][1]=D_scalar*D[0][1];
			D[1][1]=D_scalar*D[1][1];
			TripleMultiply(Bprime,2,numnodes,1,
						&D[0][0],2,2,0,
						Bprime,2,numnodes,0,
						&Ke->values[0],1);
		}
	}

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

	/*Intermediaries */
	int        stabilization;
	int        dim;
	IssmDouble Jdet,D_scalar,dt,h;
	IssmDouble vel,vx,vy;
	IssmDouble xyz_list[NUMVERTICES][3];

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

	/*Initialize Element matrix and vectors*/
	ElementMatrix* Ke     = new ElementMatrix(nodes,numnodes,this->parameters,NoneApproximationEnum);
	IssmDouble*    basis  = xNew<IssmDouble>(numnodes);
	IssmDouble*    B      = xNew<IssmDouble>(2*numnodes);
	IssmDouble*    Bprime = xNew<IssmDouble>(2*numnodes);
	IssmDouble     D[2][2];

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	this->parameters->FindParam(&stabilization,MasstransportStabilizationEnum);
	Input* vx_input=inputs->GetInput(VxEnum); _assert_(vx_input);
	Input* vy_input=inputs->GetInput(VyEnum); _assert_(vy_input);
	h=sqrt(2*this->GetArea());

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

		gauss->GaussPoint(ig);

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

		vx_input->GetInputValue(&vx,gauss);
		vy_input->GetInputValue(&vy,gauss);

		D_scalar=gauss->weight*Jdet;

		TripleMultiply(basis,1,numnodes,1,
					&D_scalar,1,1,0,
					basis,1,numnodes,0,
					&Ke->values[0],1);

		GetBMasstransport(B,&xyz_list[0][0],gauss);
		GetBprimeMasstransport(Bprime,&xyz_list[0][0],gauss);

		D_scalar=dt*gauss->weight*Jdet;

		D[0][0]=D_scalar*vx;
		D[0][1]=0.;
		D[1][0]=0.;
		D[1][1]=D_scalar*vy;
		TripleMultiply(B,2,numnodes,1,
					&D[0][0],2,2,0,
					Bprime,2,numnodes,0,
					&Ke->values[0],1);

		if(stabilization==2){
			/*Streamline upwinding*/
			vel=sqrt(vx*vx+vy*vy)+1.e-8;
			D[0][0]=h/(2*vel)*vx*vx;
			D[1][0]=h/(2*vel)*vy*vx;
			D[0][1]=h/(2*vel)*vx*vy;
			D[1][1]=h/(2*vel)*vy*vy;
		}
		else if(stabilization==1){
			/*SSA*/
			vx_input->GetInputAverage(&vx);
			vy_input->GetInputAverage(&vy);
			D[0][0]=h/2.0*fabs(vx);
			D[0][1]=0.;
			D[1][0]=0.;
			D[1][1]=h/2.0*fabs(vy);
		}
		if(stabilization==1 || stabilization==2){
			D[0][0]=D_scalar*D[0][0];
			D[1][0]=D_scalar*D[1][0];
			D[0][1]=D_scalar*D[0][1];
			D[1][1]=D_scalar*D[1][1];
			TripleMultiply(Bprime,2,numnodes,1,
						&D[0][0],2,2,0,
						Bprime,2,numnodes,0,
						&Ke->values[0],1);
		}
	}

	/*Clean up and return*/
	xDelete<IssmDouble>(basis);
	xDelete<IssmDouble>(B);
	xDelete<IssmDouble>(Bprime);
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Tria::CreatePVectorMasstransport{{{*/
ElementVector* Tria::CreatePVectorMasstransport(void){

	switch(GetElementType()){
		case P1Enum: case P2Enum:
			return CreatePVectorMasstransport_CG();
		case P1DGEnum:
			return CreatePVectorMasstransport_DG();
		default:
			_error_("Element type " << EnumToStringx(GetElementType()) << " not supported yet");
	}
}
/*}}}*/
/*FUNCTION Tria::CreatePVectorMasstransport_CG {{{*/
ElementVector* Tria::CreatePVectorMasstransport_CG(void){

	/*Intermediaries */
	IssmDouble Jdet,dt;
	IssmDouble ms,mb,mb_correction,thickness;
	IssmDouble xyz_list[NUMVERTICES][3];

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

	/*Initialize Element vector and other vectors*/
	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(&dt,TimesteppingTimeStepEnum);
	Input* ms_input     = inputs->GetInput(SurfaceforcingsMassBalanceEnum);      _assert_(ms_input);
	Input* mb_input     = inputs->GetInput(BasalforcingsMeltingRateEnum); _assert_(mb_input);
	Input* mb_correction_input = inputs->GetInput(BasalforcingsMeltingRateCorrectionEnum);
	Input* thickness_input  = inputs->GetInput(ThicknessEnum);     _assert_(thickness_input);

	/*Initialize mb_correction to 0, do not forget!:*/
	/* Start  looping on the number of gaussian points: */
	GaussTria* gauss=new GaussTria(2);
	for(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

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

		ms_input->GetInputValue(&ms,gauss);
		mb_input->GetInputValue(&mb,gauss);
		thickness_input->GetInputValue(&thickness,gauss);
		if(mb_correction_input)
		 mb_correction_input->GetInputValue(&mb_correction,gauss);
		else
		 mb_correction=0.;

		for(int i=0;i<numnodes;i++) pe->values[i]+=Jdet*gauss->weight*(thickness+dt*(ms-mb-mb_correction))*basis[i];
	}

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

	/*Intermediaries */
	IssmDouble Jdet,dt;
	IssmDouble ms,mb,thickness;
	IssmDouble xyz_list[NUMVERTICES][3];

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

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

	/*Retrieve all inputs and parameters*/
	this->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	Input* ms_input=inputs->GetInput(SurfaceforcingsMassBalanceEnum); _assert_(ms_input);
	Input* mb_input=inputs->GetInput(BasalforcingsMeltingRateEnum);   _assert_(mb_input);
	Input* thickness_input=inputs->GetInput(ThicknessEnum);           _assert_(thickness_input);

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

		gauss->GaussPoint(ig);

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

		ms_input->GetInputValue(&ms,gauss);
		mb_input->GetInputValue(&mb,gauss);
		thickness_input->GetInputValue(&thickness,gauss);

		for(int i=0;i<numnodes;i++) pe->values[i]+=Jdet*gauss->weight*(thickness+dt*(ms-mb))*basis[i];
	}

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

	/*Intermediaries */
	IssmDouble Jdet,dt;
	IssmDouble ms,surface,vz;
	IssmDouble xyz_list[NUMVERTICES][3];

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

	/*Initialize Element vector and other vectors*/
	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(&dt,TimesteppingTimeStepEnum);
	Input* vz_input     = inputs->GetInput(VzEnum);                         _assert_(vz_input);
	Input* ms_input     = inputs->GetInput(SurfaceforcingsMassBalanceEnum); _assert_(ms_input);
	Input* surface_input= inputs->GetInput(SurfaceEnum);                    _assert_(surface_input);

	/*Initialize mb_correction to 0, do not forget!:*/
	/* Start  looping on the number of gaussian points: */
	GaussTria* gauss=new GaussTria(2);
	for(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

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

		vz_input->GetInputValue(&vz,gauss);
		ms_input->GetInputValue(&ms,gauss);
		surface_input->GetInputValue(&surface,gauss);

		for(int i=0;i<numnodes;i++) pe->values[i]+=Jdet*gauss->weight*(surface + dt*ms + dt*vz)*basis[i];
	}

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

	/*Intermediaries */
	IssmDouble Jdet,dt;
	IssmDouble mb,mb_correction,bed,vz;
	IssmDouble xyz_list[NUMVERTICES][3];

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

	/*Initialize Element vector and other vectors*/
	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(&dt,TimesteppingTimeStepEnum);
	Input* vz_input  = inputs->GetInput(VzEnum);                         _assert_(vz_input);
	Input* mb_input  = inputs->GetInput(BasalforcingsMeltingRateEnum);   _assert_(mb_input);
	Input* mb_correction_input = inputs->GetInput(BasalforcingsMeltingRateCorrectionEnum);
	Input* bed_input = inputs->GetInput(BedEnum);                        _assert_(bed_input);

	/*Initialize mb_correction to 0, do not forget!:*/
	/* Start  looping on the number of gaussian points: */
	GaussTria* gauss=new GaussTria(2);
	for(int ig=gauss->begin();ig<gauss->end();ig++){

		gauss->GaussPoint(ig);

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

		vz_input->GetInputValue(&vz,gauss);
		mb_input->GetInputValue(&mb,gauss);
		bed_input->GetInputValue(&bed,gauss);
		if(mb_correction_input)
		 mb_correction_input->GetInputValue(&mb_correction,gauss);
		else
		 mb_correction=0.;

		for(int i=0;i<numnodes;i++) pe->values[i]+=Jdet*gauss->weight*(bed+dt*(mb-mb_correction) + dt*vz)*basis[i];
	}

	/*Clean up and return*/
	xDelete<IssmDouble>(basis);
	delete gauss;
	return pe;
}
/*}}}*/
#endif

#ifdef _HAVE_DAKOTA_
/*FUNCTION Tria::InputUpdateFromVectorDakota(IssmDouble* vector, int name, int type);{{{*/
void  Tria::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 TriaInput*/
			IssmDouble values[3];

			/*Get values on the 3 vertices*/
			for (i=0;i<3;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:
					IssmDouble  thickness[3];
					IssmDouble  thickness_init[3];
					IssmDouble  hydrostatic_ratio[3];
					IssmDouble  surface[3];
					IssmDouble  bed[3];

					/*retrieve inputs: */
					GetInputListOnVertices(&thickness_init[0],ThicknessEnum);
					GetInputListOnVertices(&hydrostatic_ratio[0],GeometryHydrostaticRatioEnum);
					GetInputListOnVertices(&bed[0],BedEnum);
					GetInputListOnVertices(&surface[0],SurfaceEnum);

					/*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<3; 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];
						}
					}
					else{
						/*build new thickness: */
						for (j=0; j<3; 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<3;j++)bed[j]=surface[j]-thickness[j];
					}

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

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

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

}
/*}}}*/
/*FUNCTION Tria::InputUpdateFromMatrixDakota(IssmDouble* matrix, int nrows, int ncols, int name, int type);{{{*/
void  Tria::InputUpdateFromMatrixDakota(IssmDouble* matrix, int nrows, int ncols, int name, int type){

	int             i,t,row;
	IssmDouble      time;
	TransientInput *transientinput = NULL;
	IssmDouble      values[3];

	/*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<3;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 TriaInput(name,values,P1Enum),time);
				transientinput->Configure(parameters);
			}
			this->inputs->AddInput(transientinput);
			break;

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

}
/*}}}*/
#endif

#ifdef _HAVE_BALANCED_
/*FUNCTION Tria::CreateKMatrixBalancethickness {{{*/
ElementMatrix* Tria::CreateKMatrixBalancethickness(void){

	switch(GetElementType()){
		case P1Enum:
			return CreateKMatrixBalancethickness_CG();
		case P1DGEnum:
			return CreateKMatrixBalancethickness_DG();
		default:
			_error_("Element type " << EnumToStringx(GetElementType()) << " not supported yet");
	}

}
/*}}}*/
/*FUNCTION Tria::CreateKMatrixBalancethickness_CG {{{*/
ElementMatrix* Tria::CreateKMatrixBalancethickness_CG(void){

	/*Intermediaries */
	int        stabilization;
	int        i,j,dim;
	IssmDouble Jdet,vx,vy,dvxdx,dvydy,vel,h;
	IssmDouble D_scalar;
	IssmDouble dvx[2],dvy[2];
	IssmDouble xyz_list[NUMVERTICES][3];

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

	/*Initialize Element matrix and vectors*/
	ElementMatrix* Ke     = new ElementMatrix(nodes,numnodes,this->parameters,NoneApproximationEnum);
	IssmDouble*    basis  = xNew<IssmDouble>(numnodes);
	IssmDouble*    B      = xNew<IssmDouble>(2*numnodes);
	IssmDouble*    Bprime = xNew<IssmDouble>(2*numnodes);
	IssmDouble     D[2][2];

	/*Retrieve all Inputs and parameters: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	this->parameters->FindParam(&stabilization,BalancethicknessStabilizationEnum);
	this->parameters->FindParam(&dim,MeshDimensionEnum);
	Input* vxaverage_input=NULL;
	Input* vyaverage_input=NULL;
	if(dim==2){
		vxaverage_input=inputs->GetInput(VxEnum); _assert_(vxaverage_input);
		vyaverage_input=inputs->GetInput(VyEnum); _assert_(vyaverage_input);
	}
	else{
		vxaverage_input=inputs->GetInput(VxAverageEnum); _assert_(vxaverage_input);
		vyaverage_input=inputs->GetInput(VyAverageEnum); _assert_(vyaverage_input);
	}
	h=sqrt(2.*this->GetArea());

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

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetBMasstransport(B,&xyz_list[0][0],gauss);
		GetBprimeMasstransport(Bprime,&xyz_list[0][0],gauss);

		vxaverage_input->GetInputValue(&vx,gauss);
		vyaverage_input->GetInputValue(&vy,gauss);
		vxaverage_input->GetInputDerivativeValue(&dvx[0],&xyz_list[0][0],gauss);
		vyaverage_input->GetInputDerivativeValue(&dvy[0],&xyz_list[0][0],gauss);

		dvxdx=dvx[0];
		dvydy=dvy[1];
		D_scalar=gauss->weight*Jdet;

		D[0][0]=D_scalar*dvxdx;
		D[0][1]=0.;
		D[1][0]=0.;
		D[1][1]=D_scalar*dvydy;
		TripleMultiply(B,2,numnodes,1,
					&D[0][0],2,2,0,
					B,2,numnodes,0,
					&Ke->values[0],1);

		D[0][0]=D_scalar*vx;
		D[1][1]=D_scalar*vy;
		TripleMultiply(B,2,numnodes,1,
					&D[0][0],2,2,0,
					Bprime,2,numnodes,0,
					&Ke->values[0],1);

		if(stabilization==1){
			/*Streamline upwinding*/
			vel=sqrt(vx*vx+vy*vy);
			D[0][0]=h/(2*vel)*vx*vx;
			D[1][0]=h/(2*vel)*vy*vx;
			D[0][1]=h/(2*vel)*vx*vy;
			D[1][1]=h/(2*vel)*vy*vy;
		}
		else if(stabilization==2){
			/*SSA*/
			vxaverage_input->GetInputAverage(&vx);
			vyaverage_input->GetInputAverage(&vy);
			D[0][0]=h/2.0*fabs(vx);
			D[0][1]=0.;
			D[1][0]=0.;
			D[1][1]=h/2.0*fabs(vy);
		}
		if(stabilization==1 || stabilization==2){
			D[0][0]=D_scalar*D[0][0];
			D[1][0]=D_scalar*D[1][0];
			D[0][1]=D_scalar*D[0][1];
			D[1][1]=D_scalar*D[1][1];
			TripleMultiply(Bprime,2,numnodes,1,
						&D[0][0],2,2,0,
						Bprime,2,numnodes,0,
						&Ke->values[0],1);
		}
	}

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

	/*Intermediaries*/
	int        dim;
	IssmDouble vx,vy,D_scalar,Jdet;
	IssmDouble xyz_list[NUMVERTICES][3];

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

	/*Initialize Element matrix and vectors*/
	ElementMatrix* Ke     = new ElementMatrix(nodes,numnodes,this->parameters,NoneApproximationEnum);
	IssmDouble*    basis  = xNew<IssmDouble>(numnodes);
	IssmDouble*    B      = xNew<IssmDouble>(2*numnodes);
	IssmDouble*    Bprime = xNew<IssmDouble>(2*numnodes);
	IssmDouble     D[2][2];

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

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		/*WARNING: B and Bprime are inverted compared to usual masstransport!!!!*/
		GetBMasstransport(Bprime,&xyz_list[0][0],gauss);
		GetBprimeMasstransport(B,&xyz_list[0][0],gauss);

		vx_input->GetInputValue(&vx,gauss);
		vy_input->GetInputValue(&vy,gauss);

		D_scalar=-gauss->weight*Jdet;
		D[0][0]=D_scalar*vx;
		D[0][1]=0.;
		D[1][0]=0.;
		D[1][1]=D_scalar*vy;

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

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

	/*Intermediaries */
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble dhdt_g,mb_g,ms_g,Jdet;
	IssmDouble h,gamma,thickness;
	IssmDouble hnx,hny,dhnx[2],dhny[2];

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

	/*Initialize Element matrix and vectors*/
	ElementMatrix* Ke     = new ElementMatrix(nodes,numnodes,this->parameters,NoneApproximationEnum);
	IssmDouble*    B      = xNew<IssmDouble>(2*numnodes);
	IssmDouble*    basis  = xNew<IssmDouble>(numnodes);
	IssmDouble*    dbasis = xNew<IssmDouble>(2*numnodes);
	IssmDouble*    HNx    = xNew<IssmDouble>(numnodes);
	IssmDouble*    HNy    = xNew<IssmDouble>(numnodes);
	IssmDouble*    H      = xNew<IssmDouble>(numnodes);
	IssmDouble*    Nx     = xNew<IssmDouble>(numnodes);
	IssmDouble*    Ny     = xNew<IssmDouble>(numnodes);

	/*Retrieve all Inputs and parameters: */
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	Input* H_input =inputs->GetInput(ThicknessEnum); _assert_(H_input);
	h=sqrt(2.*this->GetArea());

	/*Get vector N for all nodes and build HNx and HNy*/
	GetInputListOnNodes(Nx,SurfaceSlopeXEnum);
	GetInputListOnNodes(Ny,SurfaceSlopeYEnum);
	GetInputListOnNodes(H,ThicknessEnum);
	for(int i=0;i<numnodes;i++){
		IssmDouble norm=sqrt(Nx[i]*Nx[i]+Ny[i]*Ny[i]+1.e-10);
		HNx[i] = -H[i]*Nx[i]/norm;
		HNy[i] = -H[i]*Ny[i]/norm;
	}

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

		gauss->GaussPoint(ig);

		H_input->GetInputValue(&thickness,gauss);
		if(thickness<50.) thickness=50.;
		TriaRef::GetInputDerivativeValue(&dhnx[0],HNx,&xyz_list[0][0],gauss);
		TriaRef::GetInputDerivativeValue(&dhny[0],HNy,&xyz_list[0][0],gauss);
		TriaRef::GetInputValue(&hnx,HNx,gauss);
		TriaRef::GetInputValue(&hny,HNy,gauss);

		gamma=h/(2.*thickness+1.e-10);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctions(basis,gauss);
		GetNodalFunctionsDerivatives(dbasis,&xyz_list[0][0],gauss);

		for(int i=0;i<numnodes;i++){
			for(int j=0;j<numnodes;j++){
				Ke->values[i*numnodes+j] += gauss->weight*Jdet*(
							(basis[i]+gamma*(basis[i]*(dhnx[0]+dhny[1]) + dbasis[0*numnodes+i]*hnx + dbasis[1*numnodes+i]*hny))*
							(basis[j]*(dhnx[0]+dhny[1])  + dbasis[0*numnodes+j]*hnx + dbasis[1*numnodes+j]*hny)
							);
			}
		}
	}

	/*Clean up and return*/
	xDelete<IssmDouble>(basis);
	xDelete<IssmDouble>(dbasis);
	xDelete<IssmDouble>(H);
	xDelete<IssmDouble>(Nx);
	xDelete<IssmDouble>(Ny);
	xDelete<IssmDouble>(HNx);
	xDelete<IssmDouble>(HNy);
	xDelete<IssmDouble>(B);
	delete gauss;
	return Ke;
}
/*}}}*/
/*FUNCTION Tria::CreateKMatrixSmoothedSlope {{{*/
ElementMatrix* Tria::CreateKMatrixSmoothedSlope(void){

	/* Intermediaries */
	IssmDouble D_scalar,Jdet,thickness;
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble D[2][2];
	IssmDouble l=8.;

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

	/*Initialize Element matrix and vectors*/
	ElementMatrix* Ke     = new ElementMatrix(nodes,numnodes,this->parameters);
	IssmDouble*    dbasis = xNew<IssmDouble>(2*numnodes);
	IssmDouble*    basis  = xNew<IssmDouble>(numnodes);

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

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

		gauss->GaussPoint(ig);

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		thickness_input->GetInputValue(&thickness,gauss);
		if(thickness<50.) thickness=50.;

		GetNodalFunctions(basis,gauss);
		GetNodalFunctionsDerivatives(dbasis,&xyz_list[0][0],gauss);

		for(int i=0;i<numnodes;i++){
			for(int j=0;j<numnodes;j++){
				Ke->values[i*numnodes+j] += gauss->weight*Jdet*(
							basis[i]*basis[j]
							+(l*thickness)*(l*thickness)*(dbasis[0*numnodes+i]*dbasis[0*numnodes+j] + dbasis[1*numnodes+i]*dbasis[1*numnodes+j])
							);
			}
		}
	}

	/*Clean up and return*/
	delete gauss;
	xDelete<IssmDouble>(dbasis);
	xDelete<IssmDouble>(basis);
	return Ke;
}
/*}}}*/
/*FUNCTION Tria::CreatePVectorBalancethickness{{{*/
ElementVector* Tria::CreatePVectorBalancethickness(void){

	switch(GetElementType()){
		case P1Enum:
			return CreatePVectorBalancethickness_CG();
			break;
		case P1DGEnum:
			return CreatePVectorBalancethickness_DG();
		default:
			_error_("Element type " << EnumToStringx(GetElementType()) << " not supported yet");
	}
}
/*}}}*/
/*FUNCTION Tria::CreatePVectorBalancethickness_CG{{{*/
ElementVector* Tria::CreatePVectorBalancethickness_CG(void){

	/*Intermediaries */
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble dhdt_g,mb_g,ms_g,Jdet;

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

	/*Initialize Element vector and other vectors*/
	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);
	Input* ms_input=inputs->GetInput(SurfaceforcingsMassBalanceEnum); _assert_(ms_input);
	Input* mb_input=inputs->GetInput(BasalforcingsMeltingRateEnum);   _assert_(mb_input);
	Input* dhdt_input=inputs->GetInput(BalancethicknessThickeningRateEnum); _assert_(dhdt_input);

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

		gauss->GaussPoint(ig);

		ms_input->GetInputValue(&ms_g,gauss);
		mb_input->GetInputValue(&mb_g,gauss);
		dhdt_input->GetInputValue(&dhdt_g,gauss);

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

		for(int i=0;i<numnodes;i++) pe->values[i]+=Jdet*gauss->weight*(ms_g-mb_g-dhdt_g)*basis[i];
	}

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

	/*Intermediaries */
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble mb_g,ms_g,dhdt_g,Jdet;

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

	/*Initialize Element vector and other vectors*/
	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);
	Input* ms_input=inputs->GetInput(SurfaceforcingsMassBalanceEnum); _assert_(ms_input);
	Input* mb_input=inputs->GetInput(BasalforcingsMeltingRateEnum);   _assert_(mb_input);
	Input* dhdt_input=inputs->GetInput(BalancethicknessThickeningRateEnum);_assert_(dhdt_input);

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

		gauss->GaussPoint(ig);

		ms_input->GetInputValue(&ms_g,gauss);
		mb_input->GetInputValue(&mb_g,gauss);
		dhdt_input->GetInputValue(&dhdt_g,gauss);

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

		for(int i=0;i<numnodes;i++) pe->values[i]+=Jdet*gauss->weight*(ms_g-mb_g-dhdt_g)*basis[i];
	}

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

	/*Intermediaries */
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble dhdt_g,mb_g,ms_g,Jdet;
	IssmDouble h,gamma,thickness;
	IssmDouble hnx,hny,dhnx[2],dhny[2];

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

	/*Initialize Element vector and other vectors*/
	ElementVector* pe    = new ElementVector(nodes,numnodes,this->parameters);
	IssmDouble*    basis  = xNew<IssmDouble>(numnodes);
	IssmDouble*    dbasis = xNew<IssmDouble>(numnodes*2);
	IssmDouble*    HNx    = xNew<IssmDouble>(numnodes);
	IssmDouble*    HNy    = xNew<IssmDouble>(numnodes);
	IssmDouble*    H      = xNew<IssmDouble>(numnodes);
	IssmDouble*    Nx     = xNew<IssmDouble>(numnodes);
	IssmDouble*    Ny     = xNew<IssmDouble>(numnodes);

	/*Retrieve all inputs and parameters*/
	GetVerticesCoordinates(&xyz_list[0][0],vertices,NUMVERTICES);
	Input* ms_input=inputs->GetInput(SurfaceforcingsMassBalanceEnum); _assert_(ms_input);
	Input* mb_input=inputs->GetInput(BasalforcingsMeltingRateEnum);   _assert_(mb_input);
	Input* dhdt_input=inputs->GetInput(BalancethicknessThickeningRateEnum); _assert_(dhdt_input);
	Input* H_input=inputs->GetInput(ThicknessEnum); _assert_(H_input);
	h=sqrt(2.*this->GetArea());

	/*Get vector N for all nodes*/
	GetInputListOnNodes(Nx,SurfaceSlopeXEnum);
	GetInputListOnNodes(Ny,SurfaceSlopeYEnum);
	GetInputListOnNodes(H,ThicknessEnum);
	for(int i=0;i<numnodes;i++){
		IssmDouble norm=sqrt(Nx[i]*Nx[i]+Ny[i]*Ny[i]+1.e-10);
		Nx[i] = -H[i]*Nx[i]/norm;
		Ny[i] = -H[i]*Ny[i]/norm;
	}

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

		gauss->GaussPoint(ig);

		ms_input->GetInputValue(&ms_g,gauss);
		mb_input->GetInputValue(&mb_g,gauss);
		dhdt_input->GetInputValue(&dhdt_g,gauss);
		H_input->GetInputValue(&thickness,gauss);
		if(thickness<50.) thickness=50.;

		GetJacobianDeterminant(&Jdet, &xyz_list[0][0],gauss);
		GetNodalFunctions(basis,gauss);
		GetNodalFunctionsDerivatives(dbasis,&xyz_list[0][0],gauss);

		TriaRef::GetInputDerivativeValue(&dhnx[0],HNx,&xyz_list[0][0],gauss);
		TriaRef::GetInputDerivativeValue(&dhny[0],HNy,&xyz_list[0][0],gauss);
		TriaRef::GetInputValue(&hnx,HNx,gauss);
		TriaRef::GetInputValue(&hny,HNy,gauss);

		gamma=h/(2.*thickness+1.e-10);

		for(int i=0;i<numnodes;i++){
			pe->values[i]+=Jdet*gauss->weight*(ms_g-mb_g-dhdt_g)*(
						basis[i] + gamma*(basis[i]*(dhnx[0]+dhny[1])+hnx*dbasis[0*numnodes+i] + hny*dbasis[1*numnodes+i])
						);
		}
	}

	/*Clean up and return*/
	xDelete<IssmDouble>(basis);
	xDelete<IssmDouble>(dbasis);
	xDelete<IssmDouble>(H);
	xDelete<IssmDouble>(Nx);
	xDelete<IssmDouble>(Ny);
	xDelete<IssmDouble>(HNx);
	xDelete<IssmDouble>(HNy);
	delete gauss;
	return pe;
}
/*}}}*/
/*FUNCTION Tria::CreatePVectorSmoothedSlopeX{{{*/
ElementVector* Tria::CreatePVectorSmoothedSlopeX(void){

	/*Intermediaries */
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble Jdet;
	IssmDouble thickness,slope[2];
	IssmDouble taud_x,norms,normv,vx,vy;

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

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

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

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

		gauss->GaussPoint(ig);

		H_input->GetInputValue(&thickness,gauss);
		surface_input->GetInputDerivativeValue(&slope[0],&xyz_list[0][0],gauss);
		if(vx_input && vy_input){
			vx_input->GetInputValue(&vx,gauss);
			vy_input->GetInputValue(&vy,gauss);
			norms = sqrt(slope[0]*slope[0]+slope[1]*slope[1]+1.e-10);
			normv = sqrt(vx*vx + vy*vy);
			if(normv>15./(365.*24.*3600.)) slope[0] = -vx/normv*norms;
		}
		taud_x = matpar->GetRhoIce()*matpar->GetG()*thickness*slope[0];

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

		for(int i=0;i<numnodes;i++) pe->values[i]+=Jdet*gauss->weight*taud_x*basis[i];
	}

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

	/*Intermediaries */
	IssmDouble xyz_list[NUMVERTICES][3];
	IssmDouble Jdet;
	IssmDouble thickness,slope[2];
	IssmDouble taud_y,norms,normv,vx,vy;

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

	/*Initialize Element vector and other vectors*/
	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);
	Input* H_input       = inputs->GetInput(ThicknessEnum); _assert_(H_input);
	Input* surface_input = inputs->GetInput(SurfaceEnum);   _assert_(surface_input);
	Input* vx_input      = inputs->GetInput(VxEnum);
	Input* vy_input      = inputs->GetInput(VyEnum);

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

		gauss->GaussPoint(ig);

		H_input->GetInputValue(&thickness,gauss);
		surface_input->GetInputDerivativeValue(&slope[0],&xyz_list[0][0],gauss);
		if(vx_input && vy_input){
			vx_input->GetInputValue(&vx,gauss);
			vy_input->GetInputValue(&vy,gauss);
			norms = sqrt(slope[0]*slope[0]+slope[1]*slope[1]+1.e-10);
			normv = sqrt(vx*vx + vy*vy);
			if(normv>15./(365.*24.*3600.)) slope[1] = -vy/normv*norms;
		}
		taud_y = matpar->GetRhoIce()*matpar->GetG()*thickness*slope[1];

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

		for(int i=0;i<numnodes;i++) pe->values[i]+=Jdet*gauss->weight*taud_y*basis[i];
	}

	/*Clean up and return*/
	xDelete<IssmDouble>(basis);
	delete gauss;
	return pe;
}
/*}}}*/
#endif

#ifdef _HAVE_GROUNDINGLINE_
/*FUNCTION Tria::MigrateGroundingLine{{{*/
void  Tria::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 melting[NUMVERTICES],phi[NUMVERTICES];;
	IssmDouble h[NUMVERTICES],s[NUMVERTICES],b[NUMVERTICES],r[NUMVERTICES];
	bool       grounded[NUMVERTICES],floating[NUMVERTICES];

	/*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 element is floating before we start to change everything

	/*go through vertices, and update inputs, considering them to be TriaVertex 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{ // phi>0
			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 TriaInput(MaskGroundediceLevelsetEnum,&phi[0],P1Enum));

	/*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{
		/*Otherwise: if one floating, all floating*/
		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 TriaInput(BasalforcingsMeltingRateEnum,&melting[0],P1Enum));
	} 

	/*Update inputs*/
	this->inputs->AddInput(new TriaInput(SurfaceEnum,&s[0],P1Enum));
	this->inputs->AddInput(new TriaInput(BedEnum,&b[0],P1Enum));

}
/*}}}*/
/*FUNCTION Tria::PotentialUngrounding{{{*/
void  Tria::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 grounded 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 Tria::UpdatePotentialUngrounding{{{*/
int Tria::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<3;i++){
		if (reCast<bool>(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
