[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] | 11 | const double Pi = 3.141592653589793;
|
---|
[22758] | 12 | const double CtoK = 273.15; // Kelvin to Celcius conversion/ice melt. point T in K
|
---|
| 13 | const 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 */
|
---|
| 18 | const double Ttol = 1e-10;
|
---|
| 19 | const double Dtol = 1e-11;
|
---|
| 20 | const double Gdntol = 1e-10;
|
---|
| 21 | const double Wtol = 1e-13;
|
---|
[24313] | 22 | const double Ptol = 1e-6;
|
---|
[22758] | 23 |
|
---|
| 24 | const double CI = 2102.0; // heat capacity of snow/ice (J kg-1 k-1)
|
---|
| 25 | const double LF = 0.3345E6; // latent heat of fusion (J kg-1)
|
---|
| 26 | const double LV = 2.495E6; // latent heat of vaporization (J kg-1)
|
---|
| 27 | const double LS = 2.8295E6; // latent heat of sublimation (J kg-1)
|
---|
| 28 | const double SB = 5.67E-8; // Stefan-Boltzmann constant (W m-2 K-4)
|
---|
[23189] | 29 | const double CA = 1005.0; // heat capacity of air (J kg-1 K-1)
|
---|
| 30 | const double R = 8.314; // gas constant (J mol-1 K-1)
|
---|
[22758] | 31 |
|
---|
[24313] | 32 | const double Delflag=-99999;
|
---|
| 33 |
|
---|
[19554] | 34 | void 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 | } /*}}}*/
|
---|
| 71 | void 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 | } /*}}}*/
|
---|
| 150 | IssmDouble 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] | 183 | IssmDouble 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] | 259 | void 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] | 472 | void 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] | 696 | void 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] | 1079 | void 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] | 1285 | void 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] | 1480 | void 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] | 2079 | void 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] | 2313 | void 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 | } /*}}}*/
|
---|