source: issm/trunk/src/c/modules/SurfaceMassBalancex/Gembx.cpp@ 26744

Last change on this file since 26744 was 26744, checked in by Mathieu Morlighem, 3 years ago

merged trunk-jpl and trunk for revision 26742

File size: 75.6 KB
RevLine 
[19554]1/*!\file GEMB module from Alex Gardner.
2 * \brief: calculates SMB
3 */
4
5#include "./SurfaceMassBalancex.h"
6#include "../../shared/shared.h"
7#include "../../toolkits/toolkits.h"
[24686]8#include "../modules.h"
[25836]9#include "../../classes/Inputs/TransientInput.h"
[19554]10
[19582]11const double Pi = 3.141592653589793;
[22758]12const double CtoK = 273.15; // Kelvin to Celcius conversion/ice melt. point T in K
13const double dts = 86400.0; // Number of seconds in a day
[19554]14
[22758]15/* Tolerances have to be defined for if loops involving some specific values
16 like densitiy of ice or melting point temp because values are "rounded"
17 (e.g rho ice = 909.99... instead of 910.0) and we don't always go into the right loop */
18const double Ttol = 1e-10;
19const double Dtol = 1e-11;
20const double Gdntol = 1e-10;
21const double Wtol = 1e-13;
[24313]22const double Ptol = 1e-6;
[22758]23
24const double CI = 2102.0; // heat capacity of snow/ice (J kg-1 k-1)
25const double LF = 0.3345E6; // latent heat of fusion (J kg-1)
26const double LV = 2.495E6; // latent heat of vaporization (J kg-1)
27const double LS = 2.8295E6; // latent heat of sublimation (J kg-1)
28const double SB = 5.67E-8; // Stefan-Boltzmann constant (W m-2 K-4)
[23189]29const double CA = 1005.0; // heat capacity of air (J kg-1 K-1)
30const double R = 8.314; // gas constant (J mol-1 K-1)
[22758]31
[24313]32const double Delflag=-99999;
33
[19554]34void Gembx(FemModel* femmodel){ /*{{{*/
35
[24686]36 int count=0;
[25836]37 IssmDouble time,dt,finaltime;
[24686]38 IssmDouble timeclim=0.0;
39 IssmDouble t,smb_dt;
40 IssmDouble delta;
[25836]41 bool linear_interp=true;
[24686]42
[26744]43 femmodel->parameters->FindParam(&linear_interp,TimesteppingInterpForcingEnum); /*is interpolation requested*/
[24686]44 femmodel->parameters->FindParam(&time,TimeEnum); /*transient core time at which we run the smb core*/
45 femmodel->parameters->FindParam(&dt,TimesteppingTimeStepEnum); /*transient core time step*/
46 femmodel->parameters->FindParam(&finaltime,TimesteppingFinalTimeEnum);
47 femmodel->parameters->FindParam(&smb_dt,SmbDtEnum); /*time period for the smb solution, usually smaller than the glaciological dt*/
48
49 //before starting loop, realize that the transient core runs this smb_core at time = time +deltaT.
50 //go back to time - deltaT:
51 time-=dt;
52
53 IssmDouble timeinputs = time;
54
55 /*Start loop: */
56 count=1;
[25836]57 for (t=time;t<=time+dt-smb_dt;t=t+smb_dt){
[24686]58
[25836]59 for(Object* & object : femmodel->elements->objects){
60 Element* element=xDynamicCast<Element*>(object);
[24686]61
62 timeclim=time;
[25836]63 if (linear_interp) timeinputs = t-time+timeclim;
64 else timeinputs = t-time+timeclim+smb_dt/2;
[24686]65 element->SmbGemb(timeinputs,count);
66 }
67 count=count+1;
[19554]68 }
69
70} /*}}}*/
71void GembgridInitialize(IssmDouble** pdz, int* psize, IssmDouble zTop, IssmDouble dzTop, IssmDouble zMax, IssmDouble zY){ /*{{{*/
72
73 /* This file sets up the initial grid spacing and total grid depth. The
74 grid structure is set as constant grid length 'dzTop' for the top
75 'zTop' meters of the model grid. Bellow 'zTop' the gid length increases
76 linearly with depth */
77
78 /*intermediary:*/
[22758]79 IssmDouble dgpTop=0.0;
80 int gpTop=0;
81 int gpBottom=0;
82 int i=0;
83 IssmDouble gp0=0.0;
84 IssmDouble z0=0.0;
[19554]85 IssmDouble* dzT=NULL;
86 IssmDouble* dzB=NULL;
87
88 /*output: */
89 IssmDouble* dz=NULL;
90
91 //----------------------Calculate Grid Lengths------------------------------
92 //calculate number of top grid points
93 dgpTop = zTop/dzTop;
94
95 //check to see if the top grid cell structure length (dzTop) goes evenly
96 //into specified top structure depth (zTop). Also make sure top grid cell
97 //structure length (dzTop) is greater than 5 cm
[23394]98 #ifndef _HAVE_AD_ //avoid the round operation check!
[19613]99 if (dgpTop != round(dgpTop)){
[25836]100 _error_("top grid cell structure length does not go evenly into specified top structure depth, adjust dzTop or zTop\n");
[19554]101 }
[19613]102 #endif
103 if(dzTop < 0.05){
[25836]104 _printf_("initial top grid cell length (dzTop) is < 0.05 m\n");
[19554]105 }
[19613]106 gpTop=reCast<int,IssmDouble>(dgpTop);
[19554]107
108 //initialize top grid depth vector
109 dzT = xNew<IssmDouble>(gpTop);
110 for (i=0;i<gpTop;i++)dzT[i]=dzTop;
111
112 //bottom grid cell depth = x*zY^(cells from to structure)
113 //figure out the number of grid points in the bottom vector (not known a priori)
114 gp0 = dzTop;
115 z0 = zTop;
116 gpBottom = 0;
117 while (zMax > z0){
118 gp0= gp0 * zY;
119 z0 = z0 + gp0;
120 gpBottom++;
121 }
[21341]122 //initialize bottom vectors
[19554]123 dzB = xNewZeroInit<IssmDouble>(gpBottom);
124 gp0 = dzTop;
125 z0 = zTop;
126 for(i=0;i<gpBottom;i++){
127 gp0=gp0*zY;
128 dzB[i]=gp0;
129 }
130
131 //combine top and bottom dz vectors
132 dz = xNew<IssmDouble>(gpTop+gpBottom);
133 for(i=0;i<gpTop;i++){
134 dz[i]=dzT[i];
135 }
136 for(i=0;i<gpBottom;i++){
137 dz[gpTop+i]=dzB[i];
138 }
139
[24686]140 /*Free resouces:*/
[19554]141 xDelete(dzT);
142 xDelete(dzB);
[23189]143
[19554]144 //---------NEED TO IMPLEMENT A PROPER GRID STRECHING ALGORITHM------------
145
146 /*assign ouput pointers: */
147 *pdz=dz;
148 *psize=gpTop+gpBottom;
149} /*}}}*/
150IssmDouble Marbouty(IssmDouble T, IssmDouble d, IssmDouble dT){ /*{{{*/
151
152 // calculates grain growth according to Fig. 9 of Marbouty, 1980
[21341]153 // ------NO GRAIN GROWTH FOR d > 400 kg m-3 because H is set to zero------
[19554]154 // ---------------this is a major limitation of the model-------------------
155
156 // initialize
[22758]157 IssmDouble F = 0.0, H=0.0, G=0.0;
[19582]158 const IssmDouble E = 0.09; //[mm d-1] model time growth constant E
[24313]159 // convert T from K to degC
[22758]160 T = T - CtoK;
[19554]161
162 // temperature coefficient F
[24313]163 if(T> -6.0+Ttol) F = 0.7 + ((T/-6.0) * 0.3);
164 if(T<= -6.0+Ttol && T> -22.0+Ttol) F = 1.0 - ((T+6.0)/-16.0 * 0.8);
165 if(T<= -22.0+Ttol && T> -40.0+Ttol) F = 0.2 - ((T+22.0)/-18.0 * 0.2);
[19554]166
167 // density coefficient F
[24313]168 if(d< 150.0-Dtol) H=1.0;
[19554]169
[24313]170 if(d>= 150.0-Dtol && d <400.0-Dtol) H = 1.0 - ((d-150.0)/250.0);
[19554]171
172 // temperature gradient coefficient G
[24313]173 if(dT >= 0.16-Ttol && dT < 0.25-Ttol) G = ((dT - 0.16)/0.09) * 0.1;
174 if(dT >= 0.25-Ttol && dT < 0.4-Ttol) G = 0.1 + (((dT - 0.25)/0.15) * 0.57);
175 if(dT >= 0.4-Ttol && dT < 0.5-Ttol) G = 0.67 + (((dT - 0.4)/0.1) * 0.23);
176 if(dT >= 0.5-Ttol && dT < 0.7-Ttol) G = 0.9 + (((dT - 0.5)/0.2) * 0.1);
177 if(dT >= 0.7-Ttol) G = 1.0;
[19554]178
179 // grouped coefficient Q
180 return F*H*G*E;
181
182} /*}}}*/
[26744]183IssmDouble gardnerAlb(IssmDouble* re, IssmDouble* dz, IssmDouble* d, IssmDouble clabSnow, IssmDouble clabIce, IssmDouble SZA, IssmDouble COT, IssmDouble dPHC, int m){ /*{{{*/
184 //gardnerAlb(S1, c1, SZA, t, z1, S2, c2)
185 //This is an implementation of the snow and ice broadband albedo
186 // parameterization developed by Alex Gardner.
187 //Created By: Alex S. Gardner, Jet Propulsion Laboratory [alex.s.gardner@jpl.nasa.gov]
188 // Last Modified: June, 2014
189 //Full Reference: Gardner, A. S., and Sharp, M. J.: A review of snow and
190 // ice albedo and the development of a new physically based broadband albedo
191 // parameterization, J. Geophys. Res., 115, F01009, 10.1029/2009jf001444,
192 // 2010.
193
194 //INPUTS
195 // ONE LAYER
196 // - S1 : specific surface area of the snow or ice [cm^2 g-1]
197 // - c1 : concentration of light absorbing carbon [ppm1]
198 // - SZA : solar zenith angle of the incident radiation [deg]
199 // - t : cloud optical thickness
200 // TWO LAYER
201 // - z1 : depth of snow suface layer [mm w.e.]
202 // - S2 : specific surface area of bottom ice layer [cm^2 g-1]
203 // - c2 : concentration of light absorbing carbon of bottom ice
204 // layer [ppm1]
205 IssmDouble c1=clabSnow;
206 IssmDouble c2=clabIce;
207 IssmDouble t=COT;
208 IssmDouble a=0.0;
209
210 //Single layer albedo parameterization
211 //convert effective radius to specific surface area [cm2 g-1]
212 IssmDouble S1 = 3.0 / (0.091 * re[0]);
213
214 //effective solar zenith angle
215 IssmDouble x = min(pow(t/(3*cos(Pi*SZA/180.0)),0.5), 1.0);
216 IssmDouble u = 0.64*x + (1-x)*cos(Pi*SZA/180.0);
217
218 // pure snow albedo
219 IssmDouble as = 1.48 - pow(S1,-0.07);
220
221 //change in pure snow albedo due to soot loading
222 IssmDouble dac = max(0.04 - as, pow(-c1,0.55)/(0.16 + 0.6*pow(S1,0.5) + (1.8*pow(c1,0.6))*pow(S1,-0.25)));
223
224 //Two layer albedo parameterization
225 // do two layer calculation if there is more than 1 layer
226 IssmDouble z1=0.0;
227 int lice=0;
228 for(int l=0;(l<m & d[l]<dPHC-Dtol);l++){
229 z1=z1+dz[l]*d[l]; //mm
230 lice=l+1;
231 }
232 if (m>0 & lice<m & z1 > Dtol){
233 // determine albedo values for bottom layer
234 IssmDouble S2 = 3.0 / (0.091 * re[lice]);
235
236 // pure snow albedo
237 IssmDouble as2 = 1.48 - pow(S2,-0.07);
238
239 // change in pure snow albedo due to soot loading
240 IssmDouble dac2 = max(0.04 - as2, pow(-c2,0.55)/(0.16 + 0.6*pow(S1,0.5) + (1.8*pow(c2,0.6))*pow(S2,-0.25)));
241
242 // determine the effective change due to finite depth and soot loading
243 IssmDouble A = min(1.0, (2.1 * pow(z1,1.35*(1-as) - 0.1*c1 - 0.13)));
244
245 dac = (as2 + dac2 - as) + A*((as + dac) - (as2 + dac2));
246 }
247
248 // change in albedo due to solar zenith angle
249 IssmDouble dasz = 0.53*as*(1 - (as + dac))*pow(1 - u,1.2);
250
251 // change in albedo due to cloud (apart from change in diffuse fraction)
252 IssmDouble dat = (0.1*t*pow(as + dac,1.3)) / (pow(1 + 1.5*t,as));
253
254 // Broadband albedo
255 a = as + dac + dasz + dat;
256
257 return a;
258} /*}}}*/
[22758]259void grainGrowth(IssmDouble** pre, IssmDouble** pgdn, IssmDouble** pgsp, IssmDouble* T,IssmDouble* dz,IssmDouble* d, IssmDouble* W,IssmDouble smb_dt,int m,int aIdx,int sid){ /*{{{*/
[19554]260
261 /*Created by: Alex S. Gardner, University of Alberta
262
263 *Description*: models the effective snow grain size
264
265 *Reference:*
266 DENDRITIC SNOW METAMORPHISM:
267 Brun, E., P. David, M. Sudul, and G. Brunot, 1992: A numerical model to
268 simulate snow-cover stratigraphy for operational avalanche forecasting.
269 Journal of Glaciology, 38, 13-22.
270
271 NONDENDRITIC SNOW METAMORPHISM:
272 Dry snow metamorphism:
273 Marbouty, D., 1980: An experimental study of temperature-gradient
274 metamorphism. Journal of Glaciology, 26, 303-312.
275
276 WET SNOW METAMORPHISM:
277 Brun, E., 1989: Investigation on wet-snow metamorphism in respect of
278 liquid-water content. Annals of Glaciology, 13, 22-26.
279
280 INPUTS
281 * T: grid cell temperature [k]
282 * dz: grid cell depth [m]
283 * d: grid cell density [kg m-3]
284 * W: water content [kg/m^2]
285 * re: effective grain size [mm]
286 * gdn: grain dentricity
287 * gsp: grain sphericity
288 * dt: time step of input data [s]
289
290 OUTPUTS
291 * re: effective grain size [mm]
292 * gdn: grain dentricity
293 * gsp: grain sphericity*/
294
295 /*intermediary: */
[22758]296 IssmDouble dt=0.0;
[19554]297 IssmDouble* gsz=NULL;
298 IssmDouble* dT=NULL;
299 IssmDouble* zGPC=NULL;
300 IssmDouble* lwc=NULL;
[22758]301 IssmDouble Q=0.0;
[19554]302
[22758]303 /*output: */
304 IssmDouble* re=NULL;
305 IssmDouble* gdn=NULL;
306 IssmDouble* gsp=NULL;
307
[19566]308 if(VerboseSmb() && sid==0 && IssmComm::GetRank()==0)_printf0_(" grain growth module\n");
[22758]309
310 /*Recover pointers: */
311 re=*pre;
312 gdn=*pgdn;
313 gsp=*pgsp;
[23189]314
[19554]315 /*only when aIdx = 1 or 2 do we run grainGrowth: */
[24313]316 if(aIdx!=1 && aIdx!=2){
[19554]317 /*come out as we came in: */
318 return;
319 }
320
321 /*Figure out grain size from effective grain radius: */
[19582]322 gsz=xNew<IssmDouble>(m); for(int i=0;i<m;i++)gsz[i]=re[i]*2.0;
[19554]323
324 /*Convert dt from seconds to day: */
[22758]325 dt=smb_dt/dts;
[19554]326
327 /*Determine liquid-water content in percentage: */
328 lwc=xNew<IssmDouble>(m); for(int i=0;i<m;i++)lwc[i]= W[i] / (d[i]*dz[i])*100.0;
329
330 //set maximum water content by mass to 9 percent (Brun, 1980)
[24313]331 for(int i=0;i<m;i++)if(lwc[i]>9.0+Wtol) lwc[i]=9.0;
[19554]332
333 /* Calculate temperature gradiant across grid cells
334 * Returns the avereage gradient across the upper and lower grid cell */
335
336 //initialize
337 dT=xNewZeroInit<IssmDouble>(m);
338
339 //depth of grid point center from surface
340 zGPC=xNewZeroInit<IssmDouble>(m);
341 for(int i=0;i<m;i++){
342 for (int j=0;j<=i;j++) zGPC[i]+=dz[j];
[22758]343 zGPC[i]-=(dz[i]/2.0);
[19554]344 }
345
346 // Take forward differences on left and right edges
[25836]347 if(m>1){
348 dT[0] = (T[1] - T[0])/(zGPC[1]-zGPC[0]);
349 dT[m-1] = (T[m-1] - T[m-2])/(zGPC[m-1]-zGPC[m-2]);
350 }
[19554]351
352 //Take centered differences on interior points
353 for(int i=1;i<m-1;i++) dT[i] = (T[i+1]-T[i-1])/(zGPC[i+1]-zGPC[i-1]);
354
355 // take absolute value of temperature gradient
[19613]356 for(int i=0;i<m;i++)dT[i]=fabs(dT[i]);
[23189]357
[19554]358 /*Snow metamorphism. Depends on value of snow dendricity and wetness of the snowpack: */
359 for(int i=0;i<m;i++){
[22758]360 if (gdn[i]>0.0+Gdntol){
[19554]361
[24313]362 if(W[i]<=0.0+Wtol){
363 //_printf_("Dendritic dry snow metamorphism\n");
364 //index for dentricity > 0 and T gradients < 5 degC m-1 and >= 5 degC m-1
365 if(dT[i]<=5.0+Ttol){
366 //determine coefficients
367 IssmDouble A = - 2e8 * exp(-6e3 / T[i]) * dt;
368 IssmDouble B = 1e9 * exp(-6e3 / T[i]) * dt;
369 //new dentricity and sphericity for dT < 5 degC m-1
370 gdn[i] += A;
371 gsp[i] += B;
372 }
373 else{
374 // new dendricity and sphericity for dT >= 5 degC m-1
[19554]375
[24313]376 //determine coeff0icients
377 IssmDouble C = (-2e8 * exp(-6e3 / T[i]) * dt) * pow(dT[i],.4);
378 gdn[i] +=C;
379 gsp[i] +=C;
380 }
[19554]381 }
382 else{
[24313]383 // wet snow metamorphism
384 //_printf_("Dendritic wet snow metamorphism\n");
[19554]385
386 //determine coefficient
[19582]387 IssmDouble D = (1.0/16.0) * pow(lwc[i],3.0) * dt;
[19554]388
[24313]389 // new dendricity for wet snow
[19554]390 gdn[i] -= D;
[24313]391 // new sphericity for wet snow
[19554]392 gsp[i] += D;
393 }
394 // dendricity and sphericity can not be > 1 or < 0
[26744]395 if (gdn[i]<=0.0+Gdntol)gdn[i]=0.0;
396 if (gsp[i]<=0.0+Gdntol)gsp[i]=0.0;
397 if (gdn[i]>=1.0-Gdntol)gdn[i]=1.0;
398 if (gsp[i]>=1.0-Gdntol)gsp[i]=1.0;
[19554]399
[24313]400 // determine new grain size (mm)
401 gsz[i] = 0.1 + (1.0-gdn[i])*0.25 + (0.5-gsp[i])*0.1;
[19554]402
403 }
404 else{
405
[24313]406 //When wet-snow grains (class 6) are submitted to a
[25836]407 // temperature gradient higher than 5 degC m-1, their sphericity
[24313]408 // decreases according to Equations (4). When sphericity
409 // reaches 0, their size increases according to the functions
410 // determined by Marbouty. (Brun et al., 1992)
[26744]411 if(gsp[i]>0.0+Gdntol && gsp[i]<1.0-Gdntol){
412
413 IssmDouble F = 0.0;
414
415 if (dT[i]>5.0+Ttol){
416 F = (-2e8 * exp(-6e3 / T[i]) * dt) * pow(dT[i],.4);
417 }
418 else if (W[i]>0.0+Wtol){
419 F = (1.0/16.0) * pow(lwc[i],3.0) * dt;
420 }
421 else{
422 F = 1e9 * exp(-6e3 / T[i]) * dt;
423 }
424 gsp[i] +=F;
425
[24313]426 }
[26744]427 if (gsp[i]<=0.0+Gdntol)gsp[i]=0.0;
428 if (gsp[i]>=1.0-Gdntol)gsp[i]=1.0;
429
[24313]430 /*Dry snow metamorphism (Marbouty, 1980) grouped model coefficents
[19554]431 *from Marbouty, 1980: Figure 9*/
[26744]432 if(W[i]<=0.0+Wtol || (gsp[i]<=0.0+Gdntol && dT[i]>5.0+Ttol)){
[24313]433 //_printf_("Nondendritic snow metamorphism\n");
434 Q = Marbouty(T[i],d[i],dT[i]);
[19554]435
[24313]436 // calculate grain growth
437 gsz[i] += (Q*dt);
438 }
[19554]439 //Wet snow metamorphism (Brun, 1989)
[24313]440 else{
441 //_printf_("Nondendritic wet snow metamorphism\n");
[19554]442 //wet rate of change coefficient
[25836]443 IssmDouble E = (1.28e-8 + 4.22e-10 * pow(lwc[i],3.0))* (dt *dts); // [mm^3 s^-1]
[19554]444
445 // calculate change in grain volume and convert to grain size
[19582]446 gsz[i] = 2.0 * pow(3.0/(Pi * 4.0)*((4.0/ 3.0)*Pi*pow(gsz[i]/2.0,3.0) + E),1.0/3.0);
[19554]447 }
448
449 // grains with sphericity == 1 can not have grain sizes > 2 mm (Brun, 1992)
[22758]450 if (fabs(gsp[i]-1.0)<Wtol && gsz[i]>2.0-Wtol) gsz[i]=2.0;
[19554]451
452 // grains with sphericity == 0 can not have grain sizes > 5 mm (Brun, 1992)
[24313]453 if (fabs(gsp[i]-1.0)>=Wtol && gsz[i]>5.0-Wtol) gsz[i]=5.0;
[19554]454 }
455
[24313]456 //convert grain size back to effective grain radius:
[19582]457 re[i]=gsz[i]/2.0;
[19554]458 }
[23189]459
[24686]460 /*Free resources:*/
[19554]461 xDelete<IssmDouble>(gsz);
462 xDelete<IssmDouble>(dT);
463 xDelete<IssmDouble>(zGPC);
464 xDelete<IssmDouble>(lwc);
465
[22758]466 /*Assign output pointers:*/
467 *pre=re;
468 *pgdn=gdn;
469 *pgsp=gsp;
470
[19554]471} /*}}}*/
[26744]472void albedo(IssmDouble** pa, IssmDouble** padiff, int aIdx, IssmDouble* re, IssmDouble* dz, IssmDouble* d, IssmDouble cldFrac, IssmDouble aIce, IssmDouble aSnow, IssmDouble aValue, IssmDouble adThresh, IssmDouble* TK, IssmDouble* W, IssmDouble P, IssmDouble EC, IssmDouble Msurf, IssmDouble clabSnow, IssmDouble clabIce, IssmDouble SZA, IssmDouble COT, IssmDouble t0wet, IssmDouble t0dry, IssmDouble K, IssmDouble dt, IssmDouble dIce, int m,int sid) { /*{{{*/
[19554]473
474 //// Calculates Snow, firn and ice albedo as a function of:
[22758]475 // 0 : direct input from aValue parameter
[19554]476 // 1 : effective grain radius (Gardner & Sharp, 2009)
[25836]477 // 2 : effective grain radius (Brun et al., 1992, Lefebre et al., 2003)
[19554]478 // 3 : density and cloud amount (Greuell & Konzelmann, 1994)
479 // 4 : exponential time decay & wetness (Bougamont & Bamber, 2005)
480
481 //// Inputs
482 // aIdx = albedo method to use
[23189]483
[22758]484 // Method 0
485 // aValue = direct input value for albedo, override all changes to albedo
[19554]486
[22758]487 // adThresh
488 // Apply below method to all areas with densities below this value,
489 // or else apply direct input value, allowing albedo to be altered.
490 // Default value is rho water (1023 kg m-3).
491
[19554]492 // Methods 1 & 2
493 // re = surface effective grain radius [mm]
[26744]494 // Method 1, optional
495 // clabSnow = concentration of light absorbing carbon [ppm1], default 0
496 // SZA = solar zenith angle of the incident radiation [deg], default 0
497 // COT = cloud optical thickness, default 0
498 // For TWO LAYER
499 // clabIce = concentration of light absorbing carbon of first ice layer [ppm1], default 0
[19554]500
[26744]501 // Method 3
[19554]502 // d = snow surface density [kg m-3]
503 // n = cloud amount
504 // aIce = albedo of ice
505 // aSnow = albedo of fresh snow
506
[26744]507 // Method 4
[19554]508 // aIce = albedo of ice
509 // aSnow = albedo of fresh snow
510 // a = grid cell albedo from prevous time step;
511 // T = grid cell temperature [k]
512 // W = pore water [kg]
513 // P = precipitation [mm w.e.] or [kg m-3]
514 // EC = surface evaporation (-) condensation (+) [kg m-2]
515 // t0wet = time scale for wet snow (15-21.9) [d]
516 // t0dry = warm snow timescale [15] [d]
517 // K = time scale temperature coef. (7) [d]
518 // dt = time step of input data [s]
519
520 //// Output
521 // a = grid cell albedo
522
523 //// Usage
524 // Method 1
525 // a = albedo(1, 0.1);
526
527 // Method 4
528 // a = albedo(4, [], [], [], 0.48, 0.85, [0.8 0.5 ... 0.48], ...
529 // [273 272.5 ... 265], [0 0.001 ... 0], 0, 0.01, 15, 15, 7, 3600)
[19566]530
[22758]531 /*output: */
532 IssmDouble* a=NULL;
[26744]533 IssmDouble* adiff=NULL;
[22758]534
[19566]535 if(VerboseSmb() && sid==0 && IssmComm::GetRank()==0)_printf0_(" albedo module\n");
[22758]536
537 /*Recover pointers: */
538 a=*pa;
[26744]539 adiff=*padiff;
[23189]540
[19554]541 //some constants:
542 const IssmDouble dSnow = 300; // density of fresh snow [kg m-3]
[25836]543 const IssmDouble dPHC = 830; //Pore closeoff density
544 const IssmDouble ai_max = 0.58; //maximum ice albedo, from Lefebre,2003
545 const IssmDouble ai_min = aIce; //minimum ice albedo
546 const IssmDouble as_min = 0.65; //minimum snow albedo, from Alexander 2014
[19554]547
[24313]548 if(aIdx==0 || (adThresh - d[0])<Dtol){
[22758]549 a[0] = aValue;
[19554]550 }
[22758]551 else{
552 if(aIdx==1){
553 //function of effective grain radius
[26744]554 // clabSnow, IssmDouble clabIce, IssmDouble SZA, IssmDouble COT, int m
555 a[0]=gardnerAlb(re, dz, d, clabSnow, clabIce, SZA, COT, dPHC, m);
556 adiff[0]=gardnerAlb(re, dz, d, clabSnow, clabIce, 50, COT, dPHC, m);
[22758]557 }
558 else if(aIdx==2){
[19554]559
[22758]560 // Spectral fractions (Lefebre et al., 2003)
561 // [0.3-0.8um 0.8-1.5um 1.5-2.8um]
562
563 IssmDouble sF[3] = {0.606, 0.301, 0.093};
564
565 // convert effective radius to grain size in meters
566 IssmDouble gsz = (re[0] * 2.0) / 1000.0;
567
568 // spectral range:
569 // 0.3 - 0.8um
[23394]570 IssmDouble a0 = min(0.98, 1 - 1.58 *pow(gsz,0.5));
[22758]571 // 0.8 - 1.5um
[23394]572 IssmDouble a1 = max(0., 0.95 - 15.4 *pow(gsz,0.5));
[22758]573 // 1.5 - 2.8um
[23394]574 IssmDouble a2 = max(0.127, 0.88 + 346.3*gsz - 32.31*pow(gsz,0.5));
[22758]575
576 // broadband surface albedo
577 a[0] = sF[0]*a0 + sF[1]*a1 + sF[2]*a2;
[25836]578
[22758]579 }
580 else if(aIdx==3){
581
582 // a as a function of density
583
584 // calculate albedo
585 a[0] = aIce + (d[0] - dIce)*(aSnow - aIce) / (dSnow - dIce) + (0.05 * (cldFrac - 0.5));
586 }
587 else if(aIdx==4){
588
589 // exponential time decay & wetness
590
591 // change in albedo with time:
592 // (d_a) = (a - a_old)/(t0)
593 // where: t0 = timescale for albedo decay
594
595 dt = dt / dts; // convert from [s] to [d]
596
597 // initialize variables
598 IssmDouble* t0=xNew<IssmDouble>(m);
599 IssmDouble* T=xNew<IssmDouble>(m);
600 IssmDouble* t0warm=xNew<IssmDouble>(m);
601 IssmDouble* d_a=xNew<IssmDouble>(m);
602
603 // specify constants
604 // a_wet = 0.15; // water albedo (0.15)
605 // a_new = aSnow // new snow albedo (0.64 - 0.89)
606 // a_old = aIce; // old snow/ice albedo (0.27-0.53)
607 // t0_wet = t0wet; // time scale for wet snow (15-21.9) [d]
608 // t0_dry = t0dry; // warm snow timescale [15] [d]
609 // K = 7 // time scale temperature coef. (7) [d]
610 // W0 = 300; // 200 - 600 [mm]
611 const IssmDouble z_snow = 15.0; // 16 - 32 [mm]
612
613 // determine timescale for albedo decay
614 for(int i=0;i<m;i++)if(W[i]>0.0+Wtol)t0[i]=t0wet; // wet snow timescale
615 for(int i=0;i<m;i++)T[i]=TK[i] - CtoK; // change T from K to degC
616 for(int i=0;i<m;i++) t0warm[i]= fabs(T[i]) * K + t0dry; //// 'warm' snow timescale
617 for(int i=0;i<m;i++)if(fabs(W[i])<Wtol && T[i]>=-10.0-Ttol)t0[i]= t0warm[i];
618 for(int i=0;i<m;i++)if(T[i]<-10.0-Ttol) t0[i] = 10.0 * K + t0dry; // 'cold' snow timescale
619
620 // calculate new albedo
621 for(int i=0;i<m;i++)d_a[i] = (a[i] - aIce) / t0[i] * dt; // change in albedo
622 for(int i=0;i<m;i++)a[i] -= d_a[i]; // new albedo
623
624 // modification of albedo due to thin layer of snow or solid
[24686]625 // condensation (deposition) at the surface
[22758]626
627 // check if condensation occurs & if it is deposited in solid phase
628 if ( EC > 0.0 + Dtol && T[0] < 0.0-Ttol) P = P + (EC/dSnow) * 1000.0; // add cond to precip [mm]
629
630 a[0] = aSnow - (aSnow - a[0]) * exp(-P/z_snow);
631
632 //----------THIS NEEDS TO BE IMPLEMENTED AT A LATER DATE------------
633 // modification of albedo due to thin layer of water on the surface
634 // a_surf = a_wet - (a_wet - a_surf) * exp(-W_surf/W0);
635
[24686]636 /*Free resources:*/
[22758]637 xDelete<IssmDouble>(t0);
638 xDelete<IssmDouble>(T);
639 xDelete<IssmDouble>(t0warm);
640 xDelete<IssmDouble>(d_a);
641
642 }
643 else _error_("albedo method switch should range from 0 to 4!");
[25836]644
645 //If we do not have fresh snow
646 if (aIdx<3 && aIdx>0 && (adThresh - d[0])>=Dtol){
[26744]647 // In a snow layer < 10cm, account for mix of ice and snow,
[25836]648 // after P. Alexander et al., 2014
649 IssmDouble depthsnow=0.0;
650 IssmDouble aice=0.0;
651 int lice=0;
652 for(int l=0;(l<m & d[l]<dPHC-Dtol);l++){
653 depthsnow=depthsnow+dz[l];
654 lice=l+1;
655 }
656 if (depthsnow<=0.1+Dtol & lice<m & d[lice]>=dPHC-Dtol){
657 aice = ai_max + (as_min - ai_max)*(d[lice]-dIce)/(dPHC-dIce);
658 a[0]= aice + max(a[0]-aice,0.0)*(depthsnow/0.1);
659 }
660
661 if (d[0]>=dPHC-Dtol){
662 if (d[0]<dIce-Dtol){ //For continuity of albedo in firn i.e. P. Alexander et al., 2014
663
664 //ai=ai_max + (as_min - ai_max)*(dI-dIce)/(dPHC-dIce);
665 //dPHC is pore close off (830 kg m^-3)
666 //dI is density of the upper firn layer
667
668 a[0] = ai_max + (as_min - ai_max)*(d[0]-dIce)/(dPHC-dIce);
669
670 }
671 else{ //surface layer is density of ice
672
673 //When density is > dIce (typically 910 kg m^-3, 920 is used by Alexander in MAR),
674 //ai=ai_min + (ai_max - ai_min)*e^(-1*(Msw(t)/K))
675 //K is a scale factor (set to 200 kg m^-2)
676 //Msw(t) is the time-dependent accumulated amount of excessive surface meltwater
677 // before run-off in kg m^-2 (melt per GEMB timestep, i.e. 3 hourly)
678 IssmDouble M = Msurf+W[0];
679 a[0]=max(ai_min + (ai_max - ai_min)*exp(-1*(M/200)), ai_min);
680
681 }
682 }
683 }
[19554]684 }
[22758]685
[19554]686 // Check for erroneous values
687 if (a[0] > 1) _printf_("albedo > 1.0\n");
688 else if (a[0] < 0) _printf_("albedo is negative\n");
[19613]689 else if (xIsNan(a[0])) _error_("albedo == NAN\n");
[22758]690
691 /*Assign output pointers:*/
692 *pa=a;
[26744]693 *padiff=adiff;
[22758]694
[19554]695} /*}}}*/
[26744]696void thermo(IssmDouble* pEC, IssmDouble** pT, IssmDouble* pulwrf, IssmDouble* re, IssmDouble* dz, IssmDouble* d, IssmDouble* swf, IssmDouble dlwrf, IssmDouble Ta, IssmDouble V, IssmDouble eAir, IssmDouble pAir, IssmDouble eIdx, IssmDouble teValue, IssmDouble dulwrfValue, IssmDouble teThresh, IssmDouble Ws, IssmDouble dt0, int m, IssmDouble Vz, IssmDouble Tz, IssmDouble thermo_scaling, IssmDouble dIce, int sid, bool isconstrainsurfaceT, bool isdeltaLWup) { /*{{{*/
[19554]697
698 /* ENGLACIAL THERMODYNAMICS*/
[23189]699
[19554]700 /* Description:
701 computes new temperature profile accounting for energy absorption and
702 thermal diffusion.*/
703
704 // INPUTS
705 // T: grid cell temperature [k]
706 // dz: grid cell depth [m]
707 // d: grid cell density [kg m-3]
708 // swf: shortwave radiation fluxes [W m-2]
709 // dlwrf: downward longwave radiation fluxes [W m-2]
710 // Ta: 2 m air temperature
711 // V: wind velocity [m s-1]
712 // eAir: screen level vapor pressure [Pa]
713 // Ws: surface water content [kg]
714 // dt0: time step of input data [s]
715 // elev: surface elevation [m a.s.l.]
716 // Vz: air temperature height above surface [m]
717 // Tz: wind height above surface [m]
[22758]718 // thermo_scaling: scaling factor to multiply the thermal diffusion timestep (delta t)
[19554]719
720 // OUTPUTS
721 // T: grid cell temperature [k]
722 // EC: evaporation/condensation [kg]
[24313]723 // ulwrf: upward longwave radiation flux [W m-2]
[23189]724
[19554]725 /*intermediary: */
726 IssmDouble* K = NULL;
727 IssmDouble* KU = NULL;
728 IssmDouble* KD = NULL;
729 IssmDouble* KP = NULL;
730 IssmDouble* Au = NULL;
731 IssmDouble* Ad = NULL;
732 IssmDouble* Ap = NULL;
733 IssmDouble* Nu = NULL;
734 IssmDouble* Nd = NULL;
735 IssmDouble* Np = NULL;
736 IssmDouble* dzU = NULL;
737 IssmDouble* dzD = NULL;
738 IssmDouble* sw = NULL;
739 IssmDouble* dT_sw = NULL;
740 IssmDouble* T0 = NULL;
741 IssmDouble* Tu = NULL;
742 IssmDouble* Td = NULL;
743
[22758]744 IssmDouble z0=0.0;
745 IssmDouble dt=0.0;
746 IssmDouble max_fdt=0.0;
747 IssmDouble Ts=0.0;
748 IssmDouble L=0.0;
749 IssmDouble eS=0.0;
750 IssmDouble Ri=0.0;
751 IssmDouble coefM=0.0;
752 IssmDouble coefH=0.0;
753 IssmDouble An=0.0;
754 IssmDouble C=0.0;
755 IssmDouble shf=0.0;
756 IssmDouble ds=0.0;
757 IssmDouble dAir=0.0;
758 IssmDouble TCs=0.0;
759 IssmDouble lhf=0.0;
760 IssmDouble EC_day=0.0;
761 IssmDouble dT_turb=0.0;
762 IssmDouble turb=0.0;
763 IssmDouble ulw=0.0;
764 IssmDouble dT_ulw=0.0;
765 IssmDouble dlw=0.0;
766 IssmDouble dT_dlw=0.0;
[23189]767
[19554]768 /*outputs:*/
[22758]769 IssmDouble EC=0.0;
770 IssmDouble* T=*pT;
[24313]771 IssmDouble ulwrf=0.0;
[19554]772
[19566]773 if(VerboseSmb() && sid==0 && IssmComm::GetRank()==0)_printf0_(" thermal module\n");
774
[19554]775 ds = d[0]; // density of top grid cell
776
777 // calculated air density [kg/m3]
[22758]778 dAir = 0.029 * pAir /(R * Ta);
[19554]779
780 // thermal capacity of top grid cell [J/k]
781 TCs = d[0]*dz[0]*CI;
782
783 //initialize Evaporation - Condenstation
[22758]784 EC = 0.0;
[23189]785
[19554]786 // check if all SW applied to surface or distributed throught subsurface
787 // swIdx = length(swf) > 1
788
789 // SURFACE ROUGHNESS (Bougamont, 2006)
790 // wind/temperature surface roughness height [m]
[22758]791 if (ds < dIce-Dtol && fabs(Ws) < Wtol) z0 = 0.00012; // 0.12 mm for dry snow
792 else if (ds >= dIce-Dtol) z0 = 0.0032; // 3.2 mm for ice
[19554]793 else z0 = 0.0013; // 1.3 mm for wet snow
794
795 // if V = 0 goes to infinity therfore if V = 0 change
[22758]796 if(V<0.01-Dtol)V=0.01;
[23189]797
[19554]798 // Bulk-transfer coefficient for turbulent fluxes
[24313]799 //An = pow(0.4,2) / pow(log(Tz/z0),2); // Bulk-transfer coefficient
800 An = pow(0.4,2) / (log(Tz/z0)*log(Vz/z0)); // Bulk-transfer coefficient
[19554]801 C = An * dAir * V; // shf & lhf common coefficient
802
803 // THERMAL CONDUCTIVITY (Sturm, 1997: J. Glaciology)
804 // calculate new K profile [W m-1 K-1]
805
806 // initialize conductivity
807 K= xNewZeroInit<IssmDouble>(m);
808
809 // for snow and firn (density < 910 kg m-3) (Sturn et al, 1997)
[22758]810 for(int i=0;i<m;i++) if(d[i]<dIce-Dtol) K[i] = 0.138 - 1.01E-3 * d[i] + 3.233E-6 * (pow(d[i],2));
[19554]811
812 // for ice (density >= 910 kg m-3)
[22758]813 for(int i=0;i<m;i++) if(d[i]>=dIce-Dtol) K[i] = 9.828 * exp(-5.7E-3*T[i]);
[19554]814
815 // THERMAL DIFFUSION COEFFICIENTS
[23189]816
[19554]817 // A discretization scheme which truncates the Taylor-Series expansion
818 // after the 3rd term is used. See Patankar 1980, Ch. 3&4
[23189]819
[19554]820 // discretized heat equation:
[23189]821
[24686]822 // Tp = (Au*Tuo+ Ad*Tdo+ (Ap-Au-Ad)Tpo+ S) / Ap
[23189]823
[19554]824 // where neighbor coefficients Au, Ap, & Ad are
[23189]825
[19554]826 // Au = [dz_u/2KP + dz_p/2KE]^-1
827 // Ad = [dz_d/2KP + dz_d/2KD]^-1
828 // Ap = d*CI*dz/Dt
829
830 // and u & d represent grid points up and down from the center grid point
[24686]831 // point p and o identifies previous time step values. S is a source term.
[19554]832
833 // u, d, and p conductivities
834 KU = xNew<IssmDouble>(m);
835 KD = xNew<IssmDouble>(m);
836 KP = xNew<IssmDouble>(m);
[23189]837
[24313]838 KU[0] = Delflag;
839 KD[m-1] = Delflag;
[19554]840 for(int i=1;i<m;i++) KU[i]= K[i-1];
841 for(int i=0;i<m-1;i++) KD[i] = K[i+1];
842 for(int i=0;i<m;i++) KP[i] = K[i];
843
844 // determine u, d & p cell widths
845 dzU = xNew<IssmDouble>(m);
846 dzD = xNew<IssmDouble>(m);
[24313]847 dzU[0]=Delflag;
848 dzD[m-1]=Delflag;
[23189]849
[19554]850 for(int i=1;i<m;i++) dzU[i]= dz[i-1];
851 for(int i=0;i<m-1;i++) dzD[i] = dz[i+1];
852
853 // determine minimum acceptable delta t (diffusion number > 1/2) [s]
[22758]854 // NS: 2.16.18 divided dt by scaling factor, default set to 1/11 for stability
[19554]855 dt=1e12;
[23394]856 for(int i=0;i<m;i++)dt = min(dt,CI * pow(dz[i],2) * d[i] / (3 * K[i]) * thermo_scaling);
[19554]857
858 // smallest possible even integer of 60 min where diffusion number > 1/2
859 // must go evenly into one hour or the data frequency if it is smaller
860
[19566]861 // all integer factors of the number of second in a day (86400 [s])
[19554]862 int f[45] = {1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30, 36, 40, 45, 48, 50, 60,
863 72, 75, 80, 90, 100, 120, 144, 150, 180, 200, 225, 240, 300, 360, 400, 450, 600, 720, 900, 1200, 1800, 3600};
864
865 // return the min integer factor that is < dt
[19566]866 max_fdt=f[0];
[19554]867 for(int i=0;i<45;i++){
[22758]868 if (f[i]<dt-Dtol)if(f[i]>=max_fdt-Dtol)max_fdt=f[i];
[19554]869 }
870 dt=max_fdt;
[22758]871
[19554]872 // determine mean (harmonic mean) of K/dz for u, d, & p
873 Au = xNew<IssmDouble>(m);
874 Ad = xNew<IssmDouble>(m);
875 Ap = xNew<IssmDouble>(m);
876 for(int i=0;i<m;i++){
[22758]877 Au[i] = pow((dzU[i]/2.0/KP[i] + dz[i]/2.0/KU[i]),-1.0);
878 Ad[i] = pow((dzD[i]/2.0/KP[i] + dz[i]/2.0/KD[i]),-1.0);
[19554]879 Ap[i] = (d[i]*dz[i]*CI)/dt;
880 }
[23189]881
[19554]882 // create "neighbor" coefficient matrix
883 Nu = xNew<IssmDouble>(m);
884 Nd = xNew<IssmDouble>(m);
885 Np = xNew<IssmDouble>(m);
886 for(int i=0;i<m;i++){
887 Nu[i] = Au[i] / Ap[i];
888 Nd[i] = Ad[i] / Ap[i];
[22758]889 Np[i]= 1.0 - Nu[i] - Nd[i];
[19554]890 }
[23189]891
[19554]892 // specify boundary conditions: constant flux at bottom
[22758]893 Nu[m-1] = 0.0;
894 Np[m-1] = 1.0;
[23189]895
[19554]896 // zero flux at surface
[22758]897 Np[0] = 1.0 - Nd[0];
[23189]898
[19554]899 // Create neighbor arrays for diffusion calculations instead of a tridiagonal matrix
[22758]900 Nu[0] = 0.0;
901 Nd[m-1] = 0.0;
[23189]902
[19554]903 /* RADIATIVE FLUXES*/
904
905 // energy supplied by shortwave radiation [J]
906 sw = xNew<IssmDouble>(m);
907 for(int i=0;i<m;i++) sw[i]= swf[i] * dt;
[23189]908
[19554]909 // temperature change due to SW
910 dT_sw = xNew<IssmDouble>(m);
911 for(int i=0;i<m;i++) dT_sw[i]= sw[i] / (CI * d[i] * dz[i]);
912
913 // Upward longwave radiation flux is calculated from the snow surface
914 // temperature which is set equal to the average temperature of the
915 // top grid cells.
916
917 // energy supplied by downward longwave radiation to the top grid cell [J]
918 dlw = dlwrf * dt;
919
920 // temperature change due to dlw_surf
921 dT_dlw = dlw / TCs;
922
923 // PREALLOCATE ARRAYS BEFORE LOOP FOR IMPROVED PERFORMANCE
[19582]924 T0 = xNewZeroInit<IssmDouble>(m+2);
[19554]925 Tu=xNew<IssmDouble>(m);
926 Td=xNew<IssmDouble>(m);
927
928 /* CALCULATE ENERGY SOURCES AND DIFFUSION FOR EVERY TIME STEP [dt]*/
[19613]929 for (IssmDouble i=1;i<=dt0;i+=dt){
[19554]930
931 // PART OF ENERGY CONSERVATION CHECK
932 // store initial temperature
933 //T_init = T;
[23189]934
[19554]935 // calculate temperature of snow surface (Ts)
936 // when incoming SW radition is allowed to penetrate the surface,
937 // the modeled energy balance becomes very sensitive to how Ts is
938 // calculated. The estimated enegy balance & melt are significanly
939 // less when Ts is taken as the mean of the x top grid cells.
[25836]940 if(m>1) Ts = (T[0] + T[1])/2.0;
941 else Ts = T[0];
[23394]942 Ts = min(CtoK,Ts); // don't allow Ts to exceed 273.15 K (0 degC)
[23189]943
[19554]944 //TURBULENT HEAT FLUX
[23189]945
[22758]946 // Monin-Obukhov Stability Correction
[19554]947 // Reference:
948 // Ohmura, A., 1982: Climate and Energy-Balance on the Arctic Tundra.
949 // Journal of Climatology, 2, 65-84.
950
951 // calculate the Bulk Richardson Number (Ri)
[22758]952 Ri = (2.0*9.81* (Vz - z0) * (Ta - Ts)) / ((Ta + Ts)* pow(V,2.0));
[25836]953 //Ri = (2.0*9.81*(Ta - Ts)) / ((Tz - z0)*(Ta + Ts)* pow(V/(Vz - z0),2.0));
[23189]954
[22758]955 // calculate Monin-Obukhov stability factors 'coefM' and 'coefH'
[23189]956
[25836]957 // do not allow Ri to exceed 0.16
958 // Ri = min(Ri, 0.19);
959 Ri = min(Ri, 0.16); //Ohmura, 1982
[19554]960
961 // calculate momentum 'coefM' stability factor
[22758]962 if (Ri > 0.0+Ttol){
[19554]963 // if stable
[22758]964 coefM = 1.0/(1.0-5.2*Ri);
[19554]965 }
966 else {
[22758]967 coefM =pow (1.0-18*Ri,-0.25);
[19554]968 }
[23189]969
[19554]970 // calculate heat/wind 'coef_H' stability factor
[22758]971 if (Ri <= -0.03+Ttol) coefH = coefM/1.3;
[19554]972 else coefH = coefM;
[23189]973
[19554]974 //// Sensible Heat
975 // calculate the sensible heat flux [W m-2](Patterson, 1998)
[22758]976 shf = C * CA * (Ta - Ts);
[19554]977
[22758]978 // adjust using Monin-Obukhov stability theory
[19554]979 shf = shf / (coefM * coefH);
980
981 //// Latent Heat
982 // determine if snow pack is melting & calcualte surface vapour pressure over ice or liquid water
[22758]983 if (Ts >= CtoK-Ttol){
984 L = LV; //for liquid water at 273.15 k to vapor
[19554]985
[22758]986 //for liquid surface (assume liquid on surface when Ts == 0 deg C)
987 // Wright (1997), US Meteorological Handbook from Murphy and Koop, 2005 Appendix A
988 eS = 611.21 * exp(17.502 * (Ts - CtoK) / (240.97 + Ts - CtoK));
989 }
990 else{
991 L = LS; // latent heat of sublimation
[23189]992
[19554]993 // for an ice surface Murphy and Koop, 2005 [Equation 7]
994 eS = exp(9.550426 - 5723.265/Ts + 3.53068 * log(Ts) - 0.00728332 * Ts);
995 }
[22758]996
[19554]997 // Latent heat flux [W m-2]
998 lhf = C * L * (eAir - eS) * 0.622 / pAir;
[22758]999
1000 // adjust using Monin-Obukhov stability theory (if lhf '+' then there is energy and mass gained at the surface,
[19554]1001 // if '-' then there is mass and energy loss at the surface.
1002 lhf = lhf / (coefM * coefH);
1003
1004 //mass loss (-)/acreation(+) due to evaporation/condensation [kg]
[22758]1005 EC_day = lhf * dts / L;
[19554]1006
1007 // temperature change due turbulent fluxes
1008 turb = (shf + lhf)* dt;
1009 dT_turb = turb / TCs;
1010
1011 // upward longwave contribution
[26744]1012 IssmDouble deltaULW=0.0;
1013 IssmDouble emissivity=1.0;
1014 //If user wants to set a upward long wave bias
1015 if(isdeltaLWup) deltaULW = dulwrfValue;
1016 //If user wants to directly set emissivity, or grain radius is larger than the
1017 // threshold, or eIdx is 2 and we have wet snow or ice, use prescribed emissivity
1018 if(eIdx==0 || (teThresh - re[0])<Gdntol || (eIdx==2 && z0>0.001)) emissivity = teValue;
1019 ulw = - (SB * pow(Ts,4.0)* emissivity + deltaULW) * dt;
[24313]1020 ulwrf = ulwrf - ulw/dt0;
1021
[19554]1022 dT_ulw = ulw / TCs;
[23189]1023
[19554]1024 // new grid point temperature
[23189]1025
[19554]1026 //SW penetrates surface
[25836]1027 if(!isconstrainsurfaceT) for(int j=0;j<m;j++) T[j] = T[j] + dT_sw[j];
1028 if(!isconstrainsurfaceT) T[0] = T[0] + dT_dlw + dT_ulw + dT_turb;
[23189]1029
[19554]1030 // temperature diffusion
[22758]1031 for(int j=0;j<m;j++) T0[1+j]=T[j];
[19566]1032 for(int j=0;j<m;j++) Tu[j] = T0[j];
1033 for(int j=0;j<m;j++) Td[j] = T0[2+j];
1034 for(int j=0;j<m;j++) T[j] = (Np[j] * T[j]) + (Nu[j] * Tu[j]) + (Nd[j] * Td[j]);
[23189]1035
[19554]1036 // calculate cumulative evaporation (+)/condensation(-)
[25836]1037 if(!isconstrainsurfaceT) EC = EC + (EC_day/dts)*dt;
[22758]1038
[19554]1039 /* CHECK FOR ENERGY (E) CONSERVATION [UNITS: J]
1040 //energy flux across lower boundary (energy supplied by underling ice)
1041 base_flux = Ad(-1)*(T_init()-T_init(-1)) * dt;
1042
1043 E_used = sum((T - T_init) * (d*dz*CI));
1044 E_sup = ((sum(swf) * dt) + dlw + ulw + turb + base_flux);
1045
1046 E_diff = E_used - E_sup;
1047
[25836]1048 if fabs(E_diff) > 1E-6 || isnan(E_diff)
[19554]1049 disp(T(1))
1050 _error_("energy not conserved in thermodynamics equations");
1051 */
1052 }
[22758]1053
[24686]1054 /*Free resources:*/
[19554]1055 xDelete<IssmDouble>(K);
1056 xDelete<IssmDouble>(KU);
1057 xDelete<IssmDouble>(KD);
1058 xDelete<IssmDouble>(KP);
1059 xDelete<IssmDouble>(Au);
1060 xDelete<IssmDouble>(Ad);
1061 xDelete<IssmDouble>(Ap);
1062 xDelete<IssmDouble>(Nu);
1063 xDelete<IssmDouble>(Nd);
1064 xDelete<IssmDouble>(Np);
1065 xDelete<IssmDouble>(dzU);
1066 xDelete<IssmDouble>(dzD);
1067 xDelete<IssmDouble>(sw);
1068 xDelete<IssmDouble>(dT_sw);
1069 xDelete<IssmDouble>(T0);
1070 xDelete<IssmDouble>(Tu);
1071 xDelete<IssmDouble>(Td);
1072
1073 /*Assign output pointers:*/
1074 *pEC=EC;
[22758]1075 *pT=T;
[24313]1076 *pulwrf=ulwrf;
[19554]1077
1078} /*}}}*/
[26744]1079void shortwave(IssmDouble** pswf, int swIdx, int aIdx, IssmDouble dsw, IssmDouble dswdiff, IssmDouble as, IssmDouble asdiff, IssmDouble* d, IssmDouble* dz, IssmDouble* re, IssmDouble dIce, int m, int sid){ /*{{{*/
[19554]1080
1081 // DISTRIBUTES ABSORBED SHORTWAVE RADIATION WITHIN SNOW/ICE
1082
1083 // swIdx = 0 : all absorbed SW energy is assigned to the top grid cell
1084
1085 // swIdx = 1 : absorbed SW is distributed with depth as a function of:
[26744]1086 // default : snow density (taken from Bassford, 2002)
[25836]1087 // if aIdx=2 : grain size in 3 spectral bands (Brun et al., 1992)
[19554]1088
1089 // Inputs
1090 // swIdx = shortwave allowed to penetrate surface (0 = No, 1 = Yes)
1091 // aIdx = method for calculating albedo (1-4)
1092 // dsw = downward shortwave radiative flux [w m-2]
[26744]1093 // dswdiff = downward shortwave diffuse radiative flux [w m-2]
[19554]1094 // as = surface albedo
[26744]1095 // asdiff = surface albedo for diffuse radiation
[19554]1096 // d = grid cell density [kg m-3]
1097 // dz = grid cell depth [m]
1098 // re = grid cell effective grain radius [mm]
1099
1100 // Outputs
1101 // swf = absorbed shortwave radiation [W m-2]
[19582]1102 //
[23189]1103
[19582]1104 /*outputs: */
1105 IssmDouble* swf=NULL;
[19554]1106
[19566]1107 if(VerboseSmb() && sid==0 && IssmComm::GetRank()==0)_printf0_(" shortwave module\n");
[19554]1108
[19582]1109 /*Initialize and allocate: */
1110 swf=xNewZeroInit<IssmDouble>(m);
1111
[19554]1112 // SHORTWAVE FUNCTION
1113 if (swIdx == 0) {// all sw radation is absorbed in by the top grid cell
[23189]1114
[19554]1115 // calculate surface shortwave radiation fluxes [W m-2]
[26744]1116 if (aIdx == 1){
1117 swf[0] = (1.0 - as) * max(0.0,(dsw - dswdiff)) + (1.0 - asdiff) * dswdiff;
1118 }
1119 else{
1120 swf[0] = (1.0 - as) * dsw;
1121 }
[19554]1122 }
1123 else{ // sw radation is absorbed at depth within the glacier
1124
1125 if (aIdx == 2){ // function of effective radius (3 spectral bands)
1126
1127 IssmDouble * gsz=NULL;
1128 IssmDouble * B1_cum=NULL;
1129 IssmDouble * B2_cum=NULL;
1130 IssmDouble* h =NULL;
1131 IssmDouble* B1 =NULL;
1132 IssmDouble* B2 =NULL;
1133 IssmDouble* exp1 = NULL;
1134 IssmDouble* exp2 = NULL;
1135 IssmDouble* Qs1 = NULL;
1136 IssmDouble* Qs2 = NULL;
1137
1138 // convert effective radius [mm] to grain size [m]
1139 gsz=xNew<IssmDouble>(m);
[22758]1140 for(int i=0;i<m;i++) gsz[i]= (re[i] * 2.0) / 1000.0;
[19554]1141
1142 // Spectral fractions [0.3-0.8um 0.8-1.5um 1.5-2.8um]
1143 // (Lefebre et al., 2003)
1144 IssmDouble sF[3] = {0.606, 0.301, 0.093};
1145
1146 // initialize variables
1147 B1_cum=xNew<IssmDouble>(m+1);
1148 B2_cum=xNew<IssmDouble>(m+1);
1149 for(int i=0;i<m+1;i++){
[22758]1150 B1_cum[i]=1.0;
1151 B2_cum[i]=1.0;
[19554]1152 }
1153
1154 // spectral albedos:
1155 // 0.3 - 0.8um
[23394]1156 IssmDouble a0 = min(0.98, 1.0 - 1.58 *pow(gsz[0],0.5));
[19554]1157 // 0.8 - 1.5um
[23394]1158 IssmDouble a1 = max(0.0, 0.95 - 15.4 *pow(gsz[0],0.5));
[19554]1159 // 1.5 - 2.8um
[23394]1160 IssmDouble a2 = max(0.127, 0.88 + 346.3*gsz[0] - 32.31*pow(gsz[0],0.5));
[19554]1161
1162 // separate net shortwave radiative flux into spectral ranges
1163 IssmDouble swfS[3];
[22758]1164 swfS[0] = (sF[0] * dsw) * (1.0 - a0);
1165 swfS[1] = (sF[1] * dsw) * (1.0 - a1);
1166 swfS[2] = (sF[2] * dsw) * (1.0 - a2);
[19554]1167
[26744]1168 // absorption coefficient for spectral range:
[19554]1169 h =xNew<IssmDouble>(m);
1170 B1 =xNew<IssmDouble>(m);
1171 B2 =xNew<IssmDouble>(m);
1172 for(int i=0;i<m;i++) h[i]= d[i] /(pow(gsz[i],0.5));
[22758]1173 for(int i=0;i<m;i++) B1[i] = 0.0192 * h[i]; // 0.3 - 0.8um
1174 for(int i=0;i<m;i++) B2[i]= 0.1098 * h[i]; // 0.8 - 1.5um
[19554]1175 // B3 = +inf // 1.5 - 2.8um
1176
1177 // cumulative extinction factors
1178 exp1 = xNew<IssmDouble>(m);
1179 exp2 = xNew<IssmDouble>(m);
1180 for(int i=0;i<m;i++) exp1[i]=exp(-B1[i]*dz[i]);
1181 for(int i=0;i<m;i++) exp2[i]=exp(-B2[i]*dz[i]);
1182
1183 for(int i=0;i<m;i++){
1184 IssmDouble cum1=exp1[0];
1185 IssmDouble cum2=exp2[0];
1186 for(int j=1;j<=i;j++){
1187 cum1 = cum1*exp1[j];
1188 cum2 = cum2*exp2[j];
1189 }
1190 B1_cum[i+1]=cum1;
1191 B2_cum[i+1]=cum2;
1192 }
1193
1194 // flux across grid cell boundaries
1195 Qs1 = xNew<IssmDouble>(m+1);
1196 Qs2 = xNew<IssmDouble>(m+1);
1197 for(int i=0;i<m+1;i++){
1198 Qs1[i] = swfS[0] * B1_cum[i];
1199 Qs2[i] = swfS[1] * B2_cum[i];
1200 }
1201
1202 // net energy flux to each grid cell
1203 for(int i=0;i<m;i++) swf[i]= (Qs1[i]-Qs1[i+1]) + (Qs2[i]-Qs2[i+1]);
1204
1205 // add flux absorbed at surface
1206 swf[0] = swf[0]+ swfS[2];
1207
[24686]1208 /*Free resources: */
[19554]1209 xDelete<IssmDouble>(gsz);
1210 xDelete<IssmDouble>(B1_cum);
1211 xDelete<IssmDouble>(B2_cum);
1212 xDelete<IssmDouble>(h);
1213 xDelete<IssmDouble>(B1);
1214 xDelete<IssmDouble>(B2);
1215 xDelete<IssmDouble>(exp1);
1216 xDelete<IssmDouble>(exp2);
1217 xDelete<IssmDouble>(Qs1);
1218 xDelete<IssmDouble>(Qs2);
[23189]1219
[19554]1220 }
1221 else{ //function of grid cell density
1222
1223 /*intermediary: */
1224 IssmDouble* B_cum = NULL;
1225 IssmDouble* exp_B = NULL;
1226 IssmDouble* Qs = NULL;
1227 IssmDouble* B = NULL;
1228
1229 // fraction of sw radiation absorbed in top grid cell (wavelength > 0.8um)
1230 IssmDouble SWs = 0.36;
1231
1232 // SWs and SWss coefficients need to be better constranted. Greuell
1233 // and Konzelmann 1994 used SWs = 0.36 and SWss = 0.64 as this the
[24686]1234 // the % of SW radiation with wavelengths > and < 800 nm
[19554]1235 // respectively. This, however, may not account for the fact that
1236 // the albedo of wavelengths > 800 nm has a much lower albedo.
1237
1238 // calculate surface shortwave radiation fluxes [W m-2]
[22758]1239 IssmDouble swf_s = SWs * (1.0 - as) * dsw;
[19554]1240
1241 // calculate surface shortwave radiation fluxes [W m-2]
[22758]1242 IssmDouble swf_ss = (1.0-SWs) * (1.0 - as) * dsw;
[19554]1243
1244 // SW allowed to penetrate into snowpack
[22758]1245 IssmDouble Bs = 10.0; // snow SW extinction coefficient [m-1] (Bassford,2006)
[19554]1246 IssmDouble Bi = 1.3; // ice SW extinction coefficient [m-1] (Bassford,2006)
1247
1248 // calculate extinction coefficient B [m-1] vector
1249 B=xNew<IssmDouble>(m);
[22758]1250 for(int i=0;i<m;i++) B[i] = Bs + (300.0 - d[i]) * ((Bs - Bi)/(dIce - 300.0));
[19554]1251
1252 // cumulative extinction factor
1253 B_cum = xNew<IssmDouble>(m+1);
1254 exp_B = xNew<IssmDouble>(m);
1255 for(int i=0;i<m;i++)exp_B[i]=exp(-B[i]*dz[i]);
1256
[22758]1257 B_cum[0]=1.0;
[19554]1258 for(int i=0;i<m;i++){
1259 IssmDouble cum_B=exp_B[0];
1260 for(int j=1;j<=i;j++) cum_B=cum_B*exp_B[j];
1261 B_cum[i+1]= cum_B;
1262 }
1263
1264 // flux across grid cell boundaries
1265 Qs=xNew<IssmDouble>(m+1);
1266 for(int i=0;i<m+1;i++) Qs[i] = swf_ss * B_cum[i];
1267
1268 // net energy flux to each grid cell
1269 for(int i=0;i<m;i++) swf[i] = (Qs[i]-Qs[i+1]);
1270
1271 // add flux absorbed at surface
[19566]1272 swf[0] += swf_s;
[19554]1273
[24686]1274 /*Free resources:*/
[19554]1275 xDelete<IssmDouble>(B_cum);
1276 xDelete<IssmDouble>(exp_B);
1277 xDelete<IssmDouble>(Qs);
1278 xDelete<IssmDouble>(B);
1279 }
1280 }
[19582]1281 /*Assign output pointers: */
1282 *pswf=swf;
1283
[19554]1284} /*}}}*/
[26744]1285void accumulation(IssmDouble** pT, IssmDouble** pdz, IssmDouble** pd, IssmDouble** pW, IssmDouble** pa, IssmDouble** padiff, IssmDouble** pre, IssmDouble** pgdn, IssmDouble** pgsp, IssmDouble* pRa, int* pm, int aIdx, int dsnowIdx, IssmDouble Tmean, IssmDouble T_air, IssmDouble P, IssmDouble dzMin, IssmDouble aSnow, IssmDouble C, IssmDouble V, IssmDouble Vmean, IssmDouble dIce, int sid){ /*{{{*/
[19554]1286
1287 // Adds precipitation and deposition to the model grid
1288
1289 // Author: Alex Gardner, University of Alberta
1290 // Date last modified: JAN, 2008
1291
1292 /* Description:
1293 adjusts the properties of the top grid cell to account for accumulation
1294 T_air & T = Air and top grid cell temperatures [K]
[24313]1295 Tmean = average surface temperature [K]
1296 Vmean = average wind velocity [m s-1]
1297 V = wind velocity [m s-1]
1298 C = average accumulation rate [kg m-2 yr-1]
[19554]1299 dz = topgrid cell length [m]
1300 d = density of top grid gell [kg m-3]
[26744]1301 P = precipitation [mm w.e.] or [kg m-3]
1302 Ra = rainfall [mm w.e.] or [kg m-3]
1303 re = effective grain radius [mm]
1304 gdn = grain dentricity
1305 gsp = grain sphericity*/
[19554]1306
1307 // MAIN FUNCTION
1308 // specify constants
[24313]1309 IssmDouble dSnow = 150; // density of snow [kg m-3]
1310 const IssmDouble reNew = 0.05; // new snow grain size [mm]
[22758]1311 const IssmDouble gdnNew = 1.0; // new snow dendricity
[19554]1312 const IssmDouble gspNew = 0.5; // new snow sphericity
1313
1314 /*intermediary: */
1315 IssmDouble* mInit=NULL;
1316 bool top=true;
[22758]1317 IssmDouble mass=0;
1318 IssmDouble massinit=0;
1319 IssmDouble mass_diff=0;
[19554]1320
1321 /*output: */
1322 IssmDouble* T=NULL;
1323 IssmDouble* dz=NULL;
1324 IssmDouble* d=NULL;
1325 IssmDouble* W=NULL;
1326 IssmDouble* a=NULL;
[26744]1327 IssmDouble* adiff=NULL;
[19554]1328 IssmDouble* re=NULL;
1329 IssmDouble* gdn=NULL;
1330 IssmDouble* gsp=NULL;
[26744]1331 IssmDouble ra=0.0;
[22758]1332 int m=0;
[19554]1333
[19566]1334 if(VerboseSmb() && sid==0 && IssmComm::GetRank()==0)_printf0_(" accumulation module\n");
1335
[19554]1336 /*Recover pointers: */
1337 T=*pT;
1338 dz=*pdz;
1339 d=*pd;
1340 W=*pW;
1341 a=*pa;
[26744]1342 adiff=*padiff;
[19554]1343 re=*pre;
1344 gdn=*pgdn;
1345 gsp=*pgsp;
1346 m=*pm;
[26744]1347 ra=*pRa;
[19554]1348
[24313]1349 //Density of fresh snow [kg m-3]
1350 switch (dsnowIdx){
1351 case 0: // Default value defined above
1352 break;
1353
1354 case 1: // Density of Antarctica snow
1355 dSnow = 350;
[25836]1356 //dSnow = 360; //FirnMICE Lundin et al., 2017
[24313]1357 break;
1358
[26744]1359 case 2: // Density of Greenland snow, Fausto et al., 2018
[24313]1360 dSnow = 315;
1361 break;
1362
1363 case 3: //Surface snow accumulation density from Kaspers et al., 2004, Antarctica
1364 //dSnow = alpha1 + beta1*T + delta1*C + epsilon1*W
1365 // 7.36x10-2 1.06x10-3 6.69x10-2 4.77x10-3
1366 dSnow=(7.36e-2 + 1.06e-3*min(Tmean,CtoK-Ttol) + 6.69e-2*C/1000 + 4.77e-3*Vmean)*1000;
1367 break;
1368
1369 case 4: // Kuipers Munneke and others (2015), Greenland
1370 dSnow = 481.0 + 4.834*(Tmean-CtoK);
1371 break;
1372 }
1373
[19582]1374 // determine initial mass
[19554]1375 mInit=xNew<IssmDouble>(m);
[19582]1376 for(int i=0;i<m;i++) mInit[i]= d[i] * dz[i];
[22758]1377 massinit=0.0;
1378 for(int i=0;i<m;i++)massinit+=mInit[i];
[19554]1379
[24313]1380 if (P > 0.0+Ptol){
[19554]1381
[22758]1382 if (T_air <= CtoK+Ttol){ // if snow
[19554]1383
1384 IssmDouble z_snow = P/dSnow; // depth of snow
[24313]1385 IssmDouble dfall = gdnNew;
1386 IssmDouble sfall = gspNew;
1387 IssmDouble refall = reNew;
[19554]1388
[24313]1389 //From Vionnet et al., 2012 (Crocus)
1390 dfall = min(max(1.29 - 0.17*V,0.20),1.0);
1391 sfall = min(max(0.08*V + 0.38,0.5),0.9);
1392 refall = max((0.1 + (1.0-dfall)*0.25 + (0.5-sfall)*0.1)/2.0,Gdntol);
1393
[19554]1394 // if snow depth is greater than specified min dz, new cell created
[22758]1395 if (z_snow > dzMin+Dtol){
[19554]1396
1397 newcell(&T,T_air,top,m); //new cell T
1398 newcell(&dz,z_snow,top,m); //new cell dz
1399 newcell(&d,dSnow,top,m); //new cell d
[22758]1400 newcell(&W,0.0,top,m); //new cell W
[19554]1401 newcell(&a,aSnow,top,m); //new cell a
[26744]1402 newcell(&adiff,aSnow,top,m); //new cell a
[24313]1403 newcell(&re,refall,top,m); //new cell grain size
1404 newcell(&gdn,dfall,top,m); //new cell grain dendricity
1405 newcell(&gsp,sfall,top,m); //new cell grain sphericity
[19554]1406 m=m+1;
1407 }
1408 else { // if snow depth is less than specified minimum dz snow
1409
[24313]1410 mass = mInit[0] + P; // grid cell adjust mass
[19554]1411
1412 dz[0] = dz[0] + P/dSnow; // adjust grid cell depth
1413 d[0] = mass / dz[0]; // adjust grid cell density
1414
1415 // adjust variables as a linearly weighted function of mass
1416 // adjust temperature (assume P is same temp as air)
1417 T[0] = (T_air * P + T[0] * mInit[0])/mass;
1418
1419 // adjust a, re, gdn & gsp
[22758]1420 if(aIdx>0)a[0] = (aSnow * P + a[0] * mInit[0])/mass;
[24313]1421 gdn[0] = (dfall * P + gdn[0] * mInit[0])/mass;
1422 gsp[0] = (sfall * P + gsp[0] * mInit[0])/mass;
1423 re[0] = max((0.1 + (1.0-gdn[0])*0.25 + (0.5-gsp[0])*0.1)/2.0,Gdntol);
[19554]1424 }
1425 }
1426 else{ // if rain
1427
1428 /*rain is added by increasing the mass and temperature of the ice
1429 of the top grid cell. Temperatures are set artifically high to
1430 account for the latent heat of fusion. This is the same as
1431 directly adding liquid water to the the snow pack surface but
1432 makes the numerics easier.*/
1433
1434 // grid cell adjust mass
1435 mass = mInit[0] + P;
1436
1437 // adjust temperature
1438 // liquid: must account for latent heat of fusion
1439 T[0] = (P *(T_air + LF/CI) + T[0] * mInit[0]) / mass;
1440
1441 // adjust grid cell density
1442 d[0] = mass / dz[0];
1443
1444 // if d > the density of ice, d = dIce
[24313]1445 if (d[0] > dIce-Dtol){
[19554]1446 d[0] = dIce; // adjust d
1447 dz[0] = mass / d[0]; // dz is adjusted to conserve mass
1448 }
[26744]1449
1450 ra=P;
[19554]1451 }
1452
1453 // check for conservation of mass
1454 mass=0; for(int i=0;i<m;i++)mass+=d[i]*dz[i];
1455
1456 mass_diff = mass - massinit - P;
[23189]1457
[23394]1458 #ifndef _HAVE_AD_ //avoid round operation. only check in forward mode.
[22758]1459 mass_diff = round(mass_diff * 100.0)/100.0;
[19554]1460 if (mass_diff > 0) _error_("mass not conserved in accumulation function");
[19613]1461 #endif
[19554]1462
1463 }
[24686]1464 /*Free resources:*/
[19554]1465 if(mInit)xDelete<IssmDouble>(mInit);
1466
1467 /*Assign output pointers:*/
1468 *pT=T;
1469 *pdz=dz;
1470 *pd=d;
1471 *pW=W;
1472 *pa=a;
[26744]1473 *padiff=adiff;
[19554]1474 *pre=re;
1475 *pgdn=gdn;
1476 *pgsp=gsp;
1477 *pm=m;
[26744]1478 *pRa=ra;
[19554]1479} /*}}}*/
[26744]1480void melt(IssmDouble* pM, IssmDouble* pMs, IssmDouble* pR, IssmDouble* pF, IssmDouble* pmAdd, IssmDouble* pdz_add, IssmDouble** pT, IssmDouble** pd, IssmDouble** pdz, IssmDouble** pW, IssmDouble** pa, IssmDouble** padiff, IssmDouble** pre, IssmDouble** pgdn, IssmDouble** pgsp, int* pn, IssmDouble Ra, IssmDouble dzMin, IssmDouble zMax, IssmDouble zMin, IssmDouble zTop, IssmDouble zY, IssmDouble dIce, int sid){ /*{{{*/
[19554]1481
1482 //// MELT ROUTINE
1483
1484 // Description:
1485 // computes the quantity of meltwater due to snow temperature in excess of
1486 // 0 deg C, determines pore water content and adjusts grid spacing
1487
1488 /*intermediary:*/
1489 IssmDouble* m=NULL;
1490 IssmDouble* maxF=NULL;
1491 IssmDouble* dW=NULL;
1492 IssmDouble* exsW=NULL;
1493 IssmDouble* exsT=NULL;
1494 IssmDouble* surpT=NULL;
1495 IssmDouble* surpE=NULL;
1496 IssmDouble* flxDn=NULL;
[22758]1497 IssmDouble ER=0.0;
[19554]1498 IssmDouble* EI=NULL;
1499 IssmDouble* EW=NULL;
1500 IssmDouble* M=NULL;
[26744]1501 int* D=NULL;
[23189]1502
[22758]1503 IssmDouble sumM=0.0;
1504 IssmDouble sumER=0.0;
1505 IssmDouble addE=0.0;
1506 IssmDouble mSum0=0.0;
1507 IssmDouble sumE0=0.0;
1508 IssmDouble mSum1=0.0;
1509 IssmDouble sumE1=0.0;
1510 IssmDouble dE=0.0;
1511 IssmDouble dm=0.0;
1512 IssmDouble X=0.0;
1513 IssmDouble Wi=0.0;
[23189]1514
[22758]1515 IssmDouble Ztot=0.0;
1516 IssmDouble T_bot=0.0;
1517 IssmDouble m_bot=0.0;
1518 IssmDouble dz_bot=0.0;
1519 IssmDouble d_bot=0.0;
1520 IssmDouble W_bot=0.0;
1521 IssmDouble a_bot=0.0;
[26744]1522 IssmDouble adiff_bot=0.0;
[22758]1523 IssmDouble re_bot=0.0;
1524 IssmDouble gdn_bot=0.0;
1525 IssmDouble gsp_bot=0.0;
1526 IssmDouble EI_bot=0.0;
1527 IssmDouble EW_bot=0.0;
[21341]1528 bool top=false;
[23189]1529
[21341]1530 IssmDouble* Zcum=NULL;
1531 IssmDouble* dzMin2=NULL;
[25836]1532 IssmDouble* dzMax2=NULL;
1533 IssmDouble zY2=zY;
[22758]1534 bool lastCellFlag = false;
[21341]1535 int X1=0;
1536 int X2=0;
[23189]1537
[22758]1538 int D_size = 0;
[19554]1539
1540 /*outputs:*/
[25836]1541 IssmDouble Msurf = 0.0;
[22758]1542 IssmDouble mAdd = 0.0;
[24686]1543 IssmDouble surplusE = 0.0;
1544 IssmDouble surplusT = 0.0;
[26744]1545 IssmDouble dz_add = 0.0;
[22758]1546 IssmDouble Rsum = 0.0;
[26744]1547 IssmDouble Fsum = 0.0;
[19554]1548 IssmDouble* T=*pT;
1549 IssmDouble* d=*pd;
1550 IssmDouble* dz=*pdz;
1551 IssmDouble* W=*pW;
1552 IssmDouble* a=*pa;
[26744]1553 IssmDouble* adiff=*padiff;
[19554]1554 IssmDouble* re=*pre;
1555 IssmDouble* gdn=*pgdn;
1556 IssmDouble* gsp=*pgsp;
1557 int n=*pn;
[22758]1558 IssmDouble* R=NULL;
[23189]1559
[19566]1560 if(VerboseSmb() && sid==0 && IssmComm::GetRank()==0)_printf0_(" melt module\n");
1561
[19554]1562 //// INITIALIZATION
1563
1564 /*Allocations: */
1565 M=xNewZeroInit<IssmDouble>(n);
[22758]1566 maxF=xNewZeroInit<IssmDouble>(n);
1567 dW=xNewZeroInit<IssmDouble>(n);
[19554]1568
1569 // store initial mass [kg] and energy [J]
1570 m=xNew<IssmDouble>(n); for(int i=0;i<n;i++) m[i] = dz[i]* d[i]; // grid cell mass [kg]
1571 EI=xNew<IssmDouble>(n); for(int i=0;i<n;i++)EI[i] = m[i] * T[i] * CI; // initial enegy of snow/ice
1572 EW=xNew<IssmDouble>(n); for(int i=0;i<n;i++)EW[i]= W[i] * (LF + CtoK * CI); // initial enegy of water
1573
1574 mSum0 = cellsum(W,n) + cellsum(m,n); // total mass [kg]
1575 sumE0 = cellsum(EI,n) + cellsum(EW,n); // total energy [J]
1576
1577 // initialize melt and runoff scalars
[22758]1578 Rsum = 0.0; // runoff [kg]
[26744]1579 Fsum = 0.0; // refreeze [kg]
[22758]1580 sumM = 0.0; // total melt [kg]
1581 mAdd = 0.0; // mass added/removed to/from base of model [kg]
1582 addE = 0.0; // energy added/removed to/from base of model [J]
1583 dz_add=0.0; // thickness of the layer added/removed to/from base of model [m]
[19554]1584
[19582]1585 // calculate temperature excess above 0 deg C
[19554]1586 exsT=xNewZeroInit<IssmDouble>(n);
[23394]1587 for(int i=0;i<n;i++) exsT[i]= max(0.0, T[i] - CtoK); // [K] to [degC]
[19554]1588
1589 // new grid point center temperature, T [K]
[22758]1590 // for(int i=0;i<n;i++) T[i]-=exsT[i];
[23394]1591 for(int i=0;i<n;i++) T[i]=min(T[i],CtoK);
[23189]1592
[19554]1593 // specify irreducible water content saturation [fraction]
1594 const IssmDouble Swi = 0.07; // assumed constant after Colbeck, 1974
[25836]1595 const IssmDouble dPHC = 830; //Pore closeoff density
[19554]1596
1597 //// REFREEZE PORE WATER
1598 // check if any pore water
[22758]1599 if (cellsum(W,n) >0.0+Wtol){
[19582]1600 if(VerboseSmb() && sid==0 && IssmComm::GetRank()==0)_printf0_(" pore water refreeze\n");
[19554]1601 // calculate maximum freeze amount, maxF [kg]
[23394]1602 for(int i=0;i<n;i++) maxF[i] = max(0.0, -((T[i] - CtoK) * m[i] * CI) / LF);
[19554]1603
1604 // freeze pore water and change snow/ice properties
[23394]1605 for(int i=0;i<n;i++) dW[i] = min(maxF[i], W[i]); // freeze mass [kg]
[19554]1606 for(int i=0;i<n;i++) W[i] -= dW[i]; // pore water mass [kg]
1607 for(int i=0;i<n;i++) m[i] += dW[i]; // new mass [kg]
1608 for(int i=0;i<n;i++) d[i] = m[i] / dz[i]; // density [kg m-3]
[24313]1609 for(int i=0;i<n;i++) if(m[i]>Wtol) T[i] = T[i] + (dW[i]*(LF+(CtoK - T[i])*CI)/(m[i]*CI)); // temperature [K]
[19554]1610
1611 // if pore water froze in ice then adjust d and dz thickness
[24313]1612 for(int i=0;i<n;i++)if(d[i]> dIce-Dtol)d[i]=dIce;
[19582]1613 for(int i=0;i<n;i++) dz[i]= m[i]/d[i];
[22758]1614
[19554]1615 }
1616
1617 // squeeze water from snow pack
[19582]1618 exsW=xNew<IssmDouble>(n);
1619 for(int i=0;i<n;i++){
[22758]1620 Wi= (dIce - d[i]) * Swi * (m[i] / d[i]); // irreducible water content [kg]
[23394]1621 exsW[i] = max(0.0, W[i] - Wi); // water "squeezed" from snow [kg]
[19554]1622 }
1623
1624 //// MELT, PERCOLATION AND REFREEZE
1625
[26744]1626 // initialize refreeze, runoff, flxDn and dW vectors [kg]
1627 IssmDouble* F = xNewZeroInit<IssmDouble>(n);
1628
1629 // Add previous refreeze to F and reset dW
1630 for(int i=0;i<n;i++){
1631 F[i]=F[i]+dW[i];
1632 dW[i] = 0.0;
1633 }
1634
[19554]1635 // run melt algorithm if there is melt water or excess pore water
[24313]1636 if ((cellsum(exsT,n) > 0.0+Ttol) || (cellsum(exsW,n) > 0.0+Wtol)){
[19554]1637 // _printf_(""MELT OCCURS");
1638 // check to see if thermal energy exceeds energy to melt entire cell
1639 // if so redistribute temperature to lower cells (temperature surplus)
1640 // (maximum T of snow before entire grid cell melts is a constant
1641 // LF/CI = 159.1342)
[23394]1642 surpT=xNew<IssmDouble>(n); for(int i=0;i<n;i++)surpT[i] = max(0.0, exsT[i]- LF/CI);
[22758]1643
[24313]1644 if (cellsum(surpT,n) > 0.0+Ttol ){
[19554]1645 // _printf_("T Surplus");
1646 // calculate surplus energy
[21341]1647 surpE=xNew<IssmDouble>(n); for(int i=0;i<n;i++)surpE[i] = surpT[i] * CI * m[i];
[22758]1648
[19554]1649 int i = 0;
[24313]1650 while (cellsum(surpE,n) > 0.0+Ttol && i<n){
[22758]1651
[24686]1652 if (i<n-1){
1653 // use surplus energy to increase the temperature of lower cell
1654 T[i+1] = surpE[i]/m[i+1]/CI + T[i+1];
[22758]1655
[24686]1656 exsT[i+1] = max(0.0, T[i+1] - CtoK) + exsT[i+1];
1657 T[i+1] = min(CtoK, T[i+1]);
[22758]1658
[24686]1659 surpT[i+1] = max(0.0, exsT[i+1] - LF/CI);
1660 surpE[i+1] = surpT[i+1] * CI * m[i+1];
1661 }
1662 else{
1663 surplusT=max(0.0, exsT[i] - LF/CI);
1664 surplusE=surpE[i];
1665 if(VerboseSmb() && sid==0 && IssmComm::GetRank()==0){
1666 _printf0_(" WARNING: surplus energy at the base of GEMB column\n");
1667 }
1668 }
1669
[19554]1670 // adjust current cell properties (again 159.1342 is the max T)
[22758]1671 exsT[i] = LF/CI;
1672 surpE[i] = 0.0;
[19554]1673 i = i + 1;
1674 }
1675 }
1676
1677 // convert temperature excess to melt [kg]
[24313]1678 IssmDouble Mmax=0.0;
1679 for(int i=0;i<n;i++){
1680 Mmax=exsT[i] * d[i] * dz[i] * CI / LF;
1681 M[i] = min(Mmax, m[i]); // melt
1682 }
[25836]1683 Msurf = M[0];
[26744]1684 sumM = max(0.0,cellsum(M,n)-Ra); // total melt [kg] minus the liquid rain that had been added
[19554]1685
1686 // calculate maximum refreeze amount, maxF [kg]
[23394]1687 for(int i=0;i<n;i++)maxF[i] = max(0.0, -((T[i] - CtoK) * d[i] * dz[i] * CI)/ LF);
[19554]1688
1689 // initialize refreeze, runoff, flxDn and dW vectors [kg]
[22758]1690 IssmDouble* R = xNewZeroInit<IssmDouble>(n);
[19582]1691
[22758]1692 flxDn=xNewZeroInit<IssmDouble>(n+1);
[19554]1693
1694 // determine the deepest grid cell where melt/pore water is generated
1695 X = 0;
1696 for(int i=n-1;i>=0;i--){
[24313]1697 if(M[i]> 0.0+Wtol || exsW[i]> 0.0+Wtol){
[19554]1698 X=i;
1699 break;
1700 }
1701 }
1702
[25836]1703 IssmDouble depthice=0.0;
[26744]1704 int Xi=0;
[19554]1705 //// meltwater percolation
1706 for(int i=0;i<n;i++){
1707 // calculate total melt water entering cell
1708 IssmDouble inM = M[i]+ flxDn[i];
1709
[25836]1710 depthice=0.0;
1711 if (d[i] >= dPHC-Dtol){
1712 for(int l=i;(l<n && d[l]>=dPHC-Dtol);l++) depthice=depthice+dz[l];
1713 }
1714
[19554]1715 // break loop if there is no meltwater and if depth is > mw_depth
[24313]1716 if (fabs(inM) < Wtol && i > X){
[19554]1717 break;
1718 }
1719 // if reaches impermeable ice layer all liquid water runs off (R)
[25836]1720 else if (d[i] >= dIce-Dtol || (d[i] >= dPHC-Dtol && depthice>0.1)){ // dPHC = pore hole close off [kg m-3]
[19554]1721 // _printf_("ICE LAYER");
1722 // no water freezes in this cell
1723 // no water percolates to lower cell
1724 // cell ice temperature & density do not change
1725
1726 m[i] = m[i] - M[i]; // mass after melt
[22758]1727 Wi = (dIce-d[i]) * Swi * (m[i]/d[i]); // irreducible water
[23394]1728 dW[i] = max(min(inM, Wi - W[i]),-1*W[i]); // change in pore water
1729 R[i] = max(0.0, inM - dW[i]); // runoff
[19554]1730 }
[21341]1731 // check if no energy to refreeze meltwater
[22758]1732 else if (fabs(maxF[i]) < Dtol){
[19554]1733 // _printf_("REFREEZE == 0");
1734 // no water freezes in this cell
1735 // cell ice temperature & density do not change
1736
1737 m[i] = m[i] - M[i]; // mass after melt
[22758]1738 Wi = (dIce-d[i]) * Swi * (m[i]/d[i]); // irreducible water
[23394]1739 dW[i] = max(min(inM, Wi - W[i]),-1*W[i]); // change in pore water
1740 flxDn[i+1] = max(0.0, inM - dW[i]); // meltwater out
[22758]1741 R[i] = 0.0;
[19554]1742 }
1743 // some or all meltwater refreezes
1744 else{
[24313]1745 // change in density and temperature
[19554]1746 // _printf_("MELT REFREEZE");
1747 //-----------------------melt water-----------------------------
[22758]1748 m[i] = m[i] - M[i];
[19554]1749 IssmDouble dz_0 = m[i]/d[i];
1750 IssmDouble dMax = (dIce - d[i])*dz_0; // d max = dIce
[23394]1751 IssmDouble F1 = min(min(inM,dMax),maxF[i]); // maximum refreeze
[19554]1752 m[i] = m[i] + F1; // mass after refreeze
1753 d[i] = m[i]/dz_0;
1754
1755 //-----------------------pore water-----------------------------
[22758]1756 Wi = (dIce-d[i])* Swi * dz_0; // irreducible water
[26744]1757 dW[i] = max(min(inM - F1, Wi-W[i]),-1*W[i]); // change in pore water
[22758]1758 IssmDouble F2 = 0.0;
[19554]1759
[22758]1760 if (dW[i] < 0.0-Wtol){ // excess pore water
[19554]1761 dMax = (dIce - d[i])*dz_0; // maximum refreeze
[23394]1762 IssmDouble maxF2 = min(dMax, maxF[i]-F1); // maximum refreeze
1763 F2 = min(-1*dW[i], maxF2); // pore water refreeze
[19554]1764 m[i] = m[i] + F2; // mass after refreeze
1765 d[i] = m[i]/dz_0;
1766 }
1767
[26744]1768 F[i] = F[i] + F1 + F2;
[22758]1769
[26744]1770 flxDn[i+1] = max(0.0,inM - F1 - dW[i]); // meltwater out
[24313]1771 if (m[i]>Wtol){
1772 T[i] = T[i] + ((F1+F2)*(LF+(CtoK - T[i])*CI)/(m[i]*CI));// change in temperature
1773 }
[19554]1774
1775 // check if an ice layer forms
[22758]1776 if (fabs(d[i] - dIce) < Dtol){
[19554]1777 // _printf_("ICE LAYER FORMS");
1778 // excess water runs off
1779 R[i] = flxDn[i+1];
1780 // no water percolates to lower cell
[22758]1781 flxDn[i+1] = 0.0;
[19554]1782 }
1783 }
[26744]1784 Xi=Xi+1;
[19554]1785 }
1786
1787 //// GRID CELL SPACING AND MODEL DEPTH
[24313]1788 for(int i=0;i<n;i++)if (W[i] < 0.0-Wtol) _error_("negative pore water generated in melt equations");
[23189]1789
[19554]1790 // delete all cells with zero mass
1791 // adjust pore water
1792 for(int i=0;i<n;i++)W[i] += dW[i];
1793
[22758]1794 //calculate Rsum:
[26744]1795 Rsum=cellsum(R,n) + flxDn[Xi];
[22758]1796
[19554]1797 // delete all cells with zero mass
[24313]1798 D_size=0; for(int i=0;i<n;i++)if(m[i]> (0.0+Wtol))D_size++;
[22758]1799 D=xNew<int>(D_size);
[24313]1800 D_size=0; for(int i=0;i<n;i++)if(m[i]> (0.0+Wtol)){ D[D_size] = i; D_size++;}
[22758]1801
[19554]1802 celldelete(&m,n,D,D_size);
1803 celldelete(&W,n,D,D_size);
1804 celldelete(&d,n,D,D_size);
[25836]1805 celldelete(&dz,n,D,D_size);
[19554]1806 celldelete(&T,n,D,D_size);
1807 celldelete(&a,n,D,D_size);
[26744]1808 celldelete(&adiff,n,D,D_size);
[19554]1809 celldelete(&re,n,D,D_size);
1810 celldelete(&gdn,n,D,D_size);
1811 celldelete(&gsp,n,D,D_size);
[19582]1812 celldelete(&EI,n,D,D_size);
1813 celldelete(&EW,n,D,D_size);
[19554]1814 n=D_size;
[19613]1815 xDelete<int>(D);
[23189]1816
[19554]1817 // calculate new grid lengths
1818 for(int i=0;i<n;i++)dz[i] = m[i] / d[i];
[19582]1819
[24686]1820 /*Free resources:*/
[19613]1821 xDelete<IssmDouble>(R);
[19554]1822 }
[22758]1823
[26744]1824 //calculate Fsum:
1825 Fsum=cellsum(F,n);
1826
1827 /*Free resources:*/
1828 xDelete<IssmDouble>(F);
1829
[21341]1830 //Merging of cells as they are burried under snow.
1831 Zcum=xNew<IssmDouble>(n);
1832 dzMin2=xNew<IssmDouble>(n);
[25836]1833 dzMax2=xNew<IssmDouble>(n);
[23189]1834
[25836]1835 X=0;
[21341]1836 Zcum[0]=dz[0]; // Compute a cumulative depth vector
1837 for (int i=0;i<n;i++){
[25836]1838 if (i==0){
[21341]1839 dzMin2[i]=dzMin;
1840 }
1841 else{
[25836]1842 Zcum[i]=Zcum[i-1]+dz[i];
1843 if (Zcum[i]<=zTop+Dtol){
1844 dzMin2[i]=dzMin;
1845 X=i;
1846 }
1847 else{
1848 dzMin2[i]=zY2*dzMin2[i-1];
1849 }
[21341]1850 }
1851 }
[19554]1852
[25836]1853 // Check to see if any cells are too small and need to be merged
1854 for (int i=0; i<n; i++){
1855 if ( (i<=X && dz[i]<dzMin-Dtol) || (i>X && dz[i]<dzMin2[i]-Dtol) ) {
[23189]1856
[25836]1857 if (i==n-1){
1858 X2=i;
1859 //find closest cell to merge with
1860 for(int j=n-2;j>=0;j--){
1861 if(m[j]!=Delflag){
1862 X1=j;
1863 break;
1864 }
1865 }
1866 }
1867 else{
1868 X1=i+1;
1869 X2=i;
1870 }
[19554]1871
1872 // adjust variables as a linearly weighted function of mass
[25836]1873 IssmDouble m_new = m[X2] + m[X1];
1874 T[X1] = (T[X2]*m[X2] + T[X1]*m[X1]) / m_new;
1875 a[X1] = (a[X2]*m[X2] + a[X1]*m[X1]) / m_new;
[26744]1876 adiff[X1] = (adiff[X2]*m[X2] + adiff[X1]*m[X1]) / m_new;
[25836]1877 re[X1] = (re[X2]*m[X2] + re[X1]*m[X1]) / m_new;
1878 gdn[X1] = (gdn[X2]*m[X2] + gdn[X1]*m[X1]) / m_new;
1879 gsp[X1] = (gsp[X2]*m[X2] + gsp[X1]*m[X1]) / m_new;
[23189]1880
[19554]1881 // merge with underlying grid cell and delete old cell
[25836]1882 dz[X1] = dz[X2] + dz[X1]; // combine cell depths
1883 d[X1] = m_new / dz[X1]; // combine top densities
1884 W[X1] = W[X1] + W[X2]; // combine liquid water
1885 m[X1] = m_new; // combine top masses
[23189]1886
[24686]1887 // set cell to -99999 for deletion
[25836]1888 m[X2] = Delflag;
[19554]1889 }
1890 }
1891
1892 // delete combined cells
[24313]1893 D_size=0; for(int i=0;i<n;i++)if(m[i]> Delflag+Wtol)D_size++;
[22758]1894 D=xNew<int>(D_size);
[24313]1895 D_size=0; for(int i=0;i<n;i++)if(m[i]> Delflag+Wtol){ D[D_size] = i; D_size++;}
[19554]1896
[19582]1897 celldelete(&m,n,D,D_size);
1898 celldelete(&W,n,D,D_size);
1899 celldelete(&dz,n,D,D_size);
1900 celldelete(&d,n,D,D_size);
1901 celldelete(&T,n,D,D_size);
1902 celldelete(&a,n,D,D_size);
[26744]1903 celldelete(&adiff,n,D,D_size);
[19582]1904 celldelete(&re,n,D,D_size);
1905 celldelete(&gdn,n,D,D_size);
1906 celldelete(&gsp,n,D,D_size);
1907 celldelete(&EI,n,D,D_size);
1908 celldelete(&EW,n,D,D_size);
1909 n=D_size;
[19613]1910 xDelete<int>(D);
[23189]1911
[25836]1912 // check if any of the cell depths are too large
[19582]1913 X=0;
[25836]1914 Zcum[0]=dz[0]; // Compute a cumulative depth vector
1915 for (int i=0;i<n;i++){
1916 if (i==0){
1917 dzMax2[i]=dzMin*2;
[19582]1918 }
[25836]1919 else{
1920 Zcum[i]=Zcum[i-1]+dz[i];
1921 if (Zcum[i]<=zTop+Dtol){
1922 dzMax2[i]=dzMin*2;
1923 X=i;
1924 }
1925 else{
1926 dzMax2[i]=max(zY2*dzMin2[i-1],dzMin*2);
1927 }
1928 }
[19582]1929 }
[23189]1930
[25836]1931 for (int j=n-1;j>=0;j--){
1932 if ((j<X && dz[j] > dzMax2[j]+Dtol) || (dz[j] > dzMax2[j]*zY2+Dtol)){
[24686]1933 // _printf_("dz > dzMin * 2");
1934 // split in two
1935 cellsplit(&dz, n, j,.5);
1936 cellsplit(&W, n, j,.5);
1937 cellsplit(&m, n, j,.5);
1938 cellsplit(&T, n, j,1.0);
1939 cellsplit(&d, n, j,1.0);
1940 cellsplit(&a, n, j,1.0);
[26744]1941 cellsplit(&adiff, n, j,1.0);
[24686]1942 cellsplit(&EI, n, j,.5);
1943 cellsplit(&EW, n, j,.5);
1944 cellsplit(&re, n, j,1.0);
1945 cellsplit(&gdn, n, j,1.0);
1946 cellsplit(&gsp, n, j,1.0);
1947 n++;
[19582]1948 }
1949 }
1950
[19554]1951 //// CORRECT FOR TOTAL MODEL DEPTH
1952 // WORKS FINE BUT HAS BEEN DISABLED FOR CONVIENCE OF MODEL OUTPUT
1953 // INTERPRETATION
[22758]1954 // calculate total model depth
[21341]1955 Ztot = cellsum(dz,n);
[23189]1956
[22758]1957 if (Ztot < zMin-Dtol){
[21341]1958 // printf("Total depth < zMin %f \n", Ztot);
1959 // mass and energy to be added
1960 mAdd = m[n-1]+W[n-1];
[22758]1961 addE = T[n-1]*m[n-1]*CI + W[n-1]*(LF+CtoK*CI);
[23189]1962
[21341]1963 // add a grid cell of the same size and temperature to the bottom
1964 dz_bot=dz[n-1];
1965 T_bot=T[n-1];
1966 W_bot=W[n-1];
1967 m_bot=m[n-1];
1968 d_bot=d[n-1];
1969 a_bot=a[n-1];
[26744]1970 adiff_bot=adiff[n-1];
[21341]1971 re_bot=re[n-1];
1972 gdn_bot=gdn[n-1];
1973 gsp_bot=gsp[n-1];
[22758]1974 EI_bot=EI[n-1];
1975 EW_bot=EW[n-1];
[23189]1976
[21341]1977 dz_add=dz_bot;
[23189]1978
[21341]1979 newcell(&dz,dz_bot,top,n);
1980 newcell(&T,T_bot,top,n);
1981 newcell(&W,W_bot,top,n);
1982 newcell(&m,m_bot,top,n);
1983 newcell(&d,d_bot,top,n);
1984 newcell(&a,a_bot,top,n);
[26744]1985 newcell(&adiff,adiff_bot,top,n);
[21341]1986 newcell(&re,re_bot,top,n);
1987 newcell(&gdn,gdn_bot,top,n);
1988 newcell(&gsp,gsp_bot,top,n);
[22758]1989 newcell(&EI,EI_bot,top,n);
1990 newcell(&EW,EW_bot,top,n);
[21341]1991 n=n+1;
1992 }
[22758]1993 else if (Ztot > zMax+Dtol){
[21341]1994 // printf("Total depth > zMax %f \n", Ztot);
1995 // mass and energy loss
1996 mAdd = -(m[n-1]+W[n-1]);
[22758]1997 addE = -(T[n-1]*m[n-1]*CI) - (W[n-1]*(LF+CtoK*CI));
[21341]1998 dz_add=-(dz[n-1]);
[23189]1999
[22758]2000 // remove a grid cell from the bottom
[21341]2001 D_size=n-1;
2002 D=xNew<int>(D_size);
[23189]2003
[21341]2004 for(int i=0;i<n-1;i++) D[i]=i;
2005 celldelete(&dz,n,D,D_size);
2006 celldelete(&T,n,D,D_size);
2007 celldelete(&W,n,D,D_size);
2008 celldelete(&m,n,D,D_size);
2009 celldelete(&d,n,D,D_size);
2010 celldelete(&a,n,D,D_size);
[26744]2011 celldelete(&adiff,n,D,D_size);
[21341]2012 celldelete(&re,n,D,D_size);
2013 celldelete(&gdn,n,D,D_size);
2014 celldelete(&gsp,n,D,D_size);
[22758]2015 celldelete(&EI,n,D,D_size);
2016 celldelete(&EW,n,D,D_size);
[21341]2017 n=D_size;
2018 xDelete<int>(D);
2019 }
[19554]2020
2021 //// CHECK FOR MASS AND ENERGY CONSERVATION
2022
2023 // calculate final mass [kg] and energy [J]
[19582]2024 sumER = Rsum * (LF + CtoK * CI);
[19554]2025 for(int i=0;i<n;i++)EI[i] = m[i] * T[i] * CI;
2026 for(int i=0;i<n;i++)EW[i] = W[i] * (LF + CtoK * CI);
2027
2028 mSum1 = cellsum(W,n) + cellsum(m,n) + Rsum;
2029 sumE1 = cellsum(EI,n) + cellsum(EW,n);
2030
[19613]2031 /*checks: */
[22758]2032 for(int i=0;i<n;i++) if (W[i]<0.0-Wtol) _error_("negative pore water generated in melt equations\n");
2033
[19613]2034 /*only in forward mode! avoid round in AD mode as it is not differentiable: */
[23394]2035 #ifndef _HAVE_AD_
[22758]2036 dm = round((mSum0 - mSum1 + mAdd)*100.0)/100.0;
[24686]2037 dE = round(sumE0 - sumE1 - sumER + addE - surplusE);
[21341]2038 if (dm !=0 || dE !=0) _error_("mass or energy are not conserved in melt equations\n"
[19582]2039 << "dm: " << dm << " dE: " << dE << "\n");
[19613]2040 #endif
[23189]2041
[24686]2042 /*Free resources:*/
[19554]2043 if(m)xDelete<IssmDouble>(m);
2044 if(EI)xDelete<IssmDouble>(EI);
[19582]2045 if(EW)xDelete<IssmDouble>(EW);
[19554]2046 if(maxF)xDelete<IssmDouble>(maxF);
2047 if(dW)xDelete<IssmDouble>(dW);
2048 if(exsW)xDelete<IssmDouble>(exsW);
2049 if(exsT)xDelete<IssmDouble>(exsT);
2050 if(surpT)xDelete<IssmDouble>(surpT);
2051 if(surpE)xDelete<IssmDouble>(surpE);
2052 if(flxDn)xDelete<IssmDouble>(flxDn);
2053 if(D)xDelete<int>(D);
2054 if(M)xDelete<IssmDouble>(M);
[21341]2055 xDelete<IssmDouble>(Zcum);
2056 xDelete<IssmDouble>(dzMin2);
[25836]2057 xDelete<IssmDouble>(dzMax2);
[23189]2058
[19554]2059 /*Assign output pointers:*/
[25836]2060 *pMs=Msurf;
[19554]2061 *pM=sumM;
2062 *pR=Rsum;
[26744]2063 *pF=Fsum;
[19554]2064 *pmAdd=mAdd;
[21341]2065 *pdz_add=dz_add;
2066
[19554]2067 *pT=T;
2068 *pd=d;
2069 *pdz=dz;
2070 *pW=W;
2071 *pa=a;
[26744]2072 *padiff=adiff;
[19554]2073 *pre=re;
2074 *pgdn=gdn;
2075 *pgsp=gsp;
2076 *pn=n;
2077
2078} /*}}}*/
[25836]2079void densification(IssmDouble** pd,IssmDouble** pdz, IssmDouble* T, IssmDouble* re, int denIdx, int aIdx, int swIdx, IssmDouble adThresh, IssmDouble C, IssmDouble dt, IssmDouble Tmean, IssmDouble dIce, int m, int sid){ /*{{{*/
[19554]2080
[22758]2081 //// THIS NEEDS TO BE DOUBLE CHECKED AS THERE SEAMS TO BE LITTLE DENSIFICATION IN THE MODEL OUTOUT [MAYBE COMPACTION IS COMPENSATED FOR BY TRACES OF SNOW???]
[19554]2082
2083 //// FUNCTION INFO
2084
2085 // Author: Alex Gardner, University of Alberta
2086 // Date last modified: FEB, 2008
2087
2088 // Description:
2089 // computes the densification of snow/firn using the emperical model of
2090 // Herron and Langway (1980) or the semi-emperical model of Anthern et al.
2091 // (2010)
2092
2093 // Inputs:
2094 // denIdx = densification model to use:
2095 // 1 = emperical model of Herron and Langway (1980)
2096 // 2 = semi-imerical model of Anthern et al. (2010)
2097 // 3 = physical model from Appendix B of Anthern et al. (2010)
2098 // d = initial snow/firn density [kg m-3]
2099 // T = temperature [K]
2100 // dz = grid cell size [m]
2101 // C = average accumulation rate [kg m-2 yr-1]
2102 // dt = time lapsed [s]
2103 // re = effective grain radius [mm];
2104 // Ta = mean annual temperature
2105
2106 // Reference:
2107 // Herron and Langway (1980), Anthern et al. (2010)
2108
2109 //// FOR TESTING
2110 // denIdx = 2;
2111 // d = 800;
2112 // T = 270;
2113 // dz = 0.005;
2114 // C = 200;
2115 // dt = 60*60;
2116 // re = 0.7;
2117 // Tmean = 273.15-18;
2118
2119 //// MAIN FUNCTION
2120 // specify constants
[22758]2121 dt = dt / dts; // convert from [s] to [d]
[19554]2122 // R = 8.314 // gas constant [mol-1 K-1]
2123 // Ec = 60 // activation energy for self-diffusion of water
2124 // // molecules through the ice tattice [kJ mol-1]
2125 // Eg = 42.4 // activation energy for grain growth [kJ mol-1]
2126
2127 /*intermediary: */
[22758]2128 IssmDouble c0=0.0;
2129 IssmDouble c1=0.0;
2130 IssmDouble H=0.0;
[23189]2131 IssmDouble M0=0.0;
2132 IssmDouble M1=0.0;
2133 IssmDouble c0arth=0.0;
2134 IssmDouble c1arth=0.0;
[22758]2135
2136 /*output: */
2137 IssmDouble* dz=NULL;
2138 IssmDouble* d=NULL;
[23189]2139
[19566]2140 if(VerboseSmb() && sid==0 && IssmComm::GetRank()==0)_printf0_(" densification module\n");
[19554]2141
[22758]2142 /*Recover pointers: */
2143 dz=*pdz;
2144 d=*pd;
2145
[19554]2146 // initial mass
2147 IssmDouble* mass_init = xNew<IssmDouble>(m);for(int i=0;i<m;i++) mass_init[i]=d[i] * dz[i];
[23189]2148
[19554]2149 /*allocations and initialization of overburden pressure and factor H: */
2150 IssmDouble* cumdz = xNew<IssmDouble>(m-1);
2151 cumdz[0]=dz[0];
2152 for(int i=1;i<m-1;i++)cumdz[i]=cumdz[i-1]+dz[i];
2153
2154 IssmDouble* obp = xNew<IssmDouble>(m);
[22758]2155 obp[0]=0.0;
[19582]2156 for(int i=1;i<m;i++)obp[i]=cumdz[i-1]*d[i-1];
[23189]2157
[19554]2158 // calculate new snow/firn density for:
2159 // snow with densities <= 550 [kg m-3]
2160 // snow with densities > 550 [kg m-3]
[23189]2161
[19554]2162 for(int i=0;i<m;i++){
2163 switch (denIdx){
2164 case 1: // Herron and Langway (1980)
[23189]2165 c0 = (11.0 * exp(-10160.0 / (T[i] * R))) * C/1000.0;
2166 c1 = (575.0 * exp(-21400.0 / (T[i]* R))) * pow(C/1000.0,.5);
[19554]2167 break;
[23189]2168
[19554]2169 case 2: // Arthern et al. (2010) [semi-emperical]
2170 // common variable
2171 // NOTE: Ec=60000, Eg=42400 (i.e. should be in J not kJ)
[23189]2172 H = exp((-60000.0/(T[i] * R)) + (42400.0/(Tmean * R))) * (C * 9.81);
[19554]2173 c0 = 0.07 * H;
2174 c1 = 0.03 * H;
2175 break;
2176
2177 case 3: // Arthern et al. (2010) [physical model eqn. B1]
2178
2179 // common variable
[23189]2180 H = exp((-60000.0/(T[i] * R))) * obp[i] / pow(re[i]/1000.0,2.0);
[19582]2181 c0 = 9.2e-9 * H;
2182 c1 = 3.7e-9 * H;
[19554]2183 break;
2184
2185 case 4: // Li and Zwally (2004)
[22758]2186 c0 = (C/dIce) * (139.21 - 0.542*Tmean)*8.36*pow(CtoK - T[i],-2.061);
[19554]2187 c1 = c0;
2188 break;
2189
2190 case 5: // Helsen et al. (2008)
2191 // common variable
[22758]2192 c0 = (C/dIce) * (76.138 - 0.28965*Tmean)*8.36*pow(CtoK - T[i],-2.061);
[19554]2193 c1 = c0;
2194 break;
[23189]2195
2196 case 6: // Ligtenberg and others (2011) [semi-emperical], Antarctica
2197 // common variable
[24313]2198 // From literature: H = exp((-60000.0/(Tmean * R)) + (42400.0/(Tmean * R))) * (C * 9.81);
2199 H = exp((-60000.0/(T[i] * R)) + (42400.0/(Tmean * R))) * (C * 9.81);
[23189]2200 c0arth = 0.07 * H;
2201 c1arth = 0.03 * H;
[25836]2202 //ERA-5 old
2203 //M0 = max(2.3128 - (0.2480 * log(C)),0.25);
2204 //M1 = max(2.7950 - (0.3318 * log(C)),0.25);
2205 // ERA5 new aIdx=1, swIdx=0
2206 if (aIdx==1 && swIdx==0){
2207 if (fabs(adThresh - 820) < Dtol){
2208 // ERA5 new aIdx=1, swIdx=0, MODIS 820
[26744]2209 //M0 = max(1.8230 - (0.1645 * log(C)),0.25);
2210 //M1 = max(2.5134 - (0.3244 * log(C)),0.25);
2211 // ERA5 new aIdx=1, swIdx=0, MODIS 820, p90 new (ERA5, 40 and e97)
2212 M0 = max(1.3045 - (0.0988 * log(C)),0.25);
2213 M1 = max(1.3694 - (0.1354 * log(C)),0.25);
[25836]2214 }
2215 else{
2216 // ERA5 new aIdx=1, swIdx=0
2217 //M0 = max(1.8785 - (0.1811 * log(C)),0.25);
2218 //M1 = max(2.0005 - (0.2346 * log(C)),0.25);
2219 // ERA5 new aIdx=1, swIdx=0, bare ice
2220 M0 = max(1.8422 - (0.1688 * log(C)),0.25);
2221 M1 = max(2.4979 - (0.3225 * log(C)),0.25);
2222 }
2223 }
2224 // ERA5 new aIdx=2, swIdx=1
2225 else if (aIdx<3 && swIdx>0){
2226 M0 = max(2.2191 - (0.2301 * log(C)),0.25);
2227 M1 = max(2.2917 - (0.2710 * log(C)),0.25);
2228 }
2229 // ERA5 new aIdx=2, swIdx=0
2230 //else if (aIdx==2){
2231 //}
[24686]2232 //From Ligtenberg
2233 //H = exp((-60000.0/(Tmean * R)) + (42400.0/(Tmean * R))) * (C * 9.81);
[24313]2234 //M0 = max(1.435 - (0.151 * log(C)),0.25);
2235 //M1 = max(2.366 - (0.293 * log(C)),0.25);
[25836]2236 //RACMO
2237 M0 = max(1.6599 - (0.1724 * log(C)),0.25);
2238 M1 = max(2.0102 - (0.2458 * log(C)),0.25);
[23189]2239 c0 = M0*c0arth;
2240 c1 = M1*c1arth;
2241 break;
2242
2243 case 7: // Kuipers Munneke and others (2015) [semi-emperical], Greenland
2244 // common variable
[24313]2245 // From literature: H = exp((-60000.0/(T[i] * R)) + (42400.0/(T[i] * R))) * (C * 9.81);
2246 H = exp((-60000.0/(T[i] * R)) + (42400.0/(Tmean * R))) * (C * 9.81);
[23189]2247 c0arth = 0.07 * H;
2248 c1arth = 0.03 * H;
[25836]2249 // ERA5 old
2250 //M0 = max(1.8554 - (0.1316 * log(C)),0.25);
2251 //M1 = max(2.8901 - (0.3014 * log(C)),0.25);
2252 // ERA5 new aIdx=1, swIdx=0
2253 if (aIdx==1 && swIdx==0){
2254 if (fabs(adThresh - 820) < Dtol){
2255 // ERA5 new aIdx=1, swIdx=0, MODIS 820
[26744]2256 //M0 = max(1.4174 - (0.1037 * log(C)),0.25);
2257 //M1 = max(2.2010 - (0.2460 * log(C)),0.25);
2258 // ERA5 new aIdx=1, swIdx=0, MODIS 820, p90 new (ERA5 lwt, 40 and e97)
2259 M0 = max(1.2138 - (0.1057 * log(C)),0.25);
2260 M1 = max(1.4946 - (0.1607 * log(C)),0.25);
[25836]2261 }
2262 else{
2263 // ERA5 new aIdx=1, swIdx=0
2264 //M0 = max(1.4574 - (0.1123 * log(C)),0.25);
2265 //M1 = max(2.0238 - (0.2070 * log(C)),0.25);
2266 // ERA5 new aIdx=1, swIdx=0, bare ice
2267 M0 = max(1.4318 - (0.1055 * log(C)),0.25);
2268 M1 = max(2.0453 - (0.2137 * log(C)),0.25);
2269 }
2270 }
2271 // ERA5 new aIdx=2, swIdx=1
2272 else if (aIdx<3 && swIdx>0){
2273 M0 = max(1.7834 - (0.1409 * log(C)),0.25);
2274 M1 = max(1.9260 - (0.1527 * log(C)),0.25);
2275 }
2276 // ERA5 new aIdx=2, swIdx=0
2277 //else if (aIdx==2){
2278 //}
2279 // From Kuipers Munneke
2280 //M0 = max(1.042 - (0.0916 * log(C)),0.25);
2281 //M1 = max(1.734 - (0.2039 * log(C)),0.25);
[24686]2282 // RACMO
2283 M0 = max(1.6201 - (0.1450 * log(C)),0.25);
2284 M1 = max(2.5577 - (0.2899 * log(C)),0.25);
[23189]2285 c0 = M0*c0arth;
2286 c1 = M1*c1arth;
2287 break;
[19554]2288 }
2289
2290 // new snow density
[22758]2291 if(d[i] <= 550.0+Dtol) d[i] = d[i] + (c0 * (dIce - d[i]) / 365.0 * dt);
2292 else d[i] = d[i] + (c1 * (dIce - d[i]) / 365.0 * dt);
[19554]2293
2294 //disp((num2str(nanmean(c0 .* (dIce - d(idx)) / 365 * dt))))
2295
2296 // do not allow densities to exceed the density of ice
[24313]2297 if(d[i] > dIce-Ptol) d[i]=dIce;
[19554]2298
2299 // calculate new grid cell length
2300 dz[i] = mass_init[i] / d[i];
2301 }
[19566]2302
[24686]2303 /*Free resources:*/
[19554]2304 xDelete<IssmDouble>(mass_init);
2305 xDelete<IssmDouble>(cumdz);
2306 xDelete<IssmDouble>(obp);
2307
[22758]2308 /*Assign output pointers:*/
2309 *pdz=dz;
2310 *pd=d;
2311
[19554]2312} /*}}}*/
[22758]2313void turbulentFlux(IssmDouble* pshf, IssmDouble* plhf, IssmDouble* pEC, IssmDouble Ta, IssmDouble Ts, IssmDouble V, IssmDouble eAir, IssmDouble pAir, IssmDouble ds, IssmDouble Ws, IssmDouble Vz, IssmDouble Tz, IssmDouble dIce, int sid){ /*{{{*/
[19554]2314
2315 //// TURBULENT HEAT FLUX
2316
2317 // Description:
2318 // computed the surface sensible and latent heat fluxes [W m-2], this
2319 // function also calculated the mass loss/acreation due to
2320 // condensation/evaporation [kg]
2321
2322 // Reference:
2323 // Dingman, 2002.
2324
2325 //// INPUTS:
2326 // Ta: 2m air temperature [K]
2327 // Ts: snow/firn/ice surface temperature [K]
2328 // V: wind speed [m s^-^1]
2329 // eAir: screen level vapor pressure [Pa]
2330 // pAir: surface pressure [Pa]
2331 // ds: surface density [kg/m^3]
2332 // Ws: surface liquid water content [kg/m^2]
2333 // Vz: height above ground at which wind (V) eas sampled [m]
2334 // Tz: height above ground at which temperature (T) was sampled [m]
2335
2336 /*intermediary:*/
[22758]2337 IssmDouble d_air=0.0;
2338 IssmDouble Ri=0.0;
2339 IssmDouble z0=0.0;
2340 IssmDouble coef_M=0.0;
2341 IssmDouble coef_H=0.0;
2342 IssmDouble An=0.0;
2343 IssmDouble C=0.0;
2344 IssmDouble L=0.0;
2345 IssmDouble eS=0.0;
[19554]2346
2347 /*output: */
[22758]2348 IssmDouble shf=0.0;
2349 IssmDouble lhf=0.0;
2350 IssmDouble EC=0.0;
[23189]2351
[19566]2352 if(VerboseSmb() && sid==0 && IssmComm::GetRank()==0)_printf0_(" turbulentFlux module\n");
[19554]2353
2354 // calculated air density [kg/m3]
[22758]2355 d_air = 0.029 * pAir /(R * Ta);
[19554]2356
2357 //// Determine Surface Roughness
2358 // Bougamont, 2006
2359 // wind/temperature surface roughness height [m]
[22758]2360 if (ds < dIce-Dtol && fabs(Ws) < Wtol) z0 = 0.00012; // 0.12 mm for dry snow
2361 else if (ds >= dIce-Dtol) z0 = 0.0032; // 3.2 mm for ice
[19554]2362 else z0 = 0.0013; // 1.3 mm for wet snow
2363
[22758]2364 //// Monin-Obukhov Stability Correction
[19554]2365 // Reference:
2366 // Ohmura, A., 1982: Climate and Energy-Balance on the Arctic Tundra.
2367 // Journal of Climatology, 2, 65-84.
2368
2369 // if V = 0 goes to infinity therfore if V = 0 change
[24313]2370 if(V< 0.01-Dtol)V=0.01;
[19554]2371
2372 // calculate the Bulk Richardson Number (Ri)
[24686]2373 Ri = (2.0*9.81* (Vz - z0) * (Ta - Ts)) / ((Ta + Ts)* pow(V,2.0));
[25836]2374 //Ri = (2.0*9.81*(Ta - Ts)) / ((Tz - z0)*(Ta + Ts)* pow(V/(Vz - z0),2.0));
[19554]2375
[24686]2376 // calculate Monin-Obukhov stability factors 'coefM' and 'coefH'
[23189]2377
[25836]2378 // do not allow Ri to exceed 0.16
2379 // Ri = min(Ri, 0.19);
2380 Ri = min(Ri, 0.16); //Ohmura, 1982
[19554]2381
[22758]2382 // calculate momentum 'coefM' stability factor
2383 if (Ri > 0.0+Ttol){
2384 // if stable
2385 coef_M = 1.0/(1.0-5.2*Ri);
2386 }
2387 else {
2388 coef_M =pow (1.0-18*Ri,-0.25);
2389 }
[19554]2390
2391 // calculate heat/wind 'coef_H' stability factor
[22758]2392 if (Ri <= -0.03+Ttol) coef_H = coef_M/1.3;
[19554]2393 else coef_H = coef_M;
[22758]2394
[19554]2395 //// Bulk-transfer coefficient
[24313]2396 //An = pow(0.4,2) / pow(log(Tz/z0),2); // Bulk-transfer coefficient
2397 An = pow(0.4,2) / (log(Tz/z0)*log(Vz/z0)); // Bulk-transfer coefficient
[19554]2398 C = An * d_air * V; // shf & lhf common coefficient
2399
2400 //// Sensible Heat
2401 // calculate the sensible heat flux [W m-2](Patterson, 1998)
[22758]2402 shf = C * CA * (Ta - Ts);
[19554]2403
[22758]2404 // adjust using Monin-Obukhov stability theory
[19554]2405 shf = shf / (coef_M * coef_H);
2406
[22758]2407 // Latent Heat
2408 // determine if snow pack is melting & calcualte surface vapour pressure over ice or liquid water
2409 if (Ts >= CtoK-Ttol){
2410 L = LV; //for liquid water at 273.15 k to vapor
[19554]2411
[22758]2412 //for liquid surface (assume liquid on surface when Ts == 0 deg C)
2413 // Wright (1997), US Meteorological Handbook from Murphy and Koop, 2005 Appendix A
2414 eS = 611.21 * exp(17.502 * (Ts - CtoK) / (240.97 + Ts - CtoK));
[19554]2415 }
2416 else{
[22758]2417 L = LS; // latent heat of sublimation
2418
2419 // for an ice surface Murphy and Koop, 2005 [Equation 7]
2420 eS = exp(9.550426 - 5723.265/Ts + 3.53068 * log(Ts) - 0.00728332 * Ts);
[19554]2421 }
2422
2423 // Latent heat flux [W m-2]
2424 lhf = C * L * (eAir - eS) * 0.622 / pAir;
2425
[22758]2426 // adjust using Monin-Obukhov stability theory (if lhf '+' then there is
[19554]2427 // energy and mass gained at the surface, if '-' then there is mass and
2428 // energy loss at the surface.
2429 lhf = lhf / (coef_M * coef_H);
2430
2431 // mass loss (-)/acreation(+) due to evaporation/condensation [kg]
[22758]2432 EC = lhf * dts / L;
[19554]2433
2434 /*assign output poitners: */
2435 *pshf=shf;
2436 *plhf=lhf;
2437 *pEC=EC;
2438
2439} /*}}}*/
Note: See TracBrowser for help on using the repository browser.