#include "./SealevelriseAnalysis.h"
#include <math.h>
#include "../toolkits/toolkits.h"
#include "../classes/classes.h"
#include "../classes/Inputs/TransientInput.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,Inputs* inputs,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(inputs,i,iomodel,analysis_counter,analysis_type,P1Enum);
			counter++;
		}
	}

	/*Create inputs: */
	iomodel->FetchDataToInput(inputs,elements,"md.mask.ocean_levelset",MaskOceanLevelsetEnum);
	iomodel->FetchDataToInput(inputs,elements,"md.mask.ice_levelset",MaskIceLevelsetEnum);
	iomodel->FetchData(&geodetic,"md.solidearth.settings.computesealevelchange");
	iomodel->FetchDataToInput(inputs,elements,"md.solidearth.surfaceload.icethicknesschange",SurfaceloadIceThicknessChangeEnum);
	iomodel->FetchDataToInput(inputs,elements,"md.solidearth.initialsealevel",SealevelEnum,0);
	iomodel->FetchDataToInput(inputs,elements,"md.geometry.bed",BedEnum);
	iomodel->FetchDataToInput(inputs,elements,"md.solidearth.surfaceload.waterheightchange",SurfaceloadWaterHeightChangeEnum);
		
	/*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: */
		inputs->SetTransientInput(DslGlobalAverageThermostericSeaLevelChangeEnum,times,N);
		TransientInput* transientinput = inputs->GetTransientInput(DslGlobalAverageThermostericSeaLevelChangeEnum);
		
			
		for(Object* & object : elements->objects){
			Element* element=xDynamicCast<Element*>(object);

			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(inputs,elements,"md.dsl.sea_surface_height_change_above_geoid", DslSeaSurfaceHeightChangeAboveGeoidEnum);
		iomodel->FetchDataToInput(inputs,elements,"md.dsl.sea_water_pressure_change_at_sea_floor", DslSeaWaterPressureChangeAtSeaFloorEnum);
		
	} /*}}}*/
	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.global_average_thermosteric_sea_level_change {{{*/
		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];

			TransientInput* transientinput=inputs->SetDatasetTransientInput(DslGlobalAverageThermostericSeaLevelChangeEnum,i, times,N);
			
			for(Object* & object : elements->objects){
				Element* element=xDynamicCast<Element*>(object);

				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);
		/*}}}*/
		iomodel->FetchDataToInput(inputs,elements,"md.dsl.sea_surface_height_change_above_geoid",DslSeaSurfaceHeightChangeAboveGeoidEnum);
		iomodel->FetchDataToInput(inputs,elements,"md.dsl.sea_water_pressure_change_at_sea_floor",DslSeaWaterPressureChangeAtSeaFloorEnum);

	} /*}}}*/
	else _error_("Dsl model " << dslmodel << " not implemented yet!");
		
	

	/*Initialize cumdeltalthickness and sealevel rise rate input*/
	InputUpdateFromConstantx(inputs,elements,0.,SealevelriseCumDeltathicknessEnum);
	InputUpdateFromConstantx(inputs,elements,0.,SealevelNEsaRateEnum);
	InputUpdateFromConstantx(inputs,elements,0.,SealevelUEsaRateEnum);
	InputUpdateFromConstantx(inputs,elements,0.,SealevelRSLRateEnum);
	InputUpdateFromConstantx(inputs,elements,0.,SealevelEustaticMaskEnum);
	InputUpdateFromConstantx(inputs,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;
	IssmDouble* love_th=NULL;
	IssmDouble* love_tk=NULL;
	IssmDouble* love_tl=NULL;
	int         dslmodel=0;

	IssmDouble* G_rigid = NULL;
	IssmDouble* G_rigid_local = NULL;
	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;
	IssmDouble  planetradius=0;
	IssmDouble  planetarea=0;
	bool		rigid=false;
	bool		elastic=false;
	bool		rotation=false;

	int     numoutputs;
	char**  requestedoutputs = NULL;

	/*transition vectors: */
	IssmDouble **transitions    = NULL;
	int         *transitions_M    = NULL;
	int         *transitions_N    = NULL;
	int          ntransitions;
	IssmDouble*  partitionice=NULL;
	IssmDouble*  partitionhydro=NULL;
	IssmDouble*  bslrice_partition=NULL;
	IssmDouble*  bslrhydro_partition=NULL;
	int          npartice,nparthydro,nel;


	/*some constant parameters: */
	parameters->AddObject(iomodel->CopyConstantObject("md.dsl.model",DslModelEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.solidearth.settings.runfrequency",SolidearthSettingsRunFrequencyEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.solidearth.settings.reltol",SolidearthSettingsReltolEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.solidearth.settings.abstol",SolidearthSettingsAbstolEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.solidearth.settings.maxiter",SolidearthSettingsMaxiterEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.solidearth.settings.rigid",SolidearthSettingsRigidEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.solidearth.settings.horiz",SolidearthSettingsHorizEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.solidearth.settings.elastic",SolidearthSettingsElasticEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.solidearth.settings.rotation",SolidearthSettingsRotationEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.solidearth.rotational.equatorialmoi",RotationalEquatorialMoiEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.solidearth.rotational.polarmoi",RotationalPolarMoiEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.solidearth.rotational.angularvelocity",RotationalAngularVelocityEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.solidearth.settings.ocean_area_scaling",SolidearthSettingsOceanAreaScalingEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.solidearth.settings.computesealevelchange",SolidearthSettingsComputesealevelchangeEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.solidearth.settings.isgrd",SolidearthSettingsGRDEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.solidearth.planetradius",SolidearthPlanetRadiusEnum));
	parameters->AddObject(iomodel->CopyConstantObject("md.solidearth.settings.glfraction",SolidearthSettingsGlfractionEnum));
	parameters->AddObject(new DoubleParam(CumBslrEnum,0.0));
	parameters->AddObject(new DoubleParam(CumBslrIceEnum,0.0));
	parameters->AddObject(new DoubleParam(CumBslrHydroEnum,0.0));
	parameters->AddObject(new DoubleParam(CumGmtslrEnum,0.0));

	/*compute planet area and plug into parameters:*/
	iomodel->FetchData(&planetradius,"md.solidearth.planetradius");
	planetarea=4*PI*planetradius*planetradius;
	parameters->AddObject(new DoubleParam(SolidearthPlanetAreaEnum,planetarea));

	/*Deal with partition of the barystatic contribution:*/
	iomodel->FetchData(&npartice,"md.solidearth.npartice");
	parameters->AddObject(new IntParam(SolidearthNpartIceEnum,npartice));
	if(npartice){
		iomodel->FetchData(&partitionice,&nel,NULL,"md.solidearth.partitionice");
		parameters->AddObject(new DoubleMatParam(SolidearthPartitionIceEnum,partitionice,nel,1));
		parameters->AddObject(iomodel->CopyConstantObject("md.solidearth.npartice",SolidearthNpartIceEnum));
		bslrice_partition=xNewZeroInit<IssmDouble>(npartice);
		parameters->AddObject(new DoubleMatParam(CumBslrIcePartitionEnum,bslrice_partition,npartice,1));
		xDelete<IssmDouble>(partitionice);
	}
	iomodel->FetchData(&nparthydro,"md.solidearth.nparthydro");
	parameters->AddObject(new IntParam(SolidearthNpartHydroEnum,nparthydro));
	if(nparthydro){
		iomodel->FetchData(&partitionhydro,&nel,NULL,"md.solidearth.partitionhydro");
		parameters->AddObject(new DoubleMatParam(SolidearthPartitionHydroEnum,partitionhydro,nel,1));
		parameters->AddObject(iomodel->CopyConstantObject("md.solidearth.nparthydro",SolidearthNpartHydroEnum));
		bslrhydro_partition=xNewZeroInit<IssmDouble>(nparthydro);
		parameters->AddObject(new DoubleMatParam(CumBslrHydroPartitionEnum,bslrhydro_partition,nparthydro,1));
		xDelete<IssmDouble>(partitionhydro);
	}

	/*Deal with dsl multi-model ensembles: {{{*/
	iomodel->FetchData(&dslmodel,"md.dsl.model");
	parameters->AddObject(iomodel->CopyConstantObject("md.dsl.compute_fingerprints",DslComputeFingerprintsEnum));
	if(dslmodel==2){
		IssmDouble modelid; 
		int nummodels;
		
		/*create double param, not int param, because Dakota will be updating it as 
		 * a double potentially: */
		iomodel->FetchData(&modelid,"md.dsl.modelid");
		parameters->AddObject(new DoubleParam(DslModelidEnum,modelid));
		parameters->AddObject(iomodel->CopyConstantObject("md.dsl.nummodels",DslNummodelsEnum));
		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(&rigid,"md.solidearth.settings.rigid");
	iomodel->FetchData(&elastic,"md.solidearth.settings.elastic");
	iomodel->FetchData(&rotation,"md.solidearth.settings.rotation");

	if(elastic | rigid){
		/*compute green functions for a range of angles*/
		iomodel->FetchData(&degacc,"md.solidearth.settings.degacc");
		M=reCast<int,IssmDouble>(180./degacc+1.);
	}

	/*love numbers: */
	if(elastic){
		iomodel->FetchData(&love_h,&nl,NULL,"md.solidearth.lovenumbers.h");
		iomodel->FetchData(&love_k,&nl,NULL,"md.solidearth.lovenumbers.k");
		iomodel->FetchData(&love_l,&nl,NULL,"md.solidearth.lovenumbers.l");
		iomodel->FetchData(&love_th,&nl,NULL,"md.solidearth.lovenumbers.th");
		iomodel->FetchData(&love_tk,&nl,NULL,"md.solidearth.lovenumbers.tk");
		iomodel->FetchData(&love_tl,&nl,NULL,"md.solidearth.lovenumbers.tl");

		parameters->AddObject(new DoubleMatParam(LoadLoveHEnum,love_h,nl,1));
		parameters->AddObject(new DoubleMatParam(LoadLoveKEnum,love_k,nl,1));
		parameters->AddObject(new DoubleMatParam(LoadLoveLEnum,love_l,nl,1));
		parameters->AddObject(new DoubleMatParam(TidalLoveHEnum,love_th,nl,1));
		parameters->AddObject(new DoubleMatParam(TidalLoveKEnum,love_tk,nl,1));
		parameters->AddObject(new DoubleMatParam(TidalLoveLEnum,love_tl,nl,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
	}
	if(rigid){
		#ifdef _HAVE_AD_
		G_rigid=xNew<IssmDouble>(M,"t");
		#else
		G_rigid=xNew<IssmDouble>(M);
		#endif
	}
	
	if(rotation)parameters->AddObject(iomodel->CopyConstantObject("md.solidearth.lovenumbers.tk2secular",TidalLoveK2SecularEnum));

	if(rigid | elastic){

		/*compute combined legendre + love number (elastic green function:*/
		m=DetermineLocalSize(M,IssmComm::GetComm());
		GetOwnershipBoundariesFromRange(&lower_row,&upper_row,m,IssmComm::GetComm());
	}
	if(elastic){
		#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
	}
	if(rigid){
		#ifdef _HAVE_AD_
		G_rigid_local=xNew<IssmDouble>(m,"t");
		#else
		G_rigid_local=xNew<IssmDouble>(m);
		#endif
	}

	if(rigid){
		for(int i=lower_row;i<upper_row;i++){
			IssmDouble alpha,x;
			alpha= reCast<IssmDouble>(i)*degacc * PI / 180.0;
			G_rigid_local[i-lower_row]= .5/sin(alpha/2.0);
		}
	}
	if(elastic){
		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])*G_rigid_local[i-lower_row];
			U_elastic_local[i-lower_row]= (love_h[nl-1])*G_rigid_local[i-lower_row];
			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 
			}
		}
	}
	if(rigid){

		/*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_rigid_local, m, ISSM_MPI_DOUBLE, G_rigid, recvcounts, displs, ISSM_MPI_DOUBLE,IssmComm::GetComm());
		if(elastic){
			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 resources: */
		xDelete<int>(recvcounts);
		xDelete<int>(displs);

		/*Avoid singularity at 0: */
		G_rigid[0]=G_rigid[1];
		if(elastic){
			G_elastic[0]=G_elastic[1];
			U_elastic[0]=U_elastic[1];
			H_elastic[0]=H_elastic[1];
		}
		

		/*Save our precomputed tables into parameters*/
		parameters->AddObject(new DoubleVecParam(SealevelriseGRigidEnum,G_rigid,M));
		if(elastic){
			parameters->AddObject(new DoubleVecParam(SealevelriseGElasticEnum,G_elastic,M));
			parameters->AddObject(new DoubleVecParam(SealevelriseUElasticEnum,U_elastic,M));
			parameters->AddObject(new DoubleVecParam(SealevelriseHElasticEnum,H_elastic,M));
		}

		/*free resources: */
		xDelete<IssmDouble>(G_rigid);
		xDelete<IssmDouble>(G_rigid_local);
		if(elastic){
			xDelete<IssmDouble>(love_h);
			xDelete<IssmDouble>(love_k);
			xDelete<IssmDouble>(love_l);
			xDelete<IssmDouble>(love_th);
			xDelete<IssmDouble>(love_tk);
			xDelete<IssmDouble>(love_tl);
			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);
		}
	} /*}}}*/

	/*Indicate we have not yet run the Geometry Core module: */
	parameters->AddObject(new BoolParam(SealevelriseGeometryDoneEnum,false));
	/*}}}*/

	/*Transitions:{{{ */
	iomodel->FetchData(&transitions,&transitions_M,&transitions_N,&ntransitions,"md.solidearth.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.solidearth.requested_outputs");
	if(numoutputs)parameters->AddObject(new StringArrayParam(SealevelriseRequestedOutputsEnum,requestedoutputs,numoutputs));
	iomodel->DeleteData(&requestedoutputs,numoutputs,"md.solidearth.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_interp,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;
}/*}}}*/
