|
| 1 | +/* |
| 2 | + * SUMMARY: SnowPackEnergyBalance.c - Calculate snow pack energy balance |
| 3 | + * USAGE: Part of DHSVM |
| 4 | + * |
| 5 | + * AUTHOR: Bart Nijssen |
| 6 | + * ORG: University of Washington, Department of Civil Engineering |
| 7 | + |
| 8 | + * ORIG-DATE: 8-Oct-1996 at 09:09:29 |
| 9 | + * LAST-MOD: Mon Apr 21 16:39:04 2003 by Keith Cherkauer <[email protected]> |
| 10 | + * DESCRIPTION: Calculate snow pack energy balance |
| 11 | + * DESCRIP-END. |
| 12 | + * FUNCTIONS: SnowPackEnergyBalance() |
| 13 | + * COMMENTS: |
| 14 | + */ |
| 15 | + |
| 16 | +#include <stdarg.h> |
| 17 | +#include <stdio.h> |
| 18 | +#include <stdlib.h> |
| 19 | +#include <vicNl.h> |
| 20 | + |
| 21 | +static char vcid[] = "$Id$"; |
| 22 | + |
| 23 | +/***************************************************************************** |
| 24 | + Function name: SnowPackEnergyBalance() |
| 25 | +
|
| 26 | + Purpose : Calculate the surface energy balance for the snow pack |
| 27 | +
|
| 28 | + Required : |
| 29 | + double TSurf - new estimate of effective surface temperature |
| 30 | + va_list ap - Argument list initialized by va_start(). For |
| 31 | + elements of list and order, see beginning of |
| 32 | + routine |
| 33 | +
|
| 34 | + Returns : |
| 35 | + double RestTerm - Rest term in the energy balance |
| 36 | +
|
| 37 | + Modifies : |
| 38 | + double *RefreezeEnergy - Refreeze energy (W/m2) |
| 39 | + double *vapor_flux - Mass flux of water vapor to or from the |
| 40 | + intercepted snow (m/timestep) |
| 41 | + double *blowing_flux - Mass flux of water vapor from blowing snow (m/timestep) |
| 42 | + double *surface_flux - Mass flux of water vapor from pack snow (m/timestep) |
| 43 | +
|
| 44 | + Comments : |
| 45 | + Reference: Bras, R. A., Hydrology, an introduction to hydrologic |
| 46 | + science, Addisson Wesley, Inc., Reading, etc., 1990. |
| 47 | +
|
| 48 | + Modifications: |
| 49 | + 10-6-2000 modified to handle partial snow cover including the |
| 50 | + advection of sensible heat from bare patches to the edges |
| 51 | + of the remaining snow cover. KAC |
| 52 | + 11-18-02 Modified to include the effects of blowing snow on the |
| 53 | + accumulation and ablation of the snowpack. LCB |
| 54 | + 04-21-03 Removed constant variable declarations. Those still |
| 55 | + required by the VIC model are now defined in |
| 56 | + vicNl_def.h. KAC |
| 57 | + 16-Jul-04 Renamed VaporMassFlux, BlowingMassFlux, and SurfaceMassFlux |
| 58 | + to vapor_flux, blowing_flux, and surface_flux, respectively, |
| 59 | + to denote fact that their units are m/timestep rather than |
| 60 | + kg/m2s. Created new variables VaporMassFlux, BlowingMassFlux, |
| 61 | + and SurfaceMassFlux with units of kg/m2s. The addresses of |
| 62 | + the *MassFlux variables are passed to latent_heat_from_snow() |
| 63 | + where values for the variables are computed. After these |
| 64 | + values are computed, vapor_flux, blowing_flux and surface_flux |
| 65 | + are derived from them by unit conversion. vapor_flux, |
| 66 | + blowing_flux, and surface_flux are the variables that are |
| 67 | + passed in/out of this function. TJB |
| 68 | + 16-Jul-04 Changed the type of the last few variables (lag_one, iveg, |
| 69 | + etc) in the va_list to be double. For some reason, passing |
| 70 | + them as float or int caused them to become garbage. This may |
| 71 | + have to do with the fact that they followed variables of type |
| 72 | + (double *) in va_list, which may have caused memory alignment |
| 73 | + problems. TJB |
| 74 | + 05-Aug-04 Removed lag_one, sigma_slope, fetch, iveg, Nveg, month, overstory, |
| 75 | + LastSnow, and SnowDepth from argument list, since they were only |
| 76 | + needed to pass to latent_heat_from_snow(), which no longer needs |
| 77 | + them. TJB |
| 78 | + 28-Sep-04 Added Ra_used to store the aerodynamic resistance used in flux |
| 79 | + calculations. TJB |
| 80 | + 2006-Sep-23 Replaced redundant STEFAN_B constant with STEFAN_B_B. TJB |
| 81 | + 2009-Jan-16 Modified aero_resist_used and Ra_used to become arrays of |
| 82 | + two elements (surface and overstory); added |
| 83 | + options.AERO_RESIST_CANSNOW. TJB |
| 84 | + 2009-Sep-19 Added Added ground flux computation consistent with 4.0.6. TJB |
| 85 | + 2013-Dec-27 Moved SPATIAL_SNOW from compile-time to run-time options. TJB |
| 86 | +
|
| 87 | +*****************************************************************************/ |
| 88 | +double SnowPackEnergyBalance(double TSurf, va_list ap) |
| 89 | +{ |
| 90 | + |
| 91 | + extern option_struct options; |
| 92 | + |
| 93 | + const char *Routine = "SnowPackEnergyBalance"; |
| 94 | + |
| 95 | + /* Define Variable Argument List */ |
| 96 | + |
| 97 | + /* General Model Parameters */ |
| 98 | + double Dt; /* Model time step (sec) */ |
| 99 | + double Ra; /* Aerodynamic resistance (s/m) */ |
| 100 | + double *Ra_used; /* Aerodynamic resistance (s/m) after stability correction */ |
| 101 | + |
| 102 | + /* Vegetation Parameters */ |
| 103 | + double Displacement; /* Displacement height (m) */ |
| 104 | + double Z; /* Reference height (m) */ |
| 105 | + double *Z0; /* surface roughness height (m) */ |
| 106 | + |
| 107 | + /* Atmospheric Forcing Variables */ |
| 108 | + double AirDens; /* Density of air (kg/m3) */ |
| 109 | + double EactAir; /* Actual vapor pressure of air (Pa) */ |
| 110 | + double LongSnowIn; /* Incoming longwave radiation (W/m2) */ |
| 111 | + double Lv; /* Latent heat of vaporization (J/kg3) */ |
| 112 | + double Press; /* Air pressure (Pa) */ |
| 113 | + double Rain; /* Rain fall (m/timestep) */ |
| 114 | + double NetShortUnder; /* Net incident shortwave radiation |
| 115 | + (W/m2) */ |
| 116 | + double Vpd; /* Vapor pressure deficit (Pa) */ |
| 117 | + double Wind; /* Wind speed (m/s) */ |
| 118 | + |
| 119 | + /* Snowpack Variables */ |
| 120 | + double OldTSurf; /* Surface temperature during previous time |
| 121 | + step */ |
| 122 | + double SnowCoverFract; /* Fraction of area covered by snow */ |
| 123 | + double SnowDepth; /* Depth of snowpack (m) */ |
| 124 | + double SnowDensity; /* Density of snowpack (kg/m^3) */ |
| 125 | + double SurfaceLiquidWater; /* Liquid water in the surface layer (m) */ |
| 126 | + double SweSurfaceLayer; /* Snow water equivalent in surface layer |
| 127 | + (m) */ |
| 128 | + |
| 129 | + /* Energy Balance Components */ |
| 130 | + double Tair; /* Canopy air / Air temperature (C) */ |
| 131 | + double TGrnd; /* Ground surface temperature (C) */ |
| 132 | + |
| 133 | + double *AdvectedEnergy; /* Energy advected by precipitation (W/m2) */ |
| 134 | + double *AdvectedSensibleHeat; /* Sensible heat advected from snow-free |
| 135 | + area into snow covered area (W/m^2) */ |
| 136 | + double *DeltaColdContent; /* Change in cold content of surface |
| 137 | + layer (W/m2) */ |
| 138 | + double *GroundFlux; /* Ground Heat Flux (W/m2) */ |
| 139 | + double *LatentHeat; /* Latent heat exchange at surface (W/m2) */ |
| 140 | + double *LatentHeatSub; /* Latent heat of sublimation exchange at |
| 141 | + surface (W/m2) */ |
| 142 | + double *NetLongUnder; /* Net longwave radiation at snowpack |
| 143 | + surface (W/m^2) */ |
| 144 | + double *RefreezeEnergy; /* Refreeze energy (W/m2) */ |
| 145 | + double *SensibleHeat; /* Sensible heat exchange at surface |
| 146 | + (W/m2) */ |
| 147 | + double *vapor_flux; /* Mass flux of water vapor to or from the |
| 148 | + intercepted snow (m/timestep) */ |
| 149 | + double *blowing_flux; /* Mass flux of water vapor from blowing snow. (m/timestep) */ |
| 150 | + double *surface_flux; /* Mass flux of water vapor from pack snow. (m/timestep) */ |
| 151 | + |
| 152 | + /* Internal Routine Variables */ |
| 153 | + |
| 154 | + double Density; /* Density of water/ice at TMean (kg/m3) */ |
| 155 | + /* double LongRadOut; */ /* long wave radiation emitted by surface |
| 156 | + (W/m2) */ |
| 157 | + double NetRad; /* Net radiation exchange at surface |
| 158 | + (W/m2) */ |
| 159 | + double RestTerm; /* Rest term in surface energy balance |
| 160 | + (W/m2) */ |
| 161 | + double TMean; /* Average temperature for time step (C) */ |
| 162 | + double Tmp; |
| 163 | + double VaporMassFlux; /* Mass flux of water vapor to or from the |
| 164 | + intercepted snow (kg/m2s) */ |
| 165 | + double BlowingMassFlux; /* Mass flux of water vapor from blowing snow. (kg/m2s) */ |
| 166 | + double SurfaceMassFlux; /* Mass flux of water vapor from pack snow. (kg/m2s) */ |
| 167 | + |
| 168 | + /* Assign the elements of the array to the appropriate variables. The list |
| 169 | + is traversed as if the elements are doubles, because: |
| 170 | +
|
| 171 | + In the variable-length part of variable-length argument lists, the old |
| 172 | + ``default argument promotions'' apply: arguments of type double are |
| 173 | + always promoted (widened) to type double, and types char and short int |
| 174 | + are promoted to int. Therefore, it is never correct to invoke |
| 175 | + va_arg(argp, double); instead you should always use va_arg(argp, |
| 176 | + double). |
| 177 | +
|
| 178 | + (quoted from the comp.lang.c FAQ list) |
| 179 | + */ |
| 180 | + |
| 181 | + /* General Model Parameters */ |
| 182 | + Dt = (double) va_arg(ap, double); |
| 183 | + Ra = (double) va_arg(ap, double); |
| 184 | + Ra_used = (double *) va_arg(ap, double *); |
| 185 | + |
| 186 | + /* Vegetation Parameters */ |
| 187 | + Displacement = (double) va_arg(ap, double); |
| 188 | + Z = (double) va_arg(ap, double); |
| 189 | + Z0 = (double *) va_arg(ap, double *); |
| 190 | + |
| 191 | + /* Atmospheric Forcing Variables */ |
| 192 | + AirDens = (double) va_arg(ap, double); |
| 193 | + EactAir = (double) va_arg(ap, double); |
| 194 | + LongSnowIn = (double) va_arg(ap, double); |
| 195 | + Lv = (double) va_arg(ap, double); |
| 196 | + Press = (double) va_arg(ap, double); |
| 197 | + Rain = (double) va_arg(ap, double); |
| 198 | + NetShortUnder = (double) va_arg(ap, double); |
| 199 | + Vpd = (double) va_arg(ap, double); |
| 200 | + Wind = (double) va_arg(ap, double); |
| 201 | + |
| 202 | + /* Snowpack Variables */ |
| 203 | + OldTSurf = (double) va_arg(ap, double); |
| 204 | + SnowCoverFract = (double) va_arg(ap, double); |
| 205 | + SnowDepth = (double) va_arg(ap, double); |
| 206 | + SnowDensity = (double) va_arg(ap, double); |
| 207 | + SurfaceLiquidWater = (double) va_arg(ap, double); |
| 208 | + SweSurfaceLayer = (double) va_arg(ap, double); |
| 209 | + |
| 210 | + /* Energy Balance Components */ |
| 211 | + Tair = (double) va_arg(ap, double); |
| 212 | + TGrnd = (double) va_arg(ap, double); |
| 213 | + |
| 214 | + AdvectedEnergy = (double *) va_arg(ap, double *); |
| 215 | + AdvectedSensibleHeat = (double *)va_arg(ap, double *); |
| 216 | + DeltaColdContent = (double *) va_arg(ap, double *); |
| 217 | + GroundFlux = (double *) va_arg(ap, double *); |
| 218 | + LatentHeat = (double *) va_arg(ap, double *); |
| 219 | + LatentHeatSub = (double *) va_arg(ap, double *); |
| 220 | + NetLongUnder = (double *) va_arg(ap, double *); |
| 221 | + RefreezeEnergy = (double *) va_arg(ap, double *); |
| 222 | + SensibleHeat = (double *) va_arg(ap, double *); |
| 223 | + vapor_flux = (double *) va_arg(ap, double *); |
| 224 | + blowing_flux = (double *) va_arg(ap, double *); |
| 225 | + surface_flux = (double *) va_arg(ap, double *); |
| 226 | + |
| 227 | + /* Calculate active temp for energy balance as average of old and new */ |
| 228 | + |
| 229 | + TMean = TSurf; |
| 230 | + Density = RHO_W; |
| 231 | + |
| 232 | + /* Correct aerodynamic conductance for stable conditions |
| 233 | + Note: If air temp >> snow temp then aero_cond -> 0 (i.e. very stable) |
| 234 | + velocity (vel_2m) is expected to be in m/sec */ |
| 235 | + |
| 236 | + /* Apply the stability correction to the aerodynamic resistance |
| 237 | + NOTE: In the old code 2m was passed instead of Z-Displacement. I (bart) |
| 238 | + think that it is more correct to calculate ALL fluxes at the same |
| 239 | + reference level */ |
| 240 | + |
| 241 | + |
| 242 | + if (Wind > 0.0) |
| 243 | + Ra_used[0] = Ra / StabilityCorrection(Z, 0.f, TMean, Tair, Wind, Z0[2]); |
| 244 | + else |
| 245 | + Ra_used[0] = HUGE_RESIST; |
| 246 | + |
| 247 | + /* Calculate longwave exchange and net radiation */ |
| 248 | + |
| 249 | + Tmp = TMean + KELVIN; |
| 250 | + (*NetLongUnder) = LongSnowIn - STEFAN_B * Tmp * Tmp * Tmp * Tmp; |
| 251 | + NetRad = NetShortUnder + (*NetLongUnder); |
| 252 | + |
| 253 | + /* Calculate the sensible heat flux */ |
| 254 | + |
| 255 | + *SensibleHeat = AirDens * Cp * (Tair - TMean) / Ra_used[0]; |
| 256 | + |
| 257 | + if (options.SPATIAL_SNOW) { |
| 258 | + /* Add in Sensible heat flux turbulent exchange from surrounding |
| 259 | + snow free patches - if present */ |
| 260 | + if ( SnowCoverFract > 0 ) { |
| 261 | + *(AdvectedSensibleHeat) = advected_sensible_heat(SnowCoverFract, |
| 262 | + AirDens, Tair, TGrnd, |
| 263 | + Ra_used[0]); |
| 264 | + } |
| 265 | + else (*AdvectedSensibleHeat) = 0; |
| 266 | + } |
| 267 | + else { |
| 268 | + (*AdvectedSensibleHeat) = 0; |
| 269 | + } |
| 270 | + |
| 271 | + /* Convert sublimation terms from m/timestep to kg/m2s */ |
| 272 | + VaporMassFlux = *vapor_flux * Density / Dt; |
| 273 | + BlowingMassFlux = *blowing_flux * Density / Dt; |
| 274 | + SurfaceMassFlux = *surface_flux * Density / Dt; |
| 275 | + |
| 276 | + /* Calculate the mass flux of ice to or from the surface layer */ |
| 277 | + |
| 278 | + /* Calculate the saturated vapor pressure in the snow pack, |
| 279 | + (Equation 3.32, Bras 1990) */ |
| 280 | + |
| 281 | + latent_heat_from_snow(AirDens, EactAir, Lv, Press, Ra_used[0], TMean, Vpd, |
| 282 | + LatentHeat, LatentHeatSub, &VaporMassFlux, &BlowingMassFlux, |
| 283 | + &SurfaceMassFlux); |
| 284 | + |
| 285 | + /* Convert sublimation terms from kg/m2s to m/timestep */ |
| 286 | + *vapor_flux = VaporMassFlux * Dt / Density; |
| 287 | + *blowing_flux = BlowingMassFlux * Dt / Density; |
| 288 | + *surface_flux = SurfaceMassFlux * Dt / Density; |
| 289 | + |
| 290 | + /* Calculate advected heat flux from rain |
| 291 | + Equation 7.3.12 from H.B.H. for rain falling on melting snowpack */ |
| 292 | + |
| 293 | + if ( TMean == 0 ) |
| 294 | + *AdvectedEnergy = (CH_WATER * (Tair) * Rain) / (Dt); |
| 295 | + else |
| 296 | + *AdvectedEnergy = 0.; |
| 297 | + |
| 298 | + /* Calculate change in cold content */ |
| 299 | + *DeltaColdContent = CH_ICE * SweSurfaceLayer * (TSurf - OldTSurf) / |
| 300 | + (Dt); |
| 301 | + |
| 302 | + /* Calculate Ground Heat Flux */ |
| 303 | + if(SnowDepth>0.) { |
| 304 | + *GroundFlux = 2.9302e-6 * SnowDensity * SnowDensity |
| 305 | + * (TGrnd - TMean) / SnowDepth / (Dt); |
| 306 | + } |
| 307 | + else *GroundFlux=0; |
| 308 | + *DeltaColdContent -= *GroundFlux; |
| 309 | + |
| 310 | + /* Calculate energy balance error at the snowpack surface */ |
| 311 | + RestTerm = NetRad + *SensibleHeat + *LatentHeat + *LatentHeatSub |
| 312 | + + *AdvectedEnergy + *GroundFlux - *DeltaColdContent |
| 313 | + + *AdvectedSensibleHeat; |
| 314 | + |
| 315 | + *RefreezeEnergy = (SurfaceLiquidWater * Lf * Density)/(Dt); |
| 316 | + |
| 317 | + if (TSurf == 0.0 && RestTerm > -(*RefreezeEnergy)) { |
| 318 | + *RefreezeEnergy = -RestTerm; /* available energy input over cold content |
| 319 | + used to melt, i.e. Qrf is negative value |
| 320 | + (energy out of pack)*/ |
| 321 | + RestTerm = 0.0; |
| 322 | + } |
| 323 | + else { |
| 324 | + RestTerm += *RefreezeEnergy; /* add this positive value to the pack */ |
| 325 | + } |
| 326 | + |
| 327 | + return RestTerm; |
| 328 | +} |
| 329 | + |
0 commit comments