diff --git a/docs/Development/ReleaseNotes.md b/docs/Development/ReleaseNotes.md index 0e544b05d..a2e47bfdb 100644 --- a/docs/Development/ReleaseNotes.md +++ b/docs/Development/ReleaseNotes.md @@ -131,6 +131,10 @@ To check which release of VIC you are running: 6. Updated names of variables and options for LAI and FCANOPY in documentation to match their new names in the code 7. Removed constants MAX_VEG and MAX_BANDS from code; all arrays that were declared with those lengths were replaced with dynamic allocations. This allowed for specification of veg libraries containing more classes without recompiling the code, and more efficient memory usage. + [GH#766](https://github.com/UW-Hydro/VIC/pull/766) + + 1. Improved logic in computing soil evaporation (esoil), primarily in func_surf_energy_bal(), by creating explicit terms for transpiration (transp) and esoil in the layer data structure. + #### Bug Fixes: 1. NetCDF forcing files are now closed at the last timestep in stead of after the last timestep. ([GH#774](https://github.com/UW-Hydro/VIC/pull/774)) diff --git a/vic/drivers/shared_all/src/initialize_soil.c b/vic/drivers/shared_all/src/initialize_soil.c index 6b9761f92..8850fb3bd 100644 --- a/vic/drivers/shared_all/src/initialize_soil.c +++ b/vic/drivers/shared_all/src/initialize_soil.c @@ -70,7 +70,8 @@ initialize_soil(cell_data_struct **cell, cell[veg][band].RhSlow = 0.0; cell[veg][band].RhTot = 0.0; for (lindex = 0; lindex < options.Nlayer; lindex++) { - cell[veg][band].layer[lindex].bare_evap_frac = 0.0; + cell[veg][band].layer[lindex].esoil = 0.0; + cell[veg][band].layer[lindex].transp = 0.0; cell[veg][band].layer[lindex].evap = 0.0; } } diff --git a/vic/drivers/shared_all/src/print_library_shared.c b/vic/drivers/shared_all/src/print_library_shared.c index be48bd09d..22002cb27 100644 --- a/vic/drivers/shared_all/src/print_library_shared.c +++ b/vic/drivers/shared_all/src/print_library_shared.c @@ -420,7 +420,8 @@ void print_layer_data_fluxes(layer_data_struct *ldata) { fprintf(LOG_DEST, "layer_data (fluxes):\n"); - fprintf(LOG_DEST, "\tbare_evap_frac: %f\n", ldata->evap); + fprintf(LOG_DEST, "\tesoil: %f\n", ldata->esoil); + fprintf(LOG_DEST, "\ttransp: %f\n", ldata->transp); fprintf(LOG_DEST, "\tevap: %f\n", ldata->evap); } @@ -567,8 +568,9 @@ print_stream(stream_struct *stream, fprintf(LOG_DEST, "\tngridcells: %zu\n", stream->ngridcells); fprintf(LOG_DEST, "\tagg_alarm:\n "); print_alarm(&(stream->agg_alarm)); - fprintf(LOG_DEST, - "\t# \tVARID \tVARNAME \tTYPE \tMULT \tFORMAT \tAGGTYPE\n"); + fprintf( + LOG_DEST, + "\t# \tVARID \tVARNAME \tTYPE \tMULT \tFORMAT \tAGGTYPE\n"); for (i = 0; i < stream->nvars; i++) { varid = stream->varid[i]; fprintf(LOG_DEST, "\t%zu \t%u \t%20s \t%hu \t%f \t%10s \t%hu\n", diff --git a/vic/drivers/shared_all/src/put_data.c b/vic/drivers/shared_all/src/put_data.c index 90b163607..a94ec9845 100644 --- a/vic/drivers/shared_all/src/put_data.c +++ b/vic/drivers/shared_all/src/put_data.c @@ -201,10 +201,11 @@ put_data(all_vars_struct *all_vars, ThisTreeAdjust = 1; } - if (ThisAreaFract > 0. && (veg == veg_con[0].vegetat_type_num || - (!AboveTreeLine[band] || - (AboveTreeLine[band] && - !overstory)))) { + if (ThisAreaFract > 0. && + (veg == veg_con[0].vegetat_type_num || + (!AboveTreeLine[band] || + (AboveTreeLine[band] && + !overstory)))) { /** compute running totals of various landcovers **/ if (HasVeg) { cv_veg += Cv * ThisAreaFract * ThisTreeAdjust; @@ -415,7 +416,8 @@ put_data(all_vars_struct *all_vars, lake_var.runoff_out * MM_PER_M / soil_con->cell_area; // mm over gridcell // mm over gridcell - out_data[OUT_LAKE_EVAP][0] = lake_var.evapw * MM_PER_M / + out_data[OUT_LAKE_EVAP][0] = lake_var.evapw * + MM_PER_M / soil_con->cell_area; // mm over gridcell out_data[OUT_LAKE_RCHRG][0] = lake_var.recharge * @@ -540,10 +542,12 @@ put_data(all_vars_struct *all_vars, } storage += out_data[OUT_SWE][0] + out_data[OUT_SNOW_CANOPY][0] + out_data[OUT_WDEW][0] + out_data[OUT_SURFSTOR][0]; - out_data[OUT_WATER_ERROR][0] = calc_water_balance_error(inflow, - outflow, - storage, - save_data->total_moist_storage); + out_data[OUT_WATER_ERROR][0] = \ + calc_water_balance_error(inflow, + outflow, + storage, + save_data-> + total_moist_storage); // Store total storage for next timestep save_data->total_moist_storage = storage; @@ -612,19 +616,11 @@ collect_wb_terms(cell_data_struct cell, tmp_evap = 0.0; for (index = 0; index < options.Nlayer; index++) { tmp_evap += cell.layer[index].evap; + out_data[OUT_EVAP_BARE][0] += cell.layer[index].esoil * + AreaFactor; if (HasVeg) { - out_data[OUT_EVAP_BARE][0] += cell.layer[index].evap * - cell.layer[index].bare_evap_frac - * - AreaFactor; - out_data[OUT_TRANSP_VEG][0] += cell.layer[index].evap * - (1 - - cell.layer[index]. - bare_evap_frac) * AreaFactor; - } - else { - out_data[OUT_EVAP_BARE][0] += cell.layer[index].evap * - AreaFactor; + out_data[OUT_TRANSP_VEG][0] += cell.layer[index].transp * + AreaFactor; } } tmp_evap += snow.vapor_flux * MM_PER_M; diff --git a/vic/vic_run/include/vic_def.h b/vic/vic_run/include/vic_def.h index e6ea48473..a78dbc15d 100644 --- a/vic/vic_run/include/vic_def.h +++ b/vic/vic_run/include/vic_def.h @@ -773,8 +773,9 @@ typedef struct { double phi; /**< moisture diffusion parameter */ double zwt; /**< water table position relative to soil surface within the layer (cm) */ // Fluxes - double bare_evap_frac; /**< fraction of evapotranspiration coming from bare soil evap, from soil layer (mm) */ + double esoil; /**< soil evaporation from soil layer (mm) */ double evap; /**< evapotranspiration from soil layer (mm) */ + double transp; /**< transpiration from soil layer (mm) */ } layer_data_struct; /****************************************************************************** diff --git a/vic/vic_run/src/arno_evap.c b/vic/vic_run/src/arno_evap.c index 7b3ed3a32..3f4db87e9 100644 --- a/vic/vic_run/src/arno_evap.c +++ b/vic/vic_run/src/arno_evap.c @@ -59,7 +59,7 @@ arno_evap(layer_data_struct *layer, double ratio, as; double Epot; /* potential bare soil evaporation */ double moist; - double evap; + double esoil; double max_infil; double Evap; double tmpsum; @@ -115,7 +115,7 @@ arno_evap(layer_data_struct *layer, /**********************************************************************/ if (tmp >= max_infil) { - evap = Epot; + esoil = Epot; } else { /********************************************************************/ @@ -159,7 +159,7 @@ arno_evap(layer_data_struct *layer, } beta_asp = as + (1.0 - as) * (1.0 - ratio) * dummy; - evap = Epot * beta_asp; + esoil = Epot * beta_asp; } /***********************************************************************/ @@ -168,21 +168,21 @@ arno_evap(layer_data_struct *layer, /***********************************************************************/ /* only consider positive evaporation; we won't put limits on condensation */ - if (evap > 0.0) { + if (esoil > 0.0) { if (moist > moist_resid * depth1 * MM_PER_M) { - /* there is liquid moisture available; cap evap at available liquid moisture */ - if (evap > moist - moist_resid * depth1 * MM_PER_M) { - evap = moist - moist_resid * depth1 * MM_PER_M; + /* there is liquid moisture available; cap esoil at available liquid moisture */ + if (esoil > moist - moist_resid * depth1 * MM_PER_M) { + esoil = moist - moist_resid * depth1 * MM_PER_M; } } else { - /* no moisture available; cap evap at 0 */ - evap = 0.0; + /* no moisture available; cap esoil at 0 */ + esoil = 0.0; } } - layer[0].evap = evap; - Evap += evap / MM_PER_M / delta_t; + layer[0].esoil = esoil; + Evap += esoil / MM_PER_M / delta_t; return(Evap); } diff --git a/vic/vic_run/src/canopy_evap.c b/vic/vic_run/src/canopy_evap.c index 8f6689497..ac4e0d568 100644 --- a/vic/vic_run/src/canopy_evap.c +++ b/vic/vic_run/src/canopy_evap.c @@ -70,14 +70,14 @@ canopy_evap(layer_data_struct *layer, double tmp_Evap; double canopyevap; double tmp_Wdew; - double layerevap[MAX_LAYERS]; + double layertransp[MAX_LAYERS]; double rc; Evap = 0; /* Initialize variables */ for (i = 0; i < options.Nlayer; i++) { - layerevap[i] = 0; + layertransp[i] = 0; } canopyevap = 0; throughfall = 0; @@ -144,7 +144,7 @@ canopy_evap(layer_data_struct *layer, if (CALC_EVAP) { transpiration(layer, veg_var, veg_class, rad, vpd, net_short, air_temp, ra, *dryFrac, delta_t, elevation, Wmax, Wcr, - Wpwp, layerevap, frost_fract, root, shortwave, Catm, + Wpwp, layertransp, frost_fract, root, shortwave, Catm, CanopLayerBnd); } @@ -153,8 +153,8 @@ canopy_evap(layer_data_struct *layer, veg_var->Wdew = tmp_Wdew; tmp_Evap = canopyevap; for (i = 0; i < options.Nlayer; i++) { - layer[i].evap = layerevap[i]; - tmp_Evap += layerevap[i]; + layer[i].transp = layertransp[i]; + tmp_Evap += layertransp[i]; } Evap += tmp_Evap / (MM_PER_M * delta_t); @@ -180,7 +180,7 @@ transpiration(layer_data_struct *layer, double *Wmax, double *Wcr, double *Wpwp, - double *layerevap, + double *layertransp, double *frost_fract, double *root, double shortwave, @@ -195,10 +195,10 @@ transpiration(layer_data_struct *layer, size_t frost_area; double gsm_inv; /* soil moisture stress factor */ double moist1, moist2; /* tmp holding of moisture */ - double evap; /* tmp holding for evap total */ + double transp; /* tmp holding for transp total */ double Wcr1; /* tmp holding of critical water for upper layers */ double root_sum; /* proportion of roots in moist>Wcr zones */ - double spare_evap; /* evap for 2nd distribution */ + double spare_transp; /* transp for 2nd distribution */ double avail_moist[MAX_LAYERS]; /* moisture available for trans */ double ice[MAX_LAYERS]; double gc; @@ -280,8 +280,8 @@ transpiration(layer_data_struct *layer, Potential evapotranspiration not hindered by soil dryness. If layer with less than half the roots is dryer than Wcr, extra - evaporation is taken from the wetter layer. Otherwise layers - contribute to evapotransipration based on root fraction. + transpiration is taken from the wetter layer. Otherwise layers + contribute to transpiration based on root fraction. ******************************************************************/ if (options.SHARE_LAYER_MOIST && @@ -324,17 +324,17 @@ transpiration(layer_data_struct *layer, } /* compute transpiration */ - evap = penman(air_temp, elevation, rad, vpd, ra, veg_var->rc, - vic_run_veg_lib[veg_class].rarc) * - delta_t / CONST_CDAY * dryFrac; + transp = penman(air_temp, elevation, rad, vpd, ra, veg_var->rc, + vic_run_veg_lib[veg_class].rarc) * + delta_t / CONST_CDAY * dryFrac; - /** divide up evap based on root distribution **/ + /** divide up transp based on root distribution **/ /** Note the indexing of the roots **/ root_sum = 1.0; - spare_evap = 0.0; + spare_transp = 0.0; for (i = 0; i < options.Nlayer; i++) { if (avail_moist[i] >= Wcr[i]) { - layerevap[i] = evap * (double) root[i]; + layertransp[i] = transp * (double) root[i]; } else { if (avail_moist[i] >= Wpwp[i]) { @@ -345,29 +345,29 @@ transpiration(layer_data_struct *layer, gsm_inv = 0.0; } - layerevap[i] = evap * gsm_inv * (double) root[i]; + layertransp[i] = transp * gsm_inv * (double) root[i]; root_sum -= root[i]; - spare_evap = evap * (double) root[i] * (1.0 - gsm_inv); + spare_transp = transp * (double) root[i] * (1.0 - gsm_inv); } } - /** Assign excess evaporation to wetter layer **/ - if (spare_evap > 0.0) { + /** Assign excess transpiration to wetter layer **/ + if (spare_transp > 0.0) { for (i = 0; i < options.Nlayer; i++) { if (avail_moist[i] >= Wcr[i]) { - layerevap[i] += (double) root[i] * spare_evap / root_sum; + layertransp[i] += (double) root[i] * + spare_transp / root_sum; } } } } /********************************************************************* - CASE 2: Independent evapotranspirations + CASE 2: Independent transpirations Evapotranspiration is restricted by low soil moisture. Evaporation is computed independantly from each soil layer. *********************************************************************/ - else { /* Initialize conductances for aggregation over soil layers */ gc = 0; @@ -380,7 +380,7 @@ transpiration(layer_data_struct *layer, } for (i = 0; i < options.Nlayer; i++) { - /** Set evaporation restriction factor **/ + /** Set transpiration restriction factor **/ if (avail_moist[i] >= Wcr[i]) { gsm_inv = 1.0; } @@ -428,11 +428,11 @@ transpiration(layer_data_struct *layer, } /* compute transpiration */ - layerevap[i] = penman(air_temp, elevation, rad, vpd, ra, - veg_var->rc, - vic_run_veg_lib[veg_class].rarc) * - delta_t / CONST_CDAY * dryFrac * - (double) root[i]; + layertransp[i] = penman(air_temp, elevation, rad, vpd, ra, + veg_var->rc, + vic_run_veg_lib[veg_class].rarc) * + delta_t / CONST_CDAY * dryFrac * + (double) root[i]; if (veg_var->rc > 0) { gc += 1 / (veg_var->rc); @@ -453,7 +453,7 @@ transpiration(layer_data_struct *layer, } } else { - layerevap[i] = 0.0; + layertransp[i] = 0.0; gc += 0; if (options.CARBON) { for (cidx = 0; cidx < options.Ncanopy; cidx++) { @@ -494,32 +494,32 @@ transpiration(layer_data_struct *layer, } /**************************************************************** - Check that evapotransipration does not cause soil moisture to + Check that transpiration does not cause soil moisture to fall below wilting point. ****************************************************************/ for (i = 0; i < options.Nlayer; i++) { if (ice[i] > 0) { if (ice[i] >= Wpwp[i]) { // ice content greater than wilting point can use all unfrozen moist - if (layerevap[i] > avail_moist[i]) { - layerevap[i] = avail_moist[i]; + if (layertransp[i] > avail_moist[i]) { + layertransp[i] = avail_moist[i]; } } else { // ice content less than wilting point restrict loss of unfrozen moist - if (layerevap[i] > layer[i].moist - Wpwp[i]) { - layerevap[i] = layer[i].moist - Wpwp[i]; + if (layertransp[i] > layer[i].moist - Wpwp[i]) { + layertransp[i] = layer[i].moist - Wpwp[i]; } } } else { // No ice restrict loss of unfrozen moist - if (layerevap[i] > layer[i].moist - Wpwp[i]) { - layerevap[i] = layer[i].moist - Wpwp[i]; + if (layertransp[i] > layer[i].moist - Wpwp[i]) { + layertransp[i] = layer[i].moist - Wpwp[i]; } } - if (layerevap[i] < 0.0) { - layerevap[i] = 0.0; + if (layertransp[i] < 0.0) { + layertransp[i] = 0.0; } } } diff --git a/vic/vic_run/src/func_surf_energy_bal.c b/vic/vic_run/src/func_surf_energy_bal.c index 1fe7ef4ca..9d51ef818 100644 --- a/vic/vic_run/src/func_surf_energy_bal.c +++ b/vic/vic_run/src/func_surf_energy_bal.c @@ -203,7 +203,7 @@ func_surf_energy_bal(double Ts, double T1_plus; double D1_minus; double D1_plus; - double *transp = NULL; + double SurfRad; double Ra_bare[3]; double tmp_wind[3]; double tmp_height; @@ -366,12 +366,6 @@ func_surf_energy_bal(double Ts, TMean = Ts; - transp = calloc(options.Nlayer, sizeof(*transp)); - check_alloc_status(transp, "Memory allocation error."); - for (i = 0; i < options.Nlayer; i++) { - transp[i] = 0.; - } - /********************************************** Compute Surface Temperature at Half Time Step **********************************************/ @@ -749,66 +743,42 @@ func_surf_energy_bal(double Ts, Should evapotranspiration be active when the ground is only partially covered with snow???? - - Use Arno Evap in the exposed soil portion, and/or - if LAI is zero. *************************************************/ - if (VEG && !SNOWING && veg_var->fcanopy > 0) { - Evap = canopy_evap(layer, veg_var, true, - veg_class, Wdew, delta_t, NetBareRad, vpd, - NetShortBare, Tair, Ra_veg[1], elevation, rainfall, - Wmax, Wcr, Wpwp, frost_fract, root, dryFrac, - shortwave, Catm, CanopLayerBnd); - if (veg_var->fcanopy < 1) { - for (i = 0; i < options.Nlayer; i++) { - transp[i] = layer[i].evap; - layer[i].evap = 0.; - } + for (i = 0; i < options.Nlayer; i++) { + layer[i].transp = 0; + layer[i].esoil = 0; + } + Evap = 0.; + if (!SNOWING) { + if (VEG && veg_var->fcanopy > 0) { + Evap = canopy_evap(layer, veg_var, true, veg_class, Wdew, + delta_t, NetBareRad, vpd, NetShortBare, + Tair, Ra_veg[1], elevation, rainfall, + Wmax, Wcr, Wpwp, frost_fract, root, + dryFrac, shortwave, Catm, CanopLayerBnd); Evap *= veg_var->fcanopy; - Evap += (1 - veg_var->fcanopy) * - arno_evap(layer, surf_atten * NetBareRad, Tair, vpd, - depth[0], max_moist * depth[0] * MM_PER_M, - elevation, b_infilt, Ra_used[0], delta_t, - resid_moist[0], frost_fract); for (i = 0; i < options.Nlayer; i++) { - layer[i].evap = veg_var->fcanopy * transp[i] + - (1 - veg_var->fcanopy) * layer[i].evap; - if (layer[i].evap > 0.) { - layer[i].bare_evap_frac = 1 - - (veg_var->fcanopy * - transp[i]) / layer[i].evap; - } - else { - layer[i].bare_evap_frac = 0.; - } + layer[i].transp *= veg_var->fcanopy; } - veg_var->throughfall = - (1 - - veg_var->fcanopy) * rainfall + veg_var->fcanopy * - veg_var->throughfall; - veg_var->canopyevap *= veg_var->fcanopy; - veg_var->Wdew *= veg_var->fcanopy; + SurfRad = surf_atten * NetBareRad; } else { - for (i = 0; i < options.Nlayer; i++) { - layer[i].bare_evap_frac = 0.; - } + SurfRad = NetBareRad; } - } - else if (!SNOWING) { - Evap = arno_evap(layer, NetBareRad, Tair, vpd, - depth[0], max_moist * depth[0] * MM_PER_M, - elevation, b_infilt, Ra_used[0], delta_t, - resid_moist[0], frost_fract); + Evap += (1 - veg_var->fcanopy) * + arno_evap(layer, SurfRad, Tair, vpd, + depth[0], max_moist * depth[0] * MM_PER_M, + elevation, b_infilt, Ra_used[0], delta_t, + resid_moist[0], frost_fract); for (i = 0; i < options.Nlayer; i++) { - layer[i].bare_evap_frac = 1; + layer[i].esoil *= (1 - veg_var->fcanopy); + layer[i].evap = layer[i].transp + layer[i].esoil; } + veg_var->throughfall = (1 - veg_var->fcanopy) * rainfall + + veg_var->fcanopy * veg_var->throughfall; + veg_var->canopyevap *= veg_var->fcanopy; + veg_var->Wdew *= veg_var->fcanopy; } - else { - Evap = 0.; - } - - free(transp); /********************************************************************** Compute the Latent Heat Flux from the Surface and Covering Vegetation