source: issm/trunk-jpl/src/c/analyses/EnthalpyAnalysis.cpp@ 26047

Last change on this file since 26047 was 26047, checked in by Eric.Larour, 4 years ago

CHG: huge commit on solid earth capability rewrite. Complete cleanup of the sea level core.
New mass transport capabilities for ocean and tws. No more giacore. GiaIvins folded into
the sea level core. Debugging of Materials.

File size: 67.9 KB
Line 
1#include "./EnthalpyAnalysis.h"
2#include "../toolkits/toolkits.h"
3#include "../classes/classes.h"
4#include "../shared/shared.h"
5#include "../modules/modules.h"
6#include "../solutionsequences/solutionsequences.h"
7#include "../cores/cores.h"
8
9/*Model processing*/
10void EnthalpyAnalysis::CreateConstraints(Constraints* constraints,IoModel* iomodel){/*{{{*/
11
12 /*Intermediary*/
13 int count;
14 int M,N;
15 bool spcpresent = false;
16 int finiteelement;
17 IssmDouble heatcapacity;
18 IssmDouble referencetemperature;
19
20 /*Output*/
21 IssmDouble *spcvector = NULL;
22 IssmDouble *spcvectorstatic = NULL;
23 IssmDouble* times=NULL;
24 IssmDouble* values=NULL;
25 IssmDouble* issurface = NULL;
26
27 /*Fetch parameters: */
28 iomodel->FindConstant(&heatcapacity,"md.materials.heatcapacity");
29 iomodel->FindConstant(&referencetemperature,"md.constants.referencetemperature");
30 iomodel->FindConstant(&finiteelement,"md.thermal.fe");
31
32 /*return if 2d mesh*/
33 if(iomodel->domaintype==Domain2DhorizontalEnum) return;
34
35 /*Fetch data: */
36 iomodel->FetchData(&issurface,&M,&N,"md.mesh.vertexonsurface"); _assert_(N>0); _assert_(M==iomodel->numberofvertices);
37 iomodel->FetchData(&spcvector,&M,&N,"md.thermal.spctemperature");
38 iomodel->FetchData(&spcvectorstatic,&M,&N,"md.thermal.spctemperature");
39
40 /*Specific case for PDD, we want the constaints to be updated by the PDD scheme itself*/
41 bool isdynamic = false;
42 if (iomodel->solution_enum==TransientSolutionEnum){
43 int smb_model;
44 iomodel->FindConstant(&smb_model,"md.smb.model");
45 if(smb_model==SMBpddEnum) isdynamic=true;
46 if(smb_model==SMBd18opddEnum) isdynamic=true;
47 if(smb_model==SMBpddSicopolisEnum) isdynamic=true;
48 }
49
50 /*Convert spcs from temperatures to enthalpy*/
51 _assert_(N>0); _assert_(M>=iomodel->numberofvertices);
52 for(int i=0;i<iomodel->numberofvertices;i++){
53 for(int j=0;j<N;j++){
54 if (isdynamic){
55 if (issurface[i]==1){
56 spcvector[i*N+j] = heatcapacity*(spcvector[i*N+j]-referencetemperature);
57 spcvectorstatic[i*N+j] = NAN;
58 }
59 else{
60 spcvector[i*N+j] = NAN;
61 spcvectorstatic[i*N+j] = heatcapacity*(spcvectorstatic[i*N+j]-referencetemperature);
62 }
63 }
64 else{
65 spcvector[i*N+j] = heatcapacity*(spcvector[i*N+j]-referencetemperature);
66 }
67 }
68 }
69
70 if(isdynamic){
71 IoModelToDynamicConstraintsx(constraints,iomodel,spcvector,iomodel->numberofvertices,1,EnthalpyAnalysisEnum,finiteelement);
72 IoModelToConstraintsx(constraints,iomodel,spcvectorstatic,M,N,EnthalpyAnalysisEnum,finiteelement);
73 }
74 else{
75 IoModelToConstraintsx(constraints,iomodel,spcvector,M,N,EnthalpyAnalysisEnum,finiteelement);
76 }
77
78 /*Free ressources:*/
79 iomodel->DeleteData(spcvector,"md.thermal.spctemperature");
80 iomodel->DeleteData(spcvectorstatic,"md.thermal.spctemperature");
81 iomodel->DeleteData(issurface,"md.mesh.vertexonsurface");
82 xDelete<IssmDouble>(times);
83 xDelete<IssmDouble>(values);
84}/*}}}*/
85void EnthalpyAnalysis::CreateLoads(Loads* loads, IoModel* iomodel){/*{{{*/
86
87 /*No loads */
88}/*}}}*/
89void EnthalpyAnalysis::CreateNodes(Nodes* nodes,IoModel* iomodel,bool isamr){/*{{{*/
90
91 int finiteelement;
92 iomodel->FindConstant(&finiteelement,"md.thermal.fe");
93
94 if(iomodel->domaintype==Domain3DEnum) iomodel->FetchData(2,"md.mesh.vertexonbase","md.mesh.vertexonsurface");
95 ::CreateNodes(nodes,iomodel,EnthalpyAnalysisEnum,finiteelement);
96 iomodel->DeleteData(2,"md.mesh.vertexonbase","md.mesh.vertexonsurface");
97}/*}}}*/
98int EnthalpyAnalysis::DofsPerNode(int** doflist,int domaintype,int approximation){/*{{{*/
99 return 1;
100}/*}}}*/
101void EnthalpyAnalysis::UpdateElements(Elements* elements,Inputs* inputs,IoModel* iomodel,int analysis_counter,int analysis_type){/*{{{*/
102
103 bool dakota_analysis,ismovingfront,isenthalpy;
104 int frictionlaw,basalforcing_model,materialstype;
105 int FrictionCoupling;
106
107 /*Now, is the model 3d? otherwise, do nothing: */
108 if(iomodel->domaintype==Domain2DhorizontalEnum)return;
109
110 /*Is enthalpy requested?*/
111 iomodel->FindConstant(&isenthalpy,"md.thermal.isenthalpy");
112 if(!isenthalpy) return;
113
114 /*Fetch data needed: */
115 iomodel->FetchData(3,"md.initialization.temperature","md.initialization.waterfraction","md.initialization.pressure");
116
117 /*Finite element type*/
118 int finiteelement;
119 iomodel->FindConstant(&finiteelement,"md.thermal.fe");
120
121 /*Update elements: */
122 int counter=0;
123 for(int i=0;i<iomodel->numberofelements;i++){
124 if(iomodel->my_elements[i]){
125 Element* element=(Element*)elements->GetObjectByOffset(counter);
126 element->Update(inputs,i,iomodel,analysis_counter,analysis_type,finiteelement);
127 counter++;
128 }
129 }
130
131 iomodel->FindConstant(&dakota_analysis,"md.qmu.isdakota");
132 iomodel->FindConstant(&ismovingfront,"md.transient.ismovingfront");
133 iomodel->FindConstant(&frictionlaw,"md.friction.law");
134 iomodel->FindConstant(&materialstype,"md.materials.type");
135
136 iomodel->FetchDataToInput(inputs,elements,"md.geometry.thickness",ThicknessEnum);
137 iomodel->FetchDataToInput(inputs,elements,"md.geometry.surface",SurfaceEnum);
138 iomodel->FetchDataToInput(inputs,elements,"md.initialization.sealevel",SealevelEnum,0);
139 iomodel->FetchDataToInput(inputs,elements,"md.geometry.base",BaseEnum);
140 iomodel->FetchDataToInput(inputs,elements,"md.mask.ice_levelset",MaskIceLevelsetEnum);
141 iomodel->FetchDataToInput(inputs,elements,"md.mask.ocean_levelset",MaskOceanLevelsetEnum);
142 if(iomodel->domaintype!=Domain2DhorizontalEnum){
143 iomodel->FetchDataToInput(inputs,elements,"md.mesh.vertexonbase",MeshVertexonbaseEnum);
144 iomodel->FetchDataToInput(inputs,elements,"md.mesh.vertexonsurface",MeshVertexonsurfaceEnum);
145 }
146 iomodel->FetchDataToInput(inputs,elements,"md.initialization.pressure",PressureEnum);
147 iomodel->FetchDataToInput(inputs,elements,"md.initialization.temperature",TemperatureEnum);
148 iomodel->FetchDataToInput(inputs,elements,"md.initialization.waterfraction",WaterfractionEnum);
149 iomodel->FetchDataToInput(inputs,elements,"md.initialization.enthalpy",EnthalpyEnum);
150 iomodel->FetchDataToInput(inputs,elements,"md.initialization.watercolumn",WatercolumnEnum);
151 iomodel->FetchDataToInput(inputs,elements,"md.basalforcings.groundedice_melting_rate",BasalforcingsGroundediceMeltingRateEnum);
152 iomodel->FetchDataToInput(inputs,elements,"md.initialization.vx",VxEnum);
153 iomodel->FetchDataToInput(inputs,elements,"md.initialization.vy",VyEnum);
154 iomodel->FetchDataToInput(inputs,elements,"md.initialization.vz",VzEnum);
155 InputUpdateFromConstantx(inputs,elements,0.,VxMeshEnum);
156 InputUpdateFromConstantx(inputs,elements,0.,VyMeshEnum);
157 InputUpdateFromConstantx(inputs,elements,0.,VzMeshEnum);
158 if(ismovingfront){
159 iomodel->FetchDataToInput(inputs,elements,"md.mesh.vertexonbase",MeshVertexonbaseEnum); // required for updating active nodes
160 }
161
162 /*Basal forcings variables*/
163 iomodel->FindConstant(&basalforcing_model,"md.basalforcings.model");
164 switch(basalforcing_model){
165 case MantlePlumeGeothermalFluxEnum:
166 break;
167 default:
168 iomodel->FetchDataToInput(inputs,elements,"md.basalforcings.geothermalflux",BasalforcingsGeothermalfluxEnum);
169 break;
170 }
171
172 /*Rheology type*/
173 iomodel->FetchDataToInput(inputs,elements,"md.materials.rheology_B",MaterialsRheologyBEnum);
174 switch(materialstype){
175 case MatenhancediceEnum:
176 iomodel->FetchDataToInput(inputs,elements,"md.materials.rheology_n",MaterialsRheologyNEnum);
177 iomodel->FetchDataToInput(inputs,elements,"md.materials.rheology_E",MaterialsRheologyEEnum);
178 break;
179 case MatdamageiceEnum:
180 iomodel->FetchDataToInput(inputs,elements,"md.materials.rheology_n",MaterialsRheologyNEnum);
181 break;
182 case MatestarEnum:
183 iomodel->FetchDataToInput(inputs,elements,"md.materials.rheology_Ec",MaterialsRheologyEcEnum);
184 iomodel->FetchDataToInput(inputs,elements,"md.materials.rheology_Es",MaterialsRheologyEsEnum);
185 break;
186 case MaticeEnum:
187 iomodel->FetchDataToInput(inputs,elements,"md.materials.rheology_n",MaterialsRheologyNEnum);
188 break;
189 default:
190 _error_("not supported");
191 }
192
193 /*Friction law variables*/
194 switch(frictionlaw){
195 case 1:
196 iomodel->FindConstant(&FrictionCoupling,"md.friction.coupling");
197 iomodel->FetchDataToInput(inputs,elements,"md.friction.coefficient",FrictionCoefficientEnum);
198 iomodel->FetchDataToInput(inputs,elements,"md.friction.p",FrictionPEnum);
199 iomodel->FetchDataToInput(inputs,elements,"md.friction.q",FrictionQEnum);
200 if (FrictionCoupling==3){
201 iomodel->FetchDataToInput(inputs,elements,"md.friction.effective_pressure",FrictionEffectivePressureEnum);}
202 else if(FrictionCoupling==4){
203 iomodel->FetchDataToInput(inputs,elements,"md.friction.effective_pressure",EffectivePressureEnum);
204 }
205 break;
206 case 2:
207 iomodel->FetchDataToInput(inputs,elements,"md.friction.C",FrictionCEnum);
208 iomodel->FetchDataToInput(inputs,elements,"md.friction.m",FrictionMEnum);
209 break;
210 case 3:
211 iomodel->FindConstant(&FrictionCoupling,"md.friction.coupling");
212 iomodel->FetchDataToInput(inputs,elements,"md.friction.C",FrictionCEnum);
213 iomodel->FetchDataToInput(inputs,elements,"md.friction.As",FrictionAsEnum);
214 iomodel->FetchDataToInput(inputs,elements,"md.friction.q",FrictionQEnum);
215 if (FrictionCoupling==3){
216 iomodel->FetchDataToInput(inputs,elements,"md.friction.effective_pressure",FrictionEffectivePressureEnum);}
217 else if(FrictionCoupling==4){
218 iomodel->FetchDataToInput(inputs,elements,"md.friction.effective_pressure",EffectivePressureEnum);
219 }
220 break;
221 case 4:
222 iomodel->FetchDataToInput(inputs,elements,"md.friction.coefficient",FrictionCoefficientEnum);
223 iomodel->FetchDataToInput(inputs,elements,"md.friction.p",FrictionPEnum);
224 iomodel->FetchDataToInput(inputs,elements,"md.friction.q",FrictionQEnum);
225 iomodel->FetchDataToInput(inputs,elements,"md.initialization.pressure",PressureEnum);
226 iomodel->FetchDataToInput(inputs,elements,"md.initialization.temperature",TemperatureEnum);
227 iomodel->FindConstant(&FrictionCoupling,"md.friction.coupling");
228 break;
229 case 5:
230 iomodel->FetchDataToInput(inputs,elements,"md.friction.coefficient",FrictionCoefficientEnum);
231 iomodel->FetchDataToInput(inputs,elements,"md.friction.p",FrictionPEnum);
232 iomodel->FetchDataToInput(inputs,elements,"md.friction.q",FrictionQEnum);
233 iomodel->FetchDataToInput(inputs,elements,"md.friction.water_layer",FrictionWaterLayerEnum);
234 break;
235 case 6:
236 iomodel->FetchDataToInput(inputs,elements,"md.friction.C",FrictionCEnum);
237 iomodel->FetchDataToInput(inputs,elements,"md.friction.m",FrictionMEnum);
238 iomodel->FetchDataToInput(inputs,elements,"md.initialization.pressure",PressureEnum);
239 iomodel->FetchDataToInput(inputs,elements,"md.initialization.temperature",TemperatureEnum);
240 break;
241 case 7:
242 iomodel->FindConstant(&FrictionCoupling,"md.friction.coupling");
243 iomodel->FetchDataToInput(inputs,elements,"md.friction.coefficient",FrictionCoefficientEnum);
244 iomodel->FetchDataToInput(inputs,elements,"md.friction.coefficientcoulomb",FrictionCoefficientcoulombEnum);
245 iomodel->FetchDataToInput(inputs,elements,"md.friction.p",FrictionPEnum);
246 iomodel->FetchDataToInput(inputs,elements,"md.friction.q",FrictionQEnum);
247 if (FrictionCoupling==3){
248 iomodel->FetchDataToInput(inputs,elements,"md.friction.effective_pressure",FrictionEffectivePressureEnum);}
249 else if(FrictionCoupling==4){
250 iomodel->FetchDataToInput(inputs,elements,"md.friction.effective_pressure",EffectivePressureEnum);
251 }
252 break;
253 case 9:
254 iomodel->FetchDataToInput(inputs,elements,"md.friction.coefficient",FrictionCoefficientEnum);
255 iomodel->FetchDataToInput(inputs,elements,"md.friction.pressure_adjusted_temperature",FrictionPressureAdjustedTemperatureEnum);
256 InputUpdateFromConstantx(inputs,elements,1.,FrictionPEnum);
257 InputUpdateFromConstantx(inputs,elements,1.,FrictionQEnum);
258 break;
259 default:
260 _error_("friction law not supported");
261 }
262
263 /*Free data: */
264 iomodel->DeleteData(3,"md.initialization.temperature","md.initialization.waterfraction","md.initialization.pressure");
265
266}/*}}}*/
267void EnthalpyAnalysis::UpdateParameters(Parameters* parameters,IoModel* iomodel,int solution_enum,int analysis_enum){/*{{{*/
268
269 int numoutputs;
270 char** requestedoutputs = NULL;
271
272 parameters->AddObject(iomodel->CopyConstantObject("md.thermal.stabilization",ThermalStabilizationEnum));
273 parameters->AddObject(iomodel->CopyConstantObject("md.thermal.maxiter",ThermalMaxiterEnum));
274 parameters->AddObject(iomodel->CopyConstantObject("md.thermal.reltol",ThermalReltolEnum));
275 parameters->AddObject(iomodel->CopyConstantObject("md.thermal.isenthalpy",ThermalIsenthalpyEnum));
276 parameters->AddObject(iomodel->CopyConstantObject("md.thermal.isdynamicbasalspc",ThermalIsdynamicbasalspcEnum));
277 parameters->AddObject(iomodel->CopyConstantObject("md.thermal.isdrainicecolumn",ThermalIsdrainicecolumnEnum));
278 parameters->AddObject(iomodel->CopyConstantObject("md.thermal.watercolumn_upperlimit",ThermalWatercolumnUpperlimitEnum));
279 parameters->AddObject(iomodel->CopyConstantObject("md.friction.law",FrictionLawEnum));
280
281 iomodel->FindConstant(&requestedoutputs,&numoutputs,"md.thermal.requested_outputs");
282 parameters->AddObject(new IntParam(ThermalNumRequestedOutputsEnum,numoutputs));
283 if(numoutputs)parameters->AddObject(new StringArrayParam(ThermalRequestedOutputsEnum,requestedoutputs,numoutputs));
284 iomodel->DeleteData(&requestedoutputs,numoutputs,"md.thermal.requested_outputs");
285
286 /*Deal with friction parameters*/
287 int frictionlaw;
288 iomodel->FindConstant(&frictionlaw,"md.friction.law");
289 if(frictionlaw==6){
290 parameters->AddObject(iomodel->CopyConstantObject("md.friction.gamma",FrictionGammaEnum));
291 }
292 if(frictionlaw==4){
293 parameters->AddObject(iomodel->CopyConstantObject("md.friction.gamma",FrictionGammaEnum));
294 parameters->AddObject(iomodel->CopyConstantObject("md.friction.coupling",FrictionCouplingEnum));
295 parameters->AddObject(iomodel->CopyConstantObject("md.friction.effective_pressure_limit",FrictionEffectivePressureLimitEnum));
296 }
297 if(frictionlaw==1 || frictionlaw==3 || frictionlaw==7){
298 parameters->AddObject(iomodel->CopyConstantObject("md.friction.coupling",FrictionCouplingEnum));
299 parameters->AddObject(iomodel->CopyConstantObject("md.friction.effective_pressure_limit",FrictionEffectivePressureLimitEnum));
300 }
301 if(frictionlaw==9){
302 parameters->AddObject(iomodel->CopyConstantObject("md.friction.gamma",FrictionGammaEnum));
303 parameters->AddObject(iomodel->CopyConstantObject("md.friction.effective_pressure_limit",FrictionEffectivePressureLimitEnum));
304 parameters->AddObject(new IntParam(FrictionCouplingEnum,0));
305 }
306}/*}}}*/
307
308/*Finite Element Analysis*/
309void EnthalpyAnalysis::ApplyBasalConstraints(IssmDouble* serial_spc,Element* element){/*{{{*/
310
311 /* Do not check if ice in element, this may lead to inconsistencies between cpu partitions */
312 /* Only update constraints at the base. */
313 if(!(element->IsOnBase())) return;
314
315 /*Intermediary*/
316 bool isdynamicbasalspc;
317 int numindices;
318 int *indices = NULL;
319 Node* node = NULL;
320 IssmDouble pressure;
321
322 /*Check wether dynamic basal boundary conditions are activated */
323 element->FindParam(&isdynamicbasalspc,ThermalIsdynamicbasalspcEnum);
324 if(!isdynamicbasalspc) return;
325
326 /*Get parameters and inputs: */
327 Input* pressure_input = element->GetInput(PressureEnum); _assert_(pressure_input);
328
329 /*Fetch indices of basal & surface nodes for this finite element*/
330 Penta *penta = (Penta *) element; // TODO: add Basal-/SurfaceNodeIndices to element.h, and change this to Element*
331 penta->BasalNodeIndices(&numindices,&indices,element->GetElementType());
332
333 GaussPenta* gauss=new GaussPenta();
334 for(int i=0;i<numindices;i++){
335 gauss->GaussNode(element->GetElementType(),indices[i]);
336
337 pressure_input->GetInputValue(&pressure,gauss);
338
339 /*apply or release spc*/
340 node=element->GetNode(indices[i]);
341 if(!node->IsActive()) continue;
342 if(serial_spc[node->Sid()]==1.){
343 pressure_input->GetInputValue(&pressure, gauss);
344 node->ApplyConstraint(0,PureIceEnthalpy(element,pressure));
345 }
346 else {
347 node->DofInFSet(0);
348 }
349 }
350
351 /*Free ressources:*/
352 xDelete<int>(indices);
353 delete gauss;
354}/*}}}*/
355void EnthalpyAnalysis::ComputeBasalMeltingrate(FemModel* femmodel){/*{{{*/
356 /*Compute basal melting rates: */
357 for(Object* & object : femmodel->elements->objects){
358 Element* element=xDynamicCast<Element*>(object);
359 ComputeBasalMeltingrate(element);
360 }
361
362 /*extrude inputs*/
363 femmodel->parameters->SetParam(BasalforcingsGroundediceMeltingRateEnum,InputToExtrudeEnum);
364 extrudefrombase_core(femmodel);
365}/*}}}*/
366void EnthalpyAnalysis::ComputeBasalMeltingrate(Element* element){/*{{{*/
367 /*Calculate the basal melt rates of the enthalpy model after Aschwanden 2012*/
368 /* melting rate is positive when melting, negative when refreezing*/
369
370 /* Check if ice in element */
371 if(!element->IsIceInElement()) return;
372
373 /* Only compute melt rates at the base of grounded ice*/
374 if(!element->IsOnBase() || element->IsFloating()) return;
375
376 /* Intermediaries */
377 bool converged;
378 const int dim=3;
379 int i,is,state;
380 int nodedown,nodeup,numnodes,numsegments;
381 int enthalpy_enum;
382 IssmDouble vec_heatflux[dim],normal_base[dim],d1enthalpy[dim],d1pressure[dim];
383 IssmDouble basalfriction,alpha2,geothermalflux,heatflux;
384 IssmDouble dt,yts;
385 IssmDouble melting_overshoot,lambda;
386 IssmDouble vx,vy,vz;
387 IssmDouble *xyz_list = NULL;
388 IssmDouble *xyz_list_base = NULL;
389 int *pairindices = NULL;
390
391 /*Fetch parameters*/
392 element->GetVerticesCoordinates(&xyz_list);
393 element->GetVerticesCoordinatesBase(&xyz_list_base);
394 element->GetInputValue(&converged,ConvergedEnum);
395 element->FindParam(&dt,TimesteppingTimeStepEnum);
396 element->FindParam(&yts, ConstantsYtsEnum);
397
398 if(dt==0. && !converged) enthalpy_enum=EnthalpyPicardEnum;
399 else enthalpy_enum=EnthalpyEnum;
400
401 IssmDouble latentheat = element->FindParam(MaterialsLatentheatEnum);
402 IssmDouble rho_ice = element->FindParam(MaterialsRhoIceEnum);
403 IssmDouble rho_water = element->FindParam(MaterialsRhoFreshwaterEnum);
404 IssmDouble beta = element->FindParam(MaterialsBetaEnum);
405 IssmDouble kappa = EnthalpyDiffusionParameterVolume(element,enthalpy_enum); _assert_(kappa>=0.);
406 IssmDouble kappa_mix;
407
408 /*retrieve inputs*/
409 Input* enthalpy_input = element->GetInput(enthalpy_enum); _assert_(enthalpy_input);
410 Input* pressure_input = element->GetInput(PressureEnum); _assert_(pressure_input);
411 Input* geothermalflux_input = element->GetInput(BasalforcingsGeothermalfluxEnum); _assert_(geothermalflux_input);
412 Input* vx_input = element->GetInput(VxEnum); _assert_(vx_input);
413 Input* vy_input = element->GetInput(VyEnum); _assert_(vy_input);
414 Input* vz_input = element->GetInput(VzEnum); _assert_(vz_input);
415
416 /*Build friction element, needed later: */
417 Friction* friction=new Friction(element,dim);
418
419 /******** MELTING RATES ************************************//*{{{*/
420 element->NormalBase(&normal_base[0],xyz_list_base);
421 element->VerticalSegmentIndicesBase(&pairindices,&numsegments);
422 IssmDouble* meltingrate_enthalpy = xNew<IssmDouble>(numsegments);
423 IssmDouble* heating = xNew<IssmDouble>(numsegments);
424
425 numnodes=element->GetNumberOfNodes();
426 IssmDouble* enthalpies = xNew<IssmDouble>(numnodes);
427 IssmDouble* pressures = xNew<IssmDouble>(numnodes);
428 IssmDouble* watercolumns = xNew<IssmDouble>(numnodes);
429 IssmDouble* basalmeltingrates = xNew<IssmDouble>(numnodes);
430 element->GetInputListOnNodes(enthalpies,enthalpy_enum);
431 element->GetInputListOnNodes(pressures,PressureEnum);
432 element->GetInputListOnNodes(watercolumns,WatercolumnEnum);
433 element->GetInputListOnNodes(basalmeltingrates,BasalforcingsGroundediceMeltingRateEnum);
434
435 IssmDouble watercolumnupperlimit = element->FindParam(ThermalWatercolumnUpperlimitEnum);
436
437 Gauss* gauss=element->NewGauss();
438 for(is=0;is<numsegments;is++){
439 nodedown = pairindices[is*2+0];
440 nodeup = pairindices[is*2+1];
441 gauss->GaussNode(element->GetElementType(),nodedown);
442
443 state=GetThermalBasalCondition(element, enthalpies[nodedown], enthalpies[nodeup], pressures[nodedown], pressures[nodeup], watercolumns[nodedown], basalmeltingrates[nodedown]);
444 switch (state) {
445 case 0:
446 // cold, dry base: apply basal surface forcing
447 for(i=0;i<3;i++) vec_heatflux[i]=0.;
448 break;
449 case 1: case 2: case 3:
450 // case 1 : cold, wet base: keep at pressure melting point
451 // case 2: temperate, thin refreezing base: release spc
452 // case 3: temperate, thin melting base: set spc
453 enthalpy_input->GetInputDerivativeValue(&d1enthalpy[0],xyz_list,gauss);
454 for(i=0;i<3;i++) vec_heatflux[i]=-kappa*d1enthalpy[i];
455 break;
456 case 4:
457 // temperate, thick melting base: set grad H*n=0
458 kappa_mix=GetWetIceConductivity(element, enthalpies[nodedown], pressures[nodedown]);
459 pressure_input->GetInputDerivativeValue(&d1pressure[0],xyz_list,gauss);
460 for(i=0;i<3;i++) vec_heatflux[i]=kappa_mix*beta*d1pressure[i];
461 break;
462 default:
463 _printf0_(" unknown thermal basal state found!");
464 }
465 if(state==0) meltingrate_enthalpy[is]=0.;
466 else{
467 /*heat flux along normal*/
468 heatflux=0.;
469 for(i=0;i<3;i++) heatflux+=(vec_heatflux[i])*normal_base[i];
470
471 /*basal friction*/
472 friction->GetAlpha2(&alpha2,gauss);
473 vx_input->GetInputValue(&vx,gauss); vy_input->GetInputValue(&vy,gauss); vz_input->GetInputValue(&vz,gauss);
474 basalfriction=alpha2*(vx*vx + vy*vy + vz*vz);
475 geothermalflux_input->GetInputValue(&geothermalflux,gauss);
476 /* -Mb= Fb-(q-q_geo)/((1-w)*L*rho), and (1-w)*rho=rho_ice, cf Aschwanden 2012, eqs.1, 2, 66*/
477 heating[is]=(heatflux+basalfriction+geothermalflux);
478 meltingrate_enthalpy[is]=heating[is]/(latentheat*rho_ice); // m/s water equivalent
479 }
480 }/*}}}*/
481
482 /******** UPDATE MELTINGRATES AND WATERCOLUMN **************//*{{{*/
483 for(is=0;is<numsegments;is++){
484 nodedown = pairindices[is*2+0];
485 nodeup = pairindices[is*2+1];
486 if(dt!=0.){
487 if(watercolumns[nodedown]+meltingrate_enthalpy[is]*dt<0.){ // prevent too much freeze on
488 lambda = -watercolumns[nodedown]/(dt*meltingrate_enthalpy[is]); _assert_(lambda>=0.); _assert_(lambda<1.);
489 watercolumns[nodedown]=0.;
490 basalmeltingrates[nodedown]=lambda*meltingrate_enthalpy[is]; // restrict freeze on only to size of watercolumn
491 enthalpies[nodedown]+=(1.-lambda)*dt/yts*meltingrate_enthalpy[is]*latentheat*rho_ice; // use rest of energy to cool down base: dE=L*m, m=(1-lambda)*meltingrate*rho_ice
492 }
493 else{
494 basalmeltingrates[nodedown]=meltingrate_enthalpy[is];
495 watercolumns[nodedown]+=dt*meltingrate_enthalpy[is];
496 }
497 if(watercolumns[nodedown]>watercolumnupperlimit) watercolumns[nodedown]=watercolumnupperlimit;
498 }
499 else{
500 basalmeltingrates[nodedown]=meltingrate_enthalpy[is];
501 if(watercolumns[nodedown]+meltingrate_enthalpy[is]<0.)
502 watercolumns[nodedown]=0.;
503 else
504 watercolumns[nodedown]+=meltingrate_enthalpy[is];
505 }
506 basalmeltingrates[nodedown]*=rho_water/rho_ice; // convert meltingrate from water to ice equivalent
507 _assert_(watercolumns[nodedown]>=0.);
508 }/*}}}*/
509
510 /*feed updated variables back into model*/
511 int finite_element = element->GetElementType(); if(finite_element==P1Enum) finite_element = P1DGEnum;
512 if(dt!=0.){
513 element->AddInput(enthalpy_enum,enthalpies,finite_element);
514 element->AddInput(WatercolumnEnum,watercolumns,finite_element);
515 }
516 element->AddInput(BasalforcingsGroundediceMeltingRateEnum,basalmeltingrates,P1DGEnum);
517
518 /*Clean up and return*/
519 delete gauss;
520 delete friction;
521 xDelete<int>(pairindices);
522 xDelete<IssmDouble>(enthalpies);
523 xDelete<IssmDouble>(pressures);
524 xDelete<IssmDouble>(watercolumns);
525 xDelete<IssmDouble>(basalmeltingrates);
526 xDelete<IssmDouble>(meltingrate_enthalpy);
527 xDelete<IssmDouble>(heating);
528 xDelete<IssmDouble>(xyz_list);
529 xDelete<IssmDouble>(xyz_list_base);
530}/*}}}*/
531void EnthalpyAnalysis::Core(FemModel* femmodel){/*{{{*/
532
533 IssmDouble dt;
534 bool isdynamicbasalspc;
535
536 femmodel->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
537 femmodel->parameters->FindParam(&isdynamicbasalspc,ThermalIsdynamicbasalspcEnum);
538
539 if(VerboseSolution()) _printf0_(" computing enthalpy\n");
540 femmodel->SetCurrentConfiguration(EnthalpyAnalysisEnum);
541 if((dt>0.) && isdynamicbasalspc) UpdateBasalConstraints(femmodel);
542 solutionsequence_thermal_nonlinear(femmodel);
543
544 /*transfer enthalpy to enthalpy picard for the next step: */
545 InputDuplicatex(femmodel,EnthalpyEnum,EnthalpyPicardEnum);
546
547 PostProcessing(femmodel);
548
549}/*}}}*/
550void EnthalpyAnalysis::PreCore(FemModel* femmodel){/*{{{*/
551 _error_("not implemented");
552}/*}}}*/
553ElementVector* EnthalpyAnalysis::CreateDVector(Element* element){/*{{{*/
554 /*Default, return NULL*/
555 return NULL;
556}/*}}}*/
557ElementMatrix* EnthalpyAnalysis::CreateJacobianMatrix(Element* element){/*{{{*/
558_error_("Not implemented");
559}/*}}}*/
560ElementMatrix* EnthalpyAnalysis::CreateKMatrix(Element* element){/*{{{*/
561
562 /* Check if ice in element */
563 if(!element->IsIceInElement()) return NULL;
564
565 /*compute all stiffness matrices for this element*/
566 ElementMatrix* Ke1=CreateKMatrixVolume(element);
567 ElementMatrix* Ke2=CreateKMatrixShelf(element);
568 ElementMatrix* Ke =new ElementMatrix(Ke1,Ke2);
569
570 /*clean-up and return*/
571 delete Ke1;
572 delete Ke2;
573 return Ke;
574}/*}}}*/
575ElementMatrix* EnthalpyAnalysis::CreateKMatrixVolume(Element* element){/*{{{*/
576
577 /* Check if ice in element */
578 if(!element->IsIceInElement()) return NULL;
579
580 /*Intermediaries */
581 int stabilization;
582 IssmDouble Jdet,dt,u,v,w,um,vm,wm,vel;
583 IssmDouble h,hx,hy,hz,vx,vy,vz;
584 IssmDouble tau_parameter,diameter;
585 IssmDouble tau_parameter_anisotropic[2],tau_parameter_hor,tau_parameter_ver;
586 IssmDouble D_scalar;
587 IssmDouble* xyz_list = NULL;
588
589 /*Fetch number of nodes and dof for this finite element*/
590 int numnodes = element->GetNumberOfNodes();
591
592 /*Initialize Element vector and other vectors*/
593 ElementMatrix* Ke = element->NewElementMatrix();
594 IssmDouble* basis = xNew<IssmDouble>(numnodes);
595 IssmDouble* dbasis = xNew<IssmDouble>(3*numnodes);
596 IssmDouble K[3][3];
597
598 /*Retrieve all inputs and parameters*/
599 element->GetVerticesCoordinates(&xyz_list);
600 element->FindParam(&dt,TimesteppingTimeStepEnum);
601 element->FindParam(&stabilization,ThermalStabilizationEnum);
602 IssmDouble rho_water = element->FindParam(MaterialsRhoSeawaterEnum);
603 IssmDouble rho_ice = element->FindParam(MaterialsRhoIceEnum);
604 IssmDouble gravity = element->FindParam(ConstantsGEnum);
605 IssmDouble heatcapacity = element->FindParam(MaterialsHeatcapacityEnum);
606 IssmDouble thermalconductivity = element->FindParam(MaterialsThermalconductivityEnum);
607 Input* vx_input = element->GetInput(VxEnum); _assert_(vx_input);
608 Input* vy_input = element->GetInput(VyEnum); _assert_(vy_input);
609 Input* vz_input = element->GetInput(VzEnum); _assert_(vz_input);
610 Input* vxm_input = element->GetInput(VxMeshEnum); _assert_(vxm_input);
611 Input* vym_input = element->GetInput(VyMeshEnum); _assert_(vym_input);
612 Input* vzm_input = element->GetInput(VzMeshEnum); _assert_(vzm_input);
613
614 /*Enthalpy diffusion parameter*/
615 IssmDouble kappa=this->EnthalpyDiffusionParameterVolume(element,EnthalpyPicardEnum); _assert_(kappa>=0.);
616
617 /* Start looping on the number of gaussian points: */
618 Gauss* gauss=element->NewGauss(4);
619 while(gauss->next()){
620
621 element->JacobianDeterminant(&Jdet,xyz_list,gauss);
622 element->NodalFunctions(basis,gauss);
623 element->NodalFunctionsDerivatives(dbasis,xyz_list,gauss);
624
625 D_scalar=gauss->weight*Jdet;
626 if(dt!=0.) D_scalar=D_scalar*dt;
627
628 /*Conduction: */
629 for(int i=0;i<numnodes;i++){
630 for(int j=0;j<numnodes;j++){
631 Ke->values[i*numnodes+j] += D_scalar*kappa/rho_ice*(
632 dbasis[0*numnodes+j]*dbasis[0*numnodes+i] + dbasis[1*numnodes+j]*dbasis[1*numnodes+i] + dbasis[2*numnodes+j]*dbasis[2*numnodes+i]
633 );
634 }
635 }
636
637 /*Advection: */
638 vx_input->GetInputValue(&u,gauss); vxm_input->GetInputValue(&um,gauss); vx=u-um;
639 vy_input->GetInputValue(&v,gauss); vym_input->GetInputValue(&vm,gauss); vy=v-vm;
640 vz_input->GetInputValue(&w,gauss); vzm_input->GetInputValue(&wm,gauss); vz=w-wm;
641 for(int i=0;i<numnodes;i++){
642 for(int j=0;j<numnodes;j++){
643 Ke->values[i*numnodes+j] += D_scalar*(
644 vx*dbasis[0*numnodes+j]*basis[i] + vy*dbasis[1*numnodes+j]*basis[i] +vz*dbasis[2*numnodes+j]*basis[i]
645 );
646 }
647 }
648
649 /*Transient: */
650 if(dt!=0.){
651 D_scalar=gauss->weight*Jdet;
652 for(int i=0;i<numnodes;i++){
653 for(int j=0;j<numnodes;j++){
654 Ke->values[i*numnodes+j] += D_scalar*basis[j]*basis[i];
655 }
656 }
657 D_scalar=D_scalar*dt;
658 }
659
660 /*Artificial diffusivity*/
661 if(stabilization==1){
662 element->ElementSizes(&hx,&hy,&hz);
663 vel=sqrt(vx*vx + vy*vy + vz*vz)+1.e-14;
664 h=sqrt( pow(hx*vx/vel,2) + pow(hy*vy/vel,2) + pow(hz*vz/vel,2));
665 K[0][0]=h/(2.*vel)*fabs(vx*vx); K[0][1]=h/(2.*vel)*fabs(vx*vy); K[0][2]=h/(2.*vel)*fabs(vx*vz);
666 K[1][0]=h/(2.*vel)*fabs(vy*vx); K[1][1]=h/(2.*vel)*fabs(vy*vy); K[1][2]=h/(2.*vel)*fabs(vy*vz);
667 K[2][0]=h/(2.*vel)*fabs(vz*vx); K[2][1]=h/(2.*vel)*fabs(vz*vy); K[2][2]=h/(2.*vel)*fabs(vz*vz);
668 for(int i=0;i<3;i++) for(int j=0;j<3;j++) K[i][j] = D_scalar*K[i][j];
669
670 for(int i=0;i<numnodes;i++){
671 for(int j=0;j<numnodes;j++){
672 Ke->values[i*numnodes+j] += (
673 dbasis[0*numnodes+i] *(K[0][0]*dbasis[0*numnodes+j] + K[0][1]*dbasis[1*numnodes+j]+ K[0][2]*dbasis[2*numnodes+j]) +
674 dbasis[1*numnodes+i] *(K[1][0]*dbasis[0*numnodes+j] + K[1][1]*dbasis[1*numnodes+j]+ K[1][2]*dbasis[2*numnodes+j]) +
675 dbasis[2*numnodes+i] *(K[2][0]*dbasis[0*numnodes+j] + K[2][1]*dbasis[1*numnodes+j]+ K[2][2]*dbasis[2*numnodes+j])
676 );
677 }
678 }
679 }
680 /*SUPG*/
681 else if(stabilization==2){
682 element->NodalFunctionsDerivatives(dbasis,xyz_list,gauss);
683 diameter=element->MinEdgeLength(xyz_list);
684 tau_parameter=element->StabilizationParameter(u-um,v-vm,w-wm,diameter,kappa/rho_ice);
685 for(int i=0;i<numnodes;i++){
686 for(int j=0;j<numnodes;j++){
687 Ke->values[i*numnodes+j]+=tau_parameter*D_scalar*
688 ((u-um)*dbasis[0*numnodes+i]+(v-vm)*dbasis[1*numnodes+i]+(w-wm)*dbasis[2*numnodes+i])*
689 ((u-um)*dbasis[0*numnodes+j]+(v-vm)*dbasis[1*numnodes+j]+(w-wm)*dbasis[2*numnodes+j]);
690 }
691 }
692 if(dt!=0.){
693 D_scalar=gauss->weight*Jdet;
694 for(int i=0;i<numnodes;i++){
695 for(int j=0;j<numnodes;j++){
696 Ke->values[i*numnodes+j]+=tau_parameter*D_scalar*basis[j]*((u-um)*dbasis[0*numnodes+i]+(v-vm)*dbasis[1*numnodes+i]+(w-wm)*dbasis[2*numnodes+i]);
697 }
698 }
699 }
700 }
701 /*anisotropic SUPG*/
702 else if(stabilization==3){
703 element->NodalFunctionsDerivatives(dbasis,xyz_list,gauss);
704 element->ElementSizes(&hx,&hy,&hz);
705 element->StabilizationParameterAnisotropic(&tau_parameter_anisotropic[0],u-um,v-vm,w-wm,hx,hy,hz,kappa/rho_ice);
706 tau_parameter_hor=tau_parameter_anisotropic[0];
707 tau_parameter_ver=tau_parameter_anisotropic[1];
708 for(int i=0;i<numnodes;i++){
709 for(int j=0;j<numnodes;j++){
710 Ke->values[i*numnodes+j]+=D_scalar*
711 (sqrt(tau_parameter_hor)*(u-um)*dbasis[0*numnodes+i]+sqrt(tau_parameter_hor)*(v-vm)*dbasis[1*numnodes+i]+sqrt(tau_parameter_ver)*(w-wm)*dbasis[2*numnodes+i])*
712 (sqrt(tau_parameter_hor)*(u-um)*dbasis[0*numnodes+j]+sqrt(tau_parameter_hor)*(v-vm)*dbasis[1*numnodes+j]+sqrt(tau_parameter_ver)*(w-wm)*dbasis[2*numnodes+j]);
713 }
714 }
715 }
716 }
717
718 /*Clean up and return*/
719 xDelete<IssmDouble>(xyz_list);
720 xDelete<IssmDouble>(basis);
721 xDelete<IssmDouble>(dbasis);
722 delete gauss;
723 return Ke;
724}/*}}}*/
725ElementMatrix* EnthalpyAnalysis::CreateKMatrixShelf(Element* element){/*{{{*/
726
727 /* Check if ice in element */
728 if(!element->IsIceInElement()) return NULL;
729
730 /*Initialize Element matrix and return if necessary*/
731 if(!element->IsOnBase() || !element->IsFloating()) return NULL;
732
733 /*Intermediaries*/
734 IssmDouble dt,Jdet,D;
735 IssmDouble *xyz_list_base = NULL;
736
737 /*Fetch number of nodes for this finite element*/
738 int numnodes = element->GetNumberOfNodes();
739
740 /*Initialize vectors*/
741 ElementMatrix* Ke = element->NewElementMatrix();
742 IssmDouble* basis = xNew<IssmDouble>(numnodes);
743
744 /*Retrieve all inputs and parameters*/
745 element->GetVerticesCoordinatesBase(&xyz_list_base);
746 element->FindParam(&dt,TimesteppingTimeStepEnum);
747 IssmDouble gravity = element->FindParam(ConstantsGEnum);
748 IssmDouble rho_water = element->FindParam(MaterialsRhoSeawaterEnum);
749 IssmDouble rho_ice = element->FindParam(MaterialsRhoIceEnum);
750 IssmDouble heatcapacity = element->FindParam(MaterialsHeatcapacityEnum);
751 IssmDouble mixed_layer_capacity= element->FindParam(MaterialsMixedLayerCapacityEnum);
752 IssmDouble thermal_exchange_vel= element->FindParam(MaterialsThermalExchangeVelocityEnum);
753
754 /* Start looping on the number of gaussian points: */
755 Gauss* gauss=element->NewGaussBase(4);
756 while(gauss->next()){
757
758 element->JacobianDeterminantBase(&Jdet,xyz_list_base,gauss);
759 element->NodalFunctions(basis,gauss);
760
761 D=gauss->weight*Jdet*rho_water*mixed_layer_capacity*thermal_exchange_vel/(heatcapacity*rho_ice);
762 if(reCast<bool,IssmDouble>(dt)) D=dt*D;
763 for(int i=0;i<numnodes;i++) for(int j=0;j<numnodes;j++) Ke->values[i*numnodes+j] += D*basis[i]*basis[j];
764 }
765
766 /*Clean up and return*/
767 delete gauss;
768 xDelete<IssmDouble>(basis);
769 xDelete<IssmDouble>(xyz_list_base);
770 return Ke;
771}/*}}}*/
772ElementVector* EnthalpyAnalysis::CreatePVector(Element* element){/*{{{*/
773
774 /* Check if ice in element */
775 if(!element->IsIceInElement()) return NULL;
776
777 /*compute all load vectors for this element*/
778 ElementVector* pe1=CreatePVectorVolume(element);
779 ElementVector* pe2=CreatePVectorSheet(element);
780 ElementVector* pe3=CreatePVectorShelf(element);
781 ElementVector* pe =new ElementVector(pe1,pe2,pe3);
782
783 /*clean-up and return*/
784 delete pe1;
785 delete pe2;
786 delete pe3;
787 return pe;
788}/*}}}*/
789ElementVector* EnthalpyAnalysis::CreatePVectorVolume(Element* element){/*{{{*/
790
791 /* Check if ice in element */
792 if(!element->IsIceInElement()) return NULL;
793
794 /*Intermediaries*/
795 int i, stabilization;
796 IssmDouble Jdet,phi,dt;
797 IssmDouble enthalpy, Hpmp;
798 IssmDouble enthalpypicard, d1enthalpypicard[3];
799 IssmDouble pressure, d1pressure[3], d2pressure;
800 IssmDouble waterfractionpicard;
801 IssmDouble kappa,tau_parameter,diameter,hx,hy,hz,kappa_w;
802 IssmDouble tau_parameter_anisotropic[2],tau_parameter_hor,tau_parameter_ver;
803 IssmDouble u,v,w;
804 IssmDouble scalar_def, scalar_sens ,scalar_transient;
805 IssmDouble* xyz_list = NULL;
806 IssmDouble d1H_d1P, d1P2;
807
808 /*Fetch number of nodes and dof for this finite element*/
809 int numnodes = element->GetNumberOfNodes();
810 int numvertices = element->GetNumberOfVertices();
811
812 /*Initialize Element vector*/
813 ElementVector* pe = element->NewElementVector();
814 IssmDouble* basis = xNew<IssmDouble>(numnodes);
815 IssmDouble* dbasis = xNew<IssmDouble>(3*numnodes);
816
817 /*Retrieve all inputs and parameters*/
818 element->GetVerticesCoordinates(&xyz_list);
819 IssmDouble rho_ice = element->FindParam(MaterialsRhoIceEnum);
820 IssmDouble heatcapacity = element->FindParam(MaterialsHeatcapacityEnum);
821 IssmDouble thermalconductivity = element->FindParam(MaterialsThermalconductivityEnum);
822 IssmDouble temperateiceconductivity = element->FindParam(MaterialsTemperateiceconductivityEnum);
823 IssmDouble beta = element->FindParam(MaterialsBetaEnum);
824 IssmDouble latentheat = element->FindParam(MaterialsLatentheatEnum);
825 element->FindParam(&dt,TimesteppingTimeStepEnum);
826 element->FindParam(&stabilization,ThermalStabilizationEnum);
827 Input* vx_input=element->GetInput(VxEnum); _assert_(vx_input);
828 Input* vy_input=element->GetInput(VyEnum); _assert_(vy_input);
829 Input* vz_input=element->GetInput(VzEnum); _assert_(vz_input);
830 Input* enthalpypicard_input=element->GetInput(EnthalpyPicardEnum); _assert_(enthalpypicard_input);
831 Input* pressure_input=element->GetInput(PressureEnum); _assert_(pressure_input);
832 Input* enthalpy_input=NULL;
833 if(dt>0.){
834 enthalpy_input = element->GetInput(EnthalpyEnum); _assert_(enthalpy_input);
835 }
836
837 /* Start looping on the number of gaussian points: */
838 Gauss* gauss=element->NewGauss(4);
839 while(gauss->next()){
840
841 element->JacobianDeterminant(&Jdet,xyz_list,gauss);
842 element->NodalFunctions(basis,gauss);
843
844 /*viscous dissipation*/
845 element->ViscousHeating(&phi,xyz_list,gauss,vx_input,vy_input,vz_input);
846
847 scalar_def=phi/rho_ice*Jdet*gauss->weight;
848 if(dt!=0.) scalar_def=scalar_def*dt;
849
850 for(i=0;i<numnodes;i++) pe->values[i]+=scalar_def*basis[i];
851
852 /*sensible heat flux in temperate ice*/
853 enthalpypicard_input->GetInputValue(&enthalpypicard,gauss);
854 pressure_input->GetInputValue(&pressure,gauss);
855 Hpmp=this->PureIceEnthalpy(element, pressure);
856
857 if(enthalpypicard>=Hpmp){
858 enthalpypicard_input->GetInputDerivativeValue(&d1enthalpypicard[0],xyz_list,gauss);
859 pressure_input->GetInputDerivativeValue(&d1pressure[0],xyz_list,gauss);
860 d2pressure=0.; // for linear elements, 2nd derivative is zero
861
862 d1H_d1P=0.;
863 for(i=0;i<3;i++) d1H_d1P+=d1enthalpypicard[i]*d1pressure[i];
864 d1P2=0.;
865 for(i=0;i<3;i++) d1P2+=pow(d1pressure[i],2.);
866
867 scalar_sens=-beta*((temperateiceconductivity - thermalconductivity)/latentheat*(d1H_d1P + beta*heatcapacity*d1P2))/rho_ice;
868 if(dt!=0.) scalar_sens=scalar_sens*dt;
869 for(i=0;i<numnodes;i++) pe->values[i]+=scalar_sens*basis[i];
870 }
871
872 /* Build transient now */
873 if(dt>0.){
874 enthalpy_input->GetInputValue(&enthalpy, gauss);
875 scalar_transient=enthalpy*Jdet*gauss->weight;
876 for(i=0;i<numnodes;i++) pe->values[i]+=scalar_transient*basis[i];
877 }
878
879 /* SUPG */
880 if(stabilization==2){
881 element->NodalFunctionsDerivatives(dbasis,xyz_list,gauss);
882 diameter=element->MinEdgeLength(xyz_list);
883 kappa=this->EnthalpyDiffusionParameterVolume(element,EnthalpyPicardEnum); _assert_(kappa>=0.);
884 vx_input->GetInputValue(&u,gauss);
885 vy_input->GetInputValue(&v,gauss);
886 vz_input->GetInputValue(&w,gauss);
887 tau_parameter=element->StabilizationParameter(u,v,w,diameter,kappa/rho_ice);
888
889 for(i=0;i<numnodes;i++) pe->values[i]+=tau_parameter*scalar_def*(u*dbasis[0*numnodes+i]+v*dbasis[1*numnodes+i]+w*dbasis[2*numnodes+i]);
890
891 if(dt!=0.){
892 for(i=0;i<numnodes;i++) pe->values[i]+=tau_parameter*scalar_transient*(u*dbasis[0*numnodes+i]+v*dbasis[1*numnodes+i]+w*dbasis[2*numnodes+i]);
893 }
894 }
895 /* anisotropic SUPG */
896 else if(stabilization==3){
897 element->NodalFunctionsDerivatives(dbasis,xyz_list,gauss);
898 element->ElementSizes(&hx,&hy,&hz);
899 kappa=this->EnthalpyDiffusionParameterVolume(element,EnthalpyPicardEnum); _assert_(kappa>=0.);
900 vx_input->GetInputValue(&u,gauss);
901 vy_input->GetInputValue(&v,gauss);
902 vz_input->GetInputValue(&w,gauss);
903 element->StabilizationParameterAnisotropic(&tau_parameter_anisotropic[0],u,v,w,hx,hy,hz,kappa/rho_ice);
904 tau_parameter_hor=tau_parameter_anisotropic[0];
905 tau_parameter_ver=tau_parameter_anisotropic[1];
906
907 for(i=0;i<numnodes;i++) pe->values[i]+=scalar_def*(tau_parameter_hor*u*dbasis[0*numnodes+i]+tau_parameter_hor*v*dbasis[1*numnodes+i]+tau_parameter_ver*w*dbasis[2*numnodes+i]);
908 }
909 }
910
911 /*Clean up and return*/
912 xDelete<IssmDouble>(basis);
913 xDelete<IssmDouble>(dbasis);
914 xDelete<IssmDouble>(xyz_list);
915 delete gauss;
916 return pe;
917
918}/*}}}*/
919ElementVector* EnthalpyAnalysis::CreatePVectorSheet(Element* element){/*{{{*/
920
921 /* Check if ice in element */
922 if(!element->IsIceInElement()) return NULL;
923
924 /* implementation of the basal condition decision chart of Aschwanden 2012, Fig.5 */
925 if(!element->IsOnBase() || element->IsFloating()) return NULL;
926
927 bool converged, isdynamicbasalspc;
928 int i, state;
929 int enthalpy_enum;
930 IssmDouble dt,Jdet,scalar;
931 IssmDouble enthalpy, enthalpyup, pressure, pressureup, watercolumn, meltingrate;
932 IssmDouble vx,vy,vz;
933 IssmDouble alpha2,basalfriction,geothermalflux,heatflux;
934 IssmDouble *xyz_list_base = NULL;
935
936 /*Fetch number of nodes for this finite element*/
937 int numnodes = element->GetNumberOfNodes();
938
939 /*Initialize vectors*/
940 ElementVector* pe = element->NewElementVector();
941 IssmDouble* basis = xNew<IssmDouble>(numnodes);
942
943 /*Retrieve all inputs and parameters*/
944 element->GetVerticesCoordinatesBase(&xyz_list_base);
945 element->FindParam(&dt,TimesteppingTimeStepEnum);
946 element->FindParam(&isdynamicbasalspc,ThermalIsdynamicbasalspcEnum);
947 element->GetInputValue(&converged,ConvergedEnum);
948 if(dt==0. && !converged) enthalpy_enum=EnthalpyPicardEnum; // use enthalpy from last iteration
949 else enthalpy_enum=EnthalpyEnum; // use enthalpy from last time step
950 Input* vx_input = element->GetInput(VxEnum); _assert_(vx_input);
951 Input* vy_input = element->GetInput(VyEnum); _assert_(vy_input);
952 Input* vz_input = element->GetInput(VzEnum); _assert_(vz_input);
953 Input* enthalpy_input = element->GetInput(enthalpy_enum); _assert_(enthalpy_input);
954 Input* pressure_input = element->GetInput(PressureEnum); _assert_(pressure_input);
955 Input* watercolumn_input = element->GetInput(WatercolumnEnum); _assert_(watercolumn_input);
956 Input* meltingrate_input = element->GetInput(BasalforcingsGroundediceMeltingRateEnum); _assert_(meltingrate_input);
957 Input* geothermalflux_input = element->GetInput(BasalforcingsGeothermalfluxEnum); _assert_(geothermalflux_input);
958 IssmDouble rho_ice = element->FindParam(MaterialsRhoIceEnum);
959
960 /*Build friction element, needed later: */
961 Friction* friction=new Friction(element,3);
962
963 /* Start looping on the number of gaussian points: */
964 Gauss* gauss=element->NewGaussBase(4);
965 Gauss* gaussup=element->NewGaussTop(4);
966 while(gauss->next() && gaussup->next()){
967
968 element->JacobianDeterminantBase(&Jdet,xyz_list_base,gauss);
969 element->NodalFunctions(basis,gauss);
970
971 if(isdynamicbasalspc){
972 enthalpy_input->GetInputValue(&enthalpy,gauss);
973 enthalpy_input->GetInputValue(&enthalpyup,gaussup);
974 pressure_input->GetInputValue(&pressure,gauss);
975 pressure_input->GetInputValue(&pressureup,gaussup);
976 watercolumn_input->GetInputValue(&watercolumn,gauss);
977 meltingrate_input->GetInputValue(&meltingrate,gauss);
978 state=GetThermalBasalCondition(element, enthalpy, enthalpyup, pressure, pressureup, watercolumn, meltingrate);
979 }
980 else
981 state=0;
982
983 switch (state) {
984 case 0: case 1: case 2: case 3:
985 // cold, dry base; cold, wet base; refreezing temperate base; thin temperate base:
986 // Apply basal surface forcing.
987 // Interpolated values of enthalpy on gauss nodes may indicate cold base,
988 // although one node might have become temperate. So keep heat flux switched on.
989 geothermalflux_input->GetInputValue(&geothermalflux,gauss);
990 friction->GetAlpha2(&alpha2,gauss);
991 vx_input->GetInputValue(&vx,gauss);
992 vy_input->GetInputValue(&vy,gauss);
993 vz_input->GetInputValue(&vz,gauss);
994 basalfriction=alpha2*(vx*vx+vy*vy+vz*vz);
995 heatflux=(basalfriction+geothermalflux)/(rho_ice);
996 scalar=gauss->weight*Jdet*heatflux;
997 if(dt!=0.) scalar=dt*scalar;
998 for(i=0;i<numnodes;i++)
999 pe->values[i]+=scalar*basis[i];
1000 break;
1001 case 4:
1002 // temperate, thick melting base: set grad H*n=0
1003 for(i=0;i<numnodes;i++)
1004 pe->values[i]+=0.;
1005 break;
1006 default:
1007 _printf0_(" unknown thermal basal state found!");
1008 }
1009 }
1010
1011 /*Clean up and return*/
1012 delete gauss;
1013 delete gaussup;
1014 delete friction;
1015 xDelete<IssmDouble>(basis);
1016 xDelete<IssmDouble>(xyz_list_base);
1017 return pe;
1018
1019}/*}}}*/
1020ElementVector* EnthalpyAnalysis::CreatePVectorShelf(Element* element){/*{{{*/
1021
1022 /* Check if ice in element */
1023 if(!element->IsIceInElement()) return NULL;
1024
1025 /*Get basal element*/
1026 if(!element->IsOnBase() || !element->IsFloating()) return NULL;
1027
1028 IssmDouble Hpmp,dt,Jdet,scalar_ocean,pressure;
1029 IssmDouble *xyz_list_base = NULL;
1030
1031 /*Fetch number of nodes for this finite element*/
1032 int numnodes = element->GetNumberOfNodes();
1033
1034 /*Initialize vectors*/
1035 ElementVector* pe = element->NewElementVector();
1036 IssmDouble* basis = xNew<IssmDouble>(numnodes);
1037
1038 /*Retrieve all inputs and parameters*/
1039 element->GetVerticesCoordinatesBase(&xyz_list_base);
1040 element->FindParam(&dt,TimesteppingTimeStepEnum);
1041 Input* pressure_input=element->GetInput(PressureEnum); _assert_(pressure_input);
1042 IssmDouble gravity = element->FindParam(ConstantsGEnum);
1043 IssmDouble rho_water = element->FindParam(MaterialsRhoSeawaterEnum);
1044 IssmDouble rho_ice = element->FindParam(MaterialsRhoIceEnum);
1045 IssmDouble heatcapacity = element->FindParam(MaterialsHeatcapacityEnum);
1046 IssmDouble mixed_layer_capacity= element->FindParam(MaterialsMixedLayerCapacityEnum);
1047 IssmDouble thermal_exchange_vel= element->FindParam(MaterialsThermalExchangeVelocityEnum);
1048
1049 /* Start looping on the number of gaussian points: */
1050 Gauss* gauss=element->NewGaussBase(4);
1051 while(gauss->next()){
1052
1053 element->JacobianDeterminantBase(&Jdet,xyz_list_base,gauss);
1054 element->NodalFunctions(basis,gauss);
1055
1056 pressure_input->GetInputValue(&pressure,gauss);
1057 Hpmp=element->PureIceEnthalpy(pressure);
1058
1059 scalar_ocean=gauss->weight*Jdet*rho_water*mixed_layer_capacity*thermal_exchange_vel*Hpmp/(heatcapacity*rho_ice);
1060 if(reCast<bool,IssmDouble>(dt)) scalar_ocean=dt*scalar_ocean;
1061
1062 for(int i=0;i<numnodes;i++) pe->values[i]+=scalar_ocean*basis[i];
1063 }
1064
1065 /*Clean up and return*/
1066 delete gauss;
1067 xDelete<IssmDouble>(basis);
1068 xDelete<IssmDouble>(xyz_list_base);
1069 return pe;
1070}/*}}}*/
1071void EnthalpyAnalysis::DrainWaterfraction(FemModel* femmodel){/*{{{*/
1072 /*Drain excess water fraction in ice column: */
1073 ComputeWaterfractionDrainage(femmodel);
1074 DrainageUpdateWatercolumn(femmodel);
1075 DrainageUpdateEnthalpy(femmodel);
1076}/*}}}*/
1077void EnthalpyAnalysis::ComputeWaterfractionDrainage(FemModel* femmodel){/*{{{*/
1078
1079 int k,numnodes;
1080 IssmDouble dt;
1081 Element* element= NULL;
1082
1083 femmodel->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
1084
1085 for(Object* & object : femmodel->elements->objects){
1086 element=xDynamicCast<Element*>(object);
1087 numnodes=element->GetNumberOfNodes();
1088 IssmDouble* waterfractions= xNew<IssmDouble>(numnodes);
1089 IssmDouble* drainage= xNew<IssmDouble>(numnodes);
1090
1091 element->GetInputListOnNodes(waterfractions,WaterfractionEnum);
1092 for(k=0; k<numnodes;k++){
1093 drainage[k]=DrainageFunctionWaterfraction(waterfractions[k], dt);
1094 }
1095 int finite_element = element->GetElementType(); if(finite_element==P1Enum) finite_element = P1DGEnum;
1096 element->AddInput(WaterfractionDrainageEnum,drainage,finite_element);
1097
1098 xDelete<IssmDouble>(waterfractions);
1099 xDelete<IssmDouble>(drainage);
1100 }
1101}/*}}}*/
1102void EnthalpyAnalysis::DrainageUpdateWatercolumn(FemModel* femmodel){/*{{{*/
1103
1104 int k,numnodes, numbasalnodes;
1105 IssmDouble dt;
1106 int* basalnodeindices=NULL;
1107 Element* element= NULL;
1108
1109 femmodel->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
1110
1111 /*depth-integrate the drained water fraction */
1112 femmodel->parameters->SetParam(WaterfractionDrainageEnum,InputToDepthaverageInEnum);
1113 femmodel->parameters->SetParam(WaterfractionDrainageIntegratedEnum,InputToDepthaverageOutEnum);
1114 depthaverage_core(femmodel);
1115 femmodel->parameters->SetParam(WaterfractionDrainageIntegratedEnum,InputToExtrudeEnum);
1116 extrudefrombase_core(femmodel);
1117 /*multiply depth-average by ice thickness*/
1118 for(Object* & object : femmodel->elements->objects){
1119 element=xDynamicCast<Element*>(object);
1120 numnodes=element->GetNumberOfNodes();
1121 IssmDouble* drainage_int= xNew<IssmDouble>(numnodes);
1122 IssmDouble* thicknesses= xNew<IssmDouble>(numnodes);
1123
1124 element->GetInputListOnNodes(drainage_int,WaterfractionDrainageIntegratedEnum);
1125 element->GetInputListOnNodes(thicknesses,ThicknessEnum);
1126 for(k=0;k<numnodes;k++){
1127 drainage_int[k]*=thicknesses[k];
1128 }
1129 int finite_element = element->GetElementType(); if(finite_element==P1Enum) finite_element = P1DGEnum;
1130 element->AddInput(WaterfractionDrainageIntegratedEnum, drainage_int,finite_element);
1131
1132 xDelete<IssmDouble>(drainage_int);
1133 xDelete<IssmDouble>(thicknesses);
1134 }
1135
1136 /*update water column*/
1137 for(Object* & object : femmodel->elements->objects){
1138 element=xDynamicCast<Element*>(object);
1139 /* Check if ice in element */
1140 if(!element->IsIceInElement()) continue;
1141 if(!element->IsOnBase()) continue;
1142
1143 numnodes=element->GetNumberOfNodes();
1144 IssmDouble* watercolumn= xNew<IssmDouble>(numnodes);
1145 IssmDouble* drainage_int= xNew<IssmDouble>(numnodes);
1146 element->GetInputListOnNodes(watercolumn,WatercolumnEnum);
1147 element->GetInputListOnNodes(drainage_int,WaterfractionDrainageIntegratedEnum);
1148
1149 element->BasalNodeIndices(&numbasalnodes,&basalnodeindices,element->GetElementType());
1150 for(k=0;k<numbasalnodes;k++){
1151 watercolumn[basalnodeindices[k]]+=dt*drainage_int[basalnodeindices[k]];
1152 }
1153 int finite_element = element->GetElementType(); if(finite_element==P1Enum) finite_element = P1DGEnum;
1154 element->AddInput(WatercolumnEnum, watercolumn,finite_element);
1155
1156 xDelete<IssmDouble>(watercolumn);
1157 xDelete<IssmDouble>(drainage_int);
1158 xDelete<int>(basalnodeindices);
1159 }
1160}/*}}}*/
1161void EnthalpyAnalysis::DrainageUpdateEnthalpy(FemModel* femmodel){/*{{{*/
1162
1163 int k,numnodes;
1164 IssmDouble dt;
1165 Element* element= NULL;
1166 femmodel->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
1167
1168 for(Object* & object : femmodel->elements->objects){
1169 element=xDynamicCast<Element*>(object);
1170 numnodes=element->GetNumberOfNodes();
1171 IssmDouble* enthalpies= xNew<IssmDouble>(numnodes);
1172 IssmDouble* pressures= xNew<IssmDouble>(numnodes);
1173 IssmDouble* temperatures= xNew<IssmDouble>(numnodes);
1174 IssmDouble* waterfractions= xNew<IssmDouble>(numnodes);
1175 IssmDouble* drainage= xNew<IssmDouble>(numnodes);
1176
1177 element->GetInputListOnNodes(pressures,PressureEnum);
1178 element->GetInputListOnNodes(temperatures,TemperatureEnum);
1179 element->GetInputListOnNodes(waterfractions,WaterfractionEnum);
1180 element->GetInputListOnNodes(drainage,WaterfractionDrainageEnum);
1181
1182 for(k=0;k<numnodes;k++){
1183 if(dt==0.)
1184 waterfractions[k]-=drainage[k];
1185 else
1186 waterfractions[k]-=dt*drainage[k];
1187
1188 element->ThermalToEnthalpy(&enthalpies[k], temperatures[k], waterfractions[k], pressures[k]);
1189 }
1190 int finite_element = element->GetElementType(); if(finite_element==P1Enum) finite_element = P1DGEnum;
1191 element->AddInput(WaterfractionEnum,waterfractions,finite_element);
1192 element->AddInput(EnthalpyEnum,enthalpies,finite_element);
1193
1194 xDelete<IssmDouble>(enthalpies);
1195 xDelete<IssmDouble>(pressures);
1196 xDelete<IssmDouble>(temperatures);
1197 xDelete<IssmDouble>(waterfractions);
1198 xDelete<IssmDouble>(drainage);
1199 }
1200}/*}}}*/
1201IssmDouble EnthalpyAnalysis::EnthalpyDiffusionParameter(Element* element,IssmDouble enthalpy,IssmDouble pressure){/*{{{*/
1202
1203 IssmDouble heatcapacity = element->FindParam(MaterialsHeatcapacityEnum);
1204 IssmDouble temperateiceconductivity = element->FindParam(MaterialsTemperateiceconductivityEnum);
1205 IssmDouble thermalconductivity = element->FindParam(MaterialsThermalconductivityEnum);
1206
1207 if(enthalpy < PureIceEnthalpy(element,pressure))
1208 return thermalconductivity/heatcapacity;
1209 else
1210 return temperateiceconductivity/heatcapacity;
1211}/*}}}*/
1212IssmDouble EnthalpyAnalysis::EnthalpyDiffusionParameterVolume(Element* element,int enthalpy_enum){/*{{{*/
1213
1214 int iv;
1215 IssmDouble lambda; /* fraction of cold ice */
1216 IssmDouble kappa,kappa_c,kappa_t; /* enthalpy conductivities */
1217 IssmDouble Hc,Ht;
1218
1219 /*Get pressures and enthalpies on vertices*/
1220 int numvertices = element->GetNumberOfVertices();
1221 int effectiveconductivity_averaging;
1222 IssmDouble* pressures = xNew<IssmDouble>(numvertices);
1223 IssmDouble* enthalpies = xNew<IssmDouble>(numvertices);
1224 IssmDouble* PIE = xNew<IssmDouble>(numvertices);
1225 IssmDouble* dHpmp = xNew<IssmDouble>(numvertices);
1226 element->GetInputListOnVertices(pressures,PressureEnum);
1227 element->GetInputListOnVertices(enthalpies,enthalpy_enum);
1228 element->FindParam(&effectiveconductivity_averaging,MaterialsEffectiveconductivityAveragingEnum);
1229
1230 for(iv=0;iv<numvertices;iv++){
1231 PIE[iv] = PureIceEnthalpy(element,pressures[iv]);
1232 dHpmp[iv] = enthalpies[iv]-PIE[iv];
1233 }
1234
1235 bool allequalsign = true;
1236 if(dHpmp[0]<0.){
1237 for(iv=1; iv<numvertices;iv++) allequalsign=(allequalsign && (dHpmp[iv]<0.));
1238 }
1239 else{
1240 for(iv=1; iv<numvertices;iv++) allequalsign=(allequalsign && (dHpmp[iv]>=0.));
1241 }
1242
1243 if(allequalsign){
1244 kappa = EnthalpyDiffusionParameter(element,enthalpies[0],pressures[0]);
1245 }
1246 else{
1247 kappa_c = EnthalpyDiffusionParameter(element,PureIceEnthalpy(element,0.)-1.,0.);
1248 kappa_t = EnthalpyDiffusionParameter(element,PureIceEnthalpy(element,0.)+1.,0.);
1249
1250 Hc=0.; Ht=0.;
1251 for(iv=0; iv<numvertices;iv++){
1252 if(enthalpies[iv]<PIE[iv])
1253 Hc+=(PIE[iv]-enthalpies[iv]);
1254 else
1255 Ht+=(enthalpies[iv]-PIE[iv]);
1256 }
1257 _assert_((Hc+Ht)>0.);
1258 lambda = Hc/(Hc+Ht);
1259 _assert_(lambda>=0.);
1260 _assert_(lambda<=1.);
1261
1262 if(effectiveconductivity_averaging==0){
1263 /* return arithmetic mean (volume average) of thermal conductivities, weighted by fraction of cold/temperate ice */
1264 kappa=kappa_c*lambda+(1.-lambda)*kappa_t;
1265 }
1266 else if(effectiveconductivity_averaging==1){
1267 /* return harmonic mean (reciprocal avarage) of thermal conductivities, weighted by fraction of cold/temperate ice, cf Patankar 1980, pp44 */
1268 kappa=kappa_c*kappa_t/(lambda*kappa_t+(1.-lambda)*kappa_c);
1269 }
1270 else if(effectiveconductivity_averaging==2){
1271 /* return geometric mean (power law) of thermal conductivities, weighted by fraction of cold/temperate ice */
1272 kappa=pow(kappa_c,lambda)*pow(kappa_t,1.-lambda);
1273 }
1274 else{
1275 _error_("effectiveconductivity_averaging not supported yet");
1276 }
1277 }
1278
1279 /*Clean up and return*/
1280 xDelete<IssmDouble>(PIE);
1281 xDelete<IssmDouble>(dHpmp);
1282 xDelete<IssmDouble>(pressures);
1283 xDelete<IssmDouble>(enthalpies);
1284 return kappa;
1285}/*}}}*/
1286void EnthalpyAnalysis::GetBasalConstraints(Vector<IssmDouble>* vec_spc,Element* element){/*{{{*/
1287
1288 /*Intermediary*/
1289 bool isdynamicbasalspc;
1290 IssmDouble dt;
1291
1292 /*Check wether dynamic basal boundary conditions are activated */
1293 element->FindParam(&isdynamicbasalspc,ThermalIsdynamicbasalspcEnum);
1294 if(!isdynamicbasalspc) return;
1295
1296 element->FindParam(&dt,TimesteppingTimeStepEnum);
1297 if(dt==0.){
1298 GetBasalConstraintsSteadystate(vec_spc,element);
1299 }
1300 else{
1301 GetBasalConstraintsTransient(vec_spc,element);
1302 }
1303}/*}}}*/
1304void EnthalpyAnalysis::GetBasalConstraintsSteadystate(Vector<IssmDouble>* vec_spc,Element* element){/*{{{*/
1305
1306 /* Check if ice in element */
1307 if(!element->IsIceInElement()) return;
1308
1309 /* Only update constraints at the base.
1310 * Floating ice is not affected by basal BC decision chart. */
1311 if(!(element->IsOnBase()) || element->IsFloating()) return;
1312
1313 /*Intermediary*/
1314 int numindices, numindicesup, state;
1315 int *indices = NULL, *indicesup = NULL;
1316 IssmDouble enthalpy, enthalpyup, pressure, pressureup, watercolumn, meltingrate;
1317
1318 /*Get parameters and inputs: */
1319 Input* enthalpy_input = element->GetInput(EnthalpyPicardEnum); _assert_(enthalpy_input);
1320 Input* pressure_input = element->GetInput(PressureEnum); _assert_(pressure_input);
1321 Input* watercolumn_input = element->GetInput(WatercolumnEnum); _assert_(watercolumn_input);
1322 Input* meltingrate_input = element->GetInput(BasalforcingsGroundediceMeltingRateEnum); _assert_(meltingrate_input);
1323
1324 /*Fetch indices of basal & surface nodes for this finite element*/
1325 Penta *penta = (Penta *) element; // TODO: add Basal-/SurfaceNodeIndices to element.h, and change this to Element*
1326 penta->BasalNodeIndices(&numindices,&indices,element->GetElementType());
1327 penta->SurfaceNodeIndices(&numindicesup,&indicesup,element->GetElementType()); _assert_(numindices==numindicesup);
1328
1329 GaussPenta* gauss=new GaussPenta();
1330 GaussPenta* gaussup=new GaussPenta();
1331 for(int i=0;i<numindices;i++){
1332 gauss->GaussNode(element->GetElementType(),indices[i]);
1333 gaussup->GaussNode(element->GetElementType(),indicesup[i]);
1334
1335 enthalpy_input->GetInputValue(&enthalpy,gauss);
1336 enthalpy_input->GetInputValue(&enthalpyup,gaussup);
1337 pressure_input->GetInputValue(&pressure,gauss);
1338 pressure_input->GetInputValue(&pressureup,gaussup);
1339 watercolumn_input->GetInputValue(&watercolumn,gauss);
1340 meltingrate_input->GetInputValue(&meltingrate,gauss);
1341
1342 state=GetThermalBasalCondition(element, enthalpy, enthalpyup, pressure, pressureup, watercolumn, meltingrate);
1343 switch (state) {
1344 case 0:
1345 // cold, dry base: apply basal surface forcing
1346 vec_spc->SetValue(element->nodes[i]->Sid(),0.,INS_VAL);
1347 break;
1348 case 1:
1349 // cold, wet base: keep at pressure melting point
1350 vec_spc->SetValue(element->nodes[i]->Sid(),1.,INS_VAL);
1351 break;
1352 case 2:
1353 // temperate, thin refreezing base:
1354 vec_spc->SetValue(element->nodes[i]->Sid(),1.,INS_VAL);
1355 break;
1356 case 3:
1357 // temperate, thin melting base: set spc
1358 vec_spc->SetValue(element->nodes[i]->Sid(),1.,INS_VAL);
1359 break;
1360 case 4:
1361 // temperate, thick melting base:
1362 vec_spc->SetValue(element->nodes[i]->Sid(),1.,INS_VAL);
1363 break;
1364 default:
1365 _printf0_(" unknown thermal basal state found!");
1366 }
1367 }
1368
1369 /*Free ressources:*/
1370 xDelete<int>(indices);
1371 xDelete<int>(indicesup);
1372 delete gauss;
1373 delete gaussup;
1374}/*}}}*/
1375void EnthalpyAnalysis::GetBasalConstraintsTransient(Vector<IssmDouble>* vec_spc,Element* element){/*{{{*/
1376
1377 /* Check if ice in element */
1378 if(!element->IsIceInElement()) return;
1379
1380 /* Only update constraints at the base.
1381 * Floating ice is not affected by basal BC decision chart.*/
1382 if(!(element->IsOnBase()) || element->IsFloating()) return;
1383
1384 /*Intermediary*/
1385 int numindices, numindicesup, state;
1386 int *indices = NULL, *indicesup = NULL;
1387 IssmDouble enthalpy, enthalpyup, pressure, pressureup, watercolumn, meltingrate;
1388
1389 /*Get parameters and inputs: */
1390 Input* enthalpy_input = element->GetInput(EnthalpyEnum); _assert_(enthalpy_input); //TODO: check EnthalpyPicard?
1391 Input* pressure_input = element->GetInput(PressureEnum); _assert_(pressure_input);
1392 Input* watercolumn_input = element->GetInput(WatercolumnEnum); _assert_(watercolumn_input);
1393 Input* meltingrate_input = element->GetInput(BasalforcingsGroundediceMeltingRateEnum); _assert_(meltingrate_input);
1394
1395 /*Fetch indices of basal & surface nodes for this finite element*/
1396 Penta *penta = (Penta *) element; // TODO: add Basal-/SurfaceNodeIndices to element.h, and change this to Element*
1397 penta->BasalNodeIndices(&numindices,&indices,element->GetElementType());
1398 penta->SurfaceNodeIndices(&numindicesup,&indicesup,element->GetElementType()); _assert_(numindices==numindicesup);
1399
1400 GaussPenta* gauss=new GaussPenta();
1401 GaussPenta* gaussup=new GaussPenta();
1402
1403 for(int i=0;i<numindices;i++){
1404 gauss->GaussNode(element->GetElementType(),indices[i]);
1405 gaussup->GaussNode(element->GetElementType(),indicesup[i]);
1406
1407 enthalpy_input->GetInputValue(&enthalpy,gauss);
1408 enthalpy_input->GetInputValue(&enthalpyup,gaussup);
1409 pressure_input->GetInputValue(&pressure,gauss);
1410 pressure_input->GetInputValue(&pressureup,gaussup);
1411 watercolumn_input->GetInputValue(&watercolumn,gauss);
1412 meltingrate_input->GetInputValue(&meltingrate,gauss);
1413
1414 state=GetThermalBasalCondition(element, enthalpy, enthalpyup, pressure, pressureup, watercolumn, meltingrate);
1415
1416 switch (state) {
1417 case 0:
1418 // cold, dry base: apply basal surface forcing
1419 vec_spc->SetValue(element->nodes[i]->Sid(),0.,INS_VAL);
1420 break;
1421 case 1:
1422 // cold, wet base: keep at pressure melting point
1423 vec_spc->SetValue(element->nodes[i]->Sid(),1.,INS_VAL);
1424 break;
1425 case 2:
1426 // temperate, thin refreezing base: release spc
1427 vec_spc->SetValue(element->nodes[i]->Sid(),0.,INS_VAL);
1428 break;
1429 case 3:
1430 // temperate, thin melting base: set spc
1431 vec_spc->SetValue(element->nodes[i]->Sid(),1.,INS_VAL);
1432 break;
1433 case 4:
1434 // temperate, thick melting base: set grad H*n=0
1435 vec_spc->SetValue(element->nodes[i]->Sid(),0.,INS_VAL);
1436 break;
1437 default:
1438 _printf0_(" unknown thermal basal state found!");
1439 }
1440
1441 }
1442
1443 /*Free ressources:*/
1444 xDelete<int>(indices);
1445 xDelete<int>(indicesup);
1446 delete gauss;
1447 delete gaussup;
1448}/*}}}*/
1449void EnthalpyAnalysis::GetBConduct(IssmDouble* B,Element* element,IssmDouble* xyz_list,Gauss* gauss){/*{{{*/
1450 /*Compute B matrix. B=[B1 B2 B3 B4 B5 B6] where Bi is of size 5*1.
1451 * For node i, Bi' can be expressed in the actual coordinate system
1452 * by:
1453 * Bi_conduct=[ dh/dx ]
1454 * [ dh/dy ]
1455 * [ dh/dz ]
1456 * where h is the interpolation function for node i.
1457 *
1458 * We assume B has been allocated already, of size: 3x(1*numnodes)
1459 */
1460
1461 /*Fetch number of nodes for this finite element*/
1462 int numnodes = element->GetNumberOfNodes();
1463
1464 /*Get nodal functions derivatives*/
1465 IssmDouble* dbasis=xNew<IssmDouble>(3*numnodes);
1466 element->NodalFunctionsDerivatives(dbasis,xyz_list,gauss);
1467
1468 /*Build B: */
1469 for(int i=0;i<numnodes;i++){
1470 B[numnodes*0+i] = dbasis[0*numnodes+i];
1471 B[numnodes*1+i] = dbasis[1*numnodes+i];
1472 B[numnodes*2+i] = dbasis[2*numnodes+i];
1473 }
1474
1475 /*Clean-up*/
1476 xDelete<IssmDouble>(dbasis);
1477}/*}}}*/
1478void EnthalpyAnalysis::GetSolutionFromInputs(Vector<IssmDouble>* solution,Element* element){/*{{{*/
1479 element->GetSolutionFromInputsOneDof(solution,EnthalpyEnum);
1480}/*}}}*/
1481int EnthalpyAnalysis::GetThermalBasalCondition(Element* element, IssmDouble enthalpy, IssmDouble enthalpyup, IssmDouble pressure, IssmDouble pressureup, IssmDouble watercolumn, IssmDouble meltingrate){/*{{{*/
1482
1483 /* Check if ice in element */
1484 if(!element->IsIceInElement()) return -1;
1485
1486 /* Only update Constraints at the base of grounded ice*/
1487 if(!(element->IsOnBase())) return -1;
1488
1489 /*Intermediary*/
1490 int state=-1;
1491 IssmDouble dt;
1492
1493 /*Get parameters and inputs: */
1494 element->FindParam(&dt,TimesteppingTimeStepEnum);
1495
1496 if(enthalpy<PureIceEnthalpy(element,pressure)){
1497 if(watercolumn<=0.) state=0; // cold, dry base
1498 else state=1; // cold, wet base (refreezing)
1499 }
1500 else{
1501 if(enthalpyup<PureIceEnthalpy(element,pressureup)){
1502 if((dt==0.) && (meltingrate<0.)) state=2; // refreezing temperate base (non-physical, only for steadystate solver)
1503 else state=3; // temperate base, but no temperate layer
1504 }
1505 else state=4; // temperate layer with positive thickness
1506 }
1507
1508 _assert_(state>=0);
1509 return state;
1510}/*}}}*/
1511IssmDouble EnthalpyAnalysis::GetWetIceConductivity(Element* element, IssmDouble enthalpy, IssmDouble pressure){/*{{{*/
1512
1513 IssmDouble temperature, waterfraction;
1514 IssmDouble kappa_w = 0.6; // thermal conductivity of water (in W/m/K)
1515 IssmDouble kappa_i = element->FindParam(MaterialsThermalconductivityEnum);
1516 element->EnthalpyToThermal(&temperature, &waterfraction, enthalpy, pressure);
1517
1518 return (1.-waterfraction)*kappa_i + waterfraction*kappa_w;
1519}/*}}}*/
1520void EnthalpyAnalysis::GradientJ(Vector<IssmDouble>* gradient,Element* element,int control_type,int control_interp,int control_index){/*{{{*/
1521 _error_("Not implemented yet");
1522}/*}}}*/
1523void EnthalpyAnalysis::InputUpdateFromSolution(IssmDouble* solution,Element* element){/*{{{*/
1524
1525 bool converged;
1526 int i,rheology_law;
1527 IssmDouble B_average,s_average,T_average=0.,P_average=0.;
1528 int *doflist = NULL;
1529 IssmDouble *xyz_list = NULL;
1530
1531 /*Fetch number of nodes and dof for this finite element*/
1532 int numnodes = element->GetNumberOfNodes();
1533
1534 /*Fetch dof list and allocate solution vector*/
1535 element->GetDofListLocal(&doflist,NoneApproximationEnum,GsetEnum);
1536 IssmDouble* values = xNew<IssmDouble>(numnodes);
1537 IssmDouble* pressure = xNew<IssmDouble>(numnodes);
1538 IssmDouble* surface = xNew<IssmDouble>(numnodes);
1539 IssmDouble* B = xNew<IssmDouble>(numnodes);
1540 IssmDouble* temperature = xNew<IssmDouble>(numnodes);
1541 IssmDouble* waterfraction = xNew<IssmDouble>(numnodes);
1542
1543 /*Use the dof list to index into the solution vector: */
1544 for(i=0;i<numnodes;i++){
1545 values[i]=solution[doflist[i]];
1546
1547 /*Check solution*/
1548 if(xIsNan<IssmDouble>(values[i])) _error_("NaN found in solution vector");
1549 if(xIsInf<IssmDouble>(values[i])) _error_("Inf found in solution vector");
1550 }
1551
1552 /*Get all inputs and parameters*/
1553 element->GetInputValue(&converged,ConvergedEnum);
1554 element->GetInputListOnNodes(&pressure[0],PressureEnum);
1555 int finite_element = element->GetElementType(); if(finite_element==P1Enum) finite_element = P1DGEnum;
1556 if(converged){
1557 for(i=0;i<numnodes;i++){
1558 element->EnthalpyToThermal(&temperature[i],&waterfraction[i],values[i],pressure[i]);
1559 if(waterfraction[i]<0.) _error_("Negative water fraction found in solution vector");
1560 //if(waterfraction[i]>1.) _error_("Water fraction >1 found in solution vector");
1561 }
1562 element->AddInput(EnthalpyEnum,values,finite_element);
1563 element->AddInput(WaterfractionEnum,waterfraction,finite_element);
1564 element->AddInput(TemperatureEnum,temperature,finite_element);
1565
1566 IssmDouble* n = xNew<IssmDouble>(numnodes);
1567 if(element->material->ObjectEnum()==MatestarEnum){
1568 for(i=0;i<numnodes;i++) n[i]=3.;
1569 }
1570 else{
1571 element->GetInputListOnNodes(&n[0],MaterialsRheologyNEnum);
1572 }
1573
1574 /*Update Rheology only if converged (we must make sure that the temperature is below melting point
1575 * otherwise the rheology could be negative*/
1576 element->FindParam(&rheology_law,MaterialsRheologyLawEnum);
1577 element->GetInputListOnNodes(&surface[0],SurfaceEnum);
1578 switch(rheology_law){
1579 case NoneEnum:
1580 /*Do nothing: B is not temperature dependent*/
1581 break;
1582 case BuddJackaEnum:
1583 for(i=0;i<numnodes;i++) B[i]=BuddJacka(temperature[i]);
1584 element->AddInput(MaterialsRheologyBEnum,&B[0],finite_element);
1585 break;
1586 case CuffeyEnum:
1587 for(i=0;i<numnodes;i++) B[i]=Cuffey(temperature[i]);
1588 element->AddInput(MaterialsRheologyBEnum,&B[0],finite_element);
1589 break;
1590 case CuffeyTemperateEnum:
1591 for(i=0;i<numnodes;i++) B[i]=CuffeyTemperate(temperature[i], waterfraction[i],n[i]);
1592 element->AddInput(MaterialsRheologyBEnum,&B[0],finite_element);
1593 break;
1594 case PatersonEnum:
1595 for(i=0;i<numnodes;i++) B[i]=Paterson(temperature[i]);
1596 element->AddInput(MaterialsRheologyBEnum,&B[0],finite_element);
1597 break;
1598 case NyeH2OEnum:
1599 for(i=0;i<numnodes;i++) B[i]=NyeH2O(values[i]);
1600 element->AddInput(MaterialsRheologyBEnum,&B[0],finite_element);
1601 break;
1602 case NyeCO2Enum:
1603 for(i=0;i<numnodes;i++) B[i]=NyeCO2(values[i]);
1604 element->AddInput(MaterialsRheologyBEnum,&B[0],finite_element);
1605 break;
1606 case ArrheniusEnum:{
1607 element->GetVerticesCoordinates(&xyz_list);
1608 for(i=0;i<numnodes;i++) B[i]=Arrhenius(temperature[i],surface[i]-xyz_list[i*3+2],n[i]);
1609 element->AddInput(MaterialsRheologyBEnum,&B[0],finite_element);
1610 break;
1611 }
1612 case LliboutryDuvalEnum:{
1613 for(i=0;i<numnodes;i++) B[i]=LliboutryDuval(values[i],pressure[i],n[i],element->FindParam(MaterialsBetaEnum),element->FindParam(ConstantsReferencetemperatureEnum),element->FindParam(MaterialsHeatcapacityEnum),element->FindParam(MaterialsLatentheatEnum));
1614 element->AddInput(MaterialsRheologyBEnum,&B[0],finite_element);
1615 break;
1616 }
1617 default: _error_("Rheology law " << EnumToStringx(rheology_law) << " not supported yet");
1618 }
1619 xDelete<IssmDouble>(n);
1620 }
1621 else{
1622 element->AddInput(EnthalpyPicardEnum,values,finite_element);
1623 }
1624
1625 /*Free ressources:*/
1626 xDelete<IssmDouble>(values);
1627 xDelete<IssmDouble>(pressure);
1628 xDelete<IssmDouble>(surface);
1629 xDelete<IssmDouble>(B);
1630 xDelete<IssmDouble>(temperature);
1631 xDelete<IssmDouble>(waterfraction);
1632 xDelete<IssmDouble>(xyz_list);
1633 xDelete<int>(doflist);
1634}/*}}}*/
1635void EnthalpyAnalysis::PostProcessing(FemModel* femmodel){/*{{{*/
1636
1637 /*Intermediaries*/
1638 bool computebasalmeltingrates=true;
1639 bool isdrainicecolumn;
1640 IssmDouble dt;
1641
1642 femmodel->parameters->FindParam(&dt,TimesteppingTimeStepEnum);
1643 femmodel->parameters->FindParam(&isdrainicecolumn,ThermalIsdrainicecolumnEnum);
1644
1645 if(isdrainicecolumn){
1646 DrainWaterfraction(femmodel);
1647 }
1648 if(computebasalmeltingrates){
1649 ComputeBasalMeltingrate(femmodel);
1650 }
1651
1652}/*}}}*/
1653IssmDouble EnthalpyAnalysis::PureIceEnthalpy(Element* element,IssmDouble pressure){/*{{{*/
1654
1655 IssmDouble heatcapacity = element->FindParam(MaterialsHeatcapacityEnum);
1656 IssmDouble referencetemperature = element->FindParam(ConstantsReferencetemperatureEnum);
1657
1658 return heatcapacity*(TMeltingPoint(element,pressure)-referencetemperature);
1659}/*}}}*/
1660IssmDouble EnthalpyAnalysis::TMeltingPoint(Element* element,IssmDouble pressure){/*{{{*/
1661
1662 IssmDouble meltingpoint = element->FindParam(MaterialsMeltingpointEnum);
1663 IssmDouble beta = element->FindParam(MaterialsBetaEnum);
1664
1665 return meltingpoint-beta*pressure;
1666}/*}}}*/
1667void EnthalpyAnalysis::UpdateBasalConstraints(FemModel* femmodel){/*{{{*/
1668
1669 /*Update basal dirichlet BCs for enthalpy: */
1670 Vector<IssmDouble>* spc = NULL;
1671 IssmDouble* serial_spc = NULL;
1672
1673 spc=new Vector<IssmDouble>(femmodel->nodes->NumberOfNodes());
1674 /*First create a vector to figure out what elements should be constrained*/
1675 for(Object* & object : femmodel->elements->objects){
1676 Element* element=xDynamicCast<Element*>(object);
1677 GetBasalConstraints(spc,element);
1678 }
1679
1680 /*Assemble and serialize*/
1681 spc->Assemble();
1682 serial_spc=spc->ToMPISerial();
1683 delete spc;
1684
1685 /*Then update basal constraints nodes accordingly*/
1686 for(Object* & object : femmodel->elements->objects){
1687 Element* element=xDynamicCast<Element*>(object);
1688 ApplyBasalConstraints(serial_spc,element);
1689 }
1690
1691 femmodel->UpdateConstraintsx();
1692
1693 /*Delete*/
1694 xDelete<IssmDouble>(serial_spc);
1695}/*}}}*/
1696void EnthalpyAnalysis::UpdateConstraints(FemModel* femmodel){/*{{{*/
1697 SetActiveNodesLSMx(femmodel);
1698}/*}}}*/
Note: See TracBrowser for help on using the repository browser.