#include "./SealevelriseAnalysis.h"
#include "../toolkits/toolkits.h"
#include "../classes/classes.h"
#include "../classes/Inputs2/TransientInput2.h"
#include "../shared/shared.h"
#include "../modules/modules.h"

/*Model processing*/
void SealevelriseAnalysis::CreateConstraints(Constraints* constraints,IoModel* iomodel){/*{{{*/
	/*No constraints*/
}/*}}}*/
void SealevelriseAnalysis::CreateLoads(Loads* loads, IoModel* iomodel){/*{{{*/
	/*No loads*/
}/*}}}*/
void SealevelriseAnalysis::CreateNodes(Nodes* nodes,IoModel* iomodel,bool isamr){/*{{{*/
	::CreateNodes(nodes,iomodel,SealevelriseAnalysisEnum,P1Enum);
}/*}}}*/
int  SealevelriseAnalysis::DofsPerNode(int** doflist,int domaintype,int approximation){/*{{{*/
	return 1;
}/*}}}*/
void SealevelriseAnalysis::UpdateElements(Elements* elements,Inputs2* inputs2,IoModel* iomodel,int analysis_counter,int analysis_type){/*{{{*/

	int geodetic=0;
	int dslmodel=0;

	/*Update elements: */
	int counter=0;
	for(int i=0;i<iomodel->numberofelements;i++){
		if(iomodel->my_elements[i]){
			Element* element=(Element*)elements->GetObjectByOffset(counter);
			element->Update(inputs2,i,iomodel,analysis_counter,analysis_type,P1Enum);
			counter++;
		}
	}

	/*Create inputs: */
	iomodel->FetchDataToInput(inputs2,elements,"md.mask.groundedice_levelset",MaskGroundediceLevelsetEnum);
	iomodel->FetchDataToInput(inputs2,elements,"md.mask.ice_levelset",MaskIceLevelsetEnum);
	//those only if we have requested geodetic computations:
	iomodel->FetchData(&geodetic,"md.slr.geodetic");
	if (geodetic){
		char* masktype=NULL;
		iomodel->FetchData(&masktype,"md.mask.type");
		if (strcmp(masktype,"maskpsl")==0){
			iomodel->FetchDataToInput(inputs2,elements,"md.mask.ocean_levelset",MaskOceanLevelsetEnum);
			iomodel->FetchDataToInput(inputs2,elements,"md.mask.land_levelset",MaskLandLevelsetEnum);
		}
		xDelete<char>(masktype);
	}
	iomodel->FetchDataToInput(inputs2,elements,"md.slr.deltathickness",SealevelriseDeltathicknessEnum);
	iomodel->FetchDataToInput(inputs2,elements,"md.slr.spcthickness",SealevelriseSpcthicknessEnum);
	iomodel->FetchDataToInput(inputs2,elements,"md.slr.sealevel",SealevelEnum,0);
	iomodel->FetchDataToInput(inputs2,elements,"md.geometry.bed",BedEnum);
	iomodel->FetchDataToInput(inputs2,elements,"md.slr.Ngia",SealevelNGiaRateEnum);
	iomodel->FetchDataToInput(inputs2,elements,"md.slr.Ugia",SealevelUGiaRateEnum);
	iomodel->FetchDataToInput(inputs2,elements,"md.slr.hydro_rate",SealevelriseHydroRateEnum);
		
	/*dynamic sea level: */
	iomodel->FetchData(&dslmodel,"md.dsl.model");
	if (dslmodel==1){ /*standard dsl model:{{{*/

		/*deal with global mean steric rate: */
		IssmDouble* str=NULL; 
		IssmDouble* times = NULL;
		int M,N;

		/*fetch str vector:*/
		iomodel->FetchData(&str,&M,&N,"md.dsl.global_average_thermosteric_sea_level_change"); _assert_(M==2);
		
		//recover time vector: 
		times=xNew<IssmDouble>(N);
		for(int t=0;t<N;t++) times[t] = str[N+t];

		/*create transient input: */
		inputs2->SetTransientInput(DslGlobalAverageThermostericSeaLevelChangeEnum,times,N);
		TransientInput2* transientinput = inputs2->GetTransientInput(DslGlobalAverageThermostericSeaLevelChangeEnum);
		
			
		for(int i=0;i<elements->Size();i++){
			Element* element=xDynamicCast<Element*>(elements->GetObjectByOffset(i));

			for(int t=0;t<N;t++){
				switch(element->ObjectEnum()){
					case TriaEnum:  transientinput->AddTriaTimeInput( t,1,&element->lid,&str[t],P0Enum); break;
					case PentaEnum: transientinput->AddPentaTimeInput(t,1,&element->lid,&str[t],P0Enum); break;
					default: _error_("Not implemented yet");
				}
			}
		}

		/*cleanup:*/
		xDelete<IssmDouble>(times);
		iomodel->DeleteData(str,"md.dsl.global_average_thermosteric_sea_level_change");

		/*deal with dynamic sea level fields: */
		iomodel->FetchDataToInput(inputs2,elements,"md.dsl.sea_surface_height_change_above_geoid", DslSeaSurfaceHeightChangeAboveGeoidEnum);
		iomodel->FetchDataToInput(inputs2,elements,"md.dsl.sea_water_pressure_change_at_sea_floor", DslSeaWaterPressureChangeAtSeaFloor);
		
	} /*}}}*/
	else if (dslmodel==2){ /*multi-model ensemble dsl model:{{{*/
	
		/*variables:*/
		int nummodels;
		IssmDouble** pstr=NULL; 
		IssmDouble*  str=NULL;
		IssmDouble*  times = NULL;
		int* pM = NULL;
		int* pN = NULL;
		int M,N;

		/*deal with dsl.sea_surface_height_change_above_geoid {{{*/
		iomodel->FetchData(&pstr,&pM,&pN,&nummodels,"md.dsl.global_average_thermosteric_sea_level_change");

		/*go through the mat array and create a dataset of transient inputs:*/
		for (int i=0;i<nummodels;i++){

			M=pM[i];
			N=pN[i];
			str=pstr[i];

			//recover time vector: 
			times=xNew<IssmDouble>(N);
			for(int t=0;t<N;t++) times[t] = str[(M-1)*N+t];

			TransientInput2* transientinput=inputs2->SetDatasetTransientInput(DslGlobalAverageThermostericSeaLevelChangeEnum,i, times,N);
			
			for(int j=0;j<elements->Size();j++){
				Element* element=xDynamicCast<Element*>(elements->GetObjectByOffset(j));

				for(int t=0;t<N;t++){
					switch(element->ObjectEnum()){
						case TriaEnum:  transientinput->AddTriaTimeInput( t,1,&element->lid,&str[t],P0Enum); break;
						case PentaEnum: transientinput->AddPentaTimeInput(t,1,&element->lid,&str[t],P0Enum); break;
						default: _error_("Not implemented yet");
					}
				}
			}
			xDelete<IssmDouble>(times);
		}
		/*Delete data:*/
		for(int i=0;i<nummodels;i++){
			IssmDouble* str=pstr[i];
			xDelete<IssmDouble>(str);
		}
		xDelete<IssmDouble*>(pstr);
		xDelete<int>(pM);
		xDelete<int>(pN);
		/*}}}*/
		/*now do the same with the dsl.sea_surface_height_change_above_geoid field:{{{ */
		iomodel->FetchData(&pstr,&pM,&pN,&nummodels,"md.dsl.sea_surface_height_change_above_geoid");

		for (int i=0;i<nummodels;i++){
			M=pM[i];
			N=pN[i];
			str=pstr[i];
		

			//recover time vector: 
			times=xNew<IssmDouble>(N);
			for(int t=0;t<N;t++) times[t] = str[(M-1)*N+t];
			
			TransientInput2* transientinput=inputs2->SetDatasetTransientInput(DslSeaSurfaceHeightChangeAboveGeoidEnum,i, times,N);
	
			for(int j=0;j<elements->Size();j++){

				/*Get the right transient input*/
				Element* element=xDynamicCast<Element*>(elements->GetObjectByOffset(j));

				/*Get values and lid list*/
				const int   numvertices = element->GetNumberOfVertices();
				int        *vertexlids = xNew<int>(numvertices);
				int        *vertexsids = xNew<int>(numvertices);


				/*Recover vertices ids needed to initialize inputs*/
				_assert_(iomodel->elements);
				for(int k=0;k<numvertices;k++){
					vertexsids[k] =reCast<int>(iomodel->elements[numvertices*element->Sid()+k]); //ids for vertices are in the elements array from Matlab
					vertexlids[k]=iomodel->my_vertices_lids[vertexsids[k]-1];
				}

				//element->GetVerticesLidList(vertexlids);
				//element->GetVerticesSidList(vertexsids);
				IssmDouble* values=xNew<IssmDouble>(numvertices);

				for(int t=0;t<N;t++){
					for (int k=0;k<numvertices;k++)values[k]=str[N*vertexsids[k]+t];

					switch(element->ObjectEnum()){
						case TriaEnum:  transientinput->AddTriaTimeInput( t,numvertices,vertexlids,values,P1Enum); break;
						case PentaEnum: transientinput->AddPentaTimeInput(t,numvertices,vertexlids,values,P1Enum); break;
						default: _error_("Not implemented yet");
					}
				}
				xDelete<IssmDouble>(values);
				xDelete<int>(vertexlids);
				xDelete<int>(vertexsids);
			}
			
			xDelete<IssmDouble>(times);
		}
		
		/*Delete data:*/
		for(int i=0;i<nummodels;i++){
			IssmDouble* str=pstr[i];
			xDelete<IssmDouble>(str);
		}
		xDelete<IssmDouble*>(pstr);
		xDelete<int>(pM);
		xDelete<int>(pN);
		/*}}}*/
		/*now do the same with the dsl.sea_water_pressure_change_at_sea_floor field:{{{ */
		iomodel->FetchData(&pstr,&pM,&pN,&nummodels,"md.dsl.sea_water_pressure_change_at_sea_floor");

		for (int i=0;i<nummodels;i++){
			M=pM[i];
			N=pN[i];
			str=pstr[i];

			//recover time vector: 
			times=xNew<IssmDouble>(N);
			for(int t=0;t<N;t++) times[t] = str[(M-1)*N+t];

			TransientInput2* transientinput=inputs2->SetDatasetTransientInput(DslSeaWaterPressureChangeAtSeaFloor,i, times,N);
	
			for(int j=0;j<elements->Size();j++){

				/*Get the right transient input*/
				Element* element=xDynamicCast<Element*>(elements->GetObjectByOffset(j));

				/*Get values and lid list*/
				const int   numvertices = element->GetNumberOfVertices();
				int        *vertexlids = xNew<int>(numvertices);
				int        *vertexsids = xNew<int>(numvertices);
				
				
				/*Recover vertices ids needed to initialize inputs*/
				_assert_(iomodel->elements);
				for(int k=0;k<numvertices;k++){
					vertexsids[k] =reCast<int>(iomodel->elements[numvertices*element->Sid()+k]); //ids for vertices are in the elements array from Matlab
					vertexlids[k]=iomodel->my_vertices_lids[vertexsids[k]-1];
				}
				//element->GetVerticesLidList(vertexlids);
				//element->GetVerticesSidList(vertexsids);
				
				IssmDouble* values=xNew<IssmDouble>(numvertices);

				for(int t=0;t<N;t++){
					for (int k=0;k<numvertices;k++)values[k]=str[N*vertexsids[k]+t];

					switch(element->ObjectEnum()){
						case TriaEnum:  transientinput->AddTriaTimeInput( t,numvertices,vertexlids,values,P1Enum); break;
						case PentaEnum: transientinput->AddPentaTimeInput(t,numvertices,vertexlids,values,P1Enum); break;
						default: _error_("Not implemented yet");
					}
				}
				xDelete<IssmDouble>(values);
				xDelete<int>(vertexlids);
				xDelete<int>(vertexsids);
			}
			xDelete<IssmDouble>(times);
		}
		
		/*Delete data:*/
		for(int i=0;i<nummodels;i++){
			IssmDouble* str=pstr[i];
			xDelete<IssmDouble>(str);
		}
		xDelete<IssmDouble*>(pstr);
		xDelete<int>(pM);
		xDelete<int>(pN);
		/*}}}*/

	} /*}}}*/
	else _error_("Dsl model " << dslmodel << " not implemented yet!");
			
	/*Initialize cumdeltalthickness and sealevel rise rate input*/
	InputUpdateFromConstantx(inputs2,elements,0.,SealevelriseCumDeltathicknessEnum);
	InputUpdateFromConstantx(inputs2,elements,0.,SealevelNEsaRateEnum);
	InputUpdateFromConstantx(inputs2,elements,0.,SealevelUEsaRateEnum);
	InputUpdateFromConstantx(inputs2,elements,0.,SealevelRSLRateEnum);
	InputUpdateFromConstantx(inputs2,elements,0.,SealevelEustaticMaskEnum);
	InputUpdateFromConstantx(inputs2,elements,0.,SealevelEustaticOceanMaskEnum);

}/*}}}*/
void SealevelriseAnalysis::UpdateParameters(Parameters* parameters,IoModel* iomodel,int solution_enum,int analysis_enum){/*{{{*/

	int         nl;
	IssmDouble* love_h=NULL;
	IssmDouble* love_k=NULL;
	IssmDouble* love_l=NULL;
	int         dslmodel=0;

	bool elastic=false;
	IssmDouble* G_elastic = NULL;
	IssmDouble* G_elastic_local = NULL;
	IssmDouble* U_elastic = NULL;
	IssmDouble* U_elastic_local = NULL;
	IssmDouble* H_elastic = NULL;
	IssmDouble* H_elastic_local = NULL;
	int         M,m,lower_row,upper_row;
	IssmDouble  degacc=.01;

	int     numoutputs;
	char**  requestedoutputs = NULL;

	/*transition vectors: */
	IssmDouble **transitions    = NULL;
	int         *transitions_M    = NULL;
	int         *transitions_N    = NULL;
	int          ntransitions;

	/*some constant parameters: */
	parameters->AddObject(iomodel->CopyConstantObject("md.dsl.model",DslModelEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.slr.geodetic_run_frequency",SealevelriseGeodeticRunFrequencyEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.slr.reltol",SealevelriseReltolEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.slr.abstol",SealevelriseAbstolEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.slr.maxiter",SealevelriseMaxiterEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.slr.loop_increment",SealevelriseLoopIncrementEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.slr.rigid",SealevelriseRigidEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.slr.horiz",SealevelriseHorizEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.slr.elastic",SealevelriseElasticEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.slr.rotation",SealevelriseRotationEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.slr.tide_love_h",SealevelriseTidalLoveHEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.slr.tide_love_k",SealevelriseTidalLoveKEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.slr.fluid_love",SealevelriseFluidLoveEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.slr.equatorial_moi",SealevelriseEquatorialMoiEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.slr.polar_moi",SealevelrisePolarMoiEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.slr.angular_velocity",SealevelriseAngularVelocityEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.slr.ocean_area_scaling",SealevelriseOceanAreaScalingEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.slr.geodetic",SealevelriseGeodeticEnum));

	/*Deal with dsl multi-model ensembles: {{{*/
	iomodel->FetchData(&dslmodel,"md.dsl.model");
	parameters->AddObject(iomodel->CopyConstantObject("md.dsl.compute_fingerprints",DslComputeFingerprintsEnum));
	if(dslmodel==2){
		int modelid; 
		int nummodels;

		parameters->AddObject(iomodel->CopyConstantObject("md.dsl.modelid",DslModelidEnum));
		parameters->AddObject(iomodel->CopyConstantObject("md.dsl.nummodels",DslNummodelsEnum));
		iomodel->FetchData(&modelid,"md.dsl.modelid");
		iomodel->FetchData(&nummodels,"md.dsl.nummodels");

		/*quick checks: */
		if(nummodels<=0)_error_("dslmme object in  md.dsl field should contain at least 1 ensemble model!");
		if(modelid<=0 || modelid>nummodels)_error_("modelid field in dslmme object of md.dsl field should be between 1 and the number of ensemble runs!");
	} /*}}}*/
	/*Deal with elasticity {{{*/
	iomodel->FetchData(&elastic,"md.slr.elastic");
	if(elastic){ 

		/*love numbers: */
		iomodel->FetchData(&love_h,&nl,NULL,"md.slr.love_h");
		iomodel->FetchData(&love_k,&nl,NULL,"md.slr.love_k");
		iomodel->FetchData(&love_l,&nl,NULL,"md.slr.love_l");

		/*compute elastic green function for a range of angles*/
		iomodel->FetchData(&degacc,"md.slr.degacc");
		M=reCast<int,IssmDouble>(180./degacc+1.);

		// AD performance is sensitive to calls to ensurecontiguous.
		// // Providing "t" will cause ensurecontiguous to be called.
		#ifdef _HAVE_AD_
		G_elastic=xNew<IssmDouble>(M,"t");
		U_elastic=xNew<IssmDouble>(M,"t");
		H_elastic=xNew<IssmDouble>(M,"t");
		#else
		G_elastic=xNew<IssmDouble>(M);
		U_elastic=xNew<IssmDouble>(M);
		H_elastic=xNew<IssmDouble>(M);
		#endif

		/*compute combined legendre + love number (elastic green function:*/
		m=DetermineLocalSize(M,IssmComm::GetComm());
		GetOwnershipBoundariesFromRange(&lower_row,&upper_row,m,IssmComm::GetComm());
		// AD performance is sensitive to calls to ensurecontiguous.
		// // Providing "t" will cause ensurecontiguous to be called.
		#ifdef _HAVE_AD_
		G_elastic_local=xNew<IssmDouble>(m,"t");
		U_elastic_local=xNew<IssmDouble>(m,"t");
		H_elastic_local=xNew<IssmDouble>(m,"t");
		#else
		G_elastic_local=xNew<IssmDouble>(m);
		U_elastic_local=xNew<IssmDouble>(m);
		H_elastic_local=xNew<IssmDouble>(m);
		#endif

		for(int i=lower_row;i<upper_row;i++){
			IssmDouble alpha,x;
			alpha= reCast<IssmDouble>(i)*degacc * PI / 180.0;

			G_elastic_local[i-lower_row]= (love_k[nl-1]-love_h[nl-1])/2.0/sin(alpha/2.0);
			U_elastic_local[i-lower_row]= (love_h[nl-1])/2.0/sin(alpha/2.0);
			H_elastic_local[i-lower_row]= 0; 
			IssmDouble Pn = 0.; 
			IssmDouble Pn1 = 0.; 
			IssmDouble Pn2 = 0.; 
			IssmDouble Pn_p = 0.; 
			IssmDouble Pn_p1 = 0.; 
			IssmDouble Pn_p2 = 0.; 

			for (int n=0;n<nl;n++) {
				IssmDouble deltalove_G;
				IssmDouble deltalove_U;

				deltalove_G = (love_k[n]-love_k[nl-1]-love_h[n]+love_h[nl-1]);
				deltalove_U = (love_h[n]-love_h[nl-1]);

				/*compute legendre polynomials: P_n(cos\theta) & d P_n(cos\theta)/ d\theta: */
				if(n==0){
					Pn=1; 
					Pn_p=0; 
				}
				else if(n==1){ 
					Pn = cos(alpha); 
					Pn_p = 1; 
				}
				else{
					Pn = ( (2*n-1)*cos(alpha)*Pn1 - (n-1)*Pn2 ) /n;
					Pn_p = ( (2*n-1)*(Pn1+cos(alpha)*Pn_p1) - (n-1)*Pn_p2 ) /n;
				}
				Pn2=Pn1; Pn1=Pn;
				Pn_p2=Pn_p1; Pn_p1=Pn_p;

				G_elastic_local[i-lower_row] += deltalove_G*Pn;		// gravitational potential 
				U_elastic_local[i-lower_row] += deltalove_U*Pn;		// vertical (up) displacement 
				H_elastic_local[i-lower_row] += sin(alpha)*love_l[n]*Pn_p;		// horizontal displacements 
			}
		}

		/*merge G_elastic_local into G_elastic; U_elastic_local into U_elastic; H_elastic_local to H_elastic:{{{*/
		int* recvcounts=xNew<int>(IssmComm::GetSize());
		int* displs=xNew<int>(IssmComm::GetSize());

		//recvcounts:
		ISSM_MPI_Allgather(&m,1,ISSM_MPI_INT,recvcounts,1,ISSM_MPI_INT,IssmComm::GetComm());

		/*displs: */
		ISSM_MPI_Allgather(&lower_row,1,ISSM_MPI_INT,displs,1,ISSM_MPI_INT,IssmComm::GetComm());

		/*All gather:*/
		ISSM_MPI_Allgatherv(G_elastic_local, m, ISSM_MPI_DOUBLE, G_elastic, recvcounts, displs, ISSM_MPI_DOUBLE,IssmComm::GetComm());
		ISSM_MPI_Allgatherv(U_elastic_local, m, ISSM_MPI_DOUBLE, U_elastic, recvcounts, displs, ISSM_MPI_DOUBLE,IssmComm::GetComm());
		ISSM_MPI_Allgatherv(H_elastic_local, m, ISSM_MPI_DOUBLE, H_elastic, recvcounts, displs, ISSM_MPI_DOUBLE,IssmComm::GetComm());
		/*free ressources: */
		xDelete<int>(recvcounts);
		xDelete<int>(displs);

		/*}}}*/

		/*Avoid singularity at 0: */
		G_elastic[0]=G_elastic[1];
		parameters->AddObject(new DoubleVecParam(SealevelriseGElasticEnum,G_elastic,M));
		U_elastic[0]=U_elastic[1];
		parameters->AddObject(new DoubleVecParam(SealevelriseUElasticEnum,U_elastic,M));
		H_elastic[0]=H_elastic[1];
		parameters->AddObject(new DoubleVecParam(SealevelriseHElasticEnum,H_elastic,M));

		/*free ressources: */
		xDelete<IssmDouble>(love_h);
		xDelete<IssmDouble>(love_k);
		xDelete<IssmDouble>(love_l);
		xDelete<IssmDouble>(G_elastic);
		xDelete<IssmDouble>(G_elastic_local);
		xDelete<IssmDouble>(U_elastic);
		xDelete<IssmDouble>(U_elastic_local);
		xDelete<IssmDouble>(H_elastic);
		xDelete<IssmDouble>(H_elastic_local);
	} /*}}}*/
	/*Transitions:{{{ */
	iomodel->FetchData(&transitions,&transitions_M,&transitions_N,&ntransitions,"md.slr.transitions");
	if(transitions){
		parameters->AddObject(new DoubleMatArrayParam(SealevelriseTransitionsEnum,transitions,ntransitions,transitions_M,transitions_N));

		for(int i=0;i<ntransitions;i++){
			IssmDouble* transition=transitions[i];
			xDelete<IssmDouble>(transition);
		}
		xDelete<IssmDouble*>(transitions);
		xDelete<int>(transitions_M);
		xDelete<int>(transitions_N);
	} /*}}}*/
	/*Requested outputs {{{*/
	iomodel->FindConstant(&requestedoutputs,&numoutputs,"md.slr.requested_outputs");
	if(numoutputs)parameters->AddObject(new StringArrayParam(SealevelriseRequestedOutputsEnum,requestedoutputs,numoutputs));
	iomodel->DeleteData(&requestedoutputs,numoutputs,"md.slr.requested_outputs");
	/*}}}*/

}/*}}}*/

/*Finite Element Analysis*/
void           SealevelriseAnalysis::Core(FemModel* femmodel){/*{{{*/
	_error_("not implemented");
}/*}}}*/
ElementVector* SealevelriseAnalysis::CreateDVector(Element* element){/*{{{*/
	/*Default, return NULL*/
	return NULL;
}/*}}}*/
ElementMatrix* SealevelriseAnalysis::CreateJacobianMatrix(Element* element){/*{{{*/
_error_("Not implemented");
}/*}}}*/
ElementMatrix* SealevelriseAnalysis::CreateKMatrix(Element* element){/*{{{*/
	_error_("not implemented yet");
}/*}}}*/
ElementVector* SealevelriseAnalysis::CreatePVector(Element* element){/*{{{*/
_error_("not implemented yet");
}/*}}}*/
void           SealevelriseAnalysis::GetSolutionFromInputs(Vector<IssmDouble>* solution,Element* element){/*{{{*/
	   _error_("not implemented yet");
}/*}}}*/
void           SealevelriseAnalysis::GradientJ(Vector<IssmDouble>* gradient,Element* element,int control_type,int control_index){/*{{{*/
	_error_("Not implemented yet");
}/*}}}*/
void           SealevelriseAnalysis::InputUpdateFromSolution(IssmDouble* solution,Element* element){/*{{{*/
	_error_("not implemeneted yet!");

}/*}}}*/
void           SealevelriseAnalysis::UpdateConstraints(FemModel* femmodel){/*{{{*/
	/*Default, do nothing*/
	return;
}/*}}}*/
