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

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

merged trunk-jpl and trunk for revision 27230

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